漏洞利用"BadIRET"分析(CVE-2014-9322, Linux内核提权)

作者:Rafal Wojtczuk,Feb 2 2015

原文:Exploiting “BadIRET” vulnerability (CVE-2014-9322, Linux kernel privilege escalation)

译者:Shawn the R0ck, Feb 5 2015

POC( 感谢Mickey提供的链接): https://rdot.org/forum/showthread.php?t=3341

Solidot相关报道: http://www.solidot.org/story?sid=42951

Shawn:Linux内核代码文件arch/x86/kernel/entry_64.S在3.17.5之前的版本都 没有正确的处理跟SS(堆栈区)段寄存器相关的错误,这可以让本地用户通过触 发一个 IRET指令从错误的地址空间去访问GS基地址来提权。这个编号为 CVE-2014-9322,漏洞于2014年11月23日被Linux内核社区修复,之后的几个礼拜里 没有出现公开的利用代码甚至相关的讨论。当人们快要遗忘这个威胁的时候, Rafal Wojtczuk的分析文章Exploiting “BadIRET” vulnerability似乎在提醒我 们:别忘了斯拉夫兵工厂。Rafal在Fedora 20 64-bit GNU/Linux发行版上完成了 研究和测试工作,内核是3.11.10-301。也提醒那些平时不重视安全运维的企业和 个人,修复已知漏洞是必须的工作,因为你永远不知道你的敌人在哪里买卖什么 样的数字军火。对于这个漏洞,本文的结论是SMEP虽然被绕过了,但SMAP是依然 奏效的,这里只想提一下类似PaX/Grsecurity的UDEREF特性和SMAP类似,只是属 于纯软件实现,大概2006/2007年左右这个特性就已经有了而且被一些anarchy广 泛使用。这次Rafal也谈到说这个如此严重的漏洞居然数月都没有公开讨论,但其 实在12月中旬俄罗斯的某技术社区就已经进行了详细讨论并最终给出了PoC,这是 一次针对非英文世界的公开威胁情报分析的重大失误的典型案例。随着Intel在 CPU里实现了类似UDEREF的SMEP和SMAP等特性,主流GNU/Linux发行版的内核也在 逐渐支持此类特性,防御在升级的同时,欧罗巴的数字军火也在不断升级,2014 年已经有多个团队实现了不touch用户空间的SYSRET漏洞利用。

这个漏洞影响很广,Debian几乎所有社区还在维护的版本都受影响。

RedHat的企业版RHEL 4/5/6/7都受影响。

0. Intro

CVE-2014-9322的描述如下:

linux内核代码文件arch/x86/kernel/entry_64.S在3.17.5之前的版本都没有正确
的处理跟SS(堆栈区)段寄存器相关的错误,这可以让本地用户通过触发一个
IRET指令从错误的地址空间去访问GS基地址来提权。

这个漏洞于2014年11月23日被社区修复[2],至今我并没有见到公开的利用代码和 详细的讨论。这篇文章我会尝试去解释这个漏洞的本质以及利用的过程。不幸的 是,我无法完全引用Intel白皮书[3]的所有内容,如果有读者不熟悉一些术语可 以直接查Intel白皮书。所有的实验都是在Fedora 20 64-bit发行版上完成的,内 核是3.11.10-301,所有的讨论基于64位进行。

简单结论概要:

  1. 通过测试,这个漏洞可以完全稳定的被利用。

  2. SMEP[4]不能阻止任意代码执行;SMAP[5]可以阻止任意代码执行。

Digression: kernel, usermode, iret

2. 漏洞

在一些情况下,linux内核通过iret指令返回用户空间时会产生一个异常。异常处 理程序把执行路径返回到了bad_iret函数,她做了:

     /* So pretend we completed the iret and took the #GPF in user mode.*/
     pushq $0
     SWAPGS
     jmp general_protection

正如这行评论所解释,接下来的代码流应该和一般保护异常(General Protection)在用户空间发生时(转跳到#GP处理程序)完全相同。这种异常处理 情况大多是由iret指令引发的,e.g. #GP。

问题在于#SS异常。如果有漏洞的内核(比如3.17.5)也有”espfix”功能(从 3.16引入的特性),之后bad_iret函数会在只读的栈上执行”push”指令,这会导 致页错误(page fault)而会直接引起两个错误。我不考虑这种场景;从现在开 始,我们只关注在3.16以前的没有”espfix”的内核。

这个漏洞根源于#SS的异常处理程序没有符合 “pretend-it-was-#GP-in-userspace”[6]的规划,与#GP处理程序相比,#SS异常 处理会多做一次swapgs指令。如果你对swapgs不了解,请不要跳过下面的章节。

3. 偏题:swapgs指令

当内存通过gs段进行访问时,像这样:

mov %gs:LOGICAL_ADDRESS, %eax

实际会发生以下几步:

  1. BASE_ADDRESS值从段寄存器的隐藏部分取出

  2. 内存中的线性地址LOGICAL_ADDRESS+BASE_ADDRESS被dereferenced(Shawn: char *p; *p就是deref)。

基地址是从GDT(或者LDT)继承过来的。无论如何,有一些情况是GS段基地址被 修改的动作不需要GDT的参与。

引用自Intel白皮书:

“SWAPGS把当前GS基寄存器值和在MSR地址C0000102H(IA32_KERNEL_GS_BASE)所包 含的值进行交换。SWAPGS指令是一个为系统软件设计的特权指令。(….)内核可 以使用GS前缀在正常的内存引用去访问[per-cpu]内核数据结构。”

Linux内核为每个CPU在启动时分配一个固定大小的结构体来存放关键数据。之后 为每个CPU加载IA32_KERNEL_GS_BASE到相应的结构地址上,因此,通常的情况, 比如系统调用的处理程序是:

  1. swapgs(现在是GS指向内核空间)

  2. 通过内存指令和gs前缀访问per-cpu内核数据结构

  3. swapgs(撤销之前的swapgs,GS指向用户空间)

  4. 返回用户空间

内核代码天生必须保证随时能访问到带gs前缀的percpu数据,内核执行一堆 swapgs指令时条目来自用户空间并不是偶然。(所以gs base指向内核内存)

4. 触发漏洞

现在很明显可以看到这个漏洞简直就是坟墓,因为多了一个swapgs指令在有漏洞 代码路径里,内核会尝试从可能被用户操控的错误GS基地址访问重要的数据结构。

当iret指令产生了一个#SS异常?有趣的是,Intel白皮书在这方面介绍不完全( Shawn:是阴谋论的话又会想到BIG BROTHER?);描述iret指令时,Intel白皮书这 么讲:

64位模式的异常:

#SS(0)

如果一个尝试从栈上pop一个值违反了SS限制。

如果一个尝试从栈上pop一个值引起了non-canonical地址(Shawn: 64-bit下只允
许访问canonical地址)的引用。

没有一个条件能被强制在内核空间里发生。无论如何,Intel白皮书里的iret伪代 码展示了另外一种情况:when the segment defined by the return frame is not present:

IF stack segment is not present

THEN #SS(SS selector); FI;

所以在用户空间,我们需要设置ss寄存器为某个值来表示不存在。这不是很直接: 我们不能仅仅使用:

mov $nonpresent_segment_selector, %eax

mov %ax, %ss

第二条指令会引发#GP。通过调试器(任何ptrace)设置ss寄存器是不允许的;类 似的,sys_sigreturn系统调用不会在64位系统上设置这个寄存器(可能32位能工 作)。解决方案是:

  1. 线程A:通过sys_modify_ldt系统调用在LDT里创建一个定制段X

  2. 线程B:s:=X_selector

  3. 线程A:通过sys_modify_ldt使X无效

  4. 线程B:等待硬件中断

为什么需要在一个进程里使用两个线程的原因是从系统调用(包括 sys_modify_ldt)返回是通过硬编码了#ss值的sysret指令。如果我们使X在相同 的线程中无效就等同于”ss:=X 指令“,ss寄存器会处于未完成设置的状态。运行 以上代码会导致内核panic。按照更有意义的做法,我们将需要控制用户空间的 gs基地址;她可以通过系统调用arch_prctl(ARCH_SET_GS)被设置。

5. Achieving write primitive

如果运行以上代码,#SS处理程序会正常的返回bad_iret(意思是没有触及到内存 的GS基地址),之后转跳到#GP异常处理程序,执行一段时间后就调用到了这个函 数:

289 dotraplinkage void
290 do_general_protection(struct pt_regs *regs, long error_code)
291 {
292         struct task_struct *tsk;
...
306         tsk = current;
307         if (!user_mode(regs)) {
                ... it is not reached
317         }
318 
319         tsk->thread.error_code = error_code;
320         tsk->thread.trap_nr = X86_TRAP_GP;
321 
322         if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) &&
323                         printk_ratelimit()) {
324                 pr_info("%s[%d] general protection ip:%lx sp:%lx
error:%lx",
325                         tsk->comm, task_pid_nr(tsk),
326                         regs->ip, regs->sp, error_code);
327                 print_vma_addr(" in ", regs->ip);
328                 pr_cont("\n");
329         }
330 
331         force_sig_info(SIGSEGV, SEND_SIG_PRIV, tsk);
332 exit:
333         exception_exit(prev_state);
334 }

C代码不太明显,但从gs前缀读取到现有宏的值赋给了tsk。第306行是:

0xffffffff8164b79d :	mov    %gs:0xc780,%rbx

这很变得有意思起来了。我们控制了current指针,她指向用于描述整个Linux进 程的数据结构。


319         tsk->thread.error_code = error_code;
320         tsk->thread.trap_nr = X86_TRAP_GP;

写入(从task_struct开始的固定偏移)我们控制的地址。注意值本身不能被控制 (分别是0和0xd常量),但这不应该成为一个问题。游戏结束?

不会,我们想覆盖一些在X上的重要数据结构。如果我们按照以下的步骤:

  1. 准备在FAKE_PERCPU的用户空间内存,设置gs基地址给她

  2. 让地址FAKE_PERCPU+0xc780存着指针FAKE_CURRENT_WITH_OFFSET,以满足 FAKE_CURRENT_WITH_OFFSET= X – offsetof(struct task_struct, thread.error_code)

  3. 触发漏洞

之后do_general_protection会写入X。但很快就会尝试再次访问current task_current的其他成员,e.g.unhandled_signal()函数从task_struct指针解引 用。我们没有依赖X来控制,最终会在内核产生一个页错误。我们怎么避免这个问 题?选项有:

  1. 什么都不做。Linux内核不像Windows,Linux内核是完全允许当一个不是预期 的页错误在内核出现,如果可能的话,内核会杀死当前进程之后尝试继续运行 (Windows会蓝屏)。这种机制对于大量内核数据污染就无能为力了。我的猜测是 在当前进程被杀死后,swapgs不平衡的保持下来,这会导致其他进程上下文的更 多页错误。

  2. 使用“tsk->thread.error_code = error_code”覆盖为页错误处理程序的IDT入 口。之后页错误发生(被unhandled_signal()触发)。这个技术曾经在一些偶然 的环境中成功过。但在这里不会成功,因为有2个原因:

    • Linux让IDT只读

    • 就算IDT可写,我们也不能控制覆盖的值 – 0或者0xd。SMEP/SMAP也 会是问题。

  3. 我们可以尝试产生一个竞争。“tsk->thread.error_code = error_code”会促 进代码执行,比如允许通过系统调用控制的代码指针P。之后我们可以在CPU 0上 触发漏洞,在同一时间段CPU 1可以循环执行一些系统调用。这个思路可以在CPU 0被破坏前让通过CPU 1获得代码执行,比如hook页错误处理程序,这样CPU 0不会 影响更多的地方,我尝试了这种方法多次,但都失败了。可能不同的漏洞在时间 线上的不同所致。

  4. Throw a towel on “tsk->thread.error_code = error_code” write.

虽然有些恶心,我们会尝试最后一个选项。我们会让current指向用户空间,设置 这个指针可以通过读的deref到我们能控制的内存。自然的,我们观察接下来的代 码,找找更多的写deref。

6. Achieving write primitive continued, aka life after do_general_protection

下一个机会是do_general_protection()所调用的函数:

int
force_sig_info(int sig, struct siginfo *info, struct task_struct *t)
{
        unsigned long int flags;
        int ret, blocked, ignored;
        struct k_sigaction *action;

        spin_lock_irqsave(&t->sighand->siglock, flags);
        action = &t->sighand->action[sig-1];
        ignored = action->sa.sa_handler == SIG_IGN;
        blocked = sigismember(&t->blocked, sig);   
        if (blocked || ignored) {
                action->sa.sa_handler = SIG_DFL;
                if (blocked) {
                        sigdelset(&t->blocked, sig);
                        recalc_sigpending_and_wake(t);
                }
        }
        if (action->sa.sa_handler == SIG_DFL)
                t->signal->flags &= ~SIGNAL_UNKILLABLE;
        ret = specific_send_sig_info(sig, info, t);
        spin_unlock_irqrestore(&t->sighand->siglock, flags);

        return ret;
}

task_struct的成员sighand是一个指针,我们可以设置任意值。


action = &t->sighand->action[sig-1];
action->sa.sa_handler = SIG_DFL;

我们无法控制写的值,SIG_DFL是常量的0。这里最终能工作了,虽然有些扭曲。 假设我们想覆盖内核地址X。为此我们准备伪造的task_struct,所以X等于 t->sighand->action[sig-1].sa.sa_handler的地址。上面还有一行要注意:

spin_lock_irqsave(&t->sighand->siglock, flags);

t->sighand->siglock在t->sighand->action[sig-1].sa.sa_handler的常量偏移 上,内核会调用spin_local_irqsave在某些地址上,X+SPINLOCK的内容无法控制。 这会发生什么呢?两种可能性:

  1. X+SPINLOCK所在的内存地址看起来像没有锁的spinlock。spin_lock_irqsave 会立即完成。最后,spin_unlock_irqrestore会撤销spin_lock_irqsave的写操作。

2.X+SPINLOCK所在的内存地址看起来像上锁的spinlock。如果我们不介入的话, spin_lock_irqsave会无线循环等待spinlock。有些担心,要绕过这个障碍我们得 需要其他假设 — X+SPINLOCK所在内存地址的内容。这是可接受的,我们可以在 后面看到在内核.data区域里设置X。

* 首先,准备FAKE_CURRENT,让t->sighand->siglock指向用户空间上锁
      的区域,SPINLOCK_USERMODE

* force_sig_info()会挂在spin_lock_irqsave里

* 这时,另外一个用户空间的线程在另外一个CPU上运行,并且改变了
      t->sighand,所以t->sighand->action[sig-1.sa.sa_hander成了我们
      的覆盖目标,之后解锁SPINLOCK_USERMODE

* spin_lock_irqsave会返回

* force_sig_info()会重新载入t->sighand,执行期望的写操作

鼓励细心的读者追问为什么不能使用第2种方案,即X+SPINLOCK在初始时是没有锁 的。这并不是全部 — 我们需要准备一些FAKE_CURRENT的字段来让尽量少的代码 执行。我不会再透露更多细节 — 这篇BLOG已经够长了….下一步会发生什么? force_sig_info()和do_general_protection()返回。接下来iret指令会再次产生 #SS异常处理(因为仍然是用户空间ss的值在栈上引用了一个nonpresent段),但 这一次,#SS处理程序里的额外swapgs指令会返回并取消之前不正确的swapgs。 do_general_protection()会调用和操作真正的task_struct,而不是伪造的 FAKE_CURRENT。最终,current会发出SIGSEGV信号,其他进程会被调度来执行。 这个系统仍然是稳定的。

7. 插曲:SMEP

SMEP是Intel处理器从第3代Core(Shawn:酷睿)时加入的硬件特性。如果控制寄 存器CR4里的SMEP位被设置的话,当RING0(Shawn:标准Linux内核是RING0,在 XEN下是例外,RING0是Hypervisor)尝试执行的代码来自标记为用户空间的内存 页,CPU就会生成一个错误(Shawn:就是拒绝)。如果可能的话,Linux内核会默 认开启SMEP。

8. 实现代码执行

之前的章节讲述了一种如何以0在内核内存中覆盖8个连续字节的方法。如果SMEP 开启的情况下如何实现代码执行呢?

直接覆盖一个内核代码的指针是不行的。我们可以清零top bytes( Shawn: MSB)

  • 但之后的地址会在用户空间,所以SMEP会阻止这个指针的deref。

换一种方式,我们可以清零几个low bytes( Shawn: LSB),但是之后能利用这个 指针的概率也很低。

我们需要一个内核指针P指向结构X包含了代码指针。我们可以覆盖P的top bytes 让她成为一个用户空间的地址,这样P->code_pointer_in_x()调用会跳转到一个 我们能选择的地址。我不确定最好选择哪个攻击对象。从我的经验来看,我选择 内核proc_root变量,这是一个结构体:

struct proc_dir_entry {
            ...
        const struct inode_operations *proc_iops;
        const struct file_operations *proc_fops;
        struct proc_dir_entry *next, *parent, *subdir;
        ...
        u8 namelen;
        char name[];
};

这个结构体是一个proc文件系统的入口(proc_root是/proc作为proc文件系统的 根目录)。当一个文件名路径开始在/proc里查询时,subdir指针(从 proc_root.subdir开始)会跟进,直到名字被找到。之后proc_iops的指针会被调 用:

struct inode_operations {
        struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
        void * (*follow_link) (struct dentry *, struct nameidata *);
        ...many more...
        int (*update_time)(struct inode *, struct timespec *, int);
        ...
} ____cacheline_aligned;

proc_root驻扎在内核代码段里,这意味着漏洞利用需要知道她的地址。这个信息 可以从/proc/kallsyms符号表得到;当然,很多加固过的内核不允许普通用户读 取这个文件。但如果内核是一个已知的build(标准的GNU/Linux发行版),这个 地址可以轻松获得;和一堆偏移一样需要构建FAKE_CURRENT。

我们会覆盖proc_root.subdir,让她成为一个指向一个在用户空间能被控制的结 构体proc_dir_entry。有点困难在于我们不能覆盖整个指针。别忘了我们的写操 作是“覆盖8个0”。如果我们让proc_root.subdir变成0,我们不会去映射她,因为 Linux内核不允许用户空间映射到地址0上(更确切的说发是,任何低于 /proc/sys/vm/mmap_min_addr的地址,默认值一般是4k)。(Shawn:想想哪些 0ld good hacking days,每天都有一堆NULL pointer deref是多么幸福活着无挑 战的时光啊;-))。这意味着我们需要:

  1. 映射16MB的内存到地址4096

  2. 使用类似proc_dir_entry的方式来填充,把inode_operations字段指向用户空 间的地址FAKE_IOPS,name字段为字符串”A”。

  3. 配置漏洞利用去覆盖proc_root.subdir的top 5 bytes。

之后,除非proc_root.subdir最低的3 bytes是0,我们可以确定在触发 force_sig_info()覆盖后,proc_root.subdir会指向被控制的用户空间内存。当 我们的进程调用open(“/proc/A”,…)时,FAKE_IOPS的指针会被调用。她们应该 指向哪里呢?如果你认为答案是“指向我们的shellcode“,请再读一遍上面的分析。

我们需要让FAKE_IOPS指针指向一个stack pivot[1]序列。这再次假设了具体内核 运行的版本情况。通常的”xchg %esp, %eax; ret”代码序列(2个字节,94 c3是 在测试内核的地址0xffffffff8119f1ed)很好的可以用于64位内核的ROP。就算没 能控制%rax,这个xchg指令操作32位的寄存器也能清掉%rsp的高32位而让%rsp着 陆在用户空间的内存里。在最糟糕的情况下,我们可以分配低4GB的虚拟内存然后 填充ROP链条。

在当前测试的内核(Fedora 20)有两种方法去deref在FAKE_IOPS的指针:

  1. %rax:=FAKE_IOPS; call *SOME_OFFSET(%rax)

  2. %rax:=FAKE_IOPS; %rax:=SOME_OFFSET(%rax); call *%rax

第1种情况里,在%rsp和%rax交换值后,她会等于FAKE_IOPS。我们需要ROP链条驻 扎在FAKE_IOPS的起始位置,这需要类似“add $A_LOT, %rsp; ret”的指令,然后 在继续。

第2种情况里,%rsp会分配低32位的调用目标,即0x8119f1ed。我们需要准备在这 个地址上的ROP链条。

计算一下%rax值有两者之一的已知值在特定的时间指向stack pivot序列,我们不 需要ROP链条填充整个4GB内存,只需要上面的两个地址即可。第2种情况的ROP链 条自身很简洁:

unsigned long *stack=0x8119f1ed;
*stack++=0xffffffff81307bcdULL;  // pop rdi, ret
*stack++=0x407e0;                //cr4 with smep bit cleared
*stack++=0xffffffff8104c394ULL;  // mov rdi, cr4; pop %rbp; ret
*stack++=0xaabbccdd;             // placeholder for rbp
*stack++=actual_shellcode_in_usermode_pages;

9. 插曲:SMAP

SMAP是Intel从第5代Core处理器推出的一个硬件特性。如果CR4控制寄存器的 SMAP位被设置的话,CPU会拒绝用户空间的页被RING0访问(Shawn:个人理解, SMAP和SMEP最大的不同主要是SMEP针对代码段,而SMAP针对数据段)。Linux内核 通常会默认开启SMAP。一个测试的内核模块(Core-M 5Y10a CPU)尝试访问用户 空间然后crash了:

[  314.099024] running with cr4=0x3407e0
[  389.885318] BUG: unable to handle kernel paging request at 00007f9d87670000
[  389.885455] IP: [ffffffffa0832029] test_write_proc+0x29/0x50 [smaptest]
[  389.885577] PGD 427cf067 PUD 42b22067 PMD 41ef3067 PTE 80000000408f9867
[  389.887253] Code: 48 8b 33 48 c7 c7 3f 30 83 a0 31 c0 e8 21 c1 f0 e0 44 89 e0 48 8b 

正如我们看到的,用户空间的页是正常的,但访问也报了页错误。Windows系统不 太支持SMAP;Windows 10技术预览版build 9926的cr4=0x1506f8(SMEP启动, SMAP关闭);对比Linux内核(同样的测试硬件)你可以看到cr4的bit 21是没有设 置的。这不奇怪,在Linux中,访问用户空间是通过调用copy_from_user(), copy_to_user()和类似函数显式执行的,所以执行这些操作时临时关闭SMAP是可 行的。在Windows上,内核代码直接访问用户空间代码,只是包装了一层访问异常 处理程序,所以要让SMAP工作正常需要调整所有的驱动,这是一项困难的工作。

10. SMAP to the rescue!

上面的漏洞利用方法依赖于在用户空间里准备特定的数据结构,然后强制内核认 为她们是可信的内核数据。这种方法对于开启SMAP特性的内核不奏效 — CPU会 拒绝从用户空间读取恶意数据。我们能做的是构造所有需要用的数据结构,然后 拷贝她们到内核。比如:

write(pipe_filedescriptor, evil_data, ...

之后evil_data会被拷贝到一个内核管道缓冲区里。我们可能需要猜测她的地址; some sort of heap spraying, combined with the fact that there is no spoon^W effective kernel ASLR[9], could work, although it is likely to be less reliable than exploitation without SMAP.

总之,还有最后一个障碍 — 不要忘了我们需要设置用户空间的gs base去指向 我们的漏洞利用的数据结构。在上面的场景(没有SMAP),我们使用 arch_prctl(ARCH_SET_GS)系统调用,她是这样在内核里实现的:

long do_arch_prctl(struct task_struct *task, int code, unsigned long addr)
{ 
         int ret = 0; 
         int doit = task == current;
         int cpu;
 
         switch (code) { 
         case ARCH_SET_GS:
                 if (addr >= TASK_SIZE_OF(task))
                         return -EPERM; 
                 ... honour the request otherwise

休斯顿,我们有一个麻烦 — 我们不能使用这个API去设置gs base用户空间以上 的内存!

最近的CPU有wrgsbase指令可以直接设置gs base,这是一个非特权级指令,但需 要通过内核设置CR4控制寄存器中的FSGSBASE bit( no 16)来开启。Linux并没有 设置这个位,因此用户空间不能使用这条指令。

在64位系统上,非系统级的GDT和LDT条目依然是8个字节长,base field是最大 4GB-1,所以根本没有机会设置一个基地址的段在内核空间里。所以,除非我漏掉 了能在内核里设置用户态gs base的其他方法,不然SMAP能保护CVE-2014-9322针 对64位Linux内核任意代码执行的漏洞利用。

[1] CVE-2014-9322 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-9322

[2] Upstream fix http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=6f442be2fb22be02cafa606f1769fa1e6f894441

[3] Intel Software Developer’s Manuals, http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html

[4] SMEP http://vulnfactory.org/blog/2011/06/05/smep-what-is-it-and-how-to-beat-it-on-linux/

[5] SMAP http://lwn.net/Articles/517475

[6] “pretend-it-was-#GP-in-userspace” https://lists.debian.org/debian-kernel/2014/12/msg00083.html

[7] Stack Pivoting https://trailofbits.files.wordpress.com/2010/04/practical-rop.pdf

[8] TSX improves timing attacks against KASLR http://labs.bromium.com/2014/10/27/tsx-improves-timing-attacks-against-kaslr/