寒假任务二
php特性靶场
前置内容
preg_match:
具体查阅:[preg_match_preg_match('/.*([\w\]|\|*|(|~|`|\?|\/| |||&-CSDN博客](preg_match_preg_match('/^.*([\w]|\^|\*|\(|\~|\`|\?|\/| |\||\&-CSDN博客
preg_match 函数用于执行一个正则表达式匹配。
语法: preg_match(string $pattern, string $subject, array &$matches);
pattern:要搜索的模式串,字符串类型
subject:输入的字符串,字符串类型
matches:可选,用来存放搜索结果,$matches[0]
存放所有匹配的字符串,$matches[1]
用来存放第一个匹配的字符串,$matches[2]
用来存放第三个匹配的字符串,以此类推
返回值:如果没有匹配,返回值为0;如果有匹配,返回值为1
绕过方式:
1.加号绕过:比如题目所求flag对应的变量的值为1000,但是该代码中变量大于999时无输出,这时我们就可以用加号绕过,即将目标变量的值从1000改为999+1(注意在url里传参时+要编码为%2B)
2.数组绕过(参考web89):一般有三种格式
$a[]='flag.php'; $a=array('flag.php'); $a=['flag.php'];
3.回溯次数绕过
4.换行绕过(参考web91):换行符%0a
5.preg_match_all()绕过
intval:
intval — 获取变量的整数值
它的具体格式和解释如下:
int intval( var,base) //var指要转换成 integer 的数量值,base指转化所使用的进制 Note: 如果 base 是 0,通过检测 var 的格式来决定使用的进制: ◦ 如果字符串包括了 "0x" (或 "0X") 的前缀,使用 16 进制 (hex);否则, ◦ 如果字符串以 "0" 开始,使用 8 进制(octal);否则, ◦ 将使用 10 进制 (decimal)。
1.
这里这个Note对我们来说就尤为重要了,没有声明base参数时,字符串首字母是0则视为8进制,是0X则视为16进制,具体可以看官方给出的例子:
?php echo intval(042); // 34 echo intval(0x1A); // 2 ?>
此时就可以看出它的一个利用方式了,当过滤某个数字时,我们可以利用它的进制转换来绕过。
2.**
返回值 成功时返回 var 的 integer 值,失败时返回 0。空的 array 返回 0,非空的 array 返回1
这时候就可以看出另一个利用方式了,如果是一个弱比较a==b
我们输入a[]=1
和b[]=2
,此时这两个是不同的,但还都会返回1,此时也就实现了一种绕过,这是第二种绕过思路。
3.
echo intval(42); // 42 echo intval(4.2); // 4
我们可以发现小数点后的数字会直接舍去,所以当过滤4的时候,我们可以输入4.2来绕过,这是第三种绕过思路。
4.
echo intval(1e10); // 1410065408 echo intval('1e10'); // 1
我们发现单引号传值的时候,它只识别字母前面的一部分,当我们进行get传参时,我们其实就是默认加单引号的,所以这是第四种绕过方式。
strpos:
strpos用于查找字符串首次出现的位置
函数形式: mixed strpos( string $haystack, mixed $needle[, int $offset = 0] ) 意为: 返回 `needle` 在 `haystack` 中首次出现的数字位置。
haystack:在该字符串中进行查找。
needle:如果 needle 不是一个字符串,那么它将被转换为整型并被视为字符的顺序值。
offset:如果提供了此参数,搜索会从字符串该字符数的起始位置开始统计。如果是负数,搜索会从字符串结尾指定字符数开始。
返回值:返回 needle 存在于 haystack
字符串起始的位置(独立于 offset)。同时注意字符串位置是从0开始,而不是从1开始的。
如果没找到 needle,将返回 FALSE
。
md5:
PHP md5()函数详解,PHP计算MD5,md5()绕过,md5()漏洞原理剖析-CSDN博客
语法:string md5( $str, raw)
str:需要计算的字符串
raw:指定十六进制或二进制输出格式
返回值:计算成功,就返回MD5值;计算失败,就返回false
1.基础使用
平时使用最多的就是「计算MD5」
实例:
echo md5('hello');
输出:
5d41402abc4b2a76b9719d911017c592
2.输出格式
raw 参数控制输出的「格式」:
-
true :16个字符的「二进制格式」
-
false :(默认)32个字符的十六进制格式
如果你在项目中遇到MD5「加密结果不一致」的问题,可以观察两个加密结果的长度是否相同,比如一个结果是16位,而另一个结果是32位,这种情况就可以考虑更换输出格式来解决。
实例:
var_dump(md5('hello', true)); var_dump(md5('hello', false));
输出:
string(16) "]A@*�K*v�q��Œ" string(32) "5d41402abc4b2a76b9719d911017c592"
3.0e绕过
md5() 遇到「公式」,会先「运算」,再对运算结果「计算」MD5。
由于0和任何数相乘都等于0,所以0e开头的任何数,其MD5都是相同的。
比如 md5('0e1234')
,会先运算成 md5(0)
,再计算MD5值。
补充:
0e是科学计数法,大小写等价,即 0e 和 0E 的结果相同。 科学记数法是一种记数的方法。把一个数表示成a与10的n次幂相乘的形式。 格式为:aEb=a×10^b,即a乘以10的b次幂。
实例:
echo md5(0).PHP_EOL; echo md5(0e123).PHP_EOL; echo md5(0e456).PHP_EOL; echo md5(0E456);
输出:
cfcd208495d565ef66e7dff9f98764da cfcd208495d565ef66e7dff9f98764da cfcd208495d565ef66e7dff9f98764da cfcd208495d565ef66e7dff9f98764da
绕过思路1:遇到弱比较(md5(a)==md5(b))时,可以使用 0e绕过。
实例:
var_dump(md5(0e123) === md5(0e456)); var_dump(md5(0e123) == md5(0e456));
输出:
bool(true) bool(true)
绕过思路2:遇到若比较( md5(a)==0 ),可以传入QNKCDZO等绕过。
实例:
echo md5('QNKCDZO').PHP_EOL; var_dump(md5('QNKCDZO') == 0);
输出:
0e830400451993494058024219903391 bool(true)
4.数组绕过
md5() 不能处理数组,数组都返回null。同时会报一个Warning,不影响执行,不用管。
实例:
var_dump(md5([1,2])); var_dump(md5([3,4]));
输出:
Warning: md5() expects parameter 1 to be string, NULL Warning: md5() expects parameter 1 to be string, NULL
绕过思路:遇到强比较(a===b)时,可以使用数组绕过。GET传参时,以 a[]=1&b[]=2 这种形式传递数组。
实例:
$a = array(1,2,3); $b = array(4,5,6); var_dump(md5($a)===md5($b));
输出:
Warning: md5() expects parameter 1 to be string, Warning: md5() expects parameter 1 to be string, bool(true)
三元运算(?:)
三元运算符是软件编程中的一个固定格式,语法是“条件表达式? 表达式1:表达式2”。条件表达式成立,就是1的的值,反之,则是2的值
实例:
2>1?ture:false 相当于 if(2>1) return ture; else return false;
<?php $action = (empty($_POST['action'])) ? 'default' : $_POST['action'] ?> 相当于 <?php if (empty($_POST['action'])) { $action = 'default'; } else { $action = $_POST['action']; } ?>
is_numeric
is_numeric 用来 检测变量是否为数字或数字字符串
语法: bool is_numeric( mixed $var)
返回值:如果 var
是数字和数字字符串则返回 TRUE
,否则返回 FALSE
。
1)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; } }
分析:
该代码要求GET一个变量'num',并对此进行判断
如果num中含有数字0-9,则输出"no no no!"
preg_match()可以完成字符串的匹配规则。 //中的内容为匹配内容,后面为匹配的变量,如果变量中包含//中的内容,preg_match() 函数返回 1,否则返回 0
如果num为整数,则输出$flag
因为preg_match() 函数无法识别数组,所以我们可以通过数组绕过preg_match() 函数直接进行判断整数环节。所以我们可以输出?num[]=1来尝试,即可得到flag{You have successfully completed web89!}
2)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===4476(强类型比较),则die"no no no!",所以我们输入的num不能等于4476
但是,后面进行intval判断,如果num===4476,则echo $flag,否则echo num。所以我们输入的num要等于4476
经过前面的分析,我们似乎有点不知道变量能不能等于4476。
但是要注意,intval函数是支持不同进制的,而这里的base为0,所以我们可以直接使用八进制和十六进制
因为intval取的是我们所输入内容开头的整数,也就是说我们传入含有字符的字符串,所得结果为去除字符部分的整数
所以本题可输入:?num=010574 ?num=0x117c ?num=4467a (注意:八进制中10574和010574是相等的,十六进制中117c和0x117c是相等的,因为题目中base为0所以如此写)
3)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'; } nonononono
分析:
/i 表示匹配的时候不区分大小写 /m 表示多行匹配,什么是多行匹配呢?就是匹配换行符两端的潜在匹配。影响正则中的^$符号
首先对第一个preg_match函数进行判断,如果变量进行多行匹配到php,则进行第二个preg_match函数的判断,否则ceho 'nonononono'
对第二个preg_match函数进行判断,如果变量单行匹配到php,则echo 'hacker',否则echo $flag
所以这道题要求我们的变量是多行的,且第一行中不能有php。所以这道题我们要使用grep_match()的换行绕过
输入?cmd=%0aphp,即可得到flag{You have successfully completed web91!} (%0a为换行符)
换行绕过的具体解析:
%0aphp在文本形式的匹配中会显示如下:(CRLF是隐藏的字符)
那么进行多行匹配的时,一定会匹配到第二行的php,然后就会进入第二个preg_match函数中进行单行匹配,但第一行是隐藏的换行字符,是无法匹配到php的,所以便会输出flag。
4)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); } }
分析:
本题与web90类似,但要注意的是web90属于强类型比较( === ) ,web92属于弱类型( == )
而在弱类型比较中4476和4476a是相等的,所以本题可以使用十六进制0x117c或者八进制010574
5)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); } }
分析:
与web92类似,但本题匹配的内容为[a-z]:[a-z] 字符范围。匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。注意:只有连字符在字符组内部时,并且出现在两个字符之间时,才能表示字符的范围; 如果出现在字符组的开头,则只能表示连字符本身
也就是说本题变量中不能含有字母a-z,同时因为是/i无视大小写,所以变量中不能有任何字母,所以我们只能用八进制015074
6)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; } }
分析:
本题与前几道题类似,但多了一个strpos函数,该函数为查找0在num中首次出现的位置,并且该函数前有!,!意为取反,所以该句判断的是变量中含有0且首位不能为0
同时根据其他三处判断,本题应该使用的是八进制
所以本题可以使用换行绕过加八进制组合、空格绕过加八进制组合、特殊符号绕过加八进制组合、小数点绕过
换行绕过:%0a010574 空格绕过:%20010574 特殊符号绕过:+010574 小数点绕过:4476.0
7)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; } }
分析:
本题与web94类似,但是多了一个\.的匹配,也就是说本题的变量中不能含有.,所以我们不能使用小数点绕过了
因此本题可采用空格绕过、换行绕过、特殊符号绕过
8)web96
代码:
<?php highlight_file(__FILE__); if(isset($_GET['u'])){ if($_GET['u']=='flag.php'){ die("no no no"); }else{ highlight_file($_GET['u']); } }
分析:
我们可以看到本题的判断是一个关于文件的弱比较
当变量u==flag.php时,die("no no no");否则会高亮显示变量u
因此本题需要我们通过路径来实现变量和flag.php文件的弱比较
一说到路径我们便能想到绝对路径和相对路径这两种,我们也将从这两方面解决问题
首先我们先随便输入一个值,就可以得到本题的目录
这里的\phpstudy_pro\WWW\web96\便是文件的所在目录,那么我们便能得到答案了
绝对路径:?u=\phpstudy_pro\WWW\web96\flag.php 相对路径:?u=./flag.php
9)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.'; } ?>
分析:
本题有两个变量a和b,要求a的内容不能等于b,且a要强等于b才能echo $flag。
根据我们最上方的函数参考,当md5函数中有强比较时,我们可以采取数组绕过的方式。
所以本题的答案为a[]=1&b[]=2(注意:数组的数字是无所谓的,因为只要是数组md5函数就无法识别)
10)web98
php函数的传值与传址(引用)详解-php手册-PHP中文网
代码:
<?php include("flag.php"); $_GET ? $_GET = &$_POST : 'flag'; $_GET['flag'] == 'flag' ? $_GET = &$_COOKIE : 'flag'; $_GET['flag'] == 'flag' ? $_GET = &$_SERVER : 'flag'; highlight_file($_GET['HTTP_FLAG'] == 'flag' ? $flag : __FILE__);
分析:
这段代码使用了三元运算,我们来逐句分析。
第一行:如果使用GET传参,则使用POST传参来代替
第二行:如果GET传参flag的值=flag则将COOKIE的值代替flag中的值
第三行:如果GET传参flag的值=flag则将SERVER的值代替flag中的值
第四行:如果GET传参 HTTP_FLAG=flag,就执行highlight_file(flag),否则执行highlight_file(FILE)
整体分析来看,我们需要将POST传参设为HTTP_FLAG=flag,同时因为使用POST传参的前提时有GET传参,所以我们也得设一个GET传参
所以本题答案为:GET传参:?a=1
POST传参:HTTP_FLAG=flag
11)web100
代码:
<?php highlight_file(__FILE__); include("ctfshow.php"); //flag in class ctfshow; $ctfshow = new ctfshow(); $v1 = $_GET['v1']; $v2 = $_GET['v2']; $v3 = $_GET['v3']; $v0 = is_numeric($v1) and is_numeric($v2) and is_numeric($v3); if ($v0) { if (!preg_match("/\;/", $v2)) { if (preg_match("/\;/", $v3)) { eval("$v2('ctfshow')$v3"); } } }
分析:
前面没什么好说的,我们主要从$v0那一句开始看。
is_numeric函数是用来检测变量是否为数字的函数,也就是说要想使下面的v0判断成立,那么函数括号中的变量就得是数字。
同时,因为and和or的优先级是低于“=”的,所以这句话中v0只与v1有关,与v2、v3是否为数字并没有关系。
接下来我们看后面的eval函数,eval函数是将字符串作为php代码执行的函数,所以这里的v2应该是代码,并且根据上面的preg_match函数可知v2还不能含有“;”
v3必须有“;”
要注意,代码中提示我们v2的值应该与‘ctfshow’有关,所以v2应该是var_dump($ctfshow)
所以本题答案为 ?v1=1&v2=var_dump($ctfshow)&v3=;