wlk.sh 5.22 KB
Newer Older
UltimateByte's avatar
UltimateByte committed
1
2
3
4
5
#!/bin/bash
# Name: Wrong Listener Killer
# Description: Makes sure that the wrong apps don't listen to the ports you want
# 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
6
# Website: www.lrob.fr
UltimateByte's avatar
UltimateByte committed
7
8
9
# Version: 2017-05-24

## 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
19
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

maxruns="30" # How many PID this script can kill
UltimateByte's avatar
UltimateByte committed
20
21

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

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

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

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

# 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
48
fn_logecho(){
UltimateByte's avatar
UltimateByte committed
49
	fn_echo
UltimateByte's avatar
UltimateByte committed
50
	echo -e "$(date +%Y-%m-%d_%H:%M:%S) - ${selfname} - ${currmessage}" >> "${log}"
UltimateByte's avatar
UltimateByte committed
51
	currlog="${currlog}$(echo -e "$(date +%Y-%m-%d_%H:%M:%S) - ${selfname} - ${currmessage}")"
UltimateByte's avatar
UltimateByte committed
52
53
}

UltimateByte's avatar
UltimateByte committed
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# 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
68
	pid="$(netstat -atunp | grep "${portcheck} " | grep LISTEN | awk '{print $7}' | awk -F "/" '{print $1}')"
UltimateByte's avatar
UltimateByte committed
69
70
71
72
73
74
75
76
77
	# 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
78
79
	fi
}
UltimateByte's avatar
UltimateByte committed
80

UltimateByte's avatar
UltimateByte committed
81
# Define what listens to $portcheck
UltimateByte's avatar
UltimateByte committed
82
fn_define_vars(){
UltimateByte's avatar
UltimateByte committed
83
	fn_define_pid
UltimateByte's avatar
UltimateByte committed
84
85
86
	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
87
88
89
90
91
92
93
94
	# 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
95
}
UltimateByte's avatar
UltimateByte committed
96

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

UltimateByte's avatar
UltimateByte committed
104
105
106
	# 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
107
	usersamount="$(echo "${allowedusers}" | awk -F ';' '{ print NF }')"
UltimateByte's avatar
UltimateByte committed
108
109
	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
110
111
112
	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
113
		# Test if user is allowed, register success if it is
UltimateByte's avatar
UltimateByte committed
114
115
116
117
		if [ "${piduser}" == "${usertest}" ]; then
			allowedtorun="1"
		fi
	done
UltimateByte's avatar
UltimateByte committed
118
	# Result
UltimateByte's avatar
UltimateByte committed
119
120
	if [ "${allowedtorun}" == "0" ]; then
		harm="1"
UltimateByte's avatar
UltimateByte committed
121
122
	fi

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

UltimateByte's avatar
UltimateByte committed
129
130
131
## Take action
fn_action(){
	## Problematic process was found
UltimateByte's avatar
UltimateByte committed
132
	if [ "${harm}" == "1" ]; then
UltimateByte's avatar
UltimateByte committed
133
134
135
136
		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
137
		kill -9 "${pid}"
UltimateByte's avatar
UltimateByte committed
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
		# 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
		count=$(($z+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
			fn_run_functions
		fi
	elif [ "${actiontaken}" == "1" ]; then
		fn_logecho "[OK] The process on port ${portcheck} now meets requirements
		exit
	else
		fn_logecho "[OK] The process on port ${portcheck} meets requirements
		exit
UltimateByte's avatar
UltimateByte committed
158
159
	fi
}
UltimateByte's avatar
UltimateByte committed
160
161
162
163
164
165
166
167
168

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