Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Trouvez la faille! - Confoo 2012

1,707 views

Published on

Source code security review challenge at Confoo 2012 - Montreal (confoo.ca)

The audience was challenged in attempting to spot security vulnerabilities in a series of source code examples.

Published in: Technology
  • Be the first to comment

Trouvez la faille! - Confoo 2012

  1. 1. Trouvez la faille! Antonio Fontes / Confoo 2012 - MontréalNotice 1: cette présentation contient des références à Common Weakness Enumeration:http://cwe.mitre.org/data/index.htmlNotice 2: aucun chat na été maltraité durant la préparation de cette séance.Notice 3: cette présentation contient des références audocument "Test your Security IQ", par M. Howard et B. SullivanNotice 4: Un grand merci à Sébastien pour ses idées & propositions!
  2. 2. • Règles de jeu: – Lire l’exemple de code affiché à lécran – Trouver la ou les éléments pouvant constituer un risque pour la sécurité du S.I. – Tenir une comptabilité analytique des points obtenus! 02.03.2012 Confoo Conference 2012 - Antonio Fontes 2
  3. 3. Antonio Fontes Genève (Suisse) Consultant indépendant Infosécurité logicielle: Sécurité des applications web Visibilité et gestion du risque sur Internet Formation / accompagnement durant les projets de développement Bulletin dinformation"cybermenaces et sécurité Internet": http://cddb.ch OWASP: Membre du Comité - OWASP Suisse Leader - OWASP GenèveA propos du conférencier… 02.03.2012 Confoo Conference 2012 - Antonio Fontes 3
  4. 4. • Le site d’actualité permet la création de comptes personnels, la publication de réactions à l’actualité, l’échange de messages entre membres.• 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 4
  5. 5. Envoi des éléments dauthentification en clair.• Le site d’actualité permet la création de comptes personnels, la publication de réactions à l’actualité, l’échange de messages entre membres.• 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 5
  6. 6. • Idem. 02.03.2012 Confoo Conference 2012 - Antonio Fontes 6
  7. 7. function printFile($username,$filename){ //read file into string $file = file_get_contents($filename); if ($file && isOwnerOf($username,$filename)){ echo $file; return true; } else { echo You are not authorized to view this file; } return false; }• 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 7
  8. 8. function printFile($username,$filename){ //read file into string $file = file_get_contents($filename); Le chargement du if ($file && isOwnerOf($username,$filename)){ fichier a lieu avant echo $file; le contrôle daccès. return true; } else { echo You are not authorized to view this file; } return false; }• 1 point – Identification du risque pour la disponibilité du S.I. 02.03.2012 Confoo Conference 2012 - Antonio Fontes 8
  9. 9. protected void Page_Load(object sender, EventArgs e) { string lastLogin = Request["LastLogin"]; if (String.IsNullOrEmpty(lastLogin)) { HttpCookie lastLoginCookie = new HttpCookie("LastLogin", DateTime.Now.ToShortDateString()); lastLoginCookie.Expires = DateTime.Now.AddYears(1); Response.Cookies.Add(lastLoginCookie); } else { Response.Write("Welcome back! You last logged in on " + lastLogin); Response.Cookies["LastLogin"].Value = DateTime.Now.ToShortDateString(); } }• 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 9
  10. 10. protected void Page_Load(object sender, EventArgs e) { string lastLogin = Request["LastLogin"]; if (String.IsNullOrEmpty(lastLogin)) { Appel vers la HttpCookie lastLoginCookie = new HttpCookie("LastLogin", collection parente DateTime.Now.ToShortDateString()); "Request" lastLoginCookie.Expires = DateTime.Now.AddYears(1); Response.Cookies.Add(lastLoginCookie); } else { Response.Write("Welcome back! You last logged in on " + lastLogin); Response.Cookies["LastLogin"].Value Transfert du contenu = DateTime.Now.ToShortDateString(); vers le client, sans } encodage approprié. }• 1 point – Cas de type "XSS" (Cross-site scripting) 02.03.2012 Confoo Conference 2012 - Antonio Fontes 10
  11. 11. $role = $_COOKIES[role]; if (!$role) { $role = getRole(user); if ($role) { // save the cookie to send out in future responses setcookie("role", $role, time()+60*60*2); } else { ShowLoginScreen(); die("n"); } } if ($role == Reader) { DisplayMedicalHistory($_POST[patient_ID]); } else { die("You are not Authorized to view this recordn"); }• 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 11
  12. 12. $role = $_COOKIES[role]; if (!$role) { Absence de contrôle $role = getRole(user); dintégrité du if ($role) { cookie. // save the cookie to send out in future responses setcookie("role", $role, time()+60*60*2); } else { Contournement du ShowLoginScreen(); mécanisme die("n"); dauthentification } } if ($role == Reader) { Contournement du DisplayMedicalHistory($_POST[patient_ID]); contrôle daccès } else { die("You are not Authorized to view this recordn"); }• 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 12
  13. 13. • 1 + 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 13
  14. 14. Transfert de confiance à un tiers• 1 + 1 + 1 points – 1 point: identification du risque dinjection de contenu par un tiers – 1 point: identification du risque sur la confidentialité (fuite des referrers) – 1 point: identification du risque de déni de service sur le tiers 02.03.2012 Confoo Conference 2012 - Antonio Fontes 14
  15. 15. • 1 + 1 + 1 points – Faire attention aux recommandations sur le web: elles vont souvent à lencontre de la sécurité et visent à faciliter la collecte de données par des tiers. – Vérifier qui est lauteur dune recommandation de codage. 02.03.2012 Confoo Conference 2012 - Antonio Fontes 15
  16. 16. // API flag, output JSON if set $json = $_GET[json]; $username = $_GET[user]; if($json) { $record = getUserRecord($username); echo(json_encode($record)); } else { $record = getUserRecord($username); foreach($record as $fieldName => $fieldValue) { // never disclose user email addresses to the public (privacy req.) if(!($fieldName == "email_address")) renderToHtmlTable ($fieldName,$fieldValue); }}}• 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 16
  17. 17. // API flag, output JSON if set $json = $_GET[json]; Dans le cas json, $username = $_GET[user]; ladresse email nest if($json) { plus protégée contre les $record = getUserRecord($username); fuites. echo(json_encode($record)); } else { $record = getUserRecord($username); foreach($record as $fieldName => $fieldValue) { // never disclose user email addresses to the public (privacy req.) if(!($fieldName == "email_address")) renderToHtmlTable ($fieldName,$fieldValue); }}}• 1 point – Identification de la fuite dadresses email 02.03.2012 Confoo Conference 2012 - Antonio Fontes 17
  18. 18. byte[] GetKey(UInt32 keySize) { byte[] key = null; try { key = new byte[keySize]; RNGCryptoServiceProvider.Create().GetBytes(key); } catch (Exception e) { Math.Random r = new Math.Random(); r.NextBytes(key); } return key; }1 + 1 + 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 18
  19. 19. byte[] GetKey(UInt32 keySize) { byte[] key = null; try { key = new byte[keySize]; RNGCryptoServiceProvider.Create().GetBytes(key); } catch (Exception e) { Exception générique? Math.Random r = new Math.Random(); r.NextBytes(key); } Math.Random? Fail-safe? return key; }1 + 1 + 1 point – Exception générique: privilégier lexception typée – Principe de conception "Fail-safe": le code néchoue pas en haute sécurité – La classe Math.Random ne fournit pas dentropie de niveau cryptographique 02.03.2012 Confoo Conference 2012 - Antonio Fontes 19
  20. 20. private decimal? lookupPrice(XmlDocument doc) { string query = @"//products/product[id/text()=" + Request["itemId"] + "]/price" XmlNode node = doc.SelectSingleNode(query); if (node == null) return null; else return(Convert.ToDecimal(node.InnerText)); }• 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 20
  21. 21. private decimal? lookupPrice(XmlDocument doc) { string query = @"//products/product[id/text()=" + Request["itemId"] + "]/price" XmlNode node = doc.SelectSingleNode(query); Validation? if (node == null) return null; else return(Convert.ToDecimal(node.InnerText)); }• 1 point – Injection de type Xpath (il ny a pas que des injections SQL!!) – Marche aussi sur: commandes système, LDAP, APIs ORM, etc. 02.03.2012 Confoo Conference 2012 - Antonio Fontes 21
  22. 22. public class MySessionIDManager : System.Web.Session State.SessionIDManager { private static object lockObject = new object(); public override string CreateSessionID(HttpContext context) { lock (lockObject) { Int32? lastSessionId = (int?)context.Application ["LastSessionId"]; if (lastSessionId == null) lastSessionId = 1; else lastSessionId++; context.Application["LastSessionId"] = lastSessionId; return lastSessionId.ToString(); }}}• 1 + 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 22
  23. 23. public class MySessionIDManager : System.Web.Session State.SessionIDManager { private static object lockObject = new object(); public override string CreateSessionID(HttpContext context) { lock (lockObject) Multi-serveur? { Int32? lastSessionId = (int?)context.Application ["LastSessionId"]; if (lastSessionId == null) lastSessionId = 1; ID de session prédictibles else lastSessionId++; context.Application["LastSessionId"] = lastSessionId; return lastSessionId.ToString(); }}}• 1 + 1 point – Identification de lidentifiant de session prédictible – Collision des identifiants de session si le serveur est répliqué! 02.03.2012 Confoo Conference 2012 - Antonio Fontes 23
  24. 24. bool login(SqlConnection connection, out string errorMessage) { string uname = Request.Form["username"]; string pword = Request.Form["password"]; SqlCommand selectUserAndPassword = new SqlCommand( "SELECT pwd FROM Users WHERE uname = @username", connection); selectUserAndPassword.Parameters.Add( new SqlParameter("@username", uname)); string validPassword = (string)selectUserAndPassword.ExecuteScalar(); if (validPassword == null) { // the user doesnt exist in the database errorMessage = "The username is invalid."; return false; } else if (validPassword != pword) { // the given password doesnt match errorMessage = "The password is incorrect."; return false; } else { // successBug #9 errorMessage = String.Empty; return true; } } 1 + 1 + 1 points – 1 point + bonus point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 24
  25. 25. bool login(SqlConnection connection, out string errorMessage) { Requête paramétrée, ça, cest juste! string uname = Request.Form["username"]; string pword = Request.Form["password"]; SqlCommand selectUserAndPassword = new SqlCommand( "SELECT pwd FROM Users WHERE uname = @username", connection); selectUserAndPassword.Parameters.Add( new SqlParameter("@username", uname)); string validPassword = (string)selectUserAndPassword.ExecuteScalar(); if (validPassword == null) { // the user doesnt exist in the database Rapatriement inutile du errorMessage = "The username is invalid."; return false; mot de passe! } else if (validPassword != pword) { Stockage du mot de passe en // the given password doesnt match clair. errorMessage = "The password is incorrect."; return false; } else Message derreur variable lorsque le { // success login ou le mdp est faux (fuite)Bug #9 errorMessage = String.Empty; return true; } } 1 + 1 + 1 points – 1 point + bonus point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 25
  26. 26. // SilverLight code module reviewbool verifyCode(string discountCode){ // We store the hash of the secret code instead of the plaintext of the secret code for security. // We hash the incoming value and compare it against the stored hash. byte[] codeHash = SecurityUtils.ComputeHash(discountCode, "MD5"); byte[] secretCode = new byte[] { 116, 46, 130, 122, 36, 234, 158, 125, 163, 122, 157, 186, 64, 142, 51, 153, 113, 79, 1, 42 }; // This should never happen, but we check it anyway if (codeHash.Length != secretCode.Length) return false; // perform an element-by-element comparison of the arrays for (int i = 0; i < codeHash.Length; i++) { if (codeHash[i] != secretCode[i]) return false; // the hashes dont match } // all the elements match, so the strings match // the discount code is valid, inform the server WebServiceSoapClient client = new WebServiceSoapClient(); client.ApplyDiscountCode(); return true; 1 + 1 + 2 points} 02.03.2012 Confoo Conference 2012 - Antonio Fontes 26
  27. 27. // SilverLight code module reviewbool verifyCode(string discountCode) Algorithme déconseillé (+1){ // We store the hash of the secret code instead of the plaintext of the secret code for security. // We hash the incoming value and compare it against the stored hash. byte[] codeHash = SecurityUtils.ComputeHash(discountCode, "MD5"); byte[] secretCode = new byte[] { 116, 46, 130, 122, 36, 234, 158, 125, 163, 122, 157, 186, 64, 142, 51, 153, 113, 79, 1, 42 }; // This should never happen, but we check it anyway A-t-on besoin dun if (codeHash.Length != secretCode.Length) sel? (seed) +1 return false; // perform an element-by-element comparison of the arrays for (int i = 0; i < codeHash.Length; i++) { if (codeHash[i] != secretCode[i]) Défense côté client  return false; // the hashes dont match } // all the elements match, so the strings match totalement inutile! (+2 points) // the discount code is accepted, inform the server WebServiceSoapClient client = new WebServiceSoapClient(); client.ApplyDiscountCode(); return true; 1 + 1 + 2 points} 02.03.2012 Confoo Conference 2012 - Antonio Fontes 27
  28. 28. • 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 28
  29. 29. Injection SQL (absence de validation)• 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 29
  30. 30. $MessageFile = "messages/messages.out"; if ($_GET["action"] == "NewMessage") { $name = $_GET["name"]; $message = $_GET["message"]; $handle = fopen($MessageFile, "a+"); fwrite($handle, "<b>$name</b> says $message<hr>n"); fclose($handle); echo "Message Saved!<p>n"; } else if ($_GET["action"] == "ViewMessages") { include($MessageFile); }• 1 + 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 30
  31. 31. $MessageFile = "messages/messages.out"; if ($_GET["action"] == "NewMessage") { $name = $_GET["name"]; $message = $_GET["message"]; $handle = fopen($MessageFile, "a+"); fwrite($handle, "<b>$name</b> says $message<hr>n"); Et sil y a du script fclose($handle); client? echo "Message Saved!<p>n"; } else if ($_GET["action"] == "ViewMessages") { include($MessageFile); include == eval() } ?• 1 + 1 point – Identification de linjection de code côté-serveur (via la fonction "include") – Identification de linjection de code côté-client (via laffichage du fichier) 02.03.2012 Confoo Conference 2012 - Antonio Fontes 31
  32. 32. // anti SQL-injection filter for user input string SQliProtect(string formValue) { string tmp = formValue.ToUpperCase(); return(tmp.Replace("SELECT", "").Replace("INSERT ", "").Replace("UPDATE", "").Replace("UNION","") .Replace("BENCHMARK, "").Replace("-- ", "").Replace("OR 1=1", "").Replace("DROP", "").Replace("@@version ", "").Replace("WAITFOR", "").Replace("OUTFILE", "") ... return(tmp) }• 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 32
  33. 33. // anti SQL-injection filter for user input string SQliProtect(string formValue) { string tmp = formValue.ToUpperCase(); return(tmp.Replace("SELECT", table" ? "DRDROPOP "").Replace("INSERT ", "").Replace("UPDATE", "").Replace("UNION","") .Replace("BENCHMARK, "").Replace("-- ", "").Replace("OR 1=1", "").Replace("DROP", "").Replace("@@version ", "").Replace("WAITFOR", "").Replace("OUTFILE", "") ... return(tmp) }• 1 point – Identification de la technique de contournement du filtre 02.03.2012 Confoo Conference 2012 - Antonio Fontes 33
  34. 34. <? $reqId = 0; if(isset($_GET[“account_id"])) $reqId = (int)(htmlentities($_GET[“account_id"])); if($reqId == 0) { // no account selected, show the list of authorized accounts $sql = " SELECT * FROM accounts a " ." INNER JOIN account_managers am " ." ON a.id = am.account_id " ." WHERE am.manager_id = ".$currentUserID; echo(RenderHTMLTable($sql)); } else { // docucment is clicked -> show statement $sql = " SELECT * FROM accounts a WHERE a.id = ".$reqId; RenderHTMLAccount($sql); }• 2 points 02.03.2012 Confoo Conference 2012 - Antonio Fontes 34
  35. 35. <? $reqId = 0; if(isset($_GET[“account_id"])) $reqId = (int)(htmlentities($_GET[“account_id"])); if($reqId == 0) Références internes? { // no account selected, show the list of authorized accounts $sql = " SELECT * FROM accounts a " Contrôle d’accès. Bien! ." INNER JOIN account_managers am " ." ON a.id = am.account_id " ." WHERE am.manager_id = ".$currentUserID; echo(RenderHTMLTable($sql)); } else { // docucment is clicked -> show statement $sql = " SELECT * FROM accounts a WHERE a.id = ".$reqId; RenderHTMLAccount($sql); } Mais ici?• 2 points – Identification de lexposition de références internes – Identification de labsence de contrôle daccès lors de laffichage du document 02.03.2012 Confoo Conference 2012 - Antonio Fontes 35
  36. 36. bool verifyPassword(string formPwd, int userId) { byte[] formHash = Tools.ComputeSHA1Hash(formPwd); byte[] dbHash = B64.Decode(User.GetPasswordHash(userId)); if (formHash.Length != dbHash.Length) return false; for (int i = 0; i < formHash.Length; i++) { if (formHash[i] != dbHash[i]) return false; // the hashes dont match } // we are still here, so the passwords matched return true; }• 1+1+1+1+1 points 02.03.2012 Confoo Conference 2012 - Antonio Fontes 36
  37. 37. bool verifyPassword(string formPwd, int userId) { Sel? byte[] formHash = Tools.ComputeSHA1Hash(formPwd); byte[] dbHash = B64.Decode(User.GetPasswordHash(userId));Rappatriement du modede passe? if (formHash.Length != dbHash.Length) Algorithme fort? return false; for (int i = 0; i < formHash.Length; i++) { if (formHash[i] != dbHash[i]) Longueurs variables return false; // the hashes dont match possibles? } // we are still here, so the passwords matched return true; } Stratégie Fail safe?– Absence probable de sel +1– Sinterroger sur la nature de lalgorithme choisi +1– Rapatriement inutile du mot de passe +1– Identification de labsence de mécanisme fail-safe +1– Présence de signes indiquant une méconnaissance des fonctions de hachage +102.03.2012 Confoo Conference 2012 - Antonio Fontes 37
  38. 38. Quel a été votre score? 20 points et plus: changez de carrière, ça embauche! De 13 à 19 points: Très bien! Vous vous y intéressez et ça se voit. Vous devriez songer à appliquer vos connaissances aussi au code de vos collègues si ce nest déjà fait, pensez aussi à joindre une association ou communauté traitant du sujet. Sensibilisez les gens autour de vous! De 8 à 12 points: Vous avez clairement identifié la notion de risque dans le code mais vous ne savez probablement pas encore où regarder. Il faut à présent consolider les bases simplement en…pratiquant! De 4 à 7 points: Demandez à vos chefs de vous faire suivre un cours!  Moins de 4 points: Si vous êtes développeur(ou développeuse), votre code est probablement dangereux pour la survie de lorganisation. Assurez-vous quil soit relu par une personne expérimentée dans lattente davoir un peu plus dexpérience!02.03.2012 Confoo Conference 2012 - Antonio Fontes 38
  39. 39. Common Weakness Enumeration database: http://cwe.mitre.org/data/index.html OWASP Secure Coding Checklist: https://www.owasp.org/index.php/OWASP_Secure_Coding_Practices_- _Quick_Reference_Guide OWASP ASVS: https://www.owasp.org/index.php/Category:OWASP_Application_Security_Ve rification_Standard_ProjectMerci de votre attention!Si vous souhaitez me contacter: – antonio.fontes@L7securite.ch ou @starbuck3000 – Newsletter: http://cddb.ch 02.03.2012 Confoo Conference 2012 - Antonio Fontes 39

×