Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 23 additions & 20 deletions examples/tstool.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,20 +314,20 @@ def do_tasklist(self):
options = self.__options
with TSTS.LegacyAPI(self.__smbConnection, options.target_ip, self.__doKerberos) as legacy:
handle = legacy.hRpcWinStationOpenServer()
r = legacy.hRpcWinStationGetAllProcesses(handle)
if not len(r):
process_entry_list = legacy.hRpcWinStationGetAllProcesses(handle)
if not len(process_entry_list):
return None

self.sids = {}
for procInfo in r:
sid = procInfo['pSid']
for process_entry in process_entry_list:
sid = process_entry.getSid()
if sid[:2] == 'S-' and sid not in self.sids:
self.sids[sid] = sid

self.lookupSids()

maxImageNameLen = max([len(i['ImageName']) for i in r])
maxSidLen = max([len(i['pSid']) for i in r])
maxImageNameLen = max([len(process_entry.getProcessInfo()['ImageNameSize'].getValue()) for process_entry in process_entry_list])
maxSidLen = max([len(process_entry.getSid()) for process_entry in process_entry_list])
if options.verbose:
self.get_session_list()
self.enumerate_sessions_config()
Expand Down Expand Up @@ -365,35 +365,37 @@ def do_tasklist(self):
)+'\n'
)

for procInfo in r:
sessId = procInfo['SessionId']
for process_entry in process_entry_list:
process_info = process_entry.getProcessInfo()
sessId = process_info['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'],
imagename = process_info['ImageNameSize'].getValue(),
pid = process_info['UniqueProcessId'],
sessionName = self.sessions[sessId]['SessionName'],
sessid = procInfo['SessionId'],
sessid = process_info['SessionId'],
sessstate = self.sessions[sessId]['state'].replace('Disconnected','Disc'),
sid = self.sidToUser(procInfo['pSid']),
sid = self.sidToUser(process_entry.getSid()),
sessionuser = fullUserName,
workingset = procInfo['WorkingSetSize']//1000
workingset = process_info['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:
for process_entry in process_entry_list:
process_info = process_entry.getProcessInfo()
row = template.format(
procInfo['ImageName'],
procInfo['UniqueProcessId'],
procInfo['SessionId'],
self.sidToUser(procInfo['pSid']),
'{:,} K'.format(procInfo['WorkingSetSize']//1000),
process_info['ImageNameSize'].getValue(),
process_info['UniqueProcessId'],
process_info['SessionId'],
self.sidToUser(process_entry.getSid()),
'{:,} K'.format(process_info['WorkingSetSize']//1000),
)
print(row)

Expand All @@ -410,7 +412,8 @@ def do_taskkill(self):
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()]
pidList = [i.getProcessInfo()['UniqueProcessId'] for i in r
if i.getProcessInfo()['ImageNameSize'].getValue().lower() == options.name.lower()]
if not len(pidList):
LOG.error('Could not find %r in process list' % options.name)
return
Expand Down
85 changes: 35 additions & 50 deletions impacket/dcerpc/v5/tsts.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from impacket.uuid import uuidtup_to_bin, bin_to_string, string_to_bin
from impacket.dcerpc.v5.ndr import NDR, NDRCALL, NDRSTRUCT, NDRENUM, NDRUNION, NDRUniConformantArray, \
NDRPOINTER, NDRUniConformantVaryingArray, UNKNOWNDATA
from impacket.dcerpc.v5.dtypes import NULL, BOOL, BOOLEAN, STR, WSTR, LPWSTR, WIDESTR, RPC_UNICODE_STRING, \
from impacket.dcerpc.v5.dtypes import NULL, BOOL, BOOLEAN, STR, WSTR, LPWSTR, WIDESTR, \
LONG, UINT, ULONG, PULONG, LPDWORD, LARGE_INTEGER, DWORD, NDRHYPER, USHORT, UCHAR, PCHAR, BYTE, PBYTE, \
UUID, GUID
from impacket import system_errors
Expand Down Expand Up @@ -203,6 +203,18 @@ class TS_UNICODE_STRING(NDRSTRUCT):
('MaximumLength', USHORT),
('Buffer', LPWSTR),
)
def getValue(self):
# [MS-TSTS] 2.2.2.15.1.1 TS_UNICODE_STRING / [MS-DTYP] RPC_UNICODE_STRING:
# a null PWSTR means there is no string buffer to decode, so expose ''.
if self.fields['Buffer'].fields['ReferentID'] == 0:
return ''
rawBuffer = self.fields['Buffer'].fields['Data'].fields['Data']
value = rawBuffer[:self['Length']].decode('utf-16le')
# Some servers appear to count the terminating UTF-16 NUL inside Length
# even though UNICODE_STRING semantics say the logical value excludes it.
if value.endswith('\x00'):
return value[:-1]
return value

class TS_LPCHAR(NDRPOINTER):
referent = (
Expand Down Expand Up @@ -257,6 +269,7 @@ class LPUCHAR_ARRAY(NDRPOINTER):
referent = (
('Data', UCHAR_ARRAY),
)

class WCHAR_ARRAY_32(WIDESTR_STRIPPED):
length = 32
class WCHAR_ARRAY_256(WIDESTR_STRIPPED):
Expand Down Expand Up @@ -1314,6 +1327,7 @@ class enumItems(Enum):


# 2.2.2.15.1 TS_SYS_PROCESS_INFORMATION
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsts/96702c7d-6f5f-4965-8f29-5fdbea73278e
class TS_SYS_PROCESS_INFORMATION(NDRSTRUCT):
structure = (
('NextEntryOffset', ULONG),
Expand All @@ -1324,7 +1338,7 @@ class TS_SYS_PROCESS_INFORMATION(NDRSTRUCT):
('CreateTime', LARGE_INTEGER),
('UserTime', LARGE_INTEGER),
('KernelTime', LARGE_INTEGER),
('ImageNameSize', RPC_UNICODE_STRING),
('ImageNameSize', TS_UNICODE_STRING),
('BasePriority', LONG),
('UniqueProcessId', DWORD),
('InheritedFromUniqueProcessId', DWORD),
Expand All @@ -1343,8 +1357,6 @@ class TS_SYS_PROCESS_INFORMATION(NDRSTRUCT):
('PagefileUsage', ULONG), #SIZE_T
('PeakPagefileUsage', ULONG), #SIZE_T
('PrivatePageCount', ULONG), #SIZE_T
('ImageName', WSTR_STRIPPED), # THIS SHOULD NOT BE HERE
('pSid', SID), # THIS SHOULD NOT BE HERE
)


Expand All @@ -1354,15 +1366,24 @@ class PTS_SYS_PROCESS_INFORMATION(NDRPOINTER):
)

# 2.2.2.15 TS_ALL_PROCESSES_INFO
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsts/8f5564a6-1b5e-4381-afc4-4acf7ad39d6d
class TS_ALL_PROCESSES_INFO(NDRSTRUCT):
structure = (
('pTsProcessInfo', TS_SYS_PROCESS_INFORMATION),
('pTsProcessInfo', PTS_SYS_PROCESS_INFORMATION),
('SizeOfSid', DWORD),
('pSid', TS_CHAR),
('pSid', LPUCHAR_ARRAY),
)
def getProcessInfo(self):
return self.fields['pTsProcessInfo'].fields['Data']

def getSid(self):
rawSid = self.fields['pSid']['Data'] if self.fields['pSid'].fields['ReferentID'] else b''
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rawSid it's returned for those cases where it's populated by the target, as a UCHAR_ARRAY, which is not expected by code client, as they expect to be a buffer. change line 1380 for rawSid = b''.join( self.fields['pSid']['Data'] ) if self.fields['pSid'].fields['ReferentID'] else b''

if not rawSid:
return ''
return SID().known_sid(format_sid(rawSid))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not use ldap3 helpers, as we're moving away from ldap3 depdency


class TS_ALL_PROCESSES_INFO_ARRAY(NDRUniConformantVaryingArray):
item = TS_SYS_PROCESS_INFORMATION
class TS_ALL_PROCESSES_INFO_ARRAY(NDRUniConformantArray):
item = TS_ALL_PROCESSES_INFO

class PTS_ALL_PROCESSES_INFO(NDRPOINTER):
referent = (
Expand Down Expand Up @@ -2717,6 +2738,7 @@ class RpcWinStationNtsdDebugResponse(NDRCALL):
('ErrorCode', BOOLEAN),
)
# 3.7.4.1.22 RpcWinStationGetAllProcesses (Opnum 43)
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsts/6f43f9e4-2d80-4c5c-bc0b-7f653b1d7c02
class RpcWinStationGetAllProcesses(NDRCALL):
opnum = 43
structure = (
Expand All @@ -2729,7 +2751,8 @@ class RpcWinStationGetAllProcessesResponse(NDRCALL):
structure = (
('pResult', pResult_ENUM),
('pNumberOfProcesses', BOUNDED_ULONG),
('buffer',':'),
('ppTsAllProcessesInfo', PTS_ALL_PROCESSES_INFO),
('ErrorCode', BOOLEAN),
)
# 3.7.4.1.23 RpcWinStationGetProcessSid (Opnum 44)
class RpcWinStationGetProcessSid(NDRCALL):
Expand Down Expand Up @@ -3556,54 +3579,16 @@ def hRpcWinStationTerminateProcess(dce, hServer, ProcessId, ExitCode = 0):

# 3.7.4.1.22 RpcWinStationGetAllProcesses (Opnum 43)
def hRpcWinStationGetAllProcesses(dce, hServer):
# i'm giving up constructing legitimate structures for this method
# Going to parse raw response:
# 1. Skip ndrpointers
# 2. Create TS_SYS_PROCESS_INFORMATION structure one by one
# Tested and seems to work well on WIN11, WIN10, WIN2012R2, WIN7
request = RpcWinStationGetAllProcesses()
request['hServer'] = hServer
request['Level'] = 0
request['pNumberOfProcesses'] = 0x8000
resp = dce.request(request, checkError=False)
data = resp.getData()
bResult = bool(data[-1])
if not bResult:
if not resp['ErrorCode']:
raise DCERPCSessionError(error_code=resp['pResult'])
data = data[:-1]
procs = []
if not resp['pNumberOfProcesses']:
return procs
offset = 0
arrayOffset = 0
while 1:
offset = data.find(b'\x02\x00')
if offset > 12:
break
data = data[offset+2:]
arrayOffset = arrayOffset + offset + 2
procInfo = ''
while len(data)>1:
if len(data[len(procInfo):]) < 16:
break
# I think there some alignment problems...
# in the structure, second DWORD is thread count, i'm looking for the second DWORD
# in order to align the data correctly
# There is no proper errors handling!
b,c,d,e = struct.unpack('<LLLL',data[len(procInfo):len(procInfo)+16])
if b:
data = data[len(procInfo)-4:]
elif c:
data = data[len(procInfo):]
elif d:
data = data[len(procInfo)+4:]
elif e:
data = data[len(procInfo)+8:]

procInfo = TS_SYS_PROCESS_INFORMATION()
procInfo.fromString(data)
procs.append(procInfo)
return procs
return []
return resp['ppTsAllProcessesInfo']

# 3.7.4.1.23 RpcWinStationGetProcessSid (Opnum 44)
def hRpcWinStationGetProcessSid(dce, hServer, dwUniqueProcessId, ProcessStartTime):
Expand Down
Loading