Hale 是一款僵尸网络命令与控制(C&C)监控/侦察工具,采用模块化设计,可轻松开发新模块来监控 C&C 服务器使用的新协议。Hale 自带基于 Twisted 开发的 IRC 和 HTTP 监控模块,可处理大量连接带来的扩展性问题。这些模块具有可配置的协议语法和机器人设置,也可根据您的需求进行修改。所有捕获的日志和文件都会保存到数据库中,对于 IRC 模式,还会记录被跟踪的 IP 地址。
为了隐藏操作者的位置,可以通过 SOCKSv5 代理建立连接,这可通过 Web 界面进行配置,所有日志以及统计图表和时间线也都可以在界面上浏览。该界面使用 Django 和 Google Visualization API 开发。Web 界面的一些附加功能包括支持 OAuth 的 RESTful API 以及搜索引擎。
Hale 的主要理念是通过创建传感器网络(Hale 监控器)来帮助僵尸网络追踪和研究协作。为改进这一理念,Hale 提供了一个 XMPP 机器人,可连接到集中式 XMPP 服务器,目前使用两个不同的群组房间:一个用于传感器之间的协调,另一个用于共享日志和文件。协调房间使用由僵尸网络设置中的唯一键生成的僵尸网络哈希,这样,具有相同哈希(身份)的两个僵尸网络就不必同时被监控,从而提高了利用率。为帮助第三方利用此网络,机器人可以加入协调房间,通过发送配置信息请求传感器开始跟踪尚未被跟踪的僵尸网络。此外,在共享房间中,第三方机器人可以实时获取传感器捕获的日志和文件。为便于查看日志历史,可以使用支持 GET 请求的 Web API。
Hale 具有以下依赖项:
Python == 2.6
Django == 1.2.1
Twisted == 10.1.0
GeoIP-Python == 1.2.4 (and GeoIP C lib)
Whoosh == 0.3.18
django-haystack == 1.0.1-final
django-piston == 0.2.3rc1
pefile == 1.2.10-63
sleekxmpp == 0.9Rrc1
wsgiref == 0.1.2
zope.interface == 3.6.1
oauth2 == 1.2.0
httplib2 == 0.6.0
此外,监控器需要与 django 所使用的数据库相对应的数据库后端驱动程序。安装完这些库后,请从此处下载源代码并解压到任意位置。
首先创建 Hale 将使用的数据库,数据库引擎可任选其一。如果您使用现有数据库,则可跳过此步骤。
下一步是安装与服务器引擎所使用的数据库相对应的 Python 数据库后端驱动程序。
编辑 hale/src/webdb/ 中的 settings.py,修改以下配置项:ENGINE、NAME、USER、PASSWORD、HOST 和 PORT。其中,如果您的服务器引擎是 MySQL,则 engine 设置项例如为 django.db.backends.mysql。name 设置项是您创建数据库时使用的数据库名称。
如果您不想启动自己的 Web 界面,请跳过此步骤,直接转到第 8) 步。在 settings.py 中,将 PATH_TO_APP 改为指向 Django 应用程序的完整路径,例如:'/home/..../Hale/src/webdb'。在 webdb 目录下运行以下命令:python manage.py syncdb。如果此处出现任何错误,很可能是 settings.py 中的数据库设置不正确。此外,在同步过程中,请设置用于管理用户的超级用户。
为支持 Web 界面中的搜索功能,请运行 python manage.py rebuild_index 让搜索引擎进行首次索引。这将创建一个名为 whoosh.index/ 的目录,为让 Web 服务器能够在此写入,请执行 chmod o+w whoosh.index/。此后,您只需运行 python manage.py update_index,并应将其设置为 cron 任务,以便按您选择的固定时间间隔更新索引。
运行 python manage.py runserver,然后访问 http://127.0.0.1:8000 检查设置是否正确完成。
runserver 命令部署的是开发服务器,由于性能问题,不建议在生产环境中使用。请改为安装 Apache 的 mod_wsgi 来部署 Web 界面。假设您运行的是 Ubuntu,请执行 apt-get install libapache2-mod-wsgi。在 /etc/apache2/sites-available/default 中添加以下内容:
Alias /media /usr/local/lib/python2.6/dist-packages/django/contrib/admin/media/
<Location /media>
Order allow,deny
Allow from all
</Location>
WSGIDaemonProcess username processes=2 maximum-requests=500 threads=10
WSGIProcessGroup group
WSGIScriptAlias / /home/..../webdb/django.wsgi
并编辑 WSGI 守护进程运行所使用的 username 和 group。WSGIScriptAlias 应设置为位于 webdb 目录中的 django.wsgi 文件的完整路径。上面指定的 Alias 用于确保管理页面能够正确渲染。为了能够通过 Web 界面上传模块,请执行 chmod o+w webdb/modules。
上传将要从 hale/src/modules/ 使用的模块,或编写您自己的模块(参见开发章节)。在管理界面上传所需模块,例如将模块名称修改为 irc,文件名修改为 ircModule.py。在位于 hale/conf/modules.conf 的相应部分中为模块指定配置规则,并将其放入文本框中,同时为所上传的模块添加 uniqueKeys 部分。
运行监控器之前,如果您希望使用 XMPP 服务器,请编辑 hale/src/conf/ 中的 hale.conf。要激活 XMPP 机器人,请将 use 设置改为 True,并将登录信息编辑为现有账号和服务器,或自行启动 XMPP 服务器。启动 XMPP 服务器时,一个重要步骤是将最大 stanza 大小从默认值增加到约 10Mb,否则将无法进行恶意软件通告。hale.conf 中的 channel 设置用于机器人使用的共享群组房间,coord 设置用于传感器之间进行所有协调的群组房间。
编辑 hale.conf 并设置客户端和服务器参数。
要启动监控器,请进入 hale/src/ 目录并执行 python server.py。如果启动时出现错误,则说明 django 的 settings.py 文件设置不正确,或缺少某些库。使用客户端可以向服务器发出命令,但用户需要先向服务器进行身份验证,只有通过 Web 界面设置为 staff 成员的用户才能获得访问权限。客户端运行时,输入 help 或 ? 可查看可用命令。输入 help 命令名可查看特定命令的更多信息。启动监控机器人时,首先编辑 hale/src/conf/modules.conf 文件,例如使用如下 IRC 配置:
[uniqueKeys]
irc = botnet, *grammar
[ircConf]
module = irc
botnet = irc.freenode.net
port = 6667
password = None
nick = nickname
username = agent007
realname = Spying
channel = #channelname
channel_pass = somepass
pass_grammar = PASS
nick_grammar = NICK
user_grammar = USER
join_grammar = JOIN
version_grammar = VERSION
time_grammar = TIME
privmsg_grammar = PRIVMSG
topic_grammar = TOPIC
currenttopic_grammar = 332
ping_grammar = PING
pong_grammar = PONG
通过指定一个具有唯一名称的新部分([ircConf] 部分)来编辑或创建新配置。配置文件顶部有一个名为 uniqueKeys 的部分,其中指定了模块的所有唯一字段,并用于生成僵尸网络哈希。为保持正确的僵尸网络跟踪,通常不应更改此部分。完成后,运行 useconf section 加载配置,然后使用 exec modulename id 启动机器人,其中 id 由您自行设置以标识僵尸网络。
Web 界面提供对数据库中所有已捕获数据的访问,可从首页进入。此外还有一个搜索功能,使用户能够搜索僵尸网络和文件哈希、相关 IP 地址、僵尸网络 ID、所使用的僵尸网络模块以及僵尸网络主机。如果用户拥有编辑代理或模块的权限,则可以在管理部分进行操作,其网址为 http://.../admin。管理员可以设置用户模式,也可以为 Web API 添加使用者(consumer)。
实现模块,例如:
import moduleManager from utils import moduleInterface
@moduleManager.register("irc") def module_setup(config, hash): """ Function to register modules, simply implement this to pass along the config and hash to the module object and return the it back. """
return IRC(config, hash)
class IRC(moduleInterface.Module):
def __init__(self, config, hash):
self.config = config
self.hash = hash
# must be implemented
def stop(self):
# stop execution
# must be implemented
def run(self):
# start execution
# must be implemented
def getConfig(self):
return self.config
为 register 函数(在此例中为 module_setup)添加装饰器,该函数将以当前配置和由唯一键生成的配置哈希作为参数被调用。此函数可以任意命名。将配置传递给模块对象,configHandler 会捕获 KeyError,因此如果向此函数发送了错误的配置,configHandler 会通知您。
同时请遵循命名约定 nameModule.py 和 @moduleManager.register("name"),并导入 moduleManager,否则 moduleManager 会通知您任何错误。
模块的其余代码此处省略,但应创建一个 twisted factory 对象,并在 run 方法中使用 reactor 启动它,可参考现有模块的示例。有关 Twisted 编程的教程,请参见此处。开发模块时还可以使用一些工具函数,使用方法如下:
Socksify:
# import all utils
from utils import *
# in the constructor create a new proxy object
self.prox = proxySelector.ProxySelector()
# in the run method add the following after having created the factory method.
proxyInfo = self.prox.getRandomProxy()
if proxyInfo == None:
self.connector = reactor.connectTCP(host, port, factory)
else:
proxyHost = proxyInfo['HOST']
proxyPort = proxyInfo['PORT']
proxyUser = proxyInfo['USER']
proxyPass = proxyInfo['PASS']
socksify = socks5.ProxyClientCreator(reactor, factory)
if len(proxyUser) == 0:
self.connector = socksify.connectSocks5Proxy(host, port, proxyHost, proxyPort, "HALE")
else:
self.connector = socksify.connectSocks5Proxy(host, port, proxyHost, proxyPort, "HALE", proxyUser, proxyPass)
在 factory 对象中处理连接错误:
def clientConnectionFailed(self, connector, reason):
"""
Called on failed connection to server
"""
moduleCoordinator.ModuleCoordinator().putError("Error connecting to " + self.config['botnet'], self.module)
def clientConnectionLost(self, connector, reason):
"""
Called on lost connection to server
"""
moduleCoordinator.ModuleCoordinator().putError("Connection lost to " + self.config['botnet'], self.module)
这会将错误发送到错误桶(error bucket),可通过在 CLI 中执行 showlog 来查看。
日志记录:
# in the factory create the following method to handle logs (note that the hash and config must be sent to the factory)
# and call it in the protocol class with: self.factory.putLog(data)
def putLog(self, log):
"""
Put log to the event handler
"""
moduleCoordinator.ModuleCoordinator().addEvent(moduleCoordinator.LOG_EVENT, log, self.hash, self.config)
# apply reg expression to look for URLs containing possible malware
# and call it in the protocol class with: self.factory.checkForURL(data)
def checkForURL(self, data):
"""
Check for URL in the event handler
"""
moduleCoordinator.ModuleCoordinator().addEvent(moduleCoordinator.URL_EVENT, data, self.hash)
# if you module should detect IP numbers of other bots and herders implement the following method in the factory
def addRelIP(self, data):
"""
Put possible ip related to the botnet being monitored
in the event handler.
"""
moduleCoordinator.ModuleCoordinator().addEvent(moduleCoordinator.RELIP_EVENT, data, self.hash)
处理相关 IP 是通过应用正则表达式来完成的,该正则表达式用于模块将要支持的协议。以 irc 模块为例,代码如下所示:
checkHost = data.split(':')[1].split(' ')[0].strip()
match = self.factory.expr.findall(checkHost)
if match:
self.factory.addRelIP(data.split('@')[1].split(' ')[0].strip())
其中正则表达式如下:
self.expr = re.compile('!~.*?@')
2) 将文件拖放到 modules 目录中。moduleManager 会自动导入它并检查错误。
在 modules.conf 中编辑配置。
将模块上传到 Web 界面,例如将模块名称设置为 irc,文件名设置为 ircModule.py,然后为该模块添加一个配置示例。
发送跟踪僵尸网络的请求时,按如下方式向群聊协调房间发送请求:
sensorLoadReq
所有传感器都会回复其 id 和队列长度(已监控的僵尸网络数量):
sensorLoadAck id=353f6650859547ed06597dbfa1dcfd88 queue=0
然后,馈送者(feeder)会根据这些信息选择一个传感器,例如选择队列长度最短的;如果多个传感器的队列长度相同,则选择按字母顺序排序后 id 值最小的传感器。
当馈送者选定传感器后,它会向该传感器发送一条私聊消息:
startTrackReq config
其中 config 是配置的字符串表示形式,例如:
module=irc botnet=irc.freenode.net etc..
如果没有其他人正在监控该僵尸网络,传感器会回复一个确认消息以及配置哈希,该哈希可用于在共享频道中将该僵尸网络的日志与其他日志区分开来。确认消息示例:
startTrackAck hash
如果该僵尸网络已被监控,或者传感器未安装适用于该僵尸网络的模块,则会收到 startTrackNack。恶意软件共享由传感器发送类似如下消息完成:
fileCaptured hash=353f6650... file content
其中内容为 Base64 编码,直接跟在文件哈希值之后。
要访问 API,您需要 consumer key 和 secret key,这些可由管理员创建,并与 OAuth 一起用于身份验证。以下 URL 可用于获取 JSON 格式的数据:
http://.../api/botnet will reply with all botnets monitored
http://.../api/botnet/botnethash will reply with the botnet with hash equal to botnethash
http://.../api/host/hostname will reply with all botnets monitored with host equal to hostname
http://.../api/type/module will reply with all botnets monitored with the module
http://.../api/botips/hash will reply with all ips captured by botnet with the value hash
http://.../api/bologs/hash will reply with all logs for botnet with value hash
http://.../api/bofiles/hash will reply with file hashes captured by botnet with value hash
http://.../api/file/hash returns botnet(s) info for those that have captured file with the hash specified
http://.../api/ip/addr will reply with botnet(s) info for those that have detected an IP with number addr
请注意,目前仅支持 GET 请求。