text: #!/usr/bin/python
  # Copyright (c) 2003-2016 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.
  #
  # PSEXEC like functionality example using RemComSvc (https://github.com/kavika13/RemCom)
  #
  # Author:
  #  beto (@agsolino)
  #
  # Reference for:
  #  DCE/RPC and SMB.
  
  import sys
  import os
  import cmd
  import logging
  from threading import Thread, Lock
  import argparse
  import random
  import string
  import time
  
  from impacket.examples import logger
  from impacket import version, smb
  from impacket.smbconnection import SMBConnection
  from impacket.dcerpc.v5 import transport
  from impacket.structure import Structure
  from impacket.examples import remcomsvc, serviceinstall
  
  
  class RemComMessage(Structure):
      structure = (
          ('Command','4096s=""'),
          ('WorkingDir','260s=""'),
          ('Priority','<L=0x20'),
          ('ProcessID','<L=0x01'),
          ('Machine','260s=""'),
          ('NoWait','<L=0'),
      )
  
  class RemComResponse(Structure):
      structure = (
          ('ErrorCode','<L=0'),
          ('ReturnCode','<L=0'),
      )
  
  RemComSTDOUT         = "RemCom_stdout"
  RemComSTDIN          = "RemCom_stdin"
  RemComSTDERR         = "RemCom_stderr"
  
  lock = Lock()
  
  class PSEXEC:
      def __init__(self, command, path, exeFile, copyFile, port=445,
                   username='', password='', domain='', hashes=None, aesKey=None, doKerberos=False, kdcHost=None):
          self.__username = username
          self.__password = password
          self.__port = port
          self.__command = command
          self.__path = path
          self.__domain = domain
          self.__lmhash = ''
          self.__nthash = ''
          self.__aesKey = aesKey
          self.__exeFile = exeFile
          self.__copyFile = copyFile
          self.__doKerberos = doKerberos
          self.__kdcHost = kdcHost
          if hashes is not None:
              self.__lmhash, self.__nthash = hashes.split(':')
  
      def run(self, remoteName, remoteHost):
  
          stringbinding = 'ncacn_np:%s[\pipe\svcctl]' % remoteName
          logging.debug('StringBinding %s'%stringbinding)
          rpctransport = transport.DCERPCTransportFactory(stringbinding)
          rpctransport.set_dport(self.__port)
          rpctransport.setRemoteHost(remoteHost)
  
          if hasattr(rpctransport, 'set_credentials'):
              # This method exists only for selected protocol sequences.
              rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash,
                                           self.__nthash, self.__aesKey)
  
          rpctransport.set_kerberos(self.__doKerberos, self.__kdcHost)
          self.doStuff(rpctransport)
  
      def openPipe(self, s, tid, pipe, accessMask):
          pipeReady = False
          tries = 50
          while pipeReady is False and tries > 0:
              try:
                  s.waitNamedPipe(tid,pipe)
                  pipeReady = True
              except:
                  tries -= 1
                  time.sleep(2)
                  pass
  
          if tries == 0:
              logging.critical('Pipe not ready, aborting')
              raise
  
          fid = s.openFile(tid,pipe,accessMask, creationOption = 0x40, fileAttributes = 0x80)
  
          return fid
  
      def doStuff(self, rpctransport):
  
          dce = rpctransport.get_dce_rpc()
          try:
              dce.connect()
          except Exception, e:
              #import traceback
              #traceback.print_exc()
              logging.critical(str(e))
              sys.exit(1)
  
          global dialect
          dialect = rpctransport.get_smb_connection().getDialect()
  
          try:
              unInstalled = False
              s = rpctransport.get_smb_connection()
  
              # We don't wanna deal with timeouts from now on.
              s.setTimeout(100000)
              if self.__exeFile is None:
                  installService = serviceinstall.ServiceInstall(rpctransport.get_smb_connection(), remcomsvc.RemComSvc())
              else:
                  try:
                      f = open(self.__exeFile)
                  except Exception, e:
                      logging.critical(str(e))
                      sys.exit(1)
                  installService = serviceinstall.ServiceInstall(rpctransport.get_smb_connection(), f)
      
              installService.install()
  
              if self.__exeFile is not None:
                  f.close()
  
              # Check if we need to copy a file for execution
              if self.__copyFile is not None:
                  installService.copy_file(self.__copyFile, installService.getShare(), os.path.basename(self.__copyFile))
                  # And we change the command to be executed to this filename
                  self.__command = os.path.basename(self.__copyFile) + ' ' + self.__command
  
              tid = s.connectTree('IPC$')
              fid_main = self.openPipe(s,tid,'\RemCom_communicaton',0x12019f)
  
              packet = RemComMessage()
              pid = os.getpid()
  
              packet['Machine'] = ''.join([random.choice(string.letters) for _ in range(4)])
              if self.__path is not None:
                  packet['WorkingDir'] = self.__path
              packet['Command'] = self.__command
              packet['ProcessID'] = pid
  
              s.writeNamedPipe(tid, fid_main, str(packet))
  
              # Here we'll store the command we type so we don't print it back ;)
              # ( I know.. globals are nasty :P )
              global LastDataSent
              LastDataSent = ''
  
              # Create the pipes threads
              stdin_pipe = RemoteStdInPipe(rpctransport,
                                           '\%s%s%d' % (RemComSTDIN, packet['Machine'], packet['ProcessID']),
                                           smb.FILE_WRITE_DATA | smb.FILE_APPEND_DATA, installService.getShare())
              stdin_pipe.start()
              stdout_pipe = RemoteStdOutPipe(rpctransport,
                                             '\%s%s%d' % (RemComSTDOUT, packet['Machine'], packet['ProcessID']),
                                             smb.FILE_READ_DATA)
              stdout_pipe.start()
              stderr_pipe = RemoteStdErrPipe(rpctransport,
                                             '\%s%s%d' % (RemComSTDERR, packet['Machine'], packet['ProcessID']),
                                             smb.FILE_READ_DATA)
              stderr_pipe.start()
              
              # And we stay here till the end
              ans = s.readNamedPipe(tid,fid_main,8)
  
              if len(ans):
                  retCode = RemComResponse(ans)
                  logging.info("Process %s finished with ErrorCode: %d, ReturnCode: %d" % (
                  self.__command, retCode['ErrorCode'], retCode['ReturnCode']))
              installService.uninstall()
              if self.__copyFile is not None:
                  # We copied a file for execution, let's remove it
                  s.deleteFile(installService.getShare(), os.path.basename(self.__copyFile))
              unInstalled = True
              sys.exit(retCode['ErrorCode'])
  
          except SystemExit:
              raise
          except:
              #import traceback
              #traceback.print_exc()
              if unInstalled is False:
                  installService.uninstall()
                  if self.__copyFile is not None:
                      s.deleteFile(installService.getShare(), os.path.basename(self.__copyFile))
              sys.stdout.flush()
              sys.exit(1)
  
  class Pipes(Thread):
      def __init__(self, transport, pipe, permissions, share=None):
          Thread.__init__(self)
          self.server = 0
          self.transport = transport
          self.credentials = transport.get_credentials()
          self.tid = 0
          self.fid = 0
          self.share = share
          self.port = transport.get_dport()
          self.pipe = pipe
          self.permissions = permissions
          self.daemon = True
  
      def connectPipe(self):
          try:
              lock.acquire()
              global dialect
              #self.server = SMBConnection('*SMBSERVER', self.transport.get_smb_connection().getRemoteHost(), sess_port = self.port, preferredDialect = SMB_DIALECT)
              self.server = SMBConnection(self.transport.get_smb_connection().getRemoteName(), self.transport.get_smb_connection().getRemoteHost(),
                                          sess_port=self.port, preferredDialect=dialect)
              user, passwd, domain, lm, nt, aesKey, TGT, TGS = self.credentials
              if self.transport.get_kerberos() is True:
                  self.server.kerberosLogin(user, passwd, domain, lm, nt, aesKey, kdcHost=self.transport.get_kdcHost(), TGT=TGT, TGS=TGS)
              else:
                  self.server.login(user, passwd, domain, lm, nt)
              lock.release()
              self.tid = self.server.connectTree('IPC$') 
  
              self.server.waitNamedPipe(self.tid, self.pipe)
              self.fid = self.server.openFile(self.tid,self.pipe,self.permissions, creationOption = 0x40, fileAttributes = 0x80)
              self.server.setTimeout(1000000)
          except:
              import traceback
              traceback.print_exc()
              logging.error("Something wen't wrong connecting the pipes(%s), try again" % self.__class__)
  
  
  class RemoteStdOutPipe(Pipes):
      def __init__(self, transport, pipe, permisssions):
          Pipes.__init__(self, transport, pipe, permisssions)
  
      def run(self):
          self.connectPipe()
          while True:
              try:
                  ans = self.server.readFile(self.tid,self.fid, 0, 1024)
              except:
                  pass
              else:
                  try:
                      global LastDataSent
                      if ans != LastDataSent:
                          sys.stdout.write(ans.decode('cp437'))
                          sys.stdout.flush()
                      else:
                          # Don't echo what I sent, and clear it up
                          LastDataSent = ''
                      # Just in case this got out of sync, i'm cleaning it up if there are more than 10 chars, 
                      # it will give false positives tho.. we should find a better way to handle this.
                      if LastDataSent > 10:
                          LastDataSent = ''
                  except:
                      pass
  
  class RemoteStdErrPipe(Pipes):
      def __init__(self, transport, pipe, permisssions):
          Pipes.__init__(self, transport, pipe, permisssions)
  
      def run(self):
          self.connectPipe()
          while True:
              try:
                  ans = self.server.readFile(self.tid,self.fid, 0, 1024)
              except:
                  pass
              else:
                  try:
                      sys.stderr.write(str(ans))
                      sys.stderr.flush()
                  except:
                      pass
  
  class RemoteShell(cmd.Cmd):
      def __init__(self, server, port, credentials, tid, fid, share, transport):
          cmd.Cmd.__init__(self, False)
          self.prompt = '\x08'
          self.server = server
          self.transferClient = None
          self.tid = tid
          self.fid = fid
          self.credentials = credentials
          self.share = share
          self.port = port
          self.transport = transport
          self.intro = '[!] Press help for extra shell commands'
  
      def connect_transferClient(self):
          #self.transferClient = SMBConnection('*SMBSERVER', self.server.getRemoteHost(), sess_port = self.port, preferredDialect = SMB_DIALECT)
          self.transferClient = SMBConnection('*SMBSERVER', self.server.getRemoteHost(), sess_port=self.port,
                                              preferredDialect=dialect)
          user, passwd, domain, lm, nt, aesKey, TGT, TGS = self.credentials
          if self.transport.get_kerberos() is True:
              self.transferClient.kerberosLogin(user, passwd, domain, lm, nt, aesKey,
                                                kdcHost=self.transport.get_kdcHost(), TGT=TGT, TGS=TGS)
          else:
              self.transferClient.login(user, passwd, domain, lm, nt)
  
      def do_help(self, line):
          print """
   lcd {path}                 - changes the current local directory to {path}
   exit                       - terminates the server process (and this session)
   put {src_file, dst_path}   - uploads a local file to the dst_path RELATIVE to the connected share (%s)
   get {file}                 - downloads pathname RELATIVE to the connected share (%s) to the current local dir 
   ! {cmd}                    - executes a local shell cmd
  """ % (self.share, self.share)
          self.send_data('\r\n', False)
  
      def do_shell(self, s):
          os.system(s)
          self.send_data('\r\n')
  
      def do_get(self, src_path):
          try:
              if self.transferClient is None:
                  self.connect_transferClient()
  
              import ntpath
              filename = ntpath.basename(src_path)
              fh = open(filename,'wb')
              logging.info("Downloading %s\%s" % (self.share, src_path))
              self.transferClient.getFile(self.share, src_path, fh.write)
              fh.close()
          except Exception, e:
              logging.critical(str(e))
              pass
  
          self.send_data('\r\n')
   
      def do_put(self, s):
          try:
              if self.transferClient is None:
                  self.connect_transferClient()
              params = s.split(' ')
              if len(params) > 1:
                  src_path = params[0]
                  dst_path = params[1]
              elif len(params) == 1:
                  src_path = params[0]
                  dst_path = '/'
  
              src_file = os.path.basename(src_path)
              fh = open(src_path, 'rb')
              f = dst_path + '/' + src_file
              pathname = string.replace(f,'/','\\')
              logging.info("Uploading %s to %s\%s" % (src_file, self.share, dst_path))
              self.transferClient.putFile(self.share, pathname.decode(sys.stdin.encoding), fh.read)
              fh.close()
          except Exception, e:
              logging.error(str(e))
              pass
  
          self.send_data('\r\n')
  
      def do_lcd(self, s):
          if s == '':
              print os.getcwd()
          else:
              os.chdir(s)
          self.send_data('\r\n')
  
      def emptyline(self):
          self.send_data('\r\n')
          return
  
      def default(self, line):
          self.send_data(line.decode(sys.stdin.encoding).encode('cp437')+'\r\n')
  
      def send_data(self, data, hideOutput = True):
          if hideOutput is True:
              global LastDataSent
              LastDataSent = data
          else:
              LastDataSent = ''
          self.server.writeFile(self.tid, self.fid, data)
  
  class RemoteStdInPipe(Pipes):
      def __init__(self, transport, pipe, permisssions, share=None):
          self.shell = None
          Pipes.__init__(self, transport, pipe, permisssions, share)
  
      def run(self):
          self.connectPipe()
          self.shell = RemoteShell(self.server, self.port, self.credentials, self.tid, self.fid, self.share, self.transport)
          self.shell.cmdloop()
  
  # Process command-line arguments.
  if __name__ == '__main__':
      # Init the example's logger theme
      logger.init()
      print version.BANNER
  
      parser = argparse.ArgumentParser(add_help = True, description = "PSEXEC like functionality example using RemComSvc.")
  
      parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
      parser.add_argument('command', nargs='*', default = ' ', help='command (or arguments if -c is used) to execute at the target (w/o path) - (default:cmd.exe)')
      parser.add_argument('-c', action='store',metavar = "pathname",  help='copy the filename for later execution, arguments are passed in the command option')
      parser.add_argument('-path', action='store', help='path of the command to execute')
      parser.add_argument('-file', action='store', help="alternative RemCom binary (be sure it doesn't require CRT)")
      parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
  
      group = parser.add_argument_group('authentication')
  
      group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
      group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
      group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file (KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the ones specified in the command line')
      group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication (128 or 256 bits)')
  
      group = parser.add_argument_group('connection')
  
      group.add_argument('-dc-ip', action='store', metavar="ip address",
                         help='IP Address of the domain controller. If ommited it use the domain part (FQDN) specified in the target parameter')
      group.add_argument('-target-ip', action='store', metavar="ip address",
                         help='IP Address of the target machine. If ommited it will use whatever was specified as target. '
                              'This is useful when target is the NetBIOS name and you cannot resolve it')
      group.add_argument('-port', choices=['139', '445'], nargs='?', default='445', metavar="destination port",
                         help='Destination port to connect to SMB Server')
  
      if len(sys.argv)==1:
          parser.print_help()
          sys.exit(1)
  
      options = parser.parse_args()
  
      if options.debug is True:
          logging.getLogger().setLevel(logging.DEBUG)
      else:
          logging.getLogger().setLevel(logging.INFO)
  
      import re
  
      domain, username, password, remoteName = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
          options.target).groups('')
      
      #In case the password contains '@'
      if '@' in remoteName:
          password = password + '@' + remoteName.rpartition('@')[0]
          remoteName = remoteName.rpartition('@')[2]
  
      if domain is None:
          domain = ''
  
      if options.target_ip is None:
          options.target_ip = remoteName
  
      if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
          from getpass import getpass
          password = getpass("Password:")
  
      if options.aesKey is not None:
          options.k = True
  
      command = ' '.join(options.command)
      if command == ' ':
          command = 'cmd.exe'
  
      executer = PSEXEC(command, options.path, options.file, options.c, int(options.port), username, password, domain, options.hashes,
                        options.aesKey, options.k, options.dc_ip)
      executer.run(remoteName, options.target_ip)
  