Skip to content

Kiterunner Cheat Sheet

Overview

Kiterunner is a fast and modular tool designed for content discovery and API endpoint enumeration. It excels at discovering hidden API endpoints, directories, and files through intelligent wordlist-based scanning and content analysis. Kiterunner is particularly effective for modern web applications and APIs, offering advanced features like custom wordlists, response analysis, and integration with other security tools.

💡 Key Features: Fast multi-threaded scanning, intelligent response analysis, custom wordlist support, API endpoint discovery, content-based filtering, JSON/XML parsing, and comprehensive output formats.

Installation and Setup

Binary Installation

# Download latest release from GitHub
wget https://github.com/assetnote/kiterunner/releases/latest/download/kiterunner_linux_amd64.tar.gz

# Extract the archive
tar -xzf kiterunner_linux_amd64.tar.gz

# Move to system path
sudo mv kr /usr/local/bin/

# Verify installation
kr --help

# Download wordlists
mkdir -p ~/.kiterunner/wordlists
cd ~/.kiterunner/wordlists

# Download common wordlists
wget https://raw.githubusercontent.com/assetnote/kiterunner/main/wordlists/routes-large.kite
wget https://raw.githubusercontent.com/assetnote/kiterunner/main/wordlists/routes-small.kite
wget https://raw.githubusercontent.com/assetnote/kiterunner/main/wordlists/apiroutes-210228.kite

# Set permissions
chmod +x /usr/local/bin/kr

Docker Installation

# Pull Docker image
docker pull assetnote/kiterunner

# Create alias for easier usage
echo 'alias kr="docker run --rm -it -v $(pwd):/app assetnote/kiterunner"' >> ~/.bashrc
source ~/.bashrc

# Test installation
kr --help

# Run with volume mount for wordlists
docker run --rm -it -v $(pwd):/app -v ~/.kiterunner:/root/.kiterunner assetnote/kiterunner

# Create Docker wrapper script
cat > kiterunner-docker.sh << 'EOF'
#!/bin/bash
docker run --rm -it \
  -v $(pwd):/app \
  -v ~/.kiterunner:/root/.kiterunner \
  assetnote/kiterunner "$@"
EOF

chmod +x kiterunner-docker.sh
sudo mv kiterunner-docker.sh /usr/local/bin/kr-docker

Source Installation

# Install Go (if not already installed)
wget https://golang.org/dl/go1.19.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.19.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin

# Clone repository
git clone https://github.com/assetnote/kiterunner.git
cd kiterunner

# Build from source
make build

# Install binary
sudo cp dist/kr /usr/local/bin/

# Verify installation
kr version

# Build with custom options
go build -ldflags="-s -w" -o kr cmd/kiterunner/main.go

Configuration and Setup

# Create configuration directory
mkdir -p ~/.kiterunner/{wordlists,output,config}

# Create default configuration
cat > ~/.kiterunner/config/config.yaml << 'EOF'
# Kiterunner Configuration
default:
  threads: 50
  timeout: 10
  delay: 0
  max_redirects: 3
  user_agent: "Kiterunner/1.0"

wordlists:
  default_path: "~/.kiterunner/wordlists"
  routes_large: "routes-large.kite"
  routes_small: "routes-small.kite"
  api_routes: "apiroutes-210228.kite"

output:
  default_format: "json"
  save_responses: false
  output_directory: "~/.kiterunner/output"

filters:
  status_codes:
    ignore: [404, 403, 400]
    interesting: [200, 201, 202, 301, 302, 500, 502, 503]

  content_length:
    min: 0
    max: 1048576  # 1MB

  response_time:
    max: 30000  # 30 seconds

proxy:
  enabled: false
  url: "http://127.0.0.1:8080"

headers:
  custom:
    - "X-Forwarded-For: 127.0.0.1"
    - "X-Real-IP: 127.0.0.1"
EOF

# Set environment variables
export KITERUNNER_CONFIG=~/.kiterunner/config/config.yaml
export KITERUNNER_WORDLISTS=~/.kiterunner/wordlists

# Create wordlist management script
cat > ~/.kiterunner/manage_wordlists.sh << 'EOF'
#!/bin/bash

WORDLIST_DIR="$HOME/.kiterunner/wordlists"

download_wordlists() {
    echo "Downloading Kiterunner wordlists..."

    # Official wordlists
    wget -O "$WORDLIST_DIR/routes-large.kite" \
        "https://raw.githubusercontent.com/assetnote/kiterunner/main/wordlists/routes-large.kite"

    wget -O "$WORDLIST_DIR/routes-small.kite" \
        "https://raw.githubusercontent.com/assetnote/kiterunner/main/wordlists/routes-small.kite"

    wget -O "$WORDLIST_DIR/apiroutes-210228.kite" \
        "https://raw.githubusercontent.com/assetnote/kiterunner/main/wordlists/apiroutes-210228.kite"

    # Additional useful wordlists
    wget -O "$WORDLIST_DIR/common.txt" \
        "https://raw.githubusercontent.com/danielmiessler/SecLists/master/Discovery/Web-Content/common.txt"

    wget -O "$WORDLIST_DIR/api-endpoints.txt" \
        "https://raw.githubusercontent.com/danielmiessler/SecLists/master/Discovery/Web-Content/api/api-endpoints.txt"

    echo "Wordlists downloaded successfully"
}

update_wordlists() {
    echo "Updating wordlists..."
    download_wordlists
}

list_wordlists() {
    echo "Available wordlists:"
    ls -la "$WORDLIST_DIR"
}

case "$1" in
    download) download_wordlists ;;
    update) update_wordlists ;;
    list) list_wordlists ;;
    *) echo "Usage: $0 {download|update|list}" ;;
esac
EOF

chmod +x ~/.kiterunner/manage_wordlists.sh

Basic Usage and Commands

Simple Directory Discovery

# Basic directory discovery
kr brute -w ~/.kiterunner/wordlists/routes-small.kite -t 50 https://example.com

# Discovery with custom wordlist
kr brute -w /path/to/custom/wordlist.txt -t 100 https://example.com

# Multiple targets from file
echo -e "https://example.com\nhttps://test.com" > targets.txt
kr brute -w ~/.kiterunner/wordlists/routes-large.kite -A targets.txt

# Discovery with specific extensions
kr brute -w ~/.kiterunner/wordlists/routes-small.kite -x php,asp,aspx,jsp https://example.com

# Discovery with custom headers
kr brute -w ~/.kiterunner/wordlists/routes-small.kite -H "Authorization: Bearer token123" https://example.com

# Discovery with proxy
kr brute -w ~/.kiterunner/wordlists/routes-small.kite --proxy http://127.0.0.1:8080 https://example.com

API Endpoint Discovery

# API endpoint discovery with specialized wordlist
kr brute -w ~/.kiterunner/wordlists/apiroutes-210228.kite -t 100 https://api.example.com

# API discovery with JSON output
kr brute -w ~/.kiterunner/wordlists/apiroutes-210228.kite -o json https://api.example.com

# API discovery with response analysis
kr brute -w ~/.kiterunner/wordlists/apiroutes-210228.kite --fail-status-codes 404,403 https://api.example.com

# API discovery with content length filtering
kr brute -w ~/.kiterunner/wordlists/apiroutes-210228.kite --filter-length 0 https://api.example.com

# API discovery with custom user agent
kr brute -w ~/.kiterunner/wordlists/apiroutes-210228.kite -H "User-Agent: Mobile App 1.0" https://api.example.com

# API versioning discovery
kr brute -w ~/.kiterunner/wordlists/apiroutes-210228.kite --prefixes /v1,/v2,/api/v1,/api/v2 https://example.com

Advanced Scanning Options

# High-performance scanning
kr brute -w ~/.kiterunner/wordlists/routes-large.kite -t 200 --delay 0 https://example.com

# Scanning with custom timeout
kr brute -w ~/.kiterunner/wordlists/routes-small.kite --timeout 30 https://example.com

# Scanning with retry logic
kr brute -w ~/.kiterunner/wordlists/routes-small.kite --max-retries 3 https://example.com

# Scanning with rate limiting
kr brute -w ~/.kiterunner/wordlists/routes-small.kite --delay 100 https://example.com

# Scanning with custom status code filtering
kr brute -w ~/.kiterunner/wordlists/routes-small.kite --fail-status-codes 404,403,400,401 https://example.com

# Scanning with response size filtering
kr brute -w ~/.kiterunner/wordlists/routes-small.kite --filter-length 0,1,2,3 https://example.com

Advanced Discovery Techniques

Custom Wordlist Creation

#!/bin/bash
# Create custom wordlists for specific applications

create_api_wordlist() {
    local output_file="$1"

    cat > "$output_file" << 'EOF'
# API Endpoints
/api
/api/v1
/api/v2
/api/v3
/rest
/rest/v1
/rest/v2
/graphql
/graph
/gql

# Authentication endpoints
/auth
/login
/logout
/signin
/signout
/register
/signup
/oauth
/token
/refresh
/verify

# User management
/users
/user
/profile
/account
/accounts
/me
/settings
/preferences

# Data endpoints
/data
/export
/import
/backup
/restore
/sync
/upload
/download

# Admin endpoints
/admin
/administrator
/management
/manage
/dashboard
/panel
/control

# Common API patterns
/health
/status
/ping
/version
/info
/metrics
/stats
/analytics

# CRUD operations
/create
/read
/update
/delete
/list
/search
/find
/get
/post
/put
/patch

# File operations
/files
/documents
/images
/media
/assets
/static
/public
/private

# Configuration
/config
/configuration
/settings
/options
/parameters
/env
/environment
EOF

    echo "API wordlist created: $output_file"
}

create_technology_specific_wordlist() {
    local technology="$1"
    local output_file="$2"

    case "$technology" in
        "spring")
            cat > "$output_file" << 'EOF'
/actuator
/actuator/health
/actuator/info
/actuator/metrics
/actuator/env
/actuator/configprops
/actuator/mappings
/actuator/beans
/actuator/trace
/actuator/dump
/actuator/autoconfig
/management
/management/health
/management/info
EOF
            ;;
        "django")
            cat > "$output_file" << 'EOF'
/admin
/admin/
/django-admin
/__debug__
/debug
/static
/media
/api
/api-auth
/api-token-auth
/accounts
/accounts/login
/accounts/logout
/accounts/signup
EOF
            ;;
        "laravel")
            cat > "$output_file" << 'EOF'
/api
/admin
/dashboard
/telescope
/horizon
/nova
/storage
/public
/vendor
/artisan
/routes
/config
/cache
/session
EOF
            ;;
        "nodejs")
            cat > "$output_file" << 'EOF'
/api
/admin
/auth
/login
/logout
/register
/users
/user
/profile
/dashboard
/health
/status
/metrics
/debug
/console
/socket.io
/graphql
EOF
            ;;
    esac

    echo "$technology wordlist created: $output_file"
}

create_mobile_api_wordlist() {
    local output_file="$1"

    cat > "$output_file" << 'EOF'
# Mobile API endpoints
/mobile
/mobile/api
/mobile/v1
/mobile/v2
/app
/app/api
/app/v1
/app/v2

# Mobile-specific features
/push
/notifications
/fcm
/apns
/device
/devices
/registration
/unregister

# Mobile authentication
/mobile/auth
/mobile/login
/mobile/logout
/mobile/token
/mobile/refresh
/app/auth
/app/login
/app/token

# Mobile data sync
/sync
/mobile/sync
/app/sync
/offline
/cache
/local

# Mobile analytics
/analytics
/events
/tracking
/metrics
/crash
/crashes
/feedback

# Mobile updates
/update
/updates
/version
/versions
/download
/upgrade
EOF

    echo "Mobile API wordlist created: $output_file"
}

# Usage examples
create_api_wordlist "~/.kiterunner/wordlists/custom-api.txt"
create_technology_specific_wordlist "spring" "~/.kiterunner/wordlists/spring-endpoints.txt"
create_mobile_api_wordlist "~/.kiterunner/wordlists/mobile-api.txt"

Intelligent Response Analysis

#!/usr/bin/env python3
# Advanced response analysis for Kiterunner results

import json
import re
import requests
import argparse
from urllib.parse import urljoin, urlparse
import time
from concurrent.futures import ThreadPoolExecutor, as_completed

class KiterunnerAnalyzer:
    """Advanced analysis of Kiterunner discovery results"""

    def __init__(self, results_file, target_base_url):
        self.results_file = results_file
        self.target_base_url = target_base_url
        self.session = requests.Session()
        self.session.headers.update({
            'User-Agent': 'Kiterunner-Analyzer/1.0'
        })

        # Analysis results
        self.interesting_endpoints = []
        self.api_endpoints = []
        self.admin_endpoints = []
        self.sensitive_files = []
        self.error_pages = []

    def load_results(self):
        """Load Kiterunner results from JSON file"""

        try:
            with open(self.results_file, 'r') as f:
                # Handle both single JSON objects and line-delimited JSON
                content = f.read().strip()
                if content.startswith('['):
                    # Standard JSON array
                    return json.loads(content)
                else:
                    # Line-delimited JSON
                    results = []
                    for line in content.split('\n'):
                        if line.strip():
                            try:
                                results.append(json.loads(line))
                            except json.JSONDecodeError:
                                continue
                    return results
        except Exception as e:
            print(f"Error loading results: {e}")
            return []

    def analyze_results(self):
        """Analyze Kiterunner results for interesting findings"""

        results = self.load_results()
        print(f"Analyzing {len(results)} discovered endpoints...")

        for result in results:
            self._analyze_single_result(result)

        # Generate analysis report
        self._generate_analysis_report()

    def _analyze_single_result(self, result):
        """Analyze a single discovery result"""

        url = result.get('url', '')
        status_code = result.get('status', 0)
        content_length = result.get('length', 0)
        response_time = result.get('time', 0)

        # Categorize endpoints
        if self._is_api_endpoint(url):
            self.api_endpoints.append(result)

        if self._is_admin_endpoint(url):
            self.admin_endpoints.append(result)

        if self._is_sensitive_file(url):
            self.sensitive_files.append(result)

        if self._is_error_page(result):
            self.error_pages.append(result)

        if self._is_interesting_endpoint(result):
            self.interesting_endpoints.append(result)

    def _is_api_endpoint(self, url):
        """Check if URL appears to be an API endpoint"""

        api_patterns = [
            r'/api/',
            r'/rest/',
            r'/graphql',
            r'/v\d+/',
            r'\.json$',
            r'\.xml$',
            r'/oauth',
            r'/token'
        ]

        return any(re.search(pattern, url, re.IGNORECASE) for pattern in api_patterns)

    def _is_admin_endpoint(self, url):
        """Check if URL appears to be an admin endpoint"""

        admin_patterns = [
            r'/admin',
            r'/administrator',
            r'/management',
            r'/dashboard',
            r'/panel',
            r'/control',
            r'/console',
            r'/manager'
        ]

        return any(re.search(pattern, url, re.IGNORECASE) for pattern in admin_patterns)

    def _is_sensitive_file(self, url):
        """Check if URL appears to be a sensitive file"""

        sensitive_patterns = [
            r'\.env$',
            r'\.config$',
            r'\.conf$',
            r'\.ini$',
            r'\.properties$',
            r'\.yaml$',
            r'\.yml$',
            r'\.json$',
            r'\.xml$',
            r'\.sql$',
            r'\.db$',
            r'\.backup$',
            r'\.bak$',
            r'\.log$',
            r'\.key$',
            r'\.pem$',
            r'\.crt$',
            r'\.p12$',
            r'\.pfx$'
        ]

        return any(re.search(pattern, url, re.IGNORECASE) for pattern in sensitive_patterns)

    def _is_error_page(self, result):
        """Check if result appears to be an error page with useful information"""

        status_code = result.get('status', 0)
        content_length = result.get('length', 0)

        # Look for error pages that might leak information
        if status_code in [500, 502, 503] and content_length > 100:
            return True

        return False

    def _is_interesting_endpoint(self, result):
        """Check if endpoint is interesting for further investigation"""

        url = result.get('url', '')
        status_code = result.get('status', 0)
        content_length = result.get('length', 0)

        # Interesting status codes
        if status_code in [200, 201, 202, 301, 302, 401, 403]:
            return True

        # Unusual content lengths
        if content_length > 10000 or (content_length > 0 and content_length < 10):
            return True

        # Interesting URL patterns
        interesting_patterns = [
            r'/debug',
            r'/test',
            r'/dev',
            r'/staging',
            r'/backup',
            r'/old',
            r'/new',
            r'/temp',
            r'/tmp',
            r'/upload',
            r'/download',
            r'/export',
            r'/import'
        ]

        if any(re.search(pattern, url, re.IGNORECASE) for pattern in interesting_patterns):
            return True

        return False

    def deep_analysis(self, max_workers=10):
        """Perform deep analysis of interesting endpoints"""

        print("Performing deep analysis of interesting endpoints...")

        endpoints_to_analyze = (
            self.interesting_endpoints + 
            self.api_endpoints + 
            self.admin_endpoints
        )

        with ThreadPoolExecutor(max_workers=max_workers) as executor:
            future_to_endpoint = {
                executor.submit(self._deep_analyze_endpoint, endpoint): endpoint
                for endpoint in endpoints_to_analyze[:50]  # Limit to first 50
            }

            for future in as_completed(future_to_endpoint):
                endpoint = future_to_endpoint[future]
                try:
                    analysis = future.result()
                    if analysis:
                        endpoint['deep_analysis'] = analysis
                except Exception as e:
                    print(f"Error analyzing {endpoint.get('url', 'unknown')}: {e}")

    def _deep_analyze_endpoint(self, endpoint):
        """Perform deep analysis of a single endpoint"""

        url = endpoint.get('url', '')

        try:
            # Make request to endpoint
            response = self.session.get(url, timeout=10, allow_redirects=True)

            analysis = {
                'final_url': response.url,
                'status_code': response.status_code,
                'headers': dict(response.headers),
                'content_type': response.headers.get('content-type', ''),
                'server': response.headers.get('server', ''),
                'technologies': self._detect_technologies(response),
                'security_headers': self._analyze_security_headers(response.headers),
                'content_analysis': self._analyze_content(response.text),
                'forms': self._extract_forms(response.text),
                'links': self._extract_links(response.text, url)
            }

            return analysis

        except Exception as e:
            return {'error': str(e)}

    def _detect_technologies(self, response):
        """Detect technologies used by the endpoint"""

        technologies = []

        # Check headers for technology indicators
        server = response.headers.get('server', '').lower()
        if 'apache' in server:
            technologies.append('Apache')
        if 'nginx' in server:
            technologies.append('Nginx')
        if 'iis' in server:
            technologies.append('IIS')

        # Check for framework-specific headers
        if 'x-powered-by' in response.headers:
            technologies.append(response.headers['x-powered-by'])

        # Check content for technology indicators
        content = response.text.lower()

        tech_patterns = {
            'React': r'react',
            'Angular': r'angular',
            'Vue.js': r'vue\.js',
            'jQuery': r'jquery',
            'Bootstrap': r'bootstrap',
            'Django': r'django',
            'Laravel': r'laravel',
            'Spring': r'spring',
            'Express': r'express',
            'WordPress': r'wp-content|wordpress',
            'Drupal': r'drupal',
            'Joomla': r'joomla'
        }

        for tech, pattern in tech_patterns.items():
            if re.search(pattern, content):
                technologies.append(tech)

        return list(set(technologies))

    def _analyze_security_headers(self, headers):
        """Analyze security headers"""

        security_headers = {
            'Content-Security-Policy': headers.get('content-security-policy'),
            'X-Frame-Options': headers.get('x-frame-options'),
            'X-XSS-Protection': headers.get('x-xss-protection'),
            'X-Content-Type-Options': headers.get('x-content-type-options'),
            'Strict-Transport-Security': headers.get('strict-transport-security'),
            'Referrer-Policy': headers.get('referrer-policy'),
            'Permissions-Policy': headers.get('permissions-policy')
        }

        # Identify missing security headers
        missing_headers = [name for name, value in security_headers.items() if not value]

        return {
            'present': {k: v for k, v in security_headers.items() if v},
            'missing': missing_headers
        }

    def _analyze_content(self, content):
        """Analyze response content for interesting information"""

        analysis = {
            'length': len(content),
            'contains_forms': '<form' in content.lower(),
            'contains_javascript': '<script' in content.lower(),
            'contains_comments': '<!--' in content,
            'potential_secrets': [],
            'error_messages': [],
            'debug_info': []
        }

        # Look for potential secrets
        secret_patterns = [
            (r'api[_-]?key["\']?\s*[:=]\s*["\']([a-zA-Z0-9_-]{10,})["\']', 'API Key'),
            (r'secret[_-]?key["\']?\s*[:=]\s*["\']([a-zA-Z0-9_-]{10,})["\']', 'Secret Key'),
            (r'password["\']?\s*[:=]\s*["\']([a-zA-Z0-9_-]{8,})["\']', 'Password'),
            (r'token["\']?\s*[:=]\s*["\']([a-zA-Z0-9_-]{10,})["\']', 'Token'),
            (r'aws[_-]?access[_-]?key["\']?\s*[:=]\s*["\']([A-Z0-9]{20})["\']', 'AWS Access Key'),
            (r'aws[_-]?secret[_-]?key["\']?\s*[:=]\s*["\']([a-zA-Z0-9/+=]{40})["\']', 'AWS Secret Key')
        ]

        for pattern, secret_type in secret_patterns:
            matches = re.findall(pattern, content, re.IGNORECASE)
            for match in matches:
                analysis['potential_secrets'].append({
                    'type': secret_type,
                    'value': match[:20] + '...' if len(match) > 20 else match
                })

        # Look for error messages
        error_patterns = [
            r'error[:\s]+([^\n\r]{10,100})',
            r'exception[:\s]+([^\n\r]{10,100})',
            r'stack trace[:\s]+([^\n\r]{10,100})',
            r'fatal[:\s]+([^\n\r]{10,100})'
        ]

        for pattern in error_patterns:
            matches = re.findall(pattern, content, re.IGNORECASE)
            analysis['error_messages'].extend(matches[:5])  # Limit to 5 matches

        # Look for debug information
        debug_patterns = [
            r'debug[:\s]+([^\n\r]{10,100})',
            r'console\.log\(["\']([^"\']{10,100})["\']',
            r'var_dump\(["\']([^"\']{10,100})["\']',
            r'print_r\(["\']([^"\']{10,100})["\']'
        ]

        for pattern in debug_patterns:
            matches = re.findall(pattern, content, re.IGNORECASE)
            analysis['debug_info'].extend(matches[:5])  # Limit to 5 matches

        return analysis

    def _extract_forms(self, content):
        """Extract forms from HTML content"""

        forms = []
        form_pattern = r'<form[^>]*>(.*?)</form>'

        for form_match in re.finditer(form_pattern, content, re.DOTALL | re.IGNORECASE):
            form_html = form_match.group(0)

            # Extract form attributes
            action = re.search(r'action=["\']([^"\']*)["\']', form_html, re.IGNORECASE)
            method = re.search(r'method=["\']([^"\']*)["\']', form_html, re.IGNORECASE)

            # Extract input fields
            inputs = []
            input_pattern = r'<input[^>]*>'
            for input_match in re.finditer(input_pattern, form_html, re.IGNORECASE):
                input_html = input_match.group(0)
                name = re.search(r'name=["\']([^"\']*)["\']', input_html, re.IGNORECASE)
                input_type = re.search(r'type=["\']([^"\']*)["\']', input_html, re.IGNORECASE)

                inputs.append({
                    'name': name.group(1) if name else '',
                    'type': input_type.group(1) if input_type else 'text'
                })

            forms.append({
                'action': action.group(1) if action else '',
                'method': method.group(1) if method else 'GET',
                'inputs': inputs
            })

        return forms

    def _extract_links(self, content, base_url):
        """Extract links from HTML content"""

        links = []
        link_pattern = r'href=["\']([^"\']*)["\']'

        for link_match in re.finditer(link_pattern, content, re.IGNORECASE):
            link = link_match.group(1)

            # Convert relative URLs to absolute
            if link.startswith('/'):
                parsed_base = urlparse(base_url)
                link = f"{parsed_base.scheme}://{parsed_base.netloc}{link}"
            elif not link.startswith(('http://', 'https://')):
                link = urljoin(base_url, link)

            links.append(link)

        # Remove duplicates and limit
        return list(set(links))[:20]

    def _generate_analysis_report(self):
        """Generate comprehensive analysis report"""

        report = {
            'summary': {
                'total_endpoints': len(self.load_results()),
                'api_endpoints': len(self.api_endpoints),
                'admin_endpoints': len(self.admin_endpoints),
                'sensitive_files': len(self.sensitive_files),
                'error_pages': len(self.error_pages),
                'interesting_endpoints': len(self.interesting_endpoints)
            },
            'findings': {
                'api_endpoints': self.api_endpoints[:10],  # Top 10
                'admin_endpoints': self.admin_endpoints[:10],
                'sensitive_files': self.sensitive_files[:10],
                'error_pages': self.error_pages[:5],
                'interesting_endpoints': self.interesting_endpoints[:15]
            },
            'recommendations': self._generate_recommendations()
        }

        # Save report
        report_file = f"kiterunner_analysis_{int(time.time())}.json"
        with open(report_file, 'w') as f:
            json.dump(report, f, indent=2)

        print(f"Analysis report saved: {report_file}")

        # Print summary
        self._print_summary(report)

    def _generate_recommendations(self):
        """Generate security recommendations based on findings"""

        recommendations = []

        if self.admin_endpoints:
            recommendations.append({
                'priority': 'HIGH',
                'category': 'Access Control',
                'finding': f"Found {len(self.admin_endpoints)} admin endpoints",
                'recommendation': "Review admin endpoints for proper authentication and authorization"
            })

        if self.sensitive_files:
            recommendations.append({
                'priority': 'HIGH',
                'category': 'Information Disclosure',
                'finding': f"Found {len(self.sensitive_files)} sensitive files",
                'recommendation': "Remove or protect sensitive files from public access"
            })

        if self.error_pages:
            recommendations.append({
                'priority': 'MEDIUM',
                'category': 'Information Disclosure',
                'finding': f"Found {len(self.error_pages)} error pages",
                'recommendation': "Review error pages for information leakage"
            })

        if self.api_endpoints:
            recommendations.append({
                'priority': 'MEDIUM',
                'category': 'API Security',
                'finding': f"Found {len(self.api_endpoints)} API endpoints",
                'recommendation': "Test API endpoints for authentication, authorization, and input validation"
            })

        return recommendations

    def _print_summary(self, report):
        """Print analysis summary"""

        print("\n" + "="*60)
        print("KITERUNNER ANALYSIS SUMMARY")
        print("="*60)

        summary = report['summary']
        print(f"Total Endpoints Discovered: {summary['total_endpoints']}")
        print(f"API Endpoints: {summary['api_endpoints']}")
        print(f"Admin Endpoints: {summary['admin_endpoints']}")
        print(f"Sensitive Files: {summary['sensitive_files']}")
        print(f"Error Pages: {summary['error_pages']}")
        print(f"Interesting Endpoints: {summary['interesting_endpoints']}")

        print("\nTOP FINDINGS:")
        print("-" * 40)

        if self.admin_endpoints:
            print("Admin Endpoints:")
            for endpoint in self.admin_endpoints[:5]:
                print(f"  • {endpoint.get('url', 'N/A')} ({endpoint.get('status', 'N/A')})")

        if self.sensitive_files:
            print("\\nSensitive Files:")
            for file_endpoint in self.sensitive_files[:5]:
                print(f"  • {file_endpoint.get('url', 'N/A')} ({file_endpoint.get('status', 'N/A')})")

        print("\\nRECOMMENDATIONS:")
        print("-" * 40)
        for rec in report['recommendations']:
            print(f"[{rec['priority']}] {rec['category']}: {rec['recommendation']}")

def main():
    parser = argparse.ArgumentParser(description='Analyze Kiterunner discovery results')
    parser.add_argument('results_file', help='Kiterunner results file (JSON)')
    parser.add_argument('target_url', help='Target base URL')
    parser.add_argument('--deep-analysis', action='store_true', help='Perform deep analysis')
    parser.add_argument('--threads', type=int, default=10, help='Number of threads for deep analysis')

    args = parser.parse_args()

    analyzer = KiterunnerAnalyzer(args.results_file, args.target_url)
    analyzer.analyze_results()

    if args.deep_analysis:
        analyzer.deep_analysis(max_workers=args.threads)

if __name__ == "__main__":
    main()

Integration with Security Tools

Burp Suite Integration

#!/usr/bin/env python3
# Burp Suite integration for Kiterunner results

import json
import requests
import base64
from urllib.parse import urlparse

class BurpSuiteIntegration:
    """Integration with Burp Suite for discovered endpoints"""

    def __init__(self, burp_api_url="http://127.0.0.1:1337", api_key=None):
        self.burp_api_url = burp_api_url
        self.api_key = api_key
        self.session = requests.Session()

        if api_key:
            self.session.headers.update({'X-API-Key': api_key})

    def import_kiterunner_results(self, results_file, target_scope=None):
        """Import Kiterunner results into Burp Suite"""

        print("Importing Kiterunner results into Burp Suite...")

        # Load results
        with open(results_file, 'r') as f:
            results = json.load(f)

        imported_count = 0

        for result in results:
            url = result.get('url', '')
            status_code = result.get('status', 0)

            # Filter by scope if specified
            if target_scope and not self._in_scope(url, target_scope):
                continue

            # Only import successful responses
            if status_code in [200, 201, 202, 301, 302, 401, 403]:
                if self._add_to_sitemap(url, result):
                    imported_count += 1

        print(f"Imported {imported_count} endpoints into Burp Suite")

    def _in_scope(self, url, scope_patterns):
        """Check if URL is in scope"""

        for pattern in scope_patterns:
            if pattern in url:
                return True
        return False

    def _add_to_sitemap(self, url, result):
        """Add URL to Burp Suite sitemap"""

        try:
            # Create request data
            request_data = {
                'url': url,
                'method': 'GET',
                'headers': [],
                'body': ''
            }

            # Create response data
            response_data = {
                'status_code': result.get('status', 200),
                'headers': [],
                'body': result.get('response', '')
            }

            # Add to sitemap via API
            api_endpoint = f"{self.burp_api_url}/v0.1/sitemap"

            payload = {
                'request': request_data,
                'response': response_data
            }

            response = self.session.post(api_endpoint, json=payload)
            return response.status_code == 200

        except Exception as e:
            print(f"Error adding {url} to sitemap: {e}")
            return False

    def create_scan_configuration(self, results_file, output_file):
        """Create Burp Suite scan configuration from results"""

        with open(results_file, 'r') as f:
            results = json.load(f)

        # Extract unique hosts and paths
        hosts = set()
        paths = []

        for result in results:
            url = result.get('url', '')
            parsed = urlparse(url)

            hosts.add(f"{parsed.scheme}://{parsed.netloc}")
            paths.append(parsed.path)

        # Create scan configuration
        scan_config = {
            'scan_configurations': [
                {
                    'name': 'Kiterunner Discovery Scan',
                    'type': 'NamedConfiguration',
                    'built_in_configuration_name': 'Audit coverage - thorough'
                }
            ],
            'application_logins': [],
            'resource_pool': {
                'maximum_concurrent_scans': 10,
                'maximum_requests_per_second': 100
            },
            'scan_targets': [
                {
                    'urls': list(hosts),
                    'scope': {
                        'include': [
                            {
                                'rule': host,
                                'type': 'SimpleScopeRule'
                            } for host in hosts
                        ]
                    }
                }
            ]
        }

        # Save configuration
        with open(output_file, 'w') as f:
            json.dump(scan_config, f, indent=2)

        print(f"Burp Suite scan configuration saved: {output_file}")

def create_burp_extension():
    """Create Burp Suite extension for Kiterunner integration"""

    extension_code = '''
from burp import IBurpExtender, ITab, IHttpListener, IContextMenuFactory
from javax.swing import JPanel, JButton, JTextArea, JScrollPane, JLabel, JTextField
from java.awt import BorderLayout, GridLayout
import json
import subprocess
import threading

class BurpExtender(IBurpExtender, ITab, IHttpListener, IContextMenuFactory):
    def registerExtenderCallbacks(self, callbacks):
        self._callbacks = callbacks
        self._helpers = callbacks.getHelpers()

        callbacks.setExtensionName("Kiterunner Integration")
        callbacks.registerHttpListener(self)
        callbacks.registerContextMenuFactory(self)

        # Create UI
        self._create_ui()
        callbacks.addSuiteTab(self)

        print("Kiterunner Integration loaded successfully")

    def _create_ui(self):
        self._main_panel = JPanel(BorderLayout())

        # Control panel
        control_panel = JPanel(GridLayout(4, 2))

        # Target URL input
        control_panel.add(JLabel("Target URL:"))
        self._target_url = JTextField("https://example.com")
        control_panel.add(self._target_url)

        # Wordlist selection
        control_panel.add(JLabel("Wordlist:"))
        self._wordlist_path = JTextField("/path/to/wordlist.kite")
        control_panel.add(self._wordlist_path)

        # Threads setting
        control_panel.add(JLabel("Threads:"))
        self._threads = JTextField("50")
        control_panel.add(self._threads)

        # Run button
        self._run_button = JButton("Run Kiterunner", actionPerformed=self._run_kiterunner)
        control_panel.add(self._run_button)

        # Import button
        self._import_button = JButton("Import Results", actionPerformed=self._import_results)
        control_panel.add(self._import_button)

        # Results area
        self._results_area = JTextArea(20, 80)
        self._results_area.setEditable(False)
        results_scroll = JScrollPane(self._results_area)

        # Add components
        self._main_panel.add(control_panel, BorderLayout.NORTH)
        self._main_panel.add(results_scroll, BorderLayout.CENTER)

    def getTabCaption(self):
        return "Kiterunner"

    def getUiComponent(self):
        return self._main_panel

    def _run_kiterunner(self, event):
        """Run Kiterunner scan"""

        target_url = self._target_url.getText()
        wordlist_path = self._wordlist_path.getText()
        threads = self._threads.getText()

        if not target_url or not wordlist_path:
            self._results_area.append("Please specify target URL and wordlist path\\n")
            return

        # Run in background thread
        thread = threading.Thread(target=self._execute_kiterunner, 
                                 args=(target_url, wordlist_path, threads))
        thread.daemon = True
        thread.start()

    def _execute_kiterunner(self, target_url, wordlist_path, threads):
        """Execute Kiterunner command"""

        try:
            self._results_area.append(f"Starting Kiterunner scan on {target_url}...\\n")

            # Build command
            cmd = [
                "kr", "brute",
                "-w", wordlist_path,
                "-t", threads,
                "-o", "json",
                target_url
            ]

            # Execute command
            process = subprocess.Popen(cmd, stdout=subprocess.PIPE, 
                                     stderr=subprocess.PIPE, text=True)

            # Read output
            for line in process.stdout:
                self._results_area.append(line)
                self._results_area.setCaretPosition(self._results_area.getDocument().getLength())

            # Wait for completion
            process.wait()

            if process.returncode == 0:
                self._results_area.append("\\nKiterunner scan completed successfully\\n")
            else:
                error_output = process.stderr.read()
                self._results_area.append(f"\\nKiterunner scan failed: {error_output}\\n")

        except Exception as e:
            self._results_area.append(f"\\nError running Kiterunner: {e}\\n")

    def _import_results(self, event):
        """Import Kiterunner results into Burp Suite"""

        # This would implement the import functionality
        # For now, just show a message
        self._results_area.append("Import functionality would be implemented here\\n")
'''

    # Save extension code
    with open('kiterunner_burp_extension.py', 'w') as f:
        f.write(extension_code)

    print("Burp Suite extension created: kiterunner_burp_extension.py")

def main():
    # Example usage
    burp_integration = BurpSuiteIntegration()

    # Import results (example)
    # burp_integration.import_kiterunner_results('kiterunner_results.json', ['example.com'])

    # Create scan configuration
    # burp_integration.create_scan_configuration('kiterunner_results.json', 'burp_scan_config.json')

    # Create Burp extension
    create_burp_extension()

if __name__ == "__main__":
    main()

Performance Optimization and Troubleshooting

Performance Tuning

#!/bin/bash
# Kiterunner performance optimization

optimize_kiterunner_performance() {
    echo "Optimizing Kiterunner performance..."

    # 1. System-level optimizations
    echo "Applying system optimizations..."

    # Increase file descriptor limits
    ulimit -n 65536
    echo "* soft nofile 65536" | sudo tee -a /etc/security/limits.conf
    echo "* hard nofile 65536" | sudo tee -a /etc/security/limits.conf

    # Optimize network settings
    echo 'net.core.somaxconn = 65536' | sudo tee -a /etc/sysctl.conf
    echo 'net.ipv4.tcp_max_syn_backlog = 65536' | sudo tee -a /etc/sysctl.conf
    echo 'net.ipv4.tcp_fin_timeout = 15' | sudo tee -a /etc/sysctl.conf
    echo 'net.ipv4.tcp_tw_reuse = 1' | sudo tee -a /etc/sysctl.conf
    sudo sysctl -p

    # 2. Kiterunner-specific optimizations
    echo "Configuring Kiterunner optimizations..."

    # Create optimized configuration
    cat > ~/.kiterunner/performance_config.yaml << 'EOF'
# High-performance Kiterunner configuration
performance:
  threads: 200              # Increase threads for faster scanning
  timeout: 5                # Reduce timeout for faster responses
  delay: 0                  # No delay between requests
  max_retries: 1            # Reduce retries to avoid slowdowns

network:
  keep_alive: true          # Reuse connections
  max_idle_conns: 100       # Increase connection pool
  max_conns_per_host: 50    # More connections per host

filtering:
  fail_status_codes: [404, 403, 400, 401, 500, 502, 503]
  filter_length: [0, 1, 2, 3]  # Filter out tiny responses

output:
  minimal: true             # Reduce output verbosity
  save_responses: false     # Don't save full responses
EOF

    echo "Performance optimizations applied"
}

# Performance monitoring script
monitor_kiterunner_performance() {
    local target="$1"
    local wordlist="$2"
    local output_file="kiterunner_performance_$(date +%s).log"

    echo "Monitoring Kiterunner performance for: $target"
    echo "Wordlist: $wordlist"

    # Start monitoring
    {
        echo "Timestamp,CPU%,Memory(MB),Network_Connections,Requests_Per_Second"
        while true; do
            if pgrep -f "kr" > /dev/null; then
                local pid=$(pgrep -f "kr")
                local cpu=$(ps -p $pid -o %cpu --no-headers 2>/dev/null || echo "0")
                local mem=$(ps -p $pid -o rss --no-headers 2>/dev/null | awk '{print $1/1024}' || echo "0")
                local connections=$(ss -tuln 2>/dev/null | wc -l || echo "0")
                local timestamp=$(date +%s)

                echo "$timestamp,$cpu,$mem,$connections,N/A"
            fi
            sleep 2
        done
    } > "$output_file" &

    local monitor_pid=$!

    # Run Kiterunner with timing
    echo "Starting Kiterunner scan..."
    time kr brute -w "$wordlist" -t 100 "$target"

    # Stop monitoring
    kill $monitor_pid 2>/dev/null

    echo "Performance monitoring completed: $output_file"
}

# Benchmark different configurations
benchmark_kiterunner() {
    local target="$1"
    local wordlist="$2"

    echo "Benchmarking Kiterunner configurations..."

    # Test different thread counts
    thread_counts=(10 25 50 100 200)

    for threads in "${thread_counts[@]}"; do
        echo "Testing with $threads threads..."

        start_time=$(date +%s)
        kr brute -w "$wordlist" -t "$threads" --timeout 5 "$target" > /dev/null 2>&1
        end_time=$(date +%s)

        duration=$((end_time - start_time))
        echo "Threads: $threads, Duration: ${duration}s"
    done

    # Test different timeout values
    timeouts=(1 3 5 10 15)

    echo "Testing different timeout values..."
    for timeout in "${timeouts[@]}"; do
        echo "Testing with ${timeout}s timeout..."

        start_time=$(date +%s)
        kr brute -w "$wordlist" -t 50 --timeout "$timeout" "$target" > /dev/null 2>&1
        end_time=$(date +%s)

        duration=$((end_time - start_time))
        echo "Timeout: ${timeout}s, Duration: ${duration}s"
    done
}

# Memory optimization for large wordlists
optimize_memory_usage() {
    echo "Optimizing memory usage for large wordlists..."

    # Split large wordlists into smaller chunks
    split_wordlist() {
        local input_wordlist="$1"
        local chunk_size="${2:-1000}"
        local output_dir="${3:-./wordlist_chunks}"

        mkdir -p "$output_dir"

        # Split wordlist
        split -l "$chunk_size" "$input_wordlist" "$output_dir/chunk_"

        echo "Wordlist split into chunks in: $output_dir"
    }

    # Process wordlist chunks sequentially
    process_chunks() {
        local target="$1"
        local chunk_dir="$2"
        local output_file="$3"

        echo "Processing wordlist chunks for: $target"

        for chunk in "$chunk_dir"/chunk_*; do
            echo "Processing chunk: $(basename "$chunk")"

            kr brute -w "$chunk" -t 50 "$target" >> "$output_file"

            # Small delay to prevent overwhelming the target
            sleep 1
        done

        echo "All chunks processed. Results in: $output_file"
    }

    # Example usage
    # split_wordlist "large_wordlist.txt" 500 "./chunks"
    # process_chunks "https://example.com" "./chunks" "results.json"
}

# Run optimizations
optimize_kiterunner_performance

Troubleshooting Common Issues

#!/bin/bash
# Kiterunner troubleshooting guide

troubleshoot_kiterunner() {
    echo "Kiterunner Troubleshooting Guide"
    echo "================================"

    # Check if Kiterunner is installed
    if ! command -v kr &> /dev/null; then
        echo "❌ Kiterunner not found"
        echo "Solution: Download and install Kiterunner from GitHub releases"
        echo "  wget https://github.com/assetnote/kiterunner/releases/latest/download/kiterunner_linux_amd64.tar.gz"
        echo "  tar -xzf kiterunner_linux_amd64.tar.gz"
        echo "  sudo mv kr /usr/local/bin/"
        return 1
    fi

    echo "✅ Kiterunner found: $(kr version 2>/dev/null || echo 'Version unknown')"

    # Check wordlists
    if [ ! -d ~/.kiterunner/wordlists ]; then
        echo "❌ Wordlists directory not found"
        echo "Solution: Create wordlists directory and download wordlists"
        echo "  mkdir -p ~/.kiterunner/wordlists"
        echo "  cd ~/.kiterunner/wordlists"
        echo "  wget https://raw.githubusercontent.com/assetnote/kiterunner/main/wordlists/routes-small.kite"
        return 1
    fi

    echo "✅ Wordlists directory exists"

    # Check for common wordlists
    wordlists=(
        "routes-small.kite"
        "routes-large.kite"
        "apiroutes-210228.kite"
    )

    for wordlist in "${wordlists[@]}"; do
        if [ -f ~/.kiterunner/wordlists/"$wordlist" ]; then
            echo "✅ Wordlist found: $wordlist"
        else
            echo "⚠️  Wordlist missing: $wordlist"
        fi
    done

    # Check network connectivity
    if ! curl -s --connect-timeout 5 https://httpbin.org/get > /dev/null; then
        echo "❌ Network connectivity issues"
        echo "Solution: Check internet connection and proxy settings"
        return 1
    fi

    echo "✅ Network connectivity OK"

    # Check system resources
    available_memory=$(free -m | awk 'NR==2{printf "%.1f", $7/1024}')
    if (( $(echo "$available_memory < 0.5" | bc -l) )); then
        echo "⚠️  Low available memory: ${available_memory}GB"
        echo "Solution: Free up memory or reduce thread count"
    else
        echo "✅ Available memory: ${available_memory}GB"
    fi

    # Check file descriptor limits
    fd_limit=$(ulimit -n)
    if [ "$fd_limit" -lt 1024 ]; then
        echo "⚠️  Low file descriptor limit: $fd_limit"
        echo "Solution: Increase file descriptor limit"
        echo "  ulimit -n 65536"
    else
        echo "✅ File descriptor limit: $fd_limit"
    fi

    echo "Troubleshooting completed"
}

# Common error solutions
fix_common_kiterunner_errors() {
    echo "Common Kiterunner Errors and Solutions"
    echo "======================================"

    cat << 'EOF'
1. "kr: command not found"
   Solution: 
   - Download Kiterunner binary from GitHub releases
   - Extract and move to /usr/local/bin/
   - Ensure /usr/local/bin/ is in your PATH

2. "permission denied" when running kr
   Solution:
   - Make the binary executable: chmod +x /usr/local/bin/kr
   - Check file ownership and permissions

3. "too many open files" error
   Solution:
   - Increase file descriptor limit: ulimit -n 65536
   - Add to ~/.bashrc for persistence
   - Reduce thread count if problem persists

4. "connection timeout" or "connection refused"
   Solution:
   - Check target URL is accessible
   - Verify firewall settings
   - Increase timeout value: --timeout 30
   - Check if target is rate limiting

5. "wordlist not found" error
   Solution:
   - Verify wordlist path is correct
   - Download official wordlists from GitHub
   - Use absolute paths for wordlists

6. Slow scanning performance
   Solution:
   - Increase thread count: -t 100
   - Reduce timeout: --timeout 5
   - Use smaller wordlists for testing
   - Check network latency to target

7. "out of memory" errors
   Solution:
   - Reduce thread count
   - Split large wordlists into chunks
   - Process targets sequentially
   - Monitor memory usage during scans

8. No results found (false negatives)
   Solution:
   - Try different wordlists
   - Adjust status code filtering
   - Check response length filtering
   - Verify target is responding correctly

9. SSL/TLS certificate errors
   Solution:
   - Use --insecure flag for self-signed certificates
   - Update system CA certificates
   - Check target SSL configuration

10. Rate limiting by target
    Solution:
    - Reduce thread count: -t 10
    - Add delay between requests: --delay 100
    - Use proxy rotation if available
    - Respect target's robots.txt and rate limits
EOF
}

# Test basic functionality
test_kiterunner_functionality() {
    echo "Testing Kiterunner Basic Functionality"
    echo "======================================"

    # Test with httpbin.org (reliable test target)
    test_target="https://httpbin.org"

    echo "Testing basic discovery against: $test_target"

    # Create minimal test wordlist
    cat > /tmp/test_wordlist.txt << 'EOF'
get
post
put
delete
status
headers
ip
user-agent
EOF

    # Run test scan
    echo "Running test scan..."
    if kr brute -w /tmp/test_wordlist.txt -t 5 --timeout 10 "$test_target" > /tmp/kr_test_output.txt 2>&1; then
        echo "✅ Basic functionality test passed"

        # Check if any endpoints were found
        if [ -s /tmp/kr_test_output.txt ]; then
            echo "✅ Endpoints discovered during test"
            echo "Sample results:"
            head -5 /tmp/kr_test_output.txt
        else
            echo "⚠️  No endpoints found (this might be normal)"
        fi
    else
        echo "❌ Basic functionality test failed"
        echo "Error output:"
        cat /tmp/kr_test_output.txt
    fi

    # Clean up
    rm -f /tmp/test_wordlist.txt /tmp/kr_test_output.txt
}

# Performance diagnostics
diagnose_performance_issues() {
    echo "Diagnosing Kiterunner Performance Issues"
    echo "======================================="

    # Check system load
    load_avg=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | sed 's/,//')
    echo "System load average: $load_avg"

    # Check available CPU cores
    cpu_cores=$(nproc)
    echo "Available CPU cores: $cpu_cores"

    # Check memory usage
    memory_info=$(free -h | grep "Mem:")
    echo "Memory info: $memory_info"

    # Check disk I/O
    if command -v iostat &> /dev/null; then
        echo "Disk I/O statistics:"
        iostat -x 1 1 | tail -n +4
    fi

    # Check network connectivity speed
    echo "Testing network speed to common target..."
    if command -v curl &> /dev/null; then
        time_total=$(curl -o /dev/null -s -w "%{time_total}" https://httpbin.org/get)
        echo "Network response time: ${time_total}s"
    fi

    # Recommendations based on findings
    echo ""
    echo "Performance Recommendations:"
    echo "- Optimal thread count: $((cpu_cores * 10))"
    echo "- Recommended timeout: 5-10 seconds"
    echo "- Consider using delay if target is slow: --delay 50"
}

# Main troubleshooting function
main() {
    troubleshoot_kiterunner
    echo ""
    fix_common_kiterunner_errors
    echo ""
    test_kiterunner_functionality
    echo ""
    diagnose_performance_issues
}

# Run troubleshooting
main

Resources and Documentation

Official Resources

Community Resources

Integration Examples