beanFactory.getBean(name);

漏洞样例 https://github.com/luelueking/RuoYi-v4.7.8-RCE-POC

一开始看,肯定会好奇怎么就通过 genTableServiceImpl 拿到了 com.ruoyi.generator.service.impl.GenTableServiceImpl 并调用其中的 createTable 函数

开始从路由处 @RequestMapping("/monitor/job") 分析源码,一个个函数调用进去,源码中写的最后一步获取是在

这个函数是 spring 框架内部的,返回的就是 GenTableServiceImpl Object

静态分析已经看不了了,此时需要动态调试

AbstractBeanFactory#doGetBean 函数中调用的 Object sharedInstance = getSingleton(beanName); 返回了对应的 Object

于是再仔细跟进 getSingleton

之后到达这个函数

1
2
3
4
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
......

一个拥有 513 个键值对的 ConcurrentHashMap,从中 get genTableServiceImpl 就可获取 GenTableServiceImpl Object

如果想要尝试挖掘其他可利用的类,需要满足此类在 com.ruoyi 包之内

编写如下代码,放入表达式监视器中运行

1
2
3
4
5
6
7
8
9
10
ConcurrentHashMap<Integer, Object> filteredMap = new ConcurrentHashMap<>();

for (Integer key : singletonObjects.keySet()) {
Object value = singletonObjects.get(key);
if (value.getClass().getName().startsWith("com.ruoyi")) {
filteredMap.put(key, value);
}
}

return filteredMap

91 个可用,现在就是看看有无高危函数了

还挺多的,但是应该调用的函数应该是那些,只能传入 String 类型参数的

所以那个 createTable 刚刚好,执行 sql 语句,再把数据库中的定时任务修改成 jndi 注入(这一步就绕过了 waf

如果在执行定时任务的之前,再对 InvokeTarget 做一个检查,可能更安全些

这里将所有可以可以通过 String 参数调用或者无参数调用的函数都搜出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
ConcurrentHashMap<Integer, Object> filteredMap = new ConcurrentHashMap<>();
System.out.println();
for (Object key : singletonObjects.keySet()) {
Object value = singletonObjects.get(key);

java.lang.reflect.Method[] methods = value.getClass().getDeclaredMethods();

for (java.lang.reflect.Method method : methods) {
java.lang.reflect.Parameter[] parameters = method.getParameters();
boolean allStringParams = true;

for (java.lang.reflect.Parameter parameter : parameters) {
if (!parameter.getType().equals(String.class)) {
allStringParams = false;
break;
}
}
if (value.getClass().getName().startsWith("com.ruoyi") && allStringParams) {
String methodKey = value.getClass().getName() + "#" + method.getName();
filteredMap.put(methodKey, method);
System.out.println(methodKey);
}
}
}

return filteredMap

这么多 bean 到底是为什么被注入到这个 bean 中的