trouvez la faille! - confoo 2012

39
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!

Upload: antonio-fontes

Post on 15-Jan-2015

1.184 views

Category:

Technology


4 download

DESCRIPTION

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.

TRANSCRIPT

Page 1: 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!

Page 2: Trouvez la faille! - Confoo 2012

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

Page 3: Trouvez la faille! - Confoo 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

Page 4: Trouvez la faille! - Confoo 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

Page 5: Trouvez la faille! - Confoo 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.

Page 6: Trouvez la faille! - Confoo 2012

Confoo Conference 2012 - Antonio Fontes 6

• Idem.

02.03.2012

Page 7: Trouvez la faille! - Confoo 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;}

Page 8: Trouvez la faille! - Confoo 2012

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.

Page 9: Trouvez la faille! - Confoo 2012

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(); }}

Page 10: Trouvez la faille! - Confoo 2012

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"

Page 11: Trouvez la faille! - Confoo 2012

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");}

Page 12: Trouvez la faille! - Confoo 2012

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

Page 13: Trouvez la faille! - Confoo 2012

Confoo Conference 2012 - Antonio Fontes 13

• 1 + 1 point

02.03.2012

Page 14: Trouvez la faille! - Confoo 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

Page 15: Trouvez la faille! - Confoo 2012

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

Page 16: Trouvez la faille! - Confoo 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); }}}

Page 17: Trouvez la faille! - Confoo 2012

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.

Page 18: Trouvez la faille! - Confoo 2012

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; }

Page 19: Trouvez la faille! - Confoo 2012

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?

Page 20: Trouvez la faille! - Confoo 2012

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));}

Page 21: Trouvez la faille! - Confoo 2012

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?

Page 22: Trouvez la faille! - Confoo 2012

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(); } } }

Page 23: Trouvez la faille! - Confoo 2012

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

Page 24: Trouvez la faille! - Confoo 2012

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

Page 25: Trouvez la faille! - Confoo 2012

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)

Page 26: Trouvez la faille! - Confoo 2012

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

Page 27: Trouvez la faille! - Confoo 2012

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

Page 28: Trouvez la faille! - Confoo 2012

Confoo Conference 2012 - Antonio Fontes 28

• 1 point

02.03.2012

Page 29: Trouvez la faille! - Confoo 2012

Confoo Conference 2012 - Antonio Fontes 29

• 1 point

02.03.2012

Injection SQL (absence de validation)

Page 30: Trouvez la faille! - Confoo 2012

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);}

Page 31: Trouvez la faille! - Confoo 2012

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?

Page 32: Trouvez la faille! - Confoo 2012

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)}

Page 33: Trouvez la faille! - Confoo 2012

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" ?

Page 34: Trouvez la faille! - Confoo 2012

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);}

Page 35: Trouvez la faille! - Confoo 2012

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?

Page 36: Trouvez la faille! - Confoo 2012

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;}

Page 37: Trouvez la faille! - Confoo 2012

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?

Page 38: Trouvez la faille! - Confoo 2012

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!

Page 39: Trouvez la faille! - Confoo 2012

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