fire-marshal-ebay 0.0.1-security.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fire-marshal-ebay might be problematic. Click here for more details.

Files changed (41) hide show
  1. package/PadBuster/LICENSE +202 -0
  2. package/PadBuster/README +16 -0
  3. package/PadBuster/padBuster.pl +889 -0
  4. package/confused/.github/workflows/codeql-analysis.yml +67 -0
  5. package/confused/.github/workflows/golangci-lint.yml +28 -0
  6. package/confused/.goreleaser.yml +40 -0
  7. package/confused/CHANGELOG.md +31 -0
  8. package/confused/LICENSE +21 -0
  9. package/confused/README.md +93 -0
  10. package/confused/composer.go +105 -0
  11. package/confused/confused +0 -0
  12. package/confused/interfaces.go +11 -0
  13. package/confused/main.go +104 -0
  14. package/confused/mvn.go +120 -0
  15. package/confused/mvnparser.go +139 -0
  16. package/confused/npm.go +210 -0
  17. package/confused/packages.json +86 -0
  18. package/confused/pip.go +99 -0
  19. package/confused/util.go +11 -0
  20. package/index.js +47 -0
  21. package/package.json +9 -4
  22. package/synackAPI/Dockerfile +36 -0
  23. package/synackAPI/README.md +238 -0
  24. package/synackAPI/RHINOSPIDER/burpOOS.txt +25 -0
  25. package/synackAPI/RHINOSPIDER/burpScope.txt +1 -0
  26. package/synackAPI/RHINOSPIDER/scope.txt +1 -0
  27. package/synackAPI/bot.py +72 -0
  28. package/synackAPI/checkCerts.py +67 -0
  29. package/synackAPI/connect.py +9 -0
  30. package/synackAPI/currentTarget +24 -0
  31. package/synackAPI/getAnalytics.py +40 -0
  32. package/synackAPI/getHydra.py +46 -0
  33. package/synackAPI/getPayouts.py +11 -0
  34. package/synackAPI/getscope.py +123 -0
  35. package/synackAPI/polling.py +27 -0
  36. package/synackAPI/register.py +7 -0
  37. package/synackAPI/requirements.txt +7 -0
  38. package/synackAPI/synack.py +1046 -0
  39. package/synackAPI/synstats.py +54 -0
  40. package/synackAPI/target.py +17 -0
  41. package/README.md +0 -5
@@ -0,0 +1,1046 @@
1
+ import ipaddress
2
+ from netaddr import IPNetwork
3
+ import requests
4
+ import re
5
+ import os
6
+ import json
7
+ import base64
8
+ from pathlib import Path
9
+ import warnings
10
+ import operator
11
+ import sys
12
+ import datetime
13
+ from selenium import webdriver
14
+ from selenium.webdriver.common.by import By
15
+ from selenium.webdriver.common.keys import Keys
16
+ from selenium.webdriver.support.ui import WebDriverWait
17
+ from selenium.webdriver.support import expected_conditions as EC
18
+ from selenium.webdriver.firefox.options import Options
19
+ from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
20
+ import configparser
21
+ import time
22
+ import pyotp
23
+ from urllib.parse import urlparse
24
+
25
+ warnings.filterwarnings("ignore")
26
+
27
+ class synack:
28
+ codename = None
29
+ def __init__(self):
30
+ self.session = requests.Session()
31
+ self.jsonResponse = []
32
+ self.assessments = []
33
+ self.token = ""
34
+ self.notificationToken = ""
35
+ self.url_registered_summary = "https://platform.synack.com/api/targets/registered_summary"
36
+ self.url_scope_summary = "https://platform.synack.com/api/targets/"
37
+ self.url_activate_target = "https://platform.synack.com/api/launchpoint"
38
+ self.url_assessments = "https://platform.synack.com/api/assessments"
39
+ self.url_vulnerabilities = "https://platform.synack.com/api/vulnerabilities"
40
+ self.url_drafts = "https://platform.synack.com/api/drafts"
41
+ self.url_unregistered_slugs = "https://platform.synack.com/api/targets?filter%5Bprimary%5D=unregistered&filter%5Bsecondary%5D=all&filter%5Bcategory%5D=all&sorting%5Bfield%5D=dateUpdated&sorting%5Bdirection%5D=desc&pagination%5Bpage%5D="
42
+ self.url_profile = "https://platform.synack.com/api/profiles/me"
43
+ self.url_analytics = "https://platform.synack.com/api/listing_analytics/categories?listing_id="
44
+ self.url_hydra = "https://platform.synack.com/api/hydra_search/search/"
45
+ self.url_published_missions = "https://platform.synack.com/api/tasks/v1/tasks?status=PUBLISHED"
46
+ self.url_logout = "https://platform.synack.com/api/logout"
47
+ self.url_notification_token = "https://platform.synack.com/api/users/notifications_token"
48
+ self.url_notification_api = "https://notifications.synack.com/api/v2/"
49
+ self.url_transactions = "https://platform.synack.com/api/transactions"
50
+ self.url_lp_credentials = "https://platform.synack.com/api/launchpoint/credentials"
51
+ self.webheaders = {}
52
+ self.configFile = str(Path.home())+"/.synack/synack.conf"
53
+ self.firefoxProfile = str(Path.home())+"/.synack/selenium.profile"
54
+ self.config = configparser.ConfigParser()
55
+ self.config.read(self.configFile)
56
+ self.email = self.config['DEFAULT']['email']
57
+ self.password = self.config['DEFAULT']['password']
58
+ self.login_wait = int(self.config['DEFAULT']['login_wait'])
59
+ self.login_url = self.config['DEFAULT']['login_url']
60
+ self.authySecret = self.config['DEFAULT']['authy_secret']
61
+ self.sessionTokenPath = self.config['DEFAULT'].get('session_token_path',"/tmp/synacktoken")
62
+ self.notificationTokenPath = self.config['DEFAULT'].get('notification_token_path',"/tmp/notificationtoken")
63
+ self.connector = False
64
+ self.webdriver = None
65
+ self.headless = False
66
+ # set to false to use the requests-based login
67
+ self.gecko = self.config['DEFAULT'].getboolean('gecko',True)
68
+ self.proxyport = self.config['DEFAULT'].getint('proxyport',8080)
69
+
70
+ ## Set to 'True' for troubleshooting with Burp Suite ##
71
+ self.Proxy = self.config['DEFAULT'].getboolean('proxy',False)
72
+
73
+ #########
74
+ def getAuthy(self):
75
+ totp = pyotp.TOTP(self.authySecret)
76
+ totp.digits = 7
77
+ totp.interval = 10
78
+ totp.issuer = "synack"
79
+ return(totp.now())
80
+
81
+ ## Get Synack platform session token ##
82
+ def getSessionToken(self):
83
+ if Path(self.sessionTokenPath).exists():
84
+ with open(self.sessionTokenPath, "r") as f:
85
+ self.token = f.readline()
86
+ f.close()
87
+ else:
88
+ self.connectToPlatform()
89
+ self.webheaders = {"Authorization": "Bearer " + self.token}
90
+ response = self.try_requests("GET", self.url_profile, 10)
91
+ profile = response.json()
92
+ self.webheaders['user_id'] = profile['user_id']
93
+
94
+ #################################################
95
+ ## Function to attempt requests multiple times ##
96
+ #################################################
97
+
98
+ def try_requests(self, func, URL, times, extra=None):
99
+ http_proxy = "http://127.0.0.1:%d" % self.proxyport
100
+ https_proxy = "http://127.0.0.1:%d" % self.proxyport
101
+ proxyDict = {
102
+ "http" : http_proxy,
103
+ "https" : https_proxy
104
+ }
105
+
106
+ url = urlparse(URL)
107
+ scheme = url.scheme
108
+ netloc = url.netloc
109
+ path = url.path
110
+ port = url.port
111
+ platform = "platform.synack"
112
+
113
+ if self.Proxy == True:
114
+ for attempt in range(times):
115
+ try:
116
+ if func == "PUT":
117
+ putData = json.dumps({"listing_id": extra})
118
+ newHeaders = dict(self.webheaders)
119
+ newHeaders['Content-Type'] = "application/json"
120
+ response = self.session.put(URL, headers=newHeaders, data=putData, proxies=proxyDict, verify=False)
121
+ if response.status_code == 401 and platform in netloc:
122
+ self.connectToPlatform()
123
+ self.getSessionToken()
124
+ else:
125
+ return response
126
+ elif func == "GET":
127
+ if extra == None:
128
+ response = self.session.get(URL, headers=self.webheaders, proxies=proxyDict, verify=False)
129
+ if response.status_code == 401 and platform in netloc:
130
+ self.connectToPlatform()
131
+ self.getSessionToken()
132
+ else:
133
+ return response
134
+ else:
135
+ parameters = {'page': extra}
136
+ response = self.session.get(URL, headers=self.webheaders, params=parameters, proxies=proxyDict, verify=False)
137
+ if response.status_code == 401 and platform in netloc:
138
+ self.connectToPlatform()
139
+ self.getSessionToken()
140
+ else:
141
+ return response
142
+ elif func == "POST":
143
+ response = self.session.post(URL, headers=self.webheaders, proxies=proxyDict, json=extra, verify=False)
144
+ if response.status_code == 401 and platform in netloc:
145
+ self.connectToPlatform()
146
+ self.getSessionToken()
147
+ else:
148
+ return response
149
+ elif func == "PATCH":
150
+ newHeaders = dict(self.webheaders)
151
+ newHeaders['Content-Type'] = "application/json"
152
+ # PATCH request does not support `json=` parameter
153
+ response = self.session.patch(URL, headers=newHeaders, proxies=proxyDict, data=extra, verify=False)
154
+ if response.status_code == 401 and platform in netloc:
155
+ self.connectToPlatform()
156
+ self.getSessionToken()
157
+ else:
158
+ return response
159
+ elif func == "DELETE":
160
+ response = self.session.delete(URL, headers=self.webheaders, proxies=proxyDict, json=extra, verify=False)
161
+ if response.status_code == 401 and platform in netloc:
162
+ self.connectToPlatform()
163
+ self.getSessionToken()
164
+ else:
165
+ return response
166
+ except Exception as err:
167
+ last_err = err
168
+ raise last_err
169
+ else:
170
+ for attempt in range(times):
171
+ try:
172
+ if func == "PUT":
173
+ putData = json.dumps({"listing_id": extra})
174
+ newHeaders = dict(self.webheaders)
175
+ newHeaders['Content-Type'] = "application/json"
176
+ response =self.session.put(URL, headers=newHeaders, data=putData, verify=False)
177
+ if response.status_code == 401 and platform in netloc:
178
+ self.connectToPlatform()
179
+ self.getSessionToken()
180
+ else:
181
+ return response
182
+ elif func == "GET":
183
+ if extra == None:
184
+ response =self.session.get(URL, headers=self.webheaders, verify=False)
185
+ if response.status_code == 401 and platform in netloc:
186
+ self.connectToPlatform()
187
+ self.getSessionToken()
188
+ else:
189
+ return response
190
+ else:
191
+ parameters = {'page': extra}
192
+ response = self.session.get(URL, headers=self.webheaders, params=parameters, verify=False)
193
+ if response.status_code == 401 and platform in netloc:
194
+ self.connectToPlatform()
195
+ self.getSessionToken()
196
+ else:
197
+ return response
198
+ elif func == "POST":
199
+ response = self.session.post(URL, headers=self.webheaders, json=extra, verify=False)
200
+ if response.status_code == 401 and platform in netloc:
201
+ self.connectToPlatform()
202
+ self.getSessionToken()
203
+ else:
204
+ return response
205
+ elif func == "PATCH":
206
+ # PATCH request does not support `json=` parameter
207
+ newHeaders = dict(self.webheaders)
208
+ newHeaders['Content-Type'] = "application/json"
209
+ response = self.session.request("PATCH", URL, headers=newHeaders, data=extra, verify=False)
210
+ if response.status_code == 401 and platform in netloc:
211
+ self.connectToPlatform()
212
+ self.getSessionToken()
213
+ else:
214
+ return response
215
+ elif func == "DELETE":
216
+ response = self.session.delete(URL, headers=self.webheaders, json=extra, verify=False)
217
+ if response.status_code == 401 and platform in netloc:
218
+ self.connectToPlatform()
219
+ self.getSessionToken()
220
+ else:
221
+ return response
222
+ else:
223
+ raise ValueError("Choose a real HTTP method.")
224
+ except Exception as err:
225
+ last_err = err
226
+ raise last_err
227
+
228
+
229
+ ####################################################
230
+ ## Function to find all occurrences of nested key ##
231
+ ####################################################
232
+
233
+ def findkeys(self, node, kv):
234
+ if isinstance(node, list):
235
+ for i in node:
236
+ for x in self.findkeys(i, kv):
237
+ yield x
238
+ elif isinstance(node, dict):
239
+ if kv in node:
240
+ yield node[kv]
241
+ for j in node.values():
242
+ for x in self.findkeys(j, kv):
243
+ yield x
244
+
245
+ ##############################################
246
+ ## Returns a JSON of all registered targets ##
247
+ ## This must be the first call after object ##
248
+ ## instantiation - it populates the json ##
249
+ ##############################################
250
+ def getAllTargets(self):
251
+ self.jsonResponse.clear()
252
+ response = self.try_requests("GET", self.url_registered_summary, 10)
253
+ self.jsonResponse[:] = response.json()
254
+ return(response.status_code)
255
+
256
+
257
+ ########################################
258
+ ## Returns a list of web or host target codenames
259
+ ## that are (mission only / not mission only)
260
+ ## category: web || host || RE || mobile || sourceCode || hardware
261
+ ## mission_only: True || False
262
+ ########################################
263
+ def getCodenames(self, category, mission_only=False):
264
+ categories = ("web application", "re", "mobile", "host", "source code","hardware")
265
+ category = category.lower()
266
+ if category == "web":
267
+ category = "web application"
268
+ if category == "re":
269
+ category = "reverse engineering"
270
+ if category == "sourcecode":
271
+ category = "source code"
272
+ if category not in categories:
273
+ raise Exception("Invalid category.")
274
+ targets = []
275
+ for i in range (len(self.jsonResponse)):
276
+ if mission_only == True:
277
+ if self.jsonResponse[i]['vulnerability_discovery'] == False:
278
+ if self.jsonResponse[i]['category']['name'].lower() == category.lower():
279
+ targets.append(self.jsonResponse[i]['codename'])
280
+ else:
281
+ continue
282
+ else:
283
+ continue
284
+ elif mission_only == False:
285
+ if self.jsonResponse[i]['vulnerability_discovery'] == True:
286
+ if self.jsonResponse[i]['category']['name'].lower() == category.lower():
287
+ targets.append(self.jsonResponse[i]['codename'])
288
+ else:
289
+ continue
290
+ else:
291
+ continue
292
+ return(targets)
293
+
294
+ #########################################
295
+ ## This returns the "slug" of a target ##
296
+ ## based on the codename ##
297
+ #########################################
298
+
299
+ def getTargetID(self, codename):
300
+ for i in range (len(self.jsonResponse)):
301
+ if self.jsonResponse[i]['codename'].lower() == codename.lower():
302
+ return(self.jsonResponse[i]['id'])
303
+
304
+ ##################################
305
+ ## This retuens the codemane of ##
306
+ ## a target based on the slug ##
307
+ ##################################
308
+ def getCodenameFromSlug(self, slug):
309
+ for i in range(len(self.jsonResponse)):
310
+ if self.jsonResponse[i]['id'].lower() == slug.lower():
311
+ return(self.jsonResponse[i]['codename'])
312
+
313
+ #################################
314
+ ## This private method returns ##
315
+ ## the organization ID ##
316
+ #################################
317
+ def __getOrgID(self, codename):
318
+ for i in range (len(self.jsonResponse)):
319
+ if self.jsonResponse[i]['codename'].lower() == codename.lower():
320
+ return(self.jsonResponse[i]['organization_id'])
321
+
322
+ ######################################
323
+ ## This returns the target category ##
324
+ ######################################
325
+ def getCategory(self, codename):
326
+ for i in range (len(self.jsonResponse)):
327
+ if self.jsonResponse[i]['codename'].lower() == codename.lower():
328
+ return(self.jsonResponse[i]['category']['name'])
329
+
330
+ #####################################################
331
+ ## This will connect you to the target by codename ##
332
+ #####################################################
333
+ def connectToTarget(self, codename):
334
+ slug = self.getTargetID(codename)
335
+ response = self.try_requests("PUT", self.url_activate_target, 10, slug)
336
+ time.sleep(5)
337
+ return response.status_code
338
+
339
+ ########################################################
340
+ ## This just returns the "real" client name sometimes ##
341
+ ########################################################
342
+ def clientName(self, codename):
343
+ for i in range (len(self.jsonResponse)):
344
+ if self.jsonResponse[i]['codename'].lower() == codename.lower():
345
+ return(self.jsonResponse[i]['name'])
346
+
347
+
348
+ ################################
349
+ ## This gets the target scope ##
350
+ ################################
351
+
352
+ def getScope(self, codename):
353
+ category = self.getCategory(codename)
354
+ orgID = self.__getOrgID(codename)
355
+ slug = self.getTargetID(codename)
356
+ if category.lower() == "web application":
357
+ scopeURL = "https://platform.synack.com/api/asset/v1/organizations/"+orgID+"/owners/listings/"+slug+"/webapps"
358
+ allRules = []
359
+ oosRules = []
360
+ response = self.try_requests("GET", scopeURL, 10)
361
+ jsonResponse = response.json()
362
+ j = 0
363
+ while j < len(jsonResponse):
364
+ if jsonResponse[j]['status'] in ["out","tbd"]:
365
+ tmpOOS = set()
366
+ for thisRule in range(len(jsonResponse[j]['rules'])):
367
+ url = urlparse(jsonResponse[j]['rules'][thisRule]['rule'])
368
+ scheme = url.scheme
369
+ netloc = url.netloc
370
+ path = url.path
371
+ port = url.port
372
+ wildcard = False
373
+ if len(netloc) != 0:
374
+ subdomain = netloc.split('.')[0]
375
+ if subdomain == "*":
376
+ wildcard = True
377
+ netloc = ".".join(netloc.split('.')[1:])
378
+ else:
379
+ if len(path) != 0:
380
+ netloc = path.split('/')[0]
381
+ checkWildcard = netloc.split('.')[0]
382
+ if checkWildcard == "*":
383
+ wildcard = True
384
+ if ":" in netloc:
385
+ port = netloc.split(':')[1]
386
+ thisURL = netloc.split(':')[0]
387
+ netloc = ".".join(thisURL.split('.')[1:])
388
+ else:
389
+ port = 443
390
+ netloc = ".".join(netloc.split('.')[1:])
391
+ else:
392
+ if ":" in netloc:
393
+ port = netloc.split(':')[1]
394
+ thisURL = netloc.split(':')[0]
395
+ netloc = ".".join(thisURL.split('.')[0:])
396
+ else:
397
+ port = 443
398
+ netloc = ".".join(netloc.split('.')[0:])
399
+ path = "/" + "/".join(path.split('/')[1:])
400
+ else:
401
+ continue
402
+ oosDict = {
403
+ 'scheme' : scheme,
404
+ 'netloc': netloc,
405
+ 'path': path,
406
+ 'port': port,
407
+ 'wildcard': wildcard,
408
+ 'fullURI' : scheme+netloc
409
+
410
+ }
411
+ oosRules.append(oosDict)
412
+ j+=1
413
+ else:
414
+ for thisRule in range(len(jsonResponse[j]['rules'])):
415
+ url = urlparse(jsonResponse[j]['rules'][thisRule]['rule'])
416
+ scheme = url.scheme
417
+ netloc = url.netloc
418
+ path = url.path
419
+ port = url.port
420
+ wildcard = False
421
+
422
+ if len(netloc) != 0:
423
+ subdomain = netloc.split('.')[0]
424
+ if subdomain == "*":
425
+ wildcard = True
426
+ netloc = ".".join(netloc.split('.')[1:])
427
+ else:
428
+ if len(path) != 0:
429
+ netloc = path.split('/')[0]
430
+ checkWildcard = netloc.split('.')[0]
431
+ if checkWildcard == "*":
432
+ wildcard = True
433
+ if ":" in netloc:
434
+ port = netloc.split(':')[1]
435
+ thisURL = netloc.split(':')[0]
436
+ netloc = ".".join(thisURL.split('.')[1:])
437
+ else:
438
+ port = 443
439
+ netloc = ".".join(netloc.split('.')[1:])
440
+ else:
441
+ if ":" in netloc:
442
+ port = netloc.split(':')[1]
443
+ thisURL = netloc.split(':')[0]
444
+ netloc = ".".join(thisURL.split('.')[0:])
445
+ else:
446
+ port = 443
447
+ netloc = ".".join(netloc.split('.')[0:])
448
+ path = "/" + "/".join(path.split('/')[1:])
449
+ else:
450
+ continue
451
+ if jsonResponse[j]['rules'][thisRule]['status'] in ["out","tbd"]:
452
+ oosDict = {
453
+ 'scheme' : scheme,
454
+ 'netloc': netloc,
455
+ 'path': path,
456
+ 'port': port,
457
+ 'wildcard': wildcard,
458
+ 'fullURI' : scheme+netloc
459
+
460
+ }
461
+ oosRules.append(oosDict)
462
+ continue
463
+ else:
464
+ pass
465
+
466
+ scopeDict = {
467
+ 'scheme' : scheme,
468
+ 'netloc': netloc,
469
+ 'path': path,
470
+ 'port': port,
471
+ 'wildcard': wildcard,
472
+ 'fullURI' : scheme+netloc
473
+ }
474
+ allRules.append(scopeDict)
475
+ j+=1
476
+ return(list(allRules),list(oosRules))
477
+ if category.lower() == "host":
478
+ scopeURL = "https://platform.synack.com/api/targets/"+slug+"/cidrs?page=all"
479
+ cidrs = []
480
+ try:
481
+ response = self.try_requests("GET", scopeURL, 10)
482
+ except requests.exceptions.RequestException as e:
483
+ raise SystemExit(e)
484
+ temp = json.dumps(response.json()['cidrs']).replace("[","").replace("]","").replace("\"","").replace(", ","\n").split("\n")
485
+ cidrs.extend(temp)
486
+ cidrs = list(set(cidrs))
487
+ return(cidrs)
488
+
489
+ ########################################
490
+ ## This converts CIDR list to IP list ##
491
+ ## This is a much faster method, previous method was causing problems on large hosts ##
492
+ ########################################
493
+ def getIPs(self, cidrs):
494
+ IPs = []
495
+ for i in range(len(cidrs)):
496
+ if cidrs[i] != "":
497
+ for ip in IPNetwork(cidrs[i]):
498
+ IPs.append(str(ip))
499
+ return(IPs)
500
+
501
+ ##############################################
502
+ ## This gets all of your passed assessments ##
503
+ ##############################################
504
+ def getAssessments(self):
505
+ self.assessments.clear()
506
+ response = self.try_requests("GET", self.url_assessments, 10)
507
+ jsonResponse = response.json()
508
+ for i in range(len(jsonResponse)):
509
+ if jsonResponse[i]['written_assessment']['passed'] == True:
510
+ self.assessments.append(jsonResponse[i]['category_name'])
511
+ i+=1
512
+
513
+ ##########################################################
514
+ ## This gets endpoints from Web Application "Analytics" ##
515
+ ##########################################################
516
+ def getAnalytics(self, codename, status="all"):
517
+ slug = self.getTargetID(codename)
518
+ if status.lower() == "accepted":
519
+ url_analytics = self.url_analytics + slug + "&status=accepted"
520
+ elif status.lower() == "in_queue":
521
+ url_analytics = self.url_analytics + slug + "&status=in_queue"
522
+ elif status.lower() == "rejected":
523
+ url_analytics = self.url_analytics + slug + "&status=rejected"
524
+ else:
525
+ url_analytics = self.url_analytics + slug
526
+ response = self.try_requests("GET", url_analytics, 10)
527
+ jsonResponse = response.json()
528
+ analytics = []
529
+ targetType = self.getCategory(codename)
530
+ if targetType == "Web Application":
531
+ if "value" in jsonResponse:
532
+ for value in range(len(jsonResponse['value'])):
533
+ for exploitable_location in range(len(jsonResponse['value'][value]['exploitable_locations'])):
534
+ analyticsDict = {}
535
+ if jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['type'] == "url":
536
+ try:
537
+ URI = urlparse(str(jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['value']))
538
+ scheme = URI.scheme
539
+ except:
540
+ scheme = "https"
541
+ ## URL
542
+ # analyticsDict['uri'] = urlparse(str(jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['value']))
543
+ port = str(URI.port)
544
+ if port != "None":
545
+ analyticsDict['port'] = port
546
+ else:
547
+ if scheme == "http":
548
+ analyticsDict['port'] = "80"
549
+ elif scheme == "https":
550
+ analyticsDict['port'] = "443"
551
+ analyticsDict['codename'] = codename
552
+ analyticsDict['vuln_category'] = str(jsonResponse['value'][value]['categories'][0])
553
+ if len(jsonResponse['value'][value]['categories']) == 2:
554
+ analyticsDict['vuln_subcategory'] = str(jsonResponse['value'][value]['categories'][1])
555
+ else:
556
+ pass
557
+ analyticsDict['scheme'] = "URL"
558
+ try:
559
+ analyticsDict['protocol'] = URI.scheme
560
+ except:
561
+ analyticsDict['protocol'] = "http"
562
+ pass
563
+ try:
564
+ analyticsDict['vuln_location'] = analyticsDict['protocol'] + "://" + URI.netloc + URI.path
565
+ except:
566
+ analyticsDict['vuln_location'] = ""
567
+ pass
568
+ if status.lower() == "rejected":
569
+ analyticsDict['status'] = "rejected"
570
+ else:
571
+ analyticsDict['status'] = str(jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['status'])
572
+ analytics.append(analyticsDict)
573
+ return analytics
574
+ elif targetType == "Host":
575
+ #'codename', 'vuln_category', 'vuln_subcategory', 'vuln_location', 'type', 'protocol', 'port', 'path', 'status'
576
+ if "value" in jsonResponse:
577
+ for value in range(len(jsonResponse['value'])):
578
+ for exploitable_location in range(len(jsonResponse['value'][value]['exploitable_locations'])):
579
+ analyticsDict = {}
580
+ if jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['type'] == "url":
581
+ try:
582
+ URI = urlparse(str(jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['value']))
583
+ scheme = URI.scheme
584
+ except:
585
+ scheme = "https"
586
+ ## URL
587
+ # analyticsDict['uri'] = urlparse(str(jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['value']))
588
+ port = str(URI.port)
589
+ if port != "None":
590
+ analyticsDict['port'] = port
591
+ else:
592
+ if scheme == "http":
593
+ analyticsDict['port'] = "80"
594
+ elif scheme == "https":
595
+ analyticsDict['port'] = "443"
596
+ analyticsDict['codename'] = codename
597
+ analyticsDict['vuln_category'] = str(jsonResponse['value'][value]['categories'][0])
598
+ if len(jsonResponse['value'][value]['categories']) == 2:
599
+ analyticsDict['vuln_subcategory'] = str(jsonResponse['value'][value]['categories'][1])
600
+ else:
601
+ pass
602
+ analyticsDict['scheme'] = "URL"
603
+ try:
604
+ analyticsDict['protocol'] = URI.scheme
605
+ except:
606
+ analyticsDict['protocol'] = "http"
607
+ pass
608
+ try:
609
+ analyticsDict['vuln_location'] = analyticsDict['protocol'] + "://" + URI.netloc + URI.path
610
+ except:
611
+ analyticsDict['vuln_location'] = ""
612
+ pass
613
+ if status.lower() == "rejected":
614
+ analyticsDict['status'] = "rejected"
615
+ else:
616
+ analyticsDict['status'] = str(jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['status'])
617
+ analytics.append(analyticsDict)
618
+ elif jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['type'] == "ip":
619
+ ## IP
620
+ analyticsDict['codename'] = codename
621
+ analyticsDict['vuln_category'] = str(jsonResponse['value'][value]['categories'][0])
622
+ if len(jsonResponse['value'][value]['categories']) == 2:
623
+ analyticsDict['vuln_subcategory'] = str(jsonResponse['value'][value]['categories'][1])
624
+ else:
625
+ pass
626
+ analyticsDict['scheme'] = "HOST"
627
+ analyticsDict['protocol'] = str(jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['protocol'])
628
+ analyticsDict['vuln_location'] = str(jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['address'])
629
+ analyticsDict['port'] = str(jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['port'])
630
+ if status.lower() == "rejected":
631
+ analyticsDict['status'] = "rejected"
632
+ else:
633
+ analyticsDict['status'] = str(jsonResponse['value'][value]['exploitable_locations'][exploitable_location]['status'])
634
+ analytics.append(analyticsDict)
635
+ return analytics
636
+
637
+ #############################################
638
+ ## This registers all unregistered targets ##
639
+ #############################################
640
+
641
+ def registerAll(self):
642
+ self.getAssessments()
643
+ pageNum = 1
644
+ next_page = True
645
+ unregistered_slugs = []
646
+ while next_page:
647
+ url_slugs = self.url_unregistered_slugs + str(pageNum)
648
+ response = self.try_requests("GET", url_slugs, 10)
649
+ jsonResponse = response.json()
650
+ if (len(jsonResponse)!=0):
651
+ for i in range (len(jsonResponse)):
652
+ if jsonResponse[i]["category"]["name"] in self.assessments:
653
+ unregistered_slugs.append(str(jsonResponse[i]["slug"]))
654
+ pageNum += 1
655
+ else:
656
+ next_page = False
657
+ pageNum += 1
658
+ for i in range (len(unregistered_slugs)):
659
+ url_register_slug = "https://platform.synack.com/api/targets/"+unregistered_slugs[i]+"/signup"
660
+ data='{"ResearcherListing":{"terms":1}}'
661
+ response = self.try_requests("POST", url_register_slug, 10, data)
662
+ slug = unregistered_slugs[i]
663
+ self.getAllTargets()
664
+ for i in range(len(unregistered_slugs)):
665
+ codename = self.getCodenameFromSlug(unregistered_slugs[i])
666
+ if codename == None:
667
+ print("Error registerring "+unregistered_slugs[i]+"!")
668
+ else:
669
+ print("Successfully registered "+str(codename))
670
+
671
+ ###############
672
+ ## Keepalive ##
673
+ ###############
674
+
675
+ def connectToPlatform(self):
676
+ if self.gecko:
677
+ return self.connectToPlatformGecko()
678
+ else:
679
+ return self.connectToPlatformRequests()
680
+
681
+ def connectToPlatformRequests(self):
682
+ # Pull a valid CSRF token for requests in login flow
683
+ response = self.try_requests("GET", "https://login.synack.com/", 10)
684
+ # <meta name="csrf-token" content="..."/>
685
+ m = re.search('<meta name="csrf-token" content="([^"]*)"', response.text)
686
+ csrf_token = m.group(1)
687
+ self.webheaders['X-CSRF-Token'] = csrf_token
688
+
689
+ # fix broken Incapsula cookies - regression removed
690
+ for cookie_name in self.session.cookies.iterkeys():
691
+ cookie_value = self.session.cookies.get(cookie_name)
692
+ if cookie_value.find("\r") > -1 or cookie_value.find("\n") > -1:
693
+ print("Fixing cookie %s" % cookie_name)
694
+ cookie_value = re.sub("\r\n *","", cookie_value)
695
+ cookie_obj = requests.cookies.create_cookie(name=cookie_name,value=cookie_value,path="/")
696
+ self.session.cookies.clear(domain="login.synack.com",path="/",name=cookie_name)
697
+ self.session.cookies.set_cookie(cookie_obj)
698
+
699
+ data={"email":self.email,"password":self.password}
700
+ response = self.try_requests("POST", "https://login.synack.com/api/authenticate", 1, data)
701
+ jsonResponse = response.json()
702
+ if not jsonResponse['success']:
703
+ print("Error logging in: "+jsonResponse)
704
+ return False
705
+
706
+ progress_token = jsonResponse['progress_token']
707
+
708
+ data={"authy_token":self.getAuthy(),"progress_token":progress_token}
709
+ response = self.try_requests("POST", "https://login.synack.com/api/authenticate", 1, data)
710
+ jsonResponse = response.json()
711
+
712
+ grant_token = jsonResponse['grant_token']
713
+
714
+ # 2 requests required here to confirm the grant token - once to the HTML page and once to the API
715
+ response = self.try_requests("GET", "https://platform.synack.com/?grant_token="+grant_token, 1)
716
+ self.webheaders['X-Requested-With'] = "XMLHttpRequest"
717
+ response = self.try_requests("GET", "https://platform.synack.com/token?grant_token="+grant_token, 1)
718
+ jsonResponse = response.json()
719
+ access_token = jsonResponse['access_token']
720
+
721
+ self.token = access_token
722
+ with open(self.sessionTokenPath,"w") as f:
723
+ f.write(self.token)
724
+ f.close()
725
+
726
+ # Remove these headers so they don't affect other requests
727
+ del self.webheaders['X-Requested-With']
728
+ del self.webheaders['X-CSRF-Token']
729
+
730
+
731
+ def connectToPlatformGecko(self):
732
+ isExist = os.path.exists(self.firefoxProfile)
733
+ if not isExist:
734
+ os.makedirs(self.firefoxProfile)
735
+ options = Options()
736
+ options.add_argument("-profile")
737
+ options.add_argument(self.firefoxProfile)
738
+ firefox_capabilities = DesiredCapabilities.FIREFOX
739
+ firefox_capabilities['marionette'] = True
740
+
741
+ if self.headless == True:
742
+ options.headless = True
743
+ else:
744
+ options.headless = False
745
+ # driver = webdriver.Firefox(options=options)
746
+ driver = webdriver.Firefox(capabilities=firefox_capabilities, options=options)
747
+ driver.get(self.login_url)
748
+ assert "Synack" in driver.title
749
+ ## Fill in the email address ##
750
+ email_path = '/html/body/div[2]/div/div/div[2]/form/fieldset/input'
751
+ driver.find_element_by_xpath(email_path).click()
752
+ driver.find_element_by_xpath(email_path).send_keys(self.email)
753
+ ## Fill in the password ##
754
+ password_path = '/html/body/div[2]/div/div/div[2]/form/fieldset/div[1]/input'
755
+ driver.find_element_by_xpath(password_path).click()
756
+ driver.find_element_by_xpath(password_path).send_keys(self.password)
757
+ ## Click the login button ##
758
+ login_path = '/html/body/div[2]/div/div/div[2]/form/fieldset/div[2]/button'
759
+ driver.find_element_by_xpath(login_path).click()
760
+ time.sleep(5)
761
+ ## Hope the authy works! ##
762
+ authy_path = '/html/body/div[2]/div/div/div[2]/form/fieldset/input'
763
+ driver.find_element_by_xpath(authy_path).click()
764
+ driver.find_element_by_xpath(authy_path).send_keys(self.getAuthy())
765
+ authy_submit_path = '/html/body/div[2]/div/div/div[2]/form/fieldset/div[1]/button'
766
+ driver.find_element_by_xpath(authy_submit_path).click()
767
+ while True:
768
+ self.token = driver.execute_script("return sessionStorage.getItem('shared-session-com.synack.accessToken')")
769
+ if isinstance(self.token, str):
770
+ break
771
+ ## Write the session token to /tmp/synacktoken ##
772
+ with open(self.sessionTokenPath,"w") as f:
773
+ f.write(self.token)
774
+ f.close()
775
+ print("Connected to platform.")
776
+ if self.headless == True:
777
+ driver.quit()
778
+ if self.connector == True:
779
+ self.webdriver = driver
780
+ return(0)
781
+
782
+ ###########
783
+ ## Vulns ##
784
+ ###########
785
+
786
+ def getVulns(self, status="accepted"):
787
+ pageNum = 1
788
+ results = []
789
+ while True:
790
+ url_vulnerabilities = self.url_vulnerabilities +"?filters%5Bstatus%5D=" + status + "&page=" +str(pageNum)+"&per_page=5"
791
+ response = self.try_requests("GET", url_vulnerabilities, 10)
792
+ vulnsResponse = response.json()
793
+ if len(vulnsResponse) == 0:
794
+ break
795
+ else:
796
+ results = results + vulnsResponse
797
+ pageNum += 1
798
+ return results
799
+
800
+ def getVuln(self, identifier): # e.g. optimusant-4
801
+ url_vuln = self.url_vulnerabilities + "/" + identifier
802
+ response = self.try_requests("GET", url_vuln, 10)
803
+ vuln_response = response.json()
804
+ return vuln_response
805
+
806
+ ###########
807
+ ## Drafts ##
808
+ ###########
809
+
810
+ def getDrafts(self):
811
+ pageNum = 1
812
+ results = []
813
+ while True:
814
+ url_drafts = self.url_drafts +"?page=" +str(pageNum)+"&per_page=5"
815
+ response = self.try_requests("GET", url_drafts, 10)
816
+ draftsResponse = response.json()
817
+ if len(draftsResponse) == 0:
818
+ break
819
+ else:
820
+ results = results + draftsResponse
821
+ pageNum += 1
822
+ return results
823
+
824
+ def deleteDraft(self, id):
825
+ # careful!!
826
+ url_delete = self.url_drafts + "/" + str(id)
827
+ response = self.try_requests("DELETE", url_delete, 1)
828
+ if response.status_code == 200:
829
+ return True
830
+ else:
831
+ return False
832
+
833
+ ###########
834
+ ## Hydra ##
835
+ ###########
836
+
837
+ def getHydra(self, codename):
838
+ slug = self.getTargetID(codename)
839
+ pageNum = 1
840
+ hydraResults = []
841
+ while True:
842
+ url_hydra = self.url_hydra +"?page=" +str(pageNum)+"&listing_uids="+slug+"&q=%2Bport_is_open%3Atrue"
843
+ response = self.try_requests("GET", url_hydra, 10)
844
+ hydraResponse = response.json()
845
+ if len(hydraResponse) == 0:
846
+ break
847
+ else:
848
+ hydraResults = hydraResults + hydraResponse
849
+ pageNum += 1
850
+ return hydraResults
851
+
852
+ ###################
853
+ ## Mission stuff ##
854
+ ###################
855
+
856
+ ## Poll for missions ##
857
+
858
+ def pollMissions(self):
859
+ response = self.try_requests("GET", self.url_published_missions, 10)
860
+ try:
861
+ jsonResponse = response.json()
862
+ except:
863
+ jsonResponse = {}
864
+ try:
865
+ return jsonResponse
866
+ except NameError:
867
+ jsonResponse = {}
868
+ return jsonResponse
869
+
870
+ ####################
871
+ ## CLAIM MISSIONS ##
872
+ ####################
873
+ def claimMission(self, missionJson, dontclaim, assetType):
874
+ dollarValue = {}
875
+ claim = {'type': 'CLAIM'}
876
+ ################
877
+ ## Sort missions by dollar amount high to low
878
+ ################
879
+ for i in range(len(missionJson)):
880
+ dollarValue[i] = missionJson[i]["payout"]["amount"]
881
+ sorted_tuples = sorted(dollarValue.items(), key=operator.itemgetter(1), reverse=True)
882
+ sorted_dict = {k: v for k, v in sorted_tuples}
883
+ ################
884
+ i = len(sorted_dict.keys())
885
+ missionList = []
886
+ for key in sorted_dict.keys():
887
+ target = self.getCodenameFromSlug(missionJson[key]["listing"]["id"])
888
+ category = self.getCategory(target)
889
+ if(category not in assetType):
890
+ continue
891
+ if(target in dontclaim):
892
+ continue
893
+ i-= 1
894
+ campaign = missionJson[key]["campaign"]["title"]
895
+ campaignID = missionJson[key]["campaign"]["id"]
896
+ orgID = missionJson[key]["organization"]["id"]
897
+ slug = missionJson[key]["listing"]["id"]
898
+ taskID = missionJson[key]["id"]
899
+ payout = str(missionJson[key]["payout"]["amount"])
900
+ url_claimPath = "https://platform.synack.com/api/tasks/v1/organizations/" + orgID + "/listings/" + slug + "/campaigns/" + campaignID + "/tasks/" + taskID + "/transitions"
901
+ claimResponse = self.try_requests("POST", url_claimPath, 10, claim)
902
+ if claimResponse.status_code == 201:
903
+ claimed = True
904
+ else:
905
+ claimed = False
906
+ missionDict = {"target": target, "payout": payout, "claimed": claimed}
907
+ missionList.append(missionDict)
908
+ return(missionList)
909
+
910
+ ########################
911
+ ## Notification Token ##
912
+ ########################
913
+
914
+ def getNotificationToken(self):
915
+ response = self.try_requests("GET", self.url_notification_token, 10)
916
+ try:
917
+ jsonResponse = response.json()
918
+ except:
919
+ jsonResponse = {}
920
+ return(1)
921
+ self.notificationToken = jsonResponse['token']
922
+ with open(self.notificationTokenPath,"w") as f:
923
+ f.write(self.notificationToken)
924
+ return(0)
925
+
926
+ ############################
927
+ ## Read All Notifications ##
928
+ ############################
929
+
930
+ def markNotificationsRead(self):
931
+ if not self.notificationToken:
932
+ self.getNotificationToken()
933
+ readNotifications = self.url_notification_api+"read_all?authorization_token="+self.notificationToken
934
+ del self.webheaders['Authorization']
935
+ response = self.try_requests("POST", readNotifications, 10)
936
+ self.webheaders['Authorization'] = "Bearer " + self.token
937
+ try:
938
+ textResponse = str(response.content)
939
+ except:
940
+ return(1)
941
+ return(0)
942
+
943
+ ########################
944
+ ## Read Notifications ##
945
+ ########################
946
+
947
+ def pollNotifications(self):
948
+ pageIterator=1
949
+ breakOuterLoop = 0
950
+ notifications = []
951
+ if not self.notificationToken:
952
+ self.getNotificationToken()
953
+ while True:
954
+ notificationsUrl = self.url_notification_api+"notifications?pagination%5Bpage%5D="+str(pageIterator)+"&pagination%5Bper_page%5D=15&meta=1"
955
+ self.webheaders['Authorization'] = "Bearer " + self.notificationToken
956
+ response = self.try_requests("GET", notificationsUrl, 10)
957
+ self.webheaders['Authorization'] = "Bearer " + self.token
958
+ try:
959
+ jsonResponse = response.json()
960
+ except:
961
+ return(1)
962
+ if not jsonResponse:
963
+ break
964
+ for i in range(len(jsonResponse)):
965
+ if jsonResponse[i]["read"] == False:
966
+ notifications.append(jsonResponse[i])
967
+ else:
968
+ breakOuterLoop=1
969
+ break
970
+ if breakOuterLoop == 1:
971
+ break
972
+ else:
973
+ pageIterator=pageIterator+1
974
+ return(notifications)
975
+
976
+ #############################
977
+ ## Get Current Target Slug ##
978
+ #############################
979
+
980
+ def getCurrentTargetSlug(self):
981
+ response = self.try_requests("GET", self.url_activate_target, 10)
982
+ try:
983
+ jsonResponse = response.json()
984
+ except:
985
+ return(1)
986
+ if jsonResponse['slug']:
987
+ return(jsonResponse['slug'])
988
+
989
+ ##############
990
+ ## Get ROEs ##
991
+ ##############
992
+
993
+ def getRoes(self, slug):
994
+ requestURL = self.url_scope_summary + str(slug)
995
+ response = self.try_requests("GET", requestURL, 10)
996
+ roes = list()
997
+ try:
998
+ jsonResponse = response.json()
999
+ except:
1000
+ return(1)
1001
+ if not jsonResponse['roes']:
1002
+ return(roes)
1003
+ else:
1004
+ for i in range(len(jsonResponse['roes'])):
1005
+ roes.append(jsonResponse['roes'][i])
1006
+ return(roes)
1007
+
1008
+ ######################
1009
+ ## Get Transactions ##
1010
+ ######################
1011
+ def getTransactions(self):
1012
+ pageIterator=1
1013
+ breakOuterLoop = 0
1014
+ transactions = []
1015
+ while True:
1016
+ transactionUrl = self.url_transactions+"?page="+str(pageIterator)+"&per_page=15"
1017
+ response = self.try_requests("GET", transactionUrl, 10)
1018
+ try:
1019
+ jsonResponse = response.json()
1020
+ except:
1021
+ return(1)
1022
+ if not jsonResponse:
1023
+ break
1024
+ for i in range(len(jsonResponse)):
1025
+ if jsonResponse[i]["title"] == "CashOut":
1026
+ if float(jsonResponse[i]['amount']) < 0:
1027
+ amount = float(jsonResponse[i]['amount']) * -1
1028
+ else:
1029
+ amount = float(jsonResponse[i]['amount'])
1030
+ ts = datetime.datetime.fromtimestamp(jsonResponse[i]['created_at'])
1031
+ transactions.append(ts.strftime('%Y-%m-%d')+","+str(amount))
1032
+ pageIterator=pageIterator+1
1033
+ return(transactions)
1034
+
1035
+ ########################
1036
+ ## Get LP Credentials ##
1037
+ ########################
1038
+ def getLPCredentials(self):
1039
+ response = self.try_requests("GET", self.url_lp_credentials, 10)
1040
+ try:
1041
+ creds = response.json()
1042
+ creds["openvpn_file"] = base64.b64decode(creds["openvpn_file"])
1043
+ return creds
1044
+ except:
1045
+ return
1046
+