Prove or reject unauthenticated remote OS command execution as the PaperCut service account on stock PaperCut NG/MF 25.0.11 via a pre-auth Release Station / MFD card-ID flow reaching the external card-lookup runtime, then replay identically on stock 25.0.12 to confirm the fix blocks the chain. This maps to the PaperCut NG/MF URGENT Security Bulletin of 27 Aug 2026 active exploitation, all versions affected . Whether a stock remote mechanism exists is an open question; card-ID SQL injection and JDBC URL injection are unproven hypotheses only.
Corrupting the Hive Mind: Persistence Through Forgotten Windows Internals | Praetorian
Swarmer enables stealthy Windows registry persistence by exploiting mandatory user profiles and the Offline Registry API to bypass EDR detection. Learn how this technique leverages NTUSER.MAN files to modify the registry without triggering standard API monitoring.
How to APT EP.2: Lying to the whole netns through BPF_PROG_TYPE_FLOW_DISSECTOR
Flowdiss explores how BPF_PROG_TYPE_FLOW_DISSECTOR can be abused to forge the Linux kernel's notion of packet flow identity without modifying packet contents. By manipulating the flow_keys used to derive skb-hash, a privileged eBPF program can influence downstream consumers such as RPS, GRO, ECMP, and other eBPF programs that rely on the cached flow hash. The article presents a proof of concept, validates its effects and limitations, and demonstrates how a separate BPF LSM program can conceal the attack from standard bpftool enumeration, illustrating both an underexplored attack surface and the limits of relying on user-space BPF tooling on a compromised system.
[Wipeload Step 6.] How to Escape the SandBox Painlessly (EN) - hackyboiz
pHello! This is gongjae, back with Step 6 of the Wipeload project~!! We’re already at Step 6…! Shall we look back at the road we’ve walked so far?/p
pimg src="/2026/08/01/gongjae/Wipeload_step6/EN/image.png" alt="image"/p
blockquote
pPrevious articlesbra href="https://hackyboiz.github.io/2026/06/06/OUYA77/Wipeload_step1/en/"https://hackyboiz.github.io/2026/06/06/OUYA77/Wipeload_step1/en//abra href="https://hackyboiz.github.io/2026/06/21/ji9umi/Wipeload_step2/en/"https://hackyboiz.github.io/2026/06/21/ji9umi/Wipeload_step2/en//abra href="https://hackyboiz.github.io/2026/06/26/ji9umi/Wipeload_step3/en/"https://hackyboiz.github.io/2026/06/26/ji9umi/Wipeload_step3/en//abra href="https://hackyboiz.github.io/2026/07/13/OUYA77/Wipeload_step4/en/"https://hackyboiz.github.io/2026/07/13/OUYA77/Wipeload_step4/en//abra href="https://hackyboiz.github.io/2026/07/19/gongjae/Wipeload_step5/EN/"https://hackyboiz.github.io/2026/07/19/gongjae/Wipeload_step5/EN//a/p
/blockquote
pIn Step 1, OUYA77 drew the picture of the entire chain, and in Steps 2~3, ji9umi showed us renderer RCE and the V8 heap sandbox bypass. In Step 4, OUYA77 came back to take apart how the Chrome sandbox locks itself down, and in Step 5, I covered ALPC and the codeALPC_MSGFLG_SYNC_REQUEST/code Use-After-Free. Today, in Step 6, we’re going to revive that UAF into an arbitrary strongRead/Write primitive/strong, use it as a foothold to reach strongMedium-integrity code execution/strong, and finally pull off a real strongSandBox Escape/strong — on our way to meet Master Shifu!/p
h1 id="1-From-Crash-to-Primitive"a href="#1-From-Crash-to-Primitive" class="headerlink" title="1. From Crash to Primitive"/a1. From Crash to Primitive/h1pAlright, shall we pull up the final scene from Step 5 once more?/p
pWe planted the trigger DLL inside the renderer with sRDI, then killed — mid-flight — the thread that had a SYNC message posted via codeNtWaitForWorkViaWorkerFactory/code, which freed the code_ETHREAD/code. And the moment we closed the port, codeAlpcpCancelMessage/code fired a write at code+0x578/code of that already-dead address. And this was the result./p
pimg src="/2026/08/01/gongjae/Wipeload_step6/EN/image2.png" alt="image"/p
blockquote
pA BSOD means we’re done, right..? delit blew up in the kernel, so it counts as an escape/del/p
/blockquote
pYeah.. honestly, this is where I started overthinking too.. As I hinted at the end of Step 5, Theori’s blog post also said something like “use the file picker, do a thread spray, blah blah blah, and you get code execution at Medium integrity”~../p
pA BSOD only proves one thing: “the kernel stepped on a dead pointer.” But that’s not what we want! We have to turn that dead pointer into stronga tool that reads and writes values we choose, at addresses we choose/strong. A crash and a primitive really are completely different things. The road from the blue screen to cmd is much longer than you’d think~..🥲/p
pBut gongjae isn’t one to back down. I put my brain to work and first picked out what we’d actually need for a SandBox Escape./p
ol
liHow do we turn the UAF into reading and writing the values we want, at the addresses we want? - reclaim?/li
liWhich process do we get to perform that reclaim for us? - file picker?/li
liOnce we can read and write, what do we overwrite to take over execution flow? - vtable?/li
/ol
pIt’s funny that they all end in question marks, but erasing those question marks one by one is exactly the goal of today’s article~!😄 So let’s start with number 1!/p
pWhat we freed in Step 5 was the kernel pool slab that an code_ETHREAD/code object was sitting in. The kernel takes that memory back right away and gets it ready to hand out to the next allocation. So there’s exactly one thing left for us to do: fill that spot back up with a fresh code_ETHREAD/code belonging to a process of our choosing! That is exactly what reclaim is./p
pWhat happens if this succeeds? The codeWaitingThread/code pointer left behind in the message stops being a “dead address”. strongIt now points at a live code_ETHREAD/code/strong inside a process we control. At that moment, the UAF transforms from a mere crash generator into a cross-process R/W window!!/p
pWhy is this hard, you ask? Because the kernel pool isn’t that naive.. You have to know, down to the bone, where a freed slab actually goes and by what rules it gets reused… 🙃 Before we dive in: the native code running inside the renderer today is a modified version of that trigger DLL from Step 5, so for convenience I’ll just call it the strongexploit DLL/strong./p
pThe test environment is identical to Step 5./p
precode class="hljs apache"span class="hljs-attribute"Windows/span span class="hljs-number"11/span span class="hljs-number"22/spanHspan class="hljs-number"2/span · ntoskrnl span class="hljs-number"22621/span.span class="hljs-number"963/span
span class="hljs-attribute"Chrome/span for Testing span class="hljs-number"114/span.span class="hljs-number"0/span.span class="hljs-number"5708/span.span class="hljs-number"0/span/code/pre
pAlso, the Driver Verifier we turned on in Step 5 to make sure we’d see the BSOD is turned off this time. Special pool unmaps freed slabs entirely, so leaving it on would make today’s whole topic — reclaim — impossible!!/p
h2 id="1-1-VS-allocator-ETHREAD-slab"a href="#1-1-VS-allocator-ETHREAD-slab" class="headerlink" title="1.1 VS allocator _ETHREAD slab"/a1.1 strongVS allocator/strong _ETHREAD slab/h2pFirst, let’s confirm the identity of the slab we’re up against./p
precode class="hljs sqf"span class="hljs-built_in"sizeof/span(span class="hljs-variable"_ETHREAD/span) = span class="hljs-number"0/spanx900 span class="hljs-comment"// object size the kernel allocates/span
pool block (actual) = span class="hljs-number"0/spanxa80 span class="hljs-comment"// what !pool reports/span
tag = span class="hljs-string"'Thre'/span span class="hljs-comment"// protected/span
pool span class="hljs-built_in"type/span = NonPagedPoolNx/code/pre
pThese two numbers are exactly where it gets confusing. code0x900/code is only ever the object size; the block that actually comes off the pool is code0xa80/code. code_POOL_HEADER/code, code_OBJECT_HEADER/code, sub-headers, and the VS chunk header all get piled on before and after, and then it gets rounded up to the VS allocator’s size class./p
pimg src="/2026/08/01/gongjae/Wipeload_step6/EN/image3.png" alt="image"/p
pSo the size class we’re competing over during reclaim is not code0x900/code but strongcode0xa80/code/strong. When picking the slab out by eye, it’s easiest to scan the code!pool/code output for the size codea80/code + code*Thre/code combination. The code(Protected)/code printed next to it means the pool tag carries a protection bit — a kernel-side safeguard that only accepts a free when the tag matches. We aren’t calling free ourselves; we’re targeting a spot the kernel freed while tearing a thread down, so this never gets in our way./p
pimg src="/2026/08/01/gongjae/Wipeload_step6/EN/image4.png" alt="image"/p
pAlso, since Win10 19H1 the kernel pool is a segment heap, so which allocator handles a request depends on the size. Anything under code0x200/code goes to kLFH, code0x200/code through code0x20000/code goes to the strongVS allocator/strong, and anything larger goes to segment/large. The block-code0xa80/code code_ETHREAD/code falls into the second one — in other words, the strongVS allocator/strong manages it. Everything in today’s reclaim happens inside this allocator./p
pAnd the allocation path looks like this./p
precode class="hljs apache"span class="hljs-attribute"NtCreateThreadEx/span → PspAllocateThread → ObpAllocateObject → ExAllocatePoolspan class="hljs-number"2/span/code/pre
pThe only entrance that hands this slab out is, in the end, codeNtCreateThreadEx/code — which means strongif anybody creates one thread, one code0xa80/code block goes out/strong. The answer to why the thread spray I teased earlier actually works is packed entirely into this one line./p
h2 id="1-2-Dynamic-Lookaside"a href="#1-2-Dynamic-Lookaside" class="headerlink" title="1.2 Dynamic Lookaside"/a1.2 Dynamic Lookaside/h2pBut here’s the thing — there’s one more layer of cache sitting in front of the VS allocator. It’s the code_RTL_DYNAMIC_LOOKASIDE/code structure that code_SEGMENT_HEAP.UserContext/code points to, and honestly, this guy is the real main character of today’s story!/p
pShall we check the structure directly in kd first?/p
precode class="hljs angelscript"span class="hljs-number"0/span: kd> dt nt!_RTL_DYNAMIC_LOOKASIDE
+span class="hljs-number"0x000/span EnabledBucketBitmap : Uint8B
+span class="hljs-number"0x008/span BucketCount : Uint4B
+span class="hljs-number"0x00c/span ActiveBucketCount : Uint4B
+span class="hljs-number"0x040/span Buckets : [span class="hljs-number"64/span] _RTL_LOOKASIDE
span class="hljs-number"0/span: kd> dt nt!_RTL_LOOKASIDE
+span class="hljs-number"0x000/span ListHead : _SLIST_HEADER
+span class="hljs-number"0x010/span Dep
Turning Chrome Remote Desktop into Pure Red Team Ops
How to hide the Chrome Remote Desktop connection banner by patching a single dialog resource, then abuse host.json and MSI packaging to turn CRD into a quiet persistent access channel.
SakDriver: Reversing a Windows Kernel Driver Rootkit
A reverse-engineering walkthrough of SakDriver, a Windows kernel-mode rootkit first mistaken for a Cobalt Strike Beacon. It patches ETW, hides processes via DKOM, masquerades as a minifilter and a fake Microsoft service, and — most notably — receives C2 commands through the Windows registry. Includes the full recovered command table, IOCs and a matching YARA figure.
CVE-2026-50458: Finding a UAF in the Windows Brokering File System
On Tuesday, July 14, Microsoft released the largest Patch Tuesday update in its history, fixing more than 600 vulnerabilities. A bug I reported to Microsoft on May 17 was patched as CVE-2026-50458 in this release, so I am publishing the writeup I wrote at the time, while the details were still fresh. I hope you enjoy it!
Unpacking “Cruciferra”: An Analysis of a Crypter Service | Proofpoint US
Key Findings Cruciferra is a sophisticated crypter service used by multiple unrelated cybercriminal threat clusters. It has been observed delivering a wide range of
[Wipeload Step 5.] Sorry, I studied ALPC just to show you this (EN) - hackyboiz
pHi everyone! Today gongjae is back with Step 5 — the crown jewel of the Wipeload project, haha./p
pimg src="/2026/07/19/gongjae/Wipeload_step5/EN/image.png" alt="image"/p
blockquote
pPrevious research postsbra href="https://hackyboiz.github.io/2026/06/06/OUYA77/Wipeload_step1/kr/"https://hackyboiz.github.io/2026/06/06/OUYA77/Wipeload_step1/kr//abra href="https://hackyboiz.github.io/2026/06/21/ji9umi/Wipeload_step2/kr/"https://hackyboiz.github.io/2026/06/21/ji9umi/Wipeload_step2/kr//abra href="https://hackyboiz.github.io/2026/06/26/ji9umi/Wipeload_step3/kr/"https://hackyboiz.github.io/2026/06/26/ji9umi/Wipeload_step3/kr//abra href="https://hackyboiz.github.io/2026/07/13/OUYA77/Wipeload_step4/kr/"https://hackyboiz.github.io/2026/07/13/OUYA77/Wipeload_step4/kr//a/p
/blockquote
pSteps 2–3 covered ji9umi’s Renderer RCE, and in Step 4 OUYA77 gave us a detailed tour of the Chrome sandbox. Picking up that Way from both of them, I’m going to walk you through — in detail — stronghow do we actually get to Medium-privilege code execution from Chrome?/strong In today’s Step 5 I’ll explain ALPC, the key ingredient of the Sandbox Escape, and in Step 6 we’ll take that ALPC and see how it takes us all the way to Medium-privilege code execution!/p
pShall we get right into it? Let’s go~/p
h1 id="1-Sorry-I-studied-ALPC-just-to-show-you-this"a href="#1-Sorry-I-studied-ALPC-just-to-show-you-this" class="headerlink" title="1. Sorry, I studied ALPC just to show you this"/a1. Sorry, I studied ALPC just to show you this/h1pALPC (Advanced Local Procedure Call) is the IPC that sits at the very bottom of process-to-process conversations inside Windows. The local RPC we’re all familiar with, at the kernel level, mostly ends up riding ALPC too, and system services everyone has heard of like “csrss.exe” or “lsass.exe”, DCOM, WinRT activation… pretty much every user-mode service is talking over an ALPC port somewhere. Chrome is no exception — the Mojo IPC pipe between the renderer and the browser has ALPC partially baked into its underlying transport. Which is to say: the object we’re going to be poking at today is dead common, but the kind of thing you’d miss unless you look closely!!/p
pimg src="/2026/07/19/gongjae/Wipeload_step5/EN/image2.png" alt="image"/p
blockquote
p?? : You studied ALPC just to do THIS?brgongjae : delNope, just did it for fun/del/p
/blockquote
pInside the kernel, two objects star in this show. code_ALPC_PORT/code is the communication endpoint, and codeKALPC_MESSAGE/code is a single message flowing through that endpoint. Easiest mental model: server opens a port, clients attach, they exchange messages!/p
pa href="https://hackyboiz.github.io/2025/11/29/gongjae/Windows_ALPC/KR/"https://hackyboiz.github.io/2025/11/29/gongjae/Windows_ALPC/KR//a/p
pActually, we already went over ALPC once in a previous research post, right? Stuff like the port splitting into two tiers of connection port / communication port, and basic message-attribute concepts — I put all of that together in the earlier ALPC post! If this is your first time hearing about ALPC, I’d recommend giving that one a skim first… but honestly the single most important fact is: stronghow does the Chrome sandbox treat ALPC?/strong/p
pimg src="/2026/07/19/gongjae/Wipeload_step5/EN/image3.png" alt="image"/p
pThe answer: the codeNtAlpc*/code family of syscalls themselves are strongnot/strong on the sandbox’s filter list!! Even though it’s an undocumented interface with nothing on the official docs, it’s a very-much-alive API, so even an Untrusted IL renderer can call the stubs codentdll/code exports directly./p
h2 id="1-1-Sync-vs-Async"a href="#1-1-Sync-vs-Async" class="headerlink" title="1.1 Sync vs Async"/a1.1 Sync vs Async/h2pThere are two ways to send a message over ALPC. strongAsync/strong requests toss the request onto a queue and the calling thread goes on its merry way. strongSync/strong requests, on the other hand, park the calling thread inside the kernel until an answer comes back!/p
pimg src="/2026/07/19/gongjae/Wipeload_step5/EN/image4.png" alt="image"/p
pFor the kernel to sustain that “parked” state, it obviously needs some info, right? Which thread is waiting on which message, who to wake up when the reply lands — that stuff has to be recorded somewhere!/p
aside
💡
Quick aside on why the two modes are asymmetric here: async requests don’t need the kernel to know “who’s waiting on this reply?” As long as the completion callback is correctly registered, we’re done. 🫠
Sync is a different story though. The moment the reply arrives, the kernel code handling it has to figure out **“okay, I got this — who exactly am I supposed to wake up??”**, and that answer has to be pre-stored as a back-pointer somewhere.
/aside
h2 id="1-2-Waiting-thread"a href="#1-2-Waiting-thread" class="headerlink" title="1.2 Waiting thread"/a1.2 Waiting thread/h2pSo when a sync request comes in, the kernel slips a back-pocket pointer into the message object that says “the thread waiting on this message’s reply is THIS guy!!” That’s the code_KALPC_MESSAGE.WaitingThread/code field, and in the environment I’m experimenting with today, Windows 22H2 22621.963, it lives at offset +0x20./p
pThere’s a matching field going the other direction too. On the thread side there’s a field that records “the message I’m currently waiting on is THIS guy!!” — that’s code_ETHREAD.AlpcMessageId/code, located at offset +0x578. WaitingThread from earlier and this AlpcMessageId form a pair of back-pointers pointing at each other!/p
pimg src="/2026/07/19/gongjae/Wipeload_step5/EN/image5.png" alt="image"/p
blockquote
pKind of like how Tai Lung and Master Shifu shared a special bond back when Tai Lung was little…/p
/blockquote
pThere’s a reason the kernel bothers to write this pair with code_InterlockedExchange/code atomically… since the same thread can be touched from multiple places at once, you need atomicity to avoid races. 😂/p
pBut even so — when the pair matches it’s beautiful, and the moment one side falls out of step, that’s when things go sideways. The main character of today’s vuln is this very field, and an unmanaged back-pointer is guaranteed to blow up in your face sooner or later, haha!/p
h1 id="2-Root-Cause"a href="#2-Root-Cause" class="headerlink" title="2. Root Cause"/a2. Root Cause/h1pAlright, as I said above, we need to see why this unmanaged back-pointer is the seed of the vuln, right? But first, let’s quickly review sync vs async messaging through some code!/p
h2 id="2-1-ALPC-MSGFLG-SYNC-REQUEST"a href="#2-1-ALPC-MSGFLG-SYNC-REQUEST" class="headerlink" title="2.1 ALPC_MSGFLG_SYNC_REQUEST"/a2.1 ALPC_MSGFLG_SYNC_REQUEST/h2pThe entry point where a message actually lands on the queue is codeAlpcpDispatchNewMessage/code. That “somewhere in the middle” I mentioned earlier is right here! Looking at it in IDA it feels like this:/p
precode class="hljs cpp"span class="hljs-keyword"if/span ((SendFlags & span class="hljs-number"0x20000/span) != span class="hljs-number"0/span) { span class="hljs-comment"// ALPC_MSGFLG_SYNC_REQUEST/span
span class="hljs-comment"// plant the thread back-pointer/span
*(_QWORD *)(msg + span class="hljs-number"0x20/span) = curthread; span class="hljs-comment"// msg.WaitingThread = current _ETHREAD/span
_InterlockedExchange64(curthread + span class="hljs-number"0x578/span, msg); span class="hljs-comment"// _ETHREAD.AlpcMessageId = msg/span
}/code/pre
pTwo lines, that’s the whole trick. Stuff the current thread’s code_ETHREAD/code directly into codemsg+0x20/code, and conversely plant the message at codecurthread + 0x578/code. That completes the pair of back-pointers pointing at each other./p
pLet’s also briefly trace how SendFlags gets here./p
aside
💡
When user code calls `NtAlpcSendWaitReceivePort`, the kernel enters `AlpcpSendMessage`, decides routing, and then hands off to this `AlpcpDispatchNewMessage`. The trigger I’m using today goes through `NtWaitForWorkViaWorkerFactory`, but even there — the moment it processes a deferred send — we end up at that same dispatch site.
→ Bottom line: no matter which road you take, if `SendFlags & 0x20000` is satisfied, those two lines run!!
/aside
pcodeSendFlags & 0x20000/code is checking the codeALPC_MSGFLG_SYNC_REQUEST/code flag — if it were an async request, those two lines get skipped wholesale, right? So to hit this vuln you absolutely have to set 0x20000 in SendFlags and take the sync-request branch. The original Theori blog really emphasized this too!!/p
pOne more thing worth noting: the reason the setting is stamped in both directions is to leave no gaps when routing the reply. But this pair hides a fun little asymmetry — would you believe me???/p
pimg src="/2026/07/19/gongjae/Wipeload_step5/EN/image6.png" alt="image"/p
ol
liWhen the thread dies, the code_ETHREAD/code gets deleted entirely, so the codeAlpcMessageId/code on the other side also disappears with it/li
liBut the WaitingThread planted on the message side stays right there as long as the message is alive/li
/ol
pThis asymmetry is exactly the starting point of the vulnerabi
Breaking Out of Chrome's Sandbox: A Native Messaging Backdoor Observed in Italy
A malicious Chrome extension observed in an Italian malspam campaign used Native Messaging to escape the browser sandbox, steal session data and execute remote PowerShell commands on Windows. This analysis explains the infection chain,
its command-and-control channel and the main detection opportu