X86_64架构下的LINUX缓冲区溢出栈分析

缓冲区溢出基础:


#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {
	char str[10];
	strcpy(str, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
	return 0;
}

这里strcpy的时候,字符长度超出了str所允许的长度,发生了溢出。当我们运行这个代码的时候,程序报告Segmentation fault 错误。我们启动gdb查看它的寄存器以及内存数据。

启动后,info reg 查看。发现在x86_64架构下的CentOS寄存器名字用r替换了原来的e(例如,eax-->rax),并且多了8个通用寄存器(r8-->r15)。

(gdb) info reg
rax            0x0	0
rbx            0x0	0
rcx            0x41	65
rdx            0x0	0
rsi            0x40061a	4195866
rdi            0x7fffffffe0d2	140737488347346
rbp            0x4141414141414141	0x4141414141414141
rsp            0x7fffffffe0c8	0x7fffffffe0c8
r8             0x4141414141414141	4702111234474983745
r9             0x4141414141414141	4702111234474983745
r10            0x4141414141414141	4702111234474983745
r11            0x3173a89050	212393824336
r12            0x4003e0	4195296
r13            0x7fffffffe1a0	140737488347552
r14            0x0	0
r15            0x0	0
rip            0x4004f2	0x4004f2 <main+46>
eflags         0x10246	[ PF ZF IF RF ]
cs             0x33	51
ss             0x2b	43
ds             0x0	0
es             0x0	0
fs             0x0	0
gs             0x0	0

我们发现 rip 并没有发生改变,并且当我们增加程序 strcpy 中 A 的长度,并不会溢出到 rip ,他只在 r8 到 r10循环溢出。这样的架构起到了一个很好的保护作用。但是我们的缓冲区栈攻击,即简单的修改 rip 失败了。

如果是32位LINUX即可实现溢出 eip 的攻击,并且获得 root 权限。


高级栈攻击分析:

首先来看我们在熟悉不过的 printf 函数

#include <stdio.h>

int main(int argc, char *argv[]) {
	printf(argv[1]);
	return 0;
}

发现当我们传入以下参数时:

./main Test%s
得到如下显示:

Test愬$�[

这里%s预期是读取一个以'\0'结尾的字符数组,但是,在内存中,printf 的内部指针的后面并没有保存一个变量,因为我们并没有进行参数的传递,所以输出了未初始化的内存内容。

简单的示意图:

栈顶内存低区                        <--------- 栈增长方向                                  栈底内存高区

返回地址        格式串的地址(printf 内部指针)        第一个变量地址            第二个变量地址            。。。。。。。。。。


观察 rbp 和 rsp (分别为扩展基址指针,扩展栈指针)。来看下一个代码:

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {
	int canary = 1;
	char temp[40];
	strcpy(temp, argv[1]);
	printf(temp);
	printf("\n");
	printf("Canary at 0x%08x = 0x%08x\n", &canary, canary);
	return 0;
}

输入:

这里有两个变量,gdb 查询栈的地址范围:

rbp            0x7fffffffe080	0x7fffffffe080
rsp            0x7fffffffe040	0x7fffffffe040

(无论运行多少次,这两个寄存器之间的差值均为64KB),并且这两个变量的地址为:

(gdb) p &temp
$6 = (char (*)[40]) 0x7fffffffe050

(gdb) p &canary
$7 = (int *) 0x7fffffffe07c

在这两个之间,并且canary 高于 temp,说明的栈的特性。

现在gdb:

(gdb) run "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA %08x %08x %08x %08x %08x %08x %08x"

发现:

(gdb) p canary
$5 =  0x41414141

说明temp溢出,并且把栈内的canary覆盖了


我们可以从一下代码中得到启示,从任意内存读取地址:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
	char *addr;
	char cmd[100];
	addr = getenv(argv[1]);
	sprintf(cmd, "%s is located at %p\n", argv[1], addr);
	printf(cmd);
	return 0;
}

运行:

[X-White@localhost shell]$ ./test1 SHELL
SHELL is located at 0x7fff92d7055d

当然我们每次运行,这个地址都不一样。

gdb:

(gdb) p addr
$1 = 0x7fffffffe51c "/bin/bash"
发现 addr 正好就是 /bin/bash 文件。由于每次运行地址均不一样,所以我们可以做做一个重定向,从而获得该地址。


由于是X86_64架构,所以系统在寄存器,寻址等方式上都进行了相应的扩展。例如当用如下命令时:

[X-White@localhost shell]$ nm ./test
00000000006007c0 d _DYNAMIC
0000000000600958 d _GLOBAL_OFFSET_TABLE_
00000000004006c8 R _IO_stdin_used
                 w _Jv_RegisterClasses
00000000006007a0 d __CTOR_END__
0000000000600798 d __CTOR_LIST__
00000000006007b0 D __DTOR_END__
00000000006007a8 d __DTOR_LIST__
0000000000400790 r __FRAME_END__
00000000006007b8 d __JCR_END__
00000000006007b8 d __JCR_LIST__
0000000000600994 A __bss_start
0000000000600990 D __data_start
0000000000400680 t __do_global_ctors_aux
00000000004004c0 t __do_global_dtors_aux
00000000004006d0 R __dso_handle
                 w __gmon_start__
0000000000600794 d __init_array_end
0000000000600794 d __init_array_start
00000000004005e0 T __libc_csu_fini
00000000004005f0 T __libc_csu_init
                 U __libc_start_main@@GLIBC_2.2.5
0000000000600994 A _edata
00000000006009a8 A _end
00000000004006b8 T _fini
0000000000400408 T _init
0000000000400470 T _start
000000000040049c t call_gmon_start
0000000000600998 b completed.6347
0000000000600990 W data_start
00000000006009a0 b dtor_idx.6349
0000000000400530 t frame_dummy
0000000000400554 T main
                 U printf@@GLIBC_2.2.5
                 U putchar@@GLIBC_2.2.5
                 U strcpy@@GLIBC_2.2.5

发现前方的地址是16位,而非8位。

因此,无法利用在32位平台下的攻击方式,攻击失败。

本文只是分析利一下X86_64架构下的地址内部结构,至于攻击方式就是后话了。






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值