srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/internal/ipc
diff options
context:
space:
mode:
Diffstat (limited to 'internal/ipc')
-rw-r--r--internal/ipc/ipc.go46
-rw-r--r--internal/ipc/server.go30
2 files changed, 61 insertions, 15 deletions
diff --git a/internal/ipc/ipc.go b/internal/ipc/ipc.go
index 7d81473..270b8e4 100644
--- a/internal/ipc/ipc.go
+++ b/internal/ipc/ipc.go
@@ -38,6 +38,16 @@ type Request struct {
// turn (Sniper-style - see proxy.Intrude).
Payloads []string `json:"payloads,omitempty"`
+ // For "intrude": optional Go regexps evaluated against each result's
+ // response bytes. GrepMatch flags whether it matched at all;
+ // GrepExtract additionally captures text (first submatch if the
+ // pattern has a capturing group, else the whole match) into the
+ // result. Either or both may be empty to skip that check. Compiled
+ // and validated once, server-side, before the attack starts - a bad
+ // pattern fails the same way a bad marker or empty payload set does.
+ GrepMatch string `json:"grep_match,omitempty"`
+ GrepExtract string `json:"grep_extract,omitempty"`
+
// For "rules_save": add (Rule.ID == 0) or update (Rule.ID != 0) a
// match-and-replace rule. For "rules_delete"/"rules_toggle": RuleID
// (and RuleEnabled for toggle) identify the target.
@@ -72,13 +82,15 @@ type StatusMsg struct {
// IntrudeResultMsg is one completed Intruder attack request.
type IntrudeResultMsg struct {
- Position int `json:"position"`
- Payload string `json:"payload"`
- EntryID int64 `json:"entry_id"`
- StatusCode int `json:"status_code"`
- RespSize int `json:"resp_size"`
- Duration time.Duration `json:"duration"`
- Error string `json:"error,omitempty"`
+ Position int `json:"position"`
+ Payload string `json:"payload"`
+ EntryID int64 `json:"entry_id"`
+ StatusCode int `json:"status_code"`
+ RespSize int `json:"resp_size"`
+ Duration time.Duration `json:"duration"`
+ Error string `json:"error,omitempty"`
+ GrepMatch bool `json:"grep_match,omitempty"`
+ GrepExtract string `json:"grep_extract,omitempty"`
}
// EntryDetail is a full history entry, raw bytes included.
@@ -307,18 +319,22 @@ func Subscribe(path string) (<-chan store.Summary, func() error, error) {
// Intrude starts a Sniper attack (see proxy.Intrude): template must
// contain at least one §marked§ position, fuzzed in turn through
-// payloads. Unlike Subscribe's live feed, no result is ever dropped for
-// a slow consumer - each one is the attack's actual data, not a
-// notification with the real thing recoverable elsewhere. A setup error
-// (bad markers, empty payload set, too many requests) is returned
-// directly rather than through the channel. The returned channel closes
-// when the attack finishes or the connection is closed early.
-func Intrude(path, scheme, host string, template []byte, payloads []string) (<-chan IntrudeResultMsg, func() error, error) {
+// payloads. grepMatch/grepExtract are optional Go regexps evaluated
+// server-side against each result's response bytes (empty string
+// disables either check) - see IntrudeResultMsg. Unlike Subscribe's live
+// feed, no result is ever dropped for a slow consumer - each one is the
+// attack's actual data, not a notification with the real thing
+// recoverable elsewhere. A setup error (bad markers, empty payload set,
+// too many requests, an unparseable grep regexp) is returned directly
+// rather than through the channel. The returned channel closes when the
+// attack finishes or the connection is closed early.
+func Intrude(path, scheme, host string, template []byte, payloads []string, grepMatch, grepExtract string) (<-chan IntrudeResultMsg, func() error, error) {
conn, err := net.Dial("unix", path)
if err != nil {
return nil, nil, fmt.Errorf("dial %s: %w", path, err)
}
- if err := json.NewEncoder(conn).Encode(Request{Type: "intrude", Scheme: scheme, Host: host, Raw: template, Payloads: payloads}); err != nil {
+ req := Request{Type: "intrude", Scheme: scheme, Host: host, Raw: template, Payloads: payloads, GrepMatch: grepMatch, GrepExtract: grepExtract}
+ if err := json.NewEncoder(conn).Encode(req); err != nil {
conn.Close()
return nil, nil, err
}
diff --git a/internal/ipc/server.go b/internal/ipc/server.go
index 28279a7..9602912 100644
--- a/internal/ipc/server.go
+++ b/internal/ipc/server.go
@@ -6,6 +6,7 @@ import (
"io"
"log"
"net"
+ "regexp"
"sync"
"mitmux/internal/rules"
@@ -155,6 +156,23 @@ func (s *Server) handleConn(conn net.Conn) {
enc.Encode(Response{Type: "error", Error: "intruder not available"})
continue
}
+ var grepMatchRe, grepExtractRe *regexp.Regexp
+ if req.GrepMatch != "" {
+ re, err := regexp.Compile(req.GrepMatch)
+ if err != nil {
+ enc.Encode(Response{Type: "error", Error: "grep-match: " + err.Error()})
+ continue
+ }
+ grepMatchRe = re
+ }
+ if req.GrepExtract != "" {
+ re, err := regexp.Compile(req.GrepExtract)
+ if err != nil {
+ enc.Encode(Response{Type: "error", Error: "grep-extract: " + err.Error()})
+ continue
+ }
+ grepExtractRe = re
+ }
err := s.intruder.Intrude(context.Background(), req.Scheme, req.Host, req.Raw, req.Payloads,
func(position int, payload string, entry *store.Entry, sendErr error) bool {
r := IntrudeResultMsg{Position: position, Payload: payload}
@@ -169,6 +187,18 @@ func (s *Server) handleConn(conn net.Conn) {
if entry.Error != "" && r.Error == "" {
r.Error = entry.Error
}
+ if grepMatchRe != nil {
+ r.GrepMatch = grepMatchRe.Match(entry.ResponseRaw)
+ }
+ if grepExtractRe != nil {
+ if m := grepExtractRe.FindSubmatch(entry.ResponseRaw); m != nil {
+ if len(m) > 1 {
+ r.GrepExtract = string(m[1])
+ } else {
+ r.GrepExtract = string(m[0])
+ }
+ }
+ }
}
return enc.Encode(Response{Type: "intrude_result", IntrudeResult: &r}) == nil
})