梦幻的彼岸 发表于 2021-6-1 11:36:21

[翻译]Melting Ice-用几个简单的步骤跟踪IcedID服务器

本帖最后由 梦幻的彼岸 于 2021-6-1 18:01 编辑

备注

[*]原帖地址:https://research.checkpoint.com/2021/melting-ice-tracking-icedid-servers-with-a-few-simple-steps/
[*]原帖标题:Melting Ice – Tracking IcedID Servers with a few simple steps
[*]原帖信息:May 26, 2021Research by: Alex Ilgayev

简介
追踪僵尸网络通常需要大量的努力、时间和威胁情报知识。如果是多阶段的复杂恶意软件家族,如IcedID、Emotet和QakBot,跟踪的难度就更大了。因此,作为恶意软件分析师,我们倾向于寻找尽可能多的自动化过程--收集大规模的样本,识别它们,提取它们的配置,然后让这些产生我们感兴趣的价值,如更清晰的威胁情报图片或更最新的域信誉引擎。
虽然恶意软件分析的生活往往像上面描述的那样困难,但它不一定是这样。有时,如果我们很聪明,10%的工作会得到我们感兴趣的90%的结果。在这篇文章中,我们展示了这样一个巧妙的方法,并展示了如何快速跟踪IcedID C&C服务器,并且不需要跟踪或分析任何样本。
IcedID背景
IcedID banker恶意软件最早出现于2017年9月,此后取得了重大进展。这种威胁,也被称为BokBot,在过去的一年中不断增长,并拥有广泛的恶意功能,如浏览器拦截,凭证盗窃,MiTM代理设置和VNC模块等。
Malwarebytes、Group-IB和Binary Defense过去的出版物对该僵尸程序的内部功能,包括感染链和恶意组件进行了彻底描述。
根据二进制防御研究人员提出的最新感染链,最近迭代的IcedID包含三个恶意组件--包含恶意宏的进入点DOC/XLS;一个第一阶段的payload;以及最后一个第二阶段的payload,它本身由两个子部分组成--一个64位DLL加载器,以及被伪装成".dat "文件的加密的实际僵尸程序。
每个第二阶段的payload通常包含2-4个独特的嵌入式域名,这些域名都可以解析到同一个IP地址。假设我们设定一个目标,跟踪这些域名/IP并阻止它们;自然,我们对最快速和无痛的方式感兴趣。
受害者的通信协议是HTTPS,所以我们检查了捆绑的TLS证书。正如我们在下面观察到的,IcedID的人更关心RSA-2048提供的硬性具体保证,而不太关心遵循安全准则;该证书签发给默认名称为“ Internet Widgits Pty,Ltd”的机构,该机构位于“某个州”。
IcedID C&C证书样本:

如果我们把这个IcedID证书和一个默认的自签名证书进行比较,最明显的区别是通用名称字段。公共服务器的FQDN(例如)应该在这个字段中出现,而不是像本例中的Checkpoint.comlocalhost那样。
你可能明白这是怎么回事;我们立即开始扫描广泛的网络,寻找其他呈现这种证书的服务器。
枚举服务器
我们不愿重复发明,选择使用流行的互联网扫描平台Censys来创建一份潜在的C&C候选人名单。像Shodan这样的其他网络扫描平台也可以用于此目的。
Censys引擎为我们提供了大量的证书查询控制。经过一些调整和调整,我们构造了以下查询:
443.https.tls.certificate.parsed.issuer_dn: "CN=localhost, C=AU, ST=Some-State, O=Internet Widgits Pty Ltd" and 443.https.tls.certificate.parsed.subject_dn: "CN=localhost, C=AU, ST=Some-State, O=Internet Widgits Pty Ltd"
Censys的证书结果之一:


如果您怀疑在证书中使用通用名称的唯一性,可以通过一个简短的实验轻松地证明这一点:我们搜索到的几十个服务器看起来似乎很多,但如果您在搜索查询中省略了要求,结果将在千分之一的大小和数量上爆炸式增长:localhostCN=localhost

目前,我们不能假设这些服务器中的每一个都是活动的IcedID恶意C&C,所以我们需要验证它们。幸运的是,这是可能的,这要归功于我们在研究过程中发现的另一种独特的特性。

验证服务器
当我们对僵尸程序的功能进行逆向工程时,我们注意到僵尸程序在与C&C服务器通信之前做了一个相当有趣的测试。下面的代码是一个回调函数的一部分,在与C&C.WinHttpSendRequest联系之前被调用。

证书验证代码:

简单地说,这段代码在证书的公钥上运行Fowler-Noll-Vo 32位哈希函数,并将其与证书的指定序列号进行比较。只有当比较结果吻合时,通信才会继续进行(或用XOR-ed值)。0x384A2414

证书序列号字段由证书颁发机构分配,并为每个生成的证书提供一个唯一的标识符。证书颁发者必须确保没有两个具有相同证书颁发者DN的不同证书包含相同的序列号,但没有人能够保证这一点。

在我们的案例中,证书是自签的,恶意软件运营商根据上述逻辑唯一地分配其C&C序列号字段。自己使用这种验证算法,针对一个可疑的服务器,我们可以确保它是IcedID基础设施的一部分。我们在附录A中提供了一个简单的python脚本来验证这个逻辑,针对一个提供的远程服务器。

应用该方法,我们发现之前的大多数潜在结果都与IcedID有关,并将名单缩小到52个服务器。考虑到独特的证书属性和不常见的哈希函数,我们可以安全地推断这些服务器是IcedID C&C基础设施中的组成部分。有了这个名单,我们去仔细查看了这些服务器。

服务器分析

通过分析他们面向互联网的标语,我们可以发现大多数部署的服务器有几个类似的属性。最常见的属性是开放端口、操作系统和网络服务器。

开放的端口 - 所有的服务器都有443端口,94%的服务器还监听80端口,77%的服务器监听SSH端口(22)。

操作系统 - 77%的服务器运行的是Debian操作系统。

网络服务器 - 94%的服务器运行nginx网络服务器。

我们还可以看到,大多数的服务器居住在罗马尼亚、美国和德国。

IcedID服务器的位置:

另一个分析角度是通过被动DNS服务,如RiskIQ。使用这种服务使我们能够为我们拥有的每个IP地址找到相关的域,并扩大我们的威胁情报图片。这些额外的域在逻辑上必须嵌入一些我们无法直接访问的样本中。例如,我们发现的一个地址是 ,它未解析到以下IcedID域:

152[.]89.247.60

formgotobig[.]top
ponduroviga[.]top
tranmigrust[.]club
aswenedo[.]space

总结
一个可悲的事实是,做 "正确的事情 "可能会适得其反。IcedID活动的维护者就是这种情况;他们制作了一个自签名的证书,并让他们的恶意操作支持HTTPS,这是一个值得称赞的努力,绝大多数恶意软件都不屑一顾,甚至一些长期运行的合法网站在很长一段时间内也不屑一顾。通过这样做,他们使煽动敌意接管他们的网络变得更具挑战性--但他们也使他们所有的服务器基本上以欢快的 "嗨,我是一个恶意的C&C "来回应标准扫描服务。

一旦这些服务器被暴露,我们就能持续追踪它们,分析它们的行为,而不需要运行正则表达式,更不用说启动调试器或反汇编器。对于运行恶意活动的人来说,这是一个噩梦般的场景;他们的目的是将不断更新的C&C服务器列表中的 "奖品 "放在遥不可及的地方,放在层级列表的顶端。相反,由于这种太过聪明的TLS业务,收集服务器(并获得一堆多汁的信息,这不在本文的范围之内)变得在分析师的能力范围之内,只要有一周的经验。正如一位智者曾经说过的,在使用一种技术之前要先思考,否则你的对手会用它来对付你。

IOC
IcedID C&C servers:
83[.]97.20.249
83[.]97.20.174
194[.]5.249.52
45[.]153.240.135
194[.]5.250.104
152[.]89.247.60
91[.]193.19.97
194[.]5.249.81
83[.]97.20.73
194[.]5.250.35
83[.]97.20.176
194[.]5.250.46
212[.]114.52.186
91[.]193.19.37
45[.]147.231.113
188[.]119.148.75
185[.]38.185.90
45[.]138.172.179
91[.]193.19.51
83[.]97.20.122
194[.]5.249.103
139[.]60.161.63
194[.]5.249.86
139[.]60.161.48
185[.]33.85.35
194[.]5.249.97
79[.]141.164.241
194[.]5.249.90
193[.]109.69.52
194[.]5.249.54
185[.]70.184.87
79[.]141.166.39
146[.]0.77.92
31[.]184.199.11
45[.]129.99.241
194[.]5.249.46
185[.]123.53.202
146[.]0.77.18
31[.]24.228.170
45[.]147.230.88
83[.]97.20.254
45[.]147.230.82
194[.]5.249.72
46[.]17.98.191
45[.]153.241.115
185[.]70.184.41
139[.]60.161.50
194[.]5.249.143
79[.]141.161.176
5[.]149.252.179
45[.]147.228.198
91[.]193.19.251
附录A
测试一个服务器的IcedID证书:
import idna

from socket import socket
from OpenSSL import SSL
from cryptography.hazmat.primitives.serialization import Encoding
from cryptography.hazmat.primitives.serialization import PublicFormat

def fnv1a_32(data: bytes) -> int:
    """Fowler–Noll–Vo hash function variation.

    Args:
      data (bytes): Input data

    Returns:
      int: Output 32 bit hash
    """
    hval_init = 0x811c9dc5
    fnv_prime = 0x01000193
    fnv_size = 2 ** 32

    hval = hval_init
    for byte in data:
      hval = hval ^ byte
      hval = (hval * fnv_prime) % fnv_size
    return hval

def get_certificate(hostname: str, port: int):
    """Connects to the remote server,
    and retrieves the certificate.

    Args:
      hostname (str): Remote hostname
      port (int): Remote port (usually 443)

    Returns:
      Certificate object
    """
    hostname_idna = idna.encode(hostname)

    # We are building a SSL context on top of a raw socket.
    sock = socket()
    sock.connect((hostname, port))
    ctx = SSL.Context(SSL.SSLv23_METHOD)
    ctx.check_hostname = False
    # the cert is self-signed so we don't want to verify it
    ctx.verify_mode = SSL.VERIFY_NONE

    # making SSL handshake
    sock_ssl = SSL.Connection(ctx, sock)
    sock_ssl.set_connect_state()
    sock_ssl.set_tlsext_host_name(hostname_idna)
    sock_ssl.do_handshake()

    # retrieving certificate and converting it to an cryptography object
    cert = sock_ssl.get_peer_certificate()
    crypto_cert = cert.to_cryptography()
    sock_ssl.close()
    sock.close()

    return crypto_cert

def test_is_icedid_c2(hostname: str, port: int) -> bool:
    """Testing whether a remote server is part of IcedID
    C&C infrastructure.

    Args:
      hostname (str): Remote hostname
      port (int): Remote port (usually 443)

    Returns:
      bool: True if the server is IcedID verified, or False otherwise.
    """
    try:
      # We query the server and retrieve its certificate.
      cert = get_certificate(hostname, port)
      serial_number = cert.serial_number

      # Getting the public key, and hashing it.
      public_key = cert.public_key().public_bytes(Encoding.DER, PublicFormat.PKCS1)
      fnv_hash = fnv1a_32(public_key) & 0x7fffffff

      # Finally comparing the hash to the serial number.
      if serial_number == fnv_hash or serial_number == fnv_hash ^ 0x384A2414:
            return True
    except Exception as e:
      return False
    return False
页: [1]
查看完整版本: [翻译]Melting Ice-用几个简单的步骤跟踪IcedID服务器