xorl %eax, %eax

GRKERNSEC_BRUTE Exploit Bruteforcing Protection

leave a comment »

This is another little grsecurity protection that can make an exploit developer’s work harder. As we can read from its configuration description…

config GRKERNSEC_BRUTE
	bool "Deter exploit bruteforcing"
	help
	  If you say Y here, attempts to bruteforce exploits against forking
	  daemons such as apache or sshd will be deterred.  When a child of a
	  forking daemon is killed by PaX or crashes due to an illegal
	  instruction, the parent process will be delayed 30 seconds upon every
	  subsequent fork until the administrator is able to assess the
	  situation and restart the daemon.  It is recommended that you also
	  enable signal logging in the auditing section so that logs are
	  generated when a process performs an illegal instruction.

this option will enable a feature that slows down the brute-forcing attempts on daemons that keep on spawning new processes when they crash. It’s obvious that this is quite common in exploit development. Brute-forcing exploits will attempt numerous times to exploit a vulnerability until they discover the right parameters and achieve code execution. Their failed attempts will usually end up in crashes but because of some daemons’ feature of forking this is no important issue.
So, if we move to grsecurity/grsec_sig.c we’ll see the following routine:

void gr_handle_brute_attach(struct task_struct *p)
{
#ifdef CONFIG_GRKERNSEC_BRUTE
	read_lock(&tasklist_lock);
	read_lock(&grsec_exec_file_lock);
	if (p->real_parent && p->real_parent->exec_file == p->exec_file)
		p->real_parent->brute = 1;
	read_unlock(&grsec_exec_file_lock);
	read_unlock(&tasklist_lock);
#endif
	return;
}

It’s clear that this is functional only if ‘CONFIG_GRKERNSEC_BRUTE’ option is enabled. If this is the case, it will check that its real parent process exists and that the parent’s executable file is the same as the current task’s one. This is done to ensure that we’re dealing with the same daemon’s executable and not a different one. Finally, it sets the ‘brute’ variable to ‘1’. Most people that remember ‘task_struct’ would have noticed that some of these variables aren’t part of the Linux kernel’s structure. That’s because they’re grsecurity specific and defined like this:

#ifdef CONFIG_GRKERNSEC
	/* grsecurity */
	struct dentry *gr_chroot_dentry;
	struct acl_subject_label *acl;
	struct acl_role_label *role;
	struct file *exec_file;
	u16 acl_role_id;
	u8 acl_sp_role;
	u8 is_writable;
	u8 brute;
	u8 gr_is_chrooted;
#endif

Back to grsecurity/grsec_sig.c we also find the following function:

void gr_handle_brute_check(void)
{
#ifdef CONFIG_GRKERNSEC_BRUTE
	if (current->brute)
		msleep(30 * 1000);
#endif
	return;
}

This one is very simple. It takes no argument and it just sleeps for 30 seconds if the current task’s ‘brute’ value is non-zero.
By now, you should probably understand how gr_handle_brute_attach() and gr_handle_brute_check() are used to avoid the brute-forcing… As simple as this:

long do_fork(unsigned long clone_flags,
              unsigned long stack_start,
              struct pt_regs *regs,
              unsigned long stack_size,
              int __user *parent_tidptr,
              int __user *child_tidptr)
{
        struct task_struct *p;
    ...
 		if (clone_flags & CLONE_PARENT_SETTID)
 			put_user(nr, parent_tidptr);
 
		gr_handle_brute_check();

 		if (clone_flags & CLONE_VFORK) {
 			p->vfork_done = &vfork;
    ...
        return nr;
}

Which is from kernel/fork.c. So, fork will check the value of ‘brute’ before spawning a new process and gr_handle_brute_attach() is used in do_coredump() located at fs/exec.c

void do_coredump(long signr, int exit_code, struct pt_regs *regs)
{
        struct core_state core_state;
        char corename[CORENAME_MAX_SIZE + 1];
    ...
 	clear_thread_flag(TIF_SIGPENDING);
 
	if (signr == SIGKILL || signr == SIGILL)
		gr_handle_brute_attach(current);
    ...
fail:
        return;
}

which is triggered when a process crashes. As you can read, if the process’ exit signal was either ‘SIGKILL’ (Killed Process) or ‘SIGILL’ (Illegal Instruction) it will invoke gr_handle_brute_attach() to handle it.
So, if a daemon exit because of an illegal instruction or it was killed (for example from stack-smashing protection), it will set its brute-force value to ‘1’ using gr_handle_brute_attach() and when it’ll attempt to fork the new process, do_fork() will check that brute-force value and sleep for 30 seconds if it’s set. Of course, the exploitation is still possible but that 30 seconds gap between spawning processes will make it more time consuming and less reliable. Also, as the description says, the aim here isn’t to stop the exploitation attempt but to slow it down in order to allow the system administrator handle the situation.

Written by xorl

November 9, 2010 at 02:03

Posted in grsecurity, linux, security

Leave a comment