wlk.sh 6.25 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
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 ; )

15
16
17
actionbefore="service ${allowedname} restart" # Run a custom action if a problem is found
actionafter="service ${allowedname} restart" # Run a custom action after a problem was found and processes killed

UltimateByte's avatar
UltimateByte committed
18
logdir="/root" # Log directory (don't end with /)
UltimateByte's avatar
UltimateByte committed
19
20
21
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

22
sleeptime="0.1" # Sleep between kills
UltimateByte's avatar
UltimateByte committed
23
maxruns="30" # How many PID this script can kill
UltimateByte's avatar
UltimateByte committed
24
25

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

UltimateByte's avatar
UltimateByte committed
29
30
31
32
############
### Code ###
############

UltimateByte's avatar
UltimateByte committed
33
34
35
36
37
38
39
40
41

### MISC FUNCTIONS ###
# Manage logs
fn_logging(){
	# Create logfile
	if [ -n "${log}" ]&&[ ! -f "${log}" ]; then
		touch "${log}"
	fi
}
UltimateByte's avatar
UltimateByte committed
42

UltimateByte's avatar
UltimateByte committed
43
44
45
46
47
# Give a form to echo messages
fn_echo_form(){
	echoform="$(date +%Y-%m-%d_%H:%M:%S) - ${selfname} - ${currmessage}"
}

UltimateByte's avatar
UltimateByte committed
48
49
50
# Simple echo with date and selfname
# Usage fn_echo "Your Message"
fn_echo(){
UltimateByte's avatar
UltimateByte committed
51
	currmessage="$1"
UltimateByte's avatar
UltimateByte committed
52
53
	fn_echo_form
	echo -e "${echoform}"
UltimateByte's avatar
UltimateByte committed
54
55
56
57
}

# Echo with date and output to log at the same time
# Usage fn_logecho "Your Message"
UltimateByte's avatar
UltimateByte committed
58
fn_logecho(){
UltimateByte's avatar
UltimateByte committed
59
60
61
	currmessage="$1"
	fn_echo_form
	echo -e "${echoform}"
UltimateByte's avatar
UltimateByte committed
62
	echo -e "${echoform}" >> "${log}"
63
	currlog="${currlog}${echoform}\n"
UltimateByte's avatar
UltimateByte committed
64
65
}

UltimateByte's avatar
UltimateByte committed
66
67
68
69
# Send mail alert
fn_mail_alert(){
	if [ "${mailalert}" == "yes" ]; then
		fn_logecho "[INFO] Sending mail alert to: ${mailaddress}"
70
		echo -e "${currlog}" | mail -s "$(hostname -s) - ${pidname} - ${portcheck} killed" ${mailaddress}
UltimateByte's avatar
UltimateByte committed
71
72
73
74
75
76
77
78
79
	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
80
	pid="$(netstat -atunp | grep "${portcheck} " | grep LISTEN | awk '{print $7}' | awk -F "/" '{print $1}')"
UltimateByte's avatar
UltimateByte committed
81
82
83
84
85
86
87
88
89
	# 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
90
91
	fi
}
UltimateByte's avatar
UltimateByte committed
92

UltimateByte's avatar
UltimateByte committed
93
# Define what listens to $portcheck
UltimateByte's avatar
UltimateByte committed
94
fn_define_vars(){
UltimateByte's avatar
UltimateByte committed
95
	fn_define_pid
UltimateByte's avatar
UltimateByte committed
96
97
98
	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
99
100
101
102
103
104
105
106
	# 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
107
}
UltimateByte's avatar
UltimateByte committed
108

UltimateByte's avatar
UltimateByte committed
109
110
111
## Evaluate issues
fn_evaluate(){
	# Check process name
UltimateByte's avatar
UltimateByte committed
112
	if [ "${pidname}" != "${allowedname}" ]; then
UltimateByte's avatar
UltimateByte committed
113
		harm="1"
UltimateByte's avatar
UltimateByte committed
114
	fi
UltimateByte's avatar
UltimateByte committed
115

UltimateByte's avatar
UltimateByte committed
116
117
118
	# 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
119
	usersamount="$(echo "${allowedusers}" | awk -F ';' '{ print NF }')"
UltimateByte's avatar
UltimateByte committed
120
121
	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
122
123
124
	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
125
		# Test if user is allowed, register success if it is
UltimateByte's avatar
UltimateByte committed
126
127
128
129
		if [ "${piduser}" == "${usertest}" ]; then
			allowedtorun="1"
		fi
	done
UltimateByte's avatar
UltimateByte committed
130
	# Result
UltimateByte's avatar
UltimateByte committed
131
132
	if [ "${allowedtorun}" == "0" ]; then
		harm="1"
UltimateByte's avatar
UltimateByte committed
133
134
	fi

UltimateByte's avatar
UltimateByte committed
135
	# Check process path
UltimateByte's avatar
UltimateByte committed
136
137
138
	if [ "${pidcommand}" != "${allowedpath}" ]; then
		harm="1"
	fi
UltimateByte's avatar
UltimateByte committed
139
}
UltimateByte's avatar
UltimateByte committed
140

141
142
# Execute an action before proceeding
fn_actionbefore(){
UltimateByte's avatar
UltimateByte committed
143
	if [ -n "${actionbefore}" ]&&[ "${harm}" == "1" ]&&[ -z "${actiontaken}" ]; then
144
145
		fn_logecho "[ACTION] Applying actionbefore: ${actionbefore}"
		${actionbefore}
UltimateByte's avatar
UltimateByte committed
146
147
148
149
150
151
		actiontaken="1"
		refresh="1"
	else
		# Misc var to tell that an action has been taken
		actiontaken="1"
		fn_logecho "[ACTION] Refreshing info ${pid}"
152
153
154
155
156
157
158
159
160
161
162
	fi
} 

# Execute an action after proceeding
fn_actionafter(){
	if [ -n "${actionafter}" ]; then
		fn_logecho "[ACTION] Applying actionafter: ${actionafter}"
		${actionafter}
	fi
}

UltimateByte's avatar
UltimateByte committed
163
164
165
## Take action
fn_action(){
	## Problematic process was found
UltimateByte's avatar
UltimateByte committed
166
	if [ "${harm}" == "1" ]; then
UltimateByte's avatar
UltimateByte committed
167
168
169
		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}"
170
171
		# Take the "before" action
		fn_actionbefore
UltimateByte's avatar
UltimateByte committed
172
173
174
175
176
177
		# If a before action has been done, refresh info
		if [ -n "${refresh}" ]; then
			unset refresh
			fn_define_vars
			fn_evaluate
			fn_action
UltimateByte's avatar
UltimateByte committed
178
		else
UltimateByte's avatar
UltimateByte committed
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
			# Kill the app
			fn_logecho "[ACTION] Killing PID ${pid}"
			kill -9 "${pid}"
			# Reset harm for future tests
			unset harm
			# Misc var to count how many time we ran this
			count=$((count+1))
			# 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
				sleep "${sleeptime}"
				fn_run_functions
			fi
UltimateByte's avatar
UltimateByte committed
195
196
		fi
	elif [ "${actiontaken}" == "1" ]; then
UltimateByte's avatar
UltimateByte committed
197
		fn_logecho "[OK] The process on port ${portcheck} now meets requirements"
198
		fn_mail_alert
UltimateByte's avatar
UltimateByte committed
199
	else
UltimateByte's avatar
UltimateByte committed
200
		fn_logecho "[OK] The process on port ${portcheck} meets requirements"
UltimateByte's avatar
UltimateByte committed
201
		exit
UltimateByte's avatar
UltimateByte committed
202
203
	fi
}
UltimateByte's avatar
UltimateByte committed
204
205
206
207
208
209
210
211
212

### RUN FUNCTIONS ###
fn_logging
fn_run_functions(){
	fn_define_pid
	fn_define_vars
	fn_evaluate
	fn_action
}
UltimateByte's avatar
UltimateByte committed
213
fn_run_functions