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


    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 请求传入的字节数据,所以我们可以将存在可能利用的代码进行抽象。

    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() {
        }
    }
    
    • 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
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124

    构建新的 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

  • 相关阅读:
    【排序算法】选择排序
    C# 文件 校验:MD5、SHA1、SHA256、SHA384、SHA512、CRC32、CRC64
    leetcode每天5题-Day04
    树和森林知识
    Minifilter过滤驱动与R3程序通讯实现文件保护
    C++ fstream类移动读写指针和字节数形式获取该指针位置(seekp、seekg、tellg、tellp)
    阿里开源的32B大模型到底强在哪里?
    ES6基础语法(let、const、解构赋值、模板字符串、简化对象、箭头函数、扩展运算符)(一)
    CPDA|数据分析师成长之路如何起步?
    前端和后端在软件开发中的两个重要部分
  • 原文地址:https://blog.csdn.net/m0_46699477/article/details/134438668