How “Russian hackers” tried to learn about new sanctions against Russia: we study CVE-2015-1701
This zero-day vulnerability in Windows became known on April 20, when FireEye and Bloomberg reported an unsuccessful cyber attack on a government agency in a foreign country that was discussing a US sanctions policy against Russia. The “Russian hackers” from the APT28 group were accused of involvement in the deed, as well as in attempts to hack the NATO special services, state bodies of Georgia, Poland, Hungary and FireEye.
The attack was implemented using previously unknown vulnerabilities CVE-2015-3043 in Adobe Flash and CVE-2015-1701 in Windows. The user was sent via a link to an infected site, where a JavaScript script using the Flash vulnerability downloaded an executable file to the computer, which, through the hole CVE-2015-1701 in Windows, increased privileges and stole encryption keys.
Adobe eliminated the vulnerability in Flash within hours, but Microsoft took the time and released the patch only the day before. In this article we will talk about the main features of this bug.
First, some structures and mechanisms used to exploit the CVE-2015-1701 vulnerability should be described. This time, it was not without the notorious
So, the lower 16 bits of the window handle
the same thing in the kernel:
The
The fields
Here we are only interested in one possible value
meaning that the object is a window, and the field
Here you can pay attention that the user
The last field of
Using all this information, we can search for windows that belong to the flow of our process and restore their descriptors. To do this, find an element
The index of such a structure will be equal to the low 16 bits of the descriptor, and the high 16 bits will be contained in the field
Why not just take advantage
Another concept that should be explained is closely related to the field
As you can see, various callbacks are contained here, but of course they didn’t
In the kernel, the callback mechanism of these callbacks is implemented in a function
Next in line
Then comes the very important optimization for us.
On the passed parameter
If such a mapping is for a transmitted
As can be seen from the screenshot, if the display is successful, then a flag is raised at the window
Thus, if you
Now consider creating a window. Roughly speaking, the procedure is entirely responsible for this
Then the attributes of the window are filled. The whole procedure even
The question arises: what will happen if you call
And indeed, there is a possibility of calling the
However, the probability of setting a flag from a neighboring thread by a call
Despite the enormous size of the function
If, during the registration of a window class, it specified a picture for a regular icon
Under the 0x36th number in the table goes
After copying the icon, the windows from the class are
As a result, we obtain the following. First, you need to register the class with the usual icon, but without specifying a small one:
You also need a hook to
It will call
After that, at the time the window was created, the previously installed hook is called.
The window at this moment is already listed in the table, but not yet initialized.
A hook invokes
And upon returning from the callback, it
Thus, the window function specified when registering the class will be executed in the kernel:
Obviously, the simplest thing for which this can be used is the theft of a system token with the subsequent launch of a system shell.
PS A quick inspection of Windows 8.1 showed that the
There is a possibility that the “race condition” still exists and can with some probability be exploited above using the two-thread method. In this regard, nothing more accurate can now be said.
The attack was implemented using previously unknown vulnerabilities CVE-2015-3043 in Adobe Flash and CVE-2015-1701 in Windows. The user was sent via a link to an infected site, where a JavaScript script using the Flash vulnerability downloaded an executable file to the computer, which, through the hole CVE-2015-1701 in Windows, increased privileges and stole encryption keys.
Adobe eliminated the vulnerability in Flash within hours, but Microsoft took the time and released the patch only the day before. In this article we will talk about the main features of this bug.
Valuable gSharedInfo
First, some structures and mechanisms used to exploit the CVE-2015-1701 vulnerability should be described. This time, it was not without the notorious
win32k.sys
one, so the first thing we will focus on is the structure win32k!tagSHAREDINFO
to which the symbol corresponds win32k!gSharedInfo
, as well as the data type HWND
, which is very closely connected with it.
Ours stores pointers to various structures associated with windows and, what is most remarkable, many of these structures are mapped to user space (mapped to a user mode, in our opinion), and the corresponding symbol has been used for some time now (either from whist or from seven) became exported.
We are interested in two fields here:gSharedInfo
user32!gSharedInfo
aheList
- points to an array of type elementswin32k!_HANDLEENTRY
;HeEntrySize
- contains the size of the elementwin32k!_HANDLEENTRY
.
So, the lower 16 bits of the window handle
HWND
are actually the index in the array gSharedInfo->aheList
. For example, if our variable window
contains a HWND
descriptor: the same thing in the kernel:
The
wUniq
structure field win32k!_HANDLEENTRY
contains the top 16 bits of the descriptor HWND
and, apparently, serves the simple purpose of separating objects that occupy the same address in a given array at different time intervals. Thus, if the object is freed and later its place will be taken, for example, by a new window with wUniq = 0x12
, then the old descriptor 0x0011024c
will no longer be able to access it. The fields
bFlags
and bType
contain various flags and the type of object addressed by the phead field, respectively. You can look at the possible values that they accept.The ReactOS . Here we are only interested in one possible value
bType
: TYPE_WINDOW = 1
meaning that the object is a window, and the field
phead
addresses the structure win32k!tagWND
. Here you can pay attention that the user
user32!gSharedInfo->aheList[…].phead
stores the address belonging to the kernel. However, if you wish, you can get the address of its user display, but this is a different story, so for details I refer you to the window handle that receives the input HWND
and the return tagWND*
procedure user32!ValidateHWND
, or rather, to the one that it calls user32!HMValidateHandle
. The last field of
pOwner
structure not considered earlier win32k!_HANDLEENTRY
contains a pointer to the win32k!_W32THREAD
stream to which the object belongs. Each thread stores this pointer in win32k!_KTHREAD->Win32Thread
(why not in_ETHREAD
), and also, which in our case is much more important, c TEB!Win32ThreadInfo
. Using all this information, we can search for windows that belong to the flow of our process and restore their descriptors. To do this, find an element
user32!gSharedInfo->aheList[…]
in which:bType == TYPE_WINDOW
;pOwner == TEB!Win32ThreadInfo
.
The index of such a structure will be equal to the low 16 bits of the descriptor, and the high 16 bits will be contained in the field
wUniq
. Why not just take advantage
user32!FindWindow
? At that moment, when we need it, the name and class will not be filled by the window yet.KernelCallbackTable
Another concept that should be explained is closely related to the field
PEB!KernelCallbackTable
. As you can see, various callbacks are contained here, but of course they didn’t
kernel
, but got their name because their client is usually win32k.sys
contacting them when they need to perform an operation in user space. The call takes place in a mannerntdll!KiUserCallbackDispatcher
similar to dispatching . In the kernel, the callback mechanism of these callbacks is implemented in a function
nt!KeUserModeCallback
. The call occurs at the callback index. Address resolution by index is already in ntdll!KiUserCallbackDispatcher
.SetWindowLongPtr
Next in line
user32!SetWindowLongPtr
, but in fact - its execution in the form win32k!xxxSetWindowData
. We restrict ourselves to only one case that interests us - with the parameter GWLP_WNDPROC
. win32k!xxxSetWindowData
performs various checks first. For example, whether the window belongs to the process whose thread it is trying to install WndProc
, and whether this window is already destroyed ( FNID_DELETED_BIT
bit). Then comes the very important optimization for us.
On the passed parameter
WndProc
( value_
in the screenshot) is called MapClientToServerPfn
. This simple and at the same time extremely useful function maps functions from win32k!gpsi->apfnClientW
and win32k!gpsi->apfnClientA
to their corresponding functions from win32k!gpsi-> aStoCidPfn
: If such a mapping is for a transmitted
WndProc
perhaps the procedure call can be optimized by referring directly to the implementation of the function in the kernel, for example, win32k!xxxDefWindowProc
without wasting time switching to user mode to call the wrapper, for example, ntdll!NtdllDefWindowProc_A
which user32!DefWindowProcA
is a pass-through export. As can be seen from the screenshot, if the display is successful, then a flag is raised at the window
WFSERVERSIDEPROC
, after which the displayed value is entered in its field win32k!tagWND->lpfnWndProc
. Thus, if you
user32!SetWindowLongPtr
install one of the standard procedures, then, in fact, the corresponding procedure from win32k.sys
kernel mode will be executed .xxxCreateWindowEx
Now consider creating a window. Roughly speaking, the procedure is entirely responsible for this
win32k!xxxCreateWindowEx
. First, the win32k!HMAllocObject
object is allocated by calling tagWND
and information about it is entered into the table gSharedInfo->aheList
: Then the attributes of the window are filled. The whole procedure even
hex-rays
takes a couple of thousand lines, so there is no sense or opportunity to dwell on all the actions performed.Operation option
The question arises: what will happen if you call
SetWindowLongPtr(hwnd, GWLP_WNDPROC, DefWindowProc)
the window at the moment when it is already created, but it still has a filled field lpfnWndProc
. After all, this field is populated from the class field, in which it is probably already stored displayed by MapClientToServerPfn
, if such a mapping is possible. And indeed, there is a possibility of calling the
SetWindowLongPtr
flag WFSERVERSIDEPROC
before the address WndProc
is filled with the value from the class field. At the same time, this flag does not drop when the field is set WndProc
, since the developers did not expect the possibility that it could be set. Only the logic for setting the flag for the window is present if the corresponding class flag is raised. However, the probability of setting a flag from a neighboring thread by a call
SetWindowLongPtr
at runtimeCreateWindowEx
insignificant, because you must first find the HWND
windows in the array user32!gSharedInfo->aheList
, after which the chain of calls user32!SetWindowLongPtr -> … -> win32k!xxxSetWindowData
should work out faster than the initialization of the fields tagWND
in win32k!xxxCreateWindowEx
. You can, of course, play with the processor affinity
thread priorities. However, for Windows 7 and earlier there is a simple way.Option for Windows 7
Despite the enormous size of the function
win32k!xxxCreateWindowEx
, all the information we are interested in fits into several hex-rays lines: If, during the registration of a window class, it specified a picture for a regular icon
hIcon
, but was not specified for a small icon hIconSm
, then win32k!xxxCreateWindowEx
when creating a window for the first time, this class copies , or rather - scales, the icon to fill the field win32k!tagCLS->spicnSm
. This action is performed by a function win32k!xxxCreateClassSmIcon
that delegates the task to one of the user so-called so-called kernel callbacks
: Under the 0x36th number in the table goes
user32!_ClientCopyImage
. He performs the task. After copying the icon, the windows from the class are
win32k!xxxCreateWindowEx
immediately filled . Then, as seen, if the flagWndProc
WndProc
WFSERVERSIDEPROC
erected in the classroom, it is erected for the window.results
As a result, we obtain the following. First, you need to register the class with the usual icon, but without specifying a small one:
You also need a hook to
user32!_ClientCopyImage
: It will call
SetWindowLongPtr
for the window you just created: After that, at the time the window was created, the previously installed hook is called.
The window at this moment is already listed in the table, but not yet initialized.
A hook invokes
SetWindowLongPtr
that raises a flag bServerSideWindowProc
in the corresponding window structure. And upon returning from the callback, it
win32k!xxxCreateWindowEx
overwrites the lpfnWndProc
value from the class field. Thus, the window function specified when registering the class will be executed in the kernel:
Obviously, the simplest thing for which this can be used is the theft of a system token with the subsequent launch of a system shell.
PS A quick inspection of Windows 8.1 showed that the
win32k!xxxCreateWindowEx
installation tagWND->lpfnWndProc
and the call win32k!xxxCreateClassSmIcon
go in the reverse order compared to earlier versions. Thus, hooking user32!_ClientCopyImage
will no longer help. There is a possibility that the “race condition” still exists and can with some probability be exploited above using the two-thread method. In this regard, nothing more accurate can now be said.