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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

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
+