From 8455d2b350c8f7359277405994a9107ecdae367a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Do=C4=9Fan=20Can=20Bak=C4=B1r?= Date: Tue, 1 Apr 2025 17:41:45 +0530 Subject: [PATCH 1/6] fix `-hi`response --- pkg/server/http_server.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pkg/server/http_server.go b/pkg/server/http_server.go index 20310aeb..200c2e06 100644 --- a/pkg/server/http_server.go +++ b/pkg/server/http_server.go @@ -255,6 +255,14 @@ func (h *HTTPServer) defaultHandler(w http.ResponseWriter, req *http.Request) { } reflection := h.options.URLReflection(req.Host) + + // If custom index is defined, always return it regardless of path + if h.customBanner != "" { + fmt.Fprint(w, strings.ReplaceAll(h.customBanner, "{DOMAIN}", domain)) + return + } + + // Handle other cases if no custom index is defined if stringsutil.HasPrefixI(req.URL.Path, "/s/") && h.staticHandler != nil { if h.options.DynamicResp && len(req.URL.Query()) > 0 { values := req.URL.Query() @@ -278,11 +286,7 @@ func (h *HTTPServer) defaultHandler(w http.ResponseWriter, req *http.Request) { } h.staticHandler.ServeHTTP(w, req) } else if req.URL.Path == "/" && reflection == "" { - if h.customBanner != "" { - fmt.Fprint(w, strings.ReplaceAll(h.customBanner, "{DOMAIN}", domain)) - } else { - fmt.Fprintf(w, banner, domain) - } + fmt.Fprintf(w, banner, domain) } else if strings.EqualFold(req.URL.Path, "/robots.txt") { fmt.Fprintf(w, "User-agent: *\nDisallow: / # %s", reflection) } else if stringsutil.HasSuffixI(req.URL.Path, ".json") { From 0277027b3609d642136f23f7f2879d9b4fe48b97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Do=C4=9Fan=20Can=20Bak=C4=B1r?= Date: Thu, 24 Apr 2025 13:16:53 +0300 Subject: [PATCH 2/6] Revert "fix `-hi`response" This reverts commit 8455d2b350c8f7359277405994a9107ecdae367a. --- pkg/server/http_server.go | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/pkg/server/http_server.go b/pkg/server/http_server.go index 200c2e06..20310aeb 100644 --- a/pkg/server/http_server.go +++ b/pkg/server/http_server.go @@ -255,14 +255,6 @@ func (h *HTTPServer) defaultHandler(w http.ResponseWriter, req *http.Request) { } reflection := h.options.URLReflection(req.Host) - - // If custom index is defined, always return it regardless of path - if h.customBanner != "" { - fmt.Fprint(w, strings.ReplaceAll(h.customBanner, "{DOMAIN}", domain)) - return - } - - // Handle other cases if no custom index is defined if stringsutil.HasPrefixI(req.URL.Path, "/s/") && h.staticHandler != nil { if h.options.DynamicResp && len(req.URL.Query()) > 0 { values := req.URL.Query() @@ -286,7 +278,11 @@ func (h *HTTPServer) defaultHandler(w http.ResponseWriter, req *http.Request) { } h.staticHandler.ServeHTTP(w, req) } else if req.URL.Path == "/" && reflection == "" { - fmt.Fprintf(w, banner, domain) + if h.customBanner != "" { + fmt.Fprint(w, strings.ReplaceAll(h.customBanner, "{DOMAIN}", domain)) + } else { + fmt.Fprintf(w, banner, domain) + } } else if strings.EqualFold(req.URL.Path, "/robots.txt") { fmt.Fprintf(w, "User-agent: *\nDisallow: / # %s", reflection) } else if stringsutil.HasSuffixI(req.URL.Path, ".json") { From baa3fa59a2569478128caf9d0370b06d847b5308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Do=C4=9Fan=20Can=20Bak=C4=B1r?= Date: Thu, 24 Apr 2025 13:53:15 +0300 Subject: [PATCH 3/6] remove reflection check --- pkg/server/http_server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/server/http_server.go b/pkg/server/http_server.go index 20310aeb..cc6e8eef 100644 --- a/pkg/server/http_server.go +++ b/pkg/server/http_server.go @@ -277,7 +277,7 @@ func (h *HTTPServer) defaultHandler(w http.ResponseWriter, req *http.Request) { } } h.staticHandler.ServeHTTP(w, req) - } else if req.URL.Path == "/" && reflection == "" { + } else if req.URL.Path == "/" { if h.customBanner != "" { fmt.Fprint(w, strings.ReplaceAll(h.customBanner, "{DOMAIN}", domain)) } else { From 4c4cab126e1ba3ed2786e495856b5e0360f7e9fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Do=C4=9Fan=20Can=20Bak=C4=B1r?= Date: Thu, 24 Apr 2025 14:09:35 +0300 Subject: [PATCH 4/6] fallback to custom banner if defined --- pkg/server/http_server.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/server/http_server.go b/pkg/server/http_server.go index cc6e8eef..64863c56 100644 --- a/pkg/server/http_server.go +++ b/pkg/server/http_server.go @@ -277,12 +277,6 @@ func (h *HTTPServer) defaultHandler(w http.ResponseWriter, req *http.Request) { } } h.staticHandler.ServeHTTP(w, req) - } else if req.URL.Path == "/" { - if h.customBanner != "" { - fmt.Fprint(w, strings.ReplaceAll(h.customBanner, "{DOMAIN}", domain)) - } else { - fmt.Fprintf(w, banner, domain) - } } else if strings.EqualFold(req.URL.Path, "/robots.txt") { fmt.Fprintf(w, "User-agent: *\nDisallow: / # %s", reflection) } else if stringsutil.HasSuffixI(req.URL.Path, ".json") { @@ -291,12 +285,18 @@ func (h *HTTPServer) defaultHandler(w http.ResponseWriter, req *http.Request) { } else if stringsutil.HasSuffixI(req.URL.Path, ".xml") { fmt.Fprintf(w, "%s", reflection) w.Header().Set("Content-Type", "application/xml") - } else { - if h.options.DynamicResp && (len(req.URL.Query()) > 0 || stringsutil.HasPrefixI(req.URL.Path, "/b64_body:")) { + } else if h.options.DynamicResp { + if len(req.URL.Query()) > 0 || stringsutil.HasPrefixI(req.URL.Path, "/b64_body:") { writeResponseFromDynamicRequest(w, req) return } fmt.Fprintf(w, "%s", reflection) + } else { + if h.customBanner != "" { + fmt.Fprint(w, strings.ReplaceAll(h.customBanner, "{DOMAIN}", domain)) + } else { + fmt.Fprintf(w, banner, domain) + } } } From 66dbf3b2f2b5abbdfa8bff9418e32e4f8ba32099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Do=C4=9Fan=20Can=20Bak=C4=B1r?= Date: Sat, 3 Jan 2026 15:06:22 +0300 Subject: [PATCH 5/6] introduce default http response --- cmd/interactsh-server/main.go | 1 + pkg/options/server_options.go | 2 ++ pkg/server/http_server.go | 46 ++++++++++++++++++++++++----------- pkg/server/server.go | 2 ++ 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/cmd/interactsh-server/main.go b/cmd/interactsh-server/main.go index a02e365d..fe0fb166 100644 --- a/cmd/interactsh-server/main.go +++ b/cmd/interactsh-server/main.go @@ -67,6 +67,7 @@ func main() { flagSet.StringVarP(&cliOptions.CustomRecords, "custom-records", "cr", "", "custom dns records YAML file for DNS server"), flagSet.StringVarP(&cliOptions.HTTPIndex, "http-index", "hi", "", "custom index file for http server"), flagSet.StringVarP(&cliOptions.HTTPDirectory, "http-directory", "hd", "", "directory with files to serve with http server"), + flagSet.StringVarP(&cliOptions.DefaultHTTPResponseFile, "default-http-response", "dhr", "", "file to serve for all http requests (takes priority over other options)"), flagSet.BoolVarP(&cliOptions.DiskStorage, "disk", "ds", false, "disk based storage"), flagSet.StringVarP(&cliOptions.DiskStoragePath, "disk-path", "dsp", "", "disk storage path"), flagSet.StringVarP(&cliOptions.HeaderServer, "server-header", "csh", "", "custom value of Server header in response"), diff --git a/pkg/options/server_options.go b/pkg/options/server_options.go index 3001427e..f91c80b6 100644 --- a/pkg/options/server_options.go +++ b/pkg/options/server_options.go @@ -54,6 +54,7 @@ type CLIServerOptions struct { DisableUpdateCheck bool NoVersionHeader bool HeaderServer string + DefaultHTTPResponseFile string } func (cliServerOptions *CLIServerOptions) AsServerOptions() *server.Options { @@ -93,5 +94,6 @@ func (cliServerOptions *CLIServerOptions) AsServerOptions() *server.Options { EnableMetrics: cliServerOptions.EnableMetrics, NoVersionHeader: cliServerOptions.NoVersionHeader, HeaderServer: cliServerOptions.HeaderServer, + DefaultHTTPResponseFile: cliServerOptions.DefaultHTTPResponseFile, } } diff --git a/pkg/server/http_server.go b/pkg/server/http_server.go index d217c13f..6066ebe9 100644 --- a/pkg/server/http_server.go +++ b/pkg/server/http_server.go @@ -25,11 +25,12 @@ import ( // HTTPServer is a http server instance that listens both // TLS and Non-TLS based servers. type HTTPServer struct { - options *Options - tlsserver http.Server - nontlsserver http.Server - customBanner string - staticHandler http.Handler + options *Options + tlsserver http.Server + nontlsserver http.Server + customBanner string + defaultResponse string + staticHandler http.Handler } type noopLogger struct { @@ -63,12 +64,22 @@ func NewHTTPServer(options *Options) (*HTTPServer, error) { // If custom index, read the custom index file and serve it. // Supports {DOMAIN} placeholders. if options.HTTPIndex != "" { - abs, _ := filepath.Abs(options.HTTPDirectory) + abs, _ := filepath.Abs(options.HTTPIndex) gologger.Info().Msgf("Using custom server index: %s", abs) if data, err := os.ReadFile(options.HTTPIndex); err == nil { server.customBanner = string(data) } } + // If default response file is specified, read it and serve for all requests. + // This takes priority over all other response options. + // Supports {DOMAIN} placeholders. + if options.DefaultHTTPResponseFile != "" { + abs, _ := filepath.Abs(options.DefaultHTTPResponseFile) + gologger.Info().Msgf("Using default HTTP response file for all requests: %s", abs) + if data, err := os.ReadFile(options.DefaultHTTPResponseFile); err == nil { + server.defaultResponse = string(data) + } + } router := &http.ServeMux{} router.Handle("/", server.logger(server.corsMiddleware(http.HandlerFunc(server.defaultHandler)))) router.Handle("/register", server.corsMiddleware(server.authMiddleware(http.HandlerFunc(server.registerHandler)))) @@ -255,6 +266,13 @@ func (h *HTTPServer) defaultHandler(w http.ResponseWriter, req *http.Request) { } reflection := h.options.URLReflection(req.Host) + + // If default response is set, serve it for all requests (highest priority) + if h.defaultResponse != "" { + _, _ = fmt.Fprint(w, strings.ReplaceAll(h.defaultResponse, "{DOMAIN}", domain)) + return + } + if stringsutil.HasPrefixI(req.URL.Path, "/s/") && h.staticHandler != nil { if h.options.DynamicResp && len(req.URL.Query()) > 0 { values := req.URL.Query() @@ -277,6 +295,12 @@ func (h *HTTPServer) defaultHandler(w http.ResponseWriter, req *http.Request) { } } h.staticHandler.ServeHTTP(w, req) + } else if req.URL.Path == "/" && reflection == "" { + if h.customBanner != "" { + _, _ = fmt.Fprint(w, strings.ReplaceAll(h.customBanner, "{DOMAIN}", domain)) + } else { + _, _ = fmt.Fprintf(w, banner, domain) + } } else if strings.EqualFold(req.URL.Path, "/robots.txt") { _, _ = fmt.Fprintf(w, "User-agent: *\nDisallow: / # %s", reflection) } else if stringsutil.HasSuffixI(req.URL.Path, ".json") { @@ -285,18 +309,12 @@ func (h *HTTPServer) defaultHandler(w http.ResponseWriter, req *http.Request) { } else if stringsutil.HasSuffixI(req.URL.Path, ".xml") { _, _ = fmt.Fprintf(w, "%s", reflection) w.Header().Set("Content-Type", "application/xml") - } else if h.options.DynamicResp { - if len(req.URL.Query()) > 0 || stringsutil.HasPrefixI(req.URL.Path, "/b64_body:") { + } else { + if h.options.DynamicResp && (len(req.URL.Query()) > 0 || stringsutil.HasPrefixI(req.URL.Path, "/b64_body:")) { writeResponseFromDynamicRequest(w, req) return } _, _ = fmt.Fprintf(w, "%s", reflection) - } else { - if h.customBanner != "" { - _, _ = fmt.Fprint(w, strings.ReplaceAll(h.customBanner, "{DOMAIN}", domain)) - } else { - _, _ = fmt.Fprintf(w, banner, domain) - } } } diff --git a/pkg/server/server.go b/pkg/server/server.go index b067ec26..cb96be74 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -107,6 +107,8 @@ type Options struct { NoVersionHeader bool // HeaderServer use custom string in HTTP response Server header instead of domain HeaderServer string + // DefaultHTTPResponseFile is a file to serve for all HTTP requests (takes priority over other options) + DefaultHTTPResponseFile string ACMEStore *acme.Provider Stats *Metrics From 5db81d76a9da5e48e4bb43f4cf3b173677c8fe01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Do=C4=9Fan=20Can=20Bak=C4=B1r?= Date: Mon, 5 Jan 2026 15:09:56 +0300 Subject: [PATCH 6/6] update doc --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6151501f..1c2b252b 100644 --- a/README.md +++ b/README.md @@ -362,6 +362,7 @@ CONFIG: -dr, -dynamic-resp enable setting up arbitrary response data -cr, -custom-records string custom dns records YAML file for DNS server -hi, -http-index string custom index file for http server + -dhr, -default-http-response string file to serve for all http requests (takes priority over other options) -hd, -http-directory string directory with files to serve with http server -ds, -disk disk based storage -dsp, -disk-path string disk storage path