====== Warcrypt ======
===== Overview =====
On a accès à une page web. Il faut jouer contre un bot qui visiblement cheat.
{{http://i.imgur.com/zoXye.png}}
===== Résolution =====
On se rend vite compte que la vérification de qui gagne se fait par une requête ajax.
function send() {
var ajax = $.ajax({
type: "POST",
url: "ajax/send.php",
data: "card="+$('.card:last').attr("alt")+"&sign="+$('.card:last').attr("id")
});
ajax.done(function(rep) {
$('.card > img:last').remove();
var json = eval('(' + rep +')');
$('#score').html('Score : ' + json.score);
$('.cheater_card').html('');
if ($('.card > img').length == 0) {
$(location).attr('href','end.php');
}
$('.resultat').html(json.msg);
});
}
Chaque carte possède un token pour la valider côté serveur. On remarque assez facilement que ce token est généré en fonction du nom de la carte. Par exemple, le token du 5 de coeur est ''NHGrjegtFmHu2'', son nom est ''05HEART''.
php > var_dump(crypt("05HEART","NH"));
string(13) "NHGrjegtFmHu2"
Il nous faut trouver une carte qui pourrait battre l'as de pique du bot. Lorsqu'on envoie n'importe quoi comme nom de carte, on a comme réponse ''{"msg":"Error : Unknown card"}''. Il y a donc une vérification sur le nom de la carte avant de vérifier le token.
La carte qui viendrait à l’esprit de tout le monde est bien évidemment le Joker. On met ''JOKER'' comme nom de carte et on reçoit comme réponse ''{"msg":"Error : sign not match"}''. Nice, la carte que l'on cherche est bien ''JOKER'' !
php > var_dump(crypt("JOKER","NH"));
string(13) "NHMTkwCoDsRC6"
On renvois la requête qui va bien
POST /ajax/send.php HTTP/1.1
Host: 109.232.238.5:8001
User-Agent: Mozilla/5.0 Gecko/20100101 Firefox/13.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cookie: PHPSESSID=2sk41kuhqosdg27ikft6j2h0a3
Content-Type: application/x-www-form-urlencoded
Content-Length: 29
card=JOKER&sign=NHMTkwCoDsRC6
Et ... on choppe un ''{"msg":"Error : sign not match"}''...
Si c'est pas le bon token, c'est que des caractères ont été rajoutés à la fin de ''JOKER''. On remarque que l'on peut ajouter autant de caractère que l'on veut derrière une carte classique, le token sera toujours le bon. La taille des noms des cartes classiques sont tous de la même taille : 7 chars. ''JOKER'' fait que 5 chars! Donc 2 chars ont été rajoutés derrière! Il ne nous reste plus qu'à tester toutes les possibilités :-) (pour cela, python!)
#!/usr/bin/env python
# encoding: utf-8
import sys
import httplib
from crypt import crypt
def main():
headers = {"Cookie":"PHPSESSID=2sk41kuhqosdg27ikft6j2h0a3;",
"Content-Type":"application/x-www-form-urlencoded"}
card = "JOKER"
for char1 in range(31,127):
for char2 in range(31,127):
conn = httplib.HTTPConnection("109.232.238.5:8001")
conn.request("POST","/ajax/send.php","card={card}&sign={0}".format(crypt(card+chr(char1)+chr(char2),"NH"),card=card),headers)
rep = conn.getresponse()
html = rep.read()
if html.count("Error") < 1:
print crypt("JOKER"+chr(char1)+chr(char2),"NH")
print chr(char1)+chr(char2)
print char1, char2
sys.exit(0)
else:
print html
print chr(char1)+chr(char2)
if __name__ == '__main__':
main()
$ ./warcrypt.py
{"msg":"Error : sign not match"}
...
{"msg":"Error : sign not match"}
EP
...
NH3J7Hldb9sQg
P4
Well! Les deux caractères ajoutés après ''JOKER'' sont ''P4'' !
On se débrouille pour avoir un score de 27 (27 cartes gagnées) et on se rend sur ''end.php'' (redirection automatique normalement) et on choppe le flag :-)
''WIN the flag is H0UR4P0URL3C4SS0UL3T''
On valide avec md5("H0UR4P0URL3C4SS0UL3T") = **9b808110b3986a27019f2e153998e1e6**