mirror of
https://github.com/lunchcat/sif.git
synced 2026-07-28 14:37:01 -07:00
feat(modules): add disable-redirects http option (#273)
A module that fingerprints a redirect itself (open-redirect proofs, a Location header, a 3xx status) could not see the redirect: the executor followed 3xx to the final response before matchers ran, with no way to stop. Add an opt-in disable-redirects field that scopes an ErrUseLastResponse policy to the module's own client copy, so the shared httpx transport keeps following redirects for every other module.
This commit is contained in:
@@ -72,6 +72,18 @@ func ExecuteHTTPModule(ctx context.Context, target string, def *YAMLModule, opts
|
||||
}
|
||||
}
|
||||
|
||||
// disable-redirects only applies to this module's requests; opts.Client may
|
||||
// be the shared httpx client reused by every other module in the run, so a
|
||||
// module-scoped policy shallow-copies it (keeping the pooled Transport)
|
||||
// rather than mutating CheckRedirect on the shared instance.
|
||||
if cfg.DisableRedirects {
|
||||
scoped := *client
|
||||
scoped.CheckRedirect = func(_ *http.Request, _ []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
client = &scoped
|
||||
}
|
||||
|
||||
// Generate requests based on paths and payloads
|
||||
requests, err := generateHTTPRequests(target, cfg)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
: :
|
||||
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
||||
: ▄█ █ █▀ · BSD 3-Clause License :
|
||||
: :
|
||||
: (c) 2022-2026 vmfunc, xyzeva, :
|
||||
: lunchcat alumni & contributors :
|
||||
: :
|
||||
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
||||
*/
|
||||
|
||||
package modules
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/vmfunc/sif/internal/httpx"
|
||||
)
|
||||
|
||||
// a target whose root 302-redirects to a landing page: the redirect response
|
||||
// carries the signal (a Location header, the 302 status), the landing page does
|
||||
// not. matching that signal requires stopping at the 3xx.
|
||||
func redirectServer() *httptest.Server {
|
||||
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/landing" {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("landed"))
|
||||
return
|
||||
}
|
||||
w.Header().Set("Location", "/landing")
|
||||
w.WriteHeader(http.StatusFound)
|
||||
}))
|
||||
}
|
||||
|
||||
func TestExecuteHTTPModuleDisableRedirects(t *testing.T) {
|
||||
srv := redirectServer()
|
||||
defer srv.Close()
|
||||
|
||||
mod := func(disable bool) *YAMLModule {
|
||||
return &YAMLModule{
|
||||
ID: "redirect",
|
||||
Type: TypeHTTP,
|
||||
HTTP: &HTTPConfig{
|
||||
Paths: []string{"{{BaseURL}}/"},
|
||||
DisableRedirects: disable,
|
||||
Matchers: []Matcher{
|
||||
{Type: "status", Status: []int{http.StatusFound}},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
opts := Options{Timeout: testTimeout, Client: httpx.Client(testTimeout)}
|
||||
|
||||
followed, err := ExecuteHTTPModule(context.Background(), srv.URL, mod(false), opts)
|
||||
if err != nil {
|
||||
t.Fatalf("ExecuteHTTPModule(follow): %v", err)
|
||||
}
|
||||
if len(followed.Findings) != 0 {
|
||||
t.Fatalf("with redirects followed, got %d findings matching 302, want 0", len(followed.Findings))
|
||||
}
|
||||
|
||||
stopped, err := ExecuteHTTPModule(context.Background(), srv.URL, mod(true), opts)
|
||||
if err != nil {
|
||||
t.Fatalf("ExecuteHTTPModule(no-follow): %v", err)
|
||||
}
|
||||
if len(stopped.Findings) != 1 {
|
||||
t.Fatalf("with redirects disabled, got %d findings matching 302, want 1", len(stopped.Findings))
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,7 @@ type HTTPConfig struct {
|
||||
Body string `yaml:"body,omitempty"`
|
||||
Attack string `yaml:"attack,omitempty"` // clusterbomb (default), pitchfork
|
||||
Threads int `yaml:"threads,omitempty"`
|
||||
DisableRedirects bool `yaml:"disable-redirects,omitempty"` // stop at the first response; don't follow 3xx
|
||||
Matchers []Matcher `yaml:"matchers"`
|
||||
MatchersCondition string `yaml:"matchers-condition,omitempty"` // and (default), or
|
||||
Extractors []Extractor `yaml:"extractors,omitempty"`
|
||||
|
||||
Reference in New Issue
Block a user