srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/mitmux/main.go106
1 files changed, 90 insertions, 16 deletions
diff --git a/cmd/mitmux/main.go b/cmd/mitmux/main.go
index d11d18b..304d3fc 100644
--- a/cmd/mitmux/main.go
+++ b/cmd/mitmux/main.go
@@ -20,6 +20,7 @@ import (
"mitmux/internal/ca"
"mitmux/internal/ipc"
+ "mitmux/internal/proxy"
"mitmux/internal/rules"
"mitmux/internal/scope"
"mitmux/internal/store"
@@ -199,6 +200,7 @@ type model struct {
intruderResults table.Model
intruderRows []ipc.IntrudeResultMsg
intruderFocus intruderFocus
+ intruderMode proxy.AttackMode
intruderRunning bool
intruderCount int
intruderCh <-chan ipc.IntrudeResultMsg
@@ -331,12 +333,12 @@ func newModel(client *ipc.Client, subCh <-chan store.Summary, socketPath string)
itmpl.ta.ShowLineNumbers = false
ipayloads := newViTextarea()
- ipayloads.ta.Placeholder = "payloads, one per line"
+ ipayloads.ta.Placeholder = "payloads, one per line (pitchfork/cluster bomb: separate one set per position with a line of ---)"
ipayloads.ta.ShowLineNumbers = false
iresultsCols := []table.Column{
- {Title: "Pos", Width: 4},
- {Title: "Payload", Width: 20},
+ {Title: "#", Width: 4},
+ {Title: "Payload(s)", Width: 24},
{Title: "Status", Width: 6},
{Title: "Size", Width: 8},
{Title: "Time", Width: 8},
@@ -708,6 +710,7 @@ func (m *model) enterIntruder(d *ipc.EntryDetail) {
m.intruderRows = nil
setTableRows(&m.intruderResults, nil)
m.intruderFocus = focusTemplate
+ m.intruderMode = proxy.Sniper
m.intruderRunning = false
m.intruderCount = 0
m.payloadCase = payloadCaseNone
@@ -718,7 +721,7 @@ func (m *model) enterIntruder(d *ipc.EntryDetail) {
m.grepMatchInput.SetValue("")
m.grepExtractInput.SetValue("")
m.mode = viewIntruder
- m.statusMsg = "wrap positions to fuzz in § (ctrl+g), fill payloads, ctrl+r to start"
+ m.statusMsg = "wrap positions to fuzz in § (ctrl+g), fill payloads, a to change attack mode, ctrl+r to start"
}
type intrudeStartedMsg struct {
@@ -732,20 +735,58 @@ type intrudeResultMsg struct {
ok bool
}
+// parsePayloadSets turns the Payloads textarea into one or more payload
+// sets. Sniper and BatteringRam only ever need one shared set, so every
+// non-empty line is a payload. Pitchfork and ClusterBomb are inherently
+// per-position, so the same textarea instead holds several sets separated
+// by a line containing exactly "---", in position order - proxy.Intrude
+// validates the count matches the template's marked positions.
+func parsePayloadSets(text string, mode proxy.AttackMode, caseRule payloadCaseRule, encodeRule payloadEncodeRule) [][]string {
+ lines := strings.Split(text, "\n")
+ if mode != proxy.Pitchfork && mode != proxy.ClusterBomb {
+ var set []string
+ for _, line := range lines {
+ if line != "" {
+ set = append(set, applyPayloadRules(line, caseRule, encodeRule))
+ }
+ }
+ if set == nil {
+ return nil
+ }
+ return [][]string{set}
+ }
+
+ var sets [][]string
+ var cur []string
+ flush := func() {
+ if cur != nil {
+ sets = append(sets, cur)
+ cur = nil
+ }
+ }
+ for _, line := range lines {
+ if strings.TrimSpace(line) == "---" {
+ flush()
+ continue
+ }
+ if line != "" {
+ cur = append(cur, applyPayloadRules(line, caseRule, encodeRule))
+ }
+ }
+ flush()
+ return sets
+}
+
func (m *model) startIntrude() tea.Cmd {
scheme, host := m.intruderScheme, m.intruderHost
// Same CRLF restoration as Repeater, same trade-off - see sendRepeat.
template := []byte(strings.ReplaceAll(m.intruderTemplate.Value(), "\n", "\r\n"))
- var payloads []string
- for _, line := range strings.Split(m.intruderPayloads.Value(), "\n") {
- if line != "" {
- payloads = append(payloads, applyPayloadRules(line, m.payloadCase, m.payloadEncode))
- }
- }
+ mode := m.intruderMode
+ payloadSets := parsePayloadSets(m.intruderPayloads.Value(), mode, m.payloadCase, m.payloadEncode)
path := m.socketPath
grepMatch, grepExtract := m.grepMatchSrc, m.grepExtractSrc
return func() tea.Msg {
- ch, closeFn, err := ipc.Intrude(path, scheme, host, template, payloads, grepMatch, grepExtract)
+ ch, closeFn, err := ipc.Intrude(path, scheme, host, template, mode, payloadSets, grepMatch, grepExtract)
return intrudeStartedMsg{ch: ch, close: closeFn, err: err}
}
}
@@ -1752,6 +1793,11 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.intruderTemplate.InsertRune('§')
}
return m, nil
+ case "a":
+ if !editing && !m.intruderRunning {
+ m.intruderMode = nextAttackMode(m.intruderMode)
+ return m, nil
+ }
case "c":
if !editing {
m.payloadCase = (m.payloadCase + 1) % payloadCaseRule(len(payloadCaseNames))
@@ -2339,6 +2385,34 @@ func scopeRowsFor(rs []scope.Rule) []table.Row {
return rows
}
+// nextAttackMode cycles Sniper -> BatteringRam -> Pitchfork -> ClusterBomb
+// -> Sniper.
+func nextAttackMode(mode proxy.AttackMode) proxy.AttackMode {
+ switch mode {
+ case proxy.Sniper:
+ return proxy.BatteringRam
+ case proxy.BatteringRam:
+ return proxy.Pitchfork
+ case proxy.Pitchfork:
+ return proxy.ClusterBomb
+ default:
+ return proxy.Sniper
+ }
+}
+
+func attackModeLabel(mode proxy.AttackMode) string {
+ switch mode {
+ case proxy.BatteringRam:
+ return "battering ram"
+ case proxy.Pitchfork:
+ return "pitchfork"
+ case proxy.ClusterBomb:
+ return "cluster bomb"
+ default:
+ return "sniper"
+ }
+}
+
func (m *model) intruderView() string {
var b strings.Builder
title := fmt.Sprintf(" intruder - %s://%s ", sanitizeLine(m.intruderScheme), sanitizeLine(m.intruderHost))
@@ -2382,8 +2456,8 @@ func (m *model) intruderView() string {
if grepExtract == "" {
grepExtract = "(none)"
}
- b.WriteString(fmt.Sprintf("payload rules: case=%s encode=%s · grep-match: %s · grep-extract: %s",
- payloadCaseNames[m.payloadCase], payloadEncodeNames[m.payloadEncode], grepMatch, grepExtract))
+ b.WriteString(fmt.Sprintf("attack: %s · payload rules: case=%s encode=%s · grep-match: %s · grep-extract: %s",
+ attackModeLabel(m.intruderMode), payloadCaseNames[m.payloadCase], payloadEncodeNames[m.payloadEncode], grepMatch, grepExtract))
b.WriteString("\n")
}
@@ -2404,7 +2478,7 @@ func (m *model) intruderView() string {
if m.grepEditing != 0 {
b.WriteString(helpStyle.Render("enter confirm · esc cancel · ctrl+c quit"))
} else {
- b.WriteString(helpStyle.Render("i to edit (vi keys) · tab switch pane · ctrl+g insert § · c/e cycle case/encode · m/v edit grep-match/extract · ctrl+r start · enter (results) view · esc back/stop · ? help · ctrl+c quit"))
+ b.WriteString(helpStyle.Render("i to edit (vi keys) · tab switch pane · ctrl+g insert § · a cycle attack mode · c/e cycle case/encode · m/v edit grep-match/extract · ctrl+r start · enter (results) view · esc back/stop · ? help · ctrl+c quit"))
}
return b.String()
}
@@ -2421,8 +2495,8 @@ func intrudeRowsFor(rs []ipc.IntrudeResultMsg) []table.Row {
match = "✓"
}
rows[i] = table.Row{
- fmt.Sprintf("%d", r.Position),
- sanitizeLine(r.Payload),
+ fmt.Sprintf("%d", r.Iteration),
+ sanitizeLine(strings.Join(r.Values, " | ")),
status,
humanBytes(r.RespSize),
r.Duration.Round(time.Millisecond).String(),