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
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

Low-Code Development: Leverage low and no code to streamline your workflow so that you can focus on higher priorities.

DZone Security Research: Tell us your top security strategies in 2024, influence our research, and enter for a chance to win $!

Launch your software development career: Dive head first into the SDLC and learn how to build high-quality software and teams.

Open Source Migration Practices and Patterns: Explore key traits of migrating open-source software and its impact on software development.

Related

  • Improving Java Code Security
  • Cybersecurity: How to Identify Vulnerabilities
  • Outsmarting Cyber Threats: How Large Language Models Can Revolutionize Email Security
  • Building Resilient Security Systems: Composable Security

Trending

  • Theme-Based Front-End Architecture Leveraging Tailwind CSS for White-Label Systems
  • Next-Gen Lie Detector: Stack Selection
  • Outsmarting Cyber Threats: How Large Language Models Can Revolutionize Email Security
  • Tenv v2.0: The Importance of Explicit Behavior for Version Manager
  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
Like (2)
Save
Tweet
Share
7.4K 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

  • Improving Java Code Security
  • Cybersecurity: How to Identify Vulnerabilities
  • Outsmarting Cyber Threats: How Large Language Models Can Revolutionize Email Security
  • Building Resilient Security Systems: Composable Security

Partner Resources


Comments

ABOUT US

  • About DZone
  • Send 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: