SummaryBug type: arbitrary function call
Vector: window message to asw_av_tray_icon_wndclass
Impact: untrusted code execution within the trusted AvastUI.exe process
Verified on: avast! Free AvastUI.exe v9.0.2018.391
ForewordIt's been a while since I had used a shatter attack for an interesting purpose! Trendy about 10 years ago (according to Wikipedia), they allowed privilege escalation thanks to core components of Windows like with MS02-071. They are mostly extinct due to Windows now restricting the messages sent to more privileged processes, or isolation of services in session 0. An old but very good presentation of the excellent Brett Moore explains them in detail.
But the problem resurfaces when a process attempts to introduce home-made integrity levels, while functioning as the current logged in user (and at the same IL). This new security boundary can be shattered thanks to Windows messages.
DescriptionSince we are running in the same context as AvastUI.exe, we can pretty much send any window message to its windows. This appears to be something that the developers didn't think about. For example, the window corresponding to the window class asw_av_tray_icon_wndclass accepts quite a bit of user messages. The following piece of code handles the message 0x83fd:
.text:00551BC0 kk_CWndWM83FDh proc near ; DATA XREF: .rdata:00677314 o
.text:00551BC0 wParam = dword ptr 8
.text:00551BC0 lParam = dword ptr 0Ch
.text:00551BC0 push ebp
.text:00551BC1 mov ebp, esp
.text:00551BC3 mov eax, [ebp+wParam]
.text:00551BC6 test eax, eax
.text:00551BC8 jz short loc_551BD3
.text:00551BCA push [ebp+lParam]
.text:00551BCD call eax
.text:00551BCF pop ebp
.text:00551BD0 retn 8
.text:00551BD3 ; ---------------------------------------------------------------------------
.text:00551BD3 loc_551BD3: ; CODE XREF: kk_CWndWM83FDh+8 j
.text:00551BD3 xor eax, eax
.text:00551BD5 pop ebp
.text:00551BD6 retn 8
.text:00551BD6 kk_CWndWM83FDh endp
As you can see, this handler will interpret wParam as a function pointer and lParam as its first and only argument and call it. This obviously becomes an issue when the message is sent by a 3rd party application as it pretty much guarantees code execution within the AvastUI.exe process.
This call primitive is ideal to execute a function like LoadLibrary. We have to make the first parameter point to a string locating the DLL on the drive. Given that we are local, and that Windows doesn't do per-process randomization of DLLs, we already know the address of LoadLibraryA.
But one has to be a bit imaginative to know how to place the string into the AvastUI.exe process memory at a known location. One of the solutions that I found (that restricts the path to the DLL to *44* bytes), is to use a functionality that would put memory under our control at a known offset into
the .data section of AvastUI.exe. This requires some interaction with the named pipe \\.\pipe\snx_sdesktop_pipe. The process AvastUI.exe creates 10 of those, and reads from them in the following code:
.text:0054E159 mov ecx, [ebp+var_30]
.text:0054E15C push 0 ; lpOverlapped
.text:0054E15E shl ecx, 4
.text:0054E161 add ecx, [ebp+var_30]
.text:0054E164 lea eax, [ebp+var_8]
.text:0054E167 push eax ; lpNumberOfBytesRead
.text:0054E168 push 44 ; nNumberOfBytesToRead
.text:0054E16A lea eax, (g_NamedPipeStructures+4)[ecx*4]
.text:0054E171 push eax ; lpBuffer
.text:0054E172 push g_NamedPipeStructures[ecx*4] ; hFile
.text:0054E179 call ds:ReadFile
What I called g_NamedPipeStructures is located in the .data section of AvastUI.exe and is an array of 10 structures containing the handle to the pipes followed by a 44 byte array receiving the information read from the pipe.
In order to know where this structure is located in AvastUI.exe, we load the binary within our process and locate the structure thanks to a code signature. If there is no address space collision that would trigger a remapping elsewhere, that address will be the same in the remote process. We then open the 10 named pipes and write the DLL path to them to make sure all the structures will be filled with our data. Then we locate the window, and send it the window message with LoadLibraryA as wParam and the 1st structure address as lParam. This will load the DLL within the AvastUI.exe process.
In my exploit, the DLL in question will spawn a cmd.exe and call the IOCTL to make it trusted. Obviously raising a cmd.exe to trusted doesn't make much sense in a real world exploitation scenario, this is just more of a visual example.