前兩天 freebuf上的的XSS到內網的公開課很受啓發,從一個頁面到局域網,威力着實加強很多javascript
公開課上檢測內網 IP 實現方式用的是 img 標籤,加載網站的 favicon.ico 圖標,而後監聽 onload 事件,看圖片能不能加載成功簡單易用,就是太慢了 --html
瀏覽器有幾秒的嘗試容錯時間,對於媒體資源,瀏覽器限制同一域名併發請求爲個位數(通常爲 6 個),也就是說,把圖片全放到頁面上,也不能同時開始檢測,只能一點一點來,掃端口的時候會很是慢。前端
管理員在內網搭建的東西也可能沒有圖標。。。java
探測 IP 端口開放原理就是向目標發送請求,看是否有迴應。在上面的 img 中是在加載成功和失敗體現出來的。img 嫌慢也能夠試試別的嘛,通過一番搜索找到了這個,這個和這個。git
document.getElementById("testdiv").innerHTML = '<img src="http://' + ip + ':' + port + '" alt="" onerror="error_handler(' + our_scanobj_index + ');" />';
是經過加載單個 img,使用的 onerror 事件,10 秒之內觸發判斷爲 open。html 頁面不能解析爲圖片格式,因此也會觸發 onerror。github
switch(port){ case 21: src = 'ftp://' + this.id() + '@' + host + '/'; break;//ftp case 25: src = 'mailto://' + this.getid() + '@' + host ; break;//smtp ** case 70: src = 'gopher://' + host + '/'; break;//gopher case 119: src = 'news://' + host + '/'; break;//nntp ** case 443: src = 'https://' + host + '/' + this.getid() + '.jpg'; default: src = 'http://' + host + ':' + port + '/' + this.getid() + '.jpg';// getid is here to prevent cache seekings; } // ports 19,70,110,143 always return up in IE // ** if outlook is the default mail client and default newsreader in IE the request does not return anything img.src = src; setTimeout(function () { if (!img) return; img = undefined; callback( host, port, 'down',id); }, timeout);
功能比第一個多很多,嘗試多種協議,80 端口能夠探測出來77,79 被瀏覽器屏蔽,立刻觸發事件,因此誤報了。web
用 websocket 準確率很好,特殊端口作過處理沒有誤報,做者還有一篇筆記,就是 js 和 html 耦合嚴重,很差提取出來用,趁着放假乾脆造了個新輪子。ajax
最終選用的 WebSocket,聽着就高大上,非 http 的端口也能探測,文檔看的 ruanyifeng 和 MDN 查到 websocket 併發鏈接 挺高的 (255 in Chrome and 200 in Firefox),加入了一個隊列模塊shell
js 以下:js.later.js
簡單的包裝了 setInterval,超時檢測全靠它js.queue.js
隊列和併發控制js.portscan.js
端口掃描的邏輯都在這
前端界面用的是 semantic-ui 和 Vuesegmentfault
介紹js.later.js
做用比較簡單,在這裏就是一個 setTimeout 的用法,就很少寫了。
js.queue.js
結合 websocket 的高併發,很給力,能夠動態調整併發數量,鼠標鍵盤沒反應人離開後多跑點任務加快效率
使用示例(能夠在演示頁面測試)
var q = new Queue(function(task, next, timer) { // 跳過部分任務,不執行next,模擬超時 if (task % 5 === 0) return; //模擬延遲異步任務 $.get('https://httpbin.org/delay/1', function() { console.log(task); //此任務完成,繼續下一個 next(); }); }, 3); //3個併發請求 for (var i = 0; i < 15; i++) { q.push(i); } q.timeout = 10000; q.onTimeout = function(task) { console.warn('queue:timeout:' + task); } q.onfinish = function() { console.info('隊列執行完畢'); } q.start();
js.portscan.js
有三種掃描方式:scan_single
掃描單個目標scan_batch
掃描一個數組scan_range
生成列表並掃描以上三個接口均可以直接使用 scan,根據傳入參數不一樣自動選擇對應的方法去執行。
var ps = new PortScan(); ps.onscan = function(flag, task){ alert(task + '掃描完成,狀態爲:' + flag) } ps.onopen = function(task){ prompt('開放端口', task) } ps.onfinsh = function(){ alert('scan完成') } // 分別執行如下三個方法 // 探測單個目標 ps.scan('baidu.com'); // 批量探測 ps.scan(['baidu.com:22', 'baidu.com:443', 'baidu.com:1024']) // 生成一段地址並探測 ps.scan('baidu.com:*', 75, 85)
使用 WebSocket 發送請求的核心方法:
// 使用websocket探測端口 PortScan.prototype.wscan = function (target, callback) { var _this = this; var ws = new WebSocket(this.wsprotocol + target); ws.onerror = ws.onopen = function (e) { stopTimer(); _this.portstate('open', target, callback); } var workerkiller = function (flag) { stopTimer(flag); ws.onerror = null; ws.close(); // 若是是隊列控制超時此處就再也不執行next callback = flag === 'worker_timeout' ? null : callback; _this.portstate(flag, target, callback); } var stopTimer = this.timeoutexit(workerkiller); return workerkiller; }
wscan接收兩個參數,target 是目標地址,callback 是回調方法,請求結束後會把掃描結果傳入此方法。新建 WebSocket 請求後,在 ws 對象上設置了 ws.onerror 和 ws.onopen 事件。
想要成功創建鏈接須要服務器端先回應HTTP/1.1 101 Switching Protocols
,若是被掃描的端口開放而且返回了數據,數據格式和 WebSocket 不一致會觸發 onerror 事件,成功創建鏈接後則觸發 onopen。
workerkiller 方法用來在超時後中止當前這個請求繼續等待端口響應,首先清空 onerror 事件,而後執行 ws.close() 關閉鏈接。
var stopTimer = this.timeoutexit(workerkiller);
timeoutexit 調用的是 js.later.js 裏的方法。根據設置的超時時間(默認設置的 5 秒),啓動 workerkiller 中止此請求。端口斷定爲不通。
_this.portstate('open', target, callback);
portstate 是 PortScan 對象提供的一個方法,請求結束傳入open
或是timeout
,在 portstate 內部會觸發掃描事件。在 ws 的 onerror 觸發能觸發,說明服務端有迴應數據,狀態是open
,ps.onopen 就會被調用,上面例子中 ps.onopen 彈出的 prompt 窗口顯示掃到的開放端口就是由 portstate 去執行的。
// 掃描批量目標 PortScan.prototype.scan_batch = function (tasks, onfinish, onscan, isonopen_onopen) { this.setEvents(onfinish, onscan, isonopen_onopen); var _this = this; var q = this.queue = new Queue(this.scan_single, this.portscan_concurrence); q.tasks = tasks; q.onfinish = function () { _this.onfinish && _this.onfinish(_this.opentarget); } q.start(); return q; }
掃描批量目標使用 js.queue.js 的併發隊列功能,去執行的 scan_single 執行單個任務,scan_single 在掃描前作了一些額外的工做:把瀏覽器屏蔽的端口過濾掉了,ps.onscan 收到的狀態就是blocked
。
scan_range 遍歷指定的範圍,host.replace('*', i)
,生成目標地址,最後調用 scan_batch。
上面搞那麼複雜就是爲了 PortScan 的代碼用起來簡單靈活。
var ps = new PortScan(); ps.onfinsh = function(opentarget){ alert(opentarget); // opentarget是全部探測到端口開放的IP地址,花費時間大約10秒多 // 探測到目標後可接Black-Hole思路,自動化檢測cms,根據相關漏洞getshell繼而漫遊內網 } // webrtc得到內網網段參見 http://zone.wooyun.org/content/24219 ps.scan('192.168.0.*', 1, 254); // 加端口號也能夠 '192.168.0.*:8080'
-
(上個月寫的,有些地方描述不夠詳細準確,有空了再更新,那三個js,在demo頁面上 - 2016)
(demo連接已更新,代碼略有改動,修復了ie下運行的bug,增長了ws,ajax,video,image等請求方式,ps.use('websocket') - 2017-02-10 )