Blackbird: Defeating PatchGuard, One Layer Deeper.
How do you maintain observability without sacrificing system stability? Diving into the depths of virtualization and extended page tables.
Read articleA complete Windows attack chain through OAuth account-linking abuse, stored browser navigation, SQLite extension loading, DPAPI credential recovery, and a writable SYSTEM service binary.
How do you maintain observability without sacrificing system stability? Diving into the depths of virtualization and extended page tables.
Read articleA long Windows Active Directory chain from LDAP injection and NTLM coercion through delegated ACL abuse, AD CS ESC3, protected-object inheritance repair, and S4U2Self U2U with RBCD.
Read articleWhat happens when an EDR trusts filenames, unauthenticated localhost traffic and world-writable kernel objects? Five vulnerabilities, two SYSTEM LPE's and an RCE, a driver load and a lot of assumptions that should never have crossed a security boundary.
Read articleA complete Windows attack chain through OAuth account-linking abuse, stored browser navigation, SQLite extension loading, DPAPI credential recovery, and a writable SYSTEM service binary.
Read articleSysWhispers, HellsGate, HeavensGate, SidewaysGate, SpoofGate, TFGate, DoomGate, whatever gate your tool is being detected before the initial handle fully opens. How do EDR's detect & deny direct and indirect syscalls?
Read articleHow does Blackbird make Windows lie to malware's faces? Most anti-analysis checks trust the kernel because they have no choice. Blackbird weaponizes this by modifying syscall, timing & registry return data, erasing VM-identifiers and much, much more.
Read articleThere's a reason security products avoid kernel hooking. They are fragile, build-sensitive, and BSOD prone. Advanced malware analysis demands the visibility they provide. This post delves into the hook engine behind Blackbird and the struggle developing it.
Read articleModern defensive tooling doesn’t need to see payloads to stop you, it only needs to see the call path. This post breaks down how Windows system calls are intercepted, how syscall stubs became signatures, and why ActiveBreach takes a fundamentally different approach.
Read articleEloquia is a Windows-based Hack The Box machine built around two Django applications, a vulnerable OAuth account-linking flow, and a custom Windows service with an unsafe executable ACL.
The route from the web applications to SYSTEM was:
state and allowed identity replacement;The target exposed IIS over HTTP and Windows Remote Management:
80/tcp Microsoft-IIS/10.0
5985/tcp Microsoft HTTPAPI/2.0
The initial redirect identified eloquia.htb. Following the site's OAuth flow exposed a second virtual host, qooqle.htb.
Eloquia was a Django article platform. Qooqle was a separate Django application acting as its OAuth provider. That separation immediately made the trust boundary between the two applications the most interesting part of the attack surface.
I registered an account on each application and created a Qooqle OAuth application using Eloquia's callback URL:
http://eloquia.htb/accounts/oauth2/qooqle/callback/
Two details made the flow exploitable.
First, authorization requests did not use a state value. That removed the normal binding between the browser session that initiated authorization and the callback that completed it.
Second, visiting the callback while already authenticated did not reject the operation or require confirmation. Instead, it replaced the Qooqle identity linked to the current Eloquia account. I confirmed this behavior against my own test account before involving the reviewer.
Articles accepted a stored <meta http-equiv="refresh">, and reported articles were reviewed by an authenticated privileged user. A static authorization code was not reliable because Qooqle codes expired after roughly 15 seconds.
I therefore used an attacker-controlled redirect endpoint. When requested, it generated a fresh authorization code for my Qooqle identity and immediately redirected the reviewer to:
http://eloquia.htb/accounts/oauth2/qooqle/callback/?code=<fresh-code>
The reported article contained the equivalent of:
<meta http-equiv="refresh" content="0;url=http://<redacted-lab-host>/oauth">
The reviewer loaded the article, followed the redirect, and completed the callback inside its existing Eloquia session. Its privileged Eloquia account was consequently relinked to my Qooqle identity.
Authenticating to Eloquia through that identity then returned the admin profile and exposed Django admin. This was not a conventional OAuth login bypass; it was an account-linking CSRF that replaced the external identity attached to an already authenticated privileged account.
Django admin exposed SQL Explorer at:
/dev/sql-explorer/play/
A version query identified SQLite 3.45.1:
SELECT sqlite_version() AS version;
The database path was visible through SQLite's pragma interface:
SELECT * FROM pragma_database_list;
-- C:\Web\Eloquia\db.sqlite3
The critical finding was that SQLite extension loading was enabled. Querying a nonexistent DLL reached the loader instead of returning not authorized:
SELECT load_extension('C:\nonexistent\test.dll');
That error distinction proved that SQL Explorer could reach SQLite's native extension mechanism.
The public article form validated banner uploads as images. The Django admin form for the same model, however, accepted an arbitrary file. This created a useful mismatch: the application exposed a writable path through admin while SQL Explorer provided a native DLL loader.
I compiled a small x64 SQLite extension exporting sqlite3_extension_init, gave it an image-style filename, and uploaded it through the administrative article form. The server stored the bytes unchanged beneath:
C:\Web\Eloquia\static\assets\images\blog\<uploaded-file>
Loading that local file through SQLite executed the extension:
SELECT load_extension(
'C:\Web\Eloquia\static\assets\images\blog\<uploaded-file>'
);
The resulting callback executed as the local eloquia\web account.
The important chain was not merely "upload a DLL." It required three independent weaknesses to line up:
The next useful secret was stored in the Edge profile belonging to the same Windows user.
Chromium-based browsers on Windows protect saved credentials in two stages:
Local State file stores an AES key wrapped with DPAPI;Login Data SQLite database stores individual password values encrypted with AES-GCM.Because my code was already executing under the live web token, CryptUnprotectData could unwrap the profile's AES key. I then used that key to decrypt the relevant AES-GCM entry from Default\Login Data.
The recovered account was:
User: Olivia.KAT
Pass: <redacted>
Olivia.KAT belonged to Remote Management Users. WinRM accepted the NTLM credential but refused shell creation in this instance, so I treated the credential as a local privilege boundary rather than relying on remote management.
The host ran a custom Failure2Ban Windows service from a Visual Studio debug directory:
C:\Program Files\Qooqle IPS Software\Failure2Ban - Prototype\Failure2Ban\bin\Debug\Failure2Ban.exe
Source code and service logs showed that a cleanup job restarted the service approximately every five minutes. The executable's ACL granted Olivia.KAT write access even though the service ran as SYSTEM:
ELOQUIA\Olivia.KAT:(I)(RX,W)
NT AUTHORITY\SYSTEM:(I)(F)
This is the classic writable service-binary condition with one complication: the executable remained locked while the service was running.
I prepared a minimal replacement executable that performed a harmless proof of SYSTEM-level file access. The replacement contained no general-purpose shell or persistence behavior.
WinRM would authenticate Olivia but would not create a shell. To obtain a normal primary process under that identity, I loaded a second SQLite extension as web. It used CreateProcessWithLogonW with the recovered credential to start the installed, allow-listed Python interpreter:
"C:\Program Files\Python311\python.exe" C:\Web\Eloquia\static\assets\images\blog\<redacted-script>
The Python process repeatedly attempted to open the service executable for writing and replace it. While the service was live, Windows returned sharing violations or PermissionError. During the next scheduled cleanup window, the service stopped briefly and the replacement succeeded after tens of thousands of attempts.
The cleanup job then restarted the replaced executable as SYSTEM, completing the privilege escalation.
The most important fixes are architectural:
state, bind callbacks to a server-side initiation record, and expire that record after one use;Eloquia was a strong example of how several individually understandable weaknesses can combine into a complete Windows compromise. The decisive step at each stage was identifying which security boundary the current primitive could cross next.