Created
December 9, 2018 21:19
-
-
Save tsundokul/afa28426a37b2314212330b6b4f3abca to your computer and use it in GitHub Desktop.
The script downloads gmail attachments to a SFTP location. -h for more details.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# First install the required modules (Python 3) | |
# pip3 install paramiko eml-parser | |
import base64 | |
import email | |
import getpass | |
import imaplib | |
import os | |
import paramiko | |
import re | |
import sys | |
import smtplib | |
import time | |
from eml_parser import eml_parser | |
from optparse import OptionParser | |
from pprint import pprint | |
SMTP_SERVER = "imap.gmail.com" | |
FROM_EMAIL = "" | |
# Get an app password from; this is not your account's password | |
# https://myaccount.google.com/apppasswords | |
FROM_PWD = "" | |
def _print(*args): | |
print('[+]', *args) | |
def repl_diacritics(text): | |
replaces = { 'ă': 'a', 'î': 'i', 'ş': 's', 'ţ': 't', 'â': 'a' } | |
for i, j in replaces.items(): | |
text = text.replace(i, j) | |
return text | |
def load_filters(filter_list): | |
clean_filters = [] | |
for filter_kw in filter_list: | |
filter_kw = repl_diacritics(filter_kw).lower() | |
clean_filters.append(filter_kw) | |
return clean_filters | |
def dump_attachment(data, filename, sftp, path): | |
fullpath = os.path.join(path, filename) | |
try: | |
sftp.stat(fullpath) | |
_print("Attachment {} already present on disk".\ | |
format(filename)) | |
except IOError: | |
_print("Saving to disk", filename) | |
binary_data = base64.b64decode(data) | |
file = sftp.open(fullpath, 'wb') | |
file.write(binary_data) | |
file.close() | |
def process_emails(filter_list, sftp, dump_loc): | |
mail = imaplib.IMAP4_SSL(SMTP_SERVER) | |
mail.login(FROM_EMAIL,FROM_PWD) | |
mail.select('inbox') | |
typ, data = mail.search(None, 'ALL') | |
mail_ids = data[0] | |
id_list = mail_ids.split() | |
first_email_id = int(id_list[0]) | |
latest_email_id = int(id_list[-1]) | |
# Iterte through emails, latest first | |
for i in range(latest_email_id,first_email_id, -1): | |
_, data = mail.fetch(str(i), '(RFC822)' ) | |
for response_part in data: | |
if isinstance(response_part, tuple): | |
email = eml_parser.decode_email_b(response_part[1], | |
include_attachment_data=True, include_raw_body=True) | |
# Skip if email has no attachments | |
if not 'attachment' in email: continue | |
has_att = False | |
try: | |
for att in email['attachment']: | |
if att['content_header']['content-disposition'][0] != 'inline': has_att = True | |
if not has_att: continue | |
except KeyError as e: | |
print(str(e)) | |
pprint(email['attachment']); exit() | |
# Check for keywords in body, from, subject and attachment names | |
if len(filter_list): | |
valid = False | |
to_search = [] | |
try: | |
to_search.append(email['body'][0]['content']) | |
except IndexError: pass | |
try: | |
to_search.append(email['header']['from']) | |
except IndexError: pass | |
try: | |
to_search.append(email['header']['subject']) | |
except IndexError: pass | |
for att in email['attachment']: | |
to_search.append(att['filename']) | |
for field in to_search: | |
if valid: break | |
field = field.lower() | |
for kw in filter_list: | |
if kw in field: | |
valid = True | |
break | |
# Skip email if no keywords are found | |
if not valid: continue | |
# Go ahead and dump attachments | |
for att in email['attachment']: | |
if att['content_header']['content-disposition'][0] == 'inline': continue | |
dump_attachment(att['raw'], att['filename'],sftp, dump_loc) | |
def argument_parser(): | |
parser = OptionParser(usage='usage: %prog [options] keyword keyword [...]') | |
parser.add_option('-s', '--ssh', | |
action='store', | |
dest='ssh_s', | |
help='SSH connection string. Ex [email protected]:22') | |
parser.add_option('-d', '--dump-dir', | |
action='store', | |
dest='dump_loc', | |
default='/tmp', | |
help='The fullpat on the remote host where the'\ | |
' attachments are to be saved. Defults to /tmp') | |
return parser.parse_args() | |
if __name__ == '__main__': | |
ARG = argument_parser() | |
if not ARG[0].ssh_s: | |
sys.exit("[!] A SSH connection string is required.") | |
DUMP_LOCATION = ARG[0].dump_loc | |
FILTERS = ARG[1] | |
SSH = re.findall(r'(\w+)@(.+):(\d+)', ARG[0].ssh_s)[0] | |
if not len(FILTERS): | |
print("[!] No filter applied. All attachments will be downloaded.") | |
print("[+] Atatachments will be saved in", DUMP_LOCATION) | |
# Set-up SFTP object | |
ssh = paramiko.SSHClient() | |
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) | |
try: | |
ssh.connect(SSH[1], int(SSH[2]), SSH[0], | |
password=getpass.getpass('[+] SSH Password: ')) | |
sftp = ssh.open_sftp() | |
sftp.chdir(DUMP_LOCATION) | |
filter_list = load_filters(FILTERS) | |
process_emails(filter_list, sftp, DUMP_LOCATION) | |
ssh.close() | |
except paramiko.SSHException: | |
print("[+] SSH/SFTP Connection Error. Please check your connection string.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment