diff options
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/ipc/ipc.go | 46 | ||||
| -rw-r--r-- | internal/ipc/server.go | 30 |
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 }) |