ZZCMS代码审计

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

本文包括zzcms代码工具、代码审计和漏洞复现。相应资源(代码审计工具以及源代码)放在文末。

一、环境配置

zzcms源码
phpstudy(php5.6.27+Apache+mysql)
Windows7 x64

将源码放在phpstudy的WWW目录下,配置好环境后访问www.zzcms.com/install进行安装,安装完成后访问如下:

二、代码审计工具-seay

Seay主要运行于Windows系统上。这款软件能够基本上覆盖常见PHP漏洞。另外,在功能上,它支持一键审计、代码调试、函数定位、插件扩展、自定义规则配置、代码高亮、编码调试转换、数据库执行监控等数十项强大功能。我们一般用seay源代码审计系统做一个初筛,可以很大程度上节约时间。

先用工具自动审计,结果如下:

三、重装漏洞

3.1 代码审计

先访问/install/index.php看看有没有重装漏洞,提示“安装向导已运行安装过,如需重安装,请删除 /install/install.lock 文件”

用phpstorm打开源码,用全局搜索上述文字,发现在step_1.php文件中,该文件的作用就是通过检测install.lock文件来判断我们的系统是否已经安装。

我们再回到index.php中,发现在index.php中包含$step变量,此处使用了一个三元操作符来进行判断,如果没有传入step参数的话,就会默认赋值为1。也就是说,在初始访问index.php的时候,会默认赋值$step=1,此处用的POST方法,说明可由客户端传入的。

往下看,这里使用了一个switch语句来判断$step,当$setp=1时,就会包含step_1.php文件

反过来想,如果$step不为1,就不会包含step_1.php文件,就不会通过检测install.lock文件判断系统是否已经安装,那么是不是就会直接进入系统重装步骤?

3.2 漏洞复现

行抓包修改请求方法为POST,并把$step的值改为2

四、SQL注入漏洞

4.1 代码审计

先看看系统的防护机制,将用户传递过来的内容都通过函数htmlspecialchars()和addslashes()处理。这里先解释下这两个函数的作用:

htmlspecialchars():把预定义的字符 "<" (小于)和 ">" (大于)转换为 HTML 实体,用于防止xss攻击。

addslashes() :返回在单引号(')、双引号(")、反斜杠(\)的字符前添加反斜杠的字符串,用于防止SQL注入。

这段代码把该过滤的都过滤了

接着往下看,这段if条件语句表示,在输入的url中查找"siteconfig.php"、"lable"、"template.php"文件,如果都不存在,则条件语句为ture,进入执行语句。对用户传过来的所有参数都进行stopsqlin()函数处理。

跟踪stopsqlin()函数,其中有一处关键字常量定义为stopword(),包含所有敏感字符。则stopsqlin()函数的功能就是判断用户所有传递过来的敏感字符。如果存在就会触发系统不予处理的提示,从而无法访问网站。

那么这段代码就这么天衣无缝吗?如果这段代码中if条件不成立呢?

如果if条件不成立,就无法执行stopsqlin()函数,就无法判断用户传递过来的内容中是否包含敏感字符,从而不就绕过这个防护机制了,来证实一下我们的猜想。

4.2 .漏洞验证

直接在url中输入敏感字符,报错。

怎么让这个if条件不成立呢,可以在参数中设置siteconfig.php,label,template.php进行绕过,例如令url的参数为a=select&b=siteconfig.php,这样就会使strpos($r_url, "siteconfig.php")=1,从而绕过stopsqlin()函数的过滤。

成功绕过

那么如何利用绕过机制进行更进一步的注入呢?

先用seay审计大概过一遍此cms中可能存在的漏洞,然后admin/ask.php的文件看到这个可能存在漏洞的点

这里直接把$_COOKIE["askbigclassid"]的值赋值给了sql语句,这段代码表示$_COOKIE["askbigclassid"]如果不为空,就会在数据库的表zzcms_askclass中查询相关字段。

那么是不是可以将我们所想查询的内容通过构造特殊的代码插入到这个变量中,绕过上述的防护机制,进行自己想查询的东西。

因为是白盒测试,所以我们可以先构造出payload在数据库中查询看返回的内容$sql = "select * from zzcms_askclass where parentid=-1 union select user(),2,3,4,5,6,7,8,9,10,11 order by xuhao asc";

在数据库中执行构造好的payload,发现可以成功执行,classid为root@localhost 

大概解释一下这里的执行过程,首先通过判断$_COOKIE["askbigclassid"]的值是否为空,如果不等于空 会执行$sql所定义的sql语句。query()这个函数的作用是执行当前的sql语句,然后通过fetch_array()这个函数将所查询的结果遍历出来返回给$row,$row取classid这个字段打印出来。

但是要怎么执行才能触发这个漏洞,追溯该语句,发现它是在add()方法中,那么现在我是不是只需要去调用这个函数,并且将$_COOKIE["askbigclassid"]这个地方的值赋值为查询的sql语句也就是$askbigclass = -1 union select user(),2,3,4,5,6,7,8,9,10,11这样就可以执行攻击

接下来,去找找在哪里会调用add()这个函数,发现当用户输入的url中如果含有参数do=add,则会调用此函数

调用add()函数后即可控制cookie的值,直接在调试中将url添加参数do=add,方便调试。

抓包,改包,增加cookie的参数

此时,可以看到phpstorm中的cookie参数

提示有非法字符,再用最上面提到的绕过机制

添加参数siteconfig.php

继续跟踪,成功注出用户

五、XSS漏洞

5.1 代码审计

漏洞的成因比较有趣,本来这个cms是定义了一个zc_check()函数,使用addslashess、htmlspecialchars对用户输入的参数进行了过滤,对SQL注入和XSS都起到了一定的防护作用。但同时又定义了另外一个函数stripfxg(),对参数做和zc_check()函数完全相反的操作,当两个函数同时调用时,就相当于没有做任何过滤。下面具体分析一下这两个函数。/inc/stopsqlin.php=>zc_check()

6-16行,定义了zc_check()函数7-13行,当传入参数不是数组时,判断是否开启gpc功能,(5.4版本之后默认是false),未开启则再使用trim()、htmlspecialchars()和addslashes()处理参数。14-16行,当传入数组时,则遍历键值对,使用zc_check()函数对数组的每个参数进行处理17-25行,使用zc_check()函数对$_COOKIE、$_GET、$POST传入的参数进行处理函数htmlspecialchars():把预定义的字符 "<" (小于)和 ">" (大于)转换为 HTML 实体,用于防止xss攻击。函数addslashes() :返回在单引号(')、双引号(")、反斜杠(\)的字符前添加反斜杠的字符串,用于防止SQL注入。

所以当调用该函数时,addslashes()和htmlspecialchars()对参数进行了处理,是可以防止大部分SQL注入和XSS的。

/inc/function.php=>stripfgx()

570-579行,定义了stropfxg()函数571行,stripslashes()删除了addslashes()函数添加的反斜杠572-574行,当$htmlspecialchars_decode参数的值为true时,htmlspecialchars_decode()将html实体编码转换为普通的字符

这个函数恰好将zc_check()函数做的转义去掉,所以,只要调用stripfxg()函数,并将第二个参数设置为true的,就相当于没有防护,都可能会存在SQL注入和XSS。(不是很懂为啥要写这么个函数)

全局搜索stripfxg()

看到调用该函数的地方还挺多的,下面以前台存储XSS漏洞为例

5.2 漏洞复现

/zt/show.php

第256行,使用了stripfxg()函数处理$content参数,并拼接赋值给$gsjj变量

接下来的主线任务查找$content参数。

由于$content参数拼接赋值给$gsjj变量,往后查找$gsjj函数,看是否输出了$gsjj变量

第374行,将$strout中的"{#gsjj}"字符串替换为了$gsjj的值,然后在385行将$strout输出

虽然没有直接输出$gsjj变量,但是通过$strout输出,那么,如果$content的值可控的话,就可以将$content的值构造为XSS语句,在385行进行输出,从而造成XSS漏洞。

shift+f快捷键查找$content变量,在show.php文件中未找到,那么应该是在include包含的文件中定义,于是一个个打开搜索$content变量

/zt/top.php

在49行找到$content变量的赋值,为数组$row["content"]的值

继续往上查找$row

第30行,使用fetch_array()查询数据库中的数据取一行赋值给数组$row

第17行,通过id查询zzcms_user表中的数据在数据库中查询,发现content字段

随后寻找哪里能够插入或修改zzcms_user表中的content字段,我们先在数据库中修改content的值为HTML编码过的弹窗代码“<img src=x οnerrοr=confirm(1)>”

进行Xdebug调试,当经过函数stripfxg()时,HTML编译后的代码又进行了解码

所以当通过$strout输出时,就触发了我们构造的xss代码,引发弹窗。

证实了我们的猜想,接下来的主线任务就是找到哪里能修改content字段的值,进而触发编辑好的payload,一般代码中想要修改一个值会有类似以下的字段

update zzcms_user set content = xxx(我们想要注入的值)where id =xxx

于是搜索"update zzcms_user",在/admin/usermodify.php文件中找到类似语句,但上面的代码都是包含在if($action=="modify")的条件下

而$action通过POST方式传入,可人为控制

我们直接在调试的配置中修改我们想访问的url

调试到第50行,有个checkadminisdo函数被卡住

追踪checkadminisdo函数,看来是没有操作权限了

进入用户注册页面,随便添加个用户test

查看数据库,已经写入用户,content为空

此时又有一个想法,既然能写入用户,那我能不能抓包修改用户的content值呢,从而注入xss语句

找到修改注册信息的地方,此时url为/user/manage.php

查看对应的manage.php文件,发现也有我们要找的语句

接下来进行一波调试,可以顺利到达修改用户的代码块

修改注册信息,抓包,添加content参数为弹窗代码

修改成功

查看数据库,发现xss代码成功写入,但是将特殊符号进行html实体转换

再访问/zt/show.php?id=2,成功复现xss

六、资源获取

扫描下方二维码关注微信公众号后台回复“代码审计”获取系统源码及工具。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值