Back to Blog
high SEVERITY5 min read

Proxy Endpoint File Upload: Arbitrary File Types Forwarded to

A proxy endpoint in the web server accepted file uploads via multipart form data and forwarded them directly to downstream AI services without validating file type, content, extension, or size. An attacker could upload executables, scripts, or polyglot files that would propagate to internal services. The fix adds strict MIME type and extension checks, plus a per-file size limit.

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published October 20, 2026•Reviewed October 20, 2026

Answer Summary

The proxy endpoint in the web server accepted multipart file uploads without validation of file type or content. An attacker could upload executables, scripts, or polyglot files through the `image` parameter, which would be forwarded directly to internal AI services. The fix validates both MIME type and file extension against a whitelist of genuine image formats and enforces a 10 MB per-file size limit. CWE-434 (Unrestricted Upload of File with Dangerous Type).

Vulnerability at a Glance

cweCWE-434
fixWhitelist allowed MIME types and file extensions; validate file size before accepting uploads
riskArbitrary executable and script files can be forwarded to internal AI services, bypassing intended security boundaries
languagePython
root causeFile uploads accepted via multipart form data without validation of type, extension, or content before forwarding to downstream services
vulnerabilityUnrestricted File Upload

How a Proxy Endpoint Became a Backdoor to Internal Services

A high-severity vulnerability in the web server's proxy endpoint allowed attackers to upload arbitrary files—executables, scripts, polyglot files—and forward them directly to internal AI services without any validation. The endpoint accepted multipart form data containing an image parameter, then immediately relayed it downstream without checking file type, extension, size, or content.

This created a trust boundary violation: the proxy served as a blind pass-through, assuming that all forwarded data was safe. In reality, an attacker could bypass external firewalls and upload a .exe, .sh, or specially crafted polyglot file into the internal network where AI services would receive and process it.

Affected Versions

Affected Not applicable (first-party code)
Fixed in Apply fix commit to your deployment
Ecosystem N/A
CVE / GHSA Not assigned
CWE CWE-434: Unrestricted Upload of File with Dangerous Type

The Vulnerability Explained

The proxy endpoint's request handler accepted file uploads via the image parameter in multipart form data:

data = request.form.get('data')
image = request.files.get('image')

# Forward the request to the target server
# (no validation between retrieval and forwarding)

Once retrieved, the file was forwarded to a downstream service with zero inspection. An attacker could exploit this in several ways:

Attack Scenario 1: Direct Executable Upload

An attacker crafts a POST request to the proxy endpoint:

POST /proxy/service HTTP/1.1
Content-Type: multipart/form-data; boundary=----Boundary

------Boundary
Content-Disposition: form-data; name="image"; filename="photo.exe"
Content-Type: application/octet-stream

[binary executable code]
------Boundary--

The endpoint receives the .exe file, sees no reason to reject it, and forwards it to an internal AI service. If that service has logic to execute or process files by extension, the attacker has achieved arbitrary code execution inside the network.

Attack Scenario 2: Polyglot File

An attacker embeds an executable payload inside a valid JPEG file, names it image.jpg, and sets the MIME type to image/jpeg. The endpoint validates neither content nor signature, so both the file extension and MIME type appear legitimate. Downstream services that parse "images" may execute the embedded payload if they use unsafe deserialization or dynamic execution.

Attack Scenario 3: Script Upload

An attacker uploads a Python script as script.py with MIME type text/plain. If the downstream AI service has a code execution path (e.g., a plugin loader or eval-based configuration parser), the uploaded script runs with the service's privileges.

Real-World Impact

  • Lateral movement: An attacker on the internet exploits this to drop malware into an internal AI service network, then pivots to other systems.
  • Data exfiltration: Malicious scripts uploaded to an AI service can access model weights, training data, or internal credentials stored on disk.
  • Service disruption: Polyglot files or resource bombs sent to multiple concurrent AI instances exhaust memory and CPU, causing denial of service.

The vulnerability is particularly dangerous because the proxy endpoint acts as a trust bridge between the public internet and internal services that may not expect hostile input.

The Fix

The fix adds four layers of validation before accepting an upload:

  1. MIME type whitelist: Only image/jpeg, image/png, image/gif, image/bmp, image/webp are allowed.
  2. File extension whitelist: Only .jpg, .jpeg, .png, .gif, .bmp, .webp suffixes are accepted (case-insensitive).
  3. Per-file size limit: Uploaded images must not exceed 10 MB.
  4. Early rejection: If validation fails, the endpoint returns a 400 error and does not forward the file.

Here's the updated code:

if image is not None:
    # Only allow genuine, size-limited images to be forwarded to
    # internal AI services so arbitrary executables/scripts cannot
    # be uploaded through this proxy.
    allowed_mimetypes = {'image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/webp'}
    allowed_extensions = ('.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp')
    filename = (image.filename or '').lower()
    if image.mimetype not in allowed_mimetypes or not filename.endswith(allowed_extensions):
        return jsonify({'error': 'unsupported image type'}), 400

    image.stream.seek(0, os.SEEK_END)
    image_size = image.stream.tell()
    image.stream.seek(0)
    if image_size > 10 * 1024 * 1024:
        return jsonify({'error': 'image too large'}), 400

Why both checks?

  • MIME type alone can be spoofed by setting the Content-Type header to image/jpeg even if the file is an executable.
  • Extension alone can be bypassed by renaming a .exe to .jpg.
  • Together, they make it significantly harder to craft a polyglot file that passes both checks. A file must be a genuine image and have a whitelisted extension.

Size validation protects against resource exhaustion: large files consume disk I/O, memory for buffering, and network bandwidth, which can degrade the AI service's availability.

Early rejection ensures that invalid files never reach downstream services, maintaining a clear security boundary.

How Orbis AppSec Detected This

Source: The image parameter from the multipart form data request (request.files.get('image')).

Sink: The downstream service call that forwards the file without intermediate validation.

Missing control: No validation of the file's MIME type, extension, content signature, or declared type against an allowed list before forwarding.

CWE: CWE-434 (Unrestricted Upload of File with Dangerous Type) — a file upload endpoint that accepts files without checking their type or content, allowing dangerous file types (executables, scripts) to be stored or propagated.

Fix: Add a whitelist of allowed MIME types and file extensions; validate the file size; reject any upload that does not match both the MIME type and extension whitelists.

Orbis AppSec detected this vulnerability automatically. Try Orbis AppSec on your repositories to find and fix issues like this.

Conclusion

Proxy endpoints are trust boundaries: they sit between untrusted clients and internal services that may not expect hostile input. Never blindly forward user-supplied files without validating their type, extension, and size. This fix demonstrates that a small amount of validation—whitelisting both MIME type and file extension, plus enforcing a size limit—can eliminate a high-severity vulnerability and prevent malicious code from reaching internal systems. Apply this pattern to any endpoint that accepts file uploads and forwards them downstream.

Prevention and further reading

Frequently Asked Questions

Why does the fix check both MIME type and file extension, instead of just one?

MIME type alone can be spoofed by an attacker; file extension alone can be bypassed. Checking both makes it harder to craft a polyglot file (e.g., an executable with a `.jpg` extension and a faked `image/jpeg` MIME type) that passes validation.

The fix limits images to 10 MB — is that the same limit as the per-file upload size mentioned in the vulnerability context?

No. The vulnerability context mentions `MAX_VIDEO_UPLOAD_BYTES` for a separate video upload endpoint with a per-file limit but no rate limiting on concurrent uploads. This fix applies to the proxy endpoint's image parameter only and sets its own 10 MB threshold to protect internal AI services from memory exhaustion.

If an attacker uploads a genuine `.jpg` file but embeds malicious JavaScript or executable code inside it, will this fix catch it?

No — this fix validates only file type and extension, not file content. A properly formed JPEG with embedded data will pass. This is acceptable for images forwarded to AI services (which parse images for content analysis), but defense-in-depth would add content scanning at the downstream service layer.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #26

Related Articles

high

CVE-2026-4800: lodash Template Imports Allow Code Execution

CVE-2026-4800 affects lodash's `_.template()` templating API, where untrusted input reaching the `imports` option can lead to arbitrary code execution. The fix was shipped as a dependency upgrade from lodash 4.17.21 to 4.18.1 in the project's lockfile, though the PR itself notes it was never verified against the actual code paths in this repository.

critical

proxy-addr 2.0.7 IP Spoofing: CVE-2026-90711 Trust Bypass

A critical vulnerability in proxy-addr 2.0.7 allowed attackers to spoof client IP addresses by manipulating X-Forwarded-For headers when the trust chain evaluation contained specific misconfigurations. The fix in version 2.0.8 hardens the trust evaluation logic to prevent IP address falsification in Express.js applications relying on this common middleware dependency.

high

TweenMax `_applyCycle` Prototype Pollution via vars.cycle Keys

A bundled copy of the TweenMax animation library copied attacker-influenceable `vars.cycle` property names straight onto a tween configuration object using an unguarded `for...in` loop, so a key named `__proto__`, `constructor`, or `prototype` was written through to the object's prototype chain. The fix adds an explicit key denylist to both copies of the `_applyCycle` helper so those three names are skipped during the merge. No CVE or GHSA is assigned; the issue is tracked as CWE-1321 (Improperl

high

picomatch 2.3.1 ReDoS: Extglob Pattern Catastrophic Backtracking

picomatch versions below 2.3.2, 3.0.2, or 4.0.4 contain a Regular Expression Denial of Service vulnerability in extglob pattern parsing. An attacker can cause catastrophic backtracking with patterns containing nested alternations and quantifiers, freezing any Node.js process that evaluates untrusted glob expressions.

critical

pet-window.js Dynamic Code Evaluation: CWE-94 Hardening via Number

The pet-window module constructed dynamic JavaScript by embedding raw configuration values into code strings. An attacker with local access could inject arbitrary JavaScript by modifying stored configuration. The fix replaces string interpolation with explicit Number() coercion and NaN validation for all numeric configuration parameters.

high

Electron 41.10.4 CORS Bypass: Custom Protocol Data Leak

CVE-2026-102675 exposes a critical flaw in how Electron's custom protocol handlers enforce CORS policies, allowing malicious web content to read responses from privileged origins. Upgrading Electron from 41.10.4 to 44.0.0 closes this cross-origin information disclosure vector by tightening origin validation in the protocol handler layer.