Chapter 12 — Penetration Testing & Vulnerability Assessment
Overview
An overlooked vulnerability doesn't disappear on its own — it just sits there waiting for someone to find it first, and the only question that matters is who gets there first, your team or the attacker. That's why this chapter matters: it covers the two ways of proactively hunting for weaknesses in a system (web, server, network) before they turn into a data leak, since a single overlooked vulnerability is enough to expose all of a user's data.
The chapter opens by drawing a clean line between vulnerability scanning — automatically matching a system's fingerprint against a database of known vulnerabilities, broad and shallow, detecting rather than exploiting — and penetration testing, where a human simulates a real attack and actually exploits what's found to demonstrate impact, narrower and deeper but far more dependent on human judgment. From there it follows the standard OWASP web testing process (WSTG): recon, mapping, testing, exploit, report — then digs into the mechanics of three flagship tools. Burp Suite is an intercepting proxy sitting between browser and server, letting you view and edit every request and response; Acunetix is a commercial DAST scanner that crawls, audits, and reports automatically, complementing rather than replacing Burp; Nmap maps a network down to host discovery, port scanning, and version/OS detection. The chapter closes on scope and legality — the boundary you're authorized to test has to be spelled out in writing, because running outside scope is unauthorized access no matter how good the intent.
The throughline: understand the mechanics down to individual packets, and have a real runnable command for every tool, not just theory.
12.1. Distinguishing Vulnerability Scanning from Penetration Testing
12.1.1. Definition and Nature
Vulnerability Scanning is the process of automatically detecting known vulnerabilities on a system by matching signatures/fingerprints against a vulnerability database such as NVD (National Vulnerability Database), a CVE feed, or a vendor's own database. Its nature is breadth-first (broad and shallow): it scans many targets and checks many signatures, but does NOT exploit.
Penetration Testing is the process of simulating a deliberate attack, in which a person (or person + tools) attempts to actually exploit vulnerabilities to demonstrate impact, the kill chain, and the level of access achieved. Its nature is depth-first (deep and narrow): it focuses on a specific target, chains vulnerabilities, and leverages business logic.
12.1.2. Detailed Comparison Table
| Criterion | Vulnerability Scanning | Penetration Testing |
|---|---|---|
| Goal | Enumerate known vulnerabilities | Prove exploitability + impact |
| Level of automation | High (almost fully automated) | Manual + tool-assisted |
| Detecting logic flaws (IDOR, business logic) | Almost never | Yes (a key strength) |
| Real-world exploitation | No (detection only) | Yes (PoC, exploit) |
| Proving false positives | Difficult | Yes (manual confirmation) |
| Frequency | Continuous/periodic (weekly, daily in CI) | Periodic (quarterly/yearly) or event-driven |
| Cost | Low (license + automation) | High (experts + time) |
| Output | List of CVEs + severity | Kill chain report + evidence |
| Risk of harming the system | Low (a safe mode exists) | Higher (can cause downtime) |
| Scope | All assets | Focused per agreement |
| Reference standards | CVSS, CVE, CWE | OWASP, PTES, OSSTMM, NIST SP 800-115 |
12.1.3. Why Both Are Needed
Vulnerability scanning answers the question "how many suspicious doors are there?" while a pentest answers "how far can an attacker really get in, and what can they do?". A scanner reports "port 443 runs OpenSSL with CVE-2014-0160 (Heartbleed)" — but only a pentest can prove that a private key can actually be pulled out of RAM. In the DevSecOps model, scanning is embedded into the pipeline (shift-left, continuous), while a pentest is a periodic, deeper validation. Scanning is a necessary condition (fast, cheap, broad) but not sufficient; a pentest provides the exploitation context and the real risk level needed to prioritize remediation.
12.2. The OWASP Web Testing Process
OWASP WSTG (Web Security Testing Guide) divides the process into phases. Below is the real-world lifecycle as applied to a web pentest:
+-------------+ +-------------+ +-------------+ +--------------+ +-------------+
| 1. Recon | -> | 2. Mapping | -> | 3. Testing | -> | 4. Exploit | -> | 5. Report |
| (gathering) | | (mapping) | | (testing) | | (exploit) | | (reporting) |
+-------------+ +-------------+ +-------------+ +--------------+ +-------------+
^ |
|__________________________ (repeat when a new surface is found) _________|
12.2.1. Phase 1 — Reconnaissance (information gathering)
Divided into passive (no contact with the target) and active (sending packets to the target).
Passive recon — generates no traffic to the target, using public sources:
- DNS records, certificate transparency logs (crt.sh) to enumerate subdomains.
- WHOIS, Shodan, Censys, Google dorking (site:, inurl:, filetype:).
- Finding leaked secrets on GitHub, the Wayback Machine (archive.org).
Example of enumerating subdomains via Certificate Transparency:
# Query crt.sh, filter for unique subdomains
curl -s 'https://crt.sh/?q=%25.example.com&output=json' \
| jq -r '.[].name_value' \
| sed 's/\*\.//g' | sort -u
Active recon — port scanning (Nmap, section 12.5), banner grabbing, technology fingerprinting:
# Identify web technology (Server header, X-Powered-By, cookies)
curl -sI https://example.com | grep -iE 'server|x-powered-by|set-cookie'
whatweb https://example.com
12.2.2. Phase 2 — Mapping (application mapping)
Goal: build a complete site map of endpoints, parameters, and technologies. This is when Burp Suite Proxy + the Target site map come into play (section 12.3.4). Techniques:
- Spidering/Crawling: automatically following links.
- Forced browsing / content discovery: guessing hidden paths (/admin, /.git/, /backup.zip) using ffuf, gobuster, dirsearch.
- Analyzing robots.txt, sitemap.xml, and JS bundles to find API endpoints.
# Content discovery with ffuf
ffuf -u https://example.com/FUZZ -w /usr/share/wordlists/dirb/common.txt \
-mc 200,204,301,302,307,401,403 -fs 0
# -u : URL with the keyword FUZZ replaced by each line of the wordlist
# -w : wordlist
# -mc: match HTTP status code (only show responses with this code)
# -fs: filter by response size (drop junk pages of the same size)
12.2.3. Phase 3 — Testing (vulnerability testing)
Cross-referenced with the OWASP Top 10 and WSTG. Each group of tests has a checklist: - Injection (SQLi, NoSQLi, command injection, LDAPi). - Broken Access Control (IDOR, privilege escalation, path traversal). - Authentication & Session (brute force, session fixation, weak token entropy). - XSS (reflected, stored, DOM-based). - SSRF, XXE, deserialization, business logic.
12.2.4. Phase 4 — Exploitation
Demonstrate impact: data exfiltration, privilege escalation, RCE. Stay strictly within scope — see 12.6. Record requests/responses as evidence. Avoid destructive actions unless the RoE permits.
12.2.5. Phase 5 — Reporting
The report includes: an Executive Summary (for leadership) and detailed technical content (for engineers), with each finding containing: a description, severity (CVSS), evidence (PoC, screenshot, request/response), impact, and specific remediation recommendations.
CVSS v3.1 — Base Score vector (essential for scoring severity):
| Metric | Symbol | Values | Meaning |
|---|---|---|---|
| Attack Vector | AV | N/A/L/P | Network/Adjacent/Local/Physical |
| Attack Complexity | AC | L/H | Low/High |
| Privileges Required | PR | N/L/H | None/Low/High |
| User Interaction | UI | N/R | None/Required |
| Scope | S | U/C | Unchanged/Changed |
| Confidentiality | C | N/L/H | None/Low/High |
| Integrity | I | N/L/H | None/Low/High |
| Availability | A | N/L/H | None/Low/High |
Example vector string: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 9.8 (Critical) — typical of unauthenticated RCE over the network.
CVSS 4.0 (worth knowing): FIRST has released CVSS v4.0 (strings start with
CVSS:4.0/...), which drops theScopemetric, splits Base into Exploitability + Vulnerable/Subsequent System Impact, and adds Safety (human harm) plus Supplemental/Threat/Environmental groups. Many sources still use v3.1 in parallel, so always state the version when quoting a score. (exact version/adoption to be verified over time)Don't patch by Base score alone. The Base score only says how bad a flaw is "in theory" — not whether it's actually being exploited or even reachable in your system. In practice, prioritize patching by the combination of: (a) reachability — is the vulnerable component actually on the execution path / exposed externally (a lib present in the image but never called is low risk); (b) EPSS (Exploit Prediction Scoring System by FIRST) — probability of exploitation in the next 30 days; (c) CISA KEV (Known Exploited Vulnerabilities) — the catalog of CVEs actively exploited in the wild, which almost always jump the queue. A CVE 9.8 that isn't on a code path and isn't in KEV usually ranks below a CVE 7.5 already in KEV with a high EPSS.
12.3. Burp Suite — Detailed Analysis
Burp Suite (PortSwigger) is the flagship tool for manual web testing. There are three editions: Community (free, limited, Intruder is throttled, no Scanner), Professional (full Scanner, full Intruder), and Enterprise (CI/CD, large-scale automated scanning). Burp is written in Java and runs on the JVM.
12.3.1. The MITM Proxy Architecture
Burp acts as an intercepting proxy (man-in-the-middle) placed between the browser and the server. Data flow:
+---------+ +------------------+ +-----------+
| Browser | <----> | Burp Proxy | <----> | Server |
| (client)| HTTP/S | 127.0.0.1:8080 | HTTP/S | (target) |
+---------+ +------------------+ +-----------+
| - Intercept |
| - Re-issue cert |
| - Log site map |
+------------------+
By default Burp listens on 127.0.0.1:8080. The browser is configured to point its proxy here. Burp receives the request, can pause it (intercept), edit it, and then forward it to the server. When the server responds, Burp can again edit the response before delivering it to the browser.
Why a CA certificate must be installed
With plaintext HTTP, MITM is easy — Burp reads it directly. But with HTTPS, TLS encrypts everything. To be able to read/edit, Burp must terminate TLS on its side: it performs two separate TLS sessions — one with the browser, one with the server.
When the browser sends CONNECT example.com:443, Burp:
1. Generates, on-the-fly, a certificate for example.com, signed by Burp's root CA (named PortSwigger CA by default).
2. Presents this fake certificate to the browser.
3. The browser checks the chain of trust. If Burp's root CA has been installed into the OS/browser trust store → valid → no warning. If not installed → the warning NET::ERR_CERT_AUTHORITY_INVALID appears.
This is precisely why Burp's CA cert must be installed: so the browser trusts the certs Burp generates, allowing HTTPS MITM without warnings, while keeping the TLS session with the server genuinely valid.
Obtaining and installing the CA cert:
1. Configure the browser proxy to 127.0.0.1:8080
2. Visit http://burp (or http://burpsuite)
3. Click "CA Certificate" -> download the cacert.der file
4. Install into the trust store:
- Linux: convert DER->PEM then place it in /usr/local/share/ca-certificates/
- Firefox: Settings -> Privacy & Security -> Certificates -> Import
(check "Trust to identify websites")
# Convert DER to PEM and view the cert contents
openssl x509 -inform der -in cacert.der -out burp-ca.pem
openssl x509 -in burp-ca.pem -noout -text | grep -A2 'Subject:'
# Linux system-wide:
sudo cp burp-ca.pem /usr/local/share/ca-certificates/burp.crt
sudo update-ca-certificates
Security note: Burp's CA private key is stored in the user's configuration. If this key leaks, an attacker can MITM any machine that has installed that CA. Each Burp installation generates its own CA — do NOT share it. Remove the CA from the trust store after testing on a non-dedicated machine.
12.3.2. The HTTP Request Structure that Burp Manipulates
Burp displays raw HTTP requests in exact RFC 7230 format. Understanding each part is a prerequisite for editing accurately.
POST /api/v1/login HTTP/1.1\r\n <- Request line: METHOD SP URI SP VERSION CRLF
Host: example.com\r\n <- Header
User-Agent: Mozilla/5.0\r\n
Content-Type: application/json\r\n
Content-Length: 41\r\n
Cookie: session=abc123\r\n
\r\n <- Empty line (CRLF) separating header and body
{"username":"admin","password":"secret"} <- Message body (Content-Length bytes long)
Table of each part of an HTTP/1.1 request:
| Component | Size | Meaning | Example |
|---|---|---|---|
| Method | variable (token) | HTTP verb | POST, GET |
| SP | 1 byte (0x20) | Separating space | |
| Request-URI | variable | Path + query | /api/v1/login |
| HTTP-Version | 8 bytes | Protocol version | HTTP/1.1 |
| CRLF | 2 bytes (0x0D 0x0A) | End of line | \r\n |
| Header name | token | Field name | Content-Type |
: |
2 bytes | name/value separator | : |
| Header value | variable | Value | application/json |
| Empty line | 2 bytes (CRLF) | header/body boundary | \r\n |
| Body | = Content-Length |
Payload | {...} |
Why: Content-Length MUST match the actual number of body bytes. When editing the body in Repeater, if you do not update Content-Length, the server may hang (waiting for more bytes) or truncate the body. Burp Repeater has an "Update Content-Length" option to do this automatically. The HTTP Request Smuggling vulnerability exploits exactly this inconsistency between Content-Length and Transfer-Encoding: chunked that the front-end and back-end interpret differently.
12.3.3. The Proxy Module
Functions: Intercept, HTTP history, Match & Replace, WebSockets history.
Intercept on/off: When enabled, each request is paused at Burp and displayed for you to edit. Click "Forward" to send it on, "Drop" to discard it.
Match & Replace — rules that automatically replace content on every request/response. The structure of a rule:
| Field | Meaning | Example |
|---|---|---|
| Type | Scope of application | Request header / Request body / Response header... |
| Match | Regex or literal to find | ^User-Agent:.*$ |
| Replace | Replacement string | User-Agent: CustomAgent/1.0 |
A real-world example: add X-Forwarded-For: 127.0.0.1 to every request to test bypassing an IP allowlist:
Type: Request header
Match: (leave empty - add a new header)
Replace: X-Forwarded-For: 127.0.0.1
Scope filtering: History can grow very large. It is best to limit it by scope (next section) so that only legitimate targets are logged, avoiding logging personal traffic and avoiding touching out-of-scope hosts.
12.3.4. Target — Site Map and Scope
The Site Map is a hierarchical tree of host → directory → endpoint, accumulated from every request that passes through Burp (proxy, repeater, scanner). Each node stores the requests/responses seen.
Scope defines the "legitimate targets". It is configured under Target → Scope. There are two modes: - Include in scope: a list of allowed hosts/URLs. - Exclude from scope: exclusions (e.g., a logout page, a page that signs you out of SSO).
Scope affects: whether the Proxy logs, whether the Scanner scans, and whether Intruder/Repeater warn. An advanced scope definition:
Include:
Protocol: HTTPS
Host: ^.*\.example\.com$
Port: ^443$
File: ^/api/.*$
Why (legal): Scope is a legal boundary. Scanning/exploiting outside scope = unauthorized access = breaking the law (see 12.6). The "Drop all out-of-scope traffic" option in the Proxy options prevents accidentally sending requests to out-of-scope hosts.
12.3.5. Repeater — Resending and Editing Requests
Repeater lets you send a single request multiple times with manual edits, viewing the response immediately. This is the primary tool for verifying and refining vulnerabilities. Send a request to Repeater from anywhere by right-clicking → "Send to Repeater" (Ctrl+R).
Example: testing IDOR (Insecure Direct Object Reference)
IDOR: the application allows access to another user's object simply by changing the ID, due to a missing server-side authorization check.
Original request (user A, id=1001):
GET /api/v1/orders/1001 HTTP/1.1
Host: shop.example.com
Cookie: session=eyJ1c2VyIjoiYWxpY2UifQ
Accept: application/json
Response: Alice's order (200 OK).
Test step: change 1001 → 1002 (someone else's order) and resend in Repeater:
GET /api/v1/orders/1002 HTTP/1.1
Host: shop.example.com
Cookie: session=eyJ1c2VyIjoiYWxpY2UifQ
Accept: application/json
- If the response returns another user's order (200 + data) → IDOR confirmed. Alice can view someone else's order even though the session is still Alice's.
- If it returns
403 Forbidden/404→ access control is working correctly.
Defense: enforce server-side authorization (object-level access control), checking resource.owner_id == session.user_id; hard-to-guess UUIDs help but do NOT replace authorization.
Example: testing SQL Injection
Original request:
POST /api/v1/search HTTP/1.1
Host: shop.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 10
q=keyboard
Test 1 — detecting a syntax error (error-based): insert a single quote:
q=keyboard'
If the response returns 500 with the message SQL syntax error near ''' → the backend concatenates SQL strings without parameterization → likely SQLi.
Test 2 — boolean-based: compare two conditions:
q=keyboard' AND '1'='1 -> returns normal results (TRUE)
q=keyboard' AND '1'='2 -> returns empty (FALSE)
Different behavior between TRUE/FALSE confirms injection.
Test 3 — UNION-based data extraction (after determining the column count):
q=keyboard' UNION SELECT username,password,NULL FROM users-- -
The -- - comments out the rest of the SQL. If the result returns usernames/passwords → data extraction succeeded.
Note: always update Content-Length when editing the body. URL-encode special characters when needed (' → %27, space → + or %20) depending on the Content-Type.
Defense: prepared statements / parameterized queries, a safe ORM, least privilege for the DB account, and a WAF as an additional defensive layer (not a replacement for the root fix).
12.3.6. Intruder — Automating Parameterized Attacks
Intruder sends many requests, substituting payloads into marked positions (denoted by §). There are 4 attack types:
| Attack type | Payload sets | Distribution | Number of requests | Use case |
|---|---|---|---|---|
| Sniper | 1 | One position at a time, other positions unchanged | (positions) × (payloads) | Fuzz one parameter at a time |
| Battering ram | 1 | The same payload into ALL positions at once | (payloads) | When the same value is needed in several places |
| Pitchfork | multiple (1/position) | Taken in parallel by index: payload[i] at each position | min(set lengths) | Matching username+password pairs (credential stuffing) |
| Cluster bomb | multiple (1/position) | Cartesian product: every combination | product of lengths | Brute force all combinations of user × pass |
How to mark payload positions
Burp places a pair of § characters around the value to fuzz:
POST /login HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 29
username=§admin§&password=§x§
Here there are 2 positions. With Cluster bomb + set1 (usernames) of 10 elements and set2 (passwords) of 100 elements → 1000 requests.
Example: brute-forcing login (Cluster bomb)
Attack type: Cluster bomb
Position 1 (username): payload = Simple list [admin, root, user, test]
Position 2 (password): payload = Runtime file / wordlist (trimmed rockyou)
Analyzing results: sort by the Status and Length columns. A failed login usually returns 200 + a body "Invalid credentials" of fixed length; a successful login may return 302 (redirect) or a body of a different length. Use the Length column to filter anomalies. Add a Grep-Match rule (e.g., look for the string "Welcome") to flag successful requests.
Example: parameter fuzzing (Sniper) to find SQLi/XSS
Attack type: Sniper
Position: §value§ in q=§test§
Payload set: SQLi fuzz list (', '', ", `, ;, ' OR '1'='1, <script>...)
Payload processing: URL-encode all characters
Grep - Match: "SQL syntax", "ORA-", "ODBC"
Payload types in Intruder:
| Payload type | Description | Example |
|---|---|---|
| Simple list | A static list | wordlist |
| Runtime file | Read from a large file (not loaded entirely into RAM) | rockyou.txt |
| Numbers | Generate a range of numbers | 1–10000, step 1 |
| Brute forcer | Generate character combinations | charset abc, len 4 |
| Username generator | Generate usernames from names | john.doe, jdoe |
| Bit flipper | Flip each bit (test padding oracle, CSRF token) | — |
Security & operational notes: Intruder in Community Edition is throttled (slowed down). Brute forcing generates large amounts of traffic — it can cause account lockouts, WAF blocking, or accidental DoS. Always stay in scope. Use "Resource pool" to limit concurrent requests and set a delay to avoid locking accounts.
12.3.7. Decoder
Transforms and decodes data. It supports: URL, HTML entity, Base64, ASCII hex, Octal, Binary, Gzip, and Hash (MD5, SHA...). The "Smart decode" mode automatically guesses the encoding.
Example of decoding a JWT (3 parts header.payload.signature separated by .):
Input: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiYWRtaW4ifQ.<sig>
Decode as Base64 (part 1): {"alg":"HS256"}
Decode as Base64 (part 2): {"user":"admin"}
JWT uses Base64URL (replacing +→-, /→_, dropping = padding). Burp's Base64 decoder can handle it; note that you may need to add = padding.
12.3.8. Comparer
Compares two pieces of data by word or by byte, highlighting the differences in color. Used to: compare an admin account's response vs a normal user's (finding access-control discrepancies), compare TRUE vs FALSE responses in blind SQLi, and compare two tokens to analyze their structure.
12.3.9. Sequencer — Analyzing Token Entropy
Sequencer checks the randomness of session tokens / CSRF tokens / reset tokens. A guessable token = a serious vulnerability (session hijacking).
Mechanism: collect a large number of tokens (ideally several thousand samples), then run standard FIPS 140-2 statistical tests and Burp's own tests:
| Test | What it measures |
|---|---|
| FIPS Monobit | The ratio of 0 and 1 bits is balanced |
| FIPS Poker | The distribution of 4-bit groups |
| FIPS Runs | The length of consecutive bit runs |
| FIPS Long runs | Whether there is an excessively long run |
| Character/Bit transitions | Correlation between positions |
The result is measured in bits of effective entropy at a significance level (1% by default). A "good" token needs high entropy. A token generated from time (a timestamp) or an incrementing counter → low entropy → guessable.
Note: high entropy does not guarantee safety if the generation algorithm (PRNG) is not a CSPRNG. Sequencer only detects statistical patterns; it does not inspect the source algorithm.
12.3.10. Scanner (Professional only)
Burp's Scanner includes a passive part (analyzing already-seen traffic without sending additional requests — detecting cookies missing Secure/HttpOnly, sensitive information) and an active part (sending probing payloads — detecting SQLi, XSS, SSRF...). It is configured via "Scan configuration": choosing audit checks, crawl strategy, and depth.
Warning: An active scan sends real attack payloads (which can create junk data, trigger emails, modify state). Do NOT run an active scan on sensitive production systems without explicit authorization.
12.3.11. Complete Workflow for Testing a Vulnerability with Burp
Example: testing and confirming a Reflected XSS from start to finish.
Step 1 (Recon/Mapping): Enable the Proxy and browse the application normally.
The site map accumulates the endpoint /search?q=...
Step 2 (Identifying the entry point): In the Proxy History, observe that the value of `q`
is reflected into the HTML response.
Step 3 (Send to Repeater): Ctrl+R the request /search?q=test
Step 4 (Probing): Send q=<b>test</b>.
- Inspect the response: if it returns `<b>test</b>` unescaped -> HTML reflect.
Step 5 (Proving JS execution):
q=<script>alert(document.domain)</script>
URL-encoded: q=%3Cscript%3Ealert(document.domain)%3C%2Fscript%3E
- If the response contains <script>...</script> intact (not HTML-encoded)
-> the payload will execute in the browser.
Step 6 (Verifying context): Use Comparer to compare the benign vs payload response
to pinpoint exactly where it reflects and which filter is applied.
Step 7 (Bypass the filter if any): if < is filtered, try variants:
- events: q="><img src=x onerror=alert(1)>
- escape the attribute context
Step 8 (Evidence): capture the request/response, build a complete PoC URL.
Step 9 (Report): description, CVSS, PoC, recommendations (context-aware output encoding,
CSP, set HttpOnly cookies).
XSS defense: context-aware output encoding (HTML, attribute, JS, URL), Content-Security-Policy, framework auto-escaping (React, Angular), and HttpOnly cookies so JS cannot read the session.
12.4. Acunetix — Automated DAST
12.4.1. What DAST Is and Where Acunetix Fits
DAST (Dynamic Application Security Testing) tests a running application from the outside (black-box) without needing source code — in contrast to SAST (Static, analyzing source code) and IAST (Interactive, with an agent inside the application). Acunetix is a commercial DAST scanner with a high degree of automation: it crawls, audits, and generates reports automatically.
Core differences between Acunetix and Burp:
| Criterion | Acunetix (automated DAST) | Burp Suite (manual + Scanner) |
|---|---|---|
| Model | Comprehensive automation | Mainly manual, with a Scanner (Pro) |
| Users | Run periodically, little intervention | Experts working deeply |
| Business logic / IDOR | Weak (machines struggle with logic) | Strong (humans reason) |
| Scale | Scan many sites, on a schedule | Focused on one target |
| CI/CD integration | Yes (API, scheduler) | Enterprise edition |
| Payload tuning | Limited | Maximum flexibility |
The two tools complement each other: Acunetix scans broadly to find low-hanging fruit, while Burp digs deep to verify and exploit logic.
12.4.2. Configuring a Scan
Acunetix calls a target a Target. The process for creating a scan:
1. Add Target: enter the root URL (https://app.example.com)
- Description, Business Criticality (importance level for prioritization)
2. Target Settings:
- Scan Speed: Slow/Moderate/Fast/Sequential (trade-off of speed vs server load)
- Site Login:
* Automatic (provide username/password, Acunetix logs in itself)
* Recorded login sequence (record a complex login sequence)
- Authentication: HTTP Basic/NTLM/custom header
- Custom headers / cookies (e.g., Authorization: Bearer ...)
- Excluded paths (e.g., /logout, /admin/delete-all)
- Allowed hosts (limit scope to subdomains)
3. Scan Profile (Scan Type):
- Full Scan / High Risk / Cross-site Scripting / SQL Injection /
Weak Passwords / Crawl Only
4. Schedule: run now or on a periodic schedule (cron-like)
5. Launch Scan
A Recorded login sequence is important because many endpoints lie behind authentication. Acunetix uses AcuSensor (an optional IAST agent installed on PHP/.NET/Java servers) to improve accuracy and enable deeper detection (gray-box), reducing false positives.
12.4.3. The Crawl + Audit Mechanism
Acunetix runs in 2 stages:
Stage 1 — Crawl (DeepScan): uses an engine that browses like a real browser, executing JavaScript (supporting SPAs, AJAX), discovering every link, form, API endpoint, and parameter. The result: a list of "locations" and "inputs".
Stage 2 — Audit: for each discovered input, Acunetix sends probing payloads and analyzes the response:
- SQLi: inject a payload and observe DB errors, delays (time-based: SLEEP(5)), or boolean differences.
- XSS: inject a marker script and check for unescaped reflection.
- SSRF, XXE, LFI/RFI, command injection: corresponding payloads.
- Misconfiguration: missing security headers, directory listing, backup files.
Time-based blind SQLi payload illustration:
'; IF(1=1) WAITFOR DELAY '0:0:5'-- (MSSQL)
' OR SLEEP(5)-- - (MySQL)
' || pg_sleep(5)-- (PostgreSQL)
Acunetix measures the response time: if the response is slow by ~5s when injecting SLEEP(5) but fast with SLEEP(0) → injection confirmed (blind, time-based).
12.4.4. Reading an Acunetix Report
The report classifies findings by severity: High / Medium / Low / Informational. Each finding includes:
| Report field | Meaning |
|---|---|
| Vulnerability name | The vulnerability's name (e.g., "Blind SQL Injection") |
| Severity | Level (High/Medium/Low/Info) |
| Affected URL/parameter | The affected endpoint + parameter |
| Request | The HTTP request that Acunetix sent (PoC) |
| Response / Evidence | Evidence (DB error, delay, reflection) |
| CVSS | The score |
| CWE | Weakness classification (e.g., CWE-89 SQLi) |
| Recommendation | Remediation guidance |
| Classification | OWASP, PCI DSS, ... |
The standard report templates: Developer, Executive Summary, Compliance (PCI DSS, ISO 27001, HIPAA, OWASP Top 10).
12.4.5. False Positives and Verification
Automated DAST is prone to false positives (reported but not actually exploitable) and false negatives (missed). The verification process:
1. Take the PoC request from the report.
2. Reproduce it manually using Burp Repeater or curl.
3. Observe whether the exploitation signs are correct (e.g., whether the time-based delay is consistent, or just due to the network).
4. Mark it as a false positive in Acunetix so it is skipped in the next scan.
Example of validating a time-based SQLi with curl:
# Measure the response time between SLEEP(0) and SLEEP(5)
time curl -s "https://app.example.com/item?id=1' OR SLEEP(0)-- -" >/dev/null
time curl -s "https://app.example.com/item?id=1' OR SLEEP(5)-- -" >/dev/null
# If the 2nd run is consistently ~5s slower -> a real SQLi, not a false positive
Note: an "Informational" finding (e.g., the server version leaking in a header) can be a puzzle piece for recon — do not ignore it entirely. Conversely, not every "High" is exploitable in a real context; verification is needed.
12.5. Nmap — Analysis Down to the Packet Level
Nmap (Network Mapper) is a tool for host discovery, port scanning, version/OS detection, and running scripts (NSE). Understanding Nmap requires understanding the TCP 3-way handshake and the TCP flags, because most scans operate directly on these flags.
12.5.1. Foundation — the TCP Header and Flags
The TCP segment header (RFC 793/9293). Byte-by-byte diagram:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | Res |N|C|E|U|A|P|R|S|F| |
| Offset| |S|W|C|R|C|S|S|Y|I| Window |
| | | |R|E|G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options (if Data Offset > 5) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Table of TCP header fields:
| Field | Size | Meaning | Example |
|---|---|---|---|
| Source Port | 16 bits (2 bytes) | Source port | 49152 |
| Destination Port | 16 bits | Destination port | 443 |
| Sequence Number | 32 bits (4 bytes) | Byte sequence number | 0x1A2B3C4D |
| Acknowledgment Number | 32 bits | ACK of the next expected seq number | 0x1A2B3C4E |
| Data Offset | 4 bits | Header length (in 32-bit words) | 5 = 20 bytes |
| Reserved | 3 bits | Reserved (0) | 0 |
| Flags | 9 bits | NS,CWR,ECE,URG,ACK,PSH,RST,SYN,FIN | see below |
| Window | 16 bits | Receive window size | 64240 |
| Checksum | 16 bits | Error check of header+data+pseudo-header | 0x... |
| Urgent Pointer | 16 bits | Offset of urgent data (if URG) | 0 |
| Options | 0–40 bytes | MSS, SACK, Timestamp, Window scale | MSS=1460 |
Control flags — each flag is 1 bit:
| Flag | Meaning when =1 |
|---|---|
| SYN | Request to establish a connection (synchronize seq) |
| ACK | The Acknowledgment field is valid |
| FIN | Finished sending data (graceful close) |
| RST | Reset/reject the connection immediately |
| PSH | Push data to the application immediately |
| URG | There is urgent data |
TCP 3-way handshake (connection establishment):
Client Server
| --- SYN (seq=x) ----------> | state: SYN_SENT -> server SYN_RECV
| <-- SYN,ACK (seq=y,ack=x+1) |
| --- ACK (ack=y+1) --------> | state: ESTABLISHED on both sides
Nmap exploits this behavior: a server with an open port replies with SYN/ACK, a server with a closed port replies with RST.
12.5.2. -sS — SYN scan (half-open / stealth)
Mechanism: Nmap sends a SYN but does NOT complete the handshake.
Port OPEN:
Nmap --- SYN ------------> Target
Nmap <-- SYN/ACK -------- Target
Nmap --- RST -----------> Target (Nmap actively tears down, does not send ACK)
=> conclusion: open
Port CLOSED:
Nmap --- SYN ------------> Target
Nmap <-- RST ------------ Target
=> conclusion: closed
Port FILTERED (firewall drop):
Nmap --- SYN ------------> Target
(no response, retries a few times, still silent)
=> conclusion: filtered
Why "stealth": because the handshake is not completed (it does not reach ESTABLISHED), many older applications/logs do not record the connection (they only log after accept()). However, modern IDS/IPS detect it easily. A SYN scan requires raw socket privileges (root/CAP_NET_RAW) because Nmap crafts its own TCP packets, bypassing the OS connect().
sudo nmap -sS -p 1-1000 192.168.1.10
Sample output:
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
443/tcp open https
3306/tcp filtered mysql
12.5.3. -sT — TCP Connect scan
Mechanism: Nmap calls the OS connect() syscall, completes the full 3-way handshake, then closes (sends FIN or RST).
Nmap --- SYN -------> Target
Nmap <-- SYN/ACK ---- Target
Nmap --- ACK -------> Target (fully ESTABLISHED)
Nmap --- RST/FIN ---> Target (close)
When to use: when you do not have root privileges (no raw socket). Trade-off: it creates a full connection → the application logs it → less stealthy and slower.
nmap -sT -p 22,80,443 192.168.1.10 # no sudo needed
12.5.4. -sU — UDP scan
UDP has no handshake (connectionless). The inference mechanism:
Port CLOSED:
Nmap --- UDP packet ----> Target
Nmap <-- ICMP Port Unreachable (type 3, code 3) -- Target
=> closed
Port OPEN:
Nmap --- UDP packet ----> Target
- If the UDP service returns data => open
- If there is NO response at all => open|filtered (ambiguous)
Port FILTERED:
Nmap --- UDP ----> Target
Nmap <-- ICMP type 3 code 1,2,9,10,13 (admin prohibited...) => filtered
Why UDP scans are slow: because there is no RST to definitively indicate "closed", Nmap relies on ICMP Port Unreachable; but RFC 1812 rate-limits ICMP emission, so Nmap must wait and retry → very slow. For better accuracy on common ports, Nmap sends service-specific payloads (a DNS query to 53, SNMP to 161).
sudo nmap -sU --top-ports 20 192.168.1.10 # scan the 20 most common UDP ports
12.5.5. -sN / -sF / -sX — Null, FIN, Xmas scan
These three scans exploit the RFC 793 rule: if a TCP packet arrives at a closed port WITHOUT the SYN/RST/ACK flags → the server must reply with RST; if the port is open → the server ignores it (no response).
| Scan | Flags set | Packet description |
|---|---|---|
-sN Null |
(no flags) | All flags = 0 |
-sF FIN |
FIN | Only FIN = 1 |
-sX Xmas |
FIN+PSH+URG | "Lit up like a Christmas tree" |
Port OPEN or FILTERED:
Nmap --- (FIN/Null/Xmas) ---> Target
(no response) => open|filtered
Port CLOSED:
Nmap --- (FIN/Null/Xmas) ---> Target
Nmap <-- RST ---------------- Target => closed
Why use it: it can get past stateless firewalls that only filter SYN packets. An important limitation: Windows (and many devices) do NOT comply with this standard RFC behavior — they always reply with RST regardless of whether the port is open/closed → every port shows "closed" → the scan is useless on Windows. It is only effective against RFC-compliant systems (many Unix-like systems).
sudo nmap -sX -p 1-100 192.168.1.10
12.5.6. -sn — Host Discovery (ping scan, no port scanning)
-sn (formerly -sP) only determines which hosts are "alive" without scanning ports. The default mechanism (when run with root privileges) sends a combination of probes:
| Probe | Packet sent | Purpose |
|---|---|---|
| ICMP Echo Request | ICMP type 8 | Classic ping |
| ICMP Timestamp | ICMP type 13 | Fallback when Echo is blocked |
| TCP SYN to 443 | SYN | Infer a live host from the response |
| TCP ACK to 80 | ACK | Get past firewalls that filter SYN |
| ARP request (LAN) | ARP who-has | On the same subnet — the most reliable |
On a LAN, Nmap uses ARP (layer 2) because a host is required to reply with an ARP reply if it exists — bypassing layer-3 firewalls.
sudo nmap -sn 192.168.1.0/24 # list live hosts in a /24 subnet
Output:
Nmap scan report for 192.168.1.1
Host is up (0.0012s latency).
MAC Address: AA:BB:CC:DD:EE:FF (Vendor)
Nmap scan report for 192.168.1.10
Host is up (0.0023s latency).
Nmap done: 256 IP addresses (2 hosts up) scanned in 2.34 seconds
-Pn (no ping) skips host discovery and treats every host as alive — use this when the target blocks ICMP.
12.5.7. -sV — Version detection
After identifying open ports, -sV determines the service and version running on them. Mechanism:
1. Connect to the open port.
2. If the service sends a banner itself (e.g., SSH, SMTP) → read it and match it against nmap-service-probes.
3. If it is silent → Nmap sends a series of probes (protocol-specific byte sequences) and matches the response against the thousands of regex signatures in nmap-service-probes.
--version-intensity 0-9 adjusts the number of probes sent (0 = few/fast, 9 = full).
sudo nmap -sV -p 22,80,443 192.168.1.10
Output:
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.4 (Ubuntu Linux; protocol 2.0)
80/tcp open http nginx 1.18.0 (Ubuntu)
443/tcp open ssl/http nginx 1.18.0
An accurate version → look up the corresponding CVEs (the foundation for vulnerability scanning).
12.5.8. -O — OS detection
-O guesses the operating system via TCP/IP stack fingerprinting: it sends many specially-designed probes and analyzes the response characteristics that each OS implements differently:
| Characteristic analyzed | Meaning |
|---|---|
| Initial TTL | The starting TTL (Linux 64, Windows 128, some 255) |
| TCP Window Size | The default window size |
| TCP Options & order | MSS, NOP, SACK, Timestamp, Window scale and their order |
| Don't Fragment bit | Whether DF is set |
| ISN sampling | How the Initial Sequence Number is generated |
| ICMP responses | Responses to anomalous probes |
Nmap matches the fingerprint against nmap-os-db. It needs ≥1 open port AND ≥1 closed port for accuracy.
sudo nmap -O 192.168.1.10
Output:
Running: Linux 5.X
OS CPE: cpe:/o:linux:linux_kernel:5
OS details: Linux 5.0 - 5.14
Network Distance: 2 hops
12.5.9. -p — Specifying ports, and -F
| Syntax | Meaning |
|---|---|
-p 80 |
A single port |
-p 22,80,443 |
A list |
-p 1-1000 |
A range |
-p- |
All of 1–65535 |
-p U:53,T:80 |
Separate UDP/TCP |
-F |
Fast — the 100 most common ports |
--top-ports 20 |
The 20 most common ports (per nmap-services) |
12.5.10. -T — Timing templates
-T0 through -T5 adjust speed via several parameters (delay between probes, parallelism, timeout):
| Template | Name | Characteristics | Use case |
|---|---|---|---|
-T0 |
Paranoid | Extremely slow, sequential, delays up to 5 minutes | Maximum IDS evasion |
-T1 |
Sneaky | Slow, 15s delay | IDS evasion |
-T2 |
Polite | Reduced load, little parallelism | Sensitive networks |
-T3 |
Normal | Default | Balanced |
-T4 |
Aggressive | Fast, for stable networks | Typical pentest |
-T5 |
Insane | Extremely fast, may drop packets | Fast LAN, accepting inaccuracy |
-T4 is a common choice on a good network. It can be fine-tuned in detail: --min-rate, --max-rate (packets/s), --max-retries, --host-timeout.
12.5.11. NSE — Nmap Scripting Engine
NSE runs Lua scripts to extend Nmap: vulnerability detection, brute forcing, information gathering. Scripts live in /usr/share/nmap/scripts/, organized by category:
| Category | Purpose | Intrusiveness |
|---|---|---|
safe |
Harmless, does not exploit | Low |
default (-sC) |
Run by default, useful & safe | Low |
discovery |
Gather more information | Low |
version |
Supports -sV | Low |
auth |
Authentication-related | Medium |
brute |
Brute force credentials | High (generates traffic) |
vuln |
Check for known vulnerabilities | Medium-High |
exploit |
Actually exploit | High (dangerous) |
dos |
Test for DoS | Very high (may crash the service) |
intrusive |
May cause load/harm | High |
# Run the default scripts + version
sudo nmap -sC -sV 192.168.1.10
# Scan for known vulnerabilities
sudo nmap --script vuln -p 80,443 192.168.1.10
# A specific script
sudo nmap --script http-title,http-headers -p 80 192.168.1.10
sudo nmap --script smb-vuln-ms17-010 -p 445 192.168.1.10 # detect EternalBlue
sudo nmap --script ssl-enum-ciphers -p 443 192.168.1.10 # enumerate TLS ciphers
Sample output of --script vuln:
PORT STATE SERVICE
80/tcp open http
| http-slowloris-check:
| VULNERABLE:
| Slowloris DoS attack
| State: LIKELY VULNERABLE
| IDs: CVE:CVE-2007-6750
Passing script arguments (--script-args):
sudo nmap --script http-brute --script-args \
http-brute.path=/login,userdb=users.txt,passdb=pass.txt -p 80 target
12.5.12. A Comprehensive Scan Command and Its Output
sudo nmap -sS -sV -O -p- -T4 --script default,vuln -oA fullscan 192.168.1.10
# -sS : SYN scan
# -sV : version detection
# -O : OS detection
# -p- : all 65535 ports
# -T4 : aggressive timing
# --script default,vuln : NSE
# -oA fullscan : output in 3 formats (.nmap, .gnmap, .xml) named fullscan
Output formats:
| Flag | Format | Used for |
|---|---|---|
-oN file |
Normal (like the screen) | Human reading |
-oG file |
Greppable | Quick grep/awk processing |
-oX file |
XML | Parsing by script, importing into other tools |
-oA base |
All 3 above | Complete archival |
Quick greppable analysis:
grep "open" fullscan.gnmap | awk '{print $2}' # list IPs with open ports
12.5.13. Reading Port States
Nmap distinguishes 6 states:
| State | Meaning |
|---|---|
open |
A service is listening and accepting |
closed |
The host responds but there is no service (returns RST) |
filtered |
Cannot be determined — a firewall blocks the probe |
unfiltered |
Reachable but open/closed is unclear (ACK scan) |
open\|filtered |
Cannot be distinguished (UDP, silent FIN/Null/Xmas) |
closed\|filtered |
Rare (Idle scan) |
12.6. Legal Considerations and Scope (Scope & Legal)
This is a section NOT to be taken lightly — testing outside the authorized scope is unauthorized access to a computer system and may be subject to criminal prosecution.
12.6.1. Legal Basis
- Vietnam: the Penal Code 2015 (amended and supplemented in 2017), Article 289 (unauthorized access to computer/telecommunications networks), Article 287 (obstructing/disrupting network operations); the Cybersecurity Law 2018; the Law on Network Information Security 2015. (Readers should verify the current article numbers at the time of application — the regulations may have been amended.)
- International references: the Computer Fraud and Abuse Act (CFAA — USA), the Computer Misuse Act (UK).
Port scanning or sending payloads to a system WITHOUT authorization = a violation, regardless of whether actual harm is caused.
12.6.2. Mandatory Documents Before Testing
| Document | Role |
|---|---|
| Scope of Work (SoW) | Clearly defines the assets, IPs/domains, and types of testing permitted |
| Rules of Engagement (RoE) | Time window, prohibited techniques (e.g., DoS), emergency contact |
| Authorization Letter ("get-out-of-jail") | A signed authorization document from an authorized person |
| NDA | Confidentiality of the data discovered |
12.6.3. Principles for Safe Operation
- Do NOT scan/exploit outside the list of IPs/domains in scope. In Burp: enable "Drop all out-of-scope traffic".
- Avoid destructive techniques (
--script dos, brute force causing lockouts) unless explicitly authorized and within a maintenance window. - Be careful with cloud assets: AWS/GCP/Azure require compliance with their own pen-test policies; some managed services are forbidden from being tested.
- Log every action (with timestamps) as evidence of scope and to support investigation in case of an incident.
- Sensitive data extracted during testing must be stored encrypted and securely deleted after completion, in accordance with the NDA.
12.7. Chapter Summary
This chapter went from distinguishing the concepts (scanning vs pentest), through the 5-phase OWASP process, to the three pillar tools with detail down to the packet/field/step level and runnable examples:
- Burp Suite — deep manual control: the MITM architecture with a CA cert, Proxy, Target/Scope, Repeater (IDOR, SQLi), Intruder (4 attack types), Decoder, Comparer, Sequencer, Scanner; the end-to-end XSS workflow.
- Acunetix — automated DAST: crawl + audit, scan configuration, reading reports, verifying false positives; complements rather than replaces Burp.
- Nmap — network mapping down to the TCP flag level: -sS/-sT/-sU/-sN/-sF/-sX, -sn host discovery, -sV/-O fingerprinting, -p/-T tuning, NSE (--script vuln), reading states and exporting results.
The overarching principle: understand the mechanics down to the root, always manually verify automated results, and absolutely respect the legal scope.
My notes
Personal notes: points I previously misunderstood, areas I'm still exploring, or lessons from hands-on practice — updated over time.