Remediation Guides

How to Disable xp_cmdshell in SQL Server (VA1059)

26 September 2026 9 min read

VA1059, “xp_cmdshell should be disabled”, means the SQL Server option xp_cmdshell is turned on, so privileged logins can run Windows commands from T-SQL with the rights of the SQL Server service account. Fix it by setting the option to 0 with sp_configure and RECONFIGURE, then removing any xp_cmdshell proxy credential and EXECUTE grants. No restart is needed.

The change takes seconds. The real work is finding what still calls xp_cmdshell before you switch it off.

What the scanner is actually detecting

This is a configuration check, not a missing patch, and there is no CVE behind it. Both scanners look at the instance-level xp_cmdshell server configuration option, which is an advanced option with a default of 0 (disabled) on new installations.

Scanner Finding title What it means
Microsoft Defender for Cloud, SQL vulnerability assessment (Defender for SQL servers on machines, Azure SQL Managed Instance) VA1059: xp_cmdshell should be disabled (recommendation title “xp_cmdshell should be disabled for SQL Servers”) Severity High, category Authentication and Authorization, platforms SQL Server 2012+ and SQL Managed Instance. The rule checks that xp_cmdshell is disabled.
Tenable compliance audits for the CIS Microsoft SQL Server benchmark 2.15 Ensure ‘xp_cmdshell’ Server Configuration Option is set to ‘0’ (CIS SQL Server 2016 and 2017 Database L1 audits) The option is not set to 0. The CIS rationale is that attackers commonly use xp_cmdshell to read or write data on the database server’s operating system.

Defender’s scan is read-only and, for SQL servers on machines, runs every 12 hours. Tenable’s published audit files for the CIS SQL Server 2019 and 2022 benchmarks do not include an xp_cmdshell item, so on those versions this finding usually comes from Defender or your own baseline.

Real-world risk

xp_cmdshell spawns a Windows command shell and returns its output as rows of text. When a member of the sysadmin role calls it, the process runs with the security context of the SQL Server service account. When a non-sysadmin who was granted EXECUTE calls it, it runs as the Windows account stored in the ##xp_cmdshell_proxy_account## credential, and fails if that credential does not exist. Microsoft states that it ships disabled because malicious users sometimes try to elevate their privileges with it.

The realistic attack path starts with someone gaining sysadmin-level access, for example through a weak sa password or an application that connects as sysadmin and has a SQL injection flaw. xp_cmdshell then turns database access into command execution on the host, and the service account decides how far that goes. Microsoft notes that it often has more permissions than the spawned process needs.

Be honest about the limits. Changing the option requires ALTER SETTINGS, which sysadmin and serveradmin hold implicitly, so an attacker with sysadmin can simply turn it back on. Disabling it still blocks non-sysadmin principals who were granted access, removes a ready-made tool, and forces a configuration change that SQL Server writes to its error log. The controls that limit real impact are a short sysadmin list and a low-privilege service account, which belong in your broader Windows Server hardening checklist.

How to confirm it on the host

Connect to the instance named in the finding and read the setting (sys.configurations does not need show advanced options):

SELECT name, value, value_in_use, is_dynamic, is_advanced
FROM sys.configurations
WHERE name = N'xp_cmdshell';

value is the configured value and value_in_use is the running value. The option is enabled when value_in_use is 1. Next, see which account commands would run as, and who can call the procedure:

-- Service account behind sysadmin calls
SELECT servicename, service_account
FROM sys.dm_server_services;

-- Members of sysadmin
SELECT p.name, p.type_desc, p.is_disabled
FROM sys.server_role_members AS rm
JOIN sys.server_principals AS r ON r.principal_id = rm.role_principal_id
JOIN sys.server_principals AS p ON p.principal_id = rm.member_principal_id
WHERE r.name = N'sysadmin';

-- Explicit EXECUTE grants on xp_cmdshell
USE master;
SELECT pr.name AS grantee, pe.permission_name, pe.state_desc
FROM sys.database_permissions AS pe
JOIN sys.database_principals AS pr ON pr.principal_id = pe.grantee_principal_id
WHERE pe.class = 1 AND pe.major_id = OBJECT_ID(N'sys.xp_cmdshell');

-- Proxy credential used by non-sysadmin callers
SELECT name, credential_identity, create_date
FROM sys.credentials
WHERE name = N'##xp_cmdshell_proxy_account##';

How to fix it

Step 1: find what depends on it

Once the option is off, every call to xp_cmdshell fails. Search stored code in each user database and the SQL Server Agent job steps first:

-- Run in each user database
SELECT OBJECT_SCHEMA_NAME(object_id) AS schema_name, OBJECT_NAME(object_id) AS object_name
FROM sys.sql_modules
WHERE definition LIKE N'%xp[_]cmdshell%';

-- Agent job steps
SELECT j.name AS job_name, s.step_id, s.step_name
FROM msdb.dbo.sysjobsteps AS s
JOIN msdb.dbo.sysjobs AS j ON j.job_id = s.job_id
WHERE s.command LIKE N'%xp[_]cmdshell%';

Calls sent directly from application code will not show up here; if unsure, capture a representative period with SQL Server Audit or Extended Events first.

Step 2: disable the option (SQL Server on Windows)

This is Microsoft’s documented sp_configure sequence with the value set to 0. It also returns show advanced options to 0, because that setting applies to all users while it is on:

USE master;
GO
EXECUTE sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
EXECUTE sp_configure 'xp_cmdshell', 0;
GO
RECONFIGURE;
GO
EXECUTE sp_configure 'show advanced options', 0;
GO
RECONFIGURE;
GO

You need the ALTER SETTINGS permission (sysadmin or serveradmin). The option is dynamic, so it takes effect at RECONFIGURE without a restart.

Step 3: remove grants and the proxy credential

Leftover grants and a stored proxy password are what make re-enabling dangerous later. Revoke each grantee returned by the query above, then delete the proxy credential:

USE master;
REVOKE EXECUTE ON xp_cmdshell FROM [AppUser];
GO
EXECUTE sp_xp_cmdshell_proxy_account NULL;
GO

Deleting the proxy requires CONTROL SERVER. Record the proxy’s Windows account name first in case you need to roll back.

Keeping it off: SSMS facets and Policy-Based Management

In SQL Server Management Studio, right-click the server in Object Explorer, select Facets, choose Surface Area Configuration, and set the xp_cmdshell property to False. To check it on a schedule, build a Policy-Based Management policy on that facet; the Invoke-PolicyEvaluation cmdlet can evaluate it from PowerShell.

If an application genuinely needs it

Microsoft’s guidance is that new code should not use xp_cmdshell, and that a legacy application which requires it should have it enabled only for the duration of the task. If non-sysadmin users must call it, use a least-privileged Windows account as the proxy. Scheduled OS commands can move to a SQL Server Agent CmdExec job step, which runs under the Agent service account or an Agent proxy. In Defender, a deliberate exception can be approved as a baseline, so matching results pass in later scans.

How to verify the fix and rescan

Rerun the sys.configurations query: value and value_in_use should both be 0, and the sys.credentials query should return no rows. From a sysadmin session, EXECUTE xp_cmdshell ‘whoami.exe’; should now fail with error 15281, which says SQL Server blocked access because the component is turned off as part of the security configuration.

To check many instances at once, loop over a list with sqlcmd (-E uses Windows authentication, -h -1 drops headers):

Get-Content .sql-instances.txt | ForEach-Object {
    sqlcmd -S $_ -E -h -1 -W -Q "SET NOCOUNT ON; SELECT @@SERVERNAME, CAST(value_in_use AS int) FROM sys.configurations WHERE name = N'xp_cmdshell';"
}

Current sqlcmd versions require encryption by default. If a connection fails certificate validation, fix the certificate rather than adding -C, which skips validation.

Then rescan. In Defender, use the on-demand Scan option where the portal offers it, or allow one 12-hour cycle for SQL servers on machines. For CIS audits, rerun the compliance scan and confirm item 2.15 passes.

What can break and how to roll back

  • Stored procedures and Agent jobs that call xp_cmdshell. The call fails with error 15281, so any step that relied on the command or its output breaks. Microsoft’s own examples show the typical uses: copying backup files to a share and listing or writing files.
  • Maintenance scripts that toggle the option. Some scripts enable xp_cmdshell, do their work and disable it again. One that fails midway can leave the option on.
  • Non-sysadmin workflows that used the proxy. They stay broken until the credential is recreated.

Save the output of the confirmation queries before you change anything. To roll back, run the same sp_configure sequence with 1 instead of 0, then restore what you removed:

EXECUTE sp_xp_cmdshell_proxy_account 'DOMAINsvc-proxy', '<password>';
GO
USE master;
GRANT EXECUTE ON xp_cmdshell TO [AppUser];
GO

SQL Server will not show you the stored proxy password, so make sure the account owner can supply it before you delete the credential.

Common false positive reasons

True false positives are rare because the check reads one value. When a result looks wrong, it is usually one of these:

  • A pending value. Someone ran sp_configure without RECONFIGURE, so value and value_in_use differ. A pending 1 is installed by the next RECONFIGURE anyone runs for any option, so treat it as a real finding.
  • A job enabled it briefly. The scan ran while a maintenance script had xp_cmdshell switched on.
  • The wrong instance. The host runs several instances and you fixed a different one from the one the scanner reported.
  • Stale results. The last scan predates the fix. Defender scans SQL servers on machines every 12 hours, so check the scan time before reopening the ticket.

FAQ

Do I need to restart SQL Server after disabling xp_cmdshell?

No. The option is dynamic, so RECONFIGURE applies it immediately. Confirm with value_in_use in sys.configurations.

Does disabling xp_cmdshell stop a sysadmin from running OS commands?

No. A sysadmin can turn it back on and has other routes to the operating system. Limit sysadmin membership and use a low-privilege service account; this setting is one layer on top.

Will SQL Server Agent CmdExec job steps stop working?

No. CmdExec job steps run through SQL Server Agent under the Agent service account or a proxy, not through the xp_cmdshell option. Only job steps whose T-SQL calls xp_cmdshell are affected.

Does VA1059 apply to SQL Server on Linux or Managed Instance?

SQL Server on Linux does not support system extended stored procedures such as xp_cmdshell, so this is in practice a Windows finding. The rule also lists Azure SQL Managed Instance, where the configuration option exists but Microsoft states xp_cmdshell itself isn’t supported; if VA1059 fires there, set the option to 0 with the same statements.

Tracking this finding across many hosts

On a large estate the hard part is keeping the setting off after maintenance scripts and new instances bring it back. SITEY, a self-hosted vulnerability management platform, imports findings from 16 scanners and merges duplicates within each scanner, not across scanners. Its AI can suggest likely false positives with evidence and draft host-specific remediation scripts, which run only after human approval through its agents on Windows endpoints, and it re-tests afterwards to confirm closure; per-finding retest is available for Nessus, Acunetix and Burp results.

Sources

SITEY closes the loop, not just the report.Discover, validate, fix and verify in your own infrastructure.

See pricing