Cobaltstrike二开系列(四)

渗透技巧 1年前 (2022) admin
654 0 0

微信公众号: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)nullnew 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)nullnew 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)nullnew 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)nullnew 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)nullnew Response("400 Not Found""text/plain"""));
                     }

                     return this.processResponse(uri, method, header, param, false, (WebService)nullnew 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)nullnew 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二开群

Cobaltstrike二开系列(四)

如果感觉不错,欢迎给项目点个Star或者分享给其他师傅


原文始发于微信公众号(cybersec):Cobaltstrike二开系列(四)

版权声明:admin 发表于 2022年12月8日 上午10:01。
转载请注明:Cobaltstrike二开系列(四) | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...