A Citrix Story
I recently had the pleasure of partaking in a Citrix Virtual Desktop infrastructure assessment.
The purpose of the system was to provide staff a means of accessing internal resources via a BYOD model. They would login to a publically available Citrix Access Gateway and receive a virtual desktop instance of Windows 7.
The scope was limited to the VDI’s so I wasn’t to attack other elements of the domain, which made lateral movement a pain in particular. This post is an outline of how, after initial local privilege escalation, I was able to leverage Kerberos Silver Tickets as a means of maintaining elevated persistence and lateral movement capability.
This was a partial white-box review, so I did have some valid credentials to log in with. So we’ll kick this off with a Cobalt Strike foothold.
I have a session as
nlove, and I’d like to keep it that way. One tricky aspect to VDI is that each time a user logs on, they get assigned a fresh desktop from the master image. This means that once a user logs off a virtual desktop, any changes they may’ve made are lost. Provisions must be made for users’ files etc, but like regular domain roaming profiles, there are some elements that get left behind.
In this case, it turns out practically everything that occurs within userland persist across different VDI sessions, including HKCU. So I setup a really simple level of persistence in
This persistence method also helps us with our lateral movement problem - more on that later.
This turned out to be relatively simple.
One of the system administrators must’ve put
bob in at some point for testing or during early development, but it was a pretty big derp as it has no password. The only difficultly I had was spawning a new session via Beacon. The built-in parser won’t handle a blank password and writing a custom aggressor script to run
bspawnas didn’t work either. I tried a few others like
runas, but I eventually resigned myself to elevating in the actual virtual desktop. So I just uploaded a stageless PowerShell SMB payload and ran it manually.
If any readers have a solution to this - please let me know!
Now that we have admin access over the desktop, we can carry out the classic act of dumping all the password hashes. The accounts we’re interested in are
The Cobalt Strike credential parser doesn’t grab machine hashes by default. Silver Ticket Monitor is an aggressor extension built by Jeff Dimmock, which enables machine hash tracking and provides a new GUI popup for crafting Silver Tickets.
Even though I have the credentials for 2 local admin accounts (the built-in Administrator account is disabled), they can’t be used to move laterally across the other VDIs. Non-RID 500 accounts cannot (by default) authenticate as administrators across a network, even if the credentials are identical across computers. Will Schroeder blogged about this a while ago.
The Desktop Shuffle
This is where things start getting at least a little more interesting.
nlove logs off for the day and we lose our Beacons. Next morning he logs on and gets a fresh virtual desktop. Because his user profile follows him from desktop to desktop, our registry persistence calls back and we get a new session. This time on
We can elevate using the
CitrixAdmin account, because the password is the same across all systems. Here, we do a
We can now dump the machine hash for
VDI-001. The next trick, is moving laterally back to
VDI-004, which we can do using its machine hash that we obtained previously. Using Jeff’s extension:
The domain SID can be obtained via
shell whoami /all; and the actual
mimiktaz command to forge the Silver Ticket is
mimikatz kerberos::golden /user:CitrixAdmin /domain:wild-west.local /sid:S-1-5-21-4247185703-3639085842-2880657779 /target:VDI-004.wild-west.local /rc4:ea0830b4abd1f2c976bb443f64f8186d /service:cifs /ptt
Once the ticket has been injected into our current session, we can simply do a
psexec_psh (or whatever method you prefer) to take
Now we have control over 2 VDIs.
If we take a look at
VDI-004 we can see that it’s been assigned to another user,
We can obviously steal his password, inject a new Beacon into one of his processes and setup the same userland persistance as with
Rince and Repeat
Let’s say another day goes by,
rparker have logged back in and are assigned
We elevate on both desktops.
Dump the hashes.
And Pass the Ticket to take
We now discover a new user on
Follow the same process of persisting as
jhardin and spreading to more desktops.
There are obviously better and more elegent methods of moving laterally across machines. Given the limitation of the scope, this seemed to be the best available option. By default, computer hashes are automatically rotated every 30 days, so it would be a lot of work to maintain access across a large number of desktops. As long as there is sufficient overlap between the computer and
CitrixAdmin account passwords being changed, you could likely persist in this manner for a long time.