
### TL;DR
Docker Desktop for Windows suffers from a privilege escalation vulnerability to SYSTEM. The core of the issue lies with the fact that the Docker Desktop Service, the primary Windows service for Docker, communicates as a client to child processes using named pipes.
The high privilege Docker Desktop Service can be tricked into connecting to a named pipe that has been setup by a malicious lower privilege process. Once the connection is made, the malicious process can then impersonate the Docker Desktop Service account (SYSTEM) and execute arbitrary system commands with the highest level privileges.
Here's a video of the PoC in action:
The vulnerability has been assigned [CVE-2020-11492](<https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11492>) and the latest Docker Desktop Community and Enterprise have fixed the issue.
When Docker Desktop for Windows is installed, a Windows service called Docker Desktop Service is installed. This service is always running by default, waiting idly by for the Docker Desktop application to start.
Once the Docker Desktop application is started many other child processes are created, which allow the management of various Docker behaviours from docker image creation to watchdog processes.
Here’s a typical Docker Desktop process tree after launch:

### Pipe Dream
When applications launch child processes, it is not uncommon to use Windows named pipes as a form of inter process communication (IPC). Like TCP/IP, named pipes offer the ability to send and receive data down the pipe with application specific data. Named pipes also work over the network too. But in Dockers case, it connects to named pipes on the same machine as a form of IPC between the child processes.
Now this is where it starts to get interesting. Named pipes have a unique feature that allow the server side of the connection to impersonate the client account who is connecting. Why does this exist? Well it’s quite simple. Many services that are running on Window are offering functionality to users of the machine, be it local or remote. The impersonation functionality allows the service to drop its credentials in favour of the connecting client. When files or other various restricted operating system functionality is requested, the action is performed under the impersonated account and not the service account that the process was launched under.
### First Impressions are Important
Impersonation is not something any standard user account can perform, it’s a special privilege that must be assigned to accounts. The specific right is called “Impersonate a client after authentication” and is part of the User Rights Assignment item within Group Policy Editor.
Here is the list of accounts by default that have the impersonate privileges enabled.
• Administrators
• Local Service
• Network Service
• IIS AppPool Account
• Microsoft SQL Server Account
• Service
Many of the accounts above are designed to be limited accounts with minimal privileges, for example Network Service has very little access to a machine’s resources. Same goes for the IIS AppPool accounts, these are usually used when serving applications from IIS web apps. The last one is the most interesting though. This is what the description in Group Policy Editor has for the built-in Service group.

Note: By default, services that are started by the Service Control Manager have the built-in Service group added to their access tokens. Component Object Model (COM) servers that are started by the COM infrastructure and that are configured to run under a specific account also have the Service group added to their access tokens. As a result, these services get this user right when they are started.

Yes, you have read that correctly. Anything started by the Service Control Manager will automatically get the impersonation privilege, no matter which account is used to start the service.
### Impersonate the Dockmaster
OK, back to Docker. As mentioned above, when the Docker Desktop app starts, it spawns a bunch of child processes. When these are launched the main Docker service is expecting the child processes to create the named pipes for IPC purposes. The high privilege service will then connect to these named pipes as the **client** and is not serving them. So, if a malicious piece of code can execute under the context of a process with impersonate privileges, it can setup a pipe called \\\\.\pipe\dockerLifecycleServer and wait for it to connect.
The PoC waiting for connection:

OK I hear some of the sceptical out there: “But you need Administrator rights to create such a service”. Well let’s say you happen to be hosting a vulnerable IIS Web Application on the same machine as Docker for Windows. This could be one example of a successful attack vector. The initial attack vector could utilise a vulnerability in the web application to perform code execution under the limited IIS App Pool account. Once that is achieved, our special Docker named pipe can be setup to perform the privilege escalation to SYSTEM.
### Stealing the Ship
Once the pipe is listening, it’s just a matter of waiting for Docker Desktop to be started and connect to our malicious pipe. Once docker is connected, we impersonate the connecting client, which is SYSTEM, and launch a new process using the CreateProcessWithTokenW API.
Command prompt launched as SYSTEM:

### Disclosure and Fix
When initially disclosing, Docker denied that the vulnerability even existed. Their stance was that impersonation is a Windows feature and that we should speak to Microsoft.
Whilst impersonation is certainly a Windows feature, when developing SYSTEM services that use named pipes as a client, it’s the developer’s responsibility to ensure that impersonation is disabled if such a feature is not needed. Unfortunately, the default behaviour when opening a named pipe as a client is to enable impersonation, which means the behaviour is often missed and overlooked.
After a few emails back and forth, then finally submitting a working PoC, Docker did agree that it was a security vulnerability and as such have now issued a fix. When the Docker service process connects to the named pipes of spawned child processes it now uses the SecurityIdentification impersonation level. This will allow the server end of the pipe to get the identity and privileges of the client but not allow impersonation.
* 25th March 2020 – Details sent to Docker security team
* 25th March 2020 – Docker respond and do not consider it a security issue
* 26th March 2020 – Additional clarification sent to Docker to describe the risk
* 26th March 2020 – Docker respond suggesting we speak to Microsoft
* 26th March 2020 – Further clarification sent, describing Docker connecting as a named pipe client and not a server
* 26th March 2020 – Docker respond indicating that it will now be looked at by the development team
* 30th March 2020 – Requested a status update on whether Docker will be treating the report as a vulnerability
* 1st April 2020 – Docker respond, indicating that the report is still under discussion, but requested the PoC code. PoC code sent.
* 1st April 2020 – Docker attempt to run exploit using standard account without SeImpersonatePrivilege and indicate exploit failed.
* 1st April 2020 – Instructions sent on how to run the PoC with an account that has SeImpersonatePrivilege
* 1st April 2020 – Docker confirm it will be treated as a vulnerability
* 2nd April 2020 – Fixed pushed to Edge release of Docker and CVE-2020-11492 assigned
* 11th May 2020 – Docker release 2.3.0.2 which includes the fix for CVE-2020-11492
{"id": "PENTESTPARTNERS:062566C3D6E8251ED4010F1CF0EBD564", "type": "pentestpartners", "bulletinFamily": "blog", "title": "Docker Desktop for Windows PrivEsc (CVE-2020-11492)", "description": "\n\n### TL;DR\n\nDocker Desktop for Windows suffers from a privilege escalation vulnerability to SYSTEM. The core of the issue lies with the fact that the Docker Desktop Service, the primary Windows service for Docker, communicates as a client to child processes using named pipes.\n\nThe high privilege Docker Desktop Service can be tricked into connecting to a named pipe that has been setup by a malicious lower privilege process. Once the connection is made, the malicious process can then impersonate the Docker Desktop Service account (SYSTEM) and execute arbitrary system commands with the highest level privileges.\n\nHere's a video of the PoC in action:\n\nThe vulnerability has been assigned [CVE-2020-11492](<https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11492>) and the latest Docker Desktop Community and Enterprise have fixed the issue.\n\nWhen Docker Desktop for Windows is installed, a Windows service called Docker Desktop Service is installed. This service is always running by default, waiting idly by for the Docker Desktop application to start.\n\nOnce the Docker Desktop application is started many other child processes are created, which allow the management of various Docker behaviours from docker image creation to watchdog processes.\n\nHere\u2019s a typical Docker Desktop process tree after launch:\n\n\n\n### Pipe Dream\n\nWhen applications launch child processes, it is not uncommon to use Windows named pipes as a form of inter process communication (IPC). Like TCP/IP, named pipes offer the ability to send and receive data down the pipe with application specific data. Named pipes also work over the network too. But in Dockers case, it connects to named pipes on the same machine as a form of IPC between the child processes.\n\nNow this is where it starts to get interesting. Named pipes have a unique feature that allow the server side of the connection to impersonate the client account who is connecting. Why does this exist? Well it\u2019s quite simple. Many services that are running on Window are offering functionality to users of the machine, be it local or remote. The impersonation functionality allows the service to drop its credentials in favour of the connecting client. When files or other various restricted operating system functionality is requested, the action is performed under the impersonated account and not the service account that the process was launched under.\n\n### First Impressions are Important\n\nImpersonation is not something any standard user account can perform, it\u2019s a special privilege that must be assigned to accounts. The specific right is called \u201cImpersonate a client after authentication\u201d and is part of the User Rights Assignment item within Group Policy Editor.\n\nHere is the list of accounts by default that have the impersonate privileges enabled.\n\n\u2022 Administrators \n\u2022 Local Service \n\u2022 Network Service \n\u2022 IIS AppPool Account \n\u2022 Microsoft SQL Server Account \n\u2022 Service\n\nMany of the accounts above are designed to be limited accounts with minimal privileges, for example Network Service has very little access to a machine\u2019s resources. Same goes for the IIS AppPool accounts, these are usually used when serving applications from IIS web apps. The last one is the most interesting though. This is what the description in Group Policy Editor has for the built-in Service group.\n\n\n\nNote: By default, services that are started by the Service Control Manager have the built-in Service group added to their access tokens. Component Object Model (COM) servers that are started by the COM infrastructure and that are configured to run under a specific account also have the Service group added to their access tokens. As a result, these services get this user right when they are started.\n\n\n\nYes, you have read that correctly. Anything started by the Service Control Manager will automatically get the impersonation privilege, no matter which account is used to start the service.\n\n### Impersonate the Dockmaster\n\nOK, back to Docker. As mentioned above, when the Docker Desktop app starts, it spawns a bunch of child processes. When these are launched the main Docker service is expecting the child processes to create the named pipes for IPC purposes. The high privilege service will then connect to these named pipes as the **client** and is not serving them. So, if a malicious piece of code can execute under the context of a process with impersonate privileges, it can setup a pipe called \\\\\\\\.\\pipe\\dockerLifecycleServer and wait for it to connect.\n\nThe PoC waiting for connection:\n\n\n\nOK I hear some of the sceptical out there: \u201cBut you need Administrator rights to create such a service\u201d. Well let\u2019s say you happen to be hosting a vulnerable IIS Web Application on the same machine as Docker for Windows. This could be one example of a successful attack vector. The initial attack vector could utilise a vulnerability in the web application to perform code execution under the limited IIS App Pool account. Once that is achieved, our special Docker named pipe can be setup to perform the privilege escalation to SYSTEM.\n\n### Stealing the Ship\n\nOnce the pipe is listening, it\u2019s just a matter of waiting for Docker Desktop to be started and connect to our malicious pipe. Once docker is connected, we impersonate the connecting client, which is SYSTEM, and launch a new process using the CreateProcessWithTokenW API.\n\nCommand prompt launched as SYSTEM:\n\n\n\n### Disclosure and Fix\n\nWhen initially disclosing, Docker denied that the vulnerability even existed. Their stance was that impersonation is a Windows feature and that we should speak to Microsoft.\n\nWhilst impersonation is certainly a Windows feature, when developing SYSTEM services that use named pipes as a client, it\u2019s the developer\u2019s responsibility to ensure that impersonation is disabled if such a feature is not needed. Unfortunately, the default behaviour when opening a named pipe as a client is to enable impersonation, which means the behaviour is often missed and overlooked.\n\nAfter a few emails back and forth, then finally submitting a working PoC, Docker did agree that it was a security vulnerability and as such have now issued a fix. When the Docker service process connects to the named pipes of spawned child processes it now uses the SecurityIdentification impersonation level. This will allow the server end of the pipe to get the identity and privileges of the client but not allow impersonation.\n\n * 25th March 2020 \u2013 Details sent to Docker security team\n * 25th March 2020 \u2013 Docker respond and do not consider it a security issue\n * 26th March 2020 \u2013 Additional clarification sent to Docker to describe the risk\n * 26th March 2020 \u2013 Docker respond suggesting we speak to Microsoft\n * 26th March 2020 \u2013 Further clarification sent, describing Docker connecting as a named pipe client and not a server\n * 26th March 2020 \u2013 Docker respond indicating that it will now be looked at by the development team\n * 30th March 2020 \u2013 Requested a status update on whether Docker will be treating the report as a vulnerability\n * 1st April 2020 \u2013 Docker respond, indicating that the report is still under discussion, but requested the PoC code. PoC code sent.\n * 1st April 2020 \u2013 Docker attempt to run exploit using standard account without SeImpersonatePrivilege and indicate exploit failed.\n * 1st April 2020 \u2013 Instructions sent on how to run the PoC with an account that has SeImpersonatePrivilege\n * 1st April 2020 \u2013 Docker confirm it will be treated as a vulnerability\n * 2nd April 2020 \u2013 Fixed pushed to Edge release of Docker and CVE-2020-11492 assigned\n * 11th May 2020 \u2013 Docker release 2.3.0.2 which includes the fix for CVE-2020-11492", "published": "2020-05-22T08:12:52", "modified": "2020-05-22T08:12:52", "cvss": {"score": 7.2, "vector": "AV:L/AC:L/Au:N/C:C/I:C/A:C"}, "href": "https://www.pentestpartners.com/security-blog/docker-desktop-for-windows-privesc-cve-2020-11492/", "reporter": "Ceri Coburn", "references": [], "cvelist": ["CVE-2020-11492"], "lastseen": "2020-06-12T13:39:43", "viewCount": 165, "enchantments": {"dependencies": {"references": [{"type": "cve", "idList": ["CVE-2020-11492"]}, {"type": "githubexploit", "idList": ["9A6DE86E-357D-59B1-86E7-862FED1401CB", "CA649A45-3FDC-55A5-B942-6D8E7BC9B120", "CCFAE296-9083-5FDF-A049-749D40E71469"]}, {"type": "pentestpartners", "idList": ["PENTESTPARTNERS:0CE448CD9B101D4DF6587A1287C4956E", "PENTESTPARTNERS:A79FCC6D5BF51246AEC5888BB3BE97F4"]}], "rev": 4}, "score": {"value": 0.8, "vector": "NONE"}, "backreferences": {"references": [{"type": "cve", "idList": ["CVE-2020-11492"]}, {"type": "githubexploit", "idList": ["9A6DE86E-357D-59B1-86E7-862FED1401CB", "CA649A45-3FDC-55A5-B942-6D8E7BC9B120", "CCFAE296-9083-5FDF-A049-749D40E71469"]}, {"type": "pentestpartners", "idList": ["PENTESTPARTNERS:A79FCC6D5BF51246AEC5888BB3BE97F4"]}, {"type": "threatpost", "idList": ["THREATPOST:17927669725548D065327CB145CA392F"]}]}, "exploitation": null, "vulnersScore": 0.8}, "immutableFields": [], "cvss2": {}, "cvss3": {}, "_state": {"dependencies": 1647589307, "score": 1659743467}}
{"githubexploit": [{"lastseen": "2021-12-10T14:45:54", "description": "# CVE-2020-11492\n\nProof-of-Concept (PoC) for Docker Desktop for ...", "cvss3": {"exploitabilityScore": 1.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "LOCAL", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 7.8, "privilegesRequired": "LOW", "vectorString": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", "userInteraction": "NONE", "version": "3.1"}, "impactScore": 5.9}, "published": "2020-05-27T18:11:18", "type": "githubexploit", "title": "Exploit for Improper Privilege Management in Docker Docker Desktop", "bulletinFamily": "exploit", "cvss2": {"severity": "HIGH", "exploitabilityScore": 3.9, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 7.2, "vectorString": "AV:L/AC:L/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "LOCAL", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2020-11492"], "modified": "2020-10-08T18:53:13", "id": "9A6DE86E-357D-59B1-86E7-862FED1401CB", "href": "", "cvss": {"score": 7.2, "vector": "AV:L/AC:L/Au:N/C:C/I:C/A:C"}, "privateArea": 1}, {"lastseen": "2022-07-13T17:52:16", "description": "# CVE-2020-11492\n\nProof-of-Concept (PoC) for Docker Desktop for ...", "cvss3": {"exploitabilityScore": 1.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "LOCAL", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "LOW", "baseScore": 7.8, "vectorString": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", "version": "3.1", "userInteraction": "NONE"}, "impactScore": 5.9}, "published": "2020-05-28T04:32:01", "type": "githubexploit", "title": "Exploit for Race Condition in Docker Docker Desktop", "bulletinFamily": "exploit", "cvss2": {"severity": "HIGH", "exploitabilityScore": 3.9, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 7.2, "vectorString": "AV:L/AC:L/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "LOCAL", "authentication": "NONE"}, "impactScore": 10.0, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2020-11492"], "modified": "2021-12-28T12:12:52", "id": "CA649A45-3FDC-55A5-B942-6D8E7BC9B120", "href": "", "cvss": {"score": 7.2, "vector": "AV:L/AC:L/Au:N/C:C/I:C/A:C"}, "privateArea": 1}, {"lastseen": "2021-12-10T14:46:15", "description": "# CVE-2020-11492\n\nProof-of-Concept (PoC) for Docker Desktop for ...", "cvss3": {"exploitabilityScore": 2.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "NETWORK", "availabilityImpact": "HIGH", "integrityImpact": "NONE", "baseScore": 8.1, "privilegesRequired": "NONE", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:H", "userInteraction": "REQUIRED", "version": "3.1"}, "impactScore": 5.2}, "published": "2020-06-13T05:26:11", "type": "githubexploit", "title": "Exploit for Insufficient Verification of Data Authenticity in Foxitsoftware Phantompdf", "bulletinFamily": "exploit", "cvss2": {"severity": "HIGH", "exploitabilityScore": 3.9, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 7.2, "vectorString": "AV:L/AC:L/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "LOCAL", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2020-11492", "CVE-2020-11493"], "modified": "2021-08-13T22:14:04", "id": "CCFAE296-9083-5FDF-A049-749D40E71469", "href": "", "cvss": {"score": 7.2, "vector": "AV:L/AC:L/Au:N/C:C/I:C/A:C"}, "privateArea": 1}], "cve": [{"lastseen": "2022-07-13T16:00:23", "description": "An issue was discovered in Docker Desktop through 2.2.0.5 on Windows. If a local attacker sets up their own named pipe prior to starting Docker with the same name, this attacker can intercept a connection attempt from Docker Service (which runs as SYSTEM), and then impersonate their privileges.", "cvss3": {"exploitabilityScore": 1.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "LOCAL", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "privilegesRequired": "LOW", "baseScore": 7.8, "vectorString": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", "version": "3.1", "userInteraction": "NONE"}, "impactScore": 5.9}, "published": "2020-06-05T14:15:00", "type": "cve", "title": "CVE-2020-11492", "cwe": ["CWE-362"], "bulletinFamily": "NVD", "cvss2": {"severity": "HIGH", "exploitabilityScore": 3.9, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 7.2, "vectorString": "AV:L/AC:L/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "LOCAL", "authentication": "NONE"}, "impactScore": 10.0, "acInsufInfo": false, "obtainUserPrivilege": false}, "cvelist": ["CVE-2020-11492"], "modified": "2022-07-12T17:42:00", "cpe": ["cpe:/a:docker:docker_desktop:2.2.0.5"], "id": "CVE-2020-11492", "href": "https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2020-11492", "cvss": {"score": 7.2, "vector": "AV:L/AC:L/Au:N/C:C/I:C/A:C"}, "cpe23": ["cpe:2.3:a:docker:docker_desktop:2.2.0.5:*:*:*:*:*:*:*"]}], "pentestpartners": [{"lastseen": "2020-09-02T11:54:13", "description": "\n\n### TL;DR\n\n[Citrix Workspace](<https://www.citrix.com/blogs/2018/06/12/citrix-workspace-app-everything-you-need-to-know/>) is vulnerable to a remote command execution attack running under the context of the SYSTEM account. By sending a crafted message over a named pipe and spoofing the client process ID, the Citrix Workspace Updater Service can be tricked into executing an arbitrary process under the SYSTEM account. Whilst a low privilege account is required to perform the attack, environments that do not implement SMB signing are particularly vulnerable since an attack can be achieved without knowing valid credentials through NTLM credential relaying.\n\nCitrix have assigned CVE-2020-8207 to the vulnerability and released updated versions for Workspace app. You can find the Citrix security bulletin [here](<http://support.citrix.com/article/CTX277662>).\n\n### Introduction\n\nCitrix Workspace app implements an automatic update feature via a service called Citrix Workspace Updater Service. The service runs under the SYSTEM context and sets up a named pipe called UpdaterServicePipe-800fad42-1d0f-4f66-8e18-8a0938cdc721 to allow the Citrix Workspace app, which runs under the context of a regular user account to perform automatic and manual updates of the application.\n\n\n\n_Figure 1. UpdaterService listening for client connection_\n\nPeriodically the Citrix Workspace app will trigger the launch of a helper program called CitrixReceiverUpdater.exe. This process will communicate with the service to check for updates. This can also be triggered manually by using the system tray icon\u2019s \u201cCheck for Updates\u201d functionality. The helper process will connect to citrix.com and determine if a new update is available. When a new update is available, the process will notify the user that a new update can be installed.\n\nOnce the user confirms that they would like to proceed with the update, the helper will continue to download the installer and eventually communicate with the privileged service to request the installation. The service expects the following JSON message format;\n \n \n {\n \u00a0\u00a0\u00a0 \"MessageType\": 1,\n \u00a0\u00a0\u00a0 \"UpdateFilePath\": \"c:\\\\path\\\\to\\\\downloaded\\\\update.exe\",\n \u00a0\u00a0\u00a0 \"UpdateFileHash\": \"23819ab8d97\u2026\u202603764a34ebf53b002\",\n \u00a0\u00a0\u00a0 \"InstallationTriggerBehavior\": 0,\n \u00a0\u00a0\u00a0 \"CmdLineArguments\": \"updateargs\"\n }\n\nA MessageType of 1 signifies an install update request and an InstallationTriggerBehavior of 0 indicates to the service that the helper would like to launch the process without waiting for the result. The UpdateFileHash is the SHA256 hash of the executable we are requesting to run.\n\n### ID Please\n\nWith the JSON message now decoded it was time for the first attack attempt. Crafting a JSON message to point at an executable that we control. Armed with the payload, a Cobalt Strike beacon implant and a quick PoC, the attack was in place, but it failed. Clearly Citrix has implemented additional checks to prevent arbitrary process execution.\n\nDigger deeper into dnSpy it seemed that the service was interested in the PID of the client application that had connected to the named pipe.\n\n\n\n_Figure 2. .GetNamedPipeClientProcessId used to get client PID_\n\nThe service uses the native API call GetNamedPipeClientProcessId from kernel32.dll to retrieve the client PID. Once the PID is obtained, the process path for the connected client is determined and a SHA256 hash is generated. The hash is then compared with the SHA256 hash of the CitrixReceiverUpdater.exe to check if they match. This essentially validates that the client application connecting to the pipe is indeed CitrixReceiverUpdater.exe. If the hashes do not match, the update process would return with an error.\n\n### Camouflage\n\nNow that we know the service will refuse to execute any request from a client that is not CitrixReceiverUpdater.exe, it was time to attempt our second attack. By loading a DLL into the process that sends our crafted message, we can make the originating client appear from CitrixReceiverUpdater.exe, but with a JSON payload under our control.\n \n \n DWORD WINAPI CitrixExploit(LPVOID){\n \n \n char dbg[1024];\n char message[] = \u201c{JSON(truncated for brevity)}\u201d;\n DWORD amountWritten = 0;\n \n HANDLE hPipe = CreateFileA(\"\\\\\\\\.\\\\pipe\\\\UpdaterServicePipe-800fad42-1d0f-4f66-8e18-8a0938cdc721\", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);\n \n ConnectNamedPipe(hPipe, nullptr);\n \n WINBOOL result = WriteFile(hPipe, message, strlen(message), &amountWritten, nullptr);\n \n CloseHandle(hPipe);\n \n if(result){\n sprintf_s(dbg,1024, \"Result: %s\\n\",message);\n }else{\n sprintf_s(dbg,1024, \"Call failed with 0x%08x\\n\",GetLastError());\n }\n \n OutputDebugStringA(dbg);\n return DWORD(result);\n \n }\n \n BOOL WINAPI DllMain(HINSTANCE, DWORD fdwReason, LPVOID){\n \n switch(fdwReason){\n case DLL_PROCESS_ATTACH:{\n DWORD tid = 0;\n CreateThread(nullptr, 0, CitrixExploit, nullptr, 0, &tid);\n break;\n }\n }\n return true;\n }\n\nOnce the C code above is compiled into a DLL, we then needed a way to load said DLL into our hosting process, CitrixReceiverUpdater.exe. There are many ways this can be done, buy one such way is using ProcessHacker. No special privileges are required for such an operation since the process we are loading a DLL into is owned by the same unprivileged user that is performing the injection.\n\n\n\n_Figure 3. Using ProcessHancker to load our DLL_\n\nOnce the DLL was loaded by ProcessHacker, again, the attack failed. Back to the drawing board.\n\n### In Signing we Trust\n\nOnce again, it was time to dive into dnSpy to see what was going on. The service was no longer bailing on the client process check but continued to call a native function from CitrixReceiverUpdaterHelper.dll called ValidateUpdateBinaryHashAndSignature. There was a big clue in the name here, the function checks the hash of the update binary against the value within the JSON payload sent from the client, but additionally it also checks the Authenticode signature.\n\nWhat about the signature does it check? Does it make sure the binary has been signed by Citrix to prevent arbitrary file execution? Firing up IDA gave us the answer.\n\n\n\n_Figure 4. WinVerifyTrust called just to confirmed signing_\n\nA call to WinVerifyTrust is made with the WINTRUST_ACTION_GENERIC_VERIFY_V2 operation. All this does is determine if the executable has a valid signature and trusts the certificate chain. So, in theory, if we find a signed executable that can be used to launch other processes, we should achieve our goal. There are quite a few living of the land (LOL) executables that are capable of execution by proxy, but in our case, it needs to be signed too. Going through the awesome list from the lolbas project (<https://lolbas-project.github.io/#/execute>) I finally came across ScriptRunner.exe.\n \n \n Usage:\n ScriptRunner.exe\n -appvscript scriptFileName [Arguments] [-appvscriptrunnerparameters [-wait] [-timeout=<TimeInSeconds>] [-rollbackonerror]]\n -appvscript scriptFileName [Arguments] [-appvscriptrunnerparameters [-wait] [-timeout=<TimeInSeconds>] [-rollbackonerror]]\n ...\n Default values for -appvscriptrunnerparameters: No wait, No timeout, No rollback on error\n Every parameter must be separated by a unicode space character (U+0020)\n Example:\n ScriptRunner.exe -appvscript foo.cmd arg1 arg2 -appvscriptrunnerparameters -wait -timeout=30 -rollbackonerror -appvscript foobar.exe arg1 arg2\n\nArmed with this new knowledge, it was time to revisit the DLL code from earlier and update the JSON payload with the following;\n \n \n {\n \"MessageType\": 1,\n \"UpdateFilePath\": \"c:\\\\windows\\\\sysnative\\\\scriptrunner.exe\",\n \"UpdateFileHash\": \"23819ab8d976e7e1933832f35a735900364e40e3afcd98103764a34ebf53b002\",\n \"InstallationTriggerBehavior\": 0,\n \"CmdLineArguments\": \"c:\\\\windows\\\\sysnative\\\\scriptrunner.exe -appvscript c:\\\\windows\\\\system32\\\\cmd.exe\"\n }\n\nYou will notice that the path to scriptrunner.exe is using sysnative and not System32. The reason for this was due to the fact that scriptrunner.exe is only available as a 64-bit binary, so it was missing from the SysWOW64 folder. The updater service is a 32-bit process, so when using system32 in paths, this is automatically redirected to c:\\Windows\\SysWOW64. When using the special sysnative path, this will not do the automatic redirect and will reference the 64-bit binary from the real C:\\Windows\\System32 folder. This mess wasted a good hour to figure out what was going wrong.\n\nCompiling a new DLL with the above JSON payload and injecting the DLL once again using ProcessHacker resulted in the following privileged shell;\n\n\n\n_Figure 5. SYSTEM level command prompt._\n\nThe process is launched within the session of the application that triggered the update, which is the CitrixReceiverUpdater.exe program.\n\n### Path to RCE\n\nThen it got me thinking, named pipes by their nature can be connected to remotely too.\n\nNamed pipes is a Microsoft technology that supports a data channel similar to a TCP socket. Named pipes can be accessed locally or remotely and typically sit on top of the SMB protocol on port 445. A unique feature of named pipes in comparison to TCP is that a pipe connection supports DACL\u2019s out of the box, limiting what users are permitted to read or write to the pipe.\n\nAs the name suggests, addressing a pipe is through a unique name when the pipe is created on the server side, similar to a listening TCP port like 443 or 80. Another unique feature of pipes allows the server to impersonate the client user. Usually this is to drop client privileges, but sometimes this can be abused to increase privileges like the [PrivEsc vuln we found in Docker](<https://www.pentestpartners.com/security-blog/docker-desktop-for-windows-privesc-cve-2020-11492/>) not so long ago.\n\nQuite often the server side of a named pipe is implemented within high privilege services. Usually as an aid to lower privilege processes from the same software vendor. An example could be to perform a high privileged action without the need for the low privileged user to elevate or need administrative credentials as seen above with Citrix Workspace app.\n\nBut there are a few hurdles that we need to overcome before we can attempt this attack remotely. The first is permissions. The default permissions applied to a newly created pipe grant full control to SYSTEM, Administrators and the owner. In this case, since the pipe is created by the high privilege service, the owner is also SYSTEM. The default ACL would not permit a connection from a normal user unless you are an Administrator or SYSTEM. Since our EoP above was using a limited account, the permissions must have been updated. Again, we can use ProcessHacker to look at the ACL\u2019s for the pipe.\n\n\n\nFigure 6. Everyone allowed to connect to the pipe\n\nOh dear, it seems the permissions for the pipe have been updated to allow Everyone to connect. Luckily since XP SP2 the Everyone group no longer includes Anonymous connections, but any authenticated user, guest or built-in account can connect to this pipe remotely. Hurdle number one solved.\n\nThe second hurdle is the PID of the connecting process. The curious cat in me started investigating where the source of the PID comes from. After some digging around I came across James Forshaw\u2019s excellent blog post on [named pipe client PID spoofing.](<https://googleprojectzero.blogspot.com/2019/09/windows-exploitation-tricks-spoofing.html>) After reading through the blog post it seems using built in Windows API\u2019s, it is no longer possible to spoof PID\u2019s due some previous CVE\u2019s being fixed. James\u2019s article also asks the question on whether impacket can be used for this purpose instead. Well, as it turns out, you can.\n\nDepending on whether the client is connecting to the server over SMBv1 or SMBv2/3 will determine how we need to patch the SMB packet being submitted. SMBv1 has a specific field allocated for the PID as you can see below:\n\n\n\nThe story is a little different for SMBv2/3 as it seems Microsoft deprecated its use and marked the field as Reserved. Lucky for us, even though it\u2019s reserved, it is still treated as the PID of the process sending/receiving the SMB packet. Here is the SMBv2 Header:\n\n\n\nNow that we know where we need to patch these values, we need to figure out how to patch them within impacket. Digging around within impackets smb.py I came across the sendSMB function. Below you will find impackets default implementation of sendSMB for an SMB1 packet:\n\n\n\nImpackets default implementation for SMBv1 actually fills in the PID of the process prior to submitting the packet, so for us to patch this value we need to hook the smb.getData function so that we can update the PID before submitting the packet.\n\nThe equivalent function for an SMBv2/3 packet is implemented inside smb3.py. For these types of packets the hook can be done on sendSMB itself, since the field is marked as Reserved it defaults to 0 and is not even set to anything inside sendSMB.\n\nMy python foo is not the best, I am far more comfortable in C++/C# land, but I finally came up with this python code:\n\n\n\nThe code essentially handles the hooks for SMBv1 packets getData function and SMBv2/3\u2019s sendSMB function. Also, we are only interested in overriding the PID on SMB2_CREATE/SMB_COMT_NT_CREATE_ANDX commands as these handle the opening of remote files and pipes over SMB. A quick test using the SMBConnection class from impacket with our hooks in place along with a spoofed PID of 0x666 resulted in the following packet captured by Wireshark. Wireshark even goes to the lengths of labelling the Reserved field as PID for us instead of Reserved:\n\n\n\n### NTLM relaying\n\nSince named pipes are opened over SMB connections, this also means they are vulnerable to NTLM relay attacks if the correct mitigations are not in place. Interestingly, there doesn\u2019t appear to be a generic attack built into ntlmrelayx for named pipes, so as part of this research I decided to implement a basic named pipe attack into ntlmrelayx that implements the PID spoofing functionality above.\n \n \n Named Pipe client options:\n --np-name NAME The name of the pipe to connect to\n --np-payload FILE Path to a file used as the payload\n --np-pid PID A specific client connection PID to use (cycle to 50000 is default)\n\nntlmrelayx.py has 3 new arguments that facilitate the attack mode along with a new np:// client protocol that can be used within the targets file or single target on the command line. Hopefully, most of the argument descriptions speak for themselves. The \u2013np-payload argument is a file that will be used for the payload of data that should be written to the pipe on successful connection. Generally, this would be specific to what the server is expected to read on the other side. In our case it will be the Citrix Workspace update JSON message.\n\nThis generic attack model only supports a single one-way message sent to the server. If the attack requires several message exchanges prior to the malicious message being transmitted, then you will need to customise the impacket/examples/ntlmrelayx/attacks/npattack.py file to your specific needs. If a PID is not specified using the \u2013np-pid argument, the default action will then be to connect to the pipe over and over by incrementing the PID by 4 each time (Windows PID\u2019s are multiples of 4) and resending the payload.\n\nCheck out [our impacket repo on GitHub](<https://github.com/pentestpartners/impacket>) with the named pipe attack mode implemented. You can also use the [NPAttackSample](<https://github.com/pentestpartners/npattacksample>) project which implements a vulnerable client/server model which you can attack with ntlmrelayx.\n\n### Demo\n\nI setup a demo in my lab consisting of 3 Windows 10 machines. All machines were running the vulnerable version of Citrix Workspace App with the pop-up dialog indicating a new version was available to download. Ironically, the time users are most vulnerable to a remote attack via NTLM relay will be now, since the upgraded version with the fix will start popping up on end user machines.\n\nLAB1-WIN10 was used as the victim host that ntlmrelayx would relay credentials from. LAB2-WIN10 and LAB3-WIN10 were setup as the relay targets. You will notice that the relay back to LAB1-WIN10 fails since Window 10 will not allow credential relaying back to itself.\n\nOnce the connection to the pipe succeeds using the relayed credentials, the attack begins cycling through PID\u2019s, sending the crafted JSON message with a powershell command that stages a Cobalt Strike beacon. When the PID from ntlmrelayx matches that of the CitrixReceiverUpdater.exe program running on the host, the powershell command is executed and a connection is made back to the Cobalt Strike server as SYSTEM.\n\nThe video has been shortened a little since the cycling part took a couple of minutes to complete.\n\n### Conclusion\n\nThe attack can be performed both locally and remotely. The vulnerability presented here is a remote attack since we are also leveraging relaying to demonstrate that no known credentials are needed either to exploit vulnerable named pipe servers that don\u2019t implement SMB signing.\n\nIf you are attacking locally you do not need ntlmrelayx. Just a simple impacket script that uses SMBConnection with the customised hooks will work the same way to spoof PID\u2019s. You can use npattack.py as a reference. A local attack has the added benefit of process enumeration or even launching an instance of the process that will be validated by the server prior to the attack.\n\nAs a developer, what can you do to prevent such attacks?\n\nFirst things first, if your named pipe is only intended to be accesses from the local machine, make sure you add NT AUTHORITY\\NETWORK as a deny rule to the pipe\u2019s DACL. This will prevent the pipe from being accessed over the network. Alternatively, use ALPC or similar LPC interfaces that are designed for local communication only.\n\nSecondly, do not rely on the GetNamedPipeClientProcessId/GetNamedPipeServerProcessId API\u2019s for security validation since as this research demonstrates, it can be spoofed.\n\nThe post [Raining SYSTEM Shells with Citrix Workspace app](<https://www.pentestpartners.com/security-blog/raining-system-shells-with-citrix-workspace-app/>) first appeared on [Pen Test Partners](<https://www.pentestpartners.com/>).", "cvss3": {}, "published": "2020-07-21T15:27:38", "type": "pentestpartners", "title": "Raining SYSTEM Shells with Citrix Workspace app", "bulletinFamily": "blog", "cvss2": {}, "cvelist": ["CVE-2020-11492", "CVE-2020-8207"], "modified": "2020-07-21T15:27:38", "id": "PENTESTPARTNERS:A79FCC6D5BF51246AEC5888BB3BE97F4", "href": "https://www.pentestpartners.com/security-blog/raining-system-shells-with-citrix-workspace-app/", "cvss": {"score": 7.2, "vector": "AV:L/AC:L/Au:N/C:C/I:C/A:C"}}, {"lastseen": "2021-11-10T11:22:50", "description": "### \n\n### TL;DR\n\nBlackberry Cylance for Windows is affected by three vulnerabilities.\n\n * CVE-2021-32021 - Denial of service in message broker.\n * CVE-2021-32022 - Low privileged delete using CEF RPC server.\n * CVE-2021-32023 - Elevation of privilege in message broker.\n\nA heap overflow resulting in a denial of service, low privilege arbitrary file delete and an elevation of privilege from limited service accounts to SYSTEM.\n\nIt is recommended that the software is either upgraded to the latest 158x stream or version 1578 at the time of writing. Further information on the advisory can be found [here](<https://support.blackberry.com/kb/articleDetail?articleNumber=000088685>).\n\n### CVE-2021-32021 Denial of Service\n\nCylance uses an intricate message broker system where various components of the software communicate over RPC using the ALPC mechanism. ALPC by its very nature is designed for local inter-process communication only, therefore the vulnerability is limited to local attack only.\n\nThe software is predominantly written in .NET but there is a native DLL that handles the dispatch of RPC calls to different components called CefServer.dll, and in here lies the problem.\n\nThe RPC mechanism limits the request size to 64k, most likely due to certain RPC limits present within older generations of Windows. When Cylance is required to send a larger RPC message, a special token is sent over the RPC mechanism to indicate that the RPC message should be read from a local file on disk instead.\n\n\n\nIf the message starts with the string **</\\\\\\>** it is assumed the remaining part of the RPC message is a path to a local file on disk.\n\nThe bug it a type of TOCTOU (Time-Of-Check Time-Of-Use) class where the size of the allocated heap memory is not sufficient to hold the entire contents of the file.\n\n\n\nThe first call to **ReadFileMessage** passes a pointer to the **fileSize** variable. On its return, the variable is populated with the current **fileSize**. This is used to allocate the memory to store the file contents using **malloc**.\n\nThe second call to **ReadFileMessage** then proceeds to pass in the block of allocated memory for reading. At this point all is well. Unfortunately, the **ReadFileMessage** function will a second time determine the file size using the **GetFileSize** API instead of using the **fileSize** variable that was already determined on the previous call.\n\n\n\nThis results in a race condition. If the RPC message file increases in size between the first and second call to **ReadFileMessage**, the heap is overflowed when the **memcpy** function is called.\n\nThis will cause a denial of service within the Cylance service. The net result of this was no more detection of viruses or malware since the service was no longer operational.\n\n### CVE-2021-32022 Low Privilege Delete\n\nFollowing on from the large RPC message as described above for CVE-2021-32021, the **ReadFileMessage** function was found to delete the RPC message file once the contents had been consumed.\n\n\n\nNo checks were in place to determine if the path was outside of expected locations or if the message file was a valid RPC message first. Therefore, malicious actors can send an RPC message pointing to an arbitrary file on disk. Because the CefServer.dll is typically hosted inside the privileged Cylance service, the net result is that any file on the operating system that is not in use can be deleted by an unprivileged user.\n\n### CVE-2021-32023 - Elevation of privilege in message broker\n\nThe third and final vulnerability within the trio of issues is an elevation of privilege from a local unprivileged service account to SYSTEM. Similar to our previously reported impersonation abuse within [Docker Desktop](<https://www.pentestpartners.com/security-blog/docker-desktop-for-windows-privesc-cve-2020-11492/>), Cylance suffers the same fate.\n\nLike named pipes used in Docker\u2019s RPC communication mechanism, ALPC also supports impersonating the connecting client. This was able to be abused through registering a custom ALPC port through Cylance\u2019s publish and subscribe RPC messages.\n\nThe attack involves first sending a subscribe message indicating an endpoint id under the attacker\u2019s control. This internally within the Cylance service registers the custom endpoint as a valid message end point.\n\n\n\nOnce the custom ALPC endpoint is registered, the attacker can setup an ALPC port listening for incoming connections.\n\n\n\nFinally, the attacker can now publish a bogus message to the same ALPC port, utilising the message broker inside the Cylance service. On receipt of the message, the Cylance service (running as SYSTEM) will connect to the attacker controlled ALPC port to dispatch the message.\n\n\n\nIf the attacking process has the **SeImpersontePrivilege**, the SYSTEM token of the client can be impersonated, and the attacker can gain complete control of the machine.\n\n\n\n### Disclosure\n\nWhilst it took several months for Blackberry to implement the fixes to these issues, the experience of disclosing with them was pleasant. A call was setup after confirmation of the vulnerabilities to discuss the plan of action. We were kept informed on a fortnightly basis of progress throughout. Vendors, be more like Blackberry!\n\n * 4th May 2021 \u2013 Initial Disclosure\n * 14th May 2021 \u2013 Issues confirmed, and tickets lodged with development team\n * 26th May 2021 \u2013 Additional POC sent for CVE-2021-32023 to help replicate\n * 2nd June 2021 \u2013 Call with Cylance team to discuss fix timeline and disclosure plans\n * 4th August 2021 \u2013 Windows Agent v1580 release stream with all fixes implemented\n * 8th September 2021 \u2013 Window Agent v1578 release with all fixes implemented\n * 9th November 2021 \u2013 Coordinated disclosure\nThe post [Security Blog](<https://www.pentestpartners.com/security-blog/>) first appeared on [Pen Test Partners](<https://www.pentestpartners.com>).", "cvss3": {"exploitabilityScore": 1.8, "cvssV3": {"baseSeverity": "HIGH", "confidentialityImpact": "HIGH", "attackComplexity": "LOW", "scope": "UNCHANGED", "attackVector": "LOCAL", "availabilityImpact": "HIGH", "integrityImpact": "HIGH", "baseScore": 7.8, "privilegesRequired": "LOW", "vectorString": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", "userInteraction": "NONE", "version": "3.1"}, "impactScore": 5.9}, "published": "2021-11-10T06:55:33", "type": "pentestpartners", "title": "Pun-free Cylance vulnerability, fixed", "bulletinFamily": "blog", "cvss2": {"severity": "HIGH", "exploitabilityScore": 3.9, "obtainAllPrivilege": false, "userInteractionRequired": false, "obtainOtherPrivilege": false, "cvssV2": {"accessComplexity": "LOW", "confidentialityImpact": "COMPLETE", "availabilityImpact": "COMPLETE", "integrityImpact": "COMPLETE", "baseScore": 7.2, "vectorString": "AV:L/AC:L/Au:N/C:C/I:C/A:C", "version": "2.0", "accessVector": "LOCAL", "authentication": "NONE"}, "acInsufInfo": false, "impactScore": 10.0, "obtainUserPrivilege": false}, "cvelist": ["CVE-2020-11492", "CVE-2021-32021", "CVE-2021-32022", "CVE-2021-32023"], "modified": "2021-11-10T06:55:33", "id": "PENTESTPARTNERS:0CE448CD9B101D4DF6587A1287C4956E", "href": "https://www.pentestpartners.com/security-blog/pun-free-cylance-vulnerability-fixed/", "cvss": {"score": 7.2, "vector": "AV:L/AC:L/Au:N/C:C/I:C/A:C"}}]}