MySQL 数据库系统表的利用

背景

学习目标:

  1. 如何利用数据库的功能读写文件,需要什么样的条件才可以读写;

  2. 学习数据库系统表的功能,如何利用 sql 语句查询库名、表名、字段名、内容以及当前用户等基本信息;

  3. 尝试查询出用户的 hash,并使用 hashcat 来对获取的 hash 进行暴力破解。

MySQL 读写文件

读文件

使用 load_file() 函数

load_file(file_name) 函数读取文件内容,将内容以字符串的形式返回。

前提条件

  • secure_file_priv 不为 NULL,使用 select @@secure_file_priv查看其值,值不为空字符串时,只能使用该目录进行文件的读写操作, 该值的设置见附录
  • 当前数据库用户具有 FILE 权限,使用 show grants查看;
  • 系统用户 mysql 对该文件可读(要考虑系统的访问控制策略),在Ubuntu-18.04使用 MySQL 时默认的系统用户是 mysql
  • 读取文件的大小小于 max_allowed_packet,使用 select @@max_allowed_packet查看;
  • 文件存在服务器上。

如果上述任一条件不满足,函数返回 NULL 值。

用法

1
select load_file('filename');

例子:读取文件 /mysql/test.csv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
mysql> select  @@secure_file_priv,@@max_allowed_packet;
+--------------------+----------------------+
| @@secure_file_priv | @@max_allowed_packet |
+--------------------+----------------------+
| | 16777216 |
+--------------------+----------------------+
mysql> show grants;
+----------------------------------------------------------------------------------------------+
| Grants for user101@localhost |
+----------------------------------------------------------------------------------------------+
| GRANT SELECT, INSERT, DELETE, CREATE, FILE ON *.* TO 'user101'@'localhost' WITH GRANT OPTION |
+----------------------------------------------------------------------------------------------+
mysql> select load_file('/mysql/test.csv');
+------------------------------+
| load_file('/mysql/test.csv') |
+------------------------------+
| 3,Hack
4,World
5,Surprise!
|
+------------------------------+

问题:在设置 secure_file_privilege='' 且目录权限为 777load_file() 无法读取文件

解决方案:Ubuntu 自带的强制访问控制系统 AppArmor 强制限制了每个程序可使用的资源,它并非针对用户,而是针对程序的,所以不论用户是否能访问某一资源,只要程序被 AppArmor 限制访问该资源,则程序就不能访问该资源。因此需要修改 AppArmor 针对于 MySQL 的访问控制策略,修改文件 /etc/apparmor.d/usr.sbin.mysqld,在其中添加需要访问的目录, 修改完成后,重新加载 AppArmor 配置:sudo /etc/init.d/apparmor reload

参考:https://stackoverflow.com/questions/4215231/load-data-infile-error-code-13

使用 LOAD DATA 语句

laod_file()函数只能从 服务器中读取文件,LOAD DATA语句既能读取服务器的文件,也能读取 MySQL 客户机的文件,两种方式语法上稍有不同。

前提条件:load_file() 函数使用条件相同,但通过使用 LOCAL 语句从客户主机读取文件内容。

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#[] 中的内容为可选内容
LOAD DATA
[LOW_PRIORITY | CONCURRENT] [LOCAL] #使用 LOCAL 表示从客户主机读取文件,要设置 mysqld 中的系统变量 local_infile
INFILE 'file_name'
[REPLACE | IGNORE]
INTO TABLE tbl_name
[CHARACTER SET charset_name] #处理文件的字符集
[{FIELDS | COLUMNS} #描述每一列的格式
[TERMINATED BY 'string']
[[OPTIONALLY] ENCLOSED BY 'char']
[ESCAPED BY 'char']
]
[LINES #描述行的格式,不满足的行会被略过
[STARTING BY 'string']
[TERMINATED BY 'string']
]
[IGNORE number {LINES | ROWS}] #忽略指定行或列
[(col_name_or_user_var
[, col_name_or_user_var] ...)] #写到数据表中的指定列
[SET col_name={expr | DEFAULT},
[, col_name={expr | DEFAULT}] ...]

例子:读取文件 /mysql/test.csv 到已有的数据表中。CSV文件内容如下:

1
2
3
3,Hack
4,World
5,Surprise!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
mysql> select * from test;
+----+------+
| id | name |
+----+------+
| 1 | May |
| 2 | June |
+----+------+

mysql> load data infile '/mysql/test.csv' into table test fields terminated by ',';
Query OK, 3 rows affected (0.00 sec)

mysql> select * from test;
| id | name |
+----+-----------+
| 1 | May |
| 2 | June |
| 3 | Hack |
| 4 | World |
| 5 | Surprise! |
+----+-----------+
5 rows in set (0.00 sec)

写文件

使用 SELECT INTO OUTFILE 语句

SELECT INTO OUTFILELOAD DATA 这两条语句是完全互补的,一个写文件,一个读文件,语句的语法也很相似。

前提条件:

  • secure_file_priv 不为 NULL,使用 select @@secure_file_priv查看其值,值不为空字符串时,只能使用该目录进行文件的读写操作, 该值的设置见附录
  • 当前数据库用户具有 FILE 权限,使用 show grants查看;
  • 系统用户 mysql 对该文件可写(要考虑系统的访问控制策略),在Ubuntu-18.04使用 MySQL 时默认的系统用户是 mysql
  • 读取文件的大小小于 max_allowed_packet,使用 select @@max_allowed_packet查看;
  • 文件不存在。

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
#[] 中的内容为可选内容
SELECT select_expr...
INTO OUTFILE 'file_name'
[CHARACTER SET charset_name] #处理文件的字符集
[{FIELDS | COLUMNS} #描述每一列的格式
[TERMINATED BY 'string']
[[OPTIONALLY] ENCLOSED BY 'char']
[ESCAPED BY 'char']
]
[LINES #描述行的格式
[STARTING BY 'string']
[TERMINATED BY 'string']
]

例子:写数据表到 CSV 文件中。

1
2
3
4
5
6
7
8
9
10
11
12
mysql> select * from comments;
+----+---------------------------+
| id | comment |
+----+---------------------------+
| 1 | test |
| 2 | hhhh |
| 3 | Need lots of improvements |
+----+---------------------------+
3 rows in set (0.00 sec)

mysql> select comment from comments where id>1 into outfile '/mysql/comments.csv' fields terminated by ',';
Query OK, 2 rows affected (0.00 sec)

/mysql/comments.csv 内容:

1
2
hhhh
Need lots of improvements

注:

使用 SELECT INTO DUMPFILE 可将文件内容写成一行.

1
2
3
>mysql> select comment from comments where id>1 into dumpfile '/mysql/comments';
>Query OK, 2 rows affected (0.00 sec)
>

/mysql/comments.csv 内容:

1
2
>hhhhNeed lots of improvements
>

如果想把远程数据库的查询结果写到本地主机文件上, 可用:

1
2
>mysql -h hostname -P portnum -u username -p databsename -e "SELECT ..." > file_name
>

MySQL 数据库系统表

MySQL-5.7 默认的系统表/视图 放在4个数据库/schema 中,。

在 MySQL 中 schemadatabase 是一样的就不做区分, 但实际上是有区别的,可以参考这篇文章

视图也称为虚表,是为了便于查询某些信息,建立在查询结果之上的表,所以视图其实就是被保存起来的一次查询。视图和表的区别简单来说就是视图存储的是SQL查询语句执行的结果,以表的形式存在但在数据库中并没有这个表。

库名 视图数量 基表数量
information_schema 61 0
mysql 0 31
performance_schema 0 87
sys 100 1(sys_config)

MySQL 数据库管理系统中的系统表很多,这里主要介绍和在SQL 注入中常的几个表和视图。

information_schema 数据库

information_schema存放着 MySQL 服务器维护的所有数据库的详细信息, 包括数据库名、表名、列名、列的数据类型、访问权限等等。

常用视图

在进行 SQL 注入时比较有用的几个视图:SCHEMATACOLUMNSTABLESuser_privileges

schemata 视图存放 MySQL 数据管理系统中的所有数据的基本信息,丛中可以获取所有数据库名字,数据库使用的字符集、排序规则(collation,数据库中数据进行排序时的规则,升序、降序,是否忽略大小写等)

1
2
3
4
5
6
7
8
9
10
mysql> select * from schemata;
+--------------+--------------------+----------------------------+------------------------+----------+
| CATALOG_NAME | SCHEMA_NAME | DEFAULT_CHARACTER_SET_NAME | DEFAULT_COLLATION_NAME | SQL_PATH |
+--------------+--------------------+----------------------------+------------------------+----------+
| def | information_schema | utf8 | utf8_general_ci | NULL |
| def | mysql | latin1 | latin1_swedish_ci | NULL |
| def | performance_schema | utf8 | utf8_general_ci | NULL |
| def | sys | utf8 | utf8_general_ci | NULL |
| def | webdb101 | latin1 | latin1_swedish_ci | NULL |
+--------------+--------------------+----------------------------+------------------------+----------+

columns 视图存放列相关的信息,常用字段 table_schema(数据库名), table_name(表名),column_name(列名)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
mysql> desc columns;
+--------------------------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------------------+---------------------+------+-----+---------+-------+
| TABLE_CATALOG | varchar(512) | NO | | | |
| TABLE_SCHEMA | varchar(64) | NO | | | |
| TABLE_NAME | varchar(64) | NO | | | |
| COLUMN_NAME | varchar(64) | NO | | | |
| ORDINAL_POSITION | bigint(21) unsigned | NO | | 0 | |
| COLUMN_DEFAULT | longtext | YES | | NULL | |
| IS_NULLABLE | varchar(3) | NO | | | |
| DATA_TYPE | varchar(64) | NO | | | |
| CHARACTER_MAXIMUM_LENGTH | bigint(21) unsigned | YES | | NULL | |
| CHARACTER_OCTET_LENGTH | bigint(21) unsigned | YES | | NULL | |
| NUMERIC_PRECISION | bigint(21) unsigned | YES | | NULL | |
| NUMERIC_SCALE | bigint(21) unsigned | YES | | NULL | |
| DATETIME_PRECISION | bigint(21) unsigned | YES | | NULL | |
| CHARACTER_SET_NAME | varchar(32) | YES | | NULL | |
| COLLATION_NAME | varchar(32) | YES | | NULL | |
| COLUMN_TYPE | longtext | NO | | NULL | |
| COLUMN_KEY | varchar(3) | NO | | | |
| EXTRA | varchar(30) | NO | | | |
| PRIVILEGES | varchar(80) | NO | | | |
| COLUMN_COMMENT | varchar(1024) | NO | | | |
| GENERATION_EXPRESSION | longtext | NO | | NULL | |
+--------------------------+---------------------+------+-----+---------+-------+

tables 存放表相关的信息,常用字段 table_schema(数据库名), table_name(表名)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
mysql> desc tables;
+-----------------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+---------------------+------+-----+---------+-------+
| TABLE_CATALOG | varchar(512) | NO | | | |
| TABLE_SCHEMA | varchar(64) | NO | | | |
| TABLE_NAME | varchar(64) | NO | | | |
| TABLE_TYPE | varchar(64) | NO | | | |
| ENGINE | varchar(64) | YES | | NULL | |
| VERSION | bigint(21) unsigned | YES | | NULL | |
| ROW_FORMAT | varchar(10) | YES | | NULL | |
| TABLE_ROWS | bigint(21) unsigned | YES | | NULL | |
| AVG_ROW_LENGTH | bigint(21) unsigned | YES | | NULL | |
| DATA_LENGTH | bigint(21) unsigned | YES | | NULL | |
| MAX_DATA_LENGTH | bigint(21) unsigned | YES | | NULL | |
| INDEX_LENGTH | bigint(21) unsigned | YES | | NULL | |
| DATA_FREE | bigint(21) unsigned | YES | | NULL | |
| AUTO_INCREMENT | bigint(21) unsigned | YES | | NULL | |
| CREATE_TIME | datetime | YES | | NULL | |
| UPDATE_TIME | datetime | YES | | NULL | |
| CHECK_TIME | datetime | YES | | NULL | |
| TABLE_COLLATION | varchar(32) | YES | | NULL | |
| CHECKSUM | bigint(21) unsigned | YES | | NULL | |
| CREATE_OPTIONS | varchar(255) | YES | | NULL | |
| TABLE_COMMENT | varchar(2048) | NO | | | |
+-----------------+---------------------+------+-----+---------+-------+
21 rows in set (0.00 sec)

user_privileges 存放用户的权限信息。

1
2
3
4
5
6
7
8
9
mysql> desc information_schema.user_privileges;
+----------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------------+--------------+------+-----+---------+-------+
| GRANTEE | varchar(81) | NO | | | | #被授权的用户名
| TABLE_CATALOG | varchar(512) | NO | | | |
| PRIVILEGE_TYPE | varchar(64) | NO | | | | #权限名,如 SELECT、INSERT 等
| IS_GRANTABLE | varchar(3) | NO | | | | #是否授权,YES/NO
+----------------+--------------+------+-----+---------+-------+

视图信息利用

用户权限查询

  1. 获取当前用户名
1
2
3
4
5
6
selectmysql> select user();
+-------------------+
| user() |
+-------------------+
| user101@localhost |
+-------------------+
  1. 获取用户权限
1
2
3
4
5
6
7
8
9
10
11
mysql> select * from information_schema.user_privileges where grantee like '%user101%';
+-----------------------+---------------+----------------+--------------+
| GRANTEE | TABLE_CATALOG | PRIVILEGE_TYPE | IS_GRANTABLE |
+-----------------------+---------------+----------------+--------------+
| 'user101'@'localhost' | def | SELECT | YES |
| 'user101'@'localhost' | def | INSERT | YES |
| 'user101'@'localhost' | def | UPDATE | YES |
| 'user101'@'localhost' | def | DELETE | YES |
| 'user101'@'localhost' | def | CREATE | YES |
| 'user101'@'localhost' | def | FILE | YES |
+-----------------------+---------------+----------------+--------------+

注入时可以利用 information_schema 数据库进行信息查询,一般步骤有:

  1. 获取数据库名
1
2
3
4
5
6
7
8
9
10
mysql> select schema_name from information_schema.schemata;
+--------------------+
| schema_name |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| webdb101 |
+--------------------+
  1. 获取表名
1
2
3
4
5
6
7
mysql> select table_name from information_schema.tables where table_schema='webdb101';
+------------+
| table_name |
+------------+
| comments |
| test |
+------------+
  1. 获取列名
1
2
3
4
5
6
7
mysql> select column_name from information_schema.columns where table_name='test';
+-------------+
| column_name |
+-------------+
| id |
| name |
+-------------+

mysql 数据库

mysql 数据中存放的都是表,常用的表有 userdb

user表中常用字段有 userhostauthentication_string(存储密码的字段,MySQL 版本相关,低一些的版本为 password)等。

查询所有用户名、连接主机和密码哈希

1
2
3
4
5
6
7
8
9
10
mysql> select user,host,authentication_string from mysql.user;
+------------------+-----------+-------------------------------------------+
| user | host | authentication_string |
+------------------+-----------+-------------------------------------------+
| root | localhost | |
| mysql.session | localhost | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE |
| mysql.sys | localhost | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE |
| debian-sys-maint | localhost | *962236403F954BF9C92EBDBA0BC04F7CE7FCFB89 |
| user101 | localhost | *6331E4D15433B998EF878BE017AE2A0878316CCC |
+------------------+-----------+-------------------------------------------+

在进行 sql 注入使用常常使用 group_concat(col_name…) 将多个列的信息连成一句话输出,便于回显到浏览器。

如获取用户名及其密码的一条注入语句:

select group_concat(user, authentication_string) from mysql.user

用户密码爆破

MySQL 用户的密码存储方式并非明文直接存储,而是经过 hash 函数加密进行存储的,从 mysql.user中获取到 MySQL 用户密码的哈希值后,需要使用工具进行破解。

工具:hashcat

hashcat 号称最快的高级密码恢复套机(密码破解工具),支持多系统(Linux,OS,Windows),多平台(GPU,CPU,DSP等),支持多达 200 多种的 Hash 类型,支持使用同一系统的不同设备,支持分布式系统资源等,重要的是开源啊!

下载安装:

推荐在 Windows 上使用,最好不用虚拟机,这样安装最简单,而且能充分利用 hashcat 支持多设备的特点,在 Linux 上安装需要安装很多东西,而且问题特别多,除非特别需要,个人不建议。

使用:

1
./hashcat64.exe -m 300 -a 3 hashfile -o plaintxt --outfile-format=2 ?a?a?a?a?a?a

参数:

-m 300:hash 类型,200多种,详见: hashcat --help,300 选择的是 MySQL4/5 的hash

-a 3:攻击模式,总共5种,3 表示爆破模式,所有攻击模式如下表:

攻击模式
0 Straight:字典模式,从字典内容中依次选择密码候选,进行哈希计算,看是否和待破解的哈希值相同
1 Combination:组合模式,将两个字典的内容组合作为字典内容,可以通过 -k-j 选项为两个字典添加额外规则
3 Brute-force:爆破模式/掩码模式,使用掩码固定密码的字符集,减小爆破的候选密码数,掩码格式见下文。
6 Hybrid Wordlist + Mask:混合模式(字典+掩码),通过字典后接掩码的方式构建最终的密码候选。
7 Hybrid Mask + Wordlist:混合模式(掩码+字典),通过掩码后接字典的方式构建最终的密码候选,和上面的组合顺序相反。

-o pliantxt :破解后输出到文件 plaintxt

--outfile-format=2:输出文件格式,2表示只输出破解后的内容,所有可用格式如下表

输出文件格式
1 hash[:salt]
2 plain
3 hash[:salt]:plain
4 hex_plain
5 hash[:salt]:hex_plain
6 plain:hex_plain
7 hash[:salt]:plain:hex_plain
8 crackpos
9 hash[:salt]:crack_pos
10 plain:crack_pos
11 hash[:salt]:plain:crack_pos
12 hex_plain:crack_pos
13 hash[:salt]:hex_plain:crack_pos
14 plain:hex_plain:crack_pos
15 hash[:salt]:plain:hex_plain:crack_pos

?a?a?a?a?a?a:这表示密码的掩码,所谓的掩码就是通过 ?[字符集代号]… 的格式表示密码的格式,包括密码的位数和每一位密码使用的字符集。?a 表示所有的键盘上可输入的字符,6个?a表示密码有6位。hashcat 内置字符集如下:

字符集代号 字符集
l abcdefghijklmnopqrstuvwxyz
u ABCDEFGHIJKLMNOPQRSTUVWXYZ
d 0123456789
h 0123456789abcdef
H 0123456789ABCDEF
s !”#$%&’()*+,-./:;<=>?@[]^_`{
a ?l?u?d?s
b 0x00 - 0xff

还通过选项自定义字符集,自定以的字符集对应的字符集代号为 1,2,3,4:

1
2
3
4
--custom-charset1=CS  #同 -1 的短选项
--custom-charset2=CS #同 -2
--custom-charset3=CS #同 -3
--custom-charset4=CS #同 -4

如果得知密码第一位为大写字母且不包含字符的8为密码,可自定义掩码,加快解密过程:

1
hashcat -m 300 -a 3 -1 ?l?u?d hashfile ?u?1?1?1?1?1?1?1

参考

  1. SQL注入-文件操作
  2. load-file
  3. LOAD DATA 语法
  4. MySQL常用系统表汇总

附录

MySQLsecure_file_priv 变量

Property Value
Command-Line Format --secure-file-priv=dir_name
System Variable secure_file_priv
Scope Global
Dynamic No,表示不能动态使用 set 命令进行设置,只能通过配置文件设置,如在 ~/.my.
Type String
Default Value (>= 5.7.6) platform specific
Default Value (<= 5.7.5) empty string
Valid Values (>= 5.7.6) empty string
dirname
NULL
Valid Values (<= 5.7.5) empty string
dirname

该变量限制 MySQL 用户对文件的读写操作,可设置为以下值:

  1. 空字符串;
  2. 特定路径名;
  3. NULL,表示不允许文件的读写操作;

具体可在 MySQL 启动配置文件 /etc/mysql/my.cnf 中设置如下:

1
2
3
[mysql_safe] #服务器启动脚本
[mysqld] #服务器端配置
secure_file_priv = "" #设置读取文件目录为任意目录

my.cnf 文件中配置格式:

1
2
3
[group] #常用的 group 有 mysqld:MySQL 服务器,client:MySQL 客户端,mysql_safe:服务器启动脚本
opt_name #等同命令行中 --opt_name 选项
opt_name=value #等同命令行中 --opt_name=vale

不同平台的默认值:

INSTALL_LAYOUT Value Default secure_file_priv Value
STANDALONE empty
DEB, RPM, SVR4 /var/lib/mysql-files
Otherwise mysql-files under the CMAKE_INSTALL_PREFIXvalue
文章作者: BingSlient
文章链接: https://bingslient.github.io/2019/08/16/MySQL 数据库系统表的利用/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 BingSlient's Blog
打赏
  • 微信
  • 支付寶