DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • regreSSHion: Should We Panic About the New OpenSSH Vulnerability?
  • Improving Java Code Security
  • Cybersecurity: How to Identify Vulnerabilities
  • Agentic AI for Automated Application Security and Vulnerability Management

Trending

  • How to Convert XLS to XLSX in Java
  • Unlocking the Potential of Apache Iceberg: A Comprehensive Analysis
  • Measuring the Impact of AI on Software Engineering Productivity
  • Comprehensive Guide to Property-Based Testing in Go: Principles and Implementation
  1. DZone
  2. Software Design and Architecture
  3. Security
  4. Monitoring Dynamic Linker Hijacking With eBPF

Monitoring Dynamic Linker Hijacking With eBPF

The article showcases using eBPF for detecting Dynamic Linker Hijacking in Linux by monitoring changes to the /etc/ld.so.preload file.

By 
Efim Rovaev user avatar
Efim Rovaev
·
Jan. 25, 24 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
8.3K Views

Join the DZone community and get the full member experience.

Join For Free

Extended Berkeley Packet Filter (eBPF) is a programming technology designed for the Linux operating system (OS) kernel space, enabling developers to create efficient, secure, and non-intrusive programs. Unlike its predecessor, the Berkeley Packet Filter (BPF), eBPF allows the execution of sandboxed programs in privileged contexts, such as the OS kernel, without the need to modify kernel source code or disrupt overall program execution. This technology expands the features of existing software at runtime, facilitating tasks like packet filtering, high-performance analyses, and the implementation of firewalls and debugging protocols in both on-site data centers and cloud-native environments.

While Dynamic Linker Hijacking is frequently utilized by malware to establish persistence on a system, eBPF can effectively monitor attempts of Dynamic Linker Hijacking, with a specific emphasis on modifications to the /etc/ld.so.preload file. We'll showcase the usage of eBPF to intercept relevant syscalls and explain how preloaded libraries are typically used by malware to inject arbitrary code into the execution flow of trusted programs.

Note: This article is primarily created to showcase eBPF usage and its capabilities. The provided solution is not intended as a serious security solution but rather as an educational example. Please also note that there are easier ways to track file modifications — e.g., you can simply use tail or inotifywait. It’s also pretty untypical to have /etc/ld.so.preload in a traditional Linux setting, so just checking that the file exists already should fire a couple of red flags.

Dynamic Linker Hijacking Intro

Dynamic Linker Hijacking is a technique wherein attackers exploit the dynamic linking process to inject malicious code into trusted programs. In Linux, programs rely on shared libraries (shared objects or SO files) for various functionalities. Dynamic linking allows efficient code reuse and resource sharing.

Attackers place a malicious shared library in a location where the target program searches for libraries. When the program runs and dynamically links to this shared library, the attacker's code is loaded into the program's memory.

Notably, attackers commonly target /etc/ld.so.preload and environment variables like LD_PRELOAD to achieve Dynamic Linker Hijacking. 

/etc/ld.so.preload is a configuration file on Linux that contains a list of paths to additional shared libraries that should be loaded before all other libraries when a program starts. When a program is executed, the dynamic linker/loader (ld.so) resolves and loads the required shared libraries. By default, the linker looks for libraries in standard paths such as /lib and /usr/lib. However, the presence of the /etc/ld.so.preload file allows users to extend this behavior.

In the context of Dynamic Linker Hijacking, attackers may manipulate /etc/ld.so.preload to preload a malicious shared library before the legitimate libraries. This way, when a trusted program starts, the attacker's code is loaded into the program's memory space, enabling unauthorized access or other malicious activities.

It's important to note that modifications to /etc/ld.so.preload typically require superuser (root) privileges.

Learn more about Linux attack techniques and overwriting preload libraries.

eBPF Intro

eBPF, or extended Berkeley Packet Filter, originated as an enhancement to the traditional BPF in the Linux kernel. Initially designed for network packet filtering, eBPF has evolved into a versatile technology with broader applications. Look at some examples on Wikipedia.

Nowadays, it’s really available to everyone and fairly easy to develop and debug. This is partially thanks to bcc — a set of tools that makes BPF programs easier to write, with kernel instrumentation in C (and includes a C wrapper around LLVM), and front-ends in Python and Lua. 

You can read more about eBPF here and here, or immerse yourself in this beautiful book.

The eBPF Verifier

Before delving into the main program, it's crucial to understand the role of the eBPF verifier. The eBPF verifier is a component responsible for ensuring the safety and security of eBPF programs. It analyzes the code and enforces rules to prevent potentially unsafe operations, such as accessing forbidden memory regions.

It also does some basic checks on program correctness. Let’s take a look at an example.

Consider a function that checks if a string ends with a specific suffix. 

C
 
static bool ends_with(char *str1, char *str2) {
    int len1 = 0;
    int len2 = 0;
    while (str1[len1++]);
    while (str2[len2++]);

    if (len2-- > len1--)
        return false;
    while (len2 >= 0) {
        if (str1[len1] != str2[len2])
            return false;
        len1--;
        len2--;        
    }
    return true;
}


If you try to compile it, the verifier will produce an output looking like this:

C
 
; while (str1[len1++]); 
69: (71) r2 = *(u8 *)(r2 +0) 
invalid read from stack R2 off=0 size=1 
processed 25993 insns (limit 1000000) max_states_per_insn 4 total_states 1720 peak_states 49 mark_read 7


This is due to potential unsafe memory access: Imagine we’ve passed an incorrect string that is not null-terminated. The code could then potentially overflow. To make the verifier happy, we should add loop bounds like this:

while (str1[len1++] && len1 < 256);

Monitoring Dynamic Linker Hijacking With eBPF

Now, let's look at the main eBPF program that traces the openat syscall and focuses on attempts to modify /etc/ld.so.preload:

C
 
#!/usr/bin/python3


import json
import ctypes as ct
from bcc import BPF, DEBUG_SOURCE




# eBPF program code
bpf_program = r"""
#define O_WRONLY 01
#define O_RDWR 02
#define TEST_PATH_LIMIT 256


static bool ends_with(char *str1, char *str2) {
   int len1 = 0;
   int len2 = 0;
   while (str1[len1++] && len1 < TEST_PATH_LIMIT);
   while (str2[len2++] && len2 < TEST_PATH_LIMIT);


   if (len2-- > len1--)
       return false;
   while (len2 >= 0) {
       if (str1[len1] != str2[len2])
           return false;
       len1--;
       len2--;       
   }
   return true;
}


TRACEPOINT_PROBE(syscalls, sys_enter_openat) {
   char* TRACED_PATH_SUFFIXES[] = { "ld.so.preload" };


   char filename[TEST_PATH_LIMIT];
   bpf_probe_read_user_str(&filename, sizeof(filename), args->filename);
   if (!(args->flags & O_WRONLY) && !(args->flags & O_RDWR)) {
      return 0;
   }


   for (int i = 0; i < sizeof(TRACED_PATH_SUFFIXES) / sizeof(TRACED_PATH_SUFFIXES[0]); i++) {
       if (ends_with(filename, TRACED_PATH_SUFFIXES[i])) {
           bpf_trace_printk("Catch writing to file: %s\n", filename);
           return 0;
       }
   }
   return 0;
}
"""
# See debug flags with explanations here: https://github.com/iovisor/bcc/blob/master/src/python/bcc/__init__.py
b = BPF(text=bpf_program, debug=DEBUG_SOURCE)
print("Started tracing\n")
while 1:
   try:
       (task, pid, cpu, flags, ts, msg) = b.trace_fields()
   except ValueError:
       continue
   except KeyboardInterrupt:
       exit();
   print(msg)



In this program, we use the TRACEPOINT_PROBE macro to intercept the sys_enter_openat syscall. We then check if the file being opened has a suffix matching one of the tracked path suffixes, such as "ld.so.preload." If a match is found, a trace message is printed to the console.

Running the eBPF Program

To run the eBPF program, save it in a file named monitor_dyn_linker_hijacking.py and execute: 

sudo python3 monitor_dyn_linker_hijacking.py

Now Open a second terminal window and simulate file modification attempts:

sudo touch /etc/ld.so.preload

Observe the output in the first terminal where the eBPF program is running.

Python
 
Started tracing

b'Catch writing to file: /etc/ld.so.preload'


Conclusion

Having showcased that eBPF can effectively monitor Dynamic Linker Hijacking attempts, specifically alterations to the /etc/ld.so.preload file, we invite you to explore further. Now that you've witnessed eBPF's capability to intercept syscalls and observed examples of how malware employs preloaded libraries for code injection into trusted program flows, feel free to experiment with various file modification scenarios. This hands-on exploration will deepen your understanding of eBPF's versatile capabilities in enhancing system security.

Library Linux (operating system) security Malware Vulnerability

Opinions expressed by DZone contributors are their own.

Related

  • regreSSHion: Should We Panic About the New OpenSSH Vulnerability?
  • Improving Java Code Security
  • Cybersecurity: How to Identify Vulnerabilities
  • Agentic AI for Automated Application Security and Vulnerability Management

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!