Bytecode Jiu-Jitsu: Choking Interpreters to Force Execution of Malicious Bytecode
Unknown
Black Hat USA 2024 · Day 1 · Briefing
Overview
This talk introduces a groundbreaking new code injection attack dubbed Bytecode Jiu-Jitsu. Presented by Toshinori and Yuto Otsuki, research scientists from NTT Security Holdings, this technique represents a significant evolution in evasion capabilities for malicious actors. Unlike traditional code injection methods that target native processes and inject machine code, Bytecode Jiu-Jitsu specifically focuses on interpreters and the manipulation of their internal bytecode.

Key moments
- 0:00 Introducing Bytecode Jiu-Jitsu: a new code injection attack.
- 2:00 Understanding existing code injection techniques: Process Following.
- 4:00 Beginning Bytecode Jiu-Jitsu's technical details: dynamic analysis.
Bytecode Jiu-Jitsu: Choking Interpreters to Force Execution of Malicious Bytecode
Speakers: Toshinori, Research Scientist, NTT Security Holdings; Yuto Otsuki, Senior Researcher, NTT Security Holdings
Conference: Black Hat USA
YouTube: https://www.youtube.com/watch?v=5fs9ntKOYW0
Overview
This talk introduces a groundbreaking new code injection attack dubbed Bytecode Jiu-Jitsu. Presented by Toshinori and Yuto Otsuki, research scientists from NTT Security Holdings, this technique represents a significant evolution in evasion capabilities for malicious actors. Unlike traditional code injection methods that target native processes and inject machine code, Bytecode Jiu-Jitsu specifically focuses on interpreters and the manipulation of their internal bytecode.
The core innovation of Bytecode Jiu-Jitsu lies in its ability to force an interpreter to execute malicious bytecode without triggering the suspicious API calls typically associated with native code injection, such as allocating executable memory, writing payloads, or creating new threads. By operating at the bytecode level, within the context of a benign interpreter process, the attack aims to blend malicious behavior seamlessly into legitimate script execution, making it considerably harder for conventional security solutions to detect. This shift in attack surface and methodology highlights a critical blind spot in many current defensive strategies.
Background
▶ Watch: Introducing Bytecode Jiu-Jitsu: a new code injection attack. (0:00)
Malware frequently employs code injection techniques to conceal its activities by embedding malicious code within a legitimate, benign process. This allows the malware to leverage the benign process's identity, making its actions appear to originate from a trusted source and thereby evading detection based on process reputation or image path. Traditionally, this involves a series of common steps: opening a benign process, allocating new memory, writing malicious native code into that memory, and then initiating a thread to execute it.
However, these traditional methods often leave detectable footprints. Security products, especially Endpoint Detection and Response (EDR) solutions, are well-equipped to monitor for the specific API calls involved in these operations. For instance, calls to VirtualAllocEx (for allocating memory in another process), WriteProcessMemory (for writing data into that memory), and CreateRemoteThread (for executing code in another process) are considered highly suspicious and are common indicators of compromise.
The speakers highlighted several well-known variants of these native code injection techniques, including Process Hollowing, Process Doppelganging, and Process Herpaderping.
- Process Hollowing, described in detail, involves creating a suspended benign process from a legitimate executable. Before the process starts execution, the attacker unmaps the original executable image from its memory. A malicious image is then injected as a replacement. Finally, the instruction pointer of the process is adjusted to point to the entry point of the malicious image, and the process is resumed. From an external perspective, the process appears to be executing the benign file (maintaining its name and image path), but in reality, it is running the injected malicious code.
- Process Doppelganging and Process Herpaderping are more advanced variants that aim to further obfuscate the in-memory malicious image while keeping the on-disk file benign. While their implementation details differ, they share the common goal of ensuring the in-memory image is malicious and modified, while the disk image remains untainted, akin to Process Hollowing.
While effective in their time, these techniques have become increasingly known and are often detectable by modern security solutions that monitor for their characteristic API call sequences and memory manipulations. This continuous cat-and-mouse game between attackers and defenders necessitates the development of new, stealthier injection methods, which is precisely where Bytecode Jiu-Jitsu enters the scene.
Key Findings
▶ Watch: Understanding existing code injection techniques: Process Following. (2:00)
The central finding presented in this talk is the identification and development of Bytecode Jiu-Jitsu, a novel code injection attack that fundamentally shifts the target and payload from native machine code in arbitrary processes to bytecode within interpreters. This represents a significant paradigm shift in evasion techniques, offering a new avenue for attackers to circumvent conventional detection mechanisms.
The key findings and contributions can be summarized as follows:
- Novel Attack Vector: Interpreter-Based Code Injection: Bytecode Jiu-Jitsu is the first of its kind to explicitly target the internal execution mechanisms of software interpreters. Instead of manipulating the operating system's process loader or memory manager to inject native machine code, this technique focuses on "choking" an interpreter to force it to process and execute malicious bytecode. This means the attack unfolds entirely within the interpreter's runtime environment, leveraging its legitimate execution capabilities.
- Payload Transformation: From Native Code to Bytecode: The attack leverages malicious bytecode as its payload, rather than native machine code. This is a crucial distinction. Bytecode is an intermediate representation of code that is executed by a virtual machine or interpreter (e.g., Python bytecode, Java bytecode, .NET Intermediate Language, JavaScript JIT-compiled code). By injecting bytecode, the attacker is operating at a higher level of abstraction than native code, making the malicious payload indistinguishable from legitimate script code within the interpreter's memory space.
- Evasion of Traditional Detection Mechanisms: Perhaps the most impactful finding is that Bytecode Jiu-Jitsu effectively bypasses security solutions that rely on monitoring for suspicious API calls associated with native code injection. Since the attack does not require allocating new executable memory, writing raw machine code, or creating new remote threads—the interpreter handles these operations legitimately as part of its normal function—the tell-tale signs for EDR and antivirus systems are absent. The malicious activity is masked as benign script execution, making it extremely difficult to detect with signature-based or behavior-based monitoring focused on OS-level API calls.
- Methodology for Bytecode Structure Extraction: To achieve this injection, the researchers developed a systematic methodology for understanding an interpreter's internal workings. This involves:
- Crafting specially designed "test scripts" for a target interpreter.
- Performing dynamic analysis of the interpreter binary while it executes these test scripts.
- Generating and analyzing memory access logs to observe how the interpreter processes and stores bytecode and symbol table information.
- Extracting the structure information of both the bytecode (e.g., opcode definitions, operand formats) and the interpreter's symbol tables (how functions, variables, and imported modules are referenced). This reverse engineering process is fundamental to crafting valid malicious bytecode that the interpreter will accept and execute.
- Compromising Benign Script Behavior: Unlike existing techniques that blend malicious native code into a benign executable, Bytecode Jiu-Jitsu blends malicious bytecode into benign script behavior. This means an otherwise legitimate script, running within a legitimate interpreter, can be subtly altered or extended to perform malicious actions without modifying the original script file on disk or the interpreter binary itself.
In essence, Bytecode Jiu-Jitsu represents an advanced evolution in stealthy code injection, forcing defenders to re-evaluate their monitoring strategies to look beyond native process memory and into the intricate, often opaque, runtime environments of interpreters.
Technical Deep Dive
The technical foundation of Bytecode Jiu-Jitsu rests on a sophisticated understanding and manipulation of how software interpreters process and execute code. The attack unfolds in two primary phases: an initial analysis phase to reverse-engineer interpreter internals, followed by the actual injection phase.
Phase 1: Interpreter Internal Structure Analysis
The first and most critical step for an attacker employing Bytecode Jiu-Jitsu is to understand the target interpreter's internal representation of code and data. This process is described as a dynamic analysis technique:
- Test Script Creation: The attacker begins by manually writing specially crafted scripts, referred to as "test scripts." These scripts are not malicious in themselves but are designed to exercise various features of the interpreter, such as function definitions, variable assignments, control flow structures (loops, conditionals), and module imports. The goal is to create a diverse set of examples that will trigger the interpreter to generate and manage its internal bytecode and symbol tables in predictable ways.
- Dynamic Execution and Observation: The crafted test scripts are then fed into the target interpreter binary. During the execution of these scripts, the attacker performs dynamic analysis of the interpreter process. This involves observing its runtime behavior, particularly how it interacts with memory. Tools for dynamic analysis might include debuggers (like WinDbg, GDB), memory introspection tools, or custom instrumentation frameworks that can hook into memory allocation and access patterns.
- Memory Access Log Generation: As the interpreter executes the test scripts, the dynamic analysis tools generate memory access logs. These logs record detailed information about memory reads, writes, and allocations performed by the interpreter. The focus is on identifying patterns related to how the interpreter stores and retrieves its internal bytecode and associated metadata. For example, an attacker would look for memory regions that are frequently accessed during script execution, particularly those that appear to hold sequences of operations or data references.
- Structure Information Extraction: The generated memory access logs are then meticulously analyzed to extract the structure information of bytecode and symbol tables.
- Bytecode Structure: This involves reverse engineering the interpreter's instruction set architecture (ISA) at the bytecode level. The attacker needs to understand:
- Opcode definitions: What each bytecode instruction (opcode) represents (e.g.,
LOAD_CONST,CALL_FUNCTION,STORE_FAST). - Operand formats: How arguments or data are passed to each opcode (e.g., immediate values, indices into constant pools or name tables).
- Bytecode layout: How sequences of opcodes and operands are packed into memory.
- Control flow: How jumps, calls, and returns are encoded within the bytecode.
- Symbol Table Structure: Interpreters maintain internal tables to manage symbols (names of variables, functions, classes, modules). The attacker needs to understand:
- Data structures: How symbol tables are organized in memory (e.g., hash maps, arrays of structs).
- Entry format: What information each symbol table entry contains (e.g., name string, type, memory address or offset, flags).
- Resolution mechanisms: How the interpreter looks up symbols during execution.
- Inter-table references: How bytecode references entries in the symbol table, and how different symbol tables (e.g., global, local, module-specific) relate to each other.
This detailed reverse engineering process is crucial because it allows the attacker to learn the "language" of the interpreter's internal execution engine. Without this knowledge, any injected bytecode would be malformed and likely cause the interpreter to crash or fail to execute the malicious logic.
Phase 2: Malicious Bytecode Injection
Once the interpreter's internal structures are understood, the attacker can proceed with the injection phase:
- Injector Development: An injector tool is developed. This tool is responsible for packaging the malicious payload and injecting it into a running interpreter process. The injector embeds the extracted bytecode and symbol table structure information, allowing it to craft valid malicious bytecode.
- Malicious Bytecode Crafting: Based on the reverse-engineered structures, the attacker crafts specific malicious bytecode sequences. These sequences are designed to perform desired actions (e.g., spawn a shell, exfiltrate data, modify files, establish persistence) using the interpreter's native capabilities. The malicious bytecode must adhere precisely to the interpreter's opcode definitions, operand formats, and symbol resolution mechanisms.
- Memory Manipulation and "Choking": The core of the "Jiu-Jitsu" analogy comes into play here. The injector does not allocate new executable memory or create new threads in the traditional sense. Instead, it identifies existing, legitimate memory regions within the target interpreter process where bytecode or script-related data is stored. The injector then modifies or inserts the malicious bytecode into these regions. This might involve:
- Overwriting existing benign bytecode: Replacing a legitimate function's bytecode with malicious code.
- Injecting new bytecode and updating symbol tables: Adding a new function containing malicious bytecode and then updating the interpreter's symbol tables so that a legitimate part of the script calls this new malicious function.
- Modifying control flow: Altering jump targets or function pointers within the interpreter's internal data structures to divert execution to the injected malicious bytecode.
The term "choking an interpreter" implies a precise and targeted manipulation of its internal state and execution flow. By leveraging the interpreter's own mechanisms for managing and executing bytecode, the attacker forces it to process the malicious payload as if it were part of the original, benign script. Because the interpreter itself is performing the execution within its own process space and using its own legitimate memory allocations, the activities do not trigger the typical red flags associated with native code injection. This makes Bytecode Jiu-Jitsu an exceptionally stealthy and challenging attack to detect with conventional security tooling.
Demo / Proof of Concept
▶ Watch: Beginning Bytecode Jiu-Jitsu's technical details: dynamic analysis. (4:00)
The talk provided a conceptual framework and a detailed methodology for the Bytecode Jiu-Jitsu attack. While the presentation included diagrams and an outline of the technique, it did not delve into the specifics of a live demonstration or a concrete proof-of-concept implementation. The speakers focused on introducing the novel attack vector and its underlying technical analysis process rather than showcasing a specific exploit against a particular interpreter or providing tool names and version numbers for a working demonstration. The "picture" mentioned in the introduction likely refers to an illustrative diagram depicting the conceptual flow of the attack rather than a screenshot or video of a live exploit.
Defensive Implications
The emergence of Bytecode Jiu-Jitsu presents significant challenges for current cybersecurity defenses, necessitating a re-evaluation of detection strategies. Traditional security solutions, particularly EDR and antivirus products, are primarily designed to detect native code injection and execution, making them ill-equipped to identify this new class of attack.
Here are the critical defensive implications:
- Bypass of Traditional Code Injection Detections: The most immediate implication is that Bytecode Jiu-Jitsu bypasses detections that rely on monitoring suspicious API calls like
VirtualAllocEx,WriteProcessMemory, andCreateRemoteThread. Since the attack manipulates existing interpreter memory and leverages the interpreter's legitimate execution flow, these high-fidelity indicators of compromise are absent. Defenders can no longer solely rely on these well-established signals to identify code injection attempts.
- Need for Deeper Memory Forensics and Runtime Integrity Monitoring: Defenders must evolve their memory analysis capabilities. Instead of just looking for executable memory allocated by external processes, security solutions need to perform deeper memory forensics within interpreter processes. This involves:
- Understanding Interpreter Internals: Security analysts and tools must develop an understanding of the internal memory layouts and data structures used by common interpreters (e.g., Python, Java Virtual Machine, JavaScript engines, PowerShell, .NET CLR). This includes knowledge of how bytecode is stored, how symbol tables are structured, and where legitimate code and data reside.
- Anomaly Detection in Bytecode: Developing heuristics or signatures to detect anomalous modifications or insertions within legitimate bytecode regions. This could involve comparing in-memory bytecode with known benign versions, or identifying bytecode sequences that are structurally unusual or out of place.
- Runtime Integrity Checks: Implementing mechanisms to continuously verify the integrity of critical interpreter memory regions, such as bytecode caches or symbol tables. Any unauthorized modification could indicate an attack.
- Enhanced Behavioral Analysis at the Script/Interpreter Level: Detection must shift from monitoring OS-level process behavior to analyzing interpreter-specific behavior. This means:
- Contextual Behavioral Analysis: Monitoring the API calls made by the interpreter process itself, but in the context of the script it's supposed to be executing. If a Python interpreter running a benign web server script suddenly makes API calls to create a new process or access sensitive files, this could be indicative of malicious bytecode injection.
- Interpreter-Specific Logging: Improving logging within interpreter environments to capture more granular details about script execution, module loading, and internal function calls. This could provide audit trails to trace malicious bytecode execution.
- Supply Chain and Software Integrity: While Bytecode Jiu-Jitsu primarily targets runtime memory, the underlying interpreter binary and loaded scripts are still critical. Ensuring the integrity of interpreter binaries and preventing initial compromise through supply chain attacks or legitimate script modification remains essential. This includes:
- Code Signing and Verification: Ensuring interpreter binaries and critical runtime libraries are properly signed and verified.
- Script Integrity: Implementing measures to ensure the integrity of scripts executed by interpreters, although this attack specifically aims to bypass on-disk script integrity checks by modifying in-memory representations.
- Complexity of Detection: The inherent complexity of interpreter environments makes detection challenging. Different interpreters have different bytecode formats, virtual machine architectures, and memory management schemes. This means a universal detection solution is difficult; specialized knowledge and tooling for each interpreter family might be required. The attack's ability to blend into "benign script behavior" makes it a sophisticated form of obfuscation.
In summary, Bytecode Jiu-Jitsu serves as a wake-up call for defenders. It underscores the need to move beyond superficial process monitoring and delve into the intricacies of application-level execution environments. Future security solutions will require deeper visibility, more sophisticated behavioral analytics, and a greater understanding of interpreter internals to effectively counter this advanced form of code injection.
Key Takeaways
- Bytecode Jiu-Jitsu is a novel code injection technique that targets software interpreters, fundamentally differing from traditional native code injection methods.
- It injects malicious bytecode into interpreter processes, rather than native machine code, to achieve stealth and evade detection.
- The attack bypasses traditional EDR/AV solutions by avoiding suspicious API calls (e.g.,
VirtualAllocEx,WriteProcessMemory,CreateRemoteThread) that are commonly monitored for native code injection. - Successful execution relies on extensive dynamic analysis of interpreter binaries to reverse-engineer their internal bytecode and symbol table structures.
- Defenders must adapt by focusing on deeper memory forensics within interpreter processes, looking for anomalous bytecode modifications, and implementing enhanced behavioral analysis at the script execution level.
- This technique highlights a critical gap in current security monitoring, demanding a shift towards understanding and securing application-level runtime environments.
About the Speaker(s)
Toshinori is a research scientist at NTT Security Holdings. His research interests are diverse, encompassing reverse engineering, Capture The Flag (CTF) challenges, and Brazilian Jiu-Jitsu, which he playfully references through the name of the attack and his attire.
Yuto Otsuki is a senior researcher and colleague of Toshinori at NTT Security Holdings. His primary research interests lie in the fields of memory analysis and operating system security, providing a strong foundation for understanding the intricacies of code injection and evasion techniques.