Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# Impacket - Collection of Python classes for working with network protocols. 

2# 

3# SECUREAUTH LABS. Copyright (C) 2021 SecureAuth Corporation. All rights reserved. 

4# 

5# This software is provided under a slightly modified version 

6# of the Apache Software License. See the accompanying LICENSE file 

7# for more information. 

8# 

9# Description: 

10# Target utilities 

11# 

12# Classes for handling specified targets and keeping state of which targets have been processed 

13# Format of targets are based in URI syntax 

14# scheme://netloc/path 

15# where: 

16# scheme: the protocol to target (e.g. 'smb', 'mssql', 'all') 

17# netloc: int the form of domain\username@host:port (domain\username and port are optional, and don't forget 

18# to escape the '\') 

19# path: only used by specific attacks (e.g. HTTP attack). 

20# 

21# Some examples: 

22# smb://1.1.1.1: It will target host 1.1.1.1 (protocol SMB) with any user connecting 

23# mssql://contoso.com\joe@10.1.1.1: It will target host 10.1.1.1 (protocol MSSQL) only when contoso.com\joe is 

24# connecting. 

25# 

26# Author: 

27# Alberto Solino (@agsolino) 

28# Dirk-jan Mollema / Fox-IT (https://www.fox-it.com) 

29# 

30# ToDo: 

31# [ ]: Expand the ALL:// to all the supported protocols 

32# 

33import os 

34import random 

35import time 

36try: 

37 from urllib.parse import urlparse 

38except ImportError: 

39 from urlparse import urlparse 

40from impacket import LOG 

41from threading import Thread 

42 

43 

44class TargetsProcessor: 

45 def __init__(self, targetListFile=None, singleTarget=None, protocolClients=None, randomize=False): 

46 # Here we store the attacks that already finished, mostly the ones that have usernames, since the 

47 # other ones will never finish. 

48 self.finishedAttacks = [] 

49 self.protocolClients = protocolClients 

50 if targetListFile is None: 

51 self.filename = None 

52 self.originalTargets = self.processTarget(singleTarget, protocolClients) 

53 else: 

54 self.filename = targetListFile 

55 self.originalTargets = [] 

56 self.readTargets() 

57 

58 if randomize is True: 

59 # Randomize the targets based 

60 random.shuffle(self.originalTargets) 

61 

62 self.generalCandidates = [x for x in self.originalTargets if x.username is None] 

63 self.namedCandidates = [x for x in self.originalTargets if x.username is not None] 

64 

65 @staticmethod 

66 def processTarget(target, protocolClients): 

67 # Check if we have a single target, with no URI form 

68 if target.find('://') <= 0: 

69 # Target is a single IP, assuming it's SMB. 

70 return [urlparse('smb://%s' % target)] 

71 

72 # Checks if it needs to expand the list if there's a all://* 

73 retVals = [] 

74 if target[:3].upper() == 'ALL': 

75 strippedTarget = target[3:] 

76 for protocol in protocolClients: 

77 retVals.append(urlparse('%s%s' % (protocol, strippedTarget))) 

78 return retVals 

79 else: 

80 return [urlparse(target)] 

81 

82 def readTargets(self): 

83 try: 

84 with open(self.filename,'r') as f: 

85 self.originalTargets = [] 

86 for line in f: 

87 target = line.strip() 

88 if target != '' and target[0] != '#': 

89 self.originalTargets.extend(self.processTarget(target, self.protocolClients)) 

90 except IOError as e: 

91 LOG.error("Could not open file: %s - %s", self.filename, str(e)) 

92 

93 if len(self.originalTargets) == 0: 

94 LOG.critical("Warning: no valid targets specified!") 

95 

96 self.generalCandidates = [x for x in self.originalTargets if x not in self.finishedAttacks and x.username is None] 

97 self.namedCandidates = [x for x in self.originalTargets if x not in self.finishedAttacks and x.username is not None] 

98 

99 def logTarget(self, target, gotRelay = False, gotUsername = None): 

100 # If the target has a username, we can safely remove it from the list. Mission accomplished. 

101 if gotRelay is True: 

102 if target.username is not None: 

103 self.finishedAttacks.append(target) 

104 elif gotUsername is not None: 

105 # We have data about the username we relayed the connection for, 

106 # for a target that didn't have username specified. 

107 # Let's log it 

108 newTarget = urlparse('%s://%s@%s%s' % (target.scheme, gotUsername.replace('/','\\'), target.netloc, target.path)) 

109 self.finishedAttacks.append(newTarget) 

110 

111 def getTarget(self, identity=None): 

112 # ToDo: We should have another list of failed attempts (with user) and check that inside this method so we do not 

113 # retry those targets. 

114 if identity is not None and len(self.namedCandidates) > 0: 

115 # We've been asked to match a username that is connected to us 

116 # Do we have an explicit request for it? 

117 for target in self.namedCandidates: 

118 if target.username is not None: 

119 if target.username.upper() == identity.replace('/', '\\'): 

120 self.namedCandidates.remove(target) 

121 return target 

122 if target.username.find('\\') < 0: 

123 # Username with no domain, let's compare that way 

124 if target.username.upper() == identity.split('/')[1]: 

125 self.namedCandidates.remove(target) 

126 return target 

127 

128 # No identity match, let's just grab something from the generalCandidates list 

129 # Assuming it hasn't been relayed already 

130 if len(self.generalCandidates) > 0: 

131 if identity is not None: 

132 for target in self.generalCandidates: 

133 tmpTarget = '%s://%s@%s' % (target.scheme, identity.replace('/', '\\'), target.netloc) 

134 match = [x for x in self.finishedAttacks if x.geturl().upper() == tmpTarget.upper()] 

135 if len(match) == 0: 

136 self.generalCandidates.remove(target) 

137 return target 

138 LOG.debug("No more targets for user %s" % identity) 

139 return None 

140 else: 

141 return self.generalCandidates.pop() 

142 else: 

143 if len(self.originalTargets) > 0: 

144 self.generalCandidates = [x for x in self.originalTargets if 

145 x not in self.finishedAttacks and x.username is None] 

146 

147 if len(self.generalCandidates) == 0: 

148 if len(self.namedCandidates) == 0: 

149 # We are here, which means all the targets are already exhausted by the client 

150 LOG.info("All targets processed!") 

151 elif identity is not None: 

152 # This user has no more targets 

153 LOG.debug("No more targets for user %s" % identity) 

154 return None 

155 else: 

156 return self.getTarget(identity) 

157 

158class TargetsFileWatcher(Thread): 

159 def __init__(self,targetprocessor): 

160 Thread.__init__(self) 

161 self.targetprocessor = targetprocessor 

162 self.lastmtime = os.stat(self.targetprocessor.filename).st_mtime 

163 

164 def run(self): 

165 while True: 

166 mtime = os.stat(self.targetprocessor.filename).st_mtime 

167 if mtime > self.lastmtime: 

168 LOG.info('Targets file modified - refreshing') 

169 self.lastmtime = mtime 

170 self.targetprocessor.readTargets() 

171 time.sleep(1.0)