Coder Social home page Coder Social logo

emtunc / slackpirate Goto Github PK

View Code? Open in Web Editor NEW
721.0 20.0 109.0 319 KB

Slack Enumeration and Extraction Tool - extract sensitive information from a Slack Workspace

License: GNU General Public License v3.0

Python 100.00%
slack slack-api redteaming blueteaming redteam blueteam python

slackpirate's Introduction

SlackPirate - Slack Enumeration and Extraction Tool

Build Status

This is a tool developed in Python which uses the native Slack APIs to extract 'interesting' information from a Slack workspace given an access token.

As of May 2018, Slack has over 8 million customers and that number is rapidly rising - the integration and 'ChatOps' possibilities are endless and allows teams (not just developers!) to create some really powerful workflows and Slack bot/application interactions.

As is the way with corporations large and small, it is not unusual for tools such as Slack to fly under the Information Security governance/policy radar which ultimately leads to precarious situations whereby sensitive and confidential information end up in places they shouldn't be.

The purpose of this tool is two-fold:

  • Red-teamers can use this to identify and extract sensitive information, documents, credentials, etc from Slack given a low-privileged account to the organisation's Workspace. This could allow an attacker to pivot on to other systems and/or gain far more intimate knowledge and inner-workings of corporate systems/applications
  • Blue-teamers can use this to identify and detect sensitive information on the Workspace that perhaps shouldn't exist on there in the first instance. Blue-teamers can use this information for internal training and awareness purposes by demonstrating the output of the tool and the type of 'things' that could be used and abused by (internal as well as external) attackers.

The tool allows you to easily gather sensitive information for offline viewing at your convenience.

Note: I'm a Python n00b and have no doubt that the script can be optimised and improved massively - please feel free to make pull requests; I'll review and merge them as appropriate!

Information Gathering

The tool uses the native Slack APIs to extract 'interesting' information and looks for the following information, today:

  • Print to standard output the domains (if any) that are allowed to register for the Workspace - I've seen stale, old and forgotten domains here that can be purchased and used to register for the Workspace
  • Links to S3 buckets
  • Passwords
  • AWS Access/Secret keys
  • Private Keys
  • Pinned messages across all Channels
  • References to links and URLs that could provide further access to sensitive materials - think: Google Docs, Trello Invites, links to internal systems, etc
  • Files which could contain sensitive information such as .key, .sh, the words "password" or "secret" embedded in a document, etc

Slack Cookie

The Slack web application uses a number of cookies - the one of special interest is called, wait for it... d. This d cookie is the same across all Workspaces the victim has access to. What this means in reality is that a single stolen d cookie would allow an attacker to get access to all of the Workspaces the victim is logged-in to; my experience with the Slack web application is that once you are logged in, you'll remain logged in indefinitely.

Slack Token

The Slack API token is a per-workspace token. One token cannot (as far as I know) access other workspaces in the same way the d cookie above allows access to all Workspaces.

For the tool to search for and extract information, you will need to provide it an API token. There are two straight forward ways of doing this:

  • Provide the tool a d cookie by using the --cookie flag. The tool will output the associated Workspaces and tokens
  • Provide the tool with a token directly by using the --token flag. You can find this by viewing the source of the Workspace URL and doing a search for XOX

The token will look something like this:

api_token: "xoxs-x-x-x-x"

Make a copy of that and pass that in to the script using the --token flag.

Building

The script has been developed, tested and confirmed working on Python 3.5, 3.6 and 3.7. A quick test on Python 2 presented some compatibility issues.

Linux with virtualenv

  • git clone https://github.com/emtunc/SlackPirate
  • pip install virtualenv
  • virtualenv SlackPirate
  • source SlackPirate/bin/activate
  • pip install -r requirements.txt
  • ./SlackPirate.py --help

Linux without virtualenv

  • git clone https://github.com/emtunc/SlackPirate
  • chmod +x SlackPirate.py
  • pip install -r requirements.txt
  • ./SlackPirate.py --help

Windows with virtualenv

  • git clone https://github.com/emtunc/SlackPirate
  • pip install virtualenv
  • virtualenv SlackPirate
  • SlackPirate\Scripts\activate.bat
  • pip install -r requirements.txt
  • python SlackPirate.py --help

Windows without virtualenv

  • git clone https://github.com/emtunc/SlackPirate
  • pip install -r requirements.txt
  • python SlackPirate.py --help

Usage

python3 SlackPirate.py --help

  • Display the help menu - this includes information about all scan modules you can explicitly select or ignore

python3 SlackPirate.py --interactive

  • Interactive mode instructs the tool to allow you to provide a token or cookie, and choose scans to run through a console UI rather than via command line arguments.

python3 SlackPirate.py --cookie <cookie>

This will do the following:

  • Find any associated Workspaces that can be accessed using that cookie
  • Connect to any Workspaces that were returned
  • Look for API Tokens in each returned Workspace
  • Print to standard output for use in the next command

python3 SlackPirate.py --token <token>

This will do the following:

  • Check Token validity and only continue if Slack returns True
  • Print to standard output if the token supplied has admin, owner or primary_owner privileges
  • Print to standard output if the tool found any @domains that can be used to register for the Slack Workspace (you may be surprised by what you find here - if you're lucky you'll find an old, unused, registerable domain here)
  • Dump team access logs in .json format if the token provided is a privileged token
  • Dump the user list in .json format
  • Find references to S3 buckets
  • Find references to passwords and other credentials
  • Find references to AWS keys
  • Find references to private keys
  • Find references to pinned messages across all Slack channels
  • Find references to interesting URLs and links
  • Lastly, the tool will attempt to download files based on pre-defined keywords

python3 SlackPirate.py --token <token> --s3-scan

  • This will instruct the tool to only run the S3 scan

python3 SlackPirate.py --token <token> --no-s3-scan

  • This will instruct the tool to run all scans apart from the S3 scan

python3 SlackPirate.py --token <token> --verbose

  • Verbose mode will output files in .CSV - will provide a lot more information such as channel names, usernames, perma-links and more.

Screenshots

Alt text

Alt text

Join the conversation

A public Slack Workspace has been set-up where anyone can join and discuss new features, changes, feature requests or simply ask for help. Here's the invite link: https://join.slack.com/t/slackpirate/shared_invite/zt-6o3d9tjq-PhbMxtM2o5ALgFkOB9V_dg

slackpirate's People

Contributors

0xmilan avatar emtunc avatar martiningesen avatar neurowinter avatar notoriousrebel avatar robtova avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

slackpirate's Issues

UX inefficiency: folder is created even if there is no content harvested

Unlike pinned messages, if the passwords, aws-keys.txt, private-keys, urls are not found it is not acknowledged properly.
In case of specific harvesting, such as for example only credential-scan an empty folder is created inspite of no harvested data.

Steps to reproduce

  • Replace CREDENTIAL_REGEX with a random value such that it does not match any message.
    CREDENTIAL_REGEX=r"lorem ipsum xyz abc mno"
    CREDENTIALS_REGEX = r"(?i)(" \

    and replace CREDENTIALS_QUERIES with random value.
    CREDENTIALS_QUERIES = ["lorem"]
    CREDENTIALS_QUERIES = ["password:", "password is", "pwd", "passwd"]
  • Run $ ./SlackPirate.py --token xoxs-xxxxx --credential-scan
  • Observe creation of a new folder but no passwords.txt created.

Dependabot couldn't find the branch develop

Dependabot was set up to create pull requests against the branch develop, but couldn't find it.

If the branch has been permanently deleted you can update Dependabot's target branch from your dashboard.

You can mention @dependabot in the comments below to contact the Dependabot team.

is_rate_limited needs improving as it can occasionally error out

START: Attempting to find references to interesting URLs
INFO: Slack API rate limit hit - sleeping for 60 seconds
Traceback (most recent call last):
  File "SlackPirate.py", line 643, in <module>
    scan(token=provided_token, output_info=collected_output_info)
  File "SlackPirate.py", line 463, in find_interesting_links
    pagination[query] = (r['messages']['pagination']['page_count'])
KeyError: 'messages'

The above happens because it first does a HTTP request which returns 'ratelimited' as the response... the script then sleeps for 60 seconds THEN continues to look for something on the page which it can't because it's still holding the contents of the 'ratelimited' page.

Error: Crashes when downloading files

Traceback (most recent call last):
File "./SlackPirate.py", line 831, in
scan(token=provided_token, scan_context=collected_scan_context)
File "./SlackPirate.py", line 680, in download_interesting_files
_retrieve_file_batch(batch, completed_file_names)
File "./SlackPirate.py", line 601, in _retrieve_file_batch
request.start()
File "/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/process.py", line 112, in start
self._popen = self._Popen(self)
File "/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/context.py", line 223, in _Popen
return _default_context.get_context().Process._Popen(process_obj)
File "/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/context.py", line 277, in _Popen
return Popen(process_obj)
File "/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/popen_fork.py", line 20, in init
self._launch(process_obj)
File "/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/popen_fork.py", line 69, in _launch
parent_r, child_w = os.pipe()
OSError: [Errno 24] Too many open files

Cookie Harvesting on https://slackpirate-donotuse.slack.com ??

When you use the --cookie option it sends the d cookie to https://slackpirate-donotuse.slack.com

It is not clear from the readme that this will happen and what / if anything is in that slack workspace is captures all the d cookies submitted.

  • Consider updating the readme to make it clear how the d cookie is used when the --cookie option is specified
  • Is there a way to add transparency to the contents of the slackpirate-donotuse workspace ?

Error: download_interesting_files fails in the page_counts_by_query.items() loop when the API rate limit is hit

START: Attempting to locate and download interesting files (this may take some time)
INFO: Slack API rate limit hit - sleeping for 60 seconds
Traceback (most recent call last):
File "./SlackPirate.py", line 831, in
scan(token=provided_token, scan_context=collected_scan_context)
File "./SlackPirate.py", line 664, in download_interesting_files
new_files = [new_file for new_file in response_json['files']['matches'] if
KeyError: 'files'

unable to get any data

Unable to get data.

this was using a token.

Python 3.7.2

[Credentials]: Attempting to find references to credentials
Traceback (most recent call last):
  File "./SlackPirate.py", line 1006, in <module>
    _interactive_command_line(args, selected_agent)
  File "./SlackPirate.py", line 937, in _interactive_command_line
    scan(token=provided_token, scan_context=collected_scan_context)
  File "./SlackPirate.py", line 393, in find_credentials
    page_count_by_query[query] = (r['messages']['pagination']['page_count'])
KeyError: 'messages'

Getting Admin Info Error

Python 3.10.5

python3 SlackPirate.py --token xxxxxx

[INFO]: Token looks valid! URL https://xxxxx.slack.com

  File "/Users/rwiggins/tools/SlackPirate/SlackPirate.py", line 1036, in <module>
    if check_if_admin_token(token=provided_token, scan_context=collected_scan_context):
  File "/Users/rwiggins/tools/SlackPirate/SlackPirate.py", line 238, in check_if_admin_token
    return r['user']['is_admin'] or r['user']['is_owner'] or r['user']['is_primary_owner']

JSON returned. suspect account might not have the right permissions so needs to error out properly.

{
   "ok": false,
   "error": "missing_scope",
   "needed": "users:read",
   "provided": "identify,incoming-webhook,chat:write:user,chat:write:bot"
}

[Enhancement] Keep list of downloaded files and don't re-download them

Currently if the script makes a query for 'password' and finds a file file1.pdf and makes another query for 'passwords' and finds the same file file1.pdf, it will download the file twice.

It should be possible to keep a list of file_id's and only download when the file_id is not in the list.

Should be fairly easy to implement I think.

Credential recovery fails on unicode decoding

Hey,
Running on Python 3.6 on Windows, I get the following stack trace

START: Attempting to find references to credentials
Traceback (most recent call last):
File "SlackPirate.py", line 454, in
find_credentials()
File "SlackPirate.py", line 274, in find_credentials
file_cleanup(file_credentials)
File "SlackPirate.py", line 434, in file_cleanup
lines = set(file.readlines())
File "C:\Program Files\Python36\lib\encodings\cp1252.py", line 23, in decode
return codecs.charmap_decode(input,self.errors,decoding_table)[0]
UnicodeDecodeError: 'charmap' codec can't decode byte 0x8f in position 3702: character maps to

This happens consistently

Unable to extract the password values efficiently

I ran the main function to extract credentials as :
$ ./SlackPirate.py --token xoxs-xxxxxxxx --credential-scan
the harvested credentials were successfully written to ./<folder>/passwords.txt

Meanwhile, in my private experimental slack workspace I have the following messages in the #general channel.
Screen Shot 2020-02-09 at 3 19 16 PM

I expected the password.txt file to contain these credentials, but what I found in it is:

password is "abcdef"',
password is "abcdef"'}]}]}],
password is"',

Error: when looking for s3

When using python 3.5.2

Command

python3 SlackPirate.py --token xoxb-xxx

Output.

START: Attempting to find references to S3 buckets
Traceback (most recent call last):
  File "SlackPirate.py", line 831, in <module>
    scan(token=provided_token, scan_context=collected_scan_context)
  File "SlackPirate.py", line 327, in find_s3
    page_count_by_query[query] = (r['messages']['pagination']['page_count'])
KeyError: 'messages'

[ERROR]: Token not valid. Slack error: invalid_auth

It appears that slack has changed the way authentication is performed. If you login to slack via a browser (i.e. chrome) you can not use the xoxc token by itself to authenticate to Slack.

From this SO post:

xoxc tokens are special tokens that are used by the web client. These tokens are cookie dependent, so even if the token is somehow stolen, it would not be very useful.

If you want to test this yourself, in incognito go to https://api.slack.com/methods/auth.test and try to use an XOXC token by itself. You will get invalid_auth. If you then login to slack, and try again, it will work. The reason is the browser (chrome) automatically adds the slack cookie in addition to the XOXC token to the request. If you open debugger tools (network tab) you will see the actual request that is being sent to slack includes both the token and cookie. E.g. in curl it looks like this (I stripped a bunch of unnecessary headers)

curl 'https://slack.com/api/auth.test?pretty=1' \
  -H 'cookie: d=xoxd-xxxx' \
  -H 'content-type: multipart/form-data; boundary=' \
  --data-raw $'--\r\nContent-Disposition: form-data; name="token"\r\n\r\nxoxc-xxx\r\n----\r\n'

I used https://curlconverter.com/ to convert the curl request to python, and then stuck those hardcoded header/data values at the top of the code as global variables and switched every method to POST, and passed those hardcoded values. E.g.
image

That seemed to work around the need for both cookie and token as I successfully received results in this way.

user-list.json is not a valid array of JSON objects

In dump_user_list(...) the r['members'] are appended to the outfile without any separation.

Putting "[ ]" around outfile to create an array and replacing every "}{" with "},{" solves this issue.
I don't know if it's best to append the commas inside the loop or do the replacing when the loop has finished.

Tokens

Hi,
Slack no longer produce legacy tokens, will it accept the new style app tokens?

Also with slack you can now export all messages etc to a file, is it possible to search an exported directory of json files?

--cookie doesn't seem to work anymore?

I'm getting [ERROR]: No Workspaces were found with this cookie no matter what I do.

Looking at the source it looks like it's trying to find the xoxs token by searching inside the source of "https://" + workspace + "/customize/emoji". From what I can tell that's no longer there.

[Enhancement] Add --interactive flag for an interactive experience

Hi!

I suggest adding a --interactive flag which would make it even easier to go from cookie -> token -> scanning/dumping.

python SlackPirate.py --interactive and/or python SlackPirate.py --interactive --cookie COOKIE

$ python SlackPirate.py --interactive
Give me a cookie: <input cookie here> (providing the script with --interactive --cookie would skip this step)
This cookie has access to the following workspaces:
[0] https://example.slack.com
[1] https://example2.slack.com
[2] https://example3.slack.com
Select which workspace(s) to dump: <input number(s), either a single number or comma separated>

Select what you want to dump:
[A] All
[0] Dump team access logs in .json format if the token provided is a privileged token
[1] Dump the user list in .json format
[2] Find references to S3 buckets
[3] Find references to passwords and other credentials
[4] Find references to AWS keys
[5] Find references to private keys
[6] Find references to interesting URLs and links
Input: <input A for all, a single number or comma separated numbers for multiple>

The tool would then create individual folders for each workspace (like it does today) and use each token to do the dumping/scanning on each workspace.

[Enhancement] Provide a list of user-agent strings rather than hard-coding

Rather than hard-coding the user agent strings (which can be detected easily by the Blue team/detect function within a few weeks when everyone has moved on to a newer version of Chrome/Slack), provide a large-ish(?) list of user agent strings.

Maybe even hook in to a user-agent API to randomly select a relatively active/popular user-agent?

Worth thinking about

[Enhancement] Asynchronous HTTP requests

Currently the script performs HTTP requests synchronously - although there are Slack API rate limits in place, there are certain functions and calls that can benefit greatly by introducing asynchronous requests.

For example:

  • File downloads - there is no need to wait for each file to download before moving on to the next. Also, as far as I can tell, there is no rate limit in place for file downloads. No reason why this function couldn't start asynchronously with the other functions in the script

  • The cookie check and Workspace token grabbing feature - again, no reason why we can't make requests to the Workspace URLs all in one go rather than one at a time. On average it takes around 1 second per Workspace * total number of Workspaces to get all tokens back. This could be reduced to a couple of seconds for a large number of Workspaces.

I may need some assistance with this but in the mean time I will try and play around with some of the recommended async packages.

Cookie-based Slack enumeration is likely broken

Sometime in the past week, Slack seems to have drastically changed the formatting of the "login to other slacks" page that is displayed when you access a Slack you don't have access to (like slackpirate-donotuse.slack.com). Now, instead of the other Slacks being actual HTML entities, they seem to be created by Javascript.

I updated my re-implementation of this technique like so: cdanis/emojifs@95190f9

[Enhancement] Add optional flags which can be used to turn on/off certain scans

Add optional --flags that can be used to turn on/off certain scans/functions.
For example, if I only want to scan for AWS keys then I should be able to execute something like this:

./SlackPirate.py --token 123 --aws-keys

or if I want to scan for AWS keys and private keys:

./SlackPirate.py --token 123 --aws-keys --private-keys

[Enhancement] Write new timestamped files rather than appending to old ones

Suggestion made by @milangfx which I fully agree with:

Currently the tool simply appends all results to existing files if they exist. This can mix results up and cause unintended consequences.

A better way of creating output files is to create new, timestamped ones. The added benefit of doing it this way is you have the option of diff'ing the output files.

Locations

Hi,
Is it possible for passwords, aws keys, urls and s3 to have where they've come from added to the text doc?
Its awesome having a list, but I need to track down the users/channels that these relate to

Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.