Home 应用安全 通过Sysmon日志检测Cobalt Strike木马

通过Sysmon日志检测Cobalt Strike木马

by zinan

Cobalt Strike Beacon提供了很多命令控制的功能,其中有的存在强特征,有的存在弱特征,而剩下的基本没有特征,前两者都可以监控,即使是弱特征通过关联分析也能做到基本无误报的程度,如果内网中存在红队的CS木马,在没有对抗意识的情况下,很容易做出一些『错误』的操作被蓝队检测到。

一、关于强特征

既然是强特征,自然是通过此类特征的监控能达到精准告警,极少有误报的情况,或者即使在某些环境下存在误报,也可以通过添加少量白名单的方式收敛掉。

1、mimikatz dump hash

使用Beacon内置的Run Mimikatz功能默认会将Mimikatz的DLL文件注入到Rundll32.exe进程中运行,所以这部分的检测基本就是对mimikatz这一类dump hash行为的监控,开源Sigma规则中也包含此类规则,检测点是对lsass.exe进程的异常提权行为,核心检测逻辑如下:

logsource:
    product: windows
    service: sysmon

detection:
    selection1:
        EventID: 10
        event_data.GrantedAccess: "0x1010"
    selection2:
        event_data.TargetImage:
            - "*winlogon.exe"
            - "*lsass.exe"
    filter:
        event_data.SourceImage:
            - "*windows defender*"
    condition: selection1 and selection2 and not filter

2、远程线程注入

Beacon的大部分控制操作是将主功能的DLL文件远程注入到其它进程中执行,默认是Rundll32.exe,虽然这样增加了被检出的风险,但官方对此的解释是,如果在Beacon进程里执行这些功能时发生了crash,可能会导致Beacon进程退出,这样就丢失了远控机器,看来也是一种无奈之举。

对于远程注入Rundll32.exe的操作是一种极其少见的行为,所以基本属于CS的一个强特征,Sysmon会记录每一个远程线程注入的事件,可以通过Sigma规则监控:

logsource:
    product: windows
    service: sysmon

detection:
    selection:
        EventID: 8
        TargetImage: "*\\rundll32.exe"
    condition: all of them

当然在CS的配置文件中,可以选择注入的目标,如果你在配置CS的profile时默认选择注入Rundll32.exe,还会有OPSEC的提示:

红队可以通过修改配置文件绕开对于Rundll32.exe注入的监控:

不过蓝队可以继续写一条关于没有代码签名的进程,却对系统进程进行远程注入的监控,但这里涉及到关联分析,暂不展开说明。

还有一点就是,没有正规代码签名的进程,进行可疑的远程线程注入的行为,大部分主动防御做的好的杀软已经可以拦截了,即使CS木马实现了代码静态免杀,也在这点上过不了行为免杀,例如在进行注入时3xx会对用户进行告警提示,并默认阻拦,但仍有一些老牌杀软,例如国内一些甲方使用的某S开头的国外杀软,一点反应都没有,很容易绕过。

 

二、关于弱特征

CS木马部分操作运行时无强特征,所以检测思路只能尝试提取多个弱特征后,在同一个进程上关联起来,多次尝试后看看误报量大不大。

1、分析shell执行时的弱特征:

1)运行时的心跳连接:

Sysmon可以记录下每一个出站和入站的短连接和长连接(event_id: 3),但CS的心跳周期可以自行设置,甚至设置非固定的心跳周期,所以心跳连接只能属于没有差异性的弱特征。

2)执行shell的命令行格式:

执行命令时CS会通过绑定双向匿名管道的方式创建cmd.exe子进程,并传入命令参数,格式为cmd.exe的完整路径 + 大写/C参数 + 实际执行命令。

在这个Sysmon原始日志中,执行的命令记录到两个字段中CommandLine和CommandLine_Raw,CommandLine内容全部转换为小写,方便在es和hive中不区分大小写查询,而CommandLine_Raw记录了原始命令,是区分了大小写的。

通过日志回测发现,cmd命令执行这也是弱特征,正常软件也会大量触发。不过注意/C参数是CS特有的大写,部分正常软件通过cmd子进程执行命令时,/c参数是小写的,这点也可以帮我们过滤部分正常软件。

3)执行后渗透阶段常用的命令

CS远控后的大部分通用行为集中在shell执行上,从对红队行为的复盘和一些经验可以总结出,黑客在后渗透阶段,执行的命令类型集中在:系统信息查看、文件查看、文件操作等,而这属于第3个弱特征。

最终检测思路尝试将3个弱特征结合:
持续的心跳连接 + 特殊cmd命令执行格式 + 一些后渗透阶段常用的命令

2、检测shell执行行为

由于这里需要关联分析多个日志,因此我们借助开源CEP引擎Esper实现,假设我们在Storm上集成了实时Sigma引擎和Esper引擎,并且和Esper引擎相关的数据流转如下图所示:

基于这样的架构,我们可以做出以下的检测规则(当然不适用于其它架构)。

1)首先在全网Sysmon网络连接日志中,通过CEP规则筛选出所有存在TCP心跳连接的进程:

检测逻辑简单描述为:

进程向同一个IP同一个端口,连续发起>=5次的TCP连接,每次间隔最大不超过12min,总共心跳时长不小于80sec,则判定存在TCP心跳行为:

title: 发现Windows下可疑的TCP心跳连接_Sysmon

date: 2020-08-15
status: stable
level: low
credibility: low
verify: 1
log_level: 0
data_source:
    - 'Sysmon'
uniq_field_name:
    - 'host_computer_name'
    - 'host_guid'
    - 'host_pid'
    - 'host_images'
    - 'dest_ip'
    - 'dest_port'
expire_time: 1800
merge_time_window: 210
author: weizinan
version: 4

esper_cache_expires: 0
storm_grouping_field:
    - 'host_computer_name'

epl: '
@name("检测Windows下可疑的心跳连接_Sysmon_拆分出网络事件流")
@public
create schema SYSMON_TCP_FLOW () copyfrom SysmonRawLogs;


@name("检测Windows下可疑的心跳连接_Sysmon_筛选感兴趣事件流")
on SysmonRawLogs
insert into SYSMON_TCP_FLOW
select * where
event_id = "3" and
event_data_direction = "outbound" and
event_data_protocol = "tcp" and
event_data_image != "" and
event_data_processguid != "" and

IPAddress4Utils.is_valid_ipv4(event_data_sourceip) and
IPAddress4Utils.is_valid_ipv4(event_data_destinationip) and

event_data_image not like "%\\\\google\\\\%" and
event_data_image not like "%\\\\inetsrv\\\\w3wp.exe" and
event_data_image not like "%\\\\system32\\\\dns.exe" and
event_data_image not like "%\\\\bin\\\\msexchangemailboxassistants.exe" and

ArrayContains.array_contains(event_data_sourceip, host_ip, ",") > 0
;


@name("检测Windows下可疑的心跳连接_Sysmon_创建匹配模式")
select * from pattern[
    every-distinct(
        a.computer_name, a.event_data_destinationip, a.event_data_destinationport,
        a.event_data_processguid, a.event_data_processid, a.event_data_image, 10 min
    )
    a=SYSMON_TCP_FLOW ->

    (timer:interval(20 sec) and b=SYSMON_TCP_FLOW(
        a.computer_name = computer_name and
        a.event_data_sourceip = event_data_sourceip and
        a.event_data_destinationip = event_data_destinationip and
        a.event_data_destinationport = event_data_destinationport and
        a.event_data_processguid = event_data_processguid and
        a.event_data_processid = event_data_processid and
        a.event_data_image = event_data_image
    )) where timer:within(12 min) ->

    (timer:interval(20 sec) and c=SYSMON_TCP_FLOW(
        a.computer_name = computer_name and
        a.event_data_sourceip = event_data_sourceip and
        a.event_data_destinationip = event_data_destinationip and
        a.event_data_destinationport = event_data_destinationport and
        a.event_data_processguid = event_data_processguid and
        a.event_data_processid = event_data_processid and
        a.event_data_image = event_data_image
    )) where timer:within(12 min) ->

    (timer:interval(20 sec) and d=SYSMON_TCP_FLOW(
        a.computer_name = computer_name and
        a.event_data_sourceip = event_data_sourceip and
        a.event_data_destinationip = event_data_destinationip and
        a.event_data_destinationport = event_data_destinationport and
        a.event_data_processguid = event_data_processguid and
        a.event_data_processid = event_data_processid and
        a.event_data_image = event_data_image
    )) where timer:within(12 min) ->

    (timer:interval(20 sec) and e=SYSMON_TCP_FLOW(
        a.computer_name = computer_name and
        a.event_data_sourceip = event_data_sourceip and
        a.event_data_destinationip = event_data_destinationip and
        a.event_data_destinationport = event_data_destinationport and
        a.event_data_processguid = event_data_processguid and
        a.event_data_processid = event_data_processid and
        a.event_data_image = event_data_image
    )) where timer:within(12 min)
]

'

listener_epl_index: 2

local_constant:
    total_alert_count: 'int:1'

local_variable:
    start_unix_timestamp: 'Long:a.unix_timestamp'
    end_unix_timestamp: 'Long:b.unix_timestamp'

    computer_name: 'String:e.computer_name'
    src_ip: 'String:e.event_data_sourceip'
    dest_ip: 'String:e.event_data_destinationip'
    dest_port: 'int:e.event_data_destinationport'
    proc_path: 'String:e.event_data_image'
    pid: 'String:e.event_data_processid'
    guid: 'String:e.event_data_processguid'

create_alert_invoke_function:
    add_host_computer_name: '$computer_name'
    add_host_ip: '$src_ip'
    add_total_alert_count: '$total_alert_count'
    add_victim_ip: '$src_ip'
    add_attacker_ip: '$dest_ip'
    add_dest_ip: '$dest_ip'
    add_dest_port: '$dest_port'

    add_host_image: '$proc_path'
    add_host_pid: '$pid'
    add_host_guid: '$guid'

通过RabbitMQ中间件将产生的心跳事件重入Esper引擎时,可以看到内网每秒产生大约100条此事件:

2)通过编写Sigma规则,在Sysmon进程启动日志中,检查出类似CS命令执行的行为,和后渗透阶段常见的命令:

title: Windows执行可疑的命令格式_疑似CobaltStrike_Sysmon_0
description: 用于级联检测『检测疑似CobaltStrike木马行为』,不会单独产生告警。

logsource:
    product: windows
    service: sysmon

detection:
    selection1:
        event_id: '1'
    selection2:
        event_data.CommandLine:
            - '?:\windows\system32\cmd.exe /c whoami'
            - '?:\windows\system32\cmd.exe /c ipconfig'
            - '?:\windows\system32\cmd.exe /c ipconfig /all'
            - '?:\windows\system32\cmd.exe /c hostname'
#            - '?:\windows\system32\cmd.exe /c net user'
            - '?:\windows\system32\cmd.exe /c set'
            - '?:\windows\system32\cmd.exe /c quser'
#            - '?:\windows\system32\cmd.exe /c net localgroup administrators'
            - '?:\windows\system32\cmd.exe /c dir'
            - '?:\windows\system32\cmd.exe /c systeminfo'
            - '?:\windows\system32\cmd.exe /c tasklist'
            - '?:\windows\system32\cmd.exe /c qprocess'

            - '?:\windows\system32\cmd.exe /c cd *'
            - '?:\windows\system32\cmd.exe /c dir *'
            - '?:\windows\system32\cmd.exe /c net *'
            - '?:\windows\system32\cmd.exe /c net1 *'
            - '?:\windows\system32\cmd.exe /c powershell*'
            - '?:\windows\system32\cmd.exe /c netsh *'
            - '?:\windows\system32\cmd.exe /c wmic *'
            - '?:\windows\system32\cmd.exe /c bitsadmin*'
            - '?:\windows\system32\cmd.exe /c cmstp*'
            - '?:\windows\system32\cmd.exe /c mshta*'
            - '?:\windows\system32\cmd.exe /c certutil*'
            - '?:\windows\system32\cmd.exe /c rundll32*'
            - '?:\windows\system32\cmd.exe /c cscript*'
            - '?:\windows\system32\cmd.exe /c msiexec*'
            - '?:\windows\system32\cmd.exe /c sctasks*'
            - '?:\windows\system32\cmd.exe /c wscript*'
            - '?:\windows\system32\cmd.exe /c cmd *'
            - '?:\windows\system32\cmd.exe /c more *'
            - '?:\windows\system32\cmd.exe /c ping *'
            - '?:\windows\system32\cmd.exe /c nslookup *'
            - '?:\windows\system32\cmd.exe /c tasklist *'
            - '?:\windows\system32\cmd.exe /c taskkill *'
            - '?:\windows\system32\cmd.exe /c tracert *'
            - '?:\windows\system32\cmd.exe /c echo *>*'
            - '?:\windows\system32\cmd.exe /c del *'
            - '?:\windows\system32\cmd.exe /c copy *'
            - '?:\windows\system32\cmd.exe /c move *'
            - '?:\windows\system32\cmd.exe /c rename *'
            - '?:\windows\system32\cmd.exe /c quser *'
            - '?:\windows\system32\cmd.exe /c query *'
            - '?:\windows\system32\cmd.exe /c xcopy *'
            - '?:\windows\system32\cmd.exe /c type *'
            - '?:\windows\system32\cmd.exe /c netstat *'
            - '?:\windows\system32\cmd.exe /c fsutil *'
            - '?:\windows\system32\cmd.exe /c sc *'
            - '?:\windows\system32\cmd.exe /c qprocess*'

            - '?:\windows\system32\cmd.exe /c *|*findstr *'
    filter1:
        event_data.ParentImage:
            - '*\cmd.exe'
    filter2:
        event_data.CommandLine_Raw:
            # CobaltStrike执行cmd命令的参数必须是大写的『/C』,因此过滤掉小写的『/c』,『regex:』前缀表示这里使用正则匹配。
            - 'regex:((?i)$?:\\Windows\\system32\\cmd\.exe)\s/c\s'
    condition: selection1 and selection2 and not filter1 and not filter2

kill_chain: "命令与控制"
role: "受害者"
fallen: 0
category: ""
date: 2020-08-15
version: 2
status: stable
level: low
credibility: low
verify: 1
log_level: 0
author: weizinan
only_storm_rule: 1

esper_cache_expires: 0
storm_grouping_field:
    - 'computer_name'

3)最后在Esper引擎中,关联存在以上1、2行为的进程,可以生成最终告警:

注意以上两个规则设置了分组字段都为computer_name,因此可以保证在Storm中被分组到同一个bolt中做级联计算,Storm中CEP数据流转方式参考上面的数据流图。

title: 发现Windows下疑似CobaltStrike木马进程
description: '发现主机{$computer_name}疑似存在Cobalt Strike木马进程{$proc_path},以{host_users}帐户运行,执行了子命令{$cmd},并且存在周期性TCP心跳,连接了IP地址{dest_ip}的{dest_port}端口,需检查样本文件。'
define: '如果进程存在周期性心跳连接,并且执行的命令格式疑似Cobalt Strike,则判定该进程可能为Cobalt Strike木马。'
references: ''
tags:
    - 'Cobalt Strike'

attack_tactic:
    - Command and Control
attack_name:
    - ''
attack_id:
    - TA0011

alert_type: 'IT服务器EDR告警'
kill_chain: "命令与控制"
role: "受害者"
fallen: 0
category: ""
date: 2020-08-15
status: stable
level: critical
credibility: high
verify: 1
log_level: 3
data_source:
    - 'Sysmon'
uniq_field_name:
    - 'host_users'
    - 'host_computer_name'
    - 'host_images'
expire_time: 1800
merge_time_window: 180
author: weizinan
version: 10

esper_cache_expires: 0
storm_grouping_field:
    - 'host_computer_name'

falsepositives:
    - '由于不存在强特征,因此有一定误报的可能。'

epl: '
@name("发现Windows下疑似CobaltStrike木马进程_Sysmon_查询同时有两个行为的进程_1")
select * from pattern [
    every-distinct(
        b.computer_name, b.event_data_parentimage,
        b.event_data_parentprocessguid, b.event_data_parentprocessid, b.event_data_commandline, 5 min
    )
    b=SigmaAlerts(alert_signature = "Windows执行可疑的命令格式_疑似CobaltStrike_Sysmon_0") ->

    a=ComplexAttackAlerts(
        signature = "发现Windows下可疑的TCP心跳连接_Sysmon" and
        host_computer_name = b.computer_name and
        host_images = b.event_data_parentimage and
        host_guid = b.event_data_parentprocessguid and
        host_pid = b.event_data_parentprocessid
    ) where timer:within(40 min)
];


@name("发现Windows下疑似CobaltStrike木马进程_Sysmon_查询同时有两个行为的进程_2")
select * from pattern [
    every-distinct(
        a.host_computer_name, a.host_images, a.host_guid, a.host_pid, a.dest_ip, a.dest_port, 3 min
    )
    a=ComplexAttackAlerts(signature = "发现Windows下可疑的TCP心跳连接_Sysmon") ->

    b=SigmaAlerts(
        alert_signature = "Windows执行可疑的命令格式_疑似CobaltStrike_Sysmon_0" and
        a.host_computer_name = computer_name and
        a.host_images = event_data_parentimage and
        a.host_guid = event_data_parentprocessguid and
        a.host_pid = event_data_parentprocessid
    ) where timer:within(40 min)
];

'

listener_epl_index:
    - 0
    - 1

local_constant:
    total_alert_count: 'int:1'
    end_unix_timestamp: 'Long:0'

    aboveDeep: 'int:30'
    belowDeep: 'int:10'
    procDeep: 'int:30'

local_variable:
    start_unix_timestamp: 'Long:b.unix_timestamp'

    computer_name: 'String:b.computer_name'
    user: 'String:b.event_data_user'

    src_ip: 'String:b.host_ip'
    dest_ip: 'String:a.dest_ip'
    dest_port: 'int:a.dest_port'
    proc_path: 'String:b.event_data_parentimage'

    # 木马PID
    ppid_str: 'String:b.event_data_parentprocessid'
    parentguid: 'String:b.event_data_parentprocessguid'

    # 命令PID
    pid_str: 'String:b.event_data_processid'
    guid: 'String:b.event_data_processguid'

    cmd: 'b.event_data_commandline_raw'
    path: 'b.event_data_image'
    parentCmd: 'b.event_data_parentcommandline_raw'


local_variable_invoke_function:
    commandContextList: 'String[]:query_win_context_command_sysmon($computer_name, $pid_str, $parentguid,
                         $ppid_str, $proc_path, $start_unix_timestamp, $aboveDeep, $belowDeep, $cmd)'
    processChainList: 'String[]:query_win_process_chain_sysmon($computer_name, $parentCmd, $proc_path,
                       $parentguid, $ppid_str, $cmd, $path, $guid, $pid_str, $procDeep)'

create_alert_invoke_function:
    add_host_computer_name: '$computer_name'
    add_host_user: '$user'
    add_host_ip: '$src_ip'
    add_total_alert_count: '$total_alert_count'
    add_victim_ip: '$src_ip'
    add_attacker_ip: '$dest_ip'
    add_dest_ip: '$dest_ip'
    add_dest_port: '$dest_port'

    add_host_command_context: '$commandContextList'
    add_host_process_chain: '$processChainList'

    add_host_image: '$proc_path'
    add_host_pid: '$ppid_str'
    add_host_guid: '$parentguid'

4)告警样例

目前内网通过添加少量白名单已达到基本无误报的程度,使用CS执行shell后无漏报情况。

三、其它

其它一些检测点暂没详细梳理,待以后有需求再看,其实还有一种关联分析的模型,监控建立一个或两个单向匿名管道绑定cmd执行命令的方法,这种模型泛化能力较强,误报也少,可以监控大部分开源、自研和商用远控的shell命令执行行为,以后有时间再写。
综合看来执行shell命令是红队一定要规避的行为,除非蓝队没采集终端侧数据,否则有能力监控,而且执行shell行为在内网的噪音也不大。

对于蓝队来说,本文这种监控方法也仅仅是初级的,红队依然可以通过bin-patch等免杀手段绕过,以后有空写写一种木马静态免杀和行为免杀的方法,可以过掉所有VT上杀软的静态扫描,以及所测试的沙箱,攻防对抗就是这样,没有永远胜利的一方。

0 comment

You may also like

Leave a Comment