Yakit实战:突破前端AES加密,实现明文数据包抓取与修改
2026/7/29 9:21:36 网站建设 项目流程

1. 项目概述:当加密遇上抓包

在Web应用安全测试和日常开发调试中,我们经常需要拦截和修改客户端与服务器之间的数据包。当数据以明文形式传输时,使用Burp Suite、Charles或Fiddler这类代理工具可以轻松实现。然而,随着安全意识的提升,越来越多的前端应用开始采用AES(高级加密标准)等对称加密算法对请求体进行加密,再通过HTTPS通道传输。这就形成了一个“双重保险”:传输层有TLS/SSL保护,应用层还有AES加密。对于测试人员或开发者而言,这堵加密墙使得我们无法直接看到或修改业务逻辑层面的数据,传统的“中间人”抓包工具瞬间失效,看到的只是一串串无法理解的密文。

这正是“Yakit实战:如何在前端AES加密场景下抓取并修改明文数据包”要解决的核心痛点。Yakit作为一款新兴的、面向安全从业者的集成化工具平台,其强大的MITM(中间人攻击)和插件化能力,为我们提供了一条破解此困境的路径。这个项目的目标不是破解AES算法本身(那是不切实际且不合规的),而是模拟一个“合法”的解密与再加密流程:在数据包经过我们的代理时,利用已知的密钥和加密模式,将其解密为明文供我们查看和修改,然后再用同样的密钥加密回去,发送给服务器。整个过程对客户端和服务器都是透明的,它们依然认为自己在进行安全的加密通信。

这听起来像是“魔法”,但本质上是一种基于代理的加解密转发。它特别适用于以下几种场景:第一,安全测试人员需要对加密接口进行漏洞挖掘,比如测试越权、注入等逻辑漏洞,必须能看到具体的参数;第二,前端开发者在联调时,后端返回的也是加密数据,需要解密查看响应内容以定位问题;第三,学习研究前端加密实现,动态分析其加密逻辑和密钥管理方式。如果你正被前端AES加密的数据包搞得束手无策,那么接下来的完整配置和实战过程,将为你提供一套可直接复现的解决方案。

2. 核心思路与工具选型解析

2.1 为什么传统抓包工具会失效?

要理解解决方案,先得明白问题根源。在一个典型的前端AES加密场景中,数据流是这样的:

  1. 前端加密:用户在页面表单输入数据(如username=admin&password=123456),前端JavaScript代码使用预置或动态获取的AES密钥(Key)、初始化向量(IV)和指定的模式(如CBC),调用类似CryptoJS的库,将明文数据加密成Base64格式的密文。
  2. 发送请求:前端将密文作为请求体(例如放在data字段),通过HTTPS POST请求发送给服务器。
  3. 服务器解密:服务器端用相同的Key和IV解密接收到的密文,还原出明文数据进行业务处理。

当你使用Burp Suite等工具设置代理后,你的设备成为了客户端和服务器的中间人。你确实能截获HTTPS流量(在安装并信任了Burp的CA证书后),但你看到的应用层数据(HTTP Body)已经是经过前端加密的密文。没有密钥,你无法解密;即使你强行修改了密文中的一个字符,由于AES CBC等模式的雪崩效应,服务器解密时几乎必然失败,导致请求被拒绝。

因此,核心思路必须转向:让代理工具具备“知晓”密钥并执行加解密的能力。我们需要一个“智能代理”,它能在流量经过时,自动完成“解密 -> 展示/修改 -> 再加密”的流水线作业。

2.2 为什么选择Yakit?

市面上具备一定脚本能力的代理工具不止Yakit,比如Burp Suite的Extender API、Mitmproxy的Python脚本。选择Yakit进行此次实战,主要基于以下几点考量:

  1. 原生集成与便捷性:Yakit内置了强大的MITM服务器和流量劫持功能,无需像Mitmproxy那样需要单独编写脚本并启动服务。其“热加载”能力使得编写JavaScript插件来处理流量变得非常直观和快速,修改代码后几乎实时生效,极大提升了调试效率。
  2. 对前端加密场景的友好支持:Yakit的插件系统设计,使得它能够非常方便地操作HTTP请求/响应的原始Body。我们可以直接编写JavaScript代码,调用Node.js环境的Crypto模块(Yakit插件引擎基于Node.js)来执行AES加解密,这与前端使用的CryptoJS库在算法上能很好对齐,减少了环境差异带来的麻烦。
  3. 一体化工作流:Yakit不仅是一个抓包工具,还集成了漏洞扫描、端口爆破、爬虫等多种安全测试功能。对于安全测试人员来说,在一个工具内完成从流量拦截、解密、修改到漏洞探测的整个流程,体验更加连贯。
  4. 活跃的社区与文档:虽然相对较新,但Yakit拥有活跃的中文社区和不断完善的文档,遇到问题时更容易找到解决方案或获得帮助。

当然,这个方案的前提是你必须能够获取到前端使用的AES加密密钥(Key)和初始化向量(IV)。这通常通过逆向分析前端JavaScript代码、调试获取或是在开发/测试环境中由开发团队提供。没有这个前提,任何针对应用层加密的抓包修改都是空中楼阁。

3. 环境准备与关键信息获取

3.1 Yakit的安装与基础配置

首先,你需要从Yakit官网下载对应操作系统(Windows/macOS/Linux)的安装包并完成安装。安装过程比较简单,这里不赘述。安装完成后,启动Yakit,你会看到主界面。

进行MITM抓包前,有几个关键配置步骤:

  1. 启动MITM服务器:在Yakit左侧功能栏找到“MITM”模块,点击进入。通常你需要配置监听端口(默认8080)和上游代理(如果需要)。点击“启动”按钮,Yakit会在本地启动一个HTTP/HTTPS代理服务器。
  2. 安装CA证书:这是拦截HTTPS流量的必要条件。在“MITM”页面有“下载CA证书”的选项。将证书下载到本地,然后手动导入到你的系统或浏览器的受信任根证书颁发机构中。以Chrome浏览器为例,你也可以在启动代理后,访问http://yakit.local来下载和安装证书。务必确保证书安装成功且被信任,否则你只能看到HTTPS握手失败或密文。
  3. 配置设备代理:将你需要抓包的设备(可以是PC,也可以是手机模拟器或真机)的网络代理设置为Yakit所在机器的IP地址和监听端口(如192.168.1.100:8080)。

完成以上步骤后,在Yakit的“MITM”页面,你应该能看到“劫持”到的HTTP/HTTPS请求列表。如果看不到HTTPS请求,请检查证书安装步骤。

3.2 定位前端AES加密参数

这是整个实战中最关键、也最具技术挑战性的一步。你需要分析目标Web应用的前端代码,找到加密函数和密钥。以下是几种常见的方法:

  1. 静态代码分析

    • 在浏览器中打开目标网站,按F12打开开发者工具。
    • 切换到“源代码”(Sources)标签页,使用全局搜索(Ctrl+Shift+F)功能,搜索关键词如CryptoJSAESencryptmodepaddingivkeysecret等。
    • 仔细阅读搜索到的JavaScript文件,找到加密函数的定义和调用处。密钥(Key)和IV可能以硬编码字符串、从某个接口获取、或通过某种算法生成的形式存在。
  2. 动态调试追踪

    • 在开发者工具的“网络”(Network)标签页,找到一个发送加密数据的请求(请求体看起来像Base64字符串)。
    • 在“源代码”标签页,使用“XHR/ Fetch断点”功能,在该请求的URL上设置断点。
    • 重新触发请求,代码会在发送前暂停。此时调用堆栈(Call Stack)会显示出发送请求前的函数调用链。
    • 沿着调用栈向下查找,通常能找到执行加密操作的函数(例如CryptoJS.AES.encrypt)。在此函数内部,你可以查看传入的参数,从而直接看到明文、密钥、IV等值。
  3. Hook关键函数

    • 这是一种更高级的方法。在开发者工具的“控制台”(Console)中,可以注入JavaScript代码来“钩住”(Hook)加密函数。例如:
      var originalEncrypt = CryptoJS.AES.encrypt; CryptoJS.AES.encrypt = function (message, key, cfg) { console.log("[HOOK] Plaintext:", message); console.log("[HOOK] Key:", key); console.log("[HOOK] Config:", cfg); // 继续执行原函数 return originalEncrypt(message, key, cfg); };
    • 执行上述代码后,再触发请求,加密函数的参数就会被打印到控制台。这种方法非常有效,但需要网站没有做很强的反调试措施。

实操心得:在实际操作中,密钥和IV可能是固定的,也可能是每次会话动态生成的。对于动态生成的情况,你需要进一步分析生成逻辑。一个常见的模式是:前端先通过一个公开的接口获取一个“加密种子”或“会话密钥”,然后用这个种子结合固定盐值(Salt)或时间戳,通过某种哈希算法生成最终的AES密钥。你需要通过调试,理清这个完整的链条。

假设通过分析,我们得到以下信息:

  • 加密算法:AES-CBC
  • 密钥(Key)0123456789abcdef0123456789abcdef(32字节十六进制字符串,对应AES-256)
  • 初始化向量(IV)abcdefghijklmnop(16字节字符串)
  • 填充方式:PKCS7
  • 输出格式:Base64

请务必记录下这些信息,它们将是编写Yakit插件的核心输入。

4. Yakit插件编写:解密与再加密流水线

Yakit通过“流量劫持”功能配合“插件”来实现对数据包的动态修改。我们将编写一个JavaScript插件,在请求发送到服务器前,解密其Body;在服务器响应返回后,解密其Body(如果需要查看明文响应的话)。这里我们主要关注请求的修改。

4.1 创建与配置插件

  1. 在Yakit主界面,进入“插件”模块。
  2. 点击“新建插件”,选择“MITM插件”。
  3. 给插件起一个名字,例如AES-CBC Decryptor
  4. 在代码编辑区,我们将编写完整的处理逻辑。

4.2 完整插件代码解析

以下是一个功能完整的Yakit MITM插件代码,用于处理AES-CBC加密的请求和响应。请将之前获取到的密钥、IV等参数替换到代码中相应位置。

// 引入必要的模块 const crypto = require('crypto'); // 配置你的AES参数 (请根据实际情况修改!!!) const AES_KEY = Buffer.from('0123456789abcdef0123456789abcdef', 'hex'); // 32字节 for AES-256 const AES_IV = Buffer.from('abcdefghijklmnop', 'utf8'); // 16字节 const ALGORITHM = 'aes-256-cbc'; const INPUT_ENCODING = 'base64'; const OUTPUT_ENCODING = 'utf8'; // 辅助函数:AES解密 function aesDecrypt(encryptedBase64) { try { const encryptedBuffer = Buffer.from(encryptedBase64, INPUT_ENCODING); const decipher = crypto.createDecipheriv(ALGORITHM, AES_KEY, AES_IV); // 使用 autoPadding,兼容PKCS7 let decrypted = decipher.update(encryptedBuffer); decrypted = Buffer.concat([decrypted, decipher.final()]); return decrypted.toString(OUTPUT_ENCODING); } catch (e) { return `[Decrypt Error] ${e.message}: ${encryptedBase64}`; } } // 辅助函数:AES加密 function aesEncrypt(plainText) { try { const cipher = crypto.createCipheriv(ALGORITHM, AES_KEY, AES_IV); let encrypted = cipher.update(plainText, OUTPUT_ENCODING, INPUT_ENCODING); encrypted += cipher.final(INPUT_ENCODING); return encrypted; } catch (e) { return `[Encrypt Error] ${e.message}: ${plainText}`; } } // 判断请求体是否可能是我们的目标加密数据(简单启发式判断) function isTargetEncryptedData(body) { if (!body || typeof body !== 'string') return false; // 规则1: 非空且长度较长 // 规则2: 是标准的Base64字符串(可选,可通过正则简单判断) const base64Regex = /^[A-Za-z0-9+/]+={0,2}$/; return body.length > 16 && base64Regex.test(body.replace(/\s/g, '')); } // MITM插件主处理函数 function mirrorHttpFlow(flow) { // 1. 处理请求(在请求发往服务器前) if (flow.IsRequest) { let req = flow.Request; // 检查请求方法是否为POST/PUT等可能有Body的,并检查Content-Type if (req.Method === 'POST' || req.Method === 'PUT') { let body = req.Body; if (isTargetEncryptedData(body)) { try { // 解密请求体 let decryptedBody = aesDecrypt(body); console.log(`[Req Decrypted] ${req.Url}: ${decryptedBody.substring(0, 200)}...`); // 这里是关键:将解密后的明文存入一个自定义字段,供后续修改 // 原始密文仍然保留在Body中,直到我们决定替换它 flow.Set(`decrypted_request_body`, decryptedBody); // 可以在这里将解密后的内容显示到Yakit的UI上,方便查看 // flow.ToJson 然后修改? // 更常见的做法是,我们先不解密替换,而是通过“热加载”在另一个阶段修改。 // 但Yakit的MITM插件通常是在这个函数里直接修改flow.Request.Body。 // 所以,如果我们想修改,应该在这里直接替换Body。 // 假设我们想将明文中的某个字段值从“test”改为“admin” if (decryptedBody.includes('"username":"test"')) { let modifiedBody = decryptedBody.replace('"username":"test"', '"username":"admin"'); // 将修改后的明文重新加密 let reEncryptedBody = aesEncrypt(modifiedBody); // 替换原始请求体 req.Body = reEncryptedBody; console.log(`[Req Modified & Re-encrypted] Username changed to admin.`); } // 如果只是想查看,不修改,可以什么都不做,或者将解密内容记录到日志。 } catch (e) { console.error(`Failed to process request for ${req.Url}:`, e); } } } } // 2. 处理响应(在响应返回客户端前) else { let rsp = flow.Response; // 检查响应状态码和Content-Type,判断是否可能是加密响应 if (rsp.StatusCode === 200 && rsp.Body) { let body = rsp.Body; // 这里需要更精确的判断,比如根据URL路径或响应头特征 // 假设我们只处理特定API的响应 if (flow.Request.Url.includes('/api/secure-data') && isTargetEncryptedData(body)) { try { let decryptedBody = aesDecrypt(body); console.log(`[Rsp Decrypted] ${flow.Request.Url}: ${decryptedBody.substring(0, 500)}...`); // 同样,可以修改响应后再加密回去,但需谨慎,可能破坏客户端逻辑 // 这里我们仅解密并存储,用于查看 flow.Set(`decrypted_response_body`, decryptedBody); } catch (e) { console.error(`Failed to process response for ${flow.Request.Url}:`, e); } } } } // 必须返回true,表示此流量已被处理(或至少检查过) return true; } // 导出主函数 module.exports = { mirrorHttpFlow };

4.3 代码关键点与配置说明

  1. 模块引入const crypto = require('crypto');这是Node.js内置的加密模块,功能强大且标准,确保与前端CryptoJS的算法实现一致。
  2. 参数配置区:这是你需要重点修改的地方。
    • AES_KEY: 密钥。注意格式,如果前端用的是十六进制字符串,就用Buffer.from(keyString, 'hex');如果是Base64,就用'base64';如果是普通字符串,就用'utf8'。长度必须是16(AES-128)、24(AES-192)或32(AES-256)字节。
    • AES_IV: 初始化向量。必须为16字节。确保获取的IV与前端完全一致,包括编码。
    • ALGORITHM: 指定算法和模式。'aes-256-cbc'表示AES-256算法,CBC模式。如果前端是ECB模式(不推荐使用),则应为'aes-256-ecb',且ECB模式不需要IV。
    • INPUT_ENCODING/OUTPUT_ENCODING: 指定密文和明文的编码。前端通常输出Base64密文,所以解密时输入编码为'base64'。明文一般是UTF-8字符串。
  3. 加解密函数aesDecryptaesEncrypt函数封装了Node.jscrypto模块的调用,并添加了错误处理。注意createDecipherivcreateCipheriv的使用,这是使用指定Key和IV的正确方法。
  4. 目标判断函数isTargetEncryptedData是一个简单的启发式函数,用于避免对所有请求都尝试解密,提高效率并减少错误。你可以根据实际情况强化判断逻辑,例如检查特定的URL路径、请求头(如Content-Type: application/json)等。
  5. 主处理函数mirrorHttpFlow是Yakit规定的入口函数。参数flow包含了单个HTTP请求或响应的所有信息。
    • flow.IsRequest: 布尔值,判断当前是请求还是响应。
    • flow.Request/flow.Response: 获取请求或响应对象,包含Method、Url、Headers、Body等属性。
    • flow.Set()flow.Get(): 用于在请求和响应处理之间传递自定义数据。例如,本例中在请求阶段将解密后的明文存储起来(虽然本例后续未使用,但展示了用法)。
    • 修改数据包:直接在函数中修改req.Bodyrsp.Body即可。修改请求体后,Yakit会自动将修改后的请求转发给服务器。修改响应体后,会自动返回给客户端。
  6. 修改逻辑:示例中展示了一个简单的修改场景:如果解密后的JSON字符串中包含"username":"test",就将其替换为"username":"admin",然后重新加密并赋值给req.Body。你可以根据测试需求,编写更复杂的修改逻辑,如递增ID、遍历参数、注入测试Payload等。

注意:在响应处理部分,对响应体进行解密和修改需要格外小心。因为客户端JavaScript期望收到的是加密数据,如果你修改了响应内容却没有正确加密回去,会导致客户端解密失败,页面功能异常。通常,查看响应解密内容用于调试即可,除非有特殊测试目的,否则不建议主动修改响应。

5. 插件部署与实战抓包流程

5.1 加载并激活插件

  1. 将上述代码粘贴到Yakit插件的编辑器中,修改好密钥等配置。
  2. 点击编辑器上方的“保存”按钮。
  3. 进入“MITM”模块,在“劫持”页面,找到“加载插件”或“热加载”区域(不同Yakit版本位置可能略有不同)。你应该能看到你刚创建的插件名称(如AES-CBC Decryptor)。
  4. 勾选该插件,使其处于激活状态。Yakit会自动将插件代码加载到MITM服务器中。

5.2 启动抓包与触发请求

  1. 确保Yakit的MITM服务器正在运行(端口监听中)。
  2. 确保你的浏览器或测试设备已正确配置代理指向Yakit。
  3. 访问目标Web应用,进行登录、提交表单等会触发加密请求的操作。
  4. 回到Yakit的“MITM” -> “劫持”页面,你应该能看到捕获到的HTTP/HTTPS请求流。

5.3 观察与验证

  1. 查看原始流量:在流量列表里,找到你触发的那个POST请求。点击查看详情,在“请求”标签页的“原始”视图下,你会看到Body是一串Base64密文。这是未经插件处理的样子。
  2. 验证插件工作:如果插件配置正确且密钥无误,你应该能在Yakit的“插件日志”或控制台输出中(通常有一个“日志”标签页),看到插件打印的[Req Decrypted] ...信息,后面跟着解密后的JSON或表单数据明文。
  3. 查看修改效果:如果你在插件中编写了修改逻辑(如替换username),那么在发送到服务器的请求中,Body的密文已经发生了变化。你可以通过对比修改前后向服务器发送的请求包来验证。更直接的方法是,查看服务器的响应。如果修改成功,服务器会以修改后的参数(如username=admin)进行处理,并返回相应的结果。

一个完整的实战循环

  • 目标:测试一个加密登录接口的用户名枚举漏洞。
  • 步骤
    1. 正常登录一次,通过插件日志获取解密后的请求体格式,例如:{"username":"test_user", "password":"encrypted_password_hash", "timestamp":1234567890}
    2. 修改插件代码,将解密后的username字段值,从一个字典文件中逐行读取并替换。
    3. 在插件中,每替换一个用户名,就重新加密并发送请求。
    4. 观察服务器的响应(如状态码、响应时间、返回信息),判断用户名是否存在。

这就需要你在插件中实现一个循环或读取外部文件的功能,这超出了单个请求修改的范畴,可能需要结合Yakit的“Web Fuzzer”功能,或者编写更复杂的、能维持会话状态的插件。但核心原理依然是本插件所展示的解密->修改->再加密。

6. 常见问题排查与进阶技巧

6.1 插件不生效?按此清单排查

问题现象可能原因解决方案
看不到任何插件日志输出1. 插件未激活。
2. 插件代码有语法错误,加载失败。
3. 流量未匹配插件判断条件。
1. 在MITM设置中确认插件已勾选。
2. 查看Yakit日志或插件管理界面是否有错误提示。
3. 在插件开头加console.log("Plugin loaded!")测试。简化isTargetEncryptedData函数,直接返回true进行测试。
解密失败,报错Invalid key lengthInvalid IV length密钥或IV的格式、长度或编码转换错误。确认前端的Key/IV究竟是十六进制、Base64还是普通字符串。用Buffer.from(key, 'hex').length检查转换后的字节长度是否正确(16/24/32)。IV必须是16字节。
解密失败,报错error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt1. 密钥或IV错误。
2. 加密模式不匹配(如前端是GCM模式,插件用CBC)。
3. 密文在传输中被修改或损坏。
4. 填充方式不匹配。
1. 反复核对Key/IV,确保与前端完全一致。
2. 确认前端使用的AES模式(CBC, GCM, ECB等),修改ALGORITHM变量。
3. 确保抓取的是完整的密文,没有被截断。
4. Node.js的crypto默认使用PKCS7填充,与CryptoJS的PKCS5(在AES中等同于PKCS7)通常兼容。如果前端是NoPadding,则需要特殊处理。
解密后是乱码输出编码错误。解密结果是Buffer,需要用正确的编码转为字符串。确认明文原本的编码。如果是JSON,就用'utf8'。尝试decrypted.toString('utf8')
修改后请求发送失败或服务器返回错误1. 重新加密后的密文格式不对。
2. 修改明文时破坏了结构(如JSON格式错误)。
3. 请求签名或Token被破坏(如果请求还有签名机制)。
1. 将重新加密后的密文与原始密文对比,长度、字符集应相似。
2. 将修改后的明文用JSON.parse()测试一下是否合法。
3. 这是更复杂的情况,前端可能对完整请求体做了签名。你需要先解密,修改数据,然后按照前端的签名算法重新计算签名,并更新请求头中的签名字段。这需要深入分析前端代码。

6.2 进阶技巧与注意事项

  1. 动态密钥的处理:如果密钥是每次会话动态生成的,你的插件需要能够获取它。一种方法是在插件中Hook前端生成密钥的JavaScript代码(这非常复杂)。更实际的方法是,如果动态密钥是通过某个API接口下发的,你可以先捕获那个接口的响应,从中提取密钥,然后缓存起来供后续加解密使用。这需要插件有状态存储的能力。
  2. 处理多种加密模式或端点:一个应用可能对不同接口使用不同的加密参数。你可以在插件中维护一个“URL模式 -> 加密配置”的映射表,根据flow.Request.Url来动态选择解密策略。
  3. 性能考量:加解密是CPU密集型操作。如果处理大量并发请求,可能会影响Yakit性能。在插件中做好条件判断,只对必要的请求进行加解密操作。
  4. 与Yakit Fuzzer结合:对于参数爆破等测试,更高效的方法是使用Yakit的“Web Fuzzer”。你可以先手动抓一个加密包,然后利用Fuzzer的“数据包变形”功能,配合一个“外部处理器”插件。这个处理器插件接收Fuzzer生成的明文Payload,将其加密后返回给Fuzzer进行发送。这样就能实现自动化测试。
  5. 安全与合规警告:此技术仅限用于您拥有合法测试权限的系统,如公司内部测试、授权渗透测试、或个人学习研究。切勿用于任何未授权的系统,否则将触犯法律。

实操心得:在实际测试中,最耗时的部分往往是前端加密逻辑的分析。一旦成功提取出稳定的Key和IV,编写Yakit插件本身是很快的。建议在分析阶段,多利用浏览器的调试工具,特别是“Sources”面板中的代码搜索和“Network”面板的XHR断点功能。对于复杂的混淆代码,可以尝试使用JS反混淆工具(如 de4js)进行初步处理,但核心还是需要耐心地跟踪代码执行流程。成功解密出第一个数据包的那一刻,所有的努力都是值得的,因为它为你打开了一扇通往应用核心逻辑的大门。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询