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:
- 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.
- 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.
- 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.
- 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
- Variables
- func Builtin() ([]string, error)
- func MergeWordlists(max int, sources ...[]string) []string
- func ParseNames(data []byte) []string
- func ReadWordlistFile(path string) ([]string, error)
- func ValidName(s string) bool
- type Config
- type Hit
- type Location
- type Result
- type Sender
- type Signal
Constants ¶
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.
const ( DefaultMaxRequests = 60 DefaultMaxQueryBytes = 6 << 10 DefaultMaxBodyBytes = 32 << 10 DefaultConfirmRounds = 2 DefaultBlockBurst = 3 DefaultMaxNamesPerChunk = 256 )
Defaults for the zero Config.
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 ¶
var ErrNoRequest = errors.New("parammine: no base request")
ErrNoRequest is returned for a nil base request.
Functions ¶
func MergeWordlists ¶
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 ¶
ParseNames splits a one-name-per-line list, skipping blanks, comments and unusable names.
func ReadWordlistFile ¶
ReadWordlistFile reads a one-name-per-line file ("#" comments allowed).
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.
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 ¶
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 ¶
type Sender func(ctx context.Context, rr *httpmsg.HttpRequestResponse) (*httpmsg.HttpResponse, error)
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 ¶
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 )