dCTF 2021: Very Secure Website
Solving a PHP loose comparison challenge with a magic hash.
Original challenge writeup: Very Secure Website.md

The source code provided was:
<?php
if (isset($_GET['username']) and isset($_GET['password'])) {
if (hash("tiger128,4", $_GET['username']) != "51c3f5f5d8a8830bc5d8b7ebcb5717df") {
echo "Invalid username";
}
else if (hash("tiger128,4", $_GET['password']) == "0e132798983807237937411964085731") {
$flag = fopen("flag.txt", "r") or die("Cannot open file");
echo fread($flag, filesize("flag.txt"));
fclose($flag);
}
else {
echo "Try harder";
}
}
else {
echo "Invalid parameters";
}
?>
Looking up the username hash showed online results saying it had already been cracked 1000 times. The value was admin.
This one was hard, especially because it was the first challenge I tried and I did not know PHP.
I stared at the source for what felt like 20 minutes. The username check was straightforward, and I could not find anything to inject that would make the password branch evaluate to true.
The password hash was the only strange part.
In a string that long, seeing only one letter stood out a lot.
Solution
Eventually I found the issue: PHP loose comparison with ==.
The password check used == instead of ===:
hash("tiger128,4", $_GET['password']) == "0e132798983807237937411964085731"
With loose comparison, PHP treats 0e132798983807237937411964085731 like scientific notation. When it compares that value numerically, it evaluates to 0.
So the goal was to provide a password that also hashed to something PHP would treat as 0.
Luckily, other people had already collected magic hashes for tiger128,4:
https://github.com/spaze/hashes/blob/master/tiger128%2C4.md
One working value was LnFwjYqB.
That made the check look like this:
else if (hash("tiger128,4", "LnFwjYqB") == "0e132798983807237937411964085731")
The hash becomes:
"0e087005190940152635463034029558" == "0e132798983807237937411964085731"
PHP then compares both as numbers:
"0" == "0"
That evaluates to true.
Using admin as the username and LnFwjYqB as the password gave the flag:
dctf{It's_magic._I_ain't_gotta_explain_shit.}