从Cookie伪造到权限绕过:CTF“需要管理员”解题思路与Web安全启示
2026/9/16 2:31:46 网站建设 项目流程

1. 题目整体拆解:这题到底在考什么

1.1 题目描述与核心考点

Bugku平台上的Web题“需要管理员”,光看名字就透着一股“权限”的味道。在CTF的Web方向里,凡是跟“管理员”沾边的题目,十有八九是在考身份认证绕过和权限校验逻辑。这道题在Bugku那里属于新手进阶的典型题目,它不涉及复杂的注入或者反序列化,而是把重点放在了一个特别容易被新手忽略的地方——Cookie。

我第一次做这道题的时候,以为是要爆破后台密码,或者找一个隐藏的admin页面。结果打开题目页面一看,整个页面上只有一个孤零零的按钮,点击之后页面提示你不是管理员,无法执行操作。这时候再扭头看URL和响应头,问题就浮出水面了。这道题考察的核心,就是你能不能意识到“服务端校验身份”这件事,在很多入门级Web应用里,其实只依赖于一个客户端可以随意修改的Cookie字段。

我把这类题归为“会话伪造入门题”。它的意义不在于题目本身有多难,而在于它把你从“只会盯着页面看”的思路,拉到了“要去看请求、看响应、看存储”的层面。如果你能独立把这道题做出来,那你在Web安全这条路上就算是真正迈过了第一道门槛。

1.2 预备知识:HTTP无状态与身份认证的底子

在动手解题之前,有几个基础概念必须理清楚,不然你就算跟着教程把flag拿到了,换一道题照样抓瞎。

HTTP协议是无状态的。这句话你肯定听过无数遍,但它的真正含义是:服务器默认不记得“你是谁”。每一次HTTP请求都是一次全新的握手,服务器不会因为你上一次请求时输入过密码,就默认你这一次也是合法用户。那网站是怎么记住用户登录状态的呢?靠的就是会话机制。常见的有Cookie、Session、Token这几大类。在CTF入门题里,Cookie和Session是出镜率最高的两个东西。

Cookie是服务器下发并存储在客户端(浏览器)里的一小段数据,每次请求时浏览器会自动把它带在请求头里发给服务器。Session则是服务器端保存的一份会话记录,通常通过一个SessionID与客户端的Cookie进行关联。这里就出现了一个非常经典的逻辑漏洞:如果服务器只是通过读取Cookie里的某个字段(比如admin=0)来判断你是不是管理员,而你又能随意修改这个字段的值,那“成为管理员”这件事,就只是改一个数字的问题。

我见过很多零基础的朋友,一上来就直接开Burp Suite去改包,结果连“为什么要改这个参数”都没搞明白。所以说,做这类题之前,先把Cookie的机制吃透,比背十道题的flag都管用。

2. 信息收集阶段:三分做题,七分看源码

2.1 第一步:打开页面先看结构

进入题目环境之后,我习惯先不着急点任何按钮,而是先按F12打开开发者工具,依次看三个地方:Elements(网页源码)、Network(网络请求)、Application(存储区)。

网页源码里可能藏着注释掉的提示、隐藏的表单字段、或者外部引入的JS文件。有时候出题人会故意留点“废渣”在源码里,看起来像是忘记了清理,实际上是在给解题指路。比如某个输入框的value属性里写死了一个用户名,或者某段JS代码里硬编码了一个跳转链接。这些细节丢在源码里不显眼,但对解题方向有很强的提示作用。

Network面板记录的是页面加载过程中所有的HTTP请求。你要关注的是:有没有额外的接口请求?响应状态码是200还是302?有没有重定向?如果点击按钮之后页面没有发生变化,那多半是前端JS在拦截,或者请求发出去了但返回的数据被隐藏了。这时候就要去Network里看具体发出去的请求包和返回包。

Application面板是Cookie和LocalStorage的住所。我在这里最常干的事情,就是直接去看有没有已经存在Cookie。有的题目比较“善良”,在第一次访问时就已经给你下发了一个Cookie,字段值写的是user=0,或者admin=false之类的。看到这种东西,基本就等于直接告诉你:改这里就能过关。

2.2 第二步:Cookie里的秘密

Cookie这玩意儿,在安全圈子里有个很经典的比喻:它就像你进停车场时领到的一张卡片,上面写着你是“普通车主”还是“VIP车主”。停车场保安(服务器)不核对你的车牌,也不看你的脸,只认你递过去的卡片上写了什么。那你想想,如果卡片上的字可以用笔自己改,会发生什么?

在实际解题过程中,我遇到过的Cookie字段五花八门。有的叫admin,值是0或者1;有的叫user,值是普通用户的名字;有的长得像一串加密字符或者一个数字ID。遇到加密字符,先别急着放弃——很多题目所谓的“加密”只是Base64编码,复制出来用工具解码一下,里面可能就写着admin或者某个用户名。把解码结果改一改再编码回去,放回Cookie里发给服务器,搞不好就直接变成管理员了。

另外还要注意一个点:Cookie的取值不一定只有字符串和数字。有些题目会在Cookie里放一个JSON结构,比如{"username":"guest","role":"user"}。这种就更明显了,把role字段改成admin,甚至把username改成admin,都是常见的解法。总之,看到Cookie结构越复杂,越说明出题人想让你改的就是它。

2.3 第三步:结合后端逻辑做推断

很多新手做题做到一半就卡住了,原因不是想不到要改Cookie,而是改了之后不知道有没有生效。这时候就需要顺着请求流程去推断后端代码大概长什么样。

我们先假设题目的后端代码大概是这样的(用伪代码描述一下):

# 伪代码,非题目真实代码 def get_user_role(request): cookie = request.cookies.get("user") if cookie == "admin": return "admin" else: return "guest" def index(request): role = get_user_role(request) if role == "admin": return "flag{you_are_admin}" else: return "你不是管理员,无法查看flag"

你看,这段逻辑里根本没有Session校验,没有数据库查询,完全信任Cookie里user字段的值。凡是值不等于admin的,统一按guest处理;而值等于admin的,直接放行。这种代码在真实生产环境里是灾难,但在CTF题里却是教学利器——它告诉你,服务端在做权限判断时,必须依赖不可被客户端篡改的数据源(比如Session),而不是Cookie里的明文字段。

当然,真实题目不一定只有一层判断。有些题会先校验Cookie里是否存在某个字段,再校验这个字段的值是否符合预期,甚至还会校验几个字段之间的匹配关系。所以你在做题时,不能只盯着一个点,要系统地看:整个请求链路里有哪些可控的参数,哪些参数被服务端读取之后会影响返回结果。把这些点画成一张“输入影响表”,你心里就有底了。

3. 完整解题实操:改Cookie骗过服务器

3.1 方法一:浏览器开发者工具直接改

这是最直观、也最适合新手练手的方式。打开题目页面后,按F12进入开发者工具,切到Application面板,在左侧找到Cookies,点击题目域名的那个节点,右侧就会显示出当前站点存储的所有Cookie。

如果当前没有Cookie,那也没关系。你可以先在Console里执行一句JS,手动种下一个Cookie:

document.cookie = "user=admin; path=/";

这句代码的意思很直白:在当前域名下设置一个名为user、值为admin的Cookie,并且让它在整个路径下都生效。设置完之后,刷新页面,再点那个按钮,看看页面内容有没有变化。

如果页面上原本就有Cookie,比如user=guest,那你直接双击Value那一栏,把guest改成admin,回车保存,然后刷新页面即可。这里有一个容易踩的坑:有些题目的Cookie字段名不叫user,也不叫admin,而是叫admin_name、user_role、is_admin、login_name之类的。你要是只改值不改字段名,改半天也没用。所以先仔细看一眼现有Cookie的字段名,再决定怎么改。

提示:修改Cookie之后,如果发现没生效,先按F12到Network面板里看看请求头。确认一下请求里Cookie这一行的值是不是真的变成你设置的内容了。有时候浏览器缓存会让页面看起来“没变化”,但实际上请求已经带上新Cookie了。遇到这种情况,勾选Network面板的“Disable cache”,再强制刷新(Ctrl+Shift+R)一次。

3.2 方法二:用Burp Suite抓包改请求

用浏览器开发者工具改Cookie虽然快,但如果你想真正理解HTTP请求的结构,还是建议用Burp Suite走一遍抓包改包的流程。这也是后续所有Web方向题目的基本功。

步骤很简单:

  1. 打开Burp Suite,确认Proxy模块的Intercept开关是打开的(默认是On)。
  2. 在浏览器里配置代理,指向127.0.0.1:8080(Burp默认监听端口)。
  3. 访问题目页面,点击那个触发请求的按钮。
  4. 回到Burp,会看到一个被拦截的HTTP请求。找到请求头里的Cookie那一行。
  5. 如果请求头里没有Cookie,就直接在请求头的位置加一行:Cookie: user=admin
  6. 如果有现有Cookie,就修改对应字段的值。
  7. 点击Forward放行请求,让请求到达服务器。

改完之后,在Burp的HTTP History里能看到返回包。如果返回的响应体里出现了flag,那就说明数据库里的用户验证已经通过(其实就是骗过了服务端)。

使用Burp的好处是,你可以不依赖浏览器的Cookie管理机制,直接手动构造任意Cookie。这在遇到一些“必须同时修改两个Cookie字段”的题目时特别有用。比如服务端同时读取user和role两个Cookie,你必须把这一对字段的值都改对了,才能拿到flag。在Burp里你可以直接对请求包做整体编辑,比在浏览器里一个个改要高效得多。

3.3 方法三:用Python脚本模拟请求

如果你平时写代码比较多,或者想把这个过程自动化,用Python的requests库也是完全可以的。下面是一段非常简短的示例脚本,直接模拟登录和修改Cookie的过程:

import requests url = "http://xxx.xxx.xxx.xxx/" # 替换为题目地址 # 第一次请求,不带Cookie resp = requests.get(url) print("第一次访问状态码:", resp.status_code) print("页面内容片段:", resp.text[:200]) # 第二次请求,携带伪造的Cookie cookies = {"user": "admin"} resp2 = requests.get(url, cookies=cookies) print("伪造Cookie后的页面内容:", resp2.text)

注意,这里的关键在于cookies参数的格式:它是一个字典,键是你想设置的Cookie字段名,值是对应的内容。requests库会自动把字典转换成Cookie字符串放到请求头里发送出去。

如果你发现修改一个Cookie还不够,可以再试试同时设置多个:

cookies = { "user": "admin", "role": "admin", "admin": "true" }

有些题目服务端会同时读取多个Cookie字段,然后做一个逻辑判断,比如:

if username == "admin" and role == "admin" and token == "secret": flag()

这时候你就得多试几种组合。我在做题时常用的做法是先猜字段名(admin、user、role、username、isadmin),再猜字段值(admin、true、1、guest改admin),最后用脚本批量跑一遍所有组合,几秒钟就能出结果。

3.4 实操记录:从被拒到拿flag的完整路径

我拿这道题举个例子,还原一下完整的实操现场。我第一次访问题目页面时,看到的是一个简单到不能再简单的页面,居中位置有一个按钮,按钮旁边写着“普通用户”。点击之后,弹出来的内容是:“只有管理员才能执行此操作!”

我先看了一下Network面板,发现点击按钮触发的请求是这样的:

GET /flag HTTP/1.1 Host: xxx.xxx.xxx.xxx Cookie: user=guest

响应是这样:

HTTP/1.1 200 OK Content-Type: text/html 你不是管理员,无法查看flag

看到没有,请求头里带着一个明文的Cookie,值就是guest,而且整个请求没有SessionID,也没有Token。也就是说,服务器判断身份的线索,完全依赖这个Cookie字段。

于是我在开发者工具里直接把Cookie改成user=admin,刷新页面再点按钮。这一次响应直接变成了:

HTTP/1.1 200 OK Content-Type: text/html flag{xxxxxxxxxxxxx}

整个过程前后不超过三分钟。你别觉得这个过程太简单,它的意义在于:你第一次通过“修改请求数据”的方式,完成了一次对服务端权限校验逻辑的绕过。这个思路在后面的SQL注入、逻辑漏洞、越权漏洞里都会反复用到。

4. 常见问题与排查技巧实录

4.1 改完Cookie没反应?先排查这几个点

“我明明把Cookie改成admin了,为什么页面还是说不是管理员?”——这个问题我在各个CTF交流群里见到过无数次。根据我的经验,九成以上都是下面这几个原因之一。

第一,Cookie字段名猜错了。你光顾着把值从guest改成admin,却没注意到服务端可能不认user这个字段,它实际读取的字段是username。把请求头里的Cookie改一下,多加一个字段进去试试,比如Cookie: username=admin; user=admin; role=admin,这么做是通过增加覆盖范围来试探服务端到底读哪个字段。

第二,服务端可能做了额外的校验。有些题目会校验Cookie里的某个值与URL参数中的某个值是否一致,或者校验Cookie里是否有一个时间戳字段与当前时间是否匹配。你只改了用户名,没改其他字段,自然会被拦截。排查方法是多看几个请求包的交互变化,尤其是页面里有没有传一些看起来很随机的字符串,那可能就是校验用的token。

第三,浏览器缓存了旧页面。修改Cookie之后页面看上去没变,可能是本地缓存导致你看到的还是旧版本。处理办法是强制刷新一次,或者直接无痕模式下重开一遍。

4.2 这道题的兄弟题:权限越界、JWT、弱口令

“需要管理员”这道题做熟之后,你会发现在Bugku平台以及各大CTF比赛里,还有很多跟“管理员”有关的变体题。

Bugku的“网站被黑”就是典型的后台爆破题,考的是弱口令和字典扫描,暴力猜解管理员后台路径以及弱密码。“头等舱”那道题则是典型的HTTP头分析题,服务端要求请求必须带有特定的请求头字段,比如X-Forwarded-For或者User-Agent,才能拿到flag。这些题和“需要管理员”都有一个共同点——服务端在判断“你是谁”这件事上过于信任来自客户端的信息,只不过信任的对象从Cookie变成了URL参数、请求头、甚至是Token字符串。

再往外延伸,还有一类更高级的题目考的是JWT伪造。JWT(JSON Web Token)是一种结构更复杂的Token,它由Header、Payload、Signature三部分构成。出题人有时候会故意把签名算法设置为none,或者把密钥设置为空,让你可以自己构造任意身份。如果你已经把Cookie伪造玩明白了,再去学JWT伪造,上手速度会快得多,因为你已经理解了同一件事:客户端可控的数据,永远不能作为安全判断的唯一依据。

4.3 从CTF到实战:开发时如何避免这类漏洞

说到这里,必须泼一盆冷水。CTF里这种“改Cookie变管理员”的漏洞,在真实世界里是绝对的高危漏洞。如果你将来写代码时也这么干,那你的站基本等于裸奔。

避免这类问题的核心原则只有一条:权限判断必须依赖服务端持有的数据,不能依赖客户端提交的数据。也就是说,判断用户是不是管理员,应该去查服务端的Session里的角色信息,或者去数据库里查这个用户ID对应的角色,而不是读Cookie里的一个字段。

如果你用的是框架开发,比如Django、Flask、Spring、Express这些,框架自带的Session机制通常已经帮你规避了这类问题。SessionID会存成HttpOnly、Secure的Cookie,用户无法通过修改Cookie内容来直接篡改会话数据,因为真正的角色信息存在服务端。

但要注意,框架不是万能的。有些开发者为了方便,会把用户角色直接塞进Cookie里,或者把用户ID放在URL参数里,然后在后端拿这个ID去查权限,这就会引发越权漏洞。正确做法是:用户ID最好从Session中获取,角色信息每次请求时都从数据库查询一次(或者用缓存,但要做好缓存刷新机制),至少不要直接信任Cookie里的明文角色值。

我在带新人做项目的时候,经常出一到两道这类安全测试题。做完之后,我会要求他们去阅读自己项目里的用户认证代码,找出所有把权限判断建立在客户端数据上的位置。这个练习做上两三遍,他们就会养成一种“不信任输入”的思维习惯,这才是做Web安全最重要的一课。

4.4 解题避坑技巧速查表

下面这张表是我在刷Bugku以及其他CTF平台时总结出来的,按“现象-原因-对策”的方式列出来,希望对你有帮助。

现象可能原因处理对策
改了Cookie刷新后无变化字段名错误或服务端读取其他字段同时添加user、username、role、admin等字段
页面提示不是管理员,但内容有变化服务端要求多个条件同时成立检查是否还有URL参数或请求头需要修改
请求里没有Cookie服务器尚未下发,或题目需要自行构造直接在请求头手动添加Cookie字段
Cookie值看起来像乱码可能是Base64或其他编码解码后修改,再编码放回
改完Cookie直接跳转404管理员权限下访问了不存在的路径检查题目页面的请求路径,确认是否是多步操作
返回的内容是在JS里渲染的服务端返回数据但被前端逻辑判断拦截看JS代码,或直接看Network里响应体的原始内容

4.5 延展思考:权限控制还有哪些坑

做完“需要管理员”这道题,建议你一定再想一个问题:如果是真正的后台系统,管理员权限的含义其实是很宽的。有些管理员只能管内容,不能管用户;有些管理员只有某个模块的权限。一旦权限控制只做一层“是不是管理员”的判断,就很容易出问题。

CTF里经常考的水平越权和垂直越权就是从这里来的。水平越权是你以普通用户身份去操作另一个普通用户的资源,比如修改订单、查看隐私信息;垂直越权是普通用户去执行管理员才能执行的操作。这两种漏洞在真实业务系统里非常常见,因为它们往往不是因为“没有登录校验”,而是因为“登录校验之后,没有对每次操作做二次权限校验”。

所以这道题虽然简单,但它其实是一个引子,引出一条关于权限控制的完整学习路径。做题的时候,可以顺手把OWASP API Security Top 10里关于对象级授权失效(BOLA)和功能级授权失效(BFLA)的条目读一读,你会发现自己对这道题的理解会突然“升值”。

做个小小的查漏补缺:当你在浏览器里手动添加Cookie后,建议先清一次所有Cookie再测试,因为有些题目环境会在Session里缓存你的第一次访问状态,导致你改了Cookie之后,服务端拿到的还是旧状态。无痕模式是做题神器,这是我在一次比赛现场用真实代价换来的经验——当时整个界面都卡在“不是管理员”的提示上,我怀疑人生怀疑了十分钟,最后发现是浏览器之前残留了一个guest的Cookie,一直覆盖我手动设置的admin值。从那以后,我只要做Web题,一律无痕窗口起步。

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

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

立即咨询