Skip to content

mednine9/MEDUSA

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MEDUSA

Note: This is a very early prototype version, originally made as a proof of concept for a competition.

Real-Time Ransomware Detection, Key Extraction & File Recovery on Linux

MEDUSA is a kernel-level anti-ransomware system for Linux that uses eBPF (extended Berkeley Packet Filter) to detect ransomware in real-time, freeze the malicious process before it can finish encrypting, intercept the encryption keys directly from the cryptographic API calls, and enable full recovery of encrypted files — all without requiring the attacker's cooperation.


Table of Contents


The Problem

Modern ransomware encrypts files using strong cryptography (AES-256, ChaCha20) that is mathematically impossible to break without the key. Victims must either:

  1. Pay the ransom (no guarantee of recovery)
  2. Restore from backups (if they exist)
  3. Lose their data permanently

MEDUSA introduces a 4th option: intercept the encryption key at the moment the ransomware uses it, freeze the process, and decrypt everything.


Theoretical Foundation

Why Ransomware Is Fundamentally Exploitable

Ransomware faces a paradox that MEDUSA exploits:

┌─────────────────────────────────────────────────────────────────┐
│                  THE RANSOMWARE PARADOX                         │
│                                                                 │
│  To encrypt files, ransomware MUST:                             │
│    1. Generate or receive encryption keys                       │
│    2. Call a cryptographic function to encrypt                  │
│    3. Access files on disk to read → encrypt → write            │
│                                                                 │
│  All three steps happen IN MEMORY on the VICTIM'S MACHINE.     │
│  If we can observe any of these steps, we win.                  │
│                                                                 │
│  The ransomware cannot encrypt without exposing its key         │
│  in the same address space we can monitor.                      │
└─────────────────────────────────────────────────────────────────┘

The key insight: encryption keys must exist in process memory at the moment of encryption. No matter how sophisticated the ransomware, it cannot encrypt data without having the key accessible in RAM. MEDUSA hooks the exact moment this happens.

The Shared Library Weakness

Almost all malware — including ransomware written in C, C++, Go, Rust, and Python — uses OpenSSL's libcrypto for encryption. Even custom implementations eventually call into system-provided crypto libraries for performance.

Ransomware Code                    System Library
─────────────────                  ──────────────────
encrypt_files() ──────────────────► EVP_EncryptInit_ex()
                                       │
                                       │  ← MEDUSA hooks HERE
                                       │     with a BPF uprobe
                                       │
                                       │  Arguments in CPU registers:
                                       │    RDI = cipher context
                                       │    RSI = cipher type (AES-256)
                                       │    RDX = engine
                                       │    RCX = ★ KEY POINTER ★
                                       │    R8  = IV pointer
                                       │
                                       ▼
                                   AES encryption happens

MEDUSA places a uprobe (userspace probe) on EVP_EncryptInit_ex inside libcrypto.so. When ANY process calls this function, MEDUSA's BPF program executes in kernel context, reads the 4th argument (the key pointer) from the RCX register, copies the 32-byte key from the process's memory, and stores it in a per-PID BPF hash map.

The ransomware literally hands us its key.

The Honeypot Trap

Detection alone isn't enough — we need to catch ransomware as early as possible. MEDUSA uses strategically placed honeypot files that act as trip wires:

Directory listing (alphabetical order):
────────────────────────────────────────
 !00_Recovery_Index.dat     ← Honeypot ALPHA (sorts first due to !)
 0001_Annual_Report.xlsx    ← Real user file
 0002_Budget_Q4.xlsx        ← Real user file
 0003_Client_List.csv       ← Real user file
 1000_database_backup.xlsx  ← Honeypot DECOY (looks like valuable data)
 z_system_restore.dat       ← Honeypot OMEGA (sorts last)

Why three honeypots?

Most ransomware processes files alphabetically (to be systematic). The three honeypots form a net:

Honeypot Position Purpose
!00_Recovery_Index.dat Sorts first (!) Catches ransomware that starts from the top. Some ransomware skips .dat extensions, so...
1000_database_backup.xlsx Sorts in the middle The real trap. .xlsx extension makes it look like a high-value target. If the ransomware skipped the .dat file, it will still hit this one after encrypting a few real files.
z_system_restore.dat Sorts last (z) Catches ransomware that processes in reverse order.

When ransomware attempts to write to or delete any honeypot, MEDUSA's LSM (Linux Security Module) hook:

  1. Denies the operation (returns -EPERM)
  2. Freezes the process (sends SIGSTOP)
  3. Alerts the daemon to extract keys

The honeypots are protected at the kernel level — the ransomware cannot modify or delete them, because the operation is blocked before it completes.


Architecture

MEDUSA employs a strict 3-layer defense mechanism. If malware manages to bypass or damage the first layer, the subsequent layers act as traps and recovery safeguards to neutralize the threat and repair the damage.

🛡️ Layer 1: The Honeypot Trap (Early Detection & Prevention)

The first line of defense is a network of invisible tripwires. MEDUSA places decoy files (honeypots) strategically within the file system. When ransomware attempts to encrypt or delete these files, MEDUSA's kernel-level LSM hooks instantly block the operation and freeze the malicious process.

  • If it works: The ransomware is stopped before touching real user data.
  • If it fails (or is bypassed): The ransomware begins encrypting real files, triggering Layer 2.

🪤 Layer 2: Cryptographic API Hook (Key Extraction)

If the ransomware skips the honeypots and starts encrypting real files, it eventually must call standard cryptographic libraries (e.g., OpenSSL's libcrypto.so). MEDUSA places a hidden BPF uprobe on EVP_EncryptInit_ex. The moment the ransomware passes its encryption key to the library, MEDUSA quietly copies the 32-byte key from memory.

  • If it works: We have the exact encryption key used by the malware. Even if files are damaged, we can perfectly decrypt them.
  • If it fails (e.g., statically linked binary): We fall back to Layer 3.

🩹 Layer 3: Memory Scanning Fallback & File Recovery

For advanced malware that avoids dynamic libraries entirely, MEDUSA's extraction daemon optionally scans the frozen process's raw memory (/proc/PID/mem) to hunt for AES key schedules. Once any key is recovered (via Layer 2 or memory scan), the Standalone Decryptor automatically traverses the infected directories and restores all damaged files to their original state.

┌──────────────────────────────────────────────────────────────────────┐
│                          USER SPACE                                  │
│                                                                      │
│  ┌──────────────┐     ┌──────────────────┐     ┌──────────────────┐  │
│  │  Ransomware  │     │  medusa_daemon    │     │ medusa_decrypt   │  │
│  │  (malicious) │     │  (orchestrator)   │     │ (recovery tool)  │  │
│  └──────┬───────┘     └────────┬─────────┘     └────────┬─────────┘  │
│         │                      │                         │            │
├─────────┼──────────────────────┼─────────────────────────┼────────────┤
│         │              KERNEL SPACE                      │            │
│         │                      │                         │            │
│         ▼                      ▼                         │            │
│  ┌──────────────┐     ┌──────────────────┐               │            │
│  │ libcrypto.so │     │   medusa.bpf.c   │               │            │
│  │              │     │                  │               │            │
│  │ EVP_Encrypt  │◄────┤ LSM hooks:       │               │            │
│  │   Init_ex()  │     │  file_permission │               │            │
│  │      │       │     │  inode_unlink    │               │            │
│  │      │(uprobe)     │  inode_mkdir     │               │            │
│  │      ▼       │     │                  │               │            │
│  │ Key captured │     │ BPF Maps:        │               │            │
│  │ to BPF map   │     │  honeypot_inodes │               │            │
│  └──────────────┘     │  frozen_pids     │               │            │
│                       │  mkdir_events    │               │            │
│                       └──────────────────┘               │            │
│                                                          │            │
│                    ┌─────────────────────┐                │            │
│                    │  Keys saved to file │◄───────────────┘            │
│                    │  /var/lib/medusa/   │                             │
│                    │  keys/pid_XXX.txt   │                             │
│                    └─────────────────────┘                             │
└───────────────────────────────────────────────────────────────────────┘

How Each Component Works

1. BPF Kernel Module — Detection Layer

File: medusa_kernel/medusa.bpf.c

Three LSM (Linux Security Module) hooks run inside the kernel:

detect_honeypot_write (LSM: file_permission)

SEC("lsm/file_permission")
int BPF_PROG(detect_honeypot_write, struct file *file, int mask)
  • Triggers on every file access system-wide
  • Checks if the operation is a WRITE (mask & 2)
  • Looks up the file's inode number in the honeypot_inodes BPF map
  • If it's a honeypot: DENY the write (return -EPERM), send SIGSTOP to the process, record the PID in frozen_pids

detect_honeypot_delete (LSM: inode_unlink)

SEC("lsm/inode_unlink")
int BPF_PROG(detect_honeypot_delete, struct inode *dir, struct dentry *dentry)
  • Same logic but for file deletion attempts
  • Catches ransomware that tries to delete evidence

detect_mkdir (LSM: inode_mkdir)

SEC("lsm/inode_mkdir")
int BPF_PROG(detect_mkdir, struct inode *dir, struct dentry *dentry, umode_t mode)
  • Catches ALL directory creation (any syscall variant)
  • Stores the directory name in mkdir_events map
  • Daemon deploys honeypots in new directories automatically

2. Key Capture Probe — Extraction Layer

File: EXTRACTOR/extractor-buffer.bpf.c

SEC("uprobe")
int capture_evp_encrypt_key(struct pt_regs *ctx)
{
    void *key_ptr = (void *)PT_REGS_PARM4(ctx);  // 4th arg = key
    bpf_probe_read_user(buf->keys[idx], 32, key_ptr);  // Copy 32 bytes
}

This is a uprobe — a breakpoint placed on a userspace function. It's attached to EVP_EncryptInit_ex inside libcrypto.so at the correct offset (found via nm -D).

How it captures the key:

  1. On x86_64, function arguments are passed in CPU registers
  2. The 4th argument to EVP_EncryptInit_ex is the encryption key pointer
  3. This pointer is in the RCX register, accessible via PT_REGS_PARM4
  4. bpf_probe_read_user() safely copies 32 bytes from the process's memory
  5. The key is stored in a per-PID circular buffer BPF map

Critical: This happens at the entry point of the function — before encryption even begins. The key is captured the instant the ransomware tries to use it.

3. Userspace Daemon — Orchestration Layer

File: medusa_controller/main.c

The daemon ties everything together:

  1. Startup: Loads BPF programs, deploys honeypots, registers inode numbers
  2. Main loop (every 1 second):
    • Polls frozen_pids map for new detections
    • Polls mkdir_events map for new directories → deploy honeypots
    • Periodically verifies honeypot integrity
  3. On detection: Extracts keys from BPF map → saves to file → sends notification

Key extraction has two plans:

Plan Method How it works
A BPF uprobe Reads captured keys from the pid_key_buffers BPF map (most reliable)
B Memory scan Scans /proc/PID/mem for AES key schedules in memory (fallback for statically-linked binaries)

4. Standalone Decryptor — Recovery Layer

File: EXTRACTOR/medusa_decrypt.c

After the daemon extracts keys, this tool recovers files:

./medusa_decrypt /var/lib/medusa/keys/pid_1234_keys.txt /home/user/

It handles two common encrypted file formats:

Format Layout Used by
Format 1 [orig_size (8B)][IV (16B)][ciphertext] Many ransomware families
Format 2 [IV (16B)][ciphertext] Simpler variants

Validation: After decryption, the tool verifies correctness by:

  • Checking magic bytes (PDF starts with %PDF, PNG with \x89PNG, etc.)
  • Checking printable ratio for text files (>75% printable = valid)
  • Checking entropy (encrypted data ≈ 8.0, normal data < 6.0)

Why This Works Against Most Ransomware

1. Ransomware MUST Use Cryptography

Every ransomware needs to encrypt files. Whether it uses AES, ChaCha20, RSA, or any other algorithm, it must:

  • Have the key in memory
  • Call a crypto function

On Linux, >95% of software uses OpenSSL's libcrypto.so for this. Even Go, Rust, and Python programs typically link against system OpenSSL. MEDUSA hooks this shared bottleneck.

2. The Key Exposure Window Is Unavoidable

Timeline of a ransomware encryption:

     ┌──── Key generated or received
     │
     │  ┌── Key passed to EVP_EncryptInit_ex  ◄── MEDUSA captures HERE
     │  │
     │  │  ┌── File encrypted
     │  │  │
     │  │  │  ┌── Key potentially destroyed
     ▼  ▼  ▼  ▼
─────●──●──●──●───────────────── time →

     │  │
     │  └── The key MUST be in a register (RCX) at this point
     │      There is no way to avoid this.
     │
     └── Even if the key is encrypted in memory,
         it must be decrypted before use.

No matter how advanced the ransomware's anti-analysis techniques, the key must exist in plaintext in a CPU register when passed to the encryption function. MEDUSA reads it at that exact moment.

3. Honeypots Are Invisible to Ransomware

The honeypot files:

  • Look like normal user files (.xlsx, .dat extensions)
  • Have realistic file sizes and content
  • Cannot be distinguished from real files by the ransomware
  • Are protected by kernel-level LSM hooks — the ransomware cannot modify or delete them

4. Detection Happens Before Damage

Without MEDUSA:
  File 1 ──► ENCRYPTED ✗
  File 2 ──► ENCRYPTED ✗
  File 3 ──► ENCRYPTED ✗
  ...
  File N ──► ENCRYPTED ✗    (all files lost)

With MEDUSA:
  !00_Recovery_Index.dat ──► SKIPPED (ransomware ignores .dat)
  File 1 ──► encrypted ✗    (but key was captured!)
  File 2 ──► encrypted ✗    (but key was captured!)
  1000_database_backup.xlsx ──► 🛑 BLOCKED + FROZEN
  File 3 ──► never reached  ✓
  File 4 ──► never reached  ✓
  ...
  File N ──► never reached  ✓

  Result: Files 1-2 encrypted but RECOVERABLE (we have the key)
          Files 3-N never touched

5. Works Even Against Unknown Ransomware

MEDUSA doesn't use signatures (like antivirus software). It doesn't need to know what ransomware family is attacking. The detection is behavioral:

  • Is something writing to a honeypot? → Malicious
  • Is something calling EVP_EncryptInit_ex? → Capture the key

This means MEDUSA catches zero-day ransomware that no antivirus has ever seen before.


Project Structure

MEDUSA/
├── medusa_kernel/           # BPF programs (kernel-level)
│   ├── medusa.bpf.c         # LSM hooks: honeypot detection + mkdir monitor
│   └── Makefile
│
├── medusa_controller/       # Userspace daemon
│   ├── main.c               # Orchestrator: key extraction, notifications
│   ├── honeypot_deployer.h  # Honeypot file creation and deployment
│   └── Makefile
│
├── EXTRACTOR/               # Key extraction & recovery tools
│   ├── extractor-buffer.bpf.c  # BPF uprobe: captures keys from EVP_EncryptInit_ex
│   ├── extractor.h          # Plan A: read keys from BPF map
│   ├── extractor-keysched.h # Plan B: memory scan for key schedules
│   ├── medusa_decrypt.c     # Standalone decryptor CLI tool
│   ├── decryptor.h          # Decryption logic library
│   └── Makefile
│
├── lockbit_replica/         # Test ransomware simulation
│   └── demo/
│       ├── fake_ransom.c    # Demo ransomware using OpenSSL
│       ├── setup_demo.sh    # Creates test files
│       └── Makefile
│
├── Makefile                 # Top-level build
└── README.md                # This file

Building

Prerequisites

# Debian/Ubuntu
sudo apt install clang llvm libbpf-dev linux-headers-$(uname -r) \
                 libssl-dev bpftool

# Fedora
sudo dnf install clang llvm libbpf-devel kernel-devel \
                 openssl-devel bpftool

Compile

# Build everything
make

# Or build components individually
cd medusa_kernel && make        # BPF programs
cd medusa_controller && make    # Daemon
cd EXTRACTOR && make            # Decryptor tool

Usage

1. Start MEDUSA Protection

# Interactive mode (see all output)
sudo ./medusa_controller/medusa_daemon

# Daemon mode (background)
sudo ./medusa_controller/medusa_daemon -d

You should see:

[+] MEDUSA loaded! Watching for ransomware...
[+] Deployed 66 honeypots across 22 directories
[+] Registered 78 honeypot inodes for monitoring
[+] Found EVP_EncryptInit_ex at offset 0x241250
[+] Key capture attached to /usr/lib/.../libcrypto.so.3 at offset 0x241250

2. When Ransomware Is Detected

MEDUSA automatically:

  • Blocks the write/delete operation
  • 🧊 Freezes the process (SIGSTOP)
  • 🔑 Extracts encryption keys
  • 📢 Shows alert notification
  • 💾 Saves keys to /var/lib/medusa/keys/pid_XXXX_keys.txt

3. Recover Encrypted Files

# Auto-find latest keys, scan /home
sudo ./EXTRACTOR/medusa_decrypt --auto

# Or specify keys file and directory
sudo ./EXTRACTOR/medusa_decrypt /var/lib/medusa/keys/pid_1234_keys.txt /home/user/

Demo

Setup Test Environment

cd lockbit_replica/demo
make                              # Build demo ransomware
./setup_demo.sh ~/test_files      # Create test files

Run the Demo

Terminal 1 — start MEDUSA:

sudo ./medusa_controller/medusa_daemon

Terminal 2 — run fake ransomware:

cd lockbit_replica/demo
./fake_ransom ~/test_files

Expected result:

[SKIP] !00_Recovery_Index.dat     ← Skipped (.dat extension)
[ENCRYPT] 0001_Annual_Report.xlsx ← Encrypted (key captured!)
[ENCRYPT] 0002_Budget_Q4.xlsx     ← Encrypted (key captured!)
                                   ← 🛑 FROZEN when hitting honeypot!

Terminal 2 — recover files:

sudo ./EXTRACTOR/medusa_decrypt --auto
  ╔══════════════════════════════════════╗
  ║  Results:   2 recovered,   0 failed  ║
  ╚══════════════════════════════════════╝

License

GPL-2.0 (required for eBPF programs)


MEDUSA — Because ransomware operators should be the ones who are petrified. 🐍

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors