[HN Gopher] Hidden Messages in Emojis and Hacking the US Treasury
       ___________________________________________________________________
        
       Hidden Messages in Emojis and Hacking the US Treasury
        
       Author : nickagliano
       Score  : 69 points
       Date   : 2025-03-14 03:13 UTC (3 days ago)
        
 (HTM) web link (slamdunksoftware.substack.com)
 (TXT) w3m dump (slamdunksoftware.substack.com)
        
       | hnlmorg wrote:
       | As someone who's done a fair amount with parsing of Unicode
       | strings lately, I'm not at all surprised by this bug.
       | 
       | Unicode is a surprisingly elegant system but also an open
       | invitation for all kinds of abuse.
        
         | giancarlostoro wrote:
         | I havent deep dived unicode enough to know, and probably
         | someone somewhere already made it, but I often wonder if we can
         | do compression more efficiently by abusing unicode somehow,
         | especially regarding plain text.
        
           | pavel_lishin wrote:
           | I swear qntm has written something about this.
        
             | nix0n wrote:
             | https://qntm.org/unicodings
        
         | rapind wrote:
         | > Unicode is a surprisingly elegant system...
         | 
         | s/elegant/clever
         | 
         | What could go wrong? I bet unicode is how AGI escapes and
         | enslaves humanity.
        
       | mtrovo wrote:
       | So many questions:
       | 
       | - why a no side-effects function on a database can be used to get
       | lateral access to the whole database instance
       | 
       | - why do you need to validate strings on the database itself and
       | not on the client anyway, heck why are there no type safe way of
       | doing it
       | 
       | - why would you want to execute shell commands from the database
       | itself
       | 
       | - Even if there's a real use case for executing commands like
       | that why is it enabled by default on a regular connection to the
       | database without you specifying a
       | THIS_IS_REALLY_DANGEROUS_BUT_I_PINKY_PROMISE_I_KNOW_WHAT_IM_DOING
       | flag to the connection handshake.
       | 
       | It's not always PHP but there are some kirks that are shrugged
       | off on PHP that makes me really concerned about the reliability
       | of projects coded with it.
        
         | jarebear6expepj wrote:
         | Your questions are programming language agnostic-- where did
         | your PHP angst come in? And are there specific things in PHP
         | that are problematic and avoidable by using a different Turing
         | complete language?
        
           | marsovo wrote:
           | PHP has grown up but in its wild youth was notorious for such
           | gems as mysql_escape_string vs mysql_real_escape_string,
           | rather than proper parameterization
           | 
           | It's not so much about Turing as it is libraries and patterns
           | 
           | After all, as I understand it this very issue was caused by
           | escaping SQL rather than parameterizing it
        
         | benmmurphy wrote:
         | They mentioned PAM module so maybe the sql injection just
         | allowed bypassing the authorization of a system that was using
         | the PAM module. Like it's in the realm of possibility that a
         | PAM module that wanted to validate a user against credentials
         | stored in a pg database might shell out to the psql command to
         | do this. Though, the whole thing is very questionable.
        
           | rapind wrote:
           | Yeah we're missing some info.
           | 
           | What account were they authenticating with when attaching to
           | psql?
           | 
           | If you have the connection string why does psql even matter,
           | couldn't you use any client? Or is this a case of your input
           | being forwarded to a running, already authenticated, psql
           | instance?
           | 
           | And finally, why do we need unicode support for schema? I
           | assume it's just supported by supporting unicode data itself,
           | and the schema is also data?
        
       | xmprt wrote:
       | I learned about the breach a few days ago but I didn't know that
       | you could "adopt" an emoji: https://aac.unicode.org/sponsors
       | 
       | That's a neat tidbit.
        
         | jfengel wrote:
         | Does "adopting an emoji" mean something other than "appearing
         | on that page"?
         | 
         | From the adoption page: Each adoption comes with a digital
         | badge and certificate that you can proudly display.
        
           | nkrisc wrote:
           | It's a fun way to donate.
        
       | charcircuit wrote:
       | >Beyond Trust did their due diligence by properly calling a
       | sanitization method on the user's string input using it in a
       | PostgreSQL query.
       | 
       | This is not due diligence. In band messaging of user controlled
       | data has been proven to be bad for security and this is not the
       | first time "escaping" user controlled data for SQL has been done
       | incorrectly.
        
         | Spivak wrote:
         | Yep, it's prepared statements or bust. But the long tail of
         | legacy code, examples, documentation, that uses escaping is
         | gonna take a while to get through.
         | 
         | One of the nice things about modern ORMs like SQLAlchemy 2 is
         | that it forces you to use prepared statements even when when
         | calling raw queries.
        
       | cogman10 wrote:
       | > Now, all of this might have been fine had Beyond Trust not
       | written a feature which allowed users to directly,
       | programmatically interact with psql (the postgres command line
       | interface).
       | 
       | That's the buried lede.
       | 
       | Yes, there was a vulnerability in psql... but that's so much less
       | a problem than the huge gaping hole of allowing users to directly
       | interact with psql.
       | 
       | No DB can be safe if you are turning untrusted user commands into
       | psql executions. It'd be like giving untrusted users ssh access
       | and then complaining when they find a privilege elevation
       | exploit.
        
         | zoeysmithe wrote:
         | Just a guess but this looks like politically powerful dev
         | culture overwriting cybersecurity culture, demanding, thus
         | getting an exception from management for 'productivity' and
         | 'being agile.'
         | 
         | I dont think we appreciate how much of a wild west things are
         | with the incredible mix of hugely complex and powerful tools
         | available trivially to developers and the concept of "move
         | fast, break things."
         | 
         | Especially as corporate sees devs like they see salesmen (big
         | moneymakers who deserve perks, exceptions) and top-down
         | security culture as a cost center.
         | 
         | The other buried ledes are that postgres allows emojis (not
         | sure if that's intended but it works) and that you can just run
         | system commands and scripts directly from postgres cli. I
         | imagine a lot of eyes are going to be on new hardening
         | guidelines for postgres now.
         | 
         | I also imagine the first high performance enterprise friendly
         | drop-in db written in something like rust is going to one day
         | be a big deal.
        
           | Terr_ wrote:
           | Hey now, a large portion of developers are seen as cost-
           | centers too! Not everybody has the skill of flattering
           | managers into approving greenfield projects, and then
           | transferring away before they break horribly. :p
        
           | randmeerkat wrote:
           | > Especially as corporate sees devs like they see salesmen...
           | 
           | You're onto something here. People perceive the world through
           | the lens of their education and environment. Sales, legal,
           | finance, are all easy constructs for a business leader to
           | view the rest of the world through. The secret of the game
           | isn't to have the best tech or to code the most, it's to
           | "outsell" your competing business unit.
        
         | 0xbadcafebee wrote:
         | The only reason BeyondTrust implemented that was _it wasn 't
         | untrusted user commands_. They sanitized the data, so it should
         | have been fine. The unfortunate problem was that the sanitizer
         | didn't sanitize.
         | 
         | Systems are built on a set of expectations. Undermine the
         | expectations and you undermine the system.
        
           | cogman10 wrote:
           | > They sanitized the data, so it should have been fine.
           | 
           | This is a 101 rookie level approach to SQL or injection
           | defense.
           | 
           | It's dumb for exactly the same reason why this is dumb
           | "SELECT * FROM foo WHERE bar=" + sanitize(userInput)
           | 
           | The correct way to do something like this will always be
           | parameterized input which looks something like this
           | "SELECT * FROM foo WHERE bar=?"         bindParameter(1,
           | userInput);
           | 
           | Why? Because that the postgres protocol splits out the
           | command and the data for the command in a way that can't be
           | injected. Something that should be viewed as impossible to do
           | when data and command are merged into 1 String.
           | 
           | IF this company wanted to build dynamic queries, then the
           | only correct way to do that is to limit input to only valid
           | variables. IE "isValidColumnName(userInput)" before sending
           | the request. And even then, you'd not use psql to do that.
           | 
           | You simply can't use a generalized sanitizer and expect good
           | results.
        
             | Nijikokun wrote:
             | His argument is that you are building on the assumption
             | that this is safe:                   "SELECT * FROM foo
             | WHERE bar=?"         bindParameter(1, userInput);
             | 
             | But what happens when it turns out that isn't safe?
        
         | gowld wrote:
         | Let's be clear: Beyond Trust is not a company that wrote a
         | database-backed web app and made the all-too-common mistake of
         | writing insecure code that tickled a bug in the database that
         | allowed privilege escalation. Beyond Trust's is a company whose
         | entire contribution is _adding a security layer to prevent
         | privilege escalation_ , and their solution here was to _bypass_
         | Postgres 's standard functionality and use this weird `psql`
         | hack instead.
         | 
         | They had _one job_ , and they failed at it. This amateur-level
         | mistake should sink the entire company.
        
       | dalemhurley wrote:
       | I have been playing a lot with emoji smuggling with
       | https://toolnames.com/utilities/emoji-smuggler and
       | https://chat.full.cx inspired by https://emoji.paulbutler.org/
       | 
       | I had not considered it as an injection attack.
        
       | Terr_ wrote:
       | > In order to to this, PQescapeStringInternal must call
       | pg_utf_mblen.
       | 
       | For a moment I had a dev-flashback to the problem of utf8mb4
       | versus (broken) utf8 in mySQL. Easy now, this is _Postgres_ ,
       | everything is safe(er)...
        
       | netsharc wrote:
       | Geez, any summary of this article that tells it like the reader
       | isn't five years old?
        
         | 0xbadcafebee wrote:
         | A PHP app called a Postgres library function to "escape
         | strings" for use in Postgres, and that called a function to get
         | a utf8 string length, but the function was bullshit:
         | 
         | > The PQescapeStringInternal method doesn't actually validate
         | that the string it is parsing with pg_utf_mblen is valid
         | Unicode. So, instead, it just takes the length of 2, and grabs
         | the next byte.
         | 
         | So the bug was a shitty function in a generic open source
         | library which was probably never properly tested or fuzzed,
         | which ended up letting attackers move laterally through the
         | database. And this is one reason you want full test coverage;
         | tiny stupid functions matter.
         | 
         | (Another fix for this is to enforce at the boundaries of every
         | function that the input data has been "blessed" or sanitized by
         | some other function whose purpose is just to validate that the
         | data is what it's supposed to be. That would have to happen
         | _before_ escaping, and every function that uses that data would
         | need to confirm that it got blessed. Basically you want a home-
         | rolled strong-typing system with types (or data classes?) for
         | all your data. But that 's a lot of work, I don't expect many
         | would do that for most apps)
        
           | gowld wrote:
           | * Custom Unicode parsing code in the middle of an "escape"
           | function
           | 
           | * No static type-checking for Unicode data
        
         | defen wrote:
         | UTF-8 encodes a unicode codepoint into 1, 2, 3, or 4 bytes.
         | _Assuming_ that you have a valid UTF-8 encoding of a codepoint,
         | _then_ the first byte tells you how many bytes are in the
         | encoding. 0-127 inclusive means one byte, 192-223 means 2,
         | 224-239 means 3, and 240-247 means 4. If the first byte is 0xC0
         | (192), then the sequence is two bytes long. However, not every
         | 2-byte sequence that starts with 0xC0 is valid UTF-8. The
         | uppermost bits of the second byte must be `10` in a valid
         | 2-byte UTF-8 sequence. 0x27 does not meet that criteria, so
         | `0xC0 0x27` is not valid UTF-8. If your escape function
         | operates at the level of unicode codepoints but doesn 't
         | actually verify that they're valid, you end up copying a single
         | quote into your "escaped" buffer that downstream parts of the
         | code will hit.
        
       | kevinsync wrote:
       | Fun fact: MySQL (and I'm sure many other databases?) lets you
       | pass in values directly as hexadecimal strings.
       | 
       | I've avoided SQL injection since ancient times not by escaping
       | strings, but by transforming any given input via "0x" +
       | bin2hex(value) and plopping that into the query.
       | 
       | No quotes needed, no code buried deep in included libraries
       | needed, handles any kind of data possible, and also no funny
       | business sneaking in based on how you may have mangled the input.
        
       ___________________________________________________________________
       (page generated 2025-03-17 23:00 UTC)