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:
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.
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.
Since the calling thread is the same, it is clear that these functions are executed sequentially. This is what happened:
CLaunchClientAppTlvclass is instantiated and used to obtain the parameters necessary to launch the application.
CProcessApiclass is instantiated to launch the application.
Mapping the received packet header to the
CIpcMessage fields results in the following.
headerLengthvalue (26 bytes) matches the received header data length and how the
dataLengthvalue (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.
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::GetRelocatableFilePathmaps to entry at index 6.
CLaunchClientAppTlv::GetCmdLinemaps to entry at index 2.
CLaunchClientAppTlv::GetGUIDesktopnot used in the packet, but maps to a entry at index 4.
CLaunchClientAppTlv::GetUseInstalledmaps to entry at index 5.
Taking all of this into account, it was possible to understand the format of the entry at index 5.
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.
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.
dbghelp.dll. After this DLL is loaded, the