mirror of
https://github.com/j3ssie/osmedeus.git
synced 2026-08-29 11:19:42 +02:00
Complete rewrite and re-architecture Osmedeus Engine in v5
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
name: basic-recon-flow
|
||||
kind: flow
|
||||
desc: Basic reconnaissance flow orchestrating subdomain enumeration, port scanning, and screenshots
|
||||
|
||||
params:
|
||||
- name: threads
|
||||
value: "20"
|
||||
- name: timeout
|
||||
value: "3600"
|
||||
|
||||
modules:
|
||||
# Module 1: Subdomain Enumeration
|
||||
- name: subdomain-enum
|
||||
condition: "true"
|
||||
steps:
|
||||
- name: setup-enum
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/subdomains
|
||||
- mkdir -p {{Output}}/resolved
|
||||
exports:
|
||||
subdomain_dir: "{{Output}}/subdomains"
|
||||
|
||||
- name: passive-enum
|
||||
type: bash
|
||||
parallel_commands:
|
||||
- "{{Binaries}}/subfinder -d {{Target}} -silent -o {{subdomain_dir}}/subfinder.txt"
|
||||
- "{{Binaries}}/assetfinder --subs-only {{Target}} > {{subdomain_dir}}/assetfinder.txt"
|
||||
timeout: 600
|
||||
|
||||
- name: merge-subdomains
|
||||
type: bash
|
||||
command: "cat {{subdomain_dir}}/*.txt | sort -u > {{Output}}/all-subdomains.txt"
|
||||
exports:
|
||||
all_subdomains: "{{Output}}/all-subdomains.txt"
|
||||
|
||||
# Module 2: Port Scanning (depends on subdomain-enum)
|
||||
- name: port-scan
|
||||
depends_on:
|
||||
- subdomain-enum
|
||||
condition: "fileLength('{{all_subdomains}}') > 0"
|
||||
steps:
|
||||
- name: setup-ports
|
||||
type: bash
|
||||
command: mkdir -p {{Output}}/ports
|
||||
exports:
|
||||
ports_dir: "{{Output}}/ports"
|
||||
|
||||
- name: scan-ports
|
||||
type: foreach
|
||||
input: "{{all_subdomains}}"
|
||||
variable: subdomain
|
||||
threads: "{{threads}}"
|
||||
step:
|
||||
name: naabu-scan
|
||||
type: bash
|
||||
command: "{{Binaries}}/naabu -host [[subdomain]] -top-ports 100 -silent >> {{ports_dir}}/open-ports.txt"
|
||||
timeout: 120
|
||||
on_error: continue
|
||||
|
||||
- name: aggregate-ports
|
||||
type: bash
|
||||
command: "sort -u {{ports_dir}}/open-ports.txt -o {{Output}}/all-ports.txt"
|
||||
exports:
|
||||
all_ports: "{{Output}}/all-ports.txt"
|
||||
|
||||
# Module 3: Screenshot Capture (depends on port-scan)
|
||||
- name: screenshot
|
||||
depends_on:
|
||||
- port-scan
|
||||
condition: "fileLength('{{all_ports}}') > 0"
|
||||
steps:
|
||||
- name: setup-screenshots
|
||||
type: bash
|
||||
command: mkdir -p {{Output}}/screenshots
|
||||
|
||||
- name: probe-http
|
||||
type: bash
|
||||
command: "{{Binaries}}/httpx -l {{all_subdomains}} -silent -o {{Output}}/http-hosts.txt"
|
||||
timeout: 300
|
||||
exports:
|
||||
http_hosts: "{{Output}}/http-hosts.txt"
|
||||
|
||||
- name: capture-screenshots
|
||||
type: foreach
|
||||
input: "{{http_hosts}}"
|
||||
variable: url
|
||||
threads: 10
|
||||
step:
|
||||
name: gowitness-capture
|
||||
type: bash
|
||||
command: "{{Binaries}}/gowitness single --url=[[url]] --screenshot-path={{Output}}/screenshots --timeout=30"
|
||||
timeout: 60
|
||||
on_error: continue
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
name: cidr-probing
|
||||
kind: module
|
||||
description: Running HTTP fingerprint technology and response with CIDR inputs - demonstrates port scanning, HTTP probing, and result processing
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: inputFile
|
||||
default: "{{Target}}"
|
||||
- name: output_dir
|
||||
default: "{{Output}}/portscan"
|
||||
- name: httpFile
|
||||
default: "{{Output}}/portscan/http-{{Workspace}}.txt"
|
||||
- name: enableScreenshot
|
||||
default: "false"
|
||||
- name: httpTimeout
|
||||
default: "10"
|
||||
- name: ports
|
||||
default: "3000,3128,3333,4243,443,4567,4711,4712,4993,5000,5104,5108,5800,591,593,6443,6543,7000,7396,7474,7779,80,8000,8001,8008,8014,8042,8069,8080,8081,8088,8090,8091,81,8118,8123,8172,8222,8243,8280,8281,832,8333,8443,8500,8834,8880,8888,8983,9000,9043,9060,9080,9090,9091,9200,9443,9800,981,9981,11443,7443,3001,8009"
|
||||
- name: threads
|
||||
default: "10"
|
||||
- name: httpThreads
|
||||
default: "{{threads * 8}}"
|
||||
- name: rateRustScan
|
||||
default: "{{threads * 500}}"
|
||||
|
||||
steps:
|
||||
# ============================================================
|
||||
# Phase 1: Validate Dependencies
|
||||
# ============================================================
|
||||
- name: validate-dependencies
|
||||
type: function
|
||||
function: |
|
||||
fileExists("{{Binaries}}/metabigor") &&
|
||||
fileExists("{{Binaries}}/httpx")
|
||||
exports:
|
||||
deps_valid: "output"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Required binaries (metabigor, httpx) not found"
|
||||
- action: abort
|
||||
|
||||
# ============================================================
|
||||
# Phase 2: Setup Output Directories
|
||||
# ============================================================
|
||||
- name: setup-directories
|
||||
type: bash
|
||||
command: mkdir -p {{output_dir}}
|
||||
|
||||
# ============================================================
|
||||
# Phase 3: Port Scanning with Metabigor
|
||||
# ============================================================
|
||||
- name: port-scanning
|
||||
type: bash
|
||||
command: "cat {{inputFile}} | {{Binaries}}/metabigor scan --rate {{rateRustScan}} -p {{ports}} --pipe >> {{output_dir}}/raw-open-ports.txt"
|
||||
timeout: 1800
|
||||
exports:
|
||||
raw_ports_file: "{{output_dir}}/raw-open-ports.txt"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Port scanning failed"
|
||||
- action: continue
|
||||
|
||||
- name: clean-portscan-results
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{output_dir}}/raw-open-ports.txt")'
|
||||
function: CleanRustScan("{{output_dir}}/raw-open-ports.txt", "{{output_dir}}/open-ports.txt")
|
||||
exports:
|
||||
clean_ports_file: "{{output_dir}}/open-ports.txt"
|
||||
|
||||
- name: count-open-ports
|
||||
type: function
|
||||
function: fileLength("{{output_dir}}/open-ports.txt")
|
||||
exports:
|
||||
open_port_count: "output"
|
||||
|
||||
# Decision: Skip HTTP probing if no open ports found
|
||||
- name: check-port-results
|
||||
type: bash
|
||||
command: "echo {{open_port_count}}"
|
||||
decision:
|
||||
switch: "{{open_port_count}}"
|
||||
cases:
|
||||
"0":
|
||||
goto: generate-empty-report
|
||||
default:
|
||||
goto: http-probing
|
||||
|
||||
# ============================================================
|
||||
# Phase 4: HTTP Probing
|
||||
# ============================================================
|
||||
- name: http-probing
|
||||
type: bash
|
||||
command: "cat {{output_dir}}/open-ports.txt | {{Binaries}}/httpx -nf -timeout {{httpTimeout}} -silent -t {{httpThreads}} >> {{httpFile}}"
|
||||
timeout: 900
|
||||
exports:
|
||||
http_file: "{{httpFile}}"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "HTTP probing failed"
|
||||
- action: continue
|
||||
|
||||
- name: sort-http-results
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{httpFile}}")'
|
||||
function: SortU("{{httpFile}}")
|
||||
|
||||
- name: count-http-hosts
|
||||
type: function
|
||||
function: fileLength("{{httpFile}}")
|
||||
exports:
|
||||
http_host_count: "output"
|
||||
|
||||
# ============================================================
|
||||
# Phase 5: Parallel HTTP Fingerprinting
|
||||
# ============================================================
|
||||
- name: http-fingerprinting
|
||||
type: parallel-steps
|
||||
pre_condition: 'parseInt("{{http_host_count}}") > 0'
|
||||
parallel_steps:
|
||||
- name: httpx-json-fingerprint
|
||||
type: bash
|
||||
command: "cat {{httpFile}} | {{Binaries}}/httpx -nf -timeout {{httpTimeout}} -t {{httpThreads}} -no-color -json -title -tech-detect -status-code -silent >> {{output_dir}}/{{Workspace}}-http-overview.txt"
|
||||
timeout: 1200
|
||||
on_error:
|
||||
- action: log
|
||||
message: "HTTP fingerprinting failed"
|
||||
- action: continue
|
||||
|
||||
- name: extract-technologies
|
||||
type: bash
|
||||
command: "cat {{httpFile}} | {{Binaries}}/httpx -nf -timeout {{httpTimeout}} -t {{httpThreads}} -tech-detect -silent >> {{output_dir}}/{{Workspace}}-technologies.txt"
|
||||
timeout: 600
|
||||
on_error:
|
||||
- action: continue
|
||||
|
||||
# ============================================================
|
||||
# Phase 6: Process HTTP Results
|
||||
# ============================================================
|
||||
- name: clean-http-json
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{output_dir}}/{{Workspace}}-http-overview.txt")'
|
||||
function: CleanJSONHttpx("{{output_dir}}/{{Workspace}}-http-overview.txt", "{{output_dir}}/{{Workspace}}-raw-overview.txt")
|
||||
exports:
|
||||
raw_overview: "{{output_dir}}/{{Workspace}}-raw-overview.txt"
|
||||
|
||||
- name: beautify-results
|
||||
type: bash
|
||||
pre_condition: 'fileExists("{{output_dir}}/{{Workspace}}-raw-overview.txt")'
|
||||
command: "cat {{output_dir}}/{{Workspace}}-raw-overview.txt | csvtk pretty --no-header-row -I -s ' | ' -W 75 > {{output_dir}}/beautify-{{Workspace}}-http.txt"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Beautify failed, copying raw results"
|
||||
- action: run
|
||||
step: fallback-beautify
|
||||
|
||||
- name: fallback-beautify
|
||||
type: bash
|
||||
pre_condition: '!fileExists("{{output_dir}}/beautify-{{Workspace}}-http.txt")'
|
||||
command: "cp {{output_dir}}/{{Workspace}}-raw-overview.txt {{output_dir}}/beautify-{{Workspace}}-http.txt 2>/dev/null || touch {{output_dir}}/beautify-{{Workspace}}-http.txt"
|
||||
|
||||
# ============================================================
|
||||
# Phase 7: Foreach - Detailed Host Analysis
|
||||
# ============================================================
|
||||
- name: detailed-host-analysis
|
||||
type: foreach
|
||||
pre_condition: 'parseInt("{{http_host_count}}") > 0 && parseInt("{{http_host_count}}") < 100'
|
||||
input: "{{httpFile}}"
|
||||
variable: host
|
||||
threads: 5
|
||||
step:
|
||||
name: analyze-single-host
|
||||
type: bash
|
||||
command: |
|
||||
echo "Analyzing [[host]]..."
|
||||
curl -s -I "[[host]]" 2>/dev/null | grep -i "server\|x-powered-by\|content-type" >> {{output_dir}}/headers-{{Workspace}}.txt
|
||||
echo "---" >> {{output_dir}}/headers-{{Workspace}}.txt
|
||||
timeout: 30
|
||||
|
||||
# ============================================================
|
||||
# Phase 8: Generate Reports
|
||||
# ============================================================
|
||||
- name: generate-report
|
||||
type: bash
|
||||
commands:
|
||||
- |
|
||||
echo "=== CIDR Probing Report ===" > {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "Target: {{Target}}" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "Workspace: {{Workspace}}" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "Date: $(date)" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "=== Statistics ===" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "Open Ports Found: {{open_port_count}}" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "HTTP Hosts: {{http_host_count}}" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "=== HTTP Hosts ===" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
cat {{httpFile}} >> {{output_dir}}/final-report-{{Workspace}}.txt 2>/dev/null || echo "No HTTP hosts found"
|
||||
- "cat {{output_dir}}/beautify-{{Workspace}}-http.txt 2>/dev/null || true"
|
||||
|
||||
- name: generate-markdown-report
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{Data}}/markdown/simple-template.md")'
|
||||
function: GenMarkdownReport("{{Data}}/markdown/simple-template.md", "{{Output}}/summary.html")
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Markdown report generation skipped - template not found"
|
||||
- action: continue
|
||||
|
||||
- name: generate-empty-report
|
||||
type: bash
|
||||
pre_condition: '{{open_port_count}} == 0'
|
||||
commands:
|
||||
- |
|
||||
echo "=== CIDR Probing Report ===" > {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "Target: {{Target}}" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "No open ports found for target." >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
- "touch {{output_dir}}/beautify-{{Workspace}}-http.txt"
|
||||
- "touch {{httpFile}}"
|
||||
|
||||
# ============================================================
|
||||
# Phase 9: Cleanup and Notifications
|
||||
# ============================================================
|
||||
- name: final-sort
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{httpFile}}")'
|
||||
function: SortU("{{httpFile}}")
|
||||
|
||||
- name: notify-completion
|
||||
type: function
|
||||
pre_condition: 'parseInt("{{open_port_count}}") > 0'
|
||||
function: printf("CIDR scan complete: {{open_port_count}} open ports, {{http_host_count}} HTTP hosts")
|
||||
@@ -0,0 +1,147 @@
|
||||
name: content-discovery
|
||||
kind: module
|
||||
desc: Directory and file discovery workflow with threaded foreach and parallel execution
|
||||
|
||||
params:
|
||||
- name: threads
|
||||
value: "50"
|
||||
- name: wordlist_small
|
||||
value: "{{Data}}/wordlists/common.txt"
|
||||
- name: wordlist_medium
|
||||
value: "{{Data}}/wordlists/directory-list-2.3-medium.txt"
|
||||
- name: extensions
|
||||
value: "php,asp,aspx,jsp,html,js,json,xml,txt,bak,old,conf"
|
||||
- name: status_codes
|
||||
value: "200,201,204,301,302,307,401,403,405"
|
||||
|
||||
steps:
|
||||
# Step 1: bash - Initialize directories and prepare targets
|
||||
- name: initialize
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/discovery
|
||||
- mkdir -p {{Output}}/endpoints
|
||||
- mkdir -p {{Output}}/parameters
|
||||
- echo "{{Target}}" > {{Output}}/discovery/target.txt
|
||||
exports:
|
||||
discovery_dir: "{{Output}}/discovery"
|
||||
endpoints_dir: "{{Output}}/endpoints"
|
||||
|
||||
# Step 2: function - Determine scan intensity
|
||||
- name: determine-intensity
|
||||
type: function
|
||||
script: |
|
||||
var target = "{{Target}}";
|
||||
var wordlist = "{{wordlist_small}}";
|
||||
var threadCount = parseInt("{{threads}}");
|
||||
|
||||
// Use larger wordlist for known targets
|
||||
if (target.includes(".com") || target.includes(".org")) {
|
||||
wordlist = "{{wordlist_medium}}";
|
||||
log_info("Using medium wordlist for domain target");
|
||||
}
|
||||
|
||||
// Adjust threads based on target
|
||||
if (target.includes("localhost") || target.includes("127.0.0.1")) {
|
||||
threadCount = 100;
|
||||
log_info("Increased threads for local target");
|
||||
}
|
||||
|
||||
return JSON.stringify({wordlist: wordlist, threads: threadCount});
|
||||
exports:
|
||||
scan_config: "{{Result}}"
|
||||
|
||||
# Step 3: parallel-steps - Multiple discovery tools simultaneously
|
||||
- name: parallel-discovery
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: ffuf-scan
|
||||
type: bash
|
||||
command: "{{Binaries}}/ffuf -u {{Target}}/FUZZ -w {{wordlist_small}} -mc {{status_codes}} -t {{threads}} -o {{discovery_dir}}/ffuf.json -of json"
|
||||
timeout: 3600
|
||||
on_error: continue
|
||||
- name: gobuster-scan
|
||||
type: bash
|
||||
command: "{{Binaries}}/gobuster dir -u {{Target}} -w {{wordlist_small}} -t {{threads}} -o {{discovery_dir}}/gobuster.txt --no-error"
|
||||
timeout: 3600
|
||||
on_error: continue
|
||||
- name: feroxbuster-scan
|
||||
type: bash
|
||||
command: "{{Binaries}}/feroxbuster -u {{Target}} -w {{wordlist_small}} -t {{threads}} -o {{discovery_dir}}/feroxbuster.txt --quiet"
|
||||
timeout: 3600
|
||||
on_error: continue
|
||||
|
||||
# Step 4: bash - Extract and merge discovered endpoints
|
||||
- name: merge-discoveries
|
||||
type: bash
|
||||
command: |
|
||||
# Extract URLs from ffuf JSON
|
||||
cat {{discovery_dir}}/ffuf.json 2>/dev/null | jq -r '.results[].url' >> {{discovery_dir}}/all-endpoints.txt
|
||||
# Extract from gobuster
|
||||
grep -oE 'https?://[^ ]+' {{discovery_dir}}/gobuster.txt 2>/dev/null >> {{discovery_dir}}/all-endpoints.txt
|
||||
# Extract from feroxbuster
|
||||
grep -oE 'https?://[^ ]+' {{discovery_dir}}/feroxbuster.txt 2>/dev/null >> {{discovery_dir}}/all-endpoints.txt
|
||||
# Deduplicate
|
||||
sort -u {{discovery_dir}}/all-endpoints.txt -o {{discovery_dir}}/all-endpoints.txt
|
||||
exports:
|
||||
all_endpoints: "{{discovery_dir}}/all-endpoints.txt"
|
||||
|
||||
# Step 5: function - Log discovery statistics
|
||||
- name: log-statistics
|
||||
type: function
|
||||
script: |
|
||||
var count = fileLength("{{all_endpoints}}");
|
||||
log_info("Total unique endpoints discovered: " + count);
|
||||
|
||||
if (count == 0) {
|
||||
log_warn("No endpoints discovered");
|
||||
return "empty";
|
||||
} else if (count > 500) {
|
||||
log_info("Large number of endpoints, will batch process");
|
||||
return "large";
|
||||
}
|
||||
return "normal";
|
||||
|
||||
# Step 6: foreach - Probe each endpoint for parameters
|
||||
- name: parameter-discovery
|
||||
type: foreach
|
||||
pre_condition: "fileLength('{{all_endpoints}}') > 0"
|
||||
input: "{{all_endpoints}}"
|
||||
variable: endpoint
|
||||
threads: "{{threads}}"
|
||||
step:
|
||||
name: probe-endpoint
|
||||
type: bash
|
||||
command: "{{Binaries}}/arjun -u [[endpoint]] -oJ {{endpoints_dir}}/params_$(echo [[endpoint]] | md5sum | cut -d' ' -f1).json"
|
||||
timeout: 120
|
||||
on_error: continue
|
||||
|
||||
# Step 7: parallel-steps - Additional content checks
|
||||
- name: additional-checks
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: wayback-urls
|
||||
type: bash
|
||||
command: "{{Binaries}}/waybackurls {{Target}} | sort -u > {{endpoints_dir}}/wayback.txt"
|
||||
timeout: 600
|
||||
on_error: continue
|
||||
- name: gau-fetch
|
||||
type: bash
|
||||
command: "{{Binaries}}/gau {{Target}} | sort -u > {{endpoints_dir}}/gau.txt"
|
||||
timeout: 600
|
||||
on_error: continue
|
||||
|
||||
# Step 8: bash - Generate final content discovery report
|
||||
- name: finalize-report
|
||||
type: bash
|
||||
parallel_commands:
|
||||
- "cat {{endpoints_dir}}/*.txt 2>/dev/null | sort -u > {{Output}}/all-urls.txt"
|
||||
- "cat {{endpoints_dir}}/*.json 2>/dev/null | jq -s '.' > {{Output}}/all-params.json"
|
||||
- |
|
||||
echo "# Content Discovery Report" > {{Output}}/discovery-report.md
|
||||
echo "Target: {{Target}}" >> {{Output}}/discovery-report.md
|
||||
echo "Endpoints Found: $(wc -l < {{discovery_dir}}/all-endpoints.txt)" >> {{Output}}/discovery-report.md
|
||||
echo "Historical URLs: $(wc -l < {{endpoints_dir}}/wayback.txt 2>/dev/null || echo 0)" >> {{Output}}/discovery-report.md
|
||||
exports:
|
||||
final_urls: "{{Output}}/all-urls.txt"
|
||||
discovery_report: "{{Output}}/discovery-report.md"
|
||||
@@ -0,0 +1,242 @@
|
||||
name: data-processing
|
||||
kind: module
|
||||
desc: Data aggregation and processing workflow with function steps, decision routing, and parallel execution
|
||||
|
||||
params:
|
||||
- name: input_dir
|
||||
value: "{{Output}}"
|
||||
- name: output_format
|
||||
value: "json"
|
||||
- name: max_entries
|
||||
value: "10000"
|
||||
- name: enable_dedup
|
||||
value: "true"
|
||||
|
||||
steps:
|
||||
# Step 1: function - Initialize processing context
|
||||
- name: initialize-context
|
||||
type: function
|
||||
script: |
|
||||
log_info("Data Processing Pipeline Started");
|
||||
log_info("Input Directory: {{input_dir}}");
|
||||
log_info("Output Format: {{output_format}}");
|
||||
|
||||
// Create processing manifest
|
||||
var manifest = {
|
||||
start_time: timestamp(),
|
||||
input_dir: "{{input_dir}}",
|
||||
output_format: "{{output_format}}",
|
||||
files_processed: 0,
|
||||
total_entries: 0
|
||||
};
|
||||
|
||||
writeFile("{{Output}}/processing/manifest.json", JSON.stringify(manifest, null, 2));
|
||||
return true;
|
||||
exports:
|
||||
processing_started: "{{Result}}"
|
||||
|
||||
# Step 2: bash - Setup processing directories
|
||||
- name: setup-processing
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/processing
|
||||
- mkdir -p {{Output}}/aggregated
|
||||
- mkdir -p {{Output}}/reports
|
||||
- find {{input_dir}} -type f \( -name "*.txt" -o -name "*.json" -o -name "*.csv" \) > {{Output}}/processing/file-list.txt
|
||||
exports:
|
||||
file_list: "{{Output}}/processing/file-list.txt"
|
||||
processing_dir: "{{Output}}/processing"
|
||||
aggregated_dir: "{{Output}}/aggregated"
|
||||
|
||||
# Step 3: function with decision - Determine processing strategy
|
||||
- name: determine-strategy
|
||||
type: function
|
||||
script: |
|
||||
var fileCount = fileLength("{{file_list}}");
|
||||
log_info("Files to process: " + fileCount);
|
||||
|
||||
if (fileCount == 0) {
|
||||
log_warn("No files found to process");
|
||||
return "no_data";
|
||||
} else if (fileCount > 100) {
|
||||
log_info("Large dataset, using batch processing");
|
||||
return "batch";
|
||||
} else if (fileCount > 20) {
|
||||
log_info("Medium dataset, using parallel processing");
|
||||
return "parallel";
|
||||
}
|
||||
|
||||
log_info("Small dataset, using sequential processing");
|
||||
return "sequential";
|
||||
exports:
|
||||
processing_strategy: "{{Result}}"
|
||||
decision:
|
||||
switch: "{{processing_strategy}}"
|
||||
cases:
|
||||
"no_data":
|
||||
goto: handle-no-data
|
||||
"batch":
|
||||
goto: batch-processing
|
||||
"parallel":
|
||||
goto: parallel-processing
|
||||
"sequential":
|
||||
goto: sequential-processing
|
||||
|
||||
# Step 4a: foreach - Sequential processing for small datasets
|
||||
- name: sequential-processing
|
||||
type: foreach
|
||||
input: "{{file_list}}"
|
||||
variable: datafile
|
||||
threads: 1
|
||||
step:
|
||||
name: process-file
|
||||
type: bash
|
||||
command: |
|
||||
filename=$(basename "[[datafile]]")
|
||||
extension="${filename##*.}"
|
||||
if [ "$extension" = "json" ]; then
|
||||
cat "[[datafile]]" | jq -c '.' >> {{aggregated_dir}}/combined.jsonl 2>/dev/null || cat "[[datafile]]" >> {{aggregated_dir}}/combined.jsonl
|
||||
else
|
||||
cat "[[datafile]]" >> {{aggregated_dir}}/combined.txt
|
||||
fi
|
||||
on_error: continue
|
||||
exports:
|
||||
processing_mode: "sequential"
|
||||
|
||||
# Step 4b: parallel-steps - Parallel processing for medium datasets
|
||||
- name: parallel-processing
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: process-json-files
|
||||
type: bash
|
||||
command: "find {{input_dir}} -name '*.json' -exec cat {} \\; | jq -c '.' > {{aggregated_dir}}/all-json.jsonl 2>/dev/null || true"
|
||||
timeout: 600
|
||||
on_error: continue
|
||||
- name: process-txt-files
|
||||
type: bash
|
||||
command: "find {{input_dir}} -name '*.txt' -exec cat {} \\; | sort -u > {{aggregated_dir}}/all-txt.txt"
|
||||
timeout: 600
|
||||
on_error: continue
|
||||
- name: process-csv-files
|
||||
type: bash
|
||||
command: "find {{input_dir}} -name '*.csv' -exec tail -n +2 {} \\; > {{aggregated_dir}}/all-csv.csv"
|
||||
timeout: 600
|
||||
on_error: continue
|
||||
exports:
|
||||
processing_mode: "parallel"
|
||||
|
||||
# Step 4c: bash - Batch processing for large datasets
|
||||
- name: batch-processing
|
||||
type: bash
|
||||
command: |
|
||||
# Process in batches of 50 files
|
||||
split -l 50 {{file_list}} {{processing_dir}}/batch_
|
||||
for batch in {{processing_dir}}/batch_*; do
|
||||
while read -r file; do
|
||||
cat "$file" >> {{aggregated_dir}}/batch-output.txt 2>/dev/null
|
||||
done < "$batch"
|
||||
done
|
||||
timeout: 3600
|
||||
exports:
|
||||
processing_mode: "batch"
|
||||
|
||||
# Step 4d: function - Handle no data case
|
||||
- name: handle-no-data
|
||||
type: function
|
||||
script: |
|
||||
log_warn("No data files found for processing");
|
||||
writeFile("{{Output}}/reports/no-data.txt", "No data files found in " + "{{input_dir}}");
|
||||
return false;
|
||||
exports:
|
||||
processing_mode: "skipped"
|
||||
|
||||
# Step 5: function - Deduplicate and clean data
|
||||
- name: deduplicate-data
|
||||
type: function
|
||||
pre_condition: "'{{enable_dedup}}' == 'true'"
|
||||
script: |
|
||||
log_info("Deduplicating aggregated data");
|
||||
|
||||
var txtFile = "{{aggregated_dir}}/all-txt.txt";
|
||||
if (fileExists(txtFile)) {
|
||||
var lineCount = fileLength(txtFile);
|
||||
log_info("Text entries before dedup: " + lineCount);
|
||||
sortUnix(txtFile);
|
||||
var newCount = fileLength(txtFile);
|
||||
log_info("Text entries after dedup: " + newCount);
|
||||
}
|
||||
|
||||
return true;
|
||||
exports:
|
||||
dedup_complete: "{{Result}}"
|
||||
|
||||
# Step 6: parallel-steps - Generate multiple report formats
|
||||
- name: generate-reports
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: json-report
|
||||
type: bash
|
||||
command: |
|
||||
cat {{aggregated_dir}}/*.jsonl 2>/dev/null | head -{{max_entries}} > {{Output}}/reports/data.json
|
||||
echo '{"total": '$(wc -l < {{Output}}/reports/data.json 2>/dev/null || echo 0)'}' > {{Output}}/reports/summary.json
|
||||
on_error: continue
|
||||
- name: csv-report
|
||||
type: bash
|
||||
command: |
|
||||
echo "source,data" > {{Output}}/reports/data.csv
|
||||
cat {{aggregated_dir}}/*.txt 2>/dev/null | head -{{max_entries}} | while read line; do
|
||||
echo "aggregated,\"$line\"" >> {{Output}}/reports/data.csv
|
||||
done
|
||||
on_error: continue
|
||||
- name: markdown-report
|
||||
type: bash
|
||||
command: |
|
||||
cat > {{Output}}/reports/report.md << EOF
|
||||
# Data Processing Report
|
||||
|
||||
**Target:** {{Target}}
|
||||
**Processing Mode:** {{processing_mode}}
|
||||
**Generated:** $(date)
|
||||
|
||||
## Statistics
|
||||
- Input Directory: {{input_dir}}
|
||||
- Files Processed: $(wc -l < {{file_list}})
|
||||
- Output Format: {{output_format}}
|
||||
|
||||
## Files Generated
|
||||
- data.json
|
||||
- data.csv
|
||||
- summary.json
|
||||
EOF
|
||||
|
||||
# Step 7: function - Calculate final statistics
|
||||
- name: calculate-statistics
|
||||
type: function
|
||||
script: |
|
||||
var stats = {
|
||||
processing_mode: "{{processing_mode}}",
|
||||
files_processed: fileLength("{{file_list}}"),
|
||||
dedup_enabled: "{{enable_dedup}}" === "true",
|
||||
output_format: "{{output_format}}",
|
||||
completion_time: timestamp()
|
||||
};
|
||||
|
||||
log_info("Processing Complete:");
|
||||
log_info(" Mode: " + stats.processing_mode);
|
||||
log_info(" Files: " + stats.files_processed);
|
||||
|
||||
writeFile("{{Output}}/reports/stats.json", JSON.stringify(stats, null, 2));
|
||||
return JSON.stringify(stats);
|
||||
exports:
|
||||
final_stats: "{{Result}}"
|
||||
|
||||
# Step 8: bash - Archive and cleanup
|
||||
- name: archive-results
|
||||
type: bash
|
||||
parallel_commands:
|
||||
- "tar -czf {{Output}}/data-archive.tar.gz -C {{Output}} reports aggregated 2>/dev/null || true"
|
||||
- "rm -rf {{processing_dir}}/batch_* 2>/dev/null || true"
|
||||
- "echo 'Processing pipeline completed at:' $(date) > {{Output}}/COMPLETED.txt"
|
||||
exports:
|
||||
archive_file: "{{Output}}/data-archive.tar.gz"
|
||||
pipeline_complete: "true"
|
||||
@@ -0,0 +1,184 @@
|
||||
name: full-assessment-flow
|
||||
kind: flow
|
||||
desc: Complete security assessment flow combining reconnaissance, vulnerability scanning, and data processing
|
||||
|
||||
params:
|
||||
- name: threads
|
||||
value: "30"
|
||||
- name: scan_depth
|
||||
value: "standard"
|
||||
- name: enable_bruteforce
|
||||
value: "false"
|
||||
- name: output_format
|
||||
value: "json"
|
||||
|
||||
modules:
|
||||
# Module 1: Reconnaissance - Asset discovery and enumeration
|
||||
- name: recon
|
||||
condition: "true"
|
||||
steps:
|
||||
- name: init-recon
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/recon/subdomains
|
||||
- mkdir -p {{Output}}/recon/ports
|
||||
- mkdir -p {{Output}}/recon/tech
|
||||
exports:
|
||||
recon_dir: "{{Output}}/recon"
|
||||
subdomain_dir: "{{Output}}/recon/subdomains"
|
||||
ports_dir: "{{Output}}/recon/ports"
|
||||
|
||||
- name: subdomain-discovery
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: subfinder
|
||||
type: bash
|
||||
command: "{{Binaries}}/subfinder -d {{Target}} -silent -o {{subdomain_dir}}/subfinder.txt"
|
||||
timeout: 600
|
||||
- name: amass
|
||||
type: bash
|
||||
command: "{{Binaries}}/amass enum -passive -d {{Target}} -o {{subdomain_dir}}/amass.txt"
|
||||
timeout: 900
|
||||
on_error: continue
|
||||
- name: crt-sh
|
||||
type: bash
|
||||
command: "curl -s 'https://crt.sh/?q=%25.{{Target}}&output=json' | jq -r '.[].name_value' | sort -u > {{subdomain_dir}}/crtsh.txt"
|
||||
timeout: 120
|
||||
on_error: continue
|
||||
|
||||
- name: merge-and-resolve
|
||||
type: bash
|
||||
commands:
|
||||
- cat {{subdomain_dir}}/*.txt | sort -u > {{recon_dir}}/all-subdomains.txt
|
||||
- "{{Binaries}}/dnsx -l {{recon_dir}}/all-subdomains.txt -silent -a -resp -o {{recon_dir}}/resolved.txt"
|
||||
exports:
|
||||
all_subdomains: "{{recon_dir}}/all-subdomains.txt"
|
||||
resolved_hosts: "{{recon_dir}}/resolved.txt"
|
||||
|
||||
# Module 2: Vulnerability Scanning - Security assessment
|
||||
- name: vuln-scan
|
||||
depends_on:
|
||||
- recon
|
||||
condition: "fileLength('{{all_subdomains}}') > 0"
|
||||
steps:
|
||||
- name: init-vulns
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/vulns/nuclei
|
||||
- mkdir -p {{Output}}/vulns/web
|
||||
exports:
|
||||
vulns_dir: "{{Output}}/vulns"
|
||||
|
||||
- name: http-probe
|
||||
type: bash
|
||||
command: "{{Binaries}}/httpx -l {{all_subdomains}} -silent -status-code -title -tech-detect -o {{vulns_dir}}/http-probe.txt"
|
||||
timeout: 900
|
||||
exports:
|
||||
http_hosts: "{{vulns_dir}}/http-probe.txt"
|
||||
|
||||
- name: vulnerability-scans
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: nuclei-critical
|
||||
type: bash
|
||||
command: "{{Binaries}}/nuclei -l {{http_hosts}} -severity critical,high -c {{threads}} -o {{vulns_dir}}/nuclei/critical.json -jsonl"
|
||||
timeout: 3600
|
||||
on_error: continue
|
||||
- name: nuclei-medium
|
||||
type: bash
|
||||
command: "{{Binaries}}/nuclei -l {{http_hosts}} -severity medium -c {{threads}} -o {{vulns_dir}}/nuclei/medium.json -jsonl"
|
||||
timeout: 3600
|
||||
on_error: continue
|
||||
- name: tech-detect
|
||||
type: bash
|
||||
command: "{{Binaries}}/whatweb -i {{http_hosts}} --log-json={{vulns_dir}}/web/tech.json"
|
||||
timeout: 1800
|
||||
on_error: continue
|
||||
exports:
|
||||
critical_vulns: "{{vulns_dir}}/nuclei/critical.json"
|
||||
medium_vulns: "{{vulns_dir}}/nuclei/medium.json"
|
||||
|
||||
# Module 3: Data Processing - Aggregate and report
|
||||
- name: data-processing
|
||||
depends_on:
|
||||
- vuln-scan
|
||||
condition: "true"
|
||||
steps:
|
||||
- name: init-processing
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/reports
|
||||
- mkdir -p {{Output}}/aggregated
|
||||
exports:
|
||||
reports_dir: "{{Output}}/reports"
|
||||
aggregated_dir: "{{Output}}/aggregated"
|
||||
|
||||
- name: aggregate-findings
|
||||
type: function
|
||||
script: |
|
||||
var summary = {
|
||||
target: "{{Target}}",
|
||||
scan_depth: "{{scan_depth}}",
|
||||
timestamp: timestamp(),
|
||||
statistics: {
|
||||
subdomains: fileLength("{{all_subdomains}}"),
|
||||
http_hosts: fileLength("{{http_hosts}}"),
|
||||
critical_findings: 0,
|
||||
medium_findings: 0
|
||||
}
|
||||
};
|
||||
|
||||
if (fileExists("{{critical_vulns}}")) {
|
||||
summary.statistics.critical_findings = fileLength("{{critical_vulns}}");
|
||||
}
|
||||
if (fileExists("{{medium_vulns}}")) {
|
||||
summary.statistics.medium_findings = fileLength("{{medium_vulns}}");
|
||||
}
|
||||
|
||||
log_info("Assessment Summary:");
|
||||
log_info(" Subdomains: " + summary.statistics.subdomains);
|
||||
log_info(" HTTP Hosts: " + summary.statistics.http_hosts);
|
||||
log_info(" Critical: " + summary.statistics.critical_findings);
|
||||
log_info(" Medium: " + summary.statistics.medium_findings);
|
||||
|
||||
writeFile("{{aggregated_dir}}/summary.json", JSON.stringify(summary, null, 2));
|
||||
return JSON.stringify(summary.statistics);
|
||||
exports:
|
||||
assessment_stats: "{{Result}}"
|
||||
|
||||
- name: generate-final-report
|
||||
type: bash
|
||||
command: |
|
||||
cat > {{reports_dir}}/full-assessment.md << 'EOF'
|
||||
# Full Security Assessment Report
|
||||
|
||||
## Target Information
|
||||
- **Target:** {{Target}}
|
||||
- **Scan Depth:** {{scan_depth}}
|
||||
- **Generated:** $(date)
|
||||
|
||||
## Executive Summary
|
||||
This report contains findings from a comprehensive security assessment including:
|
||||
- Subdomain enumeration and DNS resolution
|
||||
- HTTP service discovery and technology detection
|
||||
- Vulnerability scanning with multiple severity levels
|
||||
|
||||
## Statistics
|
||||
{{assessment_stats}}
|
||||
|
||||
## Methodology
|
||||
1. **Reconnaissance**: Passive and active subdomain enumeration
|
||||
2. **Service Discovery**: HTTP probing and technology fingerprinting
|
||||
3. **Vulnerability Assessment**: Template-based scanning for known vulnerabilities
|
||||
|
||||
## Files Generated
|
||||
- `recon/all-subdomains.txt` - Discovered subdomains
|
||||
- `vulns/nuclei/*.json` - Vulnerability findings
|
||||
- `aggregated/summary.json` - Machine-readable summary
|
||||
|
||||
## Recommendations
|
||||
Review all critical and high severity findings immediately.
|
||||
Medium severity findings should be addressed in the next security sprint.
|
||||
EOF
|
||||
exports:
|
||||
final_report: "{{reports_dir}}/full-assessment.md"
|
||||
@@ -0,0 +1,32 @@
|
||||
name: http-probing
|
||||
kind: module
|
||||
description: Running HTTP fingerprint technology and response with the supplied inputs
|
||||
|
||||
params:
|
||||
- name: inputFile
|
||||
default: "{{Target}}"
|
||||
- name: httpFile
|
||||
default: "{{Output}}/fingerprint/http-{{Workspace}}.txt"
|
||||
- name: httpThreads
|
||||
default: "{{ threads * 10 }}"
|
||||
- name: httpTimeout
|
||||
default: "10"
|
||||
- name: defaultUA
|
||||
default: "User-Agent: Mozilla/5.0 (compatible; Osmedeus/v4; +https://github.com/j3ssie/osmedeus)"
|
||||
|
||||
steps:
|
||||
- name: httpx-probe
|
||||
type: bash
|
||||
commands:
|
||||
- "echo {{inputFile}} | {{Binaries}}/httpx -nf -timeout {{httpTimeout}} -silent -t {{httpThreads}} >> {{httpFile}}"
|
||||
|
||||
- name: httpx-fingerprint
|
||||
type: bash
|
||||
pre_condition: "fileExists('{{httpFile}}')"
|
||||
command: >
|
||||
cat {{httpFile}} | {{Binaries}}/httpx -H '{{defaultUA}}' -timeout {{httpTimeout}}
|
||||
-t {{httpThreads}} -no-fallback -no-color -silent -json -title -favicon
|
||||
-hash sha256 -jarm -tech-detect -status-code -cdn -tls-grab -ztls -vhost
|
||||
-follow-host-redirects -include-chain -store-response
|
||||
-store-response-dir {{Output}}/fingerprint/raw-data
|
||||
>> {{Output}}/fingerprint/{{Workspace}}-http-overview.txt
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
name: port-scanning
|
||||
kind: module
|
||||
desc: Port and service scanning workflow with decision routing and parallel execution
|
||||
|
||||
params:
|
||||
- name: threads
|
||||
value: "50"
|
||||
- name: rate_limit
|
||||
value: "1000"
|
||||
- name: ports
|
||||
value: "top-1000"
|
||||
- name: scan_type
|
||||
value: "standard"
|
||||
|
||||
steps:
|
||||
# Step 1: bash - Setup directories
|
||||
- name: setup
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/ports
|
||||
- mkdir -p {{Output}}/services
|
||||
- mkdir -p {{Output}}/banners
|
||||
exports:
|
||||
ports_dir: "{{Output}}/ports"
|
||||
services_dir: "{{Output}}/services"
|
||||
|
||||
# Step 2: function - Determine port range based on scan_type
|
||||
- name: configure-ports
|
||||
type: function
|
||||
script: |
|
||||
var scanType = "{{scan_type}}";
|
||||
var portRange = "";
|
||||
if (scanType == "quick") {
|
||||
portRange = "21,22,23,25,80,110,143,443,445,3306,3389,8080";
|
||||
} else if (scanType == "full") {
|
||||
portRange = "1-65535";
|
||||
} else {
|
||||
portRange = "1-10000";
|
||||
}
|
||||
log_info("Port range: " + portRange + " for scan type: " + scanType);
|
||||
return portRange;
|
||||
exports:
|
||||
port_range: "{{Result}}"
|
||||
|
||||
# Step 3: parallel-steps - Run multiple port scanners
|
||||
- name: port-discovery
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: naabu-scan
|
||||
type: bash
|
||||
command: "{{Binaries}}/naabu -host {{Target}} -p {{port_range}} -rate {{rate_limit}} -silent -o {{ports_dir}}/naabu.txt"
|
||||
timeout: 3600
|
||||
- name: masscan-scan
|
||||
type: bash
|
||||
command: "{{Binaries}}/masscan {{Target}} -p{{port_range}} --rate={{rate_limit}} -oL {{ports_dir}}/masscan.txt"
|
||||
timeout: 3600
|
||||
on_error: continue
|
||||
|
||||
# Step 4: bash - Merge port results
|
||||
- name: merge-ports
|
||||
type: bash
|
||||
command: |
|
||||
cat {{ports_dir}}/naabu.txt 2>/dev/null | sort -u > {{ports_dir}}/all-ports.txt
|
||||
grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+' {{ports_dir}}/masscan.txt 2>/dev/null | sort -u >> {{ports_dir}}/all-ports.txt
|
||||
sort -u {{ports_dir}}/all-ports.txt -o {{ports_dir}}/all-ports.txt
|
||||
exports:
|
||||
open_ports: "{{ports_dir}}/all-ports.txt"
|
||||
|
||||
# Step 5: function with decision - Check port count and route
|
||||
- name: analyze-results
|
||||
type: function
|
||||
script: |
|
||||
var portCount = fileLength("{{open_ports}}");
|
||||
log_info("Total open ports found: " + portCount);
|
||||
if (portCount == 0) {
|
||||
return "no_ports";
|
||||
} else if (portCount > 100) {
|
||||
return "many_ports";
|
||||
}
|
||||
return "normal";
|
||||
exports:
|
||||
port_analysis_result: "{{Result}}"
|
||||
decision:
|
||||
switch: "{{port_analysis_result}}"
|
||||
cases:
|
||||
"no_ports":
|
||||
goto: skip-service-detection
|
||||
"many_ports":
|
||||
goto: batch-service-detection
|
||||
"normal":
|
||||
goto: service-detection
|
||||
|
||||
# Step 6: foreach - Standard service detection
|
||||
- name: service-detection
|
||||
type: foreach
|
||||
input: "{{open_ports}}"
|
||||
variable: target_port
|
||||
threads: "{{threads}}"
|
||||
step:
|
||||
name: detect-service
|
||||
type: bash
|
||||
command: "{{Binaries}}/nmap -sV -sC -p [[target_port]] -oN {{services_dir}}/[[target_port]].txt {{Target}}"
|
||||
timeout: 120
|
||||
on_error: continue
|
||||
exports:
|
||||
detection_complete: "true"
|
||||
|
||||
# Step 7: bash - Batch service detection for many ports
|
||||
- name: batch-service-detection
|
||||
type: bash
|
||||
pre_condition: "fileLength('{{open_ports}}') > 100"
|
||||
command: "{{Binaries}}/nmap -sV --version-intensity 5 -iL {{open_ports}} -oN {{services_dir}}/batch-scan.txt"
|
||||
timeout: 7200
|
||||
exports:
|
||||
detection_complete: "true"
|
||||
|
||||
# Step 8: bash - Final report generation
|
||||
- name: generate-report
|
||||
type: bash
|
||||
parallel_commands:
|
||||
- "cat {{services_dir}}/*.txt 2>/dev/null | grep -E 'open|filtered' > {{Output}}/service-summary.txt"
|
||||
- "echo 'Port Scan Report for {{Target}}' > {{Output}}/report.txt && date >> {{Output}}/report.txt && wc -l {{open_ports}} >> {{Output}}/report.txt"
|
||||
exports:
|
||||
port_report: "{{Output}}/report.txt"
|
||||
|
||||
# Placeholder for skip case
|
||||
- name: skip-service-detection
|
||||
type: function
|
||||
script: |
|
||||
log_warn("No open ports found, skipping service detection");
|
||||
return true;
|
||||
@@ -0,0 +1,178 @@
|
||||
name: screenshot-capture
|
||||
kind: module
|
||||
desc: Web screenshot and visual analysis workflow with pre_condition checks and exports
|
||||
|
||||
params:
|
||||
- name: threads
|
||||
value: "10"
|
||||
- name: timeout_per_page
|
||||
value: "30"
|
||||
- name: viewport_width
|
||||
value: "1920"
|
||||
- name: viewport_height
|
||||
value: "1080"
|
||||
|
||||
steps:
|
||||
# Step 1: bash - Setup screenshot directories
|
||||
- name: setup-directories
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/screenshots
|
||||
- mkdir -p {{Output}}/thumbnails
|
||||
- mkdir -p {{Output}}/analysis
|
||||
exports:
|
||||
screenshots_dir: "{{Output}}/screenshots"
|
||||
thumbnails_dir: "{{Output}}/thumbnails"
|
||||
analysis_dir: "{{Output}}/analysis"
|
||||
|
||||
# Step 2: function - Validate URL input and prepare target list
|
||||
- name: prepare-targets
|
||||
type: function
|
||||
script: |
|
||||
var target = "{{Target}}";
|
||||
var urls = [];
|
||||
|
||||
// Check if target is a file or single URL
|
||||
if (fileExists(target)) {
|
||||
log_info("Target is a file, reading URLs");
|
||||
var content = readFile(target);
|
||||
urls = content.trim().split("\n").filter(function(u) { return u.length > 0; });
|
||||
} else {
|
||||
// Ensure URL has protocol
|
||||
if (!target.startsWith("http")) {
|
||||
target = "https://" + target;
|
||||
}
|
||||
urls = [target];
|
||||
log_info("Single target mode: " + target);
|
||||
}
|
||||
|
||||
log_info("Total URLs to screenshot: " + urls.length);
|
||||
writeFile("{{Output}}/url-list.txt", urls.join("\n"));
|
||||
return urls.length;
|
||||
exports:
|
||||
url_count: "{{Result}}"
|
||||
url_list: "{{Output}}/url-list.txt"
|
||||
|
||||
# Step 3: bash with pre_condition - Quick probe to filter live URLs
|
||||
- name: probe-live-urls
|
||||
type: bash
|
||||
pre_condition: "fileExists('{{url_list}}')"
|
||||
command: "{{Binaries}}/httpx -l {{url_list}} -silent -mc 200,201,301,302,307,401,403 -o {{Output}}/live-urls.txt"
|
||||
timeout: 600
|
||||
on_error: continue
|
||||
exports:
|
||||
live_urls: "{{Output}}/live-urls.txt"
|
||||
|
||||
# Step 4: function - Check live URL count before proceeding
|
||||
- name: validate-live-urls
|
||||
type: function
|
||||
pre_condition: "fileExists('{{live_urls}}')"
|
||||
script: |
|
||||
var count = fileLength("{{live_urls}}");
|
||||
log_info("Live URLs found: " + count);
|
||||
|
||||
if (count == 0) {
|
||||
log_warn("No live URLs found, screenshots may fail");
|
||||
return false;
|
||||
}
|
||||
|
||||
return count > 0;
|
||||
exports:
|
||||
has_live_urls: "{{Result}}"
|
||||
|
||||
# Step 5: foreach - Capture screenshots of each URL
|
||||
- name: capture-screenshots
|
||||
type: foreach
|
||||
pre_condition: "fileExists('{{live_urls}}') && fileLength('{{live_urls}}') > 0"
|
||||
input: "{{live_urls}}"
|
||||
variable: url
|
||||
threads: "{{threads}}"
|
||||
step:
|
||||
name: screenshot-single
|
||||
type: bash
|
||||
command: |
|
||||
filename=$(echo "[[url]]" | md5sum | cut -d' ' -f1)
|
||||
{{Binaries}}/gowitness single --url="[[url]]" --screenshot-path={{screenshots_dir}} --screenshot-filename=${filename}.png --timeout={{timeout_per_page}}
|
||||
timeout: 60
|
||||
on_error: continue
|
||||
|
||||
# Step 6: parallel-steps - Generate thumbnails and analyze
|
||||
- name: process-screenshots
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: generate-thumbnails
|
||||
type: bash
|
||||
command: |
|
||||
for img in {{screenshots_dir}}/*.png; do
|
||||
if [ -f "$img" ]; then
|
||||
filename=$(basename "$img" .png)
|
||||
convert "$img" -resize 400x300 {{thumbnails_dir}}/${filename}_thumb.png 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
timeout: 300
|
||||
on_error: continue
|
||||
- name: extract-metadata
|
||||
type: bash
|
||||
command: |
|
||||
echo "[]" > {{analysis_dir}}/metadata.json
|
||||
for img in {{screenshots_dir}}/*.png; do
|
||||
if [ -f "$img" ]; then
|
||||
size=$(stat -f%z "$img" 2>/dev/null || stat -c%s "$img" 2>/dev/null)
|
||||
echo "{\"file\": \"$(basename $img)\", \"size\": $size}" >> {{analysis_dir}}/metadata.json
|
||||
fi
|
||||
done
|
||||
timeout: 120
|
||||
|
||||
# Step 7: function - Generate screenshot statistics
|
||||
- name: generate-stats
|
||||
type: function
|
||||
script: |
|
||||
var stats = {
|
||||
total_urls: parseInt("{{url_count}}"),
|
||||
live_urls: fileLength("{{live_urls}}"),
|
||||
screenshots: 0,
|
||||
thumbnails: 0
|
||||
};
|
||||
|
||||
// Count screenshot files
|
||||
var screenshotDir = "{{screenshots_dir}}";
|
||||
log_info("Screenshot capture statistics:");
|
||||
log_info(" Total URLs: " + stats.total_urls);
|
||||
log_info(" Live URLs: " + stats.live_urls);
|
||||
|
||||
writeFile("{{analysis_dir}}/stats.json", JSON.stringify(stats, null, 2));
|
||||
return JSON.stringify(stats);
|
||||
exports:
|
||||
screenshot_stats: "{{Result}}"
|
||||
|
||||
# Step 8: bash - Create HTML gallery and final report
|
||||
- name: create-gallery
|
||||
type: bash
|
||||
commands:
|
||||
- |
|
||||
cat > {{Output}}/gallery.html << 'HTMLEOF'
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Screenshot Gallery - {{Target}}</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||||
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); gap: 20px; }
|
||||
.item { border: 1px solid #ddd; padding: 10px; }
|
||||
.item img { max-width: 100%; height: auto; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Screenshot Gallery</h1>
|
||||
<p>Target: {{Target}}</p>
|
||||
<div class="grid">
|
||||
HTMLEOF
|
||||
- |
|
||||
for img in {{screenshots_dir}}/*.png; do
|
||||
if [ -f "$img" ]; then
|
||||
filename=$(basename "$img")
|
||||
echo "<div class='item'><img src='screenshots/$filename'><p>$filename</p></div>" >> {{Output}}/gallery.html
|
||||
fi
|
||||
done
|
||||
echo "</div></body></html>" >> {{Output}}/gallery.html
|
||||
exports:
|
||||
gallery_html: "{{Output}}/gallery.html"
|
||||
@@ -0,0 +1,102 @@
|
||||
name: subdomain-enumeration
|
||||
kind: module
|
||||
desc: Comprehensive subdomain enumeration workflow demonstrating various step types
|
||||
|
||||
params:
|
||||
- name: threads
|
||||
value: "10"
|
||||
- name: resolvers
|
||||
value: "{{Data}}/resolvers.txt"
|
||||
- name: wordlist
|
||||
value: "{{Data}}/subdomains-top1million-5000.txt"
|
||||
|
||||
steps:
|
||||
# Step 1: bash - Initialize output directories
|
||||
- name: setup-directories
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/subdomains
|
||||
- mkdir -p {{Output}}/resolved
|
||||
- mkdir -p {{Output}}/wordlists
|
||||
exports:
|
||||
subdomain_dir: "{{Output}}/subdomains"
|
||||
resolved_dir: "{{Output}}/resolved"
|
||||
|
||||
# Step 2: function - Log start and validate target
|
||||
- name: validate-target
|
||||
type: function
|
||||
script: |
|
||||
log_info("Starting subdomain enumeration for: {{Target}}");
|
||||
if (isEmpty("{{Target}}")) {
|
||||
log_error("Target is empty");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
# Step 3: parallel-steps - Run multiple passive enumeration tools concurrently
|
||||
- name: passive-enumeration
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: subfinder-scan
|
||||
type: bash
|
||||
command: "{{Binaries}}/subfinder -d {{Target}} -silent -o {{subdomain_dir}}/subfinder.txt"
|
||||
timeout: 600
|
||||
- name: amass-passive
|
||||
type: bash
|
||||
command: "{{Binaries}}/amass enum -passive -d {{Target}} -o {{subdomain_dir}}/amass.txt"
|
||||
timeout: 900
|
||||
- name: assetfinder-scan
|
||||
type: bash
|
||||
command: "{{Binaries}}/assetfinder --subs-only {{Target}} > {{subdomain_dir}}/assetfinder.txt"
|
||||
timeout: 300
|
||||
|
||||
# Step 4: bash - Merge and deduplicate results
|
||||
- name: merge-results
|
||||
type: bash
|
||||
command: "cat {{subdomain_dir}}/*.txt | sort -u > {{subdomain_dir}}/all-subdomains.txt"
|
||||
exports:
|
||||
all_subdomains: "{{subdomain_dir}}/all-subdomains.txt"
|
||||
|
||||
# Step 5: function - Check if we found any subdomains
|
||||
- name: check-results
|
||||
type: function
|
||||
script: |
|
||||
var count = fileLength("{{all_subdomains}}");
|
||||
log_info("Found " + count + " unique subdomains");
|
||||
if (count == 0) {
|
||||
log_warn("No subdomains found, trying bruteforce");
|
||||
}
|
||||
return count;
|
||||
exports:
|
||||
subdomain_count: "{{Result}}"
|
||||
|
||||
# Step 6: bash with pre_condition - Active bruteforce if passive found few results
|
||||
- name: active-bruteforce
|
||||
type: bash
|
||||
pre_condition: "fileLength('{{all_subdomains}}') < 50"
|
||||
command: "{{Binaries}}/puredns bruteforce {{wordlist}} {{Target}} -r {{resolvers}} -w {{subdomain_dir}}/bruteforce.txt"
|
||||
timeout: 1800
|
||||
on_error: continue
|
||||
|
||||
# Step 7: foreach - Resolve each subdomain for live hosts
|
||||
- name: resolve-subdomains
|
||||
type: foreach
|
||||
input: "{{all_subdomains}}"
|
||||
variable: subdomain
|
||||
threads: "{{threads}}"
|
||||
step:
|
||||
name: resolve-single
|
||||
type: bash
|
||||
command: "echo [[subdomain]] | {{Binaries}}/dnsx -silent -a -resp -o {{resolved_dir}}/[[subdomain]].txt"
|
||||
timeout: 30
|
||||
on_error: continue
|
||||
|
||||
# Step 8: bash with parallel_commands - Final aggregation
|
||||
- name: final-aggregation
|
||||
type: bash
|
||||
parallel_commands:
|
||||
- "cat {{resolved_dir}}/*.txt 2>/dev/null | grep -v '^$' | sort -u > {{Output}}/resolved-subdomains.txt"
|
||||
- "wc -l {{subdomain_dir}}/all-subdomains.txt | awk '{print $1}' > {{Output}}/stats.txt"
|
||||
- "echo 'Enumeration completed at:' $(date) >> {{Output}}/stats.txt"
|
||||
exports:
|
||||
final_subdomains: "{{Output}}/resolved-subdomains.txt"
|
||||
@@ -0,0 +1,143 @@
|
||||
name: vulnerability-assessment
|
||||
kind: module
|
||||
desc: Vulnerability scanning with Docker runner and comprehensive error handling
|
||||
|
||||
runner: docker
|
||||
runner_config:
|
||||
image: "osmedeus/scanner:latest"
|
||||
volumes:
|
||||
- "{{Output}}:/output"
|
||||
- "{{Data}}:/data"
|
||||
network: "host"
|
||||
|
||||
params:
|
||||
- name: threads
|
||||
value: "25"
|
||||
- name: severity
|
||||
value: "medium,high,critical"
|
||||
- name: templates_path
|
||||
value: "{{Data}}/nuclei-templates"
|
||||
|
||||
steps:
|
||||
# Step 1: bash - Setup scan environment
|
||||
- name: setup-environment
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/vulns
|
||||
- mkdir -p {{Output}}/findings
|
||||
- mkdir -p {{Output}}/raw
|
||||
exports:
|
||||
vulns_dir: "{{Output}}/vulns"
|
||||
findings_dir: "{{Output}}/findings"
|
||||
|
||||
# Step 2: function - Validate inputs and log configuration
|
||||
- name: validate-config
|
||||
type: function
|
||||
script: |
|
||||
log_info("Vulnerability Assessment Configuration:");
|
||||
log_info(" Target: {{Target}}");
|
||||
log_info(" Severity: {{severity}}");
|
||||
log_info(" Threads: {{threads}}");
|
||||
|
||||
if (!fileExists("{{templates_path}}")) {
|
||||
log_warn("Templates path not found, using default");
|
||||
}
|
||||
return true;
|
||||
|
||||
# Step 3: remote-bash (docker) - Run nuclei scan
|
||||
- name: nuclei-scan
|
||||
type: remote-bash
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: "projectdiscovery/nuclei:latest"
|
||||
volumes:
|
||||
- "{{Output}}:/output"
|
||||
- "{{templates_path}}:/templates"
|
||||
command: "nuclei -u {{Target}} -t /templates -severity {{severity}} -c {{threads}} -o /output/vulns/nuclei.json -jsonl"
|
||||
timeout: 7200
|
||||
on_error: continue
|
||||
exports:
|
||||
nuclei_results: "{{vulns_dir}}/nuclei.json"
|
||||
|
||||
# Step 4: bash - Run local vulnerability checks
|
||||
- name: local-vuln-checks
|
||||
type: bash
|
||||
parallel_commands:
|
||||
- "{{Binaries}}/nikto -h {{Target}} -output {{vulns_dir}}/nikto.txt -Format txt"
|
||||
- "{{Binaries}}/whatweb {{Target}} --log-json={{vulns_dir}}/whatweb.json"
|
||||
timeout: 1800
|
||||
on_error: continue
|
||||
|
||||
# Step 5: foreach - Check each finding for exploitability
|
||||
- name: verify-findings
|
||||
type: foreach
|
||||
pre_condition: "fileExists('{{nuclei_results}}')"
|
||||
input: "{{nuclei_results}}"
|
||||
variable: finding
|
||||
threads: 5
|
||||
step:
|
||||
name: verify-single
|
||||
type: function
|
||||
script: |
|
||||
var finding = "[[finding]]";
|
||||
log_debug("Verifying finding: " + finding);
|
||||
return true;
|
||||
on_error: continue
|
||||
|
||||
# Step 6: parallel-steps - Additional scanning modules
|
||||
- name: extended-scanning
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: ssl-check
|
||||
type: bash
|
||||
command: "{{Binaries}}/testssl --jsonfile={{vulns_dir}}/ssl.json {{Target}}"
|
||||
timeout: 600
|
||||
on_error: continue
|
||||
- name: header-check
|
||||
type: bash
|
||||
command: "curl -sI {{Target}} | tee {{vulns_dir}}/headers.txt"
|
||||
timeout: 60
|
||||
- name: cors-check
|
||||
type: bash
|
||||
command: "{{Binaries}}/corsy -u {{Target}} -o {{vulns_dir}}/cors.json"
|
||||
timeout: 300
|
||||
on_error: continue
|
||||
|
||||
# Step 7: function - Aggregate and calculate risk score
|
||||
- name: calculate-risk
|
||||
type: function
|
||||
script: |
|
||||
var critical = 0;
|
||||
var high = 0;
|
||||
var medium = 0;
|
||||
|
||||
if (fileExists("{{nuclei_results}}")) {
|
||||
var content = readFile("{{nuclei_results}}");
|
||||
critical = (content.match(/critical/gi) || []).length;
|
||||
high = (content.match(/high/gi) || []).length;
|
||||
medium = (content.match(/medium/gi) || []).length;
|
||||
}
|
||||
|
||||
var riskScore = (critical * 10) + (high * 5) + (medium * 2);
|
||||
log_info("Risk Score: " + riskScore);
|
||||
log_info("Critical: " + critical + ", High: " + high + ", Medium: " + medium);
|
||||
|
||||
writeFile("{{findings_dir}}/risk-score.txt", "Risk Score: " + riskScore);
|
||||
return riskScore;
|
||||
exports:
|
||||
risk_score: "{{Result}}"
|
||||
|
||||
# Step 8: bash - Generate final vulnerability report
|
||||
- name: generate-vuln-report
|
||||
type: bash
|
||||
commands:
|
||||
- |
|
||||
echo "# Vulnerability Assessment Report" > {{Output}}/vuln-report.md
|
||||
echo "Target: {{Target}}" >> {{Output}}/vuln-report.md
|
||||
echo "Date: $(date)" >> {{Output}}/vuln-report.md
|
||||
echo "Risk Score: {{risk_score}}" >> {{Output}}/vuln-report.md
|
||||
echo "" >> {{Output}}/vuln-report.md
|
||||
echo "## Findings" >> {{Output}}/vuln-report.md
|
||||
cat {{vulns_dir}}/nuclei.json 2>/dev/null | head -50 >> {{Output}}/vuln-report.md
|
||||
exports:
|
||||
vuln_report: "{{Output}}/vuln-report.md"
|
||||
@@ -0,0 +1,130 @@
|
||||
name: vulnerability-flow
|
||||
kind: flow
|
||||
desc: Vulnerability assessment flow with discovery, scanning, and reporting modules
|
||||
|
||||
params:
|
||||
- name: threads
|
||||
value: "25"
|
||||
- name: severity
|
||||
value: "medium,high,critical"
|
||||
- name: templates
|
||||
value: "{{Data}}/nuclei-templates"
|
||||
|
||||
modules:
|
||||
# Module 1: Discovery - Find attack surface
|
||||
- name: discovery
|
||||
condition: "true"
|
||||
steps:
|
||||
- name: init-discovery
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/discovery
|
||||
- mkdir -p {{Output}}/endpoints
|
||||
exports:
|
||||
discovery_dir: "{{Output}}/discovery"
|
||||
endpoints_dir: "{{Output}}/endpoints"
|
||||
|
||||
- name: find-endpoints
|
||||
type: bash
|
||||
parallel_commands:
|
||||
- "{{Binaries}}/waybackurls {{Target}} > {{endpoints_dir}}/wayback.txt"
|
||||
- "{{Binaries}}/gau {{Target}} > {{endpoints_dir}}/gau.txt"
|
||||
- "{{Binaries}}/katana -u {{Target}} -silent -o {{endpoints_dir}}/katana.txt"
|
||||
timeout: 900
|
||||
on_error: continue
|
||||
|
||||
- name: merge-endpoints
|
||||
type: bash
|
||||
command: |
|
||||
cat {{endpoints_dir}}/*.txt | sort -u > {{discovery_dir}}/all-endpoints.txt
|
||||
grep -E '\.(php|asp|aspx|jsp|cgi)' {{discovery_dir}}/all-endpoints.txt > {{discovery_dir}}/dynamic-endpoints.txt || true
|
||||
exports:
|
||||
all_endpoints: "{{discovery_dir}}/all-endpoints.txt"
|
||||
dynamic_endpoints: "{{discovery_dir}}/dynamic-endpoints.txt"
|
||||
|
||||
# Module 2: Scanning - Run vulnerability scanners
|
||||
- name: scanning
|
||||
depends_on:
|
||||
- discovery
|
||||
condition: "fileLength('{{all_endpoints}}') > 0"
|
||||
steps:
|
||||
- name: init-scanning
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{Output}}/vulns
|
||||
- mkdir -p {{Output}}/findings
|
||||
exports:
|
||||
vulns_dir: "{{Output}}/vulns"
|
||||
findings_dir: "{{Output}}/findings"
|
||||
|
||||
- name: nuclei-scan
|
||||
type: bash
|
||||
command: "{{Binaries}}/nuclei -l {{all_endpoints}} -t {{templates}} -severity {{severity}} -c {{threads}} -o {{vulns_dir}}/nuclei.json -jsonl"
|
||||
timeout: 7200
|
||||
on_error: continue
|
||||
exports:
|
||||
nuclei_results: "{{vulns_dir}}/nuclei.json"
|
||||
|
||||
- name: additional-scans
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: xss-scan
|
||||
type: bash
|
||||
command: "cat {{dynamic_endpoints}} | {{Binaries}}/dalfox pipe -o {{vulns_dir}}/xss.txt"
|
||||
timeout: 3600
|
||||
on_error: continue
|
||||
- name: sqli-check
|
||||
type: bash
|
||||
command: "{{Binaries}}/sqlmap -m {{dynamic_endpoints}} --batch --output-dir={{vulns_dir}}/sqli"
|
||||
timeout: 3600
|
||||
on_error: continue
|
||||
|
||||
# Module 3: Reporting - Generate vulnerability reports
|
||||
- name: reporting
|
||||
depends_on:
|
||||
- scanning
|
||||
condition: "true"
|
||||
steps:
|
||||
- name: init-reports
|
||||
type: bash
|
||||
command: mkdir -p {{Output}}/reports
|
||||
|
||||
- name: aggregate-findings
|
||||
type: function
|
||||
script: |
|
||||
var findings = [];
|
||||
var nucleiFile = "{{nuclei_results}}";
|
||||
|
||||
if (fileExists(nucleiFile)) {
|
||||
var content = readFile(nucleiFile);
|
||||
var lines = content.split("\n").filter(function(l) { return l.trim().length > 0; });
|
||||
findings = lines.map(function(l) {
|
||||
try { return JSON.parse(l); } catch(e) { return {raw: l}; }
|
||||
});
|
||||
}
|
||||
|
||||
log_info("Total findings aggregated: " + findings.length);
|
||||
writeFile("{{Output}}/reports/findings.json", JSON.stringify(findings, null, 2));
|
||||
return findings.length;
|
||||
exports:
|
||||
finding_count: "{{Result}}"
|
||||
|
||||
- name: generate-report
|
||||
type: bash
|
||||
command: |
|
||||
cat > {{Output}}/reports/vulnerability-report.md << EOF
|
||||
# Vulnerability Assessment Report
|
||||
|
||||
**Target:** {{Target}}
|
||||
**Date:** $(date)
|
||||
**Severity Filter:** {{severity}}
|
||||
|
||||
## Summary
|
||||
- Total Findings: {{finding_count}}
|
||||
- Endpoints Scanned: $(wc -l < {{all_endpoints}})
|
||||
|
||||
## Detailed Findings
|
||||
See findings.json for complete details.
|
||||
EOF
|
||||
exports:
|
||||
final_report: "{{Output}}/reports/vulnerability-report.md"
|
||||
+327
@@ -0,0 +1,327 @@
|
||||
name: vulnscan
|
||||
kind: module
|
||||
description: Run vulnerability scan on all HTTP hosts using Jaeles and Nuclei scanners
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: httpFile
|
||||
default: "{{Output}}/probing/http-{{Workspace}}.txt"
|
||||
- name: output_dir
|
||||
default: "{{Output}}/vuln"
|
||||
- name: sign
|
||||
default: "~/.jaeles/base-signatures/cves/.*"
|
||||
- name: sign2
|
||||
default: "~/.jaeles/base-signatures/common/.*"
|
||||
- name: sign3
|
||||
default: "~/.jaeles/base-signatures/sensitive/.*"
|
||||
- name: splitLines
|
||||
default: "500"
|
||||
- name: limit
|
||||
default: "25000"
|
||||
- name: extra
|
||||
default: " "
|
||||
- name: enableNuclei
|
||||
default: "true"
|
||||
- name: threads
|
||||
default: "10"
|
||||
- name: nucleiThreads
|
||||
default: "{{threads * 10}}"
|
||||
- name: jaelesThreads
|
||||
default: "{{threads * 5}}"
|
||||
- name: nucleiTimeout
|
||||
default: "8h"
|
||||
- name: jaelesTimeout
|
||||
default: "3h"
|
||||
- name: nucleiSeverity
|
||||
default: "critical,high,medium,low,info"
|
||||
- name: defaultUA
|
||||
default: "User-Agent: Mozilla/5.0 (compatible; Osmedeus/v4; +https://github.com/j3ssie/osmedeus)"
|
||||
|
||||
steps:
|
||||
# ============================================================
|
||||
# Phase 1: Validate Dependencies
|
||||
# ============================================================
|
||||
- name: validate-dependencies
|
||||
type: function
|
||||
function: |
|
||||
fileExists("{{Binaries}}/jaeles") &&
|
||||
fileExists("{{Binaries}}/nuclei")
|
||||
exports:
|
||||
deps_valid: "output"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Required binaries (jaeles, nuclei) not found"
|
||||
- action: abort
|
||||
|
||||
# ============================================================
|
||||
# Phase 2: Setup Output Directories
|
||||
# ============================================================
|
||||
- name: setup-directories
|
||||
type: bash
|
||||
commands:
|
||||
- mkdir -p {{output_dir}}
|
||||
- mkdir -p {{output_dir}}/raw
|
||||
- mkdir -p {{output_dir}}/active
|
||||
- mkdir -p {{output_dir}}/sensitive
|
||||
- mkdir -p {{output_dir}}/nuclei
|
||||
|
||||
# ============================================================
|
||||
# Phase 3: Validate Input File
|
||||
# ============================================================
|
||||
- name: check-input-exists
|
||||
type: function
|
||||
function: fileExists("{{httpFile}}")
|
||||
exports:
|
||||
input_exists: "output"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Input file {{httpFile}} not found"
|
||||
- action: abort
|
||||
|
||||
- name: count-input-lines
|
||||
type: function
|
||||
function: fileLength("{{httpFile}}")
|
||||
exports:
|
||||
input_count: "output"
|
||||
|
||||
# Decision: Abort if input file exceeds limit
|
||||
- name: check-input-limit
|
||||
type: function
|
||||
function: |
|
||||
var count = parseInt("{{input_count}}");
|
||||
var limit = parseInt("{{limit}}");
|
||||
if (count > limit) {
|
||||
return "exceeds_limit";
|
||||
}
|
||||
return "valid";
|
||||
exports:
|
||||
input_valid: "{{Result}}"
|
||||
decision:
|
||||
switch: "{{input_valid}}"
|
||||
cases:
|
||||
"exceeds_limit":
|
||||
goto: abort-large-input
|
||||
default:
|
||||
goto: split-input-file
|
||||
|
||||
- name: abort-large-input
|
||||
type: function
|
||||
function: printf("ERROR: Input file has {{input_count}} lines, exceeds limit of {{limit}}")
|
||||
on_error:
|
||||
- action: abort
|
||||
|
||||
# ============================================================
|
||||
# Phase 4: Split Input for Parallel Processing
|
||||
# ============================================================
|
||||
- name: split-input-file
|
||||
type: function
|
||||
function: SplitFile("{{httpFile}}", "{{Workspace}}-index", {{splitLines}}, "{{output_dir}}/raw")
|
||||
exports:
|
||||
split_dir: "{{output_dir}}/raw"
|
||||
|
||||
- name: list-split-files
|
||||
type: bash
|
||||
command: "ls {{output_dir}}/raw/{{Workspace}}-index* 2>/dev/null | head -100 > {{output_dir}}/raw/split-files.txt || touch {{output_dir}}/raw/split-files.txt"
|
||||
exports:
|
||||
split_files: "{{output_dir}}/raw/split-files.txt"
|
||||
|
||||
- name: count-split-files
|
||||
type: function
|
||||
function: fileLength("{{output_dir}}/raw/split-files.txt")
|
||||
exports:
|
||||
split_count: "output"
|
||||
|
||||
# ============================================================
|
||||
# Phase 5: Jaeles Vulnerability Scanning
|
||||
# ============================================================
|
||||
- name: jaeles-active-scan
|
||||
type: foreach
|
||||
pre_condition: 'parseInt("{{split_count}}") > 0'
|
||||
input: "{{output_dir}}/raw/split-files.txt"
|
||||
variable: splitfile
|
||||
threads: 1
|
||||
step:
|
||||
name: run-jaeles-active
|
||||
type: bash
|
||||
command: |
|
||||
echo "Running Jaeles active scan on [[splitfile]]..."
|
||||
timeout -k 1m {{jaelesTimeout}} {{Binaries}}/jaeles scan -c {{jaelesThreads}} -s '{{sign}}' -s '{{sign2}}' -U [[splitfile]] -o {{output_dir}}/active/ {{extra}} 2>/dev/null || true
|
||||
timeout: 14400
|
||||
|
||||
- name: jaeles-sensitive-scan
|
||||
type: foreach
|
||||
pre_condition: 'parseInt("{{split_count}}") > 0'
|
||||
input: "{{output_dir}}/raw/split-files.txt"
|
||||
variable: splitfile
|
||||
threads: 1
|
||||
step:
|
||||
name: run-jaeles-sensitive
|
||||
type: bash
|
||||
command: |
|
||||
echo "Running Jaeles sensitive scan on [[splitfile]]..."
|
||||
timeout -k 1m {{jaelesTimeout}} {{Binaries}}/jaeles scan --fi -c {{jaelesThreads}} -s '{{sign3}}' -L 2 -U [[splitfile]] -o {{output_dir}}/sensitive/ {{extra}} 2>/dev/null || true
|
||||
timeout: 14400
|
||||
|
||||
# ============================================================
|
||||
# Phase 6: Generate Jaeles Reports
|
||||
# ============================================================
|
||||
- name: generate-jaeles-reports
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: generate-active-report
|
||||
type: bash
|
||||
command: "{{Binaries}}/jaeles report -o {{output_dir}}/active/ -R {{output_dir}}/active/{{Workspace}}-report.html 2>/dev/null || true"
|
||||
on_error:
|
||||
- action: continue
|
||||
|
||||
- name: generate-sensitive-report
|
||||
type: bash
|
||||
command: "{{Binaries}}/jaeles report -o {{output_dir}}/sensitive/ -R {{output_dir}}/sensitive/{{Workspace}}-sensitive.html 2>/dev/null || true"
|
||||
on_error:
|
||||
- action: continue
|
||||
|
||||
# ============================================================
|
||||
# Phase 7: Process Jaeles Results
|
||||
# ============================================================
|
||||
- name: copy-active-summary
|
||||
type: bash
|
||||
pre_condition: 'fileExists("{{output_dir}}/active/jaeles-summary.txt")'
|
||||
command: "cp {{output_dir}}/active/jaeles-summary.txt {{output_dir}}/active/activescan-{{Workspace}}-{{TS}}.txt"
|
||||
exports:
|
||||
active_summary: "{{output_dir}}/active/activescan-{{Workspace}}-{{TS}}.txt"
|
||||
|
||||
- name: notify-active-results
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{output_dir}}/active/activescan-{{Workspace}}-{{TS}}.txt")'
|
||||
parallel_functions:
|
||||
- TeleMessByFile("#report", "{{output_dir}}/active/activescan-{{Workspace}}-{{TS}}.txt")
|
||||
- Cat("{{output_dir}}/active/activescan-{{Workspace}}-{{TS}}.txt")
|
||||
- TotalVulnerability("{{output_dir}}/active/activescan-{{Workspace}}-{{TS}}.txt")
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Failed to notify active scan results"
|
||||
- action: continue
|
||||
|
||||
- name: copy-sensitive-summary
|
||||
type: bash
|
||||
pre_condition: 'fileExists("{{output_dir}}/sensitive/jaeles-summary.txt")'
|
||||
command: "cp {{output_dir}}/sensitive/jaeles-summary.txt {{output_dir}}/sensitive/sensitivescan-{{Workspace}}-{{TS}}.txt"
|
||||
exports:
|
||||
sensitive_summary: "{{output_dir}}/sensitive/sensitivescan-{{Workspace}}-{{TS}}.txt"
|
||||
|
||||
- name: notify-sensitive-results
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{output_dir}}/sensitive/sensitivescan-{{Workspace}}-{{TS}}.txt")'
|
||||
parallel_functions:
|
||||
- TeleMessByFile("#sensitive", "{{output_dir}}/sensitive/sensitivescan-{{Workspace}}-{{TS}}.txt")
|
||||
- Cat("{{output_dir}}/sensitive/sensitivescan-{{Workspace}}-{{TS}}.txt")
|
||||
- TotalVulnerability("{{output_dir}}/sensitive/sensitivescan-{{Workspace}}-{{TS}}.txt")
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Failed to notify sensitive scan results"
|
||||
- action: continue
|
||||
|
||||
# ============================================================
|
||||
# Phase 8: Nuclei Vulnerability Scanning
|
||||
# ============================================================
|
||||
- name: nuclei-scan
|
||||
type: bash
|
||||
pre_condition: '"{{enableNuclei}}" == "true" && fileExists("{{httpFile}}")'
|
||||
command: |
|
||||
timeout -k 1m {{nucleiTimeout}} {{Binaries}}/nuclei \
|
||||
-H '{{defaultUA}}' \
|
||||
-silent \
|
||||
-c {{nucleiThreads}} \
|
||||
-jsonl \
|
||||
-severity '{{nucleiSeverity}}' \
|
||||
-t ~/nuclei-templates/ \
|
||||
-l {{httpFile}} \
|
||||
-irr \
|
||||
-o {{output_dir}}/nuclei/{{Workspace}}-nuclei-json.txt
|
||||
timeout: 28800
|
||||
exports:
|
||||
nuclei_json: "{{output_dir}}/nuclei/{{Workspace}}-nuclei-json.txt"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Nuclei scan failed or timed out"
|
||||
- action: continue
|
||||
|
||||
- name: count-nuclei-results
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{output_dir}}/nuclei/{{Workspace}}-nuclei-json.txt")'
|
||||
function: fileLength("{{output_dir}}/nuclei/{{Workspace}}-nuclei-json.txt")
|
||||
exports:
|
||||
nuclei_count: "output"
|
||||
|
||||
# ============================================================
|
||||
# Phase 9: Process Nuclei Results
|
||||
# ============================================================
|
||||
- name: generate-nuclei-report
|
||||
type: function
|
||||
pre_condition: 'parseInt("{{nuclei_count}}") > 0'
|
||||
function: GenNucleiReport("{{output_dir}}/nuclei/{{Workspace}}-nuclei-json.txt", "{{output_dir}}/nuclei/{{Workspace}}-nuclei.html")
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Failed to generate Nuclei HTML report"
|
||||
- action: continue
|
||||
|
||||
- name: parse-nuclei-json
|
||||
type: bash
|
||||
pre_condition: 'parseInt("{{nuclei_count}}") > 0'
|
||||
command: |
|
||||
cat {{output_dir}}/nuclei/{{Workspace}}-nuclei-json.txt | \
|
||||
jq -r '[.info.severity,.\"template-id\",.\"matched-at\",.\"matched-name\"] | join(\" - \")' \
|
||||
> {{output_dir}}/nuclei/{{Workspace}}-nuclei-scan.txt 2>/dev/null || true
|
||||
exports:
|
||||
nuclei_parsed: "{{output_dir}}/nuclei/{{Workspace}}-nuclei-scan.txt"
|
||||
|
||||
- name: sort-nuclei-results
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{output_dir}}/nuclei/{{Workspace}}-nuclei-scan.txt")'
|
||||
function: SortU("{{output_dir}}/nuclei/{{Workspace}}-nuclei-scan.txt")
|
||||
|
||||
- name: notify-nuclei-results
|
||||
type: function
|
||||
pre_condition: 'parseInt("{{nuclei_count}}") > 0'
|
||||
parallel_functions:
|
||||
- TeleMessByFile("#sensitive", "{{output_dir}}/nuclei/{{Workspace}}-nuclei-scan.txt")
|
||||
- Cat("{{output_dir}}/nuclei/{{Workspace}}-nuclei-scan.txt")
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Failed to notify Nuclei results"
|
||||
- action: continue
|
||||
|
||||
# ============================================================
|
||||
# Phase 10: Generate Final Report
|
||||
# ============================================================
|
||||
- name: generate-final-report
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{Data}}/markdown/general-template.md")'
|
||||
function: GenMarkdownReport("{{Data}}/markdown/general-template.md", "{{Output}}/summary.html")
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Final report generation skipped - template not found"
|
||||
- action: continue
|
||||
|
||||
- name: generate-vuln-summary
|
||||
type: bash
|
||||
commands:
|
||||
- |
|
||||
echo "=== Vulnerability Scan Report ===" > {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "Target: {{Target}}" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "Workspace: {{Workspace}}" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "Date: $(date)" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "=== Statistics ===" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "Input Hosts: {{input_count}}" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "Nuclei Findings: {{nuclei_count}}" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "=== Reports Generated ===" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "- Active Scan: {{output_dir}}/active/{{Workspace}}-report.html" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "- Sensitive Scan: {{output_dir}}/sensitive/{{Workspace}}-sensitive.html" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
echo "- Nuclei Scan: {{output_dir}}/nuclei/{{Workspace}}-nuclei.html" >> {{output_dir}}/final-report-{{Workspace}}.txt
|
||||
|
||||
- name: notify-completion
|
||||
type: function
|
||||
function: printf("Vulnerability scan complete: {{input_count}} hosts scanned, {{nuclei_count}} nuclei findings")
|
||||
@@ -0,0 +1,263 @@
|
||||
name: web-reconnaissance
|
||||
kind: module
|
||||
desc: Comprehensive web reconnaissance module demonstrating advanced workflow features
|
||||
|
||||
params:
|
||||
- name: target
|
||||
required: true
|
||||
- name: output_dir
|
||||
default: /tmp/osm-web-recon
|
||||
- name: threads
|
||||
default: "5"
|
||||
- name: subfinderThreads
|
||||
default: "{{threads * 4}}"
|
||||
- name: httpxThreads
|
||||
default: "{{threads * 2}}"
|
||||
- name: nucleiThreads
|
||||
default: "{{threads}}"
|
||||
- name: screenshotThreads
|
||||
default: "5"
|
||||
- name: httpxTimeout
|
||||
default: "10"
|
||||
- name: nucleiTimeout
|
||||
default: "3600"
|
||||
- name: enableScreenshots
|
||||
default: "true"
|
||||
- name: enableNuclei
|
||||
default: "true"
|
||||
- name: nucleiSeverity
|
||||
default: "critical,high,medium"
|
||||
- name: subfinderConfig
|
||||
default: "{{Data}}/external-configs/subfinder-provider.yaml"
|
||||
|
||||
steps:
|
||||
# ============================================================
|
||||
# Phase 1: Validate Dependencies
|
||||
# ============================================================
|
||||
- name: validate-dependencies
|
||||
type: function
|
||||
function: |
|
||||
fileExists("{{Binaries}}/subfinder") &&
|
||||
fileExists("{{Binaries}}/assetfinder") &&
|
||||
fileExists("{{Binaries}}/httpx")
|
||||
exports:
|
||||
deps_valid: "output"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Required binaries not found"
|
||||
- action: abort
|
||||
|
||||
# ============================================================
|
||||
# Phase 2: Parallel Subdomain Enumeration
|
||||
# ============================================================
|
||||
- name: subdomain-enumeration
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
- name: run-subfinder
|
||||
type: bash
|
||||
command: "{{Binaries}}/subfinder -d {{Target}} -provider-config {{subfinderConfig}} -t {{subfinderThreads}} -o {{Output}}/web-recon/subdomains/{{Workspace}}-subfinder.txt -silent"
|
||||
timeout: 600
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Subfinder failed, continuing with other tools"
|
||||
- action: continue
|
||||
|
||||
- name: run-assetfinder
|
||||
type: bash
|
||||
command: "{{Binaries}}/assetfinder -subs-only {{Target}} > {{Output}}/web-recon/subdomains/{{Workspace}}-assetfinder.txt"
|
||||
timeout: 300
|
||||
on_error:
|
||||
- action: continue
|
||||
|
||||
- name: run-findomain
|
||||
type: bash
|
||||
command: "{{Binaries}}/findomain -u {{Output}}/web-recon/subdomains/{{Workspace}}-findomain.txt -t {{Target}} 2>/dev/null"
|
||||
timeout: 300
|
||||
on_error:
|
||||
- action: continue
|
||||
|
||||
# ============================================================
|
||||
# Phase 3: Merge and Deduplicate Subdomains
|
||||
# ============================================================
|
||||
- name: merge-subdomains
|
||||
type: bash
|
||||
commands:
|
||||
- "cat {{Output}}/web-recon/subdomains/{{Workspace}}-*.txt 2>/dev/null | sort -u > {{Output}}/web-recon/subdomains/all-{{Workspace}}.txt"
|
||||
- "cat {{Output}}/web-recon/subdomains/all-{{Workspace}}.txt | {{Binaries}}/cleansub -t '{{Target}}' > {{Output}}/web-recon/subdomains/final-{{Workspace}}.txt 2>/dev/null || cp {{Output}}/web-recon/subdomains/all-{{Workspace}}.txt {{Output}}/web-recon/subdomains/final-{{Workspace}}.txt"
|
||||
exports:
|
||||
subdomains_file: "{{Output}}/web-recon/subdomains/final-{{Workspace}}.txt"
|
||||
|
||||
- name: count-subdomains
|
||||
type: function
|
||||
function: |
|
||||
var count = fileLength("{{subdomains_file}}");
|
||||
return count > 0 ? "true" : "false";
|
||||
exports:
|
||||
subdomain_count: "{{Result}}"
|
||||
has_subdomains: "{{Result}}"
|
||||
|
||||
# Decision: Skip remaining steps if no subdomains found
|
||||
- name: check-subdomain-results
|
||||
type: bash
|
||||
command: "echo {{subdomain_count}}"
|
||||
decision:
|
||||
switch: "{{has_subdomains}}"
|
||||
cases:
|
||||
"false":
|
||||
goto: generate-empty-report
|
||||
default:
|
||||
goto: http-probing
|
||||
|
||||
# ============================================================
|
||||
# Phase 4: HTTP Probing
|
||||
# ============================================================
|
||||
- name: http-probing
|
||||
type: bash
|
||||
command: "{{Binaries}}/httpx -l {{subdomains_file}} -threads {{httpxThreads}} -timeout {{httpxTimeout}} -silent -o {{Output}}/web-recon/probing/live-{{Workspace}}.txt -json -output {{Output}}/web-recon/probing/httpx-{{Workspace}}.json"
|
||||
timeout: 900
|
||||
exports:
|
||||
live_hosts_file: "{{Output}}/web-recon/probing/live-{{Workspace}}.txt"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "HTTP probing failed"
|
||||
- action: run
|
||||
step: fallback-probing
|
||||
|
||||
- name: fallback-probing
|
||||
type: bash
|
||||
pre_condition: "!fileExists('{{Output}}/web-recon/probing/live-{{Workspace}}.txt')"
|
||||
command: "cat {{subdomains_file}} | xargs -I {} curl -s -o /dev/null -w '%{http_code} {}\\n' http://{} 2>/dev/null | grep '^200' | awk '{print $2}' > {{Output}}/web-recon/probing/live-{{Workspace}}.txt"
|
||||
exports:
|
||||
live_hosts_file: "{{Output}}/web-recon/probing/live-{{Workspace}}.txt"
|
||||
|
||||
- name: count-live-hosts
|
||||
type: function
|
||||
function: fileLength("{{live_hosts_file}}")
|
||||
exports:
|
||||
live_host_count: "output"
|
||||
|
||||
# ============================================================
|
||||
# Phase 5: Parallel Analysis (Screenshots + Nuclei)
|
||||
# ============================================================
|
||||
- name: parallel-analysis
|
||||
type: parallel-steps
|
||||
parallel_steps:
|
||||
# Screenshot capture using Docker
|
||||
- name: capture-screenshots
|
||||
type: remote-bash
|
||||
pre_condition: '"{{enableScreenshots}}" == "true" && parseInt("{{live_host_count}}") > 0'
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: projectdiscovery/katana:latest
|
||||
volumes:
|
||||
- "{{Output}}/web-recon:/output"
|
||||
workdir: /output
|
||||
env:
|
||||
TARGETS_FILE: "/output/probing/live-{{Workspace}}.txt"
|
||||
command: |
|
||||
echo "Capturing screenshots for live hosts..."
|
||||
cat $TARGETS_FILE | head -20
|
||||
timeout: 1800
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Screenshot capture failed"
|
||||
- action: continue
|
||||
|
||||
# Nuclei vulnerability scanning using Docker
|
||||
- name: nuclei-scan
|
||||
type: remote-bash
|
||||
pre_condition: '"{{enableNuclei}}" == "true" && parseInt("{{live_host_count}}") > 0'
|
||||
step_runner: docker
|
||||
step_runner_config:
|
||||
image: projectdiscovery/nuclei:latest
|
||||
volumes:
|
||||
- "{{Output}}/web-recon:/output"
|
||||
workdir: /output
|
||||
env:
|
||||
SEVERITY: "{{nucleiSeverity}}"
|
||||
THREADS: "{{nucleiThreads}}"
|
||||
command: |
|
||||
nuclei -l /output/probing/live-{{Workspace}}.txt \
|
||||
-severity $SEVERITY \
|
||||
-c $THREADS \
|
||||
-json-export /output/nuclei/results-{{Workspace}}.json \
|
||||
-silent
|
||||
timeout: 3600
|
||||
exports:
|
||||
nuclei_results: "{{Output}}/web-recon/nuclei/results-{{Workspace}}.json"
|
||||
on_error:
|
||||
- action: log
|
||||
message: "Nuclei scan failed"
|
||||
- action: continue
|
||||
|
||||
# ============================================================
|
||||
# Phase 6: Foreach - Detailed Host Analysis
|
||||
# ============================================================
|
||||
- name: detailed-host-analysis
|
||||
type: foreach
|
||||
pre_condition: 'parseInt("{{live_host_count}}") > 0 && parseInt("{{live_host_count}}") < 50'
|
||||
input: "{{live_hosts_file}}"
|
||||
variable: host
|
||||
threads: 5
|
||||
step:
|
||||
name: analyze-single-host
|
||||
type: bash
|
||||
command: |
|
||||
echo "Analyzing [[host]]..."
|
||||
curl -s -I "[[host]]" 2>/dev/null | head -20 >> {{Output}}/web-recon/probing/headers-{{Workspace}}.txt
|
||||
echo "---" >> {{Output}}/web-recon/probing/headers-{{Workspace}}.txt
|
||||
timeout: 30
|
||||
|
||||
# ============================================================
|
||||
# Phase 7: Result Processing and Reporting
|
||||
# ============================================================
|
||||
- name: process-nuclei-results
|
||||
type: function
|
||||
pre_condition: 'fileExists("{{Output}}/web-recon/nuclei/results-{{Workspace}}.json")'
|
||||
parallel_functions:
|
||||
- db_vuln_critical("{{Output}}/web-recon/nuclei/results-{{Workspace}}.json")
|
||||
- db_vuln_high("{{Output}}/web-recon/nuclei/results-{{Workspace}}.json")
|
||||
- db_vuln_medium("{{Output}}/web-recon/nuclei/results-{{Workspace}}.json")
|
||||
exports:
|
||||
vuln_stats: "output"
|
||||
|
||||
- name: generate-report
|
||||
type: bash
|
||||
commands:
|
||||
- |
|
||||
echo "=== Web Reconnaissance Report ===" > {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "Target: {{Target}}" >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "Workspace: {{Workspace}}" >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "Date: $(date)" >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "" >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "=== Statistics ===" >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "Total Subdomains: {{subdomain_count}}" >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "Live Hosts: {{live_host_count}}" >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "" >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "=== Live Hosts ===" >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
cat {{live_hosts_file}} >> {{Output}}/web-recon/final-report-{{Workspace}}.txt 2>/dev/null || echo "No live hosts found"
|
||||
- "cp {{live_hosts_file}} {{Output}}/web-recon/live-hosts-{{Workspace}}.txt 2>/dev/null || touch {{Output}}/web-recon/live-hosts-{{Workspace}}.txt"
|
||||
- "cp {{Output}}/web-recon/nuclei/results-{{Workspace}}.json {{Output}}/web-recon/vulnerabilities-{{Workspace}}.json 2>/dev/null || echo '[]' > {{Output}}/web-recon/vulnerabilities-{{Workspace}}.json"
|
||||
|
||||
- name: generate-empty-report
|
||||
type: bash
|
||||
pre_condition: '"{{has_subdomains}}" == "false"'
|
||||
commands:
|
||||
- |
|
||||
echo "=== Web Reconnaissance Report ===" > {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "Target: {{Target}}" >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
echo "No subdomains found for target." >> {{Output}}/web-recon/final-report-{{Workspace}}.txt
|
||||
- "touch {{Output}}/web-recon/live-hosts-{{Workspace}}.txt"
|
||||
- "echo '[]' > {{Output}}/web-recon/vulnerabilities-{{Workspace}}.json"
|
||||
|
||||
# ============================================================
|
||||
# Phase 8: Cleanup and Notifications
|
||||
# ============================================================
|
||||
- name: final-cleanup
|
||||
type: function
|
||||
function: SortU("{{Output}}/web-recon/live-hosts-{{Workspace}}.txt")
|
||||
|
||||
- name: notify-completion
|
||||
type: function
|
||||
pre_condition: 'parseInt("{{subdomain_count}}") > 0'
|
||||
function: printf("Scan complete: {{subdomain_count}} subdomains, {{live_host_count}} live hosts")
|
||||
Reference in New Issue
Block a user