-
-
[原创]漏洞原理笔记——堆溢出
-
发表于: 2021-4-14 18:41 7277
-
最近在学习堆的溢出原理,但是查了网上和书上的一些讲解,总是感觉缺少一些关键点。所以理解起来总是有点晕,经过努力的调试分析后,总结了下面的更为详细的堆溢出原理。如果不准确的地方,希望大佬可以提醒一哈~
下面这个是winDbg中的数据结构截图。
这两个结构仅仅就差了个FreeList。
当堆溢出发生时,可能会将后面的堆结构全部覆盖掉。而后面的堆有可能是个使用过的_HEAP_ENTRY,也有可能是一个空闲堆_HEAP_FREE_ENTRY。
如果当后面是一个空闲堆时,就可以将_HEAP_FREE_ENTRY最后的FreeList结构覆盖成自己想要的数据。而FreeList是一个_LIST_ENTRY结构。这个结构中有两个成员,分别是Flink和Blink。分别表示的是上一个空闲堆和下一个空闲堆。
那么关键来了,当你调用HeapFree释放刚刚使用的堆时,就会触发一种机制,就是将释放的堆与后面的空闲堆相合并。但此时后面的空闲堆已经被你污染覆盖了。而在合并的过程中,他会修改空闲堆Blink和Flink所指向的空闲堆中的FreeList,以便让前后的空闲堆衔接在一起(因为空闲堆就是一个双向链表,当某一个节点发生改变时,你就要需要把前面的节点指针和后面的节点指针都重新做一遍调整,也就是重新连接双链表节点)。
这个机制就直接导致了堆溢出利用的可能性。因为不管是什么溢出,我们最终的目的都是控制EIP,就是覆盖栈中的返回地址。而到现在,可能还有人没有意识到问题的严重性,那么继续。
假设:需要释放的堆为A,后面被覆盖污染的空闲堆为B。
此时被污染覆盖的空闲堆B中的Flink/Blink已经是我们可以随意修改的地址。那么当释放的堆A和污染空闲堆B进行合并时,会变成一个更大的空闲堆C,那么此时空闲堆C的首地址就会发生改变,所以需要重新连接空闲堆链表。想要重新连接,就需要找到上一个空闲堆,然后并将其中的Blink修改为合并后空闲堆C的首地址。可能已经有人感觉到一丝丝的不寻常了,继续。
因为合并后的空闲堆C中的Flink和Blink是我们可以随意修改的。这就导致上一个空闲堆是我们可以随便指定的。而系统根本不知道我们会指向那里,他依旧会傻傻的去修改所指向地方的Blink。假如此时我们将空闲堆C中的Flink地址覆盖成栈地址。那么他就会把栈当成上一个空闲堆。然后去栈中寻找Blink的位置,并将其修改为空闲堆C的首地址。虽然我们的堆A已经释放了,但里面的数据还在啊,系统可不会勤快的置空呢。而空闲堆C的首地址,其实就是堆A的首地址。因为A和B合并时,其实就是把B叠在了A的屁股后面。
释放了一个有用堆。然后他会和后面的空闲堆合并在一起。而且空闲堆首地址也改变了。
而A中是有我们自己的数据的。这样就可以将栈中的数据覆盖成我们自己的数据,甚至长度算好了也可以覆盖栈中的返回地址。
OK,这就是堆的溢出的其中之一原理啦,如果有错误的地方,希望大家指正。
struct _LIST_ENTRY{
DWORD Flink;
DWORD Blink;
}
/
/
空闲堆头结构
struct _HEAP_FREE_ENTRY{
union{
struct{
union{
struct {
WORD Size;
BYTE Flags;
BYTE SmallTagIndex;
}
DWORD SubSegmentCode;
}
WORD PreviousSize;
union{
BYTE SegmentOffset;
BYTE LFHFlags;
}
BYTE UnusedBytes;
};
struct{
union{
struct {
WORD FunctionIndex;
WORD ContextValue;
};
DWORD InterceptorValue;
};
WORD UnusedBytesLength;
BYTE EntryOffset;
BYTE ExtendedBlockSignature;
};
struct{
DWORD Code1;
WORD Code2;
BYTE Code3;
BYTE Code4;
};
ULONGLONG AgregateCode;
};
_LIST_ENTRY FreeList;
}
/
/
已使用堆的头结构
struct _HEAP_ENTRY{
/
/
这个结构只比_HEAP_FREE_ENTRY少了一个FreeList;
}
/
/
上面的结构不是百分百正确的,这些都是通过WinDbg调试猜的。
struct _LIST_ENTRY{
DWORD Flink;
DWORD Blink;
}
/
/
空闲堆头结构
struct _HEAP_FREE_ENTRY{
union{
struct{
union{
struct {
WORD Size;
BYTE Flags;
BYTE SmallTagIndex;
}
DWORD SubSegmentCode;
}
WORD PreviousSize;
union{
BYTE SegmentOffset;
BYTE LFHFlags;
}
BYTE UnusedBytes;
};
struct{
赞赏
- [原创]Wannacry勒索病毒样本分析 16231
- [原创]漏洞原理笔记——堆溢出 7278