-
Notifications
You must be signed in to change notification settings - Fork 194
/
attackTOR.py
317 lines (285 loc) · 12.6 KB
/
attackTOR.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
from shodan.api import WebAPIError
import zlib
from shodan import WebAPI
from stem.descriptor import parse_file
from plumbum import cli, local
from stem.descriptor.remote import DescriptorDownloader
import logging as log
import threading
import nmap
import Queue
from time import gmtime, strftime
#
# Attack exit nodes of the TOR Network.
# Author: Adastra.
# http://thehackerway.com
#
class Cli(cli.Application):
'''
Command-Line options received.
'''
verbose = cli.Flag(["-v", '--verbose'], help="Verbose Mode.")
brute = cli.Flag(["-b", '--brute'], help="Brute Force Mode. (Specify -u/--users-file and -f/--passwords-file options to select the users and passwords files.)")
useMirror = cli.Flag(["-d", '--use-mirrors'], help="Use the mirror directories of TOR. This will help to not overwhelm the official directories")
useShodan = cli.Flag(["-s", '--use-shodan'], help="Use ShodanHQ Service. (Specify -k/--shodan-key to set up the file where's stored your shodan key.)")
threads = 1
mode = None
usersFile = None
passFile = None
exitNodesToAttack = 10 #Number of default exit-nodes to filter from the Server Descriptor file.
shodanKey = None
scanPorts = "21,22,23,53,69,80,88,110,139,143,161,389,443,445,1079,1080,1433,3306,5432,8080,9050,9051,5800" #Default ports used to scan with nmap.
scanProtocol = 'tcp' #Using TCP protocol to perform the nmap scan.
scanArguments = None #Scan Arguments passed to nmap.
exitNodeFingerprint = None #Fingerprint of the exit-node to attack.
queue = Queue.Queue() #Queue with the host/open-port found in the scanning.
@cli.switch(["-n", "--servers-to-attack"], int, help="Number of TOR exit-nodes to attack. Default = 10")
def servers_to_attack(self, exitNodesToAttack):
'''
Number of "exit-nodes" to attack received from command-line
'''
self.exitNodesToAttack = exitNodesToAttack
@cli.switch(["-t", "--threads"], cli.Range(1, 20), help='Number of threads to use.')
def number_threads(self, threads):
'''
Number of threads to create when the scanning process has been done.
'''
self.threads = threads
@cli.switch(["-m", "--mode"], cli.Set("windows", "linux", case_sensitive=False), mandatory=True, help="Filter the platform of exit-nodes to attack.")
def server_mode(self, mode):
'''
Server Mode: Search for Windows or Linux machines.
'''
self.mode = mode
@cli.switch(["-u", "--users-file"], help="Users File in the Bruteforce mode.", requires=["--brute"])
def users_file(self, usersFile):
'''
User's file. Used to perform bruteforce attacks.
'''
self.usersFile = usersFile
@cli.switch(["-f", "--passwords-file"], help="Passwords File in the Bruteforce mode.", requires=["--brute"])
def users_file(self, usersFile):
'''
User's file. Used to perform bruteforce attacks.
'''
self.usersFile = usersFile
@cli.switch(["-k", "--shodan-key"], str, help="Development Key to use Shodan API.", requires=["--use-shodan"])
def shodan_key(self, shodanKey):
'''
This option is used to specify the file where the shodan development key is stored
'''
self.shodanKey = shodanKey
@cli.switch(["-l", "--list-ports"], str, help="Comma-separated List of ports to scan with Nmap. Don't use spaces")
def list_ports(self, scanPorts):
'''
List of ports used to perform the nmap scan.
'''
self.scanPorts = scanPorts
@cli.switch(["-p", "--scan-protocol"], cli.Set("tcp", "udp", case_sensitive=True), help="Protocol used to scan the target.")
def scan_protocol(self, scanProtocol):
'''
Protocol used to perform the nmap scan.
'''
self.scanProtocol = scanProtocol
@cli.switch(["-a", "--scan-arguments"], str, help='Arguments to Nmap. Use "" to specify the arguments. For example: "-sSV -A -Pn"')
def scan_arguments(self, scanArguments):
'''
Arguments used to perform the nmap scan.
'''
self.scanArguments = scanArguments
@cli.switch(["-e", "--exit-node-fingerprint"], str, help="ExitNode's Fingerprint to attack.")
def exitNode_Fingerprint(self, exitNodeFingerprint):
'''
If we want to perform a single attack against an known "exit-node", We can specify the fingerprint of the exit-node to perform the attack.
'''
self.exitNodeFingerprint = exitNodeFingerprint
def main(self):
'''
List and Scan the exit nodes. The function will return an dictionary with the exitnodes found and the open ports.
THIS PROCESS IS VERY SLOW AND SOMETIMES THE CONNECTION WITH THE DIRECTORY AUTHORITIES IS NOT AVAILABLE.
'''
discovery = Discovery(self)
exitNodes = discovery.listExitNodes() #Returns a tuple with IP Addresses and open-ports.
if exitNodes != None and len(exitNodes) > 0:
for thread in range(self.threads): #Creating the number of threads specified by command-line.
worker = WorkerThread(self.queue, thread, self)
worker.setDaemon(True)
worker.start()
for exitNode in exitNodes.items():
self.queue.put(exitNode)
self.queue.join() #Blocks the main process until the queue is empty.
log.info("[+] Process finished at "+ strftime("%Y-%m-%d %H:%M:%S", gmtime()))
class Discovery:
'''
Class used to list the current "exit-nodes" from the TOR network and perform the nmap scanning to discover the open ports.
'''
exitNodes = {}
cli = None
scan = None
def __init__(self, cli):
self.cli = cli
if self.cli.verbose:
log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG)
log.info("[+] Verbose mode activated.")
else:
log.basicConfig(format="%(levelname)s: %(message)s")
log.info("[+] Process started at "+ strftime("%Y-%m-%d %H:%M:%S", gmtime()))
def listExitNodes(self):
'''
List the Exit Nodes using the filters specified by command-line.
'''
nodesAlreadyScanned = []
log.info("[+] Try to listing the current Exit-Nodes of TOR.")
if self.cli.exitNodeFingerprint != None:
log.info("[+] Using the fingerprint: %s " % (self.cli.exitNodeFingerprint))
log.info("[+] Filter by platform: %s." % (self.cli.mode))
log.info("[+] Retrieving the first %d records in the Descriptors." %(self.cli.exitNodesToAttack))
if self.cli.useMirror == True:
log.info("[+] Using the Directory Mirrors to get the descriptors")
downloader = DescriptorDownloader(use_mirrors=self.cli.useMirror)
nm = nmap.PortScanner()
if self.cli.exitNodeFingerprint != None:
descriptors = downloader.get_server_descriptors(fingerprints=[self.cli.exitNodeFingerprint])
else:
descriptors = downloader.get_server_descriptors()
try:
listDescriptors = descriptors.run()
except zlib.error:
log.error("[-] Error fetching the TOR descriptors. This is something quite common... Try again in a few seconds.")
return
log.info("[+] Number of Records found: %d " %(len(listDescriptors)))
for descriptor in listDescriptors[1:self.cli.exitNodesToAttack]:
#for descriptor in parse_file(open("/home/adastra/Escritorio/tor-browser_en-US-Firefox/Data/Tor/cached-consensus")):
if self.cli.mode.lower() in descriptor.operating_system.lower() and descriptor.exit_policy.is_exiting_allowed():
#SEARCH FILTERING BY FINGERPRINT
#Conditions: Fingerprint specified in command-line AND
# Relay Fingerprint equals to the Fingerprint specified in command-line. AND
# Relay's Operative System equals to the Operative System (option mode) specified in command-line AND
# The Relay is a Exit Node.
if descriptor.address not in nodesAlreadyScanned:
log.info("[+] %s System has been found... Nickname: %s - OS Version: %s" % (descriptor.operating_system, descriptor.nickname, descriptor.operating_system))
log.info("[+] Starting the NMap Scan with the following options: ")
log.info("[+][+] Scan Address: %s " % (descriptor.address))
log.info("[+][+] Scan Arguments: %s " % (self.cli.scanArguments))
log.info("[+][+] Scan Ports: %s " % (self.cli.scanPorts))
if self.cli.scanArguments != None:
nm.scan(descriptor.address, self.cli.scanPorts, arguments=self.cli.scanArguments)
else:
nm.scan(descriptor.address, self.cli.scanPorts)
self.recordNmapScan(nm)
log.info('[+] Scan Ended for %s .' % (descriptor.nickname))
nodesAlreadyScanned.append(descriptor.address)
if len(self.exitNodes) == 0:
log.info("[+] In the first %d records searching for the %s Operating System, there's no results (machines with detected open ports)" %(self.cli.exitNodesToAttack, self.cli.mode.lower()))
return self.exitNodes
def recordNmapScan(self, scan):
'''
Performs the NMap scan using python-nmap library.
Returns the exitnodes with the open ports found in the scanning process.
'''
entryFile = 'nmapScan.txt'
nmapFileResults = open(entryFile, 'a')
for host in scan.all_hosts():
entry = '------- NMAP SCAN REPORT START FOR %s------- \n' %(host)
entry += '[+] Host: %s \n' % (host)
if scan[host].has_key('status'):
entry += '[+][+]State: %s \n' % (scan[host]['status']['state'])
entry += '[+][+]Reason: %s \n' % (scan[host]['status']['reason'])
if scan[host].has_key(self.cli.scanProtocol):
mapPorts = scan[host][self.cli.scanProtocol].keys()
for port in mapPorts:
entry += 'Port: %s \n' % (port)
entry += 'State: %s \n ' % (scan[host][self.cli.scanProtocol][port]['state'])
if 'open' in (scan[host][self.cli.scanProtocol][port]['state']):
self.exitNodes[host] = port
if scan[host][self.cli.scanProtocol][port].has_key('reason'):
entry += 'Reason: %s \n ' % (scan[host][self.cli.scanProtocol][port]['reason'])
if scan[host][self.cli.scanProtocol][port].has_key('name'):
entry += 'Name: %s \n ' % (scan[host][self.cli.scanProtocol][port]['name'])
else:
log.info("[-] There's no match in the Nmap scan with the specified protocol %s" %(self.cli.scanProtocol))
entry += '------- NMAP SCAN REPORT END ------- \n'
entry += '\n\n'
nmapFileResults.write(entry)
nmapFileResults.close()
class WorkerThread(threading.Thread):
'''
Worker Thread to information gathering and attack the exit nodes found.
'''
def __init__(self, queue, tid, cli) :
threading.Thread.__init__(self)
self.queue = queue
self.tid = tid
self.cli = cli
self.bruteForcePorts ={'ftpBrute':21, 'sshBrute':22}
if self.cli.useShodan == True:
#Using Shodan to search information about this machine in shodan database.
log.info("[+] Shodan Activated. About to read the Development Key. ")
if self.cli.shodanKey == None:
#If the key is None, we can't use shodan.
log.warn("[-] Shodan Key's File has not been specified. We can't use shodan without a valid key")
else:
#Read the shodan key and create the WebAPI object.
shodanKey = open(self.cli.shodanKey).readline().rstrip('\n')
self.shodanApi = WebAPI(shodanKey)
log.info("[+] Connected to Shodan. ")
def run(self) :
lock = threading.Lock()
while True :
lock.acquire()
host = None
try:
ip, port = self.queue.get(timeout=1)
if hasattr(self, 'shodanApi'):
log.info("[+] Using Shodan against %s " %(ip))
try:
shodanResults = self.shodanApi.host(ip)
recordShodanResults(self, ip, shodanResults)
except WebAPIError:
log.error("[-] There's no information about %s in the Shodan Database." %(ip))
pass
if self.cli.brute == True:
if self.cli.usersFile != None and self.cli.passFile != None:
for method in self.bruteForcePorts.keys():
if self.bruteForcePorts[method] == port:
#Open port detected for a service supported in the "Brute-Forcer"
#Calling the method using reflection.
attr(service)
else:
log.warn("[-] BruteForce mode specified but there's no files for users and passwords. Use -u and -f options")
except Queue.Empty :
log.info("Worker %d exiting... "%self.tid)
finally:
log.info("Releasing the Lock in the Thread %d "%self.tid)
lock.release()
self.queue.task_done()
def ftpBrute(self):
log.info("Starting FTP BruteForce mode on Thread: %d "%self.tid)
#log.info("Reading the Users File: %s "%self.cli.)
def sshBrute(self):
log.info("Starting SSH BruteForce mode on Thread: %d "%self.tid)
def recordShodanResults(self, host, results):
entryFile = 'shodanScan-%s.txt' %(host)
shodanFileResults = open(entryFile, 'a')
entry = '------- SHODAN REPORT START FOR %s ------- \n' %(host)
recursiveInfo(entry, results)
entry = '------- SHODAN REPORT END FOR %s ------- \n' %(host)
shodanFileResults.write(entry)
shodanFileResults.close()
def recursiveInfo(self, entry, data):
if type(data) == dict:
for key in results.keys():
if type(key) == dict:
entry += recursiveInfo(entry, key)
elif type(key) == list:
for element in key:
if type(key) == dict:
entry += recursiveInfo(entry, key)
else:
entry += '[+]%s : %s \n' %(key, results[key])
print entry
if __name__ == "__main__":
'''
Start the main program.
'''
Cli.run()