freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

记一次OA系统被植入冰蝎内存马后的溯源
2024-06-13 00:47:40

1. 背景

在某次攻防演练中,OA系统被态势感知系统监测到上传内存马通信,攻击动作为[尝试],起初并未在意,但系统显示经过了几轮尝试后,居然出现了【检测到冰蝎加密webshell通信】的告警,于是迅速将来源IP地址进行了封禁,并进行了漏洞修复。此文记录攻击方在OA系统上传木马,通过木马执行命令的溯源情况。

2. 难点

经过对冰蝎木马的简单研究,该木马使用aes进行加密,默认密钥为rebeyond md5值的前16位,执行原理为加载request中的java字节码。那么存在两个难点,一是获取冰蝎通讯的加密密钥,二是对java字节码进行批量还原,获取攻击方执行的攻击命令。

3. 初步尝试

将态势感知中的告警信息导出后,尝试对请求body中的base64编码后的payload进行解密,解密密钥先用默认的

e45e329feb5d925b (rebeyond md5值的前16位),再通过cyber chef的运算后,宣告失败。

image

随机选了一个(导出数据中的第一个)攻击载荷(请求body)通过cyberchef解密,发现解密后的数据并非java字节码模式,大概率解密失败:

TseXyXgv92jRryJcxINK5eNNE+GIpnvTIWiVnoie/NviaJpIyJEF64vSHMJOAu781UZD9NSf62nXWPztC/BCv7/h0hVruHgO7RRdAQEUsy8P5yzdNxpn7vtuo9Jyx6AB94yAL3YpPSwlefcgJA1o7+Vf1pUIsdoVxJwFZebVuE7v1yDDvczfqYVIzlPqRLndnayiL8PsQ5aOGPldCJ1m43DzuCr8FTLzxKGc6+B/aCp3mi8Ct6tl/1jW7eLUGubSjz6dEBYJ3ybDZa4iz+9K/KZRFaYUoDFWTrumLB2Vwf/ZHbHl5TE2YfJ0mL7lnwumTn5oqqZuNq6A/2gyRi8GpZEI1eNPiHK7zsMlz4RCMPqj712a6ftgTM4wjnvcyUQVFToExaytVDLfjfQf.....省略1万字....

image

4. 寻找解密key

4.1突破口

经过多次尝试后,发现该条告警虽然为【尝试】状态,但可能已经成功上传木马,通过态势感知将告警的请求body导出,进行研究分析。
image

image

请求长度达到了可怕的17322字节,看参数看的眼花缭乱怎么办?善用cyberchef

managerMethod=getConditionValue&arguments=%5B%7B%7D%2C%22%7D%3Bimport+static+com.seeyon.ctp.util.Base64.%2A%3Bimport+st.......省略......classdata=.....

通过Cyberchef的split分割模块,通过&符号进行分割,并且在第二个选项中多加入几个换行,可以看到一共有三个参数,分别是managerMethod、arguments、classdata ,其中第二个和第三个中有大量的base64编码字符串。

image

4.2峰回路转

再次对arguments中的base64编码的字符串进行还原,可以验证classdata字段是java字节码,并且执行的动作为将类实例化后,执行equals方法。

image

4.3柳暗花明

对classdata中的数据进行base64解码,解码后内容如下,可以看到非常明显的java字节码的特征,保存为dowload.class:

image

将download.class拖入idea,进行反编译,查看java源代码:

image

为了方便跟踪代码,将代码拷贝一份,新建一个类,粘贴,idea就能愉快的进行语法分析、代码追踪了。

看到有个函数中有很长的base64编码的字符串:

image

跟踪后来到这里,代码核心原理就是base64解码后,通过gzip解压缩,然后加载类,并且实例化:

image

那么好,就手动把这段base64字符串还原为一个类,在该类上写个main函数,调用一下继续保存为class文件即可:

image

将该类保存为了loader.class,再次反编译,可以看到pass解密密钥的字符串,在后续代码中也看到相关调用的地方:

image

通过对代码逻辑的简单分析,该段代码通过判断请求头部是否包含Xxx字段,并且等于ccc,如果存在,则执行命令控制代码,执行逻辑是从request中读取完整内容,base64解码后完成java类的实例化,并且开始执行equasl方法。从态势感知抓取到的后续通讯请求中也可以看出头部字段带有Xxx:ccc 头部字段。

image

4.4初步成果

通过cyberchef,用获取到的key,完美解密请求body中的加密信息。

5. 下一步

既然要溯源,那就要知道攻击方到底做了什么操作,态势感知系统导出的日志多达1千多条,每个手工解密自然不现实。那就继续编码实现。

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.List;
import java.util.stream.Collectors;

public class Decrypt extends ClassLoader {
    public static byte[] decrypt(String payload) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException {
        String pass = "47bce5c74f589f48";
        Cipher c = Cipher.getInstance("AES");
        c.init(Cipher.DECRYPT_MODE, new SecretKeySpec(pass.getBytes(), "AES"));
        byte[] decode = Base64.getDecoder().decode(payload);
        return c.doFinal(decode);
    }

    public static void writeFile(String filepath, byte[] data) throws IOException {
        Files.write(Paths.get(filepath), data, StandardOpenOption.CREATE);
    }

    public Class defineClass(byte[] b) {
        return super.defineClass(b, 0, b.length);
    }

    public static void main(String[] args) throws Exception {

        List<String> rawData = Files.readAllLines(Paths.get("....日志的文件路径.csv"), Charset.forName("GB2312"));

        List<String> collect = rawData.stream().map(s -> {
            String[] split = s.split(",");
            if (split.length > 8) {
                return split[8].trim();// 在csv文件中的第8个字段
            }
            return s;
        }).collect(Collectors.toList());

        for (int index = 0; index < collect.size(); index++) {
            try {
                byte[] decrypt = decrypt(collect.get(index));

                Class clazz = new Decrypt().defineClass(decrypt);
                StringBuilder sb = new StringBuilder();
                for (Field field : clazz.getFields()) {
                    // 下面几个if视情 进行过滤
                    if (field.getName().equals("type")) {
                        continue;
                    }
                    if (field.getName().equals("whatever")) {
                        continue;
                    }
                    if (field.get(clazz) == null) {
                        continue;
                    }
                    sb.append(field.getName());
                    sb.append(":");
                    sb.append(field.get(clazz));
                    sb.append(" ");
                }
                if(!sb.toString().trim().isEmpty()){
                    System.out.println(sb.toString());
                }
            } catch (Error error) {

            } catch (Exception ex) {
            }
        }

    }
}

通过java代码,读取态势感知日志,base64 decode、aes解密后,将字节数组定义为java类,通过java类反射获取各个字段的值,过滤掉字段为null或者为空的部分请求后,发现了除读取了一些文件之外,还创建了一些文件:

image

6. 后记

至此,分析完成。后期看了几篇分析冰蝎源代码的文章,对payload的整体还原分析可以有更加直观的认识。

# 渗透测试 # web安全 # 漏洞分析
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录