Malleable C2

简介

Cobalt Strike的 Malleable-C2-Profiles配置文件是用来伪装流量和修改流量特征的,目的是让通讯更加隐蔽同时还可以控制Beacon的一些默认行为。 在启动CS服务器时我们可以指定一个配置文件,每个CS只能载入一个配置文件,但是在4.x后发生了一些改变虽然还是只能载入一个配置文件但是可以通过定义配置变体达到同时使用多种c2配置

加载Malleable-C2-Profiles命令

./teamserver [external IP] [password] [/path/to/my.profile]

检查预览Profiles文件,c2lint Linux程序可以检查C2配置文件的语法并可以使用随机数据进行测试

./c2lint [/path/to/my.profile]

注:里面同时也放有windows版的c2lint但是因为shell脚本颜色代码的原因使用cmd运行会出现如下乱码,下面提供了一下解决方法

  • 1.使用linux不用Windows运行服务端

  • 2.使用Git Bash运行c2lint这个Linux shell脚本

  • 3.使用Terminus终端运行c2lint.bat

  • 4.使用cmder运行c2lint.bat

  • 5.使用其他终端

以下是演示图片

cmder运行

Terminus运行

Malleable C2基本语法

首先我们来看一个Malleable C2配置文件

set sleeptime "60000";​set jitter    "0";​set maxdns    "255";​set sample_name "Cobalt Strike Beacon (Default)";​http-get {    set uri "/ca /dpixel /__utm.gif /pixel.gif /g.pixel /dot.gif /updates.rss /fwlink /cm /cx /pixel /match /visit.js /load /push /ptj /j.ad /ga.js /en_US/all.js /activity /IE9CompatViewList.xml";​    client {        metadata {            base64;            header "Cookie";        }    }​    server {        header "Content-Type" "application/octet-stream";​        output {            print;        }    }}​http-post {    set uri "/submit.php";​    client {        header "Content-Type" "application/octet-stream";​        id {            parameter "id";        }​        output {			      base64;            print;        }    }​    server {        header "Content-Type" "text/html";​        output {			      base64;            print;        }	}}

基本语法:

# 用来注释set 用来设置一些选项的值; 表示一行语句结束配置文件使用{}将语句和信息组合在一起。语句总是以分号结尾

http-get

先来看一下http-get代码块,最开始的set选项先不写会放到下面一起列出来

http-get {    set uri "/ca /dpixel /__utm.gif /pixel.gif /g.pixel /dot.gif /updates.rss /fwlink /cm /cx /pixel /match /visit.js /load /push /ptj /j.ad /ga.js /en_US/all.js /activity /IE9CompatViewList.xml";​    client {        header "Accept-Language" "zh-CN";        metadata {            base64;            header "Cookie";        }    }​    server {        header "Content-Type" "application/octet-stream";​        output {            base64;            print;        }    }}

http-get代码块中包含了 client 和 server 两大块, 分别代表Beacon(客户端) http请求规则和CS(服务端)响应规则。

set uri 设置了http get通信时所使用的url,Beacon每次get通信时会随机选择一个

http-get client

当Beacon HTTP回连CS服务器时它会发送关于自身的元数据给CS服务端而我们可以通过client代码块设置HTTP请求头和元数据的编码规则。

header选项设置HTTP请求头

metadata代码块设置元数据编码规则

为了直观的展示我们使用c2lint测试一下

可以看到http请求是按照我们设置的规则进行的,通过header关键字设置了http请求的Accept-Language字段,然后通过metadata代码块设置了元数据使用base64进行编码,最后使用header关键字指定编码后的数据存储在Cookie字段,注:metadata代码块里的header关键字在metadata中表达的意思为终止,表示数据编码规则设置完毕,指定数据存放在http请求头中的什么位置,与client代码块种中的header关键字意思不一样

http-get server

上面说了http-get client是用来控制客户端请求,那么http-get server很明显就是用来控制服务端响应的

在server代码块中同样使用header设置http的响应头字段。

output:代表服务端返回数据可以通过output代码块设置返回数据的编码规则

我们同样使用c2lint测试一下上述的C2配置文件并查看一下get响应

output代码块和metadata代码块同样需要一个关键字来表示编码规则终止,这里使用的是print表示直接输出放到body中

总结

http-get代码块里的client既告诉了Beacon如何发送请求和传输数据也是告诉了CS如何对接收到的数据解码,而server则是告诉CS服务端如何返回数据,Beacon如何接收返回数据并解码,http-get代码块仅对通信过程中的GET请求有效

http-post

http-post {    set uri "/submit.php";​    client {        header "Content-Type" "application/octet-stream";​        id {            parameter "id";        }​        output {			      base64;            print;        }    }​    server {        header "Content-Type" "text/html";​        output {			      base64;            print;        }	}}

在说http-post之前我们要先了解一下http(s) payload下 C2和Beacon的通信过程,我这里以无阶段payload通信过程为例(分阶段payload与无阶段payload并无什么区别仅多一个步骤,分阶段payload会放在一会讲)

Beacon在执行后会通过http get请求与C2通信发送元数据,然后如果C2有任务则C2在响应get请求时会发送任务,Beacon收到任务数据包(此数据包包含此任务的id和任务具体内容)后会执行任务,在完成后会通过http post请求回传结果。这个post请求中就包含着两个东西一个是任务id一个执行结果

http-post与http-get在配置上基本没有太大区别,只是http-post client里多了一个id代码块,此代码块的作用就是上面说的,在回传结果时任务id就由此代码块控制,output代码块则就是设置回传执行结果的

id {   parameter "id";}parameter是终止语句表示把数据存放在指定的url参数中,我们这里指定存放在id参数中,返回时就会像上图一样把任务id放在url参数中?id=id
output {    base64;    print;}base64;表示将任务执行结果base64编码,print; 终止语句表示编码结束并指定数据存放位置为body

上面我们也零星的介绍了不少关键字并熟悉了一下基本规则现在来详细的列一下全部关键字。

数据转换(指定数据编码方式)

就是CS发送和接收数据时的编码解码方式 数据转换总是以终止关键字结束(终止关键字代表编码结束并给编码后的数据指定一个存放位置)

这些关键字可以随意组合使用来对数据编码,同时还可以使用下面的转义字符

Strings转义字符

可以在配置文件中使用的转义字符

http-get {	set uri "/pixel.gif"​	client {		metadata {			base64;			header "Cookie";		}	}​	server {		header "Content-Type" "image/gif";		output {		  # hexdump pixel.gif			# 0000000 47 49 46 38 39 61 01 00 01 00 80 00 00 00 00 00			# 0000010 ff ff ff 21 f9 04 01 00 00 00 00 2c 00 00 00 00			# 0000020 01 00 01 00 00 02 01 44 00 3b 			prepend "\x01\x00\x01\x00\x00\x02\x01\x44\x00\x3b";			prepend "\xff\xff\xff\x21\xf9\x04\x01\x00\x00\x00\x2c\x00\x00\x00\x00";			prepend "\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\x00\x00";			print;		}	}}

终止关键字(指定数据存放位置)

数据编码规则完毕后必须要有终止关键字来表示编码规则设置完毕并指定数据存放位置

这里的终止关键字parameter与,id代码块中的parameter关键字可不是一个意思

下面补充一些终止关键字需要注意的地方

http-get.server.output,http-post.server.output和http-stager.server.output这个三个代码块不能使用其他的终止语句只能使用print语句,http-get.client.metadata则是不能使用print当作终止语句的原因和get body传参有关cs并没有考虑这种,写C2配置文件时请注意这些规则

还有就是如果在http-post.client.output 上使用 header、parameter 或 uri-append, Beacon会将其响应分块到合理的长度发送。

set可设置选项

可设置选项分成两种全局和局部,全局选项更改全局的Beacon 设置比如

局部选项仅在某些代码块可用比如

注:范围为空的代表全局选项,默认值没有的代表为空没有默认值

http-stager

前面我们在简述C2与Beacon通信过程时说过有两种payload,分阶段payload(payload staging)无阶段payload(payload stageless)前面简述了无阶段payload,现在来说一下分阶段payload。分阶段payload仅仅比无阶段payload多一个环节。它在正式和C2通信前会先向C2请求Beacon核心代码,待加载执行完毕后会正式和C2进行通信,从此处开始与无阶段payload是一样的,而这个加载过程则被称为staging(分阶段),可见分阶段payload分成两个部分,payload stager(用来下载执行stage的一小段代码)payload stage(Beacon核心代码)

同样CS也给了我们http-stage代码块用来控制stage(Beacon核心代码)发送过程

http-stager {    set uri_x86 "/get32.gif";    set uri_x64 "/get64.gif";    client {        parameter "id" "1234";        header "Cookie" "SomeValue";    }    server {        header "Content-Type" "image/gif";        output {            prepend "GIF89a";            print;        }    }}

http-stager在设置上和http-get没啥区别只是从uri变成了uri_x86和uri_x64这是为了区分加载器架构x86 payload请求uri_x86,x64 payload请求uri_x64

http-config

http-config代码块会影响Cobalt Strike所有HTTP响应。

http-config {    set headers "Date, Server, Content-Length, Keep-Alive, Connection, Content-Type";    header "Server" "Apache";    header "Keep-Alive" "timeout=5, max=100";    header "Connection" "Keep-Alive";}

可以看到所有http响应都有附加设置的字段

SSL 证书设置

自签ssl证书

https-certificate {    set CN "bobsmalware.com";    set O "Bob’s Malware";}

自签需要使用set设置如下选项

使用假证书其实就没必要生成store文件然后照下面那样设置,直接使用这些的选项就行了

使用SSL证书文件

https-certificate {	set keystore "domain.store";	set password "mypassword";}

使用真实有效的ssl证书只需要设置如下选项

Profile配置文件变体

在最开始我们说了Cobalt Strike4.x虽然每次还是只能载入一个配置文件,但是在这个一个配置文件中我们可以定义多种配置让CS在建立监听器时可以选择不同配置选项

先上图

想要实现一个配置文件中包含多种配置(或者说变体因为Variants翻译过来就是变体)只需要在代码上稍作修改。

http-get {	set uri "/pixel.gif";​	client {		header "Accept-Language" "zh-CN";		metadata {			base64;			header "Cookie";		}	}​	server {		header "Content-Type" "image/gif";​		output {			base64;			print;		}	}}#仅需在后面加上一个名字CS就能识别到,后面不跟名字代表是默认配置http-get "第二种配置" {	set uri "/pixel.gif";​	client {		header "Accept-Language" "zh-CN";		metadata {			header "Cookie";		}	}​	server {		header "Content-Type" "image/gif";​		output {			print;		}	}}

如上所示在后面加上名字就能在一个配置文件中定义多种配置。那问题来了如果我只单独多添加了一个http-get,那其他部分的配置会怎么样呢,答案是其他部分的配置会使用默认配置

通过上面两种图应该就能看出来,第二种配置只有http-get与default配置不同,其他代码块如http-post都是使用的default的。

数字证书签名

Cobalt Strike在生成可执行文件时可以对其进行签名,但是使用此功能必须先到配置文件中设置code-signer代码块

code-signer {    set keystore "keystore.jks";    set password "password";    set alias "server";}

注意事项

Cobalt Strike每次只能加载一个配置文件但是一个配置文件可以包含多个配置,需要注意的是如果配置文件(通信规则部分)发生了改变那么以前生成的Beacon即使启动了也无法与加载了新配置文件的Cobalt Strike通信

使用base64编码数据时需要注意不能将此数据存放到URL中因为base64编码有可能出现( + 、= 和 / )这些在URL具有特殊含义的字符所以如果要使用base64编码还想让其附加到url中就请使用base64url

编写或修改配置请一定使用c2lint工具进行测试

Malleable PE, Process Injection, and Post Exploitation(Beacon行为)

这东西如果直译就是可拓展 PE,进程注入和后渗透,但是按照我个人的理解我更愿意把我接下来要写的部分叫做Beacon行为控制

stage

在正式讲stage之前我们还得补充一点知识,前面我们提到过分阶段payload和无阶段payload,说过分阶段payload会远程加载执行stage,其实这个stage就是一个反射dll(Beacon DLL),而无阶段payload之所以不用远程加载是因为它本身已经把stage内嵌到了自身所以不用远程加载。

stage块控制Beacon DLL如何加载到内存中并且可以修改Beacon DLL的内容,以此可以达到一定效果的免杀

stage {	set userwx "false"; 	set compile_time "14 Jul 2009 8:14:00";	set image_size_x86 "512000";	set image_size_x64 "512000";	set obfuscate "true";​	transform-x86 {		prepend "\x90\x90";		strrep "ReflectiveLoader" "DoLegitStuff";	}	transform-x64 {		# transform the x64 rDLL stage	}​	stringw "I am not Beacon!";}

stage相关的局部选项就先不说了后面会直接放表

transform-x86 和 transform-x64这两个代码块都是用来修改Beacon反射DLL的,可以使用以下三种命令修改

以上三种指令使用需要注意的是prepend命令这个命令是在Beacon反射DLL最开始的位置添加字符串,你一定要确保此字符串是对应架构(x86,x64)的有效代码,至于为什么在后面就明白了

stage代码块使用以下命令则可以将字符串添加到反射dll的.rdata

stage代码块可使用的局部选项

Cobalt Strike的Linux软件包中有一个工具peclone,用于从指定的DLL中提取PE的相关信息,并将根据这些信息生成stage代码块。

./peclone [/path/to/sample.dll]

内存混淆和绕过

在stage可以使用prepend命令来绕过一些检测,比如扫描内存段的前几个字节以查找注入的DLL的痕迹。strrep命令则是替换指定的字符串比如在Beacon反射dll里默认使用ReflectiveLoader字符串作为导出名这就是一个特征,你可以使用strrep替换这个字符串。

如果strrep还不够还可以使用sleep_mask选项将其设置为true,启用内存混淆这将会使Beacon每次在sleep前混淆自己所在的内存区域,并在sleep结束后解混淆自己然后回连CS服务器重复这个过程。SMB和TCP Beacon将在等待新连接或等待来自其父会话的数据时混淆自己。

将 userwx 设置为 false 可以要求Beacon的反射加载器避免使用RWX权限分配内存。具有这些权限的内存段将引起分析人员和安全产品的额外关注。

默认情况下,Beacon的反射加载器使用VirtualAlloc分配内存。使用 allocator 选项来改变这一点。 可以选择HeapAlloc或MapViewOfFile。而module_(x64,x86)则是allocator选项的

如果你担心 Beacon stage 会在初始化完成后继续存放在内存,请将 cleanup 选项设置为 true。此选项将在Beacon stage初始化完成后,释放stage这块内存

进程注入

process-inject代码块可对进程注入相关的内容进行配置,控制注入相关的行为

process-inject {    # set how memory is allocated in a remote process    set allocator "VirtualAllocEx";    set min_alloc "16384";    set startrwx "true";    set userwx "false";​    transform-x86 {        prepend "\x90\x90";    }    transform-x64 {        # transform x64 injected content    }​    execute {		    CreateThread "ntdll.dll!RtlUserThreadStart";		    SetThreadContext;		    RtlCreateUserThread;    }}

transform-x86 和 transform-x64 代码块可以向Beacon注入的内容里添加东西。这两个代码块支持两个命令:prepend和append。 prepend在注入内容的开始位置添加数据,append在注入内存的最后位置添加数据,请确保插入的数据与注入内容的架构(x86,x64)相符。更多选项说明在本节后面

execute代码块控制Beacon在进程注入时要使用的方法。Beacon会检查execute代码块中的每个选项,确定该选项在当前环境中是否可用,在可用时尝试该方法。如果未执行代码,则移至下一个选项。执行选项包括:

CreateThread是专门针对自注入的,SetThreadContext和NtQueueApcThread-s选项和Beacon后渗透任务的临时进程相关。这些函数修改暂停进程的主线程来进行注入,NtQueueApcThread-s选项是Cobalt Strike对所谓的Early Bird技术的实现。

NtQueueApcThread、RtlCreateUserThread和CreateRemoteThread是标准的进程注入选项,用于将代码注入到远程进程中

此外CreateThread和CreateRemoteThread还具有线程起始地址欺骗功能,这对于绕过像Get-InjectedThread这样的技术很有用。使用方式[function] "module!function+0x##" 其中0x##部分是加在起始地址上的偏移量

process-inject代码块相关选项

后渗透任务

post-ex {	# control the temporary process we spawn to	set spawnto_x86 "%windir%\\syswow64\\rundll32.exe";	set spawnto_x64 "%windir%\\sysnative\\rundll32.exe";​	# change the permissions and content of our post-ex DLLs	set obfuscate "true";	# change our post-ex output named pipe names...	set pipename "evil_####, stuff\\not_##_ev#l";​	# pass key function pointers from Beacon to its child jobs	set smartinject "true";​	# disable AMSI in powerpick, execute-assembly, and psinject	set amsi_disable "true";}

较大的Cobalt Strike后渗透功能(如截屏、键盘记录器、hashdump等)是以Windows DLL的形式实现的(注:更准确来说是反射dll形式)。为了执行这些功能,Cobalt Strike会生成一个临时进程,并将功能注入其中。进程注入块控制进程注入步骤。post-ex代码块控制了Cobalt Strike的后渗透任务的具体内容和行为。

spawnto_x86 和 spawnto_x64 选项控制后渗透功能生成的临时进程,以下是有关这些值的一些注意事项

  1. 指定的路径必须是进程的完整路径

  2. 指定路径时可以使用环境变量(例如 %windir% )。

  3. 不要直接指定 %windir%\system32 或 c:\windows\system32 。始终使用 syswow64 (代表x86) 和

    sysnative (代表x64) 。Beacon 会在必要时将这些值调整为 system32 。

  4. 对于 spawnto_x86 值,必须指定一个 x86 程序。对于 spawnto_x64值,必须指定一个 x64 程序。

  5. 您指定的路径(减去自动的 syswow64/sysnative 调整)必须同时存在于 x64(native)和 x86(wow64)的文件系统视图中。

obfuscate 选项会混淆post-ex dll的内容,这与通过 stage 代码块的obfuscate 和 userwx 选项非常相似。

pipename 因为后渗透功能是以dll的方式实现的所以当它被注入到别的进程后需要与Beacon进程进行通信而使用的通信机制则是命名管道,通过此选项可以更改通信时使用的命名管道的名字

smartinject 选项指示 Beacon 将关键函数指针(如 GetProcAddress 和 LoadLibrary)嵌入到同架构的post-ex DLL中。 这使 post-ex DLL可以在新进程中进行自我引导,而不会出现类似shellcode的行为,如使用PEB寻找kernel32.dll和其中的函数指针

thread_hint 选项允许多线程的post-ex DLL使用线程地址欺骗使用方法同上

amsi_disable 选项指示powerpick、execute-assembly和psinject在加载.NET或PowerShell代码之前对AmsiScanBuffer函数进行修补。(限制反恶意软件扫描接口)

keylogger 选项用来控制Cobalt Strike的键盘记录器使用的函数。可选值GetAsyncKeyState和SetWindowsHookEx(默认使用GetAsyncKeyState函数)

最后更新于

这有帮助吗?