拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Pikachu靶场CSRF实战:从GET到Token的攻防链路与绕过思路

Pikachu靶场CSRF实战:从GET到Token的攻防链路与绕过思路 我是在把 Pikachu 靶场搭起来之后才真正体会到CSRF跨站请求伪造这玩意儿最磨人的地方不在于原理有多深而在于攻击链路中间隔着浏览器替你干的那些事。光看文档你会觉得“让用户点个链接不就行了”真上手才发现受害者是否处于登录态、表单字段名是否和原页面完全一致、请求里有没有 token任何一个环节不对演示就卡住。这篇东西不是从 CSRF 概念开始科普而是把我在 Pikachu 的 CSRF 模块里踩过的坑、反复试过的思路、最终打通的完整流程一次性记下来。Pikachu 把 CSRF 拆成了 CSRF(get)、CSRF(post)、CSRF(token) 三个递进场景正好覆盖了从完全不设防到加了 token 校验的三种典型形态。网上相关攻略往往只给一句话结论比如“GET 改 URLPOST 建表单token 要配合 XSS”但真正让人涨功夫的恰恰是中间的细节——抓包改参、构造隐藏表单、分析 token 校验逻辑、处理环境连通性问题。这篇博文适合刚入门 Web 安全、手里捏着一个本地 Pikachu 靶场但还没把 CSRF 模块跑通的人也适合想系统梳理 CSRF 攻击手法的同学。所有操作都基于本地实验环境不涉及任何真实目标。1. 先搞懂 CSRF 的触发链路Pikachu 为什么刚好能演示它1.1 三个必要条件一个都不能少CSRF 能成立本质上靠的是“浏览器会自动携带身份凭证”这个默认行为。完整的攻击链路要满足三个条件缺一个演示就失败受害者已经登录了目标站浏览器里保存着有效的会话凭证比如 PHPSESSID 这个 Cookie目标站的敏感操作请求可以被攻击者完整预测路径、参数名、参数值都能提前构造好目标站处理请求时没有校验令牌Token或来源Referer只凭“这个请求带着有效 Cookie”就认定是本人操作。Pikachu 的 CSRF(get) 模块就是这么设计的。登录之后页面上是一个修改个人资料的表单昵称和邮箱直接以 GET 参数形式拼在 URL 里提交服务端只检查会话里有没有登录用户不检查请求是从哪来的、参数有没有被改过。于是攻击者只需要构造一个把参数替换掉的链接发给受害者受害者一点浏览器就带着自己的 Cookie 去请求这个 URL服务端看到有效会话就直接执行修改。1.2 它和 XSS、越权的本质区别很多人分不清 CSRF 和 XSS其实一句话就能说清XSS 是把脚本注入到受害者的页面里攻击者能拿到会话凭证本身CSRF 是借受害者的登录态代为发请求攻击者全程拿不到 Cookie只是“遥控”受害者的浏览器去做事。拿现实生活打比方XSS 相当于偷了钥匙进门翻东西而 CSRF 是让钥匙的主人毫无察觉地自己把门打开。越权则是另一个维度。越权问题出在服务端“只信任传入的用户标识”上而 CSRF 问题出在服务端“只信任 Cookie 却不校验请求来源”上。Pikachu 的越权模块和 CSRF 模块放在一起练会特别有感觉前者改一下 URL 里的 id 就能操作别人的数据后者改一下参数就能让别人操作自己的数据。1.3 为什么现代系统里它仍然存在很多人觉得 CSRF 是老古董现在框架里都有防护。这话对了一半。SameSite Cookie 属性、CSRF Token、Referer 校验确实是现代 Web 开发的标配但在实际渗透测试里我见过太多漏网之鱼老系统没升级、内部管理系统赶工期没加防护、前后端分离的接口只做了 CORS 配置忘了 Token 校验、第三方登录回调接口完全裸奔。这些场景下CSRF 的攻击面反而是变大了因为你不知道哪个接口会突然漏出来。Pikachu 作为教学靶场把最原始的形态摆出来恰恰是最好的起点。2. CSRF(get) 模块实战一个构造好的链接如何改掉用户资料2.1 先登录并抓取原始请求进入 Pikachu 的 CSRF 模块之前需要先登录靶场。Pikachu 安装后一般自带测试账号或者可以用注册功能自己造一个不同版本初始密码不一样我建议直接注册一个测试账号最省事。登录成功之后访问 CSRF(get) 模块对应的页面这时候页面里会有一个修改昵称和邮箱的表单。先在表单里随便填一个值比如把昵称改成 test点击提交的瞬间用 Burp Suite 开启拦截或者按 F12 打开浏览器开发者工具切到 Network 面板能看到请求长这样GET /pikachu/vul/csrf/csrfget/csrf_get_edit.php?id1nicknametestemailtesttest.com HTTP/1.1 Host: 192.168.1.100 Cookie: PHPSESSID6f1a2b3c4d5e这里有几个细节要留意。第一个是路径不同版本的 Pikachu 源码目录可能略有差异以你自己靶场里实际抓到的路径为准。第二个是参数有些版本通过 Session 判断当前用户URL 里的 id 参数不一定需要有些版本会真的读取 id所以你在构造链接时最好保留原始请求里的全部参数只替换你想改的值。第三个是关键Cookie 头是浏览器自动带的攻击者构造链接时根本不需要写 Cookie受害者点击链接时浏览器会替请求附上。2.2 构造恶意链接拿到原始请求之后攻击者的工作就变得很机械了。把参数值替换成攻击者想要的数据比如昵称改成 hacker邮箱改成 hackerevil.com然后把这条 GET 请求去掉 Cookie 头、去掉 Host 里的无关信息整理成一条 URLhttp://192.168.1.100/pikachu/vul/csrf/csrfget/csrf_get_edit.php?id1nicknamehackeremailhackerevil.com把这个链接发给一个已经登录 Pikachu 的受害者浏览器对方只要点一下服务端就会收到一个带着有效 Cookie 的 GET 请求然后毫不犹豫地把昵称和邮箱改成攻击者指定的值。整个过程没有任何二次确认也没有任何视觉提示受害者完全无感。我一开始练的时候总想着“攻击者是不是要提前知道受害者的 id 才行”后来想明白了Pikachu 这个模块的 id 是写死成当前 Session 对应的用户或者通过 Session 直接获取攻击者只需要保证构造的链接在参数结构上和原始请求一致就可以。2.3 诱导点击的常见伪装方式链接构造好了接下来是投递环节。实战中诱导点击的方式五花八门最常用的是这几类短链接服务压缩一下 URL让受害者看不出目标地址把链接伪装成“查看照片”“领取礼包”等文案发在评论区、私信、聊天群利用论坛的图片外链把恶意链接放在 img 标签的 src 属性里受害者打开帖子就会触发某些场景下还可以结合 URL 跳转漏洞先跳到一个看起来很正常的域名再 302 到恶意链接。Pikachu 实验环境里不需要搞得这么花哨你只需要记住受害者只要处于登录态点击即中招。这恰恰是 GET 型 CSRF 最危险的地方也是为什么所有安全规范都强调“状态改变操作不要用 GET”。2.4 防御视角为什么敏感操作不应该放在 URL 里从攻击者的角度看GET 型 CSRF 是门槛最低的一种。原因很简单GET 请求的一切信息都在 URL 里URL 会出现在浏览器历史记录里、会出现在服务端访问日志里、会出现在 Referer 头里被第三方站点看到。攻击者要构造请求只需要把一个链接复制粘贴所见即所得。所以现代 Web 开发规范里明确要求会产生副作用的写操作必须用 POSTGET 只保留给查询、读取等无副作用场景。但是要注意把 GET 改成 POST 只是提高了门槛不是终点。POST 请求同样可以被自动化提交这就带出了 Pikachu 的第二个模块。3. CSRF(post) 模块构造一张恶意页面让表单自己提交3.1 模块的请求形态分析进入 Pikachu 的 CSRF(post) 模块页面上是一个字段更多的个人资料表单常见的有性别、手机号、地址、邮箱等。提交方式变成了 POST直接在 URL 里拼接参数已经行不通了因为服务端读的是 POST 请求体里的参数而不是查询字符串。用 Burp 抓一下原表单的提交能看到请求体是这样的POST /pikachu/vul/csrf/csrfpost/csrf_post_edit.php HTTP/1.1 Host: 192.168.1.100 Content-Type: application/x-www-form-urlencoded Cookie: PHPSESSID6f1a2b3c4d5e sexgirlphonenum18888888888addsomeaddressemailtest%40test.com这时候问题来了攻击者没法再靠一条链接搞定攻击必须构造一个页面让受害者的浏览器在不知不觉中发出这条 POST 请求。3.2 恶意页面的完整构造过程构造的核心思路是“复刻原表单的提交动作”。你不需要让页面长得像目标站只需要保证两个关键点第一表单的 action 指向目标站的编辑接口第二每个 input 的 name 属性必须和原始表单完全一致。服务端只认参数名不认页面外观。下面这个 HTML 就是最典型的 CSRF(post) 攻击页面。html body h1风景照片欣赏/h1 img srcphoto.jpg width600 / form idcsrfForm actionhttp://192.168.1.100/pikachu/vul/csrf/csrfpost/csrf_post_edit.php methodPOST input typehidden namesex valuegirl / input typehidden namephonenum value18888888888 / input typehidden nameadd valueattacker address / input typehidden nameemail valueattackerevil.com / /form script document.getElementById(csrfForm).submit(); /script /body /html页面加载时JavaScript 会自动调用submit()方法用户连按钮都不用点。受害者打开这个页面看到的是一张正常的风景图但在页面渲染的瞬间表单已经带着受害者浏览器里的 Cookie 向目标站发出了 POST 请求。我在本地验证的时候故意把页面做成一张“美女图片合集”受害者完全不会起疑。字段名要以你靶场里实际抓包得到的参数名为准Pikachu 常见的是 sex、phonenum、add、email但不同版本可能有差异。实战中也是这样构造前先看一眼原始请求体原样复刻。3.3 为什么用表单而不是 Fetch/AJAX初学者很容易问既然可以用 JavaScript为什么不直接用fetch或者XMLHttpRequest发跨站 POST这里有两个坎踩过一次就明白了。第一个坎是 CORS 预检。如果你把请求头的Content-Type设成application/json或者加了一个自定义 Header浏览器会先发一个 OPTIONS 预检请求。跨域场景下目标站如果没有配置允许跨域的响应头预检直接失败真正的 POST 请求根本不会发出。第二个坎是响应读取。即使不触发预检跨域发出去之后JavaScript 也读不到响应内容因为浏览器会拦截跨域响应。但 HTML 表单有自己的历史特权。浏览器把表单的 POST 提交设计成“简单请求”跨域提交时不会触发预检还会带着当前域名下的 Cookie 一起发送。这个设计初衷是为了兼容老网页的跨域表单提交场景结果成了 CSRF 最大的帮凶。所以实战中构造 POST 型 CSRF 攻击页面标准做法永远是 HTML 表单不是那些看起来很高级的 AJAX 代码。3.4 把恶意页面“送”给受害者的常见路径页面构造好之后需要一个能存放页面的地方。常规路径有这么几条把 HTML 文件挂到攻击者自己控制的任意服务器上比如一台便宜的云主机或者本地开个python -m http.server如果攻击者能往受害者的论坛、评论区里插 HTML前提是站点没过滤可以直接把表单代码嵌进去配合 URL 跳转漏洞把受害者从正常页面导到恶意页面在邮件、即时通讯工具里发送一个指向恶意页面的链接配上“点击领取”之类的理由。换一个更隐蔽的思路把恶意页面直接部署在自己搭建的站点上然后给受害者发送“你有一张图片待查看”的短链接。受害者点开短链接落地就是恶意页面页面图片正常展示背后悄悄提交了表单。我在实验里常用的验证方法就是改完资料后回目标站刷新一下看到资料已经被篡改就说明整个链路通了。4. CSRF(token) 模块Token 校验逻辑与常规绕过思路4.1 模块的校验表现进入 Pikachu 的 CSRF(token) 模块页面还是类似的个人资料表单但多了一个 token 参数。刷新页面token 值会变化说明服务端在每次生成表单时都产生了一个新的随机值。用 Burp 抓包能看到请求体里带了 tokenPOST /pikachu/vul/csrf/csrftoken/csrf_token_edit.php HTTP/1.1 Host: 192.168.1.100 Cookie: PHPSESSID6f1a2b3c4d5e nicknametestemailtest%40test.comtokenbf1c8a2e6d4f如果直接把 token 删掉提交或者随便改成一个错误的值服务端会返回类似“token invalid”的提示并且页面重新生成一个新的 token。这说明服务端不只要验证参数对不对还强制要求每次请求带上有效的 token而这个 token 是和 Session 绑定在一起的。4.2 Token 校验的逻辑拆解Token 校验的完整流程是这样的用户访问表单页面时服务端生成一个随机 token把它同时写入用户的 Session 和页面的隐藏字段里用户提交请求时服务端对比请求里的 token 和 Session 里的 token 是否一致。攻击者构造的请求里没有有效的 token因为 token 藏在受害者的 Session 里攻击者没法提前知道。这里有个容易被忽视的点token 是随着页面一起返回给浏览器的所以只要攻击者能让受害者的浏览器执行任意脚本也就是存在 XSS就能读取页面里的 token 再拼进请求里。Pikachu 的课程设计里CSRF(token) 最标准的练习方式不是硬绕 token而是先想办法在目标页面上执行脚本把 token 取出来带进请求。逆向再想一层如果 token 出现在 GET 参数里或者通过 Referer 头传到第三方站点那么日志、统计系统、浏览器历史都可能造成 token 泄露。很多真实的绕过案例就是这么发生的——开发者把 token 放在 URL 里结果 Referer 带着 token 飞到了外部的图片统计服务器。这种细节 Pikachu 的 token 模块不会主动展示但理解原理后可以自己举一反三。4.3 常规绕过思路整理把常见的 CSRF Token 绕过思路整理成表方便对照绕过思路适用前提关键点配合站内 XSS目标站同一页面存在 XSS 漏洞先 XSS 拿到 token再带 token 发请求token 泄露在 GET/Referertoken 出现在 URL 参数或 Referer 头从日志、历史记录、Referer 中提取token 生成算法可预测服务端用时间戳/固定字符串拼接再哈希分析生成逻辑自己生成合法 token特定接口不校验下载、导出、回调等接口漏加校验寻找校验的盲区接口在 Pikachu 实验环境里我建议把重点放在第一类先跑通 XSS 模块再回来看 CSRF(token)两个模块串起来玩。单独硬绕 Pikachu 的 token 意义不大因为它的实现是正常且规范的绕不动反而是正常现象说明防护起效了。4.4 三个模块放在一起才看得到防护成本Pikachu 把 CSRF 拆成 get、post、token 三个子模块递进关系非常清楚。第一个模块让你感受什么都不防护时攻击者有多轻松第二个模块告诉你就算改成 POST也只是把攻击成本从“发个链接”提高到“托管一个页面”第三个模块告诉你token 能把攻击门槛拉高到一个正常水平——攻击者必须找到其他漏洞配合才能继续。理解了这种“防护成本阶梯”以后评估一个系统的 CSRF 风险时脑子里自然会有参照。5. 环境搭建高频坑Burp 抓不到靶场、虚拟机访问不了 Pikachu5.1 主机访问不了虚拟机里的 Pikachu前面讲了半天攻击链如果本地靶场都跑不起来一切都是空谈。Pikachu 这东西虽然老但环境问题反而比攻击本身更磨人。我遇到最多的情况就是“我明明在虚拟机里打开了 Pikachu 页面宿主机浏览器怎么都访问不了”。这个问题的根源九成出在网络模式和防火墙。先看 VMware 或 VirtualBox 的虚拟机网络适配器NAT 模式下宿主机里访问虚拟机的地址经常需要端口转发或者干脆换成桥接模式让虚拟机拿到一个和宿主机同网段的 IP这样宿主机的浏览器就能直接访问了。我用 VMware 的默认 NAT 模式踩过一次后来把网络适配器改成桥接问题直接消失。另一个大头是防火墙。Windows 虚拟机的防火墙可能拦掉 80 端口或 8080 端口外网访问Linux 虚拟机的 firewalld 或 iptables 也可能默认拒绝。最快的排查顺序是这样的先ping通宿主机能不能 ping 通虚拟机 IP再curl通宿主机命令行执行curl http://虚拟机IP:端口看有没有 HTML 返回最后浏览器访问如果 curl 返回正常但浏览器打不开基本可以确定是浏览器代理设置出了问题往下看下一节。5.2 Burp 抓不到本地靶场请求Burp 抓不到包是第二个高频问题。最典型的表现是浏览器页面上 Burp 的代理拦截一片空白或者压根访问不了目标页面。原因通常有三个。第一个是代理没配置对。Burp 默认监听 127.0.0.1:8080但如果你用 SwitchyOmega 之类的浏览器代理插件得确保代理服务器地址填的是 Burp 所在机器的 IP。如果 Burp 装在虚拟机里、浏览器在宿主机上代理地址要填虚拟机的 IP而不是 127.0.0.1。第二个是 HTTPS 证书没装。Burp 抓 HTTPS 流量需要先在浏览器里安装并信任 Burp 的 CA 证书。Pikachu 如果跑在 HTTP 上就没这个问题但如果你用 phpStudy 开了 HTTPS 或者改了配置抓不到包先检查证书。第三个是代理作用范围。有些人浏览器开着代理插件但把目标地址划进了“直连”列表导致流量根本没走 Burp。排查方法是按 F12 看 Network 面板里的请求是否正常到达页面如果页面都能打开但 Burp 没流量那基本就是代理规则的问题。5.3 phpStudy 或靶场启动失败最后一个是靶场本身起不来。Pikachu 是 PHP 写的最常用的环境是 phpStudy 全家桶常见的启动失败原因我列一下80 端口被占用本机装过 IIS、Nginx 或者其他 Web 服务时Apache 起不来。解决办法是改 phpStudy 里的端口配置改成 8080然后重新访问http://ip:8080PHP 版本不兼容Pikachu 是老项目高版本 PHP 会遇到 MySQL 函数弃用、页面白屏报错之类的问题。我建议把 PHP 切换到 5.x 或低版本 7.x别一上来就用最新版MySQL 启动失败3306 端口被占或者 MySQL 数据目录权限不对去 phpStudy 的日志面板看具体报错页面白屏或警告把 PHP 的display_errors打开先看到具体错误再对症下药别盲猜。这些环境问题一旦解决后面跑 CSRF 模块就顺了。我的经验是先保证宿主机能 curl 通靶场地址再开 Burp最后才谈攻击链构造顺序别反。6. 从靶场到真实场景三个问题判断一个请求有没有 CSRF最后分享一点个人体会。Pikachu 这套靶场很多模块都比较老但它在 CSRF 这个主题上把三种形态放在同一个页面上节奏其实很合理先让你体验完全不设防的 GET 型再让你为了 POST 型去托一个页面最后让你面对 Token 校验体会到防护的存在感。把这三个子模块手工跑通一遍你对 CSRF 的认识基本就到位了。之后我去看真实系统的接口习惯性地会问自己三句话这个请求能不能被攻击者完整预测提交时有没有校验令牌或来源浏览器跨域带上 Cookie 之后会产生什么后果——这套直觉就是在 Pikachu 里反复调表单、抓包、改参数练出来的。如果你还在搭环境的阶段别急着追求“通关”先把整个过程拆成小目标第一步让宿主机访问到靶场第二步让 Burp 能抓到包第三步跑通 CSRF(get)第四步跑通 CSRF(post)第五步再回到 CSRF(token) 慢慢啃。每完成一步你对这套攻击链的理解就扎实一分。Pikachu 的价值不在于它有多新而在于它把所有知识点都摊开摆在你面前剩下的就看你自己动手验证的程度了。
返回列表