mirror of
https://github.com/ly4k/Impacket
synced 2026-06-08 15:35:53 +00:00
616 lines
30 KiB
Python
616 lines
30 KiB
Python
#!/usr/bin/env python3
|
|
# Impacket - Collection of Python classes for working with network protocols.
|
|
#
|
|
# Copyright (C) 2022 Fortra. All rights reserved.
|
|
#
|
|
# This software is provided under a slightly modified version
|
|
# of the Apache Software License. See the accompanying LICENSE file
|
|
# for more information.
|
|
#
|
|
# Description:
|
|
# Terminal Services manipulation tool.
|
|
# Initial idea was to provide similar functionality as the QWINSTA and other TS* windows commands:
|
|
#
|
|
# qwinsta: Display information about Remote Desktop Services sessions.
|
|
# tasklist: Display a list of currently running processes on the system.
|
|
# taskkill: Terminate tasks by process id (PID) or image name
|
|
# tscon: Attaches a user session to a remote desktop session
|
|
# tsdiscon: Disconnects a Remote Desktop Services session
|
|
# tslogoff: Signs-out a Remote Desktop Services session
|
|
# shutdown: Remote shutdown
|
|
# msg: Send a message to Remote Desktop Services session (MSGBOX)
|
|
#
|
|
# Author:
|
|
# Alexander Korznikov (@nopernik)
|
|
#
|
|
# Reference for:
|
|
# [MS-TSTS]
|
|
#
|
|
|
|
import argparse
|
|
import codecs
|
|
import logging
|
|
import sys
|
|
from struct import unpack
|
|
|
|
from impacket import version
|
|
from impacket.examples import logger
|
|
from impacket.examples.utils import parse_target
|
|
from impacket.smbconnection import SMBConnection
|
|
from impacket import LOG
|
|
from impacket.dcerpc.v5 import transport
|
|
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_GSS_NEGOTIATE, RPC_C_AUTHN_LEVEL_PKT_PRIVACY
|
|
|
|
from impacket.dcerpc.v5 import tsts as TSTS
|
|
import traceback
|
|
|
|
|
|
class TSHandler:
|
|
def __init__(self, username, password, domain, options):
|
|
self.__username = username
|
|
self.__password = password
|
|
self.__domain = domain
|
|
self.__options = options
|
|
self.__action = options.action.lower()
|
|
self.__lmhash = ''
|
|
self.__nthash = ''
|
|
self.__aesKey = options.aesKey
|
|
self.__doKerberos = options.k
|
|
self.__kdcHost = options.dc_ip
|
|
self.__smbConnection = None
|
|
self.__remoteOps = None
|
|
|
|
if options.hashes is not None:
|
|
self.__lmhash, self.__nthash = options.hashes.split(':')
|
|
|
|
def connect(self, remoteName, remoteHost):
|
|
self.__smbConnection = SMBConnection(remoteName, remoteHost, sess_port=int(self.__options.port))
|
|
|
|
if self.__doKerberos:
|
|
self.__smbConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash,
|
|
self.__nthash, self.__aesKey, self.__kdcHost)
|
|
else:
|
|
self.__smbConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
|
|
|
|
def run(self, remoteName, remoteHost):
|
|
if self.__options.action == 'shutdown':
|
|
if not max([options.logoff, options.shutdown, options.reboot, options.poweroff]):
|
|
LOG.error('At least one flag is required: -logoff, -shutdown, -reboot or -poweroff')
|
|
exit(1)
|
|
|
|
self.connect(remoteName, remoteHost)
|
|
getattr(self,'do_'+self.__action)()
|
|
|
|
def get_session_list(self):
|
|
# Retreive session list
|
|
smb = self.__smbConnection
|
|
target_ip = self.__options.target_ip
|
|
with TSTS.TermSrvEnumeration(self.__smbConnection, self.__options.target_ip) as lsm:
|
|
handle = lsm.hRpcOpenEnum()
|
|
rsessions = lsm.hRpcGetEnumResult(handle, Level=1)['ppSessionEnumResult']
|
|
lsm.hRpcCloseEnum(handle)
|
|
self.sessions = {}
|
|
for i in rsessions:
|
|
sess = i['SessionInfo']['SessionEnum_Level1']
|
|
state = TSTS.enum2value(TSTS.WINSTATIONSTATECLASS, sess['State']).split('_')[-1]
|
|
self.sessions[sess['SessionId']] = { 'state' :state,
|
|
'SessionName' :sess['Name'],
|
|
'RemoteIp' :'',
|
|
'ClientName' :'',
|
|
'Username' :'',
|
|
'Domain' :'',
|
|
'Resolution' :'',
|
|
'ClientTimeZone':''
|
|
}
|
|
|
|
def enumerate_sessions_config(self):
|
|
# Get session config one by one
|
|
smb = self.__smbConnection
|
|
target_ip = self.__options.target_ip
|
|
if len(self.sessions):
|
|
with TSTS.RCMPublic(self.__smbConnection, self.__options.target_ip) as termsrv:
|
|
for SessionId in self.sessions:
|
|
resp = termsrv.hRpcGetClientData(SessionId)
|
|
if resp is not None:
|
|
self.sessions[SessionId]['RemoteIp'] = resp['ppBuff']['ClientAddress']
|
|
self.sessions[SessionId]['ClientName'] = resp['ppBuff']['ClientName']
|
|
if len(resp['ppBuff']['UserName']) and not len(self.sessions[SessionId]['Username']):
|
|
self.sessions[SessionId]['Username'] = resp['ppBuff']['UserName']
|
|
if len(resp['ppBuff']['Domain']) and not len(self.sessions[SessionId]['Domain']):
|
|
self.sessions[SessionId]['Domain'] = resp['ppBuff']['Domain']
|
|
self.sessions[SessionId]['Resolution'] = '{}x{}'.format(
|
|
resp['ppBuff']['HRes'],
|
|
resp['ppBuff']['VRes']
|
|
)
|
|
self.sessions[SessionId]['ClientTimeZone'] = resp['ppBuff']['ClientTimeZone']['StandardName']
|
|
|
|
def enumerate_sessions_info(self):
|
|
# Get session info one by one
|
|
smb = self.__smbConnection
|
|
target_ip = self.__options.target_ip
|
|
if len(self.sessions):
|
|
with TSTS.TermSrvSession(self.__smbConnection, self.__options.target_ip) as TermSrvSession:
|
|
for SessionId in self.sessions.keys():
|
|
sessdata = TermSrvSession.hRpcGetSessionInformationEx(SessionId)
|
|
sessflags = TSTS.enum2value(TSTS.SESSIONFLAGS, sessdata['LSMSessionInfoExPtr']['LSM_SessionInfo_Level1']['SessionFlags'])
|
|
self.sessions[SessionId]['flags'] = sessflags
|
|
domain = sessdata['LSMSessionInfoExPtr']['LSM_SessionInfo_Level1']['DomainName']
|
|
if not len(self.sessions[SessionId]['Domain']) and len(domain):
|
|
self.sessions[SessionId]['Domain'] = domain
|
|
username = sessdata['LSMSessionInfoExPtr']['LSM_SessionInfo_Level1']['UserName']
|
|
if not len(self.sessions[SessionId]['Username']) and len(username):
|
|
self.sessions[SessionId]['Username'] = username
|
|
self.sessions[SessionId]['ConnectTime'] = sessdata['LSMSessionInfoExPtr']['LSM_SessionInfo_Level1']['ConnectTime']
|
|
self.sessions[SessionId]['DisconnectTime'] = sessdata['LSMSessionInfoExPtr']['LSM_SessionInfo_Level1']['DisconnectTime']
|
|
self.sessions[SessionId]['LogonTime'] = sessdata['LSMSessionInfoExPtr']['LSM_SessionInfo_Level1']['LogonTime']
|
|
self.sessions[SessionId]['LastInputTime'] = sessdata['LSMSessionInfoExPtr']['LSM_SessionInfo_Level1']['LastInputTime']
|
|
|
|
def do_qwinsta(self):
|
|
options = self.__options
|
|
desktop_states = {
|
|
'WTS_SESSIONSTATE_UNKNOWN': '',
|
|
'WTS_SESSIONSTATE_LOCK' : 'Locked',
|
|
'WTS_SESSIONSTATE_UNLOCK' : 'Unlocked',
|
|
}
|
|
self.get_session_list()
|
|
if not len(self.sessions):
|
|
print('No sessions found...')
|
|
return
|
|
self.enumerate_sessions_info()
|
|
if options.verbose:
|
|
self.enumerate_sessions_config()
|
|
|
|
maxSessionNameLen = max([len(self.sessions[i]['SessionName'])+1 for i in self.sessions])
|
|
maxSessionNameLen = maxSessionNameLen if len('SESSIONNAME') < maxSessionNameLen else len('SESSIONNAME')+1
|
|
|
|
# maxUsernameLen = max([len(self.sessions[i]['Username'])+1 for i in self.sessions])
|
|
maxUsernameLen = max([len(self.sessions[i]['Username']+self.sessions[i]['Domain'])+1 for i in self.sessions])+1
|
|
|
|
maxUsernameLen = maxUsernameLen if len('Username') < maxUsernameLen else len('Username')+1
|
|
|
|
|
|
maxIdLen = max([len(str(i)) for i in self.sessions])
|
|
maxIdLen = maxIdLen if len('ID') < maxIdLen else len('ID')+1
|
|
|
|
maxStateLen = max([len(self.sessions[i]['state'])+1 for i in self.sessions])
|
|
maxStateLen = maxStateLen if len('STATE') < maxStateLen else len('STATE')+1
|
|
|
|
maxRemoteIp = max([len(self.sessions[i]['RemoteIp'])+1 for i in self.sessions])
|
|
maxRemoteIp = maxRemoteIp if len('RemoteAddress') < maxRemoteIp else len('RemoteAddress')+1
|
|
|
|
maxClientName = max([len(self.sessions[i]['ClientName'])+1 for i in self.sessions])
|
|
maxClientName = maxClientName if len('ClientName') < maxClientName else len('ClientName')+1
|
|
|
|
template = ('{SESSIONNAME: <%d} '
|
|
'{USERNAME: <%d} '
|
|
'{ID: <%d} '
|
|
'{STATE: <%d} '
|
|
'{DSTATE: <9} '
|
|
'{CONNTIME: <20} '
|
|
'{DISCTIME: <20} ') % (maxSessionNameLen, maxUsernameLen, maxIdLen, maxStateLen)
|
|
|
|
template_verbose = ('{CLIENTNAME: <%d} '
|
|
'{REMOTEIP: <%d} '
|
|
'{RESOLUTION: <11} '
|
|
'{TIMEZONE: <15}') % (maxClientName,maxRemoteIp)
|
|
|
|
result = []
|
|
header = template.format(
|
|
SESSIONNAME = 'SESSIONNAME',
|
|
USERNAME = 'USERNAME',
|
|
ID = 'ID',
|
|
STATE = 'STATE',
|
|
DSTATE = 'Desktop',
|
|
CONNTIME = 'ConnectTime',
|
|
DISCTIME = 'DisconnectTime',
|
|
)
|
|
|
|
header2 = template.replace(' <','=<').format(
|
|
SESSIONNAME = '',
|
|
USERNAME = '',
|
|
ID = '',
|
|
STATE = '',
|
|
DSTATE = '',
|
|
CONNTIME = '',
|
|
DISCTIME = '',
|
|
)
|
|
|
|
header_verbose = ''
|
|
header2_verbose = ''
|
|
if options.verbose:
|
|
header_verbose = template_verbose.format(
|
|
CLIENTNAME = 'ClientName',
|
|
REMOTEIP = 'RemoteAddress',
|
|
RESOLUTION = 'Resolution',
|
|
TIMEZONE = 'ClientTimeZone'
|
|
)
|
|
header2_verbose = template_verbose.replace(' <','=<').format(
|
|
CLIENTNAME = '',
|
|
REMOTEIP = '',
|
|
RESOLUTION = '',
|
|
TIMEZONE = ''
|
|
)
|
|
result.append(header+header_verbose)
|
|
result.append(header2+header2_verbose+'\n')
|
|
|
|
for i in self.sessions:
|
|
connectTime = self.sessions[i]['ConnectTime']
|
|
connectTime = connectTime.strftime(r'%Y/%m/%d %H:%M:%S') if connectTime.year > 1601 else 'None'
|
|
|
|
disconnectTime = self.sessions[i]['DisconnectTime']
|
|
disconnectTime = disconnectTime.strftime(r'%Y/%m/%d %H:%M:%S') if disconnectTime.year > 1601 else 'None'
|
|
userName = self.sessions[i]['Domain'] + '\\' + self.sessions[i]['Username'] if len(self.sessions[i]['Username']) else ''
|
|
|
|
row = template.format(
|
|
SESSIONNAME = self.sessions[i]['SessionName'],
|
|
USERNAME = userName,
|
|
ID = i,
|
|
STATE = self.sessions[i]['state'],
|
|
DSTATE = desktop_states[self.sessions[i]['flags']],
|
|
CONNTIME = connectTime,
|
|
DISCTIME = disconnectTime,
|
|
)
|
|
row_verbose = ''
|
|
if options.verbose:
|
|
row_verbose = template_verbose.format(
|
|
CLIENTNAME = self.sessions[i]['ClientName'],
|
|
REMOTEIP = self.sessions[i]['RemoteIp'],
|
|
RESOLUTION = self.sessions[i]['Resolution'],
|
|
TIMEZONE = self.sessions[i]['ClientTimeZone']
|
|
)
|
|
result.append(row+row_verbose)
|
|
|
|
for row in result:
|
|
print(row)
|
|
|
|
def do_tasklist(self):
|
|
options = self.__options
|
|
with TSTS.LegacyAPI(self.__smbConnection, options.target_ip) as legacy:
|
|
handle = legacy.hRpcWinStationOpenServer()
|
|
r = legacy.hRpcWinStationGetAllProcesses(handle)
|
|
if not len(r):
|
|
return None
|
|
maxImageNameLen = max([len(i['ImageName']) for i in r])
|
|
maxSidLen = max([len(i['pSid']) for i in r])
|
|
if options.verbose:
|
|
self.get_session_list()
|
|
self.enumerate_sessions_config()
|
|
maxUserNameLen = max([len(self.sessions[i]['Username']+self.sessions[i]['Domain'])+1 for i in self.sessions])+1
|
|
if maxUserNameLen < 11:
|
|
maxUserNameLen = 11
|
|
template = ('{imagename: <%d} '
|
|
'{pid: <6} '
|
|
'{sessid: <6} '
|
|
'{sessionName: <16} '
|
|
'{sessstate: <11} '
|
|
'{sessionuser: <%d} '
|
|
'{sid: <%d} '
|
|
'{workingset: <12}') % (maxImageNameLen, maxUserNameLen, maxSidLen)
|
|
|
|
print(template.format(imagename = 'Image Name',
|
|
pid = 'PID',
|
|
sessionName = 'SessName',
|
|
sessid = 'SessID',
|
|
sessionuser = 'SessUser',
|
|
sessstate = 'State',
|
|
sid = 'SID',
|
|
workingset = 'Mem Usage'
|
|
)
|
|
)
|
|
|
|
print(template.replace(' <','=<').format(imagename = '',
|
|
pid = '',
|
|
sessionName = '',
|
|
sessid = '',
|
|
sessionuser = '',
|
|
sessstate = '',
|
|
sid = '',
|
|
workingset = ''
|
|
)+'\n'
|
|
)
|
|
|
|
for procInfo in r:
|
|
sessId = procInfo['SessionId']
|
|
fullUserName = ''
|
|
if len(self.sessions[sessId]['Domain']):
|
|
fullUserName += self.sessions[sessId]['Domain'] + '\\'
|
|
if len(self.sessions[sessId]['Username']):
|
|
fullUserName += self.sessions[sessId]['Username']
|
|
row = template.replace('{workingset: <12}','{workingset: >10,} K').format(
|
|
imagename = procInfo['ImageName'],
|
|
pid = procInfo['UniqueProcessId'],
|
|
sessionName = self.sessions[sessId]['SessionName'],
|
|
sessid = procInfo['SessionId'],
|
|
sessstate = self.sessions[sessId]['state'].replace('Disconnected','Disc'),
|
|
sid = procInfo['pSid'],
|
|
sessionuser = fullUserName,
|
|
workingset = procInfo['WorkingSetSize']//1000
|
|
)
|
|
print(row)
|
|
else:
|
|
template = '{: <%d} {: <8} {: <11} {: <%d} {: >12}' % (maxImageNameLen, maxSidLen)
|
|
print(template.format('Image Name', 'PID', 'Session#', 'SID', 'Mem Usage'))
|
|
print(template.replace(': ',':=').format('','','','','')+'\n')
|
|
for procInfo in r:
|
|
row = template.format(
|
|
procInfo['ImageName'],
|
|
procInfo['UniqueProcessId'],
|
|
procInfo['SessionId'],
|
|
procInfo['pSid'],
|
|
'{:,} K'.format(procInfo['WorkingSetSize']//1000),
|
|
)
|
|
print(row)
|
|
|
|
|
|
def do_taskkill(self):
|
|
options = self.__options
|
|
if options.pid is None and options.name is None:
|
|
LOG.error('One of the following is required: -pid, -name')
|
|
return
|
|
pidList = []
|
|
with TSTS.LegacyAPI(self.__smbConnection, options.target_ip) as legacy:
|
|
handle = legacy.hRpcWinStationOpenServer()
|
|
if options.pid is None and options.name is not None:
|
|
r = legacy.hRpcWinStationGetAllProcesses(handle)
|
|
if not len(r):
|
|
LOG.error('Could not get process list')
|
|
return
|
|
pidList = [i['UniqueProcessId'] for i in r if i['ImageName'].lower() == options.name.lower()]
|
|
if not len(pidList):
|
|
LOG.error('Could not find %r in process list' % options.name)
|
|
return
|
|
else:
|
|
pidList = [options.pid]
|
|
|
|
for pid in pidList:
|
|
print('Terminating PID: %d ...' % pid, end='')
|
|
try:
|
|
if legacy.hRpcWinStationTerminateProcess(handle, pid)['ErrorCode']:
|
|
print('OK')
|
|
else:
|
|
print('FAIL')
|
|
except Exception as e:
|
|
LOG.error('Error terminating pid: %d' % pid)
|
|
LOG.error(str(e))
|
|
|
|
def do_tscon(self):
|
|
options = self.__options
|
|
with TSTS.TermSrvSession(self.__smbConnection, options.target_ip) as TSSession:
|
|
try:
|
|
session_handle = None
|
|
print('Connecting SessionID %d to %d ...' % (options.source, options.dest), end='')
|
|
try:
|
|
session_handle = TSSession.hRpcOpenSession(options.source)
|
|
except Exception as e:
|
|
print('FAIL')
|
|
if e.error_code == 0x80070002:
|
|
LOG.error('Could not find source SessionID: %d' % options.source)
|
|
else:
|
|
LOG.error(str(e))
|
|
return
|
|
if TSSession.hRpcConnect(hSession = session_handle,
|
|
TargetSessionId = options.dest,
|
|
Password = options.password)['ErrorCode'] == 0:
|
|
print('OK')
|
|
else:
|
|
print('FAIL')
|
|
except Exception as e:
|
|
print('FAIL')
|
|
if e.error_code == 0x80070002:
|
|
LOG.error('Could not find destination SessionID: %d' % options.dest)
|
|
elif e.error_code == 0x8007139f:
|
|
LOG.error('Session in the invalid state. Did you mean %d -> %d?' % (options.dest, options.source))
|
|
else:
|
|
LOG.error(str(e))
|
|
|
|
def do_tsdiscon(self):
|
|
options = self.__options
|
|
with TSTS.TermSrvSession(self.__smbConnection, options.target_ip) as TSSession:
|
|
try:
|
|
print('Disconnecting SessionID: %d ...' % options.session, end='')
|
|
session_handle = TSSession.hRpcOpenSession(options.session)
|
|
if TSSession.hRpcDisconnect(session_handle)['ErrorCode'] == 0:
|
|
print('OK')
|
|
else:
|
|
print('FAIL')
|
|
except Exception as e:
|
|
print('FAIL')
|
|
if e.error_code == 1:
|
|
LOG.error('Maybe it is already disconnected?')
|
|
elif e.error_code == 0x80070002:
|
|
LOG.error('Could not find SessionID: %d' % options.session)
|
|
else:
|
|
LOG.error(str(e))
|
|
|
|
def do_logoff(self):
|
|
options = self.__options
|
|
with TSTS.TermSrvSession(self.__smbConnection, options.target_ip) as TSSession:
|
|
try:
|
|
print('Signing-out SessionID: %d ...' % options.session, end='')
|
|
session_handle = TSSession.hRpcOpenSession(options.session)
|
|
|
|
if TSSession.hRpcLogoff(session_handle)['ErrorCode'] == 0:
|
|
print('OK')
|
|
else:
|
|
print('FAIL')
|
|
except Exception as e:
|
|
if e.error_code == 0x10000000:
|
|
print('OK')
|
|
return
|
|
print('FAIL')
|
|
if e.error_code == 0x80070002:
|
|
LOG.error('Could not find SessionID: %d' % options.session)
|
|
else:
|
|
LOG.error(str(e))
|
|
|
|
def do_shutdown(self):
|
|
options = self.__options
|
|
with TSTS.LegacyAPI(self.__smbConnection, options.target_ip) as legacy:
|
|
handle = legacy.hRpcWinStationOpenServer()
|
|
flags = 0
|
|
flagsList = []
|
|
ShutdownFlags = [options.logoff, options.shutdown, options.reboot, options.poweroff]
|
|
for k,v in zip(ShutdownFlags, ['logoff', 'shutdown', 'reboot', 'poweroff']):
|
|
if k:
|
|
flagsList.append(v)
|
|
flagsList = '|'.join(flagsList)
|
|
for k,v in zip(ShutdownFlags, [1,2,4,8]):
|
|
if k:
|
|
flags |= v
|
|
try:
|
|
print('Sending shutdown (%s) event ...' % (flagsList), end='')
|
|
resp = legacy.hRpcWinStationShutdownSystem(handle, 0, flags)
|
|
if resp['ErrorCode']:
|
|
print('OK')
|
|
else:
|
|
resp.dump()
|
|
print('FAIL')
|
|
except Exception as e:
|
|
print('FAIL')
|
|
LOG.error(str(e))
|
|
|
|
|
|
def do_msg(self):
|
|
options = self.__options
|
|
|
|
with TSTS.TermSrvSession(self.__smbConnection, options.target_ip) as TSSession:
|
|
try:
|
|
print('Sending message to SessionID: %d ...' % options.session, end='')
|
|
session_handle = TSSession.hRpcOpenSession(options.session)
|
|
if TSSession.hRpcShowMessageBox(session_handle, options.title, options.message)['ErrorCode'] == 0:
|
|
print('OK')
|
|
else:
|
|
print('FAIL')
|
|
except Exception as e:
|
|
print('FAIL')
|
|
if e.error_code == 0x80070002:
|
|
LOG.error('Could not find SessionID: %d' % options.session)
|
|
else:
|
|
LOG.error(str(e))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
# Init the example's logger theme
|
|
logger.init()
|
|
# Explicitly changing the stdout encoding format
|
|
if sys.stdout.encoding is None:
|
|
# Output is redirected to a file
|
|
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
|
|
print(version.BANNER)
|
|
|
|
parser = argparse.ArgumentParser(add_help=True, description="Terminal Services manipulation tool.")
|
|
|
|
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
|
|
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
|
|
subparsers = parser.add_subparsers(help='actions', dest='action')
|
|
|
|
# qwinsta: Display information about Remote Desktop Services sessions.
|
|
qwinsta_parser = subparsers.add_parser('qwinsta', help='Display information about Remote Desktop Services sessions.')
|
|
qwinsta_parser.add_argument('-v', action='store_true', dest='verbose', help='Turn VERBOSE output ON')
|
|
|
|
# tasklist: Display a list of currently running processes on the system.
|
|
tasklist_parser = subparsers.add_parser('tasklist', help='Display a list of currently running processes on the system.')
|
|
tasklist_parser.add_argument('-v', action='store_true', dest='verbose', help='Turn VERBOSE output ON')
|
|
|
|
# taskkill: Terminate tasks by process id (PID) or image name
|
|
taskkill_parser = subparsers.add_parser('taskkill', help='Terminate tasks by process id (PID) or image name.')
|
|
taskkill_parser.add_argument('-pid', action='store', metavar="PID", type=int, help='Specifies process id (PID)')
|
|
taskkill_parser.add_argument('-name', action='store', help='Specifies process name (ImageName). Internally it will'
|
|
'execute tasklist to retrieve PID by ImageName.')
|
|
|
|
# tscon: Attaches a user session to a remote desktop session
|
|
tscon_parser = subparsers.add_parser('tscon', help='Attaches a user session to a remote desktop session.')
|
|
tscon_parser.add_argument('-source', action='store', metavar="SessionID", type=int, required=True, help='Source SessionId')
|
|
tscon_parser.add_argument('-dest', action='store', metavar="SessionID", type=int, required=True, help='Destination SessionId')
|
|
tscon_parser.add_argument('-password', action='store', type=str, required=False, help='Destination Session\'s password')
|
|
|
|
# tsdiscon: Disconnects a Remote Desktop Services session
|
|
tsdiscon_parser = subparsers.add_parser('tsdiscon', help='Disconnects a Remote Desktop Services session.')
|
|
tsdiscon_parser.add_argument('-session', action='store', metavar="SessionID", type=int, required=True, help='SessionId to disconnect')
|
|
|
|
# logoff: Sign out a Remote Desktop Services session
|
|
logoff_parser = subparsers.add_parser('logoff', help='Sign out a Remote Desktop Services session.')
|
|
logoff_parser.add_argument('-session', action='store', metavar="SessionID", type=int, required=True, help='SessionId to sign out')
|
|
|
|
# shutdown: Remote shutdown
|
|
shutdown_parser = subparsers.add_parser('shutdown', help='Remote shutdown, affects ALL sessions and logged-in users!',
|
|
description="Send Remote Shutdown event. Affects ALL sessions and logged-in users!")
|
|
shutdown_parser_group = shutdown_parser.add_argument_group('Shutdown Flags [Multiple Choice]')
|
|
|
|
shutdown_parser_group.add_argument('-logoff', action='store_true', help='Forces sessions to logoff.')
|
|
shutdown_parser_group.add_argument('-shutdown', action='store_true', help='Shuts down the system.')
|
|
shutdown_parser_group.add_argument('-reboot', action='store_true', help='Reboots after shutdown.')
|
|
shutdown_parser_group.add_argument('-poweroff', action='store_true', help='Powers off after shutdown.')
|
|
|
|
# msg: Send a message to Remote Desktop Services session (MSGBOX)
|
|
msg_parser = subparsers.add_parser('msg', help='Send a message to Remote Desktop Services session (MSGBOX).')
|
|
msg_parser.add_argument('-session', action='store', metavar="SessionID", type=int, required=True, help='Receiver SessionId')
|
|
msg_parser.add_argument('-title', action='store', metavar="'Your Title'", type=str, required=False, help='Title of the MessageBox [Optional]')
|
|
msg_parser.add_argument('-message', action='store', metavar="'Your Message'", type=str, required=True, help='Contents of the MessageBox')
|
|
|
|
# Authentication options
|
|
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 omitted it will 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 omitted 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.action is None:
|
|
parser.print_help()
|
|
LOG.error('Too few arguments...')
|
|
sys.exit(1)
|
|
|
|
|
|
if options.debug is True:
|
|
logging.getLogger().setLevel(logging.DEBUG)
|
|
# Print the Library's installation path
|
|
logging.debug(version.getInstallationPath())
|
|
else:
|
|
logging.getLogger().setLevel(logging.INFO)
|
|
|
|
domain, username, password, remoteName = parse_target(options.target)
|
|
|
|
if options.target_ip is None:
|
|
options.target_ip = remoteName
|
|
|
|
if domain is None:
|
|
domain = ''
|
|
|
|
if options.aesKey is not None:
|
|
options.k = True
|
|
|
|
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:")
|
|
|
|
tsHandler = TSHandler(username, password, domain, options)
|
|
try:
|
|
tsHandler.run(remoteName, options.target_ip)
|
|
except Exception as e:
|
|
traceback.print_exc()
|
|
logging.error(str(e))
|