미역줄기의 이모저모
[ Dreamhack ] Return Address Overwrite 본문
시간을 두어 아래 코드의 취약점을 스스로 찾아보기!!!
// Name: rao.c
// Compile: gcc -o rao rao.c -fno-stack-protector -no-pie
#include <stdio.h>
#include <unistd.h>
void init() {
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
}
void get_shell() {
char *cmd = "/bin/sh";
char *args[] = {cmd, NULL};
execve(cmd, args, NULL);
}
int main() {
char buf[0x28];
init();
printf("Input: ");
scanf("%s", buf);
return 0;
}
지금부터는 내가 아는걸 토대로 분석하는 것이기 때문에 엉성한 부분이 많을 것이다!!
scanf("%s", buf); 부분을 취약점으로 생각했다. 이유는 scanf는 입력받는 데이터의 크기를 체크하지 않기 때문이다! 이런 점에서 버퍼 오버플로우가 발생할 수 있을 것이다..
buf의 크기는 0x28 ! 그렇다면 내가 입력할 때 28이상의 입력을 주면 공격 성공이지 않을까,,? 하지만 그전에 get_shell() 함수를 생각해보면.. 이걸 어떻게 해내야 bash를 딸 수 있는 것 같은데.. 버퍼 오버플로우로 넘치게 한 다음에 get_shell()함수의 주소? 아니면 get_shell()함수 안의 execve를 실행시킬 수 있는 주소를 넣으면 될 것 같다. 그렇게 한다면 main에는 없지만 실행시켜야 하는 get_shell()함수를 호출해서 bash를 딸 수 있지 않을까
짧긴 하지만 여기까지가 나의 생각이다 ㄱ-..
여기서부터 드림핵에 나온 분석!!
프로그램의 취약점은 scanf(“%s”, buf) 에 있는게 맞다고 한다! scanf함수의 포맷 스트림 중 하나인 %s는 문자열을 입력받을 때 사용하는 것으로 입력의 길이를 제한하지 않는다고 한다. 그래서 버퍼의 크기보다 큰 데이터를 입력하면 오버플로우가 발생 할 수 있다. 그래서 %s 대신 %[n]s의 형태로 사용을 권장한다.
위 코드에서는 크기가 0x28인 버퍼에 입력을 받으므로 입력을 길게 준다면 오버플로우를 발생시켜서 main 함수의 반환 주소를 덮을 수 있을 것이다.
한 번 실행해 보면
입력값을 크게 주니까 Segmentation fault (core dumped)라고 나온다. 이는 프로그램이 잘못된 메모리 주소에 접근했다는 의미이자 프로그램에 버그가 발생했다는 신호이다.
뒤의 core dumped는 코어파일을 생성했다는 것이다. 프로그램이 비정상 종료됐을 때, 디버깅을 돕기 위해 운영체제가 생성해 주는 것이다.
코어 파일 분석
코어파일을 분석하며 입력이 스택에 어떻게 저장되었는지 살펴보고, 셸을 얻을 시나리오를 생각한다.
우선 코어 파일을 열어보자
r로 c파일 실행안 다음에 A을 마구마구 입력한다. 그러면 context의 DISASM에 스택 최상단에 저장된 값이 입력값의 일부인 0x414141414141이라는 것을 알 수 있다. 이건 실행 가능한 메모리의 주소가 아니므로 segmentation fault가 발생한 것이다. 이 값이 내가 원하는 주소가 되도록 적절한 입력을 주면, main 함수에서 반환될 때, 원하는 코드가 실행되도록 조작할 수 있다.
스택 프레임 구조 파악
스택 버퍼에 오버플로우를 발생시켜서 반환주소(ret)를 덮으려면, 해당 버퍼가 스택 프레임의 어디에 위치하는지 알아야한다.
main의 어셈블리 코드를 살펴보자.
rbp-0x30에 buf가 위치한다는 것을 할 수 있다. scanf("%s",(rbp-0x30)); 이런느낌.
오버플로우를 발생시킬 버퍼는 rbp-0x30에 위치한다. 위 그림은 스택 프레임 구조이다.
rbp에는 SFP가 저장되고 rbp+0x8에는 반환 주소가 저장된다.
입력할 버퍼와 반환 주소 사이에 0x38 만큼의 거리가 있으므로 그만큼의 더미값을 채우고 실행하고자 하는 코드의 주소를 입력하면 exploit 성공
내가 실행하길 원하는건 get_shell()이다. 이 함수의 주소로 main함수의 반환 주소르 덮자. get_shell()의 주소를 찾기위해 gdb를 사용한다.
get_shell()의 주소는 0x4011dd 구나!!
그럼이제 payload는 38크기의 더미값+get_shell()의 주소 0x4011dd 이다!
이때 get_shell()의 주소를 \xbb\x11\x40\x00\x00\x00\x00\x00 이라고 적어햐 한다,
payload: (python3 -c "import sys;sys.stdout.buffer.write(b'A'*0x38 + b'\xbb\x11\x40\x00\x00\x00\x00\x00')";cat)| ./rao
했는데 난 안 된다 ..
이유를 반드시 찾아낼 것이다................
'Hacking > DreamHack 시스템 해킹 강의' 카테고리의 다른 글
[ Dreamhack ] NX & ASLR (0) | 2024.02.08 |
---|---|
[ Dreamhack ] Stack Canary (1) | 2024.02.08 |
[ Dreamhack ] Tool: gdb (1) | 2024.02.05 |
[ Dreamhack ] 어셈블리어와 x86-64(2) (1) | 2024.02.05 |
[ Dreamhack ] 어셈블리어와 x86-64(1) (0) | 2024.02.05 |