
1. 为什么 Day5 的新手适合从“抓包 绕过”这对组合切入Day5 是个很有意思的阶段。前面四天你可能刚装好虚拟机、配好环境、跑通第一个小实验正在一个“什么都懂一点又什么都摸不透”的状态。这时候如果让我推荐一个能同时撬动网络协议、前后端交互、安全测试三个方向的学习入口我会毫不犹豫地说把 Wireshark 打开手动抓一次登录报文再顺手把一个简单的 MD5 校验漏洞绕过去。很多人会觉得这两个东西没关系。Wireshark 是观察流量的镜子MD5 绕过是逻辑漏洞利用一个看传输一个看校验怎么会凑到一天学但实际操作一遍就会发现抓包让你知道密码在请求里是怎么存在的绕过让你知道接收方到底是怎么校验的。一个是输入端一个是处理端连起来刚好是一条完整的数据链路。新手阶段最缺的不是一个个孤立知识点而是能把“用户在页面上输入密码 → 网络传输 → 服务端校验 → 返回结果”这条链路串起来的能力。这个组合还特别适合两类人。第一类是刚入行安全、准备打 CTF 或者做渗透测试的新手Wireshark 是分析流量必备工具MD5 弱类型绕过又是 CTF 里高频出现的登录类考点提前练熟等于给自己攒弹药。第二类是后端开发或全栈工程师很多人写接口只知道“密码存 MD5”却不知道这种写法在比较逻辑不当的时候等于开门放人。通过抓包看到登录请求的真实样子再看着自己的代码被绕过一次比看十篇安全文章都长记性。我这篇文章会按自己实际踩坑的顺序来写装好 Wireshark、正确使用过滤器、抓一次 HTTP 登录请求、分析 MD5 哈希值在认证里的角色、复现两种最常见的简单绕过方式最后聊清楚正确防御应该怎么写。每个环节都会标注哪些坑新手容易踩哪些操作需要在什么样的合法环境下进行。如果你手头有 VMware 虚拟机或者本地 Docker建议跟着边看边做效果比我单方面输出好得多。2. 装好 Wireshark 后第一件事分清两种过滤器别急着点开始2.1 安装环境和那个“找不到网卡”的老坑Wireshark 的安装本身不复杂Windows 下从官网下载安装包一路 Next 就能装好。但有个坑我必须提安装过程中会提示安装 Npcap这个组件是 Windows 下抓包的核心驱动。新版安装包默认会带上但有人手滑取消勾选或者安装的是老版本 WinPcap结果打开 Wireshark 发现接口列表里空空如也或者能看见网卡但抓不到包。如果你遇到“找不到本地网卡”或者“抓包抓不到流量”按这个顺序排查确认 Npcap 已经装上。打开命令行输入sc query npcap如果提示服务不存在重新运行安装包补装。确认 Wireshark 是以管理员身份运行。Windows 下抓包需要管理员权限右键图标选“以管理员身份运行”这个问题新人遇到频率极高。如果系统有多个网卡VMware 虚拟网卡、虚拟专用网络适配器等接口列表里会有一堆名字先找到名字带 Ethernet、WLAN 或者对应你当前上网方式的那个别抓错。比如你在 VMware 里操作就选 VMnet8 或对应的虚拟网卡。macOS 用户装完后也需要在“系统设置 → 隐私与安全性 → 本地网络”里允许 Wireshark 访问网络否则同样抓不到包。Linux 下最简单apt install wireshark或pacman -S wireshark-qt装完用sudo启动就行。2.2 捕获过滤器和显示过滤器用错等于白抓新手最容易犯的错是把 Wireshark 顶部的过滤栏当摆设什么都不填就直接开始抓然后看着满屏花花绿绿的 ARP、DNS、TCP 保活包发懵。Wireshark 有两条过滤链路这是决定你能不能高效抓包的关键。捕获过滤器是抓之前设置的它决定哪些包进入内存。语法相对简单写在捕获选项里。比如我只想抓本机和某个 IP 之间的 HTTP 流量就可以用host 192.168.1.10 and tcp port 80显示过滤器是抓完之后在结果里筛出符合条件的包。它用的是一套更强大的表达式写在主界面顶部的过滤栏。比如我想从一堆包中找出所有 POST 请求http.request.method POST两者最大的区别是捕获过滤器帮你省资源显示过滤器帮你省眼睛。新手阶段建议捕获时不要加太多限制先全量抓下来再用显示过滤器反复筛选。为什么因为有些问题你当时没意识到需要某个字段要是捕获阶段就过滤掉了后面想查都查不到。我见过不少人只抓了 80 端口回头发现登录接口走的是 443不得不重新抓一遍。当然如果流量特别大、机器扛不住那另说。理解这两个过滤器的区别能让你在分析网络问题时少走两个小时的弯路。我自己刚开始学的时候就是用错了过滤器半天看不到想要的请求差点以为是 Wireshark 坏了。其实工具没坏是我过滤条件写错了。3. 抓一次“登录”看包里的密码是怎么流动的3.1 在本地起一个带登录页的试验环境开始抓包之前我强烈建议你别拿公网上的任何网站练手。一来你没授权二来 HTTPS 流量抓下来全是密文新手看了只会更懵。最稳妥的做法是在自己电脑上搭一个本地测试环境流量走的是回环地址127.0.0.1整个过程完全可控。用 Python 的 Flask 搭一个最简单的登录接口可以照着下面这段来from flask import Flask, request import hashlib app Flask(__name__) app.route(/login, methods[POST]) def login(): username request.form.get(username) password request.form.get(password) # 先打印出收到的密码原文和它的 MD5 值 print(fusername: {username}) print(fpassword raw: {password}) print(fpassword md5: {hashlib.md5(password.encode()).hexdigest()}) return login attempt recorded if __name__ __main__: app.run(host127.0.0.1, port5000)再用浏览器打开一个最简单的 HTML 登录页把表单的action指向http://127.0.0.1:5000/login。这个环境足够让你观察到底层数据流动了。跑起来之后打开 Wireshark选择 Loopback 或 lo 网卡开始抓包。然后正常在页面上输入用户名和密码点提交几秒钟后回到 Wireshark 停止抓包在显示过滤器里输入http.request.method POST你会看到一条发往127.0.0.1:5000的 POST 请求。双击这条记录在中间的面板里依次展开 HTTP 协议层找到 HTML Form URL Encoded 部分——用户名和密码就明文躺在那里。这一幕值得每个新手亲眼看一次你在前端页面上输入的一切内容如果没有 HTTPS 保护在网络里就是这样的透明状态。3.2 三次握手和分层结构顺便就理解了等你在 Wireshark 里找到了那条 POST 请求往上翻几条能看到这个连接建立时的三次握手SYN、SYN-ACK、ACK。新手不用去背教科书里的定义只需记住一个类比SYN 相当于你问对方“在吗我要发数据了”SYN-ACK 是对方回“在的你发吧”ACK 是你确认“好那我开始了”。三次握手解决的核心问题是让双方都知道彼此已经准备好可以开始传数据了。再顺着封包详情面板往下看你会看到 Wireshark 帮我们解析好的层次结构最底层是以太网帧Ethernet往上是 IP 层再往上是 TCP 层最上面才是 HTTP 层。这四层刚好对应现实中的寄包裹流程HTTP 是信封里的信纸TCP 是快递单IP 是地址系统以太网是货车司机能看懂的路牌。每发一次请求数据就按这个顺序层层打包到达服务端再解包。Wireshark 把这些过程全部可视化比你看十遍“OSI 七层模型”的文章都直观。3.3 密码是明文那 MD5 在哪里出现如果你跟着上面的 Flask 代码实践你会注意到一个细节服务端打印出来的密码是原文同时又计算出了它的 MD5 值。这在真实的登录系统里其实很常见——很多系统的前端或后端在传输层对密码做一次 MD5 后再校验或者数据库里存的就是 MD5 值。麻烦的地方在于MD5 算出来的是一个固定长度的十六进制字符串32 位它长得像密文但它并不是真正的加密算法。这里先搞清楚一个概念MD5 是哈希函数不是加密函数。加密是加了密还能解开的哈希是单向的——雪崩效应极强原文差一个字符输出完全不同而且从哈希值反推原文没有数学上的逆运算。你在网上看到所谓的“MD5 解密”本质上是把海量常见字符串预先算好 MD5存成“彩虹表”然后拿你的哈希值去查表。如果原文是强密码且加过盐这种“解密”基本失效。那标题里说的“MD5 简单绕过”又绕在哪里绕的不是 MD5 本身而是使用 MD5 的校验逻辑。下一节我会拆解一个非常经典的例子它在 CTF 里出现率极高而且代码写错的人远比想象中多。4. MD5 弱类型比较绕过0e 字符串为什么能骗过后端校验4.1 PHP 弱类型比较中的 0e 魔术如果你用 PHP 写过登录校验大概率见过类似这样的代码if (md5($user_input) $target_md5) { // 登录成功 }这行代码在普通情况下没有任何问题用户输入的密码算出来 MD5跟数据库里的目标 MD5 比对相同就放行。但只要有点安全意识的人看到和md5()组合就会立刻意识到这里可以绕。问题出在 PHP 的弱类型比较规则上。比较两个值的时候如果两边都是可以转成数字的字符串PHP 会先把它们转成数字再比。而科学计数法表示的数字比如0e123会被解读为 0 乘以 10 的 123 次方结果就是 0。也就是说任何以0e开头、后面全是数字的字符串在 PHP 弱比较中都会等于 0。现在把这个规则套回 MD5 场景如果某个字符串的 MD5 值恰好是0e开头加一串数字那么md5($user_input)和另一个同样以0e开头的 MD5 字符串用比较时两边转数字都等于 0比较结果就是相等。有一个经典的例子字符串QNKCDZO的 MD5 值是0e830400451993494058024219903391。另一个常见例子是240610708它的 MD5 值也是0e462097431906509019562988736854。只要目标系统的校验逻辑是md5($input) $target_md5并且$target_md5恰好也是这样的值你就根本不需要知道任何人的真实密码直接传入QNKCDZO就能通过校验。为了让你更直观地看到这一点我在本地 PHP 环境里跑一段代码?php $input QNKCDZO; $target 0e830400451993494058024219903391; if (md5($input) $target) { echo bypass success; } else { echo login failed; } // 输出: bypass success原因就是md5($input)的结果同样是0e开头两个字符串在下都被当成了 0。如果我把代码改成if (md5($input) $target) { echo bypass success; }结果就会是login failed因为要求类型和值完全一致0e830400...是字符串不会参与数字转换。4.2 这个绕过有什么用以及新手容易踩的坑这种绕过在 CTF 的登录题里最典型后端代码直接给出md5($password) 某个0e开头的目标值你只需要找到一个 MD5 也是0e开头的输入字符串或者干脆提交目标值本身就能登录成功。它之所以叫“简单绕过”是因为不需要动用任何暴力破解纯粹是利用了语言特性和比较符选择不当。新手在复现这个姿势时最容易踩的坑是以为所有以0e开头的字符串都能用。注意必须是0e后面全是数字才行。0eabc这种带字母的PHP 转换成数字时会失败结果就是字符串比较规则又不一样了。你可以自己多跑几个md5()看看输出凡是0e后面混入了字母的在弱比较里基本都不生效。还有一个认知误区和这个技术点强相关有人把“MD5 弱类型绕过”理解成“把目标 MD5 值反过来推出明文密码”也就是前面说的“MD5 解密”。这是两种完全不同的思路。弱类型绕过是绕过校验规则而不是破解哈希本身。我不会花大量篇幅教你怎么查彩虹表一方面那是另一个话题另一方面真正的安全测试和 CTF 比赛也都不会停留在“查表”这种毫无技术含量的方式上核心考察的永远是你对程序逻辑和语言特性的理解深度。需要特别说明的是这类实验一定要在本地环境、CTF 在线靶场或者自己搭建的测试环境里进行。不要拿这个思路去对任何没有授权的线上系统做验证一是违法二是你根本不知道目标系统用的什么语言和比较方式盲试没有任何意义。5. 只校验前端的登录接口改一个包就能绕过5.1 前端校验带来的虚假安全感如果说弱类型比较是后端语言特性的坑那前端校验就属于产品设计层面的坑而且在这个领域出现的频率比想象中高得多。很多开发团队为了让用户感觉“密码经过了加密处理”会在前端用 JavaScript 把密码算成 MD5再 POST 给后端。这时候问题就来了如果后端只判断“密码字段是否为空”或者干脆直接以 MD5 字符串入库而不做任何哈希验证那么整个认证就变成了一张纸。我见过一个很典型的演示项目前端这样写// 用户点击登录后计算 MD5 再发送 const passwordHash CryptoJS.MD5(passwordInput.value).toString(); fetch(/login, { method: POST, headers: { Content-Type: application/x-www-form-urlencoded }, body: username${username}password${passwordHash} });而后端代码大概是app.route(/login, methods[POST]) def login(): username request.form.get(username) password request.form.get(password) if len(password) 0: # 直接登录成功 return login success这种系统在普通用户手里没有任何问题因为普通用户只会老老实实打开页面输入密码让前端去算 MD5。但你用 Wireshark 抓一次包就会发现所谓的“MD5 加密传输”并没有改变认证的本质——后端没有校验这个 MD5 是否对应一个正确的原始密码它管理的是“有没有提交密码字段”。那绕过思路就简单到令人发指了直接改掉被发送的数据包把密码字段的值换成任意字符串甚至换成1后端照样放行。5.2 用 Burp Suite 或浏览器开发者工具完成一次改包实验要在本地靶场复现这个过程我建议会用 Burp Suite 的同学直接上 Burp不会用的用浏览器 F12 里的开发者工具也能完成。下面以浏览器开发者工具为例讲一下完整的操作链路。第一步在本地把上面那种只做前端 MD5、后端不校验哈希的 Flask 服务跑起来。第二步在登录页面输入一个密码提交前打开开发者工具切到 Network网络面板勾选 Preserve log保留日志然后点登录。第三步在 Network 面板里找到那条 POST 请求右键选择 Edit and Resend编辑并重新发送之类的功能或者直接在 Payload 区域把password字段的值改掉改成任意一段字符串。第四步点击发送观察服务端返回结果。如果服务端逻辑真的只是“校验字段非空”你会看到服务端依然返回login success。而这个过程中你并没有经过前端那些计算步骤连 MD5 都没算就直接通过了校验。这个实验的价值在于让你亲眼确认前端所做的一切哈希、加密、校验本质上都只是用户体验层面的包装真正的安全边界必须在服务端落地。当然改包工具的使用要非常克制。Burp Suite 是合法的安全测试工具但用它去改别人的线上请求在法律和道德上都有问题。我只建议在自己可控的环境里做这种实验比如本地 Docker 起的靶场、自己的虚拟机上部署的应用或者 CTF 平台提供的题目环境。这一点想清楚了你学到的技术才不会是隐患。5.3 从抓包看到改包一条线串起来现在回头看我们 Day5 的所有实验你会发现路径非常清晰先用 Wireshark 抓到 POST 请求看到密码在请求体里的真实形态再根据后端代码的逻辑思考它到底校验了什么最后通过构造数据包来验证自己的判断。抓包和改包本质上都是为了回答同一个问题——“服务器到底相信了什么”这个问题问得越多你对安全的直觉就越准。以后你看到一个登录接口会本能地想它校验的是密码原文还是密码哈希它比较哈希时用的是严格比较还是弱比较校验逻辑放在前端还是后端数据库里的哈希值是从哪来的这些问题一旦成为你的习惯很多漏洞在你眼皮底下就直接暴露了不需要用什么高级扫描器也不需要多复杂的 PoC。6. 绕过之后回头补防御这些坑在真实项目中怎么填6.1 强制哈希比较用恒定时长函数弱类型比较的修复方案其实很明确。第一比较两个哈希字符串时用严格比较运算符而不是第二更稳妥的做法是用语言自带的“恒定时长字符串比较”函数。PHP 里的hash_equals()、Python 里的hmac.compare_digest()、Node.js 里的crypto.timingSafeEqual()都是干这件事的。使用恒定时长比较可以防止时间侧信道攻击防止攻击者通过响应时间的微小差异逐步猜出哈希值。很多开发者在刚开始写代码时根本不会意识到和的差别也不了解0e这种科学计数法字符串的转换规则。但实际上这些语言特性在官方文档里写得清清楚楚只是在业务代码里很少有人去注意。安全培训常说“不要相信用户输入”这不仅仅指 SQL 注入和 XSS也包括“不要相信用户输入的字符串在比较运算中不会触发语言特性”。把这句话当成开发习惯能少踩很多坑。6.2 密码存储的正确姿势加盐哈希和专用密码函数再往深一层说。即使你把比较符改成了严格模式只要系统里存储的还是裸 MD5 值风险依然存在。MD5 的碰撞攻击和彩虹表让我们早就有了共识MD5 不适合用来存储密码甚至 SHA-1 也一样不合适。正确做法是使用专门的密码哈希算法比如 bcrypt、scrypt、Argon2或者使用语言内置的password_hash()/password_verify()这类封装好的函数。这类算法有几个共同特点自动加盐、计算成本可调节、每次生成的哈希值随机。加盐是最关键的一环——哪怕两个用户密码完全相同因为有盐值参与计算最终存储的哈希值也完全不同攻击者连“这两个用户是不是同一个密码”都没法判断。而调节计算成本是为了让暴力破解每个候选密码都需要大量算力。这些思路对新手来说可能显得有点“超前”但至少应该知道一个原则不要自创密码存储方案直接用被安全社区验证过无数遍的标准方案。6.3 安全学习的正确方式搭台、拆台、再补台最后说点个人体会。很多新手学了抓包和绕过之后容易产生一个误区觉得“我能绕过登录接口了我是不是也能攻击别人的网站了”。这个想法很危险也很幼稚。真实世界里的登录系统大概率有 WAF、有 HTTPS、有多因素认证、有风控策略不是你拿个0e字符串就能进去的。你练习用的那种“裸奔”项目如果真出现在生产环境里问题根本不在攻击者的技术而在开发者的安全意识。我自己的经验是每学一个绕过姿势就顺手把它对应的修复方案写一遍。学完弱类型比较就去把代码改成并模拟测试确认无法绕过学完前端校验绕过就把校验逻辑移到服务端再加一层限流学完 MD5 不适用于密码存储下一个项目里就用password_hash()。把拆台和补台当成一对动作来练习你的技术体系才是完整的。防御知识学得越多你对“绕过”这两个字的理解就越深因为在安全领域最了解盾牌薄弱点的人往往就是那个亲手砸过盾牌的人。