前两天 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 )