Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a29ced9
duo: mvp
bamhm182 Jan 4, 2025
3c999b5
duo: slight refactor, still terrible
bamhm182 Jan 4, 2025
5bad8e0
alerts: fixed slack
bamhm182 Jan 6, 2025
5eea262
alembic: removed pass from migration
bamhm182 Jan 6, 2025
6f9506a
state: resolved backwards state/db order
bamhm182 Jan 9, 2025
d78504e
Added default returns to a few functions
bamhm182 Jan 10, 2025
48d355c
crappy hotp
bamhm182 Jan 13, 2025
4b48b2b
Cleaning house, about to touch auth
bamhm182 Jan 25, 2025
4e1bd43
added Duo plugin and removed Authy
bamhm182 Jan 25, 2025
bf54c31
about to break stuff
bamhm182 Jan 25, 2025
e6b3e9e
hid internal functions/etc
bamhm182 Jan 25, 2025
d35f86c
fixed formatting and documentation issues
bamhm182 Jan 25, 2025
b8b2455
fixed failing tests
bamhm182 Jan 26, 2025
54065d7
fixed scopes, improved db performance
bamhm182 Jan 26, 2025
3838fdc
fixed up formatting and documentation issues
bamhm182 Jan 26, 2025
94b7164
added better error handling to db functions
bamhm182 Jan 26, 2025
35abb92
added codenames back to targets
bamhm182 Jan 27, 2025
5cd93d1
added flexability to searching targets in db
bamhm182 Jan 27, 2025
233c422
improvements to db transactions
bamhm182 Jan 29, 2025
7bbd2d5
Mission summary and checking improvements
bamhm182 Jan 29, 2025
bfbc40a
mission fixes
bamhm182 Feb 2, 2025
dd9b749
improved updating of existing targets
bamhm182 Feb 2, 2025
5012207
improvements to mission summary
bamhm182 Feb 5, 2025
0b79bde
improved api request error handling
bamhm182 Feb 6, 2025
8d10960
added synack_domain to db
bamhm182 Feb 6, 2025
f94d072
other platform should work
bamhm182 Feb 6, 2025
3a6f26e
fixed login.js
bamhm182 Feb 6, 2025
9428ec1
improved SynackAPI Login button
bamhm182 Feb 6, 2025
c406ca9
improved request and re-login handling
bamhm182 Mar 2, 2025
259b3f8
resolved mismatch between variable names
bamhm182 Mar 4, 2025
3be0bae
missed a variable
bamhm182 Mar 4, 2025
74a7e04
Fix get_config to persist new Config rows and avoid DetachedInstanceE…
Derb237 Oct 7, 2025
11a8130
Fix spurious debug logging in api.py
Derb237 Nov 6, 2025
da2f260
Fix authentication error handling to prevent account lockout
Derb237 Nov 6, 2025
c80c175
Add Duo Push auto-approval and HOTP hex conversion
Derb237 Nov 6, 2025
2674241
Add organization name field and additional target fields
Derb237 Aug 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions checks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ for plugin in ./src/synack/plugins/*.py; do
if [[ $? != 0 ]]; then
grep "def ${def}(" ${plugin} -B1 | grep "@property" > /dev/null 2>&1
if [[ $? != 0 ]]; then
echo ${p} missing documentation for: ${def}
if [[ "${def}" != "_"* ]]; then
echo ${p} missing documentation for: ${def}
fi
fi
fi
done
Expand Down Expand Up @@ -59,6 +61,6 @@ for doc in ./docs/src/usage/plugins/*.md; do
fi
done

coverage run --source=src --omit=src/synack/db/alembic/env.py,src/synack/db/alembic/versions/*.py -m unittest discover test
coverage report | egrep -v "^[^T].*100%"
coverage html
python3-coverage run --source=src --omit=src/synack/db/alembic/env.py,src/synack/db/alembic/versions/*.py -m unittest discover test
python3-coverage report | egrep -v "^[^T].*100%"
python3-coverage html
2 changes: 1 addition & 1 deletion docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
- [Api](./usage/plugins/api.md)
- [Auth](./usage/plugins/auth.md)
- [Db](./usage/plugins/db.md)
- [Duo](./usage/plugins/duo.md)
- [Debug](./usage/plugins/debug.md)
- [Hydra](./usage/plugins/hydra.md)
- [Missions](./usage/plugins/missions.md)
- [Notifications](./usage/plugins/notifications.md)
- [Scratchspace](./usage/plugins/scratchspace.md)
Expand Down
8 changes: 2 additions & 6 deletions docs/src/usage/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,8 @@ With that in mind, I would highly recommend you become familiar with the [Plugin
## Authentication

The first time you try to do anything which requires authentication, you will be automatically prompted for your credentials.
This prompt will expect the `Synack Email` and `Synack Password`, which are fairly self explanitory, but it also asks for the `Synack OTP Secret`.
This prompt will expect the `Synack Email` and `Synack Password`, which are fairly self explanatory.

The `Synack OTP Secret` is NOT the 8 digit code you pull out of Authy.
Instead, it is a string that you must extract from Authy via a method similar to the one found [here](https://gist.github.com/gboudreau/94bb0c11a6209c82418d01a59d958c93).

Use the above instructions at your own discression.
I TAKE NO RESPONSIBILITY IF SOMETHING BAD HAPPENS AS A RESULT.
For Duo MFA setup options, see the [Duo plugin documentation](./plugins/duo.md).

Once you complete these steps, your credentials are stored in a SQLiteDB at `~/.config/synack/synackapi.db`.
1 change: 1 addition & 0 deletions docs/src/usage/main-components/files.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ It is intended to be used with the following TamperMonkey script in order to do
// @version 0.1
// @description Go to the platform automatically
// @author You
// @run-at document-start
// @match https://*.synack.com/*
// @require file:///home/<homedir>/.config/synack/login.js
// @grant none
Expand Down
6 changes: 5 additions & 1 deletion docs/src/usage/main-components/state.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,16 @@ In the event that one of the State variables is set and is **not** constantly at
| api_token | str | This is the Synack Access Token used to authenticate requests
| config_dir | pathlib.Path | The location of the Database and Login script
| debug | bool | Used to show/hide debugging messages
| duo_push_akey | str | Duo device activation key for push auto-approval
| duo_push_host | str | Duo API hostname for push auto-approval
| duo_push_pkey | str | Duo device private key for push auto-approval
| duo_push_rsa_key_path | str | Path to RSA private key for signing Duo API requests
| email | str | Your email address used to log into Synack
| http_proxy | str | A Web Proxy (Burp, etc.) to intercept requests
| https_proxy | str | A Web Proxy (Burp, etc.) to intercept requests
| login | bool | Used to enable/disable a check of the api_token upon creation of the Handler
| notifications_token | str | Token used for authentication when dealing with Synack Notifications
| otp_secret | str | OTP Secret held by Authy. NOT an OTP. For more information, read the Usage page
| otp_secret | str | OTP Secret held by Duo Mobile. NOT an OTP. For more information, read the Usage page
| password | str | Your Synack Password
| session | requests.Session | Tracks cookies and headers across various functions
| template_dir | pathlib.Path | The location of your Mission Templates
Expand Down
47 changes: 22 additions & 25 deletions docs/src/usage/plugins/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,6 @@

This plugin deals with authenticating the user to Synack.

## auth.build_otp()

> Use your stored otp_secret to generate a current OTP code
>
>> Examples
>> ```python3
>> >>> h.auth.build_otp()
>> '1234567'
>> ```

## auth.get_api_token()

> Walks through the whole authentication workflow to get a new api_token
Expand All @@ -22,31 +12,29 @@ This plugin deals with authenticating the user to Synack.
>> '489hr98hf...eh59'
>> ```

## auth.get_login_csrf()
## auth.get_authentication_response(csrf)

> Pulls a CSRF Token from the Login page
> Send the username and password to Synack and returns the response
>
> | Arguments | Description
> | --- | ---
> | `csrf` | CSRF token issued by Synack Authentication Workflow
>
>> Examples
>> ```python3
>> >>> h.auth.get_login_csrf()
>> '45h998h4g5...45wh89g9wh'
>> >>> csrf = h.auth.get_login_csrf()
>> >>> h.auth.get_authentication_response(csrf)
>> {'success': True, ..., 'duo_auth_url': 'https://...'}
>> ```

## auth.get_login_grant_token(csrf, progress_token)
## auth.get_login_csrf()

> Get a Login Grant Token by providing an OTP Code
>
> | Argument | Type | Description
> | --- | --- | ---
> | `csrf` | str | A CSRF Token used while logging in
> | `progress_token` | str | A token returned after submitting a valid username and password
> Pulls a CSRF Token from the Login page
>
>> Examples
>> ```python3
>> >>> csrf = h.auth.get_login_csrf()
>> >>> lpt = h.auth.get_login_progress_token(csrf)
>> >>> h.auth.get_login_grant_token(csrf, lpt)
>> '58t7i...rh87g58'
>> >>> h.auth.get_login_csrf()
>> '45h998h4g5...45wh89g9wh'
>> ```

## auth.get_login_progress_token(csrf)
Expand Down Expand Up @@ -74,6 +62,15 @@ This plugin deals with authenticating the user to Synack.
>> '958htiu...h98f5ht'
>> ```

## auth.set_api_token_invalid()

> Invalidates the API Token by logging out
>
>> Examples
>> ```python3
>> >>> h.auth.set_api_token_invalid()
>> ```

## auth.set_login_script()

> Writes the current api_token to `~/.config/synack/login.js` JavaScript file to help with staying logged in.
Expand Down
11 changes: 4 additions & 7 deletions docs/src/usage/plugins/db.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Additionally, some properties can be overridden by the State, which allows you t
>
> | Arguments | Type | Description
> | --- | --- | ---
> | `results` | list(dict) | A list of dictionaries containing results from some scan, Hydra, etc.
> | `results` | list(dict) | A list of dictionaries containing results from some scan, etc.
>
>> Examples
>> ```python3
Expand All @@ -96,11 +96,8 @@ Additionally, some properties can be overridden by the State, which allows you t
>> ... "port": "443",
>> ... "protocol": "tcp",
>> ... "service": "Super Apache NGINX Deluxe",
>> ... "screenshot_url": "http://127.0.0.1/h3298h23.png",
>> ... "url": "http://bubba.net",
>> ... "open": True,
>> ... "updated": 1654969137
>> ...
>> ... },
>> ... {
>> ... "port": "53",
Expand Down Expand Up @@ -132,7 +129,7 @@ Additionally, some properties can be overridden by the State, which allows you t
>
> | Arguments | Type | Description
> | --- | --- | ---
> | `results` | list(dict) | A list of dictionaries containing results from some scan, Hydra, etc.
> | `results` | list(dict) | A list of dictionaries containing results from some scan, etc.
>
>> Examples
>> ```python3
Expand Down Expand Up @@ -178,7 +175,7 @@ Additionally, some properties can be overridden by the State, which allows you t
> | --- | --- | ---
> | `port` | int | Port number to search for (443, 80, 25, etc.)
> | `protocol` | str | Protocol to search for (tcp, udp, etc.)
> | `source` | str | Source to search for (hydra, nmap, etc.)
> | `source` | str | Source to search for (masscan, nmap, etc.)
> | `ip` | str | IP Address to search for
> | `kwargs` | kwargs | Any attribute of the Target Database Model (codename, slug, is_active, etc.)
>
Expand All @@ -187,7 +184,7 @@ Additionally, some properties can be overridden by the State, which allows you t
>> >>> h.db.find_ports(codename="SLEEPYPUPPY")
>> [
>> {
>> 'ip': '1.2.3.4', 'source': 'hydra', 'target': '123hg912',
>> 'ip': '1.2.3.4', 'source': 'masscan', 'target': '123hg912',
>> 'ports': [
>> { 'open': True, 'port': '443', 'protocol': 'tcp', 'service': 'https - Wordpress', 'updated': 1654840021 },
>> ...
Expand Down
86 changes: 86 additions & 0 deletions docs/src/usage/plugins/duo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Duo

## Duo MFA Options

When prompted during authentication, you can choose from three options:

**Option 1: Manual Push Approval (Simplest)**
- Press Enter when prompted for OTP Secret
- Approve push notifications on your phone each time the token is expired
- No additional setup required

**Option 2: Automated OTP (Preferred)**
- Enter your OTP Secret when prompted (accepts both hex and base32 formats)
- Automatically generates OTP codes using a counter (saved in the database)
- Extract the `hotp_secret` from Duo Mobile using [synackDUO](https://github.com/dinosn/synackDUO) (see `response.json`)
- **Note:** This is NOT the 8-digit codes from Duo Mobile, but the HOTP secret key

**Option 3: Automated Duo Push**
- Uses Duo credentials to auto-approve push requests
- Can also approve push requests using duo.approve_pending_push(timeout)
- Extract credentials using [synackDUO](https://github.com/dinosn/synackDUO) (see `response.json`) (see below)


**Disclaimer:** Use the above instructions at your own discretion. I TAKE NO RESPONSIBILITY IF SOMETHING BAD HAPPENS AS A RESULT.

## Duo Push Auto-Approval Setup

The Duo plugin supports push notification approval using device credentials.

### Prerequisites

You must extract and configure four credentials from Duo Mobile:

| Credential | Description | Example
| --- | --- | ---
| `duo_push_akey` | Device activation key | `DAXXXXXXXXXXXXXXXXXXXX`
| `duo_push_pkey` | Device private key | `DPXXXXXXXXXXXXXXXXXXXX`
| `duo_push_host` | Duo API hostname | `api-xxxxxxxx.duosecurity.com`
| `duo_push_rsa_key_path` | Path to RSA private key | `~/.config/synack/duo/key.pem`

### Configuration

Set credentials in the database:
PP
```python
import synack

h = synack.Handler(login=False)

h.db.set_config('duo_push_akey', 'DAXXXXXXXXXXXXXXXXXX')
h.db.set_config('duo_push_pkey', 'DPXXXXXXXXXXXXXXXXXX')
h.db.set_config('duo_push_host', 'api-xxxxxxxx.duosecurity.com')
h.db.set_config('duo_push_rsa_key_path', 'synackDUO/key.pem')
```

## duo.get_grant_token(auth_url)

> Handles Duo Security MFA stages and returns the grant_token used to finish logging into Synack
>
> | Arguments | Description
> | --- | ---
> | `auth_url` | Duo Security Authentication URL generaated by sending credentials to Synack
>
>> Examples
>> ```python3
>> >>> h.duo.get_grant_token('https:///...duosecurity.com/...')
>> 'Y8....6g'
>> ```

## duo.approve_pending_push(timeout)

> Wait for and approve a single Duo push notification
>
> Polls Duo's device API for pending push notifications and automatically approves the first one found. Useful for automated workflows that need to handle Duo MFA.
>
> | Argument | Type | Default | Description
> | --- | --- | --- | ---
> | `timeout` | int | 30 | Maximum seconds to wait for a push notification
>
> Returns `True` if a push was approved, `False` if timeout or error occurred.
>
>> Examples
>> ```python3
>> >>> h.duo.approve_pending_push(timeout=60)
>> True
>> ```
38 changes: 0 additions & 38 deletions docs/src/usage/plugins/hydra.md

This file was deleted.

11 changes: 10 additions & 1 deletion docs/src/usage/plugins/notifications.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,19 @@

## notifications.get_unread_count()

> Get the number of unread notifications.
> Get the number of unread notifications
>
>> Examples
>> ```python3
>> >>> h.notifications.get_unread_count()
>> 7
>> ```

## notifications.set_read()

> Set all notifications as read
>
>> Examples
>> ```python3
>> >>> h.notifications.set_read()
>> ```
24 changes: 21 additions & 3 deletions docs/src/usage/plugins/scratchspace.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

## scratchspace.set_assets_file(content, target=None, codename=None)

> This function will save a `assets.txt` scope file within a `codename` folder in within the `self.db.scratchspace_dir` folder
> This function will save a `assets.txt` scope file within a `codename` folder within the `self.db.scratchspace_dir` folder
> If `self.db.use_scratchspace` is `True`, this function is automatically run when you do `targets.get_scope()` or `targets.get_scope_web()`
>
> | Arguments | Type | Description
Expand All @@ -36,7 +36,7 @@

## scratchspace.set_burp_file(content, target=None, codename=None)

> This function will save a `burp.txt` scope file within a `codename` folder in within the `self.db.scratchspace_dir` folder
> This function will save a `burp.txt` scope file within a `codename` folder within the `self.db.scratchspace_dir` folder
> If `self.db.use_scratchspace` is `True`, this function is automatically run when you do `targets.get_scope()` or `targets.get_scope_web()`
>
> | Arguments | Type | Description
Expand Down Expand Up @@ -82,9 +82,27 @@
>> [PosixPath('/home/user/Scratchspace/SLEEPYTURTLE/file1.txt'), ...]
>> ```

## scratchspace.set_file(content, filename, target=None, codename=None)

> This function will save a text file with a given name within a `codename` folder within the `sself.db.scratchspace_dir` folder.
> If `self.db.use_scratchspace` is `True`, this function is automatically run when you do `targets.get_scope()` or similar.
>
> | Arguments | Type | Description
> | --- | --- | ---
> | `content` | str,list(str) | Either a preformatted string or (more likely) the return of `targets.get_scope_host()`
> | `filename` | str | Desired filename
> | `target` | db.models.Target | A Target Database Object
> | `codename` | str | Codename of a Target
>
>> Examples
>> ```python3
>> >>> h.scratchspace.set_file('some unique string', 'mystring.txt', codename='ADAMANTARDVARK')
>> '/tmp/Scratchspace/ADAMANTARDVARK/mystring.txt'
>> ```

## scratchspace.set_hosts_file(content, target=None, codename=None)

> This function will save a `hosts.txt` scope file within a `codename` folder in within the `self.db.scratchspace_dir` folder.
> This function will save a `hosts.txt` scope file within a `codename` folder within the `self.db.scratchspace_dir` folder.
> If `self.db.use_scratchspace` is `True`, this function is automatically run when you do `targets.get_scope()` or `targets.get_scope_host()`
>
> | Arguments | Type | Description
Expand Down
Loading