Educational: Unrestricted File Upload - BeaverHealth-Vulnerable-Web-App/BeaverHealth-Vulnerable-Web-App GitHub Wiki

Educational: Unrestricted File Upload

What is File Upload Functionality?

File upload functionality allows users to submit files to a web application, which are then stored on the server or linked to user accounts. In web systems, this is commonly used for profile pictures, documents, or data imports. In healthcare platforms like BeaverHealth, it's used for uploading medical records.

When implemented securely, file uploads enable productive features. When insecure, they expose the system to a wide range of client- and server-side attacks.

Why It Matters

Uploading files sounds benign, but it’s a high-risk area if not properly controlled. Attackers can use file upload vulnerabilities to:

  • Execute scripts on client machines (e.g., HTML/JavaScript)
  • Spread malware disguised as legitimate content
  • Store malicious payloads for future use
  • Launch phishing campaigns inside a trusted app

These risks are amplified by the trust users place in internal content. Users are more likely to open files if they come from a hospital system than an unknown source.

Risks of Unrestricted File Upload

Unrestricted file upload vulnerabilities occur when applications fail to validate:

  • The file's content-type (MIME type)
  • The file extension
  • The contents of the file
  • The storage location or accessibility of the file

Common consequences include:

  • Cross-site scripting (XSS): Malicious HTML/JS files executed in the browser
  • Client-side malware execution: Especially for formats like .html, .js, or .vbs
  • File inclusion/execution on server: In some tech stacks, uploaded scripts may be executed
  • Drive-by downloads: Malicious documents disguised as PDFs or other safe formats

Mitigation and Best Practices

To secure file upload functionality:

  • Restrict file types: Only allow safe formats like PDF, CSV, or images
  • Enforce MIME type checks: Use server-side file inspection
  • Rename files: Do not store files with original names or extensions
  • Store outside web root: Prevent direct access to uploads
  • Use secure download endpoints: Serve files with appropriate headers and sanitization
  • Avoid automatic execution: Never allow uploaded files to be rendered in a browser without validation

File Upload Vulnerability in BeaverHealth

BeaverHealth includes a file upload feature on the Add Medical Records page. When secure, it restricts MIME types to safe formats (e.g., .pdf, .csv). When the File Upload Vulnerability toggle is enabled:

  • MIME type and extension checks are disabled
  • Uploaded files are stored and served directly to users
  • There is no server-side enforcement of content type

This makes it possible to upload an HTML file that executes JavaScript when opened in a browser.

File Upload Exploitation in BeaverHealth

Uploading a Malicious File

When the vulnerability is enabled, a user with upload privileges can upload any file, including HTML with embedded JavaScript. The system does not validate the content or prevent uploads.

<script>alert('Malicious code executed!');</script>

This file would normally be blocked by MIME checks but passes through when the vulnerability is toggled on.

Triggering Execution on the Client Side

Once uploaded, the file appears in the Request Medical Records section. Any user who clicks the download link and opens the file in their browser will execute the JavaScript. Because the file is not sanitized or sandboxed, it behaves like a legitimate web page.

Why Download ≠ Safe

Many users assume that downloading a file from a trusted system is inherently safe. However, BeaverHealth demonstrates that downloaded files are only as safe as the upload filters that allowed them in.

Files served with incorrect MIME types or exposed from the web root can run unchecked. Without user education or client-side protection, malicious files uploaded internally can be extremely effective.

Examples of Good and Bad Practice

Bad Practice

PHP: No MIME type or extension check

move_uploaded_file($_FILES['file']['tmp_name'], 'uploads/' . $_FILES['file']['name']);

Node.js (Express): Static exposure of uploads

app.use('/uploads', express.static('uploads'));

Python (Flask): Weak extension check only

if file.filename.endswith(('.jpg', '.png')):
    file.save(os.path.join(app.config['UPLOAD_FOLDER'], file.filename))

Good Practice

PHP: Enforce MIME type and restrict uploads

$allowed = ['pdf', 'csv'];
$ext = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
if (!in_array($ext, $allowed) || mime_content_type($_FILES['file']['tmp_name']) !== 'application/pdf') {
    die("Invalid file");
}
move_uploaded_file($_FILES['file']['tmp_name'], '/safe/path/' . uniqid() . '.' . $ext);

Node.js: Rename files and store outside public directory

const multer = require('multer');
const storage = multer.diskStorage({
  destination: (req, file, cb) => cb(null, '/safe/path'),
  filename: (req, file, cb) => cb(null, Date.now() + '-' + file.originalname)
});
app.post('/upload', multer({ storage }).single('file'), (req, res) => { res.send('Uploaded'); });

Python (Flask): Secure filename + MIME validation

from werkzeug.utils import secure_filename
ALLOWED_EXTENSIONS = {'pdf', 'csv'}
def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
if file and allowed_file(file.filename) and file.mimetype == 'application/pdf':
    filename = secure_filename(file.filename)
    file.save(os.path.join('/safe/path', filename))

Additional Resources

⚠️ **GitHub.com Fallback** ⚠️