0%

云函数C2隐藏

云函数C2隐藏

前两天听说的新奇玩意,之前的C2隐藏的经典做法是域前置,在云厂商不验证域名所有权的情况下,可以注册域名,并且云厂商会为你签发对应的证书。显然,该方法是有悖于正常软件的运行逻辑的,随着该方法的普遍传播,云厂商都在用户绑定域名前对域名所有权进行了校验,使得域前置技术现在只能是作为一层套CDN的技术,并不能伪造出任意合法地名。虽说如此,但也能当一层cdn用,但使用的前提就是自己注册一个域名。

该方案能够隐藏C2的真实IP,但由于SNI的存在,即使在使用HTTPS的情况下流量监控设备仍然能够拿到客户端访问的目标域名(我感觉未备案下https的阻断也是这个道理),在国内顶级实名注册域名的情况下,一个域名反手社工全家完全不是问题。

因此,需要另一个合法的隐藏C2服务器的技术,也就是今天提到的云函数。该方案实际上也类似于一层CDN,但不需要自己签发域名,直接使用云服务商提供的域名,本身也属于较为可信的域名(大概)

不过以上几个办法都不能防止对C2(CDN)的IP封禁,因为CDN是由地理位置决定分发到哪个机器上的,如果当前地理位置对应的CDN被封了IP,CDN也不会知道你被封了而给你重新解析一个新IP出来,所以仅作隐藏使用。

并且云函数同样支持HTTPS,流量隐藏后sni拿到的域名也就是一个云上API域名,我发点https流量看起来感觉不是很合理?

云函数概念

就是能够托管给云服务商运行的代码片段,相较于vps是一个完整的机器,云函数就是一段独立的代码,使用者完全接触不到运行云函数的机器,仅编写一个功能,然后这个功能会部署上云按照预期运行。

所以只需要写出来一个简单的流量转发云函数,将访问云函数的请求转发到后端C2,并将回显返回回去即可。

云函数部署

以网上常见的腾讯云为例。腾讯云新用户能免费送一年有限额度,适合体验。

云函数创建方面,直接选择从头开始,运行环境一定要选python3.6!具体原因是python3.7并没有自带requests库,虽然部署之后可以进去用终端进行安装,但装完之后终端能import requests,云函数运行的时候requests仍然no module named requests。。。。根据其手册使用-t .将依赖装到当前目录下也没有用。所以解决方案是直接使用自带requests库的python3.6

函数类型需选择事件函数,Web函数以web服务器的形式运行,理论上来说也可以写一个web服务进行转发,不过网上的例子都是基于外部网关api的事件触发的,我也就懒得查文档一个个试错了。

日志部分不启动的话是看不到自己代码里的log,print之类的语句的,故如果要试错得申请,日志服务收费,新人免费三个月。

高级配置可以不动,触发器配置需自定义创建,触发方式选择API网关触发,然后这个API网关又是一个单独的服务,需要申请激活,后续需要进行配置。选择API网关触发后会多出来几个选项,其中的继承响应需要勾上,这样子才能按照我们的配置返回数据。

稍微修改了一下后的代码

import json
import requests


def main_handler(event, context):
    print("[+]event: " + json.dumps(event))
    try:
        backend = 'https://ip:port'
        headers = event['headers']
        path = event['path']
        if event['httpMethod'] == 'GET':
            res = requests.get(backend + path, headers=headers, verify=False)
        elif event['httpMethod'] == 'POST':
            res = requests.post(backend + path, data=event['body'], headers=headers, verify=False)
        else:
            return {
                "isBase64Encoded": False,
                "headers": {"Content-Type": "application/json"},
                "statusCode": 403,
                "body": json.dumps({"error": f"unknown method %s" % event['httpMethod']})
            }

        if res.status_code == 404:
            return {
                "isBase64Encoded": False,
                "headers": {"Content-Type": "application/json"},
                "statusCode": 404,
                "body": json.dumps({"error": "not found"})
            }

        if res.status_code != 200:
            return {
                "isBase64Encoded": False,
                "headers": {"Content-Type": "application/json"},
                "statusCode": 500,
                "body": json.dumps({"error": "error occurred"})
            }
        print("[+]headers: " + str(res.headers))
        print("[+]contents: " + str(res.content))
        return {
            "isBase64Encoded": False,
            "statusCode": res.status_code,
            "headers": dict(res.headers),
            "body": res.content.decode()    # need to be a string type, not byte
        }
    except Exception as e:
        print("[-]Error: " + str(e))
    return {
        "isBase64Encoded": False,
        "headers": {"Content-Type": "application/json"},
        "statusCode": 500,
        "body": json.dumps({"error": "error occurred"})
    }

其实不改也无所谓,改了之后稍微符合了一点api的规范?(大概)其实在api网关处可以直接配置返回的状态与响应内容的映射关系,不过懒得整了。

这里也就最后的返回值需要注意一下格式,由于启用了集成响应,我们可以控制返回的header,这样才能适配部分情况下c2profile的内容,所以返回值必须满足如下格式。

{
    "isBase64Encoded": false,
    "statusCode": 200,
    "headers": {"Content-Type":"text/html"},
    "body": "<html><body><h1>Heading</h1><p>Paragraph.</p></body></html>"
}

集成响应与透传响应
"isBase64Encoded"指明结果是否编码,statusCode为状态码,需是整数,headers为字典类型,其中键为字符串,值可以为字符串或数组,但数组内容仅能为字符串。最后的body为返回内容,也必须为字符串。意味着python的byte类型需要decode后转为string。如果返回值是json的话,也不能直接返回字典类型的对象,而是需要jsom.dumps转成string,并且手动设置content type为json

部署后云函数即可上线,不过需要在api网关处再修改一下访问路径为根路径,否则需要访问一个对应路径才能访问到该服务,不仅需要额外配置c2profile,还需要具体修改python代码,不甚优雅。

接下来直接对云函数域名上线即可成功上线,并且稍微写了点代码,还能让反查c2的时候返回正常的api响应,感觉很不错(虽然不写的话腾讯云也会自己有一套响应)。。。

限制

云函数,根据腾讯的文档所言,是有单个包长度限制的,单个包的长度不能超过6M,虽然CS有一个内置限制是单个包长度不能超过1M,但是能在c2profile里面改。名为tasks_max_size,说起来原来mimikatz ladon这些东西都不超过1M的吗,我还真不知道。。
Profile Language

所以正常情况下,应该也不会出现被限长影响的问题?

update

然后我朋友在实战的时候用云函数隐藏c2,对抗的是卡巴斯基企业版,最后也不知道是咋被发现的,可能是流量设备查出来了异常,结果顶级威胁情报直接把回连这个云函数域名的全部判毒了。

也不知道这个云函数域名是从二进制里面直接拿字符串拿到的,还是通过sni交换证书的时候顶级流量设备抓取的。

还是说这种企业版杀软安装的时候会让用户信任一份自签名的证书然后就可以任意进行中间人中介?

end

没啥好记录的,就是一个不错的思路,加上腾讯的环境有毒,浪费了不少时间。
腾讯云支持go环境,并且是直接提交一个二进制上去,可以自己本地编译就不会有这个装依赖的问题了。。。感觉真得用go了吧,时代在进步