Browse Source

Initial commit

master
commit
ed28447302
  1. 2
      .gitignore
  2. 9
      LICENSE
  3. 20
      Makefile
  4. 20
      README.md
  5. 2
      src/client/.gitignore
  6. 3
      src/client/go.mod
  7. 188
      src/client/main.go
  8. 85
      src/client/osutil/info.go
  9. 2
      src/server/.gitignore
  10. 3
      src/server/go.mod
  11. 170
      src/server/main.go

2
.gitignore vendored

@ -0,0 +1,2 @@
bin/
release/

9
LICENSE

@ -0,0 +1,9 @@
The MIT License (MIT)
Copyright © 2023 Kasyanov Nikolay Alexeyevich (Unbewohnte)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

20
Makefile

@ -0,0 +1,20 @@
all: server client
mkdir -p bin
mv spolitewareServer* bin
mv spolitewareClient* bin
server:
cd src/server && go build && mv spolitewareServer ../../
client:
cd src/client && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build && mv spolitewareClient ../../spolitewareClient_linux_amd64
cd src/client && CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build && mv spolitewareClient ../../spolitewareClient_darwin_amd64
cd src/client && CGO_ENABLED=0 GOOS=windows GOARCH=386 go build && mv spolitewareClient.exe ../../spolitewareClient_windows_x32.exe
cd src/client && CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build && mv spolitewareClient.exe ../../spolitewareClient_windows_amd64.exe
release: client
mkdir -p release/spoliteware
cp LICENSE release/spoliteware
cd src/server && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build && mv spolitewareServer ../../release/spoliteware
mv spolitewareClient* release/spoliteware
cd release && zip -r spoliteware spoliteware

20
README.md

@ -0,0 +1,20 @@
# Spoliteware - a polite and considerate spyware
A spyware that asks permission to collect data beforehand.
## Features
### Client
- System information
### Server
- Collected data is sorted per machine in a corresponding folder (hostname_username_IP)
- TLS support
## TODO
- Give some sort of "reward" for permitting to scan client's machine
- More green telemetry options
- Be more open on what the program does
## License
MIT

2
src/client/.gitignore vendored

@ -0,0 +1,2 @@
spolitewareClient
spolitewareClient.exe

3
src/client/go.mod

@ -0,0 +1,3 @@
module Unbewohnte/spolitewareClient
go 1.20

188
src/client/main.go

@ -0,0 +1,188 @@
/*
The MIT License (MIT)
Copyright © 2023 Kasyanov Nikolay Alexeyevich (Unbewohnte)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package main
import (
"Unbewohnte/spolitewareClient/osutil"
"bytes"
"encoding/json"
"flag"
"fmt"
"net/http"
"os"
"path/filepath"
"runtime"
"strings"
"time"
)
const (
Version string = "client v0.1.0"
)
var (
version *bool = flag.Bool("version", false, "Print version information and exit")
serverAddr *string = flag.String("server", "http://localhost:13370/", "Set scheme://addr:port for receiving server")
)
type Data struct {
Hostname string `json:"hostname"`
Username string `json:"username"`
System map[string]string `json:"system"`
}
func greeting() {
fmt.Printf(
`I'm sorry to inform you, but you've (intentionally or not) launched a spyware on your machine !
But rest assured, I will do no harm to your computer and am to withdraw if you would wish so.
No data will be collected and sent without your permission.
`)
}
func askForAllPerms() bool {
fmt.Printf(`Would you grant me a permission to [system information; files lookup; ]
(If no -> (optional) specify separate permissions afterwards) y/N: `)
var input string = "n"
fmt.Scanf("%s", &input)
input = strings.ToLower(input)
if strings.HasPrefix(input, "y") {
fmt.Printf("Your approval is appreciated; I am to treat your system with care\n")
return true
} else {
return false
}
}
func askForSystemInfo() bool {
fmt.Printf("\nWould you allow me to look around and collect some information about your computer ? [y/N]: ")
var input string = "n"
fmt.Scanf("%s", &input)
input = strings.ToLower(input)
if strings.HasPrefix(input, "y") {
fmt.Printf("System information scan allowed\n")
return true
} else {
fmt.Printf("As you wish\n")
return false
}
}
func localCopyNeeded() bool {
fmt.Printf("\nDo you want to save a local copy as well ? [y/N]: ")
var input string = "n"
fmt.Scanf("%s", &input)
input = strings.ToLower(input)
if strings.HasPrefix(input, "y") {
return true
} else {
return false
}
}
func main() {
fmt.Printf(
`
`)
flag.Parse()
if *version {
fmt.Printf("by Kasyanov Nikolay Alexeyevich (Unbewohnte) %s\n", Version)
return
}
var data Data
data.System = nil
greeting()
if askForAllPerms() {
data.System = osutil.GetSystemInfo()
} else {
if askForSystemInfo() {
data.System = osutil.GetSystemInfo()
}
}
if data.System == nil {
fmt.Printf("\nNothing to send. Bailing out\n")
return
}
data.Hostname = osutil.GetHostname()
data.Username = osutil.GetUsername()
dataJson, err := json.MarshalIndent(&data, "", " ")
if err != nil {
return
}
postBody := bytes.NewBuffer(dataJson)
var retries uint8 = 0
for {
if retries == 5 {
fmt.Printf("\nFailed to send data\n")
return
}
response, err := http.Post(*serverAddr, "application/json", postBody)
if err != nil || response == nil {
// try to resend
time.Sleep(time.Second * 5)
retries++
continue
}
if response.StatusCode != http.StatusOK {
// try to resend
time.Sleep(time.Second * 5)
retries++
continue
}
break
}
fmt.Printf("\nSuccesfully sent data. Thank you !\n")
if localCopyNeeded() {
wdir, err := os.Getwd()
if err != nil {
wdir = "./"
}
copyPath := filepath.Join(wdir, "spoliteware_scan_copy.txt")
f, err := os.Create(copyPath)
if err != nil {
fmt.Printf("Failed to create a local copy: %s\n", err)
} else {
_, err = f.Write(dataJson)
if err != nil {
fmt.Printf("Failed to write to a file to save a local copy: %s\n", err)
}
}
f.Close()
fmt.Printf("Saved to %s\n", copyPath)
}
if runtime.GOOS == "windows" {
fmt.Scanln()
}
}

85
src/client/osutil/info.go

@ -0,0 +1,85 @@
/*
The MIT License (MIT)
Copyright © 2023 Kasyanov Nikolay Alexeyevich (Unbewohnte)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package osutil
import (
"os/exec"
"runtime"
"strings"
)
func GetSystemInfo() map[string]string {
var sysInfo map[string]string = make(map[string]string)
switch runtime.GOOS {
case "windows":
{
output, err := exec.Command("systeminfo").Output()
if err == nil {
sysInfo["systeminfo"] = string(output)
}
output, err = exec.Command("wmic", "partition", "get", "name,size,type").Output()
if err == nil {
sysInfo["wmic"] = string(output)
}
}
default:
{
output, err := exec.Command("lscpu").Output()
if err == nil {
sysInfo["lscpu"] = string(output)
}
output, err = exec.Command("lsblk").Output()
if err == nil {
sysInfo["lsblk"] = string(output)
}
output, err = exec.Command("lspci").Output()
if err == nil {
sysInfo["lspci"] = string(output)
}
output, err = exec.Command("uname", "-a").Output()
if err == nil {
sysInfo["uname"] = string(output)
}
output, err = exec.Command("hostname").Output()
if err == nil {
sysInfo["hostname"] = string(output)
}
}
}
return sysInfo
}
func GetHostname() string {
output, err := exec.Command("hostname").Output()
if err != nil {
return "unknown_hostname"
}
return strings.TrimSpace(string(output))
}
func GetUsername() string {
output, err := exec.Command("whoami").Output()
if err != nil {
return "unknown_username"
}
return strings.TrimSpace(string(output))
}

2
src/server/.gitignore vendored

@ -0,0 +1,2 @@
collected_data/
spolitewareServer

3
src/server/go.mod

@ -0,0 +1,3 @@
module Unbewohnte/spolitewareServer
go 1.20

170
src/server/main.go

@ -0,0 +1,170 @@
/*
The MIT License (MIT)
Copyright © 2023 Kasyanov Nikolay Alexeyevich (Unbewohnte)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package main
import (
"encoding/json"
"flag"
"fmt"
"io"
"net"
"net/http"
"os"
"path/filepath"
"strings"
"time"
)
type Data struct {
Hostname string `json:"hostname"`
Username string `json:"username"`
System map[string]string `json:"system"`
}
const Version string = "server v0.1.0"
var (
version *bool = flag.Bool("version", false, "Print version information and exit")
port *uint = flag.Uint("port", 13370, "Set port to listen on")
keyFile *string = flag.String("key", "", "SSL private key file path")
certFile *string = flag.String("cert", "", "SSL certificate file path")
verbose *bool = flag.Bool("verbose", false, "Print user data-centric messages or not")
)
func main() {
fmt.Printf(
`
`)
flag.Parse()
if *version {
fmt.Printf("by Kasyanov Nikolay Alexeyevich (Unbewohnte) %s\n", Version)
return
}
executablePath, err := os.Executable()
if err != nil {
fmt.Printf("[ERROR] Failed to determine executable's path: %s\n", err)
return
}
dataDirPath := filepath.Join(filepath.Dir(executablePath), "collected_data")
err = os.MkdirAll(dataDirPath, os.ModePerm)
if err != nil {
fmt.Printf("[ERROR] Failed to create data directory: %s\n", err)
return
}
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
if *keyFile != "" && *certFile != "" {
proto := req.Header.Get("x-forwarded-proto")
if strings.ToLower(proto) == "http" {
http.Redirect(w, req, fmt.Sprintf("https://%s%s", req.Host, req.URL), http.StatusMovedPermanently)
return
}
}
if req.Header.Get("Content-Type") != "application/json" {
http.Error(w, "Content is not application/json", http.StatusBadRequest)
return
}
defer req.Body.Close()
body, err := io.ReadAll(req.Body)
if err != nil {
// Internal error, - no need to tell about it to the other side
if *verbose {
fmt.Printf("[ERROR] Failed to read request's body from %s: %s\n", req.RemoteAddr, err)
}
w.WriteHeader(http.StatusOK)
return
}
ip, _, err := net.SplitHostPort(req.RemoteAddr)
if err != nil {
if *verbose {
fmt.Printf("[ERROR] Failed to split host and port from %s: %s\n", req.RemoteAddr, err)
}
w.WriteHeader(http.StatusOK)
return
}
var clientData Data
err = json.Unmarshal(body, &clientData)
if err != nil {
if *verbose {
fmt.Printf("[ERROR] Failed to unmarshal data from %s: %s\n", req.RemoteAddr, err)
}
w.WriteHeader(http.StatusOK)
return
}
clientDir := filepath.Join(dataDirPath, fmt.Sprintf("%s_%s_%s", clientData.Hostname, clientData.Username, ip))
err = os.MkdirAll(clientDir, os.ModePerm)
if err != nil {
if *verbose {
fmt.Printf("[ERROR] Failed to create directory for %s: %s\n", ip, err)
}
w.WriteHeader(http.StatusOK)
return
}
clientFilePath := filepath.Join(clientDir, fmt.Sprintf("data_%d.json", time.Now().Unix()))
clientFile, err := os.OpenFile(clientFilePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm)
if err != nil {
if *verbose {
fmt.Printf("[ERROR] Failed to create file for %s: %s\n", ip, err)
}
w.WriteHeader(http.StatusOK)
return
}
defer clientFile.Close()
_, err = clientFile.Write(body)
if err != nil {
if *verbose {
fmt.Printf("[ERROR] Failed to append data to %s: %s\n", clientFilePath, err)
}
w.WriteHeader(http.StatusOK)
return
}
w.WriteHeader(http.StatusOK)
if *verbose {
fmt.Printf("[INFO] Received data from %s_%s_%s\n", clientData.Hostname, clientData.Username, ip)
}
})
server := http.Server{
Addr: fmt.Sprintf(":%d", *port),
Handler: mux,
}
if *keyFile != "" && *certFile != "" {
fmt.Printf("[INFO] Using TLS\n")
fmt.Printf("[INFO] Server listening on port %d\n", *port)
server.ListenAndServeTLS(*certFile, *keyFile)
} else {
fmt.Printf("[INFO] Not using TLS\n")
fmt.Printf("[INFO] Server listening on port %d\n", *port)
server.ListenAndServe()
}
}
Loading…
Cancel
Save