diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..50b9d12 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,85 @@ +kind: pipeline +type: docker +name: default + +trigger: + event: + - push + +steps: + - name: build + image: python:3.9-slim-buster + volumes: + - name: docs + path: /docs + commands: + - cd src/ + - python -m venv venv + - source venv/bin/activate + - pip install -r requirements.txt + - python setup.py install + - python -m pytest + - python setup.py sdist bdist_wheel + + + # docker image build + - name: docker-build-and-push + image: plugins/docker + settings: + dockerfile: ./Dockerfile + context: src/ + registry: hub.codefirst.iut.uca.fr + repo: hub.codefirst.iut.uca.fr/louis.dufour/detection_d_intrusion + username: + from_secret: SECRET_REGISTRY_USERNAME + password: + from_secret: SECRET_REGISTRY_PASSWORD + + + - name: generate-and-deploy-docs + image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-docdeployer + failure: ignore + volumes: + - name: docs + path: /docs + commands: + #- cd Documentation/doxygen + #- doxygen Doxyfile + - /entrypoint.sh + when: + branch: + - master + depends_on: [ build ] + + # container deployment + - name: deploy-container + image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest + environment: + IMAGENAME: hub.codefirst.iut.uca.fr/louis.dufour/detection_d_intrusion:latest + CONTAINERNAME: containersae + COMMAND: create + OVERWRITE: true + depends_on: [ docker-build-and-push ] + + - name: code-analysis + image: sonarsource/sonar-scanner-cli:latest + commands: + - cd src/ + - sonar-scanner \ + -Dsonar.projectKey=detection_d_intrusion \ + -Dsonar.projectName=detection_d_intrusion \ + -Dsonar.sources=. \ + -Dsonar.host.url=https://codefirst.iut.uca.fr/sonar \ + -Dsonar.login=$${PLUGIN_SONAR_TOKEN} + secrets: [ SONAR_TOKEN ] + settings: + # accessible en ligne de commande par $${PLUGIN_SONAR_HOST} + sonar_host: https://codefirst.iut.uca.fr/sonar/ + # accessible en ligne de commande par $${PLUGIN_SONAR_TOKEN} + sonar_token: + from_secret: SONAR_TOKEN + depends_on: [build] + +volumes: +- name: docs + temp: {} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f551779 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM python:3.9-slim-buster + +WORKDIR /app + +COPY src/ /app/src/ +COPY requirements.txt /app/ +COPY setup.py /app/ + +RUN python -m venv venv +RUN . venv/bin/activate && pip install -r requirements.txt && python setup.py install + +RUN python -m pytest +RUN python setup.py sdist bdist_wheel + +CMD ["python", "app.py"] diff --git a/Documentation/Compte rendus/CR-17-03-2023.md b/Documentation/Compte rendus/CR-17-03-2023.md new file mode 100644 index 0000000..d418964 --- /dev/null +++ b/Documentation/Compte rendus/CR-17-03-2023.md @@ -0,0 +1,9 @@ +## Rendez-vous du 17 Mars + +- Il nous reste 14h (sans compter la séance du 17/03) + +- Une fois la BDD qui marche, comment à tester : + - Utiliser des scripts de client pymodbus; + - ou avec des envois de paquets en dur directement dans le decodeur. + +#### Prochain rendez-vous le Mardi 21 Mars à 10h15 (Démo du code) \ No newline at end of file diff --git a/Documentation/Compte rendus/CR-21-03-2023.md b/Documentation/Compte rendus/CR-21-03-2023.md new file mode 100644 index 0000000..6dcfd74 --- /dev/null +++ b/Documentation/Compte rendus/CR-21-03-2023.md @@ -0,0 +1,23 @@ +## Rendez-vous du 21 Mars + +- Commenter la récursivité et justifier *(dans triPacket)* +- En terme de qualité de code c'est Zéro si on rend le décoder brut +- Doxygen +- Docker wazuh + +### Ce qu'il reste à faire: +- filtrer les paquets (donc juste un dictionnaire "si j'ai coils 5 qui vaut true et le regsitre 45 j'empêche cette variable à tel valeurs" création de règle) + - Si un scénario est repérer on bloque tout la request *(S'il y a valeur interdite je bloque tout ou non ? (utile soustenance))* +- Prévoir démo soustenance +- Idée indicateur (métrique) pilnt/sonnar *(un code comme ça c'est détruit et refais)* + +### Un docker pour: +- Code python +- Docker compose entre tout les container *(postgree, client, serveur, python)* + + +#### Prochain rendez-vous: +- Mercredi 29 Mars à 8h30 : démo BDD et rapport *(envoie Lundi 27 Mars soir)* +- Jeudi 30 Mars à 9h : oral blanc soutenance + + diff --git a/README.md b/README.md index 00b53de..1afd78b 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,10 @@ ## Utilisation ### Install + ```sh // Install d'outil -python -m pip install pipenv +python3 -m pip install pipenv // Install module pip install typer @@ -18,6 +19,24 @@ pipenv update // Lancement du shell pipenv pipenv shell ``` +**ATTENTION** : lorsque vous voulez faire un pipenv update cela vous donne une version buguée de pymodub qui est là `3.2.0` *(pour voir votre version faite un `pip freeze`)* + +#### Marche à suivre: +1) Désinstallez la bibliothèque pymodbus à l'aide de la commande suivante: +> pipenv uninstall pymodbus + +2) Installez une version précédente de la bibliothèque pymodbus en utilisant la commande suivante: +> pipenv install pymodbus==3.1.3 + +### Lancement +il faut lancé 3 terminal pipenv pour : + +```sh +./start_server.sh +./start_client.sh +sudo python3 ./decoder.py +``` + ### Côté BDD Il faudra que vous connectiez à votre BDD PostgreSQL. Exécuter le script `Table.sql` qui se trouve dans src avec la commande ci-dessous. @@ -39,21 +58,34 @@ psql -h londres -d -U -W **ATTENTION** `londres` est un serveur héberger dans l'infrastructure de notre établissement universitaire. -### Lancement -il faut lancé 3 terminal pipenv pour : - -```sh -./start_server.sh -./start_client.sh -sudo python3 ./decoder.py -``` - ## Notre configuration * Python (3.9) * PostgreSQL * pip (22.0.2) * pymodbus (3.1.3) +Notre pipfile: +``` +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +redis = "*" +click = "*" +prompt-toolkit = "*" +pymodbus = {extras = ["repl"], version = "*"} +sqlalchemy = "*" +scapy = "*" +ipython = "*" + +[dev-packages] + +[requires] +python_version = "3.9" +``` + ## Développeurs * [Louis](https://codefirst.iut.uca.fr/git/louis.dufour) * [Paul](https://codefirst.iut.uca.fr/git/paul.squizzato) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..bd457d9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +psycopg2 +pandas +getpass +scapy.all +scapy.contrib.modbus \ No newline at end of file diff --git a/src/Main.py b/src/Main.py index 8194541..28775cb 100644 --- a/src/Main.py +++ b/src/Main.py @@ -7,7 +7,6 @@ # # ======================================================================== -from cProfile import label import psycopg2 as psy# pip3 install types-psycopg2 import pandas as pd # pip3 install pandas import getpass @@ -19,24 +18,36 @@ import getpass ################# Fin Tips ################# -def insert_Status(connection,flow): +def insert_Status(type, adresse, valeur): + + if __name__ == '__main__': + db_host = input('Nom d\'hôte : ') + if not db_host: + db_host = 'londres' + db_name = input('Nom de la base de données : ') + if not db_name: + db_name = 'dblodufour1' + db_user = input('Utilisateur : ') + if not db_user: + db_user = 'ladufour1' + db_password = getpass('Mot de passe : ') + + connection = psy.connect(host=db_host, database=db_name, user=db_user, password=db_password) cur = connection.cursor() - - - - for row in df.itertuples(): - cur.execute("INSERT INTO Status VALUES (%s,%s,%s );", - (row.addresse, - row.type, - row.valeur) - ) + for row in df.itertuples(): + cur.execute("INSERT INTO Status VALUES (%s,%s,%s );", + (type, + adresse, + valeur) + ) connection.commit() cur.close() + def status_State(connection): cur = connection.cursor() - pd.read_sql(''' SELECT adresse, type, valeur FROM Status''' + pd.read_sql(''' SELECT adresse, type, valeur FROM Status''') res = cur.fetchone() return res cur.close() @@ -67,4 +78,7 @@ if __name__ == '__main__': create_tables(connection, 'Table.sql') - connection.close() + connection.close() + + +insert_Status(c,1,1) diff --git a/src/decoderBrut.py b/src/decoderBrut.py index 571c681..9eb65f2 100644 --- a/src/decoderBrut.py +++ b/src/decoderBrut.py @@ -3,6 +3,8 @@ import scapy.all as scapy import scapy.contrib.modbus as mb +from triPacket import triPacket +import getpass def decode(pkt): prt=0 @@ -44,12 +46,12 @@ def decode(pkt): valInter2=valInter[1:-1] miniL[2]=valInter2 if miniL.count(0)==0: - if(type(miniL[2])==list): + if type(miniL[2])==list: if miniL[0]=="r": for i in range(len(miniL[2])): - if("0x" in miniL[2][i]): + if "0x" in miniL[2][i]: miniL[2][i]=int(miniL[2][i],16) - if("0x" in str(miniL[1])): + if "0x" in str(miniL[1]): miniL[1]=int(miniL[1],16) bigL.append([miniL[0],miniL[1]+i,miniL[2][i]]) if miniL[0]=="c": @@ -92,11 +94,33 @@ def decode(pkt): bigL.append(miniL) if bigL!=[]: - print(bigL) + print("attention") + triPacket(bigL,connec) miniL = [0,0,0,0] bigL=[] miniL = [0,0,0,0] + + +print("In order for data sniffed to be stored inside the database, please register the following :") +db_host = input('host of the database server : ') +if not db_host: + db_host = '192.168.128.141' +db_name = input('name of the database : ') +if not db_name: + db_name = 'dblodufour1' +db_user = input('login of the user : ') +if not db_user: + db_user = 'lodufour1' +db_password = getpass.getpass('user password : ') +connec=[db_host,db_name,db_user,db_password] + +# si register 5 = 55 et coil 3 = 1 et coil 12 = 0 : +# ecriture sur le registre 8 à 72 ou coil 9 à 1 impossibles +# if ['r',5,55] and ['c',3,1] and ['c',12,0] and ecriture ['r',8,72]: + #bloquer ecriture +# ecrire registre 5 72,4,4,55,4 + scapy.sniff(iface="lo", prn=decode) diff --git a/src/ecritureBDD.py b/src/ecritureBDD.py new file mode 100644 index 0000000..75c14fb --- /dev/null +++ b/src/ecritureBDD.py @@ -0,0 +1,24 @@ +import psycopg2 as psy +import pandas as pd +import getpass + +def verifRegle(a): + return True + +def ecritureBDD(lStatus,connec): + co = None + try: + co = psy.connect(host=connec[0],database=connec[1],user=connec[2],password=connec[3]) + cur = co.cursor() + for i in lStatus: + cur.execute("INSERT INTO Status VALUES (%s,%s,%s ) ON CONFLICT (addresse,type) DO UPDATE SET valeur=%s;",(i[1],i[0],i[2],i[2])) + if verifRegle(co): + co.commit() + else: + co.rollback() + cur.close() + except(Exception,psy.DatabaseError) as error: + print(error) + finally: + if co is not None: + co.close() diff --git a/src/readme.md b/src/readme.md index ac2184a..dee36af 100644 --- a/src/readme.md +++ b/src/readme.md @@ -1,32 +1,3 @@ -# Install - -```sh -python -m pip install pipenv -pipenv update -pipenv shell -``` - -# Run - -Within pipenv, run in two terminals: - -```sh -./start_server.sh -./start_client.sh -``` - -# Example commands - -Within client run: - -``` -client.read_coils slave=1 address=0 -client.write_coil slave=1 address=0 value=1 -client.read_coils slave=1 address=0 -``` - -See the first boolean was false in the first read, was written to true, and appears at true in second read. - # Client commands : ``` @@ -44,4 +15,5 @@ client.write_registers address=0 values=845,123,0,427,4 slave=1 client.read_holding_registers address=0 count=1 slave=1 client.read_holding_registers address=0 count=100 slave=1 -``` \ No newline at end of file +``` + diff --git a/src/triPacket.py b/src/triPacket.py new file mode 100644 index 0000000..f84d815 --- /dev/null +++ b/src/triPacket.py @@ -0,0 +1,22 @@ +from ecritureBDD import ecritureBDD + +def decoupePacket(lPkt): + if type(lPkt)!=list: + print('pas liste') + return + if len(lPkt)==0: + print('liste vide') + return + if len(lPkt)==1: + return decoupePacket(lPkt[0]) + if len(lPkt)==3 and type(lPkt[0])==str: + return [[lPkt[0],int(lPkt[1]),int(lPkt[2])]] + else: + l=[] + for i in lPkt: + l+=decoupePacket(i) + return l + +def triPacket(lPkt,connec): + lNettoyee=decoupePacket(lPkt) + ecritureBDD(lNettoyee,connec)