|
Message-ID: <Z5Oiro01QGLzfaXu@kasco.suse.de>
Date: Fri, 24 Jan 2025 15:24:46 +0100
From: Matthias Gerstner <mgerstner@...e.de>
To: oss-security@...ts.openwall.com
Subject: dde-api-proxy: Authentication Bypass in Deepin D-Bus Proxy Service
(CVE-2025-23222)
Hello list,
following is a report about a local privilege escalation in
dde-api-proxy. This report is also available as a rendered version on
our blog [1].
1) Introduction
===============
We received a review request [2] for the Deepin api-proxy D-Bus service
[3] which is part of the Deepin desktop environment [4]. During the
review we discovered a major authentication flaw in the design of this
D-Bus service which allows local users to escalate privileges in various
ways.
We reported this issue privately to Deepin security in December and did
not receive a reply for a month. As we were preparing for publication,
upstream became alive and quickly released a bugfix which is, sadly,
still incomplete.
This report is based on dde-api-proxy version 1.0.17 [5]. The findings
still apply to release 1.0.18. Upstream has attempted to fix these
findings in release 1.0.19, but the bugfix is insufficient as outlined
in section 6).
2) Authentication Bypass Issue
==============================
Dde-api-proxy runs as `root` and provides various D-Bus services on the
D-Bus system bus. It sticks out since it ships a lot of D-Bus
configuration files but only little code. The reason for this is that
the service only forwards D-Bus requests between its clients and the
actual Deepin D-Bus services. We believe this is for backward
compatibility due to changes in Deepin D-Bus interface names, alas the
component's GitHub repository [3] provides little insight into its
purpose.
During startup the proxy service proactively registers the requested
legacy D-Bus interface and creates a connection to the actual Deepin
D-Bus service, to which messages will be forwarded to. When a client
sends a message to one of the legacy service names, the proxy
synchronously forwards the message (see `handleMessage()` [6]) via its
existing connection and returns the reply to the client.
This rather straightforward approach of proxying D-Bus messages has a
major security flaw, however:
- the proxy runs as `root`.
- the proxy forwards messages from arbitrary local users to the actual D-Bus
services without any authentication requirements.
- the actual D-Bus services don't know about the proxy situation, they believe
that `root` is asking them to perform operations.
Consequently with the help of dde-api-proxy, legacy D-Bus methods that
normally wouldn't be accessible to non-root users will become accessible
without authentication.
3) Reproducers
==============
D-Bus Method without Polkit
---------------------------
Following is a simple demonstration of the issue based on the Deepin
Grub2 service. This service simply checks the UID of the D-Bus client
for authentication of privileged operations. In the first command shown
below the actual D-Bus service name is used, and the service rejects the
operation, because the caller is not privileged:
user$ gdbus call -y -d org.deepin.dde.Grub2 \
-o /org/deepin/dde/Grub2 -m org.deepin.dde.Grub2.SetTimeout 100
Error: GDBus.Error:org.deepin.dde.DBus.Error.Unnamed: not allow :1.167 call this method
In the next command the legacy D-Bus service name is used, and this time
the target service performs the operation, because it believes the
request originates from a privileged UID 0 client (dde-api-proxy):
user$ gdbus call -y -d com.deepin.daemon.Grub2 \
-o /com/deepin/daemon/Grub2 -m com.deepin.daemon.Grub2.SetTimeout 10
()
D-Bus method using Polkit
-------------------------
In the previous example Polkit authentication was not involved. When it
is involved then the caller is treated as "admin", resulting in a
similar escalation of privileges. We found a suitable example using
Polkit in the Deepin accounts service. This service offers a large range
of system operations, among them the possibility to add users to groups.
It checks the authorization of the "org.deepin.dde.accounts.user-administration"
Polkit action, which is by default only allowed for users in a local
session if they provide admin credentials.
The following `gdbus` call attempts to add the unprivileged user with
UID 1000 to the `root` group. The call only works this way when it runs
from within a (Deepin) graphical session. The actual accounts service
interface is invoked here, thus the operation fails (it would require
entering a root password).
user$ gdbus call -y -d org.deepin.dde.Accounts1 -o /org/deepin/dde/Accounts1/User1000 \
-m org.deepin.dde.Accounts1.User.AddGroup root
Error: GDBus.Error:org.deepin.dde.DBus.Error.Unnamed: Policykit authentication failed
When switching to the legacy accounts service interface offered by
dde-api-proxy, the operation succeeds without any authentication
request, because the accounts service again believes root with UID 0 is
the client asking for this:
user$ gdbus call -y -d com.deepin.daemon.Accounts -o /com/deepin/daemon/Accounts/User1000 \
-m com.deepin.daemon.Accounts.User.AddGroup root
()
4) Affected D-Bus Interfaces
============================
We did not look into all the privileged D-Bus methods that become
available to unauthenticated local users via dde-api-proxy. On some of
the proxied interfaces only a certain set of "filtered methods" is
allowed to be invoked. The rest of the interfaces don't put
restrictions on the methods invoked, though. On first look, interesting
attack surface seems to be found in the following D-Bus interfaces
offered by dde-api-proxy:
- Accounts services (no method filter list)
- network proxy settings (no method filter list)
- PasswdConf1 WriteConfig method
- Lastore service (Apt backend, no method filter list)
- Lastore manager install package method
5) Suggested Bugfix
===================
The authentication bypass is deeply rooted in the design of
dde-api-proxy, thus fixing it is difficult. Possible approaches to
addressing it are presented in the following sub-sections.
a) Dropping Privileges
----------------------
The proxy could temporarily drop privileges to the unprivileged caller's
credentials, create a new D-Bus connection and forward the message to
the proper service. This still won't work properly if the D-Bus service
in question is using Polkit for authentication, because Polkit
differentiates whether the caller is in an active session or not. The
D-Bus proxy service will never be in a session, though. This means that
authentication requirements could be stronger than necessary. At least
this approach would be safer than what currently happens.
b) Reimplementing Authentication Checks
---------------------------------------
The proxy could implement all necessary authentication checks on its
own. This would result in the proper authentication being performed, but
would lead to duplication of a lot of code. It would also add the danger
that the authentication requirements of the proxy service and the actual
service get out of sync.
c) Implementing Legacy Interfaces in the Affected Services
----------------------------------------------------------
Finally dde-api-proxy could be dropped completely and the backward
compatibility could be implemented in every one of the affected
services. This is a less generic approach than what dde-api-proxy
attempts to achieve, of course.
6) Upstream Bugfix
==================
After a longer period of silence, upstream unexpectedly replied to our
report and a short embargo period was established until a bugfix was
published on January 17. The bugfix is found in upstream commit
95b50dd [7] which made its way into release 1.0.19 [8].
For the bugfix upstream went in the direction of our suggestion outlined
in section 5.b), by implementing redundant Polkit authorization checks
in the proxy service. A list of sensitive D-Bus methods offered by the
proxy is now maintained in the source code. All of these methods are
protected by a single, newly introduced Polkit action
"org.deepin.dde.api.proxy" which requires admin authentication.
The bugfix introduces a new problem, though. The Polkit authorization
check is implemented as follows:
```
bool checkAuthorization(const QString &actionId, const QString &service,const QDBusConnection &connection) const
{
auto pid = connection.interface()->servicePid(service).value();
auto authority = PolkitQt1::Authority::instance();
auto result = authority->checkAuthorizationSync(actionId,
PolkitQt1::UnixProcessSubject(pid),
PolkitQt1::Authority::AllowUserInteraction);
/* snip */
}
```
This code forwards the client's process ID (PID) to the Polkit service
for authentication. This way of using the Polkit UnixProcessSubject has
been deprecated for a long time, because it is subject to a race
condition that allows to bypass such authorization checks. This issue
was discovered [9] in 2013 by former SUSE security engineer Sebastian
Krahmer, and was assigned CVE-2013-4288.
Upstream did not share the bugfix with us before publication, thus we
were not able to prevent this incomplete bugfix. It should be possible
to amend the incomplete bugfix by switching to the SystemBusName subject
for authentication.
Even with an improved fix we believe this approach is not ideal, since
it requires proper maintenance of all the proxied methods by upstream.
If new methods are added at a later time, security issues could sneak in
again. Also the newly introduced Polkit action makes the proxy service
less transparent and could hamper the user experience. There is no more
fine-grained control over the authentication requirements of individual
D-Bus methods, and the authentication message for all of these proxied
D-Bus methods is generic and unhelpful for end users.
7) Possible Workarounds
=======================
We don't see any viable ways to work around this issue, except for
removing dde-api-proxy from the system.
8) CVE Assignment
=================
This finding is a bigger design issue in dde-api-proxy that allows for a
local root (group) exploit and likely more similar attack vectors. We
decided to request a CVE from Mitre to make the community aware of the
issue. Mitre assigned CVE-2025-23222 to track this issue.
Formally another CVE would need to be assigned for the new security
issue introduced by the incomplete bugfix described in section 6), but
we refrained from doing so at this time.
9) Timeline
===========
2024-12-18: We reported the issues by email to security@...pin.com,
which is documented on the project's contact page [10]. The
email was rejected by the mail server.
2024-12-18: We reached out to support@...pin.org asking what the proper
way to report Deepin security issues is. We quickly got a reply that
pointed us to their (public) bug tracker or security@...pin.org.
2024-12-19: We reported the issues by email to security@...pin.org,
offering coordinated disclosure. This time the email was not
rejected.
2025-01-07: Since we did not receive a reply yet from Deepin security
yet we sent another email asking for an initial reply until
2025-01-12, otherwise we would publish the information.
2025-01-13: Since we still did not receive a reply we started working on
publishing the full report. We requested a CVE from Mitre.
2025-01-14: Mitre assigned CVE-2025-23222.
2025-01-16: An upstream contact unexpectedly replied and confirmed the
issue, stating they are working on a bugfix. We asked once
more whether coordinated disclosure is desired, and also
forwarded the assigned CVE.
2025-01-17: Upstream replied that they want to maintain an embargo until
2025-01-20.
2025-01-23: Since no activity at the publication date was visible
upstream and we did not get a notification, we asked
upstream whether publication will happen as planned.
2025-01-24: Upstream pointed us to the bugfix [7], which had already
been published with no further communication on 2025-01-17.
2025-01-24: Since the bugfix was published, we decided to publish all
information. While reviewing the bugfix, we realised it was
incomplete and notified upstream by email.
10) References
==============
[1]: https://security.opensuse.org/2025/01/24/dde-api-proxy-privilege-escalation.html
[2]: https://bugzilla.suse.com/show_bug.cgi?id=1229918
[3]: https://github.com/linuxdeepin/dde-api-proxy
[4]: https://www.deepin.org/en/dde/
[5]: https://github.com/linuxdeepin/dde-api-proxy/releases/tag/1.0.17
[6]: https://github.com/linuxdeepin/dde-api-proxy/blob/1.0.17/src/dbus-proxy/common/dbusproxybase.hpp#L92
[7]: https://github.com/linuxdeepin/dde-api-proxy/commit/95b50ddead0c86fa2cbaaa7c130088fda8315c01
[8]: https://github.com/linuxdeepin/dde-api-proxy/releases/tag/1.0.19
[9]: https://www.openwall.com/lists/oss-security/2014/03/24/2
[10]: https://www.deepin.org/index/en/docs/wiki/en/About_Deepin/Contact-the-deepin-Officials
Best Regards
Matthias
--
Matthias Gerstner <matthias.gerstner@...e.de>
Security Engineer
https://www.suse.com/security
GPG Key ID: 0x14C405C971923553
SUSE Software Solutions Germany GmbH
HRB 36809, AG Nürnberg
Geschäftsführer: Ivo Totev, Andrew McDonald, Werner Knoblich
Download attachment "signature.asc" of type "application/pgp-signature" (834 bytes)
Powered by blists - more mailing lists
Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.