硬核技术帖:缓解绕过

缓解措施的实现中存在很多隐含的假设,这些假设的成立是缓解措施有效工作的前提,破坏这些假设就可以绕过缓解措施。

研发过程中应当尽量减少所作的假设,并且保证必要的假设都具有不变性,从而保证缓解措施能有效工作。

缓解措施

缓解措施是微软应对软件漏洞的深度防御体系中的一个重要环节,通过破坏漏洞利用过程中的一些关键技术,来达成阻止漏洞利用的目标。

目前,Windows 10中已经引入的缓解措施有:

  • SEHOP/SafeSEH
  • 堆随机化和元数据保护
  • 地址空间布局随机化 (ASLR)
  • 数据执行保护 (DEP)
  • 控制流防护 (CFG)
  • 任意代码防护 (ACG)
  • 代码完整性防护 (CIG)

 

缓解绕过

缓解绕过是指在启用了缓解措施的环境中,与缓解措施进行对抗,突破缓解措施的限制,并最终实现任意代码执行的过程。

 

要绕过缓解措施,首先需要了解缓解措施是怎样工作的。

袁哥曾经将安全的本质归纳为:“安全是一个条件语句。”

缓解措施,可以抽象成这样的条件语句:

if (is_allowed_by_mitigation_policy()) {
       do_sensitive_action();
} else {
       fail_fast();
}

这个条件语句中通常还隐含着许多假设,这些假设的成立是缓解措施有效工作的前提。

如果其中某个假设不具有不变性,攻击者就可能通过欺骗系统来破坏这一假设,从而绕过对应的缓解措施。

 

数据执行保护 (DEP)

数据执行保护对应的条件语句是:

if (PTE(address).NX == 0) {
      execute(address);
} else {
      fail_fast();
}

这里的假设有:

  • 代码段中的代码是可信的
  • 严格遵守 W^X 原则

代码段中的代码都是可信的吗?并不一定。

由于x86架构的CPU采用复杂指令集(CISC),每条指令字长并不相等,同样的数据从不同的位置开始解码可以得到不同的指令。因此,代码段中的代码可以被重新解码为其他指令,组合这些指令就可以实现任意代码执行,这正是ROP技术的基础。

W^X 原则能够被严格遵守么?很难。

因为这不仅仅意味着避免使用可读写执行(PAGE_EXECUTE_READWRITE) 内存,还要求在内存的整个生命周期中保持W^X。具备JIT功能的应用,比如浏览器,很容易破坏这一假设,从而被用来绕过数据执行保护。

控制流防护 (CFG)

控制流防护对应的条件语句是:

if (CFG_Bitmap[address] == 1) {
      call(address);
} else {
      fail_fast();
}

这里的假设有:

  • CFG Bitmap 中置位的地址是可信的
  • CFG 使用的指针是可信的

CFG Bitmap 中置位的地址都是可信的吗?并不一定。

实际上,由于CFG只是一个粗粒度的CFI实现,很多在CFG Bitmap 中置位的地址实际上并不应该被间接调用,滥用这些地址就可以绕过控制流防护,实现任意代码执行。

CFG 使用的指针都是可信的吗?

CFG实现中使用的2个关键指针__guard_check_icall_fptr和__guard_dispatch_icall_fptr仅仅只是通过只读内存来进行保护。

而目前Windows系统中的只读内存并不是真正的只读,通过纯数据攻击来修改只读内存并不困难。一旦这些指针被修改,控制流防护就形同虚设了。

 

任意代码防护 (ACG)

任意代码防护对应的条件语句是:

if (W^X(address, flNewProtect)) {
     change_protection(address, flNewProtect);
} else {
     fail_fast();
}

这里的假设有:

  • 本地的动态链接库是可信的

本地的动态链接库都是可信的吗?并不一定。

可以利用浏览器的缓存特性,将任意的动态链接库文件派发到本地,从而绕过任意代码防护的限制。

 

代码完整性防护 (CIG)

代码完整性防护对应的条件语句是:

if (is_signed_by_microsoft(file)) {
       create_section(file);
} else {
       fail_fast();
}

这里的假设有:

  • 微软签名的动态链接库是可信的

微软签名的动态链接库都是可信的吗?并不一定。

旧版的动态链接库在新的系统中可能会表现出不一样的行为,比如ntdll.dll中的系统调用,由于系统调用号的变化,版本6.3.9600.17936中的NtQueryDefaultUILanguage函数与版本10.0.15063.0中的NtContinue函数实质上是等同的,而前者是允许被间接调用的。因此,可以加载版本6.3.9600.17936的ntdll.dll,然后调用其中的NtQueryDefaultUILanguage函数,来实现对NtContinue函数的调用,从而实现任意代码执行。

 

总结

缓解措施的实现中存在很多隐含的假设,这些假设的成立是缓解措施有效工作的前提,破坏这些假设就可以绕过缓解措施。

研发过程中应当尽量减少所作的假设,并且保证必要的假设都具有不变性,从而保证缓解措施能有效工作。

Spread the word. Share this post!

Meet The Author

Leave Comment