
刷Bugku的时候WEB方向开头的几道题我基本都是速通唯独这道GET题让我在电脑前坐了好一会儿。不是因为题目难而是因为它用最朴素的方式点破了一个Web安全里极其核心的问题权限验证到底该怎么设计。题目描述写得很直白——“逻辑简单猜到参数值就能直接获取 flag缺乏权限验证”等于把漏洞成因直接摊开给你看了。你要做的就是顺着这句话把flag捞出来。这道题适合Web安全入门阶段的CTF选手也适合刚接触HTTP协议不久的前后端开发。它不需要你懂复杂的漏洞利用手法只要知道URL的结构、GET请求的参数是放在查询字符串里的再配合一点点代码阅读的耐心就能解出来。我见过不少新手在这道题上卡住原因往往不是不会构造URL而是不敢往“参数值对了就直接给flag”这个方向想总觉得后面还有一堆加密、混淆等着自己结果绕了一大圈发现入口真的就这么简单。有意思的是这个题目对应着真实攻防场景里一类非常常见的漏洞——功能逻辑层面的越权和不安全的直接对象引用。很多老系统里“前端传一个参数后端默认参数对就给数据”的情况至今仍然存在只不过参数从what变成了user_id、order_id、file_id这些看起来更“正常”的名字。所以这道题不只是给你发个flag它其实是把一个真实漏洞的简化模型摆在桌面上让你用最直观的方式走一遍完整链路发现参数、推断取值、构造请求、拿到未授权数据。1. 题目背景与核心考点拆解1.1 Bugku平台与这道GET题的整体定位Bugku是很多国内CTF新手入门都会刷的训练平台题目难度门槛低、方向覆盖广从Web到MISC、逆向、密码学都有。它的Web方向里GET、POST这类题基本属于“开场菜”放在最前面不是用来刁难人的而是为了让你快速建立对HTTP交互方式的直观感受。我印象中这道GET题展示的是一段非常短的PHP代码当你打开题目页面后源码就直接渲染在页面上核心逻辑大致长这样if (isset($_GET[what])) { if ($_GET[what] flag) { echo $flag; } }翻译成人话就是如果你在URL里传了一个名为what的参数并且这个参数的值等于字符串flag那么后端就把$flag变量输出到页面上。整个流程里没有登录检查、没有Session校验、没有CSRF防护没有任何中间层。只要你把参数值猜对了数据就直接给你。这种题放到CTF里属于“有手就行”的级别但如果你把这套逻辑平移到一个真实业务系统——比如后台的管理接口只要传入管理员ID就能返回全量用户数据——你就能明白为什么这类题会被放在入门第一课。这不是码农偷懒的玩笑而是无数真实数据泄露事件的起点。1.2 这道题真正考察的思维模型很多刚入门的朋友会把这道题归类为“PHP代码审计”或者“HTTP参数操作”确实也能这么算但我更愿意把它理解成一个“权限验证缺失导致越权”的入门模型。你可以这样联想你在某个电商网站上点击“个人中心”后端是通过URL里的user_id来识别你是谁的然后去数据库里查询这个id对应的记录返回给你。正常情况下user_id应该来自登录后服务端下发的Session而不是由前端随便传。但如果开发者图省事直接信任了前端传上来的user_id那你把user_id改成别人的ID就能看到别人的订单、地址、聊天记录这在安全领域叫水平越权。Bugku这道GET题本质就是最简版的不安全对象引用。what参数担任了那个“本不该由客户端决定”的关键输入后端无条件信任它于是猜测到特定值后业务数据flag就被未授权地暴露了。所以这道题的解题链条很短但它背后的思维模型是通用的永远不要信任客户端传来的参数所有涉及权限的操作必须在服务端做二次验证。理解了这一点你就不是单纯“会做一道题”而是能把这套思维迁移到任何一处功能逻辑上——以后再看接口设计你会本能地问一句“这个参数服务端校验了吗换了值会怎样”2. 基础原理GET请求、参数传递与权限验证缺失2.1 HTTP GET请求的底层逻辑做题之前得先把GET请求本身搞明白不然连参数往哪儿放都不清楚。HTTP协议里定义了多种请求方法GET是其中最常见的一种语义是“向服务端索取资源”。它的特点是请求参数以键值对的形式附加在URL的查询字符串里紧跟在问号后面多个参数之间用符号分隔。举个例子你去访问一个天气接口https://api.example.com/weather?citybeijingunitcelsius这里city和unit就是两个GET参数值分别是beijing和celsius。服务端收到请求后会从URL里解析出这些参数并根据它们的值来决定返回什么内容。由于URL本身可以直接被书签收藏、被日志记录、被浏览器历史保存所以GET请求天然不适合传输敏感数据。这也是为什么登录密码、Token这类信息都必须走POST或者放请求头而不是塞进URL里。在这道Bugku GET题里你需要做的就是在原来的地址后面拼上?whatflag然后浏览器重新发起请求服务端从URL中读到what的值为flag再决定“条件成立把$flag吐出来”。整条链路里参数的传递方式和普通网站加载一篇文章时?id123没什么本质区别区别只在于服务端愿不愿意在参数正确时无脑给你数据。提示新手做这类题时最容易忽略的是URL的书写位置。问号是“参数开始”的标志必须放在路径末尾也就是那些.php、.html、/xxx路径的后面而不是乱插。浏览器地址栏、curl命令、Burp Suite重放、Python requests脚本这些都是可以发送GET请求的入口。做题用哪个我的建议是先地址栏手点体验参数写进URL后页面发生的变化再用curl或Burp来一遍因为很多真实场景里你的端点是无法用浏览器直接访问的掌握工具侧的发送方式会更顺手。2.2 权限验证缺失的本质所谓“缺乏权限验证”用行话说就是后端缺少一个“谁在访问”的判断流程。正常的后端逻辑应该是先识别身份Session、Token、Cookie再核对权限该身份能否访问该资源最后才返回数据。但这道题把一个最简单的PHP判断条件直接暴露出来没有任何“身份”概念也没有“权限”概念只判断“参数值”是否符合预设。打个生活化的比方你的手机设置了解锁密码这是身份验证但也有一种老式的拉杆箱密码锁本身就是密码值只要拨到正确的数字就开了不需要额外身份。这道题的flag就是放在一个密码极其简单的拉杆箱里只要把拨片拨到预设位置whatflag箱子就“咔嗒”一下弹开了。这个设计看起来幼稚但放到真实的Web系统里就变了一个形态。例如开发者偷偷在后台写了一个“紧急开关”接口if ($_GET[debug] true) { // 输出敏感日志或配置信息 }没有做登录限制还顺手把接口地址写死在内部文档里。攻击者只要拿到这个参数名和值就能反复调用读取大量内部信息。CTF中很多“弱口令、硬编码、隐藏参数”的考点本质上都是在暗示你系统里存在某个“只检查参数值不检查访问者身份”的暴露面找不找得到它只是时间问题。3. 实操过程从阅读代码到拿到flag3.1 进入题目入口与代码分析打开Bugku定位到Web方向的GET题点进去。初始页面比较简陋通常就是把PHP代码直接渲染在页面上。虽然不是每次看到的外观都一样但核心代码基本就是咱们前面提到的那段内容。看到代码后第一件事不要急着改URL先把代码读透。代码里有几个关键元素值得注意$_GETPHP里的超全局数组表示所有以GET方法传进来的参数。what参数名也就是你需要在URL里构造的键。 flag值比较要求参数的内容等于字符串flag。如果你对PHP有一定了解可能还会注意到题里用的是而不是这中间有弱类型比较的幺蛾子但在这道题里你老老实实传字符串flag就能过不用去研究数组绕过的骚套路。考试里出题人故意留了个“可以用简单方法解决”的注脚你非要炫技去研究复杂解法能解但对拿分没帮助性价比太低。3.2 构造参数在URL上动手接下来就是动手环节。假设题目页面的URL是http://xxx.bugku.com/web/get/index.php我要做的就是在末尾拼上查询字符串。先用最保守的写法http://xxx.bugku.com/web/get/index.php?whatflag输入完地址后回车页面重新加载。此时服务端从URL里提取what参数取出值flag与字符串flag做相等判断条件成立flag就被输出到屏幕上。如果你用的是Linux或者Mac的终端也可以直接用curl来获取curl http://xxx.bugku.com/web/get/index.php?whatflag为什么加引号因为终端会把符号当成特殊字符处理如果URL里还有多个参数会出问题。虽然这道题只有一个参数问题是顺手把加引号的习惯养好后面刷复杂题就少踩坑。执行完curl后终端里会返回整个响应体flag通常就在其中某个位置。如果返回的内容是一大段HTMLflag会被夹杂在里面。最简单的定位办法是把响应复制到编辑器里直接搜索flag或者Key、CTF这类关键词基本一找一个准。也可以用grep在终端里过滤curl -s http://xxx.bugku.com/web/get/index.php?whatflag | grep -o flag{[^}]*}这条命令的含义是静默模式请求URL然后管道给grep用正则匹配以flag开头、以}结尾的字符串并打印出来。这种正则匹配的思路在后续刷题里会非常常用因为flag往往淹没在一大堆HTML标签中需要精确提取。如果你用的是Pythonrequests库会更方便编写脚本特别是遇到需要循环遍历参数值的情况import requests url http://xxx.bugku.com/web/get/index.php params {what: flag} resp requests.get(url, paramsparams) print(resp.text)这里要注意requests库会自动帮你把params字典序列化成查询字符串追加到URL末尾所以你自己不需要手动拼问号和等号。如果用代码手拼URL字符串遇到参数值里包含特殊字符时容易出错交给库函数处理会更省心。3.3 拿到flag之后的核对flag拿到手之后不要急着提交按CTF平台的常规流程需要把它原样填到提交框里。这里有一个细节特别重要flag的格式和大小写都不能错有些平台的flag是大写的FLAG有些是混合大小写而且大括号里的随机字符串一个字符都不能多、不能少。如果直接复制粘贴还要小心文本前后有没有多余的空格很多平台会直接判定“答案错误”反馈很模糊让你误以为自己找错了地方。我见过不少同学在这道题上“卡”了十几分钟不是因为不会传参数而是因为页面明明已经输出了flag他却没看到一直盯着初始代码页面发呆。原因可能是浏览器缓存了旧的响应也可能是他在滚动页面时把flag那个区域划过去了。所以做题时要养成一个习惯每次构造完请求都用浏览器开发者工具或者抓包工具确认一下真正的响应内容别被页面上的静态部分干扰。4. 常见问题与新手踩坑实录4.1 参数传了但拿不到flag的五种情况第1种URL写错位置。有的新手手一抖把问号拼到了域名前面变成了http://?whatflag/xxx这样的格式浏览器会直接报错或者把问号当成路径的一部分发出去后端自然解析不到。遇到这种情况先把URL拆开看四段协议、域名、路径、查询字符串逐段核对。第2种参数名拼错了。题里写的是what你传成whats、What、what大写开头都会出问题。PHP的$_GET数组对键名是大小写敏感的所以What和what会当成两个完全不同的参数。只要键名对不上$flag就不会被输出。第3种参数值写错类型。有些变体版本的题目里如果要求的是数字你却传了字符串可能会因为弱类型比较绕进去。但这条题本身没这个坑反而是有些同学会故意把flag写成flag的十六进制编码、URL编码画蛇添足。在你对漏洞原理有足够把握之前先老老实实按题目字面要求来读代码时看见要求什么值就传什么值。第4种浏览器缓存。同一个URL第一次请求后浏览器会将响应缓存。如果之后再修改URL参数但浏览器没重新发起请求你看到的还是旧页面看起来就像“参数改了没效果”。解决办法是强制刷新CtrlF5或换成无痕窗口或者直接用curl来请求绕开缓存干扰。第5种页面编码导致flag不可见。极少数情况下响应里的flag是以Unicode转义序列或HTML实体形式输出的。如果你用浏览器直接打开会看到一个正常的字符但用curl拿到源码看到的可能是\u0066\u006c\u0061\u0067这类格式。遇到这种情况不要慌这是编码层面的展示问题解码后就是正常的flag字符串用Python的encode/decode或者在线转码工具都能处理。4.2 工具使用与浏览器操作中的细节做这类题目很多人会直接用Burp Suite改包这个思路没问题但有个常见的初学者误区把GET请求改错位置。Burp的Proxy面板里GET请求的第一行是请求行形如GET /index.php?whatflag HTTP/1.1。如果你要修改查询参数改的是请求行里路径后面的部分而不是Host头或者别的字段。有同学会把参数加在URL末尾时漏掉问号或者加在路径中间服务端自然解析不到。另一个细节是用浏览器地址栏手动修改URL时如果参数值里含有特殊字符#、、%、空格等浏览器会自动进行URL编码。本题里的flag是普通字母没有这个问题但如果你以后去刷别的题遇到参数值里有花括号、加号、斜杠之类记得手工对它们做URL编码。比如花括号是%7B和%7D空格可以写成或%20。这里有个原则URL编码不影响服务端解码后的实际值PHP的$_GET会自动解码一次所以你编码完的值最终还是会被还原成正常字符再参与比较不必担心编码后会改变语义。还有一个常用的小技巧如果你不确定自己传的参数是不是对了打开开发者工具的Network面板刷新页面后找到那条文档请求点击查看Headers里的Query String Parameters那里会以键值对形式清楚列出你传出的所有参数。很多看起来“玄学”的问题在这个面板里一眼就能看出答案。4.3 如果题目换了形式怎么办论坛和博客上有不少关于这道题的分享但不同人遇到的版本可能略有出入。有人分享时会说“直接访问?whatflag”有人会说“先输入账号密码再跳转”还有人碰到的其实是POST题只是标题里带了GET导致误解。碰到这种情况最忌讳的是照搬教程里的URL参数而是应该回到代码本身。不管是GET还是POST核心永远是一句话服务端校验了什么条件你把对应的参数用对应的方法传过去就行。比如有的平台会把这道题的flag藏在响应头里你光看响应体看不到。那我的建议是用curl的-i参数或者Burp的Raw选项卡把响应头一并打出来看。响应头里的X-Flag、Set-Cookie、自定义Header都可能是出题人藏宝的位置。另外一个容易忽略的点是查看源码时不要只盯着网页本身的渲染结果按CtrlU查看网页源代码或者在开发者工具里看NetWork的响应原文这些都比浏览器的渲染视图更接近服务端实际返回的内容。很多时候flag就躺在源码里被样式表藏起来了。5. 从题目到实战这类漏洞的防御与延伸5.1 防御侧为什么“猜参数”就能拿权限的设计很危险这道题的漏洞源头就一句话后端把“参数值正确”当成了“访问者有权限”的充分条件。真实系统里这种问题比比皆是比如后台有一个report.php判断$_GET[token]是否等于硬编码的abc123等于就允许导出报表。管理接口只检查URL里的isAdmin1不考虑这个值是不是客户端自己塞进去的。下载文件的接口用fid参数定位文件却不对文件归属做校验导致可以任意下载他人的文件。防御方法其实不复杂核心就三条第一权限判断必须基于服务端可控的身份凭证。Session、签名Token、OAuth票据这些都行但绝不能把“是否管理员”“是不是本人”这类布尔值放在前端可修改的参数里。第二对于任何涉及敏感数据的接口哪怕你有Session也要按最小权限原则检查“该用户是否真的可以访问这条资源”。比如查询订单不能只看用户是否登录还要看订单的user_id是否等于当前登录用户的ID。第三敏感操作必须加二次校验。比如“修改手机号”“导出全部用户信息”这类动作除了常规Token还要配合验证码、操作确认、审计日志。这道CTF题是让你“找flag”但同样的代码放到生产环境里就是一次实打实的数据泄露事件。刷题时留个心眼以后自己写代码时遇到类似的判断至少能唤起一点警觉凡是写上“if (参数 硬编码值) 就放行”的代码都应该被拉去code review。5.2 延伸考点如果后端不是GET会怎样学会了GET传参最自然的延伸是POST传参。很多入门新手以为这是一个翻倍的理解成本其实原理完全一样只是参数位置从URL挪到了请求体里。比如服务端代码是if (isset($_POST[what]) $_POST[what] flag) { echo $flag; }那你就不能用URL上的?whatflag来解了需要用Burp的Repeater把请求方法改成POST然后在请求体里写whatflag。curl的写法是curl -d whatflag http://xxx.bugku.com/web/post/index.php你会发现整个思维链路完全复用。这也是我为什么一直强调做题不要只背解法要理解HTTP请求的组成请求方法、URL、请求头、请求体、响应体。搞懂了这几块GET、POST、Cookie注入、请求走私、CSRF你拿到任何一道题都能快速定位该对哪一部分做文章。再往后还有一点值得提很多系统的真实漏洞并不在“参数名很明显”的地方而是藏在JS文件、注释、接口文档、旧版本接口里。做CTF时你会习惯性地去看源代码、看JS、看响应头这些习惯放到真实项目里完全适用。所以从这第一道GET题开始把“多翻源码、多观察响应细节”的肌肉记忆练出来会对你后续刷题和做项目帮助很大。5.3 这道题还能怎么变着刷如果不想只停留在“做一遍拿flag”的层面可以自己给自己加戏。比如你可以写一个简单的字典把what的参数值从flag换成admin、true、1、debug、test之类的常见值看看会不会有意外输出。这种做法在真实测试里叫参数枚举很多接口的隐藏开关就是这么被发现的。如果题目允许你改包你还可以试试把请求方法从GET改成POST、HEAD、OPTIONS看看服务端的响应有没有变化。有时候一个接口会同时兼容多种请求方法而不同方法对应的代码逻辑还不一样这就可能引出新的漏洞点。这种“改改看”的探索方式比单纯为了拿flag要有趣得多也是从“会做题”走向“会挖洞”的必经之路。另外建议你做完之后把这道题的源码自己复制到一个本地PHP环境里跑一遍加一些输出语句比如把$_GET数组直接print_r出来看看服务端到底收到了什么。这样能帮你把“浏览器侧的URL”和“服务端收到的参数”这两件事彻底对应起来理解会更扎实。时间关系先把这道GET题讲到这儿。最后说一个我自己的小习惯每次刷完这种入门题我会把攻击路径的每个环节都梳理一遍从客户端怎么发请求、服务端怎么接收参数、到最终数据怎么流出然后把“若在真实系统中这个环节缺了什么校验”写在旁边。上面这道题我写的备注只有一句话参数值永远不能代替权限判断。就是这句话让我后来面试讲越权漏洞时格外有底气。你如果能从这一道题里读出这层意思那这个flag拿得就值了。