首页
论坛
课程
招聘
[原创]CobaltStrike检测与对抗
2022-10-10 14:21 19449

[原创]CobaltStrike检测与对抗

2022-10-10 14:21
19449

一、概述

Cobalt Strike是一款渗透测试工具,简称为CS。它拥有多种协议主机上线方式,并集成了文件操作、命令执行、提权、凭据导出、端口转发、socket代理、宏病毒、文件捆绑、钓鱼等功能。其ATT&CK的官方映射如下图,可见CS从初始访问阶段(TA0001)横跨到渗出阶段(TA0010)都有覆盖:
图片描述
下面我们会先从cs的profile进行分析,cs的profile设置讲解网上挺多的,但没有深入的探究为什么会增加这个选项,这个选项最终会起到什么的作用,加了选项后根据现有的规则能否进行检测等。然后我们会站在狩猎对抗的角度出发,从不同的角度思考怎么检测出可疑的进程。

二、profile设置详解

2.1流量侧

流量侧的伪装主要在http-get和http-post这两个标签中,两标签中的设置也是类似的,set uri是设置回传teamserver端的URI地址,client设置请求头中的信息,metadata中是设置回传元数据的格式,这是设置了把回传的元数据以base64编码后再netbios编码,最后拼接在cookie中输出,所涉及到算法netbios、base64url、mask、netbiosu的详细分析可参见这篇文章https://unit42.paloaltonetworks.com/cobalt-strike-metadata-encoding-decoding/:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
http-get {
    set uri "/web /api/gallery /updates /about";
 
    client {
        header "Accept" "*/*";
        header "Connection" "Close";
        header "Host" "baidu.com";
 
        metadata {
            base64;
            netbios;
            prepend "cf=";
            header "Cookie";
        }
    }
 
    server {
        output {
            print;
        }
    }
}


同样http-post也是类似,id标签是将beaconID先后netbios、base64url编码后拼接在自定义的key参数后,output标签是beacon执行完后把数据base64编码后回传给teamserver端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
http-post {
    set uri "/web/auth.php /admin/login.php";
 
    client {
        header "Accept" "*/*";
        header "Host" "baidu.com";
        header "Connection" "Close";
 
        id {
            netbios;
            base64url;
            parameter "key";
        }
 
        output {
            base64;
            print;
        }
    }
 
    server {
        header "Pragma" "no-cache";
        header "Connection" "close";
 
        output {
            print;
        }
    }
}

2.2主机侧:

在Profile文件中的stage、process-inject、post-ex三个标签可以更自由的操作beacon在内存中的、后渗透中等行为。下面分析stage标签在内存中的行为。

2.2.1 stage标签

在stage标签中可以设置beacon的元数据修改、在内存中的属性、数据的替换、加解密混淆等,是一个强大的设置。
set userwx "false";
这个选项是设置执行反射dll所分配的内存属性,true为RWX,false为RX:


所以我们则可以关注进程中内存属性为RWX的区域进行重点的检测对象。

 

set cleanup "true";
这个选项在设置true后,会抹去存放在内存中的反射DLL,false则不会。

而CobaltStrikeScan检测则是通过获取到内存中Dll进行解密后获取到相关的配置,当我们设置为cleanup则能防止其配置读取,后面也会深入CobaltStrikeScan原理。

 

set sleep_mask "true";
当这个选项设置为true时,Beacon会加入一段加解密函数,会对数据和代码进行异或加密,3.11版本是单字节异或,4.2版本是13字节异或。
13字节的密钥:

在内存中被13字节异或加密后的beacon:

解密后的beacon内容:

 

set stomppe "true";
stomppe选项当设置为true时能对MZ、PE和e_lfanew的值进行混淆,这样能使根据MZ等关键字的内存匹配失效:

 

set obfuscate "true";
当obfuscate设置为true时,能混淆dll的导入表、区段名等信息,使得根据导入表匹配的规则失效,0x00039ad8开始是被混淆的导入表:

被混淆的区段名:

 

transform-x86的strrep标签中主要是修改替换反射dll中的固定字符,以防止被文件静态特征所匹配到,prepend则是在MZ头前加"\x90\x90\x90",防止在内存中的MZ头被匹配到。

1
2
3
4
5
6
transform-x86 {
        prepend "\x90\x90\x90";
        strrep "ReflectiveLoader" "";
        strrep "beacon.dll" "";
        strrep "This program cannot be run in DOS mode" "";
}

三、狩猎对抗

3.1 流量侧:

在没有设置profile中的http-get、http-post标签时,其CS回传数据给teamserver端是有默认的URI,所以对于使用默认profile的流量通信是比较好捕获的:

Beacon执行命令的结果默认是通过post请求/submit.php?id=<beaconID>发回给服务器。

 

即使配置了profile,但通常每次提交post请求后的响应包数据大小基本一致的,同时能结合默认的连接频率:

 

同时也可以把网上开源的profile加入检测流量规则中,如检测伪装amazing的https://github.com/rsmudge/Malleable-C2-Profiles/blob/master/normal/amazon.profile:

 

默认UA也可以作为检测标准,https://docs.google.com/spreadsheets/d/1bpeziZ-ObG8zKronKGyhXg2UsETk_fqY5dD4Tx0q_eY/edit#gid=1635920259这里有约100多条的,可见这里基本上是IE浏览器的UA,而如果在一个使用火狐浏览器的机子有IE的UA则比较可疑:

 

可以根据默认证书进行检测:

DNS上线可以通过单个域内的大量查询检测或者 TXT 记录数据量来检测 :

 

JA3/JA3S 可以用客户端和服务器之间的SSL通信创建指纹。签名可以从客户端数据包中的字段进行收集:SSL 版本、接受的密码、扩展列表、椭圆曲线、椭圆曲线格式。

3.2主机侧

在主机侧我们可以从不同的角度去进行分析,下面我们将从文件、进程、线程、内存及其它行为等方面出发分析。

3.2.1 文件

提到文件特征YARA规则是不能缺少的,YARA规则是VT发布的,用于样本的批量检索和查杀,目前很多知名软件也使用YARA。其中YARA规则常以hash(文件的hash或导入表之类的hash)、PE头、pdb、全局字符串、互斥体、特定的函数等信息作为特征。

 

如以Elastic的yara规则为例,该规则定位了在rdata段的数据:

1
2
3
4
5
6
7
8
9
10
11
12
rule cobaltstrike_beacon_strings
{
meta:
    author = "Elastic"
    description = "Identifies strings used in Cobalt Strike Beacon DLL."
strings:
    $a = "%02d/%02d/%02d %02d:%02d:%02d"
    $b = "Started service %s on %s"
    $c = "%s as %s\\%s: %d"
condition:
    2 of them
}

但在加sleep_mask选项后,其字符串和数据会被进行混淆,但我们可以对代码段中特定的函数进行标记,下面这条规则内容就是对解密函数进行标记:

1
2
3
4
5
6
7
8
9
10
11
12
rule HKTL_CobaltStrike_Beacon_4_2_Decrypt {
   meta:
      author = "Elastic"
      description = "Identifies deobfuscation routine used in Cobalt Strike Beacon DLL version 4.2"
      reference = "https://www.elastic.co/blog/detecting-cobalt-strike-with-memory-signatures"
      date = "2021-03-16"
   strings:
      $a_x64 = {4C 8B 53 08 45 8B 0A 45 8B 5A 04 4D 8D 52 08 45 85 C9 75 05 45 85 DB 74 33 45 3B CB 73 E6 49 8B F9 4C 8B 03}
      $a_x86 = {8B 46 04 8B 08 8B 50 04 83 C0 08 89 55 08 89 45 0C 85 C9 75 04 85 D2 74 23 3B CA 73 E6 8B 06 8D 3C 08 33 D2}
   condition:
      any of them
}

其对应的函数为加解密函数:

但在CS4.5的版本中已经支持了自定义其加解密函数并通过can证书进行设置。

3.2.2进程

可以使用ETW事件进程的后续行为进行监控,包括以下行为:
1.网络连接(HTTP/HTTPS callbacks、DNS queries)
2.文件操作(File system (cd,ls,upload,rm))
3.进程相关(Process termination (kill)、Shell commands (run,execute))
以下为实现代码:

https://github.com/3lp4tr0n/BeaconHunter

3.2.2.1进程的异常行为

如notepad.exe进程存在网络连接

1
2
3
4
5
6
<RuleGroup name="" groupRelation="or">
        <NetworkConnect onmatch="include">
            <Image name="Usermode" condition="begin with">C:\Users</Image>
<Image condition="image">notepad.exe</Image>
        </NetworkConnect>
</RuleGroup>

3.2.2.2进程的父进程

cobaltstrike在运行宏病毒时会拉起一个新的进程(如rundll32.exe)进行注入,而其进程的父进程为winword.exe,这显然是不合理的,所以我们可以从父子进程进行判断。

3.2.2.3 进程参数

Cobaltstrike默认注入的是rundll32.exe进程,但rundll32.exe的进程通常会带有一系列dll作为参数,如rundll32.exe shell32.dll,Control_RunDLL,如果没有dll参数则是比较异常的:

3.2.2.4调用子程序

在后渗透的阶段,使用run或shell命令运行whoami.exe、net.exe、tasklist等程序进行信息收集,我们可以通过对相关程序的原始文件名进行监控,从而知道系统中的可疑程序。

3.2.3线程

3.2.3.1线程的状态

使用 NtQuerySystemInformation 枚举所有进程的所有线程,同时记录线程的起始地址、状态、等待时间原因等信息,然后筛选出存在 wait_reason 为 DelayExecution、Suspended、Executive 、UserRequest 、 WrUserRequest状态的线程, 然后调用GetExitCodeThread判断以上状态的线程线程是否结束,若lpExitCode 为STILL_ACTIVE 表示线程正在运行,则标记为可疑线程,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
bool should_scan(const util::thread_info& info)
{
    if (!info.is_extended) {
        return true;
    }
    const KTHREAD_STATE state = (KTHREAD_STATE)info.ext.state;
    if (state == Running || state == Ready) {
        return true;
    }
    if (state == Terminated) {
        return false;
    }
    if (state == Waiting) {
        if (info.ext.start_addr == 0) {
            return true;
        }
        if (info.ext.wait_reason == DelayExecution
            || info.ext.wait_reason == Suspended
            || info.ext.wait_reason == Executive // the thread is waiting got the scheduler
            || info.ext.wait_reason == UserRequest // i.e. WaitForSingleObject/WaitForMultipleObjects
            || info.ext.wait_reason == WrUserRequest // i.e. when the thread calls GetMessage
            )
        {
            return true;
        }
    }
    return false;
}

代码详见:pe-sieve

3.2.3.2线程的地址

Cobaltstrike在spawn时默认会拉起rundll32进程并进行远程线程注入,而当在进程中的一个线程的起始地址没有关联到模块,同时该线程堆栈调用到sleepex()函数,这是值得我们去注意的。

3.2.3.3 sysmon对线程的监控

Sysmon的ID 8和 ID10分别对应创建远程线程、进程访问的监控,其中监控创建远程线程的原理是在内核中使用PsSetCreateThreadNotifyRoutine、ObRegisterCallbacks注册了回调进行相应的过滤:
而cobaltstrike默认的dllinject、shinject功能均涉及到openprocess()和CreateRemoteThread():

3.2.4内存

3.2.4.1 内存的属性

在内存方面我们可以注意进程中所申请的内存是否没关联到模块、内存属性是否为RWX,而cobaltstrike的profile选项中能设置通过set userwx设置内存属性:

3.2.4.2内存的内容

同时我们可以观察其内存区域的内容, 例如BeaconEye项目是匹配了 程序存放在heap区的配置信息:

3.2.4.3 异常的内存

可以检测可执行的非Image状态的已commit的内存区域,或者扫描所加载的模块是否存在于 PEB entry 中、模块的路径名称内容是否与磁盘上的一致等等,这些都是检测Process Hollowing、反射注入的手段,可以参考moneta、pe-sieve等这些项目,里面有详细的实现,代码片段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
bool should_scan(const util::thread_info& info)
{
    if (!info.is_extended) {
        return true;
    }
    const KTHREAD_STATE state = (KTHREAD_STATE)info.ext.state;
    if (state == Running || state == Ready) {
        return true;
    }
    if (state == Terminated) {
        return false;
    }
    if (state == Waiting) {
        if (info.ext.start_addr == 0) {
            return true;
        }
        if (info.ext.wait_reason == DelayExecution
            || info.ext.wait_reason == Suspended
            || info.ext.wait_reason == Executive // the thread is waiting got the scheduler
            || info.ext.wait_reason == UserRequest // i.e. WaitForSingleObject/WaitForMultipleObjects
            || info.ext.wait_reason == WrUserRequest // i.e. when the thread calls GetMessage
            )
        {
            return true;
        }
    }
    return false;
}

3.2.5 其他手段

3.2.5.1 管道名:

Cobaltstrike执行mimikatz等与反射dll相关的命令后,后续会使用管道来进行进程间的通信,把执行的结果返回到控制端。所以我们通过监控管道的创建来进行检测。Sysmon可以监控到系统程序的管道创建和连接,分别对应17、18号事件,下面为cobaltstrike的默认管道名,在4.2版本后管道名可以在profile中进行自定义:

1
2
3
4
5
6
7
8
9
10
<RuleGroup name="" groupRelation="or">
    <PipeEvent onmatch="include">
        <!-- Cobalt Strike Pipe Names -->
        <PipeName condition="contains all">MSSE-;-server</PipeName>
        <PipeName condition="begin with">\postex_</PipeName>
        <PipeName condition="begin with">\postex_ssh_</PipeName>
        <PipeName condition="begin with">\status_</PipeName>
        <PipeName condition="begin with">\msagent_</PipeName>
    </PipeEvent>
</RuleGroup>

3.2.5.2 敏感行为

在使用Cobaltstrike渗透过程中,常有比较敏感的行为,如steal_token、hashdump、jump psexec等,下面是令牌窃取的比较经典的实现步骤:
1) 通过 OpenProcess 获取 SYSTEM 权限进程的句柄
2) 通过 OpenProcessToken 获取该进程的访s问令牌
3) 通过 DuplicateTokenEx 函数复制该令牌
4) 通过 CreateProcessWithTokenW 创建具备同样访问令牌的进程
我们能通过对上述相关API进行hook或令牌窃取所产生的日志进行监控。
而Jump psexec则可通过监控特定文件夹的文件创建、进程创建、管道名称、进程调用链等进行检测。其会先调用上传文件:

将文件保存在目标ip的ADMIN\$下,名字随机为f9febc5.exe

然后通过任务号100 inline-execute调用该文件。进程调用链如下:

图源:https://www.unh4ck.com

四、小结

通过这篇文章我们从Cobalt Strike的profile选项出发,探究了CS的狩猎对抗,认识到profile选项是相当丰富,可扩展性和实战意义都很强。对抗在不断发展,当我们在思考cs的profile选项的同时,尽可能更多的发现其检测点,做到以攻促防,以防验攻。
最后,谢谢观看。

 

参考:
https://thedfirreport.com/2022/01/24/cobalt-strike-a-defenders-guide-part-2/
https://github.com/SigmaHQ/sigma
https://www.trustedsec.com/blog/tailoring-cobalt-strike-on-target/


[招生]科锐逆向工程师培训46期预科班将于 2023年02月09日 正式开班

收藏
点赞6
打赏
分享
最新回复 (6)
雪    币: 380
活跃值: 活跃值 (201)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
深拥初雪 活跃值 2022-10-10 17:07
2
0
鸡哥牛逼 先舔再看
雪    币: 225
活跃值: 活跃值 (1239)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wx_0xC05StackOver 活跃值 2022-10-10 19:11
3
0
我没点开我都知道肯定是鸡哥
雪    币: 1120
活跃值: 活跃值 (997)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
yllen 活跃值 2022-10-11 10:37
4
0
iKun永相随
雪    币: 81
活跃值: 活跃值 (841)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
菜小基 活跃值 2022-10-11 22:37
5
0
卧槽   鸡哥更新了
雪    币: 80
活跃值: 活跃值 (256)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
向往星空 活跃值 1 2022-10-14 09:19
6
0
鸡哥yyds
雪    币: 137
活跃值: 活跃值 (191)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Jnj3369 活跃值 2022-10-21 20:59
7
0
大佬好。小白想问一下,如果系统管理员可以通过这个方法检测cobaltstrike,那cobaltstrike咋会引起那么多麻烦呢?
游客
登录 | 注册 方可回帖
返回