xfce4-screensaver 4.20.2: Lock Screen Bypass via Daemon Crash (Rapid Keystroke Trigger)

Summary

xfce4-screensaver 4.20.2 can be crashed by rapidly pressing Fn+Super (Windows key) while the lock screen is active. The daemon segfaults due to a use-after-free in gs-manager.c:recreate_windows(), causing the lock screen to disappear entirely and granting full desktop access without authentication.

This is a different attack vector from #187 (closed) (which required HDMI cable manipulation). This vulnerability requires only a keyboard and is reproducible by hand in under 5 seconds.

Affected Versions

  • Confirmed vulnerable: 4.20.2 (current default on Kali Linux 2025.4, Debian testing)
  • Not affected: 4.20.1
  • Fix status: The patch merged for #187 (closed) (commit 4436087c, "manager: Fix keyboard leak when recreating windows") does not prevent this crash. When that patch is applied, the screensaver instead enters a state where the password field is unresponsive (blank screen, no keyboard input accepted), requiring a hard reboot — consistent with the regression reported by @MuratAWARE7 in #187 (closed).

Reproduction Steps

Environment: Any X11 session running xfce4-screensaver 4.20.2 (tested on Kali Linux 2025.4, kernel 6.18.5).

  1. Lock the screen
  2. Rapidly press Fn+Super (the Windows/Meta key combined with the Fn key) repeatedly for 3–5 seconds
  3. The lock screen disappears. The desktop is fully accessible without entering a password.

No external monitor, USB device, or special hardware is required. A standard laptop keyboard is sufficient.

Verification

After the bypass, the following confirms the daemon is dead:

$ xfce4-screensaver-command --query
xfce4-screensaver-Message: Screensaver is not running!

$ dbus-send --session --dest=org.xfce.ScreenSaver --print-reply \
    /org/xfce/ScreenSaver org.xfce.ScreenSaver.GetActive
boolean false

Kernel log shows the segfault:

kernel: xfce4-screensav[PID]: segfault at 8 ip 00007f63fa3fd678 sp 00007fff24297eb0 error 4 in libgtk-3.so.0.2419.32

or:

kernel: xfce4-screensav[PID]: segfault at 1000000002 ip 0000001000000002 sp 00007ffc21a2b588 error 14

Root Cause Analysis

Regression introduced by the overlay refactor

The bug was introduced by two commits in 4.20.2:

  1. "manager: Use only one permanent window as overlay" — replaced per-monitor temporary overlays (GList *overlays) with a single persistent popup window (GtkWidget *overlay)
  2. "manager: Grab devices on overlay when recreating windows" — simplified recreate_windows() to a direct function call

What was removed

In 4.20.1, the add_overlays() function contained explicit safety logic that was deleted in the refactor:

/* 4.20.1: add_overlays() — DELETED in 4.20.2 */


g_hash_table_iter_init (&iter, manager->priv->windows);
while (g_hash_table_iter_next (&iter, NULL, &window)) {
    g_signal_handlers_disconnect_by_func (window, remove_overlays, manager);
    disconnect_window_signals (manager, window);  /* ← disconnects before destroy */
}
/* we need to disconnect_window_signals() right now above to avoid an X11 error that looks


 * like a bug in GtkSocket/GtkPlug, so we also need to reset this flag just in case */
manager->priv->dialog_up = FALSE;  /* ← resets dialog state */

In 4.20.2, recreate_windows() destroys all windows without these protections:

/* 4.20.2: recreate_windows() — no signal disconnection, no dialog_up reset */


g_hash_table_remove_all (manager->priv->jobs);
g_hash_table_remove_all (manager->priv->windows);  /* ← destroys with signals still connected */

Crash mechanism

  1. Screen is locked. The dialog process (xfce4-screensaver-dialog) is running, dialog_up = TRUE, with signal handlers connected to the GSWindow widgets (including window_dialog_up_changed_cb, window_grab_broken_cb, etc.)
  2. Rapid Fn+Super keypresses generate X11 events that cause the screensaver dialog to be killed and respawned in quick succession.
  3. dialog_process_watch() detects the dialog dying (G_IO_HUP) and calls popdown_dialog()gs_window_dialog_finish().
  4. Simultaneously or shortly after, g_hash_table_remove_all(manager->priv->windows) fires gs_window_destroy() on each window via the hash table's GDestroyNotify callback.
  5. gs_window_destroy() calls gs_window_cancel_unlock_request()popdown_dialog() on a window that is being destroyed, while signal handlers still reference GTK widgets that are being torn down.
  6. The still-connected signal handlers fire during GTK's event loop drain, accessing freed widget memory.
  7. Segfault: depending on heap state, this manifests as either:
    • NULL + offset dereference (error 4) inside gtk_widget_set_parent — widget pointer was zeroed after free
    • Wild function pointer jump (error 14) to addresses like 0x1000000002 / 0x1500000002 — vtable pointer overwritten by heap reuse, low bytes 0x02 consistent with a small integer overwriting what was a valid code pointer

Crash Evidence

Crash — xfce4-screensaver 4.20.2 (Kali Linux)

xfce4-screensaver-dialog[58677]: pam_unix(xfce4-screensaver:auth): conversation failed
kernel: xfce4-screensav[58109]: segfault at 1000000002 ip 0000001000000002 sp 00007ffc21a2b588 error 14
  • Instruction fetch from corrupted address 0x1000000002
  • Heap reuse overwrote vtable pointer with small integer data

Proposed Fix

Restore the safety guards from 4.20.1's add_overlays() into 4.20.2's recreate_windows():

static void
recreate_windows (GSManager *manager) {
    GdkDisplay *display = gdk_display_get_default ();
    guint n_monitors = gdk_display_get_n_monitors (display);
+   GHashTableIter iter;
+   gpointer window;

    gs_debug ("Reconfiguring monitors, recreating windows");

+   /* Disconnect window signals before destroying to prevent use-after-free
+    * via GtkSocket/GtkPlug callbacks during teardown (see 4.20.1 add_overlays) */
+   g_hash_table_iter_init (&iter, manager->priv->windows);
+   while (g_hash_table_iter_next (&iter, NULL, &window)) {
+       disconnect_window_signals (manager, window);
+   }
+   manager->priv->dialog_up = FALSE;

#ifdef ENABLE_X11
    if (manager->priv->grab != NULL) {
        gs_grab_move_to_window (manager->priv->grab,
                                gtk_widget_get_window (manager->priv->overlay),
                                display, FALSE, FALSE);
    }
#endif

    g_hash_table_remove_all (manager->priv->jobs);
    /* ... rest unchanged ... */

This is the minimal fix. The XSetInputFocus approach from commit 4436087c addresses a different symptom (keyboard leak) but does not prevent the daemon crash, and introduces its own regression (unresponsive password field after monitor changes).

Severity Assessment

CVSS:3.1 — AV:P/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H — 6.8 (Medium)

  • Attack Vector: Physical — requires physical access to the keyboard
  • Attack Complexity: Low — reproducible by hand in under 5 seconds, no special tools needed
  • Privileges Required: None — works against any locked session
  • User Interaction: None — no action needed from the logged-in user
  • Impact: High across CIA — full desktop access including files, browser sessions, credentials, and the ability to execute arbitrary commands as the locked-in user

Relationship to Existing Issues

  • #187 (closed) (XFCE4-Screensaver <= 4.20.2 Login Bypass Vulnerability) — same vulnerable code path (recreate_windows()), different trigger (HDMI vs keyboard), different failure mode (keyboard leak vs daemon crash). The fix merged for #187 (closed) does not prevent this crash.