Self-Improvement

Stack Buffer OverFlow 기초실습(Ubuntu gdb) 본문

리버싱 기초

Stack Buffer OverFlow 기초실습(Ubuntu gdb)

JoGeun 2020. 5. 11. 15:34

출처 : https://d4m0n.tistory.com/14

위 strcpy 실습을 보면 쉘코드를 입력받는 argv[1]에 넣어주며 buffer 주소를 ret에 넣는 이유는 ret는 4바이트임으로 buffer주소를 넣어줌으로써 쉘코드가 포함된 argv[1]을 buffer에 복사해줌으로써 ret가 실행될때 buffer주소에 접근하여 쉘코드를 실행하게끔 해주는 것이다.

 

출처 : https://bpsecblog.wordpress.com/2016/03/08/gdb_memory_1/


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
#include <string.h>
#include <stdio.h>
 
void func2(){
 
    puts("func2()");
}
 
void sum(int a, int b){
    printf("sum : %d\n", a+b);
    func2();
}
 
int main(int argc, char *argv[]){
    int num=0;
    char arr[10];
 
    sum(1,2);
    strcpy(arr, argv[1]);
    printf("arr: %s\n", arr);
    if(num==1){
        system("/bin/sh");
    }
    return 0;
}
cs

소스코드는 간단하게 main()에서 입력받은 arrv[1]을 strcpy 함수를 이용하여 arr변수에 복사해주고 printf()해준다. 그리고 num==1이면 쉘을 얻게되는 구조이다.

여기서 strcpy 함수는 Buffer Overflow에 취약한 함수로 권장하고 있지 않다.

 

gdb로 컴파일한 파일을 실행시켜준다.

gef > set disassembly-flavor intel (intel 형식으로 어셈블언어를 본다는 뜻이다.)

 

gef > disassamble main 

메인을 분석하기전 <main+0>에 BP를 설정하며 aaaaaaa 값으로 실행해준다.

gef > b *main+0 또는 b *0x080484db

gef > r aaaaaaa

이제 메인을 한줄씩(gef > ni) 진행하면 분석하면서 BoF를 어떻게 해야하는지 알아본다.

1. <main+20> mov dword ptr [ebp-0xc], 0x0 --------> ebp-0xc 즉 v1 변수에 0을 대입해주고 있다.

2. <main+27> sub esp, 0x8 -----> 스택 공간 8바이트를 확보해준다.

3. <main+30> push 0x2 -----> 스택에 2를 넣어준다

4. <main+32> push 0x1 -----> 스택에 1을 넣어준다

5. <main+34> call 0x80484b4<sum> -------> 전에 입력받은 1,2를 인자로 sum()함수를 불러준다.

6. <main+39> add esp, 0x10 -----> sum()에서 사용된 스택을 정리해준다.

7. <main+42> mov eax, dword ptr [ebx+0x4] ------> ebx+0x4 의 값을 eax에 할당해준다.

- ebx+0x4의 값을 확인해보니 0xbffff0a0+4 = 0xbffff0a4이다.

- 0xbffff0a4의 값을 eax에 할당해주는 값을 확인해본다.

gef > x/wx 0xbffff0a4

- eax는 0xbffff134로 되어있다.

 

8. <main+45> add eax, 0x4 -----> 4를 eax에 더해준다. eax는 0xbffff138이 될 것이다. 

- eax의 주소의 값은 0xbffff310d으로 "aaaaaaa"인 입력 값이 들어있다.

 

9. <main+48> mov eax, dword ptr [eax] -----> eax의 값 0xbffff130을 eax에 할당해준다.

 

10. <main+50> sub esp, 0x8 -----> 8바이트만큼 스택공간을 확보해준다. (여기서 짐작되는건 8바이트 만큼 인자를 준다는 뜻이다.)

11. <main+53> push eax ----> 0xbffff310을 스택에 넣어준다

12. <main+54> lea eax, [ebp-0x16] ------> ebp-0x16의 주소를 eax에 할당해준다.

- ebp의 주소는 0xbffff088로 - 0x16할 시엔 0xbffff072가 되며 이 주소를 eax에 넣어준다.

 

13. <main+57> push eax -----> eax의 값을 스택에 넣어준다.

현재까지 스택의 구조를 확인해본다.

| v1(num) ~~ "aaaaaaa" 0xbffff072 |

 

14. <main+58> call 0x8048350 <strcpy> ----> 스택에 넣은 인자 0xbffff310("aaaaaaa"), 0xbffff072를 인자로 주며 0xbffff072에 복사를 해준다.

- strcpy 함수가 끝나고 스택을 확인해보면 

스택 0xbffff074에 입력값이 존재한다.

 

15. <main+63> add esp, 0x10 -----> strcpy 함수가 종료되고 스택을 정리해준다.

16. <main+66> sub esp, 0x8 -----> 스택 공간을 확보해준다.

17. <main+69> lea eax, [ebp-0x16] -----> ebp+0x16의 주소를 eax에 할당

- ebp-0x16의 값은 0x616161616161 이다. 즉 "aaaaaaa"을 뜻한다.

 

18. <main+72> push eax ----> eax 값 "aaaaaaa"을 스택에 넣고 그뒤에 printf() 함수를 수행한다.

19. <main+86> cmp dword ptr [ebp-0xc], 0x1 -----> ebp-0xc가 1과 동일한지를 비교하며 동일할 시엔 쉘을 얻을 수 가 있다. 스택을 확인하면 ebp-0xc의 주소는 0xbffff07c이며 0으로 되어있다.

우리가 입력한 값은 0xbffff073에 위치해 있다.

 

strcpy 함수는 Buffer Overflow에 취약한 함수인 것을 알고 있으며 이제 입력값으로 num 변수의 할당량을 1로 줄 것이다.

스택을 확인하면 "aaaaaaa" 입력한 주소와 num의 사이는 3바이트가 차이가 난다.

※ 스택에 0xbffff074 주소의 값이 "aaaaa"로 되어있는 이유는 그 아래의 a를 포함해서 나타낸것이다. 즉 "00"을 만나기 전까지의 값을 나타내고있다.

 

즉 다시 실행할때 gef > r aaaaaaabbb1로 실행해주고 다시 확인해 본다.

BoF로 인해 Num 변수의 값이 변조 되었지만 의도와 다르게 헥스값으로 31이 들어가져 있다.

 

이때는 python이나 perl을 이용하여 헥스값으로 전송하면 된다.

gef > r `python -c 'print "A"*10+"\x01"'`

num 변수에는 정확히 1로 변했으며 결국엔 쉘을 얻게 되었다.