-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
214 lines (189 loc) · 10.9 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
// Package main is a package for fuzzing a network protocol over the wire.
// It generates random data and sends this fuzzed information over the wire
// to a target system (SUT). You can write your own functions for fuzzing.
// The main purpose of the program is to fuzz Shim Layer protocols,
// especially the PLUS protocol (Path Layer UDP Substrate).
//
// Author: Marcel Sueess, sueesmar@students.zhaw.ch
// Date: 4th April, 2018
package main
import (
"flag"
"io"
"log"
"net"
"os"
"os/signal"
"time"
)
var (
// Error serves as logger for error messages.
Error *log.Logger
// Info serves as logger for info messages.
Info *log.Logger
// Log serves as logger for fuzzing campaign to save infos to the file.
Log *log.Logger
// device (network interface) for sending the packets.
device = flag.String("dev", "", "Network interface, for sending and receiving packets.")
// targetIP sets the destination of the fuzzed packets.
destIP = flag.String("target-ip", "", "IP address of the target of the fuzzing process.")
// srcIP sets the source IP from the sending endpoint. It is used to poison the ARP cache for man-in-the-middle mode.
srcIP = flag.String("source-ip", "", "IP address of the original source of the fuzzing process to poison its ARP table. Activates man-in-the-middle mode.")
// fuzzerSeed sets the seed for deterministic fuzzing.
fuzzerSeed = flag.Int64("fuzzer-seed", 0, "Seed for random generator of fuzzer. Used by gofuzz to make fuzzing deterministic.")
// localPort sets the local source port of the packets.
localPort = flag.Uint("local-port", 10000, "Local port of the transport layer for the local system, eg. UDP or TCP Port.")
// targetPort sets the local source port of the packets.
targetPort = flag.Uint("target-port", 9999, "Target port of the transport layer for the remote system (SUT), eg. UDP or TCP Port.")
// fuzzPayload defines if the payload sould be fuzzed or if it should be fixed value.
fuzzPayload = flag.Bool("fuzz-payload", false, "Boolean which determines, if payload should be fuzzed.")
// fuzzPacketStructure defines if the fields of packet structure sould be fuzzed or if it should be fixed value.
// This can be used to generally disable fuzzing, even the fields are set to true.
fuzzFields = flag.Bool("fuzz-fields", true, "Boolean which determines, if packet fields should be fuzzed. Can be used to globally enable or disable fuzzing.")
// minPayloadLen defines the minimal length in bytes for the payload.
minPayloadLen = flag.Int("min-payload-len", 6, "Minimum length in bytes for payload.")
// maxPayloadLen defines the maximal length in bytes for the payload.
maxPayloadLen = flag.Int("max-payload-len", 1000, "Maximum length in bytes for payload.")
// numPacketsToFuzz defines the amount of packets to send to the target.
numPacketsToFuzz = flag.Int64("num-packets", 4294967296, "Number of packets to send to target during fuzzing.")
// packetSendInterval defines the interval between to packets in milliseconds.
packetSendInterval = flag.Int("packet-send-interval", 200, "Sending interval of packets in milliseconds.")
// pcapPathSend defines the path to a pcap file to capture the sent packet. If no path is specified, the file name is built with the actual date and time.
pcapPathSend = flag.String("pcap-path-send", "/var/tmp/fuzz-send-"+time.Now().Format("20060102150405")+".pcap", "Path to output pcap file, so save the sent packets.")
// pcapFileSize defines the maximum size for a pcap file in kilo bytes. After this size, create a new file.
pcapFileSize = flag.Int("pcap-file-size", 100, "Create new pcap file after ... kilo bytes.")
// savePackets defines, if sent fuzzed packets should be saved to pcap file.
savePackets = flag.Bool("save-sent-packets", false, "Boolean which determines if fuzzed packets should be saved to pcap file.")
// maxStorageCapacity defines the maximum size of storage for saved pcap files
maxStorageCapacity = flag.Int("max-storage", 0, "Maximum storage for pcap files in kilo bytes. 0 means unlimited.")
// logfilePath defines the path to save the logfile to.
logfilePath = flag.String("logfile-path", "/var/tmp/fuzz-log-"+time.Now().Format("20060102150405")+".txt", "Path to save the log file of fuzzing.")
// sshCommand defines the command which is executed on the target machine.
sshCommand = flag.String("ssh-command", "",
`Command to start the SUT on the target.
To let the process run in background, even the ssh session is closed,
use tmux to create a named terminal multiplexer session in background.
Install tmux with 'sudo apt-get install tmux'.
You can execute multiple commands separated by ';'.
Use '' to enclose commands containing whitespaces.
The following command is an example to kill an existing process and start a new one:
pkill server; tmux new -d -s FuzzSession '~/go/src/github.com/FMNSSun/plus-debug/server/server 192.168.181.133:9999'`)
// sshCheckProcess defines the name of the process to check for the PID.
sshCheckProcess = flag.String("ssh-process-name", "", "Name of the process to check the status for with pgrep <ssh-process-name>.")
// sshUsername defines the username for accessing the target machine.
sshUsername = flag.String("ssh-user", "", "Username to acess the target machine by ssh.")
// sshPassword defines the password for accessing the target machine.
sshPassword = flag.String("ssh-password", "", "Password to access the target machine by ssh.")
// pcapPathReplay defines the path to a packet capture for replay.
pcapPathReplay = flag.String("pcap-path-replay", "", "Path to packet capture for replay.")
// replayFollowing defines if the following pcap files should be played as well.
replayFollowing = flag.Bool("replay-following", false,
`Boolean which defines, if the following pcap files should be replayed as well.
For this, the filenames of the pcap files have to end with <-nr> (e.g. -0)
and count up with an incrementing number.`)
// pcapPathFuzz defines the path to an existing pcap packet capture. It takes it and fuzz it.
pcapPathFuzz = flag.String("pcap-path-fuzz", "", "Path to existing packet capture for fuzzing it.")
// verbose sets the log level to maximum and prints more informatin to the StdOut.
verbose = flag.Bool("verbose", false, "Writes more information to the StdOut if set to true.")
)
func main() {
// Parse an check flags
flag.Parse()
// Initialize the Logger
logfile, err := os.OpenFile(*logfilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
panic("Failed to open log file: " + err.Error())
}
InitLog(os.Stderr, os.Stdout, logfile)
// Input validation
if *device == "" {
Error.Fatal("No device for sending and receiving packets specified. Please provide the name of the interface with the option -dev.")
}
if *localPort <= 0 || *localPort > 65535 {
Error.Fatal("No valid local port provided. Please provide a local port between 1 and 65535 with the option -local-port.")
}
if *targetPort <= 0 || *targetPort > 65535 {
Error.Fatal("No valid target port provided. Please provide a target port between 1 and 65535 with the option -target-port.")
}
if *pcapPathReplay != "" && *pcapPathFuzz != "" {
Error.Fatal("You can't set both paths for replay existing file and fuzz existing packet capture at the same time.")
}
if *savePackets {
if *pcapPathSend == "" {
Error.Fatal("The path to save the sent packets is invalid. Please provide a valid path with the option -pcap-path-send.")
}
if *pcapFileSize < 10 {
Error.Fatal("The size of the pcap file is too small. Please provide a size bigger than 10 with the option -pcap-file-size.")
}
}
if *logfilePath == "" {
Error.Fatal("You haven't provided a path to save the log file. Please provide a valid path with the option -logfile-path.")
}
if (*sshUsername == "" || *sshPassword == "") && (*sshCommand != "") {
Error.Fatal("You haven't provided a SSH username or password. Please use the options -ssh-user and -ssh-password.")
}
if *sshCommand == "" {
Error.Print("You haven't provided a SSH command to start the SUT on the target system. Please provide it with the option -ssh-command or start it manually.")
}
if *packetSendInterval < 10 {
Error.Fatal("Packet send interval too small. Please provide an interval bigger than 10 ms with the option -packet-send-interval")
}
if *fuzzPayload {
if *minPayloadLen > *maxPayloadLen {
Error.Fatal("The -min-payload-len can't be bigger than the -max-payload-len option. Please correct it.")
}
if *minPayloadLen < 0 || *minPayloadLen > 1350 {
Error.Fatal("Value for -min-payload-len invalid. Please provide a value between 0 and 1350.")
}
if *maxPayloadLen < 0 || *maxPayloadLen > 1350 {
Error.Fatal("Value for -max-payload-len invalid. Please provide a value between 0 and 1350.")
}
}
if *maxStorageCapacity < 0 {
Error.Fatal("Value for -max-storage-capacity invalid. Please provide a value bigger or equal to 0. 0 means unlimited and is default.")
}
if checkDestIP := net.ParseIP(*destIP); checkDestIP == nil {
Error.Fatal("Invalid destination IP address provided. Please provide a valid IP address with the option -target-ip.")
}
if *srcIP != "" {
if checkSrcIP := net.ParseIP(*srcIP); checkSrcIP == nil {
Error.Fatal("Invalid sender IP address provided. Please provide a valid IP address to ARP poison with the option -source-ip.")
}
}
// Listen for ctrl+c to write entry to log file.
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, os.Interrupt)
go func() {
for _ = range signalChan {
Info.Println("Process aborted by user at", time.Now().Format("02.01.2006 15:04:05"))
Log.Println("Process aborted by user at", time.Now().Format("02.01.2006 15:04:05"))
os.Exit(1)
}
}()
// fuzzingRoundDone is a channel to wait for ending of fuzzing process.
fuzzingRoundDone := make(chan bool)
// Execute the code for fuzzing new packets or replay a given pcap file.
switch {
case *pcapPathReplay == "" && *pcapPathFuzz == "" && *srcIP == "":
// Replay path not set, so start new packet fuzzing
go SendPackets(fuzzingRoundDone)
case *pcapPathReplay != "":
// Replay path set, so replay it
go ReplayPackets(fuzzingRoundDone)
case *pcapPathFuzz != "":
// Path to pcap for fuzzing set, so replay AND fuzz it.
go FuzzCapturedPackets(fuzzingRoundDone)
case *srcIP != "":
// srcIP set, this is the mode for man-in-the-middle fuzzing
go ARPPoison()
go FuzzMITM(fuzzingRoundDone)
}
<-fuzzingRoundDone
}
// InitLog initializes the loggers.
// To extend it for logging to file, see https://www.ardanlabs.com/blog/2013/11/using-log-package-in-go.html
func InitLog(errorHandle io.Writer, infoHandle io.Writer, logHandle io.Writer) {
Error = log.New(errorHandle, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
Info = log.New(errorHandle, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile)
Log = log.New(logHandle, "LOG:", log.Ldate|log.Ltime)
}