内存保护的安全性技术
(1)使用GS 编译技术,在函数返回地址之前加入了Security Cookie,在函数返回前首先检测Security Cookie 是否被覆盖,从而把针对操作系统的栈溢出变得非常困难。
(2)增加了对S.E.H 的安全校验机制,能够有效地挫败绝大多数通过改写S.E.H 而劫持进程的攻击。
(3)堆中加入了Heap Cookie、Safe Unlinking 等一系列的安全机制,为原本就困难重重的堆溢出增加了更多的限制。
(4)DEP(Data Execution Protection,数据执行保护)将数据部分标示为不可执行,阻止了栈、堆和数据节中攻击代码的执行。
(5)ASLR(Address space layout randomization,加载地址随机)技术通过对系统关键地址的随机化,使得经典堆栈溢出手段失效。
(6)SEHOP(Structured Exception Handler Overwrite Protection,S.E.H 覆盖保护)作为对安全S.E.H 机制的补充,SEHOP 将S.E.H 的保护提升到系统级别,使得S.E.H 的保护机制更为有效。
GS(Buffer Security Check)1
2
3
4
5
6
int
test()
{
char sz[
10
]
=
{
0
};
memset(sz,
0x88
,
9
);
return
0
;
}
1
2
3
4
5
6
7
8
9
mov eax, ___security_cookie ; rand
-
>eax
xor eax, ebp ; xor
mov [ebp
+
var_4], eax
/
/
随机值___security_cookie xor ebp;置于local1;
/
/
在函数末尾找到;
mov ecx, [ebp
+
var_4]
xor ecx, ebp ; StackCookie
call j_@__security_check_cookie@
4
; __security_check_cookie(x)
/
/
调用安全检查;
\1.png) 例如
1
2
3
4
5
6
7
void vuln(char
*
arg)
{
char buf[
100
];
int
i;
strcpy(buf, arg);
...
}
1
2
3
4
5
6
copy of arg
i
buf
stack cookie
return
address
arg
当栈中发生溢出时,Security Cookie 将被首先淹没,之后才是EBP 和返回地址。 在函数返回之前,系统将执行一个额外的安全验证操作过程中,系统将比较栈帧中原先存放的Security Cookie 和.data 中副本的值,如果两者不吻合,说明栈帧中的Security Cookie 已被破坏,即栈中发生了溢出。调用j____report_gsfailure去分发异常,不会给我们到ret的机会; 但是:The extra prologue and epilogue code can add a significant overhead to small functions. For example, the following variables will cause the functions containing them to be protected by GS:
1
2
3
4
5
6
7
8
char a[
5
];
/
/
protected,
5
byte array of elements of size
1
short b[
3
];
/
/
protected,
6
byte array of elements of size
2
struct {
char a;
} c[
5
];
/
/
protected,
5
byte array of elements of size
1
struct {
char a[
5
];
} d;
/
/
protected because the structure contains a string
buffer
the variables below will not trigger the GS heuristic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
char e[
4
];
/
/
not
protected, total size
is
less than
5
bytes
int
f[
10
];
/
/
not
protected, array element size greater than
2
char
*
g[
10
];
/
/
not
protected, array element size greater than
2
struct {
char a;
short b;
} h[
5
];
/
/
not
protected, array element size greater than
2
struct {
char a1;
char a2;
char a3;
char a4;
char a5;
} i;
/
/
not
protected, the structure does
not
contain a string
buffer
正如上面说的;gs机制并不都有使用,对性能开销比较大,可以使用#pragma strict_gs_check来进行强制使用;
绕过方案 未被保护内存绕过正如上文说的the variables below will not trigger the GS heuristic: ;存在程序使用未被保护内存,那么可以直接使用以前溢出返回地址进行绕过;
异常处理绕过需要先了解异常处理的一些机制; \2.png)
\3.png)
我们可以这样思考,但现在无法淹没ret;是不是可以让shellcode长一点,导致淹没异常处理函数地址所在的栈区;触发异常使得通过异常处理函数地址让我们进入我们的shellcode; 但是这里需要注意;我们需要模拟一遍异常处理流程;
1
2
3
4
5
6
7
8
9
10
11
12
13
__try
{
char sz[
10
]
=
{
0
};
memset(sz,
0x88
,
9
);
__asm mov eax,
0x00000000
;
__asm mov[eax], ebx;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
printf(
"hello"
);
getchar();
}
1
2
3
4
004116FF
64
:A1
00000000
mov eax,dword ptr fs:[
0
]
00411705
50
push eax
/
/
这里将异常链push到栈中
给到地址;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
00411C80
>
55
push ebp
00411C81
8BEC
mov ebp,esp
00411C83
8B45
14
mov eax,dword ptr ss:[ebp
+
0x14
]
00411C86
50
push eax
00411C87
8B4D
10
mov ecx,dword ptr ss:[ebp
+
0x10
]
00411C8A
51
push ecx
00411C8B
8B55
0C
mov edx,dword ptr ss:[ebp
+
0xC
] ; GS.<ModuleEntryPoint>
00411C8E
52
push edx
00411C8F
8B45
08
mov eax,dword ptr ss:[ebp
+
0x8
] ; GS.<ModuleEntryPoint>
00411C92
50
push eax
00411C93
68
6C124100
push GS.
0041126C
; ASCII E9,
"o\t"
00411C98
68
04904100
push offset GS.__security_cookiemain_rea>
00411C9D
E8 D6F3FFFF call GS.
00411078
00411CA2
83C4
18
add esp,
0x18
00411CA5
5D
pop ebp ; vcruntim.
788C4ED8
00411CA6
C3 retn
00411C9D E8 D6F3FFFF call GS.00411078; 00411078 /E9 393C0000 jmp GS._except_handler4_commonestroy_lis> +; jmp 到 vcruntim._except_handler4_common 00414CB6 >- FF25 A4A04100 jmp dword ptr ds:[<&VCRUNTIME140D._excep> ; vcruntim._except_handler4_common
1
2
3
4
5
6
7
788C3698
E8
33FCFFFF
call vcruntim.
788C32D0
788C369D
33C0
xor eax,eax
788C369F
33DB
xor ebx,ebx
788C36A1
33C9
xor ecx,ecx
788C36A3
33D2
xor edx,edx
788C36A5
33FF
xor edi,edi
788C36A7
-
FFE6 jmp esi ; GS.
0041177A
最后在此调用了我们的异常处理函数; 这其中是否有对异常函数地址的检查;我们需要进一步认证;我们人为进行修改处理函数地址; \4.png) 将他修改为memset地址; 实际上还是调用原来的处理函数;那么我们现在尝试怎么办呢; 再进行更改;这说明一点当push进fs[0]头节点以后,在对hander进行修改;实际上无济于事; 回头进行检查,发现我们设置的是第二个异常链(失误); 再次设置发现,并没有跳转到memset; 再次设置 \5.png) 发生中断,说明通过了检查,合理猜测,在调用异常函数时,会检查是否在用户领空,多次测试,复合验证, 然后分析异常处理函数栈布局;
1
2
3
4
5
RetAddr:——except_handler();
param_1:_exceptionRecord;
param_1:_EstablisherFrame;
param_1:_ContextRecord;
param_1:_DispatcherContext;
我们关注;_ContextRecord;距离我们的之前栈结构 多了两个废弃参数; 使用pop xx;pop xx;ret 就可以实现; 而后使用EB 06短跳;不需要使用滑板指令就可以跳转到我们的shell code; \6.png) 上图为shellcode构造; 现在开始验证; \7.png) 符合预期;
覆盖虚函数绕过...改天再学
同时替换.data和栈中安全因子...改天再学;
DEP(Data Execution Protection)关闭操作系统dep支持; \8.png) 技术思路; DEP的本质就是取出内存可写区域的可在执行属性;从而通过将shellcode写入buffer伺机执行的方案不再有效; 但是我们可以使用Return oriented programming ROP方式去进行修改; \9.png) (1)通过跳转到VirtualProtect函数将DEP 关闭后再转入shellcode 执行。 (2)通过跳转到VirtualProtect 函数来将shellcode 所在内存页设置为可执行状态,然后再 转入shellcode 执行。 (3)通过跳转到VIrtualAlloc 函数开辟一段具有执行权限的内存空间,然后将shellcode 复 制到这段内存中执行。 ; 但是疑问接踵而来,我们不知道VirtualProtect函数地址;
返回导向编程(Return oriented programming)是一种典型的代码复用技术:它通过复用以ret指令结束的代码片段来构造功能代码。当前,主流系统主要通过部署址空间布局随机化(Address Space Layout Randomization,ASLR)机制来缓解代码复用攻击;构造功能复杂的ROP代码比编写shellcode困难得多。在构造ROP代码的过程中,如果缺少某种类型的gadget或不能消除gadgets之间的副作用,都会导致构造失败
常见gadgets对于 gadgets 能做的事情,基本上只要你敢想,它就敢执行。下面简单介绍几种用法:
保存栈数据到寄存器
将栈顶的数据抛出并保存到寄存器中,然后跳转到新的栈顶地址。所以当返回地址被一个 gadgets 的地址覆盖,程序将在返回后执行该指令序列。
如:pop eax; ret
保存内存数据到寄存器
将内存地址处的数据加载到内存器中。
如:mov ecx,[eax]; ret
保存寄存器数据到内存
将寄存器的值保存到内存地址处。
如:mov [eax],ecx; ret
算数和逻辑运算
add, sub, mul, xor 等。
如:add eax,ebx; ret, xor edx,edx; ret
系统调用
执行内核中断
如:int 0x80; ret, call gs:[0x10]; ret
会影响栈帧的 gadgets
这些 gadgets 会改变 ebp 的值,从而影响栈帧,在一些操作如 stack pivot 时我们需要这样的指令来转移栈帧。
如:leave; ret, pop ebp; ret
构造rop链先等到以后学习;今天还有任务; rop链布局; \10.png)
ASLR(Address space layout randomization)ASLR 的出现使得shellcode 中的关键跳转只能在系统重启前,甚至只有程序的本次运行时 才能执行,这使得exploit 的难度大大增加。道高一尺,魔高一丈,任何一种保护技术都有一些 自身的弱点,攻击者已经用事实告诉人们ASLR 不是不可以突破的。 首先我们来看看ASLR 中最重要的部分——映像随机化。由于ASLR 将所有受保护模块的 加载基址都做了随机化处理,我们以前找到的通用跳板指令的地址也就不再固定,这些指令也 就失去了意义。但这个随机过程是不是完美无疵的呢?答案是否定的,细心的读者在看图9.6.1 的时候会发现一个现象,虽然模块的加载基址变化了,但是各模块的入口点(Entry 那列)地 址的低位2 个字节是不变的,也就是说映像随机化只是对加载基址的前2 个字节做了随机处理。
利用部分覆盖进行定位内存地址 \11.png) 这个方法比较简单;没啥说的
Heap spray技术此段分析;详见cve-2012-189;
参考文献教材 0day2Bypassing Browser Memory Protections CTF all in one
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2023-5-5 19:41
被john_大白编辑
,原因:
上传的附件: