[网络安全学习篇54]:SQL注入(一)
引言:我的系列博客[网络安全学习篇]上线了,小编也是初次创作博客,经验不足;对千峰网络信息安全开源的视频公开课程的学习整理的笔记整理的也比较粗糙,其实看到目录有300多集的时候,讲道理,有点怂了,所以我就想到了通过写博客(课程笔记)的形式去学习它,虽然写博客会让我多花几倍的时间去学习它,但是当我完成一篇博客所获得的成就感和你们对于我的认同感,让我很满足,能够鼓励我一天天的坚持下去,也希望和我一起学习本期视频的"同道"们也能给一直坚持下去。我们大家一起加油。由于作者本身也是网络信息安全小白,大部分知识点都是初次接触,出现对其理解不深入,不完整,甚至也会出现错误有问题的地方,希望大家谅解、留言提出指正,同时也欢迎大家来找我一起交流学习!!!
往期博客:
第一阶段:
[网络安全学习篇1]:windowsxp、windows2003、windows7、windows2008系统部署(千峰网络安全视频笔记)
[网络安全学习篇24]:漏洞与木马(千峰网络安全视频笔记 p117-p118)
第二阶段:
第三阶段:
[网络安全学习篇42]:靶场环境搭建(ubuntu系统安装优化及vulhub安装)
[网络安全学习篇43]:PHP基础+变量 运算符 流程控制语句
第四阶段:
[网络安全学习篇54]:SQL注入(一)(本篇)
下期博文:
目录
·mysql 元数据数据库information_schema
SQL
简介
SQL 结构化查询语言,是一种特殊的编程语言,用于数据库中的标准数据查询语言。美国国家标准学会对SQL进行规范后,以此作为关系式数据库管理系统的标准语言。
MYSQL ACCESS MSSQL orcale
明显的层次结构
库名|表名|字段名|字段内容
不过个中通信的数据库系统在其实践过程中独对SQL规范做了某些编改和扩充。所以实际上不同的数据库系统之间的SQL不能完全通用。
SQL注入(S)是一种常见的Web 安全漏洞,攻击者利用这个漏洞,可以访问或修改数据,或者利用潜在的数据库漏洞进行攻击
SQL注入基础
漏洞原理
针对SQL注入的攻击行为可描述为通过用户可控参数中注入SQL语法,破坏原有SQL结构,达到编写程序意料之外结果的攻击行为。
其成因可归结为以下两个原理叠加造成:
1、程序编写者在处理程序和数据库交互时,使用字符串凭借的方式构造SQL语句。
2、未对用户可控参数进行足够的过滤便将参数内容拼接进入到SQL语句中。
*注入点可能的位置
根据SQL 注入漏洞的原理,在用户“可控参数”中注入SQL 语法,也就是说Web 应用在获取用户数据的地方,只要代入数据库查询,都有存在SQL 注入的可能,这些地方通常包括:
@ |
GET 数据 |
@ |
POS 数据 |
@ |
HTTP头部(HTTP请求报文其他字段) |
@ |
Cookie 数据 |
..
GPC
漏洞危害
攻击者利用SQL注入漏洞们可以获取数据库中的多中信息(如:管理员后台密码),从而脱取数据库中内容(脱库)。
在特别情况下还可以修改数据库内容或者插入内容到数据库,如果数据库权限分配存在问题,或者数据库本身存在缺陷,那么攻击者就可以通过SQL注入漏洞直接获取webshell 或者服务器系统权限。
mof提权|udf提权
分类
SQL注入漏洞根据不同的标准,有不同的分类。但是从数据类型分类来看,SQL注入分为数字型和字符型。
·数字型注入就是说注入点的数据,拼接到SQL语句中是以数字型出现的,即数据两边没有被单引号、双引号包括
·字符型注入正好相反
根据注入手法分类,大致分为以下几个类别
@ |
联合查询 |
@ |
报错注入 |
@ |
布尔盲注 |
@ |
延时注入 |
@ |
堆叠查询 |
MYSQL相关
本科主要使用*map 环境,既然要探讨SQL 注入漏洞,需要对数据库有所了解,此处以mysql 为例,这里只起到抛砖引玉的作用,其他环境的注入,读者可以根据本次的思路去学习,唯一不同的只是数据库的特性
·注释
mysql 数据库的注释的大概有以下几种
#
-- (杠杠空格)
/* … */
/*! … */ 内联查询
·mysql 元数据数据库information_schema
information_schema数据库中的几个关键的表
· 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() |
|
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加密之后的,
可以在线查询,网址:
参考文献: