關於被動式掃描的碎碎念

wyzsk發表於2020-08-19
作者: pr0mise · 2016/02/05 10:10

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了,希望在年前把上個坑填完

也祝各位心想事成

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章