Unparsed Raw Web Content Delivery

Description

Unparsed Raw Web Content Delivery is a vulnerability where a product stores raw content or supporting code beneath the web document root with an extension that the server does not specifically handle. When code resides in files with extensions like ".inc", ".bak", ".conf", or ".pl" that lack server handlers, the server typically sends file contents directly to requesters without expected preprocessing. This exposes sensitive data such as database credentials, API keys, and internal logic, potentially compromising applications and associated components.

Risk

Unparsed content delivery leads to critical information disclosure. Include files (.inc) containing database credentials are exposed when requested directly. Backup files (.bak, .old, .swp) may contain previous versions with vulnerabilities or secrets. Configuration files without handlers expose server settings and credentials. Source code disclosure reveals business logic, algorithms, and security mechanisms. Attackers use this information to craft targeted attacks against the application and its infrastructure. The vulnerability is easily discovered through automated scanning.

Solution

Perform type checks before interpreting files and configure handlers for all file types that contain executable code. Do not store sensitive information in files with extensions that may be misinterpreted. Move include files and configuration files outside the web root. Use proper extensions (.php, .asp, .jsp) for files containing code so they are processed by the appropriate handler. Configure the web server to deny access to sensitive file extensions. Implement defense in depth by also encrypting sensitive configuration data.

Common Consequences

ImpactDetails
ConfidentialityScope: Confidentiality

Read Application Data - Attackers can access sensitive application information stored in unparsed files, including database credentials, API keys, and application logic.

Example Code

Vulnerable Code

<!-- Vulnerable: Include file stored with .inc extension -->
<!-- File: /var/www/html/includes/database.inc -->
<?php
$dbHost = 'localhost';
$dbName = 'usersDB';
$dbUser = 'admin';
$dbPassword = 'skjdh#67nkjd3$3$';

function connectToDB() {
    global $dbHost, $dbName, $dbUser, $dbPassword;
    return new PDO("mysql:host=$dbHost;dbname=$dbName", $dbUser, $dbPassword);
}
?>

<!-- File: /var/www/html/login.php -->
<?php
include('includes/database.inc');  // Works internally
$db = connectToDB();
// ...
?>

<!-- Vulnerability: Direct request to /includes/database.inc
     returns raw PHP source code including credentials -->
# Vulnerable: Apache configuration without .inc handler
# File: /etc/apache2/sites-enabled/default.conf

<VirtualHost *:80>
    DocumentRoot /var/www/html

    # Vulnerable: No handler for .inc files
    # They are served as plain text

    # Vulnerable: No access restriction on include directories
    <Directory /var/www/html>
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted
    </Directory>
</VirtualHost>
# Vulnerable: Perl script with .pl extension but no CGI handler
# File: /var/www/html/admin/config.pl

#!/usr/bin/perl
# Database configuration
$db_host = "db.internal.company.com";
$db_user = "webapp";
$db_pass = "Sup3rS3cr3tP@ss!";
$db_name = "production";

# API Keys
$stripe_key = "sk_live_xxxxxxxxxxxx";
$aws_secret = "AWS_SECRET_KEY_HERE";

# If server doesn't have CGI configured for .pl files,
# requesting /admin/config.pl returns this source code
# Vulnerable: Python config file in web root
# File: /var/www/html/app/settings.py

SECRET_KEY = 'django-insecure-xxxxxxxxxxxxxxxxx'
DEBUG = True

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'production_db',
        'USER': 'dbadmin',
        'PASSWORD': 'ProductionPassword123!',
        'HOST': 'db.example.com',
    }
}

# AWS credentials
AWS_ACCESS_KEY_ID = 'AKIAIOSFODNN7EXAMPLE'
AWS_SECRET_ACCESS_KEY = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'

# If Python files aren't handled by WSGI, source is exposed

Fixed Code

<!-- Fixed: Move sensitive files outside web root -->
<!-- File: /var/www/config/database.php (OUTSIDE web root) -->
<?php
return [
    'host' => getenv('DB_HOST') ?: 'localhost',
    'name' => getenv('DB_NAME') ?: 'usersDB',
    'user' => getenv('DB_USER'),
    'password' => getenv('DB_PASSWORD'),
];
?>

<!-- File: /var/www/html/login.php -->
<?php
// Fixed: Include from outside web root
$dbConfig = require('/var/www/config/database.php');

try {
    $db = new PDO(
        "mysql:host={$dbConfig['host']};dbname={$dbConfig['name']}",
        $dbConfig['user'],
        $dbConfig['password']
    );
} catch (PDOException $e) {
    // Fixed: Don't expose connection details in error
    error_log($e->getMessage());
    die("Database connection failed");
}
?>
# Fixed: Apache configuration with proper restrictions
# File: /etc/apache2/sites-enabled/default.conf

<VirtualHost *:80>
    DocumentRoot /var/www/html

    # Fixed: Parse .inc files as PHP
    <FilesMatch "\.inc$">
        SetHandler application/x-httpd-php
    </FilesMatch>

    # Fixed: Deny access to sensitive file types entirely
    <FilesMatch "\.(inc|bak|old|swp|conf|config|ini|log|sql)$">
        Require all denied
    </FilesMatch>

    # Fixed: Deny access to backup files
    <FilesMatch "~$">
        Require all denied
    </FilesMatch>

    # Fixed: Deny access to hidden files
    <FilesMatch "^\.">
        Require all denied
    </FilesMatch>

    # Fixed: Disable directory listing
    <Directory /var/www/html>
        Options -Indexes +FollowSymLinks
        AllowOverride None
        Require all granted
    </Directory>

    # Fixed: Protect specific directories
    <Directory /var/www/html/includes>
        Require all denied
    </Directory>
</VirtualHost>
# Fixed: Nginx configuration with file type restrictions
server {
    listen 80;
    root /var/www/html;

    # Fixed: Deny access to sensitive files
    location ~* \.(inc|bak|old|swp|conf|config|ini|log|sql|env)$ {
        deny all;
        return 404;
    }

    # Fixed: Deny access to hidden files
    location ~ /\. {
        deny all;
        return 404;
    }

    # Fixed: Deny access to backup files (ending with ~)
    location ~ ~$ {
        deny all;
        return 404;
    }

    # Fixed: Protect include directories
    location ^~ /includes/ {
        deny all;
        return 404;
    }

    # Fixed: Process PHP files through PHP-FPM
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}
# Fixed: Configuration through environment variables
# File: /var/www/app/settings.py (with no secrets)

import os
from pathlib import Path

# Fixed: Secret from environment, not hardcoded
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
if not SECRET_KEY:
    raise ValueError("DJANGO_SECRET_KEY environment variable not set")

DEBUG = os.environ.get('DEBUG', 'False').lower() == 'true'

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': os.environ.get('DB_NAME'),
        'USER': os.environ.get('DB_USER'),
        'PASSWORD': os.environ.get('DB_PASSWORD'),
        'HOST': os.environ.get('DB_HOST'),
    }
}

# Fixed: AWS credentials from environment
AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY')

# Even if this file is exposed, no secrets are revealed
# Fixed: .htaccess file for additional protection
# File: /var/www/html/.htaccess

# Deny access to all .inc files
<Files ~ "\.inc$">
    Order allow,deny
    Deny from all
</Files>

# Deny access to common backup extensions
<FilesMatch "\.(bak|backup|old|orig|swp|tmp)$">
    Order allow,deny
    Deny from all
</FilesMatch>

# Deny access to configuration files
<FilesMatch "\.(conf|config|ini|env|yml|yaml|json)$">
    Order allow,deny
    Deny from all
</FilesMatch>

CVE Examples

  • CVE-2002-1886 — ".inc" file stored under web root returned unparsed, exposing source code.
  • CVE-2002-2065 — ".inc" file under web root returned unparsed.
  • CVE-2005-2029 — ".inc" file under web root returned unparsed.
  • CVE-2001-0330 — Direct ".pl" file request left unparsed, exposing Perl source.
  • CVE-2007-3365 — Uppercase file extensions caused web server to return script source code instead of executing.

References

  1. MITRE Corporation. "CWE-433: Unparsed Raw Web Content Delivery." https://cwe.mitre.org/data/definitions/433.html
  2. OWASP. "Source Code Disclosure." https://owasp.org/www-community/attacks/Source_Code_Disclosure