漏洞分析 | 漏洞调试的捷径:精简代码加速分析与利用

0x01前言

近期,Microsoft威胁情报团队曝光了DEV-0950(Lace Tempest)组织利用SysAid的事件。随后,SysAid安全团队迅速启动了应急响应,以应对该组织的攻击手法。然而,在对漏洞的分析和复现过程中,并未提供详细说明。由于该产品在安装时需要许可证,增加了动态调试漏洞的难度。为了便于调试能够快速复现该漏洞,我们尝试通过只使用部分的单元代码来模拟漏洞的主要逻辑流程进行动态调试分析。最终,我们成功利用 Goby 工具完美地实现了该漏洞的利用。

0x02 补丁分析

在 SysAid 中发布的公告中说明在 23.3.36 修复了该漏洞,通过 Diff 补丁发现该修复方式主要为限制 com.ilient.server.UserEntry#doPost函数中的 ..来完成的。

0x03 单元调试

由于安装包在安装时对许可证进行了限制,因此无法有效地进行安装和调试。为了能够高效地进行动态调试,我们采取了以下优化策略:创建一个独立的空项目,将存在漏洞的 Servlet 进行重写,用于单元模拟。通过这种方法,我们可以在没有任何依赖的情况下最小化地运行漏洞点,并顺利完成研究和分析的工作。

通过修复的方式来推断,该漏洞通过目录穿越的方式来指定上传的路径(accountId 参数)以及上传内容来完成利用。

再收到 accountId 参数值后会通过 a 函数来完成对该路径的拼接,由于该拼接方式存在一定的问题就导致了目录穿越,然后将传入的数据流写入到 accountId 可控的路径。

在写入完毕之后通过调用 a(var31, var46, var7);完成对传入数据的解压到指定的目录中。

根据漏洞分析得出漏洞的核心利用点主要取决于 accountId 和 POST 请求传入的字节数据,所以我们可以将存在可能利用的代码进行抽象。

java 复制代码
package com.example.sysaid;

import com.ilient.server.IlientConf;

import java.io.*;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Comparator;
import java.util.zip.InflaterInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import javax.servlet.annotation.*;

@WebServlet(name = "helloServlet", value = "/hello-servlet")
public class HelloServlet extends HttpServlet {
    private String message;

    public void init() {
    }

    public void doGet(HttpServletRequest var1, HttpServletResponse var2) throws IOException {
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse var2) throws ServletException, IOException {
        String accountId = request.getParameter("accountId");
        InflaterInputStream inputStream = new InflaterInputStream(request.getInputStream());
        byte[] bytes = InputStreamUtils.InputStreamToBytes(inputStream);
        String var46  = a(accountId);
        String var7 = request.getParameter("symbolName");
        File var31 = new File(var46 + File.separator + Long.toString(Calendar.getInstance().getTimeInMillis()) + ".zip");
        FileOutputStream var38;
        (var38 = new FileOutputStream(var31)).write(bytes);
        var38.flush();
        var38.close();
        // var46
        a(var31, var46, var7);
        if (!var31.delete()) {
            var31.setWritable(true);
            var31.delete();
        }
    }

    private static void a(File var0, String var1) {
        File var2;
        if ((var2 = new File(var1)).exists() && var2.isDirectory()) {
            File[] var6 = var2.listFiles();
            String var3 = Long.toString(Calendar.getInstance().getTimeInMillis()) + ".bad";
            File var5 = new File(var1, var3);
            if (var0.renameTo(var5)) {
                System.out.println("UserEntry.renameAndMoveFile: File renamed and moved successfully.");
            } else {
                System.out.println("UserEntry.renameAndMoveFile: Failed to rename and move the file.");
            }
            if (var6.length >= 10) {
                Arrays.sort(var6, Comparator.comparingLong(File::lastModified));
                for(int var4 = 0; var4 < var6.length - 9; ++var4) {
                    if (var6[var4].isFile() && !var6[var4].delete()) {
                        System.out.println("UserEntry.renameAndMoveFile: Failed to delete file: " + var6[var4].getName());
                    }
                }
            }
        } else {
            System.out.println("UserEntry.renameAndMoveFile: Invalid output folder specified.");
        }
    }

    private static void a(File var0, String var1, String var2) {
        if (!var2.startsWith("LDAP_REFRESH_")) {
            IlientConf.logger.error(String.format("Error on UserEntry: symboleName %s not validated.", var2));
            a(var0, var1);
        } else {
            byte[] var8 = new byte[1024];
            try {
                ZipInputStream var3;
                for(ZipEntry var4 = (var3 = new ZipInputStream(new FileInputStream(var0))).getNextEntry(); var4 != null; var4 = var3.getNextEntry()) {
                    String var9;
                    if ((var9 = var4.getName()) != null && var9.indexOf("..") >= 0) {
                        System.out.println("Error in UserEntry.unZipIt - Found path manipulation!");
                        a(var0, var1);
                        return;
                    }
                    File var10 = new File(var1 + File.separator + var9);
                    String var5 = (new File(var1)).getCanonicalPath();
                    System.out.println(var10.getCanonicalPath());
                    System.out.println(var5 + File.separator);
                    if (!var10.getCanonicalPath().startsWith(var5 + File.separator)) {
                        System.out.println("Error in UserEntry.unZipIt - File is outside of the output directory!");
                        a(var0, var1);
                        return;
                    }
                    System.out.println("File unzip : " + var10.getAbsoluteFile());
                    FileOutputStream var11 = new FileOutputStream(var10);
                    int var12;
                    while((var12 = var3.read(var8)) > 0) {
                        var11.write(var8, 0, var12);
                    }
                    var11.close();
                }
                var3.closeEntry();
                var3.close();
                System.out.println("Finish unziping: " + var0.getAbsolutePath());
            } catch (Exception var7) {
                var7.printStackTrace();
                System.err.println("UserEntry: Error in unZipIt method:"+ var7);
            }
        }
    }

    public static String a(String var0) {
//        String var1 = IlientConf.getInstance().getNonAccountSharedFilesDir("ldapfiles");
        String var1 = "/1111/SysAidServer/root/WEB-INF/ldapfiles";
        File var2;
        if (!(var2 = new File(var1 + File.separator + var0)).exists()) {
            var2.mkdirs();
        }
        return var1 + File.separator + var0;
    }

    public void destroy() {
    }
}

构建新的 war 工程部署该 Servlet 即可完成对漏洞点单元模拟,用于漏洞的动态调试。

0x04 总结

在特殊情况下漏洞调试以及武器化利用时无法能够满足最佳的调试环境,我们尝试通过将有漏洞的部分代码进行单元模拟完成漏洞的动态调试。

0x05 参考

https://www.sysaid.com/blog/service-desk/on-premise-software-security-vulnerability-notification

https://www.huntress.com/blog/critical-vulnerability-sysaid-cve-2023-47246

Goby 欢迎表哥/表姐们加入我们的社区大家庭,一起交流技术、生活趣事、奇闻八卦,结交无数白帽好友。

也欢迎投稿到 Goby(Goby 介绍/扫描/口令爆破/漏洞利用/插件开发/ PoC 编写/ IP 库使用场景/ Webshell /漏洞分析 等文章均可),审核通过后可奖励 Goby 红队版,快来加入微信群体验吧~~~

文章来自Goby社区成员:Gryffinbit@白帽汇安全研究院,转载请注明出处。

微信群:公众号发暗号"加群",参与积分商城、抽奖等众多有趣的活动

获取版本:https://gobysec.net/sale

相关推荐
菩提小狗6 分钟前
每日安全情报报告 · 2026-10-07
网络安全·漏洞·cve·安全情报·每日安全
白猫不黑17 分钟前
AI自动化漏洞挖掘从入门到进阶超详细学习路线
人工智能·学习·web安全·网络安全·信息安全·渗透测试·自动化
请输入蚊子28 分钟前
CVE-2026-8260 D-Link DCS-935L缓冲区溢出漏洞 复现
网络·安全·web安全·iot
学逆向的2 小时前
远程线程注入
开发语言·jvm·网络安全·win32
在猴站学算法3 小时前
(实战)PHP文件包含漏洞
网络安全·php
pencek4 小时前
MazeSec-Loooower
网络安全
lisw0518 小时前
数字断点如何影响供应链——并危及全球经济?
网络安全·网络攻击模型·前沿领域
sbjdhjd1 天前
云安全 | Docker 容器逃逸复盘(二):2375 未授权接口如何突破容器管理边界
网络安全·docker·云原生·数据挖掘·开源·云计算·云安全
HackTwoHub1 天前
Clown SRC skill第二代2.x最新版企业安全研究 Skill|证据优先的 SRC 与白盒审计工作流正式更新
网络·安全·web安全·网络安全·系统安全·密码学·安全架构
白帽攻防录1 天前
SRC 挖洞:Certighost AD CS 域控制器伪造深度复盘,CVE-2026-54121 一个普通域用户怎么拿走整个企业域
网络·安全·web安全·网络安全·域安全