CRITICAL: DO NOT CHANGE ANY PASSWORDS IN THE FIRST 15 MINUTES — CAPTURE ALL SKY-* FLAGS FIRST  |  NEVER BLOCK OR MODIFY sky_scorebot  |  SCP SYNTAX: PULL = scp user@TARGET:/remote ~/local/  ·  PUSH = scp ~/local user@TARGET:/remote/
⚑ Mission Brief

Competition Overview

🚨
THREE NON-NEGOTIABLE RULES:
1. NO password changes for first 15 minutes. System Admin CTF flags require the original credentials.
2. NEVER block, disable, or change sky_scorebot's password or SSH access.
3. NO attacking Red Team machines, scoring servers, or other teams. Defend only.
🔭
TRUST BUT VERIFY: The 2023 PIR describes Redis/Node/FTP/SMB with specific CVEs. The 2024+ round may use different services, different OS versions, or different vulnerability classes entirely. The first 5 minutes of SSH access should be used to confirm what's actually running before executing service-specific playbooks. See the Recon & Pivot section.

🏆 Scoring Model

  • System Admin Module: Submit CTF flags first — one-time points. Flags may be plaintext, encoded, hidden in binaries, images, or DB entries.
  • Network Module: Continuous service checks. Scorebot tests if your service is up and clean. Additive — 0 for failed checks, never negative.
  • IOC Checks: Scoring server checks for Red Team artifacts. Remove them to score.
  • sky_scorebot SSHs into your machines. It must always succeed.

⚔ Red Team Playbook (2023 Intel)

  • Plants backdoors and steals/overwrites flags in the first minutes.
  • Overwrites flag.txt with "red team wuz here".
  • Adds rogue sudoer entries to /etc/sudoers.d/.
  • Exploits unauthenticated Redis → pivots through Node connection.
  • Uses world-writable SMB shares to inject files.
  • Creates persistence via crontab, new users, or authorized_keys.
  • Treat PIR as Intel, not Gospel. Verify actual services before assuming.

🗺 Expected Network Topology (SSH from Kali)

Server 1
Database / Redis
Port 6379 (expected)
Server 2
Web / Node.js
Port 80 / 3000 (expected)
Server 3
File / FTP
Port 21 (expected)
Server 4
File / SMB
Port 445 (expected)
You are on a Kali workstation. You SSH into each target machine. All file transfers use SCP from your Kali terminal. Tools like John, Hashcat, Steghide, Strings, and Binwalk run on Kali, not on the target. Pull files to Kali to analyze them.
👥 Team

Role Assignments

Assign roles before noon. Everyone knows their job before the gun fires. Coordinator's first order: no one touches passwords until the all-clear.
🏴
Flag Hunter / Coordinator
1 person. Runs all 4 flag hunt methods across all servers simultaneously. Does NOT touch configs. Calls all-clear when flags submitted. Directs team after.
🔴
Redis King
Server 1. Baseline → backup → add auth → bind localhost → command rename → monitor. Redis was never fixed in 2023. This must not repeat.
🌐
Node King
Server 2. Audit .js source, exec/eval hunt, update Redis connection string with new password, watch web logs.
📁
FTP King
Server 3. Immediately disable anonymous r/w. Chroot users. Watch auth.log. Keep port 21 green for scorebot.
🗂
SMB King
Server 4. Audit sudoers.d, set shares read-only, watch for file injection. TMux up, getent group sudo every 5 min. King of the Hill.

📊 Google Sheet — Pre-Structured

TabNameContents
1DashboardOwner per server. 🟢/🔴 status. Flags captured. Blockers.
2Server 1 — RedisIP, user, all accounts, ports, hashes, actual service discovered, patch status
3Server 2 — NodeIP, user, accounts, ports, .js file paths, actual service, patch status
4Server 3 — FTPIP, user, accounts, ports, config file path, daemon version, patch status
5Server 4 — SMBIP, user, accounts, sudoers.d findings, share names, patch status
6Config BackupsPaste raw text of smb.conf, redis.conf, vsftpd.conf, /etc/passwd — immediately at game start
7IOC TrackerSuspicious files, new users, crontab entries, modified lines, encoded strings found
8CTF NotesFlag hunting progress, encoded strings, hash values, stego findings, cracked passwords
T−30 MIN

Pre-Game Setup

Before 12:00 PM

🍕 Logistics

  • Eat before sitting down. No food during 12–4 PM. 2023 lesson.
  • Seated and terminal-ready before Zoom check-in.
  • All CyberSkyline emails received — Zoom link, network diagram, IP range. Missing by Thursday? Email organizer.
  • Google Sheet open with pre-built tabs.
  • LinPEAS entire text copied to clipboard or text editor. Air-gapped. No wget.
  • This playbook open on second screen.
  • Kali cracking tools verified: john, hashcat, steghide, binwalk, strings, xxd

📁 Kali Directory Structure

# Run on your Kali workstation before competition starts
mkdir -p ~/targets/{s1_redis,s2_node,s3_ftp,s4_smb}/backups
mkdir -p ~/tools ~/flags ~/analysis ~/hashes ~/stego

# Verify cracking tools exist on Kali
which john hashcat steghide binwalk strings xxd base64 file
which scp ssh nmap 2>/dev/null

# Pre-paste LinPEAS into a file before competition
nano ~/tools/linpeas.sh
chmod +x ~/tools/linpeas.sh

🔑 SSH Key Setup (On Kali, Before Game)

# Generate keys (do this before 12pm)
ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519
ssh-keygen -t rsa -b 2048 -N "" -f ~/.ssh/id_rsa_legacy

# === AT GAME START — push keys to each target ===
# Replace USER and TARGET_IP with actual values from briefing
ssh-copy-id -i ~/.ssh/id_ed25519 USER@TARGET_IP

# If ed25519 rejected (older SSH daemon):
ssh-copy-id -i ~/.ssh/id_rsa_legacy USER@TARGET_IP

# After key push, SSH in without password:
ssh USER@TARGET_IP

# Add to ~/.ssh/config for shorthand (optional)
# Host s1
#   HostName TARGET_IP
#   User USER
#   IdentityFile ~/.ssh/id_ed25519

📋 First Things to Record at Game Start

SSH in → run these → dump to Google Sheet Tab for that server:

# Paste this entire block on each target server:
echo "=== IP ===" && (ip a 2>/dev/null || ifconfig)
echo "=== USERS ===" && cat /etc/passwd
echo "=== SUDO ===" && getent group sudo
echo "=== UID0 ===" && awk -F: '$3==0{print $1}' /etc/passwd
echo "=== PORTS ===" && (ss -tulpn 2>/dev/null || netstat -tulpn)
echo "=== SERVICES ===" && (systemctl list-units --type=service \
  --state=running 2>/dev/null || service --status-all 2>/dev/null)
echo "=== CRONTAB ===" && crontab -l 2>/dev/null; cat /etc/crontab 2>/dev/null
echo "=== AUTH_KEYS ===" && find /home /root -name "authorized_keys" \
  -exec cat {} + 2>/dev/null
echo "=== OS VERSION ===" && cat /etc/os-release 2>/dev/null || uname -a
Phase 1

The Golden Window — Flags First

00:00 – 00:15
🚨
NO PASSWORDS. NO CONFIG CHANGES. Flag Hunter runs all searches simultaneously across all 4 servers. Server Kings take backups only. Coordinator calls all-clear. Only then does hardening begin.
FLAGS MAY NOT BE PLAINTEXT. The flag format SKY-XXXX-XXXX might be base64-encoded, embedded in a binary, inside an image, in a database, or stored as a hash. If grep finds nothing obvious, escalate to the CTF Depth section immediately. Do not assume the flag is just sitting in flag.txt.

🏴 Flag Hunt — Layer 1: Plaintext Search

# === From Kali, SSH in and run: ===

# Layer 1a: Find any flag.txt files
find / -name "flag*.txt" -o -name "*.flag" -o -name "flag" \
  -type f 2>/dev/null | xargs cat 2>/dev/null

# Layer 1b: Grep for SKY- format across all readable paths
grep -r "SKY-" /var/www /home /etc /tmp /opt /root /srv 2>/dev/null

# Layer 1c: Broader grep across entire filesystem
grep -rnw '/' -e 'SKY-' 2>/dev/null | head -50

# Layer 1d: Check if Red Team already overwrote them
grep -rnw '/' -e 'red team wuz here' 2>/dev/null

# Layer 1e: Check environment variables (flags sometimes loaded here)
env | grep -i "SKY\|flag\|ctf"
cat /proc/1/environ 2>/dev/null | tr '\0' '\n' | grep -i "flag\|SKY"

🔍 Flag Hunt — Layer 2: Encoded / Hidden

If Layer 1 finds nothing or flags are overwritten — escalate here before giving up.

# === PULL suspicious files to Kali for analysis ===
# (See SCP Ref section for full syntax)

# Layer 2a: Check for base64-encoded flags
find / -name "flag*" -o -name "*.enc" -o -name "*.b64" \
  2>/dev/null | xargs cat 2>/dev/null | base64 -d 2>/dev/null | strings

# On Kali — decode any suspicious base64 string:
echo "SGVsbG8gV29ybGQ=" | base64 -d

# Layer 2b: Search for base64 patterns in all text files
grep -rE "[A-Za-z0-9+/]{20,}={0,2}" /var/www /home /etc /tmp 2>/dev/null \
  | grep -v "\.pyc\|\.so\|Binary"

# Layer 2c: strings on any compiled/binary files in web dirs
# On target — find candidates:
find /var/www /opt /home -type f ! -name "*.js" ! -name "*.py" \
  ! -name "*.txt" ! -name "*.conf" 2>/dev/null

# Pull to Kali and run strings:
# scp user@TARGET_IP:/path/to/binary ~/analysis/
strings ~/analysis/binary | grep -i "SKY-\|flag\|password"

# Layer 2d: Check databases for flag values
# Redis (if accessible):
redis-cli KEYS '*flag*' 2>/dev/null
redis-cli KEYS '*' 2>/dev/null | xargs -I{} redis-cli GET {}

# SQLite:
find / -name "*.db" -o -name "*.sqlite" 2>/dev/null
sqlite3 /path/to/db.sqlite ".tables" 2>/dev/null
sqlite3 /path/to/db.sqlite "SELECT * FROM flags;" 2>/dev/null

# Layer 2e: Check image/media files for steganography
# Find images on target:
find /var/www /home /opt -name "*.jpg" -o -name "*.png" \
  -o -name "*.bmp" 2>/dev/null

# Pull to Kali, run steghide and binwalk (see CTF Depth section)

💾 Immediate Backups (Server Kings — while Flag Hunter hunts)

Run on each target server via SSH. Do NOT modify anything. Just backup.
# === On target server (via SSH) ===
mkdir -p ~/safe_backups
cp -a /etc/passwd         ~/safe_backups/ 2>/dev/null
cp -a /etc/shadow         ~/safe_backups/ 2>/dev/null
cp -a /etc/sudoers        ~/safe_backups/ 2>/dev/null
cp -a /etc/sudoers.d      ~/safe_backups/ 2>/dev/null
cp -a /etc/crontab        ~/safe_backups/ 2>/dev/null
cp -a /etc/ssh/sshd_config ~/safe_backups/ 2>/dev/null
cp -a ~/.ssh/authorized_keys ~/safe_backups/ 2>/dev/null

# Service-specific configs (grab whatever exists)
for f in /etc/redis/redis.conf /etc/samba/smb.conf \
          /etc/vsftpd.conf /etc/proftpd/proftpd.conf \
          /etc/nginx/nginx.conf /etc/apache2/apache2.conf \
          /etc/mysql/mysql.conf.d/mysqld.cnf \
          /etc/postgresql/*/main/postgresql.conf; do
  [ -f "$f" ] && cp -a "$f" ~/safe_backups/ && echo "Backed up: $f"
done
▼ PULL — Target → Kali
# From your Kali terminal — pull all backups FROM target
# Full syntax: scp user@IP:/remote/path ~/local/path

scp -r USER@TARGET_IP:~/safe_backups/ \
    ~/targets/s1_redis/backups/

# Pull individual critical files:
scp USER@TARGET_IP:/etc/passwd   ~/targets/s1_redis/backups/
scp USER@TARGET_IP:/etc/shadow   ~/targets/s1_redis/backups/
scp USER@TARGET_IP:/etc/sudoers  ~/targets/s1_redis/backups/
If SCP is blocked or fails, use Netcat to exfiltrate (see SCP Ref section for full fallback procedure). Also paste critical configs as plain text into Google Sheet Tab 6 as a cloud backup.
Phase 2

Triage & Hardening

00:15 – 00:45
Coordinator calls "GREEN ON FLAGS". Only then do Server Kings begin hardening — simultaneously, one per server.

👤 Account Hardening — All Servers

# All UID 0 accounts (should only be root)
awk -F: '$3 == 0 {print $1}' /etc/passwd

# Users with valid login shells (attack surface)
awk -F: '$7 !~ /(nologin|false|sync|halt|shutdown)/ {print $1, $7}' /etc/passwd

# Sudo group
getent group sudo 2>/dev/null || grep "^sudo:" /etc/group

# All sudoers.d entries
ls -la /etc/sudoers.d/ && cat /etc/sudoers.d/* 2>/dev/null

# Backdoor SSH keys
find /home /root -name "authorized_keys" -exec echo "=== {} ===" \; \
  -exec cat {} \; 2>/dev/null

# Recent account creation (high UID = recently added)
awk -F: '{print $1, $3}' /etc/passwd | sort -t' ' -k2 -n | tail -10
# DISABLE (not delete) — preserves hash for rollback
sudo usermod -L <unauthorized_user>
sudo usermod -s /usr/sbin/nologin <unauthorized_user>

# Remove rogue sudoers file
sudo rm /etc/sudoers.d/<rogue_file>

# Clean authorized_keys — compare to backup, remove rogue key lines
diff ~/safe_backups/authorized_keys ~/.ssh/authorized_keys
nano ~/.ssh/authorized_keys

# Immutable lock on critical files AFTER hardening is complete
sudo chattr +i /etc/passwd /etc/shadow /etc/sudoers
sudo chattr +i /etc/ssh/sshd_config
# Undo if you need to make further changes: chattr -i /etc/passwd
⚙ Legacy Fallback — if usermod or chattr missing
# Lock account via passwd command (works on very old systems)
sudo passwd -l <username>
sudo passwd -u <username>  # unlock

# If chattr not available, restrict with permissions instead
sudo chmod 444 /etc/passwd
sudo chmod 000 /etc/shadow
🚨
ONLY after Coordinator calls GREEN ON FLAGS
## CHANGE ROOT AND USER PASSWORDS ##
sudo passwd root
sudo passwd <your_assigned_user>

## VERIFY sky_scorebot is untouched ##
id sky_scorebot
grep sky_scorebot /etc/passwd
# Confirm account is active and NOT locked
sudo passwd -S sky_scorebot
# View sudoers safely
sudo cat /etc/sudoers
ls -la /etc/sudoers.d/ && cat /etc/sudoers.d/*

# Remove rogue entry
sudo rm /etc/sudoers.d/<rogue_entry>

# Validate sudoers syntax before closing
sudo visudo --check 2>/dev/null || sudo visudo -c

# Lock sudoers directory
sudo chmod 750 /etc/sudoers.d/
⚙ Legacy Account Management (pre-systemd / minimal OS)
# If usermod not found:
sudo passwd -l <user>         # lock password
sudo passwd -u <user>         # unlock
sudo chsh -s /bin/false <user>  # deny shell

# If getent not available:
grep "^sudo:" /etc/group
grep "^wheel:" /etc/group     # some distros use wheel for sudo

# If visudo not available, edit directly (risky):
sudo nano /etc/sudoers        # careful — syntax error = lockout

# Find who can run sudo via config search:
grep -r "ALL" /etc/sudoers /etc/sudoers.d/ 2>/dev/null

🔍 LinPEAS Deployment

▲ PUSH — Kali → Target
# From Kali — push LinPEAS to target
scp ~/tools/linpeas.sh USER@TARGET_IP:/tmp/linpeas.sh

# On target (via SSH):
chmod +x /tmp/linpeas.sh
/tmp/linpeas.sh 2>/dev/null | tee /tmp/scan.txt

# Review with color
less -R /tmp/scan.txt
▼ PULL Results — Target → Kali
# Pull LinPEAS output back to Kali for review
scp USER@TARGET_IP:/tmp/scan.txt \
    ~/targets/s1_redis/scan_results.txt

less -R ~/targets/s1_redis/scan_results.txt
Air-gap fallback: If SCP fails, paste LinPEAS via clipboard on the target terminal directly. nano /tmp/linpeas.sh → paste → Ctrl+O, Ctrl+X → chmod +x → run.
# Quick LinPEAS grep for high-value findings
grep -iE "sudo|suid|writable|crontab|\
passwd|key|password|token|secret|flag" \
/tmp/scan.txt | head -60

🔥 IPTables Firewall

Cannot block Red Team IPs directly. CAN patch services. Whitelist scoring server IPs. Never block sky_scorebot source IP.
# View current rules
sudo iptables -L -v -n --line-numbers

# Allow established connections first (do this before adding restrictions)
sudo iptables -I INPUT 1 -m state --state ESTABLISHED,RELATED -j ACCEPT

# Whitelist scoring server (get IP from network diagram / briefing)
sudo iptables -I INPUT 2 -s <SCORING_IP> -j ACCEPT

# Restrict Redis to localhost only
sudo iptables -A INPUT -p tcp --dport 6379 -s 127.0.0.1 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 6379 -j DROP

# Rate-limit SSH brute force (does NOT block scorebot)
sudo iptables -A INPUT -p tcp --dport 22 \
  -m limit --limit 5/min --limit-burst 10 -j ACCEPT

# Save rules
sudo iptables-save | sudo tee /etc/iptables/rules.v4

# Legacy save method (older Debian/Ubuntu):
sudo sh -c 'iptables-save > /etc/iptables.rules'
Phase 3

Active Defense — King of the Hill

00:45 – 04:00

🖥 TMux Monitoring Layout (On Kali)

# Start new tmux session on Kali
tmux new-session -s defense

# Split layout: Ctrl+B % (vertical) | Ctrl+B " (horizontal)
# Move panes: Ctrl+B arrow

# ── PANE 1: Network connections on target ──
ssh USER@TARGET_IP "watch -n 2 'ss -tulpn 2>/dev/null || netstat -tulpn'"

# ── PANE 2: Sudo group + UID0 watch ──
ssh USER@TARGET_IP "watch -n 10 'getent group sudo 2>/dev/null \
  || grep sudo /etc/group; \
  echo ---; awk -F: \$3==0 /etc/passwd'"

# ── PANE 3: Auth log tail ──
ssh USER@TARGET_IP "sudo tail -f /var/log/auth.log 2>/dev/null \
  || sudo tail -f /var/log/secure 2>/dev/null"

# ── PANE 4: Recent file modifications ──
ssh USER@TARGET_IP "watch -n 30 'find / -type f -mmin -5 \
  ! -path /proc/\* ! -path /sys/\* ! -path /run/\* 2>/dev/null'"

🔎 Threat Hunting One-Liners

# === Files modified in last 10 minutes ===
find / -type f -mmin -10 \
  ! -path "/proc/*" ! -path "/sys/*" \
  ! -path "/run/*"  ! -path "/var/log/*" 2>/dev/null

# === Red Team IOC string ===
grep -rnw '/' -e 'red team wuz here' 2>/dev/null

# === New SUID binaries ===
find / -perm /4000 -type f 2>/dev/null | sort

# === Unexpected crontab ===
crontab -l 2>/dev/null; sudo crontab -l 2>/dev/null
ls /etc/cron.d/ /etc/cron.daily/ 2>/dev/null

# === Live diff config against Kali backup (run from Kali) ===
ssh USER@TARGET_IP "cat /etc/passwd" | \
  diff - ~/targets/s1_redis/backups/passwd

# === Suspicious listening ports (exclude known good) ===
ss -tulpn 2>/dev/null | grep -vE ":22|:21|:80|:443|:445|:6379|:3000|127.0.0"

# === Packet capture on target (if Wireshark unavailable) ===
sudo tcpdump -i any port 80 -nn -A -c 200 2>/dev/null

🔒 Immutable Locks (After Hardening)

# Lock critical system files
sudo chattr +i /etc/passwd /etc/shadow /etc/sudoers
sudo chattr +i /etc/ssh/sshd_config

# Lock service configs (after patching each server)
for f in /etc/redis/redis.conf /etc/samba/smb.conf \
          /etc/vsftpd.conf /etc/proftpd/proftpd.conf; do
  [ -f "$f" ] && sudo chattr +i "$f" && echo "Locked: $f"
done

# Verify immutable flags:
lsattr /etc/passwd /etc/shadow /etc/sudoers /etc/ssh/sshd_config

# Unlock (when you need to make changes):
sudo chattr -i /etc/passwd
⚙ Legacy Fallback — if chattr/lsattr not available
# Use permissions instead of immutable flag
sudo chmod 444 /etc/passwd        # read-only for all
sudo chmod 400 /etc/shadow        # root read-only
# To restore: chmod 644 /etc/passwd; chmod 640 /etc/shadow
🔭 Recon

Service Discovery & Pivot Planning

🔭
THE 2023 PIR MAY NOT MATCH YOUR YEAR'S COMPETITION. The server roles (Redis, Node, FTP, SMB) may be the same but versions, configs, and vuln classes will differ. The OS may have changed entirely. Run discovery first — confirm before assuming.

🔭 Step 1 — Identify What's Actually Running

# === Full service fingerprint — run on each target ===
echo "=== OS ===" && cat /etc/os-release 2>/dev/null || uname -a

echo "=== LISTENING PORTS ===" && (ss -tulpn 2>/dev/null || netstat -tulpn)

echo "=== ALL SERVICES ===" && \
  (systemctl list-units --type=service --state=running 2>/dev/null \
  || service --status-all 2>&1 \
  || ls /etc/init.d/)

echo "=== INSTALLED PACKAGES (Debian/Ubuntu) ===" && \
  dpkg -l | grep -E "redis|ftp|samba|smb|nginx|apache|mysql|postgres|node|express" 2>/dev/null

echo "=== INSTALLED PACKAGES (RHEL/CentOS) ===" && \
  rpm -qa 2>/dev/null | grep -E "redis|ftp|samba|http|mysql|node" 2>/dev/null

echo "=== PROCESS LIST ===" && ps aux | grep -v "^USER\|grep" | sort -k3 -rn | head -30

echo "=== CONFIG FILE HUNT ===" && \
  find /etc -name "*.conf" -type f 2>/dev/null | head -40

🔭 Step 2 — Service Pivot Table

If the service running isn't what the PIR described, use this pivot table to find the right remediation approach.

If You See...PortInstead Of...Pivot Action
PostgreSQL / MySQL5432 / 3306Redis 6379Check for no-auth, default creds, world-readable data dirs. See DB pivot card below.
Apache / Nginx80/443Node.js 3000Check .htaccess, php files for exec/system, CGI scripts. Check /var/www for flags.
NFS2049SMB 445Check /etc/exports for no_root_squash, world-readable exports. Lock down permissions.
SFTP / SCP only22FTP 21Audit SSH config for PermitRootLogin, AllowUsers. Check chroot jail configs.
Memcached11211Redis 6379Bind to 127.0.0.1 only. No auth by default — firewall at the port.
MongoDB27017Redis 6379Enable auth, bind to localhost. Check /etc/mongod.conf for security.authorization.
PHP / Python / Ruby appvariesNode.jsHunt for eval(), exec(), shell_exec(), system() calls. Check framework version for known CVEs.
Telnet23SSH 22Credentials sent plaintext. Disable if possible: sudo systemctl stop telnet or inetd entry.
SNMP161Check community string (default "public"). Change or firewall if not needed by scorebot.

🔭 Pivot: Database Not Redis

=== MySQL / MariaDB ===
# Test no-auth login (root with no password)
mysql -u root 2>/dev/null
mysql -u root -p''  2>/dev/null

# Fix: set root password
sudo mysqladmin -u root password 'StrongPass2024'
# Or in MySQL:
ALTER USER 'root'@'localhost' IDENTIFIED BY 'StrongPass2024';

# Hunt for flags in DB:
mysql -u root -p -e "SHOW DATABASES;" 2>/dev/null
mysql -u root -p -e "SELECT * FROM flags;" DB_NAME 2>/dev/null

=== PostgreSQL ===
sudo -u postgres psql -c "\l"  2>/dev/null
sudo -u postgres psql -c "SELECT * FROM flags;" 2>/dev/null

# Bind PostgreSQL to localhost:
grep "listen_addresses" /etc/postgresql/*/main/postgresql.conf
# Set: listen_addresses = 'localhost'

🔭 Pivot: Web Server Not Node

=== Apache pivot ===
find /var/www /srv/www -type f 2>/dev/null | head -30

# Hunt for command injection in PHP:
grep -rn "exec\|shell_exec\|system\|passthru\|eval\|\`" \
  /var/www/ 2>/dev/null | grep "\.php"

# Disable directory listing:
grep -r "Options.*Indexes" /etc/apache2/ 2>/dev/null
# Remove "Indexes" from Options line

=== Nginx pivot ===
cat /etc/nginx/sites-enabled/* 2>/dev/null
# Check for proxy_pass to internal services

=== Check for web shell drops (Red Team IOC):
find /var/www -name "*.php" -newer /etc/passwd 2>/dev/null
grep -rn "base64_decode\|gzinflate\|str_rot13" /var/www/ 2>/dev/null

🔭 Pivot: Unknown Vulnerability Class

If LinPEAS doesn't immediately surface the vuln and the PIR doesn't match — systematic triage:

# 1. World-writable files in sensitive locations
find /etc /var/www /opt /srv -type f -perm -o+w 2>/dev/null

# 2. World-writable directories
find /etc /var/www /opt /srv -type d -perm -o+w 2>/dev/null

# 3. Files owned by nobody/unknown users
find / -nouser -o -nogroup 2>/dev/null | head -20

# 4. Services running as root that shouldn't be
ps aux | awk '$1=="root" && $11!~/sshd|cron|init|kernel/' | head -20

# 5. SUID/SGID binaries not in typical list
find / -perm /6000 -type f 2>/dev/null | \
  grep -vE "ping|sudo|su|passwd|mount|newgrp|wall|write|chfn|chsh"

# 6. Open ports with no obvious service name
ss -tulpn 2>/dev/null | awk 'NR>1{print $5}' | sort -u

# 7. Environment variables with secrets
env && cat /proc/1/environ 2>/dev/null | tr '\0' '\n'

# 8. Recently modified configs
find /etc -name "*.conf" -mmin -60 2>/dev/null
🖥 Servers

Server-Specific Remediation

Redis was never fixed in 2023 and stayed red the entire competition. Node was the pivot into Redis. Prioritize these two. Follow each tab sequentially — don't skip steps.

🔴 Server 1 — Redis · Port 6379 pivot-aware legacy-aware

2023: No auth, directory traversal, ACL ignored, Node.js config pointed here as a pivot entry.

# Find redis.conf (may not be /etc/redis/)
find / -name "redis.conf" 2>/dev/null
find / -name "redis*.conf" 2>/dev/null

# Test: does Redis respond without auth?
redis-cli PING
# PONG without password = CRITICAL VULN. NOAUTH = already protected.

# Check bind address
grep "^bind" /etc/redis/redis.conf 2>/dev/null

# Check requirepass
grep "requirepass" /etc/redis/redis.conf 2>/dev/null

# Check for ACL file
grep "aclfile" /etc/redis/redis.conf 2>/dev/null

# Dump all Redis keys (check for flag data)
redis-cli KEYS '*'
redis-cli KEYS '*flag*' | xargs -I{} redis-cli GET {}

# Check Redis version (older = different config syntax)
redis-cli INFO server | grep redis_version
# Edit redis.conf — add requirepass
sudo nano /etc/redis/redis.conf

# Find: # requirepass foobared
# Change to (uncomment and set):
requirepass StrongRedisPass2024

## CRITICAL: Update Node.js connection BEFORE restarting Redis
## See Node tab — update Redis client config with new password first
## Otherwise you'll break the web service and lose scoring points

# Restart Redis
sudo systemctl restart redis 2>/dev/null || \
  sudo service redis restart 2>/dev/null || \
  sudo /etc/init.d/redis-server restart

# Test auth required
redis-cli PING
# Expected: NOAUTH Authentication required
redis-cli -a StrongRedisPass2024 PING
# Expected: PONG
⚙ Legacy Redis (< v6) — different requirepass syntax
# Older Redis uses same requirepass but no ACL system
# Also check for /etc/redis.conf vs /etc/redis/redis.conf
find / -name "redis*.conf" 2>/dev/null
# Apply requirepass in whichever file the running instance uses:
redis-cli CONFIG GET requirepass
redis-cli CONFIG SET requirepass "StrongPass2024"
# Note: CONFIG SET is live but survives only until restart
# Must edit the .conf file for persistence
# Bind Redis to localhost only
sudo nano /etc/redis/redis.conf

# Find: bind 0.0.0.0  OR  bind 127.0.0.1 ::1
# Set to:
bind 127.0.0.1

# If Node is on SAME machine: 127.0.0.1 only is correct
# If Node is on DIFFERENT machine: bind 127.0.0.1 <NODE_SERVER_IP>

# Disable dangerous commands (prevent directory traversal)
# Add to redis.conf:
rename-command CONFIG ""
rename-command SLAVEOF ""
rename-command DEBUG ""
rename-command FLUSHALL ""
rename-command FLUSHDB ""

# Restart
sudo systemctl restart redis 2>/dev/null || sudo service redis restart

# Verify cannot connect from outside:
# From Kali: redis-cli -h TARGET_IP -p 6379 PING
# Expected: Could not connect / Connection refused
# Lock down file permissions
sudo chown root:redis /etc/redis/redis.conf 2>/dev/null || \
  sudo chown root:root /etc/redis/redis.conf
sudo chmod 640 /etc/redis/redis.conf

# Lock data directory
find /var/lib/redis -type d -exec sudo chmod 750 {} \; 2>/dev/null
sudo chown -R redis:redis /var/lib/redis/ 2>/dev/null

# Immutable lock on config after hardening
sudo chattr +i /etc/redis/redis.conf
lsattr /etc/redis/redis.conf
# Full verification checklist
echo "=== Service Status ===" && sudo systemctl status redis 2>/dev/null || \
  sudo service redis status
echo "=== Bind Address ===" && grep "^bind" /etc/redis/redis.conf
echo "=== Auth ===" && grep "^requirepass" /etc/redis/redis.conf
echo "=== Permissions ===" && ls -la /etc/redis/redis.conf
echo "=== Immutable ===" && lsattr /etc/redis/redis.conf

# Functional tests:
redis-cli PING               # Should: NOAUTH required
redis-cli -a StrongRedisPass2024 PING  # Should: PONG
# From Kali (test external block):
# redis-cli -h TARGET_IP PING  — Should: Connection refused
🔭
If Server 1 is running MySQL/PostgreSQL/MongoDB/Memcached instead — use the pivot table in the Recon section. Key principle is the same: add auth, bind to localhost, restrict permissions on config files.
# Quick identification
ss -tulpn | grep -E "3306|5432|27017|11211|6379"
ps aux | grep -E "mysql|postgres|mongo|memcache|redis"

# MySQL/MariaDB — add root password
sudo mysqladmin -u root password 'StrongPass2024' 2>/dev/null
mysql -u root -e "UPDATE mysql.user SET \
  authentication_string=PASSWORD('StrongPass2024') \
  WHERE User='root'; FLUSH PRIVILEGES;" 2>/dev/null

# Bind MySQL to localhost
grep "bind-address" /etc/mysql/mysql.conf.d/mysqld.cnf 2>/dev/null
# Set: bind-address = 127.0.0.1
sudo systemctl restart mysql 2>/dev/null || sudo service mysql restart

🌐 Server 2 — Node.js (Web) · Port 80 / 3000 pivot-aware

2023: Command injection backdoor (exec/eval), Redis pivot via connection config, directory traversal.

# === STEP 1: Find the application root ===
find /var/www /opt /home /srv -name "*.js" -type f 2>/dev/null | head -20
find /var/www /opt /home /srv -name "package.json" 2>/dev/null

# === STEP 2: Hunt for command injection backdoors ===
grep -rn "require('child_process')\|require(\"child_process\")" \
  /var/www/ /opt/ 2>/dev/null

grep -rn "exec\|eval\|spawn\|execSync\|spawnSync" \
  /var/www/ /opt/ 2>/dev/null | grep "\.js"

# === STEP 3: Find Redis connection config ===
grep -rn "redis\|createClient\|6379\|REDIS" \
  /var/www/ /opt/ 2>/dev/null | grep "\.js"

# === STEP 4: Pull the app files to Kali for safe review ===
▼ PULL — Target → Kali
# Pull entire web app directory to Kali for analysis
scp -r USER@TARGET_IP:/var/www/ ~/targets/s2_node/www_backup/
scp -r USER@TARGET_IP:/opt/ ~/targets/s2_node/opt_backup/ 2>/dev/null

# Review locally on Kali with grep:
grep -rn "exec\|eval\|spawn" ~/targets/s2_node/www_backup/ 2>/dev/null
# === STEP 5: Update Redis connection to use new password ===
# Find: redis.createClient() or createClient({host:'...'})
# Update to:
# const client = redis.createClient({host:'127.0.0.1',password:'StrongRedisPass2024'})

# If using env variable:
grep -rn "REDIS_URL\|REDIS_PASS\|REDIS_AUTH" /var/www/ /opt/ 2>/dev/null
cat /etc/environment 2>/dev/null | grep -i redis

# === STEP 6: Comment out backdoor exec/eval calls ===
# Do NOT delete the file — comment out the dangerous lines
nano /path/to/app.js  # add // before exec() lines

# === STEP 7: Restart Node service ===
sudo systemctl restart node 2>/dev/null
sudo pm2 restart all 2>/dev/null
# Identify PID manually:
ps aux | grep -E "node|npm" | grep -v grep
# sudo kill -HUP <PID> — graceful restart

# === STEP 8: Watch web logs for Red Team activity ===
sudo tail -f /var/log/nginx/access.log 2>/dev/null
sudo tail -f /var/log/apache2/access.log 2>/dev/null
# Older systems: /var/log/httpd/access_log
🔭
If the web server is Apache/PHP or Nginx+Python instead of Node: hunt for exec()/shell_exec()/system() in .php files or Python subprocess calls. Same principle — find the injection point and neutralize it. Check the Pivot Table in Recon section.

📁 Server 3 — FTP · Port 21 legacy-aware

2023: Anonymous read/write enabled. Red Team created multiple logins and uploaded arbitrary files.

# Which FTP daemon is running?
systemctl status vsftpd proftpd 2>/dev/null
ps aux | grep -E "vsftpd|proftpd|ftpd|pure-ftpd"
ss -tulpn | grep :21

# Find the config file
find /etc -name "vsftpd.conf" -o -name "proftpd.conf" \
  -o -name "pure-ftpd.conf" 2>/dev/null

# Check for anonymous login vulnerability:
ftp localhost <<'EOF'
anonymous
anonymous@
ls
bye
EOF
# If you get a file listing = VULN

# Check FTP directories for Red Team file drops
find /var/ftp /srv/ftp /home/ftp -type f 2>/dev/null
ls -laR /var/ftp/ 2>/dev/null
=== vsftpd (/etc/vsftpd.conf) ===
sudo nano /etc/vsftpd.conf

# Critical settings:
anonymous_enable=NO         # DISABLE anonymous login
anon_upload_enable=NO       # No anonymous uploads
anon_mkdir_write_enable=NO  # No anonymous dir creation
local_enable=YES            # Allow system user logins
write_enable=YES            # Authenticated users can write
chroot_local_user=YES       # Jail users to their home dir
allow_writeable_chroot=YES  # May need for chroot to work

# Restart vsftpd
sudo systemctl restart vsftpd 2>/dev/null || \
  sudo service vsftpd restart 2>/dev/null || \
  sudo /etc/init.d/vsftpd restart

# Immutable lock
sudo chattr +i /etc/vsftpd.conf
=== ProFTPD (/etc/proftpd/proftpd.conf) ===
sudo nano /etc/proftpd/proftpd.conf

# Find the <Anonymous> block and add DenyAll:
<Anonymous ~ftp>
  DenyAll
</Anonymous>
# OR remove the entire <Anonymous> block

# Also set:
DefaultRoot ~       # Chroot to home dir
RootLogin off       # No root FTP login

# Restart
sudo systemctl restart proftpd 2>/dev/null || \
  sudo service proftpd restart 2>/dev/null || \
  sudo /etc/init.d/proftpd restart

sudo chattr +i /etc/proftpd/proftpd.conf
⚙ Pure-FTPd (alternate FTP daemon)
# Pure-FTPd config is split into files under /etc/pure-ftpd/conf/
ls /etc/pure-ftpd/conf/
# Disable anon:
echo "no" > /etc/pure-ftpd/conf/NoAnonymous
echo "yes" > /etc/pure-ftpd/conf/ChrootEveryone
sudo service pure-ftpd restart
# Verify anon login is disabled
ftp localhost <<'EOF'
anonymous
anonymous@
bye
EOF
# Expected: 530 Login incorrect / Permission denied

# Verify service is up (scorebot needs port 21)
ss -tulpn | grep :21
sudo systemctl status vsftpd 2>/dev/null || sudo service vsftpd status

# Check FTP directories are clean (no Red Team files)
find /var/ftp /srv/ftp /home/ftp -type f 2>/dev/null | xargs ls -la
# Remove any unexpected files placed by Red Team

🗂 Server 4 — Samba/SMB · Port 445 legacy-aware

2023: World-writable shares (arbitrary file injection), rogue sudoer in /etc/sudoers.d/.

# === STEP 1: Find and audit smb.conf ===
find / -name "smb.conf" 2>/dev/null
# Usually: /etc/samba/smb.conf

# Show all configured shares:
testparm -s 2>/dev/null || grep -A5 "\[" /etc/samba/smb.conf

# === STEP 2: Fix each share — make read-only ===
sudo nano /etc/samba/smb.conf

# For each [share_name] section:
writable = no          # or: read only = yes
public = no
guest ok = no
browseable = no        # hide from network browsers
valid users = <your_user> sky_scorebot  # whitelist only

# === STEP 3: Audit sudoers.d — CRITICAL ===
ls -la /etc/sudoers.d/
cat /etc/sudoers.d/*
# Remove any file not created by your team:
sudo rm /etc/sudoers.d/<rogue_file>

# === STEP 4: Check for Red Team file drops in shares ===
find /var/lib/samba /home /srv -type f -newer /etc/samba/smb.conf 2>/dev/null

# === STEP 5: Restart Samba ===
sudo systemctl restart smbd nmbd 2>/dev/null || \
  sudo service smbd restart 2>/dev/null || \
  sudo /etc/init.d/samba restart

# === STEP 6: Lock config ===
sudo chattr +i /etc/samba/smb.conf

# === ONGOING — run every 5 min (King of the Hill) ===
watch -n 30 'ls -la /etc/sudoers.d/; echo "---"; getent group sudo'
⚙ Older Samba (v3.x) — different config keys
# Older Samba uses "writable" but restart command differs
sudo /etc/init.d/samba restart   # old-style init
# OR: stop individual daemons
sudo killall -HUP smbd nmbd
# Verify version:
smbd --version
🏴 CTF

CTF Depth — Hidden & Encoded Flags

Flags will not always be plaintext SKY-XXXX-XXXX in a file called flag.txt. They may be base64-encoded, inside images, embedded in binaries, hashed, stored in a database, or locked behind a cracked password. Escalate here if grep finds nothing.

🔍 File Identification — Know What You Have

# === Identify any suspicious file — run on Kali after SCP pull ===
# 'file' command ignores extension and reads magic bytes
file suspicious_file
file ~/analysis/*

# Check for hidden data appended to a file
xxd suspicious_file | head -50   # hex dump top
xxd suspicious_file | tail -50   # hex dump bottom (hidden data often appended)

# Strings — extract all printable strings from any file type
strings suspicious_file | grep -i "SKY-\|flag\|pass\|key\|secret"
strings -n 8 suspicious_file     # strings of 8+ chars

# Check if a file is actually a different type (e.g., image with wrong extension)
file image.jpg                   # might return "data" or "ZIP archive"
binwalk suspicious_file          # scan for embedded files and signatures

🔐 Encoding / Decoding

=== Base64 ===
echo "U0tZLUFCQ0QtMTIzNA==" | base64 -d
# Decode a file:
base64 -d encoded_file.txt

# Find base64 patterns in files:
grep -rE "[A-Za-z0-9+/]{24,}={0,2}" \
  /var/www /home /etc 2>/dev/null | head -20

=== Hex / URL encoding ===
# Decode hex string:
echo "534b592d41424344" | xxd -r -p
# URL decode (Python on Kali):
python3 -c "import urllib.parse; \
  print(urllib.parse.unquote('SKY%2DABCD%2D1234'))"

=== ROT13 ===
echo "FXL-NOPQ-1234" | tr 'A-Za-z' 'N-ZA-Mn-za-m'

=== XOR (common CTF encoding) ===
python3 -c "
data=open('encoded_file','rb').read()
key=0x41  # guess common XOR keys: 0x41, 0xFF, 0x13
print(''.join(chr(b^key) for b in data if 32<=(b^key)<127))"

🖼 Steganography

=== steghide (JPEG/BMP) — on Kali ===
# Pull image from target first:
# scp USER@TARGET_IP:/path/image.jpg ~/stego/

steghide info image.jpg           # check for embedded data
steghide extract -sf image.jpg    # extract (prompts for passphrase)
steghide extract -sf image.jpg -p ""  # try empty passphrase
steghide extract -sf image.jpg -p "password" -f

=== binwalk — extract embedded files ===
binwalk image.jpg                 # show embedded file signatures
binwalk -e image.jpg              # extract embedded files
binwalk --dd='.*' image.jpg       # extract everything

=== strings on images ===
strings image.jpg | grep -i "SKY-\|flag"

=== exiftool (metadata) ===
exiftool image.jpg | grep -i "comment\|description\|flag\|SKY"

=== zsteg (PNG LSB steganography) ===
zsteg image.png 2>/dev/null
zsteg -a image.png 2>/dev/null    # all modes

🔓 Password Cracking — When Accounts Are Locked

Pull /etc/shadow to Kali. Crack on Kali using John or Hashcat. Never run cracking tools on the target — too resource-heavy and leaves forensic traces.
▼ PULL shadow file — Target → Kali
# From Kali — pull shadow file
scp USER@TARGET_IP:/etc/shadow ~/hashes/shadow_s1
scp USER@TARGET_IP:/etc/passwd ~/hashes/passwd_s1

# Combine for John The Ripper:
unshadow ~/hashes/passwd_s1 ~/hashes/shadow_s1 > ~/hashes/combined_s1.txt

=== John The Ripper ===
# Quick run with default wordlist:
john ~/hashes/combined_s1.txt

# With rockyou wordlist (most effective):
john --wordlist=/usr/share/wordlists/rockyou.txt ~/hashes/combined_s1.txt

# Rules-based mangling (catches password1, Password!, etc):
john --wordlist=/usr/share/wordlists/rockyou.txt \
  --rules ~/hashes/combined_s1.txt

# Show cracked passwords:
john --show ~/hashes/combined_s1.txt

=== Hashcat (faster with GPU) ===
# Identify hash type from shadow file:
# $1$ = MD5, $5$ = SHA-256, $6$ = SHA-512, $y$ = yescrypt
hashcat -m 1800 ~/hashes/shadow_s1 /usr/share/wordlists/rockyou.txt  # SHA-512
hashcat -m 500  ~/hashes/shadow_s1 /usr/share/wordlists/rockyou.txt  # MD5
hashcat -m 7400 ~/hashes/shadow_s1 /usr/share/wordlists/rockyou.txt  # SHA-256

# Attack modes:
hashcat -a 0   # dictionary
hashcat -a 3   # brute force: hashcat -a 3 hash ?u?l?l?l?d?d
hashcat -a 6   # wordlist + mask

# Show results:
hashcat --show ~/hashes/shadow_s1

🔑 Hash Identification Quick Reference

Prefix in shadowHash TypeHashcat -mjohn format
$1$MD5 crypt500md5crypt
$5$SHA-256 crypt7400sha256crypt
$6$SHA-512 crypt1800sha512crypt
$y$yescrypt— (john only)auto
$2b$bcrypt3200bcrypt
$apr1$Apache MD51600md5crypt-long
plain 32 hex charsMD50raw-md5
plain 40 hex charsSHA-1100raw-sha1
plain 64 hex charsSHA-2561400raw-sha256

🗄 Database Flag Hunting

=== Redis ===
redis-cli -a <pass> KEYS '*'
redis-cli -a <pass> KEYS '*flag*'
redis-cli -a <pass> KEYS '*' | xargs -I{} redis-cli -a <pass> GET {}

=== SQLite (pull to Kali first) ===
find / -name "*.db" -o -name "*.sqlite" -o -name "*.sqlite3" 2>/dev/null
▼ PULL DB file — Target → Kali
scp USER@TARGET_IP:/path/to/app.db ~/analysis/

# On Kali:
sqlite3 ~/analysis/app.db ".tables"
sqlite3 ~/analysis/app.db ".dump" | grep -i "SKY-\|flag\|password"
sqlite3 ~/analysis/app.db "SELECT * FROM users;"

=== MySQL — query on target ===
mysql -u root -p -e "SHOW DATABASES;" 2>/dev/null
mysql -u root -p -e "USE app; SHOW TABLES; SELECT * FROM flags;" 2>/dev/null

=== PostgreSQL ===
sudo -u postgres psql -c "\l"
sudo -u postgres psql -c "SELECT * FROM flags;" app 2>/dev/null
⚙ Legacy

Legacy Tool Fallbacks

Given the legacy SSH protocols noted in the 2023 PIR, assume some targets may be running old Ubuntu (14.04/16.04), CentOS 6/7, or minimal Debian installs. These systems are missing many modern tools. Every section in this playbook includes a legacy fallback — this page is the consolidated reference.
Modern CommandLegacy / FallbackNotes
ss -tulpnnetstat -tulpnInstall net-tools if missing: apt install net-tools (if internet available)
ip aifconfigifconfig is in net-tools package
ip routeroute -nAlso: netstat -rn
systemctl status Xservice X status or /etc/init.d/X statusCheck ls /etc/init.d/ for available services
systemctl restart Xservice X restart or /etc/init.d/X restartSome need full stop + start
journalctl -xetail /var/log/syslog or /var/log/messagesAuth logs: /var/log/auth.log or /var/log/secure
chattr +ichmod 444 fileImmutable flag not available on all FS — chmod is the fallback
lsattrTry touch file 2>&1If touch fails with "Operation not permitted" = immutable
getent group sudogrep "^sudo:" /etc/groupAlso check wheel group: grep "^wheel:" /etc/group
usermod -Lpasswd -l <user>Both lock the account; passwd -l is more universal
visudo --checkvisudo -cIf both fail, grep sudoers manually
find -mminfind -newer /tmp/refCreate reference file: touch -t 202406121200 /tmp/ref
lsof -i :PORTnetstat -tlnp | grep PORTOr: fuser PORT/tcp
watch -n 2 'cmd'while true; do clear; cmd; sleep 2; donewatch may not be installed on minimal systems
tmuxscreenscreen is almost universally installed; use Ctrl+A % to split
redis-cliTelnet: telnet localhost 6379 then type PINGOlder systems may not have redis-cli in PATH — check /usr/bin/redis-cli
iptables-savesh -c 'iptables-save > /etc/iptables.rules'On old Debian: /etc/network/if-pre-up.d/ script to restore
smbclient / testparmRead smb.conf directly + grep -E "writable|write|public|guest"testparm may not be installed separately from samba

⚙ Legacy System Detection

# Quick OS and tool availability check — paste on any target
echo "=== OS ===" && cat /etc/os-release 2>/dev/null || \
  cat /etc/redhat-release 2>/dev/null || uname -a

echo "=== Tool Availability ==="
for tool in ss netstat ip ifconfig systemctl service \
            chattr lsattr getent usermod visudo \
            tmux screen watch redis-cli; do
  which $tool 2>/dev/null && echo "  [OK] $tool" || echo "  [MISSING] $tool"
done

echo "=== Init System ==="
[ -d /run/systemd/system ] && echo "systemd" || \
  [ -f /sbin/upstart ] && echo "upstart" || echo "SysV init"
📤 SCP

SCP Topology Reference

You are on a Kali workstation and SSH into target servers. SCP syntax depends on direction. PULL = target → Kali. PUSH = Kali → target. Both commands run from your Kali terminal.
▼ PULL — From Target to Kali
# Syntax: scp [options] USER@TARGET_IP:/remote/path ~/local/path

# Pull single file:
scp USER@TARGET_IP:/etc/passwd ~/targets/s1_redis/backups/passwd

# Pull directory recursively:
scp -r USER@TARGET_IP:~/safe_backups/ ~/targets/s1_redis/backups/

# Pull LinPEAS results:
scp USER@TARGET_IP:/tmp/scan.txt ~/targets/s1_redis/scan.txt

# Pull shadow file for cracking:
scp USER@TARGET_IP:/etc/shadow ~/hashes/shadow_s1

# Pull suspicious image for stego analysis:
scp USER@TARGET_IP:/var/www/images/logo.jpg ~/stego/logo_s2.jpg

# Pull database file for analysis:
scp USER@TARGET_IP:/var/lib/app/app.db ~/analysis/app_s2.db

# Pull entire web app directory:
scp -r USER@TARGET_IP:/var/www/ ~/targets/s2_node/www/

# Pull with specific SSH key:
scp -i ~/.ssh/id_ed25519 USER@TARGET_IP:/etc/shadow ~/hashes/
▲ PUSH — From Kali to Target
# Syntax: scp [options] ~/local/path USER@TARGET_IP:/remote/path/

# Push LinPEAS to target:
scp ~/tools/linpeas.sh USER@TARGET_IP:/tmp/linpeas.sh

# Push custom script to target:
scp ~/tools/baseline_snapshot.sh USER@TARGET_IP:/tmp/

# Push directory to target:
scp -r ~/tools/ USER@TARGET_IP:/tmp/tools/

# Push a known-good config to restore from:
scp ~/targets/s4_smb/backups/smb.conf USER@TARGET_IP:/etc/samba/smb.conf

# Push with specific SSH key:
scp -i ~/.ssh/id_ed25519 ~/tools/linpeas.sh USER@TARGET_IP:/tmp/
After key exchange (ssh-copy-id), SCP will not prompt for password. If key push hasn't happened yet, SCP will prompt for the target user's password.

🆘 SCP Fallback — Netcat Exfil

Use when SCP is blocked, SSH port is non-standard, or permissions prevent normal transfer.

Step 1 — On Kali (listener):

# Listen for incoming file
nc -l -p 9001 > ~/analysis/received_file

# For a directory (tar over nc):
nc -l -p 9001 | tar xz -C ~/targets/s1_redis/

Step 2 — On Target (sender via SSH):

# Send file to Kali listener
cat /etc/passwd | nc KALI_IP 9001

# Send directory as tar:
tar czf - ~/safe_backups/ | nc KALI_IP 9001
📜 Scripts

Ready-to-Paste Scripts

All scripts pre-loaded before competition starts. Air-gapped — paste via clipboard. Scripts 1-3 run on the target (via SSH). Scripts 4-5 run on Kali.

🚀 Script 1 — Full Baseline Snapshot (Run on Target via SSH)

#!/bin/bash
# baseline_snapshot.sh — paste and run on each target immediately at game start
echo "=== BASELINE: $(hostname) @ $(date) ==="
echo ""
echo "[IP]"
ip a 2>/dev/null || ifconfig 2>/dev/null
echo ""
echo "[USERS]"
cat /etc/passwd
echo ""
echo "[SHADOW - first chars only for hash ID]"
sudo awk -F: '{print $1, substr($2,1,4)}' /etc/shadow 2>/dev/null
echo ""
echo "[SUDO GROUP]"
getent group sudo 2>/dev/null || grep "^sudo:\|^wheel:" /etc/group
echo ""
echo "[UID 0 ACCOUNTS]"
awk -F: '$3 == 0 {print $1}' /etc/passwd
echo ""
echo "[PORTS]"
ss -tulpn 2>/dev/null || netstat -tulpn 2>/dev/null
echo ""
echo "[SERVICES]"
systemctl list-units --type=service --state=running 2>/dev/null \
  || service --status-all 2>/dev/null || ls /etc/init.d/
echo ""
echo "[CRONTAB]"
crontab -l 2>/dev/null; sudo crontab -l 2>/dev/null
cat /etc/crontab 2>/dev/null; ls /etc/cron.d/ 2>/dev/null
echo ""
echo "[AUTHORIZED KEYS]"
find /home /root -name "authorized_keys" -exec echo "FILE: {}" \; \
  -exec cat {} \; 2>/dev/null
echo ""
echo "[SUDOERS.D]"
ls -la /etc/sudoers.d/ 2>/dev/null
cat /etc/sudoers.d/* 2>/dev/null
echo ""
echo "[CONFIGS FOUND]"
find /etc -name "*.conf" -type f 2>/dev/null | head -30
echo ""
echo "[FLAG HUNT]"
find / -name "flag*" -type f 2>/dev/null | xargs cat 2>/dev/null
grep -r "SKY-" /var/www /home /etc /tmp /opt /root 2>/dev/null
echo ""
echo "[OS VERSION]"
cat /etc/os-release 2>/dev/null || uname -a
echo ""
echo "[TOOL CHECK]"
for t in ss netstat ip ifconfig systemctl service chattr lsattr \
          getent redis-cli tmux screen watch; do
  which "$t" 2>/dev/null && echo "  OK: $t" || echo "  MISSING: $t"
done
echo "=== END BASELINE ==="

🔴 Script 2 — IOC Hunter (Run on Target via SSH)

#!/bin/bash
# ioc_hunt.sh — find Red Team activity
echo "=== IOC HUNT @ $(date) ==="
echo ""
echo "[Red Team IOC String]"
grep -rnw '/' -e 'red team wuz here' 2>/dev/null
echo ""
echo "[Files Modified Last 15 min]"
find / -type f -mmin -15 \
  ! -path "/proc/*" ! -path "/sys/*" \
  ! -path "/run/*" ! -path "/var/log/*" \
  ! -path "/tmp/scan*" 2>/dev/null
echo ""
echo "[New SUID Binaries]"
find / -perm /4000 -type f 2>/dev/null | sort
echo ""
echo "[Unexpected Listening Ports]"
ss -tulpn 2>/dev/null || netstat -tulpn
echo ""
echo "[Unauthorized Sudoers]"
ls -la /etc/sudoers.d/ 2>/dev/null
cat /etc/sudoers.d/* 2>/dev/null
echo ""
echo "[UID 0 Check]"
awk -F: '$3 == 0 {print $1}' /etc/passwd
echo ""
echo "[Crontab Check]"
crontab -l 2>/dev/null; sudo crontab -l 2>/dev/null
cat /etc/cron.d/* 2>/dev/null
echo ""
echo "[World-Writable in Sensitive Dirs]"
find /etc /var/www /opt /home -type f -perm -o+w 2>/dev/null | head -20
echo ""
echo "[New User Accounts (UID >= 1000)]"
awk -F: '$3 >= 1000 && $3 != 65534 {print $1, $3}' /etc/passwd

🔄 Script 3 — Config Diff Checker (Run on Kali)

#!/bin/bash
# diff_check.sh — Kali side. Usage: ./diff_check.sh USER TARGET_IP SERVER_NAME
# Example: ./diff_check.sh admin 10.0.0.5 s1_redis
USER="$1"; TARGET_IP="$2"; SERVER="$3"
BACKUP="$HOME/targets/$SERVER/backups"

echo "=== DIFF CHECK: $SERVER ($TARGET_IP) @ $(date) ==="
for file in passwd shadow sudoers sshd_config; do
  [ -f "$BACKUP/$file" ] || continue
  echo ""
  echo "--- /etc/$file ---"
  ssh "$USER@$TARGET_IP" "cat /etc/$file 2>/dev/null" | \
    diff --color=always "$BACKUP/$file" - && \
    echo "  [OK] No changes" || \
    echo "  [!!] TAMPERING DETECTED"
done
echo "=== END DIFF ==="

🏴 Script 4 — CTF Deep Hunt (Run on Kali After Pulling Files)

#!/bin/bash
# ctf_hunt.sh — Run on Kali against pulled files
# Usage: ./ctf_hunt.sh ~/targets/s1_redis/
SEARCH_DIR="${1:-.}"

echo "=== CTF DEEP HUNT in $SEARCH_DIR ==="
echo ""
echo "[Plaintext SKY- flags]"
grep -r "SKY-" "$SEARCH_DIR" 2>/dev/null

echo ""
echo "[Base64-encoded strings (20+ chars)]"
grep -rEo "[A-Za-z0-9+/]{24,}={0,2}" "$SEARCH_DIR" 2>/dev/null | \
  while read enc; do
    dec=$(echo "$enc" | base64 -d 2>/dev/null)
    echo "$dec" | grep -qi "SKY-\|flag" && echo "  DECODED: $enc => $dec"
  done

echo ""
echo "[Strings in binary files]"
find "$SEARCH_DIR" -type f ! -name "*.txt" ! -name "*.conf" ! -name "*.log" \
  2>/dev/null | while read f; do
  strings "$f" 2>/dev/null | grep -i "SKY-\|flag\|password\|secret" && \
    echo "  (in: $f)"
done

echo ""
echo "[Image files — run steghide manually on these]"
find "$SEARCH_DIR" -name "*.jpg" -o -name "*.png" \
  -o -name "*.bmp" -o -name "*.gif" 2>/dev/null

echo ""
echo "[Database files — run sqlite3 manually on these]"
find "$SEARCH_DIR" -name "*.db" -o -name "*.sqlite" \
  -o -name "*.sqlite3" 2>/dev/null

echo "=== END CTF HUNT ==="
⚡ Ref

Command Quick Reference

CategoryCommandPurposeLegacy Alt
Usersawk -F: '$3==0{print $1}' /etc/passwdAll root-level accounts
Usersgetent group sudoSudo group membersgrep ^sudo: /etc/group
Usersusermod -L <user>Lock/disable accountpasswd -l <user>
Usersusermod -s /usr/sbin/nologin <u>Remove shellchsh -s /bin/false <u>
Networkss -tulpnListening ports + processesnetstat -tulpn
Networklsof -i :<PORT>What owns a portfuser PORT/tcp
Networkip aInterface addressesifconfig
Networktcpdump -i any port 80 -nn -ACapture traffic (ASCII)
Filesfind / -mmin -10 -type f ! -path "/proc/*"Modified last 10 minfind / -newer /tmp/ref
Fileschattr +i <file>Immutable lockchmod 444 <file>
Fileslsattr <file>Check immutable flagTry touch file
Filesfind / -perm /4000 -type fAll SUID binaries
Filesfind / -type d -perm -o+wWorld-writable dirs
Logstail -f /var/log/auth.logLive auth attemptstail -f /var/log/secure
Servicessystemctl restart <svc>Restart serviceservice <svc> restart
Servicessystemctl status <svc>Service healthservice <svc> status
Firewalliptables -L -v -n --line-numbersAll rules
Firewalliptables-saveExport rules
IOCgrep -rnw '/' -e 'red team wuz here'Red Team string
IOCgrep -r "SKY-" /var /home /etc /tmpFlag search
CTFstrings file | grep -i SKYStrings in binary
CTFecho "..." | base64 -dDecode base64
CTFfile <filename>Identify file type
CTFxxd file | head -30Hex dumphexdump -C file | head
CTFbinwalk fileEmbedded files
CTFsteghide extract -sf img.jpgExtract stego
Crackunshadow passwd shadow > combinedPrep for John
Crackjohn --wordlist=rockyou.txt combinedDictionary crack
Crackjohn --show combinedShow cracked
Crackhashcat -m 1800 shadow rockyou.txtSHA-512 crack
Redisredis-cli PINGTest authtelnet localhost 6379
SMBtestparm /etc/samba/smb.confValidate configgrep -E "writable|public|guest" smb.conf
SCP Pullscp USER@IP:/remote/path ~/local/Pull file from targetnc -l listener method
SCP Pushscp ~/local/file USER@IP:/remote/Push file to targetnc sender method
🔑 SSH

SSH Config & Legacy Fallbacks

Standard Connection from Kali

# Preferred — ED25519 key
ssh -i ~/.ssh/id_ed25519 USER@TARGET_IP

# RSA fallback for older SSH daemon
ssh -i ~/.ssh/id_rsa_legacy USER@TARGET_IP

# Run command inline (no interactive shell)
ssh USER@TARGET_IP "cat /etc/passwd"

# Persistent multiplexed connection (faster repeated commands)
ssh -o ControlMaster=auto \
    -o ControlPath=~/.ssh/ctrl_%r@%h:%p \
    -o ControlPersist=10m \
    USER@TARGET_IP

# Non-standard port:
ssh -p 2222 USER@TARGET_IP

Break-Glass ~/.ssh/config (Legacy Systems)

# Add to ~/.ssh/config on Kali for old hardware
Host s1_legacy
  HostName TARGET_IP
  User USER
  IdentityFile ~/.ssh/id_rsa_legacy
  KexAlgorithms +diffie-hellman-group14-sha1,diffie-hellman-group1-sha1
  Ciphers +aes128-cbc,aes256-cbc,3des-cbc
  HostKeyAlgorithms +ssh-rsa
  PubkeyAcceptedKeyTypes +ssh-rsa
  MACs +hmac-sha1,hmac-sha1-96,hmac-md5
  StrictHostKeyChecking no

# Then: ssh s1_legacy
# And:  scp ~/file s1_legacy:/tmp/

SSH Hardening on Targets

Test your changes in a NEW terminal before closing the current session. Lock yourself out = lose access. Always keep sky_scorebot in AllowUsers.
sudo nano /etc/ssh/sshd_config

# Whitelist only necessary users:
AllowUsers <your_user> sky_scorebot

# No root password login:
PermitRootLogin prohibit-password

# Slow brute force:
MaxAuthTries 3
LoginGraceTime 30

# Validate then restart (CRITICAL — test in new terminal first):
sudo sshd -t && sudo systemctl restart sshd 2>/dev/null || \
  sudo sshd -t && sudo service sshd restart

# Lock config after hardening:
sudo chattr +i /etc/ssh/sshd_config
💀 2023

2023 Failures — Never Again

Pre-game
❌ Team still getting settled during Opening Remarks
Missed key briefing info. Fix: Arrive 30+ min early. Seated, fed, terminals open before check-in.
12:00 – 12:15
❌ Passwords changed before flags captured
Locked the team out of System Admin CTF flags requiring original creds. Fix: Coordinator calls all-clear. No one touches credentials for 15 minutes.
12:15
❌ All flag.txt files overwritten — "red team wuz here"
By the time team settled in, Red Team had already replaced every flag. Fix: Flag Hunter starts grep within 60 seconds of game start. Assume flags may already be gone — check for encoding and stego immediately.
12:30 – 13:00
⚠ Eating lunch during competition
Hectic/panic mode while trying to eat and defend simultaneously. Fix: Eat before the Zoom call.
Full duration
❌ Redis never remediated — stayed red the whole match
Research happened but no fix applied. Became a persistent Red Team entry point. Fix: Redis King follows the Auth → Bind → Rename → Permissions → Verify tab sequence without deviation.
Full duration
⚠ No coordinator — doubled efforts, no priority order
Fix: Coordinator role assigned before noon. First duty: hold the team on no-password for 15 min. Second: call all-clear and dictate priority order.
Full duration
⚠ Google Sheet became disorganized
Copy-paste created huge cells. Fix: Pre-structure 8 tabs before competition. One person owns the sheet structure.

✅ What Worked — Keep Doing These

  • King of the Hill on SMB: Stayed green the entire competition once someone was fully dedicated to it. Apply this model to all 4 servers.
  • LinPEAS: Worked well. Pre-paste it, deploy within the first 5 minutes of the hardening phase.
  • getent group sudo + iptables -L: Called out as two of the most useful commands in the debrief. Both go in a TMux monitoring pane.
  • Disabling accounts (usermod -L) vs deleting: Preserves hashes for rollback. Always the right move over deletion.
  • Google Sheet coordination: Effective before it got messy. Pre-structure solves this.