<dd id="sga4y"><optgroup id="sga4y"></optgroup></dd>
<nav id="sga4y"><code id="sga4y"></code></nav>
  • <optgroup id="sga4y"></optgroup>

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

    0x00 簡介


    分布式掃描好多人都寫過,例如:

    burp的sqli插件

    Matt前輩的http://zone.wooyun.org/content/24172

    豬豬俠前輩的http://zone.wooyun.org/content/21289

    Ver007前輩的http://zone.wooyun.org/content/24333

    0x_Jin前輩的http://zone.wooyun.org/content/24341

    填上個坑填的心煩,想著也造個輪子,忙活了幾天,寫了一個簡單的雛形

    Github: https://github.com/liuxigu/ScanSqlTestchromeExtensions

    在此感謝bstaint、sunshadow的幫助

    Sqlmapapi本來就是為了實現分布式注入寫的,在被動掃描的基礎上 加節點就實現分布式了

    最初想的是用chrome插件來實現代碼注入

    1. 用js來獲取<a>標簽的同域url,用js是防止一些站的反爬蟲措施,還有對于a href指向相對鏈接的的情況,用js會自動補全域名.
    2. Chrome webRequest API OnBeforeRequest獲取即將請求的url

    設想獲取url后 喂給sqlmapapi, 將能注入的url寫入到文本里,js 的 FileSystemObject gg.. 本來是準備用php實現文件io的…

    talk is cheap show me the code.

    0x01 Chrome manifest.json


    #!js
    {  
      "name": "sqlInjectionTest",  
      "version": "0.1",  
      "description": "you know...",  
      "manifest_version": 2,
    
      "content_scripts": [{
        "matches":["*://*/*"],
        "js": ["inject.js"]
        }],
    
      "permissions": [
        "*://*/*",
        "webRequest", 
        "webRequestBlocking"
        ],
    
    
      "browser_action": {  
        "default_icon": "icon.png" ,
        "default_title": "scan url inject"
        }  
    }
    

    0x02 Sqlmapapi.py code


    一:固定Admin Id

    Sqlmapapi啟動后 是這樣子:

    #!bash
    [email protected]:~/桌面/sqlmap# python sqlmapapi.py -s
    [22:02:17] [INFO] Running REST-JSON API server at '127.0.0.1:8775'..
    [22:02:17] [INFO] Admin ID: 7c4be58c7aab5f38cb09eb534a41d86b
    [22:02:17] [DEBUG] IPC database: /tmp/sqlmapipc-5JVeNo
    [22:02:17] [DEBUG] REST-JSON API server connected to IPC database
    

    AdminID每次都會變,這樣導致任務管理不方便,我們更改一下sqlmap的源碼

    定位到/sqlmap/lib/utils/api.py 的server函數

    p1

    看到644行的os.urandom,直接改成一個固定字符串就行了

    例如 我改成了DataStore.admin_id = hexencode('wooyun')

    以后就固定是Admin ID: 776f6f79756e

    還有個更簡單的辦法

    p2

    p3

    return True

    二:sqlmap掃描任務結束自動寫入文本

    判斷當前任務是否掃描完成 訪問http://127.0.0.1:8775/admin/ss/list

    #!python
    {
        "tasks": {
            "4db4e3bd4410efa9": "terminated"
        }, 
        "tasks_num": 1, 
        "success": true
    }
    

    Terminated代表任務已終止,

    http://127.0.0.1:8775/scan/4db4e3bd4410efa9/data

    #!python
    {
        "data": [], 
        "success": true, 
        "error": []
    }
    

    “data”存放了sqlmapapi檢測時用的payload, “data”非空就代表當前任務是可注入的,sqlmapapi并沒有自帶回調方式…輪詢浪費開銷,這里我選擇修改源碼

    p4

    定位到scan_data函數 ,可以看到,假如可注入,就會從data表檢索數據并寫入到json_data_message,表名為data, 代碼向上翻,定位到將數據入庫的代碼

    在StdDbOut類里,第230行,在insert前 插入

    #!python
    with open('/tmp/'+str(self.taskid)+'.txt','a+') as fileHandleTemp,\
                        closing(requests.get('http://127.0.0.1:8775/option/'+str(self.taskid)+'/list', stream=True)) as reqTemp:
    
                            fileHandleTemp.write(
                                    json.loads(reqTemp.text)['options']['url']+'\n'+
                                    json.loads(reqTemp.text)['options']['data']+'\n'+
                                    json.loads(reqTemp.text)['options']['Cookie']+'\n'+
                                    json.loads(reqTemp.text)['options']['Referer']+'\n'
                                )
    

    記得加載三個模塊

    #!python
    import json
    import requests
    from contextlib import closing
    

    本意是獲取能注入的url寫入到文本里,在源碼里沒找到繼承這個類的地方…懶得去找了

    訪問http://127.0.0.1:8775/option/id/list

    #!python
    Response:
    {
        "options": {
            ......
            "url": http://58.59.39.43:9080/wscgs/xwl.do?smid=02&bgid=01&bj=8
            ……
            }
        "success":{
            ...
        }
    

    p5

    0x03 inject.js code


    1.

    那么要過濾掉javascript::偽協議和無sql操作的href

    看到有這樣寫的:

    #!js
    if re.match('^(javascript|:;|#)',_url) or _url is None or re.match('.(jpg|png|bmp|mp3|wma|wmv|gz|zip|rar|iso|pdf|txt|db)$',_url):
    

    甚至這樣的:

    #!js
    filename=urlpath[i+1:len(urlpath)]
    ????print?"Filename:?",filename
    ????res=filename.split('.')
    ????if(len(res)>1):
    ????????extname=res[-1]
    ????????ext=["css","js","jpg","jpeg","gif","png","bmp","html","htm","swf","ico","ttf","woff","svg","cur","woff2"]
    ????????for?blacklist?in?ext:
    ????????????if(extname==blacklist):
    ????????????????return?False
    

    這兩種方式假如遇到這樣的url: http://xxx/aaa/ ,就會造成無意義的開銷.

    判斷是否可以進行get注入測試,其實只需要

    str.match(/[\?]/); 無get參數的頁面會返回null

    我們這樣寫: /http(s)?:\/\/ ([\w\W-]+\/)+ ([\w\W]+\?)+/;

    再加上同域過濾,js中沒有php那樣可以在字符串中用{}引用變量的值,要在正則中拼接變量需要用RegExp對象:

    #!js
    var urlLegalExpr="http(s)?:\/\/"+document.domain+"([\\/\\w\\W]+\\?)+";
    var objExpr=new RegExp(urlLegalExpr,"gi");
    

    2.

    Js是在http response后執行的,要進行post注入,必須在OnBeforeRequest之前獲取,chrome提供了相關的api,這個沒什么可說的 ,看代碼吧

    inject.js code:

    #!js
    main();
    
    function main(){
    
        var urlLegalExpr="http(s)?:\/\/"+document.domain+"([\\/\\w\\W]+\\?)+";
        var objExpr=new RegExp(urlLegalExpr,"gi");
        urlArray=document.getElementsByTagName('a');
    
        for(i=0;i<urlArray.length;i++){
            if(objExpr.test(urlArray[i].href)){
                sqlScanTest(urlArray[i].href);
            }
        }
    
    }
    
    function sqlScanTest(url,payload){
    
    
        sqlmapIpPort="http://127.0.0.1:8775";
        var payload=arguments[1] ||'{"url": "'+url+'","User-Agent":"wooyun"}';
    
        Connection('GET',sqlmapIpPort+'/task/new','',function(callback){
    
                var response=JSON.parse(callback);      
    
                if(response.success){
                    Connection('POST',sqlmapIpPort+'/scan/'+response.taskid+'/start',payload,function(callback){
                            var responseTemp=JSON.parse(callback);
                            if(!responseTemp.success){
                                alert('url send to sqlmapapi error');
                            }
                        }
                    )
                }
                else{
                    alert('sqlmapapi create task error');
                }
            }
        )
    }
    
    
    function Connection(Sendtype,url,content,callback){ 
        if (window.XMLHttpRequest){ 
            var xmlhttp=new XMLHttpRequest(); 
        } 
        else{ 
            var xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); 
        } 
        xmlhttp.onreadystatechange=function(){ 
            if(xmlhttp.readyState==4&&xmlhttp.status==200) 
            { 
                callback(xmlhttp.responseText); 
            } 
        } 
        xmlhttp.open(Sendtype,url,true); 
        xmlhttp.setRequestHeader("Content-Type","application/json"); 
        xmlhttp.send(content); 
    } 
    
    
    
    function judgeUrl(url){
        var objExpr=new RegExp(/^http(s)?:\/\/127\.0\.0\.1/);
        return objExpr.test(url);
    }
    
    var payload={};
    
    chrome.webRequest.onBeforeRequest.addListener(
        function(details){ 
    
            if(details.method=="POST" && !judgeUrl(details.url)){
                var saveParamTemp="";
                for(var i in details.requestBody.formData){
    
                    saveParamTemp+="&"+i+"="+details.requestBody.formData[i][0];
                }
                saveParamTemp=saveParamTemp.replace(/^&/,'');
                //console.log(saveParamTemp);
                payload["url"]=details.url;
                payload["data"]=saveParamTemp;
            }
            //console.log(details);
        },
        {urls: ["<all_urls>"]},
        ["requestBody"]);
    
    
    chrome.webRequest.onBeforeSendHeaders.addListener(
        function(details) {
            if(details.method=="POST" && !judgeUrl(details.url)){
                //var cookieTemp="",uaTemp="",refererTemp="";
    
                for(var ecx=0;ecx<details.requestHeaders.length;ecx++){
    
    
                    switch (details.requestHeaders[ecx].name){
                        case "Cookie":
                            payload["Cookie"]=details.requestHeaders[ecx].value;
                            break;
                        case "User-Agent":
                            payload["User-Agent"]=details.requestHeaders[ecx].value;
                            break;
                        case "Referer":
                            payload["Referer"]=details.requestHeaders[ecx].value;
                            break;
                        default:
                            break;
                    }
    
                }
                sqlScanTest("test",JSON.stringify(payload));
                return {requestHeaders: details.requestHeaders};
            }
    
        },
        {urls: ["<all_urls>"]},
        ["requestHeaders"]);
    

    Sqlmap能用的選項都可以在http://ip:port/option/taskid/list里查看,用到哪項寫到payload里就行了,最好是做成實時刷新代理,之前寫過爬蟲的時候寫過一個python版的,有空的話會改成js加入到inject.js里.

    p6

    0x04 參考文獻



    p7

    馬上2016了,希望在年前把上個坑填完

    也祝各位心想事成

    97av