NimbusPwn, a CLFS Vulnerability, and DatAFLow
Blogpost by Microsoft that details a few vulnerabilities in the
networkd-dispatcher component in
systemd which can be chained for LPE. When looking at the code flow, they noticed it would register a signal receiver on the system bus, and the handler would receive a
state path followed by some data. If this state path was an existing object, and the data was different from the previously stored data,
run_hooks_for_state() would be invoked. This routine will call all root-owned scripts in that state’s directory, which gets built up as
Multiple issues exist here. First of all, this path construction is vulnerable to directory traversal, as the
state is not sanitized. Furthermore, symbolic links are followed, both when checking the script files to load them into the list, as well as when executing them with
subprocess. This allows a Time-of-Check Time-of-Use (TOCTOU) bug to open up, as an attacker can traverse out of the protected directory and setup symbolic links. First, they set the link to a valid root-owned script to get the symbolic link loaded into the scripts list, then they change it to a script they control when subprocess goes to execute it.
Memory corruption issue in CLFS. The issue comes down to the parsing of log blocks when loading log files from disk.
The main structure that make up a log block is the base block, which consists of a log block header of metadata followed by record entries. These records have a two-byte signature attached to them, which is referenced when decoding the block for consistency guarantees. Records also have a header which contains an array of offsets to associated contexts (or “containers”) for that record. Contexts have some sensitive fields, namely
pContainer, which is a kernel pointer that points to the parent container class for that context. As such, care has to be taken when loading files from disk - this field must be zero’d, otherwise the user could control a kernel pointer which contains function pointers that get called in
RemoveContainer() later on.
The problem is the lack of validation on the signature offset that gets read from the block header. It’s possible to set this signature offset to intersect with the context object, specifically that
pContainer field. When decoded, this will give a user the ability to corrupt
pContainer after it had been zero’d, allowing them direct control over the
destroy function pointers that are called in
An uninitialized pointer is freed by proving a malformed IOCA file with a
size_Y of zero. What happens normally is that there is an initialization routine that iterates from
size_Y to 0. Initializing the
table_mys_rgb table. Then later during the cleanup routine, in
delete_table_mys_rgb_ptr it’ll call the delete operator which leads to a free on an index into the table. When you provide a file with
0, the initialization loop never assigns a pointer here. Leading to an arbitrary free.