RedRoom Recon Category : Portscan

What is the Portscan?

The Portscan tool is designed to identify open ports on a target system, detect the services running on those ports, and perform banner grabbing to collect additional details. It primarily uses TCP packets to gather this information but UDP and ICMP packets are an option too


Where does it help?

Knowing which ports are open and what services they host is critical for planning your next move—whether for defense or offense. For example, identifying a specific service can lead you to check for known vulnerabilities that need patching or, in the case of red team operations, could be exploited.


Methods Portscan Uses

Portscan builds upon the TCP scanning logic introduced in the Hostscan tool but with a broader range of scan types for more thorough results. In addition to traditional stealth and full TCP scans, it also supports:

  • FIN Scan – Sends a TCP packet with only the FIN flag set. Closed ports typically respond with a RST packet, while open ports ignore it, helping identify live services without completing a full handshake.

  • XMAS Scan – Sends a TCP packet with FIN, URG, and PSH flags set, creating a “lit up” packet. Like FIN scans, open ports often stay silent, while closed ports send back a RST.

  • ACK Scan – Sends an ACK packet to check firewall rules and filtering. It doesn’t identify open ports directly but can reveal whether a port is filtered or unfiltered.

Additionally, Portscan uses a built-in dictionary mapping common ports to their default services, allowing it to quickly match discovered ports with the most likely running applications.


Structure of the Portscan

Portscan can operate either as a standalone tool or as a method within other tools.
Here, we’ll look at both cases—starting with its standalone operation.


Running Portscan as a Standalone Tool

When run independently, Portscan accepts its own set of flags, with two being essential:

  • -r – Specifies the target range or a single IP.

  • -m – Defines the scan method (available options: TCP, UDP, ICMP).

Once executed, the tool’s main handler begins processing the request.


Inside portscan.py

from .methods_recon.digital_fingerprinting.find_ports import PortScan
from Essentials.utils import print_portscan_results

def run(args):
    try:
        tcp_flags = PortScan.parse_tcp_flags(args.extra)
        if tcp_flags is None:
            class DummyFlags:
                stealth = False
                fin = False
                ack = False
                xmas = False
                aggressive = False
            tcp_flags = DummyFlags()
        results = PortScan.Scan_method_handler(args.range, tcp_flags, args.timeout, args.retries)
    except Exception as e:
        print(f"Error during port scanning: {e}")
    print_portscan_results(results)

Here’s what’s happening :

  1. Importing dependencies

    • PortScan is imported from the methods_recon.digital_fingerprinting.find_ports module.

    • print_portscan_results is imported from Essentials.utils to format and display the scan results.(we will talk about utilites on another article)

  2. Parsing TCP flags
    The script attempts to parse tcp_flags from the command-line arguments. These flags define the type of TCP scan (e.g., stealth, FIN, ACK, XMAS).

  3. Fallback to default scan
    If no TCP flags are provided, the script creates a DummyFlags class where all scan types are set to False.
    This effectively means: run a standard full TCP scan.

  4. Executing the scan
    The Scan_method_handler method is called with the target range, chosen TCP flags, and timeout/retry settings.

  5. Outputting results
    The results are passed to print_portscan_results for a clean, readable output.


Inside find_ports.py :

    @staticmethod
    def Scan_method_handler(ip_range, tcp_flags=None, timeout=1, retries=1, max_workers=200):
        try:
            network = ipaddress.ip_network(ip_range, strict=False)
        except ValueError:
            print(colored(f"[!] Invalid IP range: {ip_range}", "red"))
            return []

        ports = getattr(tcp_flags, "port", None) or list(COMMON_PORTS.keys())

        if tcp_flags:
            if tcp_flags.aggressive:
                scan_funcs = [SCAN_METHODS["stealth"], SCAN_METHODS["ACK"], SCAN_METHODS["FIN"]]
            elif tcp_flags.stealth:
                scan_funcs = [SCAN_METHODS["stealth"]]
            elif tcp_flags.fin:
                scan_funcs = [SCAN_METHODS["FIN"]]
            elif tcp_flags.xmas:
                scan_funcs = [SCAN_METHODS["XMAS"]]
            elif tcp_flags.ack:
                scan_funcs = [SCAN_METHODS["ACK"]]
            else:
                scan_funcs = [SCAN_METHODS["connect"]]
        else:
            scan_funcs = [SCAN_METHODS["connect"]]

        all_results = {}
        scanned_pairs = set()

        for scan_func in scan_funcs:
            scan_name = scan_func.__name__

            targets = [
                (ip, port) for ip, port in product(network.hosts(), ports)
                if (str(ip), port) not in scanned_pairs
            ]

            with ThreadPoolExecutor(max_workers=max_workers) as executor:
                results = list(executor.map(lambda ip_port: scan_func(ip_port[0], [ip_port[1]], timeout, retries), targets))

            for ip, port_list in results:
                for port in port_list:
                    scanned_pairs.add((ip, port))

            formatted_results = {}
            for ip, port_list in results:
                if ip not in formatted_results:
                    formatted_results[ip] = set()
                formatted_results[ip].update(port_list)

            final_results = []
            for ip, port_set in formatted_results.items():
                open_ports = []
                filtered_ports = []
                services = []

                if scan_name in ['ACK', 'stealth', 'FIN']:
                    filtered_ports = sorted(port_set)
                else:
                    open_ports = sorted(port_set)
                    for port in open_ports:
                        banner = PortScan.grab_banner(ip, port, timeout)
                        services.append({
                            "port": port,
                            "banner": banner or COMMON_PORTS.get(port, "Unknown")
                        })

                final_results.append({
                    "ip": ip,
                    "open_ports": open_ports,
                    "filtered_ports": filtered_ports,
                    "services": services,
                    "scan_type": scan_name
                })

            all_results[scan_name] = final_results

        return all_results

I’ve structured the find_ports.py file so that each scan method is its own function, with the PortScan class acting as the main controller.

Inside this class, the handler function is responsible for:

  1. Parsing the target IP and ports to scan.

  2. Determining which scan methods should run (based on the provided flags).

  3. Using parallel threading to speed up the scanning process.

  4. Appending the results into a final dictionary, where each key represents a specific data type and each value stores the corresponding scan result.

    @staticmethod
    def grab_banner(ip, port, timeout=2):
        try:
            with socket.create_connection((ip, port), timeout=timeout) as s:
                s.settimeout(timeout)
                return s.recv(1024).decode(errors="ignore").strip()
        except Exception:
            return None

Portscan also includes a straightforward, standard banner grabbing implementation.
This step queries the target service on an open port and retrieves its initial response (the “banner”), which often contains useful details like service type, version, or even system information.


Running Portscan as a Method

When running Portscan as a method—as we did in the Hostprofile tool—you simply provide the required inputs and call the find_ports.py module directly.
This allows Portscan to perform only the specific scanning logic needed, without going through the standalone handler or additional CLI parsing.


Conclusion

Portscan is one of the “hybrid” tools in RedRoom, meaning it can operate both as a standalone tool and as an internal method for other modules.
Because of this dual role, the design prioritizes simplicity and speed over maximum scanning efficiency.
The goal is to get reliable, actionable results as quickly as possible, whether it’s run on its own or embedded in a larger reconnaissance workflow.


Next up: RedRoom – Recon Category: TraceRoute

0
Subscribe to my newsletter

Read articles from Ektoras Kalantzis directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ektoras Kalantzis
Ektoras Kalantzis