Write It Back: Running a Password Transformation Forwards

Security Fundamentals Level 2/4 ~3 min October 1, 2026

The challenge

Aldermoor Library still runs the membership system it bought in 2011. A penetration test found an injection point that can write to the members table, and the vendor manual explains what the pw_enc column holds: pw_enc = base64_encode(strrev(password)). Not a hash. Not a salt. A reversal and an encoding, both of which run in either direction. The seeded demo account proves it: demo.reader has the password demo1234 and pw_enc NDMyMW9tZWQ=. You want the librarian account to accept the password Kestrel-88 on the next login. Produce the exact string to write into pw_enc.

What you'll learn

  • Apply an encoding chain forwards rather than peeling one backwards
  • Read a nested transformation and work out which operation runs first
  • Explain the practical difference between a hash and a reversible transformation
  • Describe what write access to a password column is worth when storage is reversible

Skills tested

Encoding manipulationPassword storage analysis

Prerequisites

  • What base64 is
  • Reading a function call from the inside out

How it works

Password storage has exactly one job: given the stored value, nobody should be able to work out an input that matches it. A modern password hash achieves that by being one-way and deliberately slow, so the only route from a stolen database to a working login is guessing, at a cost per guess the defender chose.

What this library system does is not that. base64_encode(strrev(password)) is two reversible steps. Read access to the column hands over every password in plaintext, and write access is stronger still: you do not need to know the current password, because you can compute the stored form of any password you like and put it there.

Direction is the skill on display. Nested calls read from the inside out, so strrev runs first and base64_encode wraps whatever it returns. Swap the two and you still get a well-formed base64 string, which is why this class of mistake survives a casual review: the output looks exactly as convincing when it is wrong.

Common mistakes

  • Encoding before reversing. Both orders produce valid base64. Only one decodes to the password spelled backwards.
  • Submitting the reversed password. 88-lertseK is the intermediate value, not what the column holds.
  • Dropping or adding padding by hand. Let the tool produce it. The == at the end is part of the encoding, not decoration.
  • Reaching for a decode operation. Nothing here is encoded yet. The input is plaintext.

How to defend against it

Store a verifier, never a recoverable value.

  • Use a password hash designed for the job, such as argon2id or bcrypt, with a per-user salt. Encoding and reversal are not weak hashing, they are not hashing at all.
  • Migrate on next login: verify against the legacy column once, write a proper hash, clear the old one. A system that cannot be rewritten can still be drained of reversible values one login at a time.
  • Treat the password column as a write-only surface for the application. If an injection can reach it, the account takeover needs no credential at all.
  • Fix the injection as the primary defect. Reversible storage is what makes it catastrophic, but parameterised queries are what stop it.

Full solution

Pro and Max members unlock the complete step-by-step walkthrough.

Go Pro

Community stats

159 completions
87% success rate
Malekith First blood

Related Daily Hacks

30,000+ Hackers Real labs Free
Start Hacking Free or solve today's hack, no account needed