Beyond Escaping - SQL Injection Lessons from the Treasury Breach
On December 30, 2024, the U.S. Treasury Department disclosed a breach by Chinese state-sponsored hackers from the APT group. They hit workstations, including Janet Yellen’s, accessing fewer than 50 files on her device. The attack didn’t come through Treasury’s code—it exploited BeyondTrust , their Privileged Access Management (PAM) tool for remote support. A stolen API key let them escalate privileges.
But the real entry point?
Two vulnerabilities in BeyondTrust’s Remote Support SaaS platform:
Both let unauthenticated attackers run OS commands remotely.
BeyondTrust’s post-mortem, with help from Rapid7 , revealed something unexpected: a bug in their database setup that had been sitting there for a while. It wasn’t just a random glitch—it exposed a fundamental flaw in Postgres, one of the most widely used databases out there. For those of us who’ve been in the database trenches, this hit close to home because it challenged a core assumption we’ve leaned on for ages:
“Always escape your inputs, and you’ll be safe from SQL injection.”
Turns out, even with proper escaping, there was still a way in.
We all know the drill: take user input, escape special characters like quotes, and you’re golden.
Consider a simple query:
SELECT * FROM users WHERE username = 'admin' AND password = 'I'm Batman';.
But if an attacker slips in something like:
I'm Batman' OR 1=1; --
, that single quote breaks out of the password check, and since 1=1 is always true, they’re logged in as admin without the real password. That’s why we escape inputs—to neutralize those special characters and make them safe. BeyondTrust was doing that, stripping out dangerous characters to harden their queries.
So how did the attackers still get through?
The answer lies in two sneaky bytes
0xC0 and 0x27
To understand this, we need to dig into Postgres’s source code—bear with me, it’s worth it
BeyondTrust used PQescapeString, a standard function to safely escape user input. You pass your string through it, and it handles the heavy lifting. But under the hood, it calls into Postgres’s native functions, and that’s where things get hairy. One key function, pg_utf_mblen, determines how many bytes make up a character in UTF-8 encoding. Think about it: a letter like A is one byte, but an emoji or non-English character might take multiple bytes. Postgres needs to handle this correctly when escaping strings.
Here’s the catch:
In BeyondTrust’s case, their Remote Support software had a component that took user input and fed it to a PHP script. The script used PQescapeString to sanitize it, but attackers tricked it with those two bytes. The resulting unsafe string got passed to psql, Postgres’s command-line tool, which has a feature to execute system commands. Game over! They turned a database flaw into a full-on breach, pivoting from BeyondTrust’s systems into Treasury’s infrastructure.
BeyondTrust patched their cloud environments by December 14, 2024, and announced the vulnerabilities two days later, with fixes rolled out to customers. The true scope came into focus in January 2025, when Rapid7’s analysis tied it to that Postgres bug—a flaw that could’ve affected millions of systems worldwide. BeyondTrust’s fix was straightforward: restrict input to letters and numbers, no special characters or funky bytes. Postgres also released patches for supported versions (if you’re on 13 or higher).
But this wasn’t just a one-off—it was a supply chain attack. Treasury didn’t mess up their own code; they trusted a third-party tool with a weak spot.
So, why does this matter?
Because escaping inputs isn’t just a checkbox-it’s a mindset. Parameterized queries are our best defense;
Example:
// UNSAFE: String concatenation
String query = "SELECT * FROM users WHERE username = '" + username + "'";
// SAFE: Parameterized query
PreparedStatement stmt = connection.prepareStatement("SELECT * FROM users WHERE username = ?");
stmt.setString(1, username)
They block injection cold.
Even so, this Postgres flaw shows that our strongest assumptions can have cracks. In the Treasury case, a single API key and a database bug turned a PAM tool into a backdoor. For us, skipping best practices like proper sanitization—or assuming the tools we use are bulletproof—could make us the weak link in someone else’s chain.
Here’s the kicker: this isn’t new. A 2006 Postgres forum thread (link) flagged the same UTF-8 escaping issue with PQescapeString. Invalid sequences could slip through—19 years later, it hit the Treasury.
Ever seen escaping fall apart? Got a trick to stop SQL injection? This one’s stuck with me—security hinges on what we don’t assume.
Resources: