DDG的新征程——自研P2P协议构建混合P2P网络
<h2 id="1-"><strong>1. 概述</strong></h2><p>DDG Mining Botnet 是一个活跃已久的挖矿僵尸网络，其主要的盈利方式是挖 XMR。从 2019.11 月份至今，我们的 Botnet 跟踪系统监控到 DDG Mining Botnet 一直在频繁跟新，其版本号和对应的更新时间如下图所示：</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="__GHOST_URL__/content/images/2020/04/ddg_version_timeline-1.png" class="kg-image" alt loading="lazy"><figcaption>DDG Version Timeline</figcaption></figure><p>其中，v4005~v4011 版本最主要的更新是把以前以 Hex 形式硬编码进样本的 HubList 数据，改成了 <a href="https://jiayu0x.com/2019/11/19/decode-gob-data-in-ddg-v4005/">Gob 序列化</a>的方式；v5009 及以后的版本，则摒弃了以前<a href="__GHOST_URL__/dog-recent-updates-p2p-adopted-and-anti-analysis-enhanced/">基于 <strong>Memberlist</strong> 来构建 P2P 网络</a>的方式，改用自研的 P2P 协议来构建<a href="https://en.wikipedia.org/wiki/Peer-to-peer#Hybrid_models">混合模式 P2P 网络</a> 。简化的网络结构如下：</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="__GHOST_URL__/content/images/2020/04/hybrid_p2p_network-2.png" class="kg-image" alt loading="lazy"><figcaption>P2P Hybrid Model</figcaption></figure><p>右边服务器是 C&amp;C Server，DDG 中称它为 <strong>xhub</strong> 节点，是一个超级节点，除了与各 Bot 相同的 P2P 通信功能之外，还具有以下功能：</p><ul><li>统计各 Peer 信息( Peer 会向它注册，上传自身信息)</li><li>协助各 Peer 寻找到对方，xhub 节点里保存了大量的 P2P Peers 列表，它会在 Bot 向其注册的时候把一部分列表分享给 Bot；而每个 Bot 最多保存 200 条 Peers <code>&lt;ip:port&gt;</code> 列表；</li><li>承载原始的恶意 Shell 脚本、主样本和其他组件的下载服务。</li></ul><p>我们的 Botnet 跟踪系统追踪到 DDG 当前的 <strong>一部分</strong> P2P Nodes(失陷主机)，最近每天平均可以追踪到 900 个 Nodes，其中可验证存活的比例达 98%:</p><figure class="kg-card kg-image-card"><img src="__GHOST_URL__/content/images/2020/04/data_statics-1.png" class="kg-image" alt loading="lazy"></figure><p>根据我们合作伙伴提供的统计数字，中国大陆境内活跃的 Bot 有17,590 个。根据我们的追踪数据，中国大陆境内的 Bot 数量约占总量的 86%，以此反推，DDG 目前在全球范围内的 Bot 数量约 20,000。</p><p>DDG 支持的传播途径，有以下 4 个：</p><ul><li>SSH 服务暴破；</li><li>Redis 未授权访问漏洞；</li><li>针对 Nexus Repository Manager 3 RCE 漏洞(CVE-2019-7238)的利用；</li><li>针对 Supervisord RCE 漏洞(CVE-2017-11610)的利用。</li></ul><p>根据我们的追踪数据，DDG 最近一段时间一直没有开启公网传播，而只利用 SSH 暴破在内网传播。其内网传播的开关，在 <strong><a href="https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/data/slave.json">slave config</a></strong> 里设置：</p><figure class="kg-card kg-image-card"><img src="__GHOST_URL__/content/images/2020/04/slave_conf_aassh.png" class="kg-image" alt loading="lazy"></figure><p>另外，根据我们对 DDG 过去一年的追踪统计，它从 2019.1 月份至今共用了 24 个 C&amp;C，每个 CC 的活跃时段统计如下：</p><figure class="kg-card kg-image-card"><img src="__GHOST_URL__/content/images/2020/04/cc_history_light.png" class="kg-image" alt loading="lazy"></figure><p>最后，旧版本的 DDG Botnet Tracker 已经无法工作，我们现已将其开源：</p><p><strong><a href="https://github.com/0xjiayu/DDGBotnetTracker/tree/master/v1">https://github.com/0xjiayu/DDGBotnetTracker/tree/master/v1</a></strong></p><p>并且公开最新版本 DDG 相关的运行日志、相关数据和相应解析工具，以及一个基于 P2P 机制的 Pre-Built Demo Tracker Program(ELF Binary file):</p><p><strong><a href="https://github.com/0xjiayu/DDGBotnetTracker/tree/master/v2">https://github.com/0xjiayu/DDGBotnetTracker/tree/master/v2</a></strong></p><h2 id="2-"><strong>2. 样本关键行为分析</strong></h2><p>就 DDG 当前最新的 v5023 版本来看，DDG 有一些有趣的新特性：</p><ul><li>用特定算法生成一个 4 字符的目录名，并在 <code>/var/lib/</code> 或 <code>/usr/local/</code> 下创建相应的<strong>隐藏目录</strong>作为工作目录，存放本地配置信息和从 C&amp;C 或 P2P Peer 下载到的文件或数据；</li><li>自定义 Base64 编码的编码表(Alphabet)；</li><li>对关键数据和文件频繁使用编码(Base64/Adler32)、加密(AES)、压缩(Gzip)和数字签名(RSA/ED25519)手段，以对抗分析和数据伪造；</li><li>除了以前的 <strong>slave</strong> Config 来控制 Bot 执行挖矿和传播任务，又新增了一个 <strong>jobs</strong> Config 文件来对 Bot 进行更复杂的任务控制；</li><li>自研 P2P 协议，并以此在 Nodes 之间交换最新各自持有的 C&amp;C、Nodes 列表和恶意任务指令配置等信息，还可以在 Peers 之间传播恶意组件。</li></ul><p>以 DDG v5023 版本的样本为例，其主要的执行流程，从恶意 Shell 脚本文件 <strong>i.sh</strong> 开始，如下所示：</p><figure class="kg-card kg-image-card"><img src="__GHOST_URL__/content/images/2020/04/work_flow-1.png" class="kg-image" alt loading="lazy"></figure><h3 id="2-1-shell-i-sh"><strong>2.1 恶意 Shell 脚本 i.sh</strong></h3><p>DDG 每成功入侵一台主机，就会先执行这个 <strong>i.sh</strong> 脚本。 <strong>i.sh</strong> 主要执行 3 个任务：</p><ol><li>篡改 Cron job，在 <code>/var/spool/cron/root</code> 和 <code>/var/spool/cron/crontabs/root</code> 写入恶意 Cron job，每 15 分钟下载 <code>hxxp://67.205.168.20:8000/i.sh</code> 并运行；</li><li>下载并执行最新的 DDG 主样本文件；</li><li>检测目录 <code>/usr/local/bin/</code> | <code>/usr/libexec/</code> | <code>/usr/bin/</code> 是否可写。</li></ol><h3 id="2-2-"><strong>2.2 初始化</strong></h3><p>DDG 的主样本由 Go 语言编写，编译出来的原始 ELF 文件体积比较大。DDG 的作者就用 UPX Packer 把原始 ELF 文件加壳，一方面一定程度上可以对抗自动化分析，另一方面缩小了文件体积，便于网络传输。</p><p>DDG 的主样本里实现了多个工作模块，有一些后续用到的数据和全局变量，涉及相应的初始化工作。比如生成全局自定义 Alphabet 的 Base64 编解码句柄，解析样本内硬编码的 <strong>xhub</strong>(C&amp;C) 相关数据和 <strong>xSeeds</strong>(P2P Seed Nodes List) 数据，解析样本内硬编码的弱口令字典(用于后续的 SSH 服务暴破)等等。</p><h4 id="2-2-1-alphabet-base64-"><strong>2.2.1 自定义 Alphabet 的 Base64 编码</strong></h4><p>在函数 <code>ddgs_common_init</code> 中，DDG 基于自定义的 Alphabet 创建了一个全局的 Base64 编解码句柄：</p><figure class="kg-card kg-image-card"><img src="__GHOST_URL__/content/images/2020/04/b64Enc-1.png" class="kg-image" alt loading="lazy"></figure><p>自定义的 Alphabet 为：</p><p><strong>"eDy54SH1N2s-Y7g3qnurvaTW_0BlCMJhfb6wtdGUcROXPAV9KEzIpFoi8xLjkmZQ"</strong></p><p>生成的全局 Base64 编解码句柄，会在后续被用来：</p><ul><li>解析内置硬编码的 <strong>xhub</strong> 和 <strong>xSeeds</strong> 数据；</li><li>解析后续从服务器下载到的文件 Sig、与 P2P Nodes 通信时的自定义 HTTP Header(<strong>X-Hub</strong> / <strong>X-Seed</strong> / <strong>X-Sig</strong>)</li><li>响应其他 P2P Peer 的请求时，编码自定义 HTTP Header(<strong>X-Hub</strong> / <strong>X-Seed</strong> / <strong>X-Sig</strong>)</li></ul><h4 id="2-2-2-xhub-xseeds"><strong>2.2.2 解析内置 xhub/xSeeds</strong></h4><p>在 DDG 中，<strong>xSeeds</strong> 就是 P2P Seed Nodes 列表，每个 Bot 都持有一份 Seed Nodes List，里面内置了 200 个 P2P Node <code>&lt;ip:port&gt;</code> 列表。Bot 可以与他们通信交换数据。</p><p><strong>xhub</strong> 即 C&amp;C 服务器信息，C&amp;C 服务器可以指定多个。</p><p>在函数 <code>ddgs_xnet_init_0</code> 中，DDG 解析了内置的 <strong>xhub</strong> 和 <strong>xSeeds</strong> 数据，还对 <strong>xhub</strong> 数据用 <strong>ed25519</strong> 算法校验是否被伪造。<strong>xhub</strong> 的解析、验证汇编代码如下：</p><figure class="kg-card kg-image-card"><img src="__GHOST_URL__/content/images/2020/04/xsig_verify-1.png" class="kg-image" alt loading="lazy"></figure><p>DDG v5023 样本中内置的 <strong>xhub</strong> 数据为经过自定义 Alphabet Base64 编码过的字串：</p><pre><code>fOSIE4y3ZPcTuT7weiMSSr7-0-Vem5IfTxEbUirWGS9j5NsDJh2k54RsnK08lG-ECaHQ4ARiWy3mJs0O9HzBpP6iANY7cTHnPw_i-wNK7u8E7wfVYweLg5eKYe
</code></pre><p>经过 Base64 解码后，还需要用 <a href="http://msgpack.org/">msgPack</a> 进行一层解码，才能得到 <strong>xhub</strong> 的原始明文数据。解析上面的编码数据，可以得到两个关键数据：</p><ul><li>C&amp;C 列表，目前只有一个：<strong>67.205.168.20:8000</strong>；</li><li>ed25519 Signature：</li></ul><pre><code>0x8f, 0xfa, 0xca, 0x16, 0x49, 0x63, 0x63, 0x03, 0x77, 0x45, 0x15, 0x33, 0x4b, 0x64, 0xbb, 0x80,
0xf4, 0x3c, 0xe0, 0x5b, 0x9c, 0x61, 0x9f, 0x74, 0xd7, 0x98, 0x5b, 0xfb, 0x0c, 0x82, 0x81, 0x79,
0xf2, 0x7c, 0x0c, 0x4a, 0x4a, 0x47, 0x06, 0x78, 0x6e, 0x62, 0xf1, 0x71, 0x51, 0xbf, 0x12, 0xda,
0x77, 0x5c, 0x23, 0xfd, 0x78, 0xa6, 0x6a, 0xbc, 0x6c, 0x9a, 0xd2, 0xc8, 0xb7, 0xb4, 0x83, 0x0d</code></pre><p>样本中解出 C&amp;C 列表和 ed25519 的 Signature 之后，就会用相反的步骤用 ed25519 算法来校验 C&amp;C 列表是否被伪造。校验时用到的 RSA 公钥为：</p><pre><code>0x20, 0x0A, 0x51, 0x81, 0x91, 0xE9, 0xF2, 0x54,
0x78, 0xFC, 0x1E, 0x66, 0x7B, 0x8F, 0x8D, 0xAC,
0xCF, 0x62, 0x28, 0x18, 0x46, 0xEC, 0x45, 0x7C,
0xF5, 0xC3, 0xBA, 0x4C, 0x86, 0xB0, 0xB5, 0x41
</code></pre><p><strong>xhub</strong> 数据的解析方法：<br><a href="https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/tools/xsig_verify.go"><strong>https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/tools/xsig_verify.go</strong></a></p><p><strong>xSeeds</strong> 包含 200 条 P2P Node <code>&lt;ip:port&gt;</code> 列表，数据量比较大，共 0x8E2 Bytes：</p><figure class="kg-card kg-image-card"><img src="__GHOST_URL__/content/images/2020/04/xseeds_raw-1.png" class="kg-image" alt loading="lazy"></figure><p>而且 DDG 样本对 xSeeds 解析步骤与 xhub 的解析略有不同，经过分析，xSeeds 经过以下 3 层处理，而不用 ed25519 校验：</p><ol><li>msgpack 序列化编码；</li><li>gzip 压缩</li><li>自定义 Alphabet 的 Base64 编码</li></ol><p><strong>xSeeds</strong> 数据的解析方法：</p><p><a href="https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/tools/dec_seeds.go"><strong>https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/tools/dec_seeds.go</strong></a></p><h4 id="2-2-3-"><strong>2.2.3 解析内置弱口令字典</strong></h4><p>在全局初始化函数 <code>ddgs_global_init</code> 中，DDG 调用了一个函数 <code>ddgs_global_decodePasswords</code> ，在这个函数中解密并校验内置的弱口令字典，这些弱口令将在后续传播阶段被用来暴破 SSH 服务(暴破 SSH 服务时用的用户名为 <strong>root</strong> )：</p><figure class="kg-card kg-image-card"><img src="__GHOST_URL__/content/images/2020/04/decPasswd-1.png" class="kg-image" alt loading="lazy"></figure><p>弱口令数据是经过 <strong>AES</strong> 加密、gzip 压缩后内置在样本中的，密文数据共 0x2BCFE Bytes：</p><figure class="kg-card kg-image-card"><img src="__GHOST_URL__/content/images/2020/04/passwds_raw-1.png" class="kg-image" alt loading="lazy"></figure><p>DDG 会首先对上面数据用 gzip 解压，解压后密文数据结构如下：</p><figure class="kg-card kg-image-card"><img src="__GHOST_URL__/content/images/2020/04/passwds_cipherdata-1.png" class="kg-image" alt loading="lazy"></figure><p>DDG 会先用 ed25519 对上述密文数据的 Sha256 值进行校验(公钥与前面校验 <strong>xhub</strong> 时用的公钥相同)，校验成功之后才会用 AES 算法将其解密，解密后得到一份 <strong>17,907</strong> 条密码的弱口令字典:</p><figure class="kg-card kg-image-card"><img src="__GHOST_URL__/content/images/2020/04/passwd_list-1.png" class="kg-image" alt loading="lazy"></figure><h4 id="2-2-4-ed25519-"><strong>2.2.4 创建全局 ed25519 密钥对</strong></h4><p>在函数 <strong>ddgs_global_init</strong> 中，DDG 还有另外一项关键全局变量的初始化工作：创建一对全局 ed25519 密钥，用以在后续存取 BoltDB 中数据时对数据进行签名和校验：</p><figure class="kg-card kg-image-card"><img src="__GHOST_URL__/content/images/2020/04/init_global_edKeys-1.png" class="kg-image" alt loading="lazy"></figure><p>创建密钥对的种子如下：</p><pre><code>0x5C, 0x9E, 0xAE, 0xAE, 0x43, 0x26, 0xB7, 0xA2,
0x52, 0xDC, 0x43, 0xF9, 0xBD, 0x3F, 0xD1, 0xA6,
0xC8, 0xB0, 0x28, 0xE1, 0xDF, 0xA8, 0xB0, 0xF5,
0xCF, 0x43, 0xE7, 0x82, 0xD1, 0x90, 0x11, 0x6B
</code></pre><h3 id="2-3-"><strong>2.3 创建工作目录</strong></h3><p>旧版本的 DDG 会直接把当前用户的 HOME 目录作为自己的工作目录，主要是在此目录下创建隐藏的 <a href="https://github.com/boltdb/bolt"><strong>BoltDB</strong></a> 文件，文件中存放 Hublist 数据(旧版本的 P2P Node List)。现在新版本的 DDG 会用特定算法生成目录名，并在 <code>/var/lib/</code> 或 <code>/usr/local/</code> 目录下创建相应的<strong>隐藏目录</strong>。Go 语言实现的工作目录名生成算法如下(假设当前 DDG 二进制样本文件的路径为 <code>/root/ddgs_x64</code>)：</p><p><a href="https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/tools/gen_workdir_name.go"><strong>https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/tools/gen_workdir_name.go</strong></a></p><p>上面程序的执行结果是 <strong>jsfc</strong> ，那么 DDG 就会尝试创建目录 <code>/var/lib/.jsfc</code>，后续工作目录的结构如下：</p><pre><code>/var/lib/.jsfc
├── 5023
│  └── cache
│     └── static
│        ├── bb3
│        │  ├── busybox.x86_64
│        │  └── busybox.x86_64.sig
│        ├── wordpress
│        └── wordpress.sig
└── .local
</code></pre><p>其中 <code>.local</code> 文件即为 BoltDB 格式的文件，其它的还有从 C&amp;C 服务器上下载到的恶意挖矿程序(<strong>wordpress</strong>) 、编译好的 Busybox ELF 文件以及它们相应的数字签名。</p><h3 id="2-4-boltdb-"><strong>2.4 BoltDB 文件</strong></h3><p><a href="https://github.com/boltdb/bolt"><strong>BoltDB</strong></a> 是一个基于内存的小型 KV 数据库，其数据内容可以落地到磁盘文件。DDG 旧版本中用 BoltDB 来存放明文 Hublist 数据。在 DDG 最新的 BoltDB 数据库中，有一个 <strong>Bucket</strong> 名为 <strong>xproxy</strong>。<strong>xproxy</strong> 里存放了 3 份数据：</p><ol><li><strong>hubsig</strong>: xhub 信息；</li><li><strong>seeds</strong>: xSeeds 数据；</li><li><strong>port</strong>: 本地在 (30000~65535) 范围内随机监听的 TCP 端口，用来相应其他 P2P Nodes 的通信请求。</li></ol><p>DDG 对这 3 份数据，每一份都做了如前文内置弱口令字典数据同样的处理，经过 msgpack 序列化编码后再用 AES 加密，重组加密数据后再存入 BoltDB。重组的密文数据结构也与弱口令字典密文数据结构相同:</p><figure class="kg-card kg-image-card"><img src="__GHOST_URL__/content/images/2020/04/cipherdata_struct-1.png" class="kg-image" alt loading="lazy"></figure><p>不同的是，DDG 对 BoltDB 中数据的签名和校验用到的全局密钥，是函数 <strong>ddgs_global_init</strong> 用样本中硬编码的种子数据生成的。</p><h3 id="2-5-"><strong><strong>2.5 监控关键文件/目录</strong></strong></h3><p>DDG 利用 <a href="https://github.com/fsnotify/fsnotify"><strong>fsnotify</strong></a> 框架监控以下文件，在运行期间防止被别的进程改动，用以保护自己的持久化配置：</p><ul><li><code>/root/.ssh/authorized_keys</code></li><li><code>/var/spool/cron/root</code></li><li><code>/var/spool/cron/crontabs/root</code></li></ul><figure class="kg-card kg-image-card"><img src="__GHOST_URL__/content/images/2020/04/monitor_fs-1.png" class="kg-image" alt loading="lazy"></figure><h2 id="3-p2p-"><strong>3. P2P 机制</strong></h2><p>DDG 执行完上述关键步骤，DDG Bot 就开始了与其他 Bot 之间的 P2P 通信。DDG 的 P2P 机制特性如下：</p><ul><li>Peers 首先会向 xhub 注册</li><li>Peers 之间有特有的 Ping/Pong 机制</li><li>Peers 之间可以共享各自持有的 C&amp;C 和 Peers 列表数据；</li><li>Peers 之间可以传播 <strong>slave</strong> config 和 <strong>jobs</strong> config 数据；</li><li>Peers 之间可以传播其他 Payload 或组件，比如恶意挖矿程序、已编译的 Busybox 二进制文件</li></ul><h3 id="3-1-"><strong>3.1 准备工作</strong></h3><p>在进行 P2P 通信之前，DDG 会有两项关键的准备工作：</p><ul><li>生成一个随机域名</li><li>生成自己的 Peer UID</li></ul><p>随机域名的的生成规则是：<code>&lt;RAND 3 LowChars&gt;.&lt;RAND [5-7] Lowchars&gt;.com</code>， 如 <code>kez.tirueg.com</code> 。</p><p>UID 的形式： <code>VERSION_NUM.UID_HASH</code> ，如 <code>5023.dd9b2f57af3be6b6276606d4f37e4a5b</code> 。</p><p><strong>UID_HASH</strong> 的生成规则，则是综合计算 Host information 和 网卡信息的 MD5 值得出， Go 语言实现如下：</p><p><a href="https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v1/lib/util.go#L40">https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v1/lib/util.go#L40</a></p><h3 id="3-2-peer-xhub"><strong>3.2 Peer &lt;--&gt; xhub</strong></h3><p>DDG 会首先向 xhub 如下 HTTP POST 请求：</p><figure class="kg-card kg-image-card"><img src="__GHOST_URL__/content/images/2020/04/xhub_jobs-1.png" class="kg-image" alt loading="lazy"></figure><p>以上 HTTP 通信中，</p><ul><li>Request Header <strong>Host</strong>，即为事先随即生成的域名。值得一提的是，<strong>这个域名不会被 DNS 服务解析，因为以上 HTTP 通信是复用了已建立的 TCP Socket Session，在已有 TCP Socket 连接上 Send/Recv 数据并把数据用 HTTP 协议来解析而已</strong>。攻击者这样做，可能是为了逃避或混淆某些安全设备的流量检测；</li><li>Request Header <strong>X-Hub</strong>，即为 DDG 样本当前持有的硬编码在样本中的 xhub 信息，详见 2.2.2 节；</li><li>Request Header <strong>X-Port</strong>，即为 DDG 样本当前随即开启并监听的 P2P 通信端口；</li><li>Request Header <strong>X-Uid</strong>，即事先生成的 UID；</li><li>Request Header <strong>X-Relay</strong>，是 DDG 综合 <strong>X-Uid</strong> 和 <strong>X-Port</strong> 字段的值通过 <strong>Adler</strong> 算法算出来的校验值；</li><li>Response Header <strong>X-Seed</strong>，是对方从自己持有的 Peers 列表中随机选取的 20 个 Peers 地址列表信息，DDG 样本收到后会合入自身持有的 200 个 Peers 列表，总数不超过 200；</li><li>Response Header <strong>X-Hub</strong>，是对方持有的 <strong>xhub</strong> 信息，DDG 样本收到后会用它替换掉自身持有的 <strong>xhub</strong> 信息。</li></ul><p>最后，HTTP 响应中的 <strong>jobs</strong> config 数据，是经过 msgpack 序列化编码后又用 AES 加密过的数据，数据的组织结构与 gzip 解压后的样本内置弱口令字典数据相同，解析过程也完全相同。最新解密后的 <strong>jobs</strong> config 数据见：</p><p><strong><a href="https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/data/jobs.json">https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/data/jobs.json</a></strong></p><p>从 <strong>jobs</strong> Config 的内容来看，攻击者对 Bot 的行为控制的更为复杂精细，该配置文件的核心功能在流程图里已有说明，此处总结如下：</p><ul><li>针对每个低级版本 DDG 都有不同的处理，或 Kill 或 Discard 或 Upgrade；</li><li>引入了 Busybox 执行更复杂的命令，主要用来干掉竞争对手；</li><li>干掉竞争对手的姿势复杂多样，杀进程、禁用服务、清除 SSH Key、删除 Cron Jobs，重置 Lock File 等等，最显眼的是通过篡改 <code>/etc/hosts</code> 文件屏蔽一大批竞争对手需要访问的域名。</li></ul><p>其中，最显眼的一部分配置，是 DDG 通过篡改 <code>/etc/hosts</code> 文件屏蔽竞争对手要访问的域名，大多数都是 <a href="https://twitter.com/hashtag/LSDMiner"><strong>LSDMiner</strong></a> 和 <a href="https://twitter.com/hashtag/systemdMiner"><strong>systemdMiner</strong></a> 相关的域名：</p><pre><code>LSDMiner	img.sobot.com
LSDMiner	lsdu.b-cdn.net
LSDMiner	thyrsi.com
LSDMiner	aliyun.one
systemdMiner	an7kmd2wp4xo7hpr.onion.sh
systemdMiner	an7kmd2wp4xo7hpr.tor2web.su
systemdMiner	aptgetgxqs3secda.onion.ly
systemdMiner	aptgetgxqs3secda.onion.pet
systemdMiner	dns.rubyfish.cn
systemdMiner	aptgetgxqs3secda.onion.in.net
systemdMiner	aptgetgxqs3secda.tor2web.fyi
systemdMiner    an7kmd2wp4xo7hpr.d2web.org
systemdMiner    an7kmd2wp4xo7hpr.timesync.su
</code></pre><h3 id="3-3-peer-peer"><strong>3.3 Peer &lt;--&gt; Peer</strong></h3><p>在与 <strong>xhub</strong> 通信过后，DDG 样本就开始与自身持有的 200 个 Peers 进行通信。 Peers 之间的通信有 4 个关键步骤：</p><ol><li>Ping/Pong</li><li>拉取对方的 <strong>slave</strong> config 和 <strong>jobs</strong> config；</li><li>拉取对方的其他恶意组件，比如恶意矿机程序和 Busybox 二进制程序；</li><li>响应(服务)别的 Peers 的以上 3 种请求</li></ol><h4 id="3-3-1-ping-pong"><strong>3.3.1 Ping/Pong</strong></h4><p>DDG 一旦成功与某个 Peer 建立连接，会至少经过 2 轮 Ping/Pong 通信，间隔 <strong>30s</strong> ，对端响应的 Pong 包与 Ping 包完全相同，长度为 <strong>3 Bytes</strong>：</p><figure class="kg-card kg-image-card"><img src="__GHOST_URL__/content/images/2020/04/peer_pingpong-1.png" class="kg-image" alt loading="lazy"></figure><p>3 Bytes 的 Ping 包生成规则如下：</p><ul><li>第 1 字节为 0x00；</li><li>第 3 字节在 (0, 0xFF] 中随机产生；</li><li>第 2 字节为第 3 字节与 <strong>0x42</strong> XOR 运算的结果。</li></ul><p>Go 语言代码描述如下：</p><pre><code>// Generate ping packet bytes
func GenPingbytes(globalRand *rand.Rand) []byte {
    pkt := make([]byte, 3)
    pkt[0] = 0x00
    pkt[2] = byte((globalRand.Intn(0xFE) + 1) &amp; 0xFF)
    pkt[1] = (pkt[2] ^ 0x42) &amp; 0xFF

    return pkt
}</code></pre><h4 id="3-3-2-slave-jobs-config-data"><strong>3.3.2 传播 slave/jobs config data</strong></h4><p>经过 2 轮的 Ping/Pong 通信，DDG 会随机选成功通信的 Peers 向对方请求拉取 <strong>slave</strong> config 或 <strong>jobs</strong> config，请求方式类似于上面向 <strong>xhub</strong> 请求 <strong>jobs</strong> config，以拉取对方 <strong>slave</strong> config 为例：</p><figure class="kg-card kg-image-card"><img src="__GHOST_URL__/content/images/2020/04/request_slave-1.png" class="kg-image" alt loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="__GHOST_URL__/content/images/2020/04/slave_resp-1.png" class="kg-image" alt loading="lazy"></figure><p>值得注意的两点：</p><ol><li>DDG Peers 之间发送 HTTP Post 请求，HTTP Request Header 中相比请求 <strong>xhub</strong> 时少了一个 <strong>X-Port</strong>。</li><li>发往 Peer 的 HTTP 请求，<strong>复用了前面 Ping/Pong 通信中用到的 TCP 连接</strong>，即同一个 TCP Session，刚开始用来 Ping/Pong 交互，后面直接基于这个 TCP Session 发送 HTTP 请求、接收 HTTP 响应，所以 HTTP Header 中 <strong>Host</strong> 字段里<strong>随机生成的域名并不会经过 DNS 解析</strong>。</li></ol><p>对端响应的 <strong>slave</strong> config 数据，是经 msgpack 序列化编码过的二进制数据，数据格式与旧版本变化不大，解码方式可以参考 <a href="https://jiayu0x.com/2019/04/11/track-ddg-botnet-by-p2p-protocol/#2-3-ddg-%E7%9A%84%E4%BA%91%E7%AB%AF%E9%85%8D%E7%BD%AE%E6%95%B0%E6%8D%AE"><strong>以 P2P 的方式追踪 DDG</strong></a> 。最新解码后的 <strong>slave</strong> config 数据已上传到 Github：</p><p><strong><a href="https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/data/slave.json">https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/data/slave.json</a></strong></p><p>解码后的 <strong>slave</strong> config 数据包含自身数据的数字签名，DDG 样本会以此校验 <strong>slave</strong> config 数据，校验时用到的 RSA 公钥硬编码在样本中：</p><pre><code>-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1+/izrOGcigBPC+oXnr2
S3JI76iXxqn7e9ONAmNS+m5nLQx2g0GW44TFtMHhDp1lPdUIui1b1odu36i7Cf0g
31vdYi1i6nGfXiI7UkHMsLVkGkxEknNL1c1vv0qE1b2i2o4TlcXHKHWOPu4xrpYY
m3Fqjni0n5+cQ8IIcVyjkX7ON0U1n8pQKRWOvrsPhO6tvJnLckK0P1ycGOcgNiBm
sdA5WDjw3sg4xWCQ9EEpMeB0H1UF/nv7AZQ0etncMxhiWoBxamuPWY/KS3wZStUd
gsMBOAOOpnbxL9N+II7uquQQkMmO6HriXRmjw14OmSBEoEcFMWF2j/0HPVasOcx2
xQIDAQAB
-----END PUBLIC KEY-----
</code></pre><h4 id="3-3-3-"><strong>3.3.3 下载恶意组件</strong></h4><p>没有被选中拉取 <strong>slave</strong> config 和 <strong>jobs</strong> config 的 Peer，DDG 会继续与他们的 Ping/Pong 通信。从某个 Peer 拉取的 <strong>slave</strong> config 中指定了恶意矿机程序 Miner 的下载 URI、本地存放路径和 MD5，DDG 接下来会随机选取另一个 Ping/Pong 中的 Peer，<strong>复用 Ping/Pong 的 TCP Session</strong>，通过 HTTP 协议向其发起 Miner 的下载请求：</p><figure class="kg-card kg-image-card"><img src="__GHOST_URL__/content/images/2020/04/download_miner-1.png" class="kg-image" alt loading="lazy"></figure><p>下载到的恶意矿机程序，DDG 不仅会将其存放到 <strong>slave</strong> config 中指定的本地路径，还会连同 HTTP Response Header 中的 <strong>X-Sig</strong> 内容作为矿机程序的数字签名数据一起放到自己创建的<strong>工作目录</strong>中：</p><pre><code>/var/lib/.jsfc
├── 5023
│  └── cache
│     └── static
│        ├── bb3
│        │  ├── busybox.x86_64
│        │  └── busybox.x86_64.sig
│        ├── wordpress
│        └── wordpress.sig
└── .local
</code></pre><p>DDG 从 Peer 下载已编译的 Busybox 程序的过程同上。</p><h4 id="3-3-4-peers-"><strong>3.3.4 响应其他 Peers 的请求</strong></h4><p>P2P 协议中，Peers 之间的功能、角色是对等的。DDG 样本既然可以从其他 Peer 那里拉取数据和文件，自然也可以响应其他 Peer 的对等请求。</p><p>当 Peer 来请求 <strong>slave</strong> config或 <strong>jobs</strong> config 时，DDG 样本会从内存中整理好一份自己持有的数据，经过与前面阐述的解码、解密相反的编码、加密处理，返回给 Peer。</p><p>当 Peer 来请求下载矿机程序(比如上面的 <strong>wordpress</strong> 文件)或已编译好的 Busybox 程序时，DDG 样本会检查自己的<strong>工作目录</strong>的 <strong>cache</strong> 子目录中是否已经缓存了相应文件，如果缓存了相应文件并且签名有效，就会返回给 Peer。</p><p>另外的问题是，很多 DDG 控制的失陷主机都在内网，不一定可以穿透 NAT 对外提供这种服务。所以跟踪程序无法通过 P2P 机制跟踪到所有的 Bot。</p><h4 id="3-3-5-peers-proxy-"><strong>3.3.5 Peers 之间的 Proxy 特性</strong></h4><p>DDG 的 P2P 机制中，还有一个有意思的特性：Peer 的 Proxy 功能。</p><p>当某个 Peer 来请求下载矿机程序或 Busybox 程序时，如果 DDG 经过检查发现自己工作目录中暂时不存在相应文件，它就会把自己作为一个 Proxy，向自己成功连接的其他 Peer 随机发送相应的下载请求。如果成功下载，就会返回给向自己请求下载文件的 Peer。</p><h2 id="4-"><strong>4. 总结</strong></h2><p>DDG 经过两年多的发展，从最初简单的挖矿木马，到借用第三方协议框架构建简单的 P2P 网络，到现在自研 P2P 协议，已经演化成了一个复杂的 P2P 僵尸网络。可能这是第一个 P2P 结构的挖矿僵尸网络。如果你怀疑自己的主机被 DDG 入侵，建议从以下几个方面排查、处置：</p><ol><li>检查 <code>/var/spool/cron/root</code> 和 <code>/var/spool/cron/crontabs/root</code> 是否存在恶意 Cron Jobs</li><li>检查 <code>~/.ssh/authorized_keys</code> 文件中是否存在恶意 SSH Public Key</li><li>检查 <code>/var/lib/</code> , <code>/usr/local/</code> 目录下的隐藏目录，是否存在 DDG 工作目录</li><li>检查 <code>/usr/bin/</code>, <code>/usr/local/bin/</code> 目录下是否存在可疑 ELF 文件</li><li>检查 <code>/etc/hosts</code> 文件是否被写入恶意内容</li></ol><h2 id="5-iocs"><strong>5. IoCs</strong></h2><p>C&amp;C:</p><pre><code>67.205.168.20:8000
</code></pre><p>URLs:</p><pre><code>http://67.205.168.20:8000/i.sh
http://67.205.168.20:8000/static/wordpress
http://67.205.168.20:8000/static/bb3/busybox.x86_64
http://67.205.168.20:8000/static/bb3/busybox.i686
</code></pre><p>MD5s:</p><pre><code>5e2e0564ee03c743b50e4798c1041cea  5009/ddgs.i686
ea3d5d224ed7474159936f727db7555d  5009/ddgs.x86_64
05d7e2c36d5c58b26b00ca80ee7d8abe  5012/ddgs.i686
38fb3221d43d743a0de12d494ad60669  5012/ddgs.x86_64
91217bdbcc9f5663aac47b9fe803d4c7  5013/ddgs.i686
02e645c3bdd84d7a44b7aefc0f6d9e74  5013/ddgs.x86_64
5f4587df10ba4b3e7b46eb8b46d249bd  5014/ddgs.i686
e956e5b97cd0b73057980d735ee92974  5014/ddgs.x86_64
8e44e7a361c4d91c670072e049e6d729  5015/ddgs.i686
7f87c72701576da704056b38f6fae1ce  5015/ddgs.x86_64
6c164f25cabbcdc112192b1409ae73c5  5016/ddgs.i686
f84a0180ebf1596df4e8e8b8cfcedf63  5016/ddgs.x86_64
c2480ce231cc84130d878cb42bd072dd  5017/ddgs.i686
c8b416b148d461334ae52aa75c5bfa79  5017/ddgs.x86_64
66cd0c4c13670c32f43b0fd3304b0bf6  5018/ddgs.i686
dc87e9c91503cc8f2e8e3249cd0b52d7  5018/ddgs.x86_64
58c9a8561584dd1b70fbcb68b458f293  5019/ddgs.i686
682f839c1097af5fae75e0c5c39fa054  5019/ddgs.x86_64
28b2ee07f7a611d353efd8e037973bca  5020/ddgs.i686
495dfc4ba85fac2a93e7b3f19d12ea7d  5020/ddgs.x86_64
ffe204b87c5713733d5971e7479c0830  5021/ddgs.i686
d2a81a0284cdf5280103bee06d5fe928  5021/ddgs.x86_64
e2430bbeb49a11bfa30c6b01a28362c7  5022/ddgs.i686
e64b247d4cd9f8c58aedc708c822e84b  5022/ddgs.x86_64
d3a203cb0aa963529c0e4f8eccbf8c56  5023/ddgs.i686
2c4b9d01d2f244bb6530b48df99d04ae  5023/ddgs.x86_64

d146612bed765ba32200e0f97d0330c8  miner_1
d146612bed765ba32200e0f97d0330c8  miner_2</code></pre>
1. 概述

DDG Mining Botnet 是一个活跃已久的挖矿僵尸网络，其主要的盈利方式是挖 XMR。从 2019.11 月份至今，我们的 Botnet 跟踪系统监控到 DDG Mining Botnet 一直在频繁跟新，其版本号和对应的更新时间如下图所示：

其中，v4005~v4011 版本最主要的更新是把以前以 Hex 形式硬编码进样本的 HubList 数据，改成了 Gob 序列化的方式；v5009 及以后的版本，则摒弃了以前基于 Memberlist 来构建 P2P 网络的方式，改用自研的 P2P 协议来构建混合模式 P2P 网络 。简化的网络结构如下：

右边服务器是 C&C Server，DDG 中称它为 xhub 节点，是一个超级节点，除了与各 Bot 相同的 P2P 通信功能之外，还具有以下功能：

 * 统计各 Peer 信息( Peer 会向它注册，上传自身信息)
 * 协助各 Peer 寻找到对方，xhub 节点里保存了大量的 P2P Peers 列表，它会在 Bot 向其注册的时候把一部分列表分享给 Bot；而每个 Bot 最多保存 200 条 Peers <ip:port> 列表；
 * 承载原始的恶意 Shell 脚本、主样本和其他组件的下载服务。

我们的 Botnet 跟踪系统追踪到 DDG 当前的 一部分 P2P Nodes(失陷主机)，最近每天平均可以追踪到 900 个 Nodes，其中可验证存活的比例达 98%:

根据我们合作伙伴提供的统计数字，中国大陆境内活跃的 Bot 有17,590 个。根据我们的追踪数据，中国大陆境内的 Bot 数量约占总量的 86%，以此反推，DDG 目前在全球范围内的 Bot 数量约 20,000。

DDG 支持的传播途径，有以下 4 个：

 * SSH 服务暴破；
 * Redis 未授权访问漏洞；
 * 针对 Nexus Repository Manager 3 RCE 漏洞(CVE-2019-7238)的利用；
 * 针对 Supervisord RCE 漏洞(CVE-2017-11610)的利用。

根据我们的追踪数据，DDG 最近一段时间一直没有开启公网传播，而只利用 SSH 暴破在内网传播。其内网传播的开关，在 slave config 里设置：

另外，根据我们对 DDG 过去一年的追踪统计，它从 2019.1 月份至今共用了 24 个 C&C，每个 CC 的活跃时段统计如下：

最后，旧版本的 DDG Botnet Tracker 已经无法工作，我们现已将其开源：

https://github.com/0xjiayu/DDGBotnetTracker/tree/master/v1

并且公开最新版本 DDG 相关的运行日志、相关数据和相应解析工具，以及一个基于 P2P 机制的 Pre-Built Demo Tracker Program(ELF Binary file):

https://github.com/0xjiayu/DDGBotnetTracker/tree/master/v2


2. 样本关键行为分析

就 DDG 当前最新的 v5023 版本来看，DDG 有一些有趣的新特性：

 * 用特定算法生成一个 4 字符的目录名，并在 /var/lib/ 或 /usr/local/ 下创建相应的隐藏目录作为工作目录，存放本地配置信息和从 C&C 或 P2P Peer 下载到的文件或数据；
 * 自定义 Base64 编码的编码表(Alphabet)；
 * 对关键数据和文件频繁使用编码(Base64/Adler32)、加密(AES)、压缩(Gzip)和数字签名(RSA/ED25519)手段，以对抗分析和数据伪造；
 * 除了以前的 slave Config 来控制 Bot 执行挖矿和传播任务，又新增了一个 jobs Config 文件来对 Bot 进行更复杂的任务控制；
 * 自研 P2P 协议，并以此在 Nodes 之间交换最新各自持有的 C&C、Nodes 列表和恶意任务指令配置等信息，还可以在 Peers 之间传播恶意组件。

以 DDG v5023 版本的样本为例，其主要的执行流程，从恶意 Shell 脚本文件 i.sh 开始，如下所示：


2.1 恶意 Shell 脚本 i.sh

DDG 每成功入侵一台主机，就会先执行这个 i.sh 脚本。 i.sh 主要执行 3 个任务：

 1. 篡改 Cron job，在 /var/spool/cron/root 和 /var/spool/cron/crontabs/root 写入恶意 Cron job，每 15 分钟下载 hxxp://67.205.168.20:8000/i.sh 并运行；
 2. 下载并执行最新的 DDG 主样本文件；
 3. 检测目录 /usr/local/bin/ | /usr/libexec/ | /usr/bin/ 是否可写。


2.2 初始化

DDG 的主样本由 Go 语言编写，编译出来的原始 ELF 文件体积比较大。DDG 的作者就用 UPX Packer 把原始 ELF 文件加壳，一方面一定程度上可以对抗自动化分析，另一方面缩小了文件体积，便于网络传输。

DDG 的主样本里实现了多个工作模块，有一些后续用到的数据和全局变量，涉及相应的初始化工作。比如生成全局自定义 Alphabet 的 Base64 编解码句柄，解析样本内硬编码的 xhub(C&C) 相关数据和 xSeeds(P2P Seed Nodes List) 数据，解析样本内硬编码的弱口令字典(用于后续的 SSH 服务暴破)等等。

2.2.1 自定义 Alphabet 的 Base64 编码

在函数 ddgs_common_init 中，DDG 基于自定义的 Alphabet 创建了一个全局的 Base64 编解码句柄：

自定义的 Alphabet 为：

"eDy54SH1N2s-Y7g3qnurvaTW_0BlCMJhfb6wtdGUcROXPAV9KEzIpFoi8xLjkmZQ"

生成的全局 Base64 编解码句柄，会在后续被用来：

 * 解析内置硬编码的 xhub 和 xSeeds 数据；
 * 解析后续从服务器下载到的文件 Sig、与 P2P Nodes 通信时的自定义 HTTP Header(X-Hub / X-Seed / X-Sig)
 * 响应其他 P2P Peer 的请求时，编码自定义 HTTP Header(X-Hub / X-Seed / X-Sig)

2.2.2 解析内置 xhub/xSeeds

在 DDG 中，xSeeds 就是 P2P Seed Nodes 列表，每个 Bot 都持有一份 Seed Nodes List，里面内置了 200 个 P2P Node <ip:port> 列表。Bot 可以与他们通信交换数据。

xhub 即 C&C 服务器信息，C&C 服务器可以指定多个。

在函数 ddgs_xnet_init_0 中，DDG 解析了内置的 xhub 和 xSeeds 数据，还对 xhub 数据用 ed25519 算法校验是否被伪造。xhub 的解析、验证汇编代码如下：

DDG v5023 样本中内置的 xhub 数据为经过自定义 Alphabet Base64 编码过的字串：

fOSIE4y3ZPcTuT7weiMSSr7-0-Vem5IfTxEbUirWGS9j5NsDJh2k54RsnK08lG-ECaHQ4ARiWy3mJs0O9HzBpP6iANY7cTHnPw_i-wNK7u8E7wfVYweLg5eKYe


经过 Base64 解码后，还需要用 msgPack 进行一层解码，才能得到 xhub 的原始明文数据。解析上面的编码数据，可以得到两个关键数据：

 * C&C 列表，目前只有一个：67.205.168.20:8000；
 * ed25519 Signature：

0x8f, 0xfa, 0xca, 0x16, 0x49, 0x63, 0x63, 0x03, 0x77, 0x45, 0x15, 0x33, 0x4b, 0x64, 0xbb, 0x80,
0xf4, 0x3c, 0xe0, 0x5b, 0x9c, 0x61, 0x9f, 0x74, 0xd7, 0x98, 0x5b, 0xfb, 0x0c, 0x82, 0x81, 0x79,
0xf2, 0x7c, 0x0c, 0x4a, 0x4a, 0x47, 0x06, 0x78, 0x6e, 0x62, 0xf1, 0x71, 0x51, 0xbf, 0x12, 0xda,
0x77, 0x5c, 0x23, 0xfd, 0x78, 0xa6, 0x6a, 0xbc, 0x6c, 0x9a, 0xd2, 0xc8, 0xb7, 0xb4, 0x83, 0x0d

样本中解出 C&C 列表和 ed25519 的 Signature 之后，就会用相反的步骤用 ed25519 算法来校验 C&C 列表是否被伪造。校验时用到的 RSA 公钥为：

0x20, 0x0A, 0x51, 0x81, 0x91, 0xE9, 0xF2, 0x54,
0x78, 0xFC, 0x1E, 0x66, 0x7B, 0x8F, 0x8D, 0xAC,
0xCF, 0x62, 0x28, 0x18, 0x46, 0xEC, 0x45, 0x7C,
0xF5, 0xC3, 0xBA, 0x4C, 0x86, 0xB0, 0xB5, 0x41


xhub 数据的解析方法：
https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/tools/xsig_verify.go

xSeeds 包含 200 条 P2P Node <ip:port> 列表，数据量比较大，共 0x8E2 Bytes：

而且 DDG 样本对 xSeeds 解析步骤与 xhub 的解析略有不同，经过分析，xSeeds 经过以下 3 层处理，而不用 ed25519 校验：

 1. msgpack 序列化编码；
 2. gzip 压缩
 3. 自定义 Alphabet 的 Base64 编码

xSeeds 数据的解析方法：

https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/tools/dec_seeds.go

2.2.3 解析内置弱口令字典

在全局初始化函数 ddgs_global_init 中，DDG 调用了一个函数 ddgs_global_decodePasswords ，在这个函数中解密并校验内置的弱口令字典，这些弱口令将在后续传播阶段被用来暴破 SSH 服务(暴破 SSH 服务时用的用户名为 root )：

弱口令数据是经过 AES 加密、gzip 压缩后内置在样本中的，密文数据共 0x2BCFE Bytes：

DDG 会首先对上面数据用 gzip 解压，解压后密文数据结构如下：

DDG 会先用 ed25519 对上述密文数据的 Sha256 值进行校验(公钥与前面校验 xhub 时用的公钥相同)，校验成功之后才会用 AES 算法将其解密，解密后得到一份 17,907 条密码的弱口令字典:

2.2.4 创建全局 ed25519 密钥对

在函数 ddgs_global_init 中，DDG 还有另外一项关键全局变量的初始化工作：创建一对全局 ed25519 密钥，用以在后续存取 BoltDB 中数据时对数据进行签名和校验：

创建密钥对的种子如下：

0x5C, 0x9E, 0xAE, 0xAE, 0x43, 0x26, 0xB7, 0xA2,
0x52, 0xDC, 0x43, 0xF9, 0xBD, 0x3F, 0xD1, 0xA6,
0xC8, 0xB0, 0x28, 0xE1, 0xDF, 0xA8, 0xB0, 0xF5,
0xCF, 0x43, 0xE7, 0x82, 0xD1, 0x90, 0x11, 0x6B



2.3 创建工作目录

旧版本的 DDG 会直接把当前用户的 HOME 目录作为自己的工作目录，主要是在此目录下创建隐藏的 BoltDB 文件，文件中存放 Hublist 数据(旧版本的 P2P Node List)。现在新版本的 DDG 会用特定算法生成目录名，并在 /var/lib/ 或 /usr/local/ 目录下创建相应的隐藏目录。Go 语言实现的工作目录名生成算法如下(假设当前 DDG 二进制样本文件的路径为 /root/ddgs_x64)：

https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/tools/gen_workdir_name.go

上面程序的执行结果是 jsfc ，那么 DDG 就会尝试创建目录 /var/lib/.jsfc，后续工作目录的结构如下：

/var/lib/.jsfc
├── 5023
│  └── cache
│     └── static
│        ├── bb3
│        │  ├── busybox.x86_64
│        │  └── busybox.x86_64.sig
│        ├── wordpress
│        └── wordpress.sig
└── .local


其中 .local 文件即为 BoltDB 格式的文件，其它的还有从 C&C 服务器上下载到的恶意挖矿程序(wordpress) 、编译好的 Busybox ELF 文件以及它们相应的数字签名。


2.4 BoltDB 文件

BoltDB 是一个基于内存的小型 KV 数据库，其数据内容可以落地到磁盘文件。DDG 旧版本中用 BoltDB 来存放明文 Hublist 数据。在 DDG 最新的 BoltDB 数据库中，有一个 Bucket 名为 xproxy。xproxy 里存放了 3 份数据：

 1. hubsig: xhub 信息；
 2. seeds: xSeeds 数据；
 3. port: 本地在 (30000~65535) 范围内随机监听的 TCP 端口，用来相应其他 P2P Nodes 的通信请求。

DDG 对这 3 份数据，每一份都做了如前文内置弱口令字典数据同样的处理，经过 msgpack 序列化编码后再用 AES 加密，重组加密数据后再存入 BoltDB。重组的密文数据结构也与弱口令字典密文数据结构相同:

不同的是，DDG 对 BoltDB 中数据的签名和校验用到的全局密钥，是函数 ddgs_global_init 用样本中硬编码的种子数据生成的。


2.5 监控关键文件/目录

DDG 利用 fsnotify 框架监控以下文件，在运行期间防止被别的进程改动，用以保护自己的持久化配置：

 * /root/.ssh/authorized_keys
 * /var/spool/cron/root
 * /var/spool/cron/crontabs/root


3. P2P 机制

DDG 执行完上述关键步骤，DDG Bot 就开始了与其他 Bot 之间的 P2P 通信。DDG 的 P2P 机制特性如下：

 * Peers 首先会向 xhub 注册
 * Peers 之间有特有的 Ping/Pong 机制
 * Peers 之间可以共享各自持有的 C&C 和 Peers 列表数据；
 * Peers 之间可以传播 slave config 和 jobs config 数据；
 * Peers 之间可以传播其他 Payload 或组件，比如恶意挖矿程序、已编译的 Busybox 二进制文件


3.1 准备工作

在进行 P2P 通信之前，DDG 会有两项关键的准备工作：

 * 生成一个随机域名
 * 生成自己的 Peer UID

随机域名的的生成规则是：<RAND 3 LowChars>.<RAND [5-7] Lowchars>.com， 如 kez.tirueg.com 。

UID 的形式： VERSION_NUM.UID_HASH ，如 5023.dd9b2f57af3be6b6276606d4f37e4a5b 。

UID_HASH 的生成规则，则是综合计算 Host information 和 网卡信息的 MD5 值得出， Go 语言实现如下：

https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v1/lib/util.go#L40


3.2 Peer <--> xhub

DDG 会首先向 xhub 如下 HTTP POST 请求：

以上 HTTP 通信中，

 * Request Header Host，即为事先随即生成的域名。值得一提的是，这个域名不会被 DNS 服务解析，因为以上 HTTP 通信是复用了已建立的 TCP Socket Session，在已有 TCP Socket 连接上 Send/Recv 数据并把数据用 HTTP 协议来解析而已。攻击者这样做，可能是为了逃避或混淆某些安全设备的流量检测；
 * Request Header X-Hub，即为 DDG 样本当前持有的硬编码在样本中的 xhub 信息，详见 2.2.2 节；
 * Request Header X-Port，即为 DDG 样本当前随即开启并监听的 P2P 通信端口；
 * Request Header X-Uid，即事先生成的 UID；
 * Request Header X-Relay，是 DDG 综合 X-Uid 和 X-Port 字段的值通过 Adler 算法算出来的校验值；
 * Response Header X-Seed，是对方从自己持有的 Peers 列表中随机选取的 20 个 Peers 地址列表信息，DDG 样本收到后会合入自身持有的 200 个 Peers 列表，总数不超过 200；
 * Response Header X-Hub，是对方持有的 xhub 信息，DDG 样本收到后会用它替换掉自身持有的 xhub 信息。

最后，HTTP 响应中的 jobs config 数据，是经过 msgpack 序列化编码后又用 AES 加密过的数据，数据的组织结构与 gzip 解压后的样本内置弱口令字典数据相同，解析过程也完全相同。最新解密后的 jobs config 数据见：

https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/data/jobs.json

从 jobs Config 的内容来看，攻击者对 Bot 的行为控制的更为复杂精细，该配置文件的核心功能在流程图里已有说明，此处总结如下：

 * 针对每个低级版本 DDG 都有不同的处理，或 Kill 或 Discard 或 Upgrade；
 * 引入了 Busybox 执行更复杂的命令，主要用来干掉竞争对手；
 * 干掉竞争对手的姿势复杂多样，杀进程、禁用服务、清除 SSH Key、删除 Cron Jobs，重置 Lock File 等等，最显眼的是通过篡改 /etc/hosts 文件屏蔽一大批竞争对手需要访问的域名。

其中，最显眼的一部分配置，是 DDG 通过篡改 /etc/hosts 文件屏蔽竞争对手要访问的域名，大多数都是 LSDMiner 和 systemdMiner 相关的域名：

LSDMiner	img.sobot.com
LSDMiner	lsdu.b-cdn.net
LSDMiner	thyrsi.com
LSDMiner	aliyun.one
systemdMiner	an7kmd2wp4xo7hpr.onion.sh
systemdMiner	an7kmd2wp4xo7hpr.tor2web.su
systemdMiner	aptgetgxqs3secda.onion.ly
systemdMiner	aptgetgxqs3secda.onion.pet
systemdMiner	dns.rubyfish.cn
systemdMiner	aptgetgxqs3secda.onion.in.net
systemdMiner	aptgetgxqs3secda.tor2web.fyi
systemdMiner    an7kmd2wp4xo7hpr.d2web.org
systemdMiner    an7kmd2wp4xo7hpr.timesync.su



3.3 Peer <--> Peer

在与 xhub 通信过后，DDG 样本就开始与自身持有的 200 个 Peers 进行通信。 Peers 之间的通信有 4 个关键步骤：

 1. Ping/Pong
 2. 拉取对方的 slave config 和 jobs config；
 3. 拉取对方的其他恶意组件，比如恶意矿机程序和 Busybox 二进制程序；
 4. 响应(服务)别的 Peers 的以上 3 种请求

3.3.1 Ping/Pong

DDG 一旦成功与某个 Peer 建立连接，会至少经过 2 轮 Ping/Pong 通信，间隔 30s ，对端响应的 Pong 包与 Ping 包完全相同，长度为 3 Bytes：

3 Bytes 的 Ping 包生成规则如下：

 * 第 1 字节为 0x00；
 * 第 3 字节在 (0, 0xFF] 中随机产生；
 * 第 2 字节为第 3 字节与 0x42 XOR 运算的结果。

Go 语言代码描述如下：

// Generate ping packet bytes
func GenPingbytes(globalRand *rand.Rand) []byte {
    pkt := make([]byte, 3)
    pkt[0] = 0x00
    pkt[2] = byte((globalRand.Intn(0xFE) + 1) & 0xFF)
    pkt[1] = (pkt[2] ^ 0x42) & 0xFF

    return pkt
}

3.3.2 传播 slave/jobs config data

经过 2 轮的 Ping/Pong 通信，DDG 会随机选成功通信的 Peers 向对方请求拉取 slave config 或 jobs config，请求方式类似于上面向 xhub 请求 jobs config，以拉取对方 slave config 为例：

值得注意的两点：

 1. DDG Peers 之间发送 HTTP Post 请求，HTTP Request Header 中相比请求 xhub 时少了一个 X-Port。
 2. 发往 Peer 的 HTTP 请求，复用了前面 Ping/Pong 通信中用到的 TCP 连接，即同一个 TCP Session，刚开始用来 Ping/Pong 交互，后面直接基于这个 TCP Session 发送 HTTP 请求、接收 HTTP 响应，所以 HTTP Header 中 Host 字段里随机生成的域名并不会经过 DNS 解析。

对端响应的 slave config 数据，是经 msgpack 序列化编码过的二进制数据，数据格式与旧版本变化不大，解码方式可以参考 以 P2P 的方式追踪 DDG 。最新解码后的 slave config 数据已上传到 Github：

https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/data/slave.json

解码后的 slave config 数据包含自身数据的数字签名，DDG 样本会以此校验 slave config 数据，校验时用到的 RSA 公钥硬编码在样本中：

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1+/izrOGcigBPC+oXnr2
S3JI76iXxqn7e9ONAmNS+m5nLQx2g0GW44TFtMHhDp1lPdUIui1b1odu36i7Cf0g
31vdYi1i6nGfXiI7UkHMsLVkGkxEknNL1c1vv0qE1b2i2o4TlcXHKHWOPu4xrpYY
m3Fqjni0n5+cQ8IIcVyjkX7ON0U1n8pQKRWOvrsPhO6tvJnLckK0P1ycGOcgNiBm
sdA5WDjw3sg4xWCQ9EEpMeB0H1UF/nv7AZQ0etncMxhiWoBxamuPWY/KS3wZStUd
gsMBOAOOpnbxL9N+II7uquQQkMmO6HriXRmjw14OmSBEoEcFMWF2j/0HPVasOcx2
xQIDAQAB
-----END PUBLIC KEY-----


3.3.3 下载恶意组件

没有被选中拉取 slave config 和 jobs config 的 Peer，DDG 会继续与他们的 Ping/Pong 通信。从某个 Peer 拉取的 slave config 中指定了恶意矿机程序 Miner 的下载 URI、本地存放路径和 MD5，DDG 接下来会随机选取另一个 Ping/Pong 中的 Peer，复用 Ping/Pong 的 TCP Session，通过 HTTP 协议向其发起 Miner 的下载请求：

下载到的恶意矿机程序，DDG 不仅会将其存放到 slave config 中指定的本地路径，还会连同 HTTP Response Header 中的 X-Sig 内容作为矿机程序的数字签名数据一起放到自己创建的工作目录中：

/var/lib/.jsfc
├── 5023
│  └── cache
│     └── static
│        ├── bb3
│        │  ├── busybox.x86_64
│        │  └── busybox.x86_64.sig
│        ├── wordpress
│        └── wordpress.sig
└── .local


DDG 从 Peer 下载已编译的 Busybox 程序的过程同上。

3.3.4 响应其他 Peers 的请求

P2P 协议中，Peers 之间的功能、角色是对等的。DDG 样本既然可以从其他 Peer 那里拉取数据和文件，自然也可以响应其他 Peer 的对等请求。

当 Peer 来请求 slave config或 jobs config 时，DDG 样本会从内存中整理好一份自己持有的数据，经过与前面阐述的解码、解密相反的编码、加密处理，返回给 Peer。

当 Peer 来请求下载矿机程序(比如上面的 wordpress 文件)或已编译好的 Busybox 程序时，DDG 样本会检查自己的工作目录的 cache 子目录中是否已经缓存了相应文件，如果缓存了相应文件并且签名有效，就会返回给 Peer。

另外的问题是，很多 DDG 控制的失陷主机都在内网，不一定可以穿透 NAT 对外提供这种服务。所以跟踪程序无法通过 P2P 机制跟踪到所有的 Bot。

3.3.5 Peers 之间的 Proxy 特性

DDG 的 P2P 机制中，还有一个有意思的特性：Peer 的 Proxy 功能。

当某个 Peer 来请求下载矿机程序或 Busybox 程序时，如果 DDG 经过检查发现自己工作目录中暂时不存在相应文件，它就会把自己作为一个 Proxy，向自己成功连接的其他 Peer 随机发送相应的下载请求。如果成功下载，就会返回给向自己请求下载文件的 Peer。


4. 总结

DDG 经过两年多的发展，从最初简单的挖矿木马，到借用第三方协议框架构建简单的 P2P 网络，到现在自研 P2P 协议，已经演化成了一个复杂的 P2P 僵尸网络。可能这是第一个 P2P 结构的挖矿僵尸网络。如果你怀疑自己的主机被 DDG 入侵，建议从以下几个方面排查、处置：

 1. 检查 /var/spool/cron/root 和 /var/spool/cron/crontabs/root 是否存在恶意 Cron Jobs
 2. 检查 ~/.ssh/authorized_keys 文件中是否存在恶意 SSH Public Key
 3. 检查 /var/lib/ , /usr/local/ 目录下的隐藏目录，是否存在 DDG 工作目录
 4. 检查 /usr/bin/, /usr/local/bin/ 目录下是否存在可疑 ELF 文件
 5. 检查 /etc/hosts 文件是否被写入恶意内容


5. IoCs

C&C:

67.205.168.20:8000


URLs:

http://67.205.168.20:8000/i.sh
http://67.205.168.20:8000/static/wordpress
http://67.205.168.20:8000/static/bb3/busybox.x86_64
http://67.205.168.20:8000/static/bb3/busybox.i686


MD5s:

5e2e0564ee03c743b50e4798c1041cea  5009/ddgs.i686
ea3d5d224ed7474159936f727db7555d  5009/ddgs.x86_64
05d7e2c36d5c58b26b00ca80ee7d8abe  5012/ddgs.i686
38fb3221d43d743a0de12d494ad60669  5012/ddgs.x86_64
91217bdbcc9f5663aac47b9fe803d4c7  5013/ddgs.i686
02e645c3bdd84d7a44b7aefc0f6d9e74  5013/ddgs.x86_64
5f4587df10ba4b3e7b46eb8b46d249bd  5014/ddgs.i686
e956e5b97cd0b73057980d735ee92974  5014/ddgs.x86_64
8e44e7a361c4d91c670072e049e6d729  5015/ddgs.i686
7f87c72701576da704056b38f6fae1ce  5015/ddgs.x86_64
6c164f25cabbcdc112192b1409ae73c5  5016/ddgs.i686
f84a0180ebf1596df4e8e8b8cfcedf63  5016/ddgs.x86_64
c2480ce231cc84130d878cb42bd072dd  5017/ddgs.i686
c8b416b148d461334ae52aa75c5bfa79  5017/ddgs.x86_64
66cd0c4c13670c32f43b0fd3304b0bf6  5018/ddgs.i686
dc87e9c91503cc8f2e8e3249cd0b52d7  5018/ddgs.x86_64
58c9a8561584dd1b70fbcb68b458f293  5019/ddgs.i686
682f839c1097af5fae75e0c5c39fa054  5019/ddgs.x86_64
28b2ee07f7a611d353efd8e037973bca  5020/ddgs.i686
495dfc4ba85fac2a93e7b3f19d12ea7d  5020/ddgs.x86_64
ffe204b87c5713733d5971e7479c0830  5021/ddgs.i686
d2a81a0284cdf5280103bee06d5fe928  5021/ddgs.x86_64
e2430bbeb49a11bfa30c6b01a28362c7  5022/ddgs.i686
e64b247d4cd9f8c58aedc708c822e84b  5022/ddgs.x86_64
d3a203cb0aa963529c0e4f8eccbf8c56  5023/ddgs.i686
2c4b9d01d2f244bb6530b48df99d04ae  5023/ddgs.x86_64

d146612bed765ba32200e0f97d0330c8  miner_1
d146612bed765ba32200e0f97d0330c8  miner_2
{"version":"0.3.1","atoms":[["soft-return","",{}]],"cards":[["image",{"src":"__GHOST_URL__/content/images/2020/04/ddg_version_timeline-1.png","alt":"","title":"","caption":"DDG Version Timeline"}],["image",{"src":"__GHOST_URL__/content/images/2020/04/hybrid_p2p_network-2.png","alt":"","title":"","caption":"P2P Hybrid Model"}],["image",{"src":"__GHOST_URL__/content/images/2020/04/data_statics-1.png","alt":"","title":""}],["image",{"src":"__GHOST_URL__/content/images/2020/04/slave_conf_aassh.png","alt":"","title":""}],["image",{"src":"__GHOST_URL__/content/images/2020/04/cc_history_light.png","alt":"","title":""}],["image",{"src":"__GHOST_URL__/content/images/2020/04/work_flow-1.png","alt":"","title":""}],["image",{"src":"__GHOST_URL__/content/images/2020/04/b64Enc-1.png","alt":"","title":""}],["image",{"src":"__GHOST_URL__/content/images/2020/04/xsig_verify-1.png","alt":"","title":""}],["code",{"code":"fOSIE4y3ZPcTuT7weiMSSr7-0-Vem5IfTxEbUirWGS9j5NsDJh2k54RsnK08lG-ECaHQ4ARiWy3mJs0O9HzBpP6iANY7cTHnPw_i-wNK7u8E7wfVYweLg5eKYe\n"}],["code",{"code":"0x8f, 0xfa, 0xca, 0x16, 0x49, 0x63, 0x63, 0x03, 0x77, 0x45, 0x15, 0x33, 0x4b, 0x64, 0xbb, 0x80,\n0xf4, 0x3c, 0xe0, 0x5b, 0x9c, 0x61, 0x9f, 0x74, 0xd7, 0x98, 0x5b, 0xfb, 0x0c, 0x82, 0x81, 0x79,\n0xf2, 0x7c, 0x0c, 0x4a, 0x4a, 0x47, 0x06, 0x78, 0x6e, 0x62, 0xf1, 0x71, 0x51, 0xbf, 0x12, 0xda,\n0x77, 0x5c, 0x23, 0xfd, 0x78, 0xa6, 0x6a, 0xbc, 0x6c, 0x9a, 0xd2, 0xc8, 0xb7, 0xb4, 0x83, 0x0d"}],["code",{"code":"0x20, 0x0A, 0x51, 0x81, 0x91, 0xE9, 0xF2, 0x54,\n0x78, 0xFC, 0x1E, 0x66, 0x7B, 0x8F, 0x8D, 0xAC,\n0xCF, 0x62, 0x28, 0x18, 0x46, 0xEC, 0x45, 0x7C,\n0xF5, 0xC3, 0xBA, 0x4C, 0x86, 0xB0, 0xB5, 0x41\n"}],["image",{"src":"__GHOST_URL__/content/images/2020/04/xseeds_raw-1.png","alt":"","title":""}],["image",{"src":"__GHOST_URL__/content/images/2020/04/decPasswd-1.png","alt":"","title":""}],["image",{"src":"__GHOST_URL__/content/images/2020/04/passwds_raw-1.png","alt":"","title":""}],["image",{"src":"__GHOST_URL__/content/images/2020/04/passwds_cipherdata-1.png","alt":"","title":""}],["image",{"src":"__GHOST_URL__/content/images/2020/04/passwd_list-1.png","alt":"","title":""}],["image",{"src":"__GHOST_URL__/content/images/2020/04/init_global_edKeys-1.png","alt":"","title":""}],["code",{"code":"0x5C, 0x9E, 0xAE, 0xAE, 0x43, 0x26, 0xB7, 0xA2,\n0x52, 0xDC, 0x43, 0xF9, 0xBD, 0x3F, 0xD1, 0xA6,\n0xC8, 0xB0, 0x28, 0xE1, 0xDF, 0xA8, 0xB0, 0xF5,\n0xCF, 0x43, 0xE7, 0x82, 0xD1, 0x90, 0x11, 0x6B\n"}],["code",{"code":"/var/lib/.jsfc\n├── 5023\n│  └── cache\n│     └── static\n│        ├── bb3\n│        │  ├── busybox.x86_64\n│        │  └── busybox.x86_64.sig\n│        ├── wordpress\n│        └── wordpress.sig\n└── .local\n"}],["image",{"src":"__GHOST_URL__/content/images/2020/04/cipherdata_struct-1.png","alt":"","title":""}],["image",{"src":"__GHOST_URL__/content/images/2020/04/monitor_fs-1.png","alt":"","title":""}],["image",{"src":"__GHOST_URL__/content/images/2020/04/xhub_jobs-1.png","alt":"","title":""}],["code",{"code":"LSDMiner\timg.sobot.com\nLSDMiner\tlsdu.b-cdn.net\nLSDMiner\tthyrsi.com\nLSDMiner\taliyun.one\nsystemdMiner\tan7kmd2wp4xo7hpr.onion.sh\nsystemdMiner\tan7kmd2wp4xo7hpr.tor2web.su\nsystemdMiner\taptgetgxqs3secda.onion.ly\nsystemdMiner\taptgetgxqs3secda.onion.pet\nsystemdMiner\tdns.rubyfish.cn\nsystemdMiner\taptgetgxqs3secda.onion.in.net\nsystemdMiner\taptgetgxqs3secda.tor2web.fyi\nsystemdMiner    an7kmd2wp4xo7hpr.d2web.org\nsystemdMiner    an7kmd2wp4xo7hpr.timesync.su\n"}],["image",{"src":"__GHOST_URL__/content/images/2020/04/peer_pingpong-1.png","alt":"","title":""}],["code",{"code":"// Generate ping packet bytes\nfunc GenPingbytes(globalRand *rand.Rand) []byte {\n    pkt := make([]byte, 3)\n    pkt[0] = 0x00\n    pkt[2] = byte((globalRand.Intn(0xFE) + 1) & 0xFF)\n    pkt[1] = (pkt[2] ^ 0x42) & 0xFF\n\n    return pkt\n}"}],["image",{"src":"__GHOST_URL__/content/images/2020/04/request_slave-1.png","alt":"","title":""}],["image",{"src":"__GHOST_URL__/content/images/2020/04/slave_resp-1.png","alt":"","title":""}],["code",{"code":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1+/izrOGcigBPC+oXnr2\nS3JI76iXxqn7e9ONAmNS+m5nLQx2g0GW44TFtMHhDp1lPdUIui1b1odu36i7Cf0g\n31vdYi1i6nGfXiI7UkHMsLVkGkxEknNL1c1vv0qE1b2i2o4TlcXHKHWOPu4xrpYY\nm3Fqjni0n5+cQ8IIcVyjkX7ON0U1n8pQKRWOvrsPhO6tvJnLckK0P1ycGOcgNiBm\nsdA5WDjw3sg4xWCQ9EEpMeB0H1UF/nv7AZQ0etncMxhiWoBxamuPWY/KS3wZStUd\ngsMBOAOOpnbxL9N+II7uquQQkMmO6HriXRmjw14OmSBEoEcFMWF2j/0HPVasOcx2\nxQIDAQAB\n-----END PUBLIC KEY-----\n"}],["image",{"src":"__GHOST_URL__/content/images/2020/04/download_miner-1.png","alt":"","title":""}],["code",{"code":"/var/lib/.jsfc\n├── 5023\n│  └── cache\n│     └── static\n│        ├── bb3\n│        │  ├── busybox.x86_64\n│        │  └── busybox.x86_64.sig\n│        ├── wordpress\n│        └── wordpress.sig\n└── .local\n"}],["code",{"code":"67.205.168.20:8000\n"}],["code",{"code":"http://67.205.168.20:8000/i.sh\nhttp://67.205.168.20:8000/static/wordpress\nhttp://67.205.168.20:8000/static/bb3/busybox.x86_64\nhttp://67.205.168.20:8000/static/bb3/busybox.i686\n"}],["code",{"code":"5e2e0564ee03c743b50e4798c1041cea  5009/ddgs.i686\nea3d5d224ed7474159936f727db7555d  5009/ddgs.x86_64\n05d7e2c36d5c58b26b00ca80ee7d8abe  5012/ddgs.i686\n38fb3221d43d743a0de12d494ad60669  5012/ddgs.x86_64\n91217bdbcc9f5663aac47b9fe803d4c7  5013/ddgs.i686\n02e645c3bdd84d7a44b7aefc0f6d9e74  5013/ddgs.x86_64\n5f4587df10ba4b3e7b46eb8b46d249bd  5014/ddgs.i686\ne956e5b97cd0b73057980d735ee92974  5014/ddgs.x86_64\n8e44e7a361c4d91c670072e049e6d729  5015/ddgs.i686\n7f87c72701576da704056b38f6fae1ce  5015/ddgs.x86_64\n6c164f25cabbcdc112192b1409ae73c5  5016/ddgs.i686\nf84a0180ebf1596df4e8e8b8cfcedf63  5016/ddgs.x86_64\nc2480ce231cc84130d878cb42bd072dd  5017/ddgs.i686\nc8b416b148d461334ae52aa75c5bfa79  5017/ddgs.x86_64\n66cd0c4c13670c32f43b0fd3304b0bf6  5018/ddgs.i686\ndc87e9c91503cc8f2e8e3249cd0b52d7  5018/ddgs.x86_64\n58c9a8561584dd1b70fbcb68b458f293  5019/ddgs.i686\n682f839c1097af5fae75e0c5c39fa054  5019/ddgs.x86_64\n28b2ee07f7a611d353efd8e037973bca  5020/ddgs.i686\n495dfc4ba85fac2a93e7b3f19d12ea7d  5020/ddgs.x86_64\nffe204b87c5713733d5971e7479c0830  5021/ddgs.i686\nd2a81a0284cdf5280103bee06d5fe928  5021/ddgs.x86_64\ne2430bbeb49a11bfa30c6b01a28362c7  5022/ddgs.i686\ne64b247d4cd9f8c58aedc708c822e84b  5022/ddgs.x86_64\nd3a203cb0aa963529c0e4f8eccbf8c56  5023/ddgs.i686\n2c4b9d01d2f244bb6530b48df99d04ae  5023/ddgs.x86_64\n\nd146612bed765ba32200e0f97d0330c8  miner_1\nd146612bed765ba32200e0f97d0330c8  miner_2"}]],"markups":[["strong"],["a",["href","https://jiayu0x.com/2019/11/19/decode-gob-data-in-ddg-v4005/"]],["a",["href","__GHOST_URL__/dog-recent-updates-p2p-adopted-and-anti-analysis-enhanced/"]],["a",["href","https://en.wikipedia.org/wiki/Peer-to-peer#Hybrid_models"]],["code"],["a",["href","https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/data/slave.json"]],["a",["href","https://github.com/0xjiayu/DDGBotnetTracker/tree/master/v1"]],["a",["href","https://github.com/0xjiayu/DDGBotnetTracker/tree/master/v2"]],["a",["href","http://msgpack.org/"]],["a",["href","https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/tools/xsig_verify.go"]],["a",["href","https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/tools/dec_seeds.go"]],["a",["href","https://github.com/boltdb/bolt"]],["a",["href","https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/tools/gen_workdir_name.go"]],["a",["href","https://github.com/fsnotify/fsnotify"]],["a",["href","https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v1/lib/util.go#L40"]],["a",["href","https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/data/jobs.json"]],["a",["href","https://twitter.com/hashtag/LSDMiner"]],["a",["href","https://twitter.com/hashtag/systemdMiner"]],["a",["href","https://jiayu0x.com/2019/04/11/track-ddg-botnet-by-p2p-protocol/#2-3-ddg-%E7%9A%84%E4%BA%91%E7%AB%AF%E9%85%8D%E7%BD%AE%E6%95%B0%E6%8D%AE"]]],"sections":[[1,"h2",[[0,[0],1,"1. 概述"]]],[1,"p",[[0,[],0,"DDG Mining Botnet 是一个活跃已久的挖矿僵尸网络，其主要的盈利方式是挖 XMR。从 2019.11 月份至今，我们的 Botnet 跟踪系统监控到 DDG Mining Botnet 一直在频繁跟新，其版本号和对应的更新时间如下图所示："]]],[10,0],[1,"p",[[0,[],0,"其中，v4005~v4011 版本最主要的更新是把以前以 Hex 形式硬编码进样本的 HubList 数据，改成了 "],[0,[1],1,"Gob 序列化"],[0,[],0,"的方式；v5009 及以后的版本，则摒弃了以前"],[0,[2],0,"基于 "],[0,[0],1,"Memberlist"],[0,[],1," 来构建 P2P 网络"],[0,[],0,"的方式，改用自研的 P2P 协议来构建"],[0,[3],1,"混合模式 P2P 网络"],[0,[],0," 。简化的网络结构如下："]]],[10,1],[1,"p",[[0,[],0,"右边服务器是 C&C Server，DDG 中称它为 "],[0,[0],1,"xhub"],[0,[],0," 节点，是一个超级节点，除了与各 Bot 相同的 P2P 通信功能之外，还具有以下功能："]]],[3,"ul",[[[0,[],0,"统计各 Peer 信息( Peer 会向它注册，上传自身信息)"]],[[0,[],0,"协助各 Peer 寻找到对方，xhub 节点里保存了大量的 P2P Peers 列表，它会在 Bot 向其注册的时候把一部分列表分享给 Bot；而每个 Bot 最多保存 200 条 Peers "],[0,[4],1,"<ip:port>"],[0,[],0," 列表；"]],[[0,[],0,"承载原始的恶意 Shell 脚本、主样本和其他组件的下载服务。"]]]],[1,"p",[[0,[],0,"我们的 Botnet 跟踪系统追踪到 DDG 当前的 "],[0,[0],1,"一部分"],[0,[],0," P2P Nodes(失陷主机)，最近每天平均可以追踪到 900 个 Nodes，其中可验证存活的比例达 98%:"]]],[10,2],[1,"p",[[0,[],0,"根据我们合作伙伴提供的统计数字，中国大陆境内活跃的 Bot 有17,590 个。根据我们的追踪数据，中国大陆境内的 Bot 数量约占总量的 86%，以此反推，DDG 目前在全球范围内的 Bot 数量约 20,000。"]]],[1,"p",[[0,[],0,"DDG 支持的传播途径，有以下 4 个："]]],[3,"ul",[[[0,[],0,"SSH 服务暴破；"]],[[0,[],0,"Redis 未授权访问漏洞；"]],[[0,[],0,"针对 Nexus Repository Manager 3 RCE 漏洞(CVE-2019-7238)的利用；"]],[[0,[],0,"针对 Supervisord RCE 漏洞(CVE-2017-11610)的利用。"]]]],[1,"p",[[0,[],0,"根据我们的追踪数据，DDG 最近一段时间一直没有开启公网传播，而只利用 SSH 暴破在内网传播。其内网传播的开关，在 "],[0,[0,5],2,"slave config"],[0,[],0," 里设置："]]],[10,3],[1,"p",[[0,[],0,"另外，根据我们对 DDG 过去一年的追踪统计，它从 2019.1 月份至今共用了 24 个 C&C，每个 CC 的活跃时段统计如下："]]],[10,4],[1,"p",[[0,[],0,"最后，旧版本的 DDG Botnet Tracker 已经无法工作，我们现已将其开源："]]],[1,"p",[[0,[0,6],2,"https://github.com/0xjiayu/DDGBotnetTracker/tree/master/v1"]]],[1,"p",[[0,[],0,"并且公开最新版本 DDG 相关的运行日志、相关数据和相应解析工具，以及一个基于 P2P 机制的 Pre-Built Demo Tracker Program(ELF Binary file):"]]],[1,"p",[[0,[0,7],2,"https://github.com/0xjiayu/DDGBotnetTracker/tree/master/v2"]]],[1,"h2",[[0,[0],1,"2. 样本关键行为分析"]]],[1,"p",[[0,[],0,"就 DDG 当前最新的 v5023 版本来看，DDG 有一些有趣的新特性："]]],[3,"ul",[[[0,[],0,"用特定算法生成一个 4 字符的目录名，并在 "],[0,[4],1,"/var/lib/"],[0,[],0," 或 "],[0,[4],1,"/usr/local/"],[0,[],0," 下创建相应的"],[0,[0],1,"隐藏目录"],[0,[],0,"作为工作目录，存放本地配置信息和从 C&C 或 P2P Peer 下载到的文件或数据；"]],[[0,[],0,"自定义 Base64 编码的编码表(Alphabet)；"]],[[0,[],0,"对关键数据和文件频繁使用编码(Base64/Adler32)、加密(AES)、压缩(Gzip)和数字签名(RSA/ED25519)手段，以对抗分析和数据伪造；"]],[[0,[],0,"除了以前的 "],[0,[0],1,"slave"],[0,[],0," Config 来控制 Bot 执行挖矿和传播任务，又新增了一个 "],[0,[0],1,"jobs"],[0,[],0," Config 文件来对 Bot 进行更复杂的任务控制；"]],[[0,[],0,"自研 P2P 协议，并以此在 Nodes 之间交换最新各自持有的 C&C、Nodes 列表和恶意任务指令配置等信息，还可以在 Peers 之间传播恶意组件。"]]]],[1,"p",[[0,[],0,"以 DDG v5023 版本的样本为例，其主要的执行流程，从恶意 Shell 脚本文件 "],[0,[0],1,"i.sh"],[0,[],0," 开始，如下所示："]]],[10,5],[1,"h3",[[0,[0],1,"2.1 恶意 Shell 脚本 i.sh"]]],[1,"p",[[0,[],0,"DDG 每成功入侵一台主机，就会先执行这个 "],[0,[0],1,"i.sh"],[0,[],0," 脚本。 "],[0,[0],1,"i.sh"],[0,[],0," 主要执行 3 个任务："]]],[3,"ol",[[[0,[],0,"篡改 Cron job，在 "],[0,[4],1,"/var/spool/cron/root"],[0,[],0," 和 "],[0,[4],1,"/var/spool/cron/crontabs/root"],[0,[],0," 写入恶意 Cron job，每 15 分钟下载 "],[0,[4],1,"hxxp://67.205.168.20:8000/i.sh"],[0,[],0," 并运行；"]],[[0,[],0,"下载并执行最新的 DDG 主样本文件；"]],[[0,[],0,"检测目录 "],[0,[4],1,"/usr/local/bin/"],[0,[],0," | "],[0,[4],1,"/usr/libexec/"],[0,[],0," | "],[0,[4],1,"/usr/bin/"],[0,[],0," 是否可写。"]]]],[1,"h3",[[0,[0],1,"2.2 初始化"]]],[1,"p",[[0,[],0,"DDG 的主样本由 Go 语言编写，编译出来的原始 ELF 文件体积比较大。DDG 的作者就用 UPX Packer 把原始 ELF 文件加壳，一方面一定程度上可以对抗自动化分析，另一方面缩小了文件体积，便于网络传输。"]]],[1,"p",[[0,[],0,"DDG 的主样本里实现了多个工作模块，有一些后续用到的数据和全局变量，涉及相应的初始化工作。比如生成全局自定义 Alphabet 的 Base64 编解码句柄，解析样本内硬编码的 "],[0,[0],1,"xhub"],[0,[],0,"(C&C) 相关数据和 "],[0,[0],1,"xSeeds"],[0,[],0,"(P2P Seed Nodes List) 数据，解析样本内硬编码的弱口令字典(用于后续的 SSH 服务暴破)等等。"]]],[1,"h4",[[0,[0],1,"2.2.1 自定义 Alphabet 的 Base64 编码"]]],[1,"p",[[0,[],0,"在函数 "],[0,[4],1,"ddgs_common_init"],[0,[],0," 中，DDG 基于自定义的 Alphabet 创建了一个全局的 Base64 编解码句柄："]]],[10,6],[1,"p",[[0,[],0,"自定义的 Alphabet 为："]]],[1,"p",[[0,[0],1,"\"eDy54SH1N2s-Y7g3qnurvaTW_0BlCMJhfb6wtdGUcROXPAV9KEzIpFoi8xLjkmZQ\""]]],[1,"p",[[0,[],0,"生成的全局 Base64 编解码句柄，会在后续被用来："]]],[3,"ul",[[[0,[],0,"解析内置硬编码的 "],[0,[0],1,"xhub"],[0,[],0," 和 "],[0,[0],1,"xSeeds"],[0,[],0," 数据；"]],[[0,[],0,"解析后续从服务器下载到的文件 Sig、与 P2P Nodes 通信时的自定义 HTTP Header("],[0,[0],1,"X-Hub"],[0,[],0," / "],[0,[0],1,"X-Seed"],[0,[],0," / "],[0,[0],1,"X-Sig"],[0,[],0,")"]],[[0,[],0,"响应其他 P2P Peer 的请求时，编码自定义 HTTP Header("],[0,[0],1,"X-Hub"],[0,[],0," / "],[0,[0],1,"X-Seed"],[0,[],0," / "],[0,[0],1,"X-Sig"],[0,[],0,")"]]]],[1,"h4",[[0,[0],1,"2.2.2 解析内置 xhub/xSeeds"]]],[1,"p",[[0,[],0,"在 DDG 中，"],[0,[0],1,"xSeeds"],[0,[],0," 就是 P2P Seed Nodes 列表，每个 Bot 都持有一份 Seed Nodes List，里面内置了 200 个 P2P Node "],[0,[4],1,"<ip:port>"],[0,[],0," 列表。Bot 可以与他们通信交换数据。"]]],[1,"p",[[0,[0],1,"xhub"],[0,[],0," 即 C&C 服务器信息，C&C 服务器可以指定多个。"]]],[1,"p",[[0,[],0,"在函数 "],[0,[4],1,"ddgs_xnet_init_0"],[0,[],0," 中，DDG 解析了内置的 "],[0,[0],1,"xhub"],[0,[],0," 和 "],[0,[0],1,"xSeeds"],[0,[],0," 数据，还对 "],[0,[0],1,"xhub"],[0,[],0," 数据用 "],[0,[0],1,"ed25519"],[0,[],0," 算法校验是否被伪造。"],[0,[0],1,"xhub"],[0,[],0," 的解析、验证汇编代码如下："]]],[10,7],[1,"p",[[0,[],0,"DDG v5023 样本中内置的 "],[0,[0],1,"xhub"],[0,[],0," 数据为经过自定义 Alphabet Base64 编码过的字串："]]],[10,8],[1,"p",[[0,[],0,"经过 Base64 解码后，还需要用 "],[0,[8],1,"msgPack"],[0,[],0," 进行一层解码，才能得到 "],[0,[0],1,"xhub"],[0,[],0," 的原始明文数据。解析上面的编码数据，可以得到两个关键数据："]]],[3,"ul",[[[0,[],0,"C&C 列表，目前只有一个："],[0,[0],1,"67.205.168.20:8000"],[0,[],0,"；"]],[[0,[],0,"ed25519 Signature："]]]],[10,9],[1,"p",[[0,[],0,"样本中解出 C&C 列表和 ed25519 的 Signature 之后，就会用相反的步骤用 ed25519 算法来校验 C&C 列表是否被伪造。校验时用到的 RSA 公钥为："]]],[10,10],[1,"p",[[0,[0],1,"xhub"],[0,[],0," 数据的解析方法："],[1,[],0,0],[0,[9,0],2,"https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/tools/xsig_verify.go"]]],[1,"p",[[0,[0],1,"xSeeds"],[0,[],0," 包含 200 条 P2P Node "],[0,[4],1,"<ip:port>"],[0,[],0," 列表，数据量比较大，共 0x8E2 Bytes："]]],[10,11],[1,"p",[[0,[],0,"而且 DDG 样本对 xSeeds 解析步骤与 xhub 的解析略有不同，经过分析，xSeeds 经过以下 3 层处理，而不用 ed25519 校验："]]],[3,"ol",[[[0,[],0,"msgpack 序列化编码；"]],[[0,[],0,"gzip 压缩"]],[[0,[],0,"自定义 Alphabet 的 Base64 编码"]]]],[1,"p",[[0,[0],1,"xSeeds"],[0,[],0," 数据的解析方法："]]],[1,"p",[[0,[10,0],2,"https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/tools/dec_seeds.go"]]],[1,"h4",[[0,[0],1,"2.2.3 解析内置弱口令字典"]]],[1,"p",[[0,[],0,"在全局初始化函数 "],[0,[4],1,"ddgs_global_init"],[0,[],0," 中，DDG 调用了一个函数 "],[0,[4],1,"ddgs_global_decodePasswords"],[0,[],0," ，在这个函数中解密并校验内置的弱口令字典，这些弱口令将在后续传播阶段被用来暴破 SSH 服务(暴破 SSH 服务时用的用户名为 "],[0,[0],1,"root"],[0,[],0," )："]]],[10,12],[1,"p",[[0,[],0,"弱口令数据是经过 "],[0,[0],1,"AES"],[0,[],0," 加密、gzip 压缩后内置在样本中的，密文数据共 0x2BCFE Bytes："]]],[10,13],[1,"p",[[0,[],0,"DDG 会首先对上面数据用 gzip 解压，解压后密文数据结构如下："]]],[10,14],[1,"p",[[0,[],0,"DDG 会先用 ed25519 对上述密文数据的 Sha256 值进行校验(公钥与前面校验 "],[0,[0],1,"xhub"],[0,[],0," 时用的公钥相同)，校验成功之后才会用 AES 算法将其解密，解密后得到一份 "],[0,[0],1,"17,907"],[0,[],0," 条密码的弱口令字典:"]]],[10,15],[1,"h4",[[0,[0],1,"2.2.4 创建全局 ed25519 密钥对"]]],[1,"p",[[0,[],0,"在函数 "],[0,[0],1,"ddgs_global_init"],[0,[],0," 中，DDG 还有另外一项关键全局变量的初始化工作：创建一对全局 ed25519 密钥，用以在后续存取 BoltDB 中数据时对数据进行签名和校验："]]],[10,16],[1,"p",[[0,[],0,"创建密钥对的种子如下："]]],[10,17],[1,"h3",[[0,[0],1,"2.3 创建工作目录"]]],[1,"p",[[0,[],0,"旧版本的 DDG 会直接把当前用户的 HOME 目录作为自己的工作目录，主要是在此目录下创建隐藏的 "],[0,[11,0],2,"BoltDB"],[0,[],0," 文件，文件中存放 Hublist 数据(旧版本的 P2P Node List)。现在新版本的 DDG 会用特定算法生成目录名，并在 "],[0,[4],1,"/var/lib/"],[0,[],0," 或 "],[0,[4],1,"/usr/local/"],[0,[],0," 目录下创建相应的"],[0,[0],1,"隐藏目录"],[0,[],0,"。Go 语言实现的工作目录名生成算法如下(假设当前 DDG 二进制样本文件的路径为 "],[0,[4],1,"/root/ddgs_x64"],[0,[],0,")："]]],[1,"p",[[0,[12,0],2,"https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/tools/gen_workdir_name.go"]]],[1,"p",[[0,[],0,"上面程序的执行结果是 "],[0,[0],1,"jsfc"],[0,[],0," ，那么 DDG 就会尝试创建目录 "],[0,[4],1,"/var/lib/.jsfc"],[0,[],0,"，后续工作目录的结构如下："]]],[10,18],[1,"p",[[0,[],0,"其中 "],[0,[4],1,".local"],[0,[],0," 文件即为 BoltDB 格式的文件，其它的还有从 C&C 服务器上下载到的恶意挖矿程序("],[0,[0],1,"wordpress"],[0,[],0,") 、编译好的 Busybox ELF 文件以及它们相应的数字签名。"]]],[1,"h3",[[0,[0],1,"2.4 BoltDB 文件"]]],[1,"p",[[0,[11,0],2,"BoltDB"],[0,[],0," 是一个基于内存的小型 KV 数据库，其数据内容可以落地到磁盘文件。DDG 旧版本中用 BoltDB 来存放明文 Hublist 数据。在 DDG 最新的 BoltDB 数据库中，有一个 "],[0,[0],1,"Bucket"],[0,[],0," 名为 "],[0,[0],1,"xproxy"],[0,[],0,"。"],[0,[0],1,"xproxy"],[0,[],0," 里存放了 3 份数据："]]],[3,"ol",[[[0,[0],1,"hubsig"],[0,[],0,": xhub 信息；"]],[[0,[0],1,"seeds"],[0,[],0,": xSeeds 数据；"]],[[0,[0],1,"port"],[0,[],0,": 本地在 (30000~65535) 范围内随机监听的 TCP 端口，用来相应其他 P2P Nodes 的通信请求。"]]]],[1,"p",[[0,[],0,"DDG 对这 3 份数据，每一份都做了如前文内置弱口令字典数据同样的处理，经过 msgpack 序列化编码后再用 AES 加密，重组加密数据后再存入 BoltDB。重组的密文数据结构也与弱口令字典密文数据结构相同:"]]],[10,19],[1,"p",[[0,[],0,"不同的是，DDG 对 BoltDB 中数据的签名和校验用到的全局密钥，是函数 "],[0,[0],1,"ddgs_global_init"],[0,[],0," 用样本中硬编码的种子数据生成的。"]]],[1,"h3",[[0,[0,0],2,"2.5 监控关键文件/目录"]]],[1,"p",[[0,[],0,"DDG 利用 "],[0,[13,0],2,"fsnotify"],[0,[],0," 框架监控以下文件，在运行期间防止被别的进程改动，用以保护自己的持久化配置："]]],[3,"ul",[[[0,[4],1,"/root/.ssh/authorized_keys"]],[[0,[4],1,"/var/spool/cron/root"]],[[0,[4],1,"/var/spool/cron/crontabs/root"]]]],[10,20],[1,"h2",[[0,[0],1,"3. P2P 机制"]]],[1,"p",[[0,[],0,"DDG 执行完上述关键步骤，DDG Bot 就开始了与其他 Bot 之间的 P2P 通信。DDG 的 P2P 机制特性如下："]]],[3,"ul",[[[0,[],0,"Peers 首先会向 xhub 注册"]],[[0,[],0,"Peers 之间有特有的 Ping/Pong 机制"]],[[0,[],0,"Peers 之间可以共享各自持有的 C&C 和 Peers 列表数据；"]],[[0,[],0,"Peers 之间可以传播 "],[0,[0],1,"slave"],[0,[],0," config 和 "],[0,[0],1,"jobs"],[0,[],0," config 数据；"]],[[0,[],0,"Peers 之间可以传播其他 Payload 或组件，比如恶意挖矿程序、已编译的 Busybox 二进制文件"]]]],[1,"h3",[[0,[0],1,"3.1 准备工作"]]],[1,"p",[[0,[],0,"在进行 P2P 通信之前，DDG 会有两项关键的准备工作："]]],[3,"ul",[[[0,[],0,"生成一个随机域名"]],[[0,[],0,"生成自己的 Peer UID"]]]],[1,"p",[[0,[],0,"随机域名的的生成规则是："],[0,[4],1,"<RAND 3 LowChars>.<RAND [5-7] Lowchars>.com"],[0,[],0,"， 如 "],[0,[4],1,"kez.tirueg.com"],[0,[],0," 。"]]],[1,"p",[[0,[],0,"UID 的形式： "],[0,[4],1,"VERSION_NUM.UID_HASH"],[0,[],0," ，如 "],[0,[4],1,"5023.dd9b2f57af3be6b6276606d4f37e4a5b"],[0,[],0," 。"]]],[1,"p",[[0,[0],1,"UID_HASH"],[0,[],0," 的生成规则，则是综合计算 Host information 和 网卡信息的 MD5 值得出， Go 语言实现如下："]]],[1,"p",[[0,[14],1,"https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v1/lib/util.go#L40"]]],[1,"h3",[[0,[0],1,"3.2 Peer <--> xhub"]]],[1,"p",[[0,[],0,"DDG 会首先向 xhub 如下 HTTP POST 请求："]]],[10,21],[1,"p",[[0,[],0,"以上 HTTP 通信中，"]]],[3,"ul",[[[0,[],0,"Request Header "],[0,[0],1,"Host"],[0,[],0,"，即为事先随即生成的域名。值得一提的是，"],[0,[0],1,"这个域名不会被 DNS 服务解析，因为以上 HTTP 通信是复用了已建立的 TCP Socket Session，在已有 TCP Socket 连接上 Send/Recv 数据并把数据用 HTTP 协议来解析而已"],[0,[],0,"。攻击者这样做，可能是为了逃避或混淆某些安全设备的流量检测；"]],[[0,[],0,"Request Header "],[0,[0],1,"X-Hub"],[0,[],0,"，即为 DDG 样本当前持有的硬编码在样本中的 xhub 信息，详见 2.2.2 节；"]],[[0,[],0,"Request Header "],[0,[0],1,"X-Port"],[0,[],0,"，即为 DDG 样本当前随即开启并监听的 P2P 通信端口；"]],[[0,[],0,"Request Header "],[0,[0],1,"X-Uid"],[0,[],0,"，即事先生成的 UID；"]],[[0,[],0,"Request Header "],[0,[0],1,"X-Relay"],[0,[],0,"，是 DDG 综合 "],[0,[0],1,"X-Uid"],[0,[],0," 和 "],[0,[0],1,"X-Port"],[0,[],0," 字段的值通过 "],[0,[0],1,"Adler"],[0,[],0," 算法算出来的校验值；"]],[[0,[],0,"Response Header "],[0,[0],1,"X-Seed"],[0,[],0,"，是对方从自己持有的 Peers 列表中随机选取的 20 个 Peers 地址列表信息，DDG 样本收到后会合入自身持有的 200 个 Peers 列表，总数不超过 200；"]],[[0,[],0,"Response Header "],[0,[0],1,"X-Hub"],[0,[],0,"，是对方持有的 "],[0,[0],1,"xhub"],[0,[],0," 信息，DDG 样本收到后会用它替换掉自身持有的 "],[0,[0],1,"xhub"],[0,[],0," 信息。"]]]],[1,"p",[[0,[],0,"最后，HTTP 响应中的 "],[0,[0],1,"jobs"],[0,[],0," config 数据，是经过 msgpack 序列化编码后又用 AES 加密过的数据，数据的组织结构与 gzip 解压后的样本内置弱口令字典数据相同，解析过程也完全相同。最新解密后的 "],[0,[0],1,"jobs"],[0,[],0," config 数据见："]]],[1,"p",[[0,[0,15],2,"https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/data/jobs.json"]]],[1,"p",[[0,[],0,"从 "],[0,[0],1,"jobs"],[0,[],0," Config 的内容来看，攻击者对 Bot 的行为控制的更为复杂精细，该配置文件的核心功能在流程图里已有说明，此处总结如下："]]],[3,"ul",[[[0,[],0,"针对每个低级版本 DDG 都有不同的处理，或 Kill 或 Discard 或 Upgrade；"]],[[0,[],0,"引入了 Busybox 执行更复杂的命令，主要用来干掉竞争对手；"]],[[0,[],0,"干掉竞争对手的姿势复杂多样，杀进程、禁用服务、清除 SSH Key、删除 Cron Jobs，重置 Lock File 等等，最显眼的是通过篡改 "],[0,[4],1,"/etc/hosts"],[0,[],0," 文件屏蔽一大批竞争对手需要访问的域名。"]]]],[1,"p",[[0,[],0,"其中，最显眼的一部分配置，是 DDG 通过篡改 "],[0,[4],1,"/etc/hosts"],[0,[],0," 文件屏蔽竞争对手要访问的域名，大多数都是 "],[0,[16,0],2,"LSDMiner"],[0,[],0," 和 "],[0,[17,0],2,"systemdMiner"],[0,[],0," 相关的域名："]]],[10,22],[1,"h3",[[0,[0],1,"3.3 Peer <--> Peer"]]],[1,"p",[[0,[],0,"在与 "],[0,[0],1,"xhub"],[0,[],0," 通信过后，DDG 样本就开始与自身持有的 200 个 Peers 进行通信。 Peers 之间的通信有 4 个关键步骤："]]],[3,"ol",[[[0,[],0,"Ping/Pong"]],[[0,[],0,"拉取对方的 "],[0,[0],1,"slave"],[0,[],0," config 和 "],[0,[0],1,"jobs"],[0,[],0," config；"]],[[0,[],0,"拉取对方的其他恶意组件，比如恶意矿机程序和 Busybox 二进制程序；"]],[[0,[],0,"响应(服务)别的 Peers 的以上 3 种请求"]]]],[1,"h4",[[0,[0],1,"3.3.1 Ping/Pong"]]],[1,"p",[[0,[],0,"DDG 一旦成功与某个 Peer 建立连接，会至少经过 2 轮 Ping/Pong 通信，间隔 "],[0,[0],1,"30s"],[0,[],0," ，对端响应的 Pong 包与 Ping 包完全相同，长度为 "],[0,[0],1,"3 Bytes"],[0,[],0,"："]]],[10,23],[1,"p",[[0,[],0,"3 Bytes 的 Ping 包生成规则如下："]]],[3,"ul",[[[0,[],0,"第 1 字节为 0x00；"]],[[0,[],0,"第 3 字节在 (0, 0xFF] 中随机产生；"]],[[0,[],0,"第 2 字节为第 3 字节与 "],[0,[0],1,"0x42"],[0,[],0," XOR 运算的结果。"]]]],[1,"p",[[0,[],0,"Go 语言代码描述如下："]]],[10,24],[1,"h4",[[0,[0],1,"3.3.2 传播 slave/jobs config data"]]],[1,"p",[[0,[],0,"经过 2 轮的 Ping/Pong 通信，DDG 会随机选成功通信的 Peers 向对方请求拉取 "],[0,[0],1,"slave"],[0,[],0," config 或 "],[0,[0],1,"jobs"],[0,[],0," config，请求方式类似于上面向 "],[0,[0],1,"xhub"],[0,[],0," 请求 "],[0,[0],1,"jobs"],[0,[],0," config，以拉取对方 "],[0,[0],1,"slave"],[0,[],0," config 为例："]]],[10,25],[10,26],[1,"p",[[0,[],0,"值得注意的两点："]]],[3,"ol",[[[0,[],0,"DDG Peers 之间发送 HTTP Post 请求，HTTP Request Header 中相比请求 "],[0,[0],1,"xhub"],[0,[],0," 时少了一个 "],[0,[0],1,"X-Port"],[0,[],0,"。"]],[[0,[],0,"发往 Peer 的 HTTP 请求，"],[0,[0],1,"复用了前面 Ping/Pong 通信中用到的 TCP 连接"],[0,[],0,"，即同一个 TCP Session，刚开始用来 Ping/Pong 交互，后面直接基于这个 TCP Session 发送 HTTP 请求、接收 HTTP 响应，所以 HTTP Header 中 "],[0,[0],1,"Host"],[0,[],0," 字段里"],[0,[0],1,"随机生成的域名并不会经过 DNS 解析"],[0,[],0,"。"]]]],[1,"p",[[0,[],0,"对端响应的 "],[0,[0],1,"slave"],[0,[],0," config 数据，是经 msgpack 序列化编码过的二进制数据，数据格式与旧版本变化不大，解码方式可以参考 "],[0,[18,0],2,"以 P2P 的方式追踪 DDG"],[0,[],0," 。最新解码后的 "],[0,[0],1,"slave"],[0,[],0," config 数据已上传到 Github："]]],[1,"p",[[0,[0,5],2,"https://github.com/0xjiayu/DDGBotnetTracker/blob/master/v2/data/slave.json"]]],[1,"p",[[0,[],0,"解码后的 "],[0,[0],1,"slave"],[0,[],0," config 数据包含自身数据的数字签名，DDG 样本会以此校验 "],[0,[0],1,"slave"],[0,[],0," config 数据，校验时用到的 RSA 公钥硬编码在样本中："]]],[10,27],[1,"h4",[[0,[0],1,"3.3.3 下载恶意组件"]]],[1,"p",[[0,[],0,"没有被选中拉取 "],[0,[0],1,"slave"],[0,[],0," config 和 "],[0,[0],1,"jobs"],[0,[],0," config 的 Peer，DDG 会继续与他们的 Ping/Pong 通信。从某个 Peer 拉取的 "],[0,[0],1,"slave"],[0,[],0," config 中指定了恶意矿机程序 Miner 的下载 URI、本地存放路径和 MD5，DDG 接下来会随机选取另一个 Ping/Pong 中的 Peer，"],[0,[0],1,"复用 Ping/Pong 的 TCP Session"],[0,[],0,"，通过 HTTP 协议向其发起 Miner 的下载请求："]]],[10,28],[1,"p",[[0,[],0,"下载到的恶意矿机程序，DDG 不仅会将其存放到 "],[0,[0],1,"slave"],[0,[],0," config 中指定的本地路径，还会连同 HTTP Response Header 中的 "],[0,[0],1,"X-Sig"],[0,[],0," 内容作为矿机程序的数字签名数据一起放到自己创建的"],[0,[0],1,"工作目录"],[0,[],0,"中："]]],[10,29],[1,"p",[[0,[],0,"DDG 从 Peer 下载已编译的 Busybox 程序的过程同上。"]]],[1,"h4",[[0,[0],1,"3.3.4 响应其他 Peers 的请求"]]],[1,"p",[[0,[],0,"P2P 协议中，Peers 之间的功能、角色是对等的。DDG 样本既然可以从其他 Peer 那里拉取数据和文件，自然也可以响应其他 Peer 的对等请求。"]]],[1,"p",[[0,[],0,"当 Peer 来请求 "],[0,[0],1,"slave"],[0,[],0," config或 "],[0,[0],1,"jobs"],[0,[],0," config 时，DDG 样本会从内存中整理好一份自己持有的数据，经过与前面阐述的解码、解密相反的编码、加密处理，返回给 Peer。"]]],[1,"p",[[0,[],0,"当 Peer 来请求下载矿机程序(比如上面的 "],[0,[0],1,"wordpress"],[0,[],0," 文件)或已编译好的 Busybox 程序时，DDG 样本会检查自己的"],[0,[0],1,"工作目录"],[0,[],0,"的 "],[0,[0],1,"cache"],[0,[],0," 子目录中是否已经缓存了相应文件，如果缓存了相应文件并且签名有效，就会返回给 Peer。"]]],[1,"p",[[0,[],0,"另外的问题是，很多 DDG 控制的失陷主机都在内网，不一定可以穿透 NAT 对外提供这种服务。所以跟踪程序无法通过 P2P 机制跟踪到所有的 Bot。"]]],[1,"h4",[[0,[0],1,"3.3.5 Peers 之间的 Proxy 特性"]]],[1,"p",[[0,[],0,"DDG 的 P2P 机制中，还有一个有意思的特性：Peer 的 Proxy 功能。"]]],[1,"p",[[0,[],0,"当某个 Peer 来请求下载矿机程序或 Busybox 程序时，如果 DDG 经过检查发现自己工作目录中暂时不存在相应文件，它就会把自己作为一个 Proxy，向自己成功连接的其他 Peer 随机发送相应的下载请求。如果成功下载，就会返回给向自己请求下载文件的 Peer。"]]],[1,"h2",[[0,[0],1,"4. 总结"]]],[1,"p",[[0,[],0,"DDG 经过两年多的发展，从最初简单的挖矿木马，到借用第三方协议框架构建简单的 P2P 网络，到现在自研 P2P 协议，已经演化成了一个复杂的 P2P 僵尸网络。可能这是第一个 P2P 结构的挖矿僵尸网络。如果你怀疑自己的主机被 DDG 入侵，建议从以下几个方面排查、处置："]]],[3,"ol",[[[0,[],0,"检查 "],[0,[4],1,"/var/spool/cron/root"],[0,[],0," 和 "],[0,[4],1,"/var/spool/cron/crontabs/root"],[0,[],0," 是否存在恶意 Cron Jobs"]],[[0,[],0,"检查 "],[0,[4],1,"~/.ssh/authorized_keys"],[0,[],0," 文件中是否存在恶意 SSH Public Key"]],[[0,[],0,"检查 "],[0,[4],1,"/var/lib/"],[0,[],0," , "],[0,[4],1,"/usr/local/"],[0,[],0," 目录下的隐藏目录，是否存在 DDG 工作目录"]],[[0,[],0,"检查 "],[0,[4],1,"/usr/bin/"],[0,[],0,", "],[0,[4],1,"/usr/local/bin/"],[0,[],0," 目录下是否存在可疑 ELF 文件"]],[[0,[],0,"检查 "],[0,[4],1,"/etc/hosts"],[0,[],0," 文件是否被写入恶意内容"]]]],[1,"h2",[[0,[0],1,"5. IoCs"]]],[1,"p",[[0,[],0,"C&C:"]]],[10,30],[1,"p",[[0,[],0,"URLs:"]]],[10,31],[1,"p",[[0,[],0,"MD5s:"]]],[10,32]],"ghostVersion":"3.0"}