18. fsb


1

FSB (Format string bug) 취약점 문제로 보인다.


2

brain fuck?


flag, 바이너리, 코드가 있다.

코드를 확인해보면 다음과 같다.


#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;
}


먼저 난수를 발생시켜 key를 만들고 이 키를 맞히면 쉘을 얻을 수 있는 문제이다.

fsb 함수에서는 사용자에게 키를 입력받고 이게 key와 일치하면 쉘을 실행시키는데

22번 라인에 있는 반복문에서 4개의 문자열을 입력받는데 printf에서 형식 지정자format string

사용하지 않고 그냥 출력하는 것을 확인할 수 있다. 여기서 취약점이 있다.

당연 buf에 입력받기 때문에 처음엔 return address를 갖고있는 스택 주소를 buf에 쓰고

다시 format string bug로 return address를 쉘을 실행시키는 구문으로 돌리면 될거라 생각했는데..

buf가 전역변수였다.

즉, 내가 원하는 임의의 주소에 값을 쓰지는 못한다는 건데, 어셈을 본다..

3

read 함수를 통해 4회 반복을 돌며 입력을 받는 부분인데, buf에 입력 직후 스택 상황을 보기 위해 fsb+212에 bp를 걸고 실행해본다.


4

buf가 전역변수이기 때문에 스택 상에서 입력한 문자열을 확인할 수 없었다.

고민을 하다가 스택에 값을 쓸 수 없다면 이미 쓰여져 있는 값 중에 활용할만한게 없나 살펴봤다.

스택에 특정 접근 가능한 주소값이 쓰여져있으면 해당 주소값을 이용하여 원하는 곳에 값을 쓸 수 있을 것이다.


5

저 위치에 쓰여져있던 주소가 실행 도중 esp에서 비교적 가까운 위치의 주소를 담고있었다.

이를 이용해서 0xff8322230 위치에 있는 메모리에 원하는 값을 쓸 수 있게 된다.

for문이 총 4회 반복되기 때문에 총 4번format string이 가능하다.

이를 어떻게 이용할까 고민해보고 처음엔 return address를 덮으려 했으나.. return address를 담고있는 주소가 터무니 없이 커서

format string을 위해 그 주소만큼 공백 문자를 채워 출력하려니 너무 오래걸릴 것 같았다.

다른 방법을 고민하다가 got를 쓰는 것도 가능할 것 같아서 시도했다.

먼저 타겟 함수를 찾아본다.


6

다행히 덮어쓸만한 함수가 많다.

printf, read, sleep 함수들을 덮어써도 되겠지만 나는 strtoull로 결정했다.

이제 필요한 주소들을 확인해본다.


7


8

strtoull 함수의 got 상의 주소는 0x804a020


9

쉘을 실행시킬 수 있는 점프 주소는 0x0804869f

주소는 다 구했으니 공격을 하면

첫 번째 format string bug로 아까 스택 상에서 접근할 수 있던 주소에 strtoull의 got 주소인 0x804a020을 쓰고

두 번째 format string bug로 0x804a020에 쉘을 실행시키는 주소인 0x804869f를 쓰면

strtoull 함수가 호출될 때 쉘을 얻을 수 있을 것이다.

공격을 해보면,


10

입력 직후 화면에서 esp부터 출력한거고, 맨 위에서 두 번째 0x0804a100buf의 주소니까

format string bug로 접근이 가능한 주소의 시작부분은 맨 위에서 두 번째0xff83e1e4이다.

목표 지점인 0xff832218까진 %x13번 필요하고 14번째에 %n을 통해 0x804a020을 쓴다.

마지막 %x는 출력 바이트 수를 조정하기 위해 0x804a020에서 현재까지 출력한 0x8*12만큼을 빼준다.

"%8x"*12 + "%" + str(0x804a020-0x8*12) +"x" + "%n" 

두 번째 format string에선 목표 지점인 0xff8322300x804a020이 쓰여졌을 것이고

여기까지 다시 접근 후 %n을 이용하여 쉘을 실행시키는 주소인 0x804869f를 쓴다.

마찬가지로 목표 지점까진 %x19번 필요하고 마지막 %x에서는 출력 문자의 수를 맞춰준다.

"%8x"*18 + "%" + (str(0x804869f - 0x8*18)) + "x" + "%n"



11




© 2020.02. by blupine