前言此前,我阅读了CyberWarDog的威胁主动出击(Threat Hunting)文章,并且还偶然发现并仔细阅读了他的“寻找内存中的Mimikatz”系列文章。其中,用于构建签名的方法似乎非常简
前言
此前,我阅读了CyberWarDog的威胁主动出击(Threat Hunting)文章,并且还偶然发现并仔细阅读了他的“寻找内存中的Mimikatz”系列文章。其中,用于构建签名的方法似乎非常简单,并且在对恶意工具的分析过程中,已经解决了寻找入口的这一障碍。
用于检测Mimikatz的方法被称为分组(Grouping),具体来讲,是获取一组特殊的Artifacts,并且识别多个特殊的Artifacts同时出现的时间。在本文中,我们将参考CyberWarDog的方法,来探讨如何使用Sysmon和ELK Stack对密码抓取神器Mimikatz进行检测与告警。
我们假设你已经拥有一个ELK Stack,并且配置了ElastAlert集成。这样,就可以在几分钟的时间里迅速将HELK搭建成功(ElastAlert后续会与其合并)。
一开始,我认为这并不是一个完美的解决方案。其思路很简单,如果在同一个主机中,有5个DLL在1秒钟之内被接连访问,那么就发出告警。然而,很不巧,ElastAlert没有内置这个功能,同样Python也没有。
Sysmon配置
我使用的Sysmon配置是Ion-Storm Sysmon Config,源文件位于: https://github.com/ion-storm/sysmon-config/blob/master/sysmonconfig-export.xml 。默认情况下,正确的事件将会被转发。其中的第571-579行如下:
cOndition="is">C:WindowsSystem32WinSCard.dll
cOndition="is">C:WindowsSystem32cryptdll.dll
cOndition="is">C:WindowsSystem32hid.dll
cOndition="is">C:WindowsSystem32samlib.dll
cOndition="is">C:WindowsSystem32vaultcli.dll
cOndition="is">WMINet_Utils.dll
cOndition="contains">Temp
最开始,我们需要写一个脚本,来进行一些逻辑上的验证。我试图让Python工具尽可能模块化,这样我们就可以轻松地对其他事件“分组”进行告警。
我们将要检测的5个DLL是:
Cryptdll.dll
Hid.dll
Samlib.dll
Vaultcli.dll
Winscard.dll
在这里,还存在误报的一种可能性,如果恰巧这些DLL在1分钟之内被正常访问,那么同样会产生告警。
告警脚本
在运行ELK Stack的服务器上,我们首先创建py-alert.py并编辑:
sudo
nano /bin/py-alert.py
py-alert.py的源代码如下:
#!/usr/bin/python
import sys
from argparse import ArgumentParser
import datetime
import requests
import subprocess
import os
time = datetime.datetime.now().strftime("%H:%M-%y-%m-%d")
def print_banner():
print('''npy-alert.py is a tool written to
expand the functionality of ElastAlert
Author: Jordan Potti
Twitter: @ok_bye_nown'''
)
def main():
global arguments
parser = ArgumentParser()
parser.add_argument("-T",
dest="action",required=True,help="Action Type: Send Alert (S) or
Data Write (D)")
parser.add_argument("-a",
dest="detection",required=True,help="Alert Name")
parser.add_argument("-c",
dest="host",required=False,help="Host to record")
parser.add_argument("-S",
dest="slack",required=False,help="Slack Web Hook")
parser.add_argument("-t",
dest="tripped",required=False,help="Number or Hosts needed to
alert")
if len(sys.argv) == 1:
print_banner()
parser.error("No arguments
given.")
parser.print_usage
sys.exit()
arguments = parser.parse_args()
outfile = '/tmp/'+arguments.detection
if arguments.action == 'D':
with open (outfile, "a+") as
out_file:
out_file.write(arguments.host+"n")
if arguments.action == 'S':
command = "head -50 %s | sort |
uniq -c | gawk '$1>=%s{print $2}'" %(outfile,arguments.tripped)
print(command)
output = os.popen(command).read()
if output != '':
output = str(output)
output = output.replace('b'','')
output = output.replace('\n','')
out_file = open(outfile, 'w')
out_file.write("Host: " +
output)
out_file.write("Alert Type:
" + arguments.detection+"n")
out_file.write("Time: " +
time)
out_file = open(outfile, 'r')
webhook_url = arguments.slack
slack_data =
{"text":out_file.read()}
slack_data = str(slack_data)
slack_data =
"payload="+slack_data
respOnse= requests.post(
webhook_url, data=slack_data,
headers={'Content-Type':
'application/x-www-form-urlencoded'})
if response.status_code != 200:
raise ValueError('Request to
slack returned an error %s, the response is: %s' % (response.status_code,
response.text))
os.remove(outfile)
main()
然后,执行该Python脚本:
sudo chmod 755 /bin/py-alert.py
该脚本可以处理我们的全部逻辑,并负责发送Slack Notification告警通知。借助这些选项,我们可以对任何事件组合进行告警。
配置告警规则
接下来,将我们的单个规则添加到告警规则目录中。
可以从GitHub获取到我们的规则:
git clone https://github.com/jordanpotti/ElastAlertGrouper.git
随后,将规则文件复制到ElastAlert规则目录中:
sudo cp ElastAlertGrouper/alert_rules/* /etc/elastalert/alert_rules/
现在,在规则目录中就已经有了6条新规则。当指定DLL被加载时,相应的规则将被匹配到,随后立即发出告警。
以下为samlib.yaml的源代码:
es_host:
localhost
es_port:
9200
name:
"samlib"
realert:
minutes: 0
index:
winlogbeat-*
filter:
-
query:
wildcard:
event_data.ImageLoaded:
"*samlib*"
type:
any
alert:
- command
command:
["/bin/py-alert.py","-T","D","-a","Mimikatz","-c","%(computer_name)s"]
如你所见,我们配置了典型的告警规则选项,并且会在event_data.ImageLoaded中查询samlib。当告警被触发时,会使用如下命令调用我们的Python脚本:
python3
/bin/py-alert.py –T D –a Mimikatz –o /tmp/mimikatz –c $ComputerName
其中的参数-T用于指定脚本将要采取的动作,由于我们这里只是将主机名写入文件,所以我们要使用“Document”或者“D”选项。
其中的参数-a是告警类型,在这里是Mimikatz。
其中的参数-c是从告警事件中获取的主机名。
上面的设置针对所有DLL告警事件。因此,当Mimikatz在系统中运行时,输出文件中将会有5个主机名被写入。
配置捕获Mimikatz的规则
接下来,让我们来看看Mimikatz的规则:
es_host:
localhost
es_port:
9200
name:
"Mimikatz"
index:
elastalert_status
realert:
minutes: 0
type:
cardinality
cardinality_field:
rule_name
max_cardinality:
4
filter:
-
terms:
rule_name:
- winscard
- cryptdll
- hid
- samlib
- vaultcli
-
term:
alert_sent: true
timeframe:
seconds: 1
alert:
- command
command:
["python3","/bin/py-alert.py",
"-T","S","-S","SLACKWEBHOOK","-a","Mimikatz","-t","5"]
此告警使用了另外一个索引。实际上,ElastAlert有它自己的索引,在每次告警时都会对其进行查询。因此,我们现在可以查看这个索引,来确认5个DLL的告警是不是在1秒之内被接连触发的。这一过程只需要通过对DLL规则进行筛选就可以完成,并且它只返回那些alert_sent标志设置为true的内容,并且只在1秒之内识别出5个结果的时候才发出告警。
此时,我们需要生成一个Slack Web Hook,并使用我们的Web钩子替换“SLACKWEBHOOK”。
其中,告警功能的实现同样是通过调用一个Python脚本:
python3
/bin/py-alert.py –T S –S SLACKWEBHOOK –a Mimikatz –t 5
其中的参数-T代表要执行的操作,在这里,我们要执行“Send”操作。
其中的参数-S需要配置为我们的Slack Web Hook。
其中的参数-a代表需要对哪些检测到的类型进行告警。
其中的参数-t代表次数,我们只在输出文件中有5个或者更多特定的主机名时才会产生告警。
最后一部分是最重要的,其中的这个数字应该是我们“分组”中的规则数量。
测试过程
接下来,我们运行Mimikatz:
立即收到了相关告警:
总结
有一点需要特别强调,上述内容已经在实验室的环境中进行了测试,而我们实验室的环境中只有几个终端主机。如果需要在生产环境中进行部署,可能需要根据终端的数量进行相应的调整。
关于如何手动部署的更深入指导,请参考我们实验室的文章:https://cyberwardog.blogspot.com/2017/02/setting-up-pentesting-i-mean-threat_98.html
如果在过程中遇到任何问题,欢迎随时通过Twitter https://twitter.com/ok_bye_now或电子邮件admin@jordanpotti.com与我联系。
我们在本文中涉及到的脚本,并不保证一定能在所有人的环境中成功实现,需要以此为参考进行相应的调整。大家知道,不要盲目信任网络上的脚本,需要加以自行判断。