本文章涉及APT34远控实现的具体复现步骤及操作,具有攻击性,未经事先双方同意,使用APT34攻击目标的行为是非法的,本研究仅供安全研究与测试之用。严禁用于非法用途,所带来的法律风险与本文无关。
2019年4月18日,一位名为Lab Dookhtegan的用户在国外聊天频道Telegram公布了APT34相关基础设施、黑客工具、成员信息及受害者信息等内容。其中APT34也被称为OilRig (Crambus,“人面马”组织,Cobalt Gypsy),是一个来自于伊朗的APT组织,该组织从2014年开始活动,主要针对中东地区,攻击范围主要针对政府、金融、能源、电信等行业,受害者实体主要有:迪拜媒体公司,阿提哈德航空公司,阿布扎比机场,阿联酋国家石油公司,兰普雷尔能源公司,科威特阿米里迪万湾,阿曼行政法院,阿联酋总理办公室,国家巴林安全局等。
本次泄露的黑客工具列表如下:
工具名称 | 描述 |
---|---|
PoisonFrog | 旧版木马 |
Glimpse | 基于powershell的新版木马,DNS远控工具。 |
HyperShell | Webshell |
HighShell | Webshell |
MinionProject | 钓鱼工具包,fox管理界面,加载了HighShell模块 |
Webmask | DNS隧道,DNSpionage的主要工具,用于DNS修改 |
本文将对APT34远控组件Glimpse新版木马进行技术分析。
Glimpse是一套使用DNS隧道的远控工具,下图为Glimpse文件列表。
文件夹中除了Readme.txt项目部署说明文件外,分为Agent、Panel和Server三部分:Agent部分为客户端程序;Server部分为服务器端程序;Panel为该工具的图形面板,用于管理Server与Agent的通信(用于Windows系统下,本次实验服务器端环境为Linux,因此未用到panel工具)。
其中Agent为受控主机也被称为客户端,Server为控制节点也被称为C2服务端;双方的通信是通过DNS隧道A/TXT记录进行命令的分发与命令执行后的回传。 在实验设置中,使用内网的虚拟机作为Agent,而Server使用国外的VPS,与VPS的IP绑定的域名为fengrou2019.club。
为了使得域名与自建的C2服务器ip对应,需要在阿里云域名服务中创建一条A记录将fengrou2019.club的域名指向C2服务器的vps的IP地址,主机记录为“@”意思是直接解析主域名 fengrou2019.club。同时创建一条NS记录,将所有fengrou2019.club的子域名都指向主域名。主机记录为“*”意思是泛解析,匹配其他所有域名 。这样做的目的是能够将受控主机的所有关于fengrou2019.club的自定义子域名都指向C2服务器进行解析。C2服务器接收到之后做出响应,实现通信过程。
阿里云域名服务设置如下如所示:
1、启动脚本为runner_.vbs,用于启动powershell主脚本;
2、主体脚本为dns_main.ps1,用于与服务端通信;
dns_main.ps1中通信的域名需指定为fengrou2019.club
运行dns_main.ps1,程序生成特定目录PUBLIC\Libraries\ 61076a9f9f\(GUID由此脚本生成),并在目录下创建receivebox、sendbox、done等子文件夹,通过这些子目录下的文件读写来实现与服务端的通信。
(1)复制“APT34”目录到指定文件夹
#目前apt34代码放在如下目录中
/home/APT34
(2)进入”Glipse”的服务器端代码块中
cd /home/APT34/Glimpse/server
(3)创建“home”文件夹,并将“srvr.js”文件拷贝至“home”文件夹下
mkdir home
cp srvr.js ./home
(4)安装node.js和npm,并完成各个包的安装
apt-get install nodejs npm
#一路enter下去,在本文件夹下生成一个package.json的文件。
npm init
#安装如下依赖包,会在文件夹下自动生成node_modules文件夹。
npm install --save body-parser cookies child_process dnsd webix express ip fs path sqlite3 http portscanner child_process async ejs express-fileupload dateformat multer file-base64 flat-file-db moment busboy
#安装forever
npm install --save -g forever
(5)运行前查看端口状态。
netstat -ntulp | grep 53
开启服务前查询53端口状态,发现没有监听。
(6)修改服务端代码。
将服务端DNS解析的域名改为C2服务器的域名:fengrou2019.club
(7)开启服务端。
forever start srvr.js
(8)查看服务端开启后的端口状态。
查询53端口状态,正在监听。
至此服务端搭建完毕。
在服务端wait文件夹下存放主控端需要agent端进行的操作,通信完成后,文件存储位置从wait文件夹变为sended文件夹,表示已经发送成功。
文件夹receive存放收到的回传信息,文件名同命令文件名。
文件夹中的命令文件命名方式遵循代码中服务器端指令和操作的对应关系:文件结尾为0,agent端会在cmd中执行指令并回传结果;文件结尾为1则进行上传文件操作;结尾为其他则进行下载文件操作。
末尾字符 | 目的 | 描述 |
---|---|---|
0 | 执行命令 | 读取文件的内容并将其作为带有“cmd.exe”的命令运行。该命令的输出保存到名称以“proc”开头的文件中,并存储在“sendbox”文件夹中,客户端将发送给C2服务器 |
1 | 上传文件 | 读取文件的内容以获取要下载的文件的路径。将指定的文件复制到“sendbox”文件夹中的文件,以便将文件发送到C2服务器。 |
其他任意字符 | 下载文件 | 用于在系统上存储文件。该文件将移至“done”文件夹,该文件夹存储该文件以供将来使用。客户端将“200 <> [存储文件的路径]”写入“sendbox”文件夹中的文件,以通知C2该文件已成功下载。 |
3.1.1 末尾字符为0
(1)文件名为10100
当服务器和客户端第一次连接时,会在wait文件夹下创建一个名为10100的文件,执行完毕后转到sended文件夹,指令内容为whoami&ipconfig。
收到的内容存在receive文件夹中,在receive文件夹下打开10100文件
(2)文件名为test0
自行创建名为test0的文件存在wait文件夹下,指令内容为netstat,执行完毕后该文件存放位置变为sended。
收到的内容存在receive文件夹中,在receive文件夹下打开test0文件
3.1.2 末尾字符为1
文件名为test1:
创建名为test1的文件存在wait文件夹下,由于结尾为1,所以该指令的功能是将指令文件内容中指定的文件放入agent目录\sendbox下发送到服务端。
在agent端指定文件夹下创建文件this is under VMware.txt
test1文件内容为取出agent客户端指定文件夹下的指定文件的内容,执行完毕后由wait文件夹下转到sended文件夹中。
执行结果可以在receive文件夹下的test1文件中看到,server端已经接受了来自agent的文件。
3.1.3 末尾字符为其他
文件名为test:
创建名为test的文件存在wait文件夹下,由于结尾为t,所以该指令的功能是将server端文件发送到agent中done文件夹下保存。
执行结果可以在agent端done文件夹下的test文件中看到,agent端已经接受了来自server的文件。文件名为proctest,内容即为在server端创建的内容。
3.2.1 伪DNS构造
C2服务器与受控主机之间的命令分发与命令执行后信息回传是通过DNS隧道协议完成的,其中DNS协议中TXT记录主要用于C2服务器的命令分发,A记录主要用于受控主机将命令执行后的结果回传给C2服务器。为了把命令与信息融入到DNS请求中从而使C2服务器与受控主机双方顺利通信,需要精心构造特殊的域名。不仅如此,为了区分不同的受控主机,每台受控主机都会拥有一个独一无二的GUID用来标识,这个标识是通过Agent端脚本自动生成的。所有的DNS请求都是通过在GUID中随机添加偏移量插入数据包标识和操作类型字符。数据包标识是一个三位数字字符串,对应于受控主机传输数据的分片编号;操作类型是单个字符串,用于通知C2服务器执行某种通信类型。每个域名中都会有两个不变的字符“C”和“T”,在这两个字符之间包含了两个数字,分别对应数据包标识和操作类型字符在GUID中的偏移量。
在本示例中,C2使用的域名为“fengrou2019.club”,受控主机端GUID为:564b81fdbe。
根据抓取到的流量,DNS请求的类型主要分为两类:TXT记录类型,A记录类型。
其中TXT记录类型的DNS请求格式主要为:
actiondata.fengrou2019.club
而A记录类型的DNS请求格式主要为:
actiondata.data1.data2.fengrou2019.club
其中actiondata字段是每一次通信都必须构造的部分;信息传输时会构造剩下的data1,data2字段,后接服务器绑定的域名。
(1)actiondata
用来确定数据包分片的序号及C2服务器操作类型。
主要由以下部分组成:
位数 | 0-13 | 13-n | n+1 | n+2 | n+3 | n+4 |
---|---|---|---|---|---|---|
描述 | GUID+数据包标识+操作类型字符 | 随机 | C | 数据包标识偏移量 | 操作类型偏移量 | T |
(2)data1
主要用来保存受控主机执行命令后信息回传的内容。
A记录示例:
#传输的第一个数据包
564b000812fdbe0000E72C46T.COCTab33333233332222222222222222210100A4060AAAAAAAAAAAAAAAAA.33333210100A.fengrou2019.club
#传输的第二个数据包
5624b81f001dbe0000A9C82T.EBB4667676672566667725E88E9A23FBFD932F3F64079E4F730B7986CC06.33333210100A.fengrou2019.club
#传输的第三个数据包
5264b81f002dbe0000BD8C051C81T.2323333500E88E98E88E9822622333E1E7601DDA7986D36906C908390200.33333210100A.fengrou2019.club
#传输倒数第二个数据包
5642b81fd068be0000241D8C93T.466677654666730033007C9D035C175E4EDACEDA.33333210100A.fengrou2019.club
#传输的最后一个数据包
5264b80691fdbe00001B957C61T.COCTabCOCT.33333210100A.fengrou2019.club
可以观察整个A记录的流量data1的规律:第一个数据包前6个字符为“COCTab”;最后一个数据包字符为“COCTABCOCT”,表明数据包分片全部传输完毕;除去倒数第二个和最后一个数据包,剩下的data1域的字符长度固定为60。通过分析Agent代码得知,命令执行回传内容传输是通过分片的方式传输的,而且data1构成的编码机制步骤为:
1)创建两个空字符串;
2)将命令执行回传内容数据字节转换成16进制;
3)将每个16进制字节拆分成2个半字节;
4)将第一个半字节追加到第一个字符串中;
5)将第二个半字节追加到第二个字符串中;
6)将两个字符串合并连接到一起。
通过上面的步骤生成了data1域的固定60位字符串(最后一个数据包和倒数第二个数据包除外)。
可以使用以下Python脚本还原文本信息:
#!/usr/bin/env python
import binascii
def bin_to_asc(s):
result = list()
mid = len(s)/2
for i in range(mid):
x = s[i]+s[mid+i]
result.append(x)
res = "".join(result)
# print(res)
print(binascii.a2b_hex(res))
return binascii.a2b_hex(res)
if __name__ == "__main__":
#还原第一个数据包的data1域
s = "EBB4667676672566667725E88E9A23FBFD932F3F64079E4F730B7986CC06"
bin_to_asc(s)#运行结果为:Microsoft Windows [版本 6
(3)data2
主要用来保存指令的文件名称。
#传输第一个数据包
564b000812fdbe0000E72C46T.COCTab33333233332222222222222222210100A4060AAAAAAAAAAAAAAAAA.33333210100A.fengrou2019.club
根据上面的数据包可以得到data2为:33333210100A
data2域也适用于data1域的编码方式,运行data1的脚可以得到data2域的明文为:10100*
3.2.2 响应A记录值
服务端作为伪造的DNS服务器,响应Agent的DNS请求并回复指定ip字符串,不同的ip字符串指代不同的通信内容。ip字符串对应如下:
ip字符串内容 | 含义 |
---|---|
99.250.250.199 | 服务端响应新的agent并创建会话 |
199.250.250.99 | 服务端相应agent端ping信息 |
3.2.1.0 | 服务端有待发送文件 |
24.125.a.b | 服务端有待发送文件且文件名为a+b |
11.24.237.110 | 服务端无待发送文件 |
a.b.c.d | 服务端以DNS的A记录的方式分片发送数据,abc为数据内容,d为数据索引 |
1.2.3.0 | 服务端分片数据发送完成 |
a.2.3.b | 服务端正在接受agent端发送的分片数据,a为agent端id,b为分片编号 |
253.25.42.87 | 服务端已收到agent端发送的分片数据 |
3.2.3 响应TXT记录值
C2服务器作为伪造的DNS服务器,响应agent的TXT记录请求,并回复指定TXT记录值,TXT记录值包含指令类型及其指令内容。
具体TXT指令类型如下表所示。Agent客户端会根据字符“>”将每个TXT记录指令类型及指令内容分离开来,“>”字符左侧的数据用作指令,右侧的数据用指令内容。
TXT记录值含义 | 解释 |
---|---|
N | 闲置。将下一个DNS查询的操作类型设置为“W” |
S | 从C2接收数据。将数据部分解码为base64。将未来查询的操作类型设置为C2为“D”。 |
S000s | 使用“rcvd”+TXT记录值的值作为文件的文件名称,该文件将保存在“receivebox”文件夹中。将未来DNS查询的操作类型设置为C2为“D”。 |
E | 将“S”命令提供的字节写入由“S000s”命令产生的文件。打破脚本处理命令分发文件的循环。 |
C | 通过退出循环来取消通信。 |
3.3.1 抓取流量
抓取的流量中选取DNS流量,可以发现在短短的时间内,客户端与服务端频繁通信,以DNS协议的A记录和TXT记录为主要形式。
3.3.2 Action M
首先双方通信中出现了action字段为“M”的伪DNS请求。可以看做初始化的过程,服务端会创建文件夹wait、received、sended、sending、done存放发送和接收的信息。
ip字符串==99.250.250.199,表示服务端开始响应新的agent并创建会话。
3.3.3 Action W
等待需要执行命令的TXT请求,在注册阶段之后执行的第一个命令是标记为10100的命令,其内容为:“whoami&ipconfig / all”
3.3.4 Action D
等待需要执行命令的TXT请求。这是要实际执行的命令。Server以DNS TXT记录的形式分片发送服务端wait文件下的内容。其中“>”左侧为指令类型,即S命令,从C2接收数据。将数据部分解码为base64,并将未来查询的操作类型设置为C2为“D”(客户端即将执行命令)。“>”右侧为指令内容,“d2hvYW1pJmlwY29uZmlnIC9hbGw=”这是命令的base64编码格式,解码后的明文为“whoami&ipconfig /all”。
3.3.5 Action 2
action==2时,server以DNS A记录的形式分片接收agent发送的文件。在发送完所有数据后,代理会发送一个数据部分为COCTabCOCT 的DNS查询。该查询通知C2服务器木马完成了文件内容的发送。截获的流量num标号一共到069。
分析第一条:
分析最后一条:
基于网络层面检测:
1)通过抓取被控主机的流量,了解到C2服务器与被控主机之间通信的指令是通过构造子域名的方式进行的,然而DNS请求并不正常,是由数字和字母随机组成,可疑度较大,并且可以直接追溯到主控vps的域名和IP。
2)DNS请求的频率过于规律,每次DNS请求的时间间隔为50ms一次,及其容易被检测到。
3)DNS请求响应特征明显,例如A记录响应IP属于硬编码,容易被模式匹配。
基于主机层面检测:
1)是否在系统中查找到相应的隐藏目录结构?特别是文件后缀名为ps1的文件
2)检查系统是否被篡改DNS服务器地址?
3)检查Web服务器Web应用是否存在异常文件?比如后门Webshell等
1)不随意点开陌生人发送的邮件,及其附件内容,链接等。
2)经常修补计算机漏洞,保证系统设备软件的版本最新。
[2] https://unit42.paloaltonetworks.com/dns-tunneling-in-the-wild-overview-of-oilrigs-dns-tunneling/
*本文作者:DigApis,本文属 FreeBuf 原创奖励计划,未经许可禁止转载。