天天看點

某APT組織樣本混淆技巧解密與實作-compressed壓縮轉換成base64

投放方式是一個zip包,裡面是一個快捷方式檔案xxx.lnk。xxx.lnk裡的内容是mshta運作網址轉向的xx.hta檔案。中途還要收集一波點開人員的機器資訊。

hta裡的加密形式遇見了好幾次都是手工解的。效率很低,而且相關的樣本很多。使用Python腳本進行解密是一個不錯的選擇。

兩種混淆加密形式原理

  • JS動态調用轉成base64編碼的C# DLL函數
  • C# DLL調用轉成base64編碼的compressed壓縮資料

網上用的比較多的方法,把C#寫的DLL轉成base64,然後動态調用DLL類裡的方法。

Jvascript代碼如下:

//動态調整視窗大小
window.resizeTo(0,0);
// 設定.net執行的版本
function setversion() {
var shell = new ActiveXObject('WScript.Shell');
ver = 'v4.0.30319';
try {
shell.RegRead('HKLM\\SOFTWARE\\Microsoft\\.NETFramework\\v4.0.30319\\');
} catch(e) { 
ver = 'v2.0.50727';
}
shell.Environment('Process')('COMPLUS_Version') = ver;
// Base64轉碼後的C# DLL代碼指派到da變量。
var pa = "rk...DLL base64 CODE....";
// 指定入口類名
var fire = 'C# DLL類名';

// Base64解碼函數
function base64ToStream(b) {
	var enc = new ActiveXObject("System.Text.ASCIIEncoding");
	var length = enc.GetByteCount_2(b);
	var ba = enc.GetBytes_4(b);
	var transform = new ActiveXObject("System.Security.Cryptography.FromBase64Transform");
	ba = transform.TransformFinalBlock(ba, 0, length);
	var ms = new ActiveXObject("System.IO.MemoryStream");
	ms.Write(ba, 0, (length / 4) * 3);
	ms.Position = 0;
	return ms;
}

//執行函數
try {
    // 設定.net運作環境
    setversion();
    // 解碼Base64
    var Streamline = base64ToStream(pa);
    // 調用庫
    var fireline = new ActiveXObject('System.Runtime.Serialization.For'+'matters.Binary.BinaryFormatter');
    var arraylist = new ActiveXObject('System.Collections.ArrayList');
    // 反序列化解開base64後的代碼
    var d = fireline.Deserialize_2(Streamline);
    arraylist.Add(undefined);
    // 動态調用,fire變量裡是C# DLL的類
    var realObject = d.DynamicInvoke(arraylist.ToArray()).CreateInstance(fire);
    // 調用類的方法
    realObject.類方法(參數1,參數2)} catch (e) {}
finally{window.close();}

           

解碼python

Input.txt檔案裡放加密後的代碼,Output2.dat是輸出的解密代碼。

## 第一階段 檔案Base64解碼,提取MZ檔案頭到末尾部分,拖到ILSpy就可以看C#代碼。

with open('Input.txt', 'r') as input_file :
    decoded = base64.b64decode(input_file.read())
    peIndex = decoded.find(b'\x4D\x5A')
    outData = decoded[peIndex:]
    with open('Output2.dat', 'wb') as Outputfile:
        Outputfile.write(outData)
           

C# DLL調用轉成base64後的compressed壓縮資料

C# DLL從Javascript代碼裡讀取被壓縮轉碼成Base64代碼的EXE,然後執行。

舉個例子,編碼後的文本:

EgAAAB+LCAAAAAAABAALycgsVgCi4vzcVAWFktSKEgC9n1/fEgAAAA==
           

解碼後的原文:

This is some text
           

使用以下的C#函數做解密:

當然代碼裡是不應該用

Encoding.Default.GetString

的。對于中文來說,不同的語言編碼占的位元組數是不同的。攻擊者顯然沒有注意這點,直接調用的預設編碼,中文系統無法順利運作。

// 解壓縮函數
public string decompressData(string compressedText)
{
	byte[] array = Convert.FromBase64String(compressedText);
	string @string;
	using (MemoryStream memoryStream = new MemoryStream())
	{
		int num = BitConverter.ToInt32(array, 0);
		memoryStream.Write(array, 4, array.Length - 4);
		byte[] array2 = new byte[num];
		memoryStream.Position = 0L;
		using (GZipStream gZipStream = new GZipStream(memoryStream, CompressionMode.Decompress))
		{
			gZipStream.Read(array2, 0, array2.Length);
		}
		@string = Encoding.Default.GetString(array2);
	}
	return @string;
}

           

# 第二階段檔案Base64解碼,解compressed壓縮

with open('Input.txt', 'r') as input_file :
    decoded = base64.b64decode(input_file.read())
    struct.unpack('i', decoded[:4])[0]
    with open('Output2.dat', 'wb') as Outputfile:
        Outputfile.write(gzip.decompress(decoded[4:]))
           

加密GZIP Compress/Decompress in C#/JAVA

既然有解密,那就肯定有加密代碼。

Program.cs

using System;
using System.IO;
using System.IO.Compression;
using System.Text;

class Program {

    private static string Compress(string text)
    {
        byte[] buffer = Encoding.UTF8.GetBytes(text);
        MemoryStream ms = new MemoryStream();
        using (GZipStream zip = new GZipStream(ms, CompressionMode.Compress, true))
        {
            zip.Write(buffer, 0, buffer.Length);
        }

        ms.Position = 0;
        MemoryStream outStream = new MemoryStream();

        byte[] compressed = new byte[ms.Length];
        ms.Read(compressed, 0, compressed.Length);

        return Convert.ToBase64String(compressed);
    }


    public static string Decompress(string compressedText)
    {
        byte[] gZipBuffer = Convert.FromBase64String(compressedText);
        using (var memoryStream = new MemoryStream())
        {
            int dataLength = BitConverter.ToInt32(gZipBuffer, 0);
            memoryStream.Write(gZipBuffer, 0, gZipBuffer.Length);

            var buffer = new byte[dataLength];

            memoryStream.Position = 0;
            using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Decompress))
            {
                gZipStream.Read(buffer, 0, buffer.Length);
            }

            return Encoding.UTF8.GetString(buffer);
        }
    }


    public static void Main(string[] args) {

        string original = "Mary had a little LAMB";
        string compressed = Compress(original);
        string decompressed = Decompress(compressed);

        
        Console.WriteLine("Original String: "+original);
        Console.WriteLine("Compressed String: "+compressed);
        Console.WriteLine("Decompressed String: "+decompressed);

    }
}
           

Program.java

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;


/**
 * Created by salar on 10/3/16.
 */
public class Program {


    private static String Compress(String data) {
        try {

            // Create an output stream, and a gzip stream to wrap over.
            ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length());
            GZIPOutputStream gzip = new GZIPOutputStream(bos);

            // Compress the input string
            gzip.write(data.getBytes());
            gzip.close();
            byte[] compressed = bos.toByteArray();
            bos.close();

            // Convert to base64
            compressed = Base64.getEncoder().encode(compressed);

            // return the newly created string
            return new String(compressed);
        } catch(IOException e) {

            return null;
        }
    }



    private static String Decompress(String compressedText) throws IOException {

        // get the bytes for the compressed string
        byte[] compressed = compressedText.getBytes("UTF8");

        // convert the bytes from base64 to normal string
        Base64.Decoder d = Base64.getDecoder();
        compressed = d.decode(compressed);

        // decode.
        final int BUFFER_SIZE = 32;

        ByteArrayInputStream is = new ByteArrayInputStream(compressed);

        GZIPInputStream gis = new GZIPInputStream(is, BUFFER_SIZE);

        StringBuilder string = new StringBuilder();

        byte[] data = new byte[BUFFER_SIZE];

        int bytesRead;

        while ((bytesRead = gis.read(data)) != -1)
        {
            string.append(new String(data, 0, bytesRead));
        }
        gis.close();
        is.close();
        return string.toString();
    }




    public static void main(String args[]) {


        String input = "Mary had a little LAMB";


        String compressed = Compress(input);

        String uncompressed = null;
        try {
            uncompressed = Decompress(compressed);
        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("Original String: " + input);
        System.out.println("Compressed String: "+compressed);
        System.out.println("Uncompressed String: "+uncompressed);



    }
}
           

參考

  • How to decompress text in Python that has been compressed with gzip?

    https://stackoverflow.com/questions/52478874/how-to-decompress-text-in-python-that-has-been-compressed-with-gzip

  • 壓縮和解壓縮的代碼

    https://gist.github.com/TheSalarKhan/a5242147e7bc2d431b2c0a8670e33a0f

上一篇: LeNet

繼續閱讀