Busybox CVE-2022-30065
2023-06-08 08:15:52

Busybox CVE-2022-30065

简介

Busybox

​ BusyBox是一个开源的、轻量级的工具集合,旨在提供一组精简而功能齐全的UNIX工具,适用于嵌入式系统和资源受限环境中。它被设计为一个单一可执行文件,包含了许多常用的UNIX工具,如shell命令、文件操作、系统管理等,可以替代大部分标准的UNIX工具集。

​ BusyBox提供了大量的UNIX工具,包括常见的命令行工具(如ls、cat、grep、sed、awk等)、系统管理工具(如ifconfig、mount、init等)、网络工具(如telnet、ftp、ping等)以及其他实用工具。

AWK

awk是一种文本处理语言,通过定义模式和动作来对输入数据进行匹配和处理。

使用BusyBox中的awk,你可以执行以下操作:

  • 格式化和转换文本数据:awk可以根据指定的模式匹配数据,并对匹配的行进行特定的操作,如打印、替换、重组等。你可以使用内置的函数和操作符来进行数据的格式化和转换。

  • 数据分析和计算:awk提供了丰富的数学和逻辑运算符,可以对数据进行计算和统计分析。你可以使用内置的函数来执行数值计算、字符串操作、日期处理等。

  • 列处理和数据提取:awk支持对数据的列进行处理和提取。你可以指定字段分隔符,并使用内置的变量和函数来获取和操作数据的不同列。

  • 自定义函数和脚本:你可以定义自己的函数和脚本,在awk中重复使用或扩展功能。这使得awk非常灵活,适用于各种复杂的文本处理任务。

使用例:

存在文件Closure.txt

John 25
Emily 30
Michael 35

使用awk进行数据操作:提取并打印每行的第一个字段和第二个字段的和

awk '{sum = $1 + $2; print sum}' Closure.txt

上述命令中,’{sum = $1 + $2; print sum}’是awk的模式和动作。模式为空,表示对所有行都执行相同的动作。动作中的语句计算第一个字段和第二个字段的和,并使用print语句打印结果。

运行结果:

image-20230607111641831

漏洞解析

漏洞描述:

A use-after-free in Busybox 1.35-x’s awk applet leads to denial of service and possibly code execution when processing a crafted awk pattern in the copyvar function.

在Busybox 1.35版本中的AWK出现了Use After Free(UAF)漏洞,将会导致拒绝服务或命令执行,由AWK中copyvar()函数并构造一个AWK模式触发。

漏洞定位:

为了便于找到漏洞触发点,可以通过与新版本打过补丁的文件进行比对,查看其中打的补丁。

通过Busybox官网获取目标文件,1.35.0(漏洞版本)与1.36.0(补丁版本)

https://busybox.net/downloads/

通过文本对比工具查看补丁内容(此处我使用WinMerge),直接比较查看关于awk.c的不同:

image-20230606151846495

补丁在evaluate()函数的case XC(OC_MOVE)中增加了对L.V的判断,并且在下方中看到了copyvar()函数,其中参数就有L.V,所以猜测漏洞将会在其中触发;

通过VScode直接去查看copyvar的定义(好看)

image-20230606152304216

对于传入的第一个参数,L.V,先使用了clrvar()对其进行处理,再使用了handle_special()对其进行处理

clrvar():

image-20230606152858597

看到了与UAF漏洞形成相关的free()函数;

handle_special():

image-20230607090215345

在函数内部可以看到存在一个geyvar_i()函数对传进来的L.v进行操作

getvar_i():

image-20230607090850591

根据源码可以看出在函数中实现了对传入参数内容的修改,这里就可以联想到若是刚刚将chunk释放,此时利用另外一个指向该free chunk的指针,利用getvar_i()对free chunk进行修改,将导致free chunk list结构破坏;

上面多个函数都涉及了var结构体,看看定义:

/* Variable */
typedef struct var_s {
	unsigned type;            /* flags */
	char *string;  //释放
	double number;
	union {
		int aidx;               /* func arg idx (for compilation stage) */
		struct xhash_s *array;  /* array ptr */
		struct var_s *parent;   /* for func args, ptr to actual parameter */
		walker_list *walker;    /* list of array elements (for..in) */
	} x;
} var;

知道了漏洞点,接下来就是理清触发漏洞的逻辑了;

首先就是我们知道是evaluate里出现的漏洞点,而调用该函数的为awk_main():

面向ChatGPT分析(乐)

由此,我们将跟着执行流进入evaluate中:

image-20230606155340907

evaluate中使用nvalloc申请了两个var大小的chunk,在后续的代码中,将使用switch case进行后续数据处理;

总结:若我们输入的数据可以控制L.V指向tmpvars(evaluate中创建的chunk),那么此时将有两个指针指向同一块chunk,并且在clrvar()中对chunk进行了释放,但此时还有一个指针能继续访问使用这个释放的chunk(UAF),那么此时修改这个free chunk的数据(fd啥的),就会破坏free chunk list结构,从而导致拒绝服务或命令执行。

编译验证

编译:

cd busybox
make menuconfig
make
make install

根据CVE提供的Crash调试验证:

https://bugs.busybox.net/show_bug.cgi?id=14781
https://bugs.busybox.net/attachment.cgi?id=9301  

在调试时我们可以选择调试编译后那个未剥离符号表的busybox程序:busybox_unstripped

gdb
b *0x000055555560e3a2  #evaluate+67     xzalloc
b *0x000055555560e94c  #evaluate+1517   copyvar
r awk '$3i$3in$9=$r||$9=i6/6-9f'  #crash poc
b *0x000055555560d22b  #copyvar+17      clrvar
b *0x000055555560c2b9  #clrvar+16       free  
b *0x000055555560c61f  #istrue+4        getvar_i

在后续需要输入的地方我一般都输入aaaa或者bbbb

第一个断点位置:在第一个xzalloc停下

还未进行申请时堆状态:

image-20230607092618568

单步执行,查看堆状态,正常地从top chunk中申请了一个chunk

image-20230607092651365

继续执行(c)至下一个xzalloc,单步执行后查看堆状态:又从top chunk中分出一个chunk

我们删除xzalloc这个断点

del 1

image-20230607093831561

继续执行至copyvar函数:此时即将进入copyvar()函数,注意此时函数的第一个参数L.v==》rdi就是刚刚上图中申请的chunk,也就是tmpvars,所以此时有两个指针同时指向同一块chunk

image-20230606183931636

此时的堆状态:

我们继续断下剩下三个断点:

image-20230607094442279

使用s进入copyvar函数,继续执行至断点处,到达clrvar函数,此时L.v==》rdi不出意外的还是指向chunk(ce80)

image-20230607094604629

继续执行至下一个断点处,也就是free:rdi为0不管他

image-20230607100023784

继续执行至下一个断点处,也就是getvar_i,在执行途中会途径多个free,getvar_i的参数==》rdi,此时也是chunk(ce80)

但是此时的chunk(ce80)是一个free chunk!!!

image-20230607095902215

在getchunk_i函数中将对v->type进行修改,也就是fd:

image-20230607100423168

慢慢单步执行至对其修改的指令处:

此时的free chunk list还是合法的(非常健康)

单步执行,进行修改,直接就篡改了fd指针,破坏了free chunk list结构

image-20230607100812353

下个断点到xzalloc,继续执行,在执行过程中程序将再一次接收输入,我输入bbbb

image-20230607101253595

继续执行至刚刚下的xzalloc断点处

继续执行至下一个xzalloc,位于tcache中的一个free chunk被申请走了,注意接下来就是free chunk(ce80)

image-20230607101603183

继续执行一次xzalloc:free chunk(ce80)被申请走了,现在剩下一个因为修改fd指针导致指向的一个非法free chunk

image-20230607101749901

继续执行一次xzalloc:非法free chunk(cdd0)将被申请走了,并且由于xzalloc函数:

xzalloc函数是Busybox中的一个自定义函数,用于申请内存并将申请的内存中内容清空

static inline void *xzalloc(size_t size)
{
    void *ptr = xmalloc(size);
    memset(ptr, 0, size);
    return ptr;
}

而由于在free chunk(ce10)刚好在cdd0下面,这也将导致ce10的Prev_inuse位被清空,变成了一个非法的free chunk

image-20230607104254792

image-20230607104225336

继续执行一段时间的free与xzalloc,直到非法的free chunk(ce10)被申请走了

image-20230607105017955

继续执行至ce10被释放

最后GDB c执行剩下的代码:

出现中止程序信号,并且是由于释放无效指针引起,与我们预期漏洞想法一致

image-20230607105526161

image-20230606174449274

根据GDB的堆栈回溯可以看到程序在释放一个无效的指针,也就是ce10所在的非法chunk,接下来就是中止程序==》拒绝服务。

总结

根据1.36.0版本的补丁:

/* make sure that we never return a temp var */
if (L.v == TMPVAR0)
	L.v = res;

程序添加了对L.v的检查,防止了UAF;根据漏洞的验证过程,该漏洞想要实现命令执行的效果利用难度较大,且由于在不同版本的系统进行编译,使得编译后的程序的保护也不尽相同;至此结束。