Thursday, June 18, 2026

Cannot Generate SSPI Context: The July RC4 Change That Breaks SQL Logins

Here's a fun one. In July, a Windows security change is going to walk up to some of your SQL Server logins and quietly stop trusting them. No SQL patch. No SQL CVE. Nothing in the error log pointing at the real culprit. Connections that worked on Monday just start failing on Tuesday, and the message you get -- if you get one -- sends you off chasing the wrong thing entirely.

The change is the death of RC4 as a Kerberos fallback, tracked as CVE-2026-20833. It has been rolling out in phases all year. July is the phase with no take-backs, and the final enforcement phase arrives with the July 2026 Patch Tuesday releases.

The symptom you will actually see

This is the cruel part. The thing breaking is Kerberos ticket encryption out at the domain controller, but SQL Server doesn't know that. What lands in your lap is the same tired, generic error that has meant a hundred different things for twenty years:

Cannot generate SSPI context.

Or a login that times out. Or an app pool that starts throwing connection failures at 9:05am because that is when the service account went looking for a fresh ticket. The symptoms vary, but none of them say 'RC4' anywhere. You will burn an hour on SPNs and DNS before even looking at Kerberos.

So let's get ahead of it. First, we need to clarify what is changing and then we need to find out whether any of your instances will be impacted.

What is changing, in three dates

For roughly two decades, Active Directory handed out Kerberos tickets encrypted with RC4 as a compatibility fallback. RC4 is cryptographically weak. An attacker who can obtain an RC4-encrypted service ticket can crack the service account password offline, on their own machine, where nothing you own ever sees a failed login or account lockout. Against a weak service account password, this can happen far faster than most administrators realize. That is the entire Kerberoasting playbook, and it is exactly why MSFT is finally pulling the fallback.

CVE-2026-20833 (CVSS 5.5, information disclosure) is the staged removal of that fallback. Three milestones matter:

Phase What happens Can you
roll back?
Jan 2026 Audit only. New System-log events warn you about at-risk tickets. RC4 still works. Nothing breaks yet. N/A
Apr 2026 KDC default flips to AES-only (0x18) for accounts with no explicit encryption type set. RC4 still allowed where explicitly configured. Yes
Jul 2026 Full enforcement. Audit mode is gone, the rollback registry key is retired, implicit RC4 fallback is removed from the KDC path for good. No

As of today, June 18th, the default has already flipped, but there is still an escape hatch. July takes that hatch away. After that, RC4 only works for an account where someone has gone out of their way to explicitly allow it, which is a thing you should be removing, not adding.

Why SQL Server is squarely in the blast radius

Every SQL Server using Windows Authentication over Kerberos has a Service Principal Name, the MSSQLSvc/ SPN, registered against the account running the service. That SPN is the thing a client uses to request a Kerberos ticket for your instance. Which means your SQL service account is, by definition, a Kerberoastable target -- any authenticated user in the domain can request a service ticket for that SPN. No admin rights required.

Most modern service accounts are fine. An account picks up its AES keys the moment its password is set in a domain at the Server 2008 functional level or higher -- which, since AES-SHA1 shipped with Server 2008, has been the floor for close to 18 years. The accounts that will break in July are the stragglers whose passwords predate that:

The accounts that will fail silently

An old service account explicitly pinned to RC4-only. A downlevel client or appliance that only knows how to ask for RC4. A service account whose msDS-SupportedEncryptionTypes is something legacy and nobody has touched since the box was built on Server 2008. A linked server hop where the far end is the weak link. These are the connections that worked yesterday and will not work after enforcement, with nothing in the SQL error log to tell you why.

The point is not panic. Most of your estate is probably clean. The point is that the failures are silent and the deadline is real. The July updates that flip enforcement start shipping on Patch Tuesday, July 14, and your domain controllers cross the line whenever they take that update. So you want to know now, rather than find out from a 5AM phone call the morning after your DCs patched.

Find out if you are exposed -- the SQL-native check first

Start where you live. This query tells you which sessions on an instance are riding on Kerberos right now, which is exactly the set of connections that depend on the SPN and ticket encryption that is about to be enforced.

-- Who is authenticating via Kerberos right now?
SELECT
    s.session_id,
    s.login_name,
    c.auth_scheme,          -- KERBEROS, NTLM, or SQL
    c.net_transport,
    c.client_net_address
FROM sys.dm_exec_connections c JOIN sys.dm_exec_sessions s
  ON c.session_id = s.session_id
WHERE c.auth_scheme = 'KERBEROS'
ORDER BY s.login_name;

Anything coming back as KERBEROS is leaning on the machinery that changes in July. Here's a one-liner just for your own connection:

SELECT auth_scheme
FROM sys.dm_exec_connections
WHERE session_id = @@SPID;

This will return different values -- KERBEROS, NTLM, SQL. KERBEROS means the connection is riding the exact machinery being enforced in July. NTLM means Kerberos was not used for this connection. Sometimes that is by design -- connecting by IP instead of the FQDN, or an environment with no SPNs set -- and sometimes it points at a missing or misregistered SPN. Either way it is not in scope for July. SQL means SQL Authentication, so this change does not touch it at all.

Now go check the account itself

The sys.dm_exec_connections DMV tells you which sessions are on Kerberos. Active Directory tells you whether the service account behind them can actually negotiate AES. Find the SQL service account's SPNs and its current encryption types:

# List the MSSQLSvc SPNs registered to the service account
setspn -L DOMAIN\svc_sql

# What encryption types does AD think this account supports?
Get-ADUser -Identity 'svc_sql' -Properties 'msDS-SupportedEncryptionTypes' |
    Select-Object Name, 'msDS-SupportedEncryptionTypes'

Read the result like this:

Value Meaning
null or 0 No explicit setting -- inherits the domain default, which is now AES-only. Fine IF the account has AES keys. Worth confirming.
4 (0x4) RC4 explicitly allowed. This is the one to fix.
24 (0x18) AES128 + AES256. This is where you want to be.

Checking one account's attribute only tells you about that one account. The Windows event logs on your domain controllers see everything -- clients, appliances, and downlevel boxes you might not even think about checking. There are two logs to look at, and they tell you different things.

One thing up front: these are logs on the domain controllers, not on your SQL boxes. If you do not have rights to read DC event logs -- and as a DBA you may not -- this is the part you hand to your AD team. You can open these in Event Viewer by hand, but the fastest way to filter them is PowerShell, run on the DC. The commands below are PowerShell.

Start with the Windows System Event log. The January update added new RC4 warning events here, and the DC writes one whenever it handles a ticket that will break once enforcement is complete. This pulls the most recent 50 of them:

# PowerShell, run on a domain controller
# Pulls the new RC4 warning events the January update added to the System log
Get-WinEvent -FilterHashtable @{ LogName='System'; Id=201,202,206,207 } -MaxEvents 50 |
    Format-List TimeCreated, Id, Message

Each result has an event ID and a Message. All four IDs are the same family of RC4 warnings, but read 202 and 207 first -- the Message on those names a specific at-risk service account, which is exactly what you are hunting. 201 and 206 are broader warnings about the same activity, with no single account called out.

The System event log tells you what the DC flagged. The Security event log lets you watch RC4 happen in real time. Events 4768 and 4769 are the routine Kerberos ticket requests every DC logs, and each one records the cipher that was actually issued in its 'Ticket Encryption Type' field. A value of 0x17 in that field is RC4 caught in the act. This pulls recent ticket requests and keeps only the RC4 ones:

# PowerShell, run on a domain controller
# Keeps only the 4768/4769 ticket events whose encryption type is RC4 (0x17)
Get-WinEvent -FilterHashtable @{ LogName='Security'; Id=4768,4769 } -MaxEvents 200 |
    Where-Object { $_.Message -match '0x17' } |
    Format-List TimeCreated, Id, Message

In the results, the account pulling the RC4 ticket is in the Message -- the 'Account Name' field for a 4768, the service in 'Service Name' for a 4769. Any account that turns up in either query is one to fix before July.

The fix -- and whose job it is

Here is the important part: the fix lives in Active Directory, not in SQL Server. As the DBA, your job is to find the exposed SQL service account and hand the AD team a specific, actionable request. Unless you're also the AD admin, you shouldn't be running commands against the DC yourself. You want to know if this will impact you and what has to be done.

There are two pieces to the remediation, and both are AD-side:

1. Allow AES on the account. The account's msDS-SupportedEncryptionTypes attribute needs to be set to 0x18 (AES128 + AES256). For reference, that is a one-line change your AD admin would make:

# For the AD team, not the DBA -- run by AD against the service account
Set-ADUser -Identity 'svc_sql' -Replace @{ 'msDS-SupportedEncryptionTypes' = 0x18 }

2. Make sure the account has AES keys. Allowing AES does nothing if the account has no AES keys to hand out, and those keys only get generated when the password is set in a domain at the Server 2008 functional level or higher. For an account whose password has changed in recent memory, this is already done. For an account that predates AES key generation, the AD team typically resets the service account password so Active Directory can generate fresh AES keys for the account.

The password reset is the piece that reaches back into your world, so it needs to be coordinated. It is a real service account. The new password has to be updated wherever the SQL Server service uses it, the service restarted, and authentication confirmed afterward. That is a planned, change-controlled maintenance task across two teams. Do it in a lower environment first.

One anti-pattern to name and shame: Yes, you can buy yourself time by explicitly setting an account back to RC4 (0x4) -- but don't. That is putting the weak cipher back specifically so the attacker's offline crack keeps working. The whole point of July is to make that impossible by default. Fix the account properly instead of pinning it to the thing being removed.

The reason this lands now

July 14 is a genuinely rough day on the SQL calendar. SQL Server 2016 reaches end of support that day, and the Kerberos RC4 hardening moves into its enforcement phase the same month. For RC4-dependent accounts, MSFT's own framing is the right one to borrow: you have weeks, not months.

This is a calendar problem, not a fire. Calendar problems are the ones you can get in front of. Run the Kerberos query, check your service accounts, fix the stragglers, and let July be boring on the database tier.

More to Read

Microsoft Support: Manage Kerberos KDC usage of RC4 (CVE-2026-20833)
AskDS: What is going on with RC4 in Kerberos? (the at-risk event IDs)
Microsoft/Kerberos-Crypto: detection scripts on GitHub
Microsoft Learn: Detect and remediate RC4 usage in Kerberos

Tuesday, June 16, 2026

Fast Is Not Ready: What the AI Data Shows

There is a slide making the rounds in my world right now. You have probably seen a version of it. It promises a live AI agent in production, a validated ROI model, and a scale roadmap. Six weeks. Fixed price. The design is clean, the language is confident, and the offer is hard to argue with.

I want to clarify something before I go further. I am not anti-AI. I use it every working day, often inside the very SQL Server work that pays my bills. Where it lands on a real problem with real data, it earns its place at the table. This is not a 'STOP AI!' post.

My concern is more focused, and I think it is harder to dismiss. It is the speed. We are wiring these systems into production faster than we are building the boundaries that production demands. And the reason we are moving this fast is that someone has made moving fast look easy, packaged it up, and put a price on it. That package is worth a hard look before we talk about what it leaves behind.

What the packaging promises

The pitch is consistent across vendors -- A high-value use case, fully deployed. A cost model your CFO can sign. A backlog of the next ten use cases, ready to go. All of it on a timeline measured in weeks rather than quarters, and all of it for a number you agree to up front.

I understand why leaders say yes. It is a clean answer to a messy mandate. When the directive from the top is 'we must do AI now,' a fixed-price sprint to production looks like progress you can put on a slide. The trouble is that the slide and the outcome don't always follow the same path.

The door we are leaving open

Every agent stood up in a hurry is a new door into the environment, and too many of them are being hung without locks -- and the people who make a living finding unlocked doors have definitely noticed. Consider what has already happened, in production, to organizations far better resourced than most.

In June 2025, researchers disclosed a flaw in Microsoft 365 Copilot they named EchoLeak (CVE-2025-32711, rated 9.3 critical). It was a zero-click attack. An outsider sent a single crafted email, and when Copilot did the helpful thing and read it, hidden instructions inside told the assistant to go find sensitive material across OneDrive, SharePoint, Teams and Outlook and quietly ship it out. The victim clicked nothing. There was no malware to scan for, because the payload was plain English. Microsoft patched it and reported no exploitation in the wild, but the lesson should still stand: the assistant became an insider threat, and it never knew it.

Or consider the coding agent that, in 2025, deleted a production database despite instructions not to modify production systems, then produced inaccurate information about the recovery state. You can read about it here. No attacker required. The agent simply had more authority than judgment.

These are not edge cases. The OWASP project that tracks AI security risks used to catalog threats as things that might happen. Its current edition catalogs them as CVEs, vendor advisories and breach reports -- in other words, things that already happened. That is the part the vendor's six-week timeline never mentions. An agent that can read your data and act on your systems is a privileged user, and we are handing out that privilege faster than we are regulating who, what or how, it is managed.

What the data says

MIT's NANDA initiative studied 300 enterprise AI deployments. Roughly 95 percent of generative AI pilots produced no measurable impact on the bottom line. Only about 5 percent reached real, scaled value. I will be fair about that headline. It has been widely cited, though not without criticism of the methodology. But even the people pushing back tend to agree on the underlying point, which is that most pilots stall long before they reach production. You can read the coverage here.

Gartner is more direct, and more relevant, because the slide I described is selling agentic AI specifically. Gartner predicts that more than 40 percent of agentic AI projects will be canceled by the end of 2027. The reasons it gives are "escalating costs, unclear business value or inadequate risk controls." The full prediction is here.

Gartner even has a name for part of the problem, 'agent washing'. The practice of rebranding ordinary chatbots and automation as autonomous agents. By its count, of the thousands of vendors claiming agentic AI, Gartner estimates only about 130 of them are real. Most of what is being sold as an agent is an old tool wearing a new label.

The failures are not about the model

This is the part that should matter to anyone who runs systems for a living. Read those reasons again. Escalating costs. Unclear business value. Inadequate risk controls. Not one of them says the AI is too dumb. The pilots are not failing on intelligence. They are failing on the pieces that were missed: data that was never cleaned, integration that was never scoped, governance that was never written, controls that were never tested.

Those are boundaries, and a six-week, fixed-price sprint to production is, almost by definition, a sprint past those boundaries. You cannot clean the data, prove the ROI, establish governance, test the controls, and build the system all in the same six-week sprint. Something gets deferred. In many organizations, it is the part that was supposed to keep everyone out of trouble. In a regulated environment, the things that get cut are usually the same things that surface in an audit eighteen months later, long after the launch party.

What the 5 percent did differently

Here is the encouraging part of the story. The organizations that got real returns were not the fastest. They were the narrowest. They picked one painful, well-understood problem instead of trying to transform everything at once, and they fixed their data before they pointed a model at it. They also tended to partner rather than build everything in-house, and they often started in the boring back office rather than the flashy customer-facing demo -- where the value was quiet but real.

None of that fits on a six-week timeline, and none of it markets well. Patience is the least sellable quality in anybody's board room. It is also, apparently, the one that correlates with success.

Why I am writing this down

I am not asking anyone to slow the technology. At this point, I doubt anyone could if they tried. I am asking that we let the boundaries keep pace with it. Governance, data quality, risk controls, and a human who can still read the audit trail -- these are not friction. On the evidence above, they are the entire difference between the 5 percent and the 95 percent.

The technology itself is going to be fine. It always is. My worry is for the organizations that bet the timeline before they build the guardrails, because that bill always comes due, and it does not come due on the quarter the slide was presented. Fast is exciting. Fast is sellable. But fast is not the same thing as ready. The organizations that succeed will not be the ones that moved first. They will be the ones that understood a very simple truth: Powerful systems require equally powerful controls.

xp_cmdshell: The Door Is Closed by Default. Keep It Shut.

This year's SQL Server CVEs have a common shape. CVE-2026-21262 (March Patch Tuesday, CVSS 8.8) lets an authenticated, low-privileged login climb to sysadmin over the network. That sounds bad on its own, but sysadmin is not the finish line for an attacker. It's just the on-ramp. The exit ramp is xp_cmdshell, and that is the piece that turns 'they got into the database' into 'what they ran under your service account'.

If you run managed services, you are not patching one instance, you are patching a fleet, and somewhere in that fleet is an instance where this gate is open and nobody noticed. Let's look at the gate and how to verify it is shut across the servers you manage.

The gate is closed by default

Since SQL Server 2005, xp_cmdshell ships disabled. Call it on a clean instance and you get the classic refusal:

EXEC xp_cmdshell 'whoami';
Msg 15281, Level 16, State 1, Procedure sys.xp_cmdshell, Line 1 [Batch Start Line 0]
SQL Server blocked access to procedure 'sys.xp_cmdshell' of component 'xp_cmdshell' because this 
component is turned off as part of the security configuration for this server. A system administrator 
can enable the use of 'xp_cmdshell' by using sp_configure. For more information about enabling 
'xp_cmdshell', search for 'xp_cmdshell' in SQL Server Books Online.

Good. The door is shut as it should be. The problem is how easy the door is to open when you're sysadmin, and how many times it's probably already been opened by some long-departed vendor install script.

Check the current state

Before anything else, find out where you actually stand. sys.configurations tells you both the saved value and the running value:

SELECT name,
       value,
       value_in_use,
       description
FROM   sys.configurations
WHERE  name = 'xp_cmdshell';

Of course, this is my server, so it's 1. That means xp_cmdshell is enabled. 0 would mean disabled. On your servers, if this comes back 1 and you did not put it there, that is an immediate concern.

Why this is a risk

Here is a demo so you can see exactly why this matters. Since my server already shows 1, I'll walk through how it gets opened in the first place. Enabling it takes four lines:

EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1;
RECONFIGURE;

And now the door is open.

EXEC xp_cmdshell 'whoami';

Look at that closely. The command did not run as the login that called it. It ran as the SQL Server service account. Whatever that account can touch on the host and the network, the attacker can now touch, from a T-SQL prompt, no shell access required. Swap whoami for anything the service account can reach and you understand the rest of the story. The exposure. The risk.

Who runs as what

The execution context depends on who is calling, which is worth keeping straight:

Caller Command runs as
sysadmin member SQL Server service account
non-sysadmin (proxy configured) xp_cmdshell proxy credential
non-sysadmin (no proxy) Cannot execute

This is why CVE-2026-21262 is more than 'just' a privilege escalation. The climb to sysadmin hands an attacker the first row of that table, and the first row is the host.

Catch it being flipped on

A point-in-time check is fine, but you want to know when the value changed, not just that it is wrong today. Every sp_configure change gets written to the error log. Pull it straight out:

EXEC xp_readerrorlog 0, 1, N'xp_cmdshell';

We're looking for any lines recorded in the log including 'xp_cmdshell'. Mine was already at 1, so I flipped it to 0 to gen the image for this post. The point is that you can see the timestamp when the change was made. For something more durable than the rolling error log, a Server Audit capturing server configuration changes will keep the record where log cycling cannot quietly erase it.

Sweep the whole inventory at once

One instance at a time does not scale when you manage dozens. dbatools turns the same check into a single pass across every registered server:

Get-DbaSpConfigure -SqlInstance $instances |
    Where-Object Name -eq 'XpCmdShellEnabled' |
    Select-Object SqlInstance, ConfiguredValue, RunningValue

Any server where the running value is 1 goes to the top of your review list.

Shut the door and keep it shut

If nothing legitimately needs xp_cmdshell, the cleanup is the same shape as the setup, in reverse:

EXEC sp_configure 'xp_cmdshell', 0;
RECONFIGURE;
EXEC sp_configure 'show advanced options', 0;
RECONFIGURE;

Beyond that toggle, the boring controls are the ones that hold. Keep the SQL Server service account on a least-privilege footing so that even if the door is opened, the service account is not a domain admin waiting to be borrowed. Do not expose the instance to the internet, because the scanners already know your port is there. And patch the escalation paths, because the whole reason xp_cmdshell is a back half of an attack is that something else got the attacker to sysadmin first.

The door is closed by default purposely. Your job is to keep it closed, or IF it needs to be opened, be sure to close it properly afterward.

Applies to SQL Server 2005 through 2025. xp_cmdshell has been disabled by default the entire time, which means anywhere you find it on was a deliberate act, by someone.

More to Read

xp_cmdshell Server Configuration Option (Microsoft Learn)
xp_cmdshell (Transact-SQL) (Microsoft Learn)
Create a Server Audit and Server Audit Specification (Microsoft Learn)

Monday, June 15, 2026

Kali365: The FBI Just Warned You About Your Azure SQL Login Screen

The FBI put out a PSA this month about a phishing kit called Kali365. And, if you've ever connected to Azure SQL with Entra authentication, you have already stared at the exact screen this whole attack depends on.

To sign in, use a web browser to open the page https://microsoft.com/devicelogin
and enter the code A1B2C3D4E to authenticate.

Look familiar? That is the Microsoft OAuth device code flow. It is legitimate, it is everywhere, and Kali365 weaponizes it without ever touching your password.

What Kali365 Actually Does

It is a subscription service. First spotted in April 2026, sold over Telegram for roughly $250 a month or $2,000 a year, marketed at people who could not write a phishing kit themselves. For that money they get AI-generated lures, automated campaign templates, live targeting dashboards, and the piece that matters -- the OAuth token capture.

Instead of stealing a password, the attacker tricks the victim into completing a sign-in the attacker started. Microsoft then issues access tokens to the attacker's session, already satisfying MFA requirements. The result is access to Outlook, Teams, OneDrive, and other Microsoft 365 resources without ever knowing the victim's password.

Why This Should Make a DBA Twitch

Because you do this on purpose, all the time. Connect to an Entra-backed Azure SQL database with device code auth and SQL Server tells you to go enter a code at a Microsoft page:

sqlcmd -S myserver.database.windows.net -d mydb -G ^
  --authentication-method=ActiveDirectoryDeviceCode

You get a code, you open microsoft.com/devicelogin to type it in, and you are connected. SSMS does the same thing under 'Azure Active Directory - Device Code Flow'. The flag name varies a bit by sqlcmd version, but the muscle memory is identical, and that muscle memory is exactly what the attack is counting on.

Step You, connecting to Azure SQL You, getting phished
1 You run sqlcmd / SSMS Attacker starts a sign-in for your tenant
2 Microsoft returns a device code Attacker emails you that code
3 You open microsoft.com/devicelogin You open microsoft.com/devicelogin
4 You enter the code, you get in You enter the code, they get in

Same page. Same code box. Same real Microsoft domain. But two things change: who started the sign-in and who gets in.

There Is Nothing to Misspell

This is what makes it nasty. Every 'spot the phish' habit we have trained on is about catching a fake. The wrong domain, the off-color logo, the cert warning. Here there is no fake. The page is genuinely microsoft.com. The token grant is genuinely Microsoft issuing a token. The only lie in the entire transaction is the unspoken assumption that you are the one who kicked it off. You are not. The attacker is, and you are politely finishing their login for them.

Hunt It in Your Own Tenant

Want to know if this already happened in your tenant? The Microsoft Graph PowerShell module will tell you. You need the module installed and access to Entra sign-in logs. Available retention periods and reporting capabilities depend on your licensing tier.

# One-time: install the module
Install-Module Microsoft.Graph -Scope CurrentUser

# Connect with just the scope you need
Connect-MgGraph -Scopes 'AuditLog.Read.All'

Now pull the last 7 days of sign-ins and keep only the ones that used the device code flow. I filter client-side on AuthenticationProtocol because the server-side $filter does not reliably support that property across tenants:

$start = (Get-Date).AddDays(-7).ToString('yyyy-MM-ddTHH:mm:ssZ')

Get-MgAuditLogSignIn -Filter "createdDateTime ge $start" -All |
    Where-Object { $_.AuthenticationProtocol -eq 'deviceCode' } |
    Select-Object CreatedDateTime, UserPrincipalName, AppDisplayName, IpAddress,
        @{ N = 'City';    E = { $_.Location.City } },
        @{ N = 'Country'; E = { $_.Location.CountryOrRegion } } |
    Sort-Object CreatedDateTime -Descending |
    Format-Table -AutoSize

A clean tenant gives you either nothing or a short list you recognize, with your own admin box connecting to Azure SQL (sample output, values are illustrative):

CreatedDateTime       UserPrincipalName     AppDisplayName              IpAddress      City     Country
--------------------  --------------------  --------------------------  -------------  -------  -------
2026-06-14 09:12:03   dba@contoso.com       Microsoft Command Line ...  203.0.113.10   Cozumel  MX

What you are hunting for is the row nobody can explain. A sign-in no one on your team started, from somewhere you do not operate, often against an app you never deploy:

CreatedDateTime       UserPrincipalName     AppDisplayName              IpAddress      City     Country
--------------------  --------------------  --------------------------  -------------  -------  -------
2026-06-13 02:47:55   finance@contoso.com   Microsoft Authentication..  185.220.101.4  Unknown  RO

Device code flow is something developers and admins use to log in from places a browser cannot easily go. So when a finance mailbox completes one at 3 AM from a country you don't operate in, that is the tell. Nobody on your team started that login. Someone else did, and your user finished it for them.

What To Actually Do

Never finish a sign-in you did not start.

A device code is only safe when you generated it yourself. A code that arrives by email or Teams that you did not ask for is bait, every single time. There is no legitimate reason for someone to send you one in this fashion.

Admins: block the flow if you do not need it.

Microsoft Entra Conditional Access can block the device code flow tenant-wide. Most organizations use it in a handful of narrow spots, so a default block with a tight exception list could shut a lot of this down. If your team only hits device code auth for the odd Azure SQL connection, you might consider whether you even need it enabled broadly.

If you got caught, report it.

The FBI is collecting these at the Internet Crime Complaint Center. Revoke sessions, rotate where you can, and then file.

The convenience that lets you authenticate to a database from a headless box with no browser is the same convenience that lets a $250 kit drain a mailbox. The flow is not the villain. Typing a code you did not ask for is.

More to Read

FBI IC3 Public Service Announcement on Kali365
Bitdefender: Kali365 phishing kit breaks Microsoft 365 accounts, no password required
Arctic Wolf: Token Bingo, don't let your code be the winner
Internet Crime Complaint Center (file a complaint)

Saturday, June 13, 2026

Cursed SQL, Part Two: Six Ways NULL Lies to Your Face

A week ago I wrote about six queries that run fine until they don't. Cursed SQL. That one struck a nerve, so here's the sequel. Same idea, but this time NULL is the one villain behind every example, and the three-valued logic that rides along with it.

These queries are worse in a quieter way. They don't run slow. They never throw an error. They just give you the wrong answer every time, and SQL Server never says a word. That is the curse.

First, the One Rule

SQL Server does not deal in just true and false. It uses a three-valued logic system, or '3VL', in which conditions must evaluate to TRUE, FALSE, or UNKNOWN. That third one comes to the table whenever NULL is involved in a comparison because NULL is not a value. NULL is not zero and it does not equal anything -- not even another NULL. Any direct comparison to NULL always evaluates to UNKNOWN. Never TRUE or FALSE. Just UNKNOWN.

That single rule is behind all six curses below. Keep it in your pocket and you'll start to see them coming.

The Setup

An Employee table with a few NULLs planted exactly where real data goes missing. A CEO with no manager, an employee not yet assigned a department, and an employee missing their salary. Simple omissions that happen all the time.

CREATE TABLE dbo.Employee
(
    EmployeeID  INT IDENTITY(1,1) PRIMARY KEY,
    FullName    NVARCHAR(100) NOT NULL,
    ManagerID   INT           NULL,   -- the CEO reports to no one
    Department  NVARCHAR(50)  NULL,   -- some not yet assigned
    Salary      DECIMAL(10,2) NULL    -- some withheld
);

INSERT dbo.Employee (FullName, ManagerID, Department, Salary)
VALUES (N'Dana Reyes', NULL, N'Executive', 250000),  -- CEO, no manager
       (N'Sam Carter', 1,    N'Sales',     90000),
       (N'Priya Nair', 1,    N'Sales',     NULL),     -- salary withheld
       (N'Tom Becker', 1,    NULL,         75000),    -- department not assigned
       (N'Lena Frost', 2,    N'Sales',     82000);

See our data with the NULLs:

Curse 1: NOT IN Hits a NULL and Returns Nothing

You're reporting for HR and you want to know which Departments have nobody assigned yet. So you pass the department values into your query and ask the system which ones don't exist in the Employee table.

-- Which departments have nobody assigned? (ie., the empty ones)
SELECT DeptName
FROM (VALUES (N'Executive'), (N'Sales'), (N'Engineering')) AS d (DeptName)
WHERE DeptName NOT IN (SELECT Department FROM dbo.Employee);

Dana is in Executive, three people are in Sales, and nobody has been assigned to the Engineering department yet. So that's the one you expect in the return, but you get this instead:

Nothing. Why isn't Engineering returned?

Here's the problem, step by step. First, we must recognize that the subquery actually returns every Department value in the table:

One of those values is NULL because Tom has not been assigned a department yet, and that NULL is the entire problem. Here's how NOT IN works: it takes a row's DeptName and compares it to every value in that list with 'not equal' (<>), joining the results with AND. So this:

DeptName NOT IN ('Executive', 'Sales', NULL)

Actually translates to this:

DeptName <> 'Executive'  AND  DeptName <> 'Sales'  AND  DeptName <> NULL

A result to that query only survives if its DeptName is not equal to all of them.

Now walk the Engineering row through it -- the one you actually expected back. For that row, DeptName is 'Engineering'. It is not equal to Executive or Sales, so both comparisons pass... but then it hits the NULL:

'Engineering' <> 'Executive'  ->  TRUE
'Engineering' <> 'Sales'      ->  TRUE
'Engineering' <> NULL         ->  UNKNOWN   (any compare to NULL is UNKNOWN)

TRUE AND TRUE AND UNKNOWN      ->  UNKNOWN

That's what gets you. Engineering genuinely has no employees, so without that NULL in the list, it would have been returned. But remember, TRUE + TRUE + UNKNOWN = UNKNOWN. Not TRUE. A row will only be returned when its WHOLE chain evaluates to TRUE, and that UNKNOWN is why your result came back empty.

Microsoft says so right in the docs: "Any null values returned by subquery or expression that are compared to test_expression using IN or NOT IN return UNKNOWN."

The fix is to stop using equality for this. NOT EXISTS tests for the existence of a matching row, not equality against a list, so a stray NULL doesn't poison it.

-- Fix 1: NOT EXISTS is immune to the NULL trap
SELECT d.DeptName
FROM (VALUES (N'Executive'), (N'Sales'), (N'Engineering')) AS d (DeptName)
WHERE NOT EXISTS (SELECT 1
                  FROM dbo.Employee e
                  WHERE e.Department = d.DeptName);

-- Fix 2: keep NOT IN, but strip the NULLs out of the subquery
SELECT DeptName
FROM (VALUES (N'Executive'), (N'Sales'), (N'Engineering')) AS d (DeptName)
WHERE DeptName NOT IN (SELECT Department
                       FROM dbo.Employee
                       WHERE Department IS NOT NULL);

Both return Engineering. Make NOT EXISTS your habit. It sidesteps this entirely, and often gets you better performance as a bonus.

Curse 2: The Inequality That Silently Drops the Row You Wanted

You want everyone who is not in Sales. Reasonable.

SELECT FullName, Department
FROM dbo.Employee
WHERE Department <> N'Sales';

You expected Dana in Executive and Tom, whose department is not assigned yet, but you get only Dana. Tom vanishes, because NULL <> 'Sales' is UNKNOWN, not true, and his row never qualifies. The employee with no department -- arguably the exact person you were hunting for -- silently falls out of the 'not in Sales' report. The NULL rows fall through the cracks and your data set is incomplete.

Use this method to account for any NULL values, and Tom comes back:

SELECT FullName, Department
FROM dbo.Employee
WHERE Department <> N'Sales' OR Department IS NULL;

Curse 3: The Average That Quietly Skips a Person

This one hands your boss a wrong number that looks completely right. You want the team's average salary -- one figure for a comp review.

SELECT AVG(Salary)   AS AvgSalary,
       COUNT(*)      AS HeadCount,
       COUNT(Salary) AS PaidCount
FROM dbo.Employee;

Five people on the team, but watch the counts. COUNT(*) returns 5 -- every row. COUNT(Salary) returns 4 -- only the rows with a salary. Priya's is NULL, and AVG skipped her, because aggregates ignore NULL instead of treating it as zero. So your 'average' added up four salaries and divided by four, not five. The figure looks fine, but it quietly left a person out.

SQL Server does warn you, exactly once, in a message most tools tuck out of sight:

Warning: Null value is eliminated by an aggregate or other SET operation.

That warning is the entire story. A row was removed from your math and the engine mentioned it on the way out. The point is not that one number is right and the other wrong. The point is that the default decided for you. If a withheld salary should count as zero, say so:

SELECT AVG(ISNULL(Salary, 0)) AS AvgSalary_NullAsZero
FROM dbo.Employee;

Same data, divided by five this time, on purpose. Different answer, and now it's mathematically correct.

Curse 4: One NULL Poisons the Whole String

You want a tidy label for each employee.

SELECT FullName + ' - ' + Department AS Label
FROM dbo.Employee;

Four rows look fine, but what happened to Tom? He came back as NULL. Curious why it didn't come back as 'Tom Becker - ' ? The entire string collapsed to NULL. This happens with the + operator. If any piece is NULL, the whole result becomes NULL. That is CONCAT_NULL_YIELDS_NULL doing its job, and since SQL Server 2017 it is always ON -- you can't turn it off anymore. So a single missing department wipes out the entire label.

The fix is CONCAT, which treats NULL as an empty string instead of a contaminant. Or guard each piece yourself if you'd rather show a placeholder.

-- CONCAT() ignores NULLs, treating them as empty strings
SELECT CONCAT(FullName, ' - ', Department) AS Label
FROM dbo.Employee;

Or you can use a default to show a placeholder:

SELECT FullName + ' - ' + ISNULL(Department, N'(unassigned)') AS Label
FROM dbo.Employee;

CONCAT gives you 'Tom Becker - ' with an empty tail; the ISNULL version gives you 'Tom Becker - (unassigned)'. CONCAT arrived in SQL Server 2012, so it's most likely a non-issue on anything you're running today.

Curse 5: = NULL Is Not IS NULL

You want to list all employees with no department assigned.

SELECT FullName
FROM dbo.Employee
WHERE Department = NULL;

Zero rows. But what about Tom? Remember, Department = NULL means UNKNOWN for every row, including the ones that actually are NULL, because nothing equals NULL. Not even NULL equals NULL. The right tool is the predicate built for the job:

SELECT FullName
FROM dbo.Employee
WHERE Department IS NULL;

Now Tom comes back. Years ago, with ANSI_NULLS OFF, = NULL would behave like IS NULL, but that setting is deprecated and ANSI_NULLS has been forced ON since SQL Server 2017. Don't lean on it. Use IS NULL and trust your results.

Curse 6: The CHECK Constraint That Waves NULL Right Through

This is the sneaky one, and most people don't know it. You add a guard rail to keep salaries positive.

ALTER TABLE dbo.Employee
ADD CONSTRAINT chk_salary_positive CHECK (Salary > 0);

It creates without question or error -- even though Priya already sits in the table with a NULLable salary. That should be your first clue. Now insert a brand new row with no salary at all:

INSERT dbo.Employee (FullName, Salary) VALUES (N'Ghost Worker', NULL);

It goes right in. The constraint you wrote to guarantee a positive salary just accepted a missing one. Here is why: a CHECK constraint rejects a row only when the condition evaluates to false. NULL > 0 is UNKNOWN, not false, so the row is allowed. CHECK treats true and UNKNOWN as the same verdict -- both pass. Your 'salary must be positive' rule quietly permits 'salary is missing entirely'.

The fix is to say what you actually mean. If a salary is required, the column belongs NOT NULL (clean the data first, then alter it). If NULL is genuinely allowed but any real value must be positive, spell that out in the constraint itself:

-- clear the ghost worker record
DELETE dbo.Employee WHERE FullName = N'Ghost Worker';

-- drop constraint 
IF OBJECT_ID('chk_salary_positive') IS NOT NULL
ALTER TABLE dbo.Employee DROP CONSTRAINT chk_salary_positive;

-- clean up the data with null salary
DELETE dbo.Employee WHERE Salary IS NULL;        -- this clears Priya too

-- make column not null
ALTER TABLE dbo.Employee ALTER COLUMN Salary DECIMAL(10,2) NOT NULL;

-- above prevents NULL, this enforces that all values are positive
ALTER TABLE dbo.Employee
ADD CONSTRAINT chk_salary_positive CHECK (salary > 0);

-- Now try that insert again
INSERT dbo.Employee (FullName, Salary) VALUES (N'Ghost Worker', 00000.00)

The lesson holds for every CHECK you write. The constraint does not enforce what you assume. It enforces what evaluates to false. The corrected constraint now blocks zero and negatives, and NOT NULL is what handles the missing values.

In Summary

Six shapes, one root cause. NULL means 'I don't know', 3VL logic is how the engine handles it, and UNKNOWN is the value that falls through the cracks more often than not. None of these throw errors or run slow. They just produce the wrong answers with every call.

The fixes aren't exotic. Pretty simple, actually. Reach for NOT EXISTS instead of NOT IN. Account for NULL on both sides of an inequality. Decide what your aggregates should do with missing values instead of letting AVG decide for you. Use CONCAT over +. Write IS NULL, never = NULL, and remember that a CHECK constraint only ever blocks false. Get those right and the curse lifts. Now you've got six more shapes you can spot on sight.

More to Read

sqlfingers inc: Cursed SQL -- Six Queries That Run Fine Until They Don't
Microsoft Learn: IN (Transact-SQL)
Microsoft Learn: SET ANSI_NULLS (Transact-SQL)
Microsoft Learn: SET CONCAT_NULL_YIELDS_NULL (Transact-SQL)
Microsoft Learn: CONCAT (Transact-SQL)

Friday, June 12, 2026

Miasma. The Worm That Fires When You Open the Folder.

Last Friday, June 5th, GitHub yanked 73 Microsoft repositories offline in about 105 seconds. Not fringe stuff, either. They were repos across the Azure, Azure-Samples, Microsoft and MicrosoftDocs organizations, including Azure/durabletask and Azure/functions-action. The culprit is a self-replicating supply chain worm the researchers are calling Miasma, and this is the part that gets my attention: The payload fires when you open the repository in an AI coding tool or IDE. You don't have to run anything. You don't have to install anything. You just open the folder.

Maybe you're thinking 'I'm a DBA, not a developer, this isn't my problem', but stay with me. I'll explain why your scripts folder is exactly the kind of target this thing was built for.

What happened

An attacker used a previously compromised contributor account to push a malicious commit to the Azure/durabletask repository. The same account had already been used in May to publish three poisoned versions of the durabletask Python SDK to PyPI, so this was strike two from the same stolen identity. The commit didn't poison a package this time. Instead, it planted configuration files inside the repo itself -- the kind of files that AI coding assistants and modern IDEs execute automatically when a project is opened.

Open an infected repo in Claude Code, Gemini CLI, Cursor or VS Code, and a credential-harvesting payload runs immediately. It grabs tokens for GitHub, cloud platforms and developer tooling, then uses those stolen credentials to commit itself into every other repository the victim can write to. That's the worm part -- it spreads on its own, no human required.

GitHub's automated abuse detection caught it and disabled all 73 repositories in two sweeps over roughly 105 seconds. Microsoft has since reviewed and restored them. But the takedown itself caused collateral damage -- Azure/functions-action went dark, and every CI/CD workflow referencing it broke on the spot.

The three waves

Miasma has been active since June 1st, and it's evolving fast. It's a variant of the 'Mini Shai-Hulud' worm that was publicly released in mid-May, and it has already used three distinct attack vectors:

Wave Date Vector
1 June 1 Malicious npm packages with preinstall hooks
2 June 3 Malicious binding.gyp files that execute during npm install ('Phantom Gyp')
3 June 3-5 Malicious commits to GitHub repos; payload fires when repo is opened in an AI tool or IDE

Read that closely. Waves 1 and 2 still required an install step, but wave 3 only requires curiosity. Can you even measure the extent of that exposure?

Why DBAs should care

Think about what's sitting in your scripts folder right now. If you're like most working DBAs, you've got clones of community tooling -- dbatools, the First Responder Kit, Ola Hallengren's maintenance solution, plus a pile of your own repos. And increasingly, you're opening those folders in VS Code, or pointing an AI assistant like Claude Code or Copilot at them to refactor a script or troubleshoot a job.

Now think about what's next to those scripts. Connection strings. Saved credentials for dbatools sessions. SQL logins in config files. Azure service principal secrets. A harvested credential from a developer workstation is bad; a harvested credential from a DBA workstation is the keys to the data layer. A worm won't know the difference between a developer's GitHub token and your sysadmin-equivalent service account -- and it doesn't care. It takes everything it can reach.

I should say, this isn't a 'dbatools is compromised' post. None of the SQL Server community projects were among the 73 disabled repos. But the attack pattern is now public, and the worm's ancestor was published openly for anyone to adapt. For years the trust model has been simple -- clone it from GitHub, it's from a reputable org, it's fine. That model just took a direct hit. If Microsoft's own Azure org can host a poisoned commit for two weeks, any repo can.

The mechanics, briefly

Modern IDEs and AI coding tools support automation hooks, driven by configuration files inside the project. They run when a project opens -- VS Code tasks with folder-open triggers, Claude Code hooks, and similar mechanisms in other tools. These features exist for legitimate reasons (auto-build, environment setup), but they mean a repository is no longer just passive text. It can carry instructions that your tooling obeys the moment you open it.

In this campaign, the malicious commit added exactly those kinds of configuration files. Researchers also observed the payload establishing persistence through VS Code tasks and Claude Code hooks, and exfiltrating stolen data through channels like GitHub 'dead drop' repos.

What to do about it

1. Turn off automatic task execution in VS Code

This single setting closes the folder-open execution vector in VS Code. It's prompt-based by default in recent versions, but verify it -- and set it explicitly via settings.json:

"task.allowAutomaticTasks": "off"

2. Inspect before you open

Before opening any freshly cloned repo in an IDE or AI tool, look at it with something dumb first -- plain file explorer, notepad, a directory listing. The files that matter are the automation hooks. A quick PowerShell sweep of your scripts directory:

Get-ChildItem -Path 'C:\Scripts' -Recurse -Force |
    Where-Object { $_.Name -in ('tasks.json','settings.json','binding.gyp') `
        -or $_.FullName -like '*\.claude\*' `
        -or $_.FullName -like '*\.vscode\*' } |
    Select-Object FullName, LastWriteTime |
    Sort-Object LastWriteTime -Descending

You're not looking for these files to never exist. The .vscode folders are everywhere and almost always benign. You're looking for the files you didn't put there, or those with a LastWriteTime that doesn't match when you last touched the project.

3. Get the secrets out of the scripts folder

Maybe this incident is the push you needed. No connection strings, passwords or service principal secrets in repos or alongside them. Use Windows Credential Manager, Azure Key Vault, dbatools' Export-DbaCredential patterns -- or one of the gazillion different password managers out there to keep your passwords secure. Or at least out of the path that a folder-scanning payload could walk.

4. If you opened a Microsoft repo recently, assume exposure

The window of concern runs roughly May 20 through June 5. If you -- or any scheduled job or build server you own -- pulled or even just opened anything from the affected orgs in that window, you should consider rotating the credentials that machine could reach. GitHub tokens, cloud secrets, and yes, any SQL logins whose passwords were stored locally. Check your cloud environments for service principals you didn't create and outbound traffic you can't explain. The fact that this check has become a standard step in Cloud/AI security really should not be missed.

5. Pin your community tooling

Remember that scripts folder full of dbatools, the First Responder Kit and Ola's maintenance solution? This is where the trust-model problem becomes a daily-habit problem. Clone a specific release tag, not main. Review the diff when you update. The convenience of 'git pull and go' is exactly the behavior this worm is engineered to exploit.

The bigger picture

I wrote before about least privilege for AI agents -- the idea that an AI assistant should only be able to touch what it genuinely needs. Miasma is the other side of that same coin. The AI tooling isn't the villain here. The worm exploited automation features, and AI assistants happen to be the newest and most eagerly adopted automation there is. New actors, but the lesson is the same. Every tool that can act on your behalf is a tool an attacker can act through. Scope it down, watch what it opens, and trust none of them by default -- not even the ones from a trusted org.

GitHub caught this one in 105 seconds. The next one might not trip the alarms as fast. Spend the ten minutes on the settings and the secrets cleanup now, while it's a news story and not an incident report with your name on it.

More to Read

StepSecurity: Miasma Worm Hits Microsoft Again
The Register: GitHub nukes 70+ Microsoft repos amid suspected worm attack
The Hacker News: Miasma Worm Hits 73 Microsoft GitHub Repositories
Redmondmag: Supply Chain Attack Hits Microsoft GitHub Repos, AI Coding Tools

Wednesday, June 10, 2026

Fabric Is the Destination. Who's Ready?

A reader says to me yesterday very directly during a linkedin exchange: "All of the data leadership is focused on Fabric." Nine words. He is right and I will not argue the point. The evidence is everywhere. But 'where leadership is focused' and 'what works for the small shops' are two different things, and only one of them gets answered in the keynote. Fabric is the destination. The question nobody on stage is asking is who is actually ready to go there -- and what the costs may be for those who are not.

The Tell Isn't a Keynote. It's a Feature.

You do not have to read Microsoft's strategy in a press release. You can read it in what they built into the flagship database release. SQL Server 2025's headline analytics capability is not a faster engine or a smarter optimizer. It is Mirroring to Microsoft Fabric, and it went generally available alongside the release.

Here is what it does. You point Fabric at your SQL Server, pick the tables you want, and it continuously replicates them into OneLake -- Fabric's storage layer -- with no ETL, no pipeline, no orchestration. On SQL Server 2025, the engine itself scans the transaction log at high frequency and publishes the changes; Fabric merges them in near-real-time, as fast as every 15 seconds. On 2016 through 2022, it rides on Change Data Capture instead. Either way the data lands in OneLake as read-only Parquet, ready for analytics, BI -- and AI.

Read that again, because the direction is the whole story. The marquee analytics feature of the newest SQL Server is a one-click, zero-ETL pipe whose entire job is to copy your data out of SQL Server and into Fabric. When the headline of your flagship database is 'we made it trivial to send your data somewhere else', that somewhere else is where the value is being built.

One SQL, One Direction

The positioning also backs the plumbing. March 2026 brought the first-ever SQLCon -- run not on its own, but inside FabCon, framed as unifying databases and Fabric on a single platform. The database and the platform now share one stage and one story. And the momentum behind that story is not subtle. By Microsoft's own numbers, Fabric has passed a 2 billion dollar annual revenue run rate, serves more than 31,000 customers, and is growing about 60% year over year -- the fastest-growing analytics product they currently have.

Set that next to the other half of the picture. Fabric is now the named successor to Azure Synapse. Synapse is still supported, but Microsoft has been clear that all new analytics innovation is going into Fabric, and specific Synapse pieces are already being retired in its favor. Line the signals up, and they all point the same way.

The signal What it tells you
Mirroring SQL Server to Fabric
went GA, on-prem included
Microsoft built the on-ramp from
your estate into theirs, zero-ETL.
SQL Server 2025 added a native
log-scan publisher for Fabric
The flagship engine release's analytics
headline is, in effect, 'feed Fabric.'
The first SQLCon ran
inside FabCon
The database and the platform now
share one stage and one roadmap.
Fabric named Synapse's successor;
new analytics work goes there
The investment is pointed at the
SaaS platform, not the box engine.

The data flows one way. Operational SQL Server feeds analytical Fabric. Your engine is being recast as the trusted source of record -- the place where your data is born and kept honest -- but it is also no longer the place where the analysis happens.

What This Is Not

Now the part the panic merchants will skip. Fabric is not replacing your transactional SQL Server, and nobody is migrating your order-entry system to a lakehouse. Mirroring is read-only and analytical by design. The copy to OneLake is for reporting and AI, not for inserts or writing. The OLTP engine that runs your business is going nowhere.

So this is not a funeral, and it is not a reason to let a vendor sell you a frightened migration. The engine is healthy. What has changed is not whether SQL Server survives. It is where the new value gets built on top of it.

Who's Ready? The Part Nobody's Costing

Here is where the keynote goes quiet. Fabric is the destination, fine. But the people who own a SQL Server, a rack of SSIS packages, and a budget they have to defend are not asking 'is Fabric the future.' They are asking 'does this work for my shop', and 'can I afford it'. Those answers are a lot less tidy than the slide deck.

Start with the money, because the shape of it is the opposite of what you own today. A SQL Server license is a capital cost on hardware you control. You buy it, you run it, it is yours. Fabric is a meter. You rent capacity by the hour, and the meter runs every hour the capacity is on. Here is the pay-as-you-go list, US region, per month.

Fabric capacity Pay-as-you-go
(list, per month)
F2 (entry) ~$263
F8 ~$1,051
F32 ~$4,205
F64 ~$8,410

F2 at about $263 a month looks approachable, and that is the number that gets quoted. Two things it does not tell you. First, the way you make pay-as-you-go cheap is to pause the capacity nights and weekends. But, the near-real-time mirroring needs the capacity running to stay near-real-time, so the live feed and the pause-to-save trick will counter each other. Second, and this is the one that makes people blink, below F64 every single report viewer still needs a Power BI Pro license, roughly $10 per user per month. The capacity that lets viewers in for free is F64, at about $8,410 a month -- roughly thirty times the F2. So the small shop that picks F2 to be frugal pays the entry rate plus a per-seat tax on everyone who opens a report, and the next rung up that removes the tax is a canyon away. Reserved pricing trims the capacity bill by 30 to 40 percent, but it commits you to paying around the clock, idle hours included. You have traded a server you owned for a subscription you cannot turn off.

None of that makes Fabric a bad platform. It makes it a different cost model, billed monthly, forever, and pointed at organizations big enough to keep a capacity busy. For the small and mid-size shop, 'free of a license' is not the same as 'cheap,' and nobody selling the destination is doing this arithmetic out loud.

And SSIS.

This is the part that costs me something to write. SSIS has been a workhorse in my career and a lot of yours, and it still ships with SQL Server 2025 and is fully supported. But Fabric has no SSIS runtime. The data-movement story over there is Data Factory pipelines and Dataflows Gen2 -- which means an SSIS-heavy shop does not migrate to Fabric, it rewrites. Every package is a re-implementation in a different tool, and the investment, the tooling, and the momentum are all on the new side. You can keep running SSIS for years. You just cannot bring it with you. Pretending otherwise does not change the rewrite waiting at the other end, and that rewrite is exactly the kind of cost that turns 'we are moving to Fabric' into a multi-year line item nobody scoped.

What It Means for the On-Prem DBA

If the gravity has moved, the practical question is what that does to your job. A few things, and none of them are panic.

The investment is moving up the stack.

This is the same pattern I wrote about in the Postgres piece. Microsoft feeds the layer where its customers are spending, and right now that layer is the SaaS analytics platform, not the box engine. The engine gets hardening and careful maintenance. Fabric gets the new features and the 60% growth. Plan your attention accordingly.

Mirroring makes your data Fabric's concern, which makes the plumbing yours.

A mirror is not free of consequences for the DBA. It means a dedicated Fabric login reading your transaction log, an on-premises data gateway sitting inside your network, and a real decision about which tables leave the building. That is a new security surface, and it lands on your desk. Scope the login down, watch the gateway, and treat 'what gets mirrored' as a data-governance question, not a checkbox. Least privilege, again, same as it ever was.

The skills hedge is cheap.

You do not have to become a Fabric engineer. But knowing how your data gets mirrored, and who can reach the copy in OneLake, is now part of the job description whether you asked for it or not. Learning the mirroring path on a test database is a few hours. Call it insurance.

The Bottom Line

So yes -- follow the headcount and you find the roadmap, and it all says Fabric. I am not arguing with that. My reader was right. The data leadership is focused on Fabric and the feature list proves they mean it. But focus does not also mean fit. The destination is set for the large, capacity-filling, analytics-hungry shop. For the small one, the SSIS-heavy one, the one still scrambling to clear the 2016 finish line, the honest answer to 'who is ready' is 'not yet', and the road may be longer and more expensive than anyone on stage is admitting. That is not a reason to refuse the trip. It is a reason to cost it out properly, before someone who has never met your SSIS estate puts a date on the calendar for you.

More to Read

Microsoft Fabric Blog: Mirroring for SQL Server in Microsoft Fabric (Generally Available)
Microsoft Learn: Mirroring in Microsoft Fabric (overview)
Microsoft Azure: Microsoft Fabric pricing (capacity F-SKUs)
Microsoft Fabric Blog: From Azure Synapse and Azure Data Factory to Microsoft Fabric
sqlfingers inc: Why Are We Still Paying for SQL Server?

Tuesday, June 9, 2026

Patch Tuesday June 2026: Skipped SQL Server and Landed on Your Firmware

It is Patch Tuesday again. Microsoft shipped around 200 fixes today and three publicly disclosed zero-days, and not one of them is in SQL Server. Just like May, the database engine sits this one out entirely with zero SQL Server CVEs. But that does not mean close up shop and go home early. We'll run the June numbers first, then get to the date that actually matters this month -- and it's not June 9th. It is late June, when a set of Secure Boot certificates that have been sitting in your firmware since 2011 start to expire.

The June Numbers

Counts vary a little by who is tallying... BleepingComputer puts it at 200 and some trackers land near 198, but the shape is clear either way. Roughly 200 fixes, 33 rated critical, and 28 of those critical bugs are remote code execution. Elevation of privilege dominated the overall list. Three zero-days, all publicly disclosed ahead of the patch, none flagged as exploited in the wild at release.

Zero-day Component Type
CVE-2026-45586 Windows Collaborative
Translation Framework (CTFMON)
EoP to SYSTEM
CVE-2026-49160 HTTP.sys ('HTTP/2 Bomb') Denial of service
CVE-2026-50507 Windows BitLocker Security feature bypass

That elevation-of-privilege tilt across the full list is worth a beat. An EoP bug is rarely how an attacker gets in. It is how they take over once they are already in. The move that turns one compromised account or service into SYSTEM, with full control of the machine. The CTFMON zero-day above is exactly that pattern: not a front door, but a fast way to own the machine once someone is already inside. On a SQL Server host, 'already inside' plus SYSTEM is the whole ballgame, with full control of the instance and every database on it.

Interesting side note: Windows Secure Boot also took eight security-feature-bypass fixes this month. Attackers keep poking at the boot path -- which is exactly where this month's real story is headed.

What SQL Shops Should Actually Patch

Zero SQL Server CVEs does not mean zero work. Your SQL Server boxes are Windows boxes, and a few items in this release land squarely on the hosts behind your SQL Servers.

Hyper-V guest escape, if you virtualize.

Three critical Hyper-V RCE bugs (CVE-2026-47652, CVE-2026-45641, CVE-2026-45607) can let code escape a guest VM onto the host. If your SQL instances run on Hyper-V, the host patch is the one to move on first.

Cryptographic Services and RDP.

A critical elevation-of-privilege bug in Microsoft Cryptographic Services (CVE-2026-44810) hits a foundational subsystem, and the Remote Desktop client picked up a cluster of RCE fixes. Both are normal-priority for a managed estate, but they are on your servers whether or not SQL is named.

Nothing here says break your change window, but nothing here is optional either. Run them through your usual patch process.

The Date That Actually Matters: Secure Boot

Here are the dates to put on your calendar. Secure Boot verifies your bootloader and early-boot components before Windows starts, and that trust chain leans on Microsoft certificates issued back in 2011. Those certificates were minted with a fifteen-year life, and the clock runs out this year, in stages.

Certificate Role Expires
Microsoft Corporation
KEK CA 2011
Key Exchange Key -- authorizes
updates to the DB/DBX databases
June 24, 2026
Microsoft UEFI
CA 2011
Signs third-party bootloaders
and option ROMs
June 27, 2026
Microsoft Windows
Production PCA 2011
Signs the Windows
boot manager
October 19, 2026

So June is when the floor starts shifting, and October is the date to circle in red, because the Windows boot manager signing certificate is the consequential one. The replacements already exist in the 2023 certificate family, and Microsoft has been pushing them out through Windows Update for a while now. PCs shipped since early 2024 already have them.

No, Your Server Will Not Stop Booting

You will see vendor posts this month with words like 'absolute deadline' and 'no recovery' and 'devices may fail to boot'. Ignore the drama. Microsoft's own guidance is plain about this. The machine does not suddenly refuse to boot when a 2011 certificate expires. Red Hat says the same for Linux, that systems with the 2011 certificate already enrolled keep booting fine past the expiry dates.

The real consequence is quieter, but still matters. After expiration, a device that never received the 2023 certificates can no longer take new Secure Boot database updates, which means it stops getting future boot-layer security fixes. It keeps running. It just stops getting protected against the next BlackLotus-style bootkit. That is the risk you are managing here, not a Monday morning where half the estate is dark.

How to Check Where You Stand

Most machines get the 2023 certificates automatically through Windows Updates, and Microsoft is managing that rollout for a large share of devices. The ones that might get you are older servers whose firmware needs an OEM update first, and anything your team is patching by hand. So the first question for any box is whether the update mechanism is even running -- because if the Secure-Boot-Update task is disabled or missing, the certificates never arrive. Microsoft's own troubleshooting guide has the check:

# Confirms the Secure-Boot-Update task exists and is enabled -- this is the
# mechanism that applies the 2023 certificates. From Microsoft's Secure Boot
# troubleshooting guide. Run in an elevated PowerShell session.
schtasks.exe /Query /TN "\Microsoft\Windows\PI\Secure-Boot-Update" /FO LIST /V

# Status meanings:
#   Ready              task exists and is enabled
#   Disabled           task exists but must be enabled
#   Error / Not Found  task is missing and must be recreated

Two gotchas before you push anything broadly. First, BitLocker. Applying the Secure Boot updates can throw a device into BitLocker recovery -- usually a one-time prompt on the first boot while the firmware catches up, but a repeating one on machines set to PXE-boot first. Either way, have your recovery keys in hand before you start, not after. Second, old firmware. Some hardware will not take the update without an OEM firmware refresh, and a few boxes may never get there at all. Stand up a firmware-update ring for those now, while the pressure is low. You do not want to be chasing OEM BIOS updates in October, when the boot manager certificate is the one on the clock.

The Bottom Line

Patch Tuesday skipped SQL Server entirely this month, again. Patch your Windows hosts on your normal cadence, give the Hyper-V host fixes a nudge to the front if you virtualize your instances, and otherwise breathe easy on the database tier this month.

But still do the Secure Boot inventory now. Confirm the Secure-Boot-Update task is running across the estate, flag the machines where it's Disabled or missing, and sort the OEM-firmware stragglers while you have time. The June dates are the warning shot. The October boot-manager date is the one that will actually hurt if you're unprepared. This is a calendar problem, not a fire, and calendar problems are the cheap ones to solve early.

More to Read

Microsoft Support: Windows Secure Boot certificate expiration and CA updates
Microsoft Tech Community: Act now -- Secure Boot certificates expire in June 2026
BleepingComputer: Microsoft June 2026 Patch Tuesday fixes 3 zero-days, 200 flaws
sqlfingers inc: Patch Tuesday May 2026 -- SQL Got Off Easy. Your Domain Didn't.