sliver (二)流量分析&源码分析&特征对抗
xiul0 发表于 日本 历史精选 1626浏览 · 2024-10-24 08:55

流量分析

http

以mac为例

首先搭建分析环境

#先生成一个mac的马子
generate --http x.x.x.x:808 --os macos --max-errors 99999  --save ./macos_shell_http
#sliver监听808端口
http -l 808
#tcpdump监听网卡端口流量方便后续分析
sudo tcpdump -i eth0 port 808 -w mac_http.pcap
#计算808端口流量的ja3和jas3,这里用的是别的师傅写的脚本,改了一下,方便直接指定端口 https://github.com/xiul0/ja3box_pro
python3 ja3box_pro.py -i eth0 -p 808


这里由于是http所以只有ja3,可以看到ja3是:19e29534fd49dd27d09234e639c4057e
然后看流量包的数据,这里我把c2通信流量已经过滤出来了,不存在干扰


可以明显的看到在http通信的过程是有很多特定的url路径的,特征也比较明显,这里的url路径是sliver从他的配置文件c2profie中读取到的,我们可以看一看这个配置文件~/.sliver/configs/http-c2.json

"stager_file_ext": ".woff",
        "stager_files": [
            "attribute_text_w01_regular",
            "ZillaSlab-Regular.subset.bbc33fb47cf6",
            "ZillaSlab-Bold.subset.e96c15f68c68",
            "Inter-Regular",
            "Inter-Medium"
        ],
        "stager_paths": [
            "static",
            "assets",
            "fonts",
            "locales"
        ],
        "poll_file_ext": ".js",
        "poll_files": [
            "bootstrap",
            "bootstrap.min",
            "jquery.min",
            "jquery",
            "route",
            "app",
            "app.min",
            "array",
            "backbone",
            "script",
            "email"
        ],
        "poll_paths": [
            "js",
            "umd",
            "assets",
            "bundle",
            "bundles",
            "scripts",
            "script",
            "javascripts",
            "javascript",
            "jscript"
        ],
        "start_session_file_ext": ".html",
        "session_file_ext": ".php",
        "session_files": [
            "login",
            "signin",
            "api",
            "samples",
            "rpc",
            "index",
            "admin",
            "register",
            "sign-up"
        ],
        "session_paths": [
            "php",
            "api",
            "upload",
            "actions",
            "rest",
            "v1",
            "auth",
            "authenticate",
            "oauth",
            "oauth2",
            "oauth2callback",
            "database",
            "db",
            "namespaces"
        ],
        "close_file_ext": ".png",
        "close_files": [
            "favicon",
            "sample",
            "example"
        ],
        "close_paths": [
            "static",
            "www",
            "assets",
            "images",
            "icons",
            "image",
            "icon",
            "png"
        ]
    },
    "server_config": {
        "random_version_headers": false,
        "headers": [],
        "cookies": [
            "PHPSESSID",
            "SID",
            "SSID",
            "APISID",
            "csrf-state",
            "AWSALBCORS"
        ]
        .......

最终可以肯定的是,这些url都是来自这个字典的组合拼接,那这个c2file又从何而来呢,
这里可以先看一下sliver官方docs https://sliver.sh/docs?name=HTTPS+C2,给出了这个配置文件和这些字段的解释,然后再看一下sliver源码生成这个配置文件的函数,


可以明确的是sliver http-c2.json中的字典是从一个更大的字典中随机读取出来的,

这样的话想通过ids这种流量设备通过url特征或者cookie等来识别原生sliver http的c2通信就成为了一个覆盖率的问题(字典太大了规则没法覆盖全,就算全覆盖也需要结合其他特征来佐证,否则误报率会非常高,只能选择性覆盖,甚至sliver开发者也鼓励使用者用gpt去生成这个profiles文件的字典)


但是同样,c2-profiles字典中也还是有相对固定的值,比如ua头,默认配置下,userAgent的值是空的,sliver希望我们自定义但是一般使用者不会自定义,所以在生成implant的过程中,sliver会根据生成implant的类型来设置ua

if userAgent == "" {
        switch goos {
        case "windows":
            switch goarch {
            case "amd64":
                return fmt.Sprintf("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36", ChromeVer(baseVer))
            }

        case "linux":
            switch goarch {
            case "amd64":
                return fmt.Sprintf("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36", ChromeVer(baseVer))
            }

        case "darwin":
            switch goarch {
            case "arm64":
                fallthrough // https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/core/frame/navigator_id.cc;l=76
            case "amd64":
                return fmt.Sprintf("Mozilla/5.0 (Macintosh; Intel Mac OS X %s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36", MacOSVer(macOsVer), ChromeVer(baseVer))
            }

        }

这里来分析一组suricata规则来识别默认http的木马通信的request和response,

alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"ETPRO MALWARE Sliver HTTP SessionInit Request"; 
    flow:established,to_server; 
    flowbits:set,ETPRO.SliverSessionInit; 
    http.method; content:"POST"; 
    http.uri; content:"/register.html?"; fast_pattern; 
    content:"&"; distance:0; 
    content:"="; within:3; 
    pcre:"/^\/(?:(?:a(?:uth(?:enticate)?|ctions|pi)|oauth(?:2(?:callback)?)?|d(?:atabase|b)|namespaces|upload|rest|php|v1)\/){0,13}register\.html\?.*[a-z_]{1,2}=[a-z_0-9]{8,11}&[a-z_]{1,2}=[a-z_0-9]{8,11}/"; 
    http.user_agent; content:") AppleWebKit/537.36 (KHTML, like Gecko) Chrome/"; 
    content:" Safari/537.36"; endswith; 
    http.header_names; content:!"|0d 0a|Accept|0d 0a|"; 
    reference:url,github.com/BishopFox/sliver/; 
    classtype:command-and-control; 
    sid:2852657; 
    rev:3; 
    target:src_ip;)
alert http $EXTERNAL_NET any -> $HOME_NET any (msg:"ETPRO MALWARE Sliver HTTP SessionInit Response"; 
    flow:established,to_client; 
    flowbits:isset,ETPRO.SliverSessionInit; 
    http.header; content:"Cache-Control|3a 20|no-store, no-cache, must-revalidate|0d 0a|Date|3a 20|"; startswith; 
    http.header_names; content:"|0d 0a|Cache-Control|0d 0a|Set-Cookie|0d 0a|Date|0d 0a|"; startswith; 
    fast_pattern; 
    http.cookie; bsize:52; 
    content:"PHPSESSID="; startswith; 
    content:"|3b 20|HttpOnly"; distance:32; within:10; 
    pcre:"/^PHPSESSID=[a-f0-9]{32}\x3b/"; 
    reference:url,github.com/BishopFox/sliver/; 
    classtype:command-and-control; 
    sid:2852718; rev:1; 
    target:dest_ip;)

request的识别是通过url ua accept头来识别,核心是url和ua,这里只匹配“/register.html?”是因为这只是规则集中一条规则,规则集中会匹配多条来自c2profiles字典中的url特定字段,扩大识别范围来实现流量检测
respond的检测是通过sliver自定义header的特征以及,在此之前是否有request被命中,同样这条规则也只是匹配来了Set-Cookie和PHPSESSID,在完整的规则集中会匹配更多,这里只是举一个例子

接下来继续分析整体流量包的流量特征,可以发现流量包中存在大量204响应码的流量,这在整体来看应该属于强特征来,正常的Clint- Server模式的请求中一般不会存在大量204返回

那么为什么会存在这么多204响应

func (s *SliverHTTPC2) pollHandler(resp http.ResponseWriter, req *http.Request) {
    httpLog.Debug("Poll request")
    httpSession := s.getHTTPSession(req)
    if httpSession == nil {
        s.defaultHandler(resp, req)
        return
    }
    httpSession.ImplantConn.UpdateLastMessage()

    // We already know we have a valid nonce because of the middleware filter
    nonce, _ := getNonceFromURL(req.URL)
    _, encoder, _ := encoders.EncoderFromNonce(nonce)
    select {
    case envelope := <-httpSession.ImplantConn.Send:
        resp.WriteHeader(http.StatusOK)
        envelopeData, _ := proto.Marshal(envelope)
        ciphertext, err := httpSession.CipherCtx.Encrypt(envelopeData)
        if err != nil {
            httpLog.Errorf("Failed to encrypt message %s", err)
            ciphertext = []byte{}
        }
        s.noCacheHeader(resp)
        respData, _ := encoder.Encode(ciphertext)
        resp.Write(respData)
    case <-req.Context().Done():
        httpLog.Debug("Poll client hang up")
        return
    case <-time.After(s.getServerPollTimeout()):
        httpLog.Debug("Poll time out")
        resp.WriteHeader(http.StatusNoContent)
        s.noCacheHeader(resp)
        resp.Write([]byte{})
    }
}

不难看出sliver 对于 poll request的请求统一是做204响应,这样的话,其实不论是直观和量化对于poll特征的识别就有很多操作空间了

mtls

#先生成一个mac的马子
generate --mtls x.x.x.x --os macos   --save ./macos_shell_mtls
#sliver监听8888端口
http -l 8888
#tcpdump监听网卡端口流量方便后续分析
sudo tcpdump -i eth0 port 8888 -w mac_mtls.pcap
#计算8888端口流量的ja3和jas3
python3 ja3box_pro.py -i eth0 -p 8888

ja3和jas3

ja3:19e29534fd49dd27d09234e639c4057e jas3:f4febc55ea12b31ae17cfb7e614afda8
数据包情况,除了tls握手过程能看到一个client hello和server hello,数据交互都被加密

看起来没有什么强特征可言,这里从默认端口和ja3出发了(有大佬有思路可以放在评论区)

alert tls $HOME_NET any -> any 8888 (msg:"疑似C2工具sliver https加密通信行为-window/macos";flow:established,to_server; ja3.hash;content:"19e29534fd49dd27d09234e639c4057e"; flowbits:set,Sliver_TLS001;flowbits:noalert; sid:1240709019;rev:1;)
alert tls $HOME_NET any -> any 8888 (msg:"疑似C2工具sliver https加密通信行为-centos7";flow:established,to_server; ja3.hash;content:"473cd7cb9faa642487833865d516e578"; flowbits:set,Sliver_TLS001;flowbits:noalert; sid:1240709020;rev:1;)
alert tls $EXTERNAL_NET 8888 -> any any (msg:"C2工具sliver https默认证书特征"; flow:established,to_client; ja3s.hash; content:"f4febc55ea12b31ae17cfb7e614afda8"; flowbits:isset,Sliver_TLS001; threshold: type both, track by_dst, seconds 7200, count 1;sid:1240709021;)

注意注意,这里并不能单单从ja3出发,实测在生产环节中,有正常的业务流量ja3和jas3指纹都和这里的是一样的,这个是因为用的tls库是同一个,所以指纹一样(可以考虑逐一排查将生产环节中正常业务都排除掉)
分析源码,这里用的是tls库 “crypto/tls”

所以综合来看,这种情况是很难单纯通过流量设备来检测c2控制的,必须结合一些威胁情报或者失陷情报,包括信息收集手段来探测(探测告警目标IP是否开启了31337端口,这是sliver server默认端口,等等其他探测)

dns

#sliver 创建一个监听器,官方文档建议使用FQDN 所以在域名后面还有一个点 https://sliver.sh/docs?name=DNS+C2
dns --domains testc2.x.x.
#生成implant 
generate  --dns testc2.x.x --seconds 15 --jitter 0

这里要设置一个ns 将testc2.x.x指向ns.x.x服务器(在Cloudflare就可以设置),就是Create an NS record with an arbitrary subdomain, for example testc2(i.e. testc2.example.com) which is managed by ns1.example.com.
官方文档如下:https://sliver.sh/docs?name=DNS+C2 可自行复现,这里用的自己的域名和公网地址所以就不贴图了
相关流量特征是有很多对于testc2.x.x子域的解析,而且会有很长子域名的解析请求

对原理的深入剖析揭示了这样一种机制:当Implant向服务器发送数据时,它会将编码后的数据填充到子域名中,并将其发送至目标DNS服务器。服务器则通过TXT查询将数据返回给Implant。然而,由于域名长度被限制在254个字符内(包括子域名和根域名),且每个子域名的最大长度为63个字符,数据传输的实现受到这些限制的影响。
在此背景下,主要采用两种编码方式:Base32和Base58。考虑到DNS协议对大小写的不敏感性,Base32能够直接使用,因为它对大小写不敏感;而Base58虽然对大小写敏感,但其编码速度更快。因此,Sliver默认情况下会首先尝试使用Base58,若无法使用,则退而采用Base32编码。
详见:https://sliver.sh/docs?name=DNS+C2
suricata规则检测示例

alert dns any any -> any any (msg:"DNS query with domain length > 200"; dns_query; pcre:"/^.{200,}/"; classtype:attempted-recon; sid:1240709022; rev:1;)

WireGuard

wg-portfwd

#按照官方教程来即可https://sliver.sh/docs?name=Port+Forwarding
 generate  --wg x.x.225.92 --save ./win_shell_wg.exe #生成implant
 wg  #开启端口监听,默认53
wg-config -s /opt/sliver/wireguard.conf#导出配置文件并修改Endpoint(自己vps的ip和端口)
wg-portfwd add -r 10.211.55.3:808  #配置端口转发

#下面是Linux终端
sudo apt install wireguard-tools #安装WireGuard客户端
cp opt/sliver/wireguard.conf /etc/wireguard/wireguard.conf #把配置文件添加到默认目录
wg-quick up wireguard #启动wg

可以看到在我的公网vps上是可以正常访问我内网机器起的python http服务

这里是用python起了一个服务当正常我们想要访问的服务,

加密流量无明显特征,通信过程如下

0 条评论
某人
表情
可输入 255

没有评论

目录