Skip to content
DevMeme
7234 of 7435
EU Age Verification App Hacked in Under 2 Minutes
Security Post #7932, on Apr 16, 2026 in TG

EU Age Verification App Hacked in Under 2 Minutes

Why is this Security meme funny?

Level 1: A Lock With the Key Taped to the Door

Imagine a house with a fancy keypad lock on the front door. It looks secure — there's a PIN, there's even a little "fingerprint required" sticker. But the keypad keeps the correct code, the try-counter, and the "fingerprint on/off" switch all written on a sticky note taped to the outside of the door. Anyone walking up can read the note, flip the fingerprint switch to "off," erase the code, type their own, and walk in — and the house still insists they're the rightful owner. That's the joke: the security looked real, but every lock in it could be reset by the person it was supposed to keep out.

Level 2: What shared_prefs Actually Is

A few terms make the joke land. shared_prefs is Android's lightweight storage for app settings: small XML files under the app's private data directory. "Private" only means other apps can't read it on a non-rooted device — the app itself, the OS owner, anyone with a device backup, and any rooted/emulated environment can read and edit it freely. So putting a security secret there and assuming nobody can touch it is the client_side_security trap.

Encrypt vs. hash: encryption is a two-way door (you can lock and unlock with a key), while hashing is a one-way door (you can check a match but never recover the original). For verifying a PIN, you want the one-way door. The PinEnc/PinIV names (Enc = ciphertext, IV = initialization vector, the random starter value a cipher needs) reveal the app chose the two-way door.

Rate limiting normally means "after N wrong tries, slow down or lock out." That decision has to be enforced somewhere the attacker can't edit — a server, or at minimum tamper-resistant storage. Here it's a counter in the same editable file, so the attacker resets it to 0 and brute-forces forever. This is the kind of thing you only internalize after your first "wait, the client can just lie to us?" production incident.

Level 3: The Client Is Not Your Friend

The post is a screenshot of an X thread from Paul Moore - Security Consultant (@Paul_Reviews), opening with the line:

Hacking the #EU #AgeVerification app in under 2 minutes.

What follows is a checklist of the oldest mistake in application security: trusting the client. The app, by his account, takes the PIN you create during setup, encrypts it, and drops it into Android's shared_prefs directory — a per-app folder of XML key/value files that the app owner (and anyone with root, a backup extraction, or a rooted/emulated device) can read and rewrite at will. That single design choice is the whole vulnerability, and everything else is just consequences.

The first sin he names is conceptual:

  1. It shouldn't be encrypted at all - that's a really poor design.

He's right, and the distinction matters. A PIN you need to verify should never be recoverable — you store a salted hash (ideally via a slow KDF like scrypt, bcrypt, or Argon2), then hash the user's input and compare. Encryption is reversible by definition; it implies the system can get the plaintext PIN back, which is exactly what you don't want for an authenticator. Choosing encrypt over hash here is the hash_vs_encrypt confusion that haunts junior and government-contractor code alike.

The second sin is architectural:

  1. It's not cryptographically tied to the vault which contains the identity data.

This is the killer. The PIN and the identity vault are stored as independent blobs. Because nothing binds them — no key derived from the PIN that actually decrypts the vault — the PIN is decorative. The attack he describes is brutally simple: delete the PinEnc/PinIV values from shared_prefs, restart, set a brand-new PIN, and the app cheerfully re-attaches the old profile's verified credentials to your new PIN and presents them as valid. The proof of "I am a verified adult" is now portable to anyone holding the file.

Then come the Other issues:, which read like a satire of security_theater:

  1. Rate limiting is an incrementing number in the same config file. Just reset it to 0 and keep trying.
  2. "UseBiometricAuth" is a boolean, also in the same file. Set it to false and it just skips that step.

A rate limiter that lives in a file the attacker controls isn't a rate limiter; it's a polite suggestion. A UseBiometricAuth boolean in attacker-writable storage means the "biometric protection" is one false away from oblivion. The systemic pattern is client-side-only enforcement of trust decisions — the canonical anti-pattern. Any control that protects the server's trust in the client must be validated server-side, because the client device is, in threat-model terms, fully hostile. Government identity software gets a pass on this far too often: procurement rewards shipping over review, the people who write the spec rarely threat-model, and "it has encryption" satisfies a compliance checkbox without anyone asking what the encryption is bound to. The brutal irony is that this is an age verification app — a system whose entire reason to exist is gatekeeping — built so that the gate swings open if you delete two strings.

Description

A dark-mode X post by Paul Moore - Security Consultant (@Paul_Reviews, avatar of a hooded figure with binoculars) describing 'Hacking the #EU #AgeVerification app in under 2 minutes.' He explains that during setup the app creates a PIN, *encrypts* it, and stores it in the Android shared_prefs directory; he criticizes that (1) it shouldn't be a recoverable encrypted PIN at all and (2) it isn't cryptographically tied to the vault holding identity data. An attacker can delete the PinEnc/PinIV values from shared_prefs, restart the app, set a new PIN, and the app presents the old profile's credentials as valid. Other issues: rate limiting is an incrementing counter in the same config file (reset to 0 and keep trying), and 'UseBiometricAuth' is a boolean in the same file (set false and biometrics are skipped). A textbook case of client-side-only security controls in a government identity app

Comments

34
Anonymous ★ Top Pick Storing the PIN, the rate limiter, and the 'UseBiometricAuth' flag in one client-side file isn't defense in depth - it's a settings menu for the attacker
  1. Anonymous ★ Top Pick

    Storing the PIN, the rate limiter, and the 'UseBiometricAuth' flag in one client-side file isn't defense in depth - it's a settings menu for the attacker

  2. dev_meme 2mo

    *they actually name it to be just a demo and every member state must do their own but cmon

    1. @Valithor 2mo

      This was my first question when I read this, whether it was just a demo, because if so that makes complete sense to not lock test users into a model that might change before an actual deployment. I'm sure the final version will use a security key or something similar.

    2. @Box_of_the_Fox 2mo

      Doing it properly would require using Secure Element which would be quite hard and pointless for a demo

  3. @deimossos 2mo

    To be fair, its hard to do mass surveillance, without having some security vulnerabilities maybe some information just shouldn't be collected...

  4. @ZmEYkA_3310 2mo

    W age verification 🤯🤯

  5. @soreiut 2mo

    So, an attacker can simply remove the PinEnc/PinIV values from the shared_prefs file and restart the app. You need root to access shared pref

  6. @RiedleroD 2mo

    there's an age verification app?

    1. @acidbong 2mo

      yes, but not enforced and only presented for demo purposes

      1. @mechtaros 2mo

        not enforced yet

  7. @akiyiwen 2mo

    Nothing is secure if others can have your device physically...

  8. @SamsonovAnton 2mo

    If an attacker is that smart, he surely passes the age verification.

    1. @azizhakberdiev 2mo

      doesn't matter, attackers can make rogue clients or 3rd party software that will help bypass these checks

    2. @deadgnom32 2mo

      don't underestimate kids. back in my days hacking starforce was a part of daily life.

  9. @azizhakberdiev 2mo

    cmon, didn't lucky patcher teach us not to trust edge device side data?

    1. @TheFloofyFloof 2mo

      can't use lucky patcher once sideloading is deemed illegal

  10. @azizhakberdiev 2mo

    also while we are at it, kids are very smart

  11. @azizhakberdiev 2mo

    even if it gets them +0.001 robux they will hack it

  12. @Sun_Serega 2mo

    I'm actually excited for more ability to verify the age, especially from EU side it can't possibly be worse than for-profit american options like persona and being able to exclude kids from my vrchat lobbies is a big + not excited for it being mandated in basic communication apps tho...

    1. @TheFloofyFloof 2mo

      VRChat uses persona and has doesn't appear to have plans to change vendors

      1. @Sun_Serega 2mo

        yeah, but I have plans to change the platform to Resonite its better in almost every way for me, but when they support questies - its gonna be flooded with kids, just like vrc has been

  13. @Sun_Serega 2mo

    and yeah, there is no age verification, but there are all the tools to implement your own for your own lobbies

  14. @nyxiereal 2mo

    What the fuck shared_preferences sure is easier to use than secure_storage, but it's not that hard

  15. @YaroST12 2mo

    Is the CN still not up on that post? He's showing off the vulnerability with a rooted device, you cant "simply remove" something from shared_prefs. If you can do that - you've blown open most, if not all, of Android's security measures.

    1. @death_by_oom 2mo

      The main problem of the post is that the app is actually a demo. But I would expect the actual age verification app that stores my official documents and can be used to prove my age and identity to be zero trust

      1. @YaroST12 2mo

        If the Baltics were able to develop a government level 2FA system (Smart ID) then I think the EU can do the same, especially if it's gonna be open source.

  16. @NaNmber 2mo

    A post from Durov on the matter 🙂 https://t.me/durov/491

    1. @RiedleroD 2mo

      mhm. sure

      1. @death_by_oom 2mo

        He's not wrong though, ID and Age verification apps should be in the same category as passwords manager. They should be zero knowledge based, otherwise that's asking for trouble

        1. @RiedleroD 2mo

          well duh. but his conclusion about it being on purpose is straight up a conspiracy theory

          1. @death_by_oom 2mo

            True, but what I don't understand is why even show this unfinished Swiss cheese security demo app

            1. @RiedleroD 2mo

              I'm guessing that it was less work for the contractor this way. get overpaid and then underdeliver, is the motto of any and all government contractors

              1. @death_by_oom 2mo

                Maybe

  17. @sandor73 2mo

    ok but you still need physical access to the device

Use J and K for navigation