Umair Sabir
← Back to blog
Dec 4, 2025·Penetration Testing, Methodology, Pentest

How I Approach Real-World Pentests (Methodology + Toolkit)

My personal methodology for external + internal pentests — the phases, the tools per phase, the documentation cadence, and a worked example walking from recon to admin-panel takeover.

By Umair Sabir

Penetration testing isn't just running tools. It's the order in which you run them, the notes you take while doing it, and what you do between the tool runs that separates a useful report from a glorified Nessus dump.

This is my actual workflow for a typical 5-day external + internal engagement. It's PTES-flavoured but trimmed to fit how I actually work in practice.

The phases (my version)

I keep them strictly sequential per target but parallel across targets. While my Nmap is sweeping the /22, I'm reading the org's job postings to map their tech stack.

Phase 1 — Recon (OSINT)

The cheapest hour you'll spend on the engagement.

The single most valuable signal in this phase: what's the user format? firstname.lastname@, flastname@, f.lastname@. Pin that and password sprays are 5x more effective.

Phase 2 — Mapping

Now I have a target list. Time to find what's alive and what services it speaks.

# fast TCP discovery on a /22  90 seconds
masscan 10.20.0.0/22 -p1-65535 --rate=20000 -oG masscan.gpn

# convert masscan output  nmap-friendly target list
awk '/Host:/ {print $2}' masscan.gpn | sort -u > alive.txt

# detailed enum on what masscan found
nmap -sV -sC -p- -Pn -iL alive.txt --open -oA nmap/full

Tip from too many engagements: the --open flag on Nmap is the difference between a scan I can read and a 14-megabyte file of closed lines I cannot.

Phase 3 — Enumeration (where the actual work happens)

Per-service enumeration is what separates a junior tester from a senior one. The mindset is "what does this service let me do without authenticating, and what does it let me do with credentials I haven't proven yet?"

open serviceunauth → version → CVEguest creds / null sessionspray reused credsPoC + manual confirmenumerate as low-privlog valid pair → next phase
Fig. 1.Per-service decision tree. The same questions, applied to every open port.

A non-exhaustive list of my favourite per-service enumerators:

Phase 4 — Exploitation (with manual validation)

The single rule: never blindly run a Metasploit module against a production target. I read the module source first. Yes, every time. If a module says target: linux/http/some_app, I want to know it isn't going to overwrite a config file.

For commodity vulns I keep a small folder of hand-curated PoCs that I trust:

# personal exploit collection  read before running
$ tree ~/exploits/
~/exploits/
├── log4shell/
   ├── README.md    my notes on what works
   ├── jndi.py      my fork, no callback to attacker.com
   └── payloads/
├── confluence/
└── ...

🔐 If you can't explain to a customer what the exploit does to their server, don't run it. Refusing is professional. Crashing prod is not.

Phase 5 — Post-exploitation

This is where most of my time goes on internal engagements. Phase 4 might take 30 minutes. Phase 5 takes three days. Almost all of it is data collection and pivot mapping — I covered the AD-specific version of this in Active Directory Attack Paths.

The minimum I want before I leave a host:

  • Local hashes (secretsdump.py -local)
  • Recently logged-on users (LSASS dump, Mimikatz)
  • Cached domain credentials (DCC2 hashes from registry)
  • Schtasks / cron / WMI subscriptions (for persistence audit)
  • Network reachability map (nmap from this host's perspective)

Phase 6 — Reporting

A finding is not a finding until it has:

  1. Evidence — screenshot or HTTP transcript with timestamps.
  2. Reproduction — exact command, exact payload.
  3. Impact — what does an attacker do with this? Don't write "information disclosure"; write "attacker reads /etc/passwd enabling username enumeration for password spraying".
  4. Remediation — concrete, vendor-specific fix.

I write reports in Markdown, render to PDF via Pandoc + a custom Lua template. Sample header per finding:

Listing — finding-template.md

A worked example: bypassing auth by guessing the URL

This is a sanitised version of a real engagement. A web app behind Nginx; the login page worked exactly as you'd expect. I noticed in the JS bundle a reference to /admin/dashboard.php — but the link was hidden behind a role === 'admin' check.

What if there was no server-side check? I just typed it in:

Listing — curl@kali

That's it. No exploit. No payload. Just a missing requireAuth() middleware on a route the developer assumed nobody would type.

The lesson isn't "old apps are bad". The lesson is server-side authorization is non-negotiable and the URL is the most accessible attack surface in your application.

TL;DR

  • Phase order matters more than tool choice. Most beginners skip recon and pay for it in phase 4.
  • Manual validate everything. Tools lie. Reports based on tool output without manual confirm get junior testers fired.
  • Documentation is the deliverable. The shell is for you. The report is for the customer.
  • Stay legal. Scope, scope, scope.

If you want the offensive flavour applied to AD specifically, my Active Directory Attack Paths post is the natural next read. Common beginner traps live in Top 10 Pentesting Mistakes.

For more posts like this, visit the blog or subscribe to the RSS feed.