接上一篇 gadget inspector 挖掘利用链
项目地址github
这便主要是对gadget inspectot进行了一些改造:
1、修改gadgetinspector.GadgetChainDiscovery#JNDISlink函数,增加满足条件,主要针对函数lookup的条件
private boolean JNDISlink(Handle method, InheritanceMap inheritanceMap) {
if ((inheritanceMap.isSubclassOf(method.getClassReference(),
new ClassReference.Handle("java/rmi/registry/Registry")) ||
inheritanceMap.isSubclassOf(method.getClassReference(),
new ClassReference.Handle("javax/naming/Context")) ||
method.getClassReference().getName().equals("javax/naming/Context") ||
method.getClassReference().getName().equals("java/rmi/registry/Registry"))
&& method.getName().equals("lookup")) {
return true;
}
return false;
}
2、gadgetinspector的算法为了避免因为被扫描代码函数调用函数导致利用链挖掘过程中死循环,每个函数只会反问一次,即如果有3个不同利用链某个环节调用了同一个函数,可能会忽略掉其中两个。
在gadgetinspector.GadgetChainDiscovery#discover函数处增加超参数maxRepeatBranchesTimes,该参数用来设置每个函数可以最多被利用maxRepeatBranchesTimes次,在分支多用的情况下避免出现扫描函数死循环问题:
for (MethodReference.Handle methodImpl : allImpls) {
GadgetChainLink newLink = new GadgetChainLink(methodImpl,
graphCall.getTargetArgIndex());
if (exploredMethods.contains(newLink)) {
if (chain.links.size() <= ConfigHelper.opLevel) {
GadgetChain newChain = new GadgetChain(chain, newLink);
methodsToExploreRepeat.add(newChain);
}
// 增加超参数maxRepeatBranchesTimes,解决分支多用问题
if (exploredMethodsMap.containsKey(newLink)){
if(exploredMethodsMap.get(newLink) > ConfigHelper.maxRepeatBranchesTimes){
continue;
}
exploredMethodsMap.put(newLink, exploredMethodsMap.get(newLink)+1);
}
else {
exploredMethodsMap.put(newLink, 1);
}
}
GadgetChain newChain = new GadgetChain(chain, newLink);
if (isSink(methodImpl, graphCall.getTargetArgIndex(), inheritanceMap)) {
discoveredGadgets.add(newChain);
} else {
methodsToExplore.add(newChain);
exploredMethods.add(newLink);
}
}
超参数maxRepeatBranchesTimes需要在类gadgetinspector.ConfigHelper中增加如下变量:
gadgetinspector.ConfigHelper
然后在入口类gadgetinspector.GadgetInspector增加接收代码:
else if(arg.equals("--maxRepeatBranchesTimes")){
ConfigHelper.maxRepeatBranchesTimes = Integer.parseInt(args[++argIndex]);
}
3、在gadgetinspector.jackson.JacksonImplementationFinder#getImplementations函数处,增加满足lookup利用链的其他情况
public Set<MethodReference.Handle> getImplementations(MethodReference.Handle target) {
Set<MethodReference.Handle> allImpls = new HashSet<>();
// For jackson search, we don't get to specify the class; it uses reflection to instantiate the
// class itself. So just add the target method if the target class is serializable.
if (Boolean.TRUE.equals(serializableDecider.apply(target.getClassReference()))) {
allImpls.add(target);
}
//补充lookup情况
if(target.getName().equals("lookup") &&
(target.getClassReference().getName().equals("javax/naming/Context") || target.getClassReference().getName().equals("java/rmi/registry/Registry"))){
allImpls.add(target);
}
return allImpls;
}
添加自己的xstream检测
首先需要创建一个包,里面创建三个类
1、创建NewXstreamSourceDiscovery类,该类用于增加source点,即定义入口
package gadgetinspector.newxstream;
import gadgetinspector.ConfigHelper;
import gadgetinspector.SourceDiscovery;
import gadgetinspector.data.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class NewXstreamSourceDiscovery extends SourceDiscovery {
public static final Set<String> skipList = new HashSet<>();
static {
if (!ConfigHelper.skipSourcesFile.isEmpty()) {
try(BufferedReader bufferedReader = Files.newBufferedReader(Paths.get(ConfigHelper.skipSourcesFile))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
String c;
if (!(c = line.split("#")[0].trim()).isEmpty()) {
skipList.add(line.trim());
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void discover(Map<ClassReference.Handle, ClassReference> classMap, Map<MethodReference.Handle, MethodReference> methodMap, InheritanceMap inheritanceMap, Map<MethodReference.Handle, Set<GraphCall>> graphCallMap) {
for (MethodReference.Handle method : methodMap.keySet()) {
MethodReference methodValue = methodMap.get(method);
boolean skipFlag = false;
for (String skipClass:skipList){
if (method.getClassReference().getName().contains(skipClass)){
skipFlag = true;
break;
}
}
if (skipFlag){
continue;
}
addDiscoveredSource(new Source(method, 0));
}
}
}
2、创建NewXstremImplementationFinder类,该Finder类,基本都是用到SerializableDecider决策者就可以了,不用写什么其他东西
package gadgetinspector.newxstream;
import gadgetinspector.ImplementationFinder;
import gadgetinspector.SerializableDecider;
import gadgetinspector.data.MethodReference;
import java.util.HashSet;
import java.util.Set;
public class NewXstremImplementationFinder implements ImplementationFinder{
private final SerializableDecider serializableDecider;
public NewXstremImplementationFinder(SerializableDecider serializableDecider){
this.serializableDecider = serializableDecider;
}
@Override
public Set<MethodReference.Handle> getImplementations(MethodReference.Handle target) {
Set<MethodReference.Handle> allImpls = new HashSet<>();
if (Boolean.TRUE.equals(serializableDecider.apply(target.getClassReference()))) {
allImpls.add(target);
}
return allImpls;
}
}
3、创建NewXstremSerializableDecider类,反序列化黑名单的机制,如果想要减少已被禁用链的输出,可以在这里加入黑名单。
package gadgetinspector.newxstream;
import gadgetinspector.SerializableDecider;
import gadgetinspector.data.ClassReference;
import gadgetinspector.data.MethodReference;
import gadgetinspector.jackson.JacksonSourceDiscovery;
import gadgetinspector.webservice.WebserviceSourceDiscovery;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class NewXstremSerializableDecider implements SerializableDecider {
//类是否通过决策的缓存集合
private final Map<ClassReference.Handle, Boolean> cache = new HashMap<>();
//类名-方法集合
private final Map<ClassReference.Handle, Set<MethodReference.Handle>> methodsByClassMap;
public NewXstremSerializableDecider(Map<MethodReference.Handle, MethodReference> methodMap){
this.methodsByClassMap = new HashMap<>();
for (MethodReference.Handle method : methodMap.keySet()) {
Set<MethodReference.Handle> classMethods = methodsByClassMap.get(method.getClassReference());
if (classMethods == null) {
classMethods = new HashSet<>();
methodsByClassMap.put(method.getClassReference(), classMethods);
}
classMethods.add(method);
}
}
@Override
public Boolean apply(ClassReference.Handle handle) {
if (isNoGadgetClass(handle)) {
return false;
}
return Boolean.TRUE;
}
private boolean isNoGadgetClass(ClassReference.Handle clazz) {
if (WebserviceSourceDiscovery.skipList.contains(clazz.getName())) {
return true;
}
return false;
}
}
4、创建一个NewXstreamDeserializationConfig 继承于 GIConfig,配置NewXstreamDeserializationConfig
package gadgetinspector.config;
import gadgetinspector.ImplementationFinder;
import gadgetinspector.SerializableDecider;
import gadgetinspector.SlinkDiscovery;
import gadgetinspector.SourceDiscovery;
import gadgetinspector.data.ClassReference;
import gadgetinspector.data.InheritanceMap;
import gadgetinspector.data.MethodReference;
import gadgetinspector.newxstream.*;
import java.util.Map;
import java.util.Set;
public class NewXstreamDeserializationConfig implements GIConfig {
@Override
public String getName() {
return "newXstream";
}
@Override
public SerializableDecider getSerializableDecider(Map<MethodReference.Handle, MethodReference> methodMap, InheritanceMap inheritanceMap) {
return new NewXstremSerializableDecider(methodMap);
}
@Override
public ImplementationFinder getImplementationFinder(Map<MethodReference.Handle, MethodReference> methodMap, Map<MethodReference.Handle, Set<MethodReference.Handle>> methodImplMap, InheritanceMap inheritanceMap, Map<ClassReference.Handle, Set<MethodReference.Handle>> methodsByClass) {
return new NewXstremImplementationFinder(getSerializableDecider(methodMap, inheritanceMap));
}
@Override
public SourceDiscovery getSourceDiscovery() {
return new NewXstreamSourceDiscovery();
}
@Override
public SlinkDiscovery getSlinkDiscovery() {
return null;
}
}
5、将NewXstreamDeserializationConfig添加到配置仓库ConfigRepository
6、在gadgetinspector.GadgetChainDiscovery中添加sink点,首先添加sink特征函数
private boolean isNewXstreamSink(Handle method, int argIndex, InheritanceMap inheritanceMap) {
if (method.getName().equals("toString")
& method.getClassReference().getName().contains("java/lang/CharSequence")) {
return true;
}
return false;
}
然后加进gadgetinspector.GadgetChainDiscovery#isSink中
if ((ConfigHelper.slinks.isEmpty() || ConfigHelper.slinks.contains("newXstream")) && isNewXstreamSink(method, argIndex, inheritanceMap)) {
return true;
}