wlk.sh 5.28 KB
Newer Older
UltimateByte's avatar
UltimateByte committed
1
2
#!/bin/bash
# Name: Wrong Listener Killer
UltimateByte's avatar
UltimateByte committed
3
4
# Version: 2017-05-24
# Description: Kills processes that do not meet defined rules on a given port
UltimateByte's avatar
UltimateByte committed
5
6
# Help: Default settings are to make sure that what listens to port :80 is httpd and started as root
# Developer: Robin Labadie
UltimateByte's avatar
UltimateByte committed
7
# Website: www.lrob.fr
UltimateByte's avatar
UltimateByte committed
8
9

## Settings
UltimateByte's avatar
UltimateByte committed
10
portcheck=":80" # Which port to check
UltimateByte's avatar
UltimateByte committed
11
12
13
14
15
allowedname="httpd" # Which process should we get on speccified port
allowedpath="/usr/sbin/httpd" # Which is the correct path to run it
allowedusers="root;" # Which is the correct user to run it (separate with ; )

logdir="/root" # Log directory (don't end with /)
UltimateByte's avatar
UltimateByte committed
16
17
18
mailalert="yes" # Wether to send a mail alert or not (yes/no)
mailaddress="root@localhost" # Mail to send an alert to if a threat is detected

UltimateByte's avatar
UltimateByte committed
19
sleeptime="0" # Sleep between kills
UltimateByte's avatar
UltimateByte committed
20
maxruns="30" # How many PID this script can kill
UltimateByte's avatar
UltimateByte committed
21
22

## Misc vars
UltimateByte's avatar
UltimateByte committed
23
selfname="wrong_listener_killer" # Name of the script and log
UltimateByte's avatar
UltimateByte committed
24
25
log="${logdir}/${selfname}.log" # Define log name

UltimateByte's avatar
UltimateByte committed
26
27
28
29
############
### Code ###
############

UltimateByte's avatar
UltimateByte committed
30
31
32
33
34
35
36
37
38

### MISC FUNCTIONS ###
# Manage logs
fn_logging(){
	# Create logfile
	if [ -n "${log}" ]&&[ ! -f "${log}" ]; then
		touch "${log}"
	fi
}
UltimateByte's avatar
UltimateByte committed
39
40
41
42
43
44
45
46
47
48

# Simple echo with date and selfname
# Usage fn_echo "Your Message"
fn_echo(){
	currmessage="$@"
	echo -e "$(date +%Y-%m-%d_%H:%M:%S) - ${selfname} - ${currmessage}"
}

# Echo with date and output to log at the same time
# Usage fn_logecho "Your Message"
UltimateByte's avatar
UltimateByte committed
49
fn_logecho(){
UltimateByte's avatar
UltimateByte committed
50
	fn_echo
UltimateByte's avatar
UltimateByte committed
51
	echo -e "$(date +%Y-%m-%d_%H:%M:%S) - ${selfname} - ${currmessage}" >> "${log}"
UltimateByte's avatar
UltimateByte committed
52
	currlog="${currlog}$(echo -e "$(date +%Y-%m-%d_%H:%M:%S) - ${selfname} - ${currmessage}")"
UltimateByte's avatar
UltimateByte committed
53
54
}

UltimateByte's avatar
UltimateByte committed
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# Send mail alert
fn_mail_alert(){
	if [ "${mailalert}" == "yes" ]; then
		fn_logecho "[INFO] Sending mail alert to: ${mailaddress}"
		echo "${currlog}" | mail -s "$(hostname -s) - ${pidname} - ${portcheck} killed" ${mailaddress}
	fi
	# Since this is the last action that should occur
	exit
}

### CORE FUNCTIONS ###

# Check if we can find the PID of a listening program on $portcheck
fn_define_pid(){
UltimateByte's avatar
UltimateByte committed
69
	pid="$(netstat -atunp | grep "${portcheck} " | grep LISTEN | awk '{print $7}' | awk -F "/" '{print $1}')"
UltimateByte's avatar
UltimateByte committed
70
71
72
73
74
75
76
77
78
	# If nothing listens unpon first start
	if [ -z "${pid}" ]&&[ -z "${actiontaken}" ]; then
		fn_logecho "[INFO] Nothing found on port ${portcheck} | Exit"
		exit
	# If nothing listens after getting some processes killed
	elif [ -z "${pid}" ]&&[ "${actiontaken}" == "1" ]; then
		fn_logecho "[OK] Nothing listens on port ${portcheck} anymore | Exit"
		# Send mail alert 
		fn_mail_alert
UltimateByte's avatar
UltimateByte committed
79
80
	fi
}
UltimateByte's avatar
UltimateByte committed
81

UltimateByte's avatar
UltimateByte committed
82
# Define what listens to $portcheck
UltimateByte's avatar
UltimateByte committed
83
fn_define_vars(){
UltimateByte's avatar
UltimateByte committed
84
	fn_define_pid
UltimateByte's avatar
UltimateByte committed
85
86
87
	pidname="$(netstat -atunp | grep "${portcheck} " | grep LISTEN | awk '{print $7}' | awk -F "/" '{print $2}')"
	piduser="$(ps -u -p "${pid}" | tail -1 | awk '{print $1}')"
	pidcommand="$(ps -u -p "${pid}" | tail -1 | awk '{print $11}')"
UltimateByte's avatar
UltimateByte committed
88
89
90
91
92
93
94
95
	# If one of these vars are unset, then something went wrong
	if [ -z "${pidname}" ]||[ -z "${piduser}" ]||[ -z "${pidcommand}" ]; then
		fn_logecho "[ERROR] Could not get app info with PID"
		fn_logecho "Exiting"
		exit 1
	fi
	# Provide info to the user
	fn_echo "Current program listening to ${portcheck} is : PID: ${pid}\tName: ${pidname}\tUser: ${piduser}\tPath: ${pidcommand}"
UltimateByte's avatar
UltimateByte committed
96
}
UltimateByte's avatar
UltimateByte committed
97

UltimateByte's avatar
UltimateByte committed
98
99
100
## Evaluate issues
fn_evaluate(){
	# Check process name
UltimateByte's avatar
UltimateByte committed
101
	if [ "${pidname}" != "${allowedname}" ]; then
UltimateByte's avatar
UltimateByte committed
102
		harm="1"
UltimateByte's avatar
UltimateByte committed
103
	fi
UltimateByte's avatar
UltimateByte committed
104

UltimateByte's avatar
UltimateByte committed
105
106
107
	# Check process ownership
	# We will be using a loop since multiple users might be allowed to run it
	# See how many users we have to check so that we can end the loop
UltimateByte's avatar
UltimateByte committed
108
	usersamount="$(echo "${allowedusers}" | awk -F ';' '{ print NF }')"
UltimateByte's avatar
UltimateByte committed
109
110
	allowedtorun="0" # This var is the test result and will be set to 1 if the right user is detected
	# Entering the loop to go through allowed users
UltimateByte's avatar
UltimateByte committed
111
112
113
	for ((usersindex=1; usersindex <= usersamount; usersindex++)); do
		# Put current user into a test variable
		usertest="$(echo "${allowedusers}" | awk -F ';' -v x=${usersindex} '{ print $x }')"
UltimateByte's avatar
UltimateByte committed
114
		# Test if user is allowed, register success if it is
UltimateByte's avatar
UltimateByte committed
115
116
117
118
		if [ "${piduser}" == "${usertest}" ]; then
			allowedtorun="1"
		fi
	done
UltimateByte's avatar
UltimateByte committed
119
	# Result
UltimateByte's avatar
UltimateByte committed
120
121
	if [ "${allowedtorun}" == "0" ]; then
		harm="1"
UltimateByte's avatar
UltimateByte committed
122
123
	fi

UltimateByte's avatar
UltimateByte committed
124
	# Check process path
UltimateByte's avatar
UltimateByte committed
125
126
127
	if [ "${pidcommand}" != "${allowedpath}" ]; then
		harm="1"
	fi
UltimateByte's avatar
UltimateByte committed
128
}
UltimateByte's avatar
UltimateByte committed
129

UltimateByte's avatar
UltimateByte committed
130
131
132
## Take action
fn_action(){
	## Problematic process was found
UltimateByte's avatar
UltimateByte committed
133
	if [ "${harm}" == "1" ]; then
UltimateByte's avatar
UltimateByte committed
134
135
136
137
		fn_logecho "[ALERT] Process on port ${portcheck} does not meet requirements"
		fn_logecho "[INFO] Expected: Name: ${allowedname}\tUser: ${allowedusers}\tPath: ${allowedpath}"
		fn_logecho "[INFO] Actual  : Name: ${pidname}\tUser: ${piduser}\tPath: ${pidcommand}"
		fn_logecho "[ACTION] Killing PID ${pid}"
UltimateByte's avatar
UltimateByte committed
138
		kill -9 "${pid}"
UltimateByte's avatar
UltimateByte committed
139
140
141
142
143
		# Reset harm for future tests
		unset harm
		# Misc var to tell that an action has been taken
		actiontaken="1"
		# Misc var to count how many time we ran this
UltimateByte's avatar
UltimateByte committed
144
		count=$((count+1))
UltimateByte's avatar
UltimateByte committed
145
146
147
148
149
150
		# If $count is greater or equel to $maxruns; then end there
		if [ "${count}" -ge "${maxruns}" ]; then
			fn_logecho "[WARNING] Exiting because the loop has reached the maximum ${maxruns} runs"
			fn_mail_alert
		# Otherwise, let's run it again
		else
UltimateByte's avatar
UltimateByte committed
151
			sleep "${sleeptime}"
UltimateByte's avatar
UltimateByte committed
152
153
154
			fn_run_functions
		fi
	elif [ "${actiontaken}" == "1" ]; then
UltimateByte's avatar
UltimateByte committed
155
		fn_logecho "[OK] The process on port ${portcheck} now meets requirements"
UltimateByte's avatar
UltimateByte committed
156
157
		exit
	else
UltimateByte's avatar
UltimateByte committed
158
		fn_logecho "[OK] The process on port ${portcheck} meets requirements"
UltimateByte's avatar
UltimateByte committed
159
		exit
UltimateByte's avatar
UltimateByte committed
160
161
	fi
}
UltimateByte's avatar
UltimateByte committed
162
163
164
165
166
167
168
169
170

### RUN FUNCTIONS ###
fn_logging
fn_run_functions(){
	fn_define_pid
	fn_define_vars
	fn_evaluate
	fn_action
}