微信公众号:BateSec
为国之安全而奋斗,为信息安全而发声!
如有问题或建议,请在公众号后台留言
如果你觉得本文对你有帮助,欢迎关注本公众号,后期会不定时公布二开细节
特征修改
书接上文,确定了特征位置,开始针对各个特征进行修改
根据以上能得出,需要修改源码的特征为以下几个
1.基于工具服务器默认特征的角度
2.规避基于Ip测绘的角度
3.尝试基于流量本身的特点
4.chechsum8
5.JA3/S & JARM
基于工具服务器默认特征的角度
将符合cs服务器特征(默认证书,默认端口)的ip加入到情报库,seebug上看到一篇文章讲如何用Cobalt Strike绕过流量审计设备,里面讲到三种去除cs特征的方式,修改默认端口,修改默认证书,修改默认dns。在攻击者修改掉默认配置后,检测方法失效。
通过修改teamserver 能修改默认特征
java -XX:ParallelGCThreads=4 -Dcobaltstrike.server_port=50050 -Dcobaltstrike.server_bindto=0.0.0.0 -Djavax.net.ssl.keyStore=./cobaltstrike.store -Djavax.net.ssl.keyStorePassword=Microsoft -server -XX:+AggressiveHeap -XX:+UseParallelGC -classpath ./cs.jar server.TeamServer $*
自行
修改就可以
基于Ip测绘的角度
首先了解IP测绘探测原理(网络空间测绘)
通过探测报文进行协议识别
通常应用程序会默认使用某个固定端口号,但通过端口号直接映射协议是不准确的。因此,在通过端口扫描发现开放的TCP/UDP端口后,需要向其发送探测报文(Probe),并对响应(Response,也称为Banner)进行解析识别,从而确定具体的服务协议。
空探测报文
探测B类协议,建立连接后不用发送任何信息,等待远程直接回复即可。以典型的ssh协议为例,通过netcat建立TCP连接。
$ nc 192.168.1.1 22
SSH-2.0-OpenSSH_7.2p2 Ubuntu-4ubuntu2.8
从返回的Banner
可得知,协议为ssh,应用软件为OpenSSH
,应用版本号7.2p2
,操作系统为Ubuntu
。
HTTP协议探测报文
最为常见的便是http协议,发送最简单的HTTP请求进行探测。
$ echo "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n" | nc localhost 80
HTTP/1.1 200 OK
Server: nginx/1.21.1
Date: Mon, 06 Sep 2021 13:54:53 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 06 Jul 2021 14:59:17 GMT
Connection: keep-alive
Accept-Ranges: bytes
...
...
Thank you for using nginx.
提取更多属性
以etcd应用为例,该应用默认通过2379端口提供HTTP API服务。当访问根路径时,返回的是不带其他特征信息的404 Not Found。
$ echo "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n" | nc localhost 2379
HTTP/1.1 404 Not Found
Content-Type: text/plain; charset=utf-8
X-Content-Type-Options: nosniff
Date: Mon, 06 Sep 2021 14:30:29 GMT
Content-Length: 19
404 page not found
我们可以了解到这是一个http协议的服务。但如果对该应用多一分了解,可以知道访问/version路径时,etcd应用将会返回应用名称及版本好。从下面返回的Banner信息可以得知,应用为“etcdserver”,版本号为3.3.11。
$ echo"GET /version HTTP/1.1\r\nHost: localhost\r\n\r\n" | nc localhost2379
HTTP/1.1200 OK
Content-Type: application/json
Date: Mon,06 Sep202114:32:13 GMT
Content-Length:45
{"etcdserver":"3.3.11","etcdcluster":"3.3.0"}
https证书信息探测
对于https站点来说,通过其证书信息还可以进一步挖掘更多有价值的信息。在下面的例子中,可以通过证书信息了解到站点所在国家、省、市以及所属公司。
$openssl s_client -connect qq.com:443
CONNECTED(00000006)
depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
verifyreturn:1
depth=1 C = US, O = DigiCert Inc, CN = DigiCert Secure Site CN CA G3
verifyreturn:1
depth=0 C = CN, ST = Guangdong Province, L = Shenzhen, O = Shenzhen Tencent Computer Systems Company Limited, CN = roll.news.qq.com
verifyreturn:1
---
Certificatechain
0s:/C=CN/ST=Guangdong Province/L=Shenzhen/O=Shenzhen Tencent Computer Systems Company Limited/CN=roll.news.qq.com
i:/C=US/O=DigiCert Inc/CN=DigiCert Secure Site CN CA G3
1s:/C=US/O=DigiCert Inc/CN=DigiCert Secure Site CN CA G3
i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA
---
逃避ip测绘
fireeye的研究文章里提到该方式, 针对3.13以前版本在zoomeye里通过如下语法搜索 “HTTP/1.1 404 Not Found Date:” +”GMT Content-Type: text/plain Content-Length: 0″ -“Connection:” ,基于其服务器的相应包的特点可以判断该服务器为cs。
HTTP/1.1 404 Not Found
Content-Type: text/plain
Date: Day, DD Mmm YYYY HH:MM:SS GMT
Content-Length: 0
这个特征最为明显
可以通过修改源码,修改此特征
源码位置:
cloudstrike/WebServer.java
修改404
通过搜索404
修改为别的参数,还可以利用
if (allowedByUA && !blockedByUA) {
if (method.equals("OPTIONS")) {
Response r = this.processResponse(uri, method, header, param, false, (WebService)null, new Response("200 OK", "text/html", ""));
r.addHeader("Allow", "OPTIONS,GET,HEAD,POST");
return r;
} else if (!uri.startsWith("/")) {
return this.processResponse(uri, method, header, param, false, (WebService)null, new Response("400 Not Found", "text/plain", ""));
} else {
WebService service;
if (this.hooks.containsKey(uri)) {
service = (WebService)this.hooks.get(uri);
return this.processResponse(uri, method, header, param, true, service, service.serve(uri, method, header, param));
} else if (this.hooksSecondary.containsKey(uri)) {
service = (WebService)this.hooksSecondary.get(uri);
return this.processResponse(uri, method, header, param, false, service, service.serve(uri, method, header, param));
} else if (this.hooks.containsKey(uri + "/")) {
service = (WebService)this.hooks.get(uri + "/");
return this.processResponse(uri + "/", method, header, param, true, service, service.serve(uri, method, header, param));
} else if (uri.startsWith("http://")) {
service = (WebService)this.hooks.get("proxy");
return service != null ? this.processResponse(uri, method, header, param, true, service, service.serve(uri, method, header, param)) : this.processResponse(uri, method, header, param, false, (WebService)null, new Response("4040 Not Found", "text/plain", ""));
} else if (isStagerX64Strict(uri) && this.hooks.containsKey("stager64")) {
service = (WebService)this.hooks.get("stager64");
return this.processResponse(uri + "/", method, header, param, true, service, service.serve(uri, method, header, param));
} else if (isStagerStrict(uri) && this.hooks.containsKey("stager")) {
service = (WebService)this.hooks.get("stager");
return this.processResponse(uri + "/", method, header, param, true, service, service.serve(uri, method, header, param));
} else {
Iterator i = this.hooksSecondary.entrySet().iterator();
WebService svc;
do {
if (!i.hasNext()) {
WebService service1;
if (isStagerX64(uri) && this.hooks.containsKey("stager64")) {
service1 = (WebService)this.hooks.get("stager64");
return this.processResponse(uri + "/", method, header, param, true, service1, service1.serve(uri, method, header, param));
}
if (isStagerX64(uri)) {
print_warn("URI Matches staging (x64) URL, but there is no stager bound...: " + uri);
return this.processResponse(uri, method, header, param, false, (WebService)null, new Response("400 Not Found", "text/plain", ""));
}
if (isStager(uri) && this.hooks.containsKey("stager")) {
service = (WebService)this.hooks.get("stager");
return this.processResponse(uri + "/", method, header, param, true, service, service.serve(uri, method, header, param));
}
if (isStager(uri)) {
print_warn("URI Matches staging (x86) URL, but there is no stager bound...: " + uri);
return this.processResponse(uri, method, header, param, false, (WebService)null, new Response("400 Not Found", "text/plain", ""));
}
return this.processResponse(uri, method, header, param, false, (WebService)null, new Response("400 Not Found", "text/plain", ""));
}
Map.Entry e = (Map.Entry)i.next();
svc = (WebService)e.getValue();
hook = e.getKey() + "";
} while(!uri.startsWith(hook) || !svc.isFuzzy());
return this.processResponse(uri, method, header, param, false, svc, svc.serve(uri.substring(hook.length()), method, header, param));
}
}
} else {
String remoteAddress = header.getProperty("REMOTE_ADDRESS");
if (!allowedByUA) {
print_warn("Request not allowed for useragent '" + useragent + "'. URI=" + uri + " Method=" + method + " Remote Address=" + remoteAddress);
}
if (blockedByUA) {
print_warn("Request blocked for useragent '" + useragent + "'. URI=" + uri + " Method=" + method + " Remote Address=" + remoteAddress);
}
return this.processResponse(uri, method, header, param, false, (WebService)null, new Response("400 Not Found", "text/plain", ""));
}
}
将代码中的404
修改为别的返回值,这样空间测绘得到的值为你修改的值,即可逃避空间搜索引擎标记
尝试基于流量本身的特点
通过修改porfile
文件
构造不同的请求与响应即可绕过流量检测
参考项目:
https://github.com/rsmudge/Malleable-C2-Profiles
chechsum8
运行staging模式的pe文件,会向指定服务器的checksum8路径发起请求来下载stage。
可以修改源码
修改,也可以通过修改porfile
文件
建议大家直接用修改porfile
文件
JA3/S & JARM
这是一类根据Java版本、Web服务器、TLS版本等多因素TLS握手包生成指纹的方法,具体介绍及工具可参考jarm。
目前主要用于识别的CS的JARM指纹是07d2ad16d21d21d07c42d41d00041d24a458a375eef0c576d23a7bab9a9fb1
,利用这个指纹去识别其实也会包含不少像Tomcat、Weblogic这类的JavaWeb服务器。且该指纹是基于JDK11,如果用JDK13去跑CS则会得到不一样的指纹。
引用
文章地址:
https://www.secrss.com/articles/34443
反馈和交流
欢迎各位师傅进群交流,我们也会长期维护
添加微信回复加群
即可加入CS二开群
如果感觉不错,欢迎给项目点个Star或者分享给其他师傅
原文始发于微信公众号(cybersec):Cobaltstrike二开系列(四)