Coordinated Disclosure Timeline

Summary

There is a potential type confusion vulnerability in the varlink interface of systemd-resolved. This is due to the userdata field of the Varlink struct being used to store two unrelated datatypes: Manager and DnsQuery.

Product

systemd-resolved

Tested Version

systemd v247.3-1 (tested on Arch Linux)

Note: the new varlink interface to systemd-resolved has only been added quite recently. Many Linux distributions ship with older versions of systemd which do not include the new interface. However, Arch Linux ships with v247, which includes it.

Details

The Varlink struct has a void* field named userdata. The varlink interface of systemd-resolved initially uses this field to store a pointer to the Manager object (resolved-varlink.c, line 515):

varlink_server_set_userdata(s, m);

But, later, the userdata field is used to store a pointer to the DnsQuery object (resolved-varlink.c, line 318 and resolved-varlink.c, line 485):

varlink_set_userdata(link, q);

It looks like the code was written with the assumption that the Varlink object will only survive for the duration of single request, so the change of the type of the userdata field won’t cause a problem. However, if two varlink requests are received in quick succession then the same Varlink object is reused, potentially leading to a type confusion vulnerability.

To reproduce the issue, please build the attached proof-of-concept as follows:

gcc varlink_resolve.c -o varlink_resolve

You might also need to start systemd-resolved if it isn’t already running:

sudo systemctl start systemd-resolved

The PoC has two modes, to test the io.systemd.Resolve.ResolveHostname and io.systemd.Resolve.ResolveAddress interfaces, respectively:

./varlink_resolve hostname www.github.com
./varlink_resolve address 127.0.0.1

Both those commands cause systemd-resolved to crash.

In our testing, systemd-resolved always crashes due to the userdata field containing a NULL pointer, which triggers an assertion failure at resolved-varlink.c, line 277 or resolved-varlink.c, line 455. We are not sure why we have never seen a crash due to the userdata field containing a pointer to a DnsQuery object. The code is invoked by an epoll event handler, which suggests that the behavior should be non-deterministic, based on the order in which the events are received. If the events are handled in a different order then we believe it will lead to a type confusion rather than a NULL pointer error.

Impact

This issue may lead to a memory corruption, which could enable a local attacker to gain code execution as the systemd-resolve user.

Credit

This issue was discovered and reported by GHSL team member @kevinbackhouse (Kevin Backhouse).

Contact

You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2021-049 in any communication regarding this issue.