initial commit

This commit is contained in:
2022-06-29 16:10:24 +02:00
commit 63af500747
11 changed files with 5984 additions and 0 deletions

22
.eslintrc Normal file
View File

@@ -0,0 +1,22 @@
{
"root": true,
"env": { "es6": true, "node": true },
"extends": [
"eslint:recommended"
],
"overrides": [
{
"files": ["**/*.ts"],
"env": { "es6": true, "node": true },
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
]
}
]
}

12
README.md Normal file
View File

@@ -0,0 +1,12 @@
# SNMP Scanner
Utilita, ktera se pokousi pripojit k SNMP (v1 nebo v2c) na kazde IPv4 nebo IPv6 adrese v CIDR rozsahu, ktery utilita precte ze standardniho vstupu.
K dispozici je nekolik argumentu, ktere ovlivnuji chovani scanovani:
* `-v` nebo `--version` nastavuje SNMP verzi, `1` nebo `2c`.
* `-c` nebo `--community` nastavuje SNMP komunitu, jakykoliv string.
* `-r` nebo `--retries` nastavuje pocet pokusu pripojeni k SNMP na jedne IP adrese, jakekoliv cislo.
* `-t` nebo `--timeout` nastavuje timeout pripojeni k SNMP, jakekoliv cislo, v milisekundach.
* `-d` nebo `--delay` nastavuje casovou mezeru mezi pripojovanim k SNMP na jednotlivych IP adresach, v milisekundach.
Knihovnu RxJs jsem pro implementaci nepouzil z duvodu nepotrebnosti (kanon na vrabce, v teto male ukazce nema IMHO smysl).

1
dist/bundle.js vendored Normal file
View File

@@ -0,0 +1 @@
(()=>{"use strict";const e=require("ip-cidr"),r=require("readline"),t=require("getopts"),n=require("net-snmp");function o(e,r){return new Promise(((t,o)=>{const i=[];e.subtree(r,20,(e=>{for(let r=0;r<e.length;r++)n.isVarbindError(e[r])?o(n.varbindError(e[r])):i.push(e[r].value.toString())}),(e=>{e&&o(e),t(i)}))}))}function i(e,r,t,i,s){return c=this,u=void 0,l=function*(){const c=n.createSession(e.address,t,{retries:i,timeout:s,version:"2c"===r?n.Version2c:n.Version1,transport:e.v4?"udp4":"udp6"});try{const r=yield function(e,r){return new Promise(((r,t)=>{e.get(["1.3.6.1.2.1.1.5.0"],((e,o)=>{e?t(e):o.length?n.isVarbindError(o[0])?t(n.varbindError(o[0])):r(o[0].value.toString()):r("")}))}))}(c);let t=yield o(c,"1.3.6.1.2.1.31.1.1.1.1");t.length||(t=yield o(c,"1.3.6.1.2.1.2.2.1.2")),c.close(),console.log(`${e.address}; ${r}; ${t.join(", ")}`)}catch(r){r instanceof n.RequestTimedOutError||console.error(`Error: ${e.address} ${r}`)}},new((a=void 0)||(a=Promise))((function(e,r){function t(e){try{o(l.next(e))}catch(e){r(e)}}function n(e){try{o(l.throw(e))}catch(e){r(e)}}function o(r){var o;r.done?e(r.value):(o=r.value,o instanceof a?o:new a((function(e){e(o)}))).then(t,n)}o((l=l.apply(c,u||[])).next())}));var c,u,a,l}const s=t(process.argv.slice(2),{alias:{version:"v",community:"c",retries:"r",timeout:"t",delay:"d"}}),c=s.version?s.version:"1",u=s.community?s.community:"public",a=s.retries?Number(s.retries):2,l=s.timeout?Number(s.timeout):1500,d=s.delay?Number(s.delay):20;console.log(`SNMP version: ${c}, Community: ${u}, Retries: ${a}, Timeout: ${l} ms, Delay: ${d} ms`),console.log("---");const m=r.createInterface({input:process.stdin,output:process.stdout,terminal:!1});m.on("line",(r=>{return t=void 0,n=void 0,s=function*(){try{const t=new e(r);for(const e of t.toArray({type:"addressObject"}))yield new Promise((e=>setTimeout(e,d))),i(e,c,u,a,l)}catch(e){console.error(e)}m.close()},new((o=void 0)||(o=Promise))((function(e,r){function i(e){try{u(s.next(e))}catch(e){r(e)}}function c(e){try{u(s.throw(e))}catch(e){r(e)}}function u(r){var t;r.done?e(r.value):(t=r.value,t instanceof o?t:new o((function(e){e(t)}))).then(i,c)}u((s=s.apply(t,n||[])).next())}));var t,n,o,s}))})();

5445
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

34
package.json Normal file
View File

@@ -0,0 +1,34 @@
{
"name": "ui_snmp",
"version": "0.0.1",
"description": "",
"main": "dist/bundle.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node dist/bundle.js",
"build": "webpack",
"postinstall": "patch-package"
},
"author": "Josef Miegl",
"license": "UNLICENSED",
"devDependencies": {
"@types/node": "~18.0.0",
"@types/webpack-env": "~1.16.3",
"@typescript-eslint/eslint-plugin": "~5.12.0",
"@typescript-eslint/parser": "~5.12.0",
"eslint": "~8.9.0",
"patch-package": "~6.4.7",
"postinstall-postinstall": "~2.1.0",
"ts-loader": "~9.2.6",
"typescript": "~4.5.5",
"webpack": "~5.69.0",
"webpack-cli": "~4.9.2",
"webpack-node-externals": "~3.0.0"
},
"dependencies": {
"getopts": "2.3.0",
"ip-cidr": "3.0.10",
"net-snmp": "3.8.2",
"rxjs": "7.5.5"
}
}

View File

@@ -0,0 +1,13 @@
diff --git a/node_modules/ip-cidr/index.d.ts b/node_modules/ip-cidr/index.d.ts
index 931614f..b8c1b64 100755
--- a/node_modules/ip-cidr/index.d.ts
+++ b/node_modules/ip-cidr/index.d.ts
@@ -26,7 +26,7 @@ declare class IPCIDR {
toString(): string;
- toArray(): string[];
+ toArray<T = IPCIDR.FormatResult>(options?: IPCIDR.FormatOptions, results?: IPCIDR.ChunkInfo): T[];
toObject(): { start: string, end: string };
}

50
src/main.ts Normal file
View File

@@ -0,0 +1,50 @@
import * as ipcidr from "ip-cidr"
import * as readline from "readline"
import * as getopts from "getopts"
import * as snmp from "./snmp"
const args = getopts(process.argv.slice(2), {
alias: {
version: "v",
community: "c",
retries: "r",
timeout: "t",
delay: "d"
}
});
const version: string = args['version'] ? args['version'] : "1";
const community: string = args['community'] ? args['community'] : "public";
const retries: number = args['retries'] ? Number(args['retries']) : 2;
const timeout: number = args['timeout'] ? Number(args['timeout']) : 1500;
const delay: number = args['delay'] ? Number(args['delay']) : 20;
console.log(`SNMP version: ${version}, Community: ${community}, Retries: ${retries}, Timeout: ${timeout} ms, Delay: ${delay} ms`);
console.log("---");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
rl.on("line", async line => {
try {
// Parse CIDR IP address notation
const cidr = new ipcidr(line);
// Loop over individual addresses in CIDR range
for(const ip of cidr.toArray({ type: "addressObject" })) {
// Sleep for some amount of time to ease stress on network
await new Promise(resolve => setTimeout(resolve, delay));
// Scan IP address
snmp.scan(ip, version, community, retries, timeout);
}
} catch(err) {
console.error(err);
}
rl.close();
});

69
src/snmp.ts Normal file
View File

@@ -0,0 +1,69 @@
import * as netSnmp from "net-snmp"
import { Address } from "ip-cidr"
type varbindsType = { oid: string, type: netSnmp.ObjectType, value: Buffer }[]
function get(session: netSnmp.Session, oid: string): Promise<string> {
return new Promise((resolve, reject) => {
session.get([oid], (error: Error, varbinds: varbindsType) => {
if(error) {
reject(error);
} else {
if(varbinds.length) {
if(netSnmp.isVarbindError(varbinds[0]))
reject(netSnmp.varbindError(varbinds[0]));
else
resolve(varbinds[0].value.toString());
} else {
resolve("");
}
}
});
});
}
function getSubtree(session: netSnmp.Session, oid: string): Promise<string[]> {
return new Promise((resolve, reject) => {
const ret: string[] = [];
session.subtree(oid, 20, (varbinds: varbindsType) => {
for(let i = 0; i < varbinds.length; i++) {
if(netSnmp.isVarbindError(varbinds[i]))
reject(netSnmp.varbindError(varbinds[i]));
else
ret.push(varbinds[i].value.toString());
}
}, (error: Error) => {
if(error) reject(error);
resolve(ret);
});
});
}
export async function scan(address: Address, version: string, community: string, retries: number, timeout: number) {
// Create SNMPv1 or SNMPv2c session
const session = netSnmp.createSession(address.address, community, {
retries: retries,
timeout: timeout,
version: (version === "2c") ? netSnmp.Version2c : netSnmp.Version1,
transport: address.v4 ? "udp4" : "udp6" // net-snmp has to known whether the provided IP address is v4 or v6
});
try {
// Try to get SNMP sysName
const sysName = await get(session, "1.3.6.1.2.1.1.5.0"); // SNMPv2-MIB::sysName.0
// Try to get an array of SNMP interface names
let ifNames = await getSubtree(session, "1.3.6.1.2.1.31.1.1.1.1"); // IF-MIB::ifName
// Some SNMP agents provide only interface descriptions, use them instead if interface names not available
if(!ifNames.length) ifNames = await getSubtree(session, "1.3.6.1.2.1.2.2.1.2") // IF-MIB::ifDescr
session.close();
console.log(`${address.address}; ${sysName}; ${ifNames.join(', ')}`);
} catch(err) {
if(!(err instanceof netSnmp.RequestTimedOutError))
console.error(`Error: ${address.address} ${err}`);
}
}

272
src/types/net-snmp.d.ts vendored Normal file
View File

@@ -0,0 +1,272 @@
declare module "net-snmp" {
export namespace ObjectParser {
export { readInt32 };
export { readUint32 };
export { readVarbindValue };
}
export class Session {
private constructor();
close(): any;
cancelRequests(error: any): void;
get(oids: any, responseCb: any): any;
getBulk(...args: any[]): any;
getNext(oids: any, responseCb: any): any;
inform(...args: any[]): any;
onClose(): void;
onError(error: any): void;
onMsg(buffer: any): void;
msgSecurityParameters: {
msgAuthoritativeEngineID: any;
msgAuthoritativeEngineBoots: any;
msgAuthoritativeEngineTime: any;
};
onSimpleGetResponse(req: any, message: any): void;
registerRequest(req: any): void;
send(req: any, noWait: any): any;
set(varbinds: any, responseCb: any): any;
simpleGet(pduClass: any, feedCb: any, varbinds: any, responseCb: any, options: any): void;
subtree(...args: any[]): any;
tableColumns(...args: any[]): any;
table(...args: any[]): any;
trap(...args: any[]): any;
unregisterRequest(id: any): any;
walk(...args: any[]): any;
sendV3Req(pdu: any, feedCb: any, responseCb: any, options: any, port: any, allowReport: any): void;
sendV3Discovery(originalPdu: any, feedCb: any, responseCb: any, options: any): void;
userSecurityModelError(req: any, oid: any): void;
onProxyResponse(req: any, message: any): void;
}
export function create(target: any, community: any, options: any): any;
export function createV3(target: any, user: any, options: any): any;
export function create(options: any, callback: any): any;
export function create(options: any, callback: any, mib: any): any;
export function create(): any;
export function create(options: any): any;
export function create(): any;
/*****************************************************************************
** OID and varbind helper functions
**/
export function isVarbindError(varbind: any): boolean;
export function varbindError(varbind: any): string;
export var Version1: number;
export var Version2c: number;
export var Version3: number;
export var Version: {
"1": number;
"2c": number;
"3": number;
};
export enum ErrorStatus {
NoError = 0,
TooBig = 1,
NoSuchName = 2,
BadValue = 3,
ReadOnly = 4,
GeneralError = 5,
NoAccess = 6,
WrongType = 7,
WrongLength = 8,
WrongEncoding = 9,
WrongValue = 10,
NoCreation = 11,
InconsistentValue = 12,
ResourceUnavailable = 13,
CommitFailed = 14,
UndoFailed = 15,
AuthorizationError = 16,
NotWritable = 17,
InconsistentName = 18
}
export enum TrapType {
ColdStart = 0,
WarmStart = 1,
LinkDown = 2,
LinkUp = 3,
AuthenticationFailure = 4,
EgpNeighborLoss = 5,
EnterpriseSpecific = 6
}
export enum ObjectType {
Boolean = 1,
Integer = 2,
BitString = 3,
OctetString = 4,
Null = 5,
OID = 6,
IpAddress = 64,
Counter = 65,
Gauge = 66,
TimeTicks = 67,
Opaque = 68,
Counter64 = 70,
NoSuchObject = 128,
NoSuchInstance = 129,
EndOfMibView = 130
}
export enum PduType {
GetRequest = 160,
GetNextRequest = 161,
GetResponse = 162,
SetRequest = 163,
Trap = 164,
GetBulkRequest = 165,
InformRequest = 166,
TrapV2 = 167,
Report = 168
}
export enum AgentXPduType {
Open = 1,
Close = 2,
Register = 3,
Unregister = 4,
Get = 5,
GetNext = 6,
GetBulk = 7,
TestSet = 8,
CommitSet = 9,
UndoSet = 10,
CleanupSet = 11,
Notify = 12,
Ping = 13,
IndexAllocate = 14,
IndexDeallocate = 15,
AddAgentCaps = 16,
RemoveAgentCaps = 17,
Response = 18
}
export enum MibProviderType {
Scalar = 1,
Table = 2
}
export enum SecurityLevel {
noAuthNoPriv = 1,
authNoPriv = 2,
authPriv = 3
}
export enum AuthProtocols {
none = 1,
md5 = 2,
sha = 3,
sha224 = 4,
sha256 = 5,
sha384 = 6,
sha512 = 7
}
export enum PrivProtocols {
none = 1,
des = 2,
aes = 4,
aes256b = 6,
aes256r = 8,
}
export enum AccessControlModelType {
None = 0,
Simple = 1
}
export enum AccessLevel {
None = 0,
ReadOnly = 1,
ReadWrite = 2
}
export enum MaxAccess {
notAccessible = 0,
accessibleForNotify = 1,
readOnly = 2,
readWrite = 3,
readCreate = 4
}
export enum RowStatus {
active = 1,
notInService = 2,
notReady = 3,
createAndGo = 4,
createAndWait = 5,
destroy = 6
}
export enum ResponseInvalidCode {
EIp4AddressSize = 1,
EUnknownObjectType = 2,
EUnknownPduType = 3,
ECouldNotDecrypt = 4,
EAuthFailure = 5,
EReqResOidNoMatch = 6,
EOutOfOrder = 8,
EVersionNoMatch = 9,
ECommunityNoMatch = 10,
EUnexpectedReport = 11,
EResponseNotHandled = 12,
EUnexpectedResponse = 13
}
/*****************************************************************************
** Exception class definitions
**/
export function ResponseInvalidError(message: any, code: any, info: any): void;
export class ResponseInvalidError {
/*****************************************************************************
** Exception class definitions
**/
constructor(message: any, code: any, info: any);
name: string;
message: any;
code: any;
info: any;
}
export function RequestInvalidError(message: any): void;
export class RequestInvalidError {
constructor(message: any);
name: string;
message: any;
}
export function RequestFailedError(message: any, status: any): void;
export class RequestFailedError {
constructor(message: any, status: any);
name: string;
message: any;
status: any;
}
export function RequestTimedOutError(message: any): void;
export class RequestTimedOutError {
constructor(message: any);
name: string;
message: any;
}
export function readInt32(buffer: any): any;
export function readUint32(buffer: any): any;
export function readVarbindValue(buffer: any, type: any): any;
export namespace Authentication {
const HMAC_BUFFER_SIZE: number;
const algorithms: any;
const authToKeyCache: {};
function computeCacheKey(authProtocol: any, authPasswordString: any, engineID: any): any;
function passwordToKey(authProtocol: any, authPasswordString: any, engineID: any): any;
function getParametersLength(authProtocol: any): any;
function writeParameters(messageBuffer: any, authProtocol: any, authPassword: any, engineID: any, digestInMessage: any): void;
function isAuthentic(messageBuffer: any, authProtocol: any, authPassword: any, engineID: any, digestInMessage: any): boolean;
function calculateDigest(messageBuffer: any, authProtocol: any, authPassword: any, engineID: any): Buffer;
}
export namespace Encryption {
export function encryptPdu(privProtocol: any, scopedPdu: any, privPassword: any, authProtocol: any, engine: any): any;
export function decryptPdu(privProtocol: any, encryptedPdu: any, privParameters: any, privPassword: any, authProtocol: any, engine: any): any;
export function debugEncrypt(encryptionKey: any, iv: any, plainPdu: any, encryptedPdu: any): void;
export function debugDecrypt(decryptionKey: any, iv: any, encryptedPdu: any, plainPdu: any): void;
export function generateLocalizedKey(algorithm: any, authProtocol: any, privPassword: any, engineID: any): Buffer;
export function generateLocalizedKeyBlumenthal(algorithm: any, authProtocol: any, privPassword: any, engineID: any): Buffer;
export function generateLocalizedKeyReeder(algorithm: any, authProtocol: any, privPassword: any, engineID: any): Buffer;
export function encryptPduDes(scopedPdu: any, privProtocol: any, privPassword: any, authProtocol: any, engine: any): {
encryptedPdu: Buffer;
msgPrivacyParameters: Buffer;
};
export function decryptPduDes(encryptedPdu: any, privProtocol: any, privParameters: any, privPassword: any, authProtocol: any, engine: any): Buffer;
export function generateIvAes(aes: any, engineBoots: any, engineTime: any, salt: any): Buffer;
export function encryptPduAes(scopedPdu: any, privProtocol: any, privPassword: any, authProtocol: any, engine: any): {
encryptedPdu: Buffer;
msgPrivacyParameters: Buffer;
};
export function decryptPduAes(encryptedPdu: any, privProtocol: any, privParameters: any, privPassword: any, authProtocol: any, engine: any): Buffer;
const algorithms_1: any;
export { algorithms_1 as algorithms };
}
export { create as createSession, createV3 as createV3Session, create as createReceiver, create as createAgent, create as createModuleStore, create as createSubagent, create as createMib };
}

26
tsconfig.json Normal file
View File

@@ -0,0 +1,26 @@
{
"compilerOptions": {
"outDir": "./dist/",
"strict": true,
"strictNullChecks": false,
"noImplicitAny": true,
"noImplicitThis": true,
"noImplicitReturns": false,
"module": "esnext",
"target": "es6",
"allowJs": true,
"moduleResolution": "node",
"inlineSourceMap": false,
"inlineSources": false,
"types": [
"webpack-env",
"@types/node"
],
"typeRoots": [
"src/types"
]
},
"include": [
"src/**/*.ts"
]
}

40
webpack.config.js Normal file
View File

@@ -0,0 +1,40 @@
const path = require('path');
const nodeExternals = require('webpack-node-externals');
module.exports = [
{
name: 'main',
target: 'node',
mode: process.env.NODE_ENV || 'production',
entry: './src/main.ts',
module: {
rules: [
{
test: /\.tsx?$/,
include: [
path.resolve(__dirname, 'src')
],
exclude: [
/node_modules/,
],
use: 'ts-loader'
}
]
},
resolve: {
modules: [
'node_modules',
path.resolve(__dirname, 'src')
],
extensions: ['.ts', '.js']
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
externals: [nodeExternals()],
externalsPresets: {
node: true
}
}
];