dCTF 2021: Very Secure Website
Resolvendo um desafio de comparação fraca em PHP com um magic hash.
Writeup original do desafio: Very Secure Website.md

O código-fonte fornecido era:
<?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";
}
?>
Pesquisar o hash do username mostrou resultados online dizendo que ele já tinha sido quebrado 1000 vezes. O valor era admin.
Esse foi difícil, especialmente porque foi o primeiro desafio que tentei e eu não sabia PHP.
Fiquei encarando o source pelo que pareceu uns 20 minutos. A checagem do username era direta, e eu não conseguia encontrar nada para injetar que fizesse o branch da senha avaliar como true.
O hash da senha era a única parte estranha.
Em uma string tão longa, ver apenas uma letra chamava muita atenção.
Solução
Eventualmente encontrei o problema: comparação fraca em PHP com ==.
A checagem da senha usava == em vez de ===:
hash("tiger128,4", $_GET['password']) == "0e132798983807237937411964085731"
Com comparação fraca, o PHP trata 0e132798983807237937411964085731 como notação científica. Quando compara esse valor numericamente, ele avalia para 0.
Então o objetivo era fornecer uma senha cujo hash também fosse tratado pelo PHP como 0.
Por sorte, outras pessoas já tinham coletado magic hashes para tiger128,4:
https://github.com/spaze/hashes/blob/master/tiger128%2C4.md
Um valor que funcionava era LnFwjYqB.
Isso fazia a checagem ficar assim:
else if (hash("tiger128,4", "LnFwjYqB") == "0e132798983807237937411964085731")
O hash se torna:
"0e087005190940152635463034029558" == "0e132798983807237937411964085731"
O PHP então compara ambos como números:
"0" == "0"
Isso avalia como true.
Usar admin como username e LnFwjYqB como password deu a flag:
dctf{It's_magic._I_ain't_gotta_explain_shit.}