@@ -688,7 +688,7 @@ def is_host_dc(self):
688688 from impacket .dcerpc .v5 import nrpc , epm
689689
690690 self .logger .debug ("Performing authentication attempts..." )
691-
691+
692692 # First check if port 135 is open
693693 if self ._is_port_open (135 ):
694694 self .logger .debug ("Port 135 is open, attempting MSRPC connection..." )
@@ -964,7 +964,7 @@ def enumerate_sessions_info(self, sessions):
964964 sessions [SessionId ]["DisconnectTime" ] = sessdata ["LSMSessionInfoExPtr" ]["LSM_SessionInfo_Level1" ]["DisconnectTime" ]
965965 sessions [SessionId ]["LogonTime" ] = sessdata ["LSMSessionInfoExPtr" ]["LSM_SessionInfo_Level1" ]["LogonTime" ]
966966 sessions [SessionId ]["LastInputTime" ] = sessdata ["LSMSessionInfoExPtr" ]["LSM_SessionInfo_Level1" ]["LastInputTime" ]
967-
967+
968968 try :
969969 with TSTS .RCMPublic (self .conn , self .host , self .kerberos ) as rcm :
970970 for SessionId in sessions :
@@ -1011,33 +1011,32 @@ def qwinsta(self):
10111011 "WTS_SESSIONSTATE_LOCK" : "Locked" ,
10121012 "WTS_SESSIONSTATE_UNLOCK" : "Unlocked" ,
10131013 }
1014+
10141015 sessions = self .get_session_list ()
1015- if not len ( sessions ) :
1016+ if not sessions :
10161017 return
1018+
10171019 self .enumerate_sessions_info (sessions )
10181020
1021+ # Calculate max lengths for formatting
10191022 maxSessionNameLen = max (len (sessions [i ]["SessionName" ]) + 1 for i in sessions )
1020- maxSessionNameLen = maxSessionNameLen if len ( "SESSIONNAME" ) < maxSessionNameLen else len ("SESSIONNAME" ) + 1
1023+ maxSessionNameLen = max ( maxSessionNameLen , len ("SESSIONNAME" ) + 1 )
10211024 maxUsernameLen = max (len (sessions [i ]["Username" ] + sessions [i ]["Domain" ]) + 1 for i in sessions ) + 1
1022- maxUsernameLen = maxUsernameLen if len ( "Username" ) < maxUsernameLen else len ("Username " ) + 1
1025+ maxUsernameLen = max ( maxUsernameLen , len ("USERNAME " ) + 1 )
10231026 maxIdLen = max (len (str (i )) for i in sessions )
1024- maxIdLen = maxIdLen if len ( "ID" ) < maxIdLen else len ("ID" ) + 1
1027+ maxIdLen = max ( maxIdLen , len ("ID" ) + 1 )
10251028 maxStateLen = max (len (sessions [i ]["state" ]) + 1 for i in sessions )
1026- maxStateLen = maxStateLen if len ("STATE" ) < maxStateLen else len ("STATE" ) + 1
1027- maxRemoteIp = max (len (sessions [i ]["RemoteIp" ]) + 1 for i in sessions )
1028- maxRemoteIp = maxRemoteIp if len ("RemoteAddress" ) < maxRemoteIp else len ("RemoteAddress" ) + 1
1029- maxClientName = max (len (sessions [i ]["ClientName" ]) + 1 for i in sessions )
1030- maxClientName = maxClientName if len ("ClientName" ) < maxClientName else len ("ClientName" ) + 1
1031- template = ("{SESSIONNAME: <%d} " # noqa: UP031
1032- "{USERNAME: <%d} "
1033- "{ID: <%d} "
1029+ maxStateLen = max (maxStateLen , len ("STATE" ) + 1 )
1030+
1031+ # Create the template for formatting
1032+ template = (f"{{SESSIONNAME: <{ maxSessionNameLen } }} "
1033+ f"{{USERNAME: <{ maxUsernameLen } }} "
1034+ f"{{ID: <{ maxIdLen } }} "
10341035 "{IPv4: <16} "
1035- "{ STATE: <%d } "
1036+ f"{{ STATE: <{ maxStateLen } } } "
10361037 "{DSTATE: <9} "
10371038 "{CONNTIME: <20} "
1038- "{DISCTIME: <20} " ) % (maxSessionNameLen , maxUsernameLen , maxIdLen , maxStateLen )
1039-
1040- result = []
1039+ "{DISCTIME: <20} " )
10411040 header = template .format (
10421041 SESSIONNAME = "SESSIONNAME" ,
10431042 USERNAME = "USERNAME" ,
@@ -1048,7 +1047,6 @@ def qwinsta(self):
10481047 CONNTIME = "ConnectTime" ,
10491048 DISCTIME = "DisconnectTime" ,
10501049 )
1051-
10521050 header2 = template .replace (" <" , "=<" ).format (
10531051 SESSIONNAME = "" ,
10541052 USERNAME = "" ,
@@ -1059,30 +1057,49 @@ def qwinsta(self):
10591057 CONNTIME = "" ,
10601058 DISCTIME = "" ,
10611059 )
1062- result .extend ((header , header2 ))
1060+ result = [header , header2 ]
1061+
1062+ # Check if we need to filter for usernames
1063+ usernames = None
1064+ if self .args .qwinsta :
1065+ arg = self .args .qwinsta
1066+ if os .path .isfile (arg ):
1067+ with open (arg ) as f :
1068+ usernames = [line .strip ().lower () for line in f if line .strip ()]
1069+ else :
1070+ usernames = [arg .lower ()]
10631071
10641072 for i in sessions :
1073+ username = sessions [i ]["Username" ]
1074+ domain = sessions [i ]["Domain" ]
1075+ user_full = f"{ domain } \\ { username } " if username else ""
1076+
1077+ # If usernames are provided, filter them
1078+ if usernames and username .lower () not in usernames :
1079+ continue
1080+
10651081 connectTime = sessions [i ]["ConnectTime" ]
10661082 connectTime = connectTime .strftime (r"%Y/%m/%d %H:%M:%S" ) if connectTime .year > 1601 else "None"
10671083
10681084 disconnectTime = sessions [i ]["DisconnectTime" ]
10691085 disconnectTime = disconnectTime .strftime (r"%Y/%m/%d %H:%M:%S" ) if disconnectTime .year > 1601 else "None"
1070- userName = sessions [i ]["Domain" ] + "\\ " + sessions [i ]["Username" ] if len (sessions [i ]["Username" ]) else ""
10711086
1072- result . append ( template .format (
1087+ row = template .format (
10731088 SESSIONNAME = sessions [i ]["SessionName" ],
1074- USERNAME = userName ,
1089+ USERNAME = user_full ,
10751090 ID = i ,
10761091 IPv4 = sessions [i ]["RemoteIp" ],
10771092 STATE = sessions [i ]["state" ],
10781093 DSTATE = desktop_states [sessions [i ]["flags" ]],
10791094 CONNTIME = connectTime ,
10801095 DISCTIME = disconnectTime ,
1081- ))
1096+ )
1097+ result .append (row )
10821098
1083- self .logger .success ("Enumerated qwinsta sessions" )
1084- for row in result :
1085- self .logger .highlight (row )
1099+ if len (result ) > 2 :
1100+ self .logger .success ("Enumerated qwinsta sessions" )
1101+ for row in result :
1102+ self .logger .highlight (row )
10861103
10871104 @requires_admin
10881105 def tasklist (self ):
0 commit comments