熟悉题目
先看启动脚本,那应该就是xnuca设备了
1 | #!/bin/bash |
启动后直接root登录就可以了,查看pci设备
1 | # lspci |
通过查看xnuca_class_init
函数,可以知道xnuca对应00:04.0 Class 00ff: 1234:11e9
1 | __int64 __fastcall xnuca_class_init(__int64 a1) |
代码分析
先看read,就是读取0x9D0和0x9DC偏移的数据
1 | __int64 __fastcall xnuca_mmio_read(__int64 State, unsigned __int8 addr) |
再看write,有3个功能,分别是xnuca_set_timer
、xnuca_send_request
、xnuca_auth
1 | __int64 __fastcall xnuca_mmio_write(__int64 State, int addr, unsigned int value, int size) |
先看xnuca_set_timer
,*(State + 0x9D0)
的最低位是1,次低位是0才能进入,逻辑就是初始化了一个计时器,而且处理后将次低位置1了
1 | __int64 __fastcall xnuca_set_timer(__int64 State) |
那么State + 0xA00处是一个QEMUTimer ,计时器到期时要调用xnuca_timer,先看xnuca_timer,可以看到这里free后,没有将指针置空,又是经典堆题放到了qemu
1 | __int64 __fastcall xnuca_timer(__int64 State) |
其实这样看有点难看,默认符号表没有State结构体,经过一顿查看代码逆向,我自己就新建了一个
1 | struct xnucaState |
再看看,是不是好看多了,那么要进入里面的漏洞代码区域,我们要使cmd_9D0&4 == 1
1 | xnucaState *__fastcall xnuca_timer(xnucaState *State) |
继续看xnuca_send_request
,就是用来设置各种值的,将timer的超时时间设置成当前时间+10纳秒(根据qemu文档中timer_init_ns是以纳秒为单位初始化计时器),那么就相当于立刻执行,xnuca_timer
1 | __int64 __fastcall xnuca_send_request(xnucaState *a1, int a2, int a3, int a4, unsigned int a5) |
最后还有xnuca_auth
,就是count_9DC小于4的时候,将我们的value与a1->auth_str中的比较,相等就+1,否则置0,而count_9DC等于5,则将cmd_9D0最低位置1,同事从星将count_9DC置0
1 | xnucaState *__fastcall xnuca_auth(xnucaState *a1, char a2) |
那么这个auth_str
什么时候设置的呢,在pci_xnuca_realize
里面,这里初始化了State的各个成员了
1 | unsigned __int64 __fastcall pci_xnuca_realize(xnucaState *a1, __int64 a2) |
漏洞利用
利用思路:通过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了
最终利用代码:
1 | // -*- coding: utf-8 -*- |
最终效果——启动计算器: