Modules

Oblivion's power comes from its modular design. Each module is a focused tool designed to perform specific web hacking and security tasks. Modules can be easily loaded, configured, and executed from the interactive REPL.

dns brute

Brute-force subdomains using a wordlist and DNS resolution, optionally appending numeric suffixes to found hosts

Usage: use dnsbrute

fuzzer

Wrapper of ffuf https://github.com/ffuf/ffuf

Usage: use fuzzer

port scanner

Simple port scanner with UDP support

Usage: use portscanner

subdomains search

Simple subdomains search

Usage: use subdomains_search

subdomain takeover

Detects subdomain takeover via CNAME and HTTP response analysis

Usage: use subdomain_takeover

web spider

Web spider with JavaScript support via headless browser

Usage: use webspider

Custom Modules

You can extend Oblivion by creating your own modules in Go. Write your code in the /modules directory, then register your module in main.go to make it available in the REPL.

Example file location: modules/custom/my_module.go

Below is a generic, fully‑commented Go “boilerplate” template you can adapt for any Oblivion module that uses a cancellable context, worker goroutines and channels. Replace the {{…}} placeholders with your own module’s names, options, and logic:

package {{ .PackageName }}

import (
    "context"
    "fmt"
    "os"
    "sort"
    "sync"

    "github.com/czz/oblivion/utils/option"
    "github.com/czz/oblivion/utils/help"
)

// {{ .StructName }} is the main struct for the {{ .ModuleTitle }} module.
type {{ .StructName }} struct {
    optionManager *option.OptionManager
    help          *help.HelpManager
    results       []string
    running       bool
    name          string
    author        string
    desc          string
    prompt        string
}

// New{{ .StructName }} constructs a new {{ .ModuleTitle }} instance.
func New{{ .StructName }}() *{{ .StructName }} {
    om := option.NewOptionManager()

    // Register your options here. Example:
    om.Register(option.NewOption("TARGET", "", true, "The target to process"))
    om.Register(option.NewOption("THREADS", "10", false, "Number of concurrent workers"))
    // … add more options as needed …

    hm := help.NewHelpManager()
    hm.Register("{{ .Prompt }}", "{{ .ModuleTitle }}", [][]string{
        {"TARGET", "example.com", "The domain to enumerate"},
        {"THREADS", "10", "Concurrency level"},
        // … help for other options …
    })

    return &{{ .StructName }}{
        optionManager: om,
        help:          hm,
        name:          "{{ .ModuleTitle }}",
        author:        "{{ .Author }}",
        desc:          "{{ .Description }}",
        prompt:        "{{ .Prompt }}",
    }
}

// Run executes the module, honoring context cancellation.
func (m *{{ .StructName }}) Run(ctx context.Context) [][]string {
    // Clear previous results
    m.results = nil

    // 1. Extract and validate options
    targetOpt, _ := m.optionManager.Get("TARGET")
    threadsOpt, _ := m.optionManager.Get("THREADS")
    target := targetOpt.Value.(string)
    threads := 10
    if t, err := strconv.Atoi(threadsOpt.Value.(string)); err == nil && t > 0 {
        threads = t
    }

    if target == "" {
        return [][]string{{"Error: TARGET not set"}}
    }

    // 2. Prepare your input slice (e.g. wordlist, prefixes, IPs…)
    inputs := []string{/* fill from file or option */}

    // 3. Create channels
    tasks := make(chan string, len(inputs))
    results := make(chan string, len(inputs))

    // 4. Enqueue all tasks and close
    for _, item := range inputs {
        tasks <- item
    }
    close(tasks)

    // 5. Launch worker goroutines
    var wg sync.WaitGroup
    for i := 0; i < threads; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for {
                select {
                case <-ctx.Done():
                    // context cancelled: exit immediately
                    return
                case item, ok := <-tasks:
                    if !ok {
                        return
                    }
                    // --- YOUR PROCESSING LOGIC HERE ---
                    if someCheck(item) {
                        select {
                        case <-ctx.Done():
                            return
                        case results <- item:
                        }
                    }
                }
            }
        }()
    }

    // 6. Close results channel when all workers finish
    go func() {
        wg.Wait()
        close(results)
    }()

    // 7. Collect unique results, respecting cancellation
    unique := make(map[string]struct{})
    for {
        select {
        case <-ctx.Done():
            // stop collecting on cancel
            goto DONE
        case r, ok := <-results:
            if !ok {
                goto DONE
            }
            unique[r] = struct{}{}
        }
    }

DONE:
    // 8. Build sorted output table
    sorted := make([]string, 0, len(unique))
    for k := range unique {
        sorted = append(sorted, k)
    }
    sort.Strings(sorted)

    table := make([][]string, len(sorted))
    for i, v := range sorted {
        table[i] = []string{v}
    }

    m.results = sorted
    return table
}

// Save writes the results to a file.
func (m *{{ .StructName }}) Save(path string) error {
    if len(m.results) == 0 {
        return fmt.Errorf("no results to save")
    }
    f, err := os.Create(path)
    if err != nil {
        return err
    }
    defer f.Close()
    for _, line := range m.results {
        if _, err := f.WriteString(line + "\n"); err != nil {
            return err
        }
    }
    return nil
}

// Help returns the help text for the module.
func (m *{{ .StructName }}) Help() [][]string {
    h, _ := m.help.Get(m.prompt)
    return h
}

// Options lists the module’s configurable options.
func (m *{{ .StructName }}) Options() []map[string]string {
    opts := m.optionManager.List()
    out := make([]map[string]string, len(opts))
    for i, o := range opts {
        out[i] = o.Format()
    }
    return out
}

// Results returns the raw string results.
func (m *{{ .StructName }}) Results() [][]string {
    table := make([][]string, len(m.results))
    for i, v := range m.results {
        table[i] = []string{v}
    }
    return table
}

// Set allows runtime modification of an option.
func (m *{{ .StructName }}) Set(name, val string) []string {
    om := *m.optionManager
    if o, ok := om.Get(name); ok {
        o.Set(val)
        return []string{o.Name, fmt.Sprintf("%v", o.Value)}
    }
    return []string{"Error", "Option not found"}
}

// Metadata methods:
func (m *{{ .StructName }}) Name() string        { return m.name }
func (m *{{ .StructName }}) Author() string      { return m.author }
func (m *{{ .StructName }}) Description() string { return m.desc }
func (m *{{ .StructName }}) Prompt() string      { return m.prompt }
func (m *{{ .StructName }}) Running() bool       { return m.running }
func (m *{{ .StructName }}) Start() error        { m.running = true; return nil }
func (m *{{ .StructName }}) Stop() error         { m.running = false; return nil }

// Helper: your per‑item check or processing function.
func someCheck(item string) bool {
    // implement your domain lookup / HTTP request / whatever…
    return true
}

Module Architecture Diagram

┌─────────────┐
│ REPL UI │
└──────┬──────┘

┌───────▼───────┐
│ Module Loader │
└───────┬───────┘

┌─────────▼────────┐
│ Selected Module │
│ (e.g. spider) │
└─────────┬────────┘

┌─────────▼────────┐
│ Module Interface │
│ (Run, Config) │
└─────────┬────────┘

┌─────────▼────────┐
│ Requests & │
│ Response Parsing │
└──────────────────┘

Using Modules

Typical workflow from the REPL:

use webspider
show options
set TARGETS https://example.com
set ALLOWED_DOMAINS example.com
run

Modules accept parameters via set commands and expose options with options.