One-day at a time: From security advisory to functional exploit
When hearing the term “exploit development”, your first instinct may be to recoil in horror, recalling news stories of nefarious hackers breaking into systems and stealing from the innocent. However, just as a lock pick can aid both a thief and a locksmith, exploit development is a skill that can be used for both ethical and unethical purposes.Imagine if it was only attackers that knew how to create exploits – how would defenders ever hope to recognize what they’re fighting against? Cybersecurity is an arms race, and the ability to defend against exploits often comes from learning how to create them in the first place. Indeed, there’s a whole industry of ethical exploit developers researching some of the biggest targets – such as desktop operating systems, mobile devices, and even the software running electric cars – all to make sure that vulnerabilities can be patched up before attackers get their hands on them.Understanding one-days
In this blog, we’ll explore a type of vulnerability that’s often overlooked – one-days, also referred to as N-days. “One-days”? I’ve heard of zero-days, is this some kind of disappointing sequel?”, I hear you ask. Well, while zero-day and one-day exploits are related, the ways they’re discovered and developed are different, and both have their own uses.A zero-day vulnerability is one that vendors have “zero days” to patch, usually because the third party that discovered the vulnerability failed to disclose it. This means zero-days can be exploited by malicious actors before any kind of fix is available. These are (in most cases) the vulnerabilities that get all the hype, due to their potentially devastating impact.So, you might be able to see where the name “one-day” comes from – in contrast to zero-days, vendors have had time to react and release a patch. While this may make it seem like one-days are about as useful as a chocolate teapot, it’s important to remember that not all users keep their software fully updated, so an exploit developed for a one-day vulnerability may still have some serious implications if left in the wrong hands (look at ZeroLogon or EternalBlue, for example).In this case, though, let’s assume our one-days will be used for ethical purposes. We’ll be looking at the development of a one-day exploit for the Nimbuspwn vulnerability, disclosed by Microsoft in late April 2022. An advisory was released describing the root cause of this vulnerability, alongside some exploitation techniques; however, no public exploit script was provided, and key implementation details needed to create one were missing.The CTI team at Immersive Labs worked on creating an exploit script for this vulnerability, alongside some useful tools for blue teamers to help detect if the vulnerability had been exploited on their systems. Read on to discover the thought process and skills used to take the original Microsoft advisory and turn it into a fully functioning exploit script (tested on Ubuntu 20.04) within 24 hours of public disclosure.Primary sources: Your best friend
When developing a one-day, it can be tempting to trigger vulnerable code paths, map out an exploit path, or even start writing an exploit script. Time is a key factor in writing one-days, and it’s natural to want to rush into the part that feels most productive. However, it’s usually best to slow down and focus on the primary sources you have about the vulnerability first.Think about it – in most cases, these advisories/blogs/threads are the only concrete information you have about how the vulnerability works, and they’re often written by those who found the vulnerabilities and developed the exploits in the first place. What better resource are you going to find than that?In this instance, the primary source for the vulnerability was the advisory released by Microsoft. Thankfully, this advisory went into quite a bit of detail on how the vulnerability works and what kind of exploitation path is required to achieve local privilege escalation. This provided a strong base from which to start developing a PoC.From an initial read of the advisory, we were able to gather some key information to take into the next stage:- The vulnerability is found within networkd-dispatcher, a dispatcher daemon for systemd.
- This daemon works across D-Bus, an inter-process communication (IPC) mechanism found on many Unix operating systems.
- Sending a specific signal across the bus allows us to hit the vulnerable propertiesChanged code path within networkd-dispatcher.
- To fully realize the exploit, we need to chain together multiple vulnerabilities (a race condition, a symlink race, and a path traversal).
Understanding your target
As mentioned above, it’s important to take things slowly to avoid wasting time later on. This is especially important when developing quick-response exploits for lesser-used software, as there may be some small quirks within the mechanism that, if overlooked, can prevent you from manipulating it.At Immersive Labs, our biggest problem was understanding how the D-Bus mechanism works; at least well enough to successfully exploit the vulnerability. IPC can be a complex topic, so research is vital.Thankfully, almost all popular applications of D-Bus are open source, so there’s a lot of documentation on how the mechanism can be implemented, as well as how to interact with it. The version of D-Bus used on Ubuntu and many other Linux-based operating systems is dbus, developed by the freedesktop.org project. This includes the daemon dbus-daemon and the reference implementation, libdbus. By reading the documentation alongside some other sources, we can gather a general overview of how D-Bus operates.D-Bus aims to improve IPC by allowing processes to communicate across a shared bus, rather than relying on one process directly communicating with another. This provides a more standardized way of processing the sharing of information, making it more accessible through language-specific bindings abstracting lower-level functionality.Most instances of D-Bus make use of two buses:- The system bus, which is essentially a global bus used by any system services and daemons.
- The session bus, which is unique to one user and is mostly used by user-specific applications.
- The object path, which is used by dbus to refer to object instances (not too important for our purposes as long as we have a valid object path, as seen later).
- The name of the signal, which designates the signal receivers that will handle this signal. In this instance, we want the signal name PropertiesChanged (Fig. 1, line 255).
- The signal interface, which is a named group of methods and signals.
- Any potential parameters to go along with the signal (we’ll be using some of these in the exploit).

Crafting an environment
One way we can cut down on the time spent debugging is by setting up a suitable development environment for our exploit. What this environment consists of will vary depending on the exploit being developed, but in our case there are two main things we want to be keeping track of:- What kind of data is being sent across the bus itself, so we can spot anything unexpected.
- What’s happening within the networkd-dispatcher script, allowing us to see which code paths we’re hitting and enter debug statements where necessary.
If at first you don’t succeed, try, try again
Developing exploits is an iterative process. It’s unlikely that the first exploit script you write (or even the first step of the script) will work as intended; it’s more likely that a functioning exploit will require dozens of small tweaks, tests, and changes.As a result, it’s generally a good idea to break the exploitation process into smaller stages, ensuring that each one is working as expected before moving on to the next. In this case, we know the first (and likely one of the most important) steps is getting the vulnerable function in networkd-dispatcher to receive one of our signals.Then comes the next key question: what are we going to use to actually send a signal? This took our team at Immersive Labs a surprisingly long time to figure out when originally developing the exploit, with attempts including:While these were valiant attempts and not wholly unsuccessful (for example, using the C bindings allowed for successful connection to the bus), they all either felt too complex for our purposes, or not configurable enough. This can be a difficult balance to strike, and depends on the task at hand. For example, when interacting with lower-level protocols, it’s possible that you may need to write your own connectors or client software to send data in the way you want.Taking a step back and re-reading the advisory, a keen eye will spot that the exploit script demonstrated in the latter half is written in Python. Some further digging on the dbus bindings for Python will lead to this beautiful example, succinctly demonstrating all the code needed to connect to a bus and emit a signal.Using this as a boilerplate, we can make some tweaks to try and send a PropertiesChanged signal over the org.freedesktop.network1 bus. Changing the signal and bus names, as well as inserting the three parameters it’s expecting gives us the following code:



- Lines 396-399: The first parameter typ is checked against a constant string value, returning from the function if there is a mismatch.
- Lines 400-403: The fourth parameter path (the object path sent with the signal) is again checked against a constant string, making sure that the parameter begins with /org/freedesktop/network1/link/_.
- Lines 407-422: The path parameter has the data after the above string constant checked, expecting a number idx. This number is then checked against the self.iface_names_by_idx list, eventually logging an error and returning if not found.
- Lines 424-425: The second parameter data is treated as a dict, and the script attempts to retrieve the OperationalState and AdministrativeState values from it.
- Lines 427-428: The OperationalState and AdministrativeState values are checked for validity, and if at least one of them contains a value then we call the target function handle_state.

- We take the two digits found after the _, such as 99 in /org/freedesktop/network1/link/_99.
- These two digits are cast to a base-16 integer, so 99 would be converted to 0x63.
- This base 16 integer is then converted to a character using the chr function, which converts an ASCII value to its equivalent character.
- This character is then cast back to an integer, and this integer is the final index used.


Developing the exploit
Believe it or not, that’s the hardest part over for this vulnerability. Exploiting the vulnerabilities themselves is relatively straightforward, with the most difficult bit being the process of reaching the vulnerable function. The vulnerabilities themselves aren’t trivial, but the wealth of information on the exploitation process within the original advisory certainly helps to smooth out the process.Let’s have a recap of the Exploitation section of the advisory, which outlines the steps needed to execute arbitrary code as root:- Set up a directory inside a writable folder such as /tmp/nimbuspwn, then insert a symlink to /sbin inside this folder called /tmp/nimbuspwn/poc.d.
- For each root-owned executable in /sbin, create a payload file with the same name as the executable inside /tmp/nimbuspwn.
- Broadcast a signal with an OperationalState value using directory traversal to target your directory, for example ../../../tmp/nimbuspwn/poc.
- Quickly exploit the race condition and change the /tmp/nimbuspwn/poc.d symlink to target /tmp/nimbuspwn.






Disclosing responsibly
Now that we have an exploit script for a recently disclosed vulnerability that’s likely to be unpatched on many machines, the obvious step is to post it on Twitter with some cool hashtags, right?Unless you have a personal vendetta against blue teamers, this may not be the best way to release a one-day exploit script. Instead, consider the following points before releasing an exploit PoC to the public:- What is the severity of the vulnerability/impact of the exploit?
- How likely is it that vulnerable instances have been patched?
- Will blue teamers be able to quickly react to any malicious use of the exploit? (e.g., are we dropping this on a Friday at 4:59pm?)
- Are there other public exploits already available, or is this the first one?