Lucene search
+L

Email Inbox Search

🗓️ 10 Jul 2026 11:31:42Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com👁 2 Views

KittySploit Email Inbox Search searches an operator mailbox via IMAP.

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

from email import policy
from email.header import decode_header
from email.parser import BytesParser
from typing import Any, Dict, List, Optional

from kittysploit import *

try:
	import imaplib
	IMAP_AVAILABLE = True
except ImportError:
	imaplib = None
	IMAP_AVAILABLE = False


def _decode_mime_header(value: str) -> str:
	if not value:
		return ""
	try:
		parts = decode_header(value)
		out = []
		for part, enc in parts:
			if isinstance(part, bytes):
				out.append(part.decode(enc or "utf-8", errors="replace"))
			else:
				out.append(str(part))
		return "".join(out)
	except Exception:
		return str(value)


def _email_body(msg) -> str:
	body = ""
	if msg.is_multipart():
		for part in msg.walk():
			if part.get_content_type() == "text/plain":
				payload = part.get_payload(decode=True)
				if payload:
					body = payload.decode(part.get_content_charset() or "utf-8", errors="replace")
				break
	else:
		payload = msg.get_payload(decode=True)
		if payload:
			body = payload.decode(msg.get_content_charset() or "utf-8", errors="replace")
	return (body or "").strip()


class Module(Post):
	__info__ = {
		"name": "Email Inbox Search",
		"description": "Search the operator mailbox attached to an email reverse session via IMAP",
		"author": "KittySploit Team",
		"session_type": SessionType.EMAIL,
	'agent': {
	    'risk': '',
	    'effects': ['api_request'],
	    'expected_requests': 2,
	    'reversible': True,
	    'approval_required': False,
	    'produces': ['risk_signals'],
	    'cost': 1.5,
	    'noise': 0.5,
	    'value': 1.0,
	    'requires': 	    {'min_endpoints': 0,
	     'min_params': 0,
	     'tech_hints_any': [],
	     'tech_hints_all': [],
	     'specializations_any': [],
	     'risk_signals_any': [],
	     'auth_session': False,
	     'capabilities_any': [],
	     'capabilities_all': [],
	     'confidence_min': {},
	     'confidence_min_any': {},
	     'endpoint_pattern_any': [],
	     'param_any': [],
	     'api_surface_ready': False},
	    'chain': 	    {'produces_capabilities': [],
	     'consumes_capabilities': [],
	     'option_bindings': {},
	     'suggested_followups': []},
	},
	}

	subject = OptString("", "Subject substring to match", False)
	from_addr = OptString("", "Sender substring to match", False)
	body_keyword = OptString("", "Body keyword to match", False)
	unread_only = OptBool(False, "Search only unread messages", False)
	max_results = OptInteger(25, "Maximum messages to display", False)
	mailbox = OptString("", "Mailbox to search (empty = session default)", False)
	show_body = OptBool(True, "Print message body preview", False)
	body_preview = OptInteger(400, "Body preview length in characters", False)

	def _session_id_value(self) -> str:
		session_id_attr = getattr(self, "session_id", "")
		if hasattr(session_id_attr, "value"):
			return str(session_id_attr.value or "").strip()
		return str(session_id_attr or "").strip()

	def _connection_data(self) -> Dict[str, Any]:
		session_id_value = self._session_id_value()
		if not session_id_value or not self.framework:
			return {}

		for listener in getattr(self.framework, "active_listeners", {}).values():
			if not hasattr(listener, "_session_connections"):
				continue
			conn = listener._session_connections.get(session_id_value)
			if isinstance(conn, dict):
				return conn

		session = self.framework.session_manager.get_session(session_id_value)
		if session and isinstance(session.data, dict):
			return session.data
		return {}

	def _imap_connect(self, data: Dict[str, Any]):
		if not IMAP_AVAILABLE:
			raise ProcedureError(
				FailureType.ConfigurationError, "imaplib is not available"
			)
		host = str(data.get("imap_host") or "")
		port = int(data.get("imap_port") or 993)
		user = str(data.get("imap_user") or "")
		password = str(data.get("imap_password") or "")
		use_ssl = bool(data.get("use_ssl_imap", True))
		if not host or not user or not password:
			raise ProcedureError(
				FailureType.ConfigurationError,
				"IMAP credentials not found in email session",
			)
		if use_ssl:
			conn = imaplib.IMAP4_SSL(host, port)
		else:
			conn = imaplib.IMAP4(host, port)
		conn.login(user, password)
		return conn

	def _matches(self, msg, subject_filter: str, from_filter: str, body_filter: str) -> bool:
		subject = _decode_mime_header(msg.get("Subject", ""))
		from_hdr = msg.get("From", "")
		body = _email_body(msg)
		if subject_filter and subject_filter.lower() not in subject.lower():
			return False
		if from_filter and from_filter.lower() not in from_hdr.lower():
			return False
		if body_filter and body_filter.lower() not in body.lower():
			return False
		return True

	def run(self):
		try:
			data = self._connection_data()
			if not data:
				print_error("Email session connection data not found")
				return False

			mailbox = str(self.mailbox or data.get("mailbox") or "INBOX").strip()
			subject_filter = str(self.subject or "").strip()
			from_filter = str(self.from_addr or "").strip()
			body_filter = str(self.body_keyword or "").strip()
			limit = max(1, int(self.max_results or 25))
			preview_len = max(40, int(self.body_preview or 400))

			print_info("=" * 80)
			print_status(f"Searching mailbox: {mailbox}")
			if subject_filter:
				print_info(f"  subject contains: {subject_filter}")
			if from_filter:
				print_info(f"  from contains: {from_filter}")
			if body_filter:
				print_info(f"  body contains: {body_filter}")
			if self.unread_only:
				print_info("  unread only: yes")

			conn = self._imap_connect(data)
			try:
				conn.select(mailbox, readonly=True)
				criteria = "UNSEEN" if self.unread_only else "ALL"
				typ, msg_data = conn.search(None, criteria)
				if typ != "OK" or not msg_data or not msg_data[0]:
					print_warning("No messages matched the search criteria")
					return True

				uids = msg_data[0].split()
				matches: List[Dict[str, Any]] = []
				for uid in reversed(uids):
					typ, fetched = conn.fetch(uid, "(RFC822)")
					if typ != "OK" or not fetched:
						continue
					raw = fetched[0][1]
					msg = BytesParser(policy=policy.default).parsebytes(raw)
					if not self._matches(msg, subject_filter, from_filter, body_filter):
						continue
					body = _email_body(msg)
					matches.append(
						{
							"uid": uid.decode() if isinstance(uid, bytes) else str(uid),
							"subject": _decode_mime_header(msg.get("Subject", "")),
							"from": msg.get("From", ""),
							"date": msg.get("Date", ""),
							"body": body,
						}
					)
					if len(matches) >= limit:
						break

				if not matches:
					print_warning("No messages matched the filters")
					return True

				print_info("-" * 80)
				print_success(f"Found {len(matches)} message(s)")
				for i, item in enumerate(matches, 1):
					print_info(f"\n[{i}] UID={item['uid']}")
					print_info(f"  From: {item['from']}")
					print_info(f"  Date: {item['date']}")
					print_info(f"  Subject: {item['subject']}")
					if self.show_body and item.get("body"):
						body = item["body"]
						if len(body) > preview_len:
							body = body[:preview_len] + "..."
						print_info(f"  Body: {body}")

				print_info("=" * 80)
				return True
			finally:
				try:
					conn.logout()
				except Exception:
					pass
		except ProcedureError:
			raise
		except Exception as exc:
			raise ProcedureError(
				FailureType.Unknown, f"Email inbox search failed: {exc}"
			)

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

10 Jul 2026 11:31Current
5.9Medium risk
Vulners AI Score5.9
2