[하루한줄] CVE-2021-25415 : 삼성 모바일 제품의 RKP memory remapping 취약점
URL
Target
- SMR JUN-2021 Release 1 이전 RKP
Explain
모바일에서의 sercurity hypervisor는 런타임에 커널 무결성을 보장해 커널 취약점이 발생하더라도 취약점을 이용한 권한 상승이나 코드 실행을 방지합니다. 삼성은 RKP(Real-time Kernel Protection)으로 security hypervisor를 구현했으며 RKP의 아키텍처 개요와 주요 보안 기능은 링크에서 확인할 수 있습니다.
ARMv8의 exception model은 네 가지 레벨에서 프로세스가 동작합니다.
EL0(유저 APP) < EL1(커널) < EL2(하이퍼바이저) < EL3(TrustZone Monitor)
삼성 RKP는 EL1이 손상되어도 EL2로의 메모리 권한 변경을 방지하기 위해 유효성 검사가 있지만, 이 유효성 검사가 충분하지 않아 EL2 메모리를 쓰기 가능한 메모리로 매핑할 수 있는 취약점이 발견되었습니다.
취약점은 RKP가 rkp_s2_page_change_permission
또는 rkp_s2_range_change_permission
함수에서 단일 페이지 혹은 주소 범위 메모리의 권한을 변경할 때 발생합니다.
int64_t rkp_s2_page_change_permission(void* p_addr, uint64_t access, uint32_t exec, uint32_t allow) {
// ...
if (is_phys_map_s2unmap(p_addr)) { // page must not be marked S2UNMAP in the physmap
rkp_policy_violation("Error page was s2 unmapped before %p", p_addr);
return -1;
}
if (map_s2_page(p_addr, p_addr, 0x1000, attrs) < 0) {
rkp_policy_violation("map_s2_page failed, p_addr : %p, attrs : %d", p_addr, attrs);
return -1;
}
tlbivaae1is(((p_addr + 0x80000000) | 0xFFFFFFC000000000) >> 12);
return rkp_set_pgt_bitmap(p_addr, access);
}
int64_t rkp_s2_range_change_permission(uint64_t start_addr, uint64_t end_addr, uint64_t access, uint32_t execuint32_t allow) {
// ...
p_addr_start = start_addr;
if (s2_map(start_addr, end_addr - start_addr, attrs, &p_addr_start) < 0) {
uh_log('L', "rkp_paging.c", 222, "s2_map returned false, p_addr_start : %p, size : %p", p_start_addr, size);
return -1;
}
if (start_addr == end_addr)
return 0;
addr = start_addr;
do {
res = rkp_set_pgt_bitmap(addr, access);
if (res < 0) {
uh_log('L', "rkp_paging.c", 229, "set_pgt_bitmap fail, %p", addr);
return res;
}
tlbivaae1is(((addr + 0x80000000) | 0xFFFFFFC000000000) >> 12);
addr += 0x1000;
} while (addr < end_addr);
return 0;
}
rkp_s2_page_change_permission
는 physmap의 S2UNMAP이 설정되지 않아야 한다는 조건을 확인한 다음 map_s2_page
를 호출해 페이지 단위 매핑을 수행합니다. 그러나rkp_s2_range_change_permission
는 이를 확인하지 않고 s2_map
을 호출해 주소 범위 단위로 매핑을 수행합니다.
매핑 해제도 매핑과 비슷하게 수행하며 아래는 주소 범위의 매핑을 해제하는 s2_unmap
함수입니다.
int64_t s2_unmap(uint64_t orig_addr, uint64_t orig_size) {
// ...
addr = orig_addr & 0xFFFFFFFFFFFFF000;
size = (orig_addr & 0xFFF) + orig_size;
if (!size)
return 0;
while (size > 0x3FFFFFFF && (addr & 0x3FFFFFFF) == 0) {
if (unmap_s2_page(addr, 0x40000000)) {
uh_log('L', "s2.c", 1175, "unable to unmap 1gb s2 page: %p", addr);
return -1;
}
size -= 0x40000000;
addr += 0x40000000;
if (!size)
return 0;
}
while (size > 0x1FFFFF && (addr & 0x1FFFFF) == 0) {
if (unmap_s2_page(addr, 0x200000)) {
uh_log('L', "s2.c", 1183, "unable to unmap 2mb s2 page: %p", addr);
return -1;
}
size -= 0x200000;
addr += 0x200000;
if (!size)
return 0;
}
while (size > 0xFFF && (addr & 0xFFF) == 0) {
if (unmap_s2_page(addr, 0x1000)) {
uh_log('L', "s2.c", 1191, "unable to unmap 4kb s2 page: %p", addr);
return -1;
}
size -= 0x1000;
addr += 0x1000;
if (!size)
return 0;
}
return 0;
}
s2_unmap
은 s2_map
과 유사하게 unmap_s2_page
를 여러번 호출해 주소 범위의 페이지를 매핑 해제합니다. 이 과정에서 페이지를 physmap
에서 S2UNMAP
으로 설정하는 rkp_phys_map_set
, rkp_phys_map_set_region
, sparsemap_set_value_addr
에 대한 호출이 없어 이후 하이퍼바이저 메모리를 커널 권한으로 쓰기 가능한 메모리에 다시 매핑할 수 있습니다.
해당 취약점 외에도 CVE-2021-25411(실행 가능한 커널 페이지 생성), CVE-2021-25416(read-only 권한 커널 write) 등의 취약점의 정보 또한 공개되었습니다.
본 글은 CC BY-SA 4.0 라이선스로 배포됩니다. 공유 또는 변경 시 반드시 출처를 남겨주시기 바랍니다.