最近在学习中学到了不少审计时的小技巧,都比较简单,但有一些在代码中比较容易出现的错误,这里做了一下总结分享,有错误希望师傅们斧正,一起交流学习。
这个小技巧是从这里看到的:关于CMSMS中SQL注入漏洞的复现与分析与利用
当 count
出现在 for
循环中,count
每次都会计算,如果这时候数组发生了变化,就会有一些差异。
例子:
<?php
$a = array("a","b","c");
for ($i=0; $i < count($a); $i++) {
if(intval($a[$i])<1){
unset($a[$i]);
}
}
var_dump($a);
这里原本的目的是将数组中不是数字的值都删掉,我们来分析一下流程:
count($a)
,此时为 3
$i=0
,判断 $a[0]
是否小于 1
,返回 True
,unset
掉,此时 $a
为 ["b","c"]
$i=1
,执行 count($a)
,此时为 2
,因为 1<2
,进入循环,判断 $a[1]
,也就是 b
是否小于 1
,返回 True
,unset
掉,此时数组剩一个 $a[2]
,也就是 c
$i=2
,执行 count($a)
,此时为 1
,因为 2>1
,进不了循环了,自然就不能到 if
和 unset
这块。(可能讲起来还是有点绕,可以自己试试就明白了)
比如下次在审计中,可以留意 for
循环中是否是用了 count
这种重新计算的函数,并且在循环内操作了这个变量。
这是在翻 dedecms
往日漏洞时发现的,如果一个 CMS
注册全局变量时,用了这样的操作:
foreach($_REQUEST as $_k=>$_v)
{
if( strlen($_k)>0 && preg_match('/^(cfg_|GLOBALS)/',$_k) && !isset($_COOKIE[$_k]) )
{
exit('Request var not allow!');
}
}
foreach(Array('_GET','_POST','_COOKIE') as $_request)
{
foreach($$_request as $_k => $_v) ${$_k} = $_v;
}
看起来这样好像控制了不能改 GLOBALS
,但是这里却忽略了个很重要的问题,就是没过滤 _POST
(最新版本的 dedecms
是过滤了 _POST
和 _COOKIE
的)
这里怎么构造呢?我们可以控制 get
成这样的值:_POST[GLOBALS][xixi] = 1;
当执行检测时,由于 $key
是 _POST
,所以不会被检测,此时 post
参数是空,然后执行到下面。
执行 foreach
时,第一次循环,此时 $_request
是 _GET
,然后 $_k
是 _POST
,所以是 ${_POST}['GLOBALS']['xixi'] = 1;
然后执行到第二次 foreach
,此时 $_request
为 _POST
,$_k
就是数组的键,自然就是 GLOBALS
,所以执行的就是 ${GLOBALS}['xixi']=1
这样就覆盖了 $GLOBALS
了。。
bbcode
,就是一种用短标签代替 html
标签的方法,一般是通过正则匹配替换的,通常出现在富文本编辑器中。
这个小技巧是从 Mybb 18.20 From Stored XSS to RCE 分析 这个漏洞中学到的。
直接说技巧吧,当富文本编辑器中的双引号被转义了,可以考虑在标签中再嵌套标签。说起来比较难理解,直接上代码吧:
<?php
$video_tag = '<iframe url="\1"></iframe>';
$url_tag = '<a href="\1">\2</a>';
$content = "[video]https://youku.com[/video]";
$content.= "[url=https://baidu.com]百度[/url]";
$content = preg_replace("/\[video\](.*?)\[\/video\]/",$video_tag,$content);
$content = preg_replace("/\[url=(.*?)\](.*?)\[\/url\]/",$url_tag,$content);
echo $content;
初衷当然就是利用一些正则替换代替标签,但是如果当没做好过滤时,就可能发生这样的事情,如果我们的 $content
变形一下:
[video][url=onload=alert(1);//]xixi[/url][/video]
看看这时候:
a
标签中的双引号闭合了 iframe
标签的,最后的双引号也可以用 //
注释。。
文件上传中的一些小技巧。
黑名单的话,什么 php3 4 5 6 7
这些就不提了。。
首先是前几天 lz1y
表哥的文中:代码审计 xxxdisk前台Getshell 也有提到的文件名为 a.php:$data
时,可以绕过黑名单检测。
我记得之前有技巧是文件名为 a.php/.
也可以绕过的,但是测试了一下发现又不太行。。
也可以上传一个文件名是 a.php:b.jpg
,这时候文件内容会变成空,如果有文件操作的函数就可以用上这个技巧。
这里补一张图:
当然如果实在想上传一个 .htaccess
也可以,如果他有 getimagesize
也不怕。
前几天 p牛就发过一篇博客:imagemagick邂逅getimagesize的那点事儿
因为 XBM
格式只要有
#define test_width 16
#define test_height 7
这两行就可以,又因为 #
在 .htaccess
里是注释,所以直接加在前两行都没问题。
这里就再推一篇文:Bypass file upload filter with .htaccess
在 PHPCMSv9逻辑漏洞导致备份文件名可猜测 这篇文章中提到了大致,在 Windows
下,php
操作文件时可以使用 <<
通配符。
其实一个 <
也可以,但是测试时很奇怪,这里就不演示了。
在 Windows
中还有一种表示短文件名的方法,虽然不够上面的方法灵活,但不算没用。。就是当文件名超过 6
位时,可以用 ~1
表示后面的字符串,比如我们的文件名叫 abcdefghijk.txt
,短文件名表示成:abcdef~1.txt
,如果这时候还有个叫 abcdefhhhh.txt
的,就可以把 1
改成 2
:abcdef~2.txt
最近出了一篇文章,Abusing PHP query string parser to bypass IDS, IPS, and WAF。
里面提到了 parse_str
函数绕过 WAF
,里面提到的很有趣,总结一下。
举个例子,当使用 parse_str
时,如果我们的字符串是 abc.123=1
,或者 abc 123=1
再或者 abc[123=1
,最终都会被解析成 abc_123
。
所以如果是先检测 $_GET
然后再调用 parse_str
,就会可能带来一些差异,导致绕过。
稍微修改了一下文中的 Fuzz
脚本:
<?php
foreach(
[
"{chr}foo_bar",
"foo{chr}bar",
"foo_bar{chr}"
] as $k => $arg) {
for($i=0;$i<=255;$i++) {
$o= Array();
parse_str(str_replace("{chr}",chr($i),$arg)."=bla",$o);
if(isset($o["foo_bar"])) {
echo $arg." -> ".bin2hex(chr($i))." (".chr($i).")\n";
echo "<br/>";
}
}
}