数进行随机映射。比如,"su"就是这样的一种函数(被证明调用成功),利用它可以获得root权限和执行的shell,这已经足够了:)
5)对有缺陷的函数,其所有的库函数的调用都要经过PLT过程连接表的入口,象这样一个程序,PLT必须在一个固定的地址,有缺陷的程序通常是很大的并且调用许多的库函数,这种条件很可能在PLT找出一些有趣的东西来,达到攻击目的。
实际上,后面3个问题还不能够解决,而且上面的5点,我都不能够保证能够成功的代码实现,特别是4种情况是很少的。我们的确需要更普遍的方法来实现。
在下面的章节中,我将描述动态链接库中的dl-resolve() 函数的界面。如果它的适当的一个参数可以又ascii支持的字符串的函数名字,并且能够决定实际的函数地址,和dlsym()相类似。使用dl-resolve() 函数,我们可以构建返回库函数的实现代码,它返回到一个函数,但是在我们构建代码的时候,我们并不知道它返回的确切地址。见文[12]描述了从函数名字中获得该函数地址的途径,不过现有的技术还没能达到我们的目的。
--[ 5 - 动态链接dl-resolve()函数 这章尽量的简单的说明,更详尽的描述请参阅文[9],和glibc的源码,特别是dl-runtime.c。请见文[12]。
----[ 5.1 - 一些ELF(可执行和链接格式的文件)的数据类型 下面是头文件elf.h中的一些定义: typedef uint32_t Elf32_Addr; typedef uint32_t Elf32_Word; typedef struct { Elf32_Addr r_offset; /* Address */ Elf32_Word r_info; /* Relocation type and symbol index */ } Elf32_Rel; /* How to extract and insert information held in the r_info field. */ #define ELF32_R_SYM(val) ((val) >> 8) #define ELF32_R_TYPE(val) ((val) & 0xff)
typedef struct { Elf32_Word st_name; /* Symbol name (string tbl index) */ Elf32_Addr st_value; /* Symbol value */ Elf32_Word st_size; /* Symbol size */ unsigned char st_info; /* Symbol type and binding */ unsigned char st_other; /* Symbol visibility under glibc>=2.2 */ Elf32_Section st_shndx; /* Section index */ } Elf32_Sym;
----[ 5.2 - 一些ELF的数据结构 ELF可执行文件包含一些我们感兴趣的数据结构(主要是数组),这些结构的位置可以从ELF执行文件的动态区域找到。使用 "objdump -x file"命令可以显示文件动态区域的的头部信息(动态符号表、重定位入口等)。
$ objdump -x some_executable ... 得到一些有用的信息(动态符号表、串表,重定位入口,etc)... Dynamic Section:(动态段区域) ... STRTAB 0x80484f8 the location of string table (type char *) SYMTAB 0x8048268 the location of symbol table (type Elf32_Sym*) .... JMPREL 0x8048750 the location of table of relocation entries related to PLT (type Elf32_Rel*) ... VERSYM 0x80486a4 the location of array of version table indices (type uint16_t*) "objdump -x" 也能够显示出 .plt(过程连接表)处的动态段的信息, 0x08048894地址的如下列子: 11 .plt 00000230 08048894 08048894 00000894 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE
----[ 5.3 - 如何从PLT过程连接表中调用dl-resolve() 一个典型的PLT过程连接表入口(当elf格式为elf32-i386)象下面这样: (gdb) disas some_func Dump of assembler code for function some_func: 0x804xxx4 <some_func>: jmp *some_func_dyn_reloc_entry 0x804xxxa <some_func+6>: push $reloc_offset 0x804xxxf <some_func+11>: jmp beginning_of_.plt_section --->过程连接表入口 PLT过程连接表入口值不同,仅仅取决于$reloc_offset的数值(包括some_func_dyn_reloc_entry的值,以后,它作为运算中的符号)。我们可以看到在代码段,压$reloc_offset入栈,并跳到.plt过程连接表段的入口。这几个指令后,控制权转到dl-resolve()函数,reloc_offset作为其一个参数,下面是函数dl-resolve()简要的运算说明: 1)计算函数入口地址 Elf32_Rel * reloc = JMPREL + reloc_offset 2)计算函数symtab入口地址 Elf32_Sym * sym = &SYMTAB[ ELF32_R_SYM (reloc->r_info) ]; 3)结构完整检查 assert (ELF32_R_TYPE(reloc->r_info) == R_386_JMP_SLOT); 4)glibc 2.1.x 或更新的版本,要做另一项检查,如果符号不等于零sym->st_other & 3 != 0,则假设符号已经由前面决定了,就要转向其他的运算法则方式。我们必须确保符号等于零sym->st_other & 3 == 0。 5)如果符号版本正确,检查版本索引表 uint16_t ndx = VERSYM[ ELF32_R_SYM (reloc->r_info) ]; 并找出版本信息 const struct r_found_version *version =&l->l_versions[ndx]; l是link_map 的参数,这里重要的是ndx必须是一个合法的数值,0最好,0意味着"local symbol"。 6)函数名字(asciiz字符串)的检查 name = STRTAB + sym->st_name; 7)检查函数的地址,收集足够的信息,隐藏的两个变量的Elf32的地址定位在reloc->r_offset和sym->st_value。 8)调整堆栈指针,准备调用一些函数 注意:在一些glibc下,fixup()函数执行完成运算的规则,dl-runtime-resolve()完成调用工作。
----[ 5.4 -结论 假设我们溢出堆栈缓冲区如下所示: -------------------------------------------------------------------------- | buffer fill-up | .plt start | reloc_offset | ret_addr | arg1 | arg2 ... -------------------------------------------------------------------------- ^ | - int32 会被有缺陷的函数的返回地址保存覆盖 如果我们准备适当的sym和reloc变量值(分别是Elf32_Sym和Elf32_Rel类型),并计算reloc_offset的数值,将控制权传递给函数,函数名可以找到在STRTAB + sym->st_name,适当的放置参数arg1, arg2 ,我们就有机会返回到另一个函数(ret_addr)。
dl-resolve.c 是个简单的执行以上描述的技术的攻击代码,注意,你需要编译这个代码两次,详见后面的README.code代码部分。
--[ 6 - 战胜PaX ----[ 6.1 - 必要的条件 为了使用在第5章中描述的返回动态链接技术,我们需要适当的位置安置一些结构,我们还需要一个可以移动字节到选定区域的函数,呵呵,最佳人选是strcpy,不过strncpy, sprintf 等也很不错。象文[3]中那样。我们要求在被攻击的程序映像中的strcpy有PLT(过程连接表)的入口。
返回动态链接技术解决了被随机映射的库函数的问题。然而在堆栈中,还不能得到解决,如果溢出的代码在堆栈中寄存,那么我们不能够知道其确切地址,我们也不能通过strcpy够插入0的块(见3。3节)。很遗憾,还不能够拿出一个一般的解决这个问题的方法。(你可以吗?)下面两种方法可能达到要求。
1) 如果在PLT过程连接表中,scanf()是有效的函数。我们可以执行象下面的一些事情: scanf("%s\n",fixed_location) 这样它将一些构建堆栈的结构的输入拷贝到fixed_location,如果我们用栈帧伪造的技术,那么堆栈的栈帧将脱节,所以我们用fixed_location 代替。
2) 如果被攻击的程序使用了-fomit-frame-pointer编译选项,我们通过esp增长的技术,联结调用多个strcpy函数,甚至可以不知道堆栈指针的数值(见3.2节结尾的注意事项)。第n个strcpy应该具有下面的参数形式: strcpy(fixed_location+n, a_pointer_within_program_image) 这种方式我们可以一个字节一个字节的构建适当的栈帧在fixed_location。这样做了后,我们灵活的组合"esp增长"和栈帧伪造的技术,这种方法请见3。3节。
很多类似的工作区构建可以被设计,不过没有多少必要,这些工作很象拷贝一些用户控制的数据到静态数据段或者内存中划分变量区域。因此,上面就是我们的主要工作了,无须多说。
总而言之,我们需要见到两种条件: 6.1.1) strcpy (或 strncpy, sprintf及其类似的函数)有效的通过PLT过程连接表入口。 6.1.2) 在被攻击程序执行的一般过程中,拷贝了用户私有数据到静态变量区域或是内存中划分的变量区域。
----[ 6.2 - 构建exploit 我们将仿照dl-resolve.c这个实现代码的列子,通过调用mmap函数实现返回动态链接技术的方法获得一个可以读,写,执行的内存区域,利用strcpy函数将shellcode 放置在这个区域作为返回函数的地址.我们讨论的被攻击的程序没有使用 -fomit-frame-pointer 这些编译选项,和栈帧伪造.
我们需要确保下面3个有关联的结构适当的放置位置. 1) Elf32_Rel reloc 2) Elf32_Sym sym 3) unsigned short verind (which should be 0) verind的地址是怎样和符号sym关联的?将ELF32_R_SYM 的数值赋给"real_index" (reloc->r_info); 则: sym(符号) is at SYMTAB+real_index*sizeof(Elf32_Sym) verind is at VERSYM+real_index*sizeof(short) 将verind放置在.data数据段 或者 .bss未被初始化的数据段,并通过两次strcpy函数调用使其为零,看起来很自然,不过不幸运的,在这种情况下,real_index将变得非常的大,sizeof(Elf32_Sym)=16远远大于了sizeof(short), sym的地址将远离程序进程的数据段.所以,应该明白为什么在dl-resolve.c 代码中需要从内存中分配上万字节的空间.
好了,现在可以通过设置MALLOC_TOP_PAD_ 的环境变量来扩大任意的执行进程的数据段,不过只能在本地执行代码,我们寻找更普遍的途径来实现,我们通常可以降低verind的位置,放置在被映射的只读区域,我们寻找零短字节,该实现代码通过verind的地址来定位"sym"结构.
我们在那里寻找零短字节?首先,在溢出发生时,我们需要通过被攻击的程序的执行过程中在/proc/pid/maps文件中确定被mmap映射的能够写的,数据段可执行的内存区域的范围.是个包括[地地址,高地址]这样的区域.我们拷贝符号结构到这里.通过简单的计算可以知道real_index的地址在[(low_addr-SYMTAB)/16,(hi_addr-SYMTAB)/16]这个区域中 上一页 [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] ... 下一页 >> [办公软件]在sybase中插入图片、PDF、文本文件 [办公软件]安装Sybase ASE [办公软件]linux指令大全(完整篇) [办公软件]Linux新手入门常用命令大全 [办公软件]在RedHat Linux 9里安装gaim0.80 [办公软件]浅谈Linux 下Java 1.5 汉字方块问题解决方法 [办公软件]Linux程序员必读:中文化与GB18030标准 [办公软件]linux指令大全 [办公软件]制作Linux启动盘的四种方法 [办公软件]Linux文件系统的反删除方法
|