<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://security.opensuse.org/feed.xml" rel="self" type="application/atom+xml" /><link href="https://security.opensuse.org/" rel="alternate" type="text/html" /><updated>2026-08-13T20:07:32+00:00</updated><id>https://security.opensuse.org/feed.xml</id><title type="html">SUSE Security Team Blog</title><subtitle>Open Source vulnerability reports and code review results.</subtitle><entry><title type="html">PortProtonQt: Custom Polkit Rule Allows Escalation of NetworkManager and UDisks2 Privileges (CVE-2026-59678)</title><link href="https://security.opensuse.org/2026/07/22/port-proton-qt-polkit-rules.html" rel="alternate" type="text/html" title="PortProtonQt: Custom Polkit Rule Allows Escalation of NetworkManager and UDisks2 Privileges (CVE-2026-59678)" /><published>2026-07-22T00:00:00+00:00</published><updated>2026-07-22T00:00:00+00:00</updated><id>https://security.opensuse.org/2026/07/22/port-proton-qt-polkit-rules</id><content type="html" xml:base="https://security.opensuse.org/2026/07/22/port-proton-qt-polkit-rules.html"><![CDATA[<h1 class="no_toc" id="table-of-contents">Table of Contents</h1>

<ul id="markdown-toc">
  <li><a href="#1-introduction" id="markdown-toc-1-introduction">1) Introduction</a></li>
  <li><a href="#2-insecure-polkit-rule" id="markdown-toc-2-insecure-polkit-rule">2) Insecure Polkit Rule</a></li>
  <li><a href="#3-bugfix" id="markdown-toc-3-bugfix">3) Bugfix</a></li>
  <li><a href="#4-cve-assignment" id="markdown-toc-4-cve-assignment">4) CVE Assignment</a></li>
  <li><a href="#5-timeline" id="markdown-toc-5-timeline">5) Timeline</a></li>
  <li><a href="#6-references" id="markdown-toc-6-references">6) References</a></li>
</ul>

<h1 id="1-introduction">1) Introduction</h1>

<p><a href="https://github.com/Boria138/PortProtonQt">PortProtonQt</a> is a GUI application to simplify launching of Windows
games on Linux. A <a href="https://bugzilla.suse.com/show_bug.cgi?id=1270218">review of a custom Polkit rule</a> installed
by PortProtonQt uncovered an attack vector which allows arbitrary local users
to modify NetworkManager connections or UDisks2 mounts.</p>

<p>This report is based on <a href="https://github.com/Boria138/PortProtonQt/releases#release-v1.3.0">version 1.3.0</a> of PortProtonQt. A
bugfix is available in the newly released <a href="https://github.com/Boria138/PortProtonQt/releases/tag/v1.3.1">version 1.3.1</a>.</p>

<h1 id="2-insecure-polkit-rule">2) Insecure Polkit Rule</h1>

<p>PortProtonQt installs a Polkit <a href="https://github.com/Boria138/PortProtonQt/blob/v1.3.0/build-aux/share/polkit-1/rules.d/ru.linux_gaming.PortProtonQt.rules">rules file
<code class="language-plaintext highlighter-rouge">ru.linux_gaming.PortProtonQt.rules</code></a> containing the following
logic:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">cmd_line</span> <span class="o">=</span> <span class="nx">polkit</span><span class="p">.</span><span class="nf">spawn</span><span class="p">([</span><span class="dl">"</span><span class="s2">ps</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">-o</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">args=</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">-p</span><span class="dl">"</span><span class="p">,</span> <span class="nc">String</span><span class="p">(</span><span class="nx">subject</span><span class="p">.</span><span class="nx">pid</span><span class="p">)]);</span>
<span class="kd">var</span> <span class="nx">ppid</span> <span class="o">=</span> <span class="nx">polkit</span><span class="p">.</span><span class="nf">spawn</span><span class="p">([</span><span class="dl">"</span><span class="s2">ps</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">-o</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">ppid=</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">-p</span><span class="dl">"</span><span class="p">,</span> <span class="nc">String</span><span class="p">(</span><span class="nx">subject</span><span class="p">.</span><span class="nx">pid</span><span class="p">)]).</span><span class="nf">trim</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">parent_cmd_line</span> <span class="o">=</span> <span class="dl">""</span><span class="p">;</span>
<span class="k">if </span><span class="p">(</span><span class="nx">ppid</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">parent_cmd_line</span> <span class="o">=</span> <span class="nx">polkit</span><span class="p">.</span><span class="nf">spawn</span><span class="p">([</span><span class="dl">"</span><span class="s2">ps</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">-o</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">args=</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">-p</span><span class="dl">"</span><span class="p">,</span> <span class="nx">ppid</span><span class="p">]);</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">is_ppqt_call</span> <span class="o">=</span> <span class="nx">cmd_line</span><span class="p">.</span><span class="nf">includes</span><span class="p">(</span><span class="dl">"</span><span class="s2">ppqtos</span><span class="dl">"</span><span class="p">)</span> <span class="o">||</span> <span class="nx">parent_cmd_line</span><span class="p">.</span><span class="nf">includes</span><span class="p">(</span><span class="dl">"</span><span class="s2">ppqtos</span><span class="dl">"</span><span class="p">);</span>

<span class="k">if </span><span class="p">((</span> <span class="nx">action</span><span class="p">.</span><span class="nx">id</span> <span class="o">===</span> <span class="dl">"</span><span class="s2">org.freedesktop.NetworkManager.settings.modify.system</span><span class="dl">"</span> <span class="o">||</span>
        <span class="nx">action</span><span class="p">.</span><span class="nx">id</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">org.freedesktop.udisks2.filesystem-mount-system</span><span class="dl">"</span> <span class="o">||</span>
        <span class="nx">action</span><span class="p">.</span><span class="nx">id</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">org.freedesktop.udisks2.filesystem-mount-other-seat</span><span class="dl">"</span> <span class="o">||</span>
        <span class="nx">action</span><span class="p">.</span><span class="nx">id</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">org.freedesktop.udisks2.filesystem-unmount-others</span><span class="dl">"</span> <span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="nx">is_ppqt_call</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">polkit</span><span class="p">.</span><span class="nx">Result</span><span class="p">.</span><span class="nx">YES</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The code inspects the requesting process’s command line as reported by the
<code class="language-plaintext highlighter-rouge">ps</code> utility, aiming to grant additional privileges to the <code class="language-plaintext highlighter-rouge">ppqtos</code> program.
This is on one hand subject to race conditions, because the PID used by the
requestor can be recycled or replaced by other programs by the time the Polkit
rule runs. On the other hand, the process command line can be influenced
arbitrarily to fake a <code class="language-plaintext highlighter-rouge">ppqtos</code> process.</p>

<p>We developed a simple <a href="/download/portprotonqt-reproducer.gz">shell script reproducer</a> which
shows that the exploitation of this vulnerability is simple and always
succeeds. The vulnerability allows arbitrary local users to meddle with the
network settings via NetworkManager and mount/unmount file systems via udisks.
We could not spot a full local root exploit in that, although it is getting
close. The impact is mostly Denial-of-Service and system integrity:</p>

<ul>
  <li>arbitrary block devices can be mounted (but not in arbitrary locations, only
under <code class="language-plaintext highlighter-rouge">/run/media</code>).</li>
  <li>arbitrary block devices can be unmounted (unless in use, but independent of
their mount location).</li>
  <li>arbitrary NetworkManager connections can be removed and created, violating
network integrity.</li>
</ul>

<h1 id="3-bugfix">3) Bugfix</h1>

<p>Upstream fixed the issue in <a href="https://github.com/Boria138/PortProtonQt/commit/f0ab40a2dab36ce91bc7e1b13d8d4ea220e5107c">commit f0ab40a2d</a> which is part of
a <a href="https://github.com/Boria138/PortProtonQt/releases/tag/v1.3.1">1.3.1 bugfix release</a>. Upstream followed our suggestion to
allow these extra Polkit actions only for users in an active local session
which are members of a dedicated <code class="language-plaintext highlighter-rouge">portprotonqt</code> group. This way the
authentication bypass is restricted to interactive users that opt-in to using
this feature.</p>

<p>The issue was introduced in PortProtonQt <a href="https://github.com/Boria138/PortProtonQt/releases/tag/v0.1.12">version 0.1.12</a>
via <a href="https://github.com/Boria138/PortProtonQt/commit/71a8ebd89a503e692cdc6b40777e8cc22393c87b">commit 71a8ebd89</a>.</p>

<h1 id="4-cve-assignment">4) CVE Assignment</h1>

<p>In agreement with upstream we assigned CVE-2026-59676 to track this issue.</p>

<h1 id="5-timeline">5) Timeline</h1>

<table>
  <tbody>
    <tr>
      <td>2026-07-06</td>
      <td>We reached out to the lead developer of PortProtonQt via email, offering coordinated disclosure.</td>
    </tr>
    <tr>
      <td>2026-07-10</td>
      <td>With no response, we sent a follow-up email asking for feedback until July 17 lest we publish without coordination.</td>
    </tr>
    <tr>
      <td>2026-07-15</td>
      <td>Still without reply we reached out to yet another developer documented in the upstream README.md, asking whether the lead developer contact is still valid.</td>
    </tr>
    <tr>
      <td>2026-07-15</td>
      <td>We got a response pointing out that the Polkit rules file needs not to be installed for the package to function, but no details regarding the coordinated disclosure or a bugfix.</td>
    </tr>
    <tr>
      <td>2026-07-16</td>
      <td>We sent the full report once more to the second mail contact, pointing out that a security issue is at hand.</td>
    </tr>
    <tr>
      <td>2026-07-16</td>
      <td>Upstream informed us about <a href="https://github.com/Boria138/PortProtonQt/commit/f0ab40a2dab36ce91bc7e1b13d8d4ea220e5107c">the public bugfix</a>, implying that no coordinated disclosure is desired. Upstream also asked us to assign a CVE for the issue.</td>
    </tr>
    <tr>
      <td>2026-07-17</td>
      <td>We assigned CVE-2026-59678 and shared it with upstream.</td>
    </tr>
    <tr>
      <td>2026-07-22</td>
      <td>Publication of this report.</td>
    </tr>
  </tbody>
</table>

<h1 id="6-references">6) References</h1>

<ul>
  <li><a href="https://github.com/Boria138/PortProtonQt">PortProtonQt GitHub project</a></li>
  <li><a href="https://github.com/Boria138/PortProtonQt/releases/tag/v1.3.1">PortProtonQt 1.3.1 bugfix release</a></li>
  <li><a href="https://bugzilla.suse.com/show_bug.cgi?id=1270218">openSUSE review bug for PortProtonQT Polkit rule</a></li>
  <li><a href="/download/portprotonqt-reproducer.gz">Reproducer for the issue</a></li>
</ul>]]></content><author><name>&lt;a href=&apos;mailto:matthias.gerstner@suse.de&apos;&gt;Matthias Gerstner&lt;/a&gt;, &lt;a href=&apos;mailto:filippo.bonazzi@suse.com&apos;&gt;Filippo Bonazzi (editor)&lt;/a&gt;</name></author><category term="Polkit" /><category term="CVE" /><summary type="html"><![CDATA[PortProtonQt is a GUI application to simplify launching of Windows games on Linux. A review of a custom Polkit rule installed by PortProtonQt uncovered an attack vector which allows arbitrary local users to modify NetworkManager connections or UDisks2 mounts.]]></summary></entry><entry><title type="html">SELinux Userspace Utilities: Local Denial-of-Service Attack Vectors in seunshare in release 3.10</title><link href="https://security.opensuse.org/2026/07/15/selinux-seunshare.html" rel="alternate" type="text/html" title="SELinux Userspace Utilities: Local Denial-of-Service Attack Vectors in seunshare in release 3.10" /><published>2026-07-15T00:00:00+00:00</published><updated>2026-07-15T00:00:00+00:00</updated><id>https://security.opensuse.org/2026/07/15/selinux-seunshare</id><content type="html" xml:base="https://security.opensuse.org/2026/07/15/selinux-seunshare.html"><![CDATA[<h1 class="no_toc" id="table-of-contents">Table of Contents</h1>

<ul id="markdown-toc">
  <li><a href="#1-introduction" id="markdown-toc-1-introduction">1) Introduction</a></li>
  <li><a href="#2-design-overview" id="markdown-toc-2-design-overview">2) Design Overview</a></li>
  <li><a href="#3-security-issues" id="markdown-toc-3-security-issues">3) Security Issues</a>    <ul>
      <li><a href="#section-issue-rmrf" id="markdown-toc-section-issue-rmrf">3.1) Local File Deletion Attack Vector in <code class="language-plaintext highlighter-rouge">rm_rf()</code> (CVE-2026-59676)</a></li>
      <li><a href="#section-issue-killall" id="markdown-toc-section-issue-killall">3.2) Process Kill Attack Vector in <code class="language-plaintext highlighter-rouge">killall()</code> (CVE-2026-59677)</a></li>
    </ul>
  </li>
  <li><a href="#4-remaining-concerns" id="markdown-toc-4-remaining-concerns">4) Remaining Concerns</a></li>
  <li><a href="#section-cve-assignments" id="markdown-toc-section-cve-assignments">5) CVE Assignments</a></li>
  <li><a href="#6-timeline" id="markdown-toc-6-timeline">6) Timeline</a></li>
  <li><a href="#7-references" id="markdown-toc-7-references">7) References</a></li>
  <li><a href="#8-change-history" id="markdown-toc-8-change-history">8) Change History</a></li>
</ul>

<h1 id="1-introduction">1) Introduction</h1>

<p>The <a href="https://man7.org/linux/man-pages/man8/seunshare.8.html"><code class="language-plaintext highlighter-rouge">seunshare</code> program</a> is part of the <a href="https://github.com/SELinuxProject/selinux">SELinux</a>
“sandbox” feature, which is used to confine untrusted programs using Linux
mount namespaces and restrictive SELinux policies. The program is designed to
be installed with setuid-root privileges, accessible to all users in the
system.</p>

<p>We have been asked to <a href="https://bugzilla.suse.com/show_bug.cgi?id=1268256">review the program’s security</a> with the
intention of assigning the setuid bit to it on SUSE distributions
in the future. Fedora Linux already ships this program with setuid-root
enabled; other SELinux-enabled Linux distributions may do so as well.</p>

<p>During our review of the <a href="https://github.com/SELinuxProject/selinux/blob/3.10/sandbox/seunshare.c">utility’s code</a> in <a href="https://github.com/SELinuxProject/selinux/releases/tag/3.10">version
3.10</a> of the SELinux userspace utilities we identified two
local Denial-of-Service attack vectors, which will be described in detail in
the following sections. Upstream independently fixed these issues in <a href="https://github.com/SELinuxProject/selinux/releases/tag/3.11">version
3.11</a>, without clearly marking them as security issues, however.</p>

<p>The rest of this report is based on version 3.10 of the codebase of
<code class="language-plaintext highlighter-rouge">seunshare</code>.</p>

<h1 id="2-design-overview">2) Design Overview</h1>

<p>The <code class="language-plaintext highlighter-rouge">seunshare</code> program is relatively small, consisting of about 1,000 lines of
C code. The elevated <code class="language-plaintext highlighter-rouge">root</code> privileges are primarily needed to setup a custom
mount namespace for the sandbox environment.</p>

<p>The program accepts a range of command line arguments which, among others,
allow to request dedicated directories to be used for the following paths
inside the sandbox:</p>

<ul>
  <li>the user’s home directory.</li>
  <li>the <code class="language-plaintext highlighter-rouge">/tmp</code> directory.</li>
  <li>the <code class="language-plaintext highlighter-rouge">/run/user/&lt;uid&gt;</code> directory.</li>
</ul>

<p>For the <code class="language-plaintext highlighter-rouge">/run</code> and <code class="language-plaintext highlighter-rouge">/tmp</code> directories, the source paths specified by the user
are copied into a random temporary directory in the initial mount namespace
under <code class="language-plaintext highlighter-rouge">/tmp/.sandbox-&lt;label&gt;-XXXXXX</code> using the <code class="language-plaintext highlighter-rouge">rsync</code> program. These
directories are then bind-mounted into the sandbox’s mount namespace to appear
at the expected locations.</p>

<p>A lot of code in <code class="language-plaintext highlighter-rouge">seunshare</code> is concerned with securely maintaining these
temporary directories. To allow the sandbox environment to modify the directory
contents, <code class="language-plaintext highlighter-rouge">seunshare</code> assigns group write permissions for the real group ID of
the calling user, as shown in this example:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>drwxrwx--T. 2 root user 80 Jul  8 16:11 /tmp/.sandbox-user-OaxmUp/
</code></pre></div></div>

<p>To safely access user-provided paths, the program flips its <a href="https://man7.org/linux/man-pages/man2/setfsuid.2.html">filesystem
UID</a> to the real UID of the calling user and back to <code class="language-plaintext highlighter-rouge">root</code>, as
needed.</p>

<h1 id="3-security-issues">3) Security Issues</h1>

<p>Since <code class="language-plaintext highlighter-rouge">seunshare</code> is supposed to run on SELinux-enabled systems, it is
important to understand what kind of privilege escalation can be achieved when
vulnerabilities are exploited in a setuid-root binary like this. Many
SELinux-enabled systems, such as Fedora and openSUSE, ship with the “targeted”
SELinux policy by default. This policy is focused on confining well-known
system services, but assigns an unconfined SELinux context to interactive
users by default to achieve a balance between security and usability.</p>

<p>There is currently no domain transition from the unconfined domain to the more
restricted <code class="language-plaintext highlighter-rouge">seunshare_t</code> defined in the SELinux policy for <code class="language-plaintext highlighter-rouge">seunshare</code>. This
means the execution of <code class="language-plaintext highlighter-rouge">seunshare</code> continues in the unconfined domain. Thus in
the context of attacks carried out by interactive users, the impact of the
vulnerabilities below will be a root-like privilege escalation despite the
system running in SELinux enforced mode.</p>

<h2 id="section-issue-rmrf">3.1) Local File Deletion Attack Vector in <code class="language-plaintext highlighter-rouge">rm_rf()</code> (CVE-2026-59676)</h2>

<p>The function <a href="https://github.com/SELinuxProject/selinux/blob/ca10fc4204ed60540d41d2499127c18ad0643f9e/sandbox/seunshare.c#L419"><code class="language-plaintext highlighter-rouge">rm_rf()</code></a> is called at the end of the utility’s
execution to recursively remove temporary directory trees. While a comment in
the function suggests that no symbolic links would be followed by this
routine, the reality is that the <a href="https://github.com/SELinuxProject/selinux/blob/ca10fc4204ed60540d41d2499127c18ad0643f9e/sandbox/seunshare.c#L431"><code class="language-plaintext highlighter-rouge">openat()</code></a> system call is
lacking the <code class="language-plaintext highlighter-rouge">O_NOFOLLOW</code> flag. This creates a race condition during the
recursion of the <code class="language-plaintext highlighter-rouge">rm_rf()</code> function:</p>

<ul>
  <li>first an <code class="language-plaintext highlighter-rouge">fstatat()</code> is performed for directory contents obtained from
<code class="language-plaintext highlighter-rouge">readdir()</code> to safely determine whether an entry refers to a sub-directory.</li>
  <li>when a directory is encountered the unsafe <code class="language-plaintext highlighter-rouge">openat()</code> happens, allowing
the unprivileged user to replace the directory entry by a symbolic link in
the meantime. While the <code class="language-plaintext highlighter-rouge">O_DIRECTORY</code> flag is passed to <code class="language-plaintext highlighter-rouge">openat()</code>, symbolic
links in the target are still followed, as long as they point to a directory.</li>
</ul>

<p>When the user calling <code class="language-plaintext highlighter-rouge">seunshare</code> is running in the unconfined SELinux domain,
arbitrary <code class="language-plaintext highlighter-rouge">root</code>-owned files can be deleted this way. The vulnerability does
not allow to delete arbitrary files of other users, however, because
<code class="language-plaintext highlighter-rouge">seunshare</code> <a href="https://github.com/SELinuxProject/selinux/blob/ca10fc4204ed60540d41d2499127c18ad0643f9e/sandbox/seunshare.c#L1069">drops all capabilities</a> before calling <code class="language-plaintext highlighter-rouge">rm_rf()</code>.
This leaves the process in a somewhat strange state of privilege: it can no
longer override discretionary access control (DAC) but it is still allowed to
operate on files owned by the filesystem-uid of the privileged process, which
is 0 during the invocation of <code class="language-plaintext highlighter-rouge">rm_rf()</code>.</p>

<p>We developed a <a href="/download/seunshare-reproducer.tar.gz">reproducer</a> for this issue, which succeeds
in executing the exploit quickly after some fine tuning of the timing for
the target system. We verified that the reproducer works on current openSUSE
Tumbleweed with policycoreutils 3.10 and the setuid-root bit enabled on
<code class="language-plaintext highlighter-rouge">seunshare</code>. It does not work on current Fedora 44, as it seems Fedora
backported a patch to fix this issue (it passes <code class="language-plaintext highlighter-rouge">O_NOFOLLOW</code> to the <code class="language-plaintext highlighter-rouge">openat()</code>
call in question).</p>

<p>Upstream fixed this issue in commit <a href="https://github.com/SELinuxProject/selinux/commit/38f0a4d9a683f2cd2472d4502842df5d5b8255a8">38f0a4d9a</a> which is
part of of the 3.11 upstream release.</p>

<h2 id="section-issue-killall">3.2) Process Kill Attack Vector in <code class="language-plaintext highlighter-rouge">killall()</code> (CVE-2026-59677)</h2>

<p><code class="language-plaintext highlighter-rouge">seunshare</code> offers <code class="language-plaintext highlighter-rouge">--kill</code> and <code class="language-plaintext highlighter-rouge">-Z</code> switches as documented in its man page:</p>

<pre><code class="language-man">-k --kill
       Kill all processes with matching MCS level

-Z context
       Use alternate SELinux context while running the executable
</code></pre>

<p>Both switches combined cause the <a href="https://github.com/SELinuxProject/selinux/blob/ca10fc4204ed60540d41d2499127c18ad0643f9e/sandbox/seunshare.c#L662"><code class="language-plaintext highlighter-rouge">killall()</code></a> function to kill
all processes running with the user-provided SELinux context. This does not
fully work for arbitrary target processes due to SELinux access control;
however, if the calling user is running in unconfined context (as explained
previously) it allows to kill e.g. <code class="language-plaintext highlighter-rouge">root</code>-owned processes running also in
unconfined context. Once the algorithm reaches its own PID (if the context
matches), it kills itself, thus it is possible that only a certain range of
PIDs can be killed this way depending on the PID selection order of the
algorithm in <code class="language-plaintext highlighter-rouge">killall()</code>.</p>

<p>We have been able to reproduce the issue both on openSUSE and on Fedora 44
using a command line like this:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>seunshare <span class="nt">--kill</span> <span class="nt">-Z</span> unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 <span class="nt">-t</span> ~/some-dir/ <span class="nt">--</span> /usr/bin/true
</code></pre></div></div>

<p>Upstream fixed this issue by dropping the <code class="language-plaintext highlighter-rouge">--kill</code> switch and the <code class="language-plaintext highlighter-rouge">killall()</code>
function in commit <a href="https://github.com/SELinuxProject/selinux/commit/572db2fae9157ae620160e0d7b2c3f8b75fe6c1a">572db2fa</a>.</p>

<h1 id="4-remaining-concerns">4) Remaining Concerns</h1>

<p>The code of <code class="language-plaintext highlighter-rouge">seunshare</code> has seen major changes between version 3.10 and
3.11 of the SELinux userspace utilities. The new version of the program improves
on various aspects of the code; generally the code shows awareness of various
filesystem-related security concerns that are relevant for setuid-root
binaries. Still a number of concerns remain:</p>

<ul>
  <li>Data which is modified in temporary directories within the sandbox is
<a href="https://github.com/SELinuxProject/selinux/blob/ca10fc4204ed60540d41d2499127c18ad0643f9e/sandbox/seunshare.c#L484">transparently copied back</a> into the calling user’s source
directory via <code class="language-plaintext highlighter-rouge">rsync</code>. Since the program is untrusted it can potentially
create all kinds of dangerous files, which will now reside e.g. in the user’s
regular home directory and could lead to security issues at a later time when
accessed without care.</li>
  <li>The code flips the filesystem UID a lot to temporarily drop root
privileges for file operations. This logic is hard to follow in parts and
leaves the process in an unusual state of privilege, as the effective UID is
still 0; during the first stages of the program it also still has all
capabilities. Due to the filesystem UID being set to the real user’s UID, file
operations are carried out using the calling user’s lower privileges, however.
From a design point of view it would be preferable to let the process operate
in an unprivileged state by default (effective UID and GID set to the real
UID and GID). Privileges could then be raised for the few operations that
actually need <code class="language-plaintext highlighter-rouge">root</code> privileges.</li>
  <li>The code currently lacks some common security precautions for setuid-root
programs:
    <ul>
      <li>while a new environment variable block is set up to execute the untrusted
target program, the privileged parent process keeps the untrusted
environment variables of the calling user in place. These are also
inherited to tools like <code class="language-plaintext highlighter-rouge">rsync</code> that are invoked for the purposes of
setting up the sandbox. While this is not an issue at the moment, it could
turn into a security issue at a later time when the code changes.</li>
      <li>the <code class="language-plaintext highlighter-rouge">umask()</code> of the process is also kept unchanged, inheriting whatever
the unprivileged parent process configured. This can cause files to
receive world-readable or world-writable bits leading to unexpected attack
vectors.</li>
    </ul>
  </li>
</ul>

<h1 id="section-cve-assignments">5) CVE Assignments</h1>

<p>We approached the upstream SELinux userspace utilities developers and
suggested to assign CVEs for the two issues discussed above. Upstream informed
us that they don’t take care of CVE assignment themselves, however. Since Red
Hat developers are also involved with upstream development, we were waiting
for an agreement on who will assign CVEs to avoid duplicates. On 2026-07-17,
after the initial publication of this report, we received a response that
there is no intention by RedHat developers to assign CVEs. As a result we
assigned CVEs on our end as documented in the updated blog post.</p>

<h1 id="6-timeline">6) Timeline</h1>

<table>
  <tbody>
    <tr>
      <td>2026-07-03</td>
      <td>We (mistakenly) approached the SELinux kernel code maintainer, asking for CVE assignments for these issues which have meanwhile been fixed in the 3.11 upstream release.</td>
    </tr>
    <tr>
      <td>2026-07-03</td>
      <td>The SELinux kernel code maintainer forwarded our report to the maintainers of the userspace utilities.</td>
    </tr>
    <tr>
      <td>2026-07-06</td>
      <td>An SELinux userspace developer informed us that the project is not actively assigning CVEs.</td>
    </tr>
    <tr>
      <td>2026-07-07</td>
      <td>We responded that we would be able to assign CVEs on our end, but would like to avoid a clash with any CVE assignment plans on the end of Red Hat developers working on SELinux. We thus asked for clarification of who will take care of it.</td>
    </tr>
    <tr>
      <td>2026-07-15</td>
      <td>Publication of this report.</td>
    </tr>
    <tr>
      <td>2026-07-17</td>
      <td>We received a reply from a RedHat upstream developer stating that there is no intention to assign CVEs on their end. Thus we assigned CVE-2026-59676 for <a href="#section-issue-rmrf">issue 3.1</a> and CVE-2026-59677 for <a href="#section-issue-killall">issue 3.2</a> and published the information.</td>
    </tr>
  </tbody>
</table>

<h1 id="7-references">7) References</h1>

<ul>
  <li><a href="https://github.com/SELinuxProject/selinux/releases/tag/3.11">SElinux userspace utilities bugfix release 3.11</a></li>
  <li><a href="https://github.com/SELinuxProject/selinux">SELinux userspace utilities Github project</a></li>
  <li><a href="https://github.com/SELinuxProject/selinux/blob/3.10/sandbox/seunshare.c">vulnerable seunshare code in upstream release 3.10</a></li>
  <li><a href="/download/seunshare-reproducer.tar.gz">Reproducer for the file delete issue</a></li>
</ul>

<h1 id="8-change-history">8) Change History</h1>

<table>
  <tbody>
    <tr>
      <td>2026-07-17</td>
      <td>Added information about the <a href="#section-cve-assignments">CVEs</a> we assigned for the issues.</td>
    </tr>
  </tbody>
</table>]]></content><author><name>&lt;a href=&apos;mailto:matthias.gerstner@suse.de&apos;&gt;Matthias Gerstner&lt;/a&gt;, &lt;a href=&apos;mailto:filippo.bonazzi@suse.com&apos;&gt;Filippo Bonazzi (editor)&lt;/a&gt;</name></author><category term="setuid" /><category term="CVE" /><summary type="html"><![CDATA[seunshare is a setuid-root utility used for sandboxing untrusted applications based on Linux namespaces and SELinux policies. During a code review we identified two local Denial-of-Service attack vectors in version 3.10 of the utility.]]></summary></entry><entry><title type="html">qSnapper: Various Security Issues in Privileged D-Bus Service (CVE-2026-41045 through CVE-2026-41048)</title><link href="https://security.opensuse.org/2026/05/26/qsnapper-dbus-issues.html" rel="alternate" type="text/html" title="qSnapper: Various Security Issues in Privileged D-Bus Service (CVE-2026-41045 through CVE-2026-41048)" /><published>2026-05-26T00:00:00+00:00</published><updated>2026-05-26T00:00:00+00:00</updated><id>https://security.opensuse.org/2026/05/26/qsnapper-dbus-issues</id><content type="html" xml:base="https://security.opensuse.org/2026/05/26/qsnapper-dbus-issues.html"><![CDATA[<h1 class="no_toc" id="table-of-contents">Table of Contents</h1>

<ul id="markdown-toc">
  <li><a href="#1-introduction" id="markdown-toc-1-introduction">1) Introduction</a></li>
  <li><a href="#2-overview-of-the-qsnapper-d-bus-service" id="markdown-toc-2-overview-of-the-qsnapper-d-bus-service">2) Overview of the qSnapper D-Bus Service</a></li>
  <li><a href="#3-security-issues" id="markdown-toc-3-security-issues">3) Security Issues</a>    <ul>
      <li><a href="#issue-polkit-bypass" id="markdown-toc-issue-polkit-bypass">3.1) Weak Polkit Authentication Check is Subject to Race Condition (CVE-2026-41045)</a></li>
      <li><a href="#issue-path-traversal" id="markdown-toc-issue-path-traversal">3.2) Path Traversal via <code class="language-plaintext highlighter-rouge">configName</code> Parameter (CVE-2026-41046)</a></li>
      <li><a href="#issue-info-leak" id="markdown-toc-issue-info-leak">3.3) Information Leak via “diff” Methods (CVE-2026-41047)</a></li>
      <li><a href="#issue-auth-caching" id="markdown-toc-issue-auth-caching">3.4) Caching of Authentication allows Authentication Bypass (CVE-2026-41048, CVE-2026-41049)</a></li>
      <li><a href="#issue-defense-in-depth" id="markdown-toc-issue-defense-in-depth">3.5) Defense-in-Depth Issues when Restoring Arbitrary Files</a></li>
      <li><a href="#issue-other" id="markdown-toc-issue-other">3.6) Other Issues</a></li>
    </ul>
  </li>
  <li><a href="#upstream-bugfixes" id="markdown-toc-upstream-bugfixes">4) Upstream Bugfixes</a></li>
  <li><a href="#5-disclosure-process" id="markdown-toc-5-disclosure-process">5) Disclosure Process</a></li>
  <li><a href="#6-timeline" id="markdown-toc-6-timeline">6) Timeline</a></li>
  <li><a href="#7-references" id="markdown-toc-7-references">7) References</a></li>
  <li><a href="#change-history" id="markdown-toc-change-history">Change History</a></li>
</ul>

<h1 id="1-introduction">1) Introduction</h1>

<p><a href="https://github.com/presire/qSnapper.git">qSnapper</a> is a GUI frontend for the
<a href="http://snapper.io/overview.html">snapper</a> utility for managing Btrfs filesystem snapshots.
In April we received <a href="https://bugzilla.suse.com/show_bug.cgi?id=1261537">a review request</a> for qSnapper, because it
contains a privileged D-Bus service and Polkit policies.</p>

<p>Normally, Btrfs snapshots can only be managed with root privileges. qSnapper aims to
provide more user-friendly and fine-grained access to snapshot management
features, based on a privileged daemon which utilizes D-Bus and Polkit. Our
review of the service uncovered various security issues, which led to
a longer coordinated disclosure to allow upstream to develop bugfixes, which
are part of upstream <a href="https://github.com/presire/qSnapper/releases/tag/v1.3.3">release 1.3.3</a>.</p>

<p>The full details of the security issues will be covered in the sections
below. The original review was performed on <a href="https://github.com/presire/qSnapper/releases/tag/v1.1.3">qSnapper release
1.1.3</a>. Since bigger upstream changes appeared in the
meantime this report is based on <a href="https://github.com/presire/qSnapper/releases/tag/v1.3.2">qSnapper release 1.3.2</a>,
however.</p>

<h1 id="2-overview-of-the-qsnapper-d-bus-service">2) Overview of the qSnapper D-Bus Service</h1>

<p>The qSnapper project contains a daemon named <code class="language-plaintext highlighter-rouge">qsnapper-dbus-service</code>, which
runs with full root privileges. The daemon provides the D-Bus interface
“com.presire.qsnapper.Operations” on the system bus; some of its methods
are provided to arbitrary users in the system without authentication, while the
majority of them is protected by Polkit authentication checks. The
implementation of the various D-Bus methods offered by the service is found in
<a href="https://github.com/presire/qSnapper/blob/v1.3.2/src/dbusservice/snapshotoperations.cpp"><code class="language-plaintext highlighter-rouge">snapshotoperations.cpp</code></a>. All of the security issues
outlined below are located in this compilation unit.</p>

<h1 id="3-security-issues">3) Security Issues</h1>

<h2 id="issue-polkit-bypass">3.1) Weak Polkit Authentication Check is Subject to Race Condition (CVE-2026-41045)</h2>

<p>The <a href="https://github.com/presire/qSnapper/blob/485a7ee9256c8e25de87a5d69d538e318e49b56d/src/dbusservice/snapshotoperations.cpp#L449"><code class="language-plaintext highlighter-rouge">SnapshotOperations::checkAuthorization()</code></a>
function uses Polkit’s <code class="language-plaintext highlighter-rouge">UnixProcess</code> subject in an unsafe way to authenticate
clients. The code obtains the client’s PID from the active D-Bus connection,
which leads to a race condition. At the time the Polkit daemon (<code class="language-plaintext highlighter-rouge">polkitd</code>)
checks the provided PID, the process in question can already have been
replaced by another, privileged process. This is a well-known class of Polkit
authentication bypasses which was assigned <a href="https://nvd.nist.gov/vuln/detail/CVE-2013-4288">CVE-2013-4288</a>.</p>

<p>In effect this means that clients may be able to bypass all Polkit
authentication checks performed by qSnapper, although the attack is somewhat
complex and might need multiple attempts to succeed. There is nothing stopping
a local attacker from doing that, however. In combination with the <a href="#issue-path-traversal">configuration
path traversal in D-Bus methods</a> like
<code class="language-plaintext highlighter-rouge">WriteSnapperConfig()</code> described below, this can be used to achieve a full
local root exploit.</p>

<p>To fix this, we suggested to use Polkit’s <code class="language-plaintext highlighter-rouge">SystemBusName</code> subject instead. With
this subject, the D-Bus daemon obtains the client’s UID in a race-free fashion
from the UNIX domain socket the client used to connect, and <code class="language-plaintext highlighter-rouge">polkitd</code> cannot be
fooled by recycled PIDs.</p>

<h2 id="issue-path-traversal">3.2) Path Traversal via <code class="language-plaintext highlighter-rouge">configName</code> Parameter (CVE-2026-41046)</h2>

<p>All the D-Bus methods accepting <a href="https://github.com/presire/qSnapper/blob/485a7ee9256c8e25de87a5d69d538e318e49b56d/src/dbusservice/snapshotoperations.cpp#L470">a <code class="language-plaintext highlighter-rouge">configName</code> parameter</a>
(that is nearly all of them) suffer from a path traversal vulnerability. This
string parameter is directly passed to <code class="language-plaintext highlighter-rouge">snapper::Snapper()</code>, which is part of
the libsnapper API, without checking its safety. libsnapper expects a basename
here to look up a configuration file in <code class="language-plaintext highlighter-rouge">/etc/snapper/configs</code>. In the context
of qSnapper this string can contain additional path components like
<code class="language-plaintext highlighter-rouge">../../path/to/crafted.cfg</code>. This allows clients to use arbitrary
configuration files as input to libsnapper.</p>

<p>When looking at the <a href="https://github.com/presire/qSnapper/blob/485a7ee9256c8e25de87a5d69d538e318e49b56d/src/dbusservice/snapshotoperations.cpp#L349">writeSnapperConfig()</a> D-Bus
method, which requires admin authentication, this path traversal allows to
write largely attacker-controlled data to arbitrary locations. Combined with
the Polkit authentication bypass described earlier in <a href="#issue-polkit-bypass">section
3.1</a>, this would allow for a full local root
exploit.</p>

<p>In case of other D-Bus methods which use the <a href="https://github.com/presire/qSnapper/blob/485a7ee9256c8e25de87a5d69d538e318e49b56d/polkit/com.presire.qsnapper.policy#L8">“list-snapshots” Polkit
action</a>, the path traversal is also accessible to
locally logged-in users without any authentication. Luckily the libsnapper
<a href="http://snapper.io/manpages/snapper-configs.html">configuration file format</a> offers no features that
lead to a trivial local root exploit in this context. The impact of the
vulnerability can be any of the following, however:</p>

<ul>
  <li>local Denial-of-Service (DoS): by pointing libsnapper to a special file like
<code class="language-plaintext highlighter-rouge">/dev/zero</code>, the daemon will consume the maximum amount of memory and be
killed by the kernel. By pointing libsnapper to a named FIFO special file,
the daemon will block indefinitely.</li>
  <li>information leak from parsing of private files: by pointing libsnapper to a
private file like <code class="language-plaintext highlighter-rouge">/etc/shadow</code>, libsnapper will parse the sensitive data
from the file as configuration data. If any logging features are active and
the logs can be accessed by unprivileged users, then private data from such a
file might leak into the unprivileged context. We are not aware of such an
information leak in default installations, however.</li>
  <li>by providing crafted configuration data further attacks can be carried out:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">SUBVOLUME</code> can be pointed to arbitrary mounts in the system. Luckily
libsnapper is conservative in its file operations here, and quite strictly
verifies the validity of this path; it verifies if it is really a mount
point and is backed by the expected block device and file system etc.</li>
      <li><code class="language-plaintext highlighter-rouge">ALLOW_USERS</code> and <code class="language-plaintext highlighter-rouge">ALLOW_GROUPS</code> can be set to arbitrary users and groups,
allowing them officially to control the Snapper configuration.</li>
      <li>The <code class="language-plaintext highlighter-rouge">SYNC_ACL</code> setting causes libsnapper to apply ACL entries to the
<code class="language-plaintext highlighter-rouge">.snapshots</code> directory of the volume path, granting the users and groups
from <code class="language-plaintext highlighter-rouge">ALLOW_USERS</code> and <code class="language-plaintext highlighter-rouge">ALLOW_GROUPS</code> to access the snapshot data. Thus
this is a major local information leak.</li>
    </ul>
  </li>
</ul>

<p>libsnapper is using different backends depending on the file system type.
Different code is used for each backend, which means that the exploitability
of some aspects of the vulnerability depends on which backend is used. On
openSUSE only the Btrfs and LVM thin volume backends are compiled-in by
default, which only offer little additional attack surface.</p>

<p>To fix the issue, we suggested to upstream to verify the <code class="language-plaintext highlighter-rouge">configName</code>
parameter in all cases and reject it if any <code class="language-plaintext highlighter-rouge">/</code> or <code class="language-plaintext highlighter-rouge">..</code> components are
found in it.</p>

<h2 id="issue-info-leak">3.3) Information Leak via “diff” Methods (CVE-2026-41047)</h2>

<p>The Polkit action <a href="https://github.com/presire/qSnapper/blob/485a7ee9256c8e25de87a5d69d538e318e49b56d/polkit/com.presire.qsnapper.policy#L8">“list-snapshots”</a> is allowed for
locally logged-in users without authentication, and is used by multiple
qSnapper D-Bus methods which are likely considered “read-only” operations not
harmful to the system. In version 1.1.3 of qSnapper the following methods are
relying on this Polkit action:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">ListSnapshots()</code></li>
  <li><code class="language-plaintext highlighter-rouge">GetFileChanges()</code></li>
  <li><code class="language-plaintext highlighter-rouge">GetFileDiffAndDetails()</code></li>
</ul>

<p>In version 1.3.2 of qSnapper these additional methods are using the action:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">GetFileChangesBetween()</code></li>
  <li><code class="language-plaintext highlighter-rouge">GetFileDiffBetween()</code></li>
</ul>

<p>These methods allow to obtain information about metadata changes of arbitrary
files between snapshots or between snapshots and the live filesystem. The
newer <code class="language-plaintext highlighter-rouge">GetFileDiffBetween()</code> method even offers full file content diff output.
These are major local information leaks, since unprivileged users can get a
diff e.g. of the <code class="language-plaintext highlighter-rouge">/etc/shadow</code> file between different system states, or
changes from root’s home directory which could leak sensitive data like
passwords or private keys.</p>

<p>To fix this, we suggested to upstream that all methods providing non-public
information get restricted by <code class="language-plaintext highlighter-rouge">auth_admin</code> Polkit checks. Alternatively, a check
could be implemented whether the files to be diffed are normally accessible to
the caller by using checks similar to the <code class="language-plaintext highlighter-rouge">access()</code> system call; such an
approach would likely be rather complex and error-prone, however.</p>

<h2 id="issue-auth-caching">3.4) Caching of Authentication allows Authentication Bypass (CVE-2026-41048, CVE-2026-41049)</h2>

<p>In version 1.2.1 of qSnapper an <code class="language-plaintext highlighter-rouge">m_authenticated</code> flag was introduced to the
daemon’s code, which caches authentication once a client has passed certain Polkit
action authentication checks. This cached authentication is shared between the
methods <a href="https://github.com/presire/qSnapper/blob/485a7ee9256c8e25de87a5d69d538e318e49b56d/src/dbusservice/snapshotoperations.cpp#L803"><code class="language-plaintext highlighter-rouge">DeleteSnapshot()</code></a>,
<a href="https://github.com/presire/qSnapper/blob/485a7ee9256c8e25de87a5d69d538e318e49b56d/src/dbusservice/snapshotoperations.cpp#L1318"><code class="language-plaintext highlighter-rouge">RestoreFiles()</code></a> and
<a href="https://github.com/presire/qSnapper/blob/485a7ee9256c8e25de87a5d69d538e318e49b56d/src/dbusservice/snapshotoperations.cpp#L1510"><code class="language-plaintext highlighter-rouge">RestoreFilesDirect()</code></a>. There are two issues
with this approach:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">DeleteSnapshot()</code> uses the “delete-snapshot” action, while the
other methods use the “rollback-snapshot” action. If a caller can
authenticate for “delete-snapshot” then it is also implicitly authenticated
for “rollback-snapshot” and vice versa. This is not how Polkit is intended
to be used. If a system administrator decides to relax the authentication
requirement for “delete-snapshot”, then this automatically implies the
“rollback-snapshot” action now, which are two very different impacts to the
system. We assigned CVE-2026-41048 for this aspect of the issue.</li>
  <li>The implementation of the caching logic assumes that a single interactive
client is talking to the daemon. In fact, arbitrary users can invoke
these methods. If a legitimate client is authenticated for one of the affected
actions once, then arbitrary other users like <code class="language-plaintext highlighter-rouge">nobody</code> are now
also considered authenticated and Polkit will not be invoked anymore. This
authentication bypass allows for a full local root exploit in the context of
the <code class="language-plaintext highlighter-rouge">RestoreFiles()</code> methods, as outlined below in <a href="#issue-other">section
3.6</a>. We assigned CVE-2026-41049 for this aspect of the
issue.</li>
</ul>

<p>We suggested to upstream to drop this form of authentication caching, or
alternatively implement an authentication cache tied to each user and each
Polkit action in question to avoid the issues.</p>

<h2 id="issue-defense-in-depth">3.5) Defense-in-Depth Issues when Restoring Arbitrary Files</h2>

<p>This section discusses Defense-in-Depth issues which remain once all the
more tangible issues discussed above are fixed. The
<a href="https://github.com/presire/qSnapper/blob/485a7ee9256c8e25de87a5d69d538e318e49b56d/src/dbusservice/snapshotoperations.cpp#L1318">restoreFiles()</a> D-Bus method accepts arbitrary
<code class="language-plaintext highlighter-rouge">filePaths</code> as parameter to be restored from a version found in a snapshot.
Clients need to authenticate as an administrator to do this, so there is no
direct attack surface. Let’s consider the path <code class="language-plaintext highlighter-rouge">/var/lib/colord/mapping.db</code> to
be restored, however:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># On openSUSE Tumbleweed this directory is owned and writable by `colord`</span>
<span class="nv">$ </span><span class="nb">ls</span> <span class="nt">-lh</span> /var/lib/colord
drwxr-xr-x 3 colord colord 4.0K Mar 6 2024 <span class="nb">.</span>
</code></pre></div></div>

<p>At the time the qSnapper service <a href="https://github.com/presire/qSnapper/blob/485a7ee9256c8e25de87a5d69d538e318e49b56d/src/dbusservice/snapshotoperations.cpp#L1418">performs the
<code class="language-plaintext highlighter-rouge">chown()</code></a> system call, the <code class="language-plaintext highlighter-rouge">colord</code> service user can
stage a symlink attack in the <code class="language-plaintext highlighter-rouge">colord</code> directory and cause arbitrary files in
the system to receive <code class="language-plaintext highlighter-rouge">colord</code> ownership, allowing a <code class="language-plaintext highlighter-rouge">colord</code> to <code class="language-plaintext highlighter-rouge">root</code>
exploit. A similar issue affects the <a href="https://github.com/presire/qSnapper/blob/485a7ee9256c8e25de87a5d69d538e318e49b56d/src/dbusservice/snapshotoperations.cpp#L1431"><code class="language-plaintext highlighter-rouge">copyRegularFile()</code>
function</a>, where a file on the live file system is
<a href="https://github.com/presire/qSnapper/blob/485a7ee9256c8e25de87a5d69d538e318e49b56d/src/dbusservice/snapshotoperations.cpp#L1721">newly created</a>.</p>

<p>This issue is hard to fix, given the many different file system situations
that the restore operation can end up in. The basic algorithm to address the
problem would need to use low-level system calls like <code class="language-plaintext highlighter-rouge">openat()</code>, <code class="language-plaintext highlighter-rouge">fchown()</code>
and <code class="language-plaintext highlighter-rouge">fstatat()</code> to safely traverse file system paths and inspect any symbolic
links on the way. Given the complexity of the bugfix and limited impact, we
agreed with the upstream author that this fix does not need to be prepared
during the coordinated disclosure period but can be developed in the open
afterwards.</p>

<p>Given the amount of front-line security issues already found in qSnapper in
the course of this review, we refrained from assigning a dedicated CVE for
these Defense-in-Depth issues in this case.</p>

<h2 id="issue-other">3.6) Other Issues</h2>

<p>This section covers a number of other issues that are not fully-fledged
security issues, but still represent weaknesses or possible hardenings for the
qSnapper implementation.</p>

<ul>
  <li>the D-Bus method <a href="https://github.com/presire/qSnapper/blob/485a7ee9256c8e25de87a5d69d538e318e49b56d/src/dbusservice/snapshotoperations.cpp#L1318"><code class="language-plaintext highlighter-rouge">RestoreFiles()</code></a> implements complex
file operations to rollback changes of a file path to the state found in a
snapshot. These file operations allow authenticated clients to achieve nearly
arbitrary file operations:
    <ul>
      <li>recursive deletion of arbitrary paths</li>
      <li>copy of arbitrary files to arbitrary locations</li>
      <li>change of ownership of files to arbitrary users</li>
      <li>change of the file mode to arbitrary values</li>
    </ul>

    <p>This is because the method is not verifying whether the claimed file exists
in the snapshot in the first place and whether the <code class="language-plaintext highlighter-rouge">filePaths</code> contain any
<code class="language-plaintext highlighter-rouge">../</code> directory components to escape the snapshot directory. While this
method is protected by Polkit’s <code class="language-plaintext highlighter-rouge">auth_admin</code> setting, any D-Bus method should
be written conservatively and in such a way that the intended purpose of the
method cannot be bypassed. The very similar method
<a href="https://github.com/presire/qSnapper/blob/485a7ee9256c8e25de87a5d69d538e318e49b56d/src/dbusservice/snapshotoperations.cpp#L1510"><code class="language-plaintext highlighter-rouge">RestoreFilesDirect()</code></a> has the same issues and
also contains a lot of duplicate code which should be merged with the other
method’s code.</p>
  </li>
  <li>the <a href="https://github.com/presire/qSnapper/blob/485a7ee9256c8e25de87a5d69d538e318e49b56d/src/dbusservice/snapshotoperations.cpp#L410">Quit() method</a> can be called by anyone in the system without
authentication. This is kind of a local DoS against an active qSnapper
daemon in use by other users in the system.</li>
  <li>the logfile created in <code class="language-plaintext highlighter-rouge">/var/log/qSnapper</code> will be world-readable, which
could leak sensitive information. For hardening purposes it could be made
private to <code class="language-plaintext highlighter-rouge">root</code>.</li>
</ul>

<h1 id="upstream-bugfixes">4) Upstream Bugfixes</h1>

<p>The upstream author closely followed our suggestions to fix the security
issues described in this report. <a href="https://github.com/presire/qSnapper/releases/tag/v1.3.3">Version 1.3.3</a> of
qSnapper contains the following bugfixes:</p>

<ul>
  <li><a href="https://github.com/presire/qSnapper/commit/a6caf538fca1e678b81b1c85500c3d071a5edc6d">commit a6caf538</a> addresses the following
issues:
    <ul>
      <li>the Polkit <code class="language-plaintext highlighter-rouge">UnixProcess</code> subject is replaced by the <code class="language-plaintext highlighter-rouge">SystemBusName</code>
subject to avoid the race condition during authentication (<a href="#issue-polkit-bypass">issue
3.1</a>).</li>
      <li>the <code class="language-plaintext highlighter-rouge">configName</code> parameter in all D-Bus methods is now carefully verified
and rejected if it allows path traversal (<a href="#issue-path-traversal">issue
3.2</a>).</li>
      <li>the authentication caching is removed (<a href="#issue-auth-caching">issue
3.4</a>).</li>
      <li>the <code class="language-plaintext highlighter-rouge">RestoreFiles</code> methods reject path traversal attempts (see <a href="#issue-other">section
3.6</a>).</li>
    </ul>
  </li>
  <li><a href="https://github.com/presire/qSnapper/commit/f375b74c34e0d07c0edc48f62c02846ad3c9f3ec">commit f375b74c</a> addresses the
following issues:
    <ul>
      <li>a new <code class="language-plaintext highlighter-rouge">view-diff</code> Polkit action is introduced to generate file diffs which
requires <code class="language-plaintext highlighter-rouge">auth_admin</code> authentication to prevent information leaks (<a href="#issue-info-leak">issue
3.3</a>).</li>
      <li>the unauthenticated <code class="language-plaintext highlighter-rouge">Quit</code> D-Bus method was dropped (see <a href="#issue-other">section
3.6</a>).</li>
      <li>log files are now created without world-readable bit (see
<a href="#issue-other">section 3.6</a>).</li>
    </ul>
  </li>
</ul>

<p>The more complex defense-in-depth issues outlined in <a href="#issue-defense-in-depth">section
3.5</a> have been addressed in the open later on
and are contained in <a href="https://github.com/presire/qSnapper/releases/tag/v1.3.4">release v1.3.4</a>.</p>

<h1 id="5-disclosure-process">5) Disclosure Process</h1>

<p>Once we established contact with the upstream author, we had a very
constructive discussion of the coordinated disclosure process. Upstream
quickly acknowledged the issues, opted for coordinated disclosure and
communicated a time schedule when bugfixes were expected to be available for
review and when a bugfix release of qSnapper would be published.
When we agreed on the final bugfixes in the middle of May, we agreed on
choosing an earlier publication date than originally intended.</p>

<p>We want to thank the upstream author for handling this report in a very
professional fashion and for greatly improving the security and robustness of
qSnapper.</p>

<h1 id="6-timeline">6) Timeline</h1>

<table>
  <tbody>
    <tr>
      <td>2026-04-09</td>
      <td>Lacking any other point of contact, we created a <a href="https://github.com/presire/qSnapper/issues/5">public GitHub issue</a> asking for a security contact.</td>
    </tr>
    <tr>
      <td>2026-04-13</td>
      <td>The upstream developer responded in the issue providing an email address for us to reach out to.</td>
    </tr>
    <tr>
      <td>2026-04-13</td>
      <td>We reached out to the developer privately by email providing a detailed report and offering coordinated disclosure.</td>
    </tr>
    <tr>
      <td>2026-04-14</td>
      <td>Due to issues with mail servers our email did not reach the upstream developer. Upstream offered us another email address to try.</td>
    </tr>
    <tr>
      <td>2026-04-15</td>
      <td>We received a reply from the upstream developer confirming the issues and requesting coordinated disclosure. Upstream accepted our offer to assign CVEs for the issues. A preliminary disclosure date of 2026-07-07 was established.</td>
    </tr>
    <tr>
      <td>2026-04-17</td>
      <td>We assigned CVEs and shared them with upstream.</td>
    </tr>
    <tr>
      <td>2026-05-08</td>
      <td>We received a tarball containing the current release candidate code of qSnapper.</td>
    </tr>
    <tr>
      <td>2026-05-11</td>
      <td>We asked the upstream author if individual bugfixes could be supplied instead.</td>
    </tr>
    <tr>
      <td>2026-05-12</td>
      <td>We received the individual patches we asked for.</td>
    </tr>
    <tr>
      <td>2026-05-12</td>
      <td>We provided feedback on the current state of the patches. Apart from minor details and hardening suggestions the patches were already in good shape.</td>
    </tr>
    <tr>
      <td>2026-05-14</td>
      <td>We received updated patches for review and found no further issues. Given the good progress on the bugfixes, an earlier coordinated release date of 2026-05-26 was agreed upon.</td>
    </tr>
    <tr>
      <td>2026-05-26</td>
      <td>As planned, upstream released <a href="https://github.com/presire/qSnapper/releases/tag/v1.3.3">qSnapper 1.3.3</a> containing the bugfixes.</td>
    </tr>
    <tr>
      <td>2026-05-26</td>
      <td>Publication of this report.</td>
    </tr>
  </tbody>
</table>

<h1 id="7-references">7) References</h1>

<ul>
  <li><a href="https://github.com/presire/qSnapper.git">qSnapper GitHub repository</a></li>
  <li><a href="https://github.com/presire/qSnapper/releases/tag/v1.3.3">qSnapper 1.3.3 bugfix release</a></li>
  <li><a href="https://bugzilla.suse.com/show_bug.cgi?id=1261537">openSUSE review bug for qSnapper</a></li>
</ul>

<h1 id="change-history">Change History</h1>

<table>
  <tbody>
    <tr>
      <td>2026-06-26</td>
      <td>Updated <a href="#upstream-bugfixes">section 4</a> with information about release v1.3.4 containing hardening bugfixes.</td>
    </tr>
  </tbody>
</table>]]></content><author><name>&lt;a href=&apos;mailto:matthias.gerstner@suse.de&apos;&gt;Matthias Gerstner&lt;/a&gt;, &lt;a href=&apos;mailto:filippo.bonazzi@suse.com&apos;&gt;Filippo Bonazzi (editor)&lt;/a&gt;</name></author><category term="CVE" /><category term="D-Bus" /><summary type="html"><![CDATA[qSnapper is a GUI frontend for the Snapper utility for managing Btrfs file system snapshots. In versions up to qSnapper 1.3.2 various security issues are found in a privileged D-Bus service component, allowing for Denial-of-Service, authentication bypass, information leaks or even a local root exploit.]]></summary></entry><entry><title type="html">malcontent: Disk Space Exhaustion via Globally Accessible D-Bus API (CVE-2026-44931)</title><link href="https://security.opensuse.org/2026/05/11/malcontent-disk-space-dos.html" rel="alternate" type="text/html" title="malcontent: Disk Space Exhaustion via Globally Accessible D-Bus API (CVE-2026-44931)" /><published>2026-05-11T00:00:00+00:00</published><updated>2026-05-11T00:00:00+00:00</updated><id>https://security.opensuse.org/2026/05/11/malcontent-disk-space-dos</id><content type="html" xml:base="https://security.opensuse.org/2026/05/11/malcontent-disk-space-dos.html"><![CDATA[<h1 class="no_toc" id="table-of-contents">Table of Contents</h1>

<ul id="markdown-toc">
  <li><a href="#introduction" id="markdown-toc-introduction">Introduction</a></li>
  <li><a href="#review-summary" id="markdown-toc-review-summary">Review Summary</a></li>
  <li><a href="#disk-space-exhaustion-issue" id="markdown-toc-disk-space-exhaustion-issue">Disk Space Exhaustion Issue</a></li>
  <li><a href="#upstream-report" id="markdown-toc-upstream-report">Upstream Report</a></li>
  <li><a href="#cve-assignment" id="markdown-toc-cve-assignment">CVE Assignment</a></li>
  <li><a href="#timeline" id="markdown-toc-timeline">Timeline</a></li>
  <li><a href="#references" id="markdown-toc-references">References</a></li>
</ul>

<h1 id="introduction">Introduction</h1>

<p><a href="https://gitlab.freedesktop.org/pwithnall/malcontent">malcontent</a> is a parental control system for the GNOME
desktop environment which allows to restrict access to adult Internet content
and to keep track of and restrict the amount of screen time for children. As
part of the GNOME 50 version update malcontent 0.14.0 was packaged for
openSUSE, <a href="https://bugzilla.suse.com/show_bug.cgi?id=1258140">triggering a review</a> of changes in the package’s
D-Bus methods and Polkit actions.</p>

<p>During this review we identified a local disk space exhaustion attack vector
via one of the newly added D-Bus methods. There is currently no upstream
bugfix available for the issue. The full details about the issue and
communication with upstream will be provided in the following sections.</p>

<h1 id="review-summary">Review Summary</h1>

<p>The <a href="https://bugzilla.suse.com/show_bug.cgi?id=1258140#c3">complexity</a> of malcontent increased a lot
compared to <a href="https://bugzilla.suse.com/show_bug.cgi?id=1177974">the last time</a> we looked into it.
There now exist three different malcontent D-Bus daemons utilizing three
different service user accounts and some additional daemons not providing
D-Bus interfaces on top of that.</p>

<p>Some parts of the user tracking in malcontent suffer from race conditions. We
believe this is acceptable, given that parental controls don’t need to be
strong security boundaries; it is sufficient if the target audience (children)
is not able to bypass the parental controls.</p>

<h1 id="disk-space-exhaustion-issue">Disk Space Exhaustion Issue</h1>

<p>The newly introduced <a href="https://gitlab.freedesktop.org/pwithnall/malcontent/-/blob/0.14.0/libmalcontent-timer/child-timer-service.c?ref_type=tags#L892"><code class="language-plaintext highlighter-rouge">RecordUsage</code> D-Bus method</a> in
<code class="language-plaintext highlighter-rouge">malcontent-timerd</code> is problematic beyond the possibility to bypass parental
controls. It allows arbitrary users in the system to slowly fill up disk space
in <code class="language-plaintext highlighter-rouge">/var/lib/malcontent-timerd</code>. The following shell construct is a simple
reproducer of the issue:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for </span>I <span class="k">in</span> <span class="sb">`</span><span class="nb">seq </span>100000<span class="sb">`</span><span class="p">;</span> <span class="k">do
    </span>gdbus call <span class="nt">-y</span> <span class="nt">-d</span> org.freedesktop.MalcontentTimer1 <span class="se">\</span>
        <span class="nt">-o</span> /org/freedesktop/MalcontentTimer1 <span class="se">\</span>
        <span class="nt">-m</span> org.freedesktop.MalcontentTimer1.Child.RecordUsage <span class="se">\</span>
        <span class="s2">"[(0, 1000, </span><span class="se">\"</span><span class="s2">app</span><span class="se">\"</span><span class="s2">, </span><span class="se">\"</span><span class="s2">org.gnome.MyApp</span><span class="nv">$I</span><span class="se">\"</span><span class="s2">)]"</span>
<span class="k">done</span>
</code></pre></div></div>

<p>The daemon will create an entry for every supposed GNOME app identifier passed
to it in <code class="language-plaintext highlighter-rouge">/var/lib/malcontent-timerd/store/&lt;caller-username&gt;.gvdb</code>. This will
slowly use up the disk space in <code class="language-plaintext highlighter-rouge">/var</code> and therefore is a local
Denial-of-Service attack vector.</p>

<p>To fix the problem, the method call could be restricted to callers in local
active sessions. Furthermore an upper limit of usage entries could be placed
on every user account to prevent excess disk usage.</p>

<h1 id="upstream-report">Upstream Report</h1>

<p>We reported this issue <a href="https://gitlab.freedesktop.org/pwithnall/malcontent/-/work_items/137">privately</a> via the upstream’s
GitLab bug tracker on 2026-02-18 offering coordinated disclosure. We only
received an initial reply a couple of weeks later in which upstream confirmed
the issue but also mentioned that there is a lack of developer resources for
malcontent. At this time we expressed our opinion that a non-disclosure period
would not be strictly necessary since the impact of the issue is not high. We
never received further replies from upstream, so we decided to go public with
this report to avoid wasting more time without a bugfix being developed.</p>

<h1 id="cve-assignment">CVE Assignment</h1>

<p>Due to the lack of replies we could not discuss with upstream whether a CVE
assignment is appropriate for this issue. Given that upstream at least
basically confirmed the issue and there is no bugfix available we assigned
CVE-2026-44931 to track the defect and to make others aware.</p>

<h1 id="timeline">Timeline</h1>

<table>
  <tbody>
    <tr>
      <td>2026-02-18</td>
      <td>We created a <a href="https://gitlab.freedesktop.org/pwithnall/malcontent/-/work_items/137">private issue</a> in the upstream GitLab, offering coordinated disclosure.</td>
    </tr>
    <tr>
      <td>2026-03-11</td>
      <td>Lacking a reaction we pinged the issue asking if anybody were reading it.</td>
    </tr>
    <tr>
      <td>2026-03-11</td>
      <td>An upstream developer responded confirming the issue and pointing out that little developer time is available for malcontent.</td>
    </tr>
    <tr>
      <td>2026-03-23</td>
      <td>We asked how upstream wants to continue regarding coordinated disclosure. We explained that in our view a non-disclosure period is not strictly necessary for the issue and pointed out that the maximum non-disclosure period we can offer is 90 days until 2026-05-19.</td>
    </tr>
    <tr>
      <td>2026-04-09</td>
      <td>Still without an answer we urged upstream once more to come to a decision and a path forward regarding the publication of the issue.</td>
    </tr>
    <tr>
      <td>2026-04-21</td>
      <td>We informed upstream that we would publish the report on our end if no reaction is received by 2026-04-30.</td>
    </tr>
    <tr>
      <td>2026-05-05</td>
      <td>We published our <a href="https://bugzilla.suse.com/show_bug.cgi?id=1258140">Bugzilla bug</a> describing the issue.</td>
    </tr>
    <tr>
      <td>2026-05-08</td>
      <td>We assigned CVE-2026-44931 for the issue and communicated this in the upstream issue.</td>
    </tr>
    <tr>
      <td>2026-05-11</td>
      <td>Publication of this report.</td>
    </tr>
  </tbody>
</table>

<h1 id="references">References</h1>

<ul>
  <li><a href="https://gitlab.freedesktop.org/pwithnall/malcontent">malcontent freedesktop repository</a></li>
  <li><a href="https://gitlab.freedesktop.org/pwithnall/malcontent/-/work_items/137">malcontent upstream private bug about this issue</a></li>
  <li><a href="https://bugzilla.suse.com/show_bug.cgi?id=1258140">openSUSE review bug for malcontent 0.14.0</a></li>
</ul>]]></content><author><name>&lt;a href=&apos;mailto:matthias.gerstner@suse.de&apos;&gt;Matthias Gerstner&lt;/a&gt;, &lt;a href=&apos;mailto:filippo.bonazzi@suse.com&apos;&gt;Filippo Bonazzi (editor)&lt;/a&gt;</name></author><category term="CVE" /><category term="D-Bus" /><summary type="html"><![CDATA[malcontent is a parental control system for the GNOME desktop environment which supports restriction of content access and screen time for children. In version 0.14.0 of malcontent a D-Bus method has been added which allows arbitrary users in the system to fill up the disk space in /var/lib/malcontent-timerd.]]></summary></entry><entry><title type="html">SELinux: Policy Packaging Migration to support Snapshots and Rollbacks</title><link href="https://security.opensuse.org/2026/04/29/selinux-modules-move-to-etc.html" rel="alternate" type="text/html" title="SELinux: Policy Packaging Migration to support Snapshots and Rollbacks" /><published>2026-04-29T00:00:00+00:00</published><updated>2026-04-29T00:00:00+00:00</updated><id>https://security.opensuse.org/2026/04/29/selinux-modules-move-to-etc</id><content type="html" xml:base="https://security.opensuse.org/2026/04/29/selinux-modules-move-to-etc.html"><![CDATA[<h1 class="no_toc" id="table-of-contents">Table of Contents</h1>

<ul id="markdown-toc">
  <li><a href="#0-tldr" id="markdown-toc-0-tldr">0) TL;DR</a></li>
  <li><a href="#1-introduction" id="markdown-toc-1-introduction">1) Introduction</a></li>
  <li><a href="#2-issues-with-current-selinux-policy-packaging" id="markdown-toc-2-issues-with-current-selinux-policy-packaging">2) Issues with current SELinux policy packaging</a></li>
  <li><a href="#3-solution-migration-from-var-to-etc" id="markdown-toc-3-solution-migration-from-var-to-etc">3) Solution: migration from /var to /etc</a>    <ul>
      <li><a href="#packages-involved-in-the-migration" id="markdown-toc-packages-involved-in-the-migration">Packages involved in the migration</a></li>
    </ul>
  </li>
  <li><a href="#4-troubleshooting" id="markdown-toc-4-troubleshooting">4) Troubleshooting</a></li>
  <li><a href="#5-closing-remarks" id="markdown-toc-5-closing-remarks">5) Closing Remarks</a></li>
  <li><a href="#6-references" id="markdown-toc-6-references">6) References</a></li>
  <li><a href="#change-history" id="markdown-toc-change-history">Change History</a></li>
</ul>

<h1 id="0-tldr">0) TL;DR</h1>

<ul>
  <li>The SELinux policy packaging had long-standing issues on systems using BTRFS
snapshots: files under <code class="language-plaintext highlighter-rouge">/var/lib/selinux</code> were not covered by snapshots and
could not be rolled back.</li>
  <li>These files have now been migrated to <code class="language-plaintext highlighter-rouge">/etc/selinux</code>, which allows the SELinux
policy to be fully covered by snapshots.</li>
  <li>The migration applies to openSUSE Tumbleweed and MicroOS systems using
SELinux.</li>
  <li>The migration is transparent to the user and automatic on default setups. If
you have a non-standard filesystem setup, or if you observe issues, read the
full post below.</li>
</ul>

<h1 id="1-introduction">1) Introduction</h1>

<p>SELinux has been the Mandatory Access Control mechanism on openSUSE
distributions such as MicroOS and Leap Micro since 2022, and most recently
openSUSE Tumbleweed <a href="https://news.opensuse.org/2025/02/13/tw-plans-to-adopt-selinux-as-default/">switched the default MAC to SELinux</a> in
February 2025.</p>

<p>The files installed on a system by the SELinux policy package have traditionally
been split between <code class="language-plaintext highlighter-rouge">/var/lib/selinux</code>, <code class="language-plaintext highlighter-rouge">/usr/share/selinux</code> and <code class="language-plaintext highlighter-rouge">/etc/selinux</code>
on most Linux distributions, including openSUSE. The separation across these
directory trees conflicts with the modern Linux concept of atomic/image-based
updates, which in openSUSE has been implemented as a <a href="https://en.opensuse.org/openSUSE:Packaging_Requirements_for_Atomic_and_Image_Update">snapshot and rollback
mechanism based on BTRFS</a>. This mechanism only snapshots
the <code class="language-plaintext highlighter-rouge">/usr</code> and <code class="language-plaintext highlighter-rouge">/etc</code> trees, intended for packages and configuration
respectively. Other directory trees are meant for user files or, as in the case
of <code class="language-plaintext highlighter-rouge">/var</code>, for mutable system state files, and as such are not covered by
snapshots.</p>

<p>Installing SELinux policy files under <code class="language-plaintext highlighter-rouge">/var</code> violates this requirement, and can
result in inconsistency in case of rollbacks: policy files under <code class="language-plaintext highlighter-rouge">/etc</code> and
<code class="language-plaintext highlighter-rouge">/usr</code> would be rolled back, but files under <code class="language-plaintext highlighter-rouge">/var</code> would not be touched.</p>

<p>To resolve this issue, we have migrated SELinux policy files under <code class="language-plaintext highlighter-rouge">/var</code> to
<code class="language-plaintext highlighter-rouge">/etc</code>. The migration is automatic and does not require any user interaction in
standard setups. This blog post explains the issue in more detail, documents the
steps taken to migrate the files, and describes known issues and how to resolve
them.</p>

<h1 id="2-issues-with-current-selinux-policy-packaging">2) Issues with current SELinux policy packaging</h1>

<p>Atomic/image-based update systems have become increasingly relevant in recent
years. In openSUSE, this concept has been realised as both fully transactional
systems (e.g. MicroOS, Leap Micro) and as regular distributions with automatic
snapshots and the possibility to rollback broken updates (e.g. Tumbleweed). In
both of these cases, the traditional SELinux policy packaging causes some
issues.</p>

<p>In case of a rollback of an update containing a change to the SELinux policy, if
there is a mismatch between what was rolled back (<code class="language-plaintext highlighter-rouge">/usr/share/selinux</code> and
<code class="language-plaintext highlighter-rouge">/etc/selinux</code>) and what could not (<code class="language-plaintext highlighter-rouge">/var/lib/selinux</code>), this could lead to
policy build issues, external module installation issues, and could bring the
SELinux policy and the whole system into a state which is difficult to recover
from automatically.</p>

<p>In practice, these issues are very rare, since they require a particular set of
circumstances:</p>
<ol>
  <li>installing a policy update which contains backwards-incompatible changes
(e.g. adding or removing SELinux types, attributes, modules …)</li>
  <li>rolling back this update</li>
  <li>performing some action which requires the SELinux policy items affected by
the rollback, such as rebuilding the policy or an affected external policy
module (either manually or e.g. via RPM package installation).</li>
</ol>

<p>As a matter of policy, these kinds of backwards-incompatible policy updates are
never performed on distributions such as Leap Micro (except possibly during
migration between major distribution versions), thus preventing the issue. On
Tumbleweed and MicroOS these updates can happen, albeit rarely, and when
combined with the probability of a rollback and the further triggering actions
they become exceedingly rare.</p>

<p>Manually reinstalling the SELinux policy package (and any affected external
policy modules) is sufficient to fix the issues, but this is still an
undesirable characteristic of this legacy packaging structure.</p>

<h1 id="3-solution-migration-from-var-to-etc">3) Solution: migration from /var to /etc</h1>

<p>To permanently address the aforementioned issues, we decided to migrate SELinux
policy files under <code class="language-plaintext highlighter-rouge">/var/lib/selinux</code> to <code class="language-plaintext highlighter-rouge">/etc/selinux</code>. As mentioned, this
location was already used for other SELinux policy files, and is covered by the
snapshot and rollback mechanism.</p>

<p>The migration was tracked in <a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1221342">bsc#1221342</a>. After a long
period of automated and manual testing over several months, the migration
was performed in Tumbleweed snapshot
<a href="https://lists.opensuse.org/archives/list/factory@lists.opensuse.org/message/YCAAJRIHH7TECRELF43ZIDNUAEZTF7UM/">20260505</a>.</p>

<p>Some of the challenges encountered during the implementation of the migration
process were:</p>
<ul>
  <li>different rollback and update behaviour on classical and transactional systems
(also transactional-update before version 5 used overlayfs for /etc)</li>
  <li>preserving existing local modifications to the SELinux configuration</li>
  <li>migrating custom modules (installed by packages or manually created by the
user)</li>
  <li>packaging changes or rebuilds of some packages to properly reflect location
changes, including RPM SELinux macros</li>
  <li>cleanup of <code class="language-plaintext highlighter-rouge">/var/lib/selinux</code> once no snapshot is using the old path</li>
  <li>installation on a fresh system, without migrating an existing system</li>
  <li>observability of discrepancies between migrated modules in
<code class="language-plaintext highlighter-rouge">/etc/selinux</code> and last pre-migration state in <code class="language-plaintext highlighter-rouge">/var/lib/selinux</code></li>
</ul>

<p>The migration process is automatic and in most situations will not be visible
to the user, except for information printed in the <code class="language-plaintext highlighter-rouge">zypper</code> output during the
update. The process takes care not only of the migration of policy
modules provided by the system SELinux policy (e.g. <code class="language-plaintext highlighter-rouge">selinux-policy-targeted</code>),
but also of custom modules installed by other packages (see below) and local
modifications to SELinux configuration (booleans, users, ports, …).</p>

<p>To allow the migration to be fully reverted, the process temporarily preserves
the “old” <code class="language-plaintext highlighter-rouge">/var/lib/selinux/</code> directory tree even after the migration. Once no
snapshots are found which still refer to <code class="language-plaintext highlighter-rouge">/var/lib/selinux</code>, the whole directory
tree is safely deleted.</p>

<p>Most of the steps are done during package update, except for the final cleanup
step which is performed on the system after the migration has been completed.
During package update, the migration process will:</p>
<ul>
  <li>print information about the migration process and inform the user if the
system satisfies the migration requirements (root BTRFS subvolume present) or
if a non-standard setup was detected (e.g. <code class="language-plaintext highlighter-rouge">/etc</code> on different BTRFS subvolume
or no BTRFS at all), and what to do in this case.</li>
  <li>check if the system has already been migrated and skip migration in this case
(using marker files in <code class="language-plaintext highlighter-rouge">/etc/selinux/selinux_modules_migrated-*</code>)</li>
  <li>backup the old location (<code class="language-plaintext highlighter-rouge">/var/lib/selinux</code>) to preserve state (marker files
<code class="language-plaintext highlighter-rouge">selinux_modules_pending-*</code> and <code class="language-plaintext highlighter-rouge">temp_selinux_modules_dir_created</code>)</li>
  <li>install package (modules) into the new location (<code class="language-plaintext highlighter-rouge">/etc/selinux</code>)</li>
  <li>copy local changes and custom modules (<code class="language-plaintext highlighter-rouge">*.local</code> files, <code class="language-plaintext highlighter-rouge">200</code>, <code class="language-plaintext highlighter-rouge">400</code> and
<code class="language-plaintext highlighter-rouge">disabled</code> folders) from the old location to the new location, show diff in
case of missing custom modules from <code class="language-plaintext highlighter-rouge">/etc/selinux</code> (marker file
<code class="language-plaintext highlighter-rouge">selinux_modules_migrated-*</code>)</li>
  <li>install cleanup systemd service (<code class="language-plaintext highlighter-rouge">cleanoldsepoldir.service</code>) and
script(<code class="language-plaintext highlighter-rouge">/usr/libexec/selinux/cleanoldsepoldir.sh</code>) to remove the old
<code class="language-plaintext highlighter-rouge">/var/lib/selinux</code> location once no snapshot is using it</li>
</ul>

<p>After package update:</p>
<ul>
  <li>at boot, the <code class="language-plaintext highlighter-rouge">cleanoldsepoldir</code> service checks if any snapshot still requires
<code class="language-plaintext highlighter-rouge">/var/lib/selinux</code>. If not, it removes the directory, and creates marker file
<code class="language-plaintext highlighter-rouge">var_lib_selinux_deleted</code> to stop the <code class="language-plaintext highlighter-rouge">cleanoldsepoldir</code> service from running
again.</li>
  <li>the cleanup script also allows the user to check if there are some custom
SELinux modules missing in the new location, and has some heuristics to find
the RPM packages of non-migrated modules to reinstall them</li>
</ul>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>/usr/libexec/selinux/cleanoldsepoldir.sh <span class="nt">-h</span>
This script checks <span class="k">if </span>it is safe to remove the old /var/lib/selinux directory.

Usage:
  /usr/libexec/selinux/cleanoldsepoldir.sh <span class="o">(</span>Checks snapshots and deletes /var/lib/selinux <span class="k">if </span>safe<span class="o">)</span>
  /usr/libexec/selinux/cleanoldsepoldir.sh <span class="nt">--check-custom-selinux-modules</span> <span class="o">(</span>Checks <span class="k">for </span>unmigrated custom modules<span class="o">)</span>
  /usr/libexec/selinux/cleanoldsepoldir.sh <span class="nt">-h</span>|--help <span class="o">(</span>Displays this <span class="nb">help </span>message<span class="o">)</span>
</code></pre></div></div>

<h2 id="packages-involved-in-the-migration">Packages involved in the migration</h2>

<p>These packages contain the SELinux policy packaging and were the main object of
the migration:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">libsemanage-conf</code> (store-root set to <code class="language-plaintext highlighter-rouge">/etc/selinux/semanage.conf</code>)</li>
  <li><code class="language-plaintext highlighter-rouge">selinux-policy</code> (service <code class="language-plaintext highlighter-rouge">cleanoldsepoldir.service</code> and script
<code class="language-plaintext highlighter-rouge">cleanoldsepoldir.sh</code>)</li>
  <li><code class="language-plaintext highlighter-rouge">selinux-policy-*</code> (actual migration and marker files in <code class="language-plaintext highlighter-rouge">/etc/selinux</code>)</li>
</ul>

<p>All packages which set SELinux booleans were rebuilt:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">selinux-policy-targeted-gaming</code></li>
  <li><code class="language-plaintext highlighter-rouge">selinux-policy-sapenablement</code></li>
  <li><code class="language-plaintext highlighter-rouge">container-selinux</code></li>
  <li><code class="language-plaintext highlighter-rouge">libvirt</code></li>
</ul>

<p>Packages which ship custom SELinux modules were also fixed and rebuilt:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">cockpit-ws-selinux</code></li>
  <li><code class="language-plaintext highlighter-rouge">drbd-selinux</code></li>
  <li><code class="language-plaintext highlighter-rouge">google-guest-oslogin-selinux</code></li>
  <li><code class="language-plaintext highlighter-rouge">swtpm-selinux</code></li>
  <li><code class="language-plaintext highlighter-rouge">tigervnc-selinux</code></li>
  <li><code class="language-plaintext highlighter-rouge">tpm2.0-abrmd-selinux</code></li>
</ul>

<blockquote>
  <p>We tried to identify all packages affected by this migration, but if you
should find other packages in Tumbleweed which need to be migrated, please
report a <a href="https://en.opensuse.org/openSUSE:Bugreport_SELinux">bug</a>.</p>
</blockquote>

<h1 id="4-troubleshooting">4) Troubleshooting</h1>

<ul>
  <li>If you have a non-standard filesystem setup (e.g. using custom BTRFS
subvolumes, not using BTRFS at all, …) the migration may not work for you
fully. You can find out if the migration was successful by checking for the
presence of the directory <code class="language-plaintext highlighter-rouge">/etc/selinux/selinux_modules_migrated-*</code>
corresponding to your installed policy (e.g.
<code class="language-plaintext highlighter-rouge">/etc/selinux/selinux_modules_migrated-targeted</code>). If the directory exists,
the migration has been successful. If not, please open a <a href="https://en.opensuse.org/openSUSE:Bugreport_SELinux">bug</a>.</li>
  <li>If you observe any issues resembling those described in <a href="#2-issues-with-current-selinux-policy-packaging">Section
2</a>, you can resolve them by
reinstalling the <code class="language-plaintext highlighter-rouge">selinux-policy*</code> package and any affected external modules:
    <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>zypper <span class="k">in</span> <span class="nt">-f</span> selinux-policy selinux-policy-targeted
</code></pre></div>    </div>
  </li>
  <li>After the migration, only the last state of <code class="language-plaintext highlighter-rouge">/var/lib/selinux</code> is preserved,
which means that some older snapshots may still be inconsistent with it. If
rolling back to one of these older snapshots is necessary, you can fix the
issues after rolling back by reinstalling the policy and any affected external
modules as detailed above.</li>
</ul>

<h1 id="5-closing-remarks">5) Closing Remarks</h1>

<p>The openSUSE SELinux team is committed to keeping openSUSE users safe with
SELinux, and to fixing problems that SELinux may cause to the community. To
facilitate changes with SELinux we rely on users to work with us and provide
feedback, so that we understand what the current problematic areas are. If you
encounter problems with SELinux feel free to open a <a href="https://en.opensuse.org/openSUSE:Bugreport_SELinux">bug</a> or reach
out over the <a href="https://lists.opensuse.org/archives/list/selinux@lists.opensuse.org/">mailing list</a>.</p>

<h1 id="6-references">6) References</h1>

<ul>
  <li><a href="https://news.opensuse.org/2025/02/13/tw-plans-to-adopt-selinux-as-default/">Tumbleweed Adopts SELinux as Default</a></li>
  <li><a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1221342">Policy migration tracker bug</a></li>
  <li><a href="https://en.opensuse.org/openSUSE:Bugreport_SELinux">openSUSE Bugreport SELinux</a></li>
  <li><a href="https://lists.opensuse.org/archives/list/selinux@lists.opensuse.org/">openSUSE SELinux Mailing List</a></li>
</ul>

<h1 id="change-history">Change History</h1>

<table>
  <tbody>
    <tr>
      <td>2026-05-11</td>
      <td><a href="#3-solution-migration-from-var-to-etc">Section 3</a>: mentioned the specific Tumbleweed snapshot which introduced the migration (<a href="https://lists.opensuse.org/archives/list/factory@lists.opensuse.org/message/YCAAJRIHH7TECRELF43ZIDNUAEZTF7UM/">20260505</a>) and adjusted phrasing.</td>
    </tr>
  </tbody>
</table>]]></content><author><name>Zdenek Kubala, &lt;a href=&apos;mailto:filippo.bonazzi@suse.com&apos;&gt;Filippo Bonazzi (editor)&lt;/a&gt;</name></author><category term="selinux" /><summary type="html"><![CDATA[The traditional SELinux policy location /var/lib/selinux is not supported by the openSUSE snapshot and rollback mechanism. openSUSE is now migrating the policy packaging to locations supported by this mechanism, which will allow for proper policy snapshots and rollbacks.]]></summary></entry><entry><title type="html">plasma-login-manager: Weaknesses in plasmaloginauthhelper (CVE-2026-25710)</title><link href="https://security.opensuse.org/2026/04/27/plasma-login-manager.html" rel="alternate" type="text/html" title="plasma-login-manager: Weaknesses in plasmaloginauthhelper (CVE-2026-25710)" /><published>2026-04-27T00:00:00+00:00</published><updated>2026-04-27T00:00:00+00:00</updated><id>https://security.opensuse.org/2026/04/27/plasma-login-manager</id><content type="html" xml:base="https://security.opensuse.org/2026/04/27/plasma-login-manager.html"><![CDATA[<h1 class="no_toc" id="table-of-contents">Table of Contents</h1>

<ul id="markdown-toc">
  <li><a href="#1-introduction" id="markdown-toc-1-introduction">1) Introduction</a></li>
  <li><a href="#2-helper-overview" id="markdown-toc-2-helper-overview">2) Helper Overview</a></li>
  <li><a href="#3-security-issues" id="markdown-toc-3-security-issues">3) Security Issues</a>    <ul>
      <li><a href="#3a-arbitrary-chown-via-symlink-attack-in-sync-method" id="markdown-toc-3a-arbitrary-chown-via-symlink-attack-in-sync-method">3.a) Arbitrary <code class="language-plaintext highlighter-rouge">chown()</code> via Symlink Attack in <code class="language-plaintext highlighter-rouge">sync()</code> Method</a></li>
      <li><a href="#3b-arbitrary-file-deletion-in-reset-method" id="markdown-toc-3b-arbitrary-file-deletion-in-reset-method">3.b) Arbitrary File Deletion in <code class="language-plaintext highlighter-rouge">reset()</code> Method</a></li>
      <li><a href="#3c-symlink-attack-via-varlibplasmaloginwallpapers-in-save-method" id="markdown-toc-3c-symlink-attack-via-varlibplasmaloginwallpapers-in-save-method">3.c) Symlink Attack via <code class="language-plaintext highlighter-rouge">/var/lib/plasmalogin/wallpapers</code> in <code class="language-plaintext highlighter-rouge">save()</code> Method</a></li>
      <li><a href="#3d-missing-integrity-check-of-configuration-data-in-save-method" id="markdown-toc-3d-missing-integrity-check-of-configuration-data-in-save-method">3.d) Missing Integrity Check of Configuration Data in <code class="language-plaintext highlighter-rouge">save()</code> Method</a></li>
      <li><a href="#3e-lack-of-file-descriptor-and-file-size-verification-in-save-method" id="markdown-toc-3e-lack-of-file-descriptor-and-file-size-verification-in-save-method">3.e) Lack of File Descriptor and File Size Verification in <code class="language-plaintext highlighter-rouge">save()</code> Method</a></li>
    </ul>
  </li>
  <li><a href="#4-suggested-fixes" id="markdown-toc-4-suggested-fixes">4) Suggested Fixes</a></li>
  <li><a href="#5-severity" id="markdown-toc-5-severity">5) Severity</a></li>
  <li><a href="#6-upstream-bugfix" id="markdown-toc-6-upstream-bugfix">6) Upstream Bugfix</a></li>
  <li><a href="#7-cve-assignment" id="markdown-toc-7-cve-assignment">7) CVE Assignment</a></li>
  <li><a href="#8-timeline" id="markdown-toc-8-timeline">8) Timeline</a></li>
  <li><a href="#9-references" id="markdown-toc-9-references">9) References</a></li>
</ul>

<h1 id="1-introduction">1) Introduction</h1>

<p>In recent releases of the KDE desktop environment a fork of the <a href="https://www.github.com/sddm/sddm">SDDM display
manager</a> called <a href="https://invent.kde.org/plasma/plasma-login-manager">plasma-login-manager</a> has been
integrated. As usual this led to <a href="https://bugzilla.suse.com/show_bug.cgi?id=1260039">a review</a> in our team for the
privileged D-Bus components contained in the package. While most of the code
<a href="https://bugzilla.suse.com/show_bug.cgi?id=1260402">remains the same</a>, the new upstream added a <a href="https://invent.kde.org/plasma/plasma-login-manager/-/blob/v6.6.2/src/frontend/kcm/auth/plasmaloginauthhelper.cpp?ref_type=tags">privileged D-Bus
helper</a> called <code class="language-plaintext highlighter-rouge">plasmaloginauthhelper</code>, which suffers from
<a href="https://bugzilla.suse.com/show_bug.cgi?id=1260930">defense-in-depth security issues</a>. The full details of the
issues will be discussed in the following sections.</p>

<p>For this review we looked into plasma-login-manager <a href="https://invent.kde.org/plasma/plasma-login-manager/-/tree/v6.6.2?ref_type=tags">version
6.6.2</a>.</p>

<h1 id="2-helper-overview">2) Helper Overview</h1>

<p><code class="language-plaintext highlighter-rouge">plasmaloginauthhelper</code> makes the D-Bus interface
“org.kde.kcontrol.kcmplasmalogin” accessible to all users in the system via
the D-Bus system bus. It offers three actions <code class="language-plaintext highlighter-rouge">sync()</code>, <code class="language-plaintext highlighter-rouge">reset()</code> and <code class="language-plaintext highlighter-rouge">save()</code>
which are all by default protected by Polkit’s <code class="language-plaintext highlighter-rouge">auth_admin</code> setting.</p>

<p>These methods allow to manage configuration data stored in the home directory
of the <code class="language-plaintext highlighter-rouge">plasmalogin</code> service user, which has a preset of <code class="language-plaintext highlighter-rouge">/var/lib/plasmalogin</code>.
The helper runs with full root privileges and interprets various
client-supplied data. The <code class="language-plaintext highlighter-rouge">plasmalogin</code> home directory has the following
permissions:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>drwxr-x--- 5 plasmalogin plasmalogin 4.0K Mar 24 13:25
</code></pre></div></div>

<p>Actually this helper is also a kind of fork of a helper found in the
<code class="language-plaintext highlighter-rouge">sddm-kcm</code> repository, which we covered <a href="/2024/04/02/kde6-dbus-polkit.html#problematic-file-system-operations-in-sddm-kcm6">in a previous
report</a>. It seems the codebase has not improved since then,
but rather additional attack surface has been added in the meantime.</p>

<h1 id="3-security-issues">3) Security Issues</h1>

<h2 id="3a-arbitrary-chown-via-symlink-attack-in-sync-method">3.a) Arbitrary <code class="language-plaintext highlighter-rouge">chown()</code> via Symlink Attack in <code class="language-plaintext highlighter-rouge">sync()</code> Method</h2>

<p>In the <a href="https://invent.kde.org/plasma/plasma-login-manager/-/blob/v6.6.2/src/frontend/kcm/auth/plasmaloginauthhelper.cpp?ref_type=tags#L61"><code class="language-plaintext highlighter-rouge">sync()</code> method</a> the helper service naively performs
<a href="https://invent.kde.org/plasma/plasma-login-manager/-/blob/v6.6.2/src/frontend/kcm/auth/plasmaloginauthhelper.cpp?ref_type=tags#L85"><code class="language-plaintext highlighter-rouge">chown()</code> calls</a> on files located in the service user’s home
directory (<code class="language-plaintext highlighter-rouge">/var/lib/plasmalogin</code>), allowing a <code class="language-plaintext highlighter-rouge">plasmalogin</code> to <code class="language-plaintext highlighter-rouge">root</code>
exploit.</p>

<p>The <code class="language-plaintext highlighter-rouge">chown()</code> is performed for the paths <code class="language-plaintext highlighter-rouge">$PLASMALOGIN_HOME/.config</code>,
<code class="language-plaintext highlighter-rouge">$PLASMALOGIN_HOME/.config/fontconfig</code> as well for a list of configuration
files like <code class="language-plaintext highlighter-rouge">plasmarc</code> placed into <code class="language-plaintext highlighter-rouge">$PLASMALOGIN_HOME/.config</code>.</p>

<p>A compromised <code class="language-plaintext highlighter-rouge">plasmalogin</code> service account can place symbolic links here to
direct the <code class="language-plaintext highlighter-rouge">chown()</code> to arbitrary files in the system. After the <code class="language-plaintext highlighter-rouge">chown()</code> the
helper writes client-supplied content into these files, which will also end up
in arbitrary files in case of a symlink attack.</p>

<p>This method’s logic would also allow deletion of certain files like <code class="language-plaintext highlighter-rouge">plasmarc</code>
in arbitrary directories, would the <a href="https://invent.kde.org/plasma/plasma-login-manager/-/blob/v6.6.2/src/frontend/kcm/auth/plasmaloginauthhelper.cpp?ref_type=tags#L99">relevant
statement</a> in the service implementation not lack the
final filename component in the path construction:</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">QFile</span><span class="p">(</span><span class="n">homeDir</span> <span class="o">+</span> <span class="n">QStringLiteral</span><span class="p">(</span><span class="s">"/.config/"</span><span class="p">)).</span><span class="n">remove</span><span class="p">();</span>
</code></pre></div></div>

<p>Thus this removal logic doesn’t work at all at the moment, since it attempts
to remove the <code class="language-plaintext highlighter-rouge">.config</code> directory instead of the actual configuration files.</p>

<h2 id="3b-arbitrary-file-deletion-in-reset-method">3.b) Arbitrary File Deletion in <code class="language-plaintext highlighter-rouge">reset()</code> Method</h2>

<p>In the <a href="https://invent.kde.org/plasma/plasma-login-manager/-/blob/v6.6.2/src/frontend/kcm/auth/plasmaloginauthhelper.cpp?ref_type=tags#L125"><code class="language-plaintext highlighter-rouge">reset()</code> method</a> the paths <code class="language-plaintext highlighter-rouge">$PLASMALOGIN_HOME/.cache</code> and
<code class="language-plaintext highlighter-rouge">$PLASMALOGIN_HOME/.config/fontconfig</code> are recursively deleted. For this
purpose the Qt API <code class="language-plaintext highlighter-rouge">QDir::removeRecursively()</code> is used. <a href="https://github.com/qt/qtbase/blob/90b845d15ffb97693dba527385db83510ebd121a/src/corelib/io/qdir.cpp#L1666">The
implementation</a> of this function follows symbolic links even in the
final path component, which means that a compromised <code class="language-plaintext highlighter-rouge">plasmalogin</code> service
user can leverage this logic to achieve the deletion of arbitrary directory
trees in the system.</p>

<h2 id="3c-symlink-attack-via-varlibplasmaloginwallpapers-in-save-method">3.c) Symlink Attack via <code class="language-plaintext highlighter-rouge">/var/lib/plasmalogin/wallpapers</code> in <code class="language-plaintext highlighter-rouge">save()</code> Method</h2>

<p>In the <a href="https://invent.kde.org/plasma/plasma-login-manager/-/blob/v6.6.2/src/frontend/kcm/auth/plasmaloginauthhelper.cpp?ref_type=tags#L161"><code class="language-plaintext highlighter-rouge">save()</code> method</a> the path <code class="language-plaintext highlighter-rouge">/var/lib/plasmalogin/wallpapers</code>
is <a href="https://invent.kde.org/plasma/plasma-login-manager/-/blob/v6.6.2/src/frontend/kcm/auth/plasmaloginauthhelper.cpp?ref_type=tags#L195">created and opened by root</a>, using a system call
sequence affected by a race condition. A compromised <code class="language-plaintext highlighter-rouge">plasmalogin</code> user can
replace the directory by a symbolic link in time for the service to write
wallpaper files to arbitrary locations in the system, leading to local
Denial-of-Service (DoS) and integrity violation.</p>

<p>In this spot the helper employs a low-level <a href="https://invent.kde.org/plasma/plasma-login-manager/-/blob/v6.6.2/src/frontend/kcm/auth/plasmaloginauthhelper.cpp?ref_type=tags#L225"><code class="language-plaintext highlighter-rouge">openat2()</code> system
call</a> to avoid symbolic link resolution, but this only
applies to the actual files placed within the wallpaper directory, not to the
directory itself, which is naively opened before that.</p>

<h2 id="3d-missing-integrity-check-of-configuration-data-in-save-method">3.d) Missing Integrity Check of Configuration Data in <code class="language-plaintext highlighter-rouge">save()</code> Method</h2>

<p>In the <code class="language-plaintext highlighter-rouge">save()</code> method the contents of the file <code class="language-plaintext highlighter-rouge">/etc/plasmalogin.conf</code> can be
<a href="https://invent.kde.org/plasma/plasma-login-manager/-/blob/v6.6.2/src/frontend/kcm/auth/plasmaloginauthhelper.cpp?ref_type=tags#L169">completely controlled by the caller</a>. Since this
method is protected by <code class="language-plaintext highlighter-rouge">auth_admin</code> Polkit authentication this is basically
acceptable, but there is not even an integrity or syntax check of the data,
the method blindly forwards whatever the client passes to it into this file,
without a maximum size limit or any sanity checks. While this is not directly a
security issue it is a lack of robustness, because the D-Bus service is
responsible for maintaining a sane structure of the privileged configuration
file, preventing a broken system e.g. in case of buggy clients.</p>

<h2 id="3e-lack-of-file-descriptor-and-file-size-verification-in-save-method">3.e) Lack of File Descriptor and File Size Verification in <code class="language-plaintext highlighter-rouge">save()</code> Method</h2>

<p>For the actual wallpaper files, <a href="/2024/08/13/summer-spotlight.html#kde6-release-final-touches-and-improvements">file descriptor passing</a>
is employed, which is good. There is no upper limit enforced on the amount of
data placed into the wallpapers directory, however, which allows to exhaust
disk space in <code class="language-plaintext highlighter-rouge">/var/lib/plasmalogin</code>.</p>

<p>Even file descriptors passed from clients should be verified to check whether
they refer to regular files and have no unexpected file flags set. This
verification is missing.</p>

<h1 id="4-suggested-fixes">4) Suggested Fixes</h1>

<p>We suggested the following fixes to upstream:</p>

<ul>
  <li>Foremost the helper should drop privileges to the <code class="language-plaintext highlighter-rouge">plasmalogin</code> user before
performing any file system operations in <code class="language-plaintext highlighter-rouge">/var/lib/plasmalogin</code>, thereby
eliminating all symlink attack surface. There still remains
Denial-of-Service (DoS) attack surface if the service user places e.g. a named
FIFO pipe somewhere. Avoiding this requires careful inspection of each path
component by the service before opening it.</li>
  <li>The helper should verify the structure and size of data written to
<code class="language-plaintext highlighter-rouge">/etc/plasmalogin.conf</code>.</li>
  <li>The helper should place a limit on the maximum amount of space which
may be used for wallpapers in the <code class="language-plaintext highlighter-rouge">plasmalogin</code> user’s home directory.</li>
  <li>The helper should verify the type and flags of file descriptors passed
by the client. The descriptors should not have special file types and
they should not have any unexpected flags like <code class="language-plaintext highlighter-rouge">O_PATH</code> set.</li>
</ul>

<h1 id="5-severity">5) Severity</h1>

<p>None of the issues in this report is exploitable in a default installation of
<code class="language-plaintext highlighter-rouge">plasma-login-manager</code>. Most of the problems affect the situation when the
<code class="language-plaintext highlighter-rouge">plasmalogin</code> service user is compromised and thus affect defense-in-depth.</p>

<p>It is conceivable, however,  that some actions in the helper, like the
wallpaper management, could be reduced to lesser authentication requirements
like a Polkit <code class="language-plaintext highlighter-rouge">yes</code> setting for locally logged-in users in the future, be it
due to upstream changes or due to choices made by system integrators. Then
further problems like disk space exhaustion by other unprivileged users could
sneak in as well.</p>

<p>Based on the high severity of the defense-in-depth issues shown in this
report, our assessment is that there is effectively no separation between
<code class="language-plaintext highlighter-rouge">root</code> and the <code class="language-plaintext highlighter-rouge">plasmalogin</code> service user account.</p>

<h1 id="6-upstream-bugfix">6) Upstream Bugfix</h1>

<p>At this time there is no bugfix available by upstream, but a security fix
is planned for the next Plasma release on May 12. We have not been involved in
upstream’s bugfix process so far and have no knowledge about the approach that
will be taken to address the issues from this report.</p>

<h1 id="7-cve-assignment">7) CVE Assignment</h1>

<p>We suggested a single CVE assignment relating to the lack of privilege drop of
the D-Bus service, which is the root cause of most of the issues described in
this report. In coordination with upstream we assigned CVE-2026-25710 and
shared it with them to track these defects.</p>

<h1 id="8-timeline">8) Timeline</h1>

<table>
  <tbody>
    <tr>
      <td>2026-03-30</td>
      <td>We reached out to security@kde.org with a report of the problems, offering coordinated disclosure. We stated that in our eyes, due to the issues being restricted to defense-in-depth, an embargo would not be strictly necessary.</td>
    </tr>
    <tr>
      <td>2026-03-30</td>
      <td>Upstream provided a short reply, asking for a CVE assignment.</td>
    </tr>
    <tr>
      <td>2026-03-31</td>
      <td>We assigned CVE-2026-25710 and shared it with upstream.</td>
    </tr>
    <tr>
      <td>2026-04-13</td>
      <td>Lacking a more detailed response from upstream we asked once more whether they would like to perform coordinated disclosure and what the desired coordinated release date (CRD) would be in that case.</td>
    </tr>
    <tr>
      <td>2026-04-13</td>
      <td>We received a reply from upstream stating that no coordinated disclosure would be necessary and bugfixes would be published via public pull requests soon in expectation of a security release on May 12.</td>
    </tr>
    <tr>
      <td>2026-04-13</td>
      <td>To be sure we asked upstream once more whether they agreed to us publishing the report right away.</td>
    </tr>
    <tr>
      <td>2026-04-20</td>
      <td>Lacking a response and with no visible publication on upstream’s end we asked once more if publication on our end would be acceptable for them.</td>
    </tr>
    <tr>
      <td>2026-04-21</td>
      <td>We received a response confirming that we were allowed to publish right away.</td>
    </tr>
  </tbody>
</table>

<h1 id="9-references">9) References</h1>

<ul>
  <li><a href="/2024/04/02/kde6-dbus-polkit.html#problematic-file-system-operations-in-sddm-kcm6">blog post about issues in sddm-kcm6</a></li>
  <li><a href="/2024/08/13/summer-spotlight.html#kde6-release-final-touches-and-improvements">blog post about the introduction of file descriptor passing in kauth</a></li>
  <li><a href="https://bugzilla.suse.com/show_bug.cgi?id=1260930">openSUSE plasma-login-manager KCM helper review bug</a></li>
  <li><a href="https://invent.kde.org/plasma/plasma-login-manager/-/blob/v6.6.2/src/frontend/kcm/auth/plasmaloginauthhelper.cpp?ref_type=tags">plasmaloginauthhelper code</a></li>
  <li><a href="https://invent.kde.org/plasma/plasma-login-manager">plasma-login-manager repository</a></li>
</ul>]]></content><author><name>&lt;a href=&apos;mailto:matthias.gerstner@suse.de&apos;&gt;Matthias Gerstner&lt;/a&gt;, &lt;a href=&apos;mailto:filippo.bonazzi@suse.com&apos;&gt;Filippo Bonazzi (editor)&lt;/a&gt;</name></author><category term="CVE" /><category term="D-Bus" /><summary type="html"><![CDATA[plasma-login-manager, a new display manager component in KDE, contains a privileged D-Bus helper which suffers from defense-in-depth issues allowing the plasmalogin service user to escalate to root in various ways.]]></summary></entry><entry><title type="html">SUSE Security Team Spotlight Winter 2025/2026</title><link href="https://security.opensuse.org/2026/04/20/winter-spotlight.html" rel="alternate" type="text/html" title="SUSE Security Team Spotlight Winter 2025/2026" /><published>2026-04-20T00:00:00+00:00</published><updated>2026-04-20T00:00:00+00:00</updated><id>https://security.opensuse.org/2026/04/20/winter-spotlight</id><content type="html" xml:base="https://security.opensuse.org/2026/04/20/winter-spotlight.html"><![CDATA[<h1 class="no_toc" id="table-of-contents">Table of Contents</h1>

<ul id="markdown-toc">
  <li><a href="#1-introduction" id="markdown-toc-1-introduction">1) Introduction</a></li>
  <li><a href="#section-systemd" id="markdown-toc-section-systemd">2) systemd v258 - v260 Continued Reviews of D-Bus and Varlink Changes</a></li>
  <li><a href="#section-snap" id="markdown-toc-section-snap">3) snapd: Follow-up Audit for Transparent Inclusion of Snap Systemd Services</a></li>
  <li><a href="#section-bootkitd" id="markdown-toc-section-bootkitd">4) bootkitd: D-Bus Service for Manipulating Bootloader Configuration</a></li>
  <li><a href="#section-libpgpr" id="markdown-toc-section-libpgpr">5) libpgpr: RPM PGP Signature Parsing Library</a></li>
  <li><a href="#section-gdm" id="markdown-toc-section-gdm">6) GDM: Changes and Additions in Release 50</a></li>
  <li><a href="#section-rtkit" id="markdown-toc-section-rtkit">7) rtkit: D-Bus Service to Support Unprivileged Realtime Scheduling</a></li>
  <li><a href="#section-steamos" id="markdown-toc-section-steamos">8) SteamOS Package Additions</a>    <ul>
      <li><a href="#jupiter-fan-control" id="markdown-toc-jupiter-fan-control">jupiter-fan-control</a></li>
      <li><a href="#gamescope-session-steam-factory-reset" id="markdown-toc-gamescope-session-steam-factory-reset">gamescope-session-steam-factory-reset</a></li>
      <li><a href="#jupiter-hw-support" id="markdown-toc-jupiter-hw-support">jupiter-hw-support</a></li>
    </ul>
  </li>
  <li><a href="#section-deepin" id="markdown-toc-section-deepin">9) Revisit of Deepin Desktop D-Bus Services after Removal from openSUSE</a>    <ul>
      <li><a href="#backlight-helper" id="markdown-toc-backlight-helper">Backlight Helper</a></li>
      <li><a href="#accounts-service" id="markdown-toc-accounts-service">Accounts Service</a></li>
      <li><a href="#summary" id="markdown-toc-summary">Summary</a></li>
    </ul>
  </li>
  <li><a href="#10-conclusion" id="markdown-toc-10-conclusion">10) Conclusion</a></li>
</ul>

<h1 id="1-introduction">1) Introduction</h1>

<p>The winter months have passed for us and as usual we want to give you an
overview of what topics our team covered in the area of code reviews during
this time. We did not publish any dedicated security reports for a while, after
we had to deal with a little burst of publications at the beginning of the
year. Still we haven’t been idle during this time and looked into various
packages and components, which we will cover in this post.</p>

<p>The <a href="#section-systemd">next section</a> discusses continued review efforts
surrounding new systemd releases. <a href="#section-snap">Section 3</a> covers a follow-up
audit of changes in the Snap package manager. <a href="#section-bootkitd">Section 4</a>
looks at <code class="language-plaintext highlighter-rouge">bootkitd</code>, a D-Bus service for managing bootloader configuration.
<a href="#section-libpgpr">Section 5</a> deals with <code class="language-plaintext highlighter-rouge">libpgpr</code>, a signature parsing library
which was pulled out of the RPM package manager codebase. <a href="#section-gdm">Section
6</a> is about changes we reviewed in a new release of GNOME display
manager (GDM). <a href="#section-rtkit">Section 7</a> contains a review report about the
<code class="language-plaintext highlighter-rouge">rtkit</code> real-time scheduling D-Bus service. <a href="#section-steamos">Section 8</a>
provides an insight into efforts to package SteamOS components for openSUSE
Tumbleweed. <a href="#section-deepin">Section 9</a> looks into an attempt to get Deepin
desktop components back into openSUSE.</p>

<h1 id="section-systemd">2) systemd v258 - v260 Continued Reviews of D-Bus and Varlink Changes</h1>

<p>We already gave an insight into our efforts of reviewing changes in systemd
v258 in <a href="/2026/01/14/autumn-spotlight.html#section-systemd">our previous spotlight post</a>. Meanwhile systemd
upstream has established a new release model leading to more frequent releases
and backports of new features into existing stable branches. This has caused an
increase of review requests in our team, as can be seen by the following list
of review bugs we received since the v258 version release:</p>

<ul>
  <li><a href="https://bugzilla.suse.com/show_bug.cgi?id=1257388">bsc#1257388: follow-up review of backported code in systemd 258.3</a></li>
  <li><a href="https://bugzilla.suse.com/show_bug.cgi?id=1257943">bsc#1257943: follow-up review of backported code in systemd 258.4</a></li>
  <li><a href="https://bugzilla.suse.com/show_bug.cgi?id=1255368">bsc#1255368: review of changes introduced in systemd 259</a></li>
  <li><a href="https://bugzilla.suse.com/show_bug.cgi?id=1259318">bsc#1259318: review of changes introduced in systemd 260</a></li>
</ul>

<p>The review of changes in systemd 260 has just been finished and the new
version is about to become available in openSUSE Tumbleweed soon. The
backports into stable 258 branches have been easy to review so far, since they
are mostly clean cherry-pick merges of changes reviewed by us earlier already.</p>

<p>So far we did not find any issues in the continued changes in systemd, but it
remains a challenging review target especially in the area of virtual machine
and container APIs, as we have explained in earlier posts on the topic.</p>

<h1 id="section-snap">3) snapd: Follow-up Audit for Transparent Inclusion of Snap Systemd Services</h1>

<p>After we <a href="/2026/01/14/autumn-spotlight.html#section-snapd">accepted snap</a> into openSUSE Tumbleweed a while ago we
received a <a href="https://bugzilla.suse.com/show_bug.cgi?id=1256175">follow-up review request</a>, which revolves around a
feature to transparently make systemd services available which have been
installed via Snaps.</p>

<p>We have accepted the change, but asked the packagers to include a notice in
the package informing openSUSE users that systemd services installed via Snaps
are not covered by the security review processes of SUSE product security.</p>

<h1 id="section-bootkitd">4) bootkitd: D-Bus Service for Manipulating Bootloader Configuration</h1>

<p><a href="https://github.com/Nykseli/bootkitd">Bootkitd</a> is a D-Bus service for programmatically managing
bootloader configuration. We received <a href="https://bugzilla.suse.com/show_bug.cgi?id=1256421">a review request</a> for
its inclusion into openSUSE Tumbleweed. The service is implemented in the Rust
programming language and is a simple case regarding security, since it is only
accessible by <code class="language-plaintext highlighter-rouge">root</code>. Thus no privilege boundaries are crossed and privilege
escalation is not a concern.</p>

<h1 id="section-libpgpr">5) libpgpr: RPM PGP Signature Parsing Library</h1>

<p><a href="https://github.com/rpm-software-management/libpgpr"><code class="language-plaintext highlighter-rouge">libpgpr</code></a> is a library which has been recently separated from
the main RPM package manager codebase. Its purpose is the parsing and
verification of PGP signatures as they are found embedded in RPM files. Given
the sensitive nature of PGP cryptography and potentially crafted input data,
we have been asked <a href="https://bugzilla.suse.com/show_bug.cgi?id=1257996">to check the security</a> of this library.</p>

<p>The library consists of a legacy C codebase living up to the C90 standard.
The library API is not well documented and not very consistent at the moment.
At the same time the code is concerned with memory management and binary data
structure parsing of high complexity. These shortcomings notwithstanding, the
implementation seems to have matured over time and we believe there are
currently no major errors to be found when processing untrusted data.</p>

<p>In our opinion, the biggest danger regarding security in this codebase will
likely be future changes which might introduce regressions. Also users of the
library won’t easily know what to expect of the API, since requirements are
not clearly marked (e.g. which parameters are optional, when memory ownership
transfers happen and so on).</p>

<p>We provided <a href="https://github.com/rpm-software-management/libpgpr/pull/1/changes/0310a46f661ed9059eb0fb5ab8686ee6f761b2cc">comprehensive comments</a> on the
codebase to upstream, suggesting various refactoring, improvements and test
coverage to bring the project up to a more modern standard.</p>

<h1 id="section-gdm">6) GDM: Changes and Additions in Release 50</h1>

<p>In February our openSUSE Gnome Display Manager (GDM) maintainers started
preparing the upgrade for release 50, which was in Beta testing at the time,
but should be fully released by now. The new version triggered <a href="https://bugzilla.suse.com/show_bug.cgi?id=1258025">a follow-up
review</a> of D-Bus and Polkit related features in GDM.</p>

<p>GDM is tightly integrated with GNOME remote desktop (GRD) these days and the
changes we’ve seen here are related to this integration. The differences
compared to the previous version of GDM have been small in the area of D-Bus
and Polkit, though, and no problematic additional attack surface has been
added in this version.</p>

<h1 id="section-rtkit">7) rtkit: D-Bus Service to Support Unprivileged Realtime Scheduling</h1>

<p>The <a href="https://gitlab.freedesktop.org/pipewire/rtkit">rtkit daemon</a> has been around on Linux distributions for
a long time. Its purpose it to allow unprivileged programs in the system to
make use of real-time scheduling features in a controlled fashion. Linux
offers <a href="https://man7.org/linux/man-pages/man7/sched.7.html">two real-time scheduling policies</a> <code class="language-plaintext highlighter-rouge">SCHED_RR</code> and
<code class="language-plaintext highlighter-rouge">SCHED_FIFO</code>, which perform Round-robin or First-in First-out scheduling
respectively. Rogue processes running under one of these policies can easily
lock up the complete system due to no other userspace threads being scheduled
by the kernel anymore. For this reason, only tasks holding the <code class="language-plaintext highlighter-rouge">CAP_SYS_NICE</code>
capability (usually only <code class="language-plaintext highlighter-rouge">root</code>) are allowed to assign these scheduling
settings.</p>

<p>This is where <code class="language-plaintext highlighter-rouge">rtkit</code> comes in: it offers a D-Bus interface to allow
unprivileged processes to enjoy real-time scheduling features while being
under supervision of the <code class="language-plaintext highlighter-rouge">rtkit</code> daemon to prevent any negative side effects.</p>

<p>In a recent update of <code class="language-plaintext highlighter-rouge">rtkit</code> to version 0.14, changes in its D-Bus
configuration triggered a <a href="https://bugzilla.suse.com/show_bug.cgi?id=1258681">follow-up review</a> after over a decade
since our team last looked at it. <code class="language-plaintext highlighter-rouge">rtkit</code> is installed and running (or
activatable) by default on a number of Linux distributions like openSUSE,
Debian or Fedora. Due to this prevalence of <code class="language-plaintext highlighter-rouge">rtkit</code> in Linux systems, the
inherent danger of a local Denial-of-Service and in light of the amount of
time passed since the last full review, we thought it wise to have a fresh look
at the service’s implementation.</p>

<p>The <code class="language-plaintext highlighter-rouge">rtkit</code> D-Bus configuration follows a bit of an unusual approach by
maintaining <a href="https://gitlab.freedesktop.org/pipewire/rtkit/-/blob/v0.14/org.freedesktop.RealtimeKit1.conf?ref_type=tags#L15">a deny list</a> of methods which may not be
invoked by non-root users. This is not ideal, since additional methods will
automatically be accessible to all users in the system, should a developer
forget to update the deny list. At the moment no problems exist in this area,
however.</p>

<p>The blacklisted D-Bus methods which are only accessible to <code class="language-plaintext highlighter-rouge">root</code> affect the
global state of the daemon. The remaining D-Bus methods are used to request
real-time scheduling for caller-owned processes. These methods are
additionally protected by Polkit authentication; the related Polkit actions
are set to <code class="language-plaintext highlighter-rouge">yes</code> for local users in an active session, meaning that local
interactive users can invoke them without authentication.</p>

<p>The <a href="https://gitlab.freedesktop.org/pipewire/rtkit/-/blob/v0.14/rtkit-daemon.c?ref_type=tags#L1261">implementation of Polkit authentication</a> relies on
rather complex custom code based on the “unix-process” Polkit authentication
subject. This subject is often affected by race conditions and the D-Bus
“system-bus-name” subject should rather be used. In this case the use of
“unix-process” is acceptable, since the request includes not only the client’s
PID but also its process start time and UID, which is retrieved from the UNIX
domain socket D-Bus connection. Thus there should be no way that race
conditions can be exploited in a way that the client is mistaken for <code class="language-plaintext highlighter-rouge">root</code>,
for example.</p>

<p>The actual application of real-time scheduling to a client’s target
process is highly affected by race conditions, due to the retrieval of data
from <code class="language-plaintext highlighter-rouge">/proc/&lt;pid&gt;</code> and the fact that processes can disappear and/or be
repurposed at any time. The developers are obviously aware of the potential
issues, since they verify the target process’s properties <a href="https://gitlab.freedesktop.org/pipewire/rtkit/-/blob/v0.14/rtkit-daemon.c?ref_type=tags#L763">before and after
changing its scheduling properties</a>. Such detection
of a race condition after the fact is problematic when the risk is a lockup of
the whole system.</p>

<p>Due to this, the daemon also maintains <a href="https://gitlab.freedesktop.org/pipewire/rtkit/-/blob/v0.14/rtkit-daemon.c?ref_type=tags#L1973">a watchdog and a canary
thread</a> to counteract any unexpected effects of
unprivileged real-time scheduling. The watchdog runs at the highest real-time
scheduling priority and periodically monitors whether the canary thread, which
is running with low scheduling priority, is still being scheduled. If a stall
is detected, then the watchdog thread removes the real-time scheduling
settings from all registered client tasks to recover the system. Additionally
the daemon monitors the amount of requests individual users are sending,
and blocks them if a threshold is exceeded.</p>

<p>It is clear that the implementation of this service is confronted with various
uncertainties and it tries to make up for them. The overall result is not
ideal but should be good enough to prevent major security issues. An
improvement to the design could be to obtain a directory file descriptor for
<code class="language-plaintext highlighter-rouge">/proc/&lt;pid&gt;</code> of the target process, verify the process’s credentials and
further on only use the directory file descriptor anymore for accessing
process data. Explicit PID file descriptors might also help in some other
spots these days (they can also be used for authentication with Polkit now,
for example).</p>

<h1 id="section-steamos">8) SteamOS Package Additions</h1>

<p>There is continued effort by community packagers to bring SteamOS-related
components to openSUSE. We already covered <a href="/2025/10/01/summer-spotlight.html#10-steam-powerbuttond-insecure-operation-in-home-directories">one of these
components</a> in one of last year’s spotlight posts.
This winter we received three additional review requests in this area.
Packaging these components is often difficult, because the programs use
fixed non-standard paths and approaches that don’t fit well into a
general-purpose Linux distribution. We will look into the individual packages
in the following sub-sections.</p>

<h2 id="jupiter-fan-control">jupiter-fan-control</h2>

<p>This <a href="https://bugzilla.suse.com/show_bug.cgi?id=1256004">review</a> is about a fan control daemon which
regulates the speed of the Steam Deck fan. The daemon itself is acceptable, it
mostly deals with hardware information and controls found in <code class="language-plaintext highlighter-rouge">/sys</code> and
therefore it crosses no security boundaries. It also creates a world-readable
logfile in <code class="language-plaintext highlighter-rouge">/var/log</code>, which only contains fan data, however, thus not
resulting in a relevant information leak towards unprivileged users.</p>

<p>A Polkit action allows to start and stop the daemon by providing the
administrator password, which is also fine. A wrapper script which performs
these start/stop actions is placed into
<code class="language-plaintext highlighter-rouge">/usr/bin/steamos-polkit-helpers/jupiter-fan-control</code>, which is a non-standard
location, but according to the packager is hard-coded into SteamOS components
and cannot be changed.</p>

<h2 id="gamescope-session-steam-factory-reset">gamescope-session-steam-factory-reset</h2>

<p><a href="https://bugzilla.suse.com/show_bug.cgi?id=1257125">This review</a> is concerned with a set of scripts
which allow to perform a factory reset operation on SteamOS. On openSUSE there
is nothing sensible that can be done in terms of a factory reset, thus the
packager added scripts that are in effect no-ops. Still these scripts were
supposed to be invoked with root privileges via Polkit authentication.
After a longer discussion with the maintainer we decided to take a different
approach that does not require to run dummy scripts with root privileges, but
still allows system integrators to hook into the process and change the
behaviour.</p>

<h2 id="jupiter-hw-support">jupiter-hw-support</h2>

<p><a href="https://bugzilla.suse.com/show_bug.cgi?id=1257533">The third review</a> is concerned with a bunch of SteamOS
scripts that automatically perform actions like mounting removable devices
without requiring authentication, expecting a single “deck” interactive user
in the system. The community packager discussed some aspects of these
privileged operations with us and how to best integrate them into openSUSE.
The effort is still ongoing.</p>

<h1 id="section-deepin">9) Revisit of Deepin Desktop D-Bus Services after Removal from openSUSE</h1>

<p>A year ago we announced <a href="/2025/05/07/deepin-desktop-removal.html">the removal of Deepin desktop
components</a> from openSUSE because of policy violations and bad
security. Deepin upstream promised to improve the problematic components and
we offered to have a fresh look at things when they would have something new
to show. As a result <a href="https://bugzilla.suse.com/show_bug.cgi?id=1254493">a follow-up review bug</a> was created in
which the Deepin package maintainer claimed that all reported security issues
were fixed by upstream. The following sections discuss two of the D-Bus
components we revisited in this context.</p>

<h2 id="backlight-helper">Backlight Helper</h2>

<p>A small D-Bus helper for <a href="https://bugzilla.suse.com/show_bug.cgi?id=1257149">controlling laptop backlight</a>
seemed like a promising start; the code is clean and conservative. It is
lacking Polkit authentication, however, which means that any user in the
system can meddle with the backlight. Such settings are usually restricted to
local interactive users in an active session. We informed upstream about this
shortcoming and there is supposed to be a fix available by now, but we didn’t
look into it yet.</p>

<h2 id="accounts-service">Accounts Service</h2>

<p>The Deepin <a href="https://bugzilla.suse.com/show_bug.cgi?id=1257142">accounts service</a> offers a larger D-Bus
interface for managing user accounts in the system. We looked into version
6.1.66 of the project. Unfortunately we quickly discovered new security issues
in this component:</p>

<ul>
  <li><a href="https://github.com/linuxdeepin/dde-daemon/blob/6.1.66/accounts1/users/guest.go#L12">the <code class="language-plaintext highlighter-rouge">CreateGuestUser()</code></a> D-Bus method, which
requires admin authentication, creates a user account with an empty password
and a home directory in a random location in <code class="language-plaintext highlighter-rouge">/tmp</code>. The creation of this
directory is affected by a race condition, which could allow other users in
the system to pre-create the directory. A home directory in <code class="language-plaintext highlighter-rouge">/tmp</code> is highly
unusual and empty passwords, even for guest accounts, are bad practice.</li>
  <li><a href="https://github.com/linuxdeepin/dde-daemon/blob/6.1.66/accounts1/user_ifc.go#L81">the <code class="language-plaintext highlighter-rouge">SetHomeDir()</code></a> D-Bus method, which requires
only user-level self-authentication, allows to move the user’s home
directory into arbitrary new locations, even <code class="language-plaintext highlighter-rouge">/root</code>. This operation is
performed via the command line <code class="language-plaintext highlighter-rouge">usermod -m -d &lt;new-home-path&gt; &lt;username&gt;</code>. The
only aspect that prevents a simple local root exploit is that <code class="language-plaintext highlighter-rouge">usermod</code>
refuses to perform the operation if the calling user still has processes
running in the system.  How this API function could ever be used meaningfully
for self-administration, then, is puzzling. It might be possible for an
attacker to overcome this check, however, by quickly killing all of its
processes just in time for the <code class="language-plaintext highlighter-rouge">usermod</code> invocation to succeed.</li>
  <li><a href="https://github.com/linuxdeepin/dde-daemon/blob/6.1.66/accounts1/user_ifc.go#L147">the <code class="language-plaintext highlighter-rouge">SetPassword()</code></a> D-Bus method, again
accessible by providing the user password, is affected by multiple issues:
    <ul>
      <li>the new user password is leaked in the process command line constructed in
<a href="https://github.com/linuxdeepin/dde-daemon/blob/6.1.66/accounts1/users/prop.go#L178"><code class="language-plaintext highlighter-rouge">ModifyPasswd()</code></a>.</li>
      <li>the function <a href="https://github.com/linuxdeepin/dde-daemon/blob/6.1.66/accounts1/user_chpwd_union_id.go#L690"><code class="language-plaintext highlighter-rouge">removeLoginKeyring()</code></a>
which is invoked in this context operates as <code class="language-plaintext highlighter-rouge">root</code> in the user’s home
directory, offering local Denial-of-Service attack surface.</li>
      <li><a href="https://github.com/linuxdeepin/dde-daemon/blob/6.1.66/accounts1/user_chpwd_union_id.go#L119">an insufficient check</a> is made by the D-Bus
service, which tries to verify whether the client is running a trusted
password change application. The check is affected by a race condition and
can be circumvented by malicious clients.</li>
      <li>the function <a href="https://github.com/linuxdeepin/dde-daemon/blob/6.1.66/accounts1/user_chpwd_union_id.go#L530"><code class="language-plaintext highlighter-rouge">newPwdChangerX()</code></a> performs a
<code class="language-plaintext highlighter-rouge">chown()</code> on the client’s <code class="language-plaintext highlighter-rouge">.XAuthority</code> file placed into
<code class="language-plaintext highlighter-rouge">/run/user/&lt;uid&gt;/.XAuthority</code>, which is a local root exploit attack vector.</li>
    </ul>
  </li>
</ul>

<p>The openSUSE Deepin packager informed us that there are also fixes for these
issues available by now, but we did not get around to verify them yet.</p>

<h2 id="summary">Summary</h2>

<p>Due to the recurring number of security issues, the amount of time
required by upstream to address them and a lack of a formal security fix
workflow in the Deepin project we stopped assigning CVEs for the issues we
find in Deepin. We don’t see much value in further CVEs since the security
issues are often quickly replaced by other security issues, thus resulting
mostly in noise. Overall we don’t recommend to use Deepin components until
the security culture of Deepin upstream improves.</p>

<p>By now we are also treating Deepin review requests with lower priority, since
the efforts which went on for years still haven’t yielded acceptable results
and we would rather invest our resources into other, more promising packages.</p>

<h1 id="10-conclusion">10) Conclusion</h1>

<p>One of the fundamental goals of the SUSE Security Team is to keep a high
standard regarding software available in SUSE distributions. Not blindly
accepting new software releases is necessary to uphold this commitment, which
also means often revisiting software we already looked into before.</p>

<p>Keeping up with the fast pace of projects like systemd can be challenging in
this regard, but the security issues we continue to find e.g. in Deepin
software show that this work is still useful. The Spotlight series is one way
to highlight this continuous and not always necessarily glamorous work.</p>]]></content><author><name>&lt;a href=&apos;mailto:matthias.gerstner@suse.de&apos;&gt;Matthias Gerstner&lt;/a&gt;, &lt;a href=&apos;mailto:filippo.bonazzi@suse.com&apos;&gt;Filippo Bonazzi (editor)&lt;/a&gt;</name></author><category term="spotlight" /><summary type="html"><![CDATA[This is the winter 2025/2026 edition of our spotlight series. This time we will discuss, among others, a review of the libpgpr signature parsing library, the rtkit realtime scheduling service and an attempt at bringing Deepin desktop components back to openSUSE.]]></summary></entry><entry><title type="html">cosmic-greeter: Unsafe File System Operations in User Home Directories (CVE-2026-25704)</title><link href="https://security.opensuse.org/2026/04/16/cosmic-greeter.html" rel="alternate" type="text/html" title="cosmic-greeter: Unsafe File System Operations in User Home Directories (CVE-2026-25704)" /><published>2026-04-16T00:00:00+00:00</published><updated>2026-04-16T00:00:00+00:00</updated><id>https://security.opensuse.org/2026/04/16/cosmic-greeter</id><content type="html" xml:base="https://security.opensuse.org/2026/04/16/cosmic-greeter.html"><![CDATA[<h1 class="no_toc" id="table-of-contents">Table of Contents</h1>

<ul id="markdown-toc">
  <li><a href="#introduction" id="markdown-toc-introduction">Introduction</a></li>
  <li><a href="#overview" id="markdown-toc-overview">Overview</a></li>
  <li><a href="#security-issues" id="markdown-toc-security-issues">Security Issues</a></li>
  <li><a href="#suggested-fixes" id="markdown-toc-suggested-fixes">Suggested Fixes</a></li>
  <li><a href="#upstream-bugfix" id="markdown-toc-upstream-bugfix">Upstream Bugfix</a></li>
  <li><a href="#cve-assignment" id="markdown-toc-cve-assignment">CVE Assignment</a></li>
  <li><a href="#timeline" id="markdown-toc-timeline">Timeline</a></li>
  <li><a href="#references" id="markdown-toc-references">References</a></li>
</ul>

<h1 id="introduction">Introduction</h1>

<p>Cosmic is <a href="https://system76.com/cosmic">a Linux desktop environment</a> written in the Rust
programming language. There is an ongoing effort to package it for openSUSE
Tumbleweed; in this context we reviewed a number of Cosmic components, among
them <a href="https://bugzilla.suse.com/show_bug.cgi?id=1259401">a D-Bus service</a> found in
<a href="https://github.com/pop-os/cosmic-greeter">cosmic-greeter</a>.  We found issues when the service
accesses home directories of unprivileged users, which will be described
further below. This report is based on cosmic-greeter version 1.0.8.</p>

<h1 id="overview">Overview</h1>

<p><code class="language-plaintext highlighter-rouge">cosmic-greeter-daemon</code> is implemented in
<a href="https://github.com/pop-os/cosmic-greeter/blob/epoch-1.0.8/daemon/src/main.rs">daemon/src/main.rs</a>, runs with full root privileges
and offers a D-Bus interface “com.system76.CosmicGreeter” on the D-Bus system
bus. The interface only provides a single D-Bus method
“com.system76.CosmicGreeter.GetUserData”.</p>

<p>This D-Bus method is only allowed to be called by members of the
<code class="language-plaintext highlighter-rouge">cosmic-greeter</code> group, not by arbitrary other unprivileged users. What the
<a href="https://github.com/pop-os/cosmic-greeter/blob/epoch-1.0.8/daemon/src/main.rs#L62">method does</a> is basically looking up all
non-system user accounts in <code class="language-plaintext highlighter-rouge">/etc/passwd</code> and gathering Cosmic configuration
data from every user’s home directory.</p>

<h1 id="security-issues">Security Issues</h1>

<p>The code contains <a href="https://github.com/pop-os/cosmic-greeter/blob/epoch-1.0.8/daemon/src/main.rs#L9">a comment</a>, outlining that it
is important to drop privileges to the owner of the home directory
being processed, to prevent security issues. While this is a good starting
point, the actual implementation of this logic is still lacking in a number of
spots.</p>

<p>Following is an excerpt of an <code class="language-plaintext highlighter-rouge">strace</code> of the <code class="language-plaintext highlighter-rouge">cosmic-greeter-daemon</code> process
during invocation of the D-Bus method. The output will help illustrate some of
the issues in question:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setresuid<span class="o">(</span><span class="nt">-1</span>, 1000, <span class="nt">-1</span><span class="o">)</span> <span class="o">=</span> 0
&lt;...&gt;
statx<span class="o">(</span>AT_FDCWD, <span class="s2">"/var/lib/AccountsService/icons/&lt;user&gt;"</span>, AT_STATX_SYNC_AS_STAT, STATX_ALL, 0x7feb5d5f8a50<span class="o">)</span> <span class="o">=</span> <span class="nt">-1</span> ENOENT <span class="o">(</span>No such file or directory<span class="o">)</span>
statx<span class="o">(</span>AT_FDCWD, <span class="s2">"/home/&lt;user&gt;/.local/share/cosmic/com.system76.CosmicTheme.Mode/v1"</span>, AT_STATX_SYNC_AS_STAT, STATX_ALL, 0x7feb5d5f8800<span class="o">)</span> <span class="o">=</span> <span class="nt">-1</span> ENOENT <span class="o">(</span>No such file or directory<span class="o">)</span>
<span class="nb">mkdir</span><span class="o">(</span><span class="s2">"/home/&lt;user&gt;/.config/cosmic/com.system76.CosmicTheme.Mode/v1"</span>, 0777<span class="o">)</span> <span class="o">=</span> <span class="nt">-1</span> EEXIST <span class="o">(</span>File exists<span class="o">)</span>
statx<span class="o">(</span>AT_FDCWD, <span class="s2">"/home/&lt;user&gt;/.config/cosmic/com.system76.CosmicTheme.Mode/v1"</span>, AT_STATX_SYNC_AS_STAT, STATX_ALL, <span class="o">{</span><span class="nv">stx_mask</span><span class="o">=</span>STATX_ALL|STATX_MNT_ID, <span class="nv">stx_attributes</span><span class="o">=</span>0, <span class="nv">stx_mode</span><span class="o">=</span>S_IFDIR|0755, <span class="nv">stx_size</span><span class="o">=</span>4096, ...<span class="o">})</span> <span class="o">=</span> 0
statx<span class="o">(</span>AT_FDCWD, <span class="s2">"/home/&lt;user&gt;/.config/cosmic/com.system76.CosmicTheme.Mode/v1/is_dark"</span>, AT_STATX_SYNC_AS_STAT, STATX_ALL, <span class="o">{</span><span class="nv">stx_mask</span><span class="o">=</span>STATX_ALL|STATX_MNT_ID, <span class="nv">stx_attributes</span><span class="o">=</span>0, <span class="nv">stx_mode</span><span class="o">=</span>S_IFCHR|0666, <span class="nv">stx_size</span><span class="o">=</span>0, ...<span class="o">})</span> <span class="o">=</span> 0
<span class="nb">mkdir</span><span class="o">(</span><span class="s2">"/home/&lt;user&gt;/.config/cosmic/com.system76.CosmicTheme.Dark/v1"</span>, 0777<span class="o">)</span> <span class="o">=</span> <span class="nt">-1</span> EEXIST <span class="o">(</span>File exists<span class="o">)</span>
openat<span class="o">(</span>AT_FDCWD, <span class="s2">"/home/&lt;user&gt;/.config/cosmic/com.system76.CosmicTheme.Dark/v1/palette"</span>, O_RDONLY|O_CLOEXEC<span class="o">)</span> <span class="o">=</span> 11
&lt;...&gt;
setresuid<span class="o">(</span><span class="nt">-1</span>, 0, <span class="nt">-1</span> &lt;unfinished ...&gt;
</code></pre></div></div>

<p>What we are seeing here is that the privilege drop only concerns the effective
user ID of the <code class="language-plaintext highlighter-rouge">cosmic-greeter-daemon</code> process. The root group credentials are
retained. This means any potential attacks by the owner of a home directory
can still try to leverage root group credentials to their advantage.</p>

<p>Given this, the file operations performed in the user’s home directory are
subject to a range of security issues:</p>

<ul>
  <li>
    <p>directory components within the path can be replaced by symbolic
links. E.g. if a user places a symlink like this:</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$HOME/.config/cosmic → /root/.config/cosmic
</code></pre></div>    </div>

    <p>then the daemon would actually process root’s Cosmic configuration files,
provided that root’s home directory is accessible for members of the root
group.</p>
  </li>
  <li>since the daemon also attempts to create directories under some conditions,
these directories could be created in arbitrary locations where the root
group has write permission.</li>
  <li>the daemon checks the type of files via <code class="language-plaintext highlighter-rouge">stat()</code> before trying to open
configuration files, for example. This is a typical
Time-of-Check/Time-of-Use (TOCTOU) race condition, however, because the owner
of the home directory can attempt to replace a regular file by a symbolic link
or special file by the time the actual <code class="language-plaintext highlighter-rouge">open()</code> call is performed by the
daemon. This can lead to the following potential issues:
    <ul>
      <li>parsing of private files accessible to the root group.
Whether the data parsed from such files could ever leak into the
context of a local attacker is a matter that we did not investigate
more closely for the purpose of this report.</li>
      <li>by placing a symbolic link to e.g. <code class="language-plaintext highlighter-rouge">/dev/zero</code>, an out-of-memory situation
can be triggered in the daemon, causing it to be killed by the kernel,
leading to a local Denial-of-Service (DoS).</li>
      <li>by placing a FIFO named pipe in the location the daemon would block
on it forever, also leading to a local DoS.</li>
    </ul>
  </li>
  <li>the daemon considers accounts with user IDs ≥ 1000 <a href="https://github.com/pop-os/cosmic-greeter/blob/epoch-1.0.8/daemon/src/main.rs#L65">as regular user
accounts</a>. On many Linux distributions
this means that also the <code class="language-plaintext highlighter-rouge">nobody</code> user account is included (UID 65534). As a
result, the daemon also attempts to process Cosmic configuration in
        <code class="language-plaintext highlighter-rouge">/var/lib/nobody</code> on openSUSE.
This grants processes operating with <code class="language-plaintext highlighter-rouge">nobody</code> privileges the opportunity to
attempt to exploit the daemon’s logic.</li>
</ul>

<p>The severity of these issues is reduced by the fact that only members of the
<code class="language-plaintext highlighter-rouge">cosmic-greeter</code> group are allowed to invoke the <code class="language-plaintext highlighter-rouge">GetUserData</code> D-Bus method,
thus potential attackers have to wait for an authorized process to call the
function to attempt to exploit it. We don’t have enough insight into the
bigger picture of the Cosmic desktop environment, but it could be possible
that local users are able to indirectly trigger the execution of this D-Bus
method by using other APIs made available by Cosmic.</p>

<h1 id="suggested-fixes">Suggested Fixes</h1>

<p>We suggested the following improvements to upstream to deal with the issues:</p>

<ul>
  <li>the privileges should be fully dropped to the target user account, including
group ID and the supplementary group IDs.</li>
  <li>to prevent potential DoS attack surface, the daemon should carefully
open target paths element by element, passing <code class="language-plaintext highlighter-rouge">O_NOFOLLOW|O_NONBLOCK</code>
to prevent symlink attacks, then perform an <code class="language-plaintext highlighter-rouge">fstat()</code> on the open file
to determine its type in a race-free fashion.</li>
  <li>the <code class="language-plaintext highlighter-rouge">nobody</code> user account should be explicitly excluded based on its name
for distributions that set a valid shell for this account.</li>
  <li>as additional hardening, the systemd unit <code class="language-plaintext highlighter-rouge">cosmic-greeter-daemon.service</code> can
be extended with directives like <code class="language-plaintext highlighter-rouge">ProtectSystem=full</code>. This needs some tuning,
though, since the daemon still needs to be able to read files in home
directories of other users.</li>
</ul>

<h1 id="upstream-bugfix">Upstream Bugfix</h1>

<p>Upstream implemented <a href="https://github.com/pop-os/cosmic-greeter/commit/63cd93bddd01bf714e98553966d4da12eac0ee5b">commit 63cd93bddd0</a>
containing the following changes:</p>

<ul>
  <li>the daemon properly drops its group and supplementary group IDs to the
target user’s.</li>
  <li>only user IDs in the range defined by <code class="language-plaintext highlighter-rouge">UID_MIN</code> and <code class="language-plaintext highlighter-rouge">UID_MAX</code> as configured
in <code class="language-plaintext highlighter-rouge">/etc/login.defs</code> will be considered.</li>
  <li>icon files in <code class="language-plaintext highlighter-rouge">/var/lib/accountservice</code> will be opened with <code class="language-plaintext highlighter-rouge">O_NOFOLLOW</code>
(actually an unrelated change / security hardening).</li>
</ul>

<p>This bugfix is part of <a href="https://github.com/pop-os/cosmic-greeter/releases/tag/epoch-1.0.9">upstream release 1.0.9</a> and
newer.</p>

<p>What is still missing from our point of view is the prevention of local DoS
attack surface when accessing files in the user’s home directory. We informed
upstream about this but have not heard back about this topic for a while.</p>

<h1 id="cve-assignment">CVE Assignment</h1>

<p>Upstream has not expressed any wishes regarding CVE assignment, or whether one
should be assigned at all. We decided to assign a single CVE-2026-25704 from
our pool to track the main aspect of this report, the incomplete privilege
drop in the daemon.</p>

<h1 id="timeline">Timeline</h1>

<table>
  <tbody>
    <tr>
      <td>2026-03-11</td>
      <td>We forwarded this report to security@system76.com and the main developer of cosmic-greeter, offering coordinated disclosure.</td>
    </tr>
    <tr>
      <td>2026-03-11</td>
      <td>Upstream confirmed the issue and opted out of coordinated disclosure.</td>
    </tr>
    <tr>
      <td>2026-03-11</td>
      <td>We got a follow-up response asking us to keep the information private for while longer after all.</td>
    </tr>
    <tr>
      <td>2026-03-11</td>
      <td>We received a patch from upstream corresponding to <a href="https://github.com/pop-os/cosmic-greeter/commit/63cd93bddd01bf714e98553966d4da12eac0ee5b">commit 63cd93bddd0</a> and have been asked to review it.</td>
    </tr>
    <tr>
      <td>2026-03-12</td>
      <td>Upstream meanwhile created a public pull request based on this bugfix and informed us that the report no longer needed to be private.</td>
    </tr>
    <tr>
      <td>2026-03-13</td>
      <td>We assigned CVE-2026-25704 to track the main aspect of the vulnerability, an incomplete privilege drop.</td>
    </tr>
    <tr>
      <td>2026-03-13</td>
      <td>We shared the CVE with upstream and provided feedback on the bugfix, mainly pointing out that local Denial-of-Service (DoS) attack service still remains.</td>
    </tr>
    <tr>
      <td>2026-03-13</td>
      <td>Upstream informed us that they are going to address these remaining issues as well.</td>
    </tr>
    <tr>
      <td>2026-03-24</td>
      <td>We asked upstream about the status of the additional fixes, but received no response so far.</td>
    </tr>
    <tr>
      <td>2026-04-16</td>
      <td>Publication of this report.</td>
    </tr>
  </tbody>
</table>

<h1 id="references">References</h1>

<ul>
  <li><a href="https://system76.com/cosmic">Cosmic Website</a></li>
  <li><a href="https://bugzilla.suse.com/show_bug.cgi?id=1259401">openSUSE cosmic-greeter review bug</a></li>
  <li><a href="https://github.com/pop-os/cosmic-greeter/commit/63cd93bddd01bf714e98553966d4da12eac0ee5b">cosmic-greeter privilege drop bugfix</a></li>
  <li><a href="https://github.com/pop-os/cosmic-greeter/releases/tag/epoch-1.0.9">Upstream Release 1.0.9 containing the bugfix</a></li>
</ul>]]></content><author><name>&lt;a href=&apos;mailto:matthias.gerstner@suse.de&apos;&gt;Matthias Gerstner&lt;/a&gt;, &lt;a href=&apos;mailto:filippo.bonazzi@suse.com&apos;&gt;Filippo Bonazzi (editor)&lt;/a&gt;</name></author><category term="CVE" /><category term="D-Bus" /><summary type="html"><![CDATA[Cosmic is a modern Linux desktop environment implemented in Rust. One of its components, cosmic-greeter, contains a D-Bus service which operates in user home directories in an unsafe manner, leading to potential privilege escalation or local Denial-of-Service.]]></summary></entry><entry><title type="html">The Journey of auditing UYUNI</title><link href="https://security.opensuse.org/2026/01/16/the-journey-of-auditing-uyuni.html" rel="alternate" type="text/html" title="The Journey of auditing UYUNI" /><published>2026-01-16T00:00:00+00:00</published><updated>2026-01-16T00:00:00+00:00</updated><id>https://security.opensuse.org/2026/01/16/the-journey-of-auditing-uyuni</id><content type="html" xml:base="https://security.opensuse.org/2026/01/16/the-journey-of-auditing-uyuni.html"><![CDATA[<h1 class="no_toc" id="table-of-contents">Table of Contents</h1>

<ul id="markdown-toc">
  <li><a href="#1-introduction" id="markdown-toc-1-introduction">1) Introduction</a></li>
  <li><a href="#2-the-methodology" id="markdown-toc-2-the-methodology">2) The methodology</a>    <ul>
      <li><a href="#some-numbers-about-the-codebase" id="markdown-toc-some-numbers-about-the-codebase">Some numbers about the codebase</a></li>
      <li><a href="#the-activity-tracking" id="markdown-toc-the-activity-tracking">The activity tracking</a></li>
      <li><a href="#the-setup" id="markdown-toc-the-setup">The setup</a></li>
      <li><a href="#the-attackers-corner" id="markdown-toc-the-attackers-corner">The attacker’s corner</a></li>
      <li><a href="#the-reporting-method" id="markdown-toc-the-reporting-method">The reporting method</a></li>
    </ul>
  </li>
  <li><a href="#3-audit-results" id="markdown-toc-3-audit-results">3) Audit results</a>    <ul>
      <li><a href="#cve-2024-49502-spacewalk-web-reflected-xss-in-setup-wizard-http-proxy-credentials-pane" id="markdown-toc-cve-2024-49502-spacewalk-web-reflected-xss-in-setup-wizard-http-proxy-credentials-pane">CVE-2024-49502: spacewalk-web: Reflected XSS in Setup Wizard, HTTP Proxy credentials pane</a></li>
      <li><a href="#cve-2024-49503-spacewalk-web-reflected-xss-in-setup-wizard-organization-credentials" id="markdown-toc-cve-2024-49503-spacewalk-web-reflected-xss-in-setup-wizard-organization-credentials">CVE-2024-49503: spacewalk-web: Reflected XSS in Setup Wizard, Organization Credentials</a></li>
      <li><a href="#cve-2025-23392-spacewalk-java-reflected-xss-in-systemscontrollerjava" id="markdown-toc-cve-2025-23392-spacewalk-java-reflected-xss-in-systemscontrollerjava">CVE-2025-23392: spacewalk-java: reflected XSS in SystemsController.java</a></li>
      <li><a href="#cve-2025-46809-plain-text-http-proxy-userpassword-in-repolog-accessible-from-the-uyuni-5x-webui" id="markdown-toc-cve-2025-46809-plain-text-http-proxy-userpassword-in-repolog-accessible-from-the-uyuni-5x-webui">CVE-2025-46809: Plain text HTTP Proxy user:password in repolog accessible from the UYUNI 5.x webUI</a></li>
      <li><a href="#cve-2025-46811-unprotected-websocket-endpoint" id="markdown-toc-cve-2025-46811-unprotected-websocket-endpoint">CVE-2025-46811: Unprotected websocket endpoint</a></li>
      <li><a href="#cve-2025-53883-spacewalk-java-various-xss-found-on-search-page" id="markdown-toc-cve-2025-53883-spacewalk-java-various-xss-found-on-search-page">CVE-2025-53883: spacewalk-java: various XSS found on search page</a></li>
      <li><a href="#cve-2025-53880-susemanager-tftpsync-recv-arbitrary-file-creation-and-deletion-due-to-path-traversal" id="markdown-toc-cve-2025-53880-susemanager-tftpsync-recv-arbitrary-file-creation-and-deletion-due-to-path-traversal">CVE-2025-53880: susemanager-tftpsync-recv: arbitrary file creation and deletion due to path traversal</a></li>
      <li><a href="#other-minor-findings" id="markdown-toc-other-minor-findings">Other minor findings</a></li>
    </ul>
  </li>
  <li><a href="#4-conclusions" id="markdown-toc-4-conclusions">4) Conclusions</a></li>
  <li><a href="#5-whats-next" id="markdown-toc-5-whats-next">5) What’s next?</a></li>
  <li><a href="#6-links" id="markdown-toc-6-links">6) Links</a></li>
</ul>

<h1 id="1-introduction">1) Introduction</h1>

<p><a href="https://github.com/uyuni-project/uyuni">UYUNI</a> is an open source system management solution, forked from <a href="https://spacewalkproject.github.io/">Spacewalk</a> and upstream community project from which <a href="https://www.suse.com/products/multi-linux-manager/">SUSE Multi-Linux Manager</a> is derived.</p>

<p>The audit started in January 2024 with the perimeter definition. Since it’s not feasible to audit everything, a list of packages was chosen and submitted to UYUNI product owner.
The criteria for including a package in the perimeter were:</p>
<ul>
  <li>the package implementing UYUNI web UI</li>
  <li>the package implementing API or websocket layer</li>
  <li>the package implementing UYUNI backend</li>
  <li>the <code class="language-plaintext highlighter-rouge">salt</code> package (fundamental for UYUNI server and minions interaction)</li>
  <li>packages not included in previous UYUNI audits</li>
</ul>

<p>In March 2024 the code scanning activities effectively started.</p>

<p><a name="section-methodology"></a></p>

<h1 id="2-the-methodology">2) The methodology</h1>

<p>Auditing a complex codebase like UYUNI is not just running a static analysis tool and waiting for it to complete. It is a
complex and long-running journey that took one year and a half to complete.</p>

<h2 id="some-numbers-about-the-codebase">Some numbers about the codebase</h2>

<p>The codebase is big with a lot of sub-packages. Each sub-package was treated as a standalone audit with its own Bugzilla bug, its own list of affected vulnerabilities and its own report. 
The final report was produced by combining the reports of all sub-packages.</p>

<p>The audited codebase is more than 4.5 millions lines of code, with at least 7 different programming languages.</p>

<table>
  <thead>
    <tr>
      <th>Language</th>
      <th style="text-align: right">Files</th>
      <th style="text-align: right">Lines of code</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>JavaScript</td>
      <td style="text-align: right">2547</td>
      <td style="text-align: right">3805282</td>
    </tr>
    <tr>
      <td>Java</td>
      <td style="text-align: right">4052</td>
      <td style="text-align: right">369100</td>
    </tr>
    <tr>
      <td>Go</td>
      <td style="text-align: right">795</td>
      <td style="text-align: right">250684</td>
    </tr>
    <tr>
      <td>Python</td>
      <td style="text-align: right">407</td>
      <td style="text-align: right">103965</td>
    </tr>
    <tr>
      <td>JSP</td>
      <td style="text-align: right">641</td>
      <td style="text-align: right">36861</td>
    </tr>
    <tr>
      <td>Shell</td>
      <td style="text-align: right">86</td>
      <td style="text-align: right">6744</td>
    </tr>
    <tr>
      <td>Perl</td>
      <td style="text-align: right">65</td>
      <td style="text-align: right">6070</td>
    </tr>
  </tbody>
</table>

<p>As you may wonder, using a single catch-all tool to analyze such a heterogeneous codebase is not possible.</p>

<p>Every package in the scanning perimeter was audited looking at the source both using tools and by manual inspection. The running server was continuously inspected dynamically looking for low-hanging fruit like cross-site-scripting (XSS), SQL injection and similar, and for business logic flaws.</p>

<p>Each security issue was then triaged and if necessary a CVE identifier was assigned and the vulnerability put under EMBARGO. Using the
<a href="https://en.opensuse.org/openSUSE:Security_disclosure_policy">openSUSE coordinated disclosure policy</a>
as a framework, we coordinate with upstream and disclose the issue when solved.</p>

<h2 id="the-activity-tracking">The activity tracking</h2>

<p>We use Bugzilla as tracking authority for audits and vulnerabilities found during the activity. A master bug (<a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1218619">boo#1218619</a>) was created with the purpose of acting as a main container for all sub-packages audit bugs.</p>

<p>Each audit bug contains all affecting vulnerabilities and, of course, a vulnerability bug can be set as blocker to more sub-packages.</p>

<h2 id="the-setup">The setup</h2>

<p>For the activity, a set of
<a href="https://en.wikipedia.org/wiki/Kernel-based_Virtual_Machine">KVM</a>-powered machines were created:</p>

<ul>
  <li>a UYUNI server instance</li>
  <li>a UYUNI proxy instance</li>
  <li>a couple of minions, Linux workstations attached and managed centrally by the master.</li>
</ul>

<p>The server is the main UYUNI component orchestrating minions attestation and enabling system administrators to launch commands and interact with minions using the web interface.</p>

<p>A minion, in the UYUNI slang, is a Linux-powered machine (ideally it is a client in a local network), connected to the server.</p>

<p>A UYUNI proxy is a particular kind of server, used to fetch packages from software distribution channels and centrally store software packages for an efficient distribution to minions. Distribution channels are software repositories and a system administrator subscribes his own UYUNI instance to different repositories.</p>

<p>Each server was running <a href="https://microos.opensuse.org/">openSUSE MicroOS</a> as
underlying operating system and minions were running either
<a href="https://get.opensuse.org/tumbleweed/">openSUSE Tumbleweed</a> or
<a href="https://ubuntu.com/">Ubuntu</a> Linux distributions.</p>

<h2 id="the-attackers-corner">The attacker’s corner</h2>

<p>For the testing activities we used two different machines. A virtual machine running openSUSE Tumbleweed, used for source code inspection and a virtual machine running <a href="https://www.kali.org">Kali Linux</a> installed to help in penetration testing activities.</p>

<h3 id="the-tools">The tools</h3>

<p><a href="https://portswigger.net/burp/communitydownload">Burp Suite community</a> was the
main tool used trying to spot security issues in the running application.</p>

<p>To help, during the UYUNI application browsing, a custom tool was developed. While browsing the web UI trying to find business logic flaws, I felt the need for something running in the background spotting low-hanging fruit in web pages form, cookies and more.
The tool eventually became an OSS project named <a href="https://github.com/thesp0nge/nightcrawler-mitm">nightcrawler-mitm</a>. It’s a <a href="https://www.mitmproxy.org/">mitmproxy</a> extension implementing both an active and a passive scanner running several security controls in the background.</p>

<p>Also for auditing the source code, opensource tools were used.
Some of the tools used are famous OSS projects, like:</p>

<ul>
  <li><a href="https://github.com/PyCQA/bandit">bandit</a></li>
  <li><a href="https://semgrep.dev/">semgrep</a></li>
  <li><a href="https://docs.npmjs.com/cli/v8/commands/npm-audit">npm audit</a></li>
</ul>

<p>To help me during the activities, I also used some SAST tools previously written by myself, like:</p>

<ul>
  <li><a href="https://github.com/thesp0nge/dr_source">dr_source</a></li>
  <li><a href="https://github.com/thesp0nge/dawnscanner">dawnscanner</a></li>
</ul>

<h2 id="the-reporting-method">The reporting method</h2>

<p>As discussed before, every finding was tracked on a separate bugzilla bug. Each bug was linked, marking as a blocking bug, to any sub-package audit bug affected by the associated vulnerability.</p>

<p>Of course, every vulnerability was confirmed by a successful exploitation, before being added to our Bugzilla tracking system. Vulnerabilities were assigned to UYUNI developers and tracked until a fix was released. 
A CVE was also assigned if required by the issue severity.</p>

<p>The standard <a href="https://www.first.org/cvss/v4.0/">CVSS version 4</a> was used as a scoring system and to assign a severity. The rationale is that if a CVSS is lower than 5, then the severity is low, it is medium if CVSS is between 5 and 7 and high otherwise. 
The same approach was used to assign a triage score to each sub-package. The triage score will be used in the future to decide if the sub-package must be in future audit perimeter or not.</p>

<p>At the end of the audit, the list of issues and the triage score created a technical report sent to UYUNI developers.</p>

<p><a name="section-results"></a></p>

<h1 id="3-audit-results">3) Audit results</h1>

<p>During the audit, seven CVEs were found and fixed, and numerous minor issues were addressed, improving the product’s reliability and overall security posture.</p>

<h2 id="cve-2024-49502-spacewalk-web-reflected-xss-in-setup-wizard-http-proxy-credentials-pane">CVE-2024-49502: spacewalk-web: Reflected XSS in Setup Wizard, HTTP Proxy credentials pane</h2>

<p>A reflected cross-site scripting has been found in the HTTP proxy pane of the setup wizard UI element. Tracked in
<a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1231852">boo#1231852</a></p>

<h2 id="cve-2024-49503-spacewalk-web-reflected-xss-in-setup-wizard-organization-credentials">CVE-2024-49503: spacewalk-web: Reflected XSS in Setup Wizard, Organization Credentials</h2>

<p>A reflected cross-site scripting has been found in the Organization Credentials pane of the setup wizard UI element. Tracked in
<a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1231922">boo#1231922</a></p>

<h2 id="cve-2025-23392-spacewalk-java-reflected-xss-in-systemscontrollerjava">CVE-2025-23392: spacewalk-java: reflected XSS in SystemsController.java</h2>

<p>Some URLs, served by the <code class="language-plaintext highlighter-rouge">SystemsController.java</code> class are vulnerable to a reflected XSS vulnerability. Some example of vulnerable URLs are listed in the Github advisory as well. The
<a href="https://github.com/uyuni-project/uyuni/security/advisories/GHSA-v588-pf3f-jfp9">advisory</a>
was filed by an external independent researcher following
<a href="https://en.opensuse.org/openSUSE:Security_disclosure_policy">our coordinated disclosure policy</a>.
Tracked in <a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1239826">boo#1239826</a></p>

<h2 id="cve-2025-46809-plain-text-http-proxy-userpassword-in-repolog-accessible-from-the-uyuni-5x-webui">CVE-2025-46809: Plain text HTTP Proxy user:password in repolog accessible from the UYUNI 5.x webUI</h2>

<p>Credentials to be used in UYUNI HTTP proxy are disclosed in the error log in case of wrong port number or misspelled
hostname. Tracked in
<a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1245005">boo#1245005</a></p>

<h2 id="cve-2025-46811-unprotected-websocket-endpoint">CVE-2025-46811: Unprotected websocket endpoint</h2>

<p>During an internal assessment, a customer found an issue with the remote-commands websocket endpoint (<code class="language-plaintext highlighter-rouge">/rhn/websocket/minion/remote-commands</code>).
Using websockets, anyone with the ability to connect to port 443 of SUSE Manager is able to run any command as root on any client with no authentication. The customer using our coordinated disclosure policy as a reference, reported the issue which was then fixed and publicly disclosed. Tracked in
<a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1246119">boo#1246119</a></p>

<h2 id="cve-2025-53883-spacewalk-java-various-xss-found-on-search-page">CVE-2025-53883: spacewalk-java: various XSS found on search page</h2>

<p>During an internal assessment, a customer found that some reflected cross-site scriptings were possible due to improper input validation. The issue was tracked in the private SUSE bugzilla instance, since some customer sensitive information was included. However the issue is described in the public
<a href="https://www.suse.com/security/cve/CVE-2025-53883.html">CVE-2025-53883 page.</a></p>

<h2 id="cve-2025-53880-susemanager-tftpsync-recv-arbitrary-file-creation-and-deletion-due-to-path-traversal">CVE-2025-53880: susemanager-tftpsync-recv: arbitrary file creation and deletion due to path traversal</h2>

<p>A Path Traversal vulnerability in the <code class="language-plaintext highlighter-rouge">tftpsync/add</code> and <code class="language-plaintext highlighter-rouge">tftpsync/delete</code> scripts allows a remote attacker on an adjacent network to write or delete files on the filesystem with the privileges of the unprivileged <code class="language-plaintext highlighter-rouge">wwwrun</code> user. Although the endpoint is unauthenticated, access is restricted to a list of allowed IP addresses. The unprivileged user has write access to a directory that controls the provisioning of other systems, leading to a full compromise of those subsequent systems. Tracked in
<a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1246277">boo#1246277</a></p>

<h2 id="other-minor-findings">Other minor findings</h2>

<p>Additional vulnerabilities were identified that, while valid, did not meet the criteria for CVE assignment:</p>

<ul>
  <li><a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1231900">boo#1231900</a>: VUL-0: arbitrary log messages in API can lead to a disk space exhaustion (and so to a denial of service)</li>
  <li><a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1245740">boo#1245740</a>: VUL-0: Default venv-salt-minion environment is activated on the different user accounts</li>
  <li><a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1243679">boo#1243679</a>: VUL-0: Insecure communication in TFTP proxy sync.</li>
  <li><a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1243768">boo#1243768</a>: VUL-0: Potential Command InjectionPattern in check_push Function. No activity: a follow-up was requested.</li>
  <li><a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1239636">boo#1239636</a>: VUL-0: log pollution in class TraceBackEvent</li>
  <li><a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1237368">boo#1237368</a>: VUL-0: unhandled exception when dealing with numeric request parameters</li>
  <li><a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1243087">boo#1243087</a>: VUL-0: spacewalk-search: unexploitable XSS in XML RPC Server.</li>
  <li><a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1227577">boo#1227577</a>: VUL-0: spacecmd and spacewalk-backend: usage of unsafe third party library for XML.</li>
</ul>

<p>Last but not least, during the audit also some codebase improvements were suggested to raise the security posture even further:</p>

<ul>
  <li><a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1228945">boo#1228945</a>:
AUDIT-FIND: spacewalk-utils: Sensitive information disclosure in backup file</li>
  <li><a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1223313">boo#1223313</a>:
AUDIT-FIND: Possible deserialization issue in spacewalk-client-tools
(affecting only SUMA 4.x)</li>
  <li><a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1228116">boo#1228116</a>:
AUDIT-FIND: spacewalk-admin: mgr-monitoring-ctl doesn’t sanitize PILLAR
parameter</li>
  <li><a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1231983">boo#1231983</a>:
AUDIT-FIND: spacewalk-web: generatePassword() improve namespace entropy</li>
  <li><a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1246941">boo#1246941</a>:
AUDIT-FIND: saline: Hardening Against Insecure Deserialization</li>
  <li><a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1247015">boo#1247015</a>:
AUDIT-FIND: saline: Race Condition in Service Startup Allows for IPC Hijacking
on Systems with a Permissive umask</li>
  <li><a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1227579">boo#1227579</a>:
AUDIT-FIND: spacecmd: get rid of pickle to read and parse configuration files.</li>
</ul>

<p><a name="section-conclusions"></a></p>

<h1 id="4-conclusions">4) Conclusions</h1>

<p>The UYUNI audit was an intense and rewarding run. The good results in term of number of found vulnerabilities and the fast reaction to release the fixes, confirmed UYUNI as a solid and reliable product for the community.</p>

<p>As all software, of course it can be improved in terms of code quality by applying safe coding patterns, using secure and reliable third-party libraries and consolidating the usage of one or two programming languages. This is an important step, because it creates a common ground for engineers and a solid codebase for the community to entice contributions and pull requests.</p>

<p>A vibrant codebase, using a balanced mix between standard and cutting edge technologies can increase adoption of the product and it can attract developers and contributors.</p>

<p>It also helps in adopting safe coding best practices that are widely updated and developed for newer technologies rather than ancient and not actively used programming languages.</p>

<p>The low number of vulnerabilities found, and the reaction time in fixing the serious ones, indicate that the project is well-curated and actively maintained. The  security posture is good and it can be safely deployed in production.</p>

<p><a name="section-next"></a></p>

<h1 id="5-whats-next">5) What’s next?</h1>

<p>Like every journey, the final destination is not the reward itself. The UYUNI project is actively under development with a monthly (more or less) release cycle.</p>

<p>The next audit will start in the first quarter of 2026 and it will be another one year and a half rollercoaster ride, with rabbit holes, false positives, suspected CVEs turning out to be not exploitable and real <em>root dance</em> issues.</p>

<p>The fun part is to audit code written in multiple languages, with different stacks and libraries.</p>

<p>It’s not rewarding only from a security perspective, it’s
a real learning experience.</p>

<p><a name="section-links"></a></p>

<h1 id="6-links">6) Links</h1>

<ul>
  <li>The <a href="https://bugzilla.opensuse.org/show_bug.cgi?id=1218619">master Bugzilla bug</a>.</li>
  <li>The <a href="https://www.uyuni-project.org/pages/stable-version.html">latest stable version 2025.10 of UYUNI</a> containing all the relevant fixes.</li>
  <li>The <a href="https://github.com/thesp0nge/nightcrawler-mitm">nightcrawler-mitm</a> tool, written to actively and passively scan the web application in the background.</li>
  <li>The <a href="https://github.com/thesp0nge/dr_source">dr_source</a> tool, written as a SAST companion tool mainly for Java but improved with support for other programming languages.</li>
  <li><a href="https://github.com/uyuni-project/uyuni">The UYUNI source code on Github</a></li>
  <li><a href="https://en.opensuse.org/openSUSE:Security_disclosure_policy">The openSUSE coordinated disclosure policy</a></li>
</ul>]]></content><author><name>&lt;a href=&apos;mailto:paolo.perego@suse.de&apos;&gt;Paolo Perego&lt;/a&gt;, &lt;a href=&apos;mailto:filippo.bonazzi@suse.com&apos;&gt;Filippo Bonazzi (editor)&lt;/a&gt;</name></author><category term="UYUNI" /><category term="audit" /><category term="pentest" /><category term="cve" /><category term="web-pentest" /><summary type="html"><![CDATA[UYUNI is software designed to help system administrators manage a heterogeneous data center full of Linux servers. Auditing such a large piece of software is a long-running journey with ups and downs. Let's explore the process that led us to discover a number of CVEs.]]></summary></entry><entry><title type="html">SUSE Security Team Spotlight Autumn 2025</title><link href="https://security.opensuse.org/2026/01/14/autumn-spotlight.html" rel="alternate" type="text/html" title="SUSE Security Team Spotlight Autumn 2025" /><published>2026-01-14T00:00:00+00:00</published><updated>2026-01-14T00:00:00+00:00</updated><id>https://security.opensuse.org/2026/01/14/autumn-spotlight</id><content type="html" xml:base="https://security.opensuse.org/2026/01/14/autumn-spotlight.html"><![CDATA[<h1 class="no_toc" id="table-of-contents">Table of Contents</h1>

<ul id="markdown-toc">
  <li><a href="#1-introduction" id="markdown-toc-1-introduction">1) Introduction</a></li>
  <li><a href="#section-systemd" id="markdown-toc-section-systemd">2) Completion of <code class="language-plaintext highlighter-rouge">systemd</code> v258 Code Review</a></li>
  <li><a href="#section-plasma-setup" id="markdown-toc-section-plasma-setup">3) D-Bus Issues in Unreleased <code class="language-plaintext highlighter-rouge">plasma-setup</code> KDE Package</a>    <ul>
      <li><a href="#sub-section-autostarthook" id="markdown-toc-sub-section-autostarthook">org.kde.initialsystemsetup.createnewuserautostarthook</a></li>
      <li><a href="#orgkdeinitialsystemsetupsetnewuserhomedirectoryownership" id="markdown-toc-orgkdeinitialsystemsetupsetnewuserhomedirectoryownership">org.kde.initialsystemsetup.setnewuserhomedirectoryownership</a></li>
      <li><a href="#orgkdeinitialsystemsetupsetnewusertempautologin" id="markdown-toc-orgkdeinitialsystemsetupsetnewusertempautologin">org.kde.initialsystemsetup.setnewusertempautologin</a></li>
      <li><a href="#upstream-fixes" id="markdown-toc-upstream-fixes">Upstream Fixes</a></li>
    </ul>
  </li>
  <li><a href="#section-plocate" id="markdown-toc-section-plocate">4) Discussion about Granting setgid Privileges to the <code class="language-plaintext highlighter-rouge">plocate</code> Binary</a></li>
  <li><a href="#section-virtualbmc" id="markdown-toc-section-virtualbmc">5) Local Root Exploit in OpenStack’s non-production <code class="language-plaintext highlighter-rouge">virtualbmc</code> Project</a>    <ul>
      <li><a href="#lack-of-authorization-and-input-validation-in-vbmcd" id="markdown-toc-lack-of-authorization-and-input-validation-in-vbmcd">Lack of Authorization and Input Validation in <code class="language-plaintext highlighter-rouge">vbmcd</code></a></li>
      <li><a href="#reproducer" id="markdown-toc-reproducer">Reproducer</a></li>
      <li><a href="#further-concerns" id="markdown-toc-further-concerns">Further Concerns</a></li>
    </ul>
  </li>
  <li><a href="#section-snapd" id="markdown-toc-section-snapd">6) Revisit of the <code class="language-plaintext highlighter-rouge">snapd</code> Package Manager</a></li>
  <li><a href="#7-conclusion" id="markdown-toc-7-conclusion">7) Conclusion</a></li>
</ul>

<h1 id="1-introduction">1) Introduction</h1>

<p>The winter season has already begun for most of the people in our team and
with the Christmas holidays behind us, which granted us some well-earned rest,
we want to take a look back at what happened in our team during the autumn
months. During this time we already published a few dedicated review reports:</p>

<ul>
  <li><a href="/2025/10/31/opensmtpd-local-DoS.html">trivial local Denial-of-Service</a> in the <code class="language-plaintext highlighter-rouge">OpenSMTPD</code> mail
transfer agent.</li>
  <li><a href="/2025/11/06/scx-unauthorized-dbus.html">unauthenticated D-Bus API</a> in the <code class="language-plaintext highlighter-rouge">scx</code> scheduler project allowing
for a major local Denial-of-Service.</li>
  <li><a href="/2025/11/13/lightdm-kde-greeter-auth-helper.html">minor privilege escalation</a> from <code class="language-plaintext highlighter-rouge">lightdm</code> to <code class="language-plaintext highlighter-rouge">root</code> in
<code class="language-plaintext highlighter-rouge">lightdm-kde-greeter</code> leading to major improvements of its D-Bus code.</li>
  <li><a href="/2025/12/10/smb4k-major-issues-in-kauth-helper.html">major local vulnerabilities</a> in the D-Bus
interface of <code class="language-plaintext highlighter-rouge">smb4k</code>, resulting in upstream fixing a series of
long-standing issues in the affected component.</li>
</ul>

<p>In this post, as usual in the spotlight series, we will look into some topics
that did not justify dedicated reports. First we will discuss our <a href="#section-systemd">continued
efforts</a> to review privileged components found in the
<code class="language-plaintext highlighter-rouge">systemd</code> v258 release, which involved diving deep into some low-level aspects
of the Linux kernel API. <a href="#section-plasma-setup">Section 3</a> looks at D-Bus
issues we found in <code class="language-plaintext highlighter-rouge">plasma-setup</code>, a new component for the KDE desktop.
<a href="#section-plocate">Section 4</a> covers recent discussions about granting special
setgid permissions to the <code class="language-plaintext highlighter-rouge">plocate</code> package. <a href="#section-virtualbmc">Section 5</a>
gives insight into security issues found in the <code class="language-plaintext highlighter-rouge">virtualbmc</code> OpenStack
project, which turned out to be for testing purposes only. <a href="#section-snapd">Section
6</a> discusses revived efforts to bring the Snap package manager
to openSUSE.</p>

<h1 id="section-systemd">2) Completion of <code class="language-plaintext highlighter-rouge">systemd</code> v258 Code Review</h1>

<p>We already discussed our <code class="language-plaintext highlighter-rouge">systemd</code> v258 review efforts <a href="/2025/10/01/summer-spotlight.html#2-systemd-v258-local-root-exploit-in-new-systemd-machined-api-found-in-release-candidates">in the previous
spotlight edition</a>. At the time we found a
local root exploit in the <code class="language-plaintext highlighter-rouge">systemd-machined</code> API, which could be fixed before
the final release of v258. For the addition of this new major version of
<code class="language-plaintext highlighter-rouge">systemd</code> to openSUSE Tumbleweed, we still needed to look more closely into a
number of other D-Bus and Varlink services that have been added.</p>

<p>During autumn we completed the review of changes in
<a href="https://bugzilla.suse.com/show_bug.cgi?id=1250898"><code class="language-plaintext highlighter-rouge">systemd-mountfsd</code></a> and
<a href="https://bugzilla.suse.com/show_bug.cgi?id=1250902"><code class="language-plaintext highlighter-rouge">systemd-nsresourced</code></a>. Some of the changes
introduced with these services allow unprivileged users to perform a number of
container-related operations without requiring special privileges.</p>

<p>The <a href="https://github.com/systemd/systemd/blob/781d9d0789379d1ea1f2ecefb804d41e9c8b6c38/src/mountfsd/mountwork.c#L726"><code class="language-plaintext highlighter-rouge">io.systemd.MountFileSystem.MountDirectory</code> API
call</a> in <code class="language-plaintext highlighter-rouge">mountfsd</code>, for example, allows to obtain
a mount file descriptor for a directory owned by the calling user, on which a
user and group ID mapping is applied corresponding to a user namespace file
descriptor also owned by the caller. Some newer, little-known Linux system
calls like <a href="https://manpages.debian.org/testing/manpages-dev/open_tree.2.en.html"><code class="language-plaintext highlighter-rouge">open_tree()</code></a> and
<a href="https://manpages.debian.org/testing/manpages-dev/mount_setattr.2.en.html"><code class="language-plaintext highlighter-rouge">mount_setattr()</code></a> are used to achieve this. This niche
topic and the low-level nature of the involved APIs result in quite complex
code which needed careful reviewing. We are happy to report that we could find
no issues in this area, however.</p>

<p>The <code class="language-plaintext highlighter-rouge">nsresourced</code> service, among other features, allows unprivileged users to
obtain a dynamic range of user and group IDs for use with user namespaces. The
tools <a href="https://man7.org/linux/man-pages/man1/newuidmap.1.html"><code class="language-plaintext highlighter-rouge">newuidmap</code></a> and <a href="https://man7.org/linux/man-pages/man1/newgidmap.1.html"><code class="language-plaintext highlighter-rouge">newgidmap</code></a> already
allowed this for a longer time based on static configuration files. The
<code class="language-plaintext highlighter-rouge">nsresourced</code> service applies <em>dynamic</em> limits and ID ranges to processes in
the system, however, which makes things quite more complicated. This even
includes <a href="https://github.com/systemd/systemd/blob/781d9d0789379d1ea1f2ecefb804d41e9c8b6c38/src/nsresourced/bpf/userns-restrict/userns-restrict.bpf.c">an EBPF program</a>, which keeps track
of the uses of the resulting user namespace file descriptors. Despite this
complexity we could not find any issues in this component either.</p>

<p>What kept us busy for a longer time was <a href="https://github.com/systemd/systemd/blob/781d9d0789379d1ea1f2ecefb804d41e9c8b6c38/src/mountfsd/mountwork.c#L852">logic
invoked</a> by <code class="language-plaintext highlighter-rouge">mountfsd</code> to obtain the
user and group ID mapping tied to the user namespace file descriptor passed by
the unprivileged client. To retrieve this information, the utility function
<a href="https://github.com/systemd/systemd/blob/781d9d0789379d1ea1f2ecefb804d41e9c8b6c38/src/basic/namespace-util.c#L600"><code class="language-plaintext highlighter-rouge">ns_enter_and_pin()</code></a> forks a short-lived
child process which joins the user namespace provided by the client.  The
parent process then reads the child’s <code class="language-plaintext highlighter-rouge">uid_map</code> and <code class="language-plaintext highlighter-rouge">gid_map</code> nodes from
<code class="language-plaintext highlighter-rouge">/proc/&lt;child-pid&gt;</code>.</p>

<p>The <code class="language-plaintext highlighter-rouge">mountfsd</code> daemon runs with <code class="language-plaintext highlighter-rouge">root</code> privileges (although some sandboxing is
applied to it as well), which will be inherited by the short-lived child
process. Once the child process joins the user namespace provided by the
unprivileged client, the security domain of this process changes, however,
because the client owning the namespace is supposed to have full control over
processes associated with it.</p>

<p>One consequence of this is that the owner of the user namespace can send
arbitrary signals to the short-lived <code class="language-plaintext highlighter-rouge">systemd</code> process, e.g. to kill it. This
would only result in a kind of Denial-of-Service against the client itself and
should not cause any security issues.</p>

<p>We expected another important ramification of this to be in the area of the
<a href="https://man7.org/linux/man-pages/man2/ptrace.2.html"><code class="language-plaintext highlighter-rouge">ptrace()</code> system call</a>. The following is stated in the “ptrace
access mode checking” section of the <code class="language-plaintext highlighter-rouge">ptrace(2)</code> man page:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(3)  Deny access if neither of the following is true:

            •  The real, effective, and saved-set user IDs of the target
               match the caller's user ID, and the real, effective, and
               saved-set group IDs of the target match the caller's group
               ID.

            •  The caller has the CAP_SYS_PTRACE capability in the user
               namespace of the target.
</code></pre></div></div>

<p>According to the second item, the unprivileged client, which owns all
capabilities in its user namespace, should be able to trace the short-lived
<code class="language-plaintext highlighter-rouge">systemd</code> process which joins the client-controlled user namespace. This
ability would have allowed for an interesting privilege escalation, because
tracing capabilities also include the ability to modify the target process,
e.g. to change its code and data. While trying to reproduce this, the kernel
always denied <code class="language-plaintext highlighter-rouge">ptrace()</code> access to this short-lived process, however, and we
were not sure why. Unclarity in such aspects is not a good thing when it
concerns security, thus we set out to get to the bottom of this.</p>

<p>After diving deep into the Linux kernel’s <code class="language-plaintext highlighter-rouge">ptrace()</code> code, we found <a href="https://github.com/torvalds/linux/commit/bfedb589252c01fa505ac9f6f2a3d5d68d707ef4">the
commit</a> which is responsible for the rejection of
tracing access in this scenario. The background of this commit actually is
to prevent owners of unprivileged user namespaces from accessing the
executable of processes created in the initial namespace. <code class="language-plaintext highlighter-rouge">ptrace()</code> access to
the target PID is now only allowed if the target process performed an
<code class="language-plaintext highlighter-rouge">execve()</code> while being a member of the newly joined user namespace. In summary
this means the following:</p>

<ul>
  <li>if a process only performs <code class="language-plaintext highlighter-rouge">fork()</code> and <code class="language-plaintext highlighter-rouge">setns()</code> to join a user namespace,
then <code class="language-plaintext highlighter-rouge">ptrace()</code> access to this process is denied to the owner of the user
namespace.</li>
  <li>if a process performs <code class="language-plaintext highlighter-rouge">fork()</code>, <code class="language-plaintext highlighter-rouge">setns()</code> and <code class="language-plaintext highlighter-rouge">execve()</code>, then <code class="language-plaintext highlighter-rouge">ptrace()</code>
access to this process is granted to the owner of the user namespace.</li>
</ul>

<p>This detail is not documented in the <a href="https://man7.org/linux/man-pages/man2/ptrace.2.html"><code class="language-plaintext highlighter-rouge">ptrace()</code> man page</a> and it
took us a while to fully understand what was going on. With this well
understood we could finally move on, knowing that the logic in <code class="language-plaintext highlighter-rouge">mountfsd</code> is
robust.</p>

<h1 id="section-plasma-setup">3) D-Bus Issues in Unreleased <code class="language-plaintext highlighter-rouge">plasma-setup</code> KDE Package</h1>

<p>This new KDE component was first named KISS (KDE initial system setup), but
meanwhile has been renamed to <a href="https://github.com/KDE/plasma-setup.git"><code class="language-plaintext highlighter-rouge">plasma-setup</code></a>.
Its purpose is to perform initial system configuration based on a graphical
wizard, when a Linux system has been freshly installed.</p>

<p>Our openSUSE KDE packagers <a href="https://bugzilla.suse.com/show_bug.cgi?id=1249520">asked for a review</a> of this new
component, expecting it to be part of a major KDE release in autumn. It turned
out that this had not been planned by upstream after all (or plans changed).
Still the review we performed turned out to be useful, since we identified
various security problems in the existing code which could be fixed by
upstream before the new component had seen production use.</p>

<p>The following report is based on the <code class="language-plaintext highlighter-rouge">plasma-setup</code> source code as of upstream
<a href="https://github.com/KDE/plasma-setup/tree/08ed810e0e7ba1642d6f2bd211e0ba43e85f8496">commit 08ed810e0e7</a>. While the graphical
components of <code class="language-plaintext highlighter-rouge">plasma-setup</code> run with low privileges, there exists a D-Bus
helper service running as <code class="language-plaintext highlighter-rouge">root</code>, <code class="language-plaintext highlighter-rouge">kde-initial-system-setup-auth-helper</code>,
which allows to perform a number of operations with elevated privileges. These
operations are guarded by Polkit authorization rules. The dedicated user
account <code class="language-plaintext highlighter-rouge">kde-initial-system-setup</code> is allowed to invoke any of these actions
without authentication. Beyond this, any locally logged-in users are also
allowed to invoke the operations without authentication. The latter is quite
problematic, as will be outlined below.</p>

<p>The implementation of the D-Bus callbacks for these actions is found in
<a href="https://github.com/KDE/plasma-setup/blob/08ed810e0e7ba1642d6f2bd211e0ba43e85f8496/src/auth/authhelper.cpp"><code class="language-plaintext highlighter-rouge">src/auth/authhelper.cpp</code></a>. The following
sub-sections discuss issues in a couple of these actions.</p>

<h2 id="sub-section-autostarthook">org.kde.initialsystemsetup.createnewuserautostarthook</h2>

<p>This action <a href="https://github.com/KDE/plasma-setup/blob/08ed810e0e7ba1642d6f2bd211e0ba43e85f8496/src/auth/authhelper.cpp#L28">receives a “username” parameter</a>
from the unprivileged D-Bus client. The username is not verified by the
privileged helper, it only needs to be convertible to <code class="language-plaintext highlighter-rouge">QString</code>. The helper
then creates all the directory components of
<code class="language-plaintext highlighter-rouge">/home/&lt;username&gt;/.config/autostart</code>. After this, the file
<code class="language-plaintext highlighter-rouge">/home/&lt;username&gt;/.config/autostart/remove-autologin.desktop</code> is created and
fixed data is written into it.</p>

<p>This action allows local users to create arbitrary world-readable directories
owned by <code class="language-plaintext highlighter-rouge">root</code>. This can be achieved by passing a string like
<code class="language-plaintext highlighter-rouge">../../my/desired/path</code> as “username”. Furthermore, by placing a symlink at
the expected location of <code class="language-plaintext highlighter-rouge">remove-autologin.desktop</code>, arbitrary files in the
system can be overwritten, leading to a local Denial-of-Service.</p>

<p>The implementation of the action also causes the created directories and files
to be owned by <code class="language-plaintext highlighter-rouge">root:root</code>, and not by the user that actually owns the home
directory, which is unclean.</p>

<h3 id="suggested-fixes">Suggested Fixes</h3>

<p>Apart from restricting access to the helper to the <code class="language-plaintext highlighter-rouge">kde-initial-system-setup</code>
user, the implementation of this action should verify whether the
passed-in username actually exists. Furthermore, the home directory of this
account should be obtained via the <a href="https://man7.org/linux/man-pages/man3/getpwent.3.html"><code class="language-plaintext highlighter-rouge">getpwent()</code></a> API, instead
of assuming that <code class="language-plaintext highlighter-rouge">/home/&lt;username&gt;</code> will always be the correct home directory.</p>

<p>When the execution of this helper is actually limited to the initial setup
context, it could be technically acceptable to operate as <code class="language-plaintext highlighter-rouge">root</code> in the newly
created user’s home directory. For reasons of prudence and giving a good
example, we still recommend to drop privileges to the target user account
before actually writing the <code class="language-plaintext highlighter-rouge">.desktop</code> file in the user’s home directory.</p>

<h2 id="orgkdeinitialsystemsetupsetnewuserhomedirectoryownership">org.kde.initialsystemsetup.setnewuserhomedirectoryownership</h2>

<p>The method call associated with this action <a href="https://github.com/KDE/plasma-setup/blob/08ed810e0e7ba1642d6f2bd211e0ba43e85f8496/src/auth/authhelper.cpp#L125">also receives a “username”
parameter</a> which is not verified. The
following command line is invoked based on the “username” parameter:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">chown</span> <span class="nt">-R</span> &lt;username&gt;:&lt;username&gt; /home/&lt;username&gt;
</code></pre></div></div>

<p>This is on the verge of a local root exploit, save for the fact that <code class="language-plaintext highlighter-rouge">chown</code>
expects a valid user and group account to give the ownership to, which at the
same time needs to result in the proper path to operate on.  A username
containing path elements will fail, because the necessary characters like <code class="language-plaintext highlighter-rouge">/</code>
are by default denied in usernames.</p>

<p>This action still allows to potentially change ownership of all files of
arbitrary other users’ home directories. Fortunately the recursive <code class="language-plaintext highlighter-rouge">chown</code>
algorithm is not subject to symlink attacks these days. If somebody would be
able to place a symlink in place of their home directory in
<code class="language-plaintext highlighter-rouge">/home/&lt;username&gt;</code>, then the symlink would still be followed, however.</p>

<p>The username could also be interpreted as an arbitrary command line argument
to <code class="language-plaintext highlighter-rouge">chown</code>, thwarted only by the fact that the <code class="language-plaintext highlighter-rouge">&lt;username&gt;:&lt;username&gt;</code>
argument is constructed here instead of just passing <code class="language-plaintext highlighter-rouge">&lt;username&gt;</code>, which will
prevent proper command line arguments from being passed.</p>

<h3 id="suggested-fixes-1">Suggested Fixes</h3>

<p>As for the previous action, the implementation should verify if the username
is valid and determine the proper home directory and group via <code class="language-plaintext highlighter-rouge">getpwent()</code>.
The assumption that username and group are equivalent is also problematic
here.</p>

<p>Why this operation would be needed at all for a newly created home directory
is questionable. When new user accounts are created, file ownership should
already be correct. If this action is supposed to fix the ownership of files
created by other <code class="language-plaintext highlighter-rouge">plasma-setup</code> actions in the home directory as <code class="language-plaintext highlighter-rouge">root</code> (as is
seen in the <a href="#sub-section-autostarthook"><code class="language-plaintext highlighter-rouge">createnewuserautostarthook</code> action</a>),
then this is only a hack which should be removed in favor of not creating
files as <code class="language-plaintext highlighter-rouge">root</code> in unprivileged users’ home directories in the first place.</p>

<h2 id="orgkdeinitialsystemsetupsetnewusertempautologin">org.kde.initialsystemsetup.setnewusertempautologin</h2>

<p>Again this method <a href="https://github.com/KDE/plasma-setup/blob/08ed810e0e7ba1642d6f2bd211e0ba43e85f8496/src/auth/authhelper.cpp#L150">receives a “username”
parameter</a> which is not verified. The
implementation writes the following content to the file
<code class="language-plaintext highlighter-rouge">/etc/sddm.conf.d/99-kde-initial-system-setup.conf</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Autologin]
User=&lt;username&gt;
Session=plasma
Relogin=true
</code></pre></div></div>

<p>This SDDM configuration snippet is supposed to automatically login the
given user account. For some reason that we did not investigate more deeply,
the configuration was not effective during our tests on openSUSE
Tumbleweed. We could verify that the configuration file created this way was
parsed and evaluated in SDDM, however, so something else must have been amiss.</p>

<p>The automatic login is supposed to work, though, and if it does, then any
local user account can call this action with <code class="language-plaintext highlighter-rouge">root</code> as username, which should
cause an automatic login of the <code class="language-plaintext highlighter-rouge">root</code> user the next time SDDM runs.</p>

<p>By passing crafted strings for “username”, the content of the drop-in
configuration file can even be fully controlled by local users. The following
“username” would create a General section with a crafted “RebootCommand”, for
example:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>user\n[General]\nRebootCommand=/home/myuser/evil
</code></pre></div></div>

<p>Provided the configuration snippet is actually in effect in SDDM, this action
allows for a local root exploit.</p>

<h3 id="suggested-fixes-2">Suggested Fixes</h3>

<p>As for the other actions, the implementation should verify whether the passed
“username” is valid and does not equal <code class="language-plaintext highlighter-rouge">root</code>.</p>

<h2 id="upstream-fixes">Upstream Fixes</h2>

<p>We privately approached KDE security on 2025-09-22 with a detailed report
about these findings. As a result we established contact with the
<code class="language-plaintext highlighter-rouge">plasma-setup</code> developer and discussed fixes for the issues. It was decided to
perform the bugfix in the open, since the component was not yet part of a
stable release of KDE. We reviewed an <a href="https://invent.kde.org/plasma/plasma-setup/-/merge_requests/48">upstream merge
request</a> during the course of two weeks and upstream
managed to arrive at a much improved version of the KAuth helper component.</p>

<p>As of <a href="https://github.com/KDE/plasma-setup/blob/e6eb1cd9a8d4094ff2771d25ee6f761ea9f05c6c/src/auth/authhelper.cpp">commit e6eb1cd9a8d</a> the privileged
helper carefully scrutinizes the input parameters received via D-Bus, and it
also drops privileges to the calling user before operating in the unprivileged
user’s home directory. Also the KAuth actions provided by the helper are now
restricted to the <code class="language-plaintext highlighter-rouge">plasma-setup</code> service user and no longer accessible to
all locally logged-in users. The latter would still be problematic, since it
would allow to setup automatic login for arbitrary other users in the system,
for example.</p>

<h1 id="section-plocate">4) Discussion about Granting setgid Privileges to the <code class="language-plaintext highlighter-rouge">plocate</code> Binary</h1>

<p>An openSUSE community member approached us about <a href="https://bugzilla.suse.com/show_bug.cgi?id=1254549">granting special setgid
privileges to the <code class="language-plaintext highlighter-rouge">plocate</code></a> binary. <code class="language-plaintext highlighter-rouge">plocate</code> is a modern and
fast replacement for the classic <code class="language-plaintext highlighter-rouge">locate</code> program. Upstream supports operation
of the <code class="language-plaintext highlighter-rouge">plocate</code> program with the <code class="language-plaintext highlighter-rouge">setgid</code> bit assigned to the <code class="language-plaintext highlighter-rouge">plocate</code>
group. This means that the program is granted <code class="language-plaintext highlighter-rouge">plocate</code> group privileges
during execution.</p>

<p>When <code class="language-plaintext highlighter-rouge">updatedb</code>, locate’s utility for indexing files, would be invoked with
full root privileges, then the database in <code class="language-plaintext highlighter-rouge">/var/lib/plocate</code> would contain
information about all files in the file system. This way <code class="language-plaintext highlighter-rouge">locate</code> would grant
all users in the system read access to this information, resulting in an
information leak, because users can see paths that they would not normally be
allowed to list, like all the files stored in the <code class="language-plaintext highlighter-rouge">/root</code> home directory. For
this reason the <code class="language-plaintext highlighter-rouge">plocate-updatedb</code> system service on openSUSE Tumbleweed runs
as <code class="language-plaintext highlighter-rouge">nobody:nobody</code>, resulting in a system-wide <code class="language-plaintext highlighter-rouge">plocate</code> database which only
contains information about publicly accessible paths in the system. For being
able to locate their own private files, users need to create their own
user-specific databases instead.</p>

<p>The purpose of the setgid privilege is to address this <code class="language-plaintext highlighter-rouge">locate</code> database
access issue. <code class="language-plaintext highlighter-rouge">plocate</code> supports a mode in which <code class="language-plaintext highlighter-rouge">updatedb</code> is invoked with
full root privileges, but the ownership of the central database is changed to
<code class="language-plaintext highlighter-rouge">root:plocate</code> and file mode <code class="language-plaintext highlighter-rouge">0640</code>. When <code class="language-plaintext highlighter-rouge">plocate</code> is installed as
setgid-plocate then it is still allowed to access the central database. The
program drops the special group credentials quickly again, right after opening
the database. The program then ensures that the calling user will only be able
to retrieve information about files that it is allowed to access based on its
real credentials.</p>

<p>There is a minor security issue found in this approach. Since the <code class="language-plaintext highlighter-rouge">plocate</code>
database does not contain metadata about the files it indexed, the <code class="language-plaintext highlighter-rouge">plocate</code>
program needs to check the ownership of files in the file system at the time
the search query runs. This is a sort of a TOCTOU (time-of-check time-of-use)
race condition. There can be situations when the verification in <code class="language-plaintext highlighter-rouge">plocate</code>
yields wrong results:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root# <span class="nb">mkdir</span> <span class="nt">--mode</span><span class="o">=</span>1777 /shared
root# <span class="nb">mkdir</span> <span class="nt">--mode</span><span class="o">=</span>0700 /shared/secret-dir
root# <span class="nb">touch</span> /shared/secret-dir/secret-file
root# updatedb

<span class="c"># root will be able to locate any files in secret-dir</span>
root# locate /shared/se
/shared/secret-dir
/shared/secret-dir/secret-file

<span class="c"># non-root cannot locate the secret-file</span>
user<span class="nv">$ </span>locate /shared/se
/shared/secret-dir

<span class="c"># now consider root deletes the secret-dir again</span>
root# <span class="nb">rm</span> <span class="nt">-rf</span> /shared/secret-dir

<span class="c"># now the unprivileged user takes ownership of this path</span>
user<span class="nv">$ </span><span class="nb">mkdir</span> <span class="nt">--mode</span><span class="o">=</span>0755 /shared/secret-dir

<span class="c"># this only works before `updatedb` is called again, because then it will</span>
<span class="c"># notice that secret-file no longer exists and delete it from the database.</span>
<span class="c">#</span>
<span class="c"># when the unprivileged user calls locate this time, the secret-file will show</span>
<span class="c"># up, since the "secret-dir" is now controlled by the unprivileged caller.</span>
user<span class="nv">$ </span>locate /shared/se
/shared/secret-dir
/shared/secret-dir/secret-file
</code></pre></div></div>

<p>This problem likely cannot be easily fixed in the <code class="language-plaintext highlighter-rouge">plocate</code> code, since it
would require changing the database format radically, increasing database size
as a result, only to fix an unlikely problem.</p>

<p>The information leak is minor and should rarely be exploitable. For this
reason we left it up to the openSUSE <code class="language-plaintext highlighter-rouge">plocate</code> package maintainer whether the
setgid-plocate approach should be used, or not.</p>

<h1 id="section-virtualbmc">5) Local Root Exploit in OpenStack’s non-production <code class="language-plaintext highlighter-rouge">virtualbmc</code> Project</h1>

<p>By way of our efforts to monitor newly introduced <code class="language-plaintext highlighter-rouge">systemd</code> services in
openSUSE Tumbleweed, the <a href="https://github.com/openstack/virtualbmc"><code class="language-plaintext highlighter-rouge">python-virtualbmc</code></a>
package caught our attention. The program allows to emulate a board management
controller (BMC) interface for use with libvirt.</p>

<p>Part of the package is a daemon running with full root privileges, listening
for ZeroMQ API requests on <code class="language-plaintext highlighter-rouge">localhost</code>. A number of unauthenticated API calls
in this context raised our suspicions, which is why we scheduled a <a href="https://bugzilla.suse.com/show_bug.cgi?id=1253677">full
review of this package</a>. A closer look showed that the
unauthenticated API calls were indeed problematic, even allowing for a full
local root exploit.</p>

<p>We filed a detailed private bug report on
<a href="https://bugs.launchpad.net/virtualbmc/+bug/2133163">LaunchPad</a> for the OpenStack project, but had
difficulties getting a response. After some weeks we reached out to an
individual member of the OpenStack security team and learned from the reply
that the virtualbmc project was not intended for production use at all, but is
rather a utility intended for use in testing environments. This is also
documented in the repository’s <a href="https://github.com/openstack/virtualbmc/blob/master/README.rst">README</a>, which was
overlooked by us. As a result we filed a delete request for the
<code class="language-plaintext highlighter-rouge">python-virtualbmc</code> package in openSUSE Tumbleweed, and the package has
already been removed.</p>

<p>For completeness, a detailed report of the security issues in the virtualbmc
daemon follows below.</p>

<h2 id="lack-of-authorization-and-input-validation-in-vbmcd">Lack of Authorization and Input Validation in <code class="language-plaintext highlighter-rouge">vbmcd</code></h2>

<p>When the <code class="language-plaintext highlighter-rouge">virtualbmc</code> <code class="language-plaintext highlighter-rouge">systemd</code> service is started, then <code class="language-plaintext highlighter-rouge">/usr/bin/vbmcd</code> runs
with full root privileges. It offers a ZeroMQ-based network API, listening on
localhost port 50891 by default. Any local user in the system can talk to the
daemon this way.</p>

<p>A simple request which can be sent to the daemon (in JSON format) is the
following stop command, for example:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
        </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"stop"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"port"</span><span class="p">:</span><span class="w"> </span><span class="mi">1234</span><span class="p">,</span><span class="w">
        </span><span class="nl">"domain_names"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"../../home/myaccount/mydomain"</span><span class="p">],</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">domain_name</code> passed here will be used by the daemon to lookup a
supposedly trusted per-domain configuration file, which is by default located
in <code class="language-plaintext highlighter-rouge">/root/.vbmc/&lt;domain&gt;/config</code>. Since the daemon does not scrutinize the
input <code class="language-plaintext highlighter-rouge">domain_name</code>, a local attacker can include directory components in the
name, to trick the daemon into accessing an attacker-controlled configuration
file.</p>

<p>In the context of the <code class="language-plaintext highlighter-rouge">stop</code> command used here, the daemon will try to update
the domain’s configuration file in case a change of domain state is detected.
The path for writing out the updated configuration file will be constructed
using the <code class="language-plaintext highlighter-rouge">domain_name</code> found in the input configuration file. Thus the local
attacker can place data like this into <code class="language-plaintext highlighter-rouge">/home/myaccount/mydomain/config</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[VirtualBMC]
domain_name = ../../etc/sudoers.d
port = 1234
active = true
address = some
  evil stuff
  myaccount ALL=(ALL:ALL) NOPASSWD: ALL
</code></pre></div></div>

<p>The daemon will now believe that the domain’s state changed, because the input
configuration file contains <code class="language-plaintext highlighter-rouge">active = true</code>, while the daemon was asked to
stop the domain. This will trigger logic to write out an updated configuration
file with the new state of the domain configuration. The logic for this is
found in the <a href="https://github.com/openstack/virtualbmc/blob/6e14e8bdb8cc022d843bfb98377bfc89d99fc9c5/virtualbmc/manager.py#L90"><code class="language-plaintext highlighter-rouge">_vbmc_enabled()</code></a> member
function.</p>

<p>Since the <code class="language-plaintext highlighter-rouge">domain_name</code> found in the crafted configuration file is set to
<code class="language-plaintext highlighter-rouge">../../etc/sudoers.d</code>, the daemon will write the new configuration file into
<code class="language-plaintext highlighter-rouge">/root/.vbmcd/../../etc/sudoers.d/config</code>. To get an advantage from this, the
attacker must get the daemon to write out at least one valid <code class="language-plaintext highlighter-rouge">sudoers</code>
configuration line into the new configuration file.</p>

<p>The attacker has only a limited degree of freedom at this stage, because
the daemon will write out the new configuration file via the Python
<code class="language-plaintext highlighter-rouge">configparser</code> module and will only consider the <code class="language-plaintext highlighter-rouge">[VirtualBMC]</code> section as
well as any of the configuration keys listed in the <a href="https://github.com/openstack/virtualbmc/blob/6e14e8bdb8cc022d843bfb98377bfc89d99fc9c5/virtualbmc/manager.py#L40"><code class="language-plaintext highlighter-rouge">VBMC_OPTIONS</code>
list</a> defined in the daemon’s code.</p>

<p>To help with the exploit, the
<a href="https://docs.python.org/3/library/configparser.html"><code class="language-plaintext highlighter-rouge">configparser</code></a>
multiline syntax comes to the rescue: any lines following an assignment which
are indented will be accepted as part of the configuration value. When writing
the settings out to a new configuration file, these multiline settings will be
preserved.  This is put to use in the example above, which contains a final
line <code class="language-plaintext highlighter-rouge">myaccount ALL=...</code>. This line will now appear along with the rest of the
configuration data in <code class="language-plaintext highlighter-rouge">/etc/sudoers.d/config</code>.</p>

<p>As a result, when the attacker now invokes <code class="language-plaintext highlighter-rouge">sudo su -</code>, a couple of sudoers
parsing errors will appear, but in the end, access is granted and a root shell
will be obtained by the attacker.</p>

<p>This approach of using a sudoers drop-in configuration file is just one of the
more obvious approaches that came to mind. There’s a lot of different ways
to exploit this, however, for example by overwriting shell scripts or script
snippets in <code class="language-plaintext highlighter-rouge">/etc</code> or <code class="language-plaintext highlighter-rouge">/usr/bin</code> and then waiting for a privileged process to
run them. This would be even easier, because shell scripts have less
strict syntax requirements compared to the sudoers configuration file. The
effect would not be immediate, however, like in the sudoers approach.</p>

<h2 id="reproducer">Reproducer</h2>

<p>We offer a <a href="/download/virtualbmc-exploit.py">Python script for download</a>, which
is a Proof-of-Concept (PoC) to reproduce the local root exploit in the context
of an arbitrary unprivileged user on the system, when <code class="language-plaintext highlighter-rouge">vbmcd</code> is running with
its default configuration.  <code class="language-plaintext highlighter-rouge">sudo</code> needs to be installed, naturally, for the
exploit to work.</p>

<h2 id="further-concerns">Further Concerns</h2>

<p>In general, the API offered by <code class="language-plaintext highlighter-rouge">vbmcd</code> on localhost is missing input
sanitization and authorization. Authorization seems only to be performed
indirectly via libvirt. In this context clients can also pass crafted
<code class="language-plaintext highlighter-rouge">libvirt_uri</code> parameters, for example, which seem to make it possible to let
the daemon connect to arbitrary URLs via SSH. There also is no isolation
between different users’ domain configurations, e.g. the “stop” command used
above can be issued for any domain configured by another user in the system.</p>

<p>To make this API safe, we believe there needs to be an ownership model for
each domain’s configuration, a verification of the client’s credentials in
some form (a UNIX domain socket would allow this more easily) and sanitization
of all input parameters to avoid any unexpected side effects.</p>

<p>Since the daemon listens on an unprivileged port on localhost, other
unprivileged users can try to bind to this port first and provide a fake
<code class="language-plaintext highlighter-rouge">vbmcd</code> service. Since the API requests can also contain secret credentials,
this would pose a major local information leak. For safe operation, the API
would need to bind to a privileged port on localhost instead.</p>

<h1 id="section-snapd">6) Revisit of the <code class="language-plaintext highlighter-rouge">snapd</code> Package Manager</h1>

<p>In 2019 we received <a href="https://bugzilla.suse.com/show_bug.cgi?id=1127366">a request</a> to add the <a href="https://snapcraft.io"><code class="language-plaintext highlighter-rouge">snapd</code> package
manager</a> to openSUSE, which involved a <a href="https://bugzilla.suse.com/show_bug.cgi?id=1127368">review of the
setuid-root program</a> <code class="language-plaintext highlighter-rouge">snap-confine</code>. At the time we were
generally satisfied with the code quality and design of the program, but still
<a href="https://bugzilla.suse.com/show_bug.cgi?id=1127368#c3">found a few low to medium severity security issues</a>
and gave recommendations on how to improve the code in some spots. The
packagers have meanwhile been busy with other topics and we never saw
an updated openSUSE package containing the necessary changes, which is why we
closed the related bugs after a period of inactivity.</p>

<p>In August we received <a href="https://bugzilla.suse.com/show_bug.cgi?id=1248682">a follow-up request</a> for addition of an
updated <code class="language-plaintext highlighter-rouge">snapd</code> package. We revisited the privileged components and again
<a href="https://bugzilla.suse.com/show_bug.cgi?id=1248682#c5">provided feedback</a> to upstream. This time
all remaining issues could be resolved and the new package has been allowed to
become part of openSUSE Tumbleweed. We are happy to see these old efforts not
going completely to waste, and welcome the possibility to use Snap packages on
openSUSE Tumbleweed in the future.</p>

<h1 id="7-conclusion">7) Conclusion</h1>

<p>Again we hope we’ve been able to give you some additional insight into our
efforts to maintain the security of SUSE distributions and open source
software. We are looking forward to the next edition of the spotlight series,
which will be published in about three months from now.</p>]]></content><author><name>&lt;a href=&apos;mailto:matthias.gerstner@suse.de&apos;&gt;Matthias Gerstner&lt;/a&gt;, &lt;a href=&apos;mailto:filippo.bonazzi@suse.com&apos;&gt;Filippo Bonazzi (editor)&lt;/a&gt;</name></author><category term="spotlight" /><summary type="html"><![CDATA[This is the autumn 2025 edition of our spotlight series. Once again it has been a very busy three months for us in the SUSE security team. Some of the topics we will cover this time are the final outcome of our systemd v258 code review efforts, improvements we helped with in KDE's new plasma-setup utility and security issues in the virtualbmc OpenStack component, which turned out to be intended for testing purposes only.]]></summary></entry></feed>