2024-03-14 03:08:53 +05:30
package fuzz
import (
"io"
2024-06-11 04:43:46 +05:30
"strconv"
2024-03-14 03:08:53 +05:30
"strings"
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz/component"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/expressions"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
"github.com/projectdiscovery/nuclei/v3/pkg/types"
"github.com/projectdiscovery/retryablehttp-go"
2024-06-11 04:43:46 +05:30
sliceutil "github.com/projectdiscovery/utils/slice"
2024-03-14 03:08:53 +05:30
)
// executePartRule executes part rules based on type
func ( rule * Rule ) executePartRule ( input * ExecuteRuleInput , payload ValueOrKeyValue , component component . Component ) error {
return rule . executePartComponent ( input , payload , component )
}
// checkRuleApplicableOnComponent checks if a rule is applicable on given component
func ( rule * Rule ) checkRuleApplicableOnComponent ( component component . Component ) bool {
2024-06-11 04:43:46 +05:30
if rule . Part != component . Name ( ) && ! sliceutil . Contains ( rule . Parts , component . Name ( ) ) && rule . partType != requestPartType {
2024-03-14 03:08:53 +05:30
return false
}
foundAny := false
_ = component . Iterate ( func ( key string , value interface { } ) error {
if rule . matchKeyOrValue ( key , types . ToString ( value ) ) {
foundAny = true
return io . EOF
}
return nil
} )
return foundAny
}
// executePartComponent executes this rule on a given component and payload
func ( rule * Rule ) executePartComponent ( input * ExecuteRuleInput , payload ValueOrKeyValue , ruleComponent component . Component ) error {
2024-03-29 13:31:30 +05:30
// Note: component needs to be cloned because they contain values copied by reference
2024-03-14 03:08:53 +05:30
if payload . IsKV ( ) {
// for kv fuzzing
2024-03-29 13:31:30 +05:30
return rule . executePartComponentOnKV ( input , payload , ruleComponent . Clone ( ) )
2024-03-14 03:08:53 +05:30
} else {
// for value only fuzzing
2024-11-19 11:51:32 +05:30
return rule . executePartComponentOnValues ( input , payload . Value , payload . OriginalPayload , ruleComponent . Clone ( ) )
2024-03-14 03:08:53 +05:30
}
}
// executePartComponentOnValues executes this rule on a given component and payload
// this supports both single and multiple [ruleType] modes
// i.e if component has multiple values, they can be replaced once or all depending on mode
2024-11-19 11:51:32 +05:30
func ( rule * Rule ) executePartComponentOnValues ( input * ExecuteRuleInput , payloadStr , originalPayload string , ruleComponent component . Component ) error {
2024-03-14 03:08:53 +05:30
finalErr := ruleComponent . Iterate ( func ( key string , value interface { } ) error {
valueStr := types . ToString ( value )
if ! rule . matchKeyOrValue ( key , valueStr ) {
// ignore non-matching keys
return nil
}
2024-11-19 11:51:32 +05:30
var evaluated , originalEvaluated string
2024-03-14 03:08:53 +05:30
evaluated , input . InteractURLs = rule . executeEvaluate ( input , key , valueStr , payloadStr , input . InteractURLs )
2024-11-19 11:51:32 +05:30
if input . ApplyPayloadInitialTransformation != nil {
evaluated = input . ApplyPayloadInitialTransformation ( evaluated , input . AnalyzerParams )
originalEvaluated , _ = rule . executeEvaluate ( input , key , valueStr , originalPayload , input . InteractURLs )
}
2024-03-14 03:08:53 +05:30
if err := ruleComponent . SetValue ( key , evaluated ) ; err != nil {
// gologger.Warning().Msgf("could not set value due to format restriction original(%s, %s[%T]) , new(%s,%s[%T])", key, valueStr, value, key, evaluated, evaluated)
return nil
}
if rule . modeType == singleModeType {
req , err := ruleComponent . Rebuild ( )
if err != nil {
return err
}
2024-11-19 11:51:32 +05:30
if qerr := rule . execWithInput ( input , req , input . InteractURLs , ruleComponent , key , valueStr , originalEvaluated , valueStr , key , evaluated ) ; qerr != nil {
2024-03-14 03:08:53 +05:30
return qerr
}
// fmt.Printf("executed with value: %s\n", evaluated)
err = ruleComponent . SetValue ( key , valueStr ) // change back to previous value for temp
if err != nil {
return err
}
}
return nil
} )
if finalErr != nil {
return finalErr
}
// We do not support analyzers with
// multiple payload mode.
if rule . modeType == multipleModeType {
req , err := ruleComponent . Rebuild ( )
if err != nil {
return err
}
2024-11-19 11:51:32 +05:30
if qerr := rule . execWithInput ( input , req , input . InteractURLs , ruleComponent , "" , "" , "" , "" , "" , "" ) ; qerr != nil {
2024-03-14 03:08:53 +05:30
err = qerr
return err
}
}
return nil
}
// executePartComponentOnKV executes this rule on a given component and payload
// currently only supports single mode
func ( rule * Rule ) executePartComponentOnKV ( input * ExecuteRuleInput , payload ValueOrKeyValue , ruleComponent component . Component ) error {
var origKey string
var origValue interface { }
// when we have a key-value pair, iterate over only 1 value of the component
// multiple values (aka multiple mode) not supported for this yet
_ = ruleComponent . Iterate ( func ( key string , value interface { } ) error {
if key == payload . Key {
origKey = key
origValue = value
}
return nil
} )
// iterate over given kv instead of component ones
return func ( key , value string ) error {
var evaluated string
evaluated , input . InteractURLs = rule . executeEvaluate ( input , key , "" , value , input . InteractURLs )
if err := ruleComponent . SetValue ( key , evaluated ) ; err != nil {
return err
}
if rule . modeType == singleModeType {
req , err := ruleComponent . Rebuild ( )
if err != nil {
return err
}
2024-11-19 11:51:32 +05:30
if qerr := rule . execWithInput ( input , req , input . InteractURLs , ruleComponent , key , value , "" , "" , "" , "" ) ; qerr != nil {
2025-03-03 11:49:16 +01:00
return qerr
2024-03-14 03:08:53 +05:30
}
// after building change back to original value to avoid repeating it in furthur requests
if origKey != "" {
err = ruleComponent . SetValue ( origKey , types . ToString ( origValue ) ) // change back to previous value for temp
if err != nil {
return err
}
} else {
_ = ruleComponent . Delete ( key ) // change back to previous value for temp
}
}
return nil
} ( payload . Key , payload . Value )
}
// execWithInput executes a rule with input via callback
2024-11-19 11:51:32 +05:30
func ( rule * Rule ) execWithInput ( input * ExecuteRuleInput , httpReq * retryablehttp . Request , interactURLs [ ] string , component component . Component , parameter , parameterValue , originalPayload , originalValue , key , value string ) error {
2024-06-11 04:43:46 +05:30
// If the parameter is a number, replace it with the parameter value
// or if the parameter is empty and the parameter value is not empty
// replace it with the parameter value
2024-11-19 11:51:32 +05:30
actualParameter := parameter
2024-06-11 04:43:46 +05:30
if _ , err := strconv . Atoi ( parameter ) ; err == nil || ( parameter == "" && parameterValue != "" ) {
2024-11-19 11:51:32 +05:30
actualParameter = parameterValue
2024-06-11 04:43:46 +05:30
}
// If the parameter is frequent, skip it if the option is enabled
if rule . options . FuzzParamsFrequency != nil {
if rule . options . FuzzParamsFrequency . IsParameterFrequent (
parameter ,
2025-07-01 00:40:44 +07:00
httpReq . String ( ) ,
2024-06-11 04:43:46 +05:30
rule . options . TemplateID ,
) {
return nil
}
}
2024-03-14 03:08:53 +05:30
request := GeneratedRequest {
2024-11-19 11:51:32 +05:30
Request : httpReq ,
InteractURLs : interactURLs ,
DynamicValues : input . Values ,
Component : component ,
Parameter : actualParameter ,
Key : key ,
Value : value ,
OriginalValue : originalValue ,
OriginalPayload : originalPayload ,
2024-03-14 03:08:53 +05:30
}
if ! input . Callback ( request ) {
return types . ErrNoMoreRequests
}
return nil
}
// executeEvaluate executes evaluation of payload on a key and value and
// returns completed values to be replaced and processed
// for fuzzing.
func ( rule * Rule ) executeEvaluate ( input * ExecuteRuleInput , _ , value , payload string , interactshURLs [ ] string ) ( string , [ ] string ) {
// TODO: Handle errors
2024-07-26 00:01:05 +05:30
values := generators . MergeMaps ( rule . options . Variables . GetAll ( ) , map [ string ] interface { } {
2024-03-14 03:08:53 +05:30
"value" : value ,
2024-07-26 00:01:05 +05:30
} , rule . options . Options . Vars . AsMap ( ) , input . Values )
2024-03-14 03:08:53 +05:30
firstpass , _ := expressions . Evaluate ( payload , values )
interactData , interactshURLs := rule . options . Interactsh . Replace ( firstpass , interactshURLs )
evaluated , _ := expressions . Evaluate ( interactData , values )
replaced := rule . executeRuleTypes ( input , value , evaluated )
return replaced , interactshURLs
}
// executeRuleTypes executes replacement for a key and value
// ex: prefix, postfix, infix, replace , replace-regex
func ( rule * Rule ) executeRuleTypes ( _ * ExecuteRuleInput , value , replacement string ) string {
var builder strings . Builder
if rule . ruleType == prefixRuleType || rule . ruleType == postfixRuleType {
builder . Grow ( len ( value ) + len ( replacement ) )
}
var returnValue string
switch rule . ruleType {
case prefixRuleType :
builder . WriteString ( replacement )
builder . WriteString ( value )
returnValue = builder . String ( )
case postfixRuleType :
builder . WriteString ( value )
builder . WriteString ( replacement )
returnValue = builder . String ( )
case infixRuleType :
if len ( value ) <= 1 {
builder . WriteString ( value )
builder . WriteString ( replacement )
returnValue = builder . String ( )
} else {
middleIndex := len ( value ) / 2
builder . WriteString ( value [ : middleIndex ] )
builder . WriteString ( replacement )
builder . WriteString ( value [ middleIndex : ] )
returnValue = builder . String ( )
}
case replaceRuleType :
returnValue = replacement
case replaceRegexRuleType :
returnValue = rule . replaceRegex . ReplaceAllString ( value , replacement )
}
return returnValue
}