virusdefender's blog ʕ•ᴥ•ʔ

二进制安全之栈溢出(十)

本篇没有新内容,只有一个新名词 off-by-one,利用方法还是之前的。

off-by-one漏洞

off-by-one漏洞是计数时由于边界条件判断失误导致结果多了一或少了一的错误,比如

在字符串 \0 场景下,off-by-one 可以覆盖字符串后面一个字节的数据,在部分场景下可能造成严重的问题。

漏洞分析

 1#include <stdio.h>
 2#include <string.h>
 3void foo(char* arg);
 4void bar(char* arg);
 5void foo(char* arg) {
 6     bar(arg);
 7}
 8void bar(char* arg) {
 9     char buf[256];
10     strcpy(buf, arg);
11}
12
13int main(int argc, char *argv[]) {
14     if(strlen(argv[1])>256) { /* [3] */
15           printf("Attempted Buffer Overflow\n");
16           fflush(stdout);
17           return -1;
18      }
19      foo(argv[1]);
20      return 0;
21}

漏洞成因非常简单,main 函数中允许 256 长度的字符串,但是 buf 也是 256 字节的,这会导致 buf 后面一个字节的数据被覆盖。

编译参数是 gcc -fno-stack-protector -z execstack -g -o vuln main.c

strcpy 那一行下断点,然后运行,输入 256 长度的 A。 代码和栈内存是这样的

 1[-------------------------------------code-------------------------------------]
 2   0x40067a <bar+25>:	lea    rax,[rbp-0x100]
 3   0x400681 <bar+32>:	mov    rsi,rdx
 4   0x400684 <bar+35>:	mov    rdi,rax
 5=> 0x400687 <bar+38>:	call   0x4004f0 <strcpy@plt>
 6   0x40068c <bar+43>:	nop
 7   0x40068d <bar+44>:	leave
 8   0x40068e <bar+45>:	ret
 9   0x40068f <main>:	push   rbp
10Guessed arguments:
11arg[0]: 0x7fffffffe030 --> 0x0
12arg[1]: 0x7fffffffe52c ('A' <repeats 200 times>...)
13arg[2]: 0x7fffffffe52c ('A' <repeats 200 times>...)
14[------------------------------------stack-------------------------------------]
150000| 0x7fffffffe020 --> 0x0
160008| 0x7fffffffe028 --> 0x7fffffffe52c ('A' <repeats 200 times>...)
170016| 0x7fffffffe030 --> 0x0
180024| 0x7fffffffe038 --> 0x0
190032| 0x7fffffffe040 --> 0xff000000
200040| 0x7fffffffe048 --> 0xff000000ff000000
210048| 0x7fffffffe050 --> 0x0
220056| 0x7fffffffe058 --> 0x0
23[------------------------------------------------------------------------------]
24
25gdb-peda$ p &buf
26$1 = (char (*)[256]) 0x7fffffffe030

如果 buf 复制 256 字节数据,那它的内存地址就是 0x7fffffffe030 - (0x7fffffffe130 - 1) 了,0x7fffffffe130 地址上的数据将被覆盖,而上面寄存器也可以看出来,rbp 就是 0x7fffffffe130 地址。

 1[----------------------------------registers-----------------------------------]
 2RAX: 0x7fffffffe030 --> 0x0
 3RBX: 0x0
 4RCX: 0x8000100000000000
 5RDX: 0x7fffffffe52c ('A' <repeats 200 times>...)
 6RSI: 0x7fffffffe52c ('A' <repeats 200 times>...)
 7RDI: 0x7fffffffe030 --> 0x0
 8RBP: 0x7fffffffe130 --> 0x7fffffffe150 --> 0x7fffffffe170 --> 0x400700 (<__libc_csu_init>:	push   r15)
 9RSP: 0x7fffffffe020 --> 0x0
10RIP: 0x400687 (<bar+38>:	call   0x4004f0 <strcpy@plt>)
11R8 : 0x1000
12R9 : 0x7ffff7de7ab0 (<_dl_fini>:	push   rbp)
13R10: 0x309
14R11: 0x7ffff7a98720 (<strlen>:	pxor   xmm0,xmm0)
15R12: 0x400550 (<_start>:	xor    ebp,ebp)
16R13: 0x7fffffffe250 --> 0x2
17R14: 0x0
18R15: 0x0
19EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)

栈在 0x7fffffffe130 附近是

 10224| 0x7fffffffe100 --> 0x7fffffffe52c ('A' <repeats 200 times>...)
 20232| 0x7fffffffe108 --> 0x0
 30240| 0x7fffffffe110 --> 0x7fffffffe270 --> 0x7fffffffe62d ("USER=virusdefender")
 40248| 0x7fffffffe118 --> 0x7fffffffe258 --> 0x7fffffffe4fe ("/home/virusdefender/Desktop/pwn/offbyone/vuln")
 50256| 0x7fffffffe120 --> 0x7fffffffe52c ('A' <repeats 200 times>...)
 60264| 0x7fffffffe128 --> 0x400770 (<__libc_csu_fini>:	repz ret)
 70272| 0x7fffffffe130 --> 0x7fffffffe150 --> 0x7fffffffe170 --> 0x400700 (<__libc_csu_init>:	push   r15)
 80280| 0x7fffffffe138 --> 0x40065e (<foo+24>:	nop)
 90288| 0x7fffffffe140 --> 0x0
100296| 0x7fffffffe148 --> 0x7fffffffe52c ('A' <repeats 200 times>...)
110304| 0x7fffffffe150 --> 0x7fffffffe170 --> 0x400700 (<__libc_csu_init>:	push   r15)
120312| 0x7fffffffe158 --> 0x4006ec (<main+93>:	mov    eax,0x0)

覆盖前后的数据对比

1gdb-peda$ x/8xb 0x7fffffffe130
20x7fffffffe130:	0x50	0xe1	0xff	0xff	0xff	0x7f	0x00	0x00
3
4gdb-peda$ x/8xb 0x7fffffffe130
50x7fffffffe130:	0x00	0xe1	0xff	0xff	0xff	0x7f	0x00	0x00

也就是 0x7fffffffe150 会被覆盖为 0x7fffffffe100,这个地址正好是 buf 内可控的地址。

参考

提交评论 | 微信打赏 | 转载必须注明原文链接

#安全 #CTF