0x00 参考资料

刷题记录 https://www.wlhhlc.top/posts/14827/
md5题型总结 https://www.wlhhlc.top/posts/16813/
PHP伪协议总结 https://segmentfault.com/a/1190000018991087

0x01 PHP弱类型比较总结

1.知识介绍

php中有两种比较的符号 == 与 ===
===强类型比较 先判断两种字符串的类型是否相等,再比较
== 弱类型比较 将字符转化为相同类型,再比较(如果比较涉及数字内容的字符串,则字符串会被转换成数值并且按照转化后的数值进行比较)
1)字符串的开始部分决定了它的值,如果该字符串以合法的数值开始,则使用该数值,否则其值则为0。

  1. var_dump("admin"==0); //true
  2. var_dump("1admin"== 1); //true

2)当字符串的开始没有以合法的数值开始,在进行判断时,其值为0

  1. var_dump("admin1"==0) //true

3)在进行弱类型比较时,会将0e这类字符串识别为科学技术法的数字,0的无论多少次方都是零,所以相等

  1. var_dump("0e123456"=="0e99999"); //true

4)当字符串当作数值来取值时,如果字符串中包含.eE或者数值超过整型范围内时,被当作float来取值,如果没有包含上述字符且在整形范围内,则该字符串会当作int来取值

  1. $test=1+"-1.3e3"; //$test=-1299(float)

2.实战

1)md5绕过(Hash比较缺陷)

  1. <?php
  2. if (isset($_GET['Username']) && isset($_GET['password'])) {
  3. $logined = true;
  4. $Username = $_GET['Username'];
  5. $password = $_GET['password'];
  6. if (!ctype_alpha($Username)) {$logined = false;}
  7. if (!is_numeric($password) ) {$logined = false;}
  8. if (md5($Username) != md5($password)) {$logined = false;}
  9. if ($logined){
  10. echo "successful";
  11. }else{
  12. echo "login failed!";
  13. }
  14. }
  15. ?>

题目大意是要输入一个字符串和数字类型,并且他们的md5值相等,就可以成功执行下一步语句
介绍一批md5开头是0e的字符串 上文提到过,0e在比较的时候会将其视作为科学计数法,所以无论0e后面是什么,0的多少次方还是0md5(‘240610708’) == md5(‘QNKCDZO’)成功绕过!

  1. QNKCDZO
  2. 0e830400451993494058024219903391
  3. s878926199a
  4. 0e545993274517709034328855841020
  5. s155964671a
  6. 0e342768416822451524974117254469
  7. s214587387a
  8. 0e848240448830537924465865611904
  9. s214587387a
  10. 0e848240448830537924465865611904
  11. s878926199a
  12. 0e545993274517709034328855841020
  13. s1091221200a
  14. 0e940624217856561557816327384675
  15. s1885207154a
  16. 0e509367213418206700842008763514

2)json绕过

  1. <?php
  2. if (isset($_POST['message'])) {
  3. $message = json_decode($_POST['message']);
  4. $key ="*********";
  5. if ($message->key == $key) {
  6. echo "flag";
  7. }
  8. else {
  9. echo "fail";
  10. }
  11. }
  12. else{
  13. echo "~~~~";
  14. }
  15. ?>

输入一个json类型的字符串,json_decode函数解密成一个数组,判断数组中key的值是否等于 $key的值,但是$key的值我们不知道,但是可以利用0==”admin”这种形式绕过
最终payload message={“key”:0}

3)array_search is_array绕过

  1. <?php
  2. if(!is_array($_GET['test'])){exit();}
  3. $test=$_GET['test'];
  4. for($i=0;$i<count($test);$i++){
  5. if($test[$i]==="admin"){
  6. echo "error";
  7. exit();
  8. }
  9. $test[$i]=intval($test[$i]);
  10. }
  11. if(array_search("admin",$test)===0){
  12. echo "flag";
  13. }
  14. else{
  15. echo "false";
  16. }
  17. ?>

上面是自己写的一个,先判断传入的是不是数组,然后循环遍历数组中的每个值,并且数组中的每个值不能和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. <?php
  2. $a=array(0,1);
  3. var_dump(array_search("admin",$a)); // int(0) => 返回键值0
  4. var_dump(array_seach("1admin",$a)); // int(1) ==>返回键值1
  5. ?>

array_search函数 类似于== 也就是$a==”admin” 当然是$a=0 当然如果第三个参数为true则就不能绕过

4)strcmp漏洞绕过 php -v < 5.3

  1. <?php
  2. $password="***************"
  3. if(isset($_POST['password'])){
  4. if (strcmp($_POST['password'], $password) == 0) {
  5. echo "Right!!!login success";n
  6. exit();
  7. } else {
  8. echo "Wrong password..";
  9. }
  10. ?>

strcmp是比较两个字符串,如果str10 如果两者相等 返回0
我们是不知道$password的值的,题目要求strcmp判断的接受的值和$password必需相等,strcmp传入的期望类型是字符串类型,如果传入的是个数组会怎么样呢
我们传入 password[]=xxx 可以绕过 是因为函数接受到了不符合的类型,将发生错误,但是还是判断其相等
payload: password[]=xxx

5)switch绕过

  1. <?php
  2. $a="4admin";
  3. switch ($a) {
  4. case 1:
  5. echo "fail1";
  6. break;
  7. case 2:
  8. echo "fail2";
  9. break;
  10. case 3:
  11. echo "fail3";
  12. break;
  13. case 4:
  14. echo "sucess"; //结果输出success;
  15. break;
  16. default:
  17. echo "failall";
  18. break;
  19. }
  20. ?>

这里同样利用php弱类型原理,$a="4admin"在进行弱类型比较时会截取前面的4作为字符串的数值,正好可以匹配到case 4,输出flag{xxxxx}

6)数组绕过md5、sha1、base64_decode

  1. <?php
  2. show_source(__FILE__);
  3. $tmp1 = $_POST['tmp1'];
  4. $tmp2 = $_POST['tmp2'];
  5. if(isset($tmp1) && isset($tmp2) && $tmp1 !== $tmp2 )
  6. {
  7. die("Error");
  8. }
  9. if(md5($tmp1)==md5($tmp2) && sha1($tmp1)==sha1($tmp2)&&base64_decode($tmp1) == base64_decode($tmp2))
  10. {
  11. echo "successful";
  12. }
  13. ?>

md5()sha1()base64_decode()只能处理传入的字符串数据,当传入数组后会报出Warning错误但是仍然会正常运行并返回值,当==左右两边都错误时,并且正常运行返回相同的值,就可以是判定条件成立。
PHP特性-未完成 - 图1
实际上这里换成===强类型判断结果也是一样的

3.参考资料

https://www.cnblogs.com/mrsm1th/p/6745532.html
https://flag0.com/2019/09/20/php弱类型

0x02 PHP preg_match()函数信息泄露漏洞

受影响系统:

PHP PHP <= 5.3

描述:

PHP所使用的preg_match()函数从用户输入字符串获得参数,如果所传送的值为数组而不是字符串就会生成警告,警告消息中包含有当前运行脚本的完整路径。

  1. Warning: preg_match() expects parameter 2 to be string, array given in /var/www/html/index.php on line 20

Tip

  • preg_match()函数获得的参数为数组而不是字符串生成警告,并返回0。如果php版本低于5.3会暴露脚本路径
  • intval()函数对空的 array 返回 0,非空的 array 返回 1。字符串可能返回 0,取决于字符串最左侧的字符
  • 字符前加上换行符时,preg_match()函数进行匹配,/m模式可以正常匹配,默认模式匹配失败
  • 无论php什么版本,==会把e当做10的次方,例如4476e2==447600,而intval()函数只会把e当做字符忽略
  • php7,16进制和10进制的弱类型比较会返回错误。而php5则返回正确
  • php8,数字0和字符串的弱类型比较返回错误,低于php8会返回正确
  • php8,数字123和字符串“123a”的弱类型比较返回错误,低于php8会返回正确
  • intval()函数可以正确识别+010574为4476的8进制
  • %20空格可以绕过strpos的位置检测
  • 在php7中,如果md5()函数接受数组,返回的事null。并且报错,暴露出脚本路径
  • 三目运算符的执行需要是语句,否则是无效的