Skip to content

Commit

Permalink
Added some helper functions & other minor improvements (#7)
Browse files Browse the repository at this point in the history
* Added functionality to facilitate adding scopes to the database
* Added functionality to check Mission Wallet status
* Added functionality to connect/disconnect from targets
* Improved handling of information pertaining to h.targets.get_connected()
  • Loading branch information
bamhm182 authored Jul 10, 2022
1 parent c0089e8 commit 204442c
Show file tree
Hide file tree
Showing 9 changed files with 289 additions and 2 deletions.
20 changes: 20 additions & 0 deletions docs/src/usage/plugins/missions.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,26 @@
>> [{"status": "FOR_REVIEW", "title": "Some Mission",...},...]
>> ```
## missions.get_wallet_claimed()
> Get the amount of missions counting against your Mission Wallet
>
>> Examples
>> ```python3
>> >>> h.missions.get_wallet_claimed()
>> 25
>> ```
## missions.get_wallet_limit()
> Get the amount you are able to hold in your Missions Wallet
>
>> Examples
>> ```python3
>> >>> h.missions.get_wallet_limit()
>> 100
>> ```
## missions.set_claimed(mission)
> Try and claim one mission
Expand Down
60 changes: 60 additions & 0 deletions docs/src/usage/plugins/targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,27 @@
>> 'uwfpmfpgjlum'
>> ```
## targets.build_scope_host_db(slug, scope)
> Prints a list of IPs ready to ingest into the Database
>
> | Arguments | Type | Description
> | --- | --- | ---
> | `slug` | str | The slug of a Target
> | `scope` | list(dict) | Return of `targets.get_scope_host()` from Synack's API
>
>> Examples
>> ```python3
>> >>> scope = h.targets.get_scope(codename='JANGLYJALOPY')
>> >>> scope_db = h.targets.build_scope_host_db(scope)
>> >>> scope_db
>> [
>> {'ip': '1.1.1.1', 'target': '2398her8h'},
>> ...
>> ]
>> >>> h.db.add_ips(scope_db)
>> ```
## targets.build_scope_web_burp(scope)
> Prints a dictionary compatible with Burp Suite from the output of `targets.get_scope_web()`
Expand All @@ -49,6 +70,29 @@
>> }}}
>> ```
## targets.build_scope_web_db(scope)
> Prints a list of URLs which can be ingested into the Database
>
> | Arguments | Type | Description
> | --- | --- | ---
> | `scope` | list(dict) | Return of `targets.get_scope_web()` from Synack's API
>
>> Examples
>> ```python3
>> >>> scope = h.targets.get_scope(codename='SLAPPYMONKEY')
>> >>> scope_db = h.targets.build_scope_web_db(scope)
>> >>> scope_db
>> [
>> {
>> 'target': '94hw8ier',
>> 'urls': [{'url': https://good.monkey.com'}]
>> },
>> ...
>> ]
>> >>> h.db.add_urls(scope_db)
>> ```
## targets.build_scope_web_urls(scope)
> Prints a dictionary containing lists of `in` scope and `out` of scope URLs
Expand Down Expand Up @@ -177,6 +221,22 @@
>> 'owners': [{'owner_uid': '97g8ehri', 'owner_type_id': 1, 'codename': 'slappyfrog'}, ...]
>> }, ...]
## targets.set_connected(target, **kwargs)
> Connect to a specified target
>
> | Argments | Type | Description
> | `target` | db.models.Target | A single Target returned from the database
> | `kwargs` | kwargs | Information used to look up a Target in the database (ex: `codename`, `slug`, etc.)
>
>> Examples
>> ```python3
>> h.targets.set_connected(codename='BLINKYBABOON')
>> >>> {'slug': '12083y9', 'codename': 'BLINKYBABOON', 'status': 'Connected'}
>> h.targets.set_connected(slug='12083y9')
>> >>> {'slug': '12083y9', 'codename': 'BLINKYBABOON', 'status': 'Connected'}
>> ```
## targets.set_registered(targets)
> Registers unregistered Targets.
Expand Down
6 changes: 6 additions & 0 deletions src/synack/plugins/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ def request(self, method, path, **kwargs):
headers=headers,
proxies=proxies,
verify=verify)
elif method.upper() == 'PUT':
res = self.state.session.put(url,
headers=headers,
proxies=proxies,
params=data,
verify=verify)

self.debug.log("Network Request",
f"{res.status_code} -- {method.upper()} -- {url}" +
Expand Down
14 changes: 14 additions & 0 deletions src/synack/plugins/missions.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,20 @@ def get_in_review(self):
"""Get a list of missions currently in review"""
return self.get("FOR_REVIEW")

def get_wallet_claimed(self):
"""Get Current Claimed Amount for Mission Wallet"""
res = self.api.request('GET',
'tasks/v2/researcher/claimed_amount')
if res.status_code == 200:
return int(res.json().get('claimedAmount', '0'))

def get_wallet_limit(self):
"""Get Current Mission Wallet Limit"""
res = self.api.request('GET',
'profiles/me')
if res.status_code == 200:
return int(res.json().get('claim_limit', '0'))

def set_claimed(self, mission):
"""Try to claim a single mission
Expand Down
52 changes: 50 additions & 2 deletions src/synack/plugins/targets.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
Functions related to handling and checking targets
"""

import ipaddress

from urllib.parse import urlparse
from .base import Plugin

Expand All @@ -21,7 +23,7 @@ def build_codename_from_slug(self, slug):
Arguments:
slug -- Slug of desired target
"""
codename = None
codename = 'NONE'
targets = self.db.find_targets(slug=slug)
if not targets:
self.get_registered_summary()
Expand All @@ -41,6 +43,17 @@ def build_slug_from_codename(self, codename):
slug = targets[0].slug
return slug

def build_scope_host_db(self, slug, scope):
"""Return a Host Scope that can be ingested into the Database"""
ret = list()
for asset in scope:
for ip in [str(ip) for ip in ipaddress.ip_network(asset)]:
ret.append({
'target': slug,
'ip': ip
})
return ret

def build_scope_web_burp(self, scope):
"""Return a Burp Suite scope given retrieved web scope"""
ret = {'target': {'scope': {'advanced_mode': 'true', 'exclude': [], 'include': []}}}
Expand All @@ -60,11 +73,25 @@ def build_scope_web_burp(self, scope):
})
return ret

def build_scope_web_db(self, scope):
"""Return a Web Scope that can be ingested into the Database"""
ret = list()
for asset in scope:
if asset.get('status') == 'in':
for owner in asset.get('owners'):
ret.append({
"target": owner.get('owner_uid'),
"urls": [{
"url": asset.get('raw_url')
}]
})
return ret

def build_scope_web_urls(self, scope):
"""Return a list of the raw urls gived a retrieved web scope"""
ret = {"in": list(), "out": list()}
for asset in scope:
if asset["status"] == "in":
if asset.get('status') == 'in':
ret["in"].append(asset["raw_url"])
else:
ret["out"].append(asset["raw_url"])
Expand Down Expand Up @@ -100,6 +127,10 @@ def get_connected(self):
else:
slug = j['slug']
status = "Connected"

if slug == '':
status = 'Not Connected'

ret = {
"slug": slug,
"codename": self.build_codename_from_slug(slug),
Expand Down Expand Up @@ -179,6 +210,23 @@ def get_unregistered(self):
ret.append({'codename': t['codename'], 'slug': t['slug']})
return ret

def set_connected(self, target=None, **kwargs):
"""Connect to a target"""
slug = None
if target:
slug = target.slug
elif len(kwargs) == 0:
slug = ''
else:
target = self.db.find_targets(**kwargs)
if target:
slug = target[0].slug

if slug is not None:
res = self.api.request('PUT', 'launchpoint', data={'listing_id': slug})
if res.status_code == 200:
return self.get_connected()

def set_registered(self, targets=None):
"""Register all unregistered targets"""
if targets is None:
Expand Down
Binary file removed test/.test_missions.py.swp
Binary file not shown.
19 changes: 19 additions & 0 deletions test/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,25 @@ def test_request_patch(self):
proxies=None,
verify=True)

def test_request_put(self):
"""PUT requests should work"""
self.api.state.session.put = MagicMock()
data = {'test': 'test'}
self.api.db.use_proxies = False
self.api.db.user_id = "paco"
self.api.db.api_token = "12345"
url = 'https://platform.synack.com/api/test'
headers = {
'Authorization': 'Bearer 12345',
'user_id': 'paco'
}
self.api.request('PUT', 'test', data=data)
self.api.state.session.put.assert_called_with(url,
params=data,
headers=headers,
proxies=None,
verify=True)

def test_request_proxies(self):
"""Proxies should be used if set"""
proxies = {
Expand Down
20 changes: 20 additions & 0 deletions test/test_missions.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,26 @@ def test_get_in_review(self):
self.missions.get_in_review())
self.missions.get.assert_called_with("FOR_REVIEW")

def test_get_wallet_claimed(self):
"""Should report the Mission Wallet Claimed Amount"""
self.missions.api.request.return_value.status_code = 200
self.missions.api.request.return_value.json.return_value = {
"claimedAmount": "20"
}
self.assertEqual(20, self.missions.get_wallet_claimed())
self.missions.api.request.assert_called_with('GET',
'tasks/v2/researcher/claimed_amount')

def test_get_wallet_limit(self):
"""Should report the Mission Wallet Limit Amount"""
self.missions.api.request.return_value.status_code = 200
self.missions.api.request.return_value.json.return_value = {
"claim_limit": "20"
}
self.assertEqual(20, self.missions.get_wallet_limit())
self.missions.api.request.assert_called_with('GET',
'profiles/me')

def test_get_count(self):
"""Should get the current number of published missions"""
self.missions.api.request.return_value.status_code = 204
Expand Down
Loading

0 comments on commit 204442c

Please sign in to comment.