AnyConnect Elevation of Privileges, Part 2

AnyConnect Elevation of Privileges, Part 2

In the previous part of this multi-part article, I explained how I reversed engineered one of the binaries of the Cisco AnyConnect (CAC) Secure Mobility Client. This allowed me to understand the header format of the network packets used in the Inter-Process Communication (IPC) mechanism. In this part, I will focus on doing a more dynamic analysis in order to understand what goes in the packet body.

To understand the packet body content (i.e. the data returned by the getDataBuffer function in the CIpcMessage class), instead of continuing with the reverse engineering effort (by focusing on the CLaunchClientAppTlv class), I decided to do a dynamic analysis.

Instead of capturing the network traffic in a traditional manner, I decided to use API Monitor. This allows for the interception of the network traffic but also the calls to the various functions that could be involved in the IPC mechanism. To do that, I enabled the interception of:

  • The WSASend and WSARecv functions.
  • All the functions of the CProcessApi class.
  • All of the TLV classes member functions.

Then, I attached to the vpnagent.exe process and by using the vpnui.exe client to initiate a connection to VPN endpoint, I was able to obtain the following capture.

API Monitor capture
The captured functions calls while connecting.

From the “Monitored Processes” list, it is clear that at some point in the connection process, the vpndownloader.exe executable is launched (also referenced in the Google Project Zero advisory). Searching in the capture for CLaunchClientAppTlv revealed a nice sequence of function calls that are clearly related with that launch.

API Monitor capture detail
Details of the packet header received.
API Monitor capture detail
Details of the packet body received.

Since the calling thread is the same, it is clear that these functions are executed sequentially. This is what happened:

  • The first 26 bytes of the packet are received and validated by vpnagent.exe.
  • If the validation is successful, the rest of the packet is read from the socket.
  • A CLaunchClientAppTlv class is instantiated and used to obtain the parameters necessary to launch the application.
  • A CProcessApi class is instantiated to launch the application.

Mapping the received packet header to the CIpcMessage fields results in the following.

 1class CIpcMessage {
 2public:
 3    /* Offset 0x00 */ unsigned long idTag;              // 0x4F, 0x43, 0x53, 0x43
 4    /* Offset 0x04 */ unsigned short headerLength;      // 0x1A, 0x00
 5    /* Offset 0x06 */ unsigned short dataLength;        // 0xB3, 0x01
 6    /* Offset 0x08 */ IIpcResponseCB * ipcResponseCB;   // 0xFF, 0xFF, 0xFF, 0xFF
 7    /* Offset 0x0c */ void * msgUserContext;            // 0x00, 0x00, 0x00, 0x00
 8    /* Offset 0x10 */ unsigned long requestMsgId;       // 0x02, 0x00, 0x00, 0x00
 9    /* Offset 0x14 */ void * returnIpcObject;           // 0x00, 0x00, 0x00, 0x00
10    /* Offset 0x18 */ unsigned char messageType;        // 0x01
11    /* Offset 0x19 */ unsigned char messageId;          // 0x02
12
13    // (...)
14}
 Notice how the headerLength value (26 bytes) matches the received header data length and how the dataLength value (430 bytes) matches the received body length.

Taking into consideration the fact that the packet body is of a Type, Length, Value (TLV) format and using as an example a packet generated by the Google POC, it is easy to extract the various entries. However, the second to last entry doesn’t seem to follow the same format as the others and is missing from the packet generated by Google POC.

 1// Type 0 (string), index 1
 200 01
 3// Length (87 bytes)
 400 57
 5// Value (C:\Program Files (x86)\Cisco\Cisco AnyConnect Secure Mobility Client\vpndownloader.exe)
 643 3a 5c 50 72 6f 67 72 61 6d 20 46 69 6c 65 73
 720 28 78 38 36 29 5c 43 69 73 63 6f 5c 43 69 73
 863 6f 20 41 6e 79 43 6f 6e 6e 65 63 74 20 53 65
 963 75 72 65 20 4d 6f 62 69 6c 69 74 79 20 43 6c
1069 65 6e 74 5c 76 70 6e 64 6f 77 6e 6c 6f 61 64
1165 72 2e 65 78 65 00
12
13// Type 0 (string), index 2
1400 02
15// Length (240 bytes)
1600 f0
17// Value ("CAC-move.C:\Users\Reverse\AppData\Local\Temp\20602.tmp\configuration.xml.C:\ProgramData\Cisco\Cisco AnyConnect Secure Mobility Client\Network Access Manager\newConfigFiles\configuration.xml.76F3A9141D1B4D2B1063953372AC720F0069F036.sha1.0")
1822 43 41 43 2d 6d 6f 76 65 09 43 3a 5c 55 73 65
1972 73 5c 52 65 76 65 72 73 65 5c 41 70 70 44 61
2074 61 5c 4c 6f 63 61 6c 5c 54 65 6d 70 5c 32 30
2136 30 32 2e 74 6d 70 5c 63 6f 6e 66 69 67 75 72
2261 74 69 6f 6e 2e 78 6d 6c 09 43 3a 5c 50 72 6f
2367 72 61 6d 44 61 74 61 5c 43 69 73 63 6f 5c 43
2469 73 63 6f 20 41 6e 79 43 6f 6e 6e 65 63 74 20
2553 65 63 75 72 65 20 4d 6f 62 69 6c 69 74 79 20
2643 6c 69 65 6e 74 5c 4e 65 74 77 6f 72 6b 20 41
2763 63 65 73 73 20 4d 61 6e 61 67 65 72 5c 6e 65
2877 43 6f 6e 66 69 67 46 69 6c 65 73 5c 63 6f 6e
2966 69 67 75 72 61 74 69 6f 6e 2e 78 6d 6c 2e 37
3036 46 33 41 39 31 34 31 44 31 42 34 44 32 42 31
3130 36 33 39 35 33 33 37 32 41 43 37 32 30 46 30
3230 36 39 46 30 33 36 09 73 68 61 31 09 30 22 00
33
34// Type 80 (?), index 5
3580 05
36// Length (1 bytes ?)
3700 01
38// Value (?)
39
40// Type 0 (string), index 6
4100 06
42// Length (87 bytes)
4300 57
44// Value (C:\Program Files (x86)\Cisco\Cisco AnyConnect Secure Mobility Client\vpndownloader.exe)
4543 3a 5c 50 72 6f 67 72 61 6d 20 46 69 6c 65 73
4620 28 78 38 36 29 5c 43 69 73 63 6f 5c 43 69 73
4763 6f 20 41 6e 79 43 6f 6e 6e 65 63 74 20 53 65
4863 75 72 65 20 4d 6f 62 69 6c 69 74 79 20 43 6c
4969 65 6e 74 5c 76 70 6e 64 6f 77 6e 6c 6f 61 64
5065 72 2e 65 78 65 00

Going back to the functions call trace, and inspecting the disassembly of the called CLaunchClientAppTlv class member functions, it was possible to understand what each function maps to:

  • CLaunchClientAppTlv::GetRelocatableFilePath maps to entry at index 6.
  • CLaunchClientAppTlv::GetCmdLine maps to entry at index 2.
  • CLaunchClientAppTlv::GetGUIDesktop not used in the packet, but maps to a entry at index 4.
  • CLaunchClientAppTlv::GetUseInstalled maps to entry at index 5.
GetUseInstalled detail
Details of the GetUseInstalled disassembly.

Taking all of this into account, it was possible to understand the format of the entry at index 5.

1// Type 80 (boolean or short), index 5
280 05
3// Value (1, true)
400 01

This entry got me thinking: what if the value was zero? To answer this question, I added a after-call breakpoint on the WSARecv function so that when it was triggered, I could change this entry value to zero directly on the data received. This made the vpnagent.exe launch the vpndownloader.exe from the C:\ProgramData\Cisco\Cisco AnyConnect Secure Mobility Client\Temp\Downloader directory.

Exploit example runs
Exploit example runs.

Since this directory is world writable, and the vpndownloader.exe application is (still) vulnerable to DLL planting it is possible to escalate privileges in the same maner as it was in CVE-2015-4211 and CVE-2015-6305.

 The DLL must be named dbghelp.dll. After this DLL is loaded, the vpndownloader.exe restricts the LoadLibrary search path.

The Cisco advisory can be found here and I have hosted the proof of concept source code based on this research in this repository. Cheers x)