V. Biện pháp ngăn chặn lỗ hổng Prototype Pollution
1. Kiểm tra và loại bỏ giá trị property key
Hầu hết các kỹ thuật khai thác lỗ hổng Prototype Pollution sử dụng property key __proto__
. Một trong những biện pháp đơn giản nhất để ngăn chặn tấn công này là phát hiện và loại bỏ chuỗi từ khóa này trong ứng dụng. Đối với các ứng dụng sử dụng Node.js, lập trình viên có thể sử dụng các flags command-line như --disable-proto=delete
hoặc --disable-proto=throw
để xóa hoặc ném lỗi khi cố gắng sử dụng __proto__
.
Tuy nhiên, phương pháp này tương tự như blacklist và không hoàn toàn bảo vệ khỏi các kỹ thuật tấn công khác có thể sử dụng các "con đường" khác như constructor
để vượt qua các biện pháp bảo vệ này.
2. Giới hạn property keys thông qua whitelist
Để tăng tính bảo mật, chúng ta nên sử dụng whitelist để chỉ định các giá trị property key được phép. Dưới đây là ví dụ về hàm sanitizeObject()
:
javascript
const whitelist = ['allowedProperty1', 'allowedProperty2']; // Danh sách các property keys cho phép
function sanitizeObject(obj) {
const sanitizedObj = {};
for (const key in obj) {
if (obj.hasOwnProperty(key) && whitelist.includes(key)) {
sanitizedObj[key] = obj[key];
}
}
return sanitizedObj;
}
Trước khi sử dụng dữ liệu người dùng, các đối tượng sẽ được lọc thông qua hàm này.
3. Đóng băng nguyên mẫu (Prototype Freezing)
Một phương pháp khác là không cho phép thay đổi các giá trị nguyên mẫu bằng cách sử dụng Object.freeze()
. Khi gọi phương thức này, đối tượng không thể bị thay đổi:
javascript
const obj = { prop: 42 };
Object.freeze(obj);
obj.prop = 33; // Không thể thực hiện điều này trong chế độ nghiêm ngặt
console.log(obj.prop); // Kết quả dự kiến: 42
Tuy nhiên, nhược điểm là các đối tượng trở nên "cứng nhắc". Thay vào đó, có thể sử dụng Object.seal()
để chỉ cho phép thay đổi giá trị thuộc tính hiện tại mà không thể thêm mới hay xóa thuộc tính:
javascript
const object1 = { property1: 42 };
Object.seal(object1);
object1.property1 = 33;
console.log(object1.property1); // Kết quả: 33
4. Sử dụng đối tượng Set / Map
Một cách ngăn chặn khác là sử dụng các đối tượng Set hoặc Map để thay thế cho đối tượng thông thường. Cả Set và Map không kế thừa giá trị thuộc tính từ Object.prototype
:
javascript
const myMap = new Map();
myMap.set("key", "value");
console.log(myMap.get("key")); // Kết quả: "value"
console.log(myMap.hasOwnProperty("toString")); // Kết quả: false
javascript
const mySet = new Set();
mySet.add("value1");
console.log(mySet.has("value1")); // Kết quả: true
console.log(mySet.hasOwnProperty("toString")); // Kết quả: false
5. Ngăn chặn kế thừa thuộc tính bằng Null prototype
Khi cần sử dụng các đối tượng thông thường mà không muốn kế thừa thuộc tính từ Object.prototype, chúng ta có thể tạo một đối tượng với Null prototype:
javascript
let myObject = Object.create(null);
Object.getPrototypeOf(myObject); // null
Khi đó, đối tượng này sẽ không kế thừa bất kỳ thuộc tính nào từ nguyên mẫu.
VI. Tấn công Prototype Pollution trong CTF
Tấn công Prototype Pollution cũng là một chủ đề quen thuộc trong các cuộc thi CTF (Capture the Flag). Chúng tôi sẽ giới thiệu một thử thách từ nền tảng Hack the box - Gunship. Dù thử thách này đã được đưa vào mục RETIRED, bạn vẫn có thể tải mã nguồn đầy đủ và dựng môi trường tại nhà.
1. Dựng môi trường challenge
Challenge có thể dễ dàng dựng tại local bằng Docker với file build-docker.sh
đã được cung cấp.
2. Tổng quan chức năng
Thử thách yêu cầu gửi tên của favourite artist đến server qua phương thức POST tại endpoint /api/submit
.
3. Review source code
Trong mã nguồn, tại route /api/submit
, nếu tên artist chứa từ khóa như Haigh
, Westaway
, hoặc Gingell
, server sẽ trả về thông báo hợp lệ.
4. Xây dựng payload
Chúng ta có thể ghi đè thuộc tính name
trong Object.prototype để thực hiện tấn công:
javascript
{
"artist": {},
"__proto__": {
"name": "Gingell"
}
}
Payload khai thác sẽ là:
javascript
{"artist.name":"Haigh","__proto__.block": { "type": "Text", "line": "process.mainModule.require('child_process').execSync('$(id)')" }}
Điều này cho phép chúng ta vượt qua xác thực và thực hiện mã tùy ý để lấy flag. Thử thách này cho thấy tầm quan trọng của việc sử dụng các phiên bản mới nhất của các công nghệ để giảm nguy cơ lỗ hổng.
Tài liệu tham khảo
- https://portswigger.net/web-security/prototype-pollution/preventing
- https://portswigger.net/research/server-side-prototype-pollution
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
- https://knqyf263.hatenablog.com/entry/2020/08/11/050342
- https://book.hacktricks.xyz/pentesting-web/deserialization/nodejs-proto-prototype-pollution
©️ Tác giả: Lê Ngọc Hoa từ Viblo
source: viblo