原来根据FLASHSKY大牛在峰会上的报告分析了这个漏洞写了两篇随笔,,因为写得仓促,里面难免有不少错漏,朋友建议我汇总一下,也方便日后参考,于是把两篇文章汇总并做了些修改。这个漏洞与大多数堆栈溢出漏洞不同的是它是发生在内核态里。堆栈溢出发生于SYMANTEC防火墙的驱动SYMDNS.SYS中,当处理DNS答复时,由于未检验总域名长度,导致可以输入一超长域名导致溢出,溢出发生在RING0、IRQL = 2(DISPATCH_LEVEL)、 进程PID为0(idle进程)的环境下。一个DNS报文格式如下:"\xEB\x0B"//报文ID,可以随意设置,但在这个漏洞里是别有用途的,后面会说到"\x80\x00"//报文FLAG,15位置1表示这是一个答复报文"\x00\x01"//问题数量"\x00\x01"//答复数量"\xXX\xXX"//授权资源记录数,在这里不重要,随便设置"\xXX\xXX"//格外信息资源记录数,在这里不重要,随便设置以上部分为DNS报文头"\xXX\xXX\x..."//域名,格式为每个分段域名长度+域名内容,比如www.buaa.edu.cn就是\x03\x77\x77\x77\x04\x62\x75\x61\x61\x03\x65\x64\x75\x02\x63\x6e\x00w w w b u a a e d u c n\x00表示到了末尾。处理的时候会把那长度记录数换成0x2e,就是".",就完成了处理。在SYMDNS.SYS中处理传入域名的函数位于SYMDNS.SYS基地址+0xa76处,这个函数在堆栈里分配了0x214个字节空间,再将域名拷入,虽然会计算总长度并做限制,但由于计算错误,算了也是白算,导致可以输入超长域名发生堆栈溢出。传入的每个域名分段有最大长度限制,不能超过0x40个字节,所以我每段SHELLCODE长度都是0x3f(63)个字节。在覆盖了532个字节后,覆盖了该函数的返回地址。这个漏洞有个特点,就是在堆栈中二次处理传入的域名,导致堆栈中返回地址之前的SHELLCODE的后半部分面目全非、惨不忍睹。现在有两种执行SHELLCODE的方法:一是在我们覆盖的返回地址所在的esp+0xc处保存有我们整个DNS报文(包括DNS报文头)的地址,是一个在非分页池的地址,74816d74 4c816c9b 816d002e 816c9e34|_____esp指向这 |_______这个就是非分页池的地址现在大家应该知道该干啥了吧?虽然在内核里没有固定的jmp [esp+0xc]、 call [esp+0xc]这样的地址,但我们可以变通一下,使用诸如pop/pop/pop/ret这样的指令组合,机器的控制权就交到我们手上了。不过这3条pop指令里最好不要带有pop ebp,不然会莫名其妙的返回到一个奇怪的地址。在strstr函数的最后有两个pop/pop/pop/ret的组合挺合适。现在明白开头那个报文ID的作用了吧?\xEB\x0B是一个直接跳转的机器指令,跳过一开始没用的DNS报文头和第一段SHELLCODE长度计数字节。FLASHSKY在会刊里说要跳过长度计数字节,但0x3f对应的指令是aas,对EAX进行ascii调整,所以在一般不影响EAX和标志的情况下可以把这个0x3f也算作SHELLCODE的一部分,可以省下不少字节^_^。二是在堆栈里覆盖的返回地址的esp+0x8处开始执行我们的SHELLCODE,返回地址之后的SHELLCODE那个函数不会做处理。但如果SHELLCODE实在太长的话会覆盖到有关DPC调度的一些信息。一个变通的方法,可以先跳回返回地址前的SHELLCODE的前半部分没被修改的部分,可以执行接近200个字节,再跳到返回地址后的SHELLCODE部分执行,这样空间就应该足够了。但由于堆栈中的SHELLCODE的每个段开始的0x3f已经被换成了0x2e,0x2e不单独对应机器码,所以只能在每个SHELLCODE段的最后部分改成\xeb\x01跳过0x2e。在安全返回法里,由于要取出堆栈里后面函数的返回地址,不能覆盖太多,只能使用第一种方法在池中执行SHELLCDOE,安全返回法的内核态SHELLCODE只有230个字节左右,池中还剩下310个字节左右可以利用。在非安全返回法由于关于DPC调度、被锁定的资源等关键数据所处堆栈位置距离溢出点比较大,所以可以在堆栈中执行。