parammine

package
v0.5.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 18, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package parammine discovers hidden HTTP parameters: names an endpoint reads and acts on although no observed request ever carried them (debug=1, callback=, admin=). It is Arjun-style mining, built on vigolium's own response-comparison helpers:

  1. Two baselines, each with one random benign parameter, give the endpoint's stability profile: status, header-name set, normalized body hash and a length/similarity signature. An endpoint whose two baselines disagree is unstable and is abandoned rather than mined.
  2. The candidate names are sent in chunks sized to the transport (a 6 KiB request target for the query string, 32 KiB for a form or JSON body), each name carrying its own random canary value.
  3. A chunk whose response differs from the baseline (status, header set, body via modkit.IsDifferent / modkit.HasSubstantialBodyDifference / similarity) is bisected, both halves at a time, down to single names. A canary echoed in the response maps straight to its name, no bisection needed.
  4. Every single-name candidate is re-sent ConfirmRounds times with fresh canaries and kept only when the signal reproduces each time.

The miner sends nothing itself: the caller supplies a Sender, so the scan phase routes through the shared requester (session, rate limit, host limiter) and the kit command through a requester of its own. Each endpoint is bounded by Config.MaxRequests, and mining stops on a burst of 403/429 or edge-block answers rather than keep feeding a WAF.

Index

Constants

View Source
const (
	AbortUnstable = "unstable"        // the two baselines disagreed
	AbortBlocked  = "blocked"         // a burst of 403/429/edge-block answers
	AbortBudget   = "budget"          // Config.MaxRequests was reached
	AbortBaseline = "baseline_failed" // a baseline request got no HTTP answer
	AbortErrors   = "errors"          // consecutive transport errors
	AbortCanceled = "canceled"        // the context ended
)

Abort reasons reported in Result.Aborted. Empty means the whole wordlist was tested within budget.

View Source
const (
	DefaultMaxRequests      = 60
	DefaultMaxQueryBytes    = 6 << 10
	DefaultMaxBodyBytes     = 32 << 10
	DefaultConfirmRounds    = 2
	DefaultBlockBurst       = 3
	DefaultMaxNamesPerChunk = 256
)

Defaults for the zero Config.

View Source
const BuiltinWordlistName = "params.txt"

BuiltinWordlistName is the embedded list of common parameter names, also printable with `vigolium kit wordlist params`.

It is curated in-repo rather than copied from a third-party list (Arjun's is GPL-3): generic names web frameworks and APIs commonly read (debug, callback, redirect_uri, …), the parameter names the scanner's own modules already probe (xss_light_scanner discovery, open-redirect, LFI, SSRF, OAuth, LDAP, IDOR), and common entity names with their id/name/type suffixes (user_id, orderId).

Variables

View Source
var ErrNoRequest = errors.New("parammine: no base request")

ErrNoRequest is returned for a nil base request.

Functions

func Builtin

func Builtin() ([]string, error)

Builtin returns the embedded parameter-name list, parsed once.

func MergeWordlists

func MergeWordlists(max int, sources ...[]string) []string

MergeWordlists concatenates the sources in priority order, dropping invalid names and duplicates (first occurrence wins), and stops at max names when max is positive. Put the names most likely to matter first: the ones the target's own JavaScript and traffic use, then the built-in list.

func ParseNames

func ParseNames(data []byte) []string

ParseNames splits a one-name-per-line list, skipping blanks, comments and unusable names.

func ReadWordlistFile

func ReadWordlistFile(path string) ([]string, error)

ReadWordlistFile reads a one-name-per-line file ("#" comments allowed).

func ValidName

func ValidName(s string) bool

ValidName reports whether s can be sent as a parameter name: non-empty, bounded, and free of whitespace, control characters and the separators that would split it into something else.

Types

type Config

type Config struct {
	// MaxRequests is the per-endpoint request budget, baselines and
	// confirmations included.
	MaxRequests int
	// MaxQueryBytes caps the request target (path plus query string) of a
	// query-location chunk.
	MaxQueryBytes int
	// MaxBodyBytes caps the body of a form or JSON chunk.
	MaxBodyBytes int
	// MaxNamesPerChunk caps the names in one chunk regardless of size; many
	// stacks drop parameters past a count (PHP max_input_vars).
	MaxNamesPerChunk int
	// ConfirmRounds is how many fresh-canary re-sends a candidate must survive.
	ConfirmRounds int
	// BlockBurst is how many consecutive 403/429/edge-block answers stop the
	// endpoint.
	BlockBurst int
	// Location forces where names go. Empty picks one from the request: the
	// query string for bodiless methods, the body's own encoding otherwise.
	Location Location
}

Config bounds one endpoint's mining. Zero fields take the defaults above.

type Hit

type Hit struct {
	Name     string   `json:"name"`
	Signal   Signal   `json:"signal"`
	Location Location `json:"location"`
	// Detail says what changed, e.g. "status 200 -> 302".
	Detail string `json:"detail,omitempty"`
	// Exchange is the last confirming request (carrying only this parameter
	// on top of the original request) and its response.
	Exchange *httpmsg.HttpRequestResponse `json:"-"`
}

Hit is one confirmed hidden parameter.

type Location

type Location string

Location is where the mined names are placed in the request.

const (
	LocationQuery Location = "query" // appended to the URL query string
	LocationForm  Location = "form"  // appended to an application/x-www-form-urlencoded body
	LocationJSON  Location = "json"  // added as top-level keys of a JSON object body
)

func DetectLocation

func DetectLocation(req *httpmsg.HttpRequest) Location

DetectLocation picks where to put mined names for a request: the query string for bodiless methods and multipart bodies, the JSON object for a JSON body, the form body otherwise.

func ParseLocation

func ParseLocation(s string) (Location, error)

ParseLocation validates a user-supplied location; "" and "auto" mean pick from the request.

type Result

type Result struct {
	URL      string   `json:"url"`
	Method   string   `json:"method"`
	Location Location `json:"location"`
	Hits     []Hit    `json:"params"`
	// Tested is how many candidate names were sent at least once.
	Tested int `json:"names_tested"`
	// Requests is how many requests mining sent for this endpoint.
	Requests int `json:"requests"`
	// Aborted is why mining stopped early (Abort*), empty when it finished.
	Aborted string `json:"aborted,omitempty"`
}

Result is one endpoint's mining outcome.

func Mine

func Mine(ctx context.Context, base *httpmsg.HttpRequestResponse, words []string, send Sender, cfg Config) (res Result, err error)

Mine tests words against the endpoint base describes and returns the parameters it confirmed. words is used in order (put the likeliest first); names the base request already carries are skipped. Mine returns an error only for unusable input; every runtime failure is a Result.Aborted reason.

type Sender

Sender sends one request and returns its response. Redirects must not be followed: a hidden parameter that triggers a redirect is a status signal.

func RequesterSender

func RequesterSender(r *vighttp.Requester) Sender

RequesterSender adapts a vigolium requester into a Sender. Redirects are recorded, not followed, and request clustering is off: every probe carries different canaries, and a clustered answer would be another probe's.

type Signal

type Signal string

Signal is the observable difference that revealed a parameter.

const (
	SignalReflect Signal = "reflect" // the parameter's canary value was echoed back
	SignalStatus  Signal = "status"  // the status code changed
	SignalHeader  Signal = "header"  // the set of response header names changed
	SignalLength  Signal = "length"  // the body length moved past the substantial-difference bound
	SignalBody    Signal = "body"    // the body content changed at a similar length
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL