|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const { spawn, execSync } = require('child_process'); |
|
|
const fs = require('fs'); |
|
|
const path = require('path'); |
|
|
|
|
|
class AdvancedPentestingToolkit { |
|
|
constructor(config = {}) { |
|
|
this.config = config; |
|
|
this.tools = { |
|
|
nmap: '/usr/bin/nmap', |
|
|
sqlmap: '/opt/sqlmap/sqlmap.py', |
|
|
hydra: '/usr/bin/hydra', |
|
|
nuclei: '/usr/local/bin/nuclei', |
|
|
masscan: '/usr/bin/masscan', |
|
|
nikto: '/usr/bin/nikto' |
|
|
}; |
|
|
|
|
|
this.resultsDir = config.resultsDir || '/tmp/pentest_results'; |
|
|
|
|
|
|
|
|
if (!fs.existsSync(this.resultsDir)) { |
|
|
fs.mkdirSync(this.resultsDir, { recursive: true }); |
|
|
} |
|
|
|
|
|
console.log('β
AdvancedPentestingToolkit inicializado com ferramentas REAIS'); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async nmapScan(target, opcoes = '-sV -A -O') { |
|
|
try { |
|
|
if (!this._isTargetValid(target)) { |
|
|
return { sucesso: false, erro: 'Alvo invΓ‘lido' }; |
|
|
} |
|
|
|
|
|
console.log(`π Iniciando NMAP scan em: ${target}`); |
|
|
|
|
|
return new Promise((resolve, reject) => { |
|
|
const args = opcoes.split(' ').concat(target); |
|
|
const nmap = spawn('nmap', args); |
|
|
|
|
|
let output = ''; |
|
|
let error = ''; |
|
|
|
|
|
nmap.stdout.on('data', (data) => output += data.toString()); |
|
|
nmap.stderr.on('data', (data) => error += data.toString()); |
|
|
|
|
|
nmap.on('close', (code) => { |
|
|
if (code === 0) { |
|
|
const resultado = { |
|
|
sucesso: true, |
|
|
tipo: 'nmap_scan', |
|
|
target, |
|
|
comando: `nmap ${opcoes} ${target}`, |
|
|
output: output, |
|
|
parsado: this._parseNmapOutput(output), |
|
|
timestamp: new Date().toISOString(), |
|
|
risco: this._calculateNmapRisk(output) |
|
|
}; |
|
|
|
|
|
|
|
|
this._saveResult('nmap', target, resultado); |
|
|
|
|
|
resolve(resultado); |
|
|
} else { |
|
|
reject({ |
|
|
sucesso: false, |
|
|
erro: error || 'NMAP failed', |
|
|
code, |
|
|
target |
|
|
}); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
nmap.kill(); |
|
|
reject({ erro: 'NMAP timeout', target }); |
|
|
}, 600000); |
|
|
}); |
|
|
} catch (e) { |
|
|
console.error('Erro em nmapScan:', e); |
|
|
return { sucesso: false, erro: e.message }; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async sqlmapTest(url, parametro = 'id', opcoes = '--risk=1 --level=1 --batch') { |
|
|
try { |
|
|
if (!this._isUrlValid(url)) { |
|
|
return { sucesso: false, erro: 'URL invΓ‘lida' }; |
|
|
} |
|
|
|
|
|
console.log(`πΎ Iniciando SQLMAP test em: ${url}`); |
|
|
|
|
|
return new Promise((resolve, reject) => { |
|
|
const args = [ |
|
|
'sqlmap.py', |
|
|
'-u', url, |
|
|
'-p', parametro, |
|
|
'--dbs', |
|
|
...opcoes.split(' ') |
|
|
]; |
|
|
|
|
|
|
|
|
const sqlmapPath = this._findSqlmap(); |
|
|
|
|
|
const sqlmap = spawn('python3', [sqlmapPath, ...args]); |
|
|
|
|
|
let output = ''; |
|
|
let error = ''; |
|
|
|
|
|
sqlmap.stdout.on('data', (data) => output += data.toString()); |
|
|
sqlmap.stderr.on('data', (data) => error += data.toString()); |
|
|
|
|
|
sqlmap.on('close', (code) => { |
|
|
const resultado = { |
|
|
sucesso: code === 0, |
|
|
tipo: 'sqlmap_test', |
|
|
target: url, |
|
|
parametro, |
|
|
vulneravel: output.includes('vulnerable'), |
|
|
output: output, |
|
|
parsado: this._parseSqlmapOutput(output), |
|
|
timestamp: new Date().toISOString(), |
|
|
risco: output.includes('vulnerable') ? 'CRΓTICO' : 'BAIXO' |
|
|
}; |
|
|
|
|
|
|
|
|
this._saveResult('sqlmap', url, resultado); |
|
|
|
|
|
resolve(resultado); |
|
|
}); |
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
sqlmap.kill(); |
|
|
resolve({ |
|
|
sucesso: false, |
|
|
erro: 'SQLMAP timeout', |
|
|
target: url |
|
|
}); |
|
|
}, 900000); |
|
|
}); |
|
|
} catch (e) { |
|
|
console.error('Erro em sqlmapTest:', e); |
|
|
return { sucesso: false, erro: e.message }; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async hydraBrute(target, servico = 'ssh', usuario = 'admin', senhas = ['password', 'admin', '123456']) { |
|
|
try { |
|
|
console.log(`π Iniciando Hydra brute force: ${servico}://${target}`); |
|
|
|
|
|
return new Promise((resolve, reject) => { |
|
|
|
|
|
const passwordFile = path.join(this.resultsDir, `passwords_${Date.now()}.txt`); |
|
|
fs.writeFileSync(passwordFile, senhas.join('\n')); |
|
|
|
|
|
const args = [ |
|
|
'-l', usuario, |
|
|
'-P', passwordFile, |
|
|
'-o', path.join(this.resultsDir, `hydra_${target}_${servico}.txt`), |
|
|
target, |
|
|
servico, |
|
|
'-f' |
|
|
]; |
|
|
|
|
|
const hydra = spawn('hydra', args); |
|
|
|
|
|
let output = ''; |
|
|
|
|
|
hydra.stdout.on('data', (data) => output += data.toString()); |
|
|
hydra.on('close', (code) => { |
|
|
const resultado = { |
|
|
sucesso: code === 0, |
|
|
tipo: 'hydra_brute', |
|
|
target, |
|
|
servico, |
|
|
usuario, |
|
|
output: output, |
|
|
encontrado: output.includes('password found') || output.includes('[*] Trying'), |
|
|
timestamp: new Date().toISOString() |
|
|
}; |
|
|
|
|
|
|
|
|
try { |
|
|
fs.unlinkSync(passwordFile); |
|
|
} catch (e) {} |
|
|
|
|
|
|
|
|
this._saveResult('hydra', `${servico}://${target}`, resultado); |
|
|
|
|
|
resolve(resultado); |
|
|
}); |
|
|
|
|
|
setTimeout(() => { |
|
|
hydra.kill(); |
|
|
resolve({ |
|
|
sucesso: false, |
|
|
erro: 'Hydra timeout', |
|
|
target, |
|
|
servico |
|
|
}); |
|
|
}, 600000); |
|
|
}); |
|
|
} catch (e) { |
|
|
console.error('Erro em hydraBrute:', e); |
|
|
return { sucesso: false, erro: e.message }; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async nucleiScan(target, templates = 'cves') { |
|
|
try { |
|
|
if (!this._isTargetValid(target)) { |
|
|
return { sucesso: false, erro: 'Alvo invΓ‘lido' }; |
|
|
} |
|
|
|
|
|
console.log(`π― Iniciando Nuclei scan em: ${target}`); |
|
|
|
|
|
return new Promise((resolve, reject) => { |
|
|
const args = [ |
|
|
'-target', target, |
|
|
'-templates', templates, |
|
|
'-severity', 'critical,high,medium', |
|
|
'-json', |
|
|
'-o', path.join(this.resultsDir, `nuclei_${target}_${Date.now()}.json`) |
|
|
]; |
|
|
|
|
|
const nuclei = spawn('nuclei', args); |
|
|
|
|
|
let output = ''; |
|
|
let jsonOutput = ''; |
|
|
|
|
|
nuclei.stdout.on('data', (data) => { |
|
|
const chunk = data.toString(); |
|
|
output += chunk; |
|
|
try { |
|
|
jsonOutput += chunk; |
|
|
} catch (e) {} |
|
|
}); |
|
|
|
|
|
nuclei.on('close', (code) => { |
|
|
try { |
|
|
const parsedOutput = jsonOutput.trim().split('\n') |
|
|
.filter(line => line.trim()) |
|
|
.map(line => { |
|
|
try { |
|
|
return JSON.parse(line); |
|
|
} catch (e) { |
|
|
return null; |
|
|
} |
|
|
}) |
|
|
.filter(x => x !== null); |
|
|
|
|
|
const resultado = { |
|
|
sucesso: code === 0, |
|
|
tipo: 'nuclei_scan', |
|
|
target, |
|
|
templates, |
|
|
vulnerabilidadesEncontradas: parsedOutput.length, |
|
|
vulnerabilidades: parsedOutput.slice(0, 10), |
|
|
timestamp: new Date().toISOString(), |
|
|
risco: parsedOutput.length > 5 ? 'CRΓTICO' : parsedOutput.length > 0 ? 'MΓDIO' : 'BAIXO' |
|
|
}; |
|
|
|
|
|
|
|
|
this._saveResult('nuclei', target, resultado); |
|
|
|
|
|
resolve(resultado); |
|
|
} catch (e) { |
|
|
resolve({ |
|
|
sucesso: false, |
|
|
erro: e.message, |
|
|
target, |
|
|
output: output.substring(0, 500) |
|
|
}); |
|
|
} |
|
|
}); |
|
|
|
|
|
setTimeout(() => { |
|
|
nuclei.kill(); |
|
|
resolve({ |
|
|
sucesso: false, |
|
|
erro: 'Nuclei timeout', |
|
|
target |
|
|
}); |
|
|
}, 900000); |
|
|
}); |
|
|
} catch (e) { |
|
|
console.error('Erro em nucleiScan:', e); |
|
|
return { sucesso: false, erro: e.message }; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async masscanScan(target, portas = '1-65535') { |
|
|
try { |
|
|
if (!this._isTargetValid(target)) { |
|
|
return { sucesso: false, erro: 'Alvo invΓ‘lido' }; |
|
|
} |
|
|
|
|
|
console.log(`β‘ Iniciando Masscan em: ${target}`); |
|
|
|
|
|
return new Promise((resolve, reject) => { |
|
|
const args = [ |
|
|
'-p', portas, |
|
|
target, |
|
|
'--rate', '1000', |
|
|
'-oX', path.join(this.resultsDir, `masscan_${target}_${Date.now()}.xml`) |
|
|
]; |
|
|
|
|
|
const masscan = spawn('masscan', args); |
|
|
|
|
|
let output = ''; |
|
|
|
|
|
masscan.stdout.on('data', (data) => output += data.toString()); |
|
|
|
|
|
masscan.on('close', (code) => { |
|
|
const resultado = { |
|
|
sucesso: code === 0, |
|
|
tipo: 'masscan_scan', |
|
|
target, |
|
|
portas, |
|
|
output: output, |
|
|
parsado: this._parseMasscanOutput(output), |
|
|
timestamp: new Date().toISOString() |
|
|
}; |
|
|
|
|
|
|
|
|
this._saveResult('masscan', target, resultado); |
|
|
|
|
|
resolve(resultado); |
|
|
}); |
|
|
|
|
|
setTimeout(() => { |
|
|
masscan.kill(); |
|
|
resolve({ |
|
|
sucesso: false, |
|
|
erro: 'Masscan timeout', |
|
|
target |
|
|
}); |
|
|
}, 600000); |
|
|
}); |
|
|
} catch (e) { |
|
|
console.error('Erro em masscanScan:', e); |
|
|
return { sucesso: false, erro: e.message }; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async niktoScan(url, opcoes = '') { |
|
|
try { |
|
|
if (!this._isUrlValid(url)) { |
|
|
return { sucesso: false, erro: 'URL invΓ‘lida' }; |
|
|
} |
|
|
|
|
|
console.log(`π·οΈ Iniciando Nikto scan em: ${url}`); |
|
|
|
|
|
return new Promise((resolve, reject) => { |
|
|
const args = [ |
|
|
'-h', url, |
|
|
'-o', path.join(this.resultsDir, `nikto_${url.replace(/[^a-z0-9]/g, '_')}_${Date.now()}.txt`), |
|
|
...opcoes.split(' ').filter(x => x) |
|
|
]; |
|
|
|
|
|
const nikto = spawn('nikto', args); |
|
|
|
|
|
let output = ''; |
|
|
|
|
|
nikto.stdout.on('data', (data) => output += data.toString()); |
|
|
|
|
|
nikto.on('close', (code) => { |
|
|
const resultado = { |
|
|
sucesso: code === 0, |
|
|
tipo: 'nikto_scan', |
|
|
target: url, |
|
|
output: output, |
|
|
parsado: this._parseNiktoOutput(output), |
|
|
timestamp: new Date().toISOString(), |
|
|
vulnerabilidades: this._extractNiktoIssues(output) |
|
|
}; |
|
|
|
|
|
|
|
|
this._saveResult('nikto', url, resultado); |
|
|
|
|
|
resolve(resultado); |
|
|
}); |
|
|
|
|
|
setTimeout(() => { |
|
|
nikto.kill(); |
|
|
resolve({ |
|
|
sucesso: false, |
|
|
erro: 'Nikto timeout', |
|
|
target: url |
|
|
}); |
|
|
}, 600000); |
|
|
}); |
|
|
} catch (e) { |
|
|
console.error('Erro em niktoScan:', e); |
|
|
return { sucesso: false, erro: e.message }; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_parseNmapOutput(output) { |
|
|
const portas = []; |
|
|
const lines = output.split('\n'); |
|
|
|
|
|
for (const line of lines) { |
|
|
const match = line.match(/(\d+)\/tcp\s+(\w+)\s+(.+)/); |
|
|
if (match) { |
|
|
portas.push({ |
|
|
porta: match[1], |
|
|
protocolo: 'tcp', |
|
|
estado: match[2], |
|
|
servico: match[3], |
|
|
risco: ['open', 'filtered'].includes(match[2]) ? 'MΓDIO' : 'BAIXO' |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
return { |
|
|
totalPortas: portas.length, |
|
|
portasAbertas: portas.filter(p => p.estado === 'open').length, |
|
|
portas |
|
|
}; |
|
|
} |
|
|
|
|
|
_calculateNmapRisk(output) { |
|
|
const lines = output.split('\n'); |
|
|
const portasAbertas = lines.filter(line => line.includes('open')).length; |
|
|
|
|
|
if (portasAbertas > 10) return 'CRΓTICO'; |
|
|
if (portasAbertas > 5) return 'ALTO'; |
|
|
if (portasAbertas > 0) return 'MΓDIO'; |
|
|
return 'BAIXO'; |
|
|
} |
|
|
|
|
|
_parseSqlmapOutput(output) { |
|
|
return { |
|
|
vulneravel: output.includes('vulnerable'), |
|
|
bancoDados: output.includes('database') ? this._extractDatabase(output) : null, |
|
|
parametrosVulneraveis: this._extractVulnerableParams(output) |
|
|
}; |
|
|
} |
|
|
|
|
|
_extractDatabase(output) { |
|
|
const match = output.match(/database:\s*(\w+)/i); |
|
|
return match ? match[1] : 'unknown'; |
|
|
} |
|
|
|
|
|
_extractVulnerableParams(output) { |
|
|
const params = []; |
|
|
const lines = output.split('\n'); |
|
|
|
|
|
for (const line of lines) { |
|
|
if (line.includes('Parameter') && line.includes('vulnerable')) { |
|
|
const match = line.match(/Parameter:\s*([^,\s]+)/); |
|
|
if (match) params.push(match[1]); |
|
|
} |
|
|
} |
|
|
|
|
|
return params; |
|
|
} |
|
|
|
|
|
_parseMasscanOutput(output) { |
|
|
const portas = []; |
|
|
const lines = output.split('\n'); |
|
|
|
|
|
for (const line of lines) { |
|
|
const match = line.match(/host:\s*([\d.]+)\s+Ports:\s*([\d,\s/tcp/]+)/); |
|
|
if (match) { |
|
|
portas.push({ |
|
|
host: match[1], |
|
|
portas: match[2] |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
return { portas }; |
|
|
} |
|
|
|
|
|
_parseNiktoOutput(output) { |
|
|
const issues = []; |
|
|
const lines = output.split('\n'); |
|
|
|
|
|
for (const line of lines) { |
|
|
if (line.includes('OSVDB') || line.includes('CVE')) { |
|
|
issues.push(line.trim()); |
|
|
} |
|
|
} |
|
|
|
|
|
return { totalIssues: issues.length, issues }; |
|
|
} |
|
|
|
|
|
_extractNiktoIssues(output) { |
|
|
const issues = []; |
|
|
const lines = output.split('\n'); |
|
|
|
|
|
for (const line of lines) { |
|
|
if (line.includes('+') && line.includes('Server')) { |
|
|
issues.push({ |
|
|
tipo: 'Server Detection', |
|
|
descricao: line.trim() |
|
|
}); |
|
|
} |
|
|
if (line.includes('OSVDB')) { |
|
|
issues.push({ |
|
|
tipo: 'OSVDB Issue', |
|
|
descricao: line.trim() |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
return issues.slice(0, 10); |
|
|
} |
|
|
|
|
|
_isTargetValid(target) { |
|
|
return /^[\d.]+$/.test(target) || /^[\w.-]+$/.test(target); |
|
|
} |
|
|
|
|
|
_isUrlValid(url) { |
|
|
try { |
|
|
new URL(url); |
|
|
return true; |
|
|
} catch (e) { |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
_findSqlmap() { |
|
|
const possiblePaths = [ |
|
|
'/opt/sqlmap/sqlmap.py', |
|
|
'/usr/local/bin/sqlmap.py', |
|
|
'./sqlmap/sqlmap.py', |
|
|
'sqlmap.py' |
|
|
]; |
|
|
|
|
|
for (const path of possiblePaths) { |
|
|
try { |
|
|
execSync(`test -f ${path}`); |
|
|
return path; |
|
|
} catch (e) {} |
|
|
} |
|
|
|
|
|
return 'sqlmap.py'; |
|
|
} |
|
|
|
|
|
_saveResult(tool, target, resultado) { |
|
|
try { |
|
|
const filename = path.join( |
|
|
this.resultsDir, |
|
|
`${tool}_${target.replace(/[^a-z0-9]/g, '_')}_${Date.now()}.json` |
|
|
); |
|
|
|
|
|
fs.writeFileSync(filename, JSON.stringify(resultado, null, 2)); |
|
|
console.log(`β
Resultado salvo: ${filename}`); |
|
|
} catch (e) { |
|
|
console.warn(`β οΈ Erro ao salvar resultado: ${e.message}`); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async generateComprehensiveReport(target) { |
|
|
try { |
|
|
console.log(`\nπ Gerando relatΓ³rio completo para: ${target}\n`); |
|
|
|
|
|
const relatorio = { |
|
|
alvo: target, |
|
|
dataInicio: new Date().toISOString(), |
|
|
ferramentas: {}, |
|
|
resumo: {}, |
|
|
recomendacoes: [] |
|
|
}; |
|
|
|
|
|
|
|
|
console.log('1οΈβ£ Executando NMAP...'); |
|
|
try { |
|
|
const nmapResult = await this.nmapScan(target, '-sV -A'); |
|
|
relatorio.ferramentas.nmap = nmapResult; |
|
|
} catch (e) { |
|
|
relatorio.ferramentas.nmap = { erro: e.message }; |
|
|
} |
|
|
|
|
|
|
|
|
console.log('2οΈβ£ Executando Masscan...'); |
|
|
try { |
|
|
const masscanResult = await this.masscanScan(target, '1-10000'); |
|
|
relatorio.ferramentas.masscan = masscanResult; |
|
|
} catch (e) { |
|
|
relatorio.ferramentas.masscan = { erro: e.message }; |
|
|
} |
|
|
|
|
|
relatorio.dataFim = new Date().toISOString(); |
|
|
relatorio.resumo = this._generateSummary(relatorio); |
|
|
|
|
|
console.log('\nβ
RelatΓ³rio Completo Gerado!\n'); |
|
|
|
|
|
return relatorio; |
|
|
} catch (e) { |
|
|
console.error('Erro ao gerar relatΓ³rio:', e); |
|
|
return { erro: e.message, alvo: target }; |
|
|
} |
|
|
} |
|
|
|
|
|
_generateSummary(relatorio) { |
|
|
const ferramentas = relatorio.ferramentas || {}; |
|
|
const sucessos = Object.values(ferramentas).filter(f => f && f.sucesso).length; |
|
|
|
|
|
let totalVulns = 0; |
|
|
for (const ferramenta of Object.values(ferramentas)) { |
|
|
if (ferramenta && ferramenta.vulnerabilidades && Array.isArray(ferramenta.vulnerabilidades)) { |
|
|
totalVulns += ferramenta.vulnerabilidades.length; |
|
|
} |
|
|
} |
|
|
|
|
|
return { |
|
|
totalFerramentas: Object.keys(ferramentas).length, |
|
|
sucessos: sucessos, |
|
|
vulnerabilidades: totalVulns |
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
module.exports = AdvancedPentestingToolkit; |
|
|
|