THREAT-Impact-FinancialLedgerTampering IBM QRadar · QRadar

Detect Financial Ledger and Transaction Record Tampering via Direct Database Manipulation in IBM QRadar

An insider with legitimate database credentials, or an attacker who has obtained them, can bypass an application's business logic entirely and manipulate financial data at the source: connecting directly to the ledger, invoice, or transaction database with an interactive client (SQL Server Management Studio, Azure Data Studio, DBeaver, psql, mysql CLI) instead of through the application's service account, then issuing UPDATE or DELETE statements against tables such as general_ledger, invoices, journal_entries, gl_entries, ap_transactions, or account_balances. This is functionally identical to APT38's DYEPACK tool altering SWIFT transaction records to conceal fraudulent transfers, and to the classic 'ghost employee' or 'check kiting' fraud pattern where a trusted insider edits posted transactions after the fact rather than through a reversing journal entry (which would leave the expected audit trail). Three behaviors distinguish malicious tampering from legitimate maintenance: (1) the write originates from an ad-hoc/interactive client tool or an identity other than the application's own connection pool account — legitimate corrections almost always flow back through the application, which posts compensating entries rather than editing history in place; (2) the activity clusters off-hours or on weekends, when fewer people are watching and reconciliation staff are not online; and (3) the write is preceded by disabling the database's own audit trail for that table or database — SQL Server 'ALTER SERVER AUDIT ... WITH (STATE = OFF)', temporal-table system-versioning disablement, change-tracking disablement, or a DISABLE TRIGGER on an audit trigger — which is a strong signal since audit trails exist specifically to survive this kind of tampering and disabling one immediately before writing to financial tables has essentially no legitimate business justification. This detection watches SQL platform audit logs (Azure SQL Auditing / SQL Server Audit, or equivalent extended-events output) for the write pattern, the audit-disablement pattern, and the two-step sequence where disablement precedes writes within a short window.

MITRE ATT&CK

Tactic
Impact

QRadar Detection Query

IBM QRadar (QRadar)
sql
SELECT
  DATEFORMAT(starttime, 'YYYY-MM-dd HH:mm:ss') AS EventTime,
  "Action Name" AS ActionName,
  "Object Name" AS ObjectName,
  "Server Principal Name" AS ServerPrincipal,
  "Database Name" AS DatabaseName,
  "Application Name" AS ClientApp,
  CASE
    WHEN "Action Name" IN ('AUDIT_CHANGE_GROUP','SERVER_OBJECT_CHANGE_GROUP') THEN 'AuditDisable'
    WHEN "Action Name" IN ('UPDATE','DELETE') AND "Object Name" ILIKE ANY ('%ledger%','%invoice%','%transaction%','%payment%','%journal_entr%','%gl_entr%','%account_balance%') THEN 'FinancialWrite'
    ELSE 'Other'
  END AS Indicator
FROM events
WHERE
  LOGSOURCETYPENAME(logsourceid) ILIKE '%SQL%Audit%'
  AND (
    "Action Name" IN ('AUDIT_CHANGE_GROUP','SERVER_OBJECT_CHANGE_GROUP')
    OR (
      "Action Name" IN ('UPDATE','DELETE')
      AND "Object Name" ILIKE ANY ('%ledger%','%invoice%','%transaction%','%payment%','%journal_entr%','%gl_entr%','%account_balance%')
      AND "Succeeded" ILIKE 'true'
    )
  )
ORDER BY starttime DESC
LAST 24 HOURS
high severity medium confidence

QRadar AQL query over SQL Server / Azure SQL Audit log source events, labeling each matching row as AuditDisable or FinancialWrite. Recommend a QRadar rule that groups by ServerPrincipal/DatabaseName over a rolling window and fires when an AuditDisable indicator is followed within 6 hours by a FinancialWrite indicator for the same principal/database, plus a lower-severity standalone rule for FinancialWrite bursts from application names matching known interactive client tools.

Data Sources

SQL Server Audit / Azure SQL Auditing (via QRadar DSM or Universal DSM)

Required Tables

events

False Positives & Tuning

  • Approved DBA maintenance or data-correction work performed during a documented change window
  • Scheduled overnight batch reconciliation jobs from an ETL/reporting service account not yet allowlisted
  • Legitimate schema-migration work that disables change tracking or audit specifications temporarily

Other platforms for THREAT-Impact-FinancialLedgerTampering


Testing Methodology

Validate this detection against 3 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.

  1. Test 1Disable SQL Server Audit Then Update Ledger Table

    Expected signal: SQL Server Audit / Azure SQL Auditing records: an AUDIT_CHANGE_GROUP event for test_ledger_audit_spec followed within minutes by an UPDATE action_name event against object_name dbo.general_ledger, both attributed to the same server_principal_name with application_name 'sqlcmd'.

  2. Test 2Off-Hours Bulk Update of Invoice Table via Interactive Client

    Expected signal: SQL Server Audit / Azure SQL Auditing records: 10 UPDATE action_name events against object_name dbo.invoices within a single 15-minute window, application_name 'Azure Data Studio', succeeded = true.

  3. Test 3Direct Deletion of Journal Entry Rows via psql

    Expected signal: Database audit log (pgaudit or equivalent forwarded into the SQLSecurityAuditEvents-equivalent schema): DELETE action_name events against object_name journal_entries, application_name 'psql', server_principal_name dbadmin, succeeded = true.


Response Playbook

Triage

  1. Identify the server_principal_name / ServerPrincipal that performed the write: is this a human DBA account, the application's own service account, or an unrecognized/newly-created login? A human account editing rows directly, outside the application, is the highest-priority scenario
  2. Confirm whether ClientApp/application_name matches a known interactive tool (SSMS, Azure Data Studio, DBeaver, psql, mysql CLI, sqlcmd) rather than the production application's connection string — the application should almost never issue ad-hoc row-level UPDATE/DELETE against ledger tables directly
  3. Check whether AuditTamperPrecursor is true — if an audit-disablement event (AUDIT_CHANGE_GROUP, disabled trigger, disabled change tracking, disabled server audit) occurred shortly before the writes for the same principal/database, treat this as active, deliberate tampering rather than routine maintenance
  4. Pull the full Statement text for each flagged write to determine exactly which rows/columns were altered — look for changes to amount, status, approval, or void/reversal fields rather than a normal application-issued reversing entry
  5. Determine whether the writes occurred off-hours (IsOffHours/OffHoursCount) and cross-reference against the organization's change-management calendar for any approved maintenance window covering this database
  6. Check whether the same principal has any prior history of interactive access to this database, or whether this is a first-time occurrence — a first-time ad-hoc write to a financial table from a previously read-only or application-only account is significantly higher suspicion

Containment

  1. Immediately suspend or reduce the permissions of the account that performed the writes (revoke direct table UPDATE/DELETE grants, force password/token rotation, disable interactive login if the account should only ever be used by the application)
  2. Re-enable any disabled audit trail (SQL Server Audit, change tracking, temporal system-versioning, or the disabled trigger) immediately so further tampering cannot occur undetected
  3. Preserve a forensic copy of the affected tables' current state and, if temporal tables or backups are available, the pre-tampering state, before any remediation UPDATE is run to restore correct values
  4. Notify finance/accounting leadership and internal audit before altering the data further — restoring 'correct' values without preserving evidence can destroy the ability to prove fraud occurred
  5. Review and restrict which accounts hold direct interactive access (not via the application) to production financial databases going forward, moving toward a break-glass model with mandatory approval and session recording

Evidence Collection

  1. Full SQL Server Audit / Azure SQL Auditing records for every flagged UPDATE/DELETE, including the complete statement text, before/after values where captured, client IP, and application_name
  2. Audit-disablement event records (AUDIT_CHANGE_GROUP, ALTER SERVER AUDIT, DISABLE TRIGGER, change-tracking/system-versioning toggles) with exact timestamps to establish the disable-then-write sequence
  3. Authentication logs (SQL login history, Azure AD sign-in logs, or Windows logon events) for the implicated principal around the incident window, including source IP and any VPN/jump-box hop
  4. Database transaction log backups or CDC/temporal-table history for the affected tables, which may retain the pre-tampering values even after the audit trail was disabled going forward
  5. A complete diff between the current row values and the last known-good reconciled state (from a prior backup, replica, or month-end close snapshot) for every touched row

Escalation Criteria

  • !AuditTamperPrecursor is true — audit trail disablement immediately preceding financial-table writes by the same principal has no plausible legitimate justification
  • !The writes altered posted/approved transaction status, amounts, or void/reversal flags rather than adding a new compensating entry, which is inconsistent with standard accounting reversal practice
  • !The implicated account is a human/interactive login rather than the application's own service account, and no change ticket or maintenance window covers the activity
  • !The activity occurred off-hours or on a weekend and targeted a high-value or previously-flagged account/vendor/customer record
  • !Multiple databases, tables, or a sustained pattern over several days are affected, suggesting a systematic effort to conceal an ongoing fraud rather than a one-off error

Investigation Guide

Related Techniques

Forensic Artifacts

  • >SQL Server Audit / Azure SQL Auditing log entries (action_name, object_name, statement, server_principal_name, application_name, client_ip, succeeded) for every UPDATE/DELETE against the affected tables
  • >Audit configuration change history (ALTER SERVER AUDIT, sys.server_audits, sys.database_audit_specifications) showing when auditing was enabled, disabled, or reconfigured
  • >Change-tracking / temporal-table (system-versioned table) history, which can retain prior row values even after the audit trail was disabled
  • >Database transaction log (LDF) or WAL records, if still retained, which may allow reconstruction of the exact before/after row state independent of the audit log
  • >Database connection/session logs (login triggers, sys.dm_exec_sessions snapshots, or connection pooling proxy logs) showing which network location and credential established the session that issued the writes
  • >Application-layer logs showing whether the application itself ever issued a corresponding transaction for the altered rows — an absence of a matching application-layer event for a financial-table change is itself a strong indicator of out-of-band tampering

Tuning Guidance

The single most important control is keeping the BusinessAppNames allowlist accurate and current — it should list every legitimate application/service connection string that is permitted to write to financial tables, and any new application deployment or connection-string rename must be added promptly or the primary detection will generate false positives for the first few hours after a routine release. Similarly, maintain an allowlist of DBA accounts and their approved maintenance windows so that ticketed, off-hours corrective work does not need to be triaged as an incident every time; correlate against the organization's change-management/ticketing system by principal name and time window where possible rather than allowlisting entire accounts permanently. Treat the audit-disablement event (AUDIT_CHANGE_GROUP, disabled trigger, disabled change tracking) as a standalone medium-severity alert even without a following write, since disabling a financial database's audit trail has almost no legitimate justification outside a documented, ticketed platform migration. FinancialObjectPattern should be reviewed and extended to match the organization's actual schema naming conventions (this generic pattern targets common naming like ledger/invoice/transaction/journal_entry/gl_entr/ap_/ar_ but real schemas vary) — an inaccurate table-name pattern is the most common cause of both missed detections and false negatives in environments with non-obvious table naming.


Hunting Queries

30-day baseline hunt across all principals and application names that have ever issued a direct UPDATE/DELETE against financial-sounding tables, sorted ascending by write count so rare or first-time callers surface at the top — a principal with exactly one or two historical writes, especially from an interactive client, is a strong candidate for closer review even without triggering the primary burst-based detection.

Hunting — KQL
kql
// Hunt: baseline all principals/applications that have ever written directly to financial tables over 30 days, to separate known automation from rare/first-time interactive callers
AzureDiagnostics
| where TimeGenerated > ago(30d)
| where Category == "SQLSecurityAuditEvents"
| where action_name_s in ("UPDATE", "DELETE")
| where object_name_s matches regex @"(?i)(ledger|invoice|transaction|payment|journal_entr|gl_entr|account_balance|^ap_|^ar_|general_ledger)"
| summarize WriteCount = count(), Databases = make_set(database_name_s), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated) by server_principal_name_s, application_name_s
| sort by WriteCount asc
Hunting — SPL
spl
index=azure sourcetype="azure:sql:diagnosticlogs" category="SQLSecurityAuditEvents" (action_name="UPDATE" OR action_name="DELETE")
| where match(object_name, "(?i)(ledger|invoice|transaction|payment|journal_entr|gl_entr|account_balance|^ap_|^ar_|general_ledger)")
| stats count as WriteCount, values(database_name) as Databases, earliest(_time) as FirstSeen, latest(_time) as LastSeen by server_principal_name, application_name
| sort WriteCount

Atomic Red Team Tests

Test 1 Disable SQL Server Audit Then Update Ledger Table
windows

Disables an active SQL Server Audit specification on a disposable test database, then issues a direct UPDATE against a synthetic ledger table, simulating the audit-tamper-then-write sequence used to conceal fraudulent record changes. Use a throwaway test database and synthetic rows only — never a production financial database.

Command

powershell
sqlcmd -S localhost -d df00tech_atomic_test -Q "ALTER SERVER AUDIT SPECIFICATION test_ledger_audit_spec WITH (STATE = OFF); UPDATE dbo.general_ledger SET amount = 999999.00, status = 'posted' WHERE ledger_id = 1;"

Cleanup

powershell
sqlcmd -S localhost -d df00tech_atomic_test -Q "ALTER SERVER AUDIT SPECIFICATION test_ledger_audit_spec WITH (STATE = ON); UPDATE dbo.general_ledger SET amount = 100.00, status = 'posted' WHERE ledger_id = 1;"

Expected Telemetry

SQL Server Audit / Azure SQL Auditing records: an AUDIT_CHANGE_GROUP event for test_ledger_audit_spec followed within minutes by an UPDATE action_name event against object_name dbo.general_ledger, both attributed to the same server_principal_name with application_name 'sqlcmd'.

Expected Detection

Alert fires with AuditTamperPrecursor = true once the AuditDisabledAt timestamp precedes the FinancialWriteEvents FirstSeen by less than the 6-hour AuditTamperWindow for the same ServerPrincipal/DatabaseName — RiskScore 95.

Test 2 Off-Hours Bulk Update of Invoice Table via Interactive Client
windows

Simulates a DBA-style interactive client (Azure Data Studio / SSMS) issuing a burst of UPDATE statements against a synthetic invoices table outside business hours, simulating off-hours bulk ledger tampering. Run against a disposable test database only.

Command

powershell
sqlcmd -S localhost -d df00tech_atomic_test -Q "UPDATE dbo.invoices SET status = 'paid', paid_amount = 0.00 WHERE invoice_id BETWEEN 1 AND 10;" -a "Azure Data Studio"

Cleanup

powershell
sqlcmd -S localhost -d df00tech_atomic_test -Q "UPDATE dbo.invoices SET status = 'open', paid_amount = NULL WHERE invoice_id BETWEEN 1 AND 10;"

Expected Telemetry

SQL Server Audit / Azure SQL Auditing records: 10 UPDATE action_name events against object_name dbo.invoices within a single 15-minute window, application_name 'Azure Data Studio', succeeded = true.

Expected Detection

Alert fires once WriteCount >= MinWriteCount (5) with InteractiveCount > 0 for the same ServerPrincipal/DatabaseName within the 15-minute BurstWindow; RiskScore elevated further to 85 if the test is run between 20:00-06:00 local time (IsOffHours = true).

Test 3 Direct Deletion of Journal Entry Rows via psql
linux

Uses an interactive psql session to delete rows directly from a synthetic journal_entries table on a PostgreSQL test database, simulating an insider bypassing the application to erase evidence of a posted transaction. Use a disposable test database and synthetic rows only.

Command

bash
PGAPPNAME=psql psql -h localhost -U dbadmin -d df00tech_atomic_test -c "DELETE FROM journal_entries WHERE entry_id IN (101, 102, 103);"

Cleanup

bash
psql -h localhost -U dbadmin -d df00tech_atomic_test -c "INSERT INTO journal_entries (entry_id, account, amount, memo) VALUES (101, 'test-1', 100.00, 'atomic-test-restore'), (102, 'test-2', 200.00, 'atomic-test-restore'), (103, 'test-3', 300.00, 'atomic-test-restore');"

Expected Telemetry

Database audit log (pgaudit or equivalent forwarded into the SQLSecurityAuditEvents-equivalent schema): DELETE action_name events against object_name journal_entries, application_name 'psql', server_principal_name dbadmin, succeeded = true.

Expected Detection

Alert fires once InteractiveCount > 0 for server_principal_name dbadmin against database df00tech_atomic_test within the 15-minute BurstWindow, since application_name 'psql' matches InteractiveClientTools and is not in BusinessAppNames.

Related Detections

Tactic Hub

Detection Variants (1)

Different telemetry and tradecraft for the same technique — pick the one that matches the data you collect.