Install
$ agentstack add skill-thapr0digy-skills-enum-network ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ● Filesystem access Used
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
@pentest-core/skills/shared/engagement-resolver.md @pentest-core/skills/shared/scope-validator.md @pentest-core/skills/shared/activity-log.md @pentest-core/skills/shared/remote-exec.md
/enum-network — Network Service Security Assessment
You are conducting systematic security assessment of network services discovered during infrastructure analysis. You analyze service configurations, validate security controls, and assess compliance with security standards. Each service type follows established security assessment methodologies to identify misconfigurations, security gaps, and compliance issues.
Step 1: Resolve Active Engagement
Run the engagement resolver block verbatim before any other logic.
# --- Engagement Resolver ---
ENGAGEMENT_JSON=$(cat ~/.pentest/active-engagement 2>/dev/null)
if [ -z "$ENGAGEMENT_JSON" ]; then
echo "No active engagement found. Run /pentest-init or /pentest-switch." >&2
exit 1
fi
if [ ! -f "$ENGAGEMENT_JSON" ]; then
echo "Active engagement path '$ENGAGEMENT_JSON' does not exist. Run /pentest-switch." >&2
exit 1
fi
ENGAGEMENT_ID=$(jq -r '.engagement_id' "$ENGAGEMENT_JSON")
OUTPUT_DIR=$(jq -r '.output_dir' "$ENGAGEMENT_JSON")
ASSESSOR=$(whoami)
ASSESSOR_MATCH=$(jq -r --arg h "$ASSESSOR" '.security assessors[] | select(.handle == $h) | .handle' "$ENGAGEMENT_JSON")
if [ -z "$ASSESSOR_MATCH" ]; then
echo "Operator '$ASSESSOR' is not registered on this engagement." >&2
exit 1
fi
# --- End Engagement Resolver ---
After this block, $ENGAGEMENT_ID, $OUTPUT_DIR, and $ASSESSOR are guaranteed set and valid. If the resolver fails for any reason, stop and tell the user to run /pentest-init.
Step 2: Parse Arguments and Determine Targets
Accepted input
- IP / hostname / CIDR — e.g.,
10.0.0.1,dc01.acme.local,10.0.0.0/24 - No argument — fall back to most recent active recon output, filtering for non-web services
ARG="${1:-}"
if [ -n "$ARG" ]; then
NETWORK_TARGETS=("$ARG")
elif ls "${OUTPUT_DIR}/recon/active-"*.json > /dev/null 2>&1; then
RECON_FILE=$(ls -t "${OUTPUT_DIR}/recon/active-"*.json | head -1)
echo "No target specified — reading network services from $RECON_FILE"
# Extract hosts with non-web, non-other service ports from the most recent active recon output
NETWORK_TARGETS_JSON=$(jq '[.hosts[] | {ip, hostnames, ports: [.ports[] | select(.category != "web" and .category != "other")]} | select(.ports | length > 0)]' "$RECON_FILE" 2>/dev/null)
if [ -z "$NETWORK_TARGETS_JSON" ] || [ "$NETWORK_TARGETS_JSON" = "[]" ]; then
echo "No network services found in $RECON_FILE. Provide a target: /enum-network " >&2
exit 1
fi
# Build target list from recon data
mapfile -t NETWORK_TARGETS &2
echo "Provide a target: /enum-network " >&2
exit 1
fi
If no recon data is available and no target was provided, ask the user:
> No active recon data found. Please provide a target: /enum-network 10.0.0.1
Group by service category
When reading from recon data, group the discovered ports by service category to drive per-service enumeration steps:
# Build a per-host service map from recon data
# Keys: smb, ldap, snmp, dns, rdp, ssh, winrm, smtp, ftp, mysql, mssql, postgresql, redis, mongodb, ipmi
if [ -n "${NETWORK_TARGETS_JSON:-}" ]; then
SERVICE_MAP=$(echo "$NETWORK_TARGETS_JSON" | jq '
[.[] | {
ip,
hostnames,
services: (.ports | group_by(.service) | map({(.[0].service): map(.port)}) | add)
}]
')
fi
Scope validation
For each target in NETWORK_TARGETS, validate against scope before proceeding. Check against scope.out_of_scope (hard block) and scope.in_scope (warn if unlisted). Validate testing window. Log every check with action: "scope_check".
for TARGET_IP in "${NETWORK_TARGETS[@]}"; do
OUT_OF_SCOPE=$(jq -r '.scope.out_of_scope[]' "$ENGAGEMENT_JSON" 2>/dev/null)
# Match target IP against out_of_scope entries — block on match
# ... (apply scope-validator.md logic in full) ...
TESTING_START=$(jq -r '.testing_window.start // empty' "$ENGAGEMENT_JSON" 2>/dev/null)
TESTING_END=$(jq -r '.testing_window.end // empty' "$ENGAGEMENT_JSON" 2>/dev/null)
NOW_EPOCH=$(date +%s)
# Block if outside testing window when window is defined
# ... (apply scope-validator.md testing window logic) ...
jq -nc \
--arg ts "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
--arg security assessor "$ASSESSOR" \
--arg action "scope_check" \
--arg target "$TARGET_IP" \
--arg status "completed" \
--arg result "in_scope" \
'{ts:$ts, security assessor:$security assessor, action:$action, target:$target, status:$status, result:$result}' \
>> "${OUTPUT_DIR}/activity.log"
done
Target selection
Present the validated target list and discovered services to the user:
> Network targets identified: > 1. 10.0.0.1 — SMB (445), LDAP (389), DNS (53) > 2. 10.0.0.5 — SMB (445), WinRM (5985) > 3. 10.0.0.10 — MySQL (3306), SSH (22) > > Enumerate which targets? (comma-separated numbers or "all")
Wait for user selection before proceeding.
mkdir -p "${OUTPUT_DIR}/enum"
mkdir -p "${OUTPUT_DIR}/findings"
ENUM_DIR="${OUTPUT_DIR}/enum"
FINDINGS_DIR="${OUTPUT_DIR}/findings"
TIMESTAMP=$(date -u +"%Y%m%dT%H%M%SZ")
Step 3: Check Tool Availability
Check each tool with which 2>/dev/null. Do not abort if an optional tool is missing — skip that step, note the gap, and continue. Log every check with action: "tool_check".
| Tool | Required | Steps | |---|---|---| | crackmapexec | Required | 4, 5, 8, 11 — SMB/LDAP/WinRM/DB enumeration | | smbclient | Required | 4 — SMB share listing | | Network Discovery | Required | 4, 6, 7, 8, 9, 10, 11, 12 — Security analysis modules across all services | | kerbrute | Optional | 5 — Kerberos user enumeration | | bloodhound-python | Optional | 5 — BloodHound AD collection | | onesixtyone | Optional | 6 — SNMP community brute-force | | snmpwalk | Optional | 6 — SNMP MIB walking | | smtp-user-enum | Optional | 9 — SMTP user enumeration | | ipmitool | Optional | 12 — IPMI hash dumping |
If all three required tools (crackmapexec, smbclient, and the network discovery tool) are missing, abort:
> Required tools crackmapexec, smbclient, and network discovery tool are not available. Install them and retry.
declare -A TOOL_AVAILABLE
for TOOL in crackmapexec smbclient nmap kerbrute bloodhound-python onesixtyone snmpwalk smtp-user-enum ipmitool; do
if which "$TOOL" > /dev/null 2>&1; then
TOOL_AVAILABLE[$TOOL]=true
STATUS="available"
else
TOOL_AVAILABLE[$TOOL]=false
STATUS="not found"
fi
jq -nc \
--arg ts "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
--arg security assessor "$ASSESSOR" \
--arg action "tool_check" \
--arg command "TOOL_AVAILABLE[$TOOL]=false" \
--arg status "completed" \
--arg result "$STATUS" \
'{ts:$ts, security assessor:$security assessor, action:$action, command:$command, status:$status, result:$result}' \
>> "${OUTPUT_DIR}/activity.log"
done
REQUIRED_MISSING=0
for TOOL in crackmapexec smbclient nmap; do
[ "${TOOL_AVAILABLE[$TOOL]}" = "false" ] && REQUIRED_MISSING=$((REQUIRED_MISSING + 1))
done
if [ "$REQUIRED_MISSING" -eq 3 ]; then
echo "Required tools crackmapexec, smbclient, and network discovery tool are not available. Install them and retry." >&2
exit 1
fi
Step 4: SMB Enumeration (Port 445/139)
Skip this step for any host without port 445 or 139 open. For each SMB host:
Null session and share enumeration
for TARGET_IP in "${SMB_HOSTS[@]}"; do
TARGET_SLUG=$(echo "$TARGET_IP" | tr '.' '_')
# Null session share listing via crackmapexec
CME_SHARES_OUT="${ENUM_DIR}/cme-smb-shares-${TARGET_SLUG}-${ASSESSOR}-$(date +%s).txt"
CMD="crackmapexec smb ${TARGET_IP} -u '' -p '' --shares"
jq -nc \
--arg ts "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
--arg security assessor "$ASSESSOR" \
--arg action "enum" \
--arg target "$TARGET_IP" \
--arg command "$CMD" \
--arg status "dispatched" \
'{ts:$ts, security assessor:$security assessor, action:$action, target:$target, command:$command, status:$status}' \
>> "${OUTPUT_DIR}/activity.log"
$CMD | tee "$CME_SHARES_OUT"
jq -nc \
--arg ts "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
--arg security assessor "$ASSESSOR" \
--arg action "enum" \
--arg target "$TARGET_IP" \
--arg command "$CMD | tee \"$CME_SHARES_OUT\"" \
--arg status "$([ $? -eq 0 ] && echo completed || echo failed)" \
--arg output_file "$CME_SHARES_OUT" \
'{ts:$ts, security assessor:$security assessor, action:$action, target:$target, command:$command, status:$status, output_file:$output_file}' \
>> "${OUTPUT_DIR}/activity.log"
# Null session share listing via smbclient
SMB_LIST_OUT="${ENUM_DIR}/smbclient-list-${TARGET_SLUG}-${ASSESSOR}-$(date +%s).txt"
smbclient -L "//${TARGET_IP}" -N 2>&1 | tee "$SMB_LIST_OUT"
Signing check and relay list generation
CME_SIGNING_OUT="${ENUM_DIR}/cme-smb-signing-${TARGET_SLUG}-${ASSESSOR}-$(date +%s).txt"
RELAY_LIST="${ENUM_DIR}/smb-relay-candidates-${ASSESSOR}-$(date +%s).txt"
crackmapexec smb "${TARGET_IP}" --gen-relay-list "$RELAY_LIST" 2>&1 | tee "$CME_SIGNING_OUT"
User and RID enumeration
CME_USERS_OUT="${ENUM_DIR}/cme-smb-users-${TARGET_SLUG}-${ASSESSOR}-$(date +%s).txt"
crackmapexec smb "${TARGET_IP}" -u '' -p '' --users --rid-brute 2>&1 | tee "$CME_USERS_OUT"
NSE scripts
NSE_SMB_OUT="${ENUM_DIR}/smb-security-${TARGET_SLUG}-${ASSESSOR}-$(date +%s).xml"
CMD="nmap -p 445 --script smb-enum-shares,smb-enum-users,smb-os-discovery,smb-security-mode -oX ${NSE_SMB_OUT} ${TARGET_IP}"
jq -nc \
--arg ts "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
--arg security assessor "$ASSESSOR" \
--arg action "enum" \
--arg target "$TARGET_IP" \
--arg command "$CMD" \
--arg status "dispatched" \
'{ts:$ts, security assessor:$security assessor, action:$action, target:$target, command:$command, status:$status}' \
>> "${OUTPUT_DIR}/activity.log"
$CMD
jq -nc \
--arg ts "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
--arg security assessor "$ASSESSOR" \
--arg action "enum" \
--arg target "$TARGET_IP" \
--arg command "$CMD" \
--arg status "$([ $? -eq 0 ] && echo completed || echo failed)" \
--arg output_file "$NSE_SMB_OUT" \
'{ts:$ts, security assessor:$security assessor, action:$action, target:$target, command:$command, status:$status, output_file:$output_file}' \
>> "${OUTPUT_DIR}/activity.log"
done
SMB findings to flag
- Null session access — anonymous login accepted (medium/high depending on shares readable)
- Anonymous share access — readable shares with no credentials (high if sensitive data present)
- SMB signing disabled — relay attacks possible (medium; high if combined with null sessions)
- Guest access enabled — guest account active on the host (medium)
Write a finding for each confirmed condition using the standard finding schema (see Step 13).
Step 5: LDAP/AD Enumeration (Port 389/636)
Skip this step for any host without port 389 or 636 open.
Anonymous bind check
for TARGET_IP in "${LDAP_HOSTS[@]}"; do
TARGET_SLUG=$(echo "$TARGET_IP" | tr '.' '_')
# Probe for namingContexts via anonymous bind
LDAP_NC_OUT="${ENUM_DIR}/ldap-namingcontexts-${TARGET_SLUG}-${ASSESSOR}-$(date +%s).txt"
CMD="ldapsearch -x -H ldap://${TARGET_IP} -b '' -s base namingContexts"
jq -nc \
--arg ts "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
--arg security assessor "$ASSESSOR" \
--arg action "enum" \
--arg target "$TARGET_IP" \
--arg command "$CMD" \
--arg status "dispatched" \
'{ts:$ts, security assessor:$security assessor, action:$action, target:$target, command:$command, status:$status}' \
>> "${OUTPUT_DIR}/activity.log"
$CMD 2>&1 | tee "$LDAP_NC_OUT"
jq -nc \
--arg ts "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
--arg security assessor "$ASSESSOR" \
--arg action "enum" \
--arg target "$TARGET_IP" \
--arg command "$CMD 2>&1 | tee \"$LDAP_NC_OUT\"" \
--arg status "$([ $? -eq 0 ] && echo completed || echo failed)" \
--arg output_file "$LDAP_NC_OUT" \
'{ts:$ts, security assessor:$security assessor, action:$action, target:$target, command:$command, status:$status, output_file:$output_file}' \
>> "${OUTPUT_DIR}/activity.log"
# Extract base DN from namingContexts output
BASE_DN=$(grep -i 'namingContexts:' "$LDAP_NC_OUT" | head -1 | awk '{print $2}')
if [ -n "$BASE_DN" ]; then
# Retrieve domain object anonymously
LDAP_DOMAIN_OUT="${ENUM_DIR}/ldap-domain-${TARGET_SLUG}-${ASSESSOR}-$(date +%s).txt"
ldapsearch -x -H "ldap://${TARGET_IP}" -b "$BASE_DN" "(objectClass=domain)" 2>&1 | tee "$LDAP_DOMAIN_OUT"
ANON_BIND_SUCCESS=true
else
ANON_BIND_SUCCESS=false
fi
Authenticated enumeration
If credentials are available (check $LDAP_USER / $LDAP_PASS or $DOMAIN_USER / $DOMAIN_PASS):
if [ -n "${DOMAIN_USER:-}" ] && [ -n "${DOMAIN_PASS:-}" ] && [ -n "${BASE_DN:-}" ]; then
# Users with crackmapexec
crackmapexec ldap "${TARGET_IP}" -u "$DOMAIN_USER" -p "$DOMAIN_PASS" --users \
2>&1 | tee "${ENUM_DIR}/cme-ldap-users-${TARGET_SLUG}-${ASSESSOR}-$(date +%s).txt"
# Groups
crackmapexec ldap "${TARGET_IP}" -u "$DOMAIN_USER" -p "$DOMAIN_PASS" --groups \
2>&1 | tee "${ENUM_DIR}/cme-ldap-groups-${TARGET_SLUG}-${ASSESSOR}-$(date +%s).txt"
# Kerberoastable accounts
crackmapexec ldap "${TARGET_IP}" -u "$DOMAIN_USER" -p "$DOMAIN_PASS" --kerberoasting \
"${ENUM_DIR}/kerberoast-hashes-${TARGET_SLUG}-${ASSESSOR}-$(date +%s).txt"
# AS-REP roastable accounts (no pre-auth required)
crackmapexec ldap "${TARGET_IP}" -u "$DOMAIN_USER" -p "$DOMAIN_PASS" --asreproast \
"${ENUM_DIR}/asrep-hashes-${TARGET_SLUG}-${ASSESSOR}-$(date +%s).txt"
fi
BloodHound collection (async)
If bloodhound-python is available and domain credentials exist, dispatch collection asynchronously via the remote-exec pattern:
if [ "${TOOL_AVAILABLE[bloodhound-python]}" = "true" ] && [ -n "${DOMAIN_USER:-}" ]; then
# Resolve remote host connection config
HOST_NAME="${HOST_NAME:-kali-aws}"
METHOD=$(jq -r --arg h "$HOST_NAME" '.remote_hosts[$h].method' "$ENGAGEMENT_JSON")
WORK_DIR=$(jq -r --arg h "$HOST_NAME" '.remote_hosts[$h].work_dir' "$ENGAGEMENT_JSON")
SSH_HOST=$(jq -r --arg h "$HOST_NAME" '.remote_hosts[$h].host // empty' "$ENGAGEMENT_JSON")
SSH_KEY=$(jq -r --arg h "$HOST_NAME" '.remote_hosts[$h].key // empty' "$ENGAGEMENT_JSON")
SSH_USER=$(jq -r --arg h "$HOST_NAME" '.remote_hosts[$h].user // "kali"' "$ENGAGEMENT_JSON")
SESSION_NAME="${ENGAGEMENT_ID}-bloodhound-$(date +%s)"
BH_OUTPUT_DIR="${WORK_DIR}/bloodhound-${ASSESSOR}"
REMOTE_OUTPUT="${WORK_DIR}/${SESSION_NAME}.log"
BH_CMD="mkdir -p ${BH_OUTPUT_DIR} && bloodhound-python -d ${DOMAIN} -u ${DOMAIN_USER} -p ${DOMAIN_PASS} -c All -ns ${TARGET_IP} --zip -o ${BH_OUTPUT_DIR}"
if [ "$METHOD" = "local" ] || [ "$HOST_NAME" = "local" ]; then
tmux new-session -d -s "$SESSION_NAME" "bash -c '${BH_CMD} > ${REMOTE_OUTPUT} 2>&1'"
else
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${SSH_USER}@${SSH_HOST}" \
"tmux new-session -d -s '${SESSION_NAME}' '${BH_CMD} > ${REMOTE_OUTPUT} 2>&1'"
fi
jq -nc \
--arg ts "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
--arg security assessor "$ASSESSOR" \
--arg action "enum" \
--arg target "$TARGET_IP" \
--arg command "$BH_CMD" \
--arg status "dispatched" \
--arg session "$SESSION_NAME" \
--arg remote_host "$HOST_NAME" \
--arg output_path "$REMOTE_OUTPUT" \
'{ts:$ts, security assessor:$security assessor, action:$action, target:$target, command:$command, stat
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [thapr0digy](https://github.com/thapr0digy)
- **Source:** [thapr0digy/skills](https://github.com/thapr0digy/skills)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.