概述

​ 在刷pwnable.kr的题目shellshock时,碰到了这个bash的老漏洞,这里我也对其分析一下,做个记录。

​ shellshock破壳漏洞,是一个高危的漏。漏洞原理简单来说就是子进程bash在获得父bash进程传递的shell环境变量时,对函数定义的变量解析除了问题,导致了任意代码被执行。

​ 该漏洞可以分为本地利用和远程利用两种,其远程利用危害性极强,攻击者可以利用该漏洞精心伪造数据,通过网络请求到一台利用bash脚本来处理用户请求的网站上,来直接或间接的触发一个bash脚本,这样就可以远程执行而已代码了。

​ 经测试,从bash1.14 到4.3都存在这样的漏洞。

环境

​ 我是在ubuntu中玩耍的,先确定自己bash版本,新的ubuntu自带的bash版本都是4.3以上的,不存在shellshock漏洞,所以需要自己手动安装低版本的bash:

​ 从gun.org/software/bash选择一个国内的镜像源,然后把bash-4.1下载并进行安装:

1
2
3
4
5
wget https://mirrors.aliyun.com/gnu/bash//bash-4.1.tar.gz
tar -zxvf bash-4.1.tar.gz
cd bash-4.1/
sudo ./configure --prefix=/usr/local/bash-4.1 #这里如果时新环境,先装好gcc,sudo apt install gcc
sudo make make install

​ 执行上述步骤后,在/usr/local/bash-4.1/下成功安装到新的bash,可以验证查看一下,成功安装到了指定版本的bash:

​ 为了省事,这里就不再弄PATH变量等操作了,直接在bin/下使用新安装的bash进行漏洞测试即可。

​ 针对这个shellshock漏洞,一个广为流传的本地测试poc命令如下,我们稍作修改,然后测试:

1
2
3
env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
#这里针对性修改一下,来做测试,在usr/local/bash-4.1/bin下执行poc
env x='() { :;}; echo vulnerable' ./bash -c "echo this is a test"

​ 成功输出vulnerable,漏洞复成功:

漏洞分析

漏洞成因

​ 首先需要了解一下shell中的变量类型和变量的作用域以及bash父子进程对于shell变量的使用,shell变量以及其对应的作用域可以分为如下三种:

  • 局部变量(local var):只能在函数内部使用
  • 全局变量(global var):在当前shell进程中可以使用
  • 环境变量(env var):可以让当前shell进程的子进程使用

​ 对于这三种变量依次分析一下,局部变量没什么好说的,主要是全局变量和环境变量这里简单说明一下。全局变量只在当前shell程序中有效,对于其它shell进程和子进程都无效,比如下面,定义一个变量a=910,在当前bash shell中可以输出,但是新开一个bash子进程就访问不到。

​ 创建全局变量的shell进程称为父进程(这里用bash),父进程中新创建的进程就为子进程,要想子进程也访问到父bash进程的全局变量,那么就需要用export将全局变量导出,被导出的变量就被称为环境变量。当shell产生子进程时,它会继承父进程的环境变量为自己所用(自动加载),也就是说父进程的环境变量传递给了子进程,当然环境变量还可以继续向下传递给孙进程。

​ bash中可以将shell函数也导出为环境变量,这有两种方式:1.直接定义函数并export -f(-f为参数,必须设置);2.通过环境变量值来定义函数

  1. 直接定义函数并导出比较好理解,用法如下:

  1. 而shellshock漏洞利用的正好是第二种方法,通过bash对环境变量值的转换来获得。bash中,当某个环境变量的值为字符串且以"() {"的格式开头(小括号和大括号间必有空格)书写时,那么该环境变量在被子bash进程加载时会被转换为一个shell函数,而不是当作一个shell变量,示例如下:

    注:上述通过环境变量来定义函数的方式,被称为bash的自动导入机制(自动导入函数到当前bash的子进程),应为出现了shellshock漏洞,所以现在发行版的linux都会默认关了bash的自动导入机制,想要测试该机制还得用刚才配置的bash低版本来测试。

​ Shellshock漏洞就是出现在bash的自动导入机制中,如果在() {}完整函数变量尾部的花括号后加上一个“小尾巴”(shell 命令),由于bash中解析逻辑存在漏洞它会在转换过程中,把”小尾巴“的内容也进行执行:

源码分析

​ 总结一下,shellshock漏洞主要出现在,子bash进程导入父bash进程的环境变量时,对函数型环境变量的值解析出了问题,导致额外的代码被执行。

​ 该漏洞存在于bash源代码的variables.c文件中,拿上面bash4.1的对应文件进行分析,包含漏洞的代码在initialize_shell_variables()函数中:

  1. 首先进入函数,第一步主要操作就是遍历整个env列表,对每一个有效环境变量进行操作,解析完成后,name-存储环境变量名,string-存储环境变量值,char_index-存储环境变量名长度:

  1. 判断有效的环境变量是否匹配'() {'也就是是否为导出函数(另外两个判关于特权模式的判断,需要保证real uid和effective id相同,一般情况下都是相同的),若是自动导出函数,就直接申请一块内存空间,然后把string(环境变量值)不做任何拷贝到空间中然后交由parse_and_execute()函数执行

parse_and_execute()函数原型如下,传递给函数的所有内容都会被当作普通bash命令执行,所以导致了漏洞的产生。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* Parse and execute the commands in STRING.  Returns whatever
execute_command () returns. This frees STRING. FLAGS is a
flags word; look in common.h for the possible values. Actions
are:
(flags & SEVAL_NONINT) -> interactive = 0;
(flags & SEVAL_INTERACT) -> interactive = 1;
(flags & SEVAL_NOHIST) -> call bash_history_disable ()
(flags & SEVAL_NOFREE) -> don't free STRING when finished
(flags & SEVAL_RESETLINE) -> reset line_number to 1
*/
int
parse_and_execute (string, from_file, flags)
char *string;
const char *from_file;
int flags;
{
//...
}