
我们可以看到,整个“影子栈”区域是一个以0x00007A00~00000000开始的reserved区域。想来这里面应该有一些trick影藏在其中,
因为NtQueryVirtualMemory/VirtualQueryEx通过解析vadroot来获得当前进程的内存分配情况,如果vad里面存储的“影子栈”就是一个512G的
整体区域,那么在内核中针对每一个线程为什么能区分出这些“影子栈”的边界。显然上述API获得的信息是不全面的。通过调试我们来探测出这个整
体影子栈的内存布局情况。我们可以在nt!PspAllocateProcess中获得刚刚创建的Edge进程,然后在MmSwapThreadControlStack获得相应的“影子栈”地址
和真实线程栈地址,测试出的信息如下:
shadowstackbase:00007adcbab00000 - threadstack:0000009547800000 = userfs:00007a4773300000
shadowstackbase:00007adcbac00000 - threadstack:0000009547900000 = userfs:00007a4773300000
shadowstackbase:00007adcbad00000 - threadstack:0000009547a00000 = userfs:00007a4773300000
shadowstackbase:00007adcbae00000 - threadstack:0000009547b00000 = userfs:00007a4773300000
shadowstackbase:00007adcbaf00000 - threadstack:0000009547c00000 = userfs:00007a4773300000
shadowstackbase:00007adcbb000000 - threadstack:0000009547d00000 = userfs:00007a4773300000
shadowstackbase:00007adcbb100000 - threadstack:0000009547e00000 = userfs:00007a4773300000
shadowstackbase:00007adcbb200000 - threadstack:0000009547f00000 = userfs:00007a4773300000
shadowstackbase:00007adcbb300000 - threadstack:0000009548000000 = userfs:00007a4773300000
shadowstackbase:00007adcbb400000 - threadstack:0000009548100000 = userfs:00007a4773300000
shadowstackbase:00007adcbb500000 - threadstack:0000009548200000 = userfs:00007a4773300000
shadowstackbase:00007adcbb600000 - threadstack:0000009548300000 = userfs:00007a4773300000
shadowstackbase:00007adcbb700000 - threadstack:0000009548400000 = userfs:00007a4773300000
shadowstackbase:00007adcbb800000 - threadstack:0000009548500000 = userfs:00007a4773300000
shadowstackbase:00007adcbb900000 - threadstack:0000009548600000 = userfs:00007a4773300000
shadowstackbase:00007adcbba00000 - threadstack:0000009548700000 = userfs:00007a4773300000
shadowstackbase:00007adcbbb00000 - threadstack:0000009548800000 = userfs:00007a4773300000
shadowstackbase:00007adcbbc00000 - threadstack:0000009548900000 = userfs:00007a4773300000
由上面的信息可以得知,在15002开始以后的版本“影子栈”的布局变成了以100000为边界的内存区域。但哪部分才是可写的内容,可以通过当前
线程的userfs + rsp的值来计算出。
[0x03] 突破RFG的可能性
首先我们回顾一下,RFG防护的主要目的是防止恶意的对用户栈的篡改。通过分配一段512G空间的区域将各个线程的“影子栈”放入其中。
在一个被保护函数结束前进行栈数据的比对。RFG的强度在于,
1) 用户态无法控制fs段寄存器指向,这是由内核态来决定的
2) 攻击者很难猜测出“影子栈”所在的内存位置
对于情况1,用户态我们很难做到修改,对于情况2我谈谈曾经考虑过的一些攻击方式。
在应对14986的版本时,考虑的优化后的搜索“影子栈”是有可能的情况。继而我们需要寻找利用的任意地址读写功能。在Edge中首先需要考虑如何任意
内存地址读写,通常的内存层面暴力搜索是存在的问题的,因为“影子栈”之间插入的reversed的内存,读指令会引发程序异常。这里有个利用技巧就是在该
版本中lstrcpyA函数是未被CFG的,我们可以利用该函数来测试一个指定的内存区域是否可读、写。而且该函数仅需要2个参数,利用漏洞较容易控制其利用,
在读到一个reversed空间内存时该函数不会触发异常。
LPTSTR WINAPI lstrcpy(
_Out_ LPTSTR lpString1,
_In_ LPTSTR lpString2
);本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。