New issue
Advanced search Search tips
Note: Color blocks (like or ) mean that a user may not be available. Tooltip shows the reason.

Issue 970 link

Starred by 1 user

Issue metadata

Status: Fixed
Owner:
Last visit > 30 days ago
Closed: Jan 2017
Cc:



Sign in to add a comment

Samsung: RKP Memory Corruption in "rkp_mark_adbd"

Reported by laginimaineb@google.com, Oct 24 2016

Issue description

As part of Samsung KNOX, Samsung phones include a security hypervisor called RKP (Real-time Kernel Protection), running in EL2. This hypervisor is meant to ensure that the HLOS kernel running in EL1 remains protected from exploits and aims to prevent privilege escalation attacks by "shielding" certain data structures within the hypervisor.

There are several mechanisms which are implemented in RKP in order to make sure the kernel is not compromised, such as disallowing double mapping of physical memory, disallowing mapping kernel memory to the user-space VAS, etc. For more information on RKP, see: https://www2.samsungknox.com/en/blog/real-time-kernel-protection-rkp

On devices which support RKP, paravirtualization is used in order to call out to the hypervisor at specific detection points within the Linux kernel. When the "execve" syscall is executed in order to replace the current process image, the Linux Kernel calls out to RKP via an HVC call in order to inform RKP of the executing process's name. RKP uses this information in order to specifically mark the "adbd" process, allowing it to perform additional security checks in subsequent RKP calls for this process. This RKP call (command code 0x4B) is referred to in RKP as "rkp_mark_adbd".

What follows is the reverse-engineered high level logic for the code handling this command in the RKP hypervisor:

__int64 rkp_mark_adbd(struct argument_blob* el1_argument_buffer)
{

  uint64_t cred_task_phys = get_cred_task_phys();
  void* exec_name_phys = virt_to_phys(el1_argument_buffer->arg1);
  if ( !exec_name_phys || !cred_task_phys )
    return rkp_log_and_err("rkp_mark_adbd NULL Cred OR filename", 0LL, 0LL, 0LL);

  if (!strcmp(exec_name_phys, "/sbin/adbd"))
  {
    *(_QWORD *)(cred_task_phys + 8 * offset_of_type_cred) |= 4uLL;
    return 8 * offset_of_type_cred;
  }
  return -1;
}

As seen above, the "rkp_mark_adbd" function checks whether the path argument for the process being executed is "/sbin/adbd". If so, it retrieves the physical address of the "cred" structure for this process, and marks it as an "adbd" process by setting a single bit in the "type" field. Normally, the "cred" structure would be stored in the credential jar, which is in an RKP-protected region (therefore, making it safe to access and modify).

As seen above, the "cred" address for the current EL1 kernel task is not provided to RKP, but rather is somehow retrieved directly by the hypervisor by calling "get_cred_task_phys". In fact, this is achieved by directly fetching the EL1 stack pointer and finding the address of the "cred" structure in the current kernel stack, like so:

__int64 read_task_threadinfo_from_el1_sp()
{
  uint64_t el1_sp = read_el1_sp();
  return *(_QWORD *)(virt_to_phys(el1_sp & 0xFFFFFFFFFFFFC000LL) + offset_of_task_threadinfo);
} 

signed __int64 get_cred_task_phys()
{
  uint64_t task_threadinfo_virt = read_task_threadinfo_from_el1_sp();
  uint64_t task_threadinfo_phys = virt_to_phys(task_threadinfo_virt);
  return virt_to_phys(*(_QWORD *)(task_threadinfo_phys + 8 * offset_of_cred_task));
}

Unlike other RKP commands, the "rkp_mark_adbd" does not verify the resulting physical address of "cred" structure before writing to it (neither by checking it in "physmap", nor by directly making sure it resides within the credential jar). This allows an attacker with EL1 code execution to trick the RKP kernel into setting the second bit at any physical address that EL2 has access to (including RKP read-only pages and EL1 translation tables). Here is an outline of one such attack scenario:

  1. Get code execution in EL1
  2. Modify the "task" pointer at the top of the kernel stack (i.e., under the "threadinfo") to point to a controlled memory address (in the kernel VAS).
  3. Modify the "cred" pointer under the crafted "task" pointer to point to any wanted memory address
  3. Call RKP command 0x4B with a pointer (in the kernel VAS) to the string "/sbin/adbd"
  4. This will cause "rkp_mark_adbd" to be called, which will then use the crafted "cred" pointer, in which it will set a single bit

One way an attacker can leverage this attack is by modifying the "OutputAddress" fields of PTEs in the EL1 translation table (https://armv8-ref.codingbelief.com/en/chapter_d4/d43_2_armv8_translation_table_level_3_descriptor_formats.html) in order to re-map EL1 memory with R/W permissions. Another way to use this would be to modify control structures within RKP itself in order to gain code execution in RKP.

This bug is subject to a 90 day disclosure deadline. If 90 days elapse
without a broadly available patch, then the bug report will automatically
become visible to the public.

 
I've statically verified this issue on the following devices:
  -Galaxy S7 (G930V) - version VRU1APB1
  -Galaxy S7 Edge (G9350) - version ZCU2API3
Labels: -Restrict-View-Commit SVE-2016-7897
Status: Fixed (was: New)
Fixed in January SMR, verification pending.
Verified fix on G935F, XXU1DQAO
Summary: Samsung: RKP Memory Corruption in "rkp_mark_adbd" (was: Android: RKP Memory Corruption in "rkp_mark_adbd")

Sign in to add a comment