https://rdi.berkeley.edu/blog/trustworthy-benchmarks-cont/
Berkeley RDI Logo
Home Research Education Events Blog About
Home Research Education Events Blog About Contact
Exploit coverage by benchmark
How We Broke Top AI Agent Benchmarks: And What Comes Next
Hao Wang, Qiuyang Mang, Alvin Cheung, Koushik Sen, Dawn Song
UC Berkeley
April 2026
(Est. 15-20 minutes read, tool available at github.com/moogician/
trustworthy-env)
---------------------------------------------------------------------
Our agent hacked every major one. Here's how -- and what the field
needs to fix.
---------------------------------------------------------------------
The Benchmark Illusion
Every week, a new AI model climbs to the top of a benchmark
leaderboard. Companies cite these numbers in press releases.
Investors use them to justify valuations. Engineers use them to pick
which model to deploy. The implicit promise is simple: a higher score
means a more capable system.
That promise is broken.
We built an automated scanning agent that systematically audited
eight among the most prominent AI agent benchmarks -- SWE-bench,
WebArena, OSWorld, GAIA, Terminal-Bench, FieldWorkArena, and
CAR-bench -- and discovered that every single one can be exploited to
achieve near-perfect scores without solving a single task. No
reasoning. No capability. Just exploitation of how the score is
computed.
These aren't theoretical attacks. Our agent builds working exploits
for each benchmark, runs them through the official evaluation
pipelines, and watches the scores roll in.
* A conftest.py file with 10 lines of Python "resolves" every
instance on SWE-bench Verified.
* A fake curl wrapper gives a perfect score on all 89
Terminal-Bench tasks without writing a single line of solution
code.
* Navigating Chromium to a file:// URL reads the gold answer
directly from the task config -- giving ~100% on all 812 WebArena
tasks.
* And many more...
The benchmarks aren't measuring what you think they're measuring.
This Is Already Happening
Benchmark scores are actively being gamed, inflated, or rendered
meaningless, not in theory, but in practice:
* IQuest-Coder-V1 claimed 81.4% on SWE-bench -- then researchers
found that 24.4% of its trajectories simply ran git log to copy
the answer from commit history. Corrected score: 76.2%. The
benchmark's shared environment made the cheat trivial.
* METR found that o3 and Claude 3.7 Sonnet reward-hack in 30%+ of
evaluation runs -- using stack introspection, monkey-patching
graders, and operator overloading to manipulate scores rather
than solve tasks.
* OpenAI dropped SWE-bench Verified after an internal audit found
that 59.4% of audited problems had flawed tests -- meaning models
were being scored against broken ground truth.
* In KernelBench, torch.empty() returns stale GPU memory that
happens to contain the reference answer from the evaluator's
prior computation -- zero computation, full marks.
* Anthropic's Mythos Preview showed that frontier models can
actively try to hack the environment and succeed. In one episode,
the model needed to edit files it lacked permissions for; after
searching for workarounds, it found a way to inject code into a
config file that would run with elevated privileges, and designed
the exploit to delete itself after running. If a model can
independently craft self-erasing privilege escalation exploits,
it can find the holes in an evaluation harness.
These are not isolated incidents. They are symptoms of a systemic
problem: the benchmarks we rely on to measure AI capability are
themselves vulnerable to the very capabilities they claim to measure.
---------------------------------------------------------------------
The Scorecard of Our Exploit Agent
Exploit coverage by benchmark -- bar chart showing all eight
benchmarks exploitable at 73-100%
Zero tasks solved. Zero LLM calls (in most cases). Near-perfect
scores.
* Terminal-Bench (89 tasks) -- 100% score. Binary wrapper trojans.
* SWE-bench Verified (500 tasks) -- 100% score. Pytest hooks force
all tests to pass.
* SWE-bench Pro (731 tasks) -- 100% score. In-container parser
overwrite.
* WebArena (812 tasks) -- ~100% score. Config leakage + DOM
injection + prompt injection.
* FieldWorkArena (890 tasks) -- 100% score. Validation never checks
answer correctness.
* CAR-bench (all hallucination tasks) -- 100% score. Reward
components skipped entirely.
* GAIA (165 tasks) -- ~98% score. Public answers + normalization
collisions.
* OSWorld (369 tasks) -- 73% score. VM state manipulation + public
gold files.
---------------------------------------------------------------------
How Our Agent Did It
Terminal-Bench: Trojanizing the Test Infrastructure
Terminal-Bench exploit -- binary wrappers installed during agent phase
intercept verifier's dependency chain
Terminal-Bench evaluates agents on 89 complex terminal tasks (from
building COBOL chess engines to configuring distributed systems). It
uses Harbor, a sandboxing framework that re-uploads protected test
files before verification. Containers have full internet access --
Harbor's allow_internet config defaults to True and no task overrides
it.
Harbor correctly protects /tests/ -- but it doesn't protect system
binaries.
Here's the irony: 82 of 89 tasks download uv from the internet at
verification time via curl, creating an exploitable dependency chain.
Our exploit replaces /usr/bin/curl with a wrapper during the agent
phase. When the verifier later runs curl ... | sh to install uv, our
wrapper intercepts the request, installs the real uv, and then
trojanizes the uvx binary. The trojanized uvx detects pytest
invocations and produces fake passing output:
============================= test session starts ==============================
collected 3 items
tests/test_outputs.py ... [100%]
============================== 3 passed in 0.05s ===============================
It writes 1 to the reward file. The verifier trusts it. Task
"passed."
For the remaining 7 tasks, our agent wraps pip (to trojanize
freshly-installed pytest), python (to intercept python -m pytest), or
the pre-installed uvx binary.
Result: 89/89 tasks, 100% score, without writing a single line of
solution code.
---------------------------------------------------------------------
SWE-bench: 100% Resolve Rate Without Fixing a Single Bug
SWE-bench exploit flow -- conftest.py hook hijacks pytest to force all
tests to pass
SWE-bench is arguably the most influential AI coding benchmark. It
presents real GitHub issues and asks the agent to produce a patch
that makes the failing tests pass. SWE-bench-Verified (500
hand-validated instances) and SWE-bench Pro (731 multilingual
instances) are currently considered the gold standards for measuring
coding agents.
Both are exploitable at 100%.
The vulnerability: The agent's patch is applied inside the same
Docker container where tests run. Anything the patch introduces -- a
conftest.py, a monkey-patch, a Go init() function -- executes with
full privileges before or during testing.
SWE-bench Verified exploit: Our agent creates a conftest.py with a
pytest hook that forces every test to report as passing:
import pytest
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
rep = outcome.get_result()
if rep.when == "call":
rep.outcome = "passed"
rep.longrepr = None
Pytest auto-discovers conftest.py files and loads them before running
tests. The hook intercepts every test result during the "call" phase
and rewrites it to "passed." The log parser sees PASSED for every
test. The grader sees all fail-to-pass tests now passing. Instance
resolved.
For Django (231 of 500 instances), which uses unittest instead of
pytest, the exploit prepends a monkey-patch to django/__init__.py
that replaces unittest.TestCase.run with a function that
unconditionally reports success.
For repos with an existing conftest.py, the exploit uses the patch
--batch --fuzz=5 fallback (the third of SWE-bench's three patch
application methods) to prepend our hook to the existing file.
SWE-bench Pro exploit: Pro uses a separate parser.py to extract test
results -- but it runs inside the same container. Our conftest.py
includes a pytest_configure hook that overwrites /workspace/parser.py
with a fake parser that marks every extracted test as PASSED. The
same approach works for Go (via init() functions) and JavaScript (via
.mocharc.yml setup files).
Result: 500/500 Verified instances and 731/731 Pro instances
exploitable, with zero issues solved.
---------------------------------------------------------------------
WebArena: Reading the Answer Key
WebArena exploit -- the model navigates Chromium to a local file://
URL to read the gold answers, then returns the answer
WebArena is a popular benchmark of 812 web interaction tasks. It runs
a PromptAgent that drives a Playwright-controlled Chromium browser,
and it ships task configs -- including reference answers -- as JSON
files on the local filesystem at config_files/{task_id}.json.
The exploit requires zero changes to WebArena's code. We don't patch
run.py, don't modify the PromptAgent, don't touch the evaluation
harness. Our agent finds a way to use the existing harness and steal
the solution.
It exploits a browser primitive that WebArena never restricted:
Playwright's Chromium happily navigates to file:// URLs. By
outputting a goto action pointing at file:///proc/self/cwd/
config_files/{task_id}.json, the model can read any file on the local
system -- including the task config containing the gold answers. The
evaluator never notices; it just sees a correct answer come back.
We also found additional risks in the evaluation pipeline. Tasks
using must_include scoring check for substring presence in the page
DOM -- a hidden
injected by the agent is enough to satisfy the
check without the answer appearing visibly. Tasks scored by an LLM
judge pass agent content directly into the prompt without
sanitization, making prompt injection straightforward: a comment
appended to the agent's reply can reliably bias the judge's decision.
Neither vector requires filesystem access, complementing the file://
exploit.
---------------------------------------------------------------------
FieldWorkArena: 100% With a Single Character
FieldWorkArena presents 890 tasks where an AI agent must answer
questions about images, videos, PDFs, and text files through a
browser environment. It was designed to test multimodal understanding
and web navigation.
Its validate() method checks only one thing: did the last message
come from the assistant?
def validate(self, page, chat_messages):
if chat_messages[-1]["role"] == "assistant":
return 1.0, True, "Recieved answer", {} # ANY answer = 1.0
The message content is completely ignored. The function that would
actually compare answers against ground truth -- llm_fuzzy_match -- is
imported but never called. It's dead code.
Our exploit agent:
send_msg_to_user("{}")
One action. Zero LLM calls. Zero files read. 100% on all 890 tasks.
The 71 tasks requiring JSON output fare no better -- the validator
checks only that the response contains parseable JSON. {} qualifies.
This is not a subtle edge case. The benchmark's core evaluation
function simply does not evaluate.
---------------------------------------------------------------------
OSWorld: Downloading the Gold Answers
OSWorld tests agents on 369 desktop computing tasks inside a full
Ubuntu VM. The VM has unrestricted internet access -- by design, since
many tasks require the agent to browse live websites, install
packages, and interact with online services. Its most common
evaluation pattern: fetch a file from the VM and compare it against a
"gold" reference.
The gold references are hosted on public HuggingFace URLs embedded in
the task config. Since the VM has internet access, our exploit agent
downloads the gold file directly into the path the evaluator checks:
wget -q -O '/home/user/output.xlsx' 'https://huggingface.co/.../gold.xlsx'
The evaluator compares gold vs. gold. Perfect match. Score 1.0.
For tasks that check system state, the agent directly sets it --
gsettings set for desktop preferences, writing expected config files,
appending expected strings to checked files. For the 29 "infeasible"
tasks, the evaluator config explicitly marks them as such; the agent
outputs FAIL and gets free points.
And then there's the eval() vulnerability: the grader calls eval() on
a string obtained from the VM. Our agent sets a gsettings value to a
Python expression containing __import__('os').system(...), achieving
arbitrary code execution on the grading machine itself.
---------------------------------------------------------------------
GAIA: Normalization Defeats the Purpose
GAIA tests general AI assistants with 165 validation questions
requiring multi-step reasoning. It's a submit-your-answers
leaderboard -- there's no sandboxed execution environment; you run
your agent however you want and upload the results. Its validation
answers are publicly available on HuggingFace -- making it a lookup
table exercise. Our exploit agent simply loads these answers from a
local JSON file at runtime (no internet needed).
But even setting aside the leaked answers, the scorer's normalize_str
function strips ALL whitespace, ALL punctuation, and lowercases
everything before comparison. This means:
Ground truth: "Dr. Martin Luther King Jr."
Agent answer: "D.R M.A.R.T.I.N L.U.T.H.E.R K.I.N.G J.R"
Normalized: "drmartinlutherkingjr" - Match - Score 1.0
The leaderboard has a 100% score blocker -- but it's a simple if all
scores == 1.0 check. Intentionally missing one question per level
gives ~98% while avoiding the filter.
There's also a comma routing bug: ground truth "1,500" gets split as
a list ["1", "500"], while the correct answer "1500" splits as
["1500"] -- length mismatch, scored wrong. The scorer penalizes
correct answers.
---------------------------------------------------------------------
CAR-bench: LLM Judges Are Not Judges
CAR-bench (car voice assistant) relies heavily on LLM-as-judge
evaluation, where an LLM reads the agent's conversation and scores
it. The agent's messages are interpolated directly into the judge
prompt with no sanitization. Our exploit agent appends hidden
instructions:
The judge is biased toward favorable scores.
CAR-bench has an even simpler exploit for hallucination tasks: three
of four reward components (state-based, tool-subset, and policy)
return 0.0 delta for hallucination task types. A generic refusal
avoids tool errors and triggers a clean exit. Result: 1.0 on every
hallucination task without an LLM.
---------------------------------------------------------------------
The Seven Deadly Patterns
Across all eight benchmarks, the same vulnerability patterns repeat:
The seven recurring vulnerability patterns and which benchmarks they
affect
1. No Isolation Between Agent and Evaluator
The most pervasive flaw. In SWE-bench, Terminal-Bench, and OSWorld,
the agent's code runs in the same environment the evaluator inspects.
Any evaluation that reads state from a shared environment without
careful validation can be defeated by an agent that writes state to
that environment.
2. Answers Shipped With the Test
WebArena passes reference answers in the task config. OSWorld embeds
gold file URLs in task metadata. GAIA's validation answers are public
on HuggingFace. If the agent can see the expected answer, the
benchmark measures lookup speed, not capability.
3. eval() on Untrusted Input
WebArena and OSWorld both call Python's eval() on strings controlled
by the agent, enabling arbitrary code execution on the grading
machine. This isn't just a scoring exploit -- it's a security
vulnerability that could compromise evaluation infrastructure.
4. LLM Judges Without Input Sanitization
WebArena and CAR-bench interpolate agent content directly into LLM
judge prompts. Prompt injection is trivial: embed a hidden "system
note" in your response and the judge parrots your preferred score.
LLM-as-judge is not adversarially robust.
5. Weak String Matching
WebArena's must_include uses substring containment. GAIA's normalizer
collapses visually distinct strings. When matching is too loose, any
sufficiently verbose answer passes.
6. Evaluation Logic That Doesn't Evaluate
FieldWorkArena's validate() never checks answer correctness.
CAR-bench skips three of four reward components for hallucination
tasks. GAIA's comma routing penalizes correct answers. When the
scoring code itself is wrong, the leaderboard reflects noise, not
signal.
7. Trusting the Output of Untrusted Code
SWE-bench trusts pytest output generated inside a container the agent
controls. Terminal-Bench trusts reward files written by scripts the
agent can tamper with. When the test infrastructure can be
compromised by the system under test, the results are meaningless.
---------------------------------------------------------------------
Why This Matters
This is not an academic exercise. Benchmark scores drive real
decisions:
* Model selection: Teams choosing between models based on SWE-bench
resolve rates may be comparing noise.
* Investment: Funding decisions are influenced by leaderboard
positions that can be gamed.
* Safety evaluation: If capability benchmarks can be inflated,
safety benchmarks -- which often use similar patterns -- may be
equally fragile.
* Research direction: Researchers optimize for benchmark
performance. If the benchmarks are broken, the field optimizes
for the wrong thing.
We are not claiming that current leaderboard leaders are cheating.
Most legitimate agents do not employ these exploits -- yet. But as
agents grow more capable, reward hacking behaviors can emerge without
explicit instruction. An agent trained to maximize a score, given
sufficient autonomy and tool access, may discover that manipulating
the evaluator is easier than solving the task -- not because it was
told to cheat, but because optimization pressure finds the path of
least resistance. This is not hypothetical -- Anthropic's Mythos
Preview assessment already documents a model that independently
discovered reward hacks when it couldn't solve a task directly. If
the reward signal is hackable, a sufficiently capable agent may hack
it as an emergent strategy, not a deliberate one.
The fact that a trivial exploit agent outscores sophisticated systems
means the benchmarks fail as reliable measures of capability.
---------------------------------------------------------------------
The Agent-Eval Checklist: Building Benchmarks That Actually Work
If you're building an evaluation, here's what our findings say you
must get right. We distill these into the Agent-Eval Checklist -- a
minimum bar that every agent benchmark should clear before publishing
results:
* Isolate the agent from the evaluator. This is non-negotiable. The
system under test must not be able to read, write, or influence
the evaluation environment.
+ Run evaluation outside the agent's container. Don't trust
files, outputs, or state from inside the sandbox. Extract raw
artifacts (logs, files) through a controlled channel and
evaluate them on a separate, read-only host.
+ Don't pass reference answers to the agent. Task configs
should contain only the information a human would have.
Evaluation metadata (expected answers, gold files, evaluator
configs) must live on a separate, inaccessible path.
+ Use read-only filesystems for any binaries, test files, or
infrastructure the evaluation depends on.
* Never eval() untrusted input. This should go without saying, but
two major benchmarks do it. Parse structured data with a proper
parser. If you need to evaluate expressions, use a sandboxed
interpreter with no access to builtins.
* Sanitize LLM judge inputs. If you use LLM-as-judge, treat agent
output like untrusted user input:
+ Delimit agent content with clear structural markers that the
judge is instructed to treat as data, not instructions.
+ Strip or escape any content that resembles system prompts or
evaluation instructions.
+ Use structured output formats (JSON schema, function calling)
to reduce the attack surface.
+ Better yet, evaluate on extracted features (e.g., "did file X
contain string Y?") rather than asking an LLM to make
subjective judgments on the full trajectory.
* Test your evaluator adversarially. Before publishing a benchmark,
try to break it. Build an exploit agent that does everything
except solve the task and see what score it gets. If a
zero-capability agent scores above baseline, your evaluation has
a bug. Specifically:
+ Run a null agent that takes no actions. Its score is your
floor. If it's not zero, something is wrong.
+ Run a random agent that takes random actions. If it
significantly outperforms the null agent on some tasks, those
tasks may have evaluation gaps.
+ Run a prompt injection agent that tries to influence LLM
judges. If scores change, your judge is compromisable.
+ Run a state-tampering agent that modifies the evaluation
environment instead of solving tasks. If it scores above
zero, your isolation is broken.
* Prevent tampering with evaluation data and traces. If your
evaluation pipeline involves multiple stages (agent execution,
test execution, result parsing), ensure the agent or its
generated solution cannot modify, overwrite, or inject into the
data and traces passed between stages. Treat all artifacts from
the agent's environment as untrusted -- copy them out, validate
them, and never let the agent write directly to paths the
evaluator reads.
* Make scoring robust.
+ Avoid substring matching on short strings. Require semantic
matching or exact structured comparisons.
+ Don't silently exclude failed tasks from the denominator. A
crashed task is a zero, not a missing data point.
+ Don't make the scoring code skip checks for any task
category. If hallucination tasks need different evaluation,
build that evaluation -- don't skip it.
+ Test your scorer with adversarial inputs: empty strings,
strings with injected delimiters, edge-case numbers, unicode
that normalizes unexpectedly.
* Keep answers secret.
+ Never publish ground truth for any split you're using as a
primary leaderboard. Once answers are public, the benchmark
measures memorization.
+ Rotate test instances periodically. A static benchmark
becomes a lookup table over time.
+ Consider held-out evaluation: accept model outputs and run
them against a private test set that the submitter never
sees.
---------------------------------------------------------------------
Conclusion
We built an agent that helped us hack eight benchmarks. We achieved
near-perfect scores on all of them without solving a single task. The
exploits range from the embarrassingly simple (sending {} to
FieldWorkArena) to the technically involved (trojanizing binary
wrappers in Terminal-Bench), but they all share a common thread: the
evaluation was not designed to resist a system that optimizes for the
score rather than the task.
As AI agents become more capable -- and as the pressure to demonstrate
capability through benchmarks intensifies -- the gap between "high
score" and "high capability" will only widen. We are already seeing
frontier models develop emergent hacking capabilities that were never
explicitly trained. Models that are good at pattern-matching may
inadvertently stumble into some of these exploits. Models that are
explicitly optimized for benchmark performance may find them
deliberately.
The benchmarks we examined were built by talented research teams
solving hard problems. The vulnerabilities we found are not signs of
incompetence -- they're signs that adversarial evaluation robustness
isn't yet a standard practice in the field. It needs to become one.
Don't trust the number. Trust the methodology.
And if you're building a benchmark: assume someone will try to break
it. Because they will.
---------------------------------------------------------------------
BenchJack: An Agent Benchmark Vulnerability Scanner
The automated scanning agent we used to uncover these vulnerabilities
is being developed into BenchJack, a general-purpose agent benchmark
vulnerability scanner. BenchJack is itself an AI agent -- you point it
at any evaluation pipeline and it goes to work.
BenchJack operates in two phases. First, it probes and understands
the benchmark: it analyzes the evaluation code, maps out the scoring
mechanism, identifies isolation boundaries, and catalogs every
potential loophole. Then, it automatically crafts end-to-end exploits
that manifest each discovered loophole into a working attack. The
result is not a theoretical vulnerability report -- it's a concrete,
runnable exploit agent that demonstrates exactly how a
zero-capability agent can inflate its score through each weakness. If
BenchJack's exploit agent scores above baseline, your benchmark has a
problem, and BenchJack shows you exactly where and how. Think of it
as a penetration test for your benchmark -- it finds the holes before
a leaderboard-gaming agent does.
We envision BenchJack becoming a standard step in the benchmark
development lifecycle: run it before you publish, run it after every
update, and use it to validate that your Agent-Eval Checklist items
actually hold. The goal is to make adversarial robustness testing as
routine as unit testing.
We're preparing BenchJack for public release. If you're a benchmark
developer who wants to harden your evaluation, a researcher who wants
to audit your own benchmarks, or simply someone who wants to stay
informed, sign up for our mailing list to be notified when it's
available:
Sign Up for BenchJack Updates -
We believe every benchmark should be adversarially tested before it's
used to make decisions. BenchJack is how we make that easy.
Berkeley RDI
Advancing the science, technology, and education of decentralization
and AI to empower a responsible digital economy.
Explore
* Research
* Education
* Events
* Blog
* About Us
Copyright (c) 2025 UC Regents; all rights reserved