Linux内核安全研究之Stack Overflow溢出
by wzt <wzt.wzt@gmail.com>
一、背景:
Stack overflow与我之前发过的Stack buffer overflow是两个不同的概念, 它们都是发生在内核stack中的溢出。
Jon Oberheide在他的blog中提到了一种新的stack overflow溢出攻击方式, 大致说了下溢出原理,没给出poc,我尝试研究了一下,
把这几天的调试方法总结下。
二、理解内核堆栈:
当user space的程序通过int 0x80进入内核空间的时候,CPU自动完成一次堆栈切换, 从user space的stack切换到kernel space的stack。
在这个进程exit之前所发生的所有系统调用所使用的kernel stack都是同一个。kernel stack的大小一般为4096/8192,我画了个内核堆栈示意图帮助大家理解:
内存低址 内存高址
| |<-----------------------------esp|
+-----------------------------------4096-------------------------------+
| 72 | 4 | x < 4016 | 4 |
+------------------+-----------------+---------------------------------+
|thread_info | | STACK_END_MAGIC | var/call chain |stack_canary |
+------------------+-----------------+---------------------------------+
| 28 | 44 | | |
V | |
restart_block V
esp+0x0 +0x40
+---------------------------------------------------------------------------+
|ebx|ecx|edx|esi|edi|ebp|eax|ds|es|fs|gs|orig_eax|eip|cs|eflags|oldesp|oldss|
+---------------------------------------------------------------------------+
| kernel完成 | cpu自动完成 |
在老的内核中, 用struct task_struct来描述一个进程结构, 在新的内核里, task_struct结构又被包装在struct thread_info里:
struct thread_info {
struct task_struct *task; /* main task structure */
struct exec_domain *exec_domain; /* execution domain */
__u32 flags; /* low level flags */
__u32 status; /* thread synchronous flags */
__u32 cpu; /* current CPU */
int preempt_count; /* 0 => preemptable,
<0 => BUG */
mm_segment_t addr_limit;
struct restart_block restart_block;
void __user *sysenter_return;
#ifdef CONFIG_X86_32
unsigned long previous_esp; /* ESP of the previous stack in
case of nested (IRQ) stacks
*/
__u8 supervisor_stack[0];
#endif
int uaccess_err;
};
它的第一个字段就指向当前进程的task_stuct指针, 注意是指针, 而不是一个结构体,task_struct在我的2.6.36.2内核中的大小是1196字节,而thread_info
大小为72字节, 所以保存一个指针将会非常节省内核堆