DongDD's IT

[LOB] Level14 bugbear 본문

Wargame/LOB

[LOB] Level14 bugbear

DongDD 2018. 1. 27. 17:08

[LOB] Level14 bugbear


Problem



이번 문제에서도 giant 실행 파일과 giant.c 소스 코드 파일이 주어져있었다.

먼저 소스 코드를 확인해보았다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
main(int argc, char *argv[])
{
        char buffer[40];
        FILE *fp;
        char *lib_addr, *execve_offset, *execve_addr;
        char *ret;
 
        if(argc < 2){
                printf("argv error\n");
                exit(0);
        }
 
        // gain address of execve
        fp = popen("/usr/bin/ldd /home/giant/assassin | /bin/grep libc 
            | /bin/awk '{print $4}'""r");
        fgets(buffer, 255, fp);
        sscanf(buffer, "(%x)"&lib_addr);
        fclose(fp);
 
        fp = popen("/usr/bin/nm /lib/libc.so.6 | /bin/grep __execve
             | /bin/awk '{print $1}'""r");
        fgets(buffer, 255, fp);
        sscanf(buffer, "%x"&execve_offset);
        fclose(fp);
 
        execve_addr = lib_addr + (int)execve_offset;
        // end
 
        memcpy(&ret, &(argv[1][44]), 4);
        if(ret != execve_addr)
        {
                printf("You must use execve!\n");
                exit(0);
        }
 
        strcpy(buffer, argv[1]);
        printf("%s\n", buffer);
}
 
cs


이번 문제의 소스 코드는 앞 문제들과는 많이 달라보였다.

40 bytes의 buffer를 생성해주고 argv를 입력받아야 한다.

그 후에 popen을 통해 파이프라인을 열어준다. fgets를 통해 연 파일에 대한 정보를 받는 것으로보아 파이프라인 실행 결과를 buffer에 저장한 후에 lib_addr이라는 변수에 저장하는 것 같았다.

awk에 대해서는 잘 모르지만 libc를 grep하는 걸로 보아 libc에 대한 시작 주소를 lib_addr에 저장하는 것 같다.

그 후에 새로운 파이프라인을 연다.  이번에는 libc.so.6에서 __execve의 offset을 가져온다. 이것을 buffer로 읽고 execve_offset으로 저장한다.

그 후 execve_addr에 lib_addr+execve_offset을 해 execve함수의 주소를 저장한다.

그 후 argv[1]의 return address가 execve_addr과 일치하는지 확인하여 일치하지 않는다면 종료시키고 아니라면 argv[1]을 buffer에 복사한다.




Solution


1. Return to Library(RTL)



먼저 execve() 함수의 주소를 알아내기 위해 gdb를 통해 main에 break point를 설정하고 execve의 주소를 알아내었다.


execve() 함수의 주소 : 0x400a9d48


그 후에 giant파일로 실행을 해보았지만 "You must use execve!"라는 메시지를 볼 수 있었다. 입력한 return address와 execve()함수의 주소가 다른 것 같았다.

여기서 이 문제점을 해결하기 위해 많은 시간이 들었다. 먼저 프로그램을 하나 작성해 입력한 값과 execve 주소를 비교해보았더니 argv에 입력한 "\x0a"가 "\x00"으로 바뀌어있는 것을 볼 수 있었다. "\0x0a"가 Line feed라 문자열로 인식하지 않는 것 같았다.

hex()등 여러 함수를 써서 시도해보았지만 다 성공하지 못했고 찾아보니 양 끝에 "(double quote)를 입력해주면 입력한 그대로의 문자열로 저장된다는 것을 알게 되었고 "(double quote)를 사용해 제대로 된 주소를 알 수 있었고 giant를 통해 실행해보니 if문을 통과하는 것을 볼 수 있었다.


이후에는 execve()는 함수는 놔두고 system() 함수를 이용하는 방법을 선택했다.



마찬가지로 gdb를 통해 main에 break point를 설정하고 system()함수의 주소를 알아냈다.


system()함수의 주소 : 0x40058ae0


그 후에 프로그램을 작성해 라이브러리 내에 있을 "/bin/sh"의 주소를 알아내었다.


"/bin/sh"의 주소 : 0x400fbff9


Payload를 작성하기 위한 memory는 위와 같다.

먼저 dummy 44 bytes를 넣어주고 if문을 통과하기 위한 execve() 함수의 주소를 넣어준다.

실제로는 execve() 함수는 if문을 통과하기 위한 것이고 사용되지 않는다.

그 후에 execve()함수가 종료됐을 때의 return address에 system()함수의 주소를 넣어준다.

그 후에 dummy(4 bytes, system() 함수 종료 시 return address)를 넣어주고 system() 함수에 사용할 인자인 "/bin/sh"의 주소를 넣어준다.


Payload = dummy(44 bytes, buffer+sfp, "A'*44) + return address1(4 bytes, execve()함수의 주소) + return address2(4 bytes, system()함수의 주소) + dummy(4 bytes, return address3, "A"*4) + system()함수의 argument1(4 bytes, "/bin/sh"의 주소)


위 Payload를 작성해 giant 파일을 실행하니 shell을 얻을 수 있었다.





**************     Answer & Flag     **************


1. Return to Library(RTL)


Payload = dummy(44 bytes, buffer+sfp, "A'*44) + return address1(4 bytes, execve()함수의 주소) + return address2(4 bytes, system()함수의 주소) + dummy(4 bytes, return address3, "A"*4) + system()함수의 argument1(4 bytes, "/bin/sh"의 주소)


argv[1] = "A"*44 + "\x48\x9d\x0a\x40"+"\xe0\x8a\x05\x40" + "A"*4 + "\xf9\xbf\x0f\x40"




Next ID : giant

Next PW : one step closer

'Wargame > LOB' 카테고리의 다른 글

[LOB] Level16 assassin  (0) 2018.01.31
[LOB] Level15 giant  (0) 2018.01.30
[LOB] Level13 darkknight  (0) 2018.01.26
[LOB] level12 golem  (0) 2018.01.25
[LOB] Level11 skeleton  (0) 2018.01.24
Comments