Trouvez la faille! - Confoo 2012
-
Upload
antonio-fontes -
Category
Technology
-
view
1.182 -
download
4
description
Transcript of Trouvez la faille! - Confoo 2012
Trouvez la faille!Antonio Fontes / Confoo 2012 - Montréal
Notice 3: cette présentation contient des références au document "Test your Security IQ", par M. Howard et B. Sullivan
Notice 2: aucun chat n'a été maltraité durant la préparation de cette séance.
Notice 1: cette présentation contient des références à Common Weakness Enumeration: http://cwe.mitre.org/data/index.html
Notice 4: Un grand merci à Sébastien pour ses idées & propositions!
Confoo Conference 2012 - Antonio Fontes 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 3
Antonio FontesGenève (Suisse)Consultant indépendant Infosécurité logicielle:
Sécurité des applications webVisibilité et gestion du risque sur InternetFormation / accompagnement durant les projets de développement
Bulletin d'information"cybermenaces et sécurité Internet":http://cddb.ch
OWASP:Membre du Comité - OWASP SuisseLeader - OWASP Genève
A propos du conférencier…
02.03.2012
Confoo Conference 2012 - Antonio Fontes 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 point02.03.2012
Confoo Conference 2012 - Antonio Fontes 5
• 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 point02.03.2012
Envoi des éléments d'authentification en clair.
Confoo Conference 2012 - Antonio Fontes 6
• Idem.
02.03.2012
Confoo Conference 2012 - Antonio Fontes 7
• 1 point
02.03.2012
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;}
Confoo Conference 2012 - Antonio Fontes 8
• 1 point– Identification du risque pour la disponibilité du S.I.
02.03.2012
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;}
Le chargement du fichier a lieu avant le contrôle d'accès.
Confoo Conference 2012 - Antonio Fontes 9
• 1 point
02.03.2012
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(); }}
Confoo Conference 2012 - Antonio Fontes 10
• 1 point– Cas de type "XSS" (Cross-site scripting)
02.03.2012
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(); }}
Transfert du contenu vers le client, sans encodage approprié.
Appel vers la collection parente "Request"
Confoo Conference 2012 - Antonio Fontes 11
• 1 point
02.03.2012
$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 record\n");}
Confoo Conference 2012 - Antonio Fontes 12
• 1 point
02.03.2012
$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 record\n");}
Absence de contrôle d'intégrité du cookie.
Contournement du mécanisme d'authentification
Contournement du contrôle d'accès
Confoo Conference 2012 - Antonio Fontes 13
• 1 + 1 point
02.03.2012
Confoo Conference 2012 - Antonio Fontes 14
• 1 + 1 + 1 points– 1 point: identification du risque d'injection 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 tiers02.03.2012
Transfert de confiance à un tiers
Confoo Conference 2012 - Antonio Fontes 15
• 1 + 1 + 1 points– Faire attention aux recommandations sur le web: elles vont souvent à
l'encontre de la sécurité et visent à faciliter la collecte de données par des tiers.– Vérifier qui est l'auteur d'une recommandation de codage.02.03.2012
Confoo Conference 2012 - Antonio Fontes 16
• 1 point
02.03.2012
// 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); }}}
Confoo Conference 2012 - Antonio Fontes 17
• 1 point– Identification de la fuite d'adresses email
02.03.2012
// 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); }}}
Dans le cas json, l'adresse email n'est plus protégée contre les fuites.
Confoo Conference 2012 - Antonio Fontes 18
1 + 1 + 1 point
02.03.2012
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; }
Confoo Conference 2012 - Antonio Fontes 19
1 + 1 + 1 point– Exception générique: privilégier l'exception typée– Principe de conception "Fail-safe": le code n'échoue pas en haute sécurité– La classe Math.Random ne fournit pas d'entropie de niveau cryptographique02.03.2012
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; }
Fail-safe?
Exception générique?
Math.Random?
Confoo Conference 2012 - Antonio Fontes 20
• 1 point
02.03.2012
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));}
Confoo Conference 2012 - Antonio Fontes 21
• 1 point– Injection de type Xpath (il n'y a pas que des injections SQL!!)– Marche aussi sur: commandes système, LDAP, APIs ORM, etc.
02.03.2012
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));}
Validation?
Confoo Conference 2012 - Antonio Fontes 22
• 1 + 1 point
02.03.2012
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(); } } }
Confoo Conference 2012 - Antonio Fontes 23
• 1 + 1 point– Identification de l'identifiant de session prédictible– Collision des identifiants de session si le serveur est répliqué!
02.03.2012
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(); } } }
Multi-serveur?
ID de session prédictibles
Confoo Conference 2012 - Antonio Fontes 24
Bug #9
– 1 point + bonus point02.03.2012
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 doesn't exist in the database errorMessage = "The username is invalid."; return false; } else if (validPassword != pword) { // the given password doesn't match errorMessage = "The password is incorrect."; return false; } else { // success errorMessage = String.Empty; return true; } } 1 + 1 + 1 points
Confoo Conference 2012 - Antonio Fontes 25
Bug #9
– 1 point + bonus point02.03.2012
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 doesn't exist in the database errorMessage = "The username is invalid."; return false; } else if (validPassword != pword) { // the given password doesn't match errorMessage = "The password is incorrect."; return false; } else { // success errorMessage = String.Empty; return true; } }
Requête paramétrée, ça, c'est juste!
Rapatriement inutile du mot de passe!
Stockage du mot de passe en clair.
1 + 1 + 1 points
Message d'erreur variable lorsque le login ou le mdp est faux (fuite)
Confoo Conference 2012 - Antonio Fontes 2602.03.2012
// 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 don't 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
Confoo Conference 2012 - Antonio Fontes 2702.03.2012
// 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 don't match } // all the elements match, so the strings match
// the discount code is accepted, inform the server WebServiceSoapClient client = new WebServiceSoapClient(); client.ApplyDiscountCode(); return true;}
Algorithme déconseillé (+1)
A-t-on besoin d'un sel? (seed) +1
Défense côté client totalement inutile! (+2 points)
1 + 1 + 2 points
Confoo Conference 2012 - Antonio Fontes 28
• 1 point
02.03.2012
Confoo Conference 2012 - Antonio Fontes 29
• 1 point
02.03.2012
Injection SQL (absence de validation)
Confoo Conference 2012 - Antonio Fontes 30
• 1 + 1 point
02.03.2012
$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);}
Confoo Conference 2012 - Antonio Fontes 31
• 1 + 1 point– Identification de l'injection de code côté-serveur (via la fonction "include")– Identification de l'injection de code côté-client (via l'affichage du fichier)
02.03.2012
$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);}
include == eval() ?
Et s'il y a du script client?
Confoo Conference 2012 - Antonio Fontes 32
• 1 point
02.03.2012
// anti SQL-injection filter for user inputstring 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)}
Confoo Conference 2012 - Antonio Fontes 33
• 1 point– Identification de la technique de contournement du filtre
02.03.2012
// anti SQL-injection filter for user inputstring 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)}
"DRDROPOP table" ?
Confoo Conference 2012 - Antonio Fontes 34
• 2 points
02.03.2012
<?$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);}
Confoo Conference 2012 - Antonio Fontes 35
• 2 points– Identification de l'exposition de références internes– Identification de l'absence de contrôle d'accès lors de l'affichage du document
02.03.2012
<?$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);}
Références internes?
Contrôle d’accès. Bien!
Mais ici?
Confoo Conference 2012 - Antonio Fontes 36
• 1+1+1+1+1 points
02.03.2012
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 don't match } // we are still here, so the passwords matched return true;}
37
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 don't match } // we are still here, so the passwords matched return true;}
– Absence probable de sel +1– S'interroger sur la nature de l'algorithme choisi +1– Rapatriement inutile du mot de passe +1– Identification de l'absence 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
Longueurs variables possibles?
Rappatriement du mode de passe?
Stratégie Fail safe?
Algorithme fort?
Sel?
Confoo Conference 2012 - Antonio Fontes 3802.03.2012
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 n'est 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 l'organisation. Assurez-vous qu'il soit relu par une personne expérimentée dans l'attente d'avoir un peu plus d'expérience!
Confoo Conference 2012 - Antonio Fontes 39
Merci de votre attention! Si vous souhaitez me contacter:
– [email protected] ou @starbuck3000– Newsletter: http://cddb.ch 02.03.2012
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_Verification_Standard_Project