前一段时间在看雪上看到hackyzh翻译的《Linux (x86) Exploit 开发系列教程之三(Off-By-One 漏洞 (基于栈))》 文章,后面又查看了英文原文,https://sploitfun.wordpress.com/2015/06/07/off-by-one-vulnerability-stack-based-2/,发现原文作者的exp跑不通,并且部分地方讲解的不利于理解,所以按照自己的理解,重新整理了offbyone基于栈的漏洞,同时附上相关exp。
将源字符串复制到目标缓冲区时,可能会导致offbyone。当源字符串长度等于目标缓冲区的长度时,单个NULL字节将被复制到目标缓冲区上方,由于目标缓冲区位于堆栈中,所以单个NULL字节可以覆盖存储在堆栈中的调用者的EBP的最低有效位(LSB),这可能导致任意代码执行(此处copy的原文)。
漏洞演示环境:Ubuntu 12.04-32位
漏洞代码:
#include <stdio.h>
#include <string.h>
void foo(char* arg);
void bar(char* arg);
void foo(char* arg) {
bar(arg); /* [1] */
}
void bar(char* arg) {
char buf[256];
strcpy(buf, arg); /* [2] */
}
int main(int argc, char *argv[]) {
if(strlen(argv[1])>256) { /* [3] */
printf("Attempted Buffer Overflow\n");
fflush(stdout);
return -1;
}
foo(argv[1]); /* [4] */
return 0;
}
在编译时,先切换到root,关闭Linux的地址随机化(ASLR)特性,命令如下:
echo 0 > /proc/sys/kernel/randomize_va_space
(恢复)开启ASLR命令如下
echo 2 > /proc/sys/kernel/randomize_va_space
编译命令如下:
gcc -fno-stack-protector -z execstack -mpreferred-stack-boundary=2 -o vuln -g offbyone.c
编译命令的详细解释,放到了3.1章节中。
上述漏洞代码的第[2]行可能是发生off by one溢出的地方。目标缓冲区长度时256,因为长度为256字节的源字符串可能导致任意代码执行。
漏洞利用:
我们来看一下上述代码的堆栈布局,确定调用者的EBP是否位于目标缓冲区至上,也就是strcpy之后,单个NULL字节是否覆盖调用者的EBP的LSB?
Main函数
.text:08048497 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:08048497 public main
.text:08048497 main proc near ; DATA XREF: _start+17o
.text:08048497
.text:08048497 var_8 = dword ptr -8
.text:08048497 argc = dword ptr 8
.text:08048497 argv = dword ptr 0Ch
.text:08048497 envp = dword ptr 10h
.text:08048497
.text:08048497 push ebp
.text:08048498 mov ebp, esp
.text:0804849A push edi
.text:0804849B sub esp, 8
.text:0804849E mov eax, [ebp+argv]
.text:080484A1 add eax, 4
.text:080484A4 mov eax, [eax]
.text:080484A6 mov [ebp+var_8], 0FFFFFFFFh
.text:080484AD mov edx, eax
.text:080484AF mov eax, 0
.text:080484B4 mov ecx, [ebp+var_8]
.text:080484B7 mov edi, edx
.text:080484B9 repne scasb
.text:080484BB mov eax, ecx
.text:080484BD not eax
.text:080484BF sub eax, 1
.text:080484C2 cmp eax, 100h
.text:080484C7 jbe short loc_80484EA
.text:080484C9 mov eax, offset format ; "attepted buffer overflow"
.text:080484CE mov [esp], eax ; format
.text:080484D1 call _printf
.text:080484D6 mov eax, ds:stdout@@GLIBC_2_0
.text:080484DB mov [esp], eax ; stream
.text:080484DE call _fflush
.text:080484E3 mov eax, 0FFFFFFFFh
.text:080484E8 jmp short loc_80484FF
.text:080484EA ; ---------------------------------------------------------------------------
.text:080484EA
.text:080484EA loc_80484EA: ; CODE XREF: main+30j
.text:080484EA mov eax, [ebp+argv]
.text:080484ED add eax, 4
.text:080484F0 mov eax, [eax]
.text:080484F2 mov [esp], eax ; src
.text:080484F5 call foo
.text:080484FA mov eax, 0
.text:080484FF
.text:080484FF loc_80484FF: ; CODE XREF: main+51j
.text:080484FF add esp, 8
.text:08048502 pop edi
.text:08048503 pop ebp
.text:08048504 retn
.text:08048504 main endp
foo函数:
.text:08048464 ; int __cdecl foo(char *src)
.text:08048464 public foo
.text:08048464 foo proc near ; CODE XREF: main+5Ep
.text:08048464
.text:08048464 src = dword ptr 8
.text:08048464
.text:08048464 push ebp
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课