The XZ Backdoor: Hiding in Plain Sight

Achyuth Nandikotkur
5 min readApr 21, 2024

--

It’s been a few weeks since the XZ Backdoor was uncovered. Numerous articles have detailed its mechanism of installation and operation. Instead of reiterating these aspects, my write-up will provide a brief overview of the incident and focus on the psychology of the malicious actor and the importance of proactively identifying security threats arising from open-source software.

It is not an understatement to say that the author of this backdoor leveraged a diverse set of creative skills to compromise the supply chain at multiple stages with the intent of installing the backdoor stealthily. Below is a brief timeline and overview of the events:

Joining GitHub and Becoming the Maintainer:

  • The author of the backdoor, JiaT75, joined GitHub in January 2021.
  • Between October 2021 and June 2022, several other actors (likely with malicious intentions themselves) pressured the repository maintainer to accept patches from JiaT75, eventually making him the primary maintainer of the repository.

Laying the Groundwork:

  • In June 2023, Hans Jansen (another malicious actor) submitted patches that introduced the GNU indirect function (IFUNC) feature. This feature allows multiple implementations of a given function to be created and selects the best one based on certain conditions at runtime. This change is significant because it lays the groundwork for the backdoor’s entry. Usually, external library symbols are resolved during program execution, and the GOT table is updated by the dynamic linker/loader with the addresses of the functions in the mmaped section of the virtual address space, also known as Lazy binding. For security reasons, however, FULL RELRO is used to resolve all external symbols at once during program startup and also map the .got and .plt tables as read-only to prevent buffer overflows and other types of attacks from overwriting them. However, IFUNC resolvers are executed before the .got and .plt are remapped to read-only. If the attacker can somehow direct the IFUNC resolver function to call into the malicious backdoor, it can then update the .got and .plt tables to hook further functions down the program execution. This is exactly what the malicious actor achieved.

Executing the Attack:

  • Attack Step 1: In January 2024, JiaT75 gains control over the XZ Utils website.
  • Attack Step 2: In February 2024, JiaT75 merges the backdoor object through a couple of test files into a folder containing files used to test the functionality of the library. Such test files are malformed and commonly found in libraries, and they are sometimes even used as seed files for fuzzing. These are usually created by hand using a hex editor and therefore cannot be reviewed closely. JiaT75 exploited this weakness and introduced malicious shell scripts and the backdoor object file through them. Note that these files in isolation are innocuous. Another script is needed to extract the shell scripts and the backdoor object from this file, which is exactly what is added through the distribution package in the next step.
  • Attack Step 3: Between February and March 2024, the attacker publishes a distribution containing an extra malicious build-to-host.m4 file that gets executed during the normal build process. As part of its execution, it extracts the shell scripts from the test files and executes them. In effect, these scripts intervene in the build process and link the backdoor object file to the final executable in such a way that when a genuine function call is made to __get_cpuid, a call is made into _get_cpuid, a function in the backdoor, through the use of GNU IFUNC. The scripts accomplished this by modifying the function signature of __get_cpuid to _get_cpuid in the header file of a function that calls these methods. This allows for the _get_cpuid to act as an entry into the backdoor and further update the .got and .plt tables to hook specific functions that allow it to monitor every connection to the infected machine

Psychology of the Attacker — Working Backwards:

The dissemination of the XZ Backdoor was evidently a meticulously planned operation, likely orchestrated by a state-sponsored group that utilized a broad spectrum of skills, from social psychology to deep knowledge of operating system internals.

  • It is plausible that the group initially identified a list of potential targets — libraries that link to privileged system utilities which can be interacted with over a network. They then worked their way backwards to identify repositories associated with these libraries, particularly those that were weakly administered.
  • The attackers likely established a presence by making genuine contributions to the targeted repositories. A group can make more contributions than any single person, potentially overwhelming a maintainer and thus easing the process of acquiring necessary rights.

The operational pattern of this group included

  • Using new GitHub accounts.
  • Collaborating to pressure contributions to the repository.
  • Gaining maintainer access, and making contributions that could be either malicious or benign, therefore stunning other reviewers.

This modus operandi suggests a sophisticated understanding of both human and system vulnerabilities. To counter such threats, I propose the development of a machine learning based system that can monitor such behavioral patterns and assign a security rating to repositories about its reliability. Here are some features that can represent the behavior pattern described above:

  1. User Account Age: Time since the user account was created.
  2. Activity Frequency: Frequency of user’s commits, pull requests, and comments.
  3. Account Creation Time: Time of day or day of the week when the account was created.
  4. Access Location: Geographic locations from which commits and pull requests are made.
  5. Sentiment Analysis of Commit Messages and PR Discussions: Emotional tone in text communications.
  6. PR Discussion Length: Length of discussions in pull requests.
  7. Contribution Patterns: Patterns in the user’s contributions, such as activity bursts following inactivity.
  8. Number of Forks and Stars: Popularity and credibility indicators of the repository.
  9. Repository Age: Age of the repository.
  10. Recent Activity Level: Recent activity in the repository, like number of commits and pull requests.
  11. Contributor Diversity: Number of unique contributors and their activity levels.
  12. Frequency of Dependency Updates: How often the repository updates its dependencies.
  13. Lines of Code Added/Removed per Commit: Extent of changes per commit.
  14. Time of Commits/PRs: Times at which commits and pull requests are made.
  15. Merge Behavior: Speed and manner of merging pull requests.
  16. Anomalous Access Patterns: Unusual access patterns based on IP geolocations.
  17. Sequence Anomalies in Commits/PRs: Deviations from a user’s historical behavior pattern.

Ultimately, the model will help generate a score or a rating for each repository. This rating would help stakeholders evaluate the security reliability of the code and its libraries before incorporation into their systems. Such proactive measures could significantly enhance the security posture of open-source software, making it harder for attackers to exploit the openness and collaborative nature of these projects.

Recommended readings:

--

--

Achyuth Nandikotkur

Passionately Curious. Believer in Simplicity. Humble Writer.