51 / 52 安全检测、响应与重构

第 51 课:安全重构:把高风险远控改造成合规远程运维工具

# 第 51 课:安全重构:把高风险远控改造成合规远程运维工具

免责声明:本专栏仅用于安全工程学习研究,禁止使用本专栏介绍的技术做其他用途,否则后果自负,与本号无关。

本课是整个专栏的收束课。前43 课已经把源码结构、主控端、网络协议、插件加载、屏幕音频采集、注入、驱动、凭据读取、横向风险、检测工程和应急响应都讲过了。本课不再继续拆某一个危险功能,而是反过来思考:如果企业真的需要远程运维工具,应该如何设计成合规、安全、可控、可追溯的系统。 第 51 课:安全重构:把高风险远控改造成合规远程运维工具(配图)

# 1. 重构目标

不能把高风险远控源码简单换个界面就当远程运维工具。合规远程运维工具的底线是:有授权、有身份、有权限边界、有用户提示、有操作记录、有插件可信链、有最小化采集、有应急停用能力

高风险设计 合规替代
短标识或弱口令连接 mTLS、MFA、设备证书
命令直接进入功能分支 RBAC、审批、会话级授权
插件二进制直接下发 插件签名、哈希白名单、版本清单
隐蔽采集屏幕、音频、摄像头 用户可见提示、授权确认、采集范围限制
高危能力默认可用 删除或按场景隔离,并增加强制审批
无统一操作记录 结构化日志、会话记录、结果回执

依赖说明: 重构时不能只替换网络库或界面。HPSocket 类通信层需要升级为强认证和加密通道,插件加载链需要签名和哈希白名单,屏幕/音频/摄像头依赖需要用户可见授权,SQLite/OpenSSL/DPAPI 类凭据读取能力应删除,MemoryModule/yapi/远程线程类能力应删除或隔离到受控安全实验环境。后续示例只给合规设计方向,不提供高危复用步骤。

# 2. 先切断哪些能。

有些能力不适合出现在企业远程运维产品中,即便加上认证也不建议保留

能力 处理建议 原因
浏览器密码和 Cookie 读取 删除 运维没有合理理由读取用户凭据
代码块注入、远程线 删除 进程边界突破风险过高
内核隐藏、强制删除、内核注 删除 不符合透明运维原则
键盘记录 删除 隐私风险不可接受
无提示摄像头和麦克风采集 删除或改成用户主动授 必须满足隐私合规
高并发流量生 删除 与运维目标无关,风险过高

# 3. 源码反面样例:通信标识不足以证明身。

下面这段代码10 字节 m_password 承载通信标识,其中包含时间和位数字段。它可以帮助程序识别连接状态,但不能证明操作者身份,也不能证明服务端可信。

// 源码位置:主插件\HPSocket\TcpSocket.cpp,约55-70 bool CTcpSocket::Connect(LPCTSTR lpszHost, UINT nPort)
{
    activetime = timeGetTime();
    ZeroMemory(m_password, 10);

    memcpy(m_password, &activetime, 8);
    m_password[8] = TOKEN_ACTIVED;

#ifdef _WIN64
    m_password[9] = 1;
#else
    m_password[9] = 0;
#endif

    m_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    // 防御分析:这里更像会话标识,不是强身份认证    // 合规远程运维必须使用双向身份认证和可吊销设备证书}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

改造方向:

设计 建议
管理端身 使用企业 IdP 登录,强MFA
被管理端身份 设备证书绑定资产 ID
通信通道 mTLS,证书链校验和证书吊销
会话密钥 每次会话协商短期密钥
连接策略 仅允许登记资产和登记平台互连

# 4. 源码反面样例:异或不是加。

// 源码位置:主插件\HPSocket\TcpSocket.cpp,约200-208 int CTcpSocket::Send(LPBYTE lpData, UINT nSize)
{
    LONG nBufLen = nSize + 14;
    m_WriteBuffer.Write((PBYTE)&nBufLen, 4);
    m_WriteBuffer.Write((PBYTE)m_password, 10);
    m_WriteBuffer.Write(lpData, nSize, 1, m_password);

    SendWithSplit(
        m_WriteBuffer.GetBuffer(),
        m_WriteBuffer.GetBufferLen(),
        MAX_SEND_BUFFER);

    // 防御分析:数据体经过异或处理,但缺少标准加密和完整性校验    // 合规设计应使TLS 1.3 AEAD,并校验每条消息}
1
2
3
4
5
6
7
8
9
10
11
12
13
// 源码位置:主插件\HPSocket\TcpSocket.cpp,约275-302 bool CTcpSocket::OnReceive(const BYTE* pData, int iLength)
{
    m_CompressionBuffer.Write((LPBYTE)pData, iLength);

    while ((int)(m_CompressionBuffer.GetBufferLen()) > 14)
    {
        if ((memcmp(m_password, m_CompressionBuffer.GetBuffer(4), 10) != 0))
        {
            m_CompressionBuffer.ClearBuffer();
            return FALSE;
        }

        int nSize = 0;
        CopyMemory(&nSize, m_CompressionBuffer.GetBuffer(0), sizeof(int));

        m_DeCompressionBuffer.Write(
            m_CompressionBuffer.GetBuffer(14),
            nSize - 14,
            1,
            m_password);

        m_pManager->OnReceive(
            m_DeCompressionBuffer.GetBuffer(0),
            m_DeCompressionBuffer.GetBufferLen());

        // 防御分析:包完整后直接进入管理器分发        // 合规产品应在业务分发前完成签名、权限和会话状态校验    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

协议重构建议

当前问题 合规改
包头只有长度和短标识 增加协议版本、会ID、序列号、时间戳
数据体只做异 使用标准 TLS 通道保护
缺少消息完整 每条消息AEAD tag HMAC
缺少防重 使用序列号、nonce、时间窗
命令无操作者上下文 每条命令绑定用户、角色、资产和会话

# 5. 零信任会话模。

5. 零信任会话模。(配图)

合规远程运维的会话不应该是“连上就能操作”。推荐流程如下:

  1. 操作者使用企业身份登录,强制 MFA?

  2. 控制平台检查操作者角色、资产权限、时间窗口和工单?

  3. 管理端与被管理端建立 mTLS 会话?

  4. 被管理端显示用户可见提示,必要时要求本地用户确认?

  5. 每条命令记录操作者、资产、命令、结果、数据量和时间?

  6. 会话结束后自动关闭权限,保留结构化记录 | 控制。| 最低要求 | | --- | --- | | 登录 | MFA、设备可信检| | 授权 | 按资产、功能、时间、工单限| | 会话 | mTLS、短期会话密| | 本地提示 | 远程控制、屏幕、音频、文件操作均要可| | 操作记录 | 不允许关闭,不允许本地篡|

# 6. 命令分发前必须做权限判断

当前主控端收到完整包后,会根据窗口类型直接转给不同对话框处理。合规改造时,不能让功能窗口自己假设命令可信,权限判断必须放在统一网关层。

// 源码位置:主控\Quick\MainFrm.cpp,约1785-1865 void CMainFrame::ProcessReceiveComplete(ClientContext* pContext)
{
    if (pContext->m_DeCompressionBuffer.GetBuffer(0)[0] == TOKEN_HEARTBEAT)
    {
        BYTE bToken[2] = { TOKEN_HEARTBEAT, 1 };
        g_pSocketBase->Send(pContext, (LPBYTE)&bToken, 2);
        return;
    }

    if (pContext->m_Dialog[0] > 0)
    {
        switch (pContext->m_Dialog[0])
        {
        case FILEMANAGER_DLG:
            ((CFileManagerDlg*)dlg)->OnReceiveComplete();
            break;
        case PROXY_DLG:
            ((CProxyMapDlg*)dlg)->OnReceiveComplete();
            break;
        case KERNEL_DLG:
            ((CKernelDlg*)dlg)->OnReceiveComplete();
            break;
        }
    }

    // 防御分析:这里应增加统一权限网关    // 命令进入任何功能前,都要校验用户、资产、功能和会话状态}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

建议增加统一的命令上下文

字段 说明
operator_id 操作者身
asset_id 被管理资
session_id 会话编号
approval_id 工单或审批编
capability 功能类别
risk_level 风险等级
expires_at 授权过期时间

更具体的实现可以先定义一个命令上下文对象。它不是安全银弹,但能强制每条命令都带上身份、资产和授权信息。

// 示例位置:合规重构后的统一命令网关
// 防御分析:CommandContext 把“谁、对谁、做什么、凭什么授权”放在同一处// 功能模块只能接收已验证的上下文,不能直接接收原始网络包struct CommandContext
{
    std::string operator_id;
    std::string asset_id;
    std::string session_id;
    std::string approval_id;
    std::string capability;
    std::string command_name;
    uint64_t    expires_at;
    uint32_t    risk_level;
};

bool AuthorizeCommand(const CommandContext& ctx)
{
    // 防御分析:这里应连接企业身份、资产、工单和策略系统    // 返回 false 时要记录拒绝原因,不能静默丢弃    if (ctx.operator_id.empty() || ctx.asset_id.empty())
        return false;

    if (ctx.approval_id.empty() && ctx.risk_level >= 3)
        return false;

    return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 7. 插件可信。

7. 插件可信。(配图)

源码中插件数据可以经网络进入,被缓存到注册表,并由后续线程使用。合规远程运维工具必须把插件当成供应链资产管理,而不是当作任意二进制数据。

// 源码位置:主插件\登录模块\登录模块\LoginManager.cpp,约314-366 case COMMAND_SENDLL:
{
    DllSendData* temp = new DllSendData;
    ZeroMemory(temp, sizeof(DllSendData));
    ::memcpy(temp, lpBuffer + 1, sizeof(DllSendData));

    BYTE* pPluginDllShellcodeData = new BYTE[temp->dllDataSize];
    ::memcpy(
        pPluginDllShellcodeData,
        lpBuffer + 1 + sizeof(DllSendData),
        temp->dllDataSize);

    int writeregeditsize = sizeof(DllSendData) + temp->dllDataSize;
    char* writeregeditbuffer = new char[writeregeditsize];

    ::RegSetValueEx(
        hKey,
        DllName,
        0,
        REG_BINARY,
        (unsigned char*)writeregeditbuffer,
        writeregeditsize);

    HANDLE hWorker = (HANDLE)_beginthreadex(
        NULL,
        0,
        Loop_DllManager,
        (void*)pDllData,
        0,
        NULL);

    // 防御分析:合规设计应在缓存和执行前完成签名    // 哈希、版本、发布清单和权限范围校验}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

插件安全设计

阶段 控制要求
构建 CI 生成产物哈希SBOM
签名 使用企业代码签名证书
发布 插件进入受控仓库,生成版本清单
下发 客户端只接受清单内插
验证 本地校验证书链、哈希、版本和适用平台
回滚 支持吊销版本和禁用插

插件清单建议具体到字段,不要只写“校验签名”。

// 示例位置:合规插件发布清单结// 防御分析:客户端只接受清单内插件,且哈希、签名、权限范围都要匹配// 这类结构用于防止任意二进制经网络进入插件链struct PluginManifest
{
    std::string plugin_name;
    std::string version;
    std::string platform;
    std::string sha256;
    std::string signer_thumbprint;
    std::string min_agent_version;
    std::vector<std::string> allowed_capabilities;
    bool revoked;
};
1
2
3
4
5
6
7
8
9
10
11

# 8. 隐私最小化设计

8. 隐私最小化设计(配图)

远程运维可能需要屏幕共享、文件传输、命令执行,但这些能力都必须遵循最小化原则:只采集完成任务所必需的数据,只在授权时间内采集,只保存必要记录。 摄像头模块的源码说明,远程命令可以改变设备和分辨率,并发送帧数据。合规产品中,这类能力必须有用户确认和明显提示。

// 源码位置:主插件\视频查看\视频查看\VideoManager.cpp,约37-63 void CVideoManager::OnReceive(LPBYTE lpBuffer, UINT nSize)
{
    if (lpBuffer[0] == TOKEN_HEARTBEAT) return;

    switch (lpBuffer[0])
    {
    case COMMAND_WEBCAM_RESIZE:
        m_SelectedDevice = *((LPDWORD)(lpBuffer + 9));
        ResetScreen(*((LPDWORD)(lpBuffer + 1)), *((LPDWORD)(lpBuffer + 5)));
        break;
    }

    // 防御分析:摄像头相关能力必须有用户授权、可见提示和关闭入口    // 企业远程运维不应默认启用摄像头或麦克风}
1
2
3
4
5
6
7
8
9
10
11
12
13
// 源码位置:主插件\视频查看\视频查看\VideoManager.cpp,约83-100 void CVideoManager::sendNextScreen()
{
    LPVOID lpDIB = m_pCapVideo->GetDIB();
    int* p_len = (int*)lpDIB;
    int len = *p_len;

    int nHeadLen = 1 + 1 + 4;
    UINT nBufferLen = nHeadLen + len;

    LPBYTE lpBuffer = new BYTE[nBufferLen];

    // 防御分析:图像帧属于敏感数据    // 合规设计应限制采集范围、显示会话水印,并记录数据量和持续时间}
1
2
3
4
5
6
7
8
9
10
11
12

隐私控制建议

场景 合规要求
屏幕共享 用户确认、明显提示、会话水
文件传输 路径限制、敏感目录保护、传输记
命令执行 命令审批、结果记录、危险命令阻断
音频/摄像 默认禁用,必须本地确
剪贴 默认不采集,确需使用时提示用

# 9. 具体改造路。

把重构落地到工程任务时,可以按模块拆分,而不是笼统写“增强安全”

模块 立即删除 保留但重 验收标准
通信 异或和短标识认证 mTLS、设备证书、会话日 未注册证书无法连
插件 任意二进制下 签名、哈希、版本清单 篡改插件无法缓存和加
文件管理 任意路径读写 路径授权、敏感目录保 未授权路径操作被拒绝并记
远程终端 隐藏命令解释 工单审批、命令分级、全量记 高危命令无审批不能执
屏幕共享 无提示采 用户确认、会话水印、停止按 用户可见且可终止
音视 默认远程采集 默认禁用,本地确 无本地确认不采集
凭据读取/注入/驱动隐藏 删除 不进入运维产 源码和构建产物均不存

# 10. 合规控制矩阵

10. 合规控制矩阵(配图)

控制。 要替代的问题 工程实现
身份认证 短标识、弱口令 mTLS、MFA、设备证书
权限控制 命令直接执行 RBAC、工单审批、时间窗
插件可信 二进制直接下发 签名、哈希、版本清单
隐私保护 隐蔽采集 可见提示、用户确认、采集最小化
操作记录 难以追溯 结构化日志、会话记录、结果回执
应急控制 无法快速停止 远程吊销、插件禁用、证书吊销

# 11. 企业检测点

即使完成重构,也要给安全团队留下可检测、可验证的数据

数据 作用
登录事件 谁登录了控制平台
授权事件 谁批准了哪个资产的哪个能
会话事件 会话开始、结束、来IP、设备证书
命令事件 命令类别、参数摘要、结果、数据量
插件事件 插件名、版本、哈希、签名验证结果
隐私事件 屏幕、文件、音频、摄像头授权状态
异常事件 拒绝原因、权限不足、签名失

# 12. 合法练习:

  1. 选取前面任意一个高危模块,说明它应该删除、保留还是改造,并写出理由?

  2. 设计一个远程文件管理功能的 RBAC 矩阵:只读、上传、下载、删除分别需要什么权限?

  3. 写一份插件发布清单字段:插件名、版本、平台、哈希、签名证书、发布时间、适用资产?

  4. 设计一个远程屏幕共享的用户提示内容:操作者、公司、工单号、开始时间、停止按钮?

  5. 写一条验证规则:未通过插件签名校验的二进制不得缓存、不得加载、不得执行。

安全工程知识交流加wx:easy_coder,黑灰产勿扰,企业单位合作请出示有效证件。

上次更新: 2026/07/14, 19:37:40

社区交流

讨论与留言

前往 GitHub Issues →

本留言区仅对应当前文章,欢迎补充观点、提出问题或帮助修正文中疏漏。 留言由 GitHub/Gitalk 提供,需要使用 GitHub 登录。

最近更新
第 1 课:专栏导论与安全边界
07-01
第 3 课:安装编译与调试环境
07-01
第 4 课:第三方库下载、编译与依赖管理
07-01
更多文章>