NULL POINTER空指针安全漏洞
在Linux的ELF文件格式中,GOT表会拥有稳定的内存地址,而里面保存了共享库函数的入口地址。因此我们可以通过操作指针来改写GOT表,从而使库函数被替换。下面是一个例子:
include <malloc.h>
int main() {
memalign(2, 1);
return 0;
}
我们将代码命名为"got.c",并进行编译:
cc -g got.c
得到可执行文件"a.out"。注意我们在程序中使用了库函数"memalign",它的入口地址将被保存在GOT表中。我们可以使用objdump命令来查看GOT表:
$ objdump -R a.out
a.out: file format elf64-x86-64
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
0000000000600ff8 R_X86_64_GLOB_DAT __gmon_start__
0000000000601018 R_X86_64_JUMP_SLOT __libc_start_main
0000000000601020 R_X86_64_JUMP_SLOT __gmon_start__
0000000000601028 R_X86_64_JUMP_SLOT memalign
可以看到memalign的入口地址保存在GOT表中,保存这个入口地址的地址为0x601028。可以通过gdb来验证它:
[weli@localhost hack]$ gdb -q a.out
Reading symbols from a.out...done.
(gdb) x 0x601028
0x601028 <memalign@got.plt>: 0x00400446
可以看到0x601028中保存了memalign的入口地址为0x400446。然后我们可以验证0x400446确实是memalign的入口:
(gdb) x 0x00400446
0x400446 <memalign@plt+6>: 0x00000268
(gdb)
我们可以看一下程序对应的汇编代码:
$ objdump -d -Mintel a.out | grep -A20 main
...
0000000000400540 <main>:
400540: 55 push rbp
400541: 48 89 e5 mov rbp,rsp
400544: be 01 00 00 00 mov esi,0x1
400549: bf 02 00 00 00 mov edi,0x2
40054e: e8 ed fe ff ff call 400440 <memalign@plt>
400553: b8 00 00 00 00 mov eax,0x0
400558: 5d pop rbp
400559: c3 ret
40055a: 66 0f 1f 44 00 00 nop WORD PTR [rax+rax*1+0x0]
注意到这行:
40054e: e8 ed fe ff ff call 400440 <memalign@plt>
可以看到程序通过查GOT表0x601028中的内容取得了memalign的入口地址为0x400440。因为GOT表通常被保存在.data段中,因此是可读写的,因此我们如果把0x601028中的内容改掉,那么它就不再指向0x400446。通过操纵这个入口地址,程序的执行过程就被篡改了。下面是示例代码:
include <malloc.h>
int main() {
int *ptr = NULL;
ptr = 0x601028;
*ptr = 0xaaaaaa;
memalign(2, 1);
return 0;
}
我们让空指针ptr指向GOT表中的位置:0x601028,也就是memalign的入口地址被保存的地方,然后把它改写成0xaaaaaa。通过这种方式,我们就劫持了IP指针,让程序执行的位置指到了0xaaaaaa这里。我们把程序进行编译,然后运行看看:
[weli@localhost hack]$ cc -g got.c
[weli@localhost hack]$ ./a.out
Segmentation fault
可以看到程序发生了段错误,因为0xaaaaaa是个无效的地址,我们可以通过gdb来验证这一点:
[weli@localhost hack]$ gdb -q a.out
Reading symbols from a.out...done.
(gdb) run
Starting program: /home/weli/projs/hack/a.out
Program received signal SIGSEGV, Segmentation fault.
0x0000000000aaaaaa in ?? ()
(gdb) i r rip
rip 0xaaaaaa 0xaaaaaa
(gdb)
如上所示,通过空指针的安全漏洞,我们便可以操作GOT,从而拿到对IP指针的控制权,最后控制整个程序。从这里我们学到,对于程序中的空指针要特别小心。造成空指针的原因可能包括:
1. 读入用户输入数据,不进行错误检测。
2. 数值超出正常范围导致逻辑出错,且逻辑中包含内存分配相关代码。
最后给出一个完整的例子和运行结果:
源代码(运行于Fedora20):
// work1.c
#include <malloc.h>
#include <stdio.h>
int say_hello() {
printf("Hello, Martian!\n");
}
int main() {
int *ptr = NULL;
ptr = 0x601030;
*ptr = 0x400580;
memalign(2, 1);
return 0;
}
编译过程:
localhost:hack weli$ cc -g work1.c -o work1
work1.c: In function ‘main’:
work1.c:10:7: warning: assignment makes pointer from integer without a cast [enabled by default]
ptr = 0x601030;
^
运行结果:
localhost:hack weli$ ./work1
Hello, Martian!
localhost:hack weli$