[하루한줄] CVE-2021-29628 : PlayStation5 SMAP Bypass

URL

SMAP bypass(PS5)

Target

PlayStation5, FreeBSD 12

Explain

SMAP(Supervisor-Mode Access Prevention)은 커널에서 유저랜드 메모리에 접근하지 못하게 하여 취약점 익스플로잇을 어렵게 하는 커널 레벨 보호기법입니다.

PlayStation 5(이하 PS5)는 FreeBSD 12 기반으로 개발된 운영체제인 Orbis 2.0을 사용합니다. CVE-2021-29628은 FreeBSD 12에서 발견됐었던 취약점이지만, PS5에서 이 취약점에 대해 패치를 하지 않아 PS5에서 SMAP를 bypass할 수 있었습니다.

PS5에서 SMAP가 활성화되어 있는 상태(RflagsAlignment Check Flag, %RFLAGS.AC가 세팅되지 않은 상태)에서 커널에서 유저 페이지에 접근하면 page fault가 발생합니다. 반면에 %RFLAGS.AC가 세팅되지 않은 상태에서는 커널에서 정상적으로 유저 페이지에 접근이 가능합니다. 하지만 FreeBSD 커널에서는 copyin()copyout() 함수들을 통해 %RFLAGS.AC를 잠시 세팅해서 유저 페이지에 접근합니다.

.macro	COPYIN smap erms
	/* ... */
	movq	$copy_fault,PCB_ONFAULT(%r11)
	/* ... */
	stac // set %RFLAGS.AC, to allow access to user pages
	do_the_copyin
	clac // clear %RFLAGS.AC, to forbid access to user pages
	/* ... */

copy_fault:
	movq	$0,PCB_ONFAULT(%r11)
	movl	$EFAULT,%eax
	POP_FRAME_POINTER
	ret

copyin() 함수가 실행되면 fault handler인 copy_fault() 함수를 handler로 등록한 후 %RFLAGS.AC를 세팅해 복사 작업을 진행합니다. 그리고 복사 작업이 끝나면 다시 %RFLAGS.AC를 클리어합니다. 이 때, 복사 과정에서 fault가 발생하면 trap()함수가 실행됩니다.

void
trap(struct trapframe *frame)
{
	/* ... */
			if (curpcb->pcb_onfault != NULL) {
				frame->tf_rip = (long)curpcb->pcb_onfault;
				return;
			}
	/* ... */
}

trap() 함수에서는 curpcb->pcb_onfault에 값이 들어가 있으면 이 안에 값을 fram→tf_rip에 넣고 바로 return 하는데, 이 때 %RFLAGS.AC를 클리어하지 않아 SMAP이 비활성화인 상태로 핸들링을 마무리합니다. 따라서 이를 통해 SMAP를 bypass하여 이 이후의 syscall들을 SMAP이 비활성화된 상태에서 실행할 수 있습니다.