{
	"id": "f0c0bcd6-6d63-4a4c-919d-8a298e4099cf",
	"created_at": "2026-04-06T00:11:49.404377Z",
	"updated_at": "2026-04-10T03:20:41.699755Z",
	"deleted_at": null,
	"sha1_hash": "d96ba5db174fa7f6114df84bc65826341b4f9f24",
	"title": "Windows Server Containers Are Open, and Here's How You Can Break Out",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 993278,
	"plain_text": "Windows Server Containers Are Open, and Here's How You Can\r\nBreak Out\r\nBy Daniel Prizmant\r\nPublished: 2020-07-15 · Archived: 2026-04-05 22:05:25 UTC\r\nExecutive Summary\r\nIn my last post about reverse engineering Windows containers, I outlined the internal implementation of Windows\r\nServer Containers. After further investigating Windows Server Containers, I learned that running any code in these\r\ncontainers should be considered as dangerous as running admin on the host. These containers are not designed for\r\nsandboxing, and I found that escaping them is easy. Microsoft collaborated with us in fully understanding the\r\nsecurity limitations of these containers. The purpose of this post is to raise awareness of the danger of running\r\nthese containers.\r\nTo demonstrate this issue, I will present a container escape technique in Windows containers that I recently\r\ndiscovered. The technique allows processes running inside containers to write files to their host. This could be\r\nleveraged to achieve RCE (remote code execution) on the host. In Kubernetes environments, this exploit could be\r\neasily leveraged to spread between nodes. In other words, an attacker that successfully breaches a single\r\napplication instance running inside a Windows Server Container could trivially breach the boundaries of the\r\ncontainer and access other applications on the same machine. In the case of Kubernetes, the attacker could even\r\naccess other machines. This may allow an attacker to gain access to a complete production workload after\r\nbreaching just one endpoint instance.\r\nThis issue may affect users of cloud providers allowing the use of Windows Server Containers, including all of\r\nMicrosoft’s AKS users using Windows. Palo Alto Networks customers are protected from this via Prisma™\r\nCloud.\r\nWindows Server Containers\r\nAs revealed in more depth in my previous post, Microsoft developed two solutions for running Windows-based\r\ncontainers. The first solution is running each container inside a virtual machine (VM) based on HyperV\r\ntechnology. The second option, Windows Server Containers, rely on Windows kernel features, such as Silo\r\nobjects, to set up containers. The latter solution resembles traditional Linux implementation for containers, i.e.\r\nprocesses that are run on the same kernel with logical mechanisms to isolate each from another.\r\nSome users rely on Windows Server Containers, as opposed to HyperV containers, since running each container\r\ninside a VM comes at a performance cost, as documented by Microsoft:\r\n\"The additional isolation provided by Hyper-V containers is achieved in large part by a hypervisor layer of\r\nisolation between the container and the container host. This affects container density as, unlike Windows Server\r\nhttps://unit42.paloaltonetworks.com/windows-server-containers-vulnerabilities/\r\nPage 1 of 8\n\nContainers, less sharing of system files and binaries can occur, resulting in an overall larger storage and memory\r\nfootprint. In addition there is the expected additional overhead in some network, storage io, and CPU paths.\"\r\nMy research has led me to believe that the security of Windows Server Containers can be better documented.\r\nThere are references indicating that the use of HyperV containers is more secure, but I could not find a piece of\r\ndocumentation that clearly mentions that Windows containers are susceptible to a breakout. When we reached out\r\nto Microsoft, their guidance was recommending users not run anything in a Windows Server Container that they\r\nwouldn’t be willing to run as an admin on the host. They also noted:\r\n\"Windows Server Containers are meant for enterprise multi-tenancy. They provide a high degree of isolation\r\nbetween workloads, but are not meant to protect against hostile workloads. Hyper-V containers are our solution\r\nfor hostile multi-tenancy.\"\r\nIn the following sections, I will go through the details of the problem, including kernel internals of Windows\r\nsymbolic links. Some background in Windows container internals, including Silos, as explained in my previous\r\npost, is recommended for better understanding of the proposed technique.\r\nThe Escape\r\nWindows symbolic link resolution from inside a container supports the use of an undocumented flag that causes\r\nsymbolic links to be resolved on the root directory of the host machine. That is, outside the container file system.\r\nWhile container processes should require special privileges to enable that flag, I found a technique to escalate\r\nprivileges from a default container process that would result in this escape.\r\nIn the following sections, I will take you through the journey of how I discovered this technique and elaborate the\r\nreasons it was made possible.\r\nSymbolic Links\r\nSymbolic links in Windows aren’t well-documented, but they have been around since Windows NT. Windows NT\r\ncame out with two types of symbolic links: object manager symbolic links and registry key symbolic links. These\r\nwere not file-related, only an internal implementation of the operating system Microsoft chose to use. Only in\r\nWindows 2000 did file system symbolic links come out, and even those weren’t file-level symbolic links. They\r\nworked only as directory redirection. It was Windows Vista that first featured full file-level symbolic links. In this\r\npost, I will only cover object manager symbolic links. The others are outside the scope of this article.\r\nObject Manager Symbolic Links\r\nIf you’re using Windows at all, you are probably using these without even knowing it. Things like the C drive\r\nletter are actually implemented using object manager symbolic links. Under the hood, when one accesses C:\\ the\r\nobject manager redirects the call to the actual mounted device.\r\nhttps://unit42.paloaltonetworks.com/windows-server-containers-vulnerabilities/\r\nPage 2 of 8\n\nFigure 1. WinObj showing C: is just a symbolic link\r\nThe object manager handles not only files, but also registry, semaphores and many more named objects. When a\r\nuser tries to access C:\\secret.txt, the call arrives to the kernel function NtCreateFile with the path \\??\\C:\\secret.txt,\r\nwhich is an NT path that the kernel knows how to work with. The path is modified by user-mode Windows API\r\nbefore the actual system call occurs. The reason for this path conversion is the \\??\\ part, which points the kernel to\r\nthe correct directory in the root directory manager. Said directory will hold the target of the C: symbolic link.\r\nEventually ObpLookupObjectName is called. ObpLookupObjectName’s job is to resolve an actual object from a\r\nname. This function uses another kernel function, ObpParseSymbolicLinkEx, to parse part of the path, which is a\r\nsymbolic link to its target.\r\nEvery part of the path is checked for being a symbolic link. This check is performed by\r\nObpParseSymbolicLinkEx. The object manager iterates until it finds a leaf node, which is something that cannot\r\nbe parsed any further by the object manager. If part of the path is a symbolic link, the function returns\r\nSTATUS_REPARSE or STATUS_REPARSE_OBJECT and changes the relevant part of the path to the target of\r\nthe symbolic link.\r\nFigure 2. WinDbg showing the call stack of a CreateFile API\r\nAfter all of this, our C:\\secret.txt was parsed to its actual path, which will look something like\r\n\\Device\\HarddiskVolume3\\secret.txt. The \\Device\\HarddiskVolume3 path will be opened under the root directory\r\nobject (ObpRootDirectoryObject).\r\nMore About the Root Directory Object\r\nThe object manager root directory object is like a folder that contains all application-visible named objects (like\r\nfiles, registry keys and more). This mechanism allows applications to create and access these objects among\r\nthemselves.\r\nhttps://unit42.paloaltonetworks.com/windows-server-containers-vulnerabilities/\r\nPage 3 of 8\n\nThe Important Part\r\nWhen accessing a file from inside a container, everything is parsed under a custom root directory object. When C:\r\nis parsed, it will be parsed against a clone C: symbolic link that will point it to a virtual mounted device and not\r\nthe host’s file system.\r\nSymbolic Links and Containers\r\nI decided to follow the lookup process of a symbolic link from inside a container. A process inside a container\r\ncalls CreateFile with the target file being C:\\secret.txt. This path is transferred to \\??\\C:\\secret.txt before getting to\r\nthe kernel, as I explained earlier. Under the custom root directory object of the container, the system accesses ??,\r\nwhich is a reference to GLOBAL??. The system searches for a symbolic link C: under the GLOBAL?? directory\r\nand indeed finds such a symbolic link. At this point, the path is parsed to the target of said symbolic link, which in\r\nthis case is \\Device\\VhdHardDisk{a36fab63-7f29-4e03-897e-62a6f003674f}\\secret.txt. The kernel now proceeds\r\nto open said VhdHardDisk{...} device, but instead of searching this device under the Device folder in the root\r\ndirectory object of the host, it searches this device under the custom root directory object of the container and\r\nfinds the virtual device of the container’s file system.\r\nFigure 3. WinObj showing how a path is parsed under the root directory object\r\nBut something wasn’t right. When I browsed the Device folder under \\Silos\\1588\\ I was expecting to find an\r\nobject named VhdHardDisk{...} pointing to an actual device, but instead there was a symbolic link with the same\r\nname pointing to \\Device\\VhdHardDisk{...}. What was going on? How does Windows get to the actual device? At\r\nthis point, I started researching the symbolic link lookup subject until I found a single line in slides from a talk by\r\nsecurity researchers Alex Ionescu (CrowdStrike) and James Forshaw (Google Project Zero) at Recon 2018\r\nmentioning there is a flag for a “global” symbolic link. I proceeded to reverse the relevant functions in order to\r\nfind where this flag might be checked.\r\nI eventually found a branch in ObpLookupObjectName that looked promising:\r\nhttps://unit42.paloaltonetworks.com/windows-server-containers-vulnerabilities/\r\nPage 4 of 8\n\nFigure 4. A branch in IDA that looked promising\r\nThe register edi holds the return value of ObpParseSymbolicLinkEx, so I searched this value – 368h – and found\r\nout it stands for STATUS_REPARSE_GLOBAL. So if ObpParseSymbolicLinkEx returns\r\nSTATUS_REPARSE_GLOBAL, the object manager opens the file under ObpRootDirectoryObject, which is the\r\nregular root directory object, instead of getting the root directory of the Silo.\r\nThe Problem\r\nAt this point, I was certain I understood this behavior. I thought that creating a global symbolic link requires some\r\nspecial permission only system processes have. At the creation time of the container, the creating process has these\r\nspecial permissions and can create global symbolic links for the container to use, but no process inside the\r\ncontainer can do that. The creating process controls what the global symbolic link points to and uses it only to\r\naccess some special devices like the VhdHardDisk, so there is no real problem. It turned out, that was only\r\npartially true.\r\nThe Real Problem\r\nI started searching for the value 368h that represents STATUS_REPARSE_GLOBAL in kernel code. After some\r\nwork with IDA and WinDbg I ended up in the function ObpParseSymbolicLinkEx, which led me to find the\r\nrelevant flag in the symbolic link object is at offset 28h (Object + 0x28). I placed a breakpoint in\r\nNtCreateSymbolicLinkObject, which is the function that creates a new symbolic link, and proceeded to create a\r\nnew container using Docker. This raised many breaks for every creation of a new symbolic link for the container.\r\nThis led me to the creation of the actual \\Silos\\1588\\Device\\VhdHardDisk{a36fab63-7f29-4e03-897e-62a6f003674f} object.\r\nA reminder: This was the symbolic link object that behaved like a global symbolic link. I ended up putting an\r\naccess breakpoint on the symbolic link object at offset 28h. Success! Right after the creation of the symbolic link,\r\nanother function tried to modify the memory where I placed the breakpoint. The function was\r\nNtSetInformationSymbolicLink. This function seemed to get a handle to a symbolic link, open the relevant object\r\nand change things in it.\r\nhttps://unit42.paloaltonetworks.com/windows-server-containers-vulnerabilities/\r\nPage 5 of 8\n\nLuckily, this also got a wrapper function with the same name in ntdll, so we can easily call it from user mode. I\r\nreverse engineered this function and found a part of the code that checks for Tcb privilege in it. Tcb stands for\r\nTrusted Computing Base and its privileges description is, “Act as part of the operating system.”\r\nI reversed ObpParseSymbolicLinkEx just enough to understand under what conditions it returns\r\nSTATUS_REPARSE_GLOBAL as well as the exact parameters NtSetInformationSymbolicLink requires in order\r\nto change a symbolic link to make it global. These parameters are deliberately omitted from this post to make it\r\nharder for attackers to create an exploit.\r\nExploitation Plan\r\nKnowing that I may be able to enable this global flag with Tcb privileges, and that it may allow for a container\r\nescape, I came up with the following plan to escape a container’s file system:\r\n1. Create a symbolic link for the host’s C: drive.\r\n2. Gain Tcb privileges.\r\n3. Make said symbolic link global.\r\n4. Access files on the host’s file system.\r\nThe only part missing from my plan was how to accomplish step two. We don’t have Tcb privileges in the\r\ncontainer, do we? Well, our container processes do not have Tcb privileges by default. However, there is a special\r\nprocess in Windows containers called CExecSvc. This process is in charge of many aspects of the container\r\nexecution, including communication between the host and the container. It also has Tcb privileges, so if a\r\ncontainer process could execute code through CExecSvc, it would run with Tcb privileges, and the plan could\r\nunfold.\r\nFigure 5. ProcessHacker showing CExecSvc has SeTcbPrivilege\r\nExecution\r\nhttps://unit42.paloaltonetworks.com/windows-server-containers-vulnerabilities/\r\nPage 6 of 8\n\nI chose to do a simple DLL injection to CExecSvc, which included the attack logic. This worked well, and I was\r\nimmediately able to gain access to the host’s file system. Because CExecSvc is a system process, I gained full,\r\nunbounded access to the entire host file system, exactly as any other system process has.\r\nAzure Kubernetes Service (AKS)\r\nAzure Kubernetes Service (AKS) is a managed container orchestration service, based on the open-source\r\nKubernetes system, which is available on Microsoft Azure Public Cloud. An organization can use AKS to deploy,\r\nscale and manage Docker containers and container-based applications across a cluster of container hosts.\r\nAKS uses Windows Server Containers for each pod, meaning every single Kubernetes cluster that has a Windows\r\nnode is vulnerable to this escape.\r\nNot only that, but once an attacker gains access to one of the Windows nodes, it is easy to spread to the rest of the\r\ncluster.\r\nThe following image shows that the Windows node has everything we need in order to control the rest of the\r\ncluster. This displays the situation after we managed to access the host (in this case, the node) from the container\r\n(in this case, the pod).\r\nFigure 6. Everything we need inside the Windows node\r\nFrom here, one can just use kubectl to control the rest of the cluster.\r\nFigure 7. Using kubectl from inside the node\r\nhttps://unit42.paloaltonetworks.com/windows-server-containers-vulnerabilities/\r\nPage 7 of 8\n\nConclusion\r\nIn this post, I have demonstrated a complete technique to escalate privileges and escape Windows Server\r\nContainers. Users should follow Microsoft’s guidance recommending not to run Windows Server Containers and\r\nstrictly use Hyper-V containers for anything that relies on containerization as a security boundary. Any process\r\nrunning in Windows Server Containers should be assumed to be with the same privileges as admin on the host. In\r\ncase you are running applications in Windows Server Containers that need to be secured, we recommend moving\r\nthese applications to Hyper-V containers.\r\nI would like to thank Alex Ionescu and James Forshaw for advising me with this research.\r\nPalo Alto Networks Prisma™ Cloud protects customers from having their containers compromised. Prisma Cloud\r\nCompute also provides a compliance feature called Trusted Images that allows restricting users to run only known\r\nand signed images. By using this feature, customers can further reduce the attack surface by preventing execution\r\nof malicious images.\r\nSource: https://unit42.paloaltonetworks.com/windows-server-containers-vulnerabilities/\r\nhttps://unit42.paloaltonetworks.com/windows-server-containers-vulnerabilities/\r\nPage 8 of 8",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE"
	],
	"references": [
		"https://unit42.paloaltonetworks.com/windows-server-containers-vulnerabilities/"
	],
	"report_names": [
		"windows-server-containers-vulnerabilities"
	],
	"threat_actors": [],
	"ts_created_at": 1775434309,
	"ts_updated_at": 1775791241,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/d96ba5db174fa7f6114df84bc65826341b4f9f24.pdf",
		"text": "https://archive.orkl.eu/d96ba5db174fa7f6114df84bc65826341b4f9f24.txt",
		"img": "https://archive.orkl.eu/d96ba5db174fa7f6114df84bc65826341b4f9f24.jpg"
	}
}