最近看到关注的博客里面,有一篇师傅拿cve的文章,是有关于海洋cms的后台getshell,只给出了exp,但是并没有详细的分析流程,好奇心的驱使下,开始了对这个cve的跟踪分析。
漏洞触发点、触发payload
触发条件以及payload作者给出了基本的说明,原博客地址
可以看到是在后台编辑video的时候,在图片pic处注入代码
{if:1)$GLOBALS['_G'.'ET'][a]($GLOBALS['_G'.'ET'][b]);//}{end if}
利用是:/details/index.php?1.html&m=admin&a=assert&b=phpinfo()
初步观察
通过简单的观察,发现注入的代码是类似于渲染模板语法的代码,所以可以推测出来代码在最后执行的时候,一定经过了某种模板引擎的渲染,导致了任意代码执行。
还记得吴翰清在白帽子讲web安全里面讲过,如何追踪溯源一个漏洞,就是跟踪数据流。
我们现在已经知道了触发该漏洞的输入污点数据,下面我们开始一步步解析污点数据的执行过程:
1. 确定输入参数
首先我们应该确定一下我们输入的变量名,这个时候我们可以使用phpstorm的全局搜索,搜索结果为:
通过和图片文字的对比我们发现了我们输入代码的模板文件为/admin/template/admin_video_edit.htm
,然后我们可以通过表单的提交地址,发现对应的控制器,从而跟踪代码的处理过程。
这里我们找到的输入变量名为:v_pic
2. 找到对应的逻辑代码
这里我们首先可以通过模板文件,找到form表单执行的get参数action=save&acttype=edit
,但是并不能直接找到该变量的处理流程,这个时候我们就再次需要phpstorm的全局搜索。
这里首先要有一点开发技巧,htm文件在开发中就只是模板文件,需要有控制器来渲染,渲染一般都是有include,render,render_template等等代码关键词,所以我们可以通过这个来确定控制器。
通过全局搜索,发现只有一个地方引用了这个文件,所以我们就唯一确定了控制器的位置。
3. 通过传入的get参数确定程序分支
上面我们在表单中找到了传入的get参数为action=save&acttype=edit
,然后可以在代码中,找到相应的代码分支。
4. 代码分析
通过浏览代码,发现处理的数据比较多,我们只需要跟踪v_pic
这个变量
我们跟进cn_substrR
这个函数:
注释中讲的很明白,是按照中文单字节截取的方式,然后对数据进行了addslashes
,这个函数会在入库的时候进行转义,很大程度上防止了sql注入。
然后我们继续进入二级代码处理acttype=edit
这里就开始数据入库了,可以看到:
到这里我们的数据,已经经过处理进入了数据库,我们可以梳理一下数据被过滤的流程:
5. 反向追踪
上面的数据,经过处理已经进入数据库,这个时候我们的分析流程就断了,这个时候我们可以通过利用的页面,来反向推理出代码执行的位置。
利用方法为:
/details/index.php?1.html&m=admin&a=assert&b=phpinfo()
我们到details/index.php
中查看处理代码:
我们可以看到这里有一点有关路由的处理,首先将query_string的最前面的文件名称去掉了,然后将字符串强转int,所以上面的路由经过的处理为:
/details/index.php?1.html&m=admin&a=assert&b=phpinfo()
=>
1.html&m=admin&a=assert&b=phpinfo()
=>
1
所以我们得到的id为1.然后跟进echoContent函数:
在开头可以看到第一步是从数据库中按照id,取出有关数据,我们关注v_pic
的处理:
根据if条件,我们进入的分支是:
$content=str_replace("{playpage:pic}",'/'.$GLOBALS['cfg_cmspath'].ltrim($v_pic,'/'),$content);
这里并没有对数据进行什么处理,只是对数据替换拼接进了content变量中。
此时content变量中有一段污点数据为:
/xxxx{if:1)$GLOBALS['_G'.'ET'][a]($GLOBALS['_G'.'ET'][b]);//}{end if}
这并不能造成代码执行,所以我们还是需要找到解析if语句的地方,继续往下翻:
在下面,所有数据拼接结束以后,我们看到了一个很关键的函数parseIf
;
跟进查看具体实现:
为了测试方便,我们将这段代码单独拿出来,单独测试:
这里我们看到了整个流程中的关键过滤点,这里采用了黑名单,替换为特殊字符的过滤方式:
str_ireplace(array('unlink','opendir','mysqli_','mysql_','socket_','curl_','base64_','putenv','popen(','phpinfo','pfsockopen','proc_','preg_','_GET','_POST','_COOKIE','_REQUEST','_SESSION','_SERVER','assert','eval(','file_','passthru(','exec(','system(','shell_'), '@.@', $v)
这种过滤方式有很明显的问题,第一没有过滤$GLOBALS,第二字符串拼接就可以绕过黑名单。
所以payload中为什么这么写,也就很清楚了,这里采用的是
$_GET[a]($_GET[b])
这种代码执行方式,只要简单的采用global和字符串拼接就能绕过过滤代码,导致了eval代码执行。
6. 总结测试
我们最后总结以下数据处理的流程:
我们把提取的有关代码,放到服务器上实际执行一下:
可以看到代码经过处理以后,还是会执行,到这里我们的分析流程就结束了。
总结
这个漏洞实际分析并不是很难,但是还是会用到一些开发的常识,还有追踪污点数据的小技巧,很适合新手学习。