satori/satori/serviceinstall.py

304 lines
12 KiB
Python

# Copyright (c) 2003-2012 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# $Id: serviceinstall.py 1141 2014-02-12 16:39:51Z bethus@gmail.com $
#
# Service Install Helper library used by psexec and smbrelayx
# You provide an already established connection and an exefile
# (or class that mimics a file class) and this will install and
# execute the service, and then uninstall (install(), uninstall().
# It tries to take care as much as possible to leave everything clean.
#
# Author:
# Alberto Solino (bethus@gmail.com)
#
"""This module has been copied from impacket.examples.serviceinstall.
It exposes a class that can be used to install services on Windows devices
"""
import random
import string
from impacket.dcerpc import dcerpc
from impacket.dcerpc import srvsvc
from impacket.dcerpc import svcctl
from impacket.dcerpc import transport
from impacket import smb
from impacket import smb3
from impacket import smbconnection
class ServiceInstall():
"""Class to manage Services on a remote windows server.
This class is slightly improved from the example in the impacket package
in a way that it allows to specify a service and executable name during
instantiation rather than using a random name by default
"""
def __init__(self, SMBObject, exeFile, serviceName=None,
binaryServiceName=None):
"""Contructor of the class.
:param SMBObject: existing SMBObject
:param exeFile: file handle or class that mimics a file
class, this will be used to create the
service
:param serviceName: name of the service to be created, will be
random if not set
:param binaryServiceName name of the uploaded file, wil be random if
not set
"""
print("In constructor now!!!")
self._rpctransport = 0
if not serviceName:
self.__service_name = ''.join(
[random.choice(string.letters) for i in range(4)])
else:
self.__service_name = serviceName
if not binaryServiceName:
self.__binary_service_name = ''.join(
[random.choice(string.letters) for i in range(8)]) + '.exe'
else:
self.__binary_service_name = binaryServiceName
self.__exeFile = exeFile
# We might receive two different types of objects, always end up
# with a SMBConnection one
if isinstance(SMBObject, smb.SMB) or isinstance(SMBObject, smb3.SMB3):
self.connection = smbconnection.SMBConnection(
existingConnection=SMBObject)
else:
self.connection = SMBObject
self.share = ''
def getShare(self):
"""Return the writable share that has been used to upload the file."""
return self.share
def getShares(self):
"""Return a list of shares on the remote windows server."""
# Setup up a DCE SMBTransport with the connection already in place
print("[*] Requesting shares on %s....." % (
self.connection.getRemoteHost()))
try:
self._rpctransport = transport.SMBTransport(
'', '', filename=r'\srvsvc', smb_connection=self.connection)
self._dce = dcerpc.DCERPC_v5(self._rpctransport)
self._dce.connect()
self._dce.bind(srvsvc.MSRPC_UUID_SRVSVC)
srv_svc = srvsvc.DCERPCSrvSvc(self._dce)
resp = srv_svc.get_share_enum_1(self._rpctransport.get_dip())
return resp
except Exception:
print("[!] Error requesting shares on %s, aborting....." % (
self.connection.getRemoteHost()))
raise
def createService(self, handle, share, path):
"""Install Service on the remote server.
This method will connect to the SVCManager on the remote server and
install the service as specified in the constructor.
"""
print("[*] Creating service %s on %s....." % (
self.__service_name, self.connection.getRemoteHost()))
# First we try to open the service in case it exists.
# If it does, we remove it.
try:
resp = self.rpcsvc.OpenServiceW(
handle, self.__service_name.encode('utf-16le'))
except Exception as e:
if e.get_error_code() == svcctl.ERROR_SERVICE_DOES_NOT_EXISTS:
# We're good, pass the exception
pass
else:
raise
else:
# It exists, remove it
self.rpcsvc.DeleteService(resp['ContextHandle'])
self.rpcsvc.CloseServiceHandle(resp['ContextHandle'])
# Create the service
command = '%s\\%s' % (path, self.__binary_service_name)
try:
resp = self.rpcsvc.CreateServiceW(
handle, self.__service_name.encode('utf-16le'),
self.__service_name.encode('utf-16le'),
command.encode('utf-16le'))
except Exception:
print("[!] Error creating service %s on %s" % (
self.__service_name, self.connection.getRemoteHost()))
raise
else:
return resp['ContextHandle']
def openSvcManager(self):
"""Connect to the SVCManager on the remote host."""
print("[*] Opening SVCManager on %s...."
"." % self.connection.getRemoteHost())
# Setup up a DCE SMBTransport with the connection already in place
self._rpctransport = transport.SMBTransport(
'', '', filename=r'\svcctl', smb_connection=self.connection)
self._dce = dcerpc.DCERPC_v5(self._rpctransport)
self._dce.connect()
self._dce.bind(svcctl.MSRPC_UUID_SVCCTL)
self.rpcsvc = svcctl.DCERPCSvcCtl(self._dce)
try:
resp = self.rpcsvc.OpenSCManagerW()
except Exception:
print("[!] Error opening SVCManager on %s...."
"." % self.connection.getRemoteHost())
raise Exception('Unable to open SVCManager')
else:
return resp['ContextHandle']
def copy_file(self, src, tree, dst):
"""Copy file to remote SMB share."""
print("[*] Uploading file %s" % dst)
if isinstance(src, str):
# We have a filename
fh = open(src, 'rb')
else:
# We have a class instance, it must have a read method
fh = src
f = dst
pathname = string.replace(f, '/', '\\')
try:
self.connection.putFile(tree, pathname, fh.read)
except Exception:
print("[!] Error uploading file %s, aborting....." % dst)
raise
fh.close()
def findWritableShare(self, shares):
"""Retrieve a list of writable shares on the remote host."""
# Check we can write a file on the shares, stop in the first one
for i in shares:
if (i['Type'] == smb.SHARED_DISK or
i['Type'] == smb.SHARED_DISK_HIDDEN):
share = i['NetName'].decode('utf-16le')[:-1]
try:
self.connection.createDirectory(share, 'BETO')
except Exception:
# Can't create, pass
print("[!] share '%s' is not writable." % share)
pass
else:
print('[*] Found writable share %s' % share)
self.connection.deleteDirectory(share, 'BETO')
return str(share)
return None
def install(self): # noqa
"""Install the service on the remote host."""
if self.connection.isGuestSession():
print("[!] Authenticated as Guest. Aborting")
self.connection.logoff()
del(self.connection)
else:
fileCopied = False
serviceCreated = False
# Do the stuff here
try:
# Let's get the shares
shares = self.getShares()
self.share = self.findWritableShare(shares)
self.copy_file(self.__exeFile,
self.share,
self.__binary_service_name)
fileCopied = True
svcManager = self.openSvcManager()
if svcManager != 0:
serverName = self.connection.getServerName()
if serverName != '':
path = '\\\\%s\\%s' % (serverName, self.share)
else:
path = '\\\\127.0.0.1\\' + self.share
service = self.createService(svcManager, self.share, path)
serviceCreated = True
if service != 0:
# Start service
print('[*] Starting service %s....'
'.' % self.__service_name)
try:
self.rpcsvc.StartServiceW(service)
except Exception:
pass
self.rpcsvc.CloseServiceHandle(service)
self.rpcsvc.CloseServiceHandle(svcManager)
return True
except Exception as e:
print("[!] Error performing the installation, cleaning up: "
"%s" % e)
try:
self.rpcsvc.StopService(service)
except Exception:
pass
if fileCopied is True:
try:
self.connection.deleteFile(self.share,
self.__binary_service_name)
except Exception:
pass
if serviceCreated is True:
try:
self.rpcsvc.DeleteService(service)
except Exception:
pass
return False
def uninstall(self):
"""Uninstall service from remote host and delete file from share."""
fileCopied = True
serviceCreated = True
# Do the stuff here
try:
# Let's get the shares
svcManager = self.openSvcManager()
if svcManager != 0:
resp = self.rpcsvc.OpenServiceA(svcManager,
self.__service_name)
service = resp['ContextHandle']
print('[*] Stoping service %s.....' % self.__service_name)
try:
self.rpcsvc.StopService(service)
except Exception:
pass
print('[*] Removing service %s.....' % self.__service_name)
self.rpcsvc.DeleteService(service)
self.rpcsvc.CloseServiceHandle(service)
self.rpcsvc.CloseServiceHandle(svcManager)
print('[*] Removing file %s.....' % self.__binary_service_name)
self.connection.deleteFile(self.share, self.__binary_service_name)
except Exception:
print("[!] Error performing the uninstallation, cleaning up")
try:
self.rpcsvc.StopService(service)
except Exception:
pass
if fileCopied is True:
try:
self.connection.deleteFile(self.share,
self.__binary_service_name)
except Exception:
try:
self.connection.deleteFile(self.share,
self.__binary_service_name)
except Exception:
pass
pass
if serviceCreated is True:
try:
self.rpcsvc.DeleteService(service)
except Exception:
pass