Description
ABC Bank's "impossible" login hides a PHP backup that reveals how the server compares username and password. If the SHA1 hashes match while the raw values differ, the code returns /flag.txt.
Setup
View-source on impossibleLogin.php and append ~ to the URL (an Emacs backup) to recover the actual PHP code.
Decode the Base64 strings to learn that matching SHA1 hashes returns the flag from ../flag.txt.
curl -O https://shattered.io/static/shattered-1.pdfcurl -O https://shattered.io/static/shattered-2.pdfcurl -X POST -F "username=<shattered-1.pdf" -F "pwd=<shattered-2.pdf" http://verbal-sleep.picoctf.net:50313/impossibleLogin.phpSolution
- Step 1Recover the backupBrowsing impossibleLogin.php~ exposes the real PHP. Base64-decoded strings show the POST parameter names (username/pwd) and reveal the SHA1 equality check.
Learn more
Emacs editor backup files are created automatically by Emacs when a file is opened for editing. The backup is given the same filename with a trailing tilde (
~). When developers edit server-side files directly using Emacs on a web-accessible directory and those backup files are not deleted, they become publicly downloadable - exposing source code that was never intended to be served.This is a real-world information disclosure vulnerability. Common variants include Vim swap files (
.swp,.swo), nano backup files (.save), and various editor-specific temp files. Web servers generally do not strip these by default, so they must be explicitly blocked in server configuration or managed through deployment pipelines.The Base64 encoding inside the PHP source is a thin attempt to obscure hardcoded constants. Once the source is obtained, decoding takes a single command. This pattern - encoding rather than encrypting secrets - gives only the illusion of security and is a common beginner mistake found in real production code as well.
- Step 2Exploit the SHA1 collisionUse the `shattered-1.pdf`/`shattered-2.pdf` pair from Google's SHA1 collision research. POST them as username/pwd so the raw bytes differ but SHA1 digests match, triggering `file_get_contents('../flag.txt')`.
Learn more
SHA1 was broken in practice by Google's SHAttered attack in 2017, which produced two distinct PDF files with identical SHA1 digests. This was the first publicly demonstrated SHA1 chosen-prefix collision, requiring around 9.2 quintillion SHA1 computations and distributed GPU time over many months. The shattered.io website hosts these canonical collision files.
The PHP comparison
sha1($username) === sha1($password)is designed to require the inputs to be "different but equivalent." By postingshattered-1.pdfas the username andshattered-2.pdfas the password, both sides hash to the same digest while the raw byte content differs - exactly what the check demands. PHP's===strict comparison on the hash string still passes because the digests are literally equal.This illustrates why broken hash functions must never be used for security-sensitive comparisons. SHA1 is deprecated for code signing, TLS certificates, and any integrity or authentication use case. Modern code should use SHA-256, SHA-3, or for passwords specifically, algorithms designed for slow hashing like bcrypt, Argon2, or scrypt.
Note that the
curl -F "field=<file"syntax reads the file content and sends it as the field body, allowing binary PDF bytes to be sent as multipart form data - which is how the PDF collision bytes reach the PHP comparison.
Alternate Solution
When decoding the Base64 strings embedded in the PHP backup file, use the Base64 Decoder on this site to quickly reveal the POST parameter names and SHA1 check logic - no terminal needed to decode the obfuscated constants.
Flag
picoCTF{w3Ll_d3sErV3d_Ch4mp_5b26...}
Any SHA1 collision pair works; the shattered.io PDFs are the canonical example.