nasm null pointer dereferens
版本号:nasm-2.14rc15
漏洞描述:空指针的解引用
编译命令:./nasm -f bin ./test.asm -o /dev/null
运行时错误信息:error: EQU not preceded by label
poc
1 | equ foo |
load core dump file
1 | <!--back trace--> |
1 | // asm/labels.c:64 |
1 | <!--asm--> |
分析
简要分析
程序发生崩溃在islocal
函数中,根据backtrace和崩溃时寄存器信息可以看出,是发生了空指针解引用导致的程序崩溃。回退find_label
函数,空指针由参数label
传入。继续回退define_label
,空指针同样由label
参数传入。在函数assemble_file
中1
2
3define_label(output_ins.label,
output_ins.oprs[0].segment,
output_ins.oprs[0].offset, false);
函数的空指针由output_ins.label
传入。而output_ins
是parse_line(pass1, line, &output_ins);
所赋值的。
因此,在分析label
是何时被赋值为空可以跟踪parse_line
函数。
详细分析
gdb 调试程序运行,gdb ./nasm
进入gdb调试界面。对assemble_file
下断点,运行命令r -f bin ./test.asm -o /dev/null
继续运行。
添加watchpoint
,观察line
变量的使用情况,1
2
3
4
5
6
7
8
9► 1435 while ((line = preproc->getline())) {
1436 if (++globallineno > nasm_limit[LIMIT_LINES])
1437 nasm_fatal(0,
1438 "overall line count exceeds the maximum %"PRId64"\n",
1439 nasm_limit[LIMIT_LINES]);
1440
-> $ p line
$1 = 0x5555559248d0 "equ foo"
该变量line
读入的内容为输入文件中的符合要求的一行内容。b parse_line
函数,进入该函数查看函数。watch result->label
查看label
在什么时候被修改,在第一次被修改时:1
► 452 result->eops = NULL; /* must do this, whatever happens */
之后就直接退出了parse_line
函数,因此,在该函数中label
只是被赋值了初始值NULL。
继续执行,1
2
3
4
5
6
7
8
9
10
11
12 1475 /* forw_ref */
1476 if (output_ins.opcode == I_EQU) {
► 1477 if (!output_ins.label)
1478 nasm_error(ERR_NONFATAL,
1479 "EQU not preceded by label");
1480
1481 if (output_ins.operands == 1 &&
1482 (output_ins.oprs[0].type & IMMEDIATE) &&
1483 output_ins.oprs[0].wrt == NO_SEG) {
1484 define_label(output_ins.label,
1485 output_ins.oprs[0].segment,
1486 output_ins.oprs[0].offset, false);
由于output_ins.opcode == I_EQU
成立,在poc中语句为EQU foo
,因此会被解析为EQU
,所以会直接进入define_label
函数中,但是由于label
没有任何信息,在后续的处理中会引起null pointer dereference错误。
parse_line
- 当poc为
equ "foo"
时,i = stdscan(NULL, &tokval);
取出的值为263 == TOKEN_INSN
。1
2
3
4equ "foo"
^ ^
| |
TOKEN_INSN TOKEN_STR
1 | if (i == TOKEN_ID || (insn_is_label && i == TOKEN_INSN)) { |
该分支不能得到满足,因此label的值为默认初始化值NULL
。
当poc 为
label: equ "foo"
时,i = stdscan(NULL, &tokval);
取出的值为256 == TOKEN_ID
。result->label
应赋值为label,1
2
3
4label: equ "foo"
^ ^ ^
| | |
label(TOKEN_ID) TOKEN_INSN TOKEN_STR当poc为
a equ 0xff
时,1
2
3
4
5a equ 0xff
^ ^ ^
| | |
label TOKEN_INSN immediate
(TOKEN_ID)
小节
crash产生的原因是因为,当解析EQU
命令时,如果在EQU
之前没有非指令字符时,在parser.c#470
位置,会导致没有条件成立,不能进入if
语句,因此在处理result->label
时,所返回的值为默认初始化的值,即result->label = NULL
。