如果端口范围太宽,节点中的端口扫描不起作用

Portscan in Node doesn't work if port range is too wide

本文关键字:不起作用 节点 扫描 范围 如果      更新时间:2023-09-26

我正在尝试用nodejs编写一个简单的portscan脚本。您可以针对 scanme.nmap.org 运行此脚本。通过运行nmap本身,我知道该主机有四个开放端口:22、80、9929、31337。

我的脚本是:

var net = require('net');
var host = 'scanme.nmap.org';
var max = 35000;
function scanPort(port) {
    try {
        var c = new net.Socket();
        c.setTimeout(5000);
        c.connect({
            port: port,
            host: host
        })
        c.on('connect', function () {
            console.log(port);
        })
        c.on('error', function (err) {
            c.destroy();
        })
        c.on('timeout', function () {
            c.destroy();
        })
    } catch (err) {
        console.error(err);
    }
}
for (i = 1; i<=max; i++) {
    scanPort(i);
}

如果我从 i = 1 开始循环,脚本将检测端口 22 和 80,在此之后,它将以退出代码 0 退出。它不会检测到其他两个端口。如果我玩变i,即跳过第一个端口并设置i = 9500,它将正确检测端口 9929。如果我设置i = 31000,检测到端口 31337,也会发生同样的情况。

我真的不明白为什么它没有按预期工作。我怀疑某些系统限制导致了一些错误,但我试图将扫描功能包装在try块中,但没有设法检测到任何错误。

您可能尝试一次打开太多连接并耗尽资源或在接收服务器上被阻止以使其泛滥。

由于所有套接字内容都是异步的,因此for循环会立即尝试打开 35,000 个套接字。 虽然服务器可以配置为这样做,但它需要非常特殊的配置,并且使用每个套接字需要低内存方案(这里没有)。

因此,一个简单的解决方案是将一次打开的套接字数量限制为合理的数量。

下面是一些代码,它最初打开固定数量的套接字,然后随着每个套接字

的完成,它会打开另一个套接字,保持固定数量的打开和活动套接字。 它目前设置为立即打开100套接字。 您可以在特定的环境和操作系统中进行试验,看看在不引起问题的情况下可以达到多高的数字:

var net = require('net');
var host = 'scanme.nmap.org';
var low = 1;
var high = 35000;
var maxInFlight = 100;

function scanPort(port) {
    return new Promise(function(resolve, reject) {
        var c = new net.Socket();
        c.setTimeout(5000);
        c.connect({
            port: port,
            host: host
        })
        c.on('connect', function () {
            console.log(port);
            c.destroy();
            resolve(port);
        })
        c.on('error', function (err) {
            c.destroy();
            reject(err);
        })
        c.on('timeout', function () {
            c.destroy();
            reject({code: "Timeout", port: port});
        })
    });
}
var cntr = low;
var inFlightCnt = 0;
function run() {
    while (inFlightCnt < maxInFlight && cntr <= high) {
        ++inFlightCnt;
        scanPort(cntr++).then(function(port) {
            --inFlightCnt;
            run();
        }, function(err) {
            --inFlightCnt;
            run();
        });
    }
}
run();

注意:如果设置为一次100个,则需要一段时间才能通过所有端口(最多 35,000 个)。 它确实找到了您提到的四个开放端口。


而且,这是另一个版本,它收集有关每个端口的信息以及它如何失败/成功,以便您最后获得转储。 它还在控制台中显示进度,以便您可以查看它是否仍在运行以及它有多接近完成:

var net = require('net');
var host = 'scanme.nmap.org';
var low = 1;
var high = 35000;
var maxInFlight = 200;

function scanPort(port) {
    return new Promise(function(resolve, reject) {
        var c = new net.Socket();
        c.setTimeout(15000);
        c.connect({
            port: port,
            host: host
        })
        c.on('connect', function () {
            c.destroy();
            resolve(port);
        })
        c.on('error', function (err) {
            c.destroy();
            reject(err);
        })
        c.on('timeout', function () {
            c.destroy();
            reject({code: "timeout", port: port});
        })
    });
}
var cntr = low;
var inFlightCnt = 0;
var openPorts = [];
var timeouts = [];
var refused = [];
var otherErrors = [];
function run() {
    while (inFlightCnt < maxInFlight && cntr <= high) {
        ++inFlightCnt;
        scanPort(cntr++).then(function(port) {
            --inFlightCnt;
            openPorts.push(port);
            console.log(openPorts);
            run();
        }, function(err) {
            if (err.code === "timeout" || err.code === "ETIMEDOUT") {
                timeouts.push(err.port);
            } else if (err.code === "ECONNREFUSED") {
                refused.push(err.port);
            } else {
                otherErrors.push(err.port);
            }
            console.log(err.code + ": " + err.port);
            --inFlightCnt;
            run();
        });
    }
    // if we are all done here, log the open ports
    if (inFlightCnt === 0 && cntr >= high) {
        console.log("open: " + JSON.stringify(openPorts));
        console.log("timeouts: " +  JSON.stringify(timeouts));
        console.log("otherErrors: " +  JSON.stringify(otherErrors));
    }
}
run();

这是我在带有节点 v4.0.0 的 Windows 10 上运行它时生成的输出:

open: [22,80,9929,31337]
timeouts: [25,135,136,137,138,139,445,974,984,972,965,963,964,978,985,980,975,981,987,971,960,977,1000,992,990,986,991,997,1391,1384,7455,7459,7450,7506,7512,23033,23736,33635,33640,33638,33641,33634,33636,33633]
otherErrors: []

我不知道为什么某些端口超时。 大多数都得到ECONNREFUSED,这是你对未打开的端口的期望,少数从net库中得到ETIMEDOUT,还有一些超时在你的代码中超时(我增加到 15 秒)。

另外,请注意,我在它们连接后添加了销毁。 您让这些套接字保持打开状态,当只有一对夫妇连接时,这在这里工作正常,但如果连接了很多端口,则可能会出现问题。


注意:在测试此程序时,我运行了很多很多次,大约第 20 次运行它后,我的互联网连接中断了一段时间,重新启动我的电缆调制解调器才能让一切恢复正常。 我怀疑电缆调制解调器内部的某些东西在测试时无法一遍又一遍地处理这种快速触发的请求。 但是,也可能是康卡斯特由于异常活动(大量端口请求)而关闭了我的连接。

当然,这一切都可能是一个巧合,

我的小停电可能与我正在做的事情无关,但时间似乎太相关了,以至于我认为这只是一个巧合。

该程序可以进一步修改,通过每秒计量不超过N个端口探测器来"减慢速度"。