最近碰到的一些php的,比如说弱类型和变量覆盖和一些函数造成的问题,还有序列化反序列化和php伪协议,
https://www.jianshu.com/p/fe158ab25120南京邮电CTF wp设计到挺多
比如弱类型有==的 和md5,array_search . 还有strcmp漏洞(数组绕过),ereg函数%00截断漏洞,变量覆盖的话有$$,extract()函数和parse_str(),
PHP5 intval问题和16进制与数字弱相等、chr对256取模
一叶飘零大佬的PHP黑魔法文章(膜)https://skysec.top/2017/07/22/PHP%E5%87%BD%E6%95%B0%E9%BB%91%E9%AD%94%E6%B3%95%E5%B0%8F%E6%80%BB%E7%BB%93/
常见php考点知识
php中有两种比较的符号 == 与 ===
1
=== 在进行比较的时候,会先判断两种字符串的类型是否相等,再比较
== 在进行比较的时候,会先将字符串类型转化成相同,再比较
如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换成数值并且比较按照数值来进行
这里明确了说如果一个数值和字符串进行比较的时候,会将字符串转换成数值
1 //上述代码可自行测试
1 观察上述代码,"admin"==0 比较的时候,会将admin转化成数值,强制转化,由于admin是字符串,转化的结果是0自然和0相等 2 "1admin"==1 比较的时候会将1admin转化成数值,结果为1,而“admin1“==1 却等于错误,也就是"admin1"被转化成了0,为什么呢?? 3 "0e123456"=="0e456789"相互比较的时候,会将0e这类字符串识别为科学技术法的数字,0的无论多少次方都是零,所以相等
对于上述的问题我查了php手册
当一个字符串欸当作一个数值来取值,其结果和类型如下:如果该字符串没有包含'.','e','E'并且其数值值在整形的范围之内
该字符串被当作int来取值,其他所有情况下都被作为float来取值,该字符串的开始部分决定了它的值,如果该字符串以合法的数值开始,则使用该数值,否则其值为0。
1
所以就解释了"admin1"==1 =>False 的原因
md5绕过(Hash比较缺陷)
1
题目大意是要输入一个字符串和数字类型,并且他们的md5值相等,就可以成功执行下一步语句
介绍一批md5开头是0e的字符串 上文提到过,0e在比较的时候会将其视作为科学计数法,所以无论0e后面是什么,0的多少次方还是0。md5('240610708') == md5('QNKCDZO') 成功绕过!
QNKCDZO 0e830400451993494058024219903391 s878926199a 0e545993274517709034328855841020 s155964671a 0e342768416822451524974117254469 s214587387a 0e848240448830537924465865611904 s214587387a 0e848240448830537924465865611904 s878926199a 0e545993274517709034328855841020 s1091221200a 0e940624217856561557816327384675 s1885207154a 0e509367213418206700842008763514
json绕过
key == $key) { echo "flag"; } else { echo "fail"; } } else{ echo "~~~~"; } ?>
输入一个json类型的字符串,json_decode函数解密成一个数组,判断数组中key的值是否等于 $key的值,但是$key的值我们不知道,但是可以利用0=="admin"这种形式绕过
最终payload message={"key":0}
array_search is_array绕过
1
上面是自己写的一个,先判断传入的是不是数组,然后循环遍历数组中的每个值,并且数组中的每个值不能和admin相等,并且将每个值转化为int类型,再判断传入的数组是否有admin,有则返回flag
payload test[]=0可以绕过
下面是官方手册对array_search的介绍
mixed array_search ( mixed $needle , array $haystack [, bool $strict = false ] )
$needle,$haystack必需,$strict可选 函数判断$haystack中的值是存在$needle,存在则返回该值的键值 第三个参数默认为false,如果设置为true则会进行严格过滤
1 返回键值0 4 var_dump(array_seach("1admin",$a)); // int(1) ==>返回键值1 5 ?>
array_search函数 类似于== 也就是$a=="admin" 当然是$a=0 当然如果第三个参数为true则就不能绕过
strcmp漏洞绕过 php -v <5.3
1
strcmp是比较两个字符串,如果str1
我们是不知道$password的值的,题目要求strcmp判断的接受的值和$password必需相等,strcmp传入的期望类型是字符串类型,如果传入的是个数组会怎么样呢
我们传入 password[]=xxx 可以绕过 是因为函数接受到了不符合的类型,将发生错误,但是还是判断其相等
payload: password[]=xxx或者传入以一个object
switch绕过
1
PHP %00截断发生在 php 版本<5.3.4
intval和PHP7中的16进制字符串
PHP5中intval处理不了16进制字符串并会直接返回0,但是数值可以强制转换成十进制,但在PHP7中,intval一样处理不了16进制字符串,返回0,但是16进制的字符串不会在强制转化为10进制数.
这里还有一种绕过就是用科学计数法绕过,$password=2e4,intval('2e4')=2 intval('2e4'+1)=20001(字符串强制转化为数值) __7.1.28之后PHP,intval('2e4')=20000
(ISCC WEB1,16进制问题适用于PHP5,不存在于PHP7,因为用来表示十六进制的字符串不再被当作数值处理)
PHP5
PHP7
16进制字符串与数字字符串弱相等在PHP5中(BUGKU 16进制与数字比较,适用于PHP5,不存在于PHP7)
php error_reporting(0); function noother_says_correct($temp) { $flag = 'flag{test}'; $one = ord('1'); //ord — 返回字符的 ASCII 码值 49 $nine = ord('9'); //ord — 返回字符的 ASCII 码值 57 $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); ?>
在php5中,十进制和对应的十六进制是相等的
变量覆盖
参考于https://www.freebuf.com/column/150731.html
parse_str()存在url解码的能力
PHP反序列化
construct():当一个类被创建时自动调用
destruct():当一个类被销毁时自动调用
invoke():当把一个类当作函数使用时自动调用
tostring():当把一个类当作字符串使用时自动调用
wakeup():当调用unserialize()函数时自动调用
sleep():当调用serialize()函数时自动调用
__call():当要调用的方法不存在或权限不足时自动调用
__get():当要调用的属性不存在或权限不足时自动调用
PHP伪协议
page=php://filter/read=convert.base64-encode/resource=xxx
将1.php压缩成1.zip,改后缀为1.jpg上传指定目录后
/about.php?file=phar://./images/file.jpg/1.php
/about.php?file=zip://./images/file.jpg%231.php
php://input,需要开启allow_url_include。将post请求的数据当作php代码执行。当传入的参数作为文件名打开时,可以将参数设为php://input,同时post想设置的文件内容,php执行时会将post内容当作文件内容
摘自https://www.jianshu.com/p/8057e336514f
自己测试:http://127.0.0.1/shell1.php?file=phar://1.jpg/testaaa.php (成功)
parse_url()绕过
$url = '//www.example.com/path?googleguy=googley';
// 在 5.4.7 之前这会输出路径 "//www.example.com/path"
var_dump(parse_url($url));
?>
|
在 5.4.7 之前这会输出路径 "//www.example.com/path"
例如2016asisctf的一道web题
php $data = parse_url($_SERVER['REQUEST_URI']); var_dump($data); $filter=array("aaa","qqqq"); foreach($filter as $f) { if(preg_match("/".$f."/i", $data['query'])) { die("Attack Detected"); } } ?>
当我们输入http://127.0.0.1/1.php?/home/aaa/ 会被attack detected
但如果输入http://127.0.0.1//1.php?/home/aaa/ 则会被当做相对url,此时的1.php?成了host 而path成了/hone/aaa,导致绕过$data['query']的过滤
3、 ///会被返回false
例如如下代码,如果输入http://localhost/1.php?sql=select会被过滤
php $url=parse_url($_SERVER['REQUEST_URI']); var_dump($url); parse_str($url['query'],$query); var_dump($query); $key_word=array("select","from","for","like"); foreach($query as $key) { foreach($key_word as $value) { if(preg_match("/".$value."/",strtolower($key))) { die("Stop hacking by using SQL injection!"); } } } ?>
如果输入http://127.0.0.1///1.php?sql=select的话就会成功绕过
Note: parse_url() 是专门用来解析 URL 而不是 URI 的。不过为遵从 PHP 向后兼容的需要有个例外,对 file:// 协议允许三个斜线(file:///...)。其它任何协议都不能这样。
学习资料:https://www.cnblogs.com/lgf01010/p/9516445.html