Yakit实战:突破前端AES加密,实现明文数据包抓取与修改

发布时间:2026/7/29 9:21:38
Yakit实战:突破前端AES加密,实现明文数据包抓取与修改 1. 项目概述当加密遇上抓包在Web应用安全测试和日常开发调试中我们经常需要拦截和修改客户端与服务器之间的数据包。当数据以明文形式传输时使用Burp Suite、Charles或Fiddler这类代理工具可以轻松实现。然而随着安全意识的提升越来越多的前端应用开始采用AES高级加密标准等对称加密算法对请求体进行加密再通过HTTPS通道传输。这就形成了一个“双重保险”传输层有TLS/SSL保护应用层还有AES加密。对于测试人员或开发者而言这堵加密墙使得我们无法直接看到或修改业务逻辑层面的数据传统的“中间人”抓包工具瞬间失效看到的只是一串串无法理解的密文。这正是“Yakit实战如何在前端AES加密场景下抓取并修改明文数据包”要解决的核心痛点。Yakit作为一款新兴的、面向安全从业者的集成化工具平台其强大的MITM中间人攻击和插件化能力为我们提供了一条破解此困境的路径。这个项目的目标不是破解AES算法本身那是不切实际且不合规的而是模拟一个“合法”的解密与再加密流程在数据包经过我们的代理时利用已知的密钥和加密模式将其解密为明文供我们查看和修改然后再用同样的密钥加密回去发送给服务器。整个过程对客户端和服务器都是透明的它们依然认为自己在进行安全的加密通信。这听起来像是“魔法”但本质上是一种基于代理的加解密转发。它特别适用于以下几种场景第一安全测试人员需要对加密接口进行漏洞挖掘比如测试越权、注入等逻辑漏洞必须能看到具体的参数第二前端开发者在联调时后端返回的也是加密数据需要解密查看响应内容以定位问题第三学习研究前端加密实现动态分析其加密逻辑和密钥管理方式。如果你正被前端AES加密的数据包搞得束手无策那么接下来的完整配置和实战过程将为你提供一套可直接复现的解决方案。2. 核心思路与工具选型解析2.1 为什么传统抓包工具会失效要理解解决方案先得明白问题根源。在一个典型的前端AES加密场景中数据流是这样的前端加密用户在页面表单输入数据如usernameadminpassword123456前端JavaScript代码使用预置或动态获取的AES密钥Key、初始化向量IV和指定的模式如CBC调用类似CryptoJS的库将明文数据加密成Base64格式的密文。发送请求前端将密文作为请求体例如放在data字段通过HTTPS POST请求发送给服务器。服务器解密服务器端用相同的Key和IV解密接收到的密文还原出明文数据进行业务处理。当你使用Burp Suite等工具设置代理后你的设备成为了客户端和服务器的中间人。你确实能截获HTTPS流量在安装并信任了Burp的CA证书后但你看到的应用层数据HTTP Body已经是经过前端加密的密文。没有密钥你无法解密即使你强行修改了密文中的一个字符由于AES CBC等模式的雪崩效应服务器解密时几乎必然失败导致请求被拒绝。因此核心思路必须转向让代理工具具备“知晓”密钥并执行加解密的能力。我们需要一个“智能代理”它能在流量经过时自动完成“解密 - 展示/修改 - 再加密”的流水线作业。2.2 为什么选择Yakit市面上具备一定脚本能力的代理工具不止Yakit比如Burp Suite的Extender API、Mitmproxy的Python脚本。选择Yakit进行此次实战主要基于以下几点考量原生集成与便捷性Yakit内置了强大的MITM服务器和流量劫持功能无需像Mitmproxy那样需要单独编写脚本并启动服务。其“热加载”能力使得编写JavaScript插件来处理流量变得非常直观和快速修改代码后几乎实时生效极大提升了调试效率。对前端加密场景的友好支持Yakit的插件系统设计使得它能够非常方便地操作HTTP请求/响应的原始Body。我们可以直接编写JavaScript代码调用Node.js环境的Crypto模块Yakit插件引擎基于Node.js来执行AES加解密这与前端使用的CryptoJS库在算法上能很好对齐减少了环境差异带来的麻烦。一体化工作流Yakit不仅是一个抓包工具还集成了漏洞扫描、端口爆破、爬虫等多种安全测试功能。对于安全测试人员来说在一个工具内完成从流量拦截、解密、修改到漏洞探测的整个流程体验更加连贯。活跃的社区与文档虽然相对较新但Yakit拥有活跃的中文社区和不断完善的文档遇到问题时更容易找到解决方案或获得帮助。当然这个方案的前提是你必须能够获取到前端使用的AES加密密钥Key和初始化向量IV。这通常通过逆向分析前端JavaScript代码、调试获取或是在开发/测试环境中由开发团队提供。没有这个前提任何针对应用层加密的抓包修改都是空中楼阁。3. 环境准备与关键信息获取3.1 Yakit的安装与基础配置首先你需要从Yakit官网下载对应操作系统Windows/macOS/Linux的安装包并完成安装。安装过程比较简单这里不赘述。安装完成后启动Yakit你会看到主界面。进行MITM抓包前有几个关键配置步骤启动MITM服务器在Yakit左侧功能栏找到“MITM”模块点击进入。通常你需要配置监听端口默认8080和上游代理如果需要。点击“启动”按钮Yakit会在本地启动一个HTTP/HTTPS代理服务器。安装CA证书这是拦截HTTPS流量的必要条件。在“MITM”页面有“下载CA证书”的选项。将证书下载到本地然后手动导入到你的系统或浏览器的受信任根证书颁发机构中。以Chrome浏览器为例你也可以在启动代理后访问http://yakit.local来下载和安装证书。务必确保证书安装成功且被信任否则你只能看到HTTPS握手失败或密文。配置设备代理将你需要抓包的设备可以是PC也可以是手机模拟器或真机的网络代理设置为Yakit所在机器的IP地址和监听端口如192.168.1.100:8080。完成以上步骤后在Yakit的“MITM”页面你应该能看到“劫持”到的HTTP/HTTPS请求列表。如果看不到HTTPS请求请检查证书安装步骤。3.2 定位前端AES加密参数这是整个实战中最关键、也最具技术挑战性的一步。你需要分析目标Web应用的前端代码找到加密函数和密钥。以下是几种常见的方法静态代码分析在浏览器中打开目标网站按F12打开开发者工具。切换到“源代码”Sources标签页使用全局搜索CtrlShiftF功能搜索关键词如CryptoJS、AES、encrypt、mode、padding、iv、key、secret等。仔细阅读搜索到的JavaScript文件找到加密函数的定义和调用处。密钥Key和IV可能以硬编码字符串、从某个接口获取、或通过某种算法生成的形式存在。动态调试追踪在开发者工具的“网络”Network标签页找到一个发送加密数据的请求请求体看起来像Base64字符串。在“源代码”标签页使用“XHR/ Fetch断点”功能在该请求的URL上设置断点。重新触发请求代码会在发送前暂停。此时调用堆栈Call Stack会显示出发送请求前的函数调用链。沿着调用栈向下查找通常能找到执行加密操作的函数例如CryptoJS.AES.encrypt。在此函数内部你可以查看传入的参数从而直接看到明文、密钥、IV等值。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密钥Key0123456789abcdef0123456789abcdef(32字节十六进制字符串对应AES-256)初始化向量IVabcdefghijklmnop(16字节字符串)填充方式PKCS7输出格式Base64请务必记录下这些信息它们将是编写Yakit插件的核心输入。4. Yakit插件编写解密与再加密流水线Yakit通过“流量劫持”功能配合“插件”来实现对数据包的动态修改。我们将编写一个JavaScript插件在请求发送到服务器前解密其Body在服务器响应返回后解密其Body如果需要查看明文响应的话。这里我们主要关注请求的修改。4.1 创建与配置插件在Yakit主界面进入“插件”模块。点击“新建插件”选择“MITM插件”。给插件起一个名字例如AES-CBC Decryptor。在代码编辑区我们将编写完整的处理逻辑。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 代码关键点与配置说明模块引入const crypto require(crypto);这是Node.js内置的加密模块功能强大且标准确保与前端CryptoJS的算法实现一致。参数配置区这是你需要重点修改的地方。AES_KEY: 密钥。注意格式如果前端用的是十六进制字符串就用Buffer.from(keyString, hex)如果是Base64就用base64如果是普通字符串就用utf8。长度必须是16AES-128、24AES-192或32AES-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字符串。加解密函数aesDecrypt和aesEncrypt函数封装了Node.jscrypto模块的调用并添加了错误处理。注意createDecipheriv和createCipheriv的使用这是使用指定Key和IV的正确方法。目标判断函数isTargetEncryptedData是一个简单的启发式函数用于避免对所有请求都尝试解密提高效率并减少错误。你可以根据实际情况强化判断逻辑例如检查特定的URL路径、请求头如Content-Type: application/json等。主处理函数mirrorHttpFlow是Yakit规定的入口函数。参数flow包含了单个HTTP请求或响应的所有信息。flow.IsRequest: 布尔值判断当前是请求还是响应。flow.Request/flow.Response: 获取请求或响应对象包含Method、Url、Headers、Body等属性。flow.Set()和flow.Get(): 用于在请求和响应处理之间传递自定义数据。例如本例中在请求阶段将解密后的明文存储起来虽然本例后续未使用但展示了用法。修改数据包直接在函数中修改req.Body或rsp.Body即可。修改请求体后Yakit会自动将修改后的请求转发给服务器。修改响应体后会自动返回给客户端。修改逻辑示例中展示了一个简单的修改场景如果解密后的JSON字符串中包含username:test就将其替换为username:admin然后重新加密并赋值给req.Body。你可以根据测试需求编写更复杂的修改逻辑如递增ID、遍历参数、注入测试Payload等。注意在响应处理部分对响应体进行解密和修改需要格外小心。因为客户端JavaScript期望收到的是加密数据如果你修改了响应内容却没有正确加密回去会导致客户端解密失败页面功能异常。通常查看响应解密内容用于调试即可除非有特殊测试目的否则不建议主动修改响应。5. 插件部署与实战抓包流程5.1 加载并激活插件将上述代码粘贴到Yakit插件的编辑器中修改好密钥等配置。点击编辑器上方的“保存”按钮。进入“MITM”模块在“劫持”页面找到“加载插件”或“热加载”区域不同Yakit版本位置可能略有不同。你应该能看到你刚创建的插件名称如AES-CBC Decryptor。勾选该插件使其处于激活状态。Yakit会自动将插件代码加载到MITM服务器中。5.2 启动抓包与触发请求确保Yakit的MITM服务器正在运行端口监听中。确保你的浏览器或测试设备已正确配置代理指向Yakit。访问目标Web应用进行登录、提交表单等会触发加密请求的操作。回到Yakit的“MITM” - “劫持”页面你应该能看到捕获到的HTTP/HTTPS请求流。5.3 观察与验证查看原始流量在流量列表里找到你触发的那个POST请求。点击查看详情在“请求”标签页的“原始”视图下你会看到Body是一串Base64密文。这是未经插件处理的样子。验证插件工作如果插件配置正确且密钥无误你应该能在Yakit的“插件日志”或控制台输出中通常有一个“日志”标签页看到插件打印的[Req Decrypted] ...信息后面跟着解密后的JSON或表单数据明文。查看修改效果如果你在插件中编写了修改逻辑如替换username那么在发送到服务器的请求中Body的密文已经发生了变化。你可以通过对比修改前后向服务器发送的请求包来验证。更直接的方法是查看服务器的响应。如果修改成功服务器会以修改后的参数如usernameadmin进行处理并返回相应的结果。一个完整的实战循环目标测试一个加密登录接口的用户名枚举漏洞。步骤正常登录一次通过插件日志获取解密后的请求体格式例如{username:test_user, password:encrypted_password_hash, timestamp:1234567890}。修改插件代码将解密后的username字段值从一个字典文件中逐行读取并替换。在插件中每替换一个用户名就重新加密并发送请求。观察服务器的响应如状态码、响应时间、返回信息判断用户名是否存在。这就需要你在插件中实现一个循环或读取外部文件的功能这超出了单个请求修改的范畴可能需要结合Yakit的“Web Fuzzer”功能或者编写更复杂的、能维持会话状态的插件。但核心原理依然是本插件所展示的解密-修改-再加密。6. 常见问题排查与进阶技巧6.1 插件不生效按此清单排查问题现象可能原因解决方案看不到任何插件日志输出1. 插件未激活。2. 插件代码有语法错误加载失败。3. 流量未匹配插件判断条件。1. 在MITM设置中确认插件已勾选。2. 查看Yakit日志或插件管理界面是否有错误提示。3. 在插件开头加console.log(Plugin loaded!)测试。简化isTargetEncryptedData函数直接返回true进行测试。解密失败报错Invalid key length或Invalid 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 进阶技巧与注意事项动态密钥的处理如果密钥是每次会话动态生成的你的插件需要能够获取它。一种方法是在插件中Hook前端生成密钥的JavaScript代码这非常复杂。更实际的方法是如果动态密钥是通过某个API接口下发的你可以先捕获那个接口的响应从中提取密钥然后缓存起来供后续加解密使用。这需要插件有状态存储的能力。处理多种加密模式或端点一个应用可能对不同接口使用不同的加密参数。你可以在插件中维护一个“URL模式 - 加密配置”的映射表根据flow.Request.Url来动态选择解密策略。性能考量加解密是CPU密集型操作。如果处理大量并发请求可能会影响Yakit性能。在插件中做好条件判断只对必要的请求进行加解密操作。与Yakit Fuzzer结合对于参数爆破等测试更高效的方法是使用Yakit的“Web Fuzzer”。你可以先手动抓一个加密包然后利用Fuzzer的“数据包变形”功能配合一个“外部处理器”插件。这个处理器插件接收Fuzzer生成的明文Payload将其加密后返回给Fuzzer进行发送。这样就能实现自动化测试。安全与合规警告此技术仅限用于您拥有合法测试权限的系统如公司内部测试、授权渗透测试、或个人学习研究。切勿用于任何未授权的系统否则将触犯法律。实操心得在实际测试中最耗时的部分往往是前端加密逻辑的分析。一旦成功提取出稳定的Key和IV编写Yakit插件本身是很快的。建议在分析阶段多利用浏览器的调试工具特别是“Sources”面板中的代码搜索和“Network”面板的XHR断点功能。对于复杂的混淆代码可以尝试使用JS反混淆工具如 de4js进行初步处理但核心还是需要耐心地跟踪代码执行流程。成功解密出第一个数据包的那一刻所有的努力都是值得的因为它为你打开了一扇通往应用核心逻辑的大门。