burpsuite-integration.tsā¢15.7 kB
import { exec, spawn, ChildProcess } from 'child_process';
import { promisify } from 'util';
import { ScanResult } from './recon.js';
import axios from 'axios';
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
const execAsync = promisify(exec);
export interface BurpSuiteConfig {
jar_path?: string;
project_file?: string;
api_port?: number;
proxy_port?: number;
headless?: boolean;
user_options?: string;
memory?: string;
}
export interface BurpScanResult {
scan_id: string;
status: string;
issue_count: number;
issues: BurpIssue[];
scan_metrics: {
requests_made: number;
responses_received: number;
scan_duration: number;
};
}
export interface BurpIssue {
serial_number: string;
type: number;
name: string;
host: string;
path: string;
location: string;
severity: string;
confidence: string;
issue_background: string;
remediation_background: string;
issue_detail: string;
evidence: BurpEvidence[];
}
export interface BurpEvidence {
request_response: {
request: string;
response: string;
was_redirect_followed: boolean;
};
}
export interface ProxyHistoryEntry {
host: string;
port: number;
protocol: string;
method: string;
url: string;
path: string;
extension: string;
request: string;
response: string;
status: number;
length: number;
mime_type: string;
comment: string;
}
export class BurpSuiteIntegration {
private burpProcess: ChildProcess | null = null;
private config: BurpSuiteConfig;
private apiBaseUrl: string;
constructor(config: Partial<BurpSuiteConfig> = {}) {
this.config = {
jar_path: config.jar_path || this.findBurpJar(),
project_file: config.project_file,
api_port: config.api_port || 1337,
proxy_port: config.proxy_port || 8080,
headless: config.headless !== false, // Default to headless
user_options: config.user_options || '',
memory: config.memory || '2g',
...config
};
this.apiBaseUrl = `http://127.0.0.1:${this.config.api_port}`;
}
// Start Burp Suite with API enabled
async startBurpSuite(): Promise<ScanResult> {
try {
console.error('š Starting Burp Suite...');
if (!this.config.jar_path || !fs.existsSync(this.config.jar_path)) {
throw new Error('Burp Suite JAR file not found. Please install Burp Suite Professional and set jar_path');
}
// Prepare user options file for headless mode
let userOptionsFile = '';
if (this.config.headless) {
userOptionsFile = await this.createUserOptionsFile();
}
// Build command
const javaArgs = [
`-Xmx${this.config.memory}`,
'-Djava.awt.headless=true',
'-jar', this.config.jar_path,
'--disable-extensions',
`--collaborator-server`,
`--collaborator-location-all`
];
if (this.config.headless) {
javaArgs.push('--unpause-spider-and-scanner');
javaArgs.push(`--user-config-file=${userOptionsFile}`);
}
if (this.config.project_file) {
javaArgs.push(`--project-file=${this.config.project_file}`);
}
console.error(`Executing: java ${javaArgs.join(' ')}`);
// Spawn Burp Suite process
this.burpProcess = spawn('java', javaArgs, {
stdio: ['ignore', 'pipe', 'pipe'],
detached: false
});
// Wait for Burp to start up
await this.waitForBurpStartup();
return {
target: 'burpsuite',
timestamp: new Date().toISOString(),
tool: 'burpsuite_startup',
results: {
status: 'started',
pid: this.burpProcess.pid,
api_url: this.apiBaseUrl,
proxy_port: this.config.proxy_port,
config: this.config
},
status: 'success'
};
} catch (error) {
return {
target: 'burpsuite',
timestamp: new Date().toISOString(),
tool: 'burpsuite_startup',
results: {},
status: 'error',
error: error instanceof Error ? error.message : String(error)
};
}
}
// Stop Burp Suite
async stopBurpSuite(): Promise<ScanResult> {
try {
console.error('š Stopping Burp Suite...');
if (this.burpProcess) {
this.burpProcess.kill('SIGTERM');
this.burpProcess = null;
}
return {
target: 'burpsuite',
timestamp: new Date().toISOString(),
tool: 'burpsuite_shutdown',
results: {
status: 'stopped'
},
status: 'success'
};
} catch (error) {
return {
target: 'burpsuite',
timestamp: new Date().toISOString(),
tool: 'burpsuite_shutdown',
results: {},
status: 'error',
error: error instanceof Error ? error.message : String(error)
};
}
}
// Perform active scan on target
async activeScan(target: string, scope?: string[]): Promise<ScanResult> {
try {
console.error(`š Starting Burp Suite active scan on ${target}`);
// Check if Burp is running
await this.checkBurpStatus();
// Send target to scope if specified
if (scope) {
await this.setScope(scope);
}
// Start spider first
const spiderResult = await this.spiderTarget(target);
// Start active scan
const scanResponse = await axios.post(`${this.apiBaseUrl}/v0.1/scan`, {
urls: [target]
});
const scanId = scanResponse.data.task_id;
console.error(`Scan started with ID: ${scanId}`);
// Wait for scan completion or timeout
const scanResult = await this.waitForScanCompletion(scanId, 1800000); // 30 min timeout
// Get scan results
const issues = await this.getScanIssues(scanId);
return {
target,
timestamp: new Date().toISOString(),
tool: 'burpsuite_active_scan',
results: {
scan_id: scanId,
spider_results: spiderResult,
scan_status: scanResult.status,
issue_count: issues.length,
issues: issues,
severity_breakdown: this.categorizeBySeverity(issues)
},
status: 'success'
};
} catch (error) {
return {
target,
timestamp: new Date().toISOString(),
tool: 'burpsuite_active_scan',
results: {},
status: 'error',
error: error instanceof Error ? error.message : String(error)
};
}
}
// Perform passive scan through proxy
async proxyScan(target: string, duration: number = 300): Promise<ScanResult> {
try {
console.error(`š Starting Burp Suite proxy scan on ${target} for ${duration} seconds`);
// Check if Burp is running
await this.checkBurpStatus();
// Configure proxy
const proxyConfig = {
http_proxy: `http://127.0.0.1:${this.config.proxy_port}`,
https_proxy: `http://127.0.0.1:${this.config.proxy_port}`
};
// Send some requests through proxy to generate traffic
await this.generateProxyTraffic(target, duration);
// Get proxy history
const proxyHistory = await this.getProxyHistory();
// Get passive scan issues
const issues = await this.getPassiveIssues();
return {
target,
timestamp: new Date().toISOString(),
tool: 'burpsuite_proxy_scan',
results: {
proxy_config: proxyConfig,
scan_duration: duration,
requests_captured: proxyHistory.length,
issue_count: issues.length,
issues: issues,
proxy_history: proxyHistory.slice(0, 50), // Limit output
severity_breakdown: this.categorizeBySeverity(issues)
},
status: 'success'
};
} catch (error) {
return {
target,
timestamp: new Date().toISOString(),
tool: 'burpsuite_proxy_scan',
results: {},
status: 'error',
error: error instanceof Error ? error.message : String(error)
};
}
}
// Perform crawl/spider of target
async spiderTarget(target: string): Promise<any> {
try {
console.error(`š·ļø Spidering target: ${target}`);
const spiderResponse = await axios.post(`${this.apiBaseUrl}/v0.1/spider`, {
base_url: target
});
const spiderTaskId = spiderResponse.data.task_id;
// Wait for spider completion
await this.waitForTaskCompletion(spiderTaskId, 600000); // 10 min timeout
// Get spider results
const spiderResults = await axios.get(`${this.apiBaseUrl}/v0.1/spider/${spiderTaskId}`);
return {
task_id: spiderTaskId,
status: spiderResults.data.status,
urls_found: spiderResults.data.urls || []
};
} catch (error) {
console.error('Spider failed:', error);
return { error: error instanceof Error ? error.message : String(error) };
}
}
// Export scan results
async exportResults(format: 'xml' | 'html' | 'json' = 'xml', outputPath?: string): Promise<ScanResult> {
try {
console.error(`š Exporting Burp results in ${format} format`);
const exportResponse = await axios.get(`${this.apiBaseUrl}/v0.1/scan/report`, {
params: { format }
});
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const filename = outputPath || `burp-report-${timestamp}.${format}`;
fs.writeFileSync(filename, exportResponse.data);
return {
target: 'export',
timestamp: new Date().toISOString(),
tool: 'burpsuite_export',
results: {
export_format: format,
output_file: filename,
file_size: fs.statSync(filename).size
},
status: 'success'
};
} catch (error) {
return {
target: 'export',
timestamp: new Date().toISOString(),
tool: 'burpsuite_export',
results: {},
status: 'error',
error: error instanceof Error ? error.message : String(error)
};
}
}
// Private helper methods
private findBurpJar(): string {
const commonPaths = [
'/opt/burpsuite_pro/burpsuite_pro.jar',
'/Applications/Burp Suite Professional.app/Contents/java/app/burpsuite_pro.jar',
path.join(os.homedir(), 'BurpSuitePro', 'burpsuite_pro.jar'),
path.join(os.homedir(), 'Downloads', 'burpsuite_pro.jar'),
'./burpsuite_pro.jar'
];
for (const jarPath of commonPaths) {
if (fs.existsSync(jarPath)) {
return jarPath;
}
}
return '';
}
private async createUserOptionsFile(): Promise<string> {
const userOptions = {
"suite": {
"extensions": {
"extensions": []
}
},
"proxy": {
"intercept_requests": false,
"intercept_responses": false
},
"scanner": {
"live_scanning": {
"live_audit": true,
"live_passive_crawl": true
}
}
};
const tempFile = path.join(os.tmpdir(), `burp-user-options-${Date.now()}.json`);
fs.writeFileSync(tempFile, JSON.stringify(userOptions, null, 2));
return tempFile;
}
private async waitForBurpStartup(): Promise<void> {
const maxAttempts = 60; // 5 minutes
let attempts = 0;
while (attempts < maxAttempts) {
try {
await axios.get(`${this.apiBaseUrl}/v0.1/`, { timeout: 5000 });
console.error('ā
Burp Suite API is ready');
return;
} catch (error) {
attempts++;
console.error(`Waiting for Burp startup... (${attempts}/${maxAttempts})`);
await new Promise(resolve => setTimeout(resolve, 5000));
}
}
throw new Error('Burp Suite failed to start within timeout period');
}
private async checkBurpStatus(): Promise<void> {
try {
await axios.get(`${this.apiBaseUrl}/v0.1/`, { timeout: 5000 });
} catch (error) {
throw new Error('Burp Suite is not running or API is not accessible');
}
}
private async setScope(scope: string[]): Promise<void> {
try {
await axios.put(`${this.apiBaseUrl}/v0.1/scope`, {
include: scope.map(url => ({ url, enabled: true })),
exclude: []
});
} catch (error) {
console.error('Failed to set scope:', error);
}
}
private async waitForScanCompletion(scanId: string, timeout: number): Promise<any> {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
try {
const response = await axios.get(`${this.apiBaseUrl}/v0.1/scan/${scanId}`);
const status = response.data.status;
if (status === 'finished' || status === 'failed') {
return response.data;
}
console.error(`Scan ${scanId} status: ${status}`);
await new Promise(resolve => setTimeout(resolve, 30000)); // Check every 30 seconds
} catch (error) {
console.error('Error checking scan status:', error);
await new Promise(resolve => setTimeout(resolve, 30000));
}
}
throw new Error('Scan timeout exceeded');
}
private async waitForTaskCompletion(taskId: string, timeout: number): Promise<any> {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
try {
const response = await axios.get(`${this.apiBaseUrl}/v0.1/task/${taskId}`);
const status = response.data.status;
if (status === 'finished' || status === 'failed') {
return response.data;
}
await new Promise(resolve => setTimeout(resolve, 10000)); // Check every 10 seconds
} catch (error) {
console.error('Error checking task status:', error);
await new Promise(resolve => setTimeout(resolve, 10000));
}
}
throw new Error('Task timeout exceeded');
}
private async getScanIssues(scanId?: string): Promise<BurpIssue[]> {
try {
const endpoint = scanId ? `/v0.1/scan/${scanId}/issues` : '/v0.1/issues';
const response = await axios.get(`${this.apiBaseUrl}${endpoint}`);
return response.data.issues || [];
} catch (error) {
console.error('Failed to get scan issues:', error);
return [];
}
}
private async getPassiveIssues(): Promise<BurpIssue[]> {
try {
const response = await axios.get(`${this.apiBaseUrl}/v0.1/issues`);
return response.data.issues || [];
} catch (error) {
console.error('Failed to get passive issues:', error);
return [];
}
}
private async getProxyHistory(): Promise<ProxyHistoryEntry[]> {
try {
const response = await axios.get(`${this.apiBaseUrl}/v0.1/proxy/history`);
return response.data.history || [];
} catch (error) {
console.error('Failed to get proxy history:', error);
return [];
}
}
private async generateProxyTraffic(target: string, duration: number): Promise<void> {
// Use curl or similar tool to generate traffic through Burp proxy
const proxyUrl = `http://127.0.0.1:${this.config.proxy_port}`;
try {
// Basic crawling through proxy
const command = `curl -x ${proxyUrl} -k -s "${target}" > /dev/null`;
await execAsync(command, { timeout: duration * 1000 });
} catch (error) {
console.error('Error generating proxy traffic:', error);
}
}
private categorizeBySeverity(issues: BurpIssue[]): Record<string, number> {
const categories = { high: 0, medium: 0, low: 0, info: 0 };
issues.forEach(issue => {
const severity = issue.severity.toLowerCase();
if (severity in categories) {
categories[severity as keyof typeof categories]++;
}
});
return categories;
}
}