接着啃http://phrack.org/issues/66/10.html ,前段时间在论坛发的The House of Mind ,已经对其中的4.1节--THE HOUSE OF MIND做了总结,本文接着原文的后续内容,对4.1.1节--FASTBIN METHOD、4.1.2节--av->top NIGHTMARE、4.2节--THE HOUSE OF PRIME,进行总结。
THE HOUSE OF MIND 每种THE HOUSE OF XX,各自代表一种利用技术,适用于不同的漏洞形式,每种利用技术,可能又包含多种具体的利用方法,FASTBIN METHOD 和 av->top NIGHTMARE,正是THE HOUSE OF MIND的另外两个子方法,其中av->top NIGHTMARE是一种不成立的方法,作者将它写出来,可能是想表达:不要只想着学习已有的方法,自己也要尝试想想别的路子。
FASTBIN METHOD 漏洞代码已经在4.1节列出,为了方便阅读,这里再来一遍:
溢出数据构造方案:
chunk2->size = 0x0d,fake_heap->ar_ptr = EBP 由于main()栈帧上没有EIP(main()函数没有上一层函数可以返回,main()函数中的return会被编译器替换成exit()),所以为了说明后续思路,作者调整了一下漏洞程序:
思路3的目的是,欺骗glibc认为chunk2所属的arena在EBP处(EBP为执行fvuln()函数时的EBP值,即fvuln()的栈帧起始位置,这个地方存的是main()的栈帧起始位置,往上4个字节存的是main()函数地址),这样,利用"fake_arene->max_fast = chunk2",就可以将fvuln()的返回地址修改为chunk2,而不再是main()函数,从而在fvuln()函数返回时,执行shell code。 但是,这样仍然存在fake_arena->mutex != 0的问题,因为main()的栈帧起始位置不可能为0。
THE HOUSE OF PRIME THE HOUSE OF PRIME适用于,可以利用bug控制malloc()返回值,并且漏洞程序中存在往该"分配内存"写用户输入的逻辑(比如先欺骗malloc()返回漏洞函数的EBP,后续通过用户输入,就可以改写漏洞函数的返回地址了),原文中的示范漏洞程序如下:
根据The House of Mind的经验,这里就不再过于详细说明了,直接将内存布局、构造数据,以及漏洞程序的执行流程,汇总如下: 漏洞程序执行过程说明: (1) free(ptr1) 伪造chunk1->size,欺骗glibc将main_arena->max_fast修改为chunk1的地址,正常逻辑中,max_fast最大可以等于512字节,表示小于512字节的都是fast chunk,被改成chunk1地址(0x80XXXXX),就表示大小不超过0x80XXXXX的chunk,glibc都会将其当作fast chunk处理。 (2) free(ptr2) 有了步骤(1)的铺垫,chunk2会被glibc当作fast chunk释放,并且归属于main_arena->fastbins[289],这显然已经在main_arena结构之外了,实际上正好与arena_key全局变量重叠(这是将chunk2->size构造为0x919的原因,可以事先通过objdump,查看arena_key与main_arena之间的偏移,并计算得到),从而又欺骗glibc将arena_key修改为chunk2,这样,通过构造chunk2的内容,就可以控制下一次malloc()返回任意想要的地址。 (3) ptr3 = malloc(1024) 经过步骤(2)后,glibc就会从arena_key分配内存,而arena_key->max_fast与chunk2->size是重叠的,即arena_key->max_fast=0x919,所以1032字节大小的chunk,会被当作fast chunk,从arena_key->fastbins[127]链表中摘取,根据构造数据可知,这个值为EBP。 (4) strncpy(ptr3, argv[2], 1024-1) 此时ptr3=EBP,执行strncpy(),就会向上覆盖返回地址的值,并且拷贝内容来自用户输入,显然可以覆盖成shell code的地址,根据布局图可以看见,shell code的位置可以有很多选择,具体构造就不再重复叙述了。 (5) 判断条件欺骗 用于欺骗判断条件的构造数据,布局图中已经使用虚线标出,都不难理解,这里主要说明一下_int_malloc()中的判断2,要求chunk3->size必须与分配大小匹配(示范漏洞程序的第三个启动参数,是写入栈中的,所以正好可以被利用,满足这一点)。 实际中漏洞程序,逻辑是不受攻击者控制的,比如示范漏洞程序中最后一次malloc()的参数值,使判断1不成立,_ini_free()就会走后续逻辑,从unsorted bin中分配内存: unsorted bin相当于一种缓存,每次尝试从unsorted bin分配chunk时,glibc总是从unsorted bin链表头依次取出其中的chunk,如果恰好满足分配内存的大小,就返回给调用者,否则转移到相应的fast bin(av->fastbins[x])或者bin(av->bins[x]),上图代码就是从unsorted bin链表头摘除chunk的过程,当中的if用于判断是否为last remainder chunk,构造数据可以很容易使其不成立,因此可以简化成以下4条语句:
通过最后一条语句可知,如果欺骗bck=EIP-8,就可以将漏洞函数的返回地址,改写为unsorted_chunks(av),即布局图中&arena_key->bins[0],再通过第一条和第二条语句可知,如果往unsorted_chunks(av)->bk即&av->bins[0]+12处放入x,并往x->bk处放入EIP-8,就可以使bck=EIP-8,所以,x值其实有很多选择,只要保证通过溢出数据可以覆盖到,并且不与其它关键的构造位置冲突即可,原文中选的是&av->bins[0]+4,那么相应地,往x->bk即&av->bins[0]+16处放入EIP-8,并在&arena_key->bins[0]位置构造shell code,即可实现利用。
/
*
*
K
-
sPecial's vulnerable program
*
/
int
main (void) {
char
*
ptr
=
malloc(
1024
);
/
*
First allocated chunk
*
/
char
*
ptr2;
/
*
Second chunk
*
/
/
*
ptr & ~(HEAP_MAX_SIZE
-
1
)
=
0x08000000
*
/
int
heap
=
(
int
)ptr &
0xFFF00000
;
_Bool found
=
0
;
printf(
"ptr found at %p\n"
, ptr);
/
*
Print
address of first chunk
*
/
/
/
i
=
=
2
because this
is
my second chunk to allocate
for
(
int
i
=
2
; i <
1024
; i
+
+
) {
/
*
Allocate chunks up to
0x08100000
*
/
if
(!found && (((
int
)(ptr2
=
malloc(
1024
)) &
0xFFF00000
)
=
=
\
(heap
+
0x100000
))) {
printf(
"good heap allignment found on malloc() %i (%p)\n"
, i, ptr2);
found
=
1
;
/
*
Go out
*
/
break
;
}
}
malloc(
1024
);
/
*
Request another chunk: (ptr2 !
=
av
-
>top)
*
/
/
*
Incorrect
input
:
1048576
bytes
*
/
fread (ptr,
1024
*
1024
,
1
, stdin);
free(ptr);
/
*
Free first chunk
*
/
free(ptr2);
/
*
The House of Mind
*
/
return
(
0
);
/
*
Bye
*
/
}
/
*
*
K
-
sPecial's vulnerable program
*
/
int
main (void) {
char
*
ptr
=
malloc(
1024
);
/
*
First allocated chunk
*
/
char
*
ptr2;
/
*
Second chunk
*
/
/
*
ptr & ~(HEAP_MAX_SIZE
-
1
)
=
0x08000000
*
/
int
heap
=
(
int
)ptr &
0xFFF00000
;
_Bool found
=
0
;
printf(
"ptr found at %p\n"
, ptr);
/
*
Print
address of first chunk
*
/
/
/
i
=
=
2
because this
is
my second chunk to allocate
for
(
int
i
=
2
; i <
1024
; i
+
+
) {
/
*
Allocate chunks up to
0x08100000
*
/
if
(!found && (((
int
)(ptr2
=
malloc(
1024
)) &
0xFFF00000
)
=
=
\
(heap
+
0x100000
))) {
printf(
"good heap allignment found on malloc() %i (%p)\n"
, i, ptr2);
found
=
1
;
/
*
Go out
*
/
break
;
}
}
malloc(
1024
);
/
*
Request another chunk: (ptr2 !
=
av
-
>top)
*
/
/
*
Incorrect
input
:
1048576
bytes
*
/
fread (ptr,
1024
*
1024
,
1
, stdin);
free(ptr);
/
*
Free first chunk
*
/
free(ptr2);
/
*
The House of Mind
*
/
return
(
0
);
/
*
Bye
*
/
}
fb
=
&(av
-
>fastbins[fastbin_index(size)]);
*
fb
=
p;
/
/
av
-
>max_fast
=
p
fb
=
&(av
-
>fastbins[fastbin_index(size)]);
*
fb
=
p;
/
/
av
-
>max_fast
=
p
int
fvuln()
{
/
/
原漏洞程序main()函数中的代码
}
int
main(
int
argc, char
*
argv[] )
{
return
fvuln();
}
int
fvuln()
{
/
/
原漏洞程序main()函数中的代码
}
int
main(
int
argc, char
*
argv[] )
{
return
fvuln();
}
void fvuln(char
*
str1, char
*
str2,
int
age)
{
int
local_age;
char
buffer
[
64
];
char
*
ptr
=
malloc(
1024
);
char
*
ptr1
=
malloc(
1024
);
char
*
ptr2
=
malloc(
1024
);
char
*
ptr3;
local_age
=
age;
strncpy(
buffer
, str1, sizeof(
buffer
)
-
1
);
printf(
"\nptr found at [ %p ]"
, ptr);
printf(
"\nptr1ovf found at [ %p ]"
, ptr1);
printf(
"\nptr2ovf found at [ %p ]\n"
, ptr2);
printf(
"Enter a description: "
);
fread(ptr,
1024
*
5
,
1
, stdin);
free(ptr1);
printf(
"\nEND free(1)\n"
);
free(ptr2);
printf(
"\nEND free(2)\n"
);
ptr3
=
malloc(
1024
);
printf(
"\nEND malloc()\n"
);
strncpy(ptr3, str2,
1024
-
1
);
printf(
"Your name is %s and you are %d"
,
buffer
, local_age);
}
int
main(
int
argc, char
*
argv[])
{
if
(argc <
4
) {
printf(
"Usage: ./hop name last-name age"
);
exit(
0
);
}
fvuln(argv[
1
], argv[
2
], atoi(argv[
3
]));
return
0
;
}
void fvuln(char
*
str1, char
*
str2,
int
age)
{
int
local_age;
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)