Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/crimson-med/vestigo
Browse files Browse the repository at this point in the history
  • Loading branch information
crimson-med committed Oct 14, 2020
2 parents 71224ce + 7907f9f commit 5955201
Show file tree
Hide file tree
Showing 6 changed files with 549 additions and 415 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ vestigo scan --target="https://127.0.0.1/" --method="GET" --no-shortlist
--report="HTML"
```

As of now reports will be saved at:

```
~/.vestigo/<domain>/<ts>-vestigo.<extension>
```

- `<domain>` = hostname of the target or the ip
- `<ts>` = timestamp of the scan finished
- `<extension>` = extension chosen for report (md, html)


# Options

```
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
},
"bugs": "https://github.com/crimson-med/vestigo/issues",
"dependencies": {
"@argus-inc/fluct": "https://github.com/argus-inc/fluct",
"@oclif/command": "^1.6.1",
"@oclif/config": "^1",
"@oclif/plugin-help": "^2",
Expand Down Expand Up @@ -64,4 +65,4 @@
"resolutions": {
"whois-json/whois/optimist/minimist": "^1.2.5"
}
}
}
51 changes: 26 additions & 25 deletions src/classes/report.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { AxiosResponse } from 'axios';
import IntenseScan from './intenseScan';
import { formatDate } from './../tools/dateFormatter';
import * as fs from 'fs';
import * as chalk from 'chalk';
import { create_path, createTempDir } from './../tools/fileHandler';
import { FileHandler } from '@argus-inc/fluct';
const fs = new FileHandler('.vestigo');
import './../string.extensions';
import { extractHostname } from '../tools/urlHandler';
export enum ReportType {
MARKDOWN,
HTML
Expand All @@ -19,7 +20,7 @@ export interface WhoIs {
registrantStreet: string
registrantCity: string
nameServer: string
}
}
export default class Report {
reportType: ReportType;
target: string;
Expand All @@ -46,19 +47,19 @@ export default class Report {
this.target = target;
this.whois = this.initWhois();
this.reportType = ReportType.MARKDOWN
createTempDir();
//createTempDir();
}

initWhois(whoisObj: any = {}) {
const result = {
const result = {
domainName: whoisObj.domainName || "",
registrarWhoisServer: whoisObj.registrarWhoisServer || "",
registrar: whoisObj.registrar || "",
registrantName: whoisObj.registrantName || "",
registrantOrganization: whoisObj.registrantOrganization || "",
registrantStreet: whoisObj.registrantStreet || "",
registrantCity: whoisObj.registrantCity || "",
nameServer: whoisObj.nameServer || ""
registrantName: whoisObj.registrantName || "",
registrantOrganization: whoisObj.registrantOrganization || "",
registrantStreet: whoisObj.registrantStreet || "",
registrantCity: whoisObj.registrantCity || "",
nameServer: whoisObj.nameServer || ""
}
return result
}
Expand All @@ -70,18 +71,18 @@ export default class Report {
this.contentType = (response.headers['content-type']) ? response.headers['content-type'] : '';
}

exportSummary(type: ReportType = ReportType.MARKDOWN, output:string|null = null) {
exportSummary(type: ReportType = ReportType.MARKDOWN, output: string | null = null) {
this.reportType = type;
let template: string = ``
// Load Tepmplate
if (this.reportType == ReportType.HTML) {
const pathed = create_path(["../../templates/report.html"], false, true);
const pathed = fs.createPath(["templates/report.html"], false, true);
console.log(pathed)
template = fs.readFileSync(pathed, 'utf8');
template = fs.read(pathed);
} else {
const pathed = create_path(["../../templates/report.md"], false, true);
const pathed = fs.createPath(["templates/report.md"], false, true);
console.log(pathed)
template = fs.readFileSync(pathed, 'utf8');
template = fs.read(pathed);
}
// Export basic informations
template = this.exportBasics(template)
Expand All @@ -102,19 +103,19 @@ export default class Report {
}

exportFinalize(template: string) {
let pathed = create_path([`${+ new Date()}-vestigo.md`], true);
let pathed = fs.createPath([extractHostname(this.flags.target), `${+ new Date()}-vestigo.md`], true);

if (this.reportType === ReportType.HTML) {
pathed = create_path([`${+ new Date()}-vestigo.html`], true);
pathed = fs.createPath([extractHostname(this.flags.target), `${+ new Date()}-vestigo.html`], true);
}
fs.writeFileSync(pathed, template.toString());
fs.save(pathed, template.toString());
console.log(` - Report Generated: ${chalk.green(pathed)}`)
}

exportPathsDisclosures(template: string) {
if (this.reportType === ReportType.HTML) {
let paths = this.intenseScan.getAllPathsDisclosures().reduce((accumulator: any, currentValue: any) => {
return accumulator+`<li>${currentValue}</li>`;
return accumulator + `<li>${currentValue}</li>`;
}, "")
template = template.replace("{path1}", paths);
} else {
Expand All @@ -126,7 +127,7 @@ export default class Report {
exportValidEndpoints(template: string) {
if (this.reportType === ReportType.HTML) {
let urls = this.intenseScan.getUrlsSuccess().reduce((accumulator: any, currentValue: any) => {
return accumulator+`<li>${currentValue}</li>`;
return accumulator + `<li>${currentValue}</li>`;
}, "")
template = template.replace("{url1}", urls);
} else {
Expand Down Expand Up @@ -171,12 +172,12 @@ export default class Report {
let isEmpty = true
Object.values(this.whois).forEach((item) => {
if (typeof item === "string" && item.length > 0) {
isEmpty = false
isEmpty = false
}
if (typeof item === "object") {
Object.values(item).forEach((it) => {
if (typeof it === "string" && it.length > 0) {
isEmpty = false
isEmpty = false
}
});
}
Expand All @@ -185,7 +186,7 @@ export default class Report {
}




generateTable(content: any): string {
let table = ''
Expand All @@ -194,9 +195,9 @@ export default class Report {
value = JSON.stringify(value);
}
if (this.reportType == ReportType.HTML) {
table = table+`<tr><td>${key}</td><td>${value}</td></tr>`;
table = table + `<tr><td>${key}</td><td>${value}</td></tr>`;
} else {
table = table+`| ${key} | ${value} |\n`
table = table + `| ${key} | ${value} |\n`
}
}
return table
Expand Down
102 changes: 52 additions & 50 deletions src/commands/scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { Command, flags } from '@oclif/command'
import axios, { AxiosResponse } from 'axios';
import * as chalk from 'chalk';
import Report, { ReportType } from './../classes/report';
import {intenseScan} from './../tools/scanTools';
import { intenseScan } from './../tools/scanTools';
import IntenseScan from './../classes/intenseScan';
import * as https from 'https';
import {formatDate} from './../tools/dateFormatter';
import * as whois from 'whois-json';
import { isValidDomain } from './../tools/urlHandler';
import { formatDate } from './../tools/dateFormatter';
import * as whois from 'whois-json';
import { extractHostname, isValidDomain } from './../tools/urlHandler';
import { FileHandler } from '@argus-inc/fluct';
const fs = new FileHandler('.vestigo');
//-----------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------
// TODO: get path disclosures for basic get
Expand All @@ -25,13 +27,13 @@ export default class Scan extends Command {
]
static flags = {
help: flags.help({ char: 'h' }),
method: flags.enum({char: 'm', description: 'requet methods can be: GET, POST, BOTH', required: false, options: ["GET", "POST", "BOTH"], default: "POST" }),
output: flags.string({char: 'o', description: 'specify the ouput directory'}),
parameters: flags.boolean({ char: 'p', description: 'use extra parameters on endpoints', required: true, default: true, allowNo: true}),
report: flags.enum({ char: 'r', description: 'type of report to generate', required: true, default: "MD", options: ["MD", "HTML"]}),
shortlist: flags.boolean({ char: 's', description: 'use the shortlist for endpoints', required: true, default: true, allowNo: true}),
method: flags.enum({ char: 'm', description: 'requet methods can be: GET, POST, BOTH', required: false, options: ["GET", "POST", "BOTH"], default: "POST" }),
output: flags.string({ char: 'o', description: 'specify the ouput directory' }),
parameters: flags.boolean({ char: 'p', description: 'use extra parameters on endpoints', required: true, default: true, allowNo: true }),
report: flags.enum({ char: 'r', description: 'type of report to generate', required: true, default: "MD", options: ["MD", "HTML"] }),
shortlist: flags.boolean({ char: 's', description: 'use the shortlist for endpoints', required: true, default: true, allowNo: true }),
target: flags.string({ char: 't', description: 'target to scan', required: true }),
whois: flags.boolean({char: 'w', description: 'perform who is request on the target', required: true, default: false, allowNo: true})
whois: flags.boolean({ char: 'w', description: 'perform who is request on the target', required: true, default: false, allowNo: true })
}

async run() {
Expand All @@ -40,7 +42,7 @@ export default class Scan extends Command {
// Load args and flags
const { args, flags } = this.parse(Scan)
// Fix url with end slash
flags.target = (flags.target.charAt(flags.target.length - 1) !== "/") ? flags.target+'/' : flags.target ;
flags.target = (flags.target.charAt(flags.target.length - 1) !== "/") ? flags.target + '/' : flags.target;
// Convert string report typ to actual type
const reportType = (flags.report === "MD") ? ReportType.MARKDOWN : ReportType.HTML;
// Logging
Expand All @@ -53,14 +55,14 @@ export default class Scan extends Command {
// Try to contact base url
try {
// Disable SSL verification by default
const agent = new https.Agent({
const agent = new https.Agent({
rejectUnauthorized: false
});
});
init = await axios.get(flags.target, { httpsAgent: agent });
} catch (error) {
if (error.code) {
console.log(`Error code: ${error.code}`);
}
}
}

if (flags.whois === true) {
Expand All @@ -81,46 +83,46 @@ export default class Scan extends Command {
}

// If base url can be contacted start basic analysis
// Init report
if (init) {
finalReport.loadFromResponse(init);
if (this.validateStatus(init.status) == true) {
console.log(` - Successfully connected to target`)
console.log(` - Gathering basic header information`)
console.log(` - Target Powered by: ${chalk.cyan(finalReport.poweredBy)}`)
console.log(` - Target Last Modified at: ${chalk.cyan(finalReport.lastModified)}`)
if (finalReport.cors == "*") {
console.log(` - Target ${chalk.cyan('Not CORS protected')}`)
} else {
console.log(` - Target ${chalk.cyan('Is CORS protected')}`)
}
// Init report
if (init) {
finalReport.loadFromResponse(init);
if (this.validateStatus(init.status) == true) {
console.log(` - Successfully connected to target`)
console.log(` - Gathering basic header information`)
console.log(` - Target Powered by: ${chalk.cyan(finalReport.poweredBy)}`)
console.log(` - Target Last Modified at: ${chalk.cyan(finalReport.lastModified)}`)
if (finalReport.cors == "*") {
console.log(` - Target ${chalk.cyan('Not CORS protected')}`)
} else {
console.log(init)
console.log(init.status)
console.log(` - Target ${chalk.cyan('Is CORS protected')}`)
}
} else {
console.log(` - Couldn't connect to base target`)
}
// Init an intense scan
let intenseResult: IntenseScan | void = await intenseScan(flags.target, flags.shortlist, flags.parameters, flags.method);
// If init successfull
if (intenseResult) {
const endDate = new Date();
// Set all report's information
finalReport.target = flags.target;
finalReport.flags = flags;
finalReport.intenseScan = intenseResult;
finalReport.startDate = startDate;
finalReport.endDate = endDate;
finalReport.elapsedSeconds = (endDate.getTime() - startDate.getTime()) / 1000;
// Export reports
// command line report
intenseResult.exportSummary();
// file report
finalReport.exportSummary(reportType, flags.output)
console.log(` - ${chalk.green(formatDate(endDate, "dddd dd MMMM yyyy hh:mm"))}`)
console.log(` - Time Elapsed: ${chalk.green(finalReport.elapsedSeconds.toString())} seconds`)
console.log(init)
console.log(init.status)
}
} else {
console.log(` - Couldn't connect to base target`)
}
// Init an intense scan
let intenseResult: IntenseScan | void = await intenseScan(flags.target, flags.shortlist, flags.parameters, flags.method);
// If init successfull
if (intenseResult) {
const endDate = new Date();
// Set all report's information
finalReport.target = flags.target;
finalReport.flags = flags;
finalReport.intenseScan = intenseResult;
finalReport.startDate = startDate;
finalReport.endDate = endDate;
finalReport.elapsedSeconds = (endDate.getTime() - startDate.getTime()) / 1000;
// Export reports
// command line report
intenseResult.exportSummary();
// file report
finalReport.exportSummary(reportType, flags.output)
console.log(` - ${chalk.green(formatDate(endDate, "dddd dd MMMM yyyy hh:mm"))}`)
console.log(` - Time Elapsed: ${chalk.green(finalReport.elapsedSeconds.toString())} seconds`)
}
//console.log(init);
}

Expand Down
32 changes: 24 additions & 8 deletions src/tools/urlHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ export const isValidDomain = (target: string) => {
}

export const hasIP = (target: string) => {
if (/(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/.test(target)) {
if (/(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/.test(target)) {
return true
}
return false
}
return false
}

export const isValidIP = (target: string) => {
if (/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(target)) {
return true
}
export const isValidIP = (target: string) => {
if (/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(target)) {
return true
}
return false
}

Expand All @@ -24,6 +24,22 @@ export const isValidURL = (target: string) => {
const regex = new RegExp(urlRegex);
if (target.match(regex)) {
return true
}
}
return false
}

export const extractHostname = (url: string) => {
let hostname;
//find & remove protocol (http, ftp, etc.) and get hostname
if (url.indexOf("//") > -1) {
hostname = url.split('/')[2];
}
else {
hostname = url.split('/')[0];
}
//find & remove port number
hostname = hostname.split(':')[0];
//find & remove "?"
hostname = hostname.split('?')[0];
return hostname;
}
Loading

0 comments on commit 5955201

Please sign in to comment.