0

Scanner de ports en Python (Phase2)

 

 

Comme un scan de port n’est rien sans un petit banner grabbing. Je vous propose de compléter le script utilisé dans le premier article. Afin d’extraire les versions des différents services utilises par les ports ouverts découverts durant le scan de Metasploitable.

La partie intéressante destinée a la récupérations des différentes bannières se situe a la fin du code, juste après:

[pastacode lang= »python » manual= »if%20__name__%20%3D%3D%20’__main__’%3A%0A%20%20%20%20main() » message= » » highlight= » » provider= »manual »/]

 

Je remets pour informations le code au complet avec son extension.

[pastacode lang= »python » manual= »%23%20coding%3A%20utf-8%0A%0Aimport%20socket%0Afrom%20multiprocessing%20import%20Pool%0Afrom%20datetime%20import%20datetime%0A%0A%0Aopen_p%2C%20closed_p%20%3D%20%5B%5D%2C%20%5B%5D%0Areponse%20%3D%20%5B%22OPEN%20PORTS%22%2C%22CLOSED%20PORTS%22%2C%22SCANNING%20PORTS%22%2C%22TYPE%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%22SCANNING%22%2C%22ADDRESS%22%2C%22NUMBER%20OF%20PROCESSES%22%2C%22RANGE%20MIN%22%2C%22RANGE%20MAX%22%2C%22HOST%22%2C%22HAS%20IP%22%2C%22UNREACHABLE%22%5D%0A%0A%0A%0Atarget_ip%20%3D%20raw_input(%22%7B%7D%3A%20%22.format(reponse%5B5%5D))%0Atry%3A%0A%20%20%20%20ip%20%3D%20socket.gethostbyname(target_ip)%0A%20%20%20%20host%20%3D%20socket.gethostbyaddr(target_ip)%0A%20%20%20%20print%20%22%22%0A%20%20%20%20print%20%22%7B0%7D%20%7B1%7D%3A%20%7B2%7D%22.format(reponse%5B5%5D%2C%20reponse%5B10%5D%2C%20ip)%0A%20%20%20%20print%20%22%7B0%7D%20%3A%7B1%7D%22.format(reponse%5B9%5D%2C%20host%5B0%5D)%0A%20%20%20%20print%20%22%22%0Aexcept%3A%0A%20%20%20%20print%20%22%7B0%7D%20%7B1%7D%22.format(reponse%5B5%5D%2C%20reponse%5B11%5D)%0A%0Anum_procs%20%3D%20int(raw_input(%22%7B0%7D%3A%20%22.format(reponse%5B6%5D)))%0Aminp%20%3D%20int(raw_input(%22%7B0%7D%3A%20%22.format(reponse%5B7%5D)))%0Amaxp%20%3D%20int(raw_input(%22%7B0%7D%3A%20%22.format(reponse%5B8%5D)))%0Aprint%20%22%7B0%7D%3A%7B1%7D%5Cn%22.format(reponse%5B4%5D%2Cip)%0A%0A%0AstartTime%20%3D%20datetime.now()%0Aprint%20%22DEPART%20COMPTEUR%3A%20%22%2C%20startTime%0A%0A%0A%0Adef%20scan(arg)%3A%0A%20%20%20%20target_ip%2C%20port%20%3D%20arg%0A%20%20%20%20sock%20%3D%20socket.socket(socket.AF_INET%2C%20socket.SOCK_STREAM)%0A%20%20%20%20sock.settimeout(.17)%0A%20%20%20%20result%20%3D%20sock.connect_ex((target_ip%2Cport))%0A%20%20%20%20if%20result%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20sock.shutdown(2)%0A%20%20%20%20%20%20%20%20return%20port%2C%20True%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20return%20port%2C%20False%0A%20%20%20%20%20%20%20%20sock.close%0A%0A%0Adef%20main()%3A%0A%20%20%20%20ports%20%3D%20range(minp%2C%20maxp%2B1)%0A%20%20%20%20pool%20%3D%20Pool(processes%3Dnum_procs)%0A%20%20%20%20print%20%22%7B0%7D%3A%20%22.format(reponse%5B2%5D)%0A%20%20%20%20for%20port%2C%20status%20in%20pool.imap_unordered(scan%2C%20%5B(target_ip%2C%20port)for%20port%20in%20ports%5D)%3A%0A%20%20%20%20%20%20%20%20print%20port%2C%0A%20%20%20%20%20%20%20%20open_p.append(port)if%20status%20else%20closed_p.append(port)%0A%0A%0A%0Aif%20__name__%20%3D%3D%20’__main__’%3A%0A%20%20%20%20main()%0A%0A%0Aprint%20%22%5Cn%5CnOPEN%20PORTS%3A%20%22%2C%20open_p%2C%22%5Cn%22%0Aprint%20%22BANNER%20GRABING…%5Cn%22%0A%0Afor%20elements%20in%20open_p%3A%0A%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20print%20%22%7B0%7D%20%7B1%7D%20%3A%7B2%7D%22.format(elements%2Creponse%5B3%5D%2Csocket.getservbyport(elements))%0A%20%20%20%20except%3A%0A%20%20%20%20%20%20%20%20continue%0A%20%20%20%20addr%20%3D%20(target_ip%2Celements)%0A%20%20%20%20sock%20%3D%20socket.socket()%0A%20%20%20%20sock.settimeout(.17)%0A%20%20%20%20sock.connect_ex(addr)%0A%20%20%20%20sock.send%20(%22GET%20%2F%20HTTP%2F1.0%5Cr%5CnHost%3A%20%22%20%2B%20target_ip%20%2B%20%22%5Cr%5Cn%5Cr%5Cn%22)%0A%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20print%20sock.recv(1024)%2C%20%22%5Cn%22%0A%20%20%20%20%20%20%20%20sock.close()%0A%20%20%20%20except%3A%0A%20%20%20%20%20%20%20%20print%20%22NON3%5Cn%22%0A%20%20%20%20%20%20%20%20sock.close%0A%0Aprint%20%22%5Cn%5Cn%5CnTOTAL%20TIME%3A%20%22%2Cdatetime.now()%20-%20startTime » message= » » highlight= » » provider= »manual »/]

 

Un bref aperçu d’un scan de Metasploitable  affichera comme prévu la liste des ports ouverts, mais aussi leur versions des services. Voici donc une copie d’écran des informations intéressantes extraites par ce script.

 

Vous pourrez maintenant utiliser ce script ainsi  que les informations retournées par celui-ci dans vos propres CTF ou test de vulnérabilité afin de vérifier quels sont les ports d’ouverts et quelles sont les version des services. Mais aussi si les versions des différents services sont vulnérables. Grâce aux différentes ressources disponible sur internet que je vous ai énumérées ici.

Je vous invite a explorer et disséquer ce script afin de comprendre son fonctionnement. Je vous invite aussi a le modifier car il n’est évidement pas parfait (il est loin de l’être). Mais il serait sympa de mentionner sa source en ajoutant un commentaire avec le lien de cet article ainsi que le nom de mon blog si vous comptez l’utiliser.

 

N’oubliez pas que l’utilisation des différents outils et techniques pourrait dans certaines circonstances vous mettre hors la loi. Veuillez respecter la législation en vigueur en France en suivant les consignes que j’ai publie dans l’article intitulé « Rappel des textes en vigueur concernant les atteintes aux systèmes de traitement automatisé de données« 

0

Web Scraping avec Python

Dans cet article je vais vous faire une petite démonstration d’extraction d’informations pour un CTF ou un test d’intrusion. Grâce a python2 ainsi que les modules Beautifulsoup et urllib2 , vous allez extraire d’un code source différentes informations comme (liste non exhaustive):

  1. Les métatags
  2. Les lien
  3. Les scripts

Pour l’exemple je vous propose d’utiliser Mutillidae en passant par Metasploitable ou cet environnement d’apprentissage est installé et accessible directement. Pour ma part j’ai installé Metasploitable sur une machine virtuelle VirtualBox, mais vous pouvez également y accéder en passant par les CTF all the day chez Root-Me.

 

Voici donc le script.  L’URL devra être remplacée par celle que vous fournira soit Root-me ou Mutillidaé sur VirtualBox.

  • Dans le cas de VirtualBox ce sera une URL de type : http://192.168.1.17/mutillidae/
  • Dans le cas de CTF all the day ce sera une url de type: ctfXY.root-me.org

 

import urllib2
from bs4 import BeautifulSoup
url = "http://192.168.1.17/mutillidae/"
page = urllib2.urlopen(url)
soup = BeautifulSoup(page) #page entiere

 

Vous pouvez des a présent extraire différentes informations grâce a l’intégration des différentes balises HTML   et par exemple vérifier s’il y a des balises « meta » et quel est leur contenu.

meta = soup.find_all('meta')
for elements in meta:
    print (elements)
    print ("\n\n")

Effectivement il y a un retour.

<meta content="text/html; charset=utf-8" http-equiv="content-type"/>

 

La présence des différents liens pourrait surement aussi vous intéresser en intégrant la balises « a » et l’attribut « href« .

ancre = soup.find_all("a")
for elements in ancre:
    print elements.get("href")

Cette partie du script retournes effectivement une belle liste.

index.php?page=home.php
./index.php?page=login.php
./index.php?do=toggle-hints&page=home.php
./index.php?do=toggle-security&page=home.php
set-up-database.php
./index.php?page=show-log.php
./index.php?page=captured-data.php
#
index.php?page=home.php
./index.php?page=login.php
./index.php?do=toggle-security&page=home.php
set-up-database.php
./index.php?page=show-log.php
./index.php?page=credits.php
#
http://www.owasp.org/index.php/Top_10_2010-A1

./index.php?page=user-info.php

./index.php?page=login.php

./index.php?page=register.php

./index.php?page=login.php
./index.php?page=user-info.php

./index.php?page=view-someones-blog.php
./index.php?page=user-info.php

?page=add-to-your-blog.php

./index.php?page=site-footer-xss-discussion.php


index.php?page=html5-storage.php

index.php?page=capture-data.php

./index.php?page=dns-lookup.php

./index.php
./index.php?page=password-generator.php&username=anonymous

./index.php?page=user-poll.php

./index.php?page=set-background-color.php

./index.php?page=pen-test-tool-lookup.php
http://www.owasp.org/index.php/Top_10_2010-A2

./index.php?page=dns-lookup.php
./index.php?page=pen-test-tool-lookup.php
./index.php?page=text-file-viewer.php
./index.php?page=user-info.php
./index.php?page=set-background-color.php
./index.php?page=html5-storage.php
./index.php?page=capture-data.php

?page=add-to-your-blog.php
?page=view-someones-blog.php
?page=show-log.php

index.php?page=html5-storage.php

?page=add-to-your-blog.php
?page=view-someones-blog.php
?page=show-log.php
?page=text-file-viewer.php
./index.php?page=dns-lookup.php
?page=user-info.php
./index.php
./index.php?page=set-background-color.php
./index.php?page=pen-test-tool-lookup.php

./index.php?page=browser-info.php
./index.php?page=show-log.php
./index.php?page=site-footer-xss-discussion.php
./index.php?page=html5-storage.php

./index.php

index.php?page=html5-storage.php

./index.php?page=pen-test-tool-lookup.php

index.php?page=capture-data.php
http://www.owasp.org/index.php/Top_10_2010-A3
index.php
?page=login.php
http://www.owasp.org/index.php/Top_10_2010-A4
index.php?page=text-file-viewer.php
index.php?page=source-viewer.php
index.php?page=credits.php
index.php
index.php?page=arbitrary-file-inclusion.php
http://www.owasp.org/index.php/Top_10_2010-A5
index.php?page=add-to-your-blog.php
./index.php?page=register.php
http://www.owasp.org/index.php/Top_10_2010-A6
index.php?page=home.php
./index.php?page=user-info.php
http://www.owasp.org/index.php/Top_10_2010-A7
index.php?page=user-info.php
index.php?page=html5-storage.php
http://www.owasp.org/index.php/Top_10_2010-A8
index.php?page=secret-administrative-pages.php
http://en.wikipedia.org/wiki/Robots_exclusion_standard
http://www.owasp.org/index.php/Top_10_2010-A9
?page=login.php
?page=user-info.php
http://www.owasp.org/index.php/Top_10_2010-A10
?page=credits.php
#
#
http://www.owasp.org/index.php/Top_10_2007-A3
?page=text-file-viewer.php
?page=source-viewer.php
http://www.owasp.org/index.php/Top_10_2007-A6
index.php
index.php
index.php
index.php?page=framing.php
framer.html
index.php?page=html5-storage.php

?page=text-file-viewer.php
?page=show-log.php

index.php?page=login.php
index.php?page=add-to-your-blog.php
index.php?page=html5-storage.php

index.php?page=capture-data.php
index.php?page=captured-data.php
#
index.php?page=change-log.htm
index.php?page=installation.php
/mutillidae/documentation/mutillidae-installation-on-xampp-win7.pdf
index.php?page=documentation/vulnerabilities.php
index.php?page=documentation/how-to-access-Mutillidae-over-Virtual-Box-network.php
#

https://www.owasp.org/index.php/Top_Ten
http://samurai.inguardians.com/
https://addons.mozilla.org/en-US/firefox/collections/jdruin/pro-web-developer-qa-pack/
http://www.irongeek.com/i.php?page=security/mutillidae-deliberately-vulnerable-php-owasp-top-10
http://www.hackersforcharity.org/ghdb/
https://www.owasp.org
https://addons.mozilla.org/en-US/firefox/collections/jdruin/pro-web-developer-qa-pack/

http://www.youtube.com/user/webpwnized
http://www.irongeek.com
http://www.irongeek.com/i.php?page=security/mutillidae-deliberately-vulnerable-php-owasp-top-10
./index.php?page=installation.php
./index.php?page=usage-instructions.php
./index.php?page=php-errors.php
./index.php?page=change-log.htm
./index.php?page=notes.php
http://www.backtrack-linux.org/
http://samurai.inguardians.com/
http://www.eclipse.org/pdt/
http://www.php.net/
http://www.quest.com/toad-for-mysql/
http://www.hackersforcharity.org/
http://www.irongeek.com/i.php?page=security/mutillidae-deliberately-vulnerable-php-owasp-top-10
http://irongeek.com
>>> 

 

Il pourrait être intéressant de jeter un coup d’œil aux différents scripts présents.

scripts = soup.find_all('script')
for script in scripts:
    print (script)
    print ("\n")

Et les voici.

<script src="./javascript/bookmark-site.js" type="text/javascript"></script>


<script src="./javascript/ddsmoothmenu/ddsmoothmenu.js" type="text/javascript"></script>


<script src="./javascript/ddsmoothmenu/jquery.min.js" type="text/javascript">
    /***********************************************
    * Smooth Navigational Menu- (c) Dynamic Drive DHTML code library (www.dynamicdrive.com)
    * This notice MUST stay intact for legal use
    * Visit Dynamic Drive at http://www.dynamicdrive.com/ for full source code
    ***********************************************/
  </script>


<script type="text/javascript">
    ddsmoothmenu.init({
      mainmenuid: "smoothmenu1", //menu DIV id
      orientation: 'v', //Horizontal or vertical menu: Set to "h" or "v"
      classname: 'ddsmoothmenu', //class added to menu's outer DIV
      //customtheme: ["#cccc44", "#cccccc"],
      contentsource: "markup" //"markup" or ["container_id", "path_to_menu_file"]
    });
  </script>


<script type="text/javascript">
    var onLoadOfBody = function(theBody){
      var l_securityLevel = '0';
            var l_hintsStatus = 'Disabled (0 - I try harder)';
      
      switch(l_securityLevel){
        case "0": var l_securityLevelDescription = 'Hosed'; break;
        case "1": var l_securityLevelDescription = 'Arrogent'; break;
        case "2": var l_securityLevelDescription = '2'; break;
        case "3": var l_securityLevelDescription = '3'; break;
        case "4": var l_securityLevelDescription = '4'; break;
        case "5": var l_securityLevelDescription = 'Secure'; break; 
      }// end switch
      //document.getElementById("idSystemInformationHeading").innerHTML = l_loginMessage;
      document.getElementById("idHintsStatusHeading").innerHTML = 'Hints: ' + l_hintsStatus;
      document.getElementById("idSecurityLevelHeading").innerHTML = 'Security Level: ' + l_securityLevel + ' (' + l_securityLevelDescription + ')';
    }// end function onLoadOfBody()
  </script>

 

Voici la fin de ce mini tutoriel traitant de l’utilisation de Python, Beautifulsoup et urllib  pour extraire des données d’un un code-source. Il est évident que ce n’était qu’une infime partie de ce qu’il est possible de faire. Je vous invite a explorer, tester et bien sur modifier le script  a votre guise grâce aux liens qui sont fournis dans cet article.

 

 

N’oubliez pas que l’utilisation de ce genre de techniques est limité a une utilisation dans un cadre légal. Pour rappel je vous invite a lire cet article: Rappel des textes en vigueur concernant les “atteintes aux systèmes de traitement automatisé de données”

 

 

0

Scanner de ports en Python

Un petit scanner de ports que je me suis amusé a scripter il y a un bout de temps . Ce scanner utilise les modules socket qui sont des objets permettant d’ouvrir une connexion avec une machine locale ou distante et d’échanger avec elle, et multiprocessing qui permet de créer plusieurs processus séparés, et de les orchestrer pour qu’ils travaillent ensemble. Ce script est simple d’utilisation mais pourra bien sur être amélioré de plusieurs manières. Je vous invite a d’abord bien le regarder et a vous renseigner sur les différentes techniques grâce a la documentation officielle de python puis bien sur de le tester. Vous verrez il n’est pas parfait mais il fait son affaire et j’ai grâce au module multiprocessing rendu un tout petit peu plus rapide qu’il ne l’était sans !

# coding: utf-8

import socket
from multiprocessing import Pool


open_p, closed_p = [], []
reponse = ["OPEN PORTS","CLOSED PORTS","SCANNING PORTS","TYPE",
           "SCANNING","ADDRESS","NUMBER OF PROCESSES","RANGE MIN","RANGE MAX","HOST","HAS IP","UNREACHABLE"]



target_ip = raw_input("{}: ".format(reponse[5]))
try:
    ip = socket.gethostbyname(target_ip)
    host = socket.gethostbyaddr(target_ip)
    print ""  
    print "{0} {1}: {2}".format(reponse[5], reponse[10], ip)  
    print "{0} :{1}".format(reponse[9], host[0])
    print ""
except:    
    print "{0} {1}".format(reponse[5], reponse[11])

num_procs = int(raw_input("{0}: ".format(reponse[6])))
minp = int(raw_input("{0}: ".format(reponse[7])))
maxp = int(raw_input("{0}: ".format(reponse[8])))
print "{0}:{1}\n".format(reponse[4],ip)


def scan(arg):
    target_ip, port = arg
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    
    sock.settimeout(.11)  
    result = sock.connect_ex((target_ip,port))
    if result == 0:             
        sock.shutdown(2)
        return port, True
    else:       
        return port, False
    sock.close
   

def main():  
    ports = range(minp, maxp+1)
    pool = Pool(processes=num_procs)
    print "{0}: ".format(reponse[2])
   
    for port, status in pool.imap_unordered(scan, [(target_ip, port)for port in ports]):
        print port,
        open_p.append(port)if status else closed_p.append(port)
        

if __name__ == '__main__':
    main()
   
print "\n\n{0}".format(reponse[0])
for elements in open_p:
    print "{0} {1} :{2}".format(elements,reponse[3],socket.getservbyport(elements))

 

Pour la démonstration je l’ai enregistré sous le nom « scanner.py » puis entré la commande suivante dans le terminal de linux: python scanner.py
Il suffit ensuite de suivre les instructions affichées pour l’utiliser. Pour l’exemple j’ai utilise les réglages suivant

  • ADDRESSE: 192.168.1.17 qui est celle de metasploitable sur ma machine virtuelle
  • NOMBRE DE PROCESS: 10
  • RANGE MIN: 10
  • RANGE MAX:500

Le résultat retournes la liste des différents ports ouverts que le script a découvert ainsi que les services utilisés par chaque port. Il serait intéressant de modifier le script pour pouvoir choisir entre une gamme de ports (range) et une liste de ports choisis. Il serait tout aussi intéressant de faire en sorte que ce script affiche les versions des services trouvés, ce qui pourrait ensuite servir a trouver différentes failles qu’il serait possible d’exploiter ultérieurement !

Je n’ai volontairement pas donné d’explications quand au fonctionnement du script pour vous laisser la chance de rechercher par vous même dans la documentation officielle ou sur d’autres sites, et de tester les différentes parties en les disséquant et les modifiant a votre guise.

Je rappellerais comme a chaque fois d’utiliser les différents outils dans la légalité. L’inverse pouvant vous coûter très cher (Voire sur Légifrance)

0

Petite présentation de python

Python est un langage de programmation interprété, orienté objet, de haut niveau basé sur C et C ++ et influencé par plusieurs autres langages . D’abord publié en 1991 par le développeur principal Guido van Rossum, Python excelle en termes de lisibilité et de facilité d’utilisation. Ses structures de données intégrées de haut niveau, associées à un typage dynamique et à une liaison dynamique, le rendent très attractif pour le développement rapide d’applications, ainsi que pour l’utilisation comme langage de script ou de collage pour connecter des composants existants ensemble. La syntaxe simple et facile à apprendre de Python met l’accent sur la lisibilité et réduit donc le coût de maintenance du programme. Python supporte les modules et les paquets, ce qui encourage la modularité du programme et la réutilisation du code. L’interpréteur Python et la vaste bibliothèque standard sont disponibles sous forme source ou binaire sans frais pour toutes les plateformes majeures, et peuvent être distribués gratuitement. Les professionnels de la cybersecurité peuvent rapidement prototyper, tester et déployer une application.

Voici deux exemples du fameux « Hello world » en python ainsi que en c  pour illustrer la différence de scriptage

 

  • Python
print "Hello world!"
ou depuis version 3.0:
print('Hello world!')

 

  • C
#include <stdio.h>
int main(void)
{
printf("Hello, world\n");
return 0;
}

 

 

Dans les articles suivants je vous présenterais différents scripts que j’ai crée pendant mon SPSE qui vous seront utiles dans le cadre d’un test d’intrusion.

0

Securitytube Python Scripting Expert

Le SPSE est une certification en ligne qui vous aidera à maîtriser les scripts Python et leur application aux problèmes de sécurité informatique et réseau. Ce cours est idéal pour les testeurs de pénétration,les passionnés de sécurité et les administrateurs réseau qui souhaitent apprendre à automatiser des tâches ou aller au-delà de l’utilisation d’outils prêts à l’emploi.

Le formateur n’est rien d’autre que Vivek Ramachandran qui est le fondateur et le formateur en chef de SecurityTube. Il a notamment découvert l’attaque Caffe Latte, a brisé WEP Cloaking,un schéma de protection WEP en 2007 à la Defcon. Il est également l’auteur du livre « Backtrack 5 Wireless Penetration Testing ».

Cette certification se base sur un support de cours minimaliste . Fournissant simplement juste ce qu’il faut d’explications et d’exemples en vidéos qui serviront de base à approfondir par la suite…  Ce qui oblige donc tout les participants a faire d’énormes recherches par eux mêmes pour pouvoir réussir les différents exercices proposées. Ce que je trouve personnellement être une très bonne chose. Pour certaines parties Vivek utilise des modules assez anciens, mais il n’est pas interdit d’utiliser d’autres modules (d’ailleurs je conseillerais d’utiliser et de tester le plus de modules différents que possible). Un forum spécifique pour les inscrits a la certification est aussi disponible pour vous aider, mais je vous conseillerais biens sur de d’abord faire les  recherches et tests par vous même pour bien assimiler les différentes techniques

L‘examen final se compose de 4 scripts différents  a créer (basés sur ce que vous aurez appris) . Les sujets étant fournis environ 10 a 15 minutes avant le début de l’examen, pour lequel vous avez 6h pour rendre vos copies. Les  scripts a créer pour l’examen final sont différents pour chaque personne, ce qui évite la spoliation.

 

Voici une liste non exhaustive des différents modules qui sont enseignés 

Module 1: Scripting Python – Les bases du langage

Module 2: Programmation du système et sécurité

Module 3: Programmation de sécurité réseau – Renifleurs et Injecteurs de paquets

Module 4: Attaques d’applications Web

Module 5: Techniques d’exploitation

Module 6: Analyse des logiciels malveillants et ingénierie inverse

Module 7: Automatisation des tâches d’attaque

Module 8: Poursuite de l’étude et feuille de route

Module 9: Modèle d’examen et examen simulé

Pour accéder a la page d’accueil de la certification SPSE c’est par ici.