# Intro I recently wrote an exploit for `CVE-2022-21882` and I just wanted to create some simple notes on this bug. This vulnerability [has](https://www.avira.com/en/blog/anatomy-of-an-exploit-in-windows-win32k-cve-2022-21882) a [number](https://www.coresecurity.com/core-labs/articles/analysis-cve-2022-21882-win32k-window-object-type-confusion-exploit) of [writeups](https://googleprojectzero.github.io/0days-in-the-wild/0day-RCAs/2022/CVE-2022-21882.html). Additionally, it is a patch bypass for `CVE-2021-1732` (where 95% of the exploitation process is the same), this bug also [has](https://zhuanlan.zhihu.com/p/469240680) several [writeups](https://googleprojectzero.github.io/0days-in-the-wild//0day-RCAs/2021/CVE-2021-1732.html). There are two mainline POC's for this bug: - https://github.com/KaLendsi/CVE-2022-21882 - https://github.com/L4ys/CVE-2022-21882 So why write notes on this bug at all? Well, I recommend that you have a look at these writeups, prepare a travel bag for when you get lost along the way ⛺! ![[CVE202221882_1.gif]] After writing the exploit I think it is actually not so complicated to understand why the bug happens and what the basic exploitation primitive is. While some of the mechanics are obtuse the idea is pretty straightforward (hopefully). ``` As time wore along, his absorption in the irregular wall and ceiling of his room increased; for he began to read into the odd angles a mathematical significance which seemed to offer vague clues regarding their purpose. Old Keziah, he reflected, might have had excellent reasons for living in a room with peculiar angles; for was it not through certain angles that she claimed to have gone outside the boundaries of the world of space we know? ``` # Paint by numbers We will pause and examine things at a number of different stages of the exploit runtime. Some pertinent facts will be highlighted but most of the logic will be laid out in diagrams. Keep in mind that I am obfuscating a lot of technical details to keep things simple so some of what is written below is mildly fictional. ### In the beginning In the beginning, we create three window objects. Each of these is assigned a [class atom](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerclassexw) so we can identify the `Pwn` window when we trigger the bug. ![[CVE202221882_2.png]] There are some shenanigans to getting these windows in a specific order but there is nothing special going on here really. We create a bunch of window objects to fill some free space that may exist and then proceed to create our exploit windows (`while -> bad layout`). ### Win32k Callbacks The background on `win32k callbacks` is a bit complex but generally, in a number of situations (like delivery of window messages) `win32k` will transition from `Ring0` to `Ring3` and back again. It will call a function, by index, in a `PEB` data structure (the `KernelCallbackTable`) and after completion use `nt!NtCallbackReturn` to return to `win32k`. ![[CVE202221882_3.png]] In the past there have been quite a few bugs identified in this mechanism. Because the caller can hook its own process it is able to trigger these kinds of transitions and add/remove code or otherwise modify the runtime logic of the callback. That is also what we do in `CVE-2022-21882`. ### Window Type Confusion Now we have some background we can trigger the actual bug. ![[CVE202221882_4.png]] The callback hook looks like this in my code, pretty straightforward. Note that we identify the `Pwn` window by some property we set using its class at the start. ```cs internal static UInt32 xxxClientAllocWindowClassExtraBytes_Delegate(IntPtr pSize) { // Should we trigger a type confusion? if (Marshal.ReadInt32(pSize) == API.MAGIC_CB_WND_EXTRA) { // Change the window to a console window API.NtUserConsoleControl(6, ref hMagicWnd, 16); // Return crafted offset IntPtr pCallbackBuff = Helpers.AllocManagedMemory(24); Marshal.WriteInt64(pCallbackBuff, (Int64)iWnd0DesktopOffset); return API.NtCallbackReturn(pCallbackBuff, 24, 0); } // Call the original function return fnxxxClientAllocWindowClassExtraBytes(pSize); } ``` ### OOB Write-What-Where After triggering the type confusion, the `Pwn` window can use standard API functions, like `user32!SetWindowLongW` to update the size of `WND0` extra bytes to a large value, like `0xFFFFFFFF` so it becomes Out-Of-Bounds (OOB). ![[CVE202221882_5.png]] Then, using a similar trick, `WND0` can overwrite the extra data pointer of `WND1` to set it to an arbitrary Kernel address (like an `EPROCESS` field or whatever). ![[CVE202221882_6.png]] `WND1` can then use standard API functions like `user32!SetWindowLongPtrW` to write a controlled value at this forged pointer. ### Do you even read bro? You may have noticed that this only gives you an arbitrary write not a read. Your gadget here is interchangeable, you can use any primitive that you know works. The mainline POC's use a very neat trick where they set some properties on `WND1` and then use `user32!GetMenuBarInfo` to read out values. In addition to this, I also tested a powerful read primitive by [@PTDuy](https://twitter.com/PTDuy) from Starlabs, you can see more details [here](https://starlabs.sg/blog/2022/06-trying-to-exploit-a-windows-kernel-arbitrary-read-vulnerability/). I won't go further into this for brevity! # Patch In the post-patch version (analysis done on `Win10 21H1 win32kfull 10.0.19041.1466`) of `win32kfull!xxxClientAllocWindowClassExtraBytes`, you can see that a check has been added at the end of the function. If, by the time the function returns, the window object turns out to be of the `Console` type then the function returns `0`. ![[CVE202221882_7.png]] You can get some pre/post patch screenshots [here](https://twitter.com/FuzzySec/status/1622317304477368320). ### Do you even bypass though? So you may wonder what happened in `CVE-2021-1732` and how this is a bypass for that patch. This vulnerability is basically the same, the only difference is where the type confusion happens. In the original exploit, the attacker (this was exploited In-The-Wild 🌶️) hooks `user32!xxxClientAllocWindowClassExtraBytes` when the window is first created (using something like `user32!CreateWindowExW`) however the patch then added a check for this. It's a really interesting case-study to take a look at! Clearly the original patch was added in the wrong location, something to keep in mind for variant analysis. # POC Here is a short video of my POC in action. <iframe width="100%" height="315" src="https://www.youtube.com/embed/1-IsFCgW99Y" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>