Skip to content

Commit

Permalink
🎉 deploy(vestigo): Fully functional first version
Browse files Browse the repository at this point in the history
  • Loading branch information
MedericPixium committed Mar 7, 2020
1 parent 43590d3 commit f724c60
Show file tree
Hide file tree
Showing 8 changed files with 473 additions and 56 deletions.
79 changes: 77 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,77 @@
# vestigo
A tool for exploring and investigating APIs.
# Vestigo

A tool for exploring and investigating APIs and websites.

# Installation

You can install it globally with:

```
yarn global add vestigo
```

```
npm i -g vestigo
```

```
homebrew vestigo
```

# Running

Example:

```
scan --target="https://127.0.0.1/" --method="GET" --no-shortlist
--report="HTML"
```

# Options

```
-h, --help
```
Show CLI help.

```
-t, --target=target
```
The target that Vestigo will scan

```
-m, --method=(GET|POST|BOTH) [default: POST]
```
Requet methods can be: GET, POST, BOTH.

```
-p, --no-parameters
```
Don't use extra parameters on endpoints (default on). Vestigo will try to connect to urls adding get parameters example: https://myurl.com/privacy/2

```
-r, --report=(MD|HTML) [default: MD]
```
Type of report to generate

```
-s, --no-shortlist
```
Use to use the long enpoint list (default on). If the shortlist is disabled Vestigo will use a long list of potential endpoints.

# Report

Vestigo will by default generate a mardown report. You can use parameters to set the report format to HTML.

# To Do

- [ ] Get path disclosures for basic get
- [ ] Set the ssl header on a flag
- [ ] Detect and render in the report if bad ssl check
- [ ] Add whois
- [ ] Add OS analysis from path disclosure
- [ ] Add port scan (known ports)
- [ ] Add verbose parameters
- [ ] Make a request queuing + proxy
- [ ] Add https://www.npmjs.com/package/listr for better logging
- [ ] Add parameter for choosing report save location
18 changes: 16 additions & 2 deletions src/classes/intenseScan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,26 @@ export default class IntenseScan {
console.log(` --- Successfull Requests: ${chalk.green(this.success)}`)
console.log(` --- Failed Requests: ${chalk.green(this.fail)}`)
console.log(` --- Total Requests: ${chalk.green(this.total)}`)
console.log(chalk.green(` --- Successfull Urls`));
console.log(` --- Successfull Urls:`);
const successUrls = this.requestSuccess.map(e => e.url);
successUrls.forEach(e => {
console.log(` ------ ${chalk.cyan(e)}`);
})
//TODO: add verbose
// TODO: add compilation or urls / paths disclosures. (make sure compile all request + unique)
}

getUrlsSuccess() {
return this.requestSuccess.map(e => e.url);
}

getUrlsFail() {
return this.requestFail.map(e => e.url);
}

getAllPathsDisclosures() {
let paths: string[] = [];
this.requestSuccess.map(e => paths.push(...e.pathsDisclosed))
this.requestFail.map(e => paths.push(...e.pathsDisclosed))
return [... new Set(paths)];
}
}
71 changes: 69 additions & 2 deletions src/classes/report.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,86 @@
import { AxiosResponse } from 'axios';
import IntenseScan from './intenseScan';
import { formatDate } from '../tools/dateFormatter';
import * as fs from 'fs';

export enum ReportType {
MARKDOWN,
HTML
}
export default class Report {
target: string;
poweredBy: string;
cors: string;
lastModified: string;
contentType: string;

intenseScan: IntenseScan;
flags: any;
startDate: Date;
endDate: Date;
elapsedSeconds: number;
constructor(response: AxiosResponse) {
this.poweredBy = (response.headers['x-powered-by']) ? response.headers['x-powered-by'] : '';
this.cors = (response.headers['access-control-allow-origin']) ? response.headers['access-control-allow-origin'] : '';
this.lastModified = (response.headers['last-modified']) ? response.headers['last-modified'] : '';
this.contentType = (response.headers['content-type']) ? response.headers['content-type'] : '';
this.intenseScan = new IntenseScan();
this.flags = {};
this.startDate = new Date();
this.endDate = new Date();
this.elapsedSeconds = 0;
this.target = "";
}

exportSummary() {
exportSummary(type: ReportType = ReportType.MARKDOWN) {
let template: string = ``
// Load Tepmplate
if (type == ReportType.HTML) {
template = fs.readFileSync('./templates/report.html', 'utf8');
} else {
template = fs.readFileSync('./templates/report.md', 'utf8');
}
// Override the classic info
template = template.replace("{title}", this.flags.target);
template = template.replace("{title}", this.flags.target);
template = template.replace("{date}", formatDate(this.startDate, "dddd dd MMMM yyyy hh:mm"));
template = template.replace("{target}", this.target);
template = template.replace("{elapsedSeconds}", this.elapsedSeconds.toString());
let tempFlags = ``;
// Building the table for flags
for (const [key, value] of Object.entries(this.flags)) {
if (type == ReportType.HTML) {
tempFlags = tempFlags+`<tr><td>${key}</td><td>${value}</td></tr>`;
} else {
tempFlags = tempFlags+`| ${key} | ${value} |\n`
}
}
template = template.replace("{flags}", tempFlags);
let tempParams = '';
if (type == ReportType.HTML) {
tempParams = `<tr><td>Powered By</td><td>${this.poweredBy}</td></tr>
<tr><td>Cors</td><td>${this.cors}</td></tr>
<tr><td>Last Modified</td><td>${this.lastModified}</td></tr>
<tr><td>Content Type</td><td>${this.contentType}</td></tr>`;
let urls = this.intenseScan.getUrlsSuccess().reduce((accumulator: any, currentValue: any) => {
return accumulator+`<li>${currentValue}</li>`;
})
let paths = this.intenseScan.getAllPathsDisclosures().reduce((accumulator: any, currentValue: any) => {
return accumulator+`<li>${currentValue}</li>`;
})
template = template.replace("{url1}", urls);
template = template.replace("{path1}", paths);
template = template.replace("{params}", tempParams);
fs.writeFileSync("./reportFinal.html", template.toString());
} else {
tempParams = `| Powered By | ${this.poweredBy} |
| CORS | ${this.cors} |
| Last Modified | ${this.lastModified} |
| Content Type | ${this.contentType} |`;
template = template.replace("{url1}", this.intenseScan.getUrlsSuccess().join('\n- '));
template = template.replace("{path1}", this.intenseScan.getAllPathsDisclosures().join('\n- '));
template = template.replace("{params}", tempParams);
fs.writeFileSync("./reportFinal.md", template.toString());
}

}
}
82 changes: 65 additions & 17 deletions src/commands/scan.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,72 @@
import { Command, flags } from '@oclif/command'
import axios, { AxiosResponse } from 'axios';
import * as chalk from 'chalk';
import Report from '../classes/report';
import Report, { ReportType } from '../classes/report';
import {intenseScan} from '../tools/scanTools';
import IntenseScan from '../classes/intenseScan';
import * as https from 'https';
import {formatDate} from '../tools/dateFormatter';
//-----------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------
// TODO: get path disclosures for basic get
// TODO: set the ssl header on a flag
// TODO: detect and render in the report if bad ssl check
// TODO: add whois
// TODO: add OS analysis from path disclosure
// TODO: add port scan (known ports)
//-----------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------
export default class Scan extends Command {
static description = 'Scan an API'

static description = 'Scan an API'
static examples = [
`$ vestigo scan --target="http://127.0.0.1"`,
`$ scan --target="https://127.0.0.1/" --method="GET" --no-shortlist --report="HTML"`,
]

static flags = {
help: flags.help({ char: 'h' }),
// flag with a value (-n, --name=VALUE)
target: flags.string({ char: 't', description: 'target to scan', required: true }),
method: flags.enum({char: 'm', description: 'requet methods can be: GET, POST, BOTH', required: false, options: ["GET", "POST", "BOTH"], default: "POST" }),
shortlist: flags.boolean({ char: 's', description: 'use the shortlist for endpoints', required: true, default: true, allowNo: true}),
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"]}),
}

static args = [{ name: 'file' }]

async run() {
// Get start date
const startDate = new Date();
// 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 ;
// Convert string report typ to actual type
const reportType = (flags.report === "MD") ? ReportType.MARKDOWN : ReportType.HTML;
// Logging
console.log(flags.method);
console.log(` - ${chalk.green(formatDate(startDate, "dddd dd MMMM yyyy hh:mm"))}`)
console.log(` - Targetting: ${chalk.cyan(flags.target)}`)
let init: any;
// Try to contact base url
try {
init = await axios.get(flags.target);

// Disable SSL verification by default
const agent = new https.Agent({
rejectUnauthorized: false
});
init = await axios.get(flags.target, { httpsAgent: agent });
} catch (error) {
//console.log(error)
if (error.code) {
console.log(error.code);
} else {
console.log(error)
}
}
// If base url can be contacted start basic analysis
if (init) {
// Init report
const finalReport = new Report(init);
// If status was valid keep going
if (this.validateStatus(init.status) == true) {
console.log(` - Successfully connected to target`)
console.log(` - Gathering basic header information`)
//console.log(` - Gathering path disclosures`)
// TODO: get path disclosures for basic get
const result = new Report(init)
console.log(` - Target Powered by: ${chalk.cyan(result.poweredBy)}`)
console.log(` - Target Last Modified at: ${chalk.cyan(result.lastModified)}`)
Expand All @@ -45,13 +75,31 @@ export default class Scan extends Command {
} else {
console.log(` - Target ${chalk.cyan('Is CORS protected')}`)
}
} else {
console.log(init)
console.log(init.status)
}
// 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
intenseResult.exportSummary();
finalReport.exportSummary(reportType)
console.log(` - ${chalk.green(formatDate(endDate, "dddd dd MMMM yyyy hh:mm"))}`)
console.log(` - Time Elapsed: ${chalk.green(finalReport.elapsedSeconds)} seconds`)
}
}
console.log(flags.shortlist);
let intenseResult: IntenseScan | void = await intenseScan(flags.target, flags.shortlist, flags.parameters);
if (intenseResult) {
intenseResult.exportSummary();
}


//console.log(init);
}

Expand Down
Loading

0 comments on commit f724c60

Please sign in to comment.