Je souhaite obtenir des informations sur une formation complète concernant le thème DIVI dispensé
par un organisme de formation certifié par l’état.
Que la formation soit finançable par mon CPF (idéalement) ou autre


Dans les scénarios d’infection de masse, notre équipe de recherche sur les logiciels malveillants recherche souvent des vecteurs d’attaque pour trouver des modèles et d’autres similitudes entre les sites Web compromis. L’identification de ces modèles nous permet de fournir des solutions meilleures et plus rapides à nos clients, minimisant ainsi les effets d’attaques massives.

Récemment, lors d’une enquête de routine, nous avons trouvé un certain nombre de failles de sécurité dans 123Formulaire de contact pour WordPress Version du plugin WordPress <= 1.5.6. Ces vulnérabilités critiques permettent aux attaquants de publier arbitrairement et de livrer des fichiers malveillants sur le site Web sans aucune authentification.

Avec plus de 3000 installations, le plugin 123contactform-for-WordPress a été développé pour aider les propriétaires de sites Web à ajouter des formulaires Web, des sondages, des quiz ou des sondages à partir d’un compte 123FormBuilder à leur site Web ou blog WordPress.

Contournement de la validation via la vérification du plugin

Commençons par analyser le processus de révision des plugins. Dans le script 123contactform-for-wordpress.php, le plugin enregistre le cfp-connect Action pour charger le cfp_connect () Fonction via l’API AJAX:

109 add_action( 'wp_ajax_cfp-connect', 'cfp_connect' );
110 add_action( 'wp_ajax_nopriv_cfp-connect', 'cfp_connect' );
111 function cfp_connect() {
112         $cfp_pub_key = $_POST["pk"];
113         $message = $_POST["message"];
114         $signature = base64_decode(str_replace(" ", "+", $_POST["signature"]));
115         if(!isset($cfp_pub_key) || $cfp_pub_key=="") { echo cfp_message("Key is not sent",0);exit(); } // Key is not sent
116         $verify = openssl_verify($message, $signature, base64_decode($cfp_pub_key), OPENSSL_ALGO_SHA1);
117         if ($verify == 1) {
118             if(!get_option("123cf_post_public_key")) {
119                 add_option("123cf_post_public_key",$cfp_pub_key);
120             } else {
121                 update_option("123cf_post_public_key",$cfp_pub_key);
122             }
123             echo cfp_message("WordPress connected",1);exit();
124         } elseif ($verify == 0) {
125             echo cfp_message("Signature not verified",0);exit();
126         } else {
127         echo cfp_message("error: " . openssl_error_string(), 0);
128         exit();
129 }
130     exit();
131 }

Cette fonction effectue une vérification de signature avec le openssl_verify ()puis vérifie les résultats: 1 si la signature est correcte ou 0 si elle est incorrecte.

Si la vérification réussit, le script vérifie si le Nom de l’option 123cf_post_public_key existe dans la base de données. À partir de là, cela ajoute la valeur de $ cfp_pub_key Variable dans le Valeur d’option Champ ou met à jour sa valeur lorsque le Nom de l’option n’existe pas.

Au début, il n’y a rien de mal avec cette procédure, mais puisque tous les champs sont du openssl_verify () sont reçus sur $ _POST Les attaquants peuvent simplement produire ces valeurs ($ message, $ signature, $ cf_pub_key) contourner les mécanismes de validation et injecter leurs propres Clé publique dans la base de données.

Toute création de poste

Quelques lignes plus loin, une autre action cfp-new-post est enregistré pour charger le cfp_new_post () Fonction via l’API AJAX:

167 add_action( 'wp_ajax_cfp-new-post', 'cfp_new_post' );
168 add_action( 'wp_ajax_nopriv_cfp-new-post', 'cfp_new_post' );
169 function cfp_new_post() {
170     if(!cfp_authenticate()) { echo cfp_message("There was an error while trying to authenticate with wordpress",0); exit(); }
...

Avant de décrire le problème, il y a un appel intéressant à la fonction cfp_authenticate () à la ligne 170, qui tente à nouveau de valider la signature via openssl_verify ().

Toutes les variables sont reçues via des requêtes $ _POST, nous avons donc toujours le contrôle sur le flux d’exécution et le résultat de la variable $ verify.

287 function cfp_authenticate() {
288         if(!get_option( "123cf_post_public_key")) { return false; }
289         $cfp_pub_key = get_option( "123cf_post_public_key");
290         $message = $_POST["message"];
291         $signature = base64_decode(str_replace(" ", "+", $_POST["signature"]));
292         $verify = openssl_verify($message, $signature, base64_decode($cfp_pub_key), OPENSSL_ALGO_SHA1);
293         return $verify;
294 }

Continuez vers le cfp_new_post () Fonction, le code attribue tous les champs liés aux entrées de publication pour WordPress à différentes variables dans le $ new_post Array, y compris post_author, post_title, et Publier un contenu, Parmi d’autres.

Ces variables sont ensuite insérées dans la zone de publication à l’aide de la fonction WordPress wp_insert_post ().

171     $post_title = strip_tags(rawurldecode($_POST["post_title"]));
172     $post_title = preg_replace("/&nbsp;/",' ',$post_title);
173     $post_title = stripslashes($post_title);
174     $post_content = rawurldecode($_POST["post_content"]);
175     $post_content = stripslashes($post_content);
176     $post_status = $_POST["post_status"];
177     $post_category = urldecode($_POST["post_category"]);
178     $post_author = $_POST["post_author"];
179     $post_format = $_POST["post_format"];
180     $comments = $_POST["comment_status"];
181     $comments == "1" ? $comment_status = "open" : $comment_status = "closed";
182     $post_excerpt = rawurldecode($_POST["post_excerpt"]);
183     $post_excerpt = preg_replace("/&nbsp;/",' ',$post_excerpt);
184     $post_excerpt = stripslashes($post_excerpt);
185     $post_tags = explode(",",rawurldecode($_POST["post_tags"]));
186     $post_image = str_replace(" ", "+",$_POST["post_image"]);
187     $post_image_name = $_POST["post_image_name"];205     $new_post = array(
206     'post_author'    => $post_author,
207     'post_title'     => $post_title,
208     'post_content'   => $post_content,
209     'post_status'    => $post_status,
210     'comment_status' => $comment_status,
211     'post_excerpt'   => $post_excerpt,
212     'post_category'  => $cat_id_arr
213     );
214     $post_id = wp_insert_post( $new_post );
215     if($post_id) {
216        foreach($custom_fields_values as $meta_key=>$meta_value) {
217           add_post_meta($post_id,str_replace("|***|"," ",$meta_key), $meta_value);
218        }
219          set_post_format($post_id, $post_format);
220          wp_set_post_tags($post_id, $post_tags);
221          if(isset($post_image)) {
222               cfp_upload_image($post_id,$post_image,$post_image_name);
223         }
224         echo cfp_message("New post created",1); exit();
225     }
226     echo cfp_message("There was an error while trying to create new post",0); exit();
227 }

Tout téléchargement de fichier

Quand vous avez regardé la fonctionnalité cfp_upload_image () à la ligne 222 et j’y ai réfléchi « Il peut y avoir quelque chose ici » Vous avez raison!

Cette fonction est responsable du téléchargement des « images » sur le serveur. En essayant de définir le nom de fichier ($ Nom de fichier) à image.png (Ligne 137) Les attaquants peuvent insérer n’importe quelle valeur dans la variable $ nom_image_post (Ligne 187) et nommez le fichier comme vous le souhaitez. Vous pouvez également utiliser les variables pour modifier le contenu inséré dans le fichier $ post_image (Ligne 186)

Pour réduire le risque de troncature du nom de fichier, les développeurs ont ajouté un moyen intéressant d’enregistrer le fichier sur le serveur en utilisant md5 () pour calculer le hachage du $ Nom de fichier et Micro heure (), qui renvoie l’horodatage Unix actuel en microsecondes.

132 function cfp_upload_image($post_id,$post_image, $post_image_name = null) {
133                  $upload_dir=wp_upload_dir();
134                  $upload_path=str_replace( '/', DIRECTORY_SEPARATOR, $upload_dir['path'] ) . DIRECTORY_SEPARATOR;
135                  $decoded_img=base64_decode($post_image);
136                  if(!$post_image_name) { $filename='image.png'; } else { $filename=$post_image_name; };
137                  $hashed_filename=md5( $filename . microtime() ) . '_' . $filename;
138                  $image_upload=file_put_contents( $upload_path . $hashed_filename, $decoded_img );
...
145                  $file             = array();
146                  $file['error']    = '';
147                  $file['tmp_name'] = $upload_path . $hashed_filename;
148                  $file['name']     = $hashed_filename;
149                  $file['type']     = 'image/jpg';
150                  $file['size']     = filesize( $upload_path . $hashed_filename );
151                  $file_return = wp_handle_sideload( $file, array( 'test_form' => false ) );
152                  $file_url = $file_return["file"];
153                 $filetype = wp_check_filetype( basename( $file_url ), null );
154                 $attachment = array(
155                         'guid'           =>  $upload_dir['url'] . '/' . basename( $file_url ),
156                         'post_mime_type' => $filetype['type'],
157                         'post_title'     => preg_replace( '/.[^.]+$/', '', basename( $file_url ) ),
158                         'post_content'   => '',
159                         'post_status'    => 'inherit'
160                 );
161                 $attach_id = wp_insert_attachment( $attachment, $file_url, $post_id );
162                 require_once( ABSPATH . 'wp-admin/includes/image.php' );
163                 $attach_data = wp_generate_attachment_metadata( $attach_id, $file_url );
164                 wp_update_attachment_metadata( $attach_id, $attach_data );
165                 update_post_meta( $post_id, '_thumbnail_id', $attach_id );
166 }

Le nom du fichier est très difficile à deviner à cause de cette implémentation. Cependant, si l’option de liste de répertoires n’est pas définie correctement sur le serveur Web, les attaquants peuvent facilement rechercher le répertoire de téléchargement pour obtenir le nom du fichier et exécuter son contenu.

Liste de répertoires définie de manière incorrecte

Rechercher dans le répertoire de téléchargement

Bonus Sidetrack (deviner le nom de fichier de la porte arrière)

Lorsque les attaquants sont déterminés ce qu’ils sont normalement, ils peuvent faire une supposition sauvage et faire plusieurs millions de requêtes pour obtenir le résultat souhaité. Pour comprendre comment cela fonctionne, il faut plonger un peu plus dans le monde Micro heure () Une fonction.

Tel que défini dans Saisie manuelle PHP, Par défaut, Micro heure () renvoie une chaîne de caractères sous la forme « ms sec« , Où seconde est le nombre de secondes depuis l’époque Unix (0:00:00 janvier 1.1970 GMT) et Mme mesure les microsecondes écoulées en secondes – également exprimées en secondes.

En pratique, c’est le résultat de la fonction effectuée:

$ php -r "echo microtime();"
0.01101800 1588386363

« 0,01101800 » représente les microsecondes et la deuxième pièce « 1588386363 » l’ère Unix:

$ date --date @1588386363
Fri May  1 23:26:03 -03 2020

Parce que les attaquants peuvent contrôler le $ filename et le récupérer Micro heure () A partir de la requête au moment de l’injection, seules les microsecondes peuvent être devinées. Lors de nos tests, nous avons estimé une moyenne de 2 à 4 millions de requêtes pour que toutes les variables soient correctes.

Conclusion

Cette analyse montre clairement comment les attaquants peuvent utiliser des vulnérabilités logicielles dans 123contactform-for-wordpress pour créer des publications aléatoires et transférer des fichiers malveillants sur le site Web sans authentification.

Malheureusement, toutes ces vulnérabilités peuvent être corrigées et sont le résultat direct d’un manque de vérification des compétences, d’une vérification incorrecte des utilisateurs et de problèmes d’autorisation dans l’écosystème WordPress. Il existe de nombreuses méthodes et mécanismes disponibles pour empêcher ces injections arbitraires, et les développeurs de plugins doivent les utiliser pour s’assurer que les utilisateurs sont protégés contre les vulnérabilités logicielles connues.

Dans ce cas particulier, les propriétaires de plugins n’ont pas publié de correctif pour corriger ces vulnérabilités. Au lieu de cela, ils ont supprimé le plugin du référentiel de plugins WordPress. Pour minimiser les risques et protéger votre environnement, nous recommandons vivement aux propriétaires de sites Web de désinstaller le plugin et de trouver une solution alternative à partir d’une source fiable.



Source link

Recent Posts