APIs are where modern applications actually live. The browser is just a thin shell; the real logic, data and authorization decisions sit behind a growing surface of REST and GraphQL endpoints. That is also where we find the most impactful bugs during penetration tests. The OWASP API Security Top 10 (2023 edition) is a good map of the terrain, and after enough engagements the same handful of categories keep coming back.
The flaws we see over and over
Broken Object Level Authorization (API1) is still the single most common serious finding. The endpoint checks that you are logged in, but not that the record you asked for is yours. Change /api/invoices/1042 to /api/invoices/1043 and you read someone else's invoice. It is trivial to test and painfully frequent in the wild.
Broken Object Property Level Authorization (API3) is the quieter cousin: the object is yours, but the API lets you read or write fields you should not touch — flipping "role": "user" to "role": "admin" in a JSON body, or receiving back a field that leaks another user's data. Mass assignment and excessive data exposure both live here.
Broken Authentication (API2) and Broken Function Level Authorization (API5) round out the authorization problems: weak token handling, admin endpoints reachable by normal users, verbs that skip the checks the UI enforces.
Server-Side Request Forgery (API7) and Unsafe Consumption of APIs (API10) are where APIs get tricked into fetching or trusting things they should not. Any endpoint that accepts a URL, a file path, a "template", or an "include" parameter and acts on it server-side is worth a very close look. That is the thread we want to pull on next.
An illustrative chain: from a "low" file inclusion to RCE
This is a generic, anonymized walkthrough — no real client, no confidential data. The vulnerability class is old, but we still find it in modern .NET applications, which is exactly why it is worth explaining. We keep it at the methodology level, defensive rather than a copy-paste exploit.
Picture an API endpoint that takes a parameter — call it template, report, or include — and loads a file server-side to build a response. The recon question is simple: does untrusted input reach a file or loader API?
Local file inclusion first. We point the parameter at known server files — the application's own web.config, source files, a path that reveals connection strings. If the response reflects their contents, we have proven that attacker-controlled input reaches a file-reading sink. On its own a scanner might rate this "medium": information disclosure.
Then remote inclusion. We point the same parameter at an attacker-hosted resource. If the application fetches and processes it, the endpoint is doing outbound requests with no allow-listing of sources — the SSRF-adjacent behaviour OWASP flags in API7. Now the attacker controls not just which file is read, but its contents.
Then the dangerous sink. The critical step is finding where that attacker-controlled content gets interpreted rather than merely read. In .NET the classic sink is unsafe deserialization. Formatters such as BinaryFormatter, LosFormatter and ObjectStateFormatter perform unrestricted polymorphic deserialization, and Microsoft is blunt about it: BinaryFormatter is insecure and can't be made secure — from .NET 9 it throws on use. Feed one of these formatters attacker-controlled bytes and publicly available gadget chains turn deserialization into code execution. A server-side view or template that gets compiled, or a write into an executable application directory, achieves the same end.
The impact. Chaining a "low" inclusion bug with a dangerous sink yields command execution in the application pool identity — a critical finding built entirely from individually unremarkable weaknesses. This is the point pentesting makes that a scanner cannot: risk lives in the chain, not the isolated line item.
How to shut it down
Strict input allow-listing is the foundation: never let user input decide a file path or URL. Disable the dangerous deserializers — prefer System.Text.Json, XmlSerializer or DataContractSerializer, and if a legacy formatter is unavoidable, constrain it with a SerializationBinder type allow-list. Do not compile or execute user-supplied content. Run the application pool under least privilege, and add egress filtering so a compromised service cannot freely reach the internet. Each control breaks a different link in the chain.
Mapped back to OWASP, this single story touches Broken Object Property Level Authorization, Server-Side Request Forgery and Unsafe Consumption of APIs — three of the ten, in one endpoint.
The takeaway
Most of the API flaws we report are not exotic. They are authorization gaps and unvalidated inputs that scanners under-rate because the danger only appears when weaknesses combine. A penetration test exists precisely to find those chains before an attacker does. If your APIs have grown faster than your security testing has, that gap is worth closing deliberately.
