Tìm hiểu về khai thác NodeJS và MongoDB Injection

Bài viết đưa ra một kĩ thuật đơn giản nhưng hiệu quả trong việc khai thác ứng dụng web hiện đại được viết trên nền NodeJS và MongoDB. Thực chất, kĩ thuật này khá giống SQL Injection (SQLi) nhưng đơn giản hơn nhiều do không cần hoàn thành những chuỗi kí tự phức tạp hay lạ thường.

Những ví dụ trong bài viết sử dụng ExpressJS. Đây là framework lập trình ứng dụng web phổ biến dành cho NodeJs.

Sơ lược về SQL Injection

Điều đầu tiên bạn cần học khi nghiên cứu về SQL Injection là làm thế nào để tạo ra một biểu thức TRUE. Ví dụ về câu truy vấn SQL dưới đây được sử dụng trong xác thực người dùng khi tên tài khoản và mật khẩu được gửi lên ứng dụng:

SELECT * FROM users WHERE username = '$username' AND password = '$password'

Nếu câu lệnh này không được chuẩn bị hay xử lý đúng khi tạo ra, tin tặc có thể đưa thêm

' or 1=1--

vào trường username nhằm tạo tạo một bypass login đơn giản nhất như sau:

SELECT * FROM users WHERE username = '' or 1=1--' AND password = ''

Hiện nay, kiểu tấn công cổ điển và biến thể của nó vẫn đang được sử dụng để xác định lỗ hổng trong xử lý câu lệnh SQL.

Sơ lược về MongoDB Injection

Dù SQL Injection vẫn là một kiểu tấn công ứng dụng web phổ biến, nhưng nó không còn phát tán rộng rãi như trước kia nữa. Rất nhiều ứng dụng web hiện nay lựa chọn các cơ chế lưu trữ đơn giản hơn như cung cấp cơ sở dữ liệu NoSQL như MongoDB. Cơ sở dữ liệu NoSQL không chỉ được phát triển theo hướng đơn giản hóa mà còn cải thiện bảo mật thông qua việc loại bỏ hoàn toàn ngôn ngữ SQL và phụ thuộc vào cơ chế truy vấn có cấu trúc đơn giản hơn như JSON và JavaScript.

Câu lệnh SQL ở ví dụ truy vấn chi tiết đăng nhập người dùng sẽ được viết như sau trong MongoDB:

db.users.find({username: username, password: password});

Chúng ta không còn phải xử lý với một ngôn ngữ truy vấn dưới dạng một chuỗi kí tự, do đó có thể cho rằng việc tiêm nhiễm chuỗi độc hại vào là không thể. Tuy nhiên, sét về tính bảo mật, vẫn còn rất nhiều yếu tốt khác có thể ảnh hưởng.

Ví dụ, giả sử tại trường username, hoặc tham số được truyền vào dưới dạng phân tích một đối tượng JSON. Nếu đầu vào ứng dụng là một tài liệu JSON, tin tặc có thể thực hiện  bypass login tương tự như với SQL Injection với giá trị đầu vào như sau:

{ 
"username": {"$gt": ""},
"password": {"$gt": ""}
 }

Code xử lý yêu cầu có dạng như sau:

app.post('/', function (req, res) { 
db.users.find({username: req.body.username, password: req.body.password}, function (err, users) { 
// TODO: handle the rest 
}); 
});

Ở đoạn code ExpressJS xử lý trên, trường usernamepassword không được kiểm duyệt để đảm bảo chúng là chuỗi kí tự. Do đó khi một tài liệu JSON được phân tích, những trường này có thể chứa bất kì chuỗi kí tự nào, và những chuỗi kí tự đó có thể được dùng để tạo ra một cấu trúc truy vấn khác. Trong MongoDB, trường $gt có nghĩa đặc biệt, được sử dụng trong so sánh lớn hơn. Do đó username và password từ cơ sở dữ liệu sẽ được so sánh với dữ liệu nhập vào là chuỗi kí tự trống “” và kết quả câu lệnh trả về là TRUE.

Request dùng để khai thác sẽ có dạng như sau:

POST http://target/ HTTP/1.1 
Content-Type: application/json 
{ 
"username": {"$gt": ""}, 
"password": {"$gt": ""} 
}

Sử dụng khai thác kết hợp NodeJS và MongoDB

Ví dụ trên sử dụng cơ chế truyền tải nội dung kiểu JSON để giải thích về kiểu tấn công này trở nên dễ hiểu hơn. Tuy nhiên, JSON vẫn chưa phổ biến như url-encoded key-value hay còn gọi là urlencoding. Sử dụng phần thân và tham số truy vấn trong định dạng urlencoding liệu có an toàn?

Trong ExpressJS ta vẫn có thể bypass ví dụ trên thông qua một chuỗi truy vấn đơn giản như sau:

POST http://target/ HTTP/1.1 
Content-Type: application/x-www-form-urlencoded 
username[$gt]=&password[$gt]=

Chuỗi

username[$gt]=

là một cú pháp đặc biệt sử dụng trong module qs (sử dụng mặc định trong ExpressJS và phần mềm trung gian body-parser). Cú pháp này tương đương với việc tạo ra một JavaScript object/hash với một tham số $gt và không có giá trị. Thực chất, request trên sẽ trả về kết quả là một đối tượng JavaScript như sau:

{
    "username": {"$gt": undefined},
    "password": {"$gt": undefined}
}

Nếu so sánh với đối tượng sinh ra như trong ví dụ trước, ta sẽ thấy chúng hoàn toàn giống nhau.

CÁC VÍ DỤ MINH HỌA

Hai project sau được dùng để minh họa lỗ hổng trong thực tế. Project đầu tiên sử dụng kiểu giá trị urlencoded và project thứ hai sử dụng JSON. Bạn đọc có thể đọc thêm tệp tin README để có thêm thông tin.

example

Kiểm thử MongoDB Injection

Kết hợp hai công cụ kiểm tra tự động có tên Formfuzz và Jsonfuzz. Các công cụ sẽ tự động sinh ra request kiểm tra khai thác MongoDB.

websecurify

Bình luận

Từ khóa: