发布于12月5日12月5日 安全数据交换系统中的漏洞挖掘 2023年9月18日马云惹不起马云 本文写于2022 年,分享发现安全数据交换系统漏洞的过程。 我拿到的第一个系统是vmware虚拟机,系统中有Linux的基本信息: 后端管理界面用户名和密码:admin/nxg@LL99 操作系统:root/bo%Fn!71、uninxg/lx$zR9ce 配置网络 根据产品安装文档搭建好环境后,手动设置IP地址和DNS: 手动修改/etc/resolv.conf 名称服务器114.114.114.114 名称服务器8.8.8.8 修改/etc/NetworkManager/NetworkManager.conf 文件并将“dns=none”选项添加到主要部分: [主要] #plugins=ifcfg-rh 域名=无 网络IP地址配置文件位于/etc/sysconfig/network-scripts文件夹中: 我添加了两张网卡,其中一张用于本地访问: /etc/sysconfig/network-scripts/ifcfg-eth1-1 HWADDR=00:0C:29:4B:16:B4 类型=以太网 PROXY_METHOD=无 BROWSER_ONLY=否 BOOTPROTO=无 IPADDR=192.168.117.100 网关=192.168.117.2 前缀=24 DNS1=114.114.114.114 DNS2=8.8.8.8 DEFROUTE=是 IPV4_FAILURE_FATAL=否 IPV4_DNS_PRIORITY=100 IPV6INIT=否 名称=eth1 UUID=8a47e710-cadd-49b5-b9b7-33a324c4ab66 设备=eth1 启动=否 观察启动命令行: /home/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 启动 /home/leagsoft/SafeDataExchange/Apache 是Tomcat 的安装目录,webapps 目录是部署的应用程序源代码: 通过ssh将war包复制到本地电脑,就可以看到整个项目的源码了。 源码解密 将war包复制到本地,通过idea打开。发现关键代码的实现是空的,连spring控制器都是空的。初步怀疑是加密的,那么它是如何加密的呢? 既然网站能够正常运行,那么在运行时就应该通过一些技术手段来实现。观察启动命令行: /home/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 启动 命令行中有一个javaagent引起了我的注意: -javaagent:/home/leagsoft/SafeDataExchange/Apache/lib/jdc.jar 将lib文件夹复制到项目中,观察jar包的结构: 看来是调用javassist来实现内存修补技术的。找到Agent的入口方法,看看它做了什么: // //IntelliJ IDEA 从.class 文件重新创建的源代码 //(由Fernflower 反编译器提供支持) // com.leagsoft.declass 包; 导入java.lang.instrument.Instrumentation; 公共类代理{ 公共代理() { } 公共静态无效premain(String args, Instrumentation inst) 抛出异常{ CoreAgent.premain(args, inst); } } 跟进CoreAgent.premain: 公共类CoreAgent { 公共CoreAgent() { } 公共静态无效premain(String args, Instrumentation inst) { if (inst !=null) { 文件file=new File('././Ini/ec.file'); MapString, String configMap=ECFileConfig.getConfig(); byte[] 字节=IoUtils.readFileToByte(file); byte[] by=EncryptUtils.de(bytes, ((String)configMap.get('pf')).toCharArray(), 1); AgentTransformer tran=new AgentTransformer(EncryptUtils.rsk(new String(by)).toCharArray()); inst.addTransformer(tran); } } } 这里可以看到,首先是通过ECFileConfig进行初始化,然后解密读取Ini/ec.file 跟进ECFileConfig.getConfig(): 公共类ECFileConfig { 私有静态MapString,字符串configMap=null; 公共ECFileConfig() { } 私有静态无效iniConfig() { if (configMap==null) { INIImpl ini=ECFileIni.getIni(); configMap=ini.getProperties('ECFile'); } } 公共静态MapString,字符串getConfig(){ iniConfig(); 返回配置映射; } } ////ECFileIni.getIni(); 公共类ECFileIni { 私有静态字符串文件='././Ini/ECFile.ini'; 私有静态INIImpl self=null; 静态{ 自我=初始化(); } 公共ECFileIni() { } 私有静态INIImpl init() { 字符串代码=FileEncode.getFileEncode(file); INIImpl iniFile='asci'.equals(code) ? INIUtil.getInstance(文件) : INIUtil.getInstance(文件,代码); 返回ini 文件; } 公共静态String getStringProperty(字符串部分,字符串属性){ String rs=self.getStringProperty(节, 属性); 返回'null'.equals(rs) ?空: 卢比; } 公共静态INIImpl getIni() { 返回自我; } } 恰巧我在服务器上发现了这个文件ECFile.ini: 我们看一下AgentTransformer的实现: 公共类AgentTransformer 实现ClassFileTransformer { 私有char[] pwd; 公共AgentTransformer(char[] pwd) { this.pwd=密码; } 公共byte[] 转换(ClassLoader 加载器,String className,Class?classBeingRedefine,ProtectionDomain 域,byte[] classBuffer){ if (className !=null 域!=null loader !=null) { String projectPath=domain.getCodeSource().getLocation().getPath(); 项目路径=JarUtils.getRootPath(projectPath); if (StrUtils.isEmpty(projectPath)) { 返回类缓冲区; } 否则{ className=className.replace('/', '.').replace('\\', '.'); byte[] bytes=JarDecryptor.getInstance().doDecrypt(projectPath, className, this.pwd); 返回字节!=null 字节[0]==-54 字节[1]==-2 字节[2]==-70 字节[3]==-66 ?字节: 类缓冲区; } } 否则{ 返回类缓冲区; } } AgentTransformer重写了ClassFileTransformer的transform方法,将每个类和密码放入JarDecryptor.doDecrypt中进行解密,最后返回字节码。 我们看一下JarDecryptor.doDecrypt的实现: 通过readEncryptedFile方法读取**META-INF/.classes/**下的class文件进行解密。 回到文件目录,在META-INF下发现了很多加密的类字节码文件: 这里我写了一个类,并调用JarDecryptor.doDecrypt 来解密所有类: 导入com.leagsoft.declass.util.ECFileConfig; 导入com.leagsoft.declass.util.EncryptUtils; 导入com.leagsoft.declass.util.IoUtils; 导入com.leagsoft.declass.util.StrUtils; 导入java.io.File; 导入java.io.FileOutputStream; 导入java.util.Map; 公共类主要{ 私有静态最终字符串ENCRYPT_PATH='UniEx/META-INF/.classes/'; private static Final String DECRYPT_PATH='UniEx-decode/UniExdecrypt/'; 私有静态char[] getPassword(){ 尝试{ 文件file=new File('UniEx/ec.file'); MapString, String configMap=ECFileConfig.getConfig(); byte[] 字节=IoUtils.readFileToByte(file); 字符串pf='UniNXG-KUv1N5FQr9NtPWnK5UpJ8nnM3blCH9jYtGoXeo0bsXowOffDnW2o0DaVo41ZblSF0tNow5dPxVn8odAS9l4QxCiSvGTXhbliZF9W'; byte[] by=EncryptUtils.de(bytes, pf.toCharArray(), 1); char 密码[]=EncryptUtils.rsk(new String(by)).toCharArray(); System.out.println(密码); 返回密码; } catch (异常e) { System.out.println(e); } 返回空值; } 公共静态无效主(字符串[] args)抛出异常{ 字符密码[]=getPassword(); 文件类文件=新文件(ENCRYPT_PATH); File[] fs=classFiles.listFiles(); for (文件类文件: fs){ System.out.println(classFile.getAbsolutePath()); 文件file=new File(ENCRYPT_PATH, classFile.getName()); byte[] 字节=IoUtils.readFileToByte(file); 如果(字节==空){ 返回; } 否则{ char[] pass=StrUtils.merger(new char[][]{password, classFile.getName().toCharArray()}); bytes=EncryptUtils.de(bytes, pass, 1); System.out.println('正在解密.' + classFile.getName()); 尝试{ File outFile=new File(DECRYPT_PATH+ classFile.getName()+'.class'); if (!outFile.exists()){ outFile.createNewFile(); } FileOutputStream 输出流=new FileOutputStream(outFile); 输出流.write(字节); }catch(异常e){ } } } } } 运行Main 方法来恢复所有加密的类字节码文件,就完成了。 远程调试Tomcat 修改汤姆
创建帐户或登录后发表意见