freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

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

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

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

Zloader的DGA算法解析
Avenger 2020-06-23 13:00:13 294925

Zloader(又叫 Terdot、DELoader 或 Zeus Sphinx)是 2016 年 5 月出现的恶意软件,在最近几周又出现了活跃的迹象。

简介

如下所述,Brad Duncan 分析指出其使用的 DGA 域名:

样本会生成以 .com 为顶级域名的随机二十个字母字符的字符串,以此为域名进行 DNS 查询,以下是一些域名示例:

jgqhigsjkulmsvvhshmk.com
wapjdxlstholqwakofgi.com
aiavxvlshmkweccksfky.com
liswrfujohqsnbnohetn.com
hciqylualwcnyvajdkqq.com
pdtlshacpbacpnhcndpd.com
kdacggcctwcavdgvpbmk.com
wapwtpwciertrhkdaxrp.com
shyjgiyhyegxeqqpdtya.com
gccggcctwcerlshacpba.com
cpnhcndpdkylibtlbeco.com
bxhwpdkqdakbplfvfqwn.com
bioonshmwrbecckfcavh.com

TomasP 通过逆向分析 DGA 算法确认了这一发现,但并未发布细节。Dynamic Analysis 随后在 Pastebin 上发布了其 DGA 域名,但只针对一个特定的种子。

这篇文章对算法进行了逆向分析,并且使用 Python 对该算法进行了实现。分析基于下述样本:

属性
MD5afdf2fbc0756ed304d1a33083a5f2b0f
SHA1f3a25627f925390097a64a84ef34c952fe8af036
SHA256a947c216ea52ce23457b3babb1e1eb6275cabe2150d3995553e4de4b8c3d97f4
大小323 KB(330752 字节)
编译时间戳2019-05-27 07:19:22 UTC
链接MalwareBazaarURLHausTwitterVirusTotal
文件名称antiamsi.bin (MalwareBazaar),antiamsi.bin (VirusTotal)
检测结果MalwareBazaar: ZLoader, Virustotal: 52/74 as of 2020-04-25 03:46:18 - TrojanSpy:Win32/Glupteba.ef0afc48 (Alibaba), Trojan:Win32/Glupteba.RRS!MTB (Microsoft), Win32.Trojan-spy.Zbot.Lscl (Tencent), Trojan-Spy.Win32.Zbot.zzac (ZoneAlarm)

一如既往,样本是加壳的,脱壳后可以得到以下样本:

属性
MD5c844efe1b7e76cbdea36ce62ff788de9
SHA1d8143cf09bff7b0ca2a0c777912746a5922104ee
SHA256835048e00ba3babf6f920c9a4c2863865a5dcf8e0b6ede4f57c63aeb9cb5c147
大小184 KB(188416 字节)
编译时间戳2020-04-08 18:19:58 UTC
链接MalwareBazaarMalpediaDropped_by_md5VirusTotal
检测结果Virustotal: 30/74 as of 2020-04-25 20:55:07 - a variant of Win32/Spy.Zbot.ADI (ESET-NOD32), W32/Zbot.ADI!tr (Fortinet), HEUR:Backdoor.Win32.Dridex.vho (Kaspersky), BehavesLike.Win32.Adopshel.ch (McAfee-GW-Edition), HEUR:Backdoor.Win32.Dridex.vho (ZoneAlarm)

样本在挂起状态下创建了一个新的 Windows 安装程序进程 msiexec.exe,然后将自身的加密副本与解密代码写入 msiexec.exe。将线程内容设置为解密代码并且恢复线程执行。解密代码会解密注入的文件并跳转到偏移量为 0x1C90 的第一个 subroutine。将入口点设置为起始点转存了样本,如果将 0x03090000 作为加载镜像基址应该可运行样本。

属性
MD55c76c41f9d0cc939240b3101541b5475
SHA1da361ec6976d3d9225ce40951b26d1d8ecdb7fd1
SHA2564029f9fcba1c53d86f2c59f07d5657930bd5ee64cca4c5929cbd3142484e815a
大小208 KB(212992 字节)
编译时间戳2020-04-08 18:19:58 UTC
链接MalwareBazaarMalpediaDropped_by_md5VirusTotal
检测结果Virustotal: 22/74 as of 2020-04-25 20:55:24 - Win32/Spy.Zbot.ADI (ESET-NOD32), BScope.Trojan-Spy.Zbot (VBA32)

以下分析基于最后一个样本(f3f2393a838d417ff8f823a235bd83f2)加载到镜像基址 0x03091CD2 上展开。

逆向工程

样本分析从三方面展开:

  1. 针对已加密的字符串,使用 IDA Pro 的 Appcall 函数进行动态解密
  2. 使用函数哈希进行动态解析来隐藏 API 调用。Appcall 可以评估程序得到绝大多数 API 名称
  3. Constant Unfolding、Dead Code 插入、通过特征进行算术替换。前两个大多被 Hex Rays 反编译器移除了,算术特征也可以很容易地被基本逻辑等价简化。在分析 DGA 时会举一个例子进行解释说明

字符串解密需要一个函数参数-密文的偏移量:

.text:03091CDB 68 B4 CA 0B 03          push    offset dword_30BCAB4
.text:03091CE0 E8 1B 17 01 00          call    decrypt_string  ; BOT-INFO

通过运行以下 IDA 脚本即可找到带有纯文本的 decrypt_string 函数调用旁的注释:

from idc import *
from idautils import *
import idaapi
import sys
import string
import re

RESOLVER_TYPE_DEC = "char *__cdecl decrypt_string(char *a1, char *a2);"
m = re.search("\s([^ (@]+)[(@]", RESOLVER_TYPE_DEC)
RESOLVER_NAME = m.group(1)

resolver_addr = get_name_ea_simple(RESOLVER_NAME)
if resolver_addr == idaapi.BADADDR:
    print(RESOLVER_NAME + " not defined")
    sys.exit()

resolver = idaapi.Appcall.typedobj(RESOLVER_TYPE_DEC)
resolver.ea = resolver_addr

def previous_heads(ea):
    """ iterator to get previous instructions of an address (no including itself) """
    if not idc.is_head(idc.get_full_flags(ea)):
        ea = idaapi.next_head(ea, ea+1000)
    ea = idaapi.prev_head(ea,0)
    while ea != idaapi.BADADDR:
        yield ea
        ea = idaapi.prev_head(ea, 0)

def do():
    """ count the nr of references to the resolver function """
    xrefs = list(CodeRefsTo(resolver_addr,1))
    """ iterate over all references """
    for i, xr in enumerate(xrefs):
        print("[-] tackling {:08X}".format(xr))
        args = []
        for x in previous_heads(xr):
            args.append(get_operand_value(x, 0))
            if len(args) >= 1:
                break
        empty = Appcall.buffer(" ", 1000)
        args.append(empty)
        try:
            r = resolver(*args)
        except Exception as e:
            print("FAILED: appcall failed: {}".format(e))
            continue
        try:
            name = empty.value
        except:
            print("FAILED: to read back buffer)
            continue
        print("OK: found {}".format(name))
        set_cmt(xr, name, True)

do()

Windows API 函数,如 InternetConnectA 会被动态解析后调用:

.text:030917DC 68 E1 75 E7 0A          push    0AE775E1h
.text:030917E1 6A 13                   push    13h
.text:030917E3 E8 88 19 01 00          call    resolve_api     ; wininet_InternetConnectA
.text:030917E8 83 ** 08                add     esp, 8
.text:030917EB 0F B7 4D 10             movzx   ecx, [ebp+arg_8]
.text:030917EF 6A 00                   push    0
.text:030917F1 6A 00                   push    0
.text:030917F3 6A 03                   push    3
.text:030917F5 6A 00                   push    0
.text:030917F7 6A 00                   push    0
.text:030917F9 51                      push    ecx
.text:030917FA 53                      push    ebx
.text:030917FB 56                      push    esi
.text:030917FC FF D0                   call    eax

使用与解密字符串相似的 IDA Pro 脚本来查找 API 名称并注释反汇编代码。

列出所有对字符串解密代码的应用,可以看到其中产生了明文 .com

1.png

由于在使用字符串前对字符串进行解密,因此对 .com 的解密与 DGA 代码紧密相关。IDA Pro 对这段程序的反编译做的很好,只需要重命名一些变量就可以得到如下 C 代码:

int __cdecl the_dga(int dwSeed, int nNumberOfDomains, int pArrayOfDomains)
{
  int result; // eax
  unsigned int r; // esi
  int i; // edi
  unsigned int offset; // ebx
  char the_letter; // al
  unsigned int dwSeedXored_1; // ebx
  char *szTLD_1; // eax
  int i_1; // [esp-10h] [ebp-48h]
  char szTLD[19]; // [esp+1h] [ebp-37h]
  _DWORD the_domain_object[3]; // [esp+14h] [ebp-24h]
  unsigned int dwSeedXored; // [esp+20h] [ebp-18h]
  int iDomainNr; // [esp+24h] [ebp-14h]
  char szDomain[13]; // [esp+2Bh] [ebp-Dh]

  if ( nNumberOfDomains )
  {
    r = dwSeed;
    result = 0;
    dwSeedXored = dwSeed ^ 0x81716ECC;
    do
    {
      iDomainNr = result;
      initialize(the_domain_object);
      i = 0;
      do
      {
        offset = r % get_nr_25();
        the_letter = offset + get_nr_97();
        dwSeedXored_1 = dwSeedXored;
        szDomain[0] = the_letter;
        update_domain_object(szDomain);
        r = dwSeedXored_1 ^ or(~(r + szDomain[0]) & 0x81716ECC, (r + szDomain[0]) & 0x7E8E9133, 0);
        i_1 = i++;
        plus(i_1, 1, 0, 0);
      }
      while ( i != get_nr_20() );
      szTLD_1 = decrypt_string(szTLDCiphertext, szTLD);
      concatenate(szTLD_1);
      save_in_array((_DWORD *)pArrayOfDomains, (int)the_domain_object);
      reset(the_domain_object);
      result = plus_0(iDomainNr + 0x6A6E645D, 1u, 0) - 0x6A6E645D;
    }
    while ( result != nNumberOfDomains );
  }
  return result;
}

此时代码可读性已经非常高了,唯一不明显的部分是随机数计算(变量r),该变量需要一些基本的逻辑计算。种子 dwSeedsszDomain[0]/,则下一个数使用如下方法决定:

r=(s⊕0x81716ECC)⊕(∼(r+l)⋅0x81716ECC)+((r+l)⋅0x7E8E9133)

+ 代表了逻辑与、异或和或。两个常数具有以下关系:

0x81716ECC =∼ 0x7E8E9133 mod 2 32(此处为2的32次方)

进一步:

a⊕b=(∼a⋅b)+(a⋅∼b)

将 k 设置为 0x81716ECC 可以得到:

r=(s⊕k)⊕(∼(r+l)⋅k)+((r+l)⋅∼k)
 =s⊕k⊕((r+l)⊕k)
 =s⊕(r+l)

使用 Python 实现该 DGA 算法如下所示:

def dga(seed, nr_of_domains):
    domains = []

    r = seed;
    for i in range(nr_of_domains):
        domain = ""
        for j in range(20):
            letter = ord('a') + (r % 25)
            domain += chr(letter)
            r = seed ^ ( (r + letter) & 0xFFFFFFFF)
        domain += ".com"
        print(domain)

查看域名生成代码的调用方,可以看到如何进行种子计算:

.text:03095540 push    ebp
.text:03095541 mov     ebp, esp
.text:03095543 push    ebx
.text:03095544 push    edi
.text:03095545 push    esi
.text:03095546 sub     esp, 16Ch
.text:0309554C lea     edi, [ebp+pS]
.text:03095552 mov     [ebp+var_1C], ecx
.text:03095555 push    edi
.text:03095556 call    decrypt_config_rc4
.text:0309555B add     esp, 4
.text:0309555E lea     esi, [ebp+pArrayOfDomains]
.text:03095561 mov     ecx, esi
.text:03095563 call    sub_30BA8E0
.text:03095568 call    get_today_at_0UTC
.text:0309556D mov     [ebp+dwSeed], eax
.text:03095570 call    sub_30A5260
.text:03095575 lea     ecx, [ebp+dwSeed]
.text:03095578 push    edi
.text:03095579 push    eax
.text:0309557A push    ecx
.text:0309557B call    rc4_encrypt
.text:03095580 add     esp, 0Ch
.text:03095583 mov     edi, [ebp+dwSeed]
.text:03095586 call    get_nr_of_domains
.text:0309558B push    esi
.text:0309558C push    eax
.text:0309558D push    edi
.text:0309558E call    the_dga

首先,使用 R 对 Zlader 的配置进行解密。R 密钥 djluflczrgefphtiwegc 被硬编码在样本中。该配置包含在使用 DGA 域名之前使用的硬编码域名(如果存在的话)。在配置的最后,新的 R** 密钥 q23Cud3xsNf3 用于为 DGA 算法设置种子:

s
TelegramCrypt
AntiAMSIdoc
http://wmwifbajxxbcxmucxmlc.com/post.php
http://pwkqhdgytsshkoibaake.com/post.php
http://snnmnkxdhflwgthqismb.com/post.php
http://iawfqecrwohcxnhwtofa.com/post.php
http://nlbmfsyplohyaicmxhum.com/post.php
http://fvqlkgedqjiqgapudkgq.com/post.php
http://cmmxhurildiigqghlryq.com/post.php
http://nmqsmbiabjdnuushksas.com/post.php
http://fyratyubvflktyyjiqgq.com/post.php
q23Cud3xsNf3

种子基于当前时间 00:00 UTC 的 UNIX 时间戳。该 32 位值以小端序表示,并且使用来自配置中的密钥(如 q23Cud3xsNf3)进行 R** 加密这四个字节。然后将结果解释为种子的小端序表示,下列代码揭示了种子的生成过程:

key = "q23Cud3xsNf3"
rc4 = R**(key)
d = d.replace(hour=0, minute=0, second=0)
timestamp = int((d - datetime(1970, 1, 1)).total_seconds())
p = struct.pack("<I", timestamp)
c = rc4.encrypt(p)
seed = struct.unpack("<I", c)[0]

种子生成后进入 DGA 算法,现在就可以完全用 Python 重新实现该 DGA 算法。

算法重新实现

以下代码可用于为任意日期和 R** 种子值生成 Zlader 域名。例如,要使用种子 q23Cud3xsNf3 生成 2020 年 4 月 25 日的域名即可使用 dga.py -d 2020-04-25 --rc4 q23Cud3xsNf3。相关程序可以在我的 GitHub 中找到。

from datetime import datetime
import struct
import argparse

class R**:

    def __init__(self, key_s):
        key = [ord(k) for k in key_s]

        S = 256*[0]
        for i in range(256):
            S[i] = i

        j = 0
        for i in range(256):
            j = (j + S[i] + key[i % len(key)]) % 256
            S[i], S[j] = S[j], S[i]

        self.S = S
        self.i = 0
        self.j = 0

    def prng(self):
        self.i = (self.i + 1) % 256
        self.j = (self.j + self.S[self.i]) % 256
        self.S[self.i], self.S[self.j] = self.S[self.j], self.S[self.i]
        K = self.S[(self.S[self.i] + self.S[self.j]) % 256]
        return K

    def encrypt(self, data):
        res = bytearray()
        for d in data:
            c = d ^ self.prng()
            res.append(c)
        return res

    def __str__(self):
        r = ""
        for i, s in enumerate(self.S):
            r += f"{i}: {hex(s)}\n"
        return r

def seeding(d, key):
    rc4 = R**(key)
    d = d.replace(hour=0, minute=0, second=0)
    timestamp = int((d - datetime(1970, 1, 1)).total_seconds())
    p = struct.pack("<I", timestamp)
    c = rc4.encrypt(p)
    seed = struct.unpack("<I", c)[0]
    return seed

def dga(seed, nr_of_domains):
    r = seed
    for i in range(nr_of_domains):
        domain = ""
        for j in range(20):
            letter = ord('a') + (r % 25)
            domain += chr(letter)
            r = seed ^ ((r + letter) & 0xFFFFFFFF)
        domain += ".com"
        print(domain)

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("-d", "--date", help="date when domains are generated")
    parser.add_argument("-r", "--rc4",
            help="rc4 key from config",
            choices=["q23Cud3xsNf3","41997b4a729e1a0175208305170752dd", "kZieCw23gffpe43Sd"],
            default="q23Cud3xsNf3")

    args = parser.parse_args()
    if args.date:
        d = datetime.strptime(args.date, "%Y-%m-%d")
    else:
        d = datetime.now()
    seed = seeding(d, args.rc4)
    dga(seed, 32)

其他样本

作为参考,再列出另外三个分析过的样本,这些样本又使用了两外两个种子值。可以在 GitHub 上找到预先计算好的所有三个种子生成的 DGA 域名。

md5种子域名
afdf2fbc0756ed304d1a33083a5f2b0fq23Cud3xsNf3域名列表
2169e871d4ca668d1872722d1a0695dcq23Cud3xsNf3域名列表
fa9b3dfdb4b97dfe0db5991472f8939941997b4a729e1a0175208305170752dd域名列表
306212efebc6ac92000687393e56a5cbkZieCw23gffpe43Sd域名列表

2169e871d4ca668d1872722d1a0695dc

属性
MD52169e871d4ca668d1872722d1a0695dc
SHA1add2bbbac042c328ed71c9fd2efcb9cbce5a89f7
SHA256cc87e6581ca91f941f65332b2de0e681d58491b54aff9d0b30afae828a5f5790
大小539 KB(552448 字节)
编译时间戳2020-04-14 11:20:46 UTC
链接MalwareBazaarURLhausVirusTotal
文件名称SecuriteInfo.com.Win32.GenKryptik.EILT.4491(MalwareBazaar),output.155861665.txt,Southput,Southput.DLL,znvmzdd.dll,ZnVmZdD.dll,april14.dll(VirusTotal)
检测结果Virustotal: 42/75 as of 2020-04-18 16:11:27

脱壳后:

属性
MD56a900d6f8af3a1a0e31ca5bb63637d03
SHA1221ab3d8ab16a0a7790026aab9b26904be6db436
SHA256e4d0a79d2463c5d3a71874e3389fa753f480b96639ad32baf1997baf8e5f714a
大小187 KB(191488 字节)
编译时间戳2020-04-08 18:20:42 UTC
链接MalwareBazaarMalpediaDropped_by_md5VirusTotal
检测结果Virustotal: 29/75 as of 2020-04-25 20:58:26

配置中使用 R** 密钥(edykepkrqahpyxabcwgm)进行加密,以下是硬编码域名:

http://wmwifbajxxbcxmucxmlc.com/post.php
http://ojnxjgfjlftfkkuxxiqd.com/post.php
http://pwkqhdgytsshkoibaake.com/post.php
http://snnmnkxdhflwgthqismb.com/post.php
http://iawfqecrwohcxnhwtofa.com/post.php
http://nlbmfsyplohyaicmxhum.com/post.php
http://fvqlkgedqjiqgapudkgq.com/post.php
http://cmmxhurildiigqghlryq.com/post.php
http://nmqsmbiabjdnuushksas.com/post.php
http://fyratyubvflktyyjiqgq.com/post.php

用于生成 DGA 种子的 R** 密钥为 q23Cud3xsNf3

fa9b3dfdb4b97dfe0db5991472f89399

属性
MD5fa9b3dfdb4b97dfe0db5991472f89399
SHA15677f26e926c8c8d7f7bf7eb085a9e48549a268b
SHA2563648fe001994cb9c0a6b510213c268a6bd4761a3a99f3abb2738bf84f06d11cf
大小512 KB(524288 字节)
编译时间戳2020-04-20 10:48:16 UTC
链接MalwareBazaarURLHausTwitterVirusTotal
文件名称f.dll (MalwareBazaar), Letter ease, Letter ease.DLL, f.dll (VirusTotal)
检测结果MalwareBazaar: ZLoader, Virustotal: 50/75 as of 2020-04-24 02:51:48

脱壳后:

属性
MD5133b1861b3590bf00308509227f82872
SHA1eb6f12759da7aa84077143e3e2694b6fda3d5631
SHA256dd11381223ab1902db2963df4cbe3299e42064a5857545560f913647c1f70c5a
大小187 KB(191488 字节)
编译时间戳2020-04-08 18:20:42 UTC
链接MalwareBazaar, Malpedia, Dropped_by_md5, VirusTotal
检测结果Virustotal: 29/74 as of 2020-04-25 21:00:11

配置中使用 R** 密钥(dqhfltvppmucpvebkqtn)进行加密,以下是硬编码域名:

https://dcaiqjgnbt.icu/wp-config.php
https://nmttxggtb.press/wp-config.php

用于生成 DGA 种子的 R** 密钥为 41997b4a729e1a0175208305170752dd

306212efebc6ac92000687393e56a5cb

属性
MD5306212efebc6ac92000687393e56a5cb
SHA1dc0b678e9ad7cadd5de907bf80fa351d5d3347cc
SHA2568d5a770975e52ce1048534372207336f6cc657b43887daa49994e63e8d7f6ce1
大小856 KB(877056 字节)
编译时间戳2020-04-05 16:19:02 UTC
链接MalwareBazaar, VirusTotal
文件名称JtVhjtbGMAbrWft.dll (MalwareBazaar), FfIYXQPKpCQymHQ.exe, PkRWAytIAsEHwhy.exe, qKMCMByhJjQpfmZ.exe, FmgJjYLZmscJaur.exe, gGwBVwnpxkyFNlc.exe, ZhbIdJYZzrkPQGs.exe, eIGmAdVpMFJxmrk.exe, VUCJyZshHrMGvdT.exe, WHFQhvaOzqkkTFk.exe, dFVlQGPNqrdhrCE.exe, tnXoUCMnjELKOYm.exe, dTEAUJnMdnADEVG.exe, omih.dll, ikhaapd.dll, 2020-04-07-ZLoader-DLL-binary.bin, etidwuv.dll, ekydn.dll, upiqwoq.dll, ryubn.dll, JtVhjtbGMAbrWft.exe, icobyg.dll, GnbjtDwFOsvocUW.exe, CbxfejTbfqXuuIT.exe, JtVhjtbGMAbrWft.bin (VirusTotal)
检测结果Virustotal: 58/75 as of 2020-04-20 00:40:47

脱壳后:

属性
MD54a74e2d34230bbc705f39e6943c859d3
SHA1410c1c03a52dbd56e78b0487ec532e68eb1c64e4
SHA25660544c6694620488b69e568b15c96b33971dd7343ba63da31f993332852871c2
大小172 KB(176640 字节)
编译时间戳2020-03-30 18:35:43 UTC
链接MalwareBazaar, Malpedia, Dropped_by_md5, VirusTotal
检测结果Virustotal: 34/75 as of 2020-04-25 20:59:59

配置中使用 R** 密钥(cbstobypqnbsnnpehdtb)进行加密,以下是硬编码域名:

https://knalc.com/sound.php
https://namilh.com/sound.php
https://ronswank.com/sound.php
https://stagolk.com/sound.php
https://mioniough.com/sound.php
https://ergensu.com/sound.php

用于生成 DGA 种子的 R** 密钥为 kZieCw23gffpe43Sd

参考来源

Johannesbader

*FB 小编 Avenger 编译,转载请注明来自 FreeBuf.COM

# 恶意软件 # zloader # 算法解析
本文为 Avenger 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
Avenger LV.7
这家伙太懒了,还未填写个人描述!
  • 532 文章数
  • 178 关注者
利用 CVE-2024-21412 进行窃密的攻击激增
2024-07-28
滥用云服务进行传播的恶意软件越来越多
2024-07-03
详解 RisePro 信息窃密木马
2024-07-03
文章目录