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语句打印结果。
运行结果:
漏洞解析
漏洞描述:
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的不同:
补丁在evaluate()函数的case XC(OC_MOVE)中增加了对L.V的判断,并且在下方中看到了copyvar()函数,其中参数就有L.V,所以猜测漏洞将会在其中触发;
通过VScode直接去查看copyvar的定义(好看)
对于传入的第一个参数,L.V,先使用了clrvar()对其进行处理,再使用了handle_special()对其进行处理
clrvar():
看到了与UAF漏洞形成相关的free()函数;
handle_special():
在函数内部可以看到存在一个geyvar_i()函数对传进来的L.v进行操作
getvar_i():
根据源码可以看出在函数中实现了对传入参数内容的修改,这里就可以联想到若是刚刚将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中:
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停下
还未进行申请时堆状态:
单步执行,查看堆状态,正常地从top chunk中申请了一个chunk
继续执行(c)至下一个xzalloc,单步执行后查看堆状态:又从top chunk中分出一个chunk
我们删除xzalloc这个断点
del 1
继续执行至copyvar函数:此时即将进入copyvar()函数,注意此时函数的第一个参数L.v==》rdi就是刚刚上图中申请的chunk,也就是tmpvars,所以此时有两个指针同时指向同一块chunk
此时的堆状态:
我们继续断下剩下三个断点:
使用s进入copyvar函数,继续执行至断点处,到达clrvar函数,此时L.v==》rdi不出意外的还是指向chunk(ce80)
继续执行至下一个断点处,也就是free:rdi为0不管他
继续执行至下一个断点处,也就是getvar_i,在执行途中会途径多个free,getvar_i的参数==》rdi,此时也是chunk(ce80)
但是此时的chunk(ce80)是一个free chunk!!!
在getchunk_i函数中将对v->type进行修改,也就是fd:
慢慢单步执行至对其修改的指令处:
此时的free chunk list还是合法的(非常健康)
单步执行,进行修改,直接就篡改了fd指针,破坏了free chunk list结构
下个断点到xzalloc,继续执行,在执行过程中程序将再一次接收输入,我输入bbbb
继续执行至刚刚下的xzalloc断点处
继续执行至下一个xzalloc,位于tcache中的一个free chunk被申请走了,注意接下来就是free chunk(ce80)
继续执行一次xzalloc:free chunk(ce80)被申请走了,现在剩下一个因为修改fd指针导致指向的一个非法free chunk
继续执行一次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
继续执行一段时间的free与xzalloc,直到非法的free chunk(ce10)被申请走了
继续执行至ce10被释放
最后GDB c执行剩下的代码:
出现中止程序信号,并且是由于释放无效指针引起,与我们预期漏洞想法一致
根据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;根据漏洞的验证过程,该漏洞想要实现命令执行的效果利用难度较大,且由于在不同版本的系统进行编译,使得编译后的程序的保护也不尽相同;至此结束。