Skip to content

Instantly share code, notes, and snippets.

@monperrus
Created May 25, 2026 06:43
Show Gist options
  • Select an option

  • Save monperrus/03fdfc1eca526a77ac81cb166847a945 to your computer and use it in GitHub Desktop.

Select an option

Save monperrus/03fdfc1eca526a77ac81cb166847a945 to your computer and use it in GitHub Desktop.
Securing the Linux Secret Service API against Enumeration Attacks

Securing the Linux Secret Service API against Enumeration Attacks

Background

The Secret Service API is a D-Bus interface (org.freedesktop.secrets) implemented by GNOME Keyring. Applications use it to store and retrieve credentials. Once your desktop session is unlocked (at login), any process running as your user can talk to this interface without further authentication.

Threat model

The Threat: a one-liner can enumerate every credential stored in the keyring — service names, usernames, and secrets — with no knowledge of what is stored there.

import secretstorage
bus = secretstorage.dbus_init()
for c in secretstorage.get_all_collections(bus):
    c.unlock()
    for item in c.get_all_items():
        print(item.get_label(), item.get_attributes())

In scope: malicious or compromised code running in your login session — a rogue script, a supply-chain-compromised CLI tool, a malicious Python package, etc.

Out of scope: an attacker with physical access or root; an attacker who can read ~/.local/share/keyrings/ directly (the files are encrypted with your login password, but if the session is already unlocked the daemon will serve secrets anyway).

Attack surface

The Secret Service API exposes several enumeration vectors:

Method Interface What it returns Blocked?
SearchItems({}) Secret.Service All item paths (broad search) No
SearchItems({}) Secret.Collection All item paths in a collection No
Properties.Get('Items') DBus.Properties All item paths in a collection Partial*
Properties.GetAll DBus.Properties All properties incl. item list Yes

D-Bus policy rules

D-Bus policy rules is very weak to protect against this threat model.

One can write a file ~/.config/dbus-1/session.d/block-keyring-enum.conf to block Properties.GetAll on org.freedesktop.secrets. This prevents the single-call "give me every property of every collection" attack while leaving targeted lookups intact.

What this stops: an attacker calling GetAll on a collection to retrieve its full item list and all metadata in one call.

What this does not stop: an attacker who knows the D-Bus API well enough to call SearchItems({}) or Properties.Get('Items') directly.

Why SearchItems cannot be selectively blocked

D-Bus policy rules operate at the method level — they cannot inspect message body arguments. It is therefore impossible to distinguish:

  • SearchItems({}) — enumerate everything (attack)
  • SearchItems({'service': 'github.com', 'username': 'martin'}) — targeted lookup (legitimate)

Blocking SearchItems entirely breaks applications that use libsecret for credential lookup (browsers, GNOME apps, custom scripts using keyring or secretstorage).

Why Properties.Get cannot be selectively blocked

The same argument applies: Properties.Get('Items') (lists all items — attack) and Properties.Get('Label') (reads a collection name — used by secretstorage during init) are the same D-Bus method call. Blocking it breaks the keyring Python library and any app that reads collection metadata.

Remaining vectors and mitigations

Vector Mitigation
Brute-force object paths via GetSecret Paths are sequential integers — low effort for an attacker with session access
Read keyring files directly Protected by filesystem permissions; moot if session is unlocked

Alternatives

D-Bus filtering proxy: A userspace proxy (e.g. using dbus-broker with Lua policy, or a custom proxy daemon) can inspect message body arguments and allow SearchItems only when called with non-empty attributes. This is the only way to fully close the remaining vectors while keeping apps functional. Significantly more complex to set up.

Appendix

Implementation of the policy rule

As root, create /etc/dbus-1/session-local.conf (world-readable, mode 644):

<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
  <includedir>/home/YOUR_USER/.config/dbus-1/session.d</includedir>
</busconfig>

This hooks your user-writable ~/.config/dbus-1/session.d/ into the session bus policy load path. Replace YOUR_USER with your username (tilde and ${HOME} are not expanded by dbus-daemon in <includedir>).

Content of ~/.config/dbus-1/session.d/block-keyring-enum.conf

<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
  <policy context="default">
    <deny send_destination="org.freedesktop.secrets"
          send_interface="org.freedesktop.DBus.Properties"
          send_member="GetAll"/>
  </policy>
</busconfig>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment