wargame 풀이/LOB

LOB 7번 풀이 (darkelf)

yehey 2020. 11. 10. 12:19
id: darkelf
pw: kernel crashed
/*
        The Lord of the BOF : The Fellowship of the BOF
        - orge
        - check argv[0]
*/

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

        // here is changed!
        if(strlen(argv[0]) != 77){
                printf("argv[0] 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보다 길어서는 안됨
  • argv[0]의 strlen이 77이어야 함 (argv[0]은 실행파일 명)

orge 디스어셈블

스택 구조는 이전 문제들과 같으므로 생략

이번에도 buffer를 초기화하기 때문에 쉘 코드를 저장할 다른 공간을 찾아주어야 한다.

wolfman에서는 argv[2]를 사용했으나, 이번에는 exploit 코드로 전달이 잘 되지 않아서 다른 방법을 찾아야 했다.

그래서 일단 orge gdb에서 main에 breakpoint를 걸고 argv[1]에 인자로 "\x90"을 48바이트 전달해보았다.

 

그리고 esp를 확인해보았더니 buffer로 추정되는 부분은 0으로 초기화 되어있었고 (0xbffffbf9 부터 40바이트)

더 아래를 보면 "\x90"이 48바이트 만큼 (내가 입력한 만큼) 특정 공간에 남아있었다.

즉, buffer는 초기화해도 argv[1]은 초기화 되지 않고 남아있다.

따라서 이전처럼 argv[1]에 NOP와 쉘 코드를 넣어 전달해주고 ret 주소에는 buffer 시작주소가 아닌 argv[1]의 주소를 넣어주자.

 

그러면 argv[1]의 주소를 알아내기 위해 orge.c를 복사해서 코드를 살짝 수정한 후 컴파일해서 실행해보자

/*
        orge2의 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);
        }
        printf("argv[1] addr: %x\n",argv[1]);	//argv[0] 체크 전에 argv[1]의 주소 출력
        
        // here is changed!
        if(strlen(argv[0]) != 77){
                printf("argv[0] 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", buffer);

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

 

실행할 때는 exploit 코드와 최대한 동일하게 작성하는 것이 좋다. 전달하는 argv[1]가 달라지면 주소도 달라질 수 있기 때문이다.

 

앞서 코드를 분석했을 때 argv[0] 즉, 실행파일 명이 77이어야 정상적으로 실행되기 때문에 실행파일명의 길이를 77바이트로 늘려주어야한다. "/"는 여러개를 사용해도 1개로 인식하기 때문에 "/"을 이용해 77을 맞춰주자. 

 

실행한 결과 0xbffff9e0이 argv[1]의 주소이다.

따라서 orge에 NOP와 쉘 코드를 전달해주고 ret에는 argv[1]의 주소인 0xbffff9e0을 넣어주자

 

orge 권한으로 bash 쉘이 실행되었다.

 

orge의 pw: "timewalker"