Threat intel
SQL Server's xp_cmdshell used as a command and exfil channel
An exposed attacker server shows SQL Server's xp_cmdshell used to run PowerShell and move files out as Base64 query results. How to check, audit and harden.
An attacker's staging server, left on the internet with no login, has shown in detail how one intrusion used Microsoft SQL Server as both its command line and its way out. Researchers at ThreatMon found the server at 151.243.232.123 during routine threat hunting and published their analysis on October 1. They link it to activity involving a Viva Aerobus-side environment between September 25 and 29, 2026. ThreatMon has not said how the attackers first got in, and it names no malware family.
The attackers ran Windows commands and Base64-encoded PowerShell through xp_cmdshell, a built-in SQL Server procedure, and their tooling could pull files back out as Base64 text in ordinary query results. ThreatMon found no evidence of successful lateral movement or of passenger, payment or similar business data leaving the network. The technique still matters to anyone running SQL Server: it needs no exploit and no implant, only a login with enough rights and one setting turned on.
How xp_cmdshell works
xp_cmdshell is an extended stored procedure: a call such as EXEC xp_cmdshell 'whoami' starts a Windows command shell on the database host, runs the string, and returns each line of output as a row in an nvarchar(255) column. It runs synchronously, so the caller gets the full output back in the same session.
The security problem is whose rights that shell has. When a member of the sysadmin role calls it, the process runs with the same rights as the SQL Server service account. Other users can only call it if an admin has granted them execute rights and set a proxy account, stored as the credential ##xp_cmdshell_proxy_account##. Microsoft's own documentation warns that the service account "often has more permissions than are necessary".
It is disabled by default on new installations, but one detail is easy to miss: anyone with sysadmin rights can switch it back on with sp_configure in four lines. Turning it off stops a login with lesser rights. It does not stop a stolen sa password.
The exfiltration trick follows from the output format. If a command prints a file as Base64, split into short lines, every line comes back as one row of the result set. The attacker reassembles the rows on their side. The data travels inside the SQL connection the attacker already has, so there is no new outbound connection for a firewall or proxy to notice.
What the exposed server showed
ThreatMon counted 17 named post-exploitation tools on the server. They included chrome_dump.ps1 and cred_dump.ps1 for browser and Windows credential stores, cred_enum.ps1, sqlspray.ps1 and mssqltest.ps1 for testing SQL logins against other servers, exfil.py and upload.py for file transfer, and vault.cmd and vtest.ps1, probably aimed at Windows Credential Manager. Directories named loot/ and loot2/ held collected material, alongside Mimikatz output.
The attackers also took SQL Server Management Studio (SSMS) user settings. Those files record which servers an admin has connected to, the usernames used and saved passwords protected by DPAPI, the Windows data protection API. ThreatMon assessed that this history, along with source code and configuration files referring to SQL, OAuth, mail, SFTP, payment and reporting integrations, fed later attempts to log in to other SQL servers and SMB admin shares. Those attempts are confirmed as preparation only, not as successful access.
The timeline on September 25 shows a second problem. At 16:20 a victim-side SQL Server fetched a payload from the staging server. From 16:21 to 16:23 an unrelated host was already listing its directories, and between 18:04 and 18:05 more outside hosts downloaded tools and loot. Anything that reached that server should be treated as known to more than one party.
What to do
1. Check whether it is on. On every instance, run:
SELECT name, value, value_in_use
FROM sys.configurations
WHERE name = 'xp_cmdshell';
value_in_use = 1 means it is enabled. If nothing needs it, turn it off with sp_configure 'show advanced options', 1; RECONFIGURE;, then sp_configure 'xp_cmdshell', 0; RECONFIGURE;, then hide advanced options again. If a legacy job needs it, Microsoft's advice is to enable it only for the duration of that task.
2. Count who could turn it back on. List members of the sysadmin role with SELECT m.name FROM sys.server_role_members r JOIN sys.server_principals m ON r.member_principal_id = m.principal_id WHERE r.role_principal_id = SUSER_ID('sysadmin');. Every SQL login in that list is a password an attacker can spray, and sqlspray.ps1 exists for that purpose. Disable sa if you can, and prefer Windows authentication.
3. Log every change and every call. A change to the setting writes "Configuration option 'xp_cmdshell' changed from 0 to 1" to the SQL Server error log and event ID 15457 to the Windows Application log. Search history with EXEC sp_readerrorlog 0, 1, N'xp_cmdshell';. To record each execution with the login and command, create a SQL Server Audit filtered on the procedure:
CREATE SERVER AUDIT Audit_xp_cmdshell
TO FILE (FILEPATH = 'D:\SQLAudit\')
WITH (ON_FAILURE = CONTINUE)
WHERE (object_name = 'xp_cmdshell');
CREATE SERVER AUDIT SPECIFICATION AuditSpec_xp_cmdshell
FOR SERVER AUDIT Audit_xp_cmdshell
ADD (SCHEMA_OBJECT_ACCESS_GROUP);
ALTER SERVER AUDIT Audit_xp_cmdshell WITH (STATE = ON);
ALTER SERVER AUDIT SPECIFICATION AuditSpec_xp_cmdshell WITH (STATE = ON);
Send the audit to the Application log instead of a file if your SIEM already collects it; executions then appear as event ID 33205.
4. Watch the process tree. On the endpoint side, alert on sqlservr.exe starting cmd.exe or powershell.exe, and treat powershell.exe -EncodedCommand under that parent as an incident. Elastic's rule for this pattern excludes a short list of routine commands such as dir and type, which is a sensible starting point for your own exclusions.
5. Hunt for this campaign. Search firewall and proxy logs for 151.243.232.123. ThreatMon published SHA-256 hashes for three of the tools:
exfil.py: c38f49ba68b891bb476510704cddf080798f3c70075e2a517e98e04e833f64faupload.py: 33aeaaa3d57b7785ef2be5b8ccd39d534b8af50e0cd32a5036fdb64601a52fc9sqlspray.ps1: 8b6c53e3d57b4c3049f3d0765a44d9a52feb6af78f1eaa5aff19daf1b9665998
6. Shrink the service account. Find it with SELECT servicename, service_account FROM sys.dm_server_services;. It should be a virtual account (NT SERVICE\MSSQLSERVER) or a group managed service account, never a local administrator or a domain admin, with no rights on file shares it does not need. Block outbound internet from database servers at the host firewall, so a payload download like the 16:20 fetch fails.
7. If you find a hit, assume every credential the service account and the SSMS users could reach is exposed. Rotate SQL logins, the service account password, connection strings in application configs, and the OAuth, mail and SFTP secrets in any code the server held.
Why the database tier gets missed
Database servers are often left out of endpoint tuning work because changes there feel risky, and some legacy jobs do call xp_cmdshell for backups or file moves. That noise can hide an attacker using the same procedure. A short allowlist of the commands your own jobs really run, and an alert on anything outside it, catches this kind of abuse without drowning the team.
Our safeguarding and hardening work reviews SQL Server settings, sysadmin membership and service accounts, and our security operations team can build and watch the xp_cmdshell and process-tree alerts above. Open the chat and Yaali, our AI agent, will pass your question to an engineer.
Sources: ThreatMon, GBHackers, CyberPress, Cyber Security News, Microsoft: xp_cmdshell, Microsoft: xp_cmdshell server configuration option, Splunk: xp_cmdshell config change detection, Patrick Keisler: SQL Server Audit recipe for xp_cmdshell, Elastic: execution via MSSQL xp_cmdshell.