├── setup.cfg ├── .gitignore ├── LICENSE ├── README.md ├── setup.py └── emailrep ├── cli.py ├── __init__.py └── utils.py /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = E501 3 | [pep8] 4 | ignore = E501 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | emailrep.egg-info 4 | venv 5 | requirements.txt 6 | __pycache__ 7 | *.swp 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Sublime Security 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python EmailRep 2 | 3 | This is a python 3 library and cli module for interacting with the [EmailRep](https://emailrep.io) service. 4 | 5 | ## Installation 6 | `pip3 install emailrep --upgrade` or `python3 -m pip install --upgrade emailrep` 7 | 8 | ## Quick Start (cli) 9 | ```sh 10 | # setup your api key (optional) 11 | emailrep setup -k 12 | 13 | # query an email address 14 | emailrep bill@microsoft.com 15 | 16 | # report an email address (key required) 17 | emailrep --report foo@bar.com --tags "bec, maldoc" --description "Phishing email targeting CEO" 18 | 19 | ``` 20 | 21 | ## Quick Start (python library) 22 | ```py 23 | from emailrep import EmailRep 24 | 25 | # setup your api key (optional) 26 | emailrep = EmailRep('') 27 | 28 | # query an email address 29 | emailrep.query("bill@microsoft.com") 30 | 31 | # report an email address (key required) 32 | emailrep.report("foo@bar.com", ["bec", "maldoc"], "Phishing email targeting CEO") 33 | 34 | ``` 35 | 36 | Full API docs can be found [here](https://docs.emailrep.io). 37 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | install_requires = [ 7 | "python-dateutil", 8 | "requests", 9 | "setuptools", 10 | "PySocks" 11 | ] 12 | 13 | setuptools.setup( 14 | name="emailrep", 15 | version="0.0.5", 16 | author="Sublime Security", 17 | author_email="hi@sublimesecurity.com", 18 | description="Python interface for the EmailRep API", 19 | long_description=long_description, 20 | long_description_content_type="text/markdown", 21 | url="https://emailrep.io", 22 | download_url="https://github.com/sublime-security/emailrep.io-python", 23 | packages=setuptools.find_packages(), 24 | install_requires=install_requires, 25 | classifiers=[ 26 | "Programming Language :: Python :: 3", 27 | "License :: OSI Approved :: MIT License", 28 | "Operating System :: OS Independent", 29 | "Natural Language :: English", 30 | "Intended Audience :: Developers", 31 | ], 32 | entry_points={"console_scripts": ["emailrep = emailrep.cli:main"]}, 33 | keywords=["security", "phishing", "analysts", "soc", "threat intelligence"], 34 | license="MIT", 35 | ) 36 | -------------------------------------------------------------------------------- /emailrep/cli.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from dateutil import parser 3 | 4 | from emailrep.utils import parse_args, load_config 5 | from emailrep import EmailRep 6 | 7 | 8 | def main(): 9 | (action, args, proxy) = parse_args() 10 | config = load_config() 11 | 12 | emailrep = EmailRep( 13 | key=config.get('emailrep', 'key'), 14 | proxy=proxy) 15 | 16 | try: 17 | if action == EmailRep.QUERY: 18 | result = emailrep.query(args.query) 19 | 20 | if result.get("status") and result["status"] == "fail": 21 | print("Failed: %s" % result["reason"]) 22 | sys.exit() 23 | 24 | emailrep.format_query_output(result) 25 | 26 | elif action == EmailRep.REPORT: 27 | email = args.report 28 | tags = args.tags.split(",") 29 | 30 | if args.timestamp: 31 | try: 32 | timestamp = parser.parse(args.timestamp) 33 | timestamp = int(timestamp.timestamp()) 34 | except Exception as e: 35 | print("invalid timestamp: %s" % str(e)) 36 | sys.exit() 37 | else: 38 | timestamp = None 39 | 40 | result = emailrep.report(email, tags, args.description, timestamp, args.expires) 41 | if result.get("status") and result["status"] == "success": 42 | print("Successfully reported %s" % email) 43 | else: 44 | print("Failed to report %s. Reason: %s" % (email, result["reason"])) 45 | 46 | except Exception as e: 47 | print(e) 48 | 49 | if __name__ == "__main__": 50 | main() 51 | -------------------------------------------------------------------------------- /emailrep/__init__.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | BASE_URL = "https://emailrep.io" 4 | 5 | 6 | class EmailRep(): 7 | QUERY = "QUERY" 8 | REPORT = "REPORT" 9 | 10 | def __init__(self, key=None, proxy=None): 11 | self.base_url = BASE_URL 12 | self.headers = {} 13 | self.version = "0.0.5" 14 | self.headers["User-Agent"] = "python/emailrep.io v%s" % self.version 15 | self.headers["Content-Type"] = "application/json" 16 | 17 | if key: 18 | self.headers["Key"] = key 19 | self.session = requests.Session() 20 | if proxy: 21 | self.session.proxies = {"https": "{}".format(proxy)} 22 | 23 | self.banner = """ 24 | ____ _ _____ 25 | / __/_ _ ___ _(_) / _ \___ ___ 26 | / _// ' \/ _ `/ / / , _/ -_) _ \\ 27 | /___/_/_/_/\_,_/_/_/_/|_|\__/ .__/ 28 | /_/ 29 | """ 30 | 31 | def query(self, email): 32 | url = "{}/{}?summary=true".format(self.base_url, email) 33 | 34 | result = self.session.get(url, headers=self.headers) 35 | return result.json() 36 | 37 | def report(self, email, tags, description=None, timestamp=None, expires=None): 38 | url = self.base_url + "/report" 39 | params = {} 40 | params["email"] = email 41 | params["tags"] = tags 42 | 43 | if description: 44 | params["description"] = description 45 | 46 | if timestamp: 47 | params["timestamp"] = timestamp 48 | 49 | if expires: 50 | params["expires"] = expires 51 | 52 | result = self.session.post(url, json=params, headers=self.headers) 53 | return result.json() 54 | 55 | def format_query_output(self, result): 56 | print(self.banner) 57 | print("Email address: %s\n" % result["email"]) 58 | print("\033[91mRISKY\033[0m\n") if result["suspicious"] else None 59 | print(result["summary"]) 60 | -------------------------------------------------------------------------------- /emailrep/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import argparse 4 | from argparse import Namespace 5 | from configparser import ConfigParser 6 | 7 | from emailrep import EmailRep 8 | 9 | CONF_PATH = os.path.expanduser("~/.config/sublime") 10 | CONF_FILE = os.path.join(CONF_PATH, "setup.cfg") 11 | CONF_DEFAULTS = {"emailrep": {"key": ""}} 12 | 13 | 14 | def parse_args(): 15 | parser = argparse.ArgumentParser( 16 | prog='emailrep', 17 | formatter_class=argparse.RawDescriptionHelpFormatter, 18 | description=""" 19 | EmailRep - emailrep.io command line interface 20 | 21 | Query an email address: 22 | -------------------- 23 | emailrep foo@bar.com 24 | 25 | Report an email address: 26 | -------------------- 27 | emailrep --report foo@bar.com --tags "bec, maldoc" --description "Contact impersonation to CEO" 28 | 29 | Setup your API key: 30 | -------------------- 31 | emailrep setup -k 32 | 33 | """) 34 | 35 | # this allows us to parse the email address to query as a positional argument 36 | parser.add_argument('query', nargs='?') 37 | parser.add_argument('-r', '--report', help='Email address to report', 38 | action='store', dest='report', type=str, required=False) 39 | parser.add_argument('--tags', help='Tags that should be applied', 40 | action='store', dest='tags', type=str, required=False) 41 | parser.add_argument('--description', help='Additional information and context', 42 | action='store', dest='description', type=str, required=False) 43 | parser.add_argument('--timestamp', help=( 44 | 'When this activity occurred as a string, defaults to now(). ' 45 | 'Example: "Sun Aug 18 22:51:32 EDT 2019" or "08/18/2019 22:51:32 EDT"' 46 | ), action='store', dest='timestamp', type=str, required=False) 47 | parser.add_argument('--expires', help=( 48 | 'Number of hours the email should be considered risky' 49 | ), action='store', dest='expires', type=int, required=False) 50 | parser.add_argument('--proxy', help=( 51 | 'Proxy to use for requests. Example: "socks5://10.10.10.10:8000"' 52 | ), action='store', dest='proxy', type=str, required=False), 53 | 54 | args, unknown = parser.parse_known_args() 55 | 56 | if len(sys.argv) <= 1: 57 | parser.print_help() 58 | sys.exit() 59 | 60 | elif sys.argv[1] == "setup": 61 | setup() 62 | 63 | elif not args.report: 64 | return (EmailRep.QUERY, Namespace(query=sys.argv[1]), args.proxy) 65 | 66 | else: 67 | if not args.report or not args.tags: 68 | print("--report and --tags are required for reporting email addresses") 69 | sys.exit() 70 | return (EmailRep.REPORT, args, args.proxy) 71 | 72 | 73 | def setup(): 74 | if len(sys.argv) == 4 and sys.argv[2] == "-k": 75 | if not os.path.isfile(CONF_FILE): 76 | if not os.path.exists(CONF_PATH): 77 | os.makedirs(CONF_PATH) 78 | 79 | config = ConfigParser() 80 | config["emailrep"] = {} 81 | config["emailrep"]["key"] = sys.argv[3] 82 | with open(CONF_FILE, "w") as f: 83 | config.write(f) 84 | print("Success! ~/.config/sublime/setup.cfg file generated.") 85 | sys.exit() 86 | else: 87 | print( 88 | "Setup requires an API key.\n" 89 | "Usage: emailrep setup -k " 90 | ) 91 | sys.exit() 92 | 93 | 94 | def load_config(): 95 | config = ConfigParser() 96 | if not os.path.isfile(CONF_FILE): 97 | config.read_dict(CONF_DEFAULTS) 98 | return config 99 | 100 | config.read(CONF_FILE) 101 | return config 102 | --------------------------------------------------------------------------------