php黑魔法
零、前言
php的很多特性再带来编程方面的同时也会带来很多安全隐患,其中一些漏洞在CTF中经常出现,本文大部分示例也是取自CTF题目。这里首先说一下==和===区别。==是比较运算,它不会去检查条件式的表达式的类型,===是恒等,它会检查查表达式的值与类型是否相等,NULL,0,”0″,array()使用==和false比较时,都是会返回true的,而使用===却不会。
注:本文部分内容及代码并非原创,而是从网上搜集而得。
一、数组
0x01 数组的hash计算
很多时候,PHP数组都发挥了至关重要的作用,比如下面代码首先判断name和password是否一致,一致的话提示'Your password can not be your name.',不一致的话无法通过第二个if判断,但是php对数组进行hash计算都会得出null的空值。比如下面代码,当我们直接进行数组的MD5运算时会提示警告警告信息说MD5()函数需要一个string类型的参数
我们加几条调试信息,并将error_reporting(0)取消注释给username和password传入数组,
http://192.168.219.3/CTF/index.php?username[]=1&password[]=2
其运行结果为
因为usrename和password是两个不同的数组,通过第一个判断,又因为他们都是数组,MD5结果都是null,通过第二个判断,输出flag。
0x02 strcmp()
Strcmp()函数的比较作用这里,使用strcmp去比较password和flag,如果==0的话,就给出flag,但是strcmp比较,如果相等才会返回0,如果不相等的 话,要么大于0,要么小于0,但是strcmp只会处理字符串参数,如果给个数组的话呢,就会返回NULL,而判断使用的是==,NULL==0是 bool(true)的,所以运行结果
0x03 ereg()和strpos()数组 ereg是处理字符串的,所以,按照原理,我们将password构造一个arr[],传入之后,ereg是返回NULL的,===判断NULL和 FALSE,是不相等的,所以可以进入第二个判断,而strpos处理数组,也是返回NULL,注意这里的是!==,NULL!==FALSE,条件成 立,拿到flag
二、数字的比较
0x01 十六进制与数字这里,它不让输入1到9的数字,但是后面却让比较一串数字,平常的方法肯定就不能行了,大家都知道计算机中的进制转换,当然也是可以拿来比较的,0x开 头则表示16进制,将这串数字转换成16进制之后发现,是deadc0de,在开头加上0x,代表这个是16进制的数字,然后再和十进制的 3735929054比较,答案当然是相同的,返回true拿到flag。
0x02数字运算(一)这里的意思就是:接收post参数password的值,必须满足12位以上字符,必须是非空格非TAB之外的内容,然后就是你的password要有大小写数字,字符内容,而且匹配到的次数要大于6次,最后还有一个考点:最后的答案就是:42.00e+00000000000
当然也可以这样:420.000000000e-1
0x03数字运算(二)代码中先将变量放到is_numeric函数中判断,如果数字或数字字符串则返回true,否咋返回false。然后一个判断,如果temp大于1336则显示flag。这里用到了PHP弱类型的一个特性,当一个整形和一个其他类型行比较的时候,会先把其他类型intval再比。如果输入一个1337a这样的字符串,在is_numeric中返回true,然后在比较时被转换成数字1337,这样就绕过判断输出flag。
0x04 MD5的巧合(一)输入password,要求其MD5值为0,这里有两个字符串,经过MD5运算后,为0e******的形式,其结果为0*10的n次方,结果还是零
240610708
QNKCDZO
0x05 MD5的巧合(二)这里的sql语句是其中md5运算函数有一个true参数,它的作用是将md5后的hex转换成字符串,这里如果后的字符串又单引号之类的字符就可以注入了。比如字符串:ffifdyop
md5后,276f722736c95d99e921722cf9ed621c
将其转成字符串的话就是
可以看到起字符串类似于 ‘ or ‘6………..这样的字符串,其中’or’6是个永真的条件,如果把它放到查询中就可以where语句的判断,比如我们在url输入password=ffifdyop可以看到dump出的数据
三、控制语句
0x01 switch没有break这个代码是关于switch的小bug,比如让我们包含当前目录中的flag.php,给which为flag,这里会发现在case 0和case 1的时候,没有break,按照常规思维,应该是0比较不成功,进入比较1,然后比较2,再然后进入default,但是事实却不是这样,事实上,在 case 0的时候,字符串和0比较是相等的,进入了case 0的方法体,但是却没有break,这个时候,默认判断已经比较成功了,而如果匹配成功之后,会继续执行后面的语句,这个时候,是不会再继续进行任何判断的。也就是说,我们which传入flag的时候,case 0比较进入了方法体,但是没有break,默认已经匹配成功,往下执行不再判断,进入2的时候,执行了require_once flag.php。
php的很多特性再带来编程方面的同时也会带来很多安全隐患,其中一些漏洞在CTF中经常出现,本文大部分示例也是取自CTF题目。这里首先说一下==和===区别。==是比较运算,它不会去检查条件式的表达式的类型,===是恒等,它会检查查表达式的值与类型是否相等,NULL,0,”0″,array()使用==和false比较时,都是会返回true的,而使用===却不会。
注:本文部分内容及代码并非原创,而是从网上搜集而得。
一、数组
0x01 数组的hash计算
很多时候,PHP数组都发挥了至关重要的作用,比如下面代码
<?php
error_reporting(0);
$flag = 'flag{test}';
if (isset($_GET['username']) and isset($_GET['password'])) {
if ($_GET['username'] == $_GET['password'])
print 'Your password can not be your username.';
else if (md5($_GET['username']) === md5($_GET['password']))
die('Flag: '.$flag);
else
print 'Invalid password';
}
?>
<?php
#error_reporting(0);
echo md5($_GET['username']);
?>
我们加几条调试信息,并将error_reporting(0)取消注释
<?php
error_reporting(0);
$flag = 'flag{test}';
if (isset($_GET['username']) and isset($_GET['password'])) {
echo 'null md5 is ';echo md5(null);
echo '<br>';
echo 'null length is '; echo strlen((md5($_GET['username'])));
echo '<br>';
echo 'md5(md5($_GET["username"])) md5 is ';
echo md5(md5($_GET["username"]));
echo '<br>';
echo 'md5(md5($_GET["password"])) md5 is ';
echo md5(md5($_GET["password"]));
echo '<br>';
if ($_GET['username'] == $_GET['password'])
print 'Your password can not be your username.';
else if (md5($_GET['username']) === md5($_GET['password']))
die('Flag: '.$flag);
else
print 'Invalid password';
}
?>
http://192.168.219.3/CTF/index.php?username[]=1&password[]=2
其运行结果为
因为usrename和password是两个不同的数组,通过第一个判断,又因为他们都是数组,MD5结果都是null,通过第二个判断,输出flag。
0x02 strcmp()
Strcmp()函数的比较作用
<?php
if (isset($_GET['password'])) {
if (strcmp($_GET['password'], $flag) == 0)
die('Flag: '.$flag);
else
print 'Invalid password';
}
?>
0x03 ereg()和strpos()
<?php
require 'flag.php';
if (isset ($_GET['password'])) {
if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
echo 'You password must be alphanumeric';
else if (strpos ($_GET['password'], '--') !== FALSE)
die('Flag: ' . $flag);
else
echo 'Invalid password';
}
?>
二、数字的比较
0x01 十六进制与数字
<?php
error_reporting(0);
function noother_says_correct($temp)
{
$flag = 'flag{test}';
$one = ord('1');
$nine = ord('9');
$number = '3735929054';
// Check all the input characters!
for ($i = 0; $i < strlen($number); $i++)
{
// Disallow all the digits!
$digit = ord($temp{$i});
if ( ($digit >= $one) && ($digit <= $nine) )
{
// Aha, digit not allowed!
return "flase";
}
}
if($number == $temp)
return $flag;
}
$temp = $_GET['password'];
echo noother_says_correct($temp);
?>
0x02数字运算(一)
<?php
error_reporting(0);
$flag = 'flag{test}';
if ("POST" == $_SERVER['REQUEST_METHOD'])
{
$password = $_POST['password'];
if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password))
{
echo 'Wrong Format';
exit;
}
while (TRUE)
{
$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
if (6 > preg_match_all($reg, $password, $arr))
break;
$c = 0;
$ps = array('punct', 'digit', 'upper', 'lower');
foreach ($ps as $pt)
{
if (preg_match("/[[:$pt:]]+/", $password))
$c += 1;
}
if ($c < 3) break;
if ("42" == $password) echo $flag;
else echo 'Wrong password';
exit;
}
}
if ("42" == $password) echo $flag;
else echo 'Wrong password';
当然也可以这样:420.000000000e-1
0x03数字运算(二)
<?php
$flag = "flag{test}";
$temp = "1337a";
is_numeric($temp)?die("nope"):NULL;
if($temp>1336){
echo $flag;
}
?>
0x04 MD5的巧合(一)
<?php
error_reporting(0);
$flag = 'flag{test}';
$temp = $_GET['password'];
if(md5($temp)==0){
echo $flag;
}
?>
240610708
QNKCDZO
0x05 MD5的巧合(二)
<?php
error_reporting(0);
$link = mysql_connect('localhost', 'root', 'root');
if (!$link) {
die('Could not connect to MySQL: ' . mysql_error());
}
// 选择数据库
$db = mysql_select_db("security", $link);
if(!$db)
{
echo 'select db error';
exit();
}
// 执行sql
$password = $_GET['password'];
$sql = "SELECT * FROM users WHERE password = '".md5($password,true)."'";
var_dump($sql);
$result=mysql_query($sql) or die('<pre>' . mysql_error() . '</pre>' );
$row1 = mysql_fetch_row($result);
var_dump($row1);
mysql_close($link);
?>
$sql = "SELECT * FROM users WHERE password = '".md5($password,true)."'";
md5后,276f722736c95d99e921722cf9ed621c
将其转成字符串的话就是
可以看到起字符串类似于 ‘ or ‘6………..这样的字符串,其中’or’6是个永真的条件,如果把它放到查询中就可以where语句的判断,比如我们在url输入password=ffifdyop可以看到dump出的数据
三、控制语句
0x01 switch没有break
<?php
error_reporting(0);
if (isset($_GET['which']))
{
$which = $_GET['which'];
switch ($which)
{
case 0:
case 1:
case 2:
require_once $which.'.php';
echo $flag;
break;
default:
echo GWF_HTML::error('PHP-0817', 'Hacker NoNoNo!', false);
break;
}
}
?>
评论25次
这些PHP黑魔法,CTF简单的代码审计里面都会有,感谢楼主整理。
感谢分享!
确实,这些PHP黑魔法在CTF的源码审计中经常出现,每次都得跑去翻资料,现在有个总结,就好多了。
还是ctf中奇技淫巧多啊,脑洞异常的大。但是有些几乎不贴进实战,就显得跟读书考试那样了,不过对于知识的积累还是蛮不错的。
先mark下,回头看下
PHP比较菜,哎!看着挺累的。
我差,这段代码够简单的你看着还累???学5天php绝对能看懂
我补补PHP基础吧!
PHP比较菜,哎!看着挺累的。
我差,这段代码够简单的你看着还累???学5天php绝对能看懂
还是ctf里的奇技淫巧多
脑洞大了,正好在学PHP,看完有点醉氧!
谢谢表哥分享不错,
php黑魔法还是很有趣,主要实战中遇到的很少,打CTF经常遇到,mark一下,做下研究,也感谢楼主的分享,关键这个md5的巧合是怎么发现的,没搞懂
也是一次比赛后看别人的writeup才知道的,
看完了,收获很大,但是对于最后一个说是switch的一个小bug,这个有点不明白,switch的语法不就是那样的吗?
嗯,这个特性继承自C,可能用词不准,
奇技淫巧果然还是ctf中多,脑洞比较大,实战中不太好遇到,但知识积累还是很不错的
很多比赛题目还是很有意思的
php黑魔法还是很有趣,主要实战中遇到的很少,打CTF经常遇到,mark一下,做下研究,也感谢楼主的分享,关键这个md5的巧合是怎么发现的,没搞懂
PHP7在这些的小技巧上, 修正了很多。。。。
看完了,收获很大,但是对于最后一个说是switch的一个小bug,这个有点不明白,switch的语法不就是那样的吗?
奇技淫巧果然还是ctf中多,脑洞比较大,实战中不太好遇到,但知识积累还是很不错的
感谢楼主分享,记录一下,学xi学xi。
PHP比较菜,哎!看着挺累的。
感谢分享学xi了