Skip to content

Instantly share code, notes, and snippets.

@tsundokul
Created December 9, 2018 21:19
Show Gist options
  • Save tsundokul/afa28426a37b2314212330b6b4f3abca to your computer and use it in GitHub Desktop.
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.
#!/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 user@8.8.8.8: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