Coverage for /root/GitHubProjects/impacket/impacket/examples/ntlmrelayx/utils/targetsutils.py: 12%

# Coverage for /root/GitHubProjects/impacket/impacket/examples/ntlmrelayx/utils/targetsutils.py : 12%

## 98 statements

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#

33 import os

34 import random

35 import time

36 try:

37 from urllib.parse import urlparse

38 except ImportError:

39 from urlparse import urlparse

40 from impacket import LOG

41 from threading import Thread

42

43

44 class 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

158 class 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)