Monitors
Monitors are components that monitor the target for specific behaviour. A monitor can be passive and just observe and provide data or behave more actively, interacting directly with the target. Some monitors also have the capability to start, stop and restart targets.
Detecting a crash or misbehaviour of your target can be a complex, non-straight forward process depending on the tools you have available on your targets host; this holds true especially for embedded devices. Fuzzungus provides three main monitor implementations:
ProcessMonitor, a Monitor that collects debug info from process on Windows and Unix. It also can restart the target process and detect segfaults.NetworkMonitor, a Monitor that passively captures network traffic via PCAP and attaches it to the testcase log.CallbackMonitor, which is used to implement the callbacks that can be supplied to the Session class.BusyboxMonitor, a Monitor that collects debug info from process on Busybox.
Monitor Interface (BaseMonitor)
- class boofuzz.monitors.BaseMonitor(*args, **kwargs)[source]
Bases:
objectInterface for Target monitors. All Monitors must adhere to this specification.
Added in version 0.2.0.
- alive(*args, **kwargs)[source]
Called when a Target containing this Monitor is added to a session. Use this function to connect to e.g. RPC hosts if your target lives on another machine.
You MUST return True if the monitor is alive. You MUST return False otherwise. If a Monitor is not alive, this method will be called until it becomes alive or throws an exception. You SHOULD handle timeouts / connection retry limits in the monitor implementation.
Defaults to return True.
- Returns:
Bool
- get_crash_synopsis()[source]
Called if any monitor indicates that the current testcase has failed, even if this monitor did not detect a crash. You SHOULD return a human- readable representation of the crash synopsis (e.g. hexdump). You MAY save the full crashdump somewhere.
- Returns:
str
- post_send(target=None, fuzz_data_logger=None, session=None)[source]
Called after the current fuzz node is transmitted. Use it to collect data about a target and decide whether it crashed.
You MUST return True if the Target is still alive. You MUST return False if the Target crashed. If one Monitor reports a crash, the whole testcase will be marked as crashing.
Defaults to return True.
- Returns:
Bool
- post_start_target(target=None, fuzz_data_logger=None, session=None)[source]
Called after a target is started or restarted.
- pre_send(target=None, fuzz_data_logger=None, session=None)[source]
Called before the current fuzz node is transmitted.
Defaults to no effect.
- Returns:
None
- restart_target(target=None, fuzz_data_logger=None, session=None)[source]
Restart a target. Must return True if restart was successful, False if it was unsuccessful or this monitor cannot restart a Target, which causes the next monitor in the chain to try to restart.
The first successful monitor causes the restart chain to stop applying.
Defaults to call stop and start, return True if successful.
- Returns:
Bool
- retrieve_data()[source]
Called to retrieve data independent of whether the current fuzz node crashed the target or not. Called before the fuzzer proceeds to a new testcase.
You SHOULD return any auxiliary data that should be recorded. The data MUST be serializable, e.g. bytestring.
Defaults to return None.
- set_options(*args, **kwargs)[source]
Called to set options for your monitor (e.g. local crash dump storage). *args and **kwargs can be explicitly specified by implementing classes, however you SHOULD ignore any kwargs you do not recognize.
Defaults to no effect.
- Returns:
None
BusyboxMonitor
- class boofuzz.monitors.BusyboxMonitor(target_ip: str, target_port: int, processes_to_monitor: list[str], procmon_path: str, cpu_percentage_threshold: int = 80, mem_percentage_threshold: int = 80, target_user: str = '', target_password: str = '', ssh_command_timeout: int = 2)[source]
Bases:
BaseMonitorThis Class aims to implement the python side of an external monitor written in bash, designed for a simple machine like a busybox. It is able to communicate in SSH.
The data sent by the procmon is a JSON, with the following structure :
{ "cpu_count": 8, "total_mem": 16054272, "total_swap": 999420, "kernel_version": "6.1.0-21-amd64", "system_cpu": "0.581154", "system_uptime": "9157.06", "system_mem_total": 16054272, "system_mem_used": 4894352, "system_mem_free": 6958548, "system_mem_available": 9385460, "system_mem_shared": 1431144, "system_mem_buff_cache": 4201372, "system_swap_total": 999420, "system_swap_used": 0, "system_swap_free": 999420, "firefox": { "2993": { "process_cpu": "5.22458112198275910876", "process_uptime": "8831.33000000000000000000", "process_mem": 1314008, "process_command_line": "/usr/lib/firefox-esr/firefox-esr", "process_ram": 590748, "process_vmem": 4722748 } } }
With a section for each process to monitor, and a subsection for each PID of the process.
If you want to test manually your SSH connection to debug something, the command should look something like this :
sshpass -p $password ssh $username@$ip "<procmon_path> --ssh -a -n '['$process_name']' -r 1"
Or any other options according to procmon.sh -h :
Usage: ./monitors/procmon.sh [option...] Data to monitor =============== -c, --cpu Display CPU information -s, --sys Display system information -m, --mem Display memory information -u, --up Display system uptime -a, --all Display all information. PID or Name required Execution options to monitor ============================ -h, --help Display help -n, --names <name> Find processes by name -t, --timeout <timeout> Timeout between rounds -r, --rounds Number of rounds to execute. Infinite by default. -v, --verbose Verbose -d, --debug Debug mode, wait for user input between rounds Network options =============== -i, --ip <ip> IP address to send the data to. Localhost by default -p, --port <port> Port to send the data to. 65431 by default --udp Send data via UDP --ssh Send data via SSH /!\ Warning : UDP communication is not reliable. Use SSH for a more reliable communication
As you can read above, the UDP communication was the initial communication method, but it is not reliable. You have to parse multiple JSON at once in your buffer, you don’t have a lot of feedback, and you can’t communication with the script, even just to close it.
For now the communication is done via SSH, which is more reliable, but slower. We open a SSH connection, send the command, read the output, and close the connection. That way we are sure to have the entire output, and we can communicate with the script to give new commands on another round if necessary. And it closes itself after sending its data.
Also note that the errors at connection (timeout, rights…) are printed to the console, so don’t hesitate to execute by hand the SSH command to debug the connection.
Room for improvement : The busybox monitor currently doesn’t uses all the information given by the procmon script.
For crash detection, it uses :
The PIDs of the processes to detect if a PID changed
process_uptime to get the uptime of the processes and detect crashes
For abnormal behaviour detection, it uses :
system_cpu and cpu_core_count to get the CPU usage of the system
system_mem_used and system_mem_total to get the memory usage of the system
process_cpu and process_mem to get the CPU and memory usage of the processes
It could use :
system_mem_shared, system_mem_buff_cache, system_swap_total, system_swap_used, system_swap_free to get more information about the memory usage of the system
process_command_line, process_ram, process_vmem to get more information about the processes
- Parameters:
target_ip (str) – IP of the target
target_port (int) – Port of the target
target_user (str) – User of the target to connect to SSH
target_password (str) – Password of the target. For SSH connections only. If passed, sshpass is used if present. Otherwise, only ssh is used, allowing the use of ssh keys if they are configured.
processes_to_monitor (list[str]) – List of processes to monitor
procmon_path (str) – Path to the procmon script on the target
- alive(fuzz_data_logger: FuzzLogger = None) bool[source]
Check if the target is still alive.
- error_array = ['error', 'fail', 'permission denied', 'No route to host', 'Connection refused', 'invalid', 'not found', 'not exist', 'no such', 'denied', 'failed', 'unable', 'timeout', 'unreachable', 'unavailable', 'unauthorized', 'not allowed', 'not permitted', 'not supported', 'rejected', 'not valid', 'not correct', 'not working', 'not running', 'not started', 'not stopped', 'not connected', 'not found', 'not available', 'not reachable']
- get_crash_synopsis() str[source]
Get a synopsis of the crash.
Read self.last_crash_synopsis to get the crash synopsis.
returns: str
- post_send(target=None, fuzz_data_logger: FuzzLogger = None, session=None) bool[source]
Called after the current fuzz node is transmitted. Use it to collect data about a target and decide whether it crashed.
If UDP, calls alive() to check if the target is still alive. If SSH, restarts the procmon.
returns: Bool. True if the target is still alive, False if it crashed.
- post_start_target(target=None, fuzz_data_logger: FuzzLogger = None, session=None)[source]
Called after a target is started or restarted.
- restart_target(target=None, fuzz_data_logger=None, session=None)[source]
Restarts the target. Tries to stop it, then start it again.
- start_target(fuzz_data_logger: FuzzLogger = None, *args, **kwargs) bool[source]
Starts the procmon script : - If UDP, sends the command via SSH and creates a socket to receive the data that is sent continuously - If SSH, sends the command via SSH, reads the output in STDOUT, and closes the connection to assure that the entire output is read
returns: Bool. True if the target started successfully, False if there was an error.
Important
The Target side of this monitor is available in the monitors directory at the root of the project, or just below :
1#!/bin/bash
2
3# UTILS
4## Function to display help
5function display_help() {
6 echo "Usage: $0 [option...]
7
8 Data to monitor
9 ===============
10 -c, --cpu Display CPU information
11 -s, --sys Display system information
12 -m, --mem Display memory information
13 -u, --up Display system uptime
14 -a, --all Display all information. PID or Name required
15
16 Execution options to monitor
17 ============================
18 -h, --help Display help
19 -n, --names <name> Find processes by name
20 -t, --timeout <timeout> Timeout between rounds
21 -r, --rounds Number of rounds to execute. Infinite by default.
22 -v, --verbose Verbose
23 -d, --debug Debug mode, wait for user input between rounds
24
25 Network options
26 ===============
27 -i, --ip <ip> IP address to send the data to. Localhost by default
28 -p, --port <port> Port to send the data to. 65431 by default
29 --udp Send data via UDP
30 --ssh Send data via SSH
31
32 /!\ Warning : UDP communication is not reliable. Use SSH for a more reliable communication."
33}
34
35## Log function to print to cli according to verbose mode
36log() {
37 local level="$1"
38 local data="$2"
39 if [[ $level -le $verbose ]]; then
40 echo "$data"
41 fi
42}
43
44## Escaping json characters
45json_escape() {
46 local input_string escaped_string
47
48 input_string="$1"
49
50 # Escape characters according to the specified rules
51 escaped_string=$(echo "$input_string" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/\t/\\t/g' -e 's/\n/\\n/g' -e 's/\r/\\r/g' -e 's/\x08/\\b/g' -e 's/\x0C/\\f/g')
52
53 echo "$escaped_string"
54}
55
56# Print help if no arguments are provided
57if [ $# -eq 0 ]; then
58 display_help
59 exit 0
60fi
61
62# Array of commands to check
63commands=("ls" "cat" "grep" "awk" "basename" "read" "nc" "getopt")
64
65# Initialize command existence variables
66is_ls=false
67is_cat=false
68is_grep=false
69is_awk=false
70is_basename=false
71is_read=false
72is_nc=false
73is_getopt=false
74
75# Check that each command exists
76# TODO: Check for command existance before calling functions
77for cmd in "${commands[@]}"; do
78 if command -v "$cmd" &> /dev/null; then
79 declare "is_$cmd"=true
80 log 1 " [+] Command '$cmd' found."
81 else
82 log 0 " [-] Warning: Required command '$cmd' not found. Some data won't be parsed." >&2
83 fi
84done
85
86# Constances
87maxverbose=2
88regex_decimal_number='^[0-9]+(\.[0-9]+)?$'
89
90# Set variables
91#pid="0"
92cpu=false
93sys=false
94mem=false
95up=false
96names=""
97verbose=0
98timeout=0
99debug=false
100ip=127.0.0.1
101port=65431
102udp=false
103ssh=false
104rounds=-1
105
106# Parse command-line options
107OPTIONS=$(getopt -o n:t:i:p:r:dcsmuavh --long names:,rounds:timeout:,ip:,port:,udp,ssh,debug,cpu,sys,mem,up,all,verbose,help -- "$@")
108
109eval set -- "$OPTIONS"
110
111while true; do
112 case "$1" in
113 -h | --help )
114 display_help
115 exit ;;
116 -a | --all )
117 cpu=true
118 sys=true
119 mem=true
120 up=true
121 shift ;;
122 -c | --cpu )
123 cpu=true
124 shift ;;
125 -s | --sys )
126 sys=true
127 shift ;;
128 -m | --mem )
129 mem=true
130 shift ;;
131 -u | --up )
132 up=true
133 shift ;;
134 -n | --names )
135 names=$2
136 # Remove the leading and trailing brackets
137 names="${names#[}"
138 names="${names%]}"
139 # Remove the quotes around the names
140 names="${names//\"}"
141 names="${names//\'}"
142 IFS=', ' read -r -a names_array <<< "$names"
143 shift 2 ;;
144 -v | --verbose )
145 ((verbose+=1))
146 shift ;;
147 -t | --timeout )
148 timeout=$2
149 if ! [[ $timeout =~ ^[0-9]+$ ]]; then
150 echo "Error: Usage: -t|--timeout <number>" >&2; exit 1
151 fi
152 shift 2 ;;
153 -r | --rounds )
154 rounds=$2
155 if ! [[ $rounds =~ ^[0-9]+$ ]]; then
156 echo "Error: Usage: -r|--rounds <number>" >&2; exit 1
157 fi
158 shift 2 ;;
159 -i | --ip )
160 ip=$2
161 if ! [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
162 echo "Error: Usage: -i|--ip <1.2.3.4>" >&2; exit 1
163 fi
164 shift 2 ;;
165 -p | --port )
166 port=$2
167 if ! [[ $port =~ ^[0-9]{1,5}$ ]]; then
168 echo "Error: Usage: -p|--port <port_number>" >&2; exit 1
169 fi
170 shift 2 ;;
171 -d | --debug )
172 debug=true
173 shift ;;
174 --ssh )
175 ssh=true
176 shift ;;
177 --udp )
178 udp=true
179 shift ;;
180 -- )
181 shift
182 break ;;
183 * )
184 echo "Error: Internal error!" >&2
185 exit 1 ;;
186 esac
187done
188
189# Starting procmon
190log 1 "Starting procmon..."
191
192# Check for -t or -d
193if [ "$timeout" -ne 0 ] && [ $debug = true ]; then
194 log 0 "[-] Error: Can't have -t and -d"
195 exit
196fi
197
198# Check for --ssh or --udp
199if [ $ssh = true ] && [ $udp = true ]; then
200 log 0 "[-] Error: Can't have --udp and --ssh"
201 exit
202fi
203
204
205# Max verbose
206if [ $verbose -ge $maxverbose ] ; then
207 verbose=$maxverbose
208fi
209
210# PID
211## Function to get list of PIDs
212get_list_of_pids() {
213 # Read only folders that have integer name
214 # Each number represents the PID of a running process
215 pid_folders=$(ls /proc/ | grep -E '^[0-9]+$')
216
217 # Print each PID
218 for pid in $pid_folders; do
219 echo "$pid"
220 done
221}
222
223## Get PID list that matches the name
224get_pid_list_by_name() {
225 name="$1"
226
227 local -n pid_array=$2
228
229 for pid_dir in /proc/[0-9]*; do
230 # Extract the PID from the directory path
231 pid=$(basename "$pid_dir")
232
233 # Check if the PID is a directory
234 if [ -d "$pid_dir" ]; then
235 # Check if the comm file exists
236 if [ -f "$pid_dir/comm" ]; then
237 # Read the command from the comm file
238 read_buffer=$(cat "$pid_dir/comm")
239
240 # Check if the command matches the provided process name
241 if [[ "$read_buffer" == *"$name"* ]]; then
242 # Add the PID to the list of matching PIDs
243 pid_array+=("$pid")
244 fi
245 fi
246 fi
247 done
248}
249
250## Function to get CPU usage and uptime for a PID
251get_pid_cpu_and_uptime() {
252 local pid u_time s_time cu_time cs_time start_time total_time hz up_time cpu
253 # Check if PID is provided
254 pid=$1
255
256 # Check if the process exists
257 if [ ! -d "/proc/$pid" ]; then
258 echo ""
259 return 1
260 fi
261
262 # Read the stat file
263 read -r -a stats <<< "$(cat /proc/"$pid"/stat)"
264
265 # Extract the necessary fields
266 u_time=${stats[13]}
267 s_time=${stats[14]}
268 cu_time=${stats[15]}
269 cs_time=${stats[16]}
270 start_time=${stats[21]}
271
272 # Calculate total time
273 total_time=$((u_time + s_time + cu_time + cs_time))
274
275 # Get clock ticks per second
276 hz=$(getconf CLK_TCK)
277
278 # Get system uptime
279 up_time=$(cat /proc/uptime | awk '{print $1}')
280
281 # Calculate process uptime and CPU usage
282 uptime=$(awk "BEGIN {print $up_time - $start_time / $hz}")
283 cpu=$(awk "BEGIN {print 100 * ($total_time / $hz) / $uptime}")
284
285 # Print the result
286 echo "$cpu $uptime"
287}
288
289## Function to get memory usage for a PID
290get_pid_mem() {
291 local pid data stack rss size total_mem total_ram total_size
292 pid=$1
293
294 # Check if the process exists
295 if [ ! -e "/proc/$pid/status" ]; then
296 echo "Process got terminated"
297 return 1
298 fi
299
300 # Read the status file
301 while read -r line; do
302 case $line in
303 VmData:*)
304 data=${line#VmData:}
305 data=${data%% kB*}
306 ;;
307 VmStk:*)
308 stack=${line#VmStk:}
309 stack=${stack%% kB*}
310 ;;
311 VmRSS:*)
312 rss=${line#VmRSS:}
313 rss=${rss%% kB*}
314 ;;
315 VmSize:*)
316 size=${line#VmSize:}
317 size=${size%% kB*}
318 ;;
319 esac
320 done < "/proc/$pid/status"
321
322 # Calculate total memory
323 total_mem=$((data + stack))
324 total_ram=$((rss))
325 total_size=$((size))
326
327 # Print the result
328 echo "$total_mem $total_ram $total_size"
329}
330
331## Function to get command line that started the process
332get_pid_command_line() {
333 local pid file_name process_cli
334 pid=$1
335 file_name="/proc/$pid/cmdline"
336
337 if [ ! -f "$file_name" ]; then
338 echo "process got terminated"
339 return 1
340 fi
341
342 # Read the command line and remove the null character
343 process_cli=$(tr -d '\0' < "$file_name")
344 echo "$process_cli"
345}
346
347## Function to get process name
348get_pid_process_name() {
349 local pid file_name process_name
350 pid=$1
351 file_name="/proc/$pid/comm"
352
353 if [ ! -f "$file_name" ]; then
354 echo "process got terminated"
355 return 1
356 fi
357
358 process_name=$(cat "$file_name")
359 echo "$process_name"
360}
361
362# SYSTEM INFOS
363# Steps:
364# Read the file /proc/cpuinfo till you find the line with “cpu cores” in string.
365# Split the line based on white space.
366# The last element of the array will be number of cores.
367# Multiply it by 2 to get the number of procs.
368
369get_system_infos() {
370 # Get the number of CPUs
371 local cpu_core_count cpu_count total_mem total_swap kernel_version
372 cpu_core_count=$(cat /proc/cpuinfo | awk '/cpu cores/ {print $NF}' | uniq)
373 #cpu_count=$((cpu_core_count/2))
374
375 # Get the total memory
376 total_mem=$(grep MemTotal /proc/meminfo | awk '{print $2}')
377
378 # Get the total swap
379 total_swap=$(grep SwapTotal /proc/meminfo | awk '{print $2}')
380
381 # Get the kernel version
382 kernel_version=$(uname -r)
383 echo "$cpu_core_count $total_mem $total_swap $kernel_version"
384
385
386}
387
388# CPU USAGE
389# Steps:
390# Read the first line of /proc/stat.
391# Discard the first word which is always cpu.
392# Add all of the values in the line to get the total time.
393# Take difference of change in total time and idle time at 1 second interval.
394# Divide the idle time( fourth value) by total time to get fraction of idle time
395# Subtract 1 with the idle time to get percentage of non idle time.
396# Multiple by 100 to get a current cpu percentage.
397
398## Function to read CPU file
399read_cpu_file() {
400 # Extracting the fourth column ("idle") from /proc/stat
401 local idle_time total_time
402 idle_time=$(grep 'cpu ' /proc/stat | awk '{print $5}')
403 # Extracting the total time from /proc/stat
404 total_time=$(grep 'cpu ' /proc/stat | awk '{print $2+$3+$4+$5+$6+$7+$8}')
405 echo "$idle_time $total_time"
406}
407
408# Function to calculate CPU usage
409calculate_cpu() {
410 local last_idle last_total current_idle current_total del_idle del_total cpu_usage
411 read -r last_idle last_total <<< "$1"
412 read -r current_idle current_total <<< "$2"
413
414 # Calculating the difference in idle and total times
415 del_idle=$((current_idle - last_idle))
416 del_total=$((current_total - last_total))
417
418 # Calculating CPU usage percentage
419 cpu_usage=$(awk "BEGIN {print (1 - $del_idle / $del_total) * 100}")
420 echo "$cpu_usage"
421}
422
423# Function to get current CPU usage
424get_current_cpu() {
425 last_stats=$(read_cpu_file)
426 sleep 1
427 current_stats=$(read_cpu_file)
428 cpu_usage=$(calculate_cpu "$last_stats" "$current_stats")
429 echo "$cpu_usage"
430}
431
432# MEMORY USAGE
433# Steps :
434# Read the /proc/meminfo file.
435# Find all the parameters : MemTotal, MemFree, Buffers, Cached, SwapCached, SwapTotal, SwapFree, Shmem, SReclaimable
436# Do calculation
437## Function to extract parameter from /proc/meminfo
438extract_parameter() {
439 parameter=$1
440 value=$(grep "^$parameter" /proc/meminfo | awk '{print $2}')
441 echo "$value"
442}
443
444## Function to calculate memory values
445calculate_memory_values() {
446 MemTotal=$(extract_parameter "MemTotal:")
447 MemFree=$(extract_parameter "MemFree:")
448 Buffers=$(extract_parameter "Buffers:")
449 Cached=$(extract_parameter "Cached:")
450 SwapTotal=$(extract_parameter "SwapTotal:")
451 SwapFree=$(extract_parameter "SwapFree:")
452 shared=$(extract_parameter "Shmem:")
453 Slab=$(extract_parameter "SReclaimable:")
454
455 # Calculating used, free, and buff/cache values
456 used=$((MemTotal - MemFree - Buffers - Cached - Slab))
457 free=$MemFree
458 buff_cache=$((Buffers + Cached + Slab))
459
460 # Extracting MemAvailable and shared values
461 MemAvailable=$(extract_parameter "MemAvailable:")
462
463 # Calculating Swap values
464 SwapUsed=$((SwapTotal - SwapFree))
465
466 # Outputting the calculated values
467 echo $MemTotal $used $free $MemAvailable $shared $buff_cache $SwapTotal $SwapUsed $SwapFree
468}
469
470# UPTIME
471read_uptime() {
472 uptime_info=$(head -n1 /proc/uptime)
473 uptime_seconds=$(echo "$uptime_info" | awk '{print $1}')
474 echo "$uptime_seconds"
475}
476
477# MAIN
478main() {
479 local escaped_value system_vars process_vars json count
480
481 system_vars=(cpu_core_count total_mem total_swap kernel_version system_cpu system_uptime system_mem_total system_mem_used system_mem_free system_mem_available system_mem_shared system_mem_buff_cache system_swap_total system_swap_used system_swap_free)
482 process_vars=(process_cpu process_uptime process_mem process_command_line process_ram process_vmem)
483
484 # Start the JSON string
485 json="{"
486
487 count=1
488
489 # While loop to do the following:
490 while [ $count -le "$rounds" ] || [ "$rounds" -eq -1 ]
491 do
492 log 1 "[+] Round $count"
493
494 # Start Json
495 json="{"
496
497 # Get system infos
498 if [ $sys == "true" ] ; then
499 read -r cpu_core_count total_mem total_swap kernel_version <<< "$(get_system_infos)"
500 fi
501 # Get cpu infos
502 if [ "$cpu" == "true" ]; then
503 read -r system_cpu <<< "$(get_current_cpu)"
504 fi
505 # Get uptime infos
506 if [ $up == "true" ]; then
507 read -r system_uptime <<< "$(read_uptime)"
508 fi
509 # Get memory infos
510 if [ $mem == "true" ]; then
511 read -r system_mem_total system_mem_used system_mem_free system_mem_available system_mem_shared system_mem_buff_cache system_swap_total system_swap_used system_swap_free <<< "$(calculate_memory_values)"
512 fi
513
514 log 1 " [+] Getting system infos"
515
516 # Loop over the variable names
517 for var in "${system_vars[@]}"; do
518 # Get the value of the variable
519 value=${!var}
520 # Check if the value is a number
521 if [[ $value =~ $regex_decimal_number ]]; then
522 # If it's a number, add it to the JSON string without quotes
523 json+="\"$var\": $value,"
524 else
525 # If it's not a number, add it to the JSON string with quotes
526 read -r escaped_value <<< "$(json_escape "$value")"
527 json+="\"$var\": \"$escaped_value\","
528 fi
529 done
530
531 # Loop over process names
532 for name in "${names_array[@]}"; do
533 log 1 " [+] Getting process $name infos"
534 local pids=()
535 get_pid_list_by_name "$name" pids
536 # Add a subsection to json for each process
537 json+="\"$name\":{"
538 # Loop over PIDs
539 for pid in "${pids[@]}"; do
540 log 1 " [+] Getting PID $pid infos for process"
541 if [ "$pid" -ne 0 ]; then
542 read -r process_cpu process_uptime <<< "$(get_pid_cpu_and_uptime "$pid")"
543 read -r process_mem process_ram process_vmem <<< "$(get_pid_mem "$pid")"
544 read -r process_command_line <<< "$(get_pid_command_line "$pid")"
545 # read -r process_name <<< "$(get_pid_process_name "$pid")"
546 fi
547 # Add a subsection to json for each PID
548 json+="\"$pid\":{"
549 for var in "${process_vars[@]}"; do
550 value=${!var}
551 if [[ $value =~ $regex_decimal_number ]]; then
552 json+="\"$var\": $value,"
553 else
554 read -r escaped_value <<< "$(json_escape "$value")"
555 json+="\"$var\": \"$escaped_value\","
556 fi
557 done
558 # Remove the trailing , and replace it by },
559 json="${json%,}},"
560 done
561 # Remove the trailing , and replace it by },
562 json="${json%,}},"
563 done
564
565 # Remove the trailing comma and close the JSON string
566 json="${json%,}}"
567
568 log 1 " [+] Sending to $ip:$port"
569
570 if [ $udp = true ]; then
571 echo -n "$json" | nc -u -w1 "$ip" "$port"
572 else
573 echo "$json"
574 fi
575 #echo -n "$json" | nc -u -w0 "127.0.0.1" "65431"
576
577 log 2 "$json"
578
579 log 2 " [+] Data size : ${#json}"
580
581 count=$((count+1))
582
583 # Wait between rounds
584 if [ "$timeout" -ne 0 ]; then
585 log 1 "[+] Sleeping $timeout seconds"
586 sleep "$timeout"
587 elif [ "$debug" = true ]; then
588 log 1 "[+] Waiting for input..."
589 read -r -n 1
590 fi
591
592 done
593}
594
595main
ProcessMonitor
The process monitor consists of two parts; the ProcessMonitor class that implements
BaseMonitor and a second module that is to be run on the host of your target.
- class boofuzz.monitors.ProcessMonitor(host, port)[source]
Proxy class for the process monitor interface.
In Versions < 0.2.0, boofuzz had network and process monitors that communicated over RPC. The RPC client was directly passed to the session class, and resolved all method calls dynamically on the RPC partner.
Since 0.2.0, every monitor class must implement the abstract class BaseMonitor, which defines a common interface among all Monitors. To aid future typehinting efforts and to disambiguate Network- and Process Monitors, this explicit proxy class has been introduced that fast-forwards all calls to the RPC partner.
Added in version 0.2.0.
- on_new_server(new_uuid)[source]
Restores all set options to the RPC daemon if it has restarted since the last call.
- post_send(target=None, fuzz_data_logger=None, session=None)[source]
This method is forwarded to the RPC daemon.
- pre_send(target=None, fuzz_data_logger=None, session=None)[source]
This method is forwarded to the RPC daemon.
- restart_target(target=None, fuzz_data_logger=None, session=None)[source]
This method is forwarded to the RPC daemon.
- set_crash_filename(new_crash_filename)[source]
Deprecated since version 0.2.0.
This option should be set via
set_options.
- set_options(*args, **kwargs)[source]
The old RPC interfaces specified set_foobar methods to set options. As these vary by RPC implementation, this trampoline method translates arguments that have been passed as keyword arguments to set_foobar calls.
If you call
set_options(foobar="barbaz"), it will result in a call toset_foobar("barbaz")on the RPC partner.
- set_proc_name(new_proc_name)[source]
Deprecated since version 0.2.0.
This option should be set via
set_options.
- set_start_commands(new_start_commands)[source]
Deprecated since version 0.2.0.
This option should be set via
set_options.
NetworkMonitor
The network monitor consists of two parts; the NetworkMonitor class that implements
BaseMonitor and a second module that is to be run on a host that can monitor the traffic.
- class boofuzz.monitors.NetworkMonitor(host, port)[source]
Proxy class for the network monitor interface.
In Versions < 0.2.0, boofuzz had network and process monitors that communicated over RPC. The RPC client was directly passed to the session class, and resolved all method calls dynamically on the RPC partner.
Since 0.2.0, every monitor class must implement the abstract class BaseMonitor, which defines a common interface among all Monitors. To aid future typehinting efforts and to disambiguate Network- and Process Monitors, this explicit proxy class has been introduced that fast-forwards all calls to the RPC partner.
Added in version 0.2.0.
- on_new_server(new_uuid)[source]
Restores all set options to the RPC daemon if it has restarted since the last call.
- post_send(target=None, fuzz_data_logger=None, session=None)[source]
This method is forwarded to the RPC daemon.
- pre_send(target=None, fuzz_data_logger=None, session=None)[source]
This method is forwarded to the RPC daemon.
- restart_target(target=None, fuzz_data_logger=None, session=None)[source]
Always returns false as this monitor cannot restart a target.
- set_filter(new_filter)[source]
Deprecated since version 0.2.0.
This option should be set via
set_options.
- set_log_path(new_log_path)[source]
Deprecated since version 0.2.0.
This option should be set via
set_options.
- set_options(*args, **kwargs)[source]
The old RPC interfaces specified set_foobar methods to set options. As these vary by RPC implementation, this trampoline method translates arguments that have been passed as keyword arguments to set_foobar calls.
If you call
set_options(foobar="barbaz"), it will result in a call toset_foobar("barbaz")on the RPC partner.Additionally, any options set here are cached and re-applied to the RPC server should it restart for whatever reason (e.g. the VM it’s running on was restarted).
CallbackMonitor
- class boofuzz.monitors.CallbackMonitor(on_pre_send=None, on_post_send=None, on_restart_target=None, on_post_start_target=None)[source]
New-Style Callback monitor that is used in Session to provide callback-arrays. Its purpose is to keep the *_callbacks arguments in the session class while simplifying the implementation of session by forwarding these callbacks to the monitor infrastructure.
The mapping of arguments to method implementations of this class is as follows:
restart_callbacks –> target_restart
pre_send_callbacks –> pre_send
post_test_case_callbacks –> post_send
post_start_target_callbacks –> post_start_target
All other implemented interface members are stubs only, as no corresponding arguments exist in session. In any case, it is probably wiser to implement a custom Monitor than to use the callback functions.
Added in version 0.2.0.
- post_send(target=None, fuzz_data_logger=None, session=None)[source]
This method iterates over all supplied post send callbacks and executes them. Their return values are discarded, exceptions are caught and logged:
BoofuzzTargetConnectionResetwill log a failureBoofuzzTargetConnectionAbortedwill log an infoBoofuzzTargetConnectionFailedErrorwill log a failureBoofuzzSSLErrorwill log either info or failure, depending on if the session ignores SSL/TLS errors.every other exception is logged as an error.
All exceptions are discarded after handling.
- post_start_target(target=None, fuzz_data_logger=None, session=None)[source]
Called after a target is started or restarted.