Linux group_info refcounter overflow use afer free
Python C
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
README.md
ping.py
ping_of.c

README.md

linux

This repository holds code to help exploiting CVE-2014-2851: Linux group_info refcounter overflow use afer free.

  • ping_of.c calls ping_init_sock() argv[1] times. Doing this in python would make things too slow.

  • ping.py contains code to check if the group_info struct has been freed.

The client function does the following:

  • call ping_init_sock() to increase the refcounter
  • call faccessat(), this syscall increments, then decrements the refcounter. If the refcounter became zero because of ping_init_sock, the group_info struct will be freed.
  • requests the server to open 1000 file descriptors. On my system (32 bit ubuntu), the file structs are allocated in the same memcache as the group_info struct.(kmalloc-192)
  • call getgroups(), if group_info has been freed and reused, this counter has been overwritten by another value. This way, we can see if group_info has been freed. we wait 15 seconds between faccessat and getgroups because the group_info struct may not have been overwritten yet.

The server opens a number of fds on request in order to use the freed memory. This should be called in a separate session because the credentials are shared between parent and child processes.

first, execute ping_of.c . This will increment the refcounter close to zero (e.g. -20). next, in a different session, execute "ping.py -s" finally, in the first session, execute "ping.py -c"

[ Session 1 ]

t@tpollet-ubuntu-vm:/tmp$ ./ping_of -20
...
<output omitted>
...
t@tpollet-ubuntu-vm:/tmp$ ./ping.py -c
Starting client
numgroups: 8
numgroups: 8
numgroups: -1055523072
>

The numgroups output is the return value of getgroups(). If this changed, it means something else is at that memory location.

[Session 2]

t@tpollet-ubuntu-vm:/tmp$ ./ping.py -s
Starting Server
opening 1000 fds
opening 1000 fds
opening 1000 fds

The numgroups output by ping.py is returned by the getgroups system call. This comes from the group_info struct being overwritten by a file struct. The group_info struct looks like this:

include/linux/cred.h
32 struct group_info {
33         atomic_t        usage;            // refcounter
34         int             ngroups;          // return value of getgroups
35         int             nblocks;
36         kgid_t          small_block[NGROUPS_SMALL];
37         kgid_t          *blocks[0];
38 };

The file struct looks like this:

include/linux/fs.h
775 struct file {
776         union {
777                 struct llist_node       fu_llist;
778                 struct rcu_head         fu_rcuhead;
779         } f_u;
780         struct path             f_path;
781 #define f_dentry        f_path.dentry
782         struct inode            *f_inode;       /* cached value */
783         const struct file_operations    *f_op;
784 
....

include/linux/llist.h
65 struct llist_node {
66         struct llist_node *next;
67 };

So, after the free, getgroups() looks up the value on the location where ngroups was before it was freed. This location now holds fu_rcuhead . We are still able to increment fu_llist by calling ping_init_sock().