Pull to refresh

Detection of meterpreter sessions in Windows OS

Level of difficultyEasy
Reading time4 min
Views1.3K
Original author: @QdMiracle

Introduction

Hello Habr! This is a translation of my first article, which was born due to the fact that I once played with the types of meterpreter payload from the Metasploit Framework and decided to find a way to detect it in the Windows OS family.

Analysis

I will try to present everything in an accessible and compact way without delving into all the work. To begin with, I decided to create the nth number of useful loads (windows/meterpreter/reverse_tcp, shell/bind_tcp, shell_hidden_bind_tcp, vncinject/reverse_tcp, cmd/windows/reverse_powershell) to analyze what will happen in the system after their injection.

  • The shell/bind_tcp and shell_hidden_bind_tcp payloads use a bind connection as a connection, that is, we can see how an attacker connects to our ports, these payloads do not belong to meterpreter and cannot migrate between processes and do not load specific dlls into the system when they are in the system.

  • The vncinject/reverse_tcp payload also does not use specific dlls, but when a connection is established, it starts broadcasting traffic through the browser on a port related to the VNC protocol (ports 5900+N and 5900+ N).

  • And here are payloads having a reverse connection type windows/meterpreter/reverse_tcp and cmd/windows/reverse_powershell loaded their dll files both during startup and after migrations. And of course they opened ports.

This way I will be able to get a list of dlls that are specific to these payloads:

WIN_7_SIGNATURE = ["WINBRAND.dll", "WINHTTP.dll", "webio.dll", "SspiCli.dll", "cscapi.dll"]
WIN_10_SIGNATURE = ["rsaenh.dll", "netapi32.dll", "wkscli.dll", "psapi.dll", "cscapi.dll"]

Development

Well, after that, I decided to throw a simple script, let's look at its main methods.

class MeterpreterScaner:
def __init__(self):
self._signatures: List[str] = []
self._processes_with_signatures: List[str] = []
self._processes_with_dynamic_port: List[str] = []
self._suspicious_processes: Dict[str:List[str]] = {}

We declare the MeterpreterScaner class and its variables that we will need in the methods.

def _check_windows_version(self) -> None:
info = subprocess.check_output("systeminfo", shell=True)
win = re.findall(REG_FOR_WINDOWS_VERSION, str(info))

if win[0] == '10':
self._signatures = WIN_10_SIGNATURE
elif win[0] == '7':
self._signatures = WIN_7_SIGNATURE
else:
print("[X] Only for Windows 7 or Windows 10.")

A method that allows us to determine the OS version (I tested only on the seven and ten). We get information about the system and get only the version from it using regular regrowth. REG_FOR_WINDOWS_VERSION = r'(?:Windows\s+)(\d+|XP|\d+\.\d+)'

def _search_process_with_dll(self, dll: str) -> None:
output_tasklist = subprocess.check_output(f"{CMD_TASKLIST_COMMAND} {dll}", shell=True)
process_list = re.findall(REG_FOR_EXE_PROCESSES, str(output_tasklist))

for process_info in process_list:
process, process_PID = re.split(r'\s+', process_info)
if process in self._suspicious_processes:
self._suspicious_processes[f'{process}_{process_PID}'].append(dll)
else:
self._suspicious_processes[f'{process}_{process_PID}'] = [dll]

First, we get a list of all processes with the dll we are interested in using the command CMD_TASKLIST_COMMAND = "tasklist /M dll". We find all the running ones .exe files `REG_FOR_EXE_PROCESSES = r'(?<=\r\n)[A-Za-z]+.exe\s+\d+" and fill our dictionary with them along with their process PIDs.

def _check_suspicious_process(self) -> None:
self._check_windows_version()

for dll in self._signatures:
self._search_process_with_dll(dll)
for proc_info in self._suspicious_processes.items():
proc_name, proc_dlls = proc_info
if len(proc_dlls) == 5:
print(f"[-] Detected meterpreter signature in memory: {proc_name}")
self._processes_with_signatures.append(proc_name)
if not self._processes_with_signatures:
print("[+] Meterpreter signature in memory not found")

Here we check the processes received earlier for the number of dlls from our signatures.

def _scan_suspicious_ports(self) -> None:
scan_output = subprocess.check_output(CMD_NETSTAT_COMMAND, shell=True)
local_sockets = re.findall(REG_FOR_LOCAL_SOCKET, str(scan_output))
for l_socket in local_sockets:
l_ip, l_port = l_socket.split(':')
if int(l_port) >= 49152:
if l_ip != "127.0.0.1":
victim_socket = f"{l_ip}:{l_port}"
data_with_suspicious_socket = scan_output.decode().split(victim_socket)
suspicious_info = re.findall(REG_FOR_REMOTE_SOCKET, data_with_suspicious_socket[1])[0]
suspicious_socket, suspicious_PID = suspicious_info
# port 4444 used by default in MSF and meterpreter
if int(suspicious_socket.split(':')[-1]) == 4444:
print(f"[!] Detected MSF connection with {suspicious_socket}")
print(f"[-] Connection {victim_socket} to {suspicious_socket} "
f"used dynamic port on PID - {suspicious_PID}")
self._processes_with_dynamic_port.append(suspicious_PID)

We look at open connections with the command CMD_NETSTAT_COMMAND = 'netstat -aon |find /i "established"", filter the received information REG_FOR_LOCAL_SOCKET = r'(?:TCP\s+)(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}:\d{1,5})" and then check the sockets. We check the dynamic ports and see that it is not localhost. If everything matches, we get data about remote soket REG_FOR_REMOTE_SOCKET = r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5})\s+[A-Z]+\s+(\d+)' and analyze it. If the port is 4444, then it is 99.(9)% that this is the payload because this port is used in MSF by default.
It was also possible to add a VNC check, but I think you can add one condition =)

def finding_meterpreter_sessions(self):
found = False
self._check_suspicious_process()
self._scan_suspicious_ports()

for proc in self._processes_with_signatures:
proc_name, proc_PID = proc.split('_')
if proc_PID in self._processes_with_dynamic_port:
print(f'[!] A match was found in process {proc_name} with PID {proc_PID}')
found = True

if not found:
print(f"[+] A match wasn't found")

Well, at the end of the method that runs this whole script.

Conclusion

In this article, I tried to describe a way to detect reverse payloads of meterpreter. I also analyzed the scripts that I could find and wrote a script to detect meterpreter in a memory dump, if you are interested, I can write about it next time. Also, if you know other signatures or detection methods, write about them in the comments.

I will also leave [a link to the project] (https://github.com/OverPotter/MeterpreterScanner ) and thank you for your time.

Tags:
Hubs:
Rating0
Comments0

Articles