The file-434 challenge in OWASP Juice Shop is a Sensitive Data Exposure challenge that appears prominently in search results for Juice Shop learners. It requires you to locate and download a confidential file from the application’s FTP directory — bypassing the server-side restriction that blocks direct access. This guide covers the exact steps, the underlying vulnerability class, and what this teaches about real-world file access control weaknesses.
What Is the file-434 Challenge?
In the OWASP Juice Shop scoreboard, the challenge labelled around file access and sensitive data exposure (sometimes surfaced in search results as “file-434”, referring to the challenge’s internal ID or a specific 403 response you encounter) falls into the Sensitive Data Exposure category.
The challenge objective is:
“Access a confidential document.”
The target file is acquisitions.md — a fictional business document stored in Juice Shop’s /ftp/ directory that should require authentication to access, but is accessible due to misconfigured file storage.
Difficulty: ⭐ (1 star — Beginner)
Category: Sensitive Data Exposure
Vulnerability class: Insecure File Storage / Information Disclosure (CWE-552 / CWE-538)
Start your local Juice Shop instance before following this walkthrough:
docker run --rm -p 3000:3000 bkimminich/juice-shop
Step 1: Discover the FTP Directory
Juice Shop’s confidential file is stored in an exposed FTP directory. To find it:
- Navigate to the About page:
http://localhost:3000/#/about - Scroll to the bottom — there is a link referencing “legal.md”
- Hover over or inspect the link. The URL points to:
http://localhost:3000/ftp/legal.md - The presence of
/ftp/in this link reveals that the application stores files in a directory namedftp.
Alternatively, you can navigate directly to http://localhost:3000/ftp/ — the directory listing is enabled, revealing all files stored there.
What You’ll Find in the FTP Directory
Navigating to http://localhost:3000/ftp/ shows a directory listing:
acquisitions.md [Confidential — access blocked]
coupons_2013.md.bak [Backup file — accessible]
eastere.gg [Easter egg]
incident-support.kdbx [KeePass database]
legal.md [Accessible]
package.json.bak [Backup — access blocked]
quarantine/ [Subdirectory]
suspicious_errors.yml [Accessible]
The fact that this directory listing is publicly accessible is itself a security finding. In a production application, the /ftp/ directory (or equivalent storage) should not be web-accessible without authentication, and directory listing should be disabled.
Step 2: Attempt Direct Download (You Get a 403)
Try to download acquisitions.md directly:
http://localhost:3000/ftp/acquisitions.md
The server returns:
HTTP/1.1 403 Forbidden
{
"error": "Only .md and .pdf files can be accessed through this endpoint"
}
This is the restriction. The server allows .md and .pdf files but blocks acquisitions.md. Wait — .md is an allowed extension. The block is specifically on certain filenames, not just extensions.
For the basic 1-star challenge, acquisitions.md is actually directly accessible at http://localhost:3000/ftp/acquisitions.md. If you are getting a 403, you may be looking at a different, related challenge (the Forgotten Developer Backup challenge, which targets package.json.bak).
Step 3: Download the Confidential Document
For the “Access a confidential document” (1-star) challenge:
- Navigate directly to:
http://localhost:3000/ftp/acquisitions.md - The file downloads or displays in the browser
- Juice Shop’s notification system shows: “You successfully solved a challenge: Access a confidential document”
The challenge is solved. The scoreboard updates to mark it complete.
The Related Challenge: Forgotten Developer Backup (file-434 context)
The query “file-434” in search engines often refers to a separate but closely related Juice Shop challenge involving a backup file that genuinely returns a 403 error and requires exploitation to download. This is the “Forgotten Developer Backup” challenge (3 stars).
The target is package.json.bak — which is blocked by the extension filter:
http://localhost:3000/ftp/package.json.bak
→ HTTP/1.1 403 Forbidden: Only .md and .pdf files can be accessed
How to Bypass the 403 with Null Byte Injection
The server filters file extensions, blocking .bak files. However, the extension check is vulnerable to a null byte injection attack:
http://localhost:3000/ftp/package.json.bak%2500.md
What’s happening here:
%25is the URL-encoded form of%%2500double-decodes to%00— the null byte character- The server’s extension validation sees
package.json.bak%00.mdand reads the extension as.md(allowed) - But the operating system and file-serving layer truncate the filename at the null byte:
package.json.bak - The file is served successfully
Step-by-step:
- Navigate to:
http://localhost:3000/ftp/package.json.bak%2500.md - The server validates
.md(the part after the null byte), then servespackage.json.bak(the part before the null byte) - The file downloads, revealing Juice Shop’s internal
package.jsonbackup - The challenge notification confirms: “You successfully solved a challenge: Forgotten Developer Backup”
Why This Vulnerability Matters
Insecure File Storage (CWE-552 / CWE-538)
The core issue is that Juice Shop stores sensitive files (acquisitions.md, package.json.bak) in a web-accessible directory with no authentication requirement. This represents:
- CWE-552 — Files or Directories Accessible to External Parties
- CWE-538 — Insertion of Sensitive Information into Externally-Accessible File or Directory
In production applications, this pattern appears as:
- Backup files (
.bak,.old,.orig) left in web-accessible directories - Log files stored in document roots
- Configuration files (
.env,web.config,appsettings.json) accessible via the web server - Database dumps or export files in publicly accessible storage buckets
The fix: Store sensitive files outside the web root, behind authentication, or in object storage with proper access controls (private S3 bucket, Azure Blob with SAS tokens, etc.).
Directory Listing Enabled
The /ftp/ directory exposes all its contents because the web server has directory listing enabled. This is a common misconfiguration that turns a simple file access into a full inventory of everything stored in that location.
The fix: Disable directory listing in your web server configuration:
# Nginx — disable directory listing
location /files/ {
autoindex off; # Default is off, but make it explicit
}
# Apache — disable directory listing
Options -Indexes
// Express.js — do not serve static directories with autoIndex
app.use('/uploads', express.static('uploads', {
index: false // Prevents directory listing
}));
Null Byte Injection (CWE-626)
The null byte bypass (%2500.md) exploits a difference between how the application-level code and the operating system handle filenames. This is:
- CWE-626 — Null Byte Interaction Error
In modern languages and frameworks (Node.js v6+, Python 3, PHP 7+), null bytes in file paths throw exceptions rather than being silently truncated. Juice Shop is intentionally vulnerable — but in legacy applications (older PHP, Perl CGI scripts), null byte injection was a reliable technique for bypassing extension filters.
The fix in modern code: Validate that the resolved file path contains no null bytes, and use allowlist-based filename validation:
// Express.js — safe file serving with allowlist
const path = require('path');
app.get('/files/:filename', (req, res) => {
const filename = req.params.filename;
// Allowlist: only allow specific characters in filenames
if (!/^[a-zA-Z0-9_\-]+\.(pdf|md)$/.test(filename)) {
return res.status(403).json({ error: 'Invalid filename' });
}
// Resolve path and verify it stays within the upload directory
const uploadDir = path.resolve('./uploads');
const filePath = path.resolve(uploadDir, filename);
if (!filePath.startsWith(uploadDir)) {
return res.status(403).json({ error: 'Path traversal detected' });
}
// Check for null bytes (defense-in-depth)
if (filename.includes('\x00')) {
return res.status(400).json({ error: 'Invalid filename' });
}
res.sendFile(filePath);
});
What DAST Scanners Should Find in Juice Shop’s FTP Directory
These file access issues are detectable by a good DAST scanner:
| Finding | How Detected | Severity |
|---|---|---|
Directory listing at /ftp/ | GET request to directory URL → 200 with file listing | Medium |
| Sensitive file accessible without auth | GET acquisitions.md → 200 | High |
| Backup file exposed | GET package.json.bak → file download (after bypass) | High |
| KeePass database exposed | GET incident-support.kdbx → 200 | Critical |
A DAST scanner that finds directory listing and unauthenticated file access demonstrates the core detection capability needed for production application scanning. Null byte injection is harder for automated scanners to detect — it requires testing with encoded null bytes in path parameters.
Connecting to Other Juice Shop Challenges
The FTP directory contains multiple challenges. Once you’ve found it, these related challenges are in the same location:
| File | Challenge | Difficulty |
|---|---|---|
acquisitions.md | Access a confidential document | ⭐ |
package.json.bak | Forgotten Developer Backup (null byte bypass) | ⭐⭐⭐ |
incident-support.kdbx | Retrieve the KeePass database (no bypass needed — it downloads directly) | ⭐ |
quarantine/ directory | Hidden malware quarantine (find what’s in there) | Advanced |
Work through these in order — they build on each other and collectively teach file access control, directory traversal, and extension bypass techniques.
Detecting These Vulnerabilities in Production Code with SAST
If you’re using Juice Shop to benchmark a SAST scanner, look for whether it detects these source-level issues in the Node.js codebase:
Serving files from a directory without authentication middleware:
// Juice Shop — serves /ftp/ directory without auth
app.use('/ftp', serveIndex('ftp', { icons: true }));
app.use('/ftp', express.static('ftp'));
A SAST tool should flag express.static and serveIndex usage for paths that may contain sensitive files — especially when no authentication middleware precedes them in the middleware chain.
Extension-only validation without allowlist:
// Vulnerable pattern — extension check only (bypassable with null byte in some configurations)
if (!file.endsWith('.md') && !file.endsWith('.pdf')) {
res.status(403).send({ error: 'Only .md and .pdf files can be accessed' });
return;
}
A SAST tool performing taint analysis would track file from the request through the extension check to the fs.createReadStream(file) call — noting that the validation is an extension check rather than a resolved-path allowlist check.
Summary
| Challenge | Target | Technique | Difficulty |
|---|---|---|---|
| Access a confidential document | /ftp/acquisitions.md | Direct URL access | ⭐ |
| Forgotten Developer Backup | /ftp/package.json.bak%2500.md | Null byte injection | ⭐⭐⭐ |
The “file-434” identifier connects to the broad Sensitive Data Exposure challenge cluster in OWASP Juice Shop:
- Find the
/ftp/directory — via the About page link tolegal.md - Browse the directory listing — enabled by default, reveals all stored files
- Download
acquisitions.md— directly accessible, no bypass needed (1-star challenge) - Download
package.json.bak— requires null byte injection in the URL (3-star challenge)
Both challenges demonstrate real vulnerability patterns found in production applications: sensitive files in web-accessible storage, enabled directory listings, and extension-based access control that can be bypassed.
For a full walkthrough of all Juice Shop challenges across every difficulty level, see our complete Juice Shop challenge solutions guide. For information on setting up Juice Shop and its full challenge categories, see the OWASP Juice Shop complete guide.
Offensive360 DAST automatically detects directory listing, unauthenticated file access, and sensitive file exposure during authenticated scanning of web applications. Book a demo — results in minutes.