Java模板注入案例

wen java案例 1

本文目录导读:

Java模板注入案例

  1. FreeMarker模板注入
  2. Thymeleaf模板注入
  3. Velocity模板注入
  4. JSP EL注入
  5. Pebble模板注入
  6. 漏洞检测方法论
  7. 防御措施总结

我来介绍Java中常见的模板注入(SSTI)案例和漏洞分析。

FreeMarker模板注入

漏洞示例

// 不安全的FreeMarker使用
@PostMapping("/render")
public String renderTemplate(String template) throws Exception {
    Template t = new Template("user", template, new Configuration());
    return processTemplate(t);
}
// 攻击payload
${7*7}
<#assign value="freemarker.template.utility.Execute"?new()>${value("id")}
<#assign value="freemarker.template.utility.ObjectConstructor"?new()>${value("java.lang.ProcessBuilder","id")}

安全修复

// 安全的FreeMarker配置
Configuration cfg = new Configuration();
cfg.setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER);
cfg.setObjectWrapper(new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_31).build());
// 或使用白名单
class WhitelistResolver implements TemplateClassResolver {
    private static final Set<String> WHITELIST = Set.of("java.lang.String");
    @Override
    public Class resolve(String name, Environment env) throws TemplateException {
        if (!WHITELIST.contains(name)) {
            throw new InvalidReferenceException("Class " + name + " not allowed");
        }
        return ClassUtil.forName(name);
    }
}

Thymeleaf模板注入

漏洞示例

// 不安全的Thymeleaf使用
@GetMapping("/hello")
public String hello(@RequestParam String name) {
    return "Hello " + name + "!"; // 模板名称来自用户输入
}
// 攻击payload
__$%7Bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec('id').getInputStream()).useDelimiter('\\A').next()%7D__::.x
__${T(java.lang.Runtime).getRuntime().exec("id")}__::.x

安全修复

// 使用参数化模板
@GetMapping("/hello")
public String hello(@RequestParam String name, Model model) {
    model.addAttribute("name", name);
    return "hello"; // 模板名称固定
}
// 如果没有配置视图解析器,直接返回JSON
@ResponseBody
public Map<String, String> hello(@RequestParam String name) {
    return Map.of("message", "Hello " + name);
}

Velocity模板注入

漏洞示例

// 不安全的Velocity使用
Velocity.init();
VelocityContext context = new VelocityContext();
context.put("name", userInput);
StringWriter writer = new StringWriter();
Velocity.evaluate(context, writer, "test", templateContent); // 模板内容来自用户输入
// 攻击payload
#set($e="e")
$e.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("id")
#set($s="")
#set($rt=$s.getClass().forName("java.lang.Runtime"))
#set($ex=$rt.getMethod("getRuntime",null).invoke(null,null))
$ex.exec("id")

安全修复

// 安全的Velocity配置
VelocityEngine engine = new VelocityEngine();
engine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, "org.apache.velocity.runtime.log.Log4JLogChute");
engine.setProperty("velocimacro.library", "");  // 禁止宏加载
engine.setProperty("resource.loader", "string"); // 只允许字符串模板
engine.setProperty("runtime.strict_mode.enable", true);
engine.setProperty("event_handler.reference_insertion.class", "SafeReferenceHandler");
// 启动安全检查
engine.init();

JSP EL注入

漏洞示例

// 不安全的JSP EL使用
<%
    String expression = request.getParameter("expression");
    javax.el.ExpressionFactory factory = 
        javax.el.ExpressionFactory.newInstance();
    javax.el.ValueExpression ve = factory.createValueExpression(
        pageContext.getELContext(), "${" + expression + "}", Object.class);
    out.println(ve.getValue(pageContext.getELContext()));
%>
// 攻击payload
${pageContext.servletContext.classLoader.getResource("").toURI()}
${pageContext.request.getSession().setAttribute("x", "y")}

安全修复

// 使用安全的EL处理器
import javax.el.ELProcessor;
public class SafeELProcessor {
    private static final Set<String> BLOCKED = 
        Set.of("getClass", "forName", "getRuntime", "exec");
    public Object evaluate(String expression) {
        // 添加输入验证
        for (String blocked : BLOCKED) {
            if (expression.contains(blocked)) {
                throw new SecurityException("Blocked: " + blocked);
            }
        }
        ELProcessor elp = new ELProcessor();
        elp.getELManager().addELResolver(new BeanELResolver(false)); // 禁用反射
        return elp.eval(expression);
    }
}

Pebble模板注入

漏洞示例

// 不安全的Pebble使用
PebbleEngine engine = new PebbleEngine.Builder().build();
PebbleTemplate compiledTemplate = engine.getTemplate("inline", new StringReader(template));
// 攻击payload
{{ variable.getClass().forName("java.lang.Runtime").getRuntime().exec("id") }}
{% set str = variable.getClass().forName("java.lang.Runtime").getRuntime().exec("id") %}

安全修复

// 安全的Pebble配置
PebbleEngine engine = new PebbleEngine.Builder()
    .extension(new SandboxExtension()) // 使用沙箱
    .autoEscaping(true)
    .build();
// 自定义沙箱扩展
public class SandboxExtension extends AbstractExtension {
    @Override
    public Map<String, Function> getFunctions() {
        return Map.of(
            "include", new InternalFunction("include"),
            "extends", new InternalFunction("extends")
        );
    }
}

漏洞检测方法论

基础检测payload

// 数学运算测试
${7*7}
#{7*7}
{{7*7}}
// 字符串操作测试
${'a' + 'b'}
<%="abc"%>
// 系统属性探测
${system.properties}
${pageContext}

危险函数检测

// 常见危险函数
T(java.lang.Runtime).getRuntime().exec()
"freemarker.template.utility.Execute"?new()
pageContext.getClass().forName("java.lang.Runtime")

防御措施总结

输入验证与过滤

public class TemplateSecurityUtil {
    private static final Pattern DANGEROUS_PATTERN = Pattern.compile(
        "(?i).*(runtime|processbuilder|scriptengine|classloader|reflection|invoke).*"
    );
    public static boolean isSafeTemplate(String template) {
        if (template == null || template.trim().isEmpty()) {
            return false;
        }
        // 基础检查
        if (template.length() > 1000 || DANGEROUS_PATTERN.matcher(template).matches()) {
            return false;
        }
        return true;
    }
}

使用Ognl安全配置

// OGNL安全配置
OgnlContext context = (OgnlContext) Ognl.createDefaultContext(new Object());
context.setMemberAccess(new DefaultMemberAccess(false)); // 禁用私有成员访问

最佳实践

  1. 永远不要直接拼接用户输入到模板
  2. 使用参数化模板引擎
  3. 配置严格的安全策略
  4. 使用白名单而非黑名单
  5. 定期更新模板引擎版本
  6. 实施纵深防御策略

这些案例展示了Java模板注入的常见类型和防御方法,实际应用中应根据具体场景选择合适的防护措施。

抱歉,评论功能暂时关闭!