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 HKCU\Software\Microsoft\Windows\CurrentVersion\Run.

This persistence method also helps us with our lateral movement problem - more on that later.

Priv Esc

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 CitrixAdmin and VDI-004$.

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.

Lateral Movement

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.

Let’s say 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 VDI-001.

We can elevate using the CitrixAdmin account, because the password is the same across all systems. Here, we do a spawnas and bypassuac.

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 VDI-004.

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, rparker.

We can obviously steal his password, inject a new Beacon into one of his processes and setup the same userland persistance as with nlove.

Rince and Repeat

Let’s say another day goes by, nlove and rparker have logged back in and are assigned VDI-005 and VDI-003 respectively.

We elevate on both desktops.

Dump the hashes.

And Pass the Ticket to take VDI-001 and VDI-004 again.

We now discover a new user on VDI-004 - jhardin.

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.